#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_ATH79) static int read_avm_gpio_function(struct device_node *gpio_node, struct _avm_hw_config *hw_cfg) { bool func_found, in_found, out_found; uint32_t func, in_func, out_func; //read the func filed func_found = !of_property_read_u32(gpio_node, "function", &func); in_found = !of_property_read_u32(gpio_node, "in-function", &in_func); out_found = !of_property_read_u32(gpio_node, "out-function", &out_func); if (func_found && (in_found | out_found)) { pr_err("Both function and in/out-function specified on %s. Using function.\n", gpio_node->name); } // We store the in function simply as number in the dts // this is split up into register and offset where this in function is located if (in_found) in_func = ((in_func / 4) << 8) | (((in_func % 4) * 8) << 0); if (func_found) hw_cfg->func = func; else if (in_found && out_found) { if (hw_cfg->param != avm_hw_param_gpio_in_out_active_low && hw_cfg->param != avm_hw_param_gpio_in_out_active_high) pr_err("Specified in-function and out-function on %s but param does not match\n", gpio_node->name); hw_cfg->func = ((out_func & 0xFFFF) << 16) | (in_func & 0xFFFF << 0); } else if (in_found) { if (hw_cfg->param != avm_hw_param_gpio_in_active_low && hw_cfg->param != avm_hw_param_gpio_in_active_high) pr_err("Specified in-function on %s but param does not match\n", gpio_node->name); hw_cfg->func = in_func & 0xFFFF << 0; } else if (out_found) { if (hw_cfg->param != avm_hw_param_gpio_out_active_low && hw_cfg->param != avm_hw_param_gpio_out_active_high) pr_err("Specified out-function on %s but param does not match\n", gpio_node->name); hw_cfg->func = (out_func & 0xFFFF) << 0; } else hw_cfg->func = AVM_DEF_HW_FUNCTION_GPIO; return 0; } #else static int read_avm_gpio_function(struct device_node *gpio_node, struct _avm_hw_config *hw_cfg) { uint32_t prop_found, tmp; prop_found = of_property_read_u32(gpio_node, "function", &tmp); if (!prop_found) { hw_cfg->function = tmp; } return 0; } #endif /* * Reads the GPIO config from an "avm,avm_gpio_generic" compatible node and * copies the data into the given struct _avm_hw_config */ static int read_avm_gpio_generic(struct device_node *gpio_node, struct _avm_hw_config *hw_cfg) { const uint32_t *prop __maybe_unused; uint32_t prop_found, tmp, numElems __maybe_unused; int i; pr_debug("[%s] Called. gpio_node: %p hw_cfg: %p\n", __func__, gpio_node, hw_cfg); // clear target hw_cfg memset(hw_cfg, 0x0, sizeof(*hw_cfg)); //read the name hw_cfg->name = (char *) of_get_property(gpio_node, "name", &i); pr_debug("[DTB] read gpio %s\n", hw_cfg->name); if (!(hw_cfg->name)) { //couldn't read name return -ENOMSG; } //read the value prop_found = of_property_read_u32(gpio_node, "value", &tmp); if (!prop_found) { hw_cfg->value = tmp; } //read the param prop_found = of_property_read_u32(gpio_node, "param", &tmp); if (!prop_found) { hw_cfg->param = tmp; } else { hw_cfg->param = avm_hw_param_no_param; } tmp = read_avm_gpio_function(gpio_node, hw_cfg); if (tmp < 0) { return tmp; } #if defined(CONFIG_MACH_FUSIV) prop_found = of_property_read_u32(gpio_node, "mode", &tmp); if (!prop_found) { hw_cfg->manufactor_hw_config.manufactor_ikanos_gpio_config.mode = tmp; } prop_found = of_property_read_u32(gpio_node, "dir", &tmp); if (!prop_found) { hw_cfg->manufactor_hw_config.manufactor_ikanos_gpio_config.dir = tmp; } prop_found = of_property_read_u32(gpio_node, "func_sel", &tmp); if (!prop_found) { hw_cfg->manufactor_hw_config.manufactor_ikanos_gpio_config.func_sel = tmp; } prop_found = of_property_read_u32(gpio_node, "func_val", &tmp); if (!prop_found) { hw_cfg->manufactor_hw_config.manufactor_ikanos_gpio_config.func_val = tmp; } #endif #if defined(CONFIG_ATH79) //read the func filed prop_found = of_property_read_u32(gpio_node, "default", &tmp); if (!prop_found) { if (tmp == 0) hw_cfg->default_value = AVM_HW_DEFAULT_VALUE_LOW; else if (tmp == 1) hw_cfg->default_value = AVM_HW_DEFAULT_VALUE_HIGH; else panic("Invalid default binary value %d specified for GPIO %d (%s)", tmp, hw_cfg->value, hw_cfg->name); } else { hw_cfg->default_value = AVM_HW_DEFAULT_VALUE_UNDEF; } #endif return 0; } /* * Finds all AVM GPIO Entries in the device tree and generates the hw_config_table from it */ int avm_generate_hw_config_table_from_device_tree(void) { struct device_node *node; uint8_t populated; int gpio_cnt, result; const char *of_compatible = "avm,avm_gpio_generic"; struct device_node *gpio_node; pr_err("Creating Config Table\n"); //loop through device tree, find all avm gpios, count them, allocate memory populated = of_have_populated_dt(); if (!populated) { pr_err("[DTB] No populated device tree found\n"); mdelay(10); panic("[DTB] No populated device tree found\n"); } gpio_cnt = 0; //loop over all compatible nodes, check there module_id and increment gpio_cnt if the module_id is greater than zero node = NULL; while (NULL != (node = of_find_compatible_node(node, NULL, of_compatible))) { gpio_cnt += of_get_child_count(node); } pr_debug("[%s] gpio_cnt: %d\n", __func__, gpio_cnt); // allocate one more GPIO than actually needed to have an end of config marker in the // config table, where the name is NULL avm_current_hw_config = kzalloc((gpio_cnt + 1) * sizeof(struct _avm_hw_config), GFP_KERNEL); if (!avm_current_hw_config) { panic("[DTB] Error allocating mem for config table\n"); } // copy device tree data to hw_config if (gpio_cnt > 0) { gpio_cnt = 0; node = NULL; while (NULL != (node = of_find_compatible_node(node, NULL, of_compatible))) { for_each_child_of_node(node, gpio_node) { result = read_avm_gpio_generic(gpio_node, &(avm_current_hw_config[gpio_cnt])); if (result != 0) { pr_err("[DTB] Error reading GPIO from device tree\n"); return -ENOMSG; } gpio_cnt++; } } } pr_debug("[DTB] Found %i compatible gpios 'avm,avm_gpio_generic' with module_id>0\n", gpio_cnt); pr_debug("[DTB] Device Table generated\n"); init_gpio_config(); pr_debug("[DTB] GPIOs configurated.\n"); return 0; } postcore_initcall(avm_generate_hw_config_table_from_device_tree); /* * Reads Interrupt Information from the Device Tree * Finds the Interrupt specified by irq_name and stores the information in cpu_used, cpu_mask and int_num * @param int_name: the name of the interrupt to find * @param cpu_used: pointer to store the cpu_used information * @param cpu_mask: pointer to store the cpu_mask information * @param int_num: pointer to store the int_num information */ int get_interrupt_info_from_device_tree(const char *int_name, int32_t *cpu_used, int32_t *cpu_mask, int32_t *int_num) { struct device_node *node, *child; uint8_t populated; uint32_t prop_found; int len, result; char *namecmp; const char *of_compatible = "avm,avm_interrupt"; result = -ENOENT; populated = of_have_populated_dt(); if (!populated) { pr_debug("[%s]Device Tree not populated, cannot read interrupt information\n", __func__); result = -ENOMSG; goto err_out; } node = NULL; while (NULL != (node = of_find_compatible_node(node, NULL, of_compatible)) && result != 0) { for_each_child_of_node(node, child) { namecmp = (char *) of_get_property(child, "name", &len); if (namecmp == NULL) { continue; } if (strcmp(namecmp, int_name)) { continue; } //names match, read info if (cpu_used) { prop_found = of_property_read_u32(child, "cpu_used", cpu_used); if (prop_found) { pr_debug("[DTB] Couldn't read cpu_used property!\n"); result = -ENOMSG; goto err_out; } } if (cpu_mask) { prop_found = of_property_read_u32(child, "cpu_mask", cpu_mask); if (prop_found) { pr_debug("[DTB] Couldn't read cpu_mask property!\n"); result = -ENOMSG; goto err_out; } } if (int_num) { prop_found = of_property_read_u32(child, "int_num", int_num); if (prop_found) { pr_debug("[DTB] Couldn't read int_num property!\n"); result = -ENOMSG; goto err_out; } } result = 0; } } err_out: return result; }