| /* |
| * Google LWIS Device Tree Parser |
| * |
| * Copyright (c) 2018 Google, LLC |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME "-dt: " fmt |
| |
| #include "lwis_dt.h" |
| |
| #include <linux/kernel.h> |
| #include <linux/of.h> |
| #include <linux/of_address.h> |
| #include <linux/of_gpio.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/slab.h> |
| |
| #include "lwis_clock.h" |
| #include "lwis_device_dpm.h" |
| #include "lwis_gpio.h" |
| #include "lwis_i2c.h" |
| #include "lwis_ioreg.h" |
| #include "lwis_regulator.h" |
| |
| #define SHARED_STRING "shared-" |
| #define PULSE_STRING "pulse-" |
| |
| /* Uncomment this to help debug device tree parsing. */ |
| // #define LWIS_DT_DEBUG |
| |
| static int parse_gpios(struct lwis_device *lwis_dev, char *name, bool *is_present) |
| { |
| int count; |
| struct device *dev; |
| struct gpio_descs *list; |
| |
| *is_present = false; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| |
| count = gpiod_count(dev, name); |
| |
| /* No GPIO pins found, just return */ |
| if (count <= 0) { |
| return 0; |
| } |
| |
| list = lwis_gpio_list_get(dev, name); |
| if (IS_ERR_OR_NULL(list)) { |
| pr_err("Error parsing GPIO list %s (%ld)\n", name, PTR_ERR(list)); |
| return PTR_ERR(list); |
| } |
| |
| /* The GPIO pins are valid, release the list as we do not need to hold |
| on to the pins yet */ |
| lwis_gpio_list_put(list, dev); |
| *is_present = true; |
| return 0; |
| } |
| |
| static int parse_irq_gpios(struct lwis_device *lwis_dev) |
| { |
| int count; |
| int name_count; |
| int event_count; |
| int type_count; |
| int ret; |
| struct device *dev; |
| struct device_node *dev_node; |
| struct gpio_descs *gpios; |
| const char *name; |
| char *irq_gpios_names = NULL; |
| u64 *irq_gpios_events = NULL; |
| u32 *irq_gpios_types = NULL; |
| int i; |
| |
| /* Initialize the data structure */ |
| strscpy(lwis_dev->irq_gpios_info.name, "irq", LWIS_MAX_NAME_STRING_LEN); |
| lwis_dev->irq_gpios_info.gpios = NULL; |
| lwis_dev->irq_gpios_info.irq_list = NULL; |
| lwis_dev->irq_gpios_info.is_shared = false; |
| lwis_dev->irq_gpios_info.is_pulse = false; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| count = gpiod_count(dev, "irq"); |
| /* No irq GPIO pins found, just return */ |
| if (count <= 0) { |
| return 0; |
| } |
| |
| dev_node = dev->of_node; |
| name_count = of_property_count_strings(dev_node, "irq-gpios-names"); |
| event_count = of_property_count_elems_of_size(dev_node, "irq-gpios-events", sizeof(u64)); |
| type_count = of_property_count_elems_of_size(dev_node, "irq-gpios-types", sizeof(u32)); |
| |
| if (count != event_count || count != name_count || count != type_count) { |
| pr_err("Count of irq-gpios-* is not match\n"); |
| return -EINVAL; |
| } |
| |
| gpios = lwis_gpio_list_get(dev, "irq"); |
| if (IS_ERR_OR_NULL(gpios)) { |
| pr_err("Error parsing irq GPIO list (%ld)\n", PTR_ERR(gpios)); |
| return PTR_ERR(gpios); |
| } |
| lwis_dev->irq_gpios_info.gpios = gpios; |
| |
| lwis_dev->irq_gpios_info.irq_list = lwis_interrupt_list_alloc(lwis_dev, gpios->ndescs); |
| if (IS_ERR_OR_NULL(lwis_dev->irq_gpios_info.irq_list)) { |
| ret = -ENOMEM; |
| lwis_dev->irq_gpios_info.irq_list = NULL; |
| pr_err("Failed to allocate irq list\n"); |
| goto error_parse_irq_gpios; |
| } |
| |
| irq_gpios_names = kmalloc(LWIS_MAX_NAME_STRING_LEN * name_count, GFP_KERNEL); |
| if (IS_ERR_OR_NULL(irq_gpios_names)) { |
| pr_err("Allocating event list failed\n"); |
| ret = -ENOMEM; |
| goto error_parse_irq_gpios; |
| } |
| |
| for (i = 0; i < name_count; ++i) { |
| ret = of_property_read_string_index(dev_node, "irq-gpios-names", i, &name); |
| if (ret < 0) { |
| pr_err("Error get GPIO irq name list (%d)\n", ret); |
| goto error_parse_irq_gpios; |
| } |
| strscpy(irq_gpios_names + i * LWIS_MAX_NAME_STRING_LEN, name, |
| LWIS_MAX_NAME_STRING_LEN); |
| } |
| |
| irq_gpios_types = kmalloc(sizeof(u32) * type_count, GFP_KERNEL); |
| if (IS_ERR_OR_NULL(irq_gpios_types)) { |
| pr_err("Allocating irq_gpios_types list failed\n"); |
| ret = -ENOMEM; |
| goto error_parse_irq_gpios; |
| } |
| |
| type_count = of_property_read_variable_u32_array(dev_node, "irq-gpios-types", |
| irq_gpios_types, type_count, type_count); |
| |
| if (type_count != count) { |
| pr_err("Error getting irq-gpios-types: %d\n", type_count); |
| ret = type_count; |
| goto error_parse_irq_gpios; |
| } |
| |
| irq_gpios_events = kmalloc(sizeof(u64) * event_count, GFP_KERNEL); |
| if (IS_ERR_OR_NULL(irq_gpios_events)) { |
| pr_err("Allocating event list failed\n"); |
| ret = -ENOMEM; |
| goto error_parse_irq_gpios; |
| } |
| |
| event_count = of_property_read_variable_u64_array( |
| dev_node, "irq-gpios-events", irq_gpios_events, event_count, event_count); |
| if (event_count != count) { |
| pr_err("Error getting irq-gpios-events: %d\n", event_count); |
| ret = event_count; |
| goto error_parse_irq_gpios; |
| } |
| |
| for (i = 0; i < event_count; ++i) { |
| ret = lwis_interrupt_set_gpios_event_info(lwis_dev->irq_gpios_info.irq_list, i, |
| irq_gpios_events[i]); |
| if (ret) { |
| pr_err("Error setting event info for gpios interrupt %d %d\n", i, ret); |
| goto error_parse_irq_gpios; |
| } |
| } |
| |
| for (i = 0; i < gpios->ndescs; ++i) { |
| char *name; |
| int irq; |
| irq = gpiod_to_irq(gpios->desc[i]); |
| if (irq < 0) { |
| pr_err("gpio to irq failed (%d)\n", irq); |
| lwis_interrupt_list_free(lwis_dev->irq_gpios_info.irq_list); |
| return irq; |
| } |
| name = irq_gpios_names + i * LWIS_MAX_NAME_STRING_LEN; |
| lwis_interrupt_get_gpio_irq(lwis_dev->irq_gpios_info.irq_list, i, name, irq, |
| irq_gpios_types[i]); |
| } |
| |
| kfree(irq_gpios_names); |
| kfree(irq_gpios_events); |
| kfree(irq_gpios_types); |
| return 0; |
| |
| error_parse_irq_gpios: |
| if (lwis_dev->irq_gpios_info.gpios) { |
| lwis_gpio_list_put(lwis_dev->irq_gpios_info.gpios, dev); |
| lwis_dev->irq_gpios_info.gpios = NULL; |
| } |
| if (lwis_dev->irq_gpios_info.irq_list) { |
| lwis_interrupt_list_free(lwis_dev->irq_gpios_info.irq_list); |
| lwis_dev->irq_gpios_info.irq_list = NULL; |
| } |
| kfree(irq_gpios_names); |
| kfree(irq_gpios_events); |
| kfree(irq_gpios_types); |
| return ret; |
| } |
| |
| static int parse_settle_time(struct lwis_device *lwis_dev) |
| { |
| struct device_node *dev_node; |
| struct device *dev; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| dev_node = dev->of_node; |
| lwis_dev->enable_gpios_settle_time = 0; |
| |
| of_property_read_u32(dev_node, "enable-gpios-settle-time", |
| &lwis_dev->enable_gpios_settle_time); |
| return 0; |
| } |
| |
| static int parse_regulators(struct lwis_device *lwis_dev) |
| { |
| int i; |
| int ret; |
| int count; |
| struct device_node *dev_node; |
| struct device_node *dev_node_reg; |
| const char *name; |
| struct device *dev; |
| int voltage; |
| int voltage_count; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| dev_node = dev->of_node; |
| |
| count = of_property_count_elems_of_size(dev_node, "regulators", sizeof(u32)); |
| |
| /* No regulators found, or entry does not exist, just return */ |
| if (count <= 0) { |
| lwis_dev->regulators = NULL; |
| return 0; |
| } |
| |
| /* Voltage count is allowed to be less than regulator count, |
| regulator_set_voltage will not be called for the ones with |
| unspecified voltage */ |
| voltage_count = |
| of_property_count_elems_of_size(dev_node, "regulator-voltages", sizeof(u32)); |
| |
| lwis_dev->regulators = lwis_regulator_list_alloc(count); |
| if (IS_ERR_OR_NULL(lwis_dev->regulators)) { |
| pr_err("Cannot allocate regulator list\n"); |
| ret = PTR_ERR(lwis_dev->regulators); |
| lwis_dev->regulators = NULL; |
| return ret; |
| } |
| |
| /* Parse regulator list and acquire the regulator pointers */ |
| for (i = 0; i < count; ++i) { |
| dev_node_reg = of_parse_phandle(dev_node, "regulators", i); |
| of_property_read_string(dev_node_reg, "regulator-name", &name); |
| voltage = 0; |
| if (i < voltage_count) { |
| of_property_read_u32_index(dev_node, "regulator-voltages", i, &voltage); |
| } |
| ret = lwis_regulator_get(lwis_dev->regulators, (char *)name, voltage, dev); |
| if (ret < 0) { |
| pr_err("Cannot find regulator: %s\n", name); |
| goto error_parse_reg; |
| } |
| } |
| |
| #ifdef LWIS_DT_DEBUG |
| lwis_regulator_print(lwis_dev->regulators); |
| #endif |
| |
| return 0; |
| |
| error_parse_reg: |
| /* In case of error, free all the other regulators that were alloc'ed */ |
| lwis_regulator_put_all(lwis_dev->regulators); |
| lwis_regulator_list_free(lwis_dev->regulators); |
| lwis_dev->regulators = NULL; |
| return ret; |
| } |
| |
| static int parse_clocks(struct lwis_device *lwis_dev) |
| { |
| int i; |
| int ret = 0; |
| int count; |
| int __maybe_unused bts_count; |
| struct device *dev; |
| struct device_node *dev_node; |
| const char *name; |
| u32 rate; |
| int clock_family; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| dev_node = dev->of_node; |
| |
| count = of_property_count_strings(dev_node, "clock-names"); |
| |
| /* No clocks found, just return */ |
| if (count <= 0) { |
| lwis_dev->clocks = NULL; |
| return 0; |
| } |
| |
| lwis_dev->clocks = lwis_clock_list_alloc(count); |
| if (IS_ERR_OR_NULL(lwis_dev->clocks)) { |
| pr_err("Cannot allocate clocks list\n"); |
| ret = PTR_ERR(lwis_dev->clocks); |
| lwis_dev->clocks = NULL; |
| return ret; |
| } |
| |
| /* Parse and acquire clock pointers and frequencies, if applicable */ |
| for (i = 0; i < count; ++i) { |
| of_property_read_string_index(dev_node, "clock-names", i, &name); |
| /* It is allowed to omit clock rates for some of the clocks */ |
| ret = of_property_read_u32_index(dev_node, "clock-rates", i, &rate); |
| rate = (ret == 0) ? rate : 0; |
| |
| ret = lwis_clock_get(lwis_dev->clocks, (char *)name, dev, rate); |
| if (ret < 0) { |
| pr_err("Cannot find clock: %s\n", name); |
| goto error_parse_clk; |
| } |
| } |
| |
| /* It is allowed to omit clock rates for some of the clocks */ |
| ret = of_property_read_u32(dev_node, "clock-family", &clock_family); |
| lwis_dev->clock_family = (ret == 0) ? clock_family : CLOCK_FAMILY_INVALID; |
| |
| /* Parse the BTS block names */ |
| bts_count = of_property_count_strings(dev_node, "bts-block-names"); |
| if (bts_count > 0) { |
| lwis_dev->bts_block_num = bts_count; |
| for (i = 0; i < bts_count; ++i) { |
| of_property_read_string_index(dev_node, "bts-block-names", i, &name); |
| lwis_dev->bts_block_names[i] = (const char *)name; |
| } |
| } else { |
| lwis_dev->bts_block_num = 1; |
| lwis_dev->bts_block_names[0] = lwis_dev->name; |
| } |
| |
| /* Initialize all the BTS indexes */ |
| for (i = 0; i < MAX_BTS_BLOCK_NUM; ++i) { |
| lwis_dev->bts_indexes[i] = BTS_UNSUPPORTED; |
| } |
| |
| #ifdef LWIS_DT_DEBUG |
| pr_info("%s: clock family %d", lwis_dev->name, lwis_dev->clock_family); |
| lwis_clock_print(lwis_dev->clocks); |
| #endif |
| |
| return 0; |
| |
| error_parse_clk: |
| /* Put back the clock instances for the ones that were alloc'ed */ |
| for (i = 0; i < count; ++i) { |
| lwis_clock_put_by_idx(lwis_dev->clocks, i, dev); |
| } |
| lwis_clock_list_free(lwis_dev->clocks); |
| lwis_dev->clocks = NULL; |
| return ret; |
| } |
| |
| static int parse_pinctrls(struct lwis_device *lwis_dev, char *expected_state) |
| { |
| int count; |
| struct device *dev; |
| struct device_node *dev_node; |
| struct pinctrl *pc; |
| struct pinctrl_state *pinctrl_state; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| dev_node = dev->of_node; |
| |
| lwis_dev->mclk_present = false; |
| lwis_dev->shared_pinctrl = 0; |
| count = of_property_count_strings(dev_node, "pinctrl-names"); |
| |
| /* No pinctrl found, just return */ |
| if (count <= 0) |
| return 0; |
| |
| /* Set up pinctrl */ |
| pc = devm_pinctrl_get(dev); |
| if (IS_ERR_OR_NULL(pc)) { |
| pr_err("Cannot allocate pinctrl\n"); |
| return PTR_ERR(pc); |
| } |
| |
| pinctrl_state = pinctrl_lookup_state(pc, expected_state); |
| if (IS_ERR_OR_NULL(pinctrl_state)) { |
| pr_err("Cannot find pinctrl state %s\n", expected_state); |
| devm_pinctrl_put(pc); |
| return PTR_ERR(pinctrl_state); |
| } |
| |
| /* Indicate if the pinctrl shared with other devices */ |
| of_property_read_u32(dev_node, "shared-pinctrl", &lwis_dev->shared_pinctrl); |
| |
| /* The pinctrl is valid, release it as we do not need to hold on to |
| the pins yet */ |
| devm_pinctrl_put(pc); |
| |
| lwis_dev->mclk_present = true; |
| |
| return 0; |
| } |
| |
| static int parse_irq_reg_bits(struct device_node *info, int *bits_num_result, u32 **reg_bits_result) |
| { |
| int int_reg_bits_num; |
| u32 *int_reg_bits; |
| |
| int_reg_bits_num = of_property_count_elems_of_size(info, "int-reg-bits", 4); |
| if (int_reg_bits_num <= 0) { |
| pr_err("Error getting int-reg-bits: %d\n", int_reg_bits_num); |
| return -EINVAL; |
| } |
| |
| int_reg_bits = kmalloc(sizeof(u32) * int_reg_bits_num, GFP_KERNEL); |
| if (IS_ERR_OR_NULL(int_reg_bits)) { |
| pr_err("Failed to allocate memory for irq regiater bits\n"); |
| return -ENOMEM; |
| } |
| |
| *bits_num_result = int_reg_bits_num; |
| int_reg_bits_num = of_property_read_variable_u32_array(info, "int-reg-bits", int_reg_bits, |
| int_reg_bits_num, int_reg_bits_num); |
| if (*bits_num_result != int_reg_bits_num) { |
| pr_err("Error getting int-reg-bits: %d\n", int_reg_bits_num); |
| kfree(int_reg_bits); |
| return int_reg_bits_num; |
| } |
| *reg_bits_result = int_reg_bits; |
| |
| return 0; |
| } |
| |
| static int parse_critical_irq_events(struct device_node *event_info, u64 **irq_events) |
| { |
| int ret; |
| int critical_irq_events_num; |
| u64 critical_irq_events; |
| int i; |
| |
| critical_irq_events_num = |
| of_property_count_elems_of_size(event_info, "critical-irq-events", 8); |
| /* No Critical IRQ event found, just return */ |
| if (critical_irq_events_num <= 0) { |
| return 0; |
| } |
| |
| *irq_events = kmalloc(sizeof(u64) * critical_irq_events_num, GFP_KERNEL); |
| if (*irq_events == NULL) { |
| pr_err("Failed to allocate memory for critical events\n"); |
| return 0; |
| } |
| |
| for (i = 0; i < critical_irq_events_num; ++i) { |
| ret = of_property_read_u64_index(event_info, "critical-irq-events", i, |
| &critical_irq_events); |
| if (ret < 0) { |
| pr_err("Error adding critical irq events[%d]\n", i); |
| kfree(*irq_events); |
| *irq_events = NULL; |
| return 0; |
| } |
| *irq_events[i] = critical_irq_events; |
| } |
| |
| return critical_irq_events_num; |
| } |
| |
| static int parse_interrupts_event_info(struct lwis_interrupt_list *list, int index, |
| struct device_node *event_info) |
| { |
| int irq_events_num; |
| int int_reg_bits_num = 0; |
| int critical_events_num = 0; |
| u64 *irq_events = NULL; |
| u32 *int_reg_bits = NULL; |
| u64 *critical_events = NULL; |
| int ret = 0; |
| |
| ret = parse_irq_reg_bits(event_info, &int_reg_bits_num, &int_reg_bits); |
| if (ret) { |
| return ret; |
| } |
| |
| irq_events_num = of_property_count_elems_of_size(event_info, "irq-events", 8); |
| if (irq_events_num != int_reg_bits_num || irq_events_num <= 0) { |
| pr_err("Error getting irq-events: %d\n", irq_events_num); |
| ret = -EINVAL; |
| goto event_info_exit; |
| } |
| |
| irq_events = kmalloc(sizeof(u64) * irq_events_num, GFP_KERNEL); |
| if (IS_ERR_OR_NULL(irq_events)) { |
| ret = -ENOMEM; |
| goto event_info_exit; |
| } |
| |
| irq_events_num = of_property_read_variable_u64_array(event_info, "irq-events", irq_events, |
| irq_events_num, irq_events_num); |
| if (irq_events_num != int_reg_bits_num) { |
| pr_err("Error getting irq-events: %d\n", irq_events_num); |
| ret = irq_events_num; |
| goto event_info_exit; |
| } |
| |
| critical_events_num = parse_critical_irq_events(event_info, &critical_events); |
| |
| ret = lwis_interrupt_set_event_info(list, index, (int64_t *)irq_events, irq_events_num, |
| int_reg_bits, int_reg_bits_num, |
| (int64_t *)critical_events, critical_events_num); |
| if (ret) { |
| pr_err("Error setting event info for interrupt %d %d\n", index, ret); |
| goto event_info_exit; |
| } |
| |
| event_info_exit: |
| kfree(critical_events); |
| kfree(irq_events); |
| kfree(int_reg_bits); |
| return ret; |
| } |
| |
| static int find_irq_index_by_name(struct lwis_interrupt_list *list, const char *irq_name) |
| { |
| int i; |
| |
| for (i = 0; i < list->count; ++i) { |
| if (strncmp(irq_name, list->irq[i].name, IRQ_FULL_NAME_LENGTH - 1) == 0) { |
| return i; |
| } |
| } |
| return -ENOENT; |
| } |
| |
| static int parse_interrupt_leaf_nodes(struct lwis_interrupt_list *list, int index, |
| struct device_node *leaf_info) |
| { |
| int irq_leaves_num; |
| int int_reg_bits_num; |
| u32 *int_reg_bits = NULL; |
| struct of_phandle_iterator it; |
| int i = 0, ret = 0; |
| |
| ret = parse_irq_reg_bits(leaf_info, &int_reg_bits_num, &int_reg_bits); |
| if (ret) { |
| return ret; |
| } |
| |
| irq_leaves_num = of_property_count_elems_of_size(leaf_info, "irq-leaf-nodes", 4); |
| if (irq_leaves_num != int_reg_bits_num || irq_leaves_num <= 0) { |
| pr_err("Error getting irq-leaf-nodes: %d\n", irq_leaves_num); |
| ret = -EINVAL; |
| kfree(int_reg_bits); |
| return ret; |
| } |
| |
| i = 0; |
| of_for_each_phandle (&it, ret, leaf_info, "irq-leaf-nodes", 0, 0) { |
| struct device_node *irq_group_node = of_node_get(it.node); |
| int leaf_interrupts_count; |
| const char *leaf_interrupt_name; |
| int32_t *leaf_indexes = NULL; |
| int j = 0; |
| |
| leaf_interrupts_count = |
| of_property_count_strings(irq_group_node, "leaf-interrupt-names"); |
| if (leaf_interrupts_count == -ENODATA) { |
| /* Does not have a value means no leaf interrupt is configured for this */ |
| /* leaf node */ |
| continue; |
| } else if (leaf_interrupts_count < 0) { |
| pr_err("Error counting leaf-interrupt-names for : %d\n", |
| leaf_interrupts_count); |
| ret = -EINVAL; |
| goto leaf_error; |
| } |
| |
| leaf_indexes = kmalloc(sizeof(int32_t) * leaf_interrupts_count, GFP_KERNEL); |
| if (IS_ERR_OR_NULL(leaf_indexes)) { |
| ret = -ENOMEM; |
| goto leaf_error; |
| } |
| |
| for (j = 0; j < leaf_interrupts_count; ++j) { |
| of_property_read_string_index(irq_group_node, "leaf-interrupt-names", j, |
| &leaf_interrupt_name); |
| leaf_indexes[j] = find_irq_index_by_name(list, leaf_interrupt_name); |
| if (leaf_indexes[j] < 0) { |
| ret = leaf_indexes[j]; |
| pr_err("Cannot find leaf irq %s\n", leaf_interrupt_name); |
| kfree(leaf_indexes); |
| goto leaf_error; |
| } |
| } |
| |
| ret = lwis_interrupt_add_leaf(list, index, int_reg_bits[i], leaf_interrupts_count, |
| leaf_indexes); |
| if (ret) { |
| pr_err("Error setting event info for interrupt %d %d\n", index, ret); |
| kfree(leaf_indexes); |
| goto leaf_error; |
| } |
| i++; |
| } |
| |
| return 0; |
| leaf_error: |
| lwis_interrupt_free_leaves(&list->irq[index]); |
| kfree(int_reg_bits); |
| return ret; |
| } |
| |
| static int parse_interrupts(struct lwis_device *lwis_dev) |
| { |
| int i; |
| int ret; |
| int count, event_infos_count; |
| const char *name; |
| struct device_node *dev_node; |
| struct platform_device *plat_dev; |
| struct of_phandle_iterator it; |
| |
| plat_dev = lwis_dev->plat_dev; |
| dev_node = plat_dev->dev.of_node; |
| |
| /* Test device type DEVICE_TYPE_TEST used for test, platform independent. */ |
| if (lwis_dev->type == DEVICE_TYPE_TEST) { |
| count = TEST_DEVICE_IRQ_CNT; |
| } else { |
| count = platform_irq_count(plat_dev); |
| } |
| |
| /* No interrupts found, just return */ |
| if (count <= 0) { |
| lwis_dev->irqs = NULL; |
| return 0; |
| } |
| |
| lwis_dev->irqs = lwis_interrupt_list_alloc(lwis_dev, count); |
| if (IS_ERR_OR_NULL(lwis_dev->irqs)) { |
| if (lwis_dev->type == DEVICE_TYPE_TEST) { |
| pr_err("Failed to allocate injection\n"); |
| } else { |
| pr_err("Failed to allocate IRQ list\n"); |
| } |
| ret = PTR_ERR(lwis_dev->irqs); |
| lwis_dev->irqs = NULL; |
| return ret; |
| } |
| |
| for (i = 0; i < count; ++i) { |
| of_property_read_string_index(dev_node, "interrupt-names", i, &name); |
| ret = lwis_interrupt_init(lwis_dev->irqs, i, (char *)name); |
| if (ret) { |
| pr_err("Cannot initialize irq %s\n", name); |
| goto error_get_irq; |
| } |
| } |
| |
| event_infos_count = of_property_count_elems_of_size(dev_node, "interrupt-event-infos", 4); |
| if (count != event_infos_count) { |
| pr_err("DT numbers of irqs: %d != event infos: %d in DT\n", count, |
| event_infos_count); |
| ret = -EINVAL; |
| goto error_get_irq; |
| } |
| /* Get event infos */ |
| i = 0; |
| of_for_each_phandle (&it, ret, dev_node, "interrupt-event-infos", 0, 0) { |
| const char *irq_reg_space = NULL, *irq_type_str = NULL; |
| bool irq_mask_reg_toggle; |
| u64 irq_src_reg; |
| u64 irq_reset_reg; |
| u64 irq_mask_reg; |
| u64 irq_overflow_reg = 0; |
| int irq_reg_bid = -1; |
| int irq_reg_bid_count; |
| /* To match default value of reg-addr/value-bitwidth. */ |
| u32 irq_reg_bitwidth = 32; |
| int32_t irq_type = REGULAR_INTERRUPT; |
| int j; |
| struct device_node *event_info = of_node_get(it.node); |
| |
| ret = of_property_read_string(event_info, "irq-reg-space", &irq_reg_space); |
| if (ret) { |
| pr_err("Error getting irq-reg-space from dt: %d\n", ret); |
| goto error_event_infos; |
| } |
| |
| irq_reg_bid_count = of_property_count_strings(dev_node, "reg-names"); |
| |
| if (irq_reg_bid_count <= 0) { |
| pr_err("Error getting reg-names from dt: %d\n", irq_reg_bid_count); |
| goto error_event_infos; |
| } |
| for (j = 0; j < irq_reg_bid_count; j++) { |
| const char *bid_name; |
| ret = of_property_read_string_index(dev_node, "reg-names", j, &bid_name); |
| |
| if (ret) { |
| break; |
| } |
| if (!strcmp(bid_name, irq_reg_space)) { |
| irq_reg_bid = j; |
| break; |
| } |
| } |
| if (irq_reg_bid < 0) { |
| pr_err("Could not find a reg bid for %s\n", irq_reg_space); |
| goto error_event_infos; |
| } |
| |
| ret = of_property_read_u64(event_info, "irq-src-reg", &irq_src_reg); |
| if (ret) { |
| pr_err("Error getting irq-src-reg from dt: %d\n", ret); |
| goto error_event_infos; |
| } |
| |
| ret = of_property_read_u64(event_info, "irq-reset-reg", &irq_reset_reg); |
| if (ret) { |
| pr_err("Error getting irq-reset-reg from dt: %d\n", ret); |
| goto error_event_infos; |
| } |
| |
| ret = of_property_read_u64(event_info, "irq-mask-reg", &irq_mask_reg); |
| if (ret) { |
| pr_err("Error getting irq-mask-reg from dt: %d\n", ret); |
| goto error_event_infos; |
| } |
| |
| of_property_read_u64(event_info, "irq-overflow-reg", &irq_overflow_reg); |
| |
| irq_mask_reg_toggle = of_property_read_bool(event_info, "irq-mask-reg-toggle"); |
| |
| of_property_read_u32(event_info, "irq-reg-bitwidth", &irq_reg_bitwidth); |
| |
| ret = of_property_read_string(event_info, "irq-type", &irq_type_str); |
| if (ret && ret != -EINVAL) { |
| pr_err("Error getting irq-type from dt: %d\n", ret); |
| goto error_event_infos; |
| } else if (ret && ret == -EINVAL) { |
| /* The property does not exist, which means regular*/ |
| irq_type = REGULAR_INTERRUPT; |
| } else { |
| if (strcmp(irq_type_str, "regular") == 0) { |
| irq_type = REGULAR_INTERRUPT; |
| } else if (strcmp(irq_type_str, "aggregate") == 0) { |
| irq_type = AGGREGATE_INTERRUPT; |
| } else if (strcmp(irq_type_str, "leaf") == 0) { |
| irq_type = LEAF_INTERRUPT; |
| } else if (strcmp(irq_type_str, "injection") == 0) { |
| irq_type = FAKEEVENT_INTERRUPT; |
| } else { |
| pr_err("Invalid irq-type from dt: %s\n", irq_type_str); |
| goto error_event_infos; |
| } |
| } |
| |
| lwis_interrupt_set_basic_info(lwis_dev->irqs, i, irq_reg_space, irq_reg_bid, |
| irq_src_reg, irq_reset_reg, irq_mask_reg, |
| irq_overflow_reg, irq_mask_reg_toggle, |
| irq_reg_bitwidth, irq_type); |
| |
| /* Register IRQ handler only for aggregate and regular interrupts */ |
| if (irq_type == AGGREGATE_INTERRUPT || irq_type == REGULAR_INTERRUPT) { |
| ret = lwis_interrupt_get(lwis_dev->irqs, i, plat_dev); |
| if (ret) { |
| pr_err("Cannot set irq %s\n", name); |
| goto error_event_infos; |
| } |
| } else if (irq_type == FAKEEVENT_INTERRUPT) { |
| /* |
| * Hardcode the fake injection irq number to |
| * TEST_DEVICE_FAKE_INJECTION_IRQ |
| */ |
| lwis_dev->irqs->irq[i].irq = TEST_DEVICE_FAKE_INJECTION_IRQ; |
| } |
| |
| /* Parse event info */ |
| ret = parse_interrupts_event_info(lwis_dev->irqs, i, event_info); |
| if (ret) { |
| pr_err("Cannot set event info %s\n", name); |
| goto error_event_infos; |
| } |
| |
| /* Parse leaf nodes if it's an aggregate interrupt */ |
| if (irq_type == AGGREGATE_INTERRUPT) { |
| ret = parse_interrupt_leaf_nodes(lwis_dev->irqs, i, event_info); |
| if (ret) { |
| pr_err("Error setting leaf nodes for interrupt %d %d\n", i, ret); |
| goto error_event_infos; |
| } |
| } |
| |
| of_node_put(event_info); |
| i++; |
| } |
| |
| #ifdef LWIS_DT_DEBUG |
| lwis_interrupt_print(lwis_dev->irqs); |
| #endif |
| |
| return 0; |
| error_event_infos: |
| for (i = 0; i < count; ++i) { |
| // TODO(yromanenko): lwis_interrupt_put |
| } |
| error_get_irq: |
| lwis_interrupt_list_free(lwis_dev->irqs); |
| lwis_dev->irqs = NULL; |
| return ret; |
| } |
| |
| static int parse_phys(struct lwis_device *lwis_dev) |
| { |
| struct device *dev; |
| struct device_node *dev_node; |
| int i; |
| int ret; |
| int count; |
| const char *name; |
| |
| dev = &(lwis_dev->plat_dev->dev); |
| dev_node = dev->of_node; |
| |
| count = of_count_phandle_with_args(dev_node, "phys", "#phy-cells"); |
| |
| /* No PHY found, just return */ |
| if (count <= 0) { |
| lwis_dev->phys = NULL; |
| return 0; |
| } |
| |
| lwis_dev->phys = lwis_phy_list_alloc(count); |
| if (IS_ERR_OR_NULL(lwis_dev->phys)) { |
| pr_err("Failed to allocate PHY list\n"); |
| ret = PTR_ERR(lwis_dev->phys); |
| lwis_dev->phys = NULL; |
| return ret; |
| } |
| |
| for (i = 0; i < count; ++i) { |
| of_property_read_string_index(dev_node, "phy-names", i, &name); |
| ret = lwis_phy_get(lwis_dev->phys, (char *)name, dev); |
| if (ret < 0) { |
| pr_err("Error adding PHY[%d]\n", i); |
| goto error_parse_phy; |
| } |
| } |
| |
| #ifdef LWIS_DT_DEBUG |
| lwis_phy_print(lwis_dev->phys); |
| #endif |
| |
| return 0; |
| |
| error_parse_phy: |
| for (i = 0; i < count; ++i) { |
| lwis_phy_put_by_idx(lwis_dev->phys, i, dev); |
| } |
| lwis_phy_list_free(lwis_dev->phys); |
| lwis_dev->phys = NULL; |
| return ret; |
| } |
| |
| static void parse_bitwidths(struct lwis_device *lwis_dev) |
| { |
| int __maybe_unused ret; |
| struct device *dev; |
| struct device_node *dev_node; |
| u32 addr_bitwidth = 32; |
| u32 value_bitwidth = 32; |
| |
| dev = &(lwis_dev->plat_dev->dev); |
| dev_node = dev->of_node; |
| |
| of_property_read_u32(dev_node, "reg-addr-bitwidth", &addr_bitwidth); |
| #ifdef LWIS_DT_DEBUG |
| pr_info("Addr bitwidth set to: %d\n", addr_bitwidth); |
| #endif |
| |
| of_property_read_u32(dev_node, "reg-value-bitwidth", &value_bitwidth); |
| #ifdef LWIS_DT_DEBUG |
| pr_info("Value bitwidth set to: %d\n", value_bitwidth); |
| #endif |
| |
| lwis_dev->native_addr_bitwidth = addr_bitwidth; |
| lwis_dev->native_value_bitwidth = value_bitwidth; |
| } |
| |
| static int parse_power_seqs(struct lwis_device *lwis_dev, const char *seq_name, |
| struct lwis_device_power_sequence_list **list, |
| struct device_node *dev_node_seq) |
| { |
| char str_seq_name[LWIS_MAX_NAME_STRING_LEN]; |
| char str_seq_type[LWIS_MAX_NAME_STRING_LEN]; |
| char str_seq_delay[LWIS_MAX_NAME_STRING_LEN]; |
| struct device *dev; |
| struct device_node *dev_node; |
| int power_seq_count; |
| int power_seq_type_count; |
| int power_seq_delay_count; |
| int i; |
| int ret; |
| const char *name; |
| const char *type; |
| int delay_us; |
| int type_gpio_count = 0; |
| int type_regulator_count = 0; |
| |
| scnprintf(str_seq_name, LWIS_MAX_NAME_STRING_LEN, "%s-seqs", seq_name); |
| scnprintf(str_seq_type, LWIS_MAX_NAME_STRING_LEN, "%s-seq-types", seq_name); |
| scnprintf(str_seq_delay, LWIS_MAX_NAME_STRING_LEN, "%s-seq-delays-us", seq_name); |
| |
| dev = &lwis_dev->plat_dev->dev; |
| dev_node = dev->of_node; |
| *list = NULL; |
| if (dev_node_seq) { |
| dev_node = dev_node_seq; |
| } |
| |
| power_seq_count = of_property_count_strings(dev_node, str_seq_name); |
| power_seq_type_count = of_property_count_strings(dev_node, str_seq_type); |
| power_seq_delay_count = |
| of_property_count_elems_of_size(dev_node, str_seq_delay, sizeof(u32)); |
| |
| /* No power-seqs found, just return */ |
| if (power_seq_count <= 0) { |
| return 0; |
| } |
| if (power_seq_count != power_seq_type_count || power_seq_count != power_seq_delay_count) { |
| pr_err("Count of power sequence %s is not match\n", str_seq_name); |
| return -EINVAL; |
| } |
| |
| *list = lwis_dev_power_seq_list_alloc(power_seq_count); |
| if (IS_ERR_OR_NULL(*list)) { |
| pr_err("Failed to allocate power sequence list\n"); |
| ret = PTR_ERR(*list); |
| *list = NULL; |
| return ret; |
| } |
| |
| for (i = 0; i < power_seq_count; ++i) { |
| ret = of_property_read_string_index(dev_node, str_seq_name, i, &name); |
| if (ret < 0) { |
| pr_err("Error adding power sequence[%d]\n", i); |
| goto error_parse_power_seqs; |
| } |
| strscpy((*list)->seq_info[i].name, name, LWIS_MAX_NAME_STRING_LEN); |
| |
| ret = of_property_read_string_index(dev_node, str_seq_type, i, &type); |
| if (ret < 0) { |
| pr_err("Error adding power sequence type[%d]\n", i); |
| goto error_parse_power_seqs; |
| } |
| strscpy((*list)->seq_info[i].type, type, LWIS_MAX_NAME_STRING_LEN); |
| if (strcmp(type, "gpio") == 0) { |
| type_gpio_count++; |
| } else if (strcmp(type, "regulator") == 0) { |
| type_regulator_count++; |
| } |
| |
| ret = of_property_read_u32_index(dev_node, str_seq_delay, i, &delay_us); |
| if (ret < 0) { |
| pr_err("Error adding power sequence delay[%d]\n", i); |
| goto error_parse_power_seqs; |
| } |
| (*list)->seq_info[i].delay_us = delay_us; |
| } |
| |
| #ifdef LWIS_DT_DEBUG |
| lwis_dev_power_seq_list_print(*list); |
| #endif |
| |
| if (type_gpio_count > 0 && lwis_dev->gpios_list == NULL) { |
| lwis_dev->gpios_list = lwis_gpios_list_alloc(type_gpio_count); |
| if (IS_ERR_OR_NULL(lwis_dev->gpios_list)) { |
| pr_err("Failed to allocate gpios list\n"); |
| ret = PTR_ERR(lwis_dev->gpios_list); |
| goto error_parse_power_seqs; |
| } |
| |
| type_gpio_count = 0; |
| for (i = 0; i < power_seq_count; ++i) { |
| struct lwis_gpios_info *gpios_info; |
| char *seq_item_name; |
| struct device *dev; |
| struct gpio_descs *descs; |
| |
| if (strcmp((*list)->seq_info[i].type, "gpio") != 0) { |
| continue; |
| } |
| |
| gpios_info = &lwis_dev->gpios_list->gpios_info[type_gpio_count]; |
| seq_item_name = (*list)->seq_info[i].name; |
| dev = &lwis_dev->plat_dev->dev; |
| descs = lwis_gpio_list_get(dev, seq_item_name); |
| if (IS_ERR_OR_NULL(descs)) { |
| pr_err("Error parsing GPIO list %s (%ld)\n", seq_item_name, |
| PTR_ERR(descs)); |
| ret = PTR_ERR(descs); |
| goto error_parse_power_seqs; |
| } |
| gpios_info->id = desc_to_gpio(descs->desc[0]); |
| gpios_info->hold_dev = dev; |
| /* |
| * The GPIO pins are valid, release the list as we do not need to hold |
| * on to the pins yet |
| */ |
| lwis_gpio_list_put(descs, dev); |
| |
| gpios_info->gpios = NULL; |
| gpios_info->irq_list = NULL; |
| strscpy(gpios_info->name, seq_item_name, LWIS_MAX_NAME_STRING_LEN); |
| |
| if (strncmp(SHARED_STRING, seq_item_name, strlen(SHARED_STRING)) == 0) { |
| gpios_info->is_shared = true; |
| } else { |
| gpios_info->is_shared = false; |
| } |
| if (strncmp(PULSE_STRING, seq_item_name, strlen(PULSE_STRING)) == 0) { |
| gpios_info->is_pulse = true; |
| } else { |
| gpios_info->is_pulse = false; |
| } |
| type_gpio_count++; |
| } |
| } |
| |
| if (type_regulator_count > 0 && lwis_dev->regulators == NULL) { |
| lwis_dev->regulators = lwis_regulator_list_alloc(type_regulator_count); |
| if (IS_ERR_OR_NULL(lwis_dev->regulators)) { |
| pr_err("Failed to allocate regulator list\n"); |
| ret = PTR_ERR(lwis_dev->regulators); |
| goto error_parse_power_seqs; |
| } |
| |
| for (i = 0; i < power_seq_count; ++i) { |
| struct device *dev; |
| char *seq_item_name; |
| |
| if (strcmp((*list)->seq_info[i].type, "regulator") != 0) { |
| continue; |
| } |
| |
| dev = &lwis_dev->plat_dev->dev; |
| seq_item_name = (*list)->seq_info[i].name; |
| |
| ret = lwis_regulator_get(lwis_dev->regulators, seq_item_name, |
| /*voltage=*/0, dev); |
| if (ret < 0) { |
| pr_err("Cannot find regulator: %s\n", seq_item_name); |
| goto error_parse_power_seqs; |
| } |
| } |
| } |
| |
| return 0; |
| |
| error_parse_power_seqs: |
| if (lwis_dev->regulators) { |
| lwis_regulator_put_all(lwis_dev->regulators); |
| lwis_regulator_list_free(lwis_dev->regulators); |
| lwis_dev->regulators = NULL; |
| } |
| lwis_gpios_list_free(lwis_dev->gpios_list); |
| lwis_dev->gpios_list = NULL; |
| lwis_dev_power_seq_list_free(*list); |
| *list = NULL; |
| return ret; |
| } |
| |
| static int parse_unified_power_seqs(struct lwis_device *lwis_dev) |
| { |
| struct device *dev; |
| struct device_node *dev_node; |
| struct device_node *dev_node_seq; |
| int count; |
| int ret = 0; |
| |
| dev = &lwis_dev->plat_dev->dev; |
| dev_node = dev->of_node; |
| |
| count = of_property_count_elems_of_size(dev_node, "power-seq", sizeof(u32)); |
| |
| /* No power-seq found, or entry does not exist, just return */ |
| if (count <= 0) { |
| lwis_dev->power_seq_handler = NULL; |
| return 0; |
| } |
| |
| dev_node_seq = of_parse_phandle(dev_node, "power-seq", 0); |
| if (!dev_node_seq) { |
| pr_err("Can't get power-seq node\n"); |
| return -EINVAL; |
| } |
| |
| ret = parse_power_seqs(lwis_dev, "power-up", &lwis_dev->power_up_sequence, dev_node_seq); |
| if (ret) { |
| pr_err("Error parsing power-up-seqs\n"); |
| return ret; |
| } |
| |
| ret = parse_power_seqs(lwis_dev, "power-down", &lwis_dev->power_down_sequence, |
| dev_node_seq); |
| if (ret) { |
| pr_err("Error parsing power-down-seqs\n"); |
| return ret; |
| } |
| |
| lwis_dev->power_seq_handler = dev_node_seq; |
| |
| return ret; |
| } |
| |
| static int parse_pm_hibernation(struct lwis_device *lwis_dev) |
| { |
| struct device_node *dev_node; |
| |
| dev_node = lwis_dev->plat_dev->dev.of_node; |
| lwis_dev->pm_hibernation = 1; |
| |
| of_property_read_u32(dev_node, "pm-hibernation", &lwis_dev->pm_hibernation); |
| |
| return 0; |
| } |
| |
| static int parse_access_mode(struct lwis_device *lwis_dev) |
| { |
| struct device_node *dev_node; |
| |
| dev_node = lwis_dev->plat_dev->dev.of_node; |
| |
| lwis_dev->is_read_only = of_property_read_bool(dev_node, "lwis,read-only"); |
| |
| return 0; |
| } |
| |
| static int parse_thread_priority(struct lwis_device *lwis_dev) |
| { |
| struct device_node *dev_node; |
| |
| dev_node = lwis_dev->plat_dev->dev.of_node; |
| lwis_dev->transaction_thread_priority = 0; |
| |
| of_property_read_u32(dev_node, "transaction-thread-priority", |
| &lwis_dev->transaction_thread_priority); |
| |
| return 0; |
| } |
| |
| static int parse_i2c_lock_group_id(struct lwis_i2c_device *i2c_dev) |
| { |
| struct device_node *dev_node; |
| int ret; |
| |
| dev_node = i2c_dev->base_dev.plat_dev->dev.of_node; |
| /* Set i2c_lock_group_id value to default */ |
| i2c_dev->i2c_lock_group_id = MAX_I2C_LOCK_NUM - 1; |
| |
| ret = of_property_read_u32(dev_node, "i2c-lock-group-id", &i2c_dev->i2c_lock_group_id); |
| /* If no property in device tree, just return to use default */ |
| if (ret == -EINVAL) { |
| return 0; |
| } |
| if (ret) { |
| pr_err("i2c-lock-group-id value wrong\n"); |
| return ret; |
| } |
| if (i2c_dev->i2c_lock_group_id >= MAX_I2C_LOCK_NUM - 1) { |
| pr_err("i2c-lock-group-id need smaller than MAX_I2C_LOCK_NUM - 1\n"); |
| return -EOVERFLOW; |
| } |
| |
| return 0; |
| } |
| |
| int lwis_base_parse_dt(struct lwis_device *lwis_dev) |
| { |
| struct device *dev; |
| struct device_node *dev_node; |
| const char *name_str; |
| int ret = 0; |
| |
| dev = &(lwis_dev->plat_dev->dev); |
| dev_node = dev->of_node; |
| |
| if (!dev_node) { |
| pr_err("Cannot find device node\n"); |
| return -ENODEV; |
| } |
| |
| ret = of_property_read_string(dev_node, "node-name", &name_str); |
| if (ret) { |
| pr_err("Error parsing node name\n"); |
| return -EINVAL; |
| } |
| strscpy(lwis_dev->name, name_str, LWIS_MAX_NAME_STRING_LEN); |
| |
| pr_debug("Device tree entry [%s] - begin\n", lwis_dev->name); |
| |
| ret = parse_gpios(lwis_dev, "shared-enable", &lwis_dev->shared_enable_gpios_present); |
| if (ret) { |
| pr_err("Error parsing shared-enable-gpios\n"); |
| return ret; |
| } |
| |
| ret = parse_gpios(lwis_dev, "enable", &lwis_dev->enable_gpios_present); |
| if (ret) { |
| pr_err("Error parsing enable-gpios\n"); |
| return ret; |
| } |
| |
| ret = parse_gpios(lwis_dev, "reset", &lwis_dev->reset_gpios_present); |
| if (ret) { |
| pr_err("Error parsing reset-gpios\n"); |
| return ret; |
| } |
| |
| ret = parse_irq_gpios(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing irq-gpios\n"); |
| return ret; |
| } |
| |
| ret = parse_unified_power_seqs(lwis_dev); |
| if (ret) { |
| pr_err("Error parse_unified_power_seqs\n"); |
| return ret; |
| } |
| |
| if (lwis_dev->power_up_sequence == NULL) { |
| ret = parse_power_seqs(lwis_dev, "power-up", &lwis_dev->power_up_sequence, NULL); |
| if (ret) { |
| pr_err("Error parsing power-up-seqs\n"); |
| return ret; |
| } |
| } |
| |
| if (lwis_dev->power_down_sequence == NULL) { |
| ret = parse_power_seqs(lwis_dev, "power-down", &lwis_dev->power_down_sequence, |
| NULL); |
| if (ret) { |
| pr_err("Error parsing power-down-seqs\n"); |
| return ret; |
| } |
| } |
| |
| ret = parse_power_seqs(lwis_dev, "suspend", &lwis_dev->suspend_sequence, NULL); |
| if (ret) { |
| pr_err("Error parsing suspend-seqs\n"); |
| return ret; |
| } |
| |
| ret = parse_power_seqs(lwis_dev, "resume", &lwis_dev->resume_sequence, NULL); |
| if (ret) { |
| pr_err("Error parsing resume-seqs\n"); |
| return ret; |
| } |
| |
| ret = parse_settle_time(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing settle-time\n"); |
| return ret; |
| } |
| |
| if (lwis_dev->regulators == NULL) { |
| ret = parse_regulators(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing regulators\n"); |
| return ret; |
| } |
| } |
| |
| ret = parse_clocks(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing clocks\n"); |
| return ret; |
| } |
| |
| ret = parse_pinctrls(lwis_dev, "mclk_on"); |
| if (ret) { |
| pr_err("Error parsing mclk pinctrls\n"); |
| return ret; |
| } |
| |
| ret = parse_interrupts(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing interrupts\n"); |
| return ret; |
| } |
| |
| ret = parse_phys(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing phy's\n"); |
| return ret; |
| } |
| |
| ret = parse_pm_hibernation(lwis_dev); |
| if (ret) { |
| pr_err("Error parsing pm hibernation\n"); |
| return ret; |
| } |
| |
| parse_access_mode(lwis_dev); |
| parse_thread_priority(lwis_dev); |
| parse_bitwidths(lwis_dev); |
| |
| lwis_dev->bts_scenario_name = NULL; |
| of_property_read_string(dev_node, "bts-scenario", &lwis_dev->bts_scenario_name); |
| |
| dev_node->data = lwis_dev; |
| |
| pr_debug("Device tree entry [%s] - end\n", lwis_dev->name); |
| |
| return ret; |
| } |
| |
| int lwis_i2c_device_parse_dt(struct lwis_i2c_device *i2c_dev) |
| { |
| struct device_node *dev_node; |
| struct device_node *dev_node_i2c; |
| int ret; |
| |
| dev_node = i2c_dev->base_dev.plat_dev->dev.of_node; |
| |
| dev_node_i2c = of_parse_phandle(dev_node, "i2c-bus", 0); |
| if (!dev_node_i2c) { |
| dev_err(i2c_dev->base_dev.dev, "Cannot find i2c-bus node\n"); |
| return -ENODEV; |
| } |
| |
| i2c_dev->adapter = of_find_i2c_adapter_by_node(dev_node_i2c); |
| if (!i2c_dev->adapter) { |
| dev_err(i2c_dev->base_dev.dev, "Cannot find i2c adapter\n"); |
| return -ENODEV; |
| } |
| |
| ret = of_property_read_u32(dev_node, "i2c-addr", (u32 *)&i2c_dev->address); |
| if (ret) { |
| dev_err(i2c_dev->base_dev.dev, "Failed to read i2c-addr\n"); |
| return ret; |
| } |
| |
| ret = parse_i2c_lock_group_id(i2c_dev); |
| if (ret) { |
| dev_err(i2c_dev->base_dev.dev, "Error parsing i2c lock group id\n"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int lwis_ioreg_device_parse_dt(struct lwis_ioreg_device *ioreg_dev) |
| { |
| struct device_node *dev_node; |
| int i; |
| int ret; |
| int blocks; |
| int reg_tuple_size; |
| const char *name; |
| |
| dev_node = ioreg_dev->base_dev.plat_dev->dev.of_node; |
| reg_tuple_size = of_n_addr_cells(dev_node) + of_n_size_cells(dev_node); |
| |
| blocks = of_property_count_elems_of_size(dev_node, "reg", reg_tuple_size * sizeof(u32)); |
| if (blocks <= 0) { |
| dev_err(ioreg_dev->base_dev.dev, "No register space found\n"); |
| return -EINVAL; |
| } |
| |
| ret = lwis_ioreg_list_alloc(ioreg_dev, blocks); |
| if (ret) { |
| dev_err(ioreg_dev->base_dev.dev, "Failed to allocate ioreg list\n"); |
| return ret; |
| } |
| |
| for (i = 0; i < blocks; ++i) { |
| of_property_read_string_index(dev_node, "reg-names", i, &name); |
| ret = lwis_ioreg_get(ioreg_dev, i, (char *)name); |
| if (ret) { |
| dev_err(ioreg_dev->base_dev.dev, "Cannot set ioreg info for %s\n", name); |
| goto error_ioreg; |
| } |
| } |
| |
| return 0; |
| |
| error_ioreg: |
| for (i = 0; i < blocks; ++i) { |
| lwis_ioreg_put_by_idx(ioreg_dev, i); |
| } |
| lwis_ioreg_list_free(ioreg_dev); |
| return ret; |
| } |
| |
| int lwis_top_device_parse_dt(struct lwis_top_device *top_dev) |
| { |
| /* To be implemented */ |
| return 0; |
| } |
| |
| int lwis_test_device_parse_dt(struct lwis_test_device *test_dev) |
| { |
| /* To be implemented */ |
| return 0; |
| } |