/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include struct of_spmi_dev_info { struct spmi_controller *ctrl; struct spmi_boardinfo b_info; }; struct of_spmi_res_info { struct device_node *node; uint32_t num_reg; uint32_t num_irq; }; /* * Initialize r_info structure for safe usage */ static inline void of_spmi_init_resource(struct of_spmi_res_info *r_info, struct device_node *node) { r_info->node = node; r_info->num_reg = 0; r_info->num_irq = 0; } /* * Calculate the number of resources to allocate * * The caller is responsible for initializing the of_spmi_res_info structure. */ static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg) { struct of_irq oirq; uint64_t size; uint32_t flags; int i = 0; while (of_irq_map_one(r_info->node, i, &oirq) == 0) i++; r_info->num_irq += i; if (!has_reg) return; /* * We can't use of_address_to_resource here since it includes * address translation; and address translation assumes that no * parent buses have a size-cell of 0. But SPMI does have a * size-cell of 0. */ i = 0; while (of_get_address(r_info->node, i, &size, &flags) != NULL) i++; r_info->num_reg += i; } /* * Allocate dev_node array for spmi_device - used with spmi-dev-container */ static inline int of_spmi_alloc_devnode_store(struct of_spmi_dev_info *d_info, uint32_t num_dev_node) { d_info->b_info.num_dev_node = num_dev_node; d_info->b_info.dev_node = kzalloc(sizeof(struct spmi_resource) * num_dev_node, GFP_KERNEL); if (!d_info->b_info.dev_node) return -ENOMEM; return 0; } /* * Allocate enough memory to handle the resources associated with the * primary node. */ static int of_spmi_allocate_node_resources(struct of_spmi_dev_info *d_info, struct of_spmi_res_info *r_info) { uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg; struct resource *res = NULL; if (num_irq || num_reg) { res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); if (!res) return -ENOMEM; } d_info->b_info.res.num_resources = num_reg + num_irq; d_info->b_info.res.resource = res; return 0; } /* * Allocate enough memory to handle the resources associated with the * spmi-dev-container nodes. */ static int of_spmi_allocate_devnode_resources(struct of_spmi_dev_info *d_info, struct of_spmi_res_info *r_info, uint32_t idx) { uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg; struct resource *res = NULL; if (num_irq || num_reg) { res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); if (!res) return -ENOMEM; } d_info->b_info.dev_node[idx].num_resources = num_reg + num_irq; d_info->b_info.dev_node[idx].resource = res; return 0; } /* * free node resources - used with primary node */ static void of_spmi_free_node_resources(struct of_spmi_dev_info *d_info) { kfree(d_info->b_info.res.resource); } /* * free devnode resources - used with spmi-dev-container */ static void of_spmi_free_devnode_resources(struct of_spmi_dev_info *d_info) { int i; for (i = 0; i < d_info->b_info.num_dev_node; i++) kfree(d_info->b_info.dev_node[i].resource); kfree(d_info->b_info.dev_node); } static void of_spmi_populate_resources(struct of_spmi_dev_info *d_info, struct of_spmi_res_info *r_info, struct resource *res) { uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg; int i; const __be32 *addrp; uint64_t size; uint32_t flags; if ((num_irq || num_reg) && (res != NULL)) { for (i = 0; i < num_reg; i++, res++) { /* Addresses are always 16 bits */ addrp = of_get_address(r_info->node, i, &size, &flags); BUG_ON(!addrp); res->start = be32_to_cpup(addrp); res->end = res->start + size - 1; res->flags = flags; of_property_read_string_index(r_info->node, "reg-names", i, &res->name); } WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) != num_irq); } } /* * Gather primary node resources and populate. */ static void of_spmi_populate_node_resources(struct of_spmi_dev_info *d_info, struct of_spmi_res_info *r_info) { struct resource *res; res = d_info->b_info.res.resource; d_info->b_info.res.of_node = r_info->node; of_property_read_string(r_info->node, "label", &d_info->b_info.res.label); of_spmi_populate_resources(d_info, r_info, res); } /* * Gather node devnode resources and populate - used with spmi-dev-container. */ static void of_spmi_populate_devnode_resources(struct of_spmi_dev_info *d_info, struct of_spmi_res_info *r_info, int idx) { struct resource *res; res = d_info->b_info.dev_node[idx].resource; d_info->b_info.dev_node[idx].of_node = r_info->node; of_property_read_string(r_info->node, "label", &d_info->b_info.dev_node[idx].label); of_spmi_populate_resources(d_info, r_info, res); } /* * create a single spmi_device */ static int of_spmi_create_device(struct of_spmi_dev_info *d_info, struct device_node *node) { struct spmi_controller *ctrl = d_info->ctrl; struct spmi_boardinfo *b_info = &d_info->b_info; void *result; int rc; rc = of_modalias_node(node, b_info->name, sizeof(b_info->name)); if (rc < 0) { dev_err(&ctrl->dev, "of_spmi modalias failure on %s\n", node->full_name); return rc; } b_info->of_node = of_node_get(node); result = spmi_new_device(ctrl, b_info); if (result == NULL) { dev_err(&ctrl->dev, "of_spmi: Failure registering %s\n", node->full_name); of_node_put(node); return -ENODEV; } return 0; } /* * Walks all children of a node containing the spmi-dev-container * binding. This special type of spmi_device can include resources * from more than one device node. */ static void of_spmi_walk_dev_container(struct of_spmi_dev_info *d_info, struct device_node *container) { struct of_spmi_res_info r_info = {}; struct spmi_controller *ctrl = d_info->ctrl; struct device_node *node; int rc, i, num_dev_node = 0; if (!of_device_is_available(container)) return; /* * Count the total number of device_nodes so we know how much * device_store to allocate. */ for_each_child_of_node(container, node) { if (!of_device_is_available(node)) continue; num_dev_node++; } rc = of_spmi_alloc_devnode_store(d_info, num_dev_node); if (rc) { dev_err(&ctrl->dev, "%s: unable to allocate devnode resources\n", __func__); return; } i = 0; for_each_child_of_node(container, node) { if (!of_device_is_available(node)) continue; of_spmi_init_resource(&r_info, node); of_spmi_sum_resources(&r_info, true); rc = of_spmi_allocate_devnode_resources(d_info, &r_info, i); if (rc) { dev_err(&ctrl->dev, "%s: unable to allocate" " resources\n", __func__); of_spmi_free_devnode_resources(d_info); return; } of_spmi_populate_devnode_resources(d_info, &r_info, i); i++; } of_spmi_init_resource(&r_info, container); of_spmi_sum_resources(&r_info, true); rc = of_spmi_allocate_node_resources(d_info, &r_info); if (rc) { dev_err(&ctrl->dev, "%s: unable to allocate resources\n", __func__); of_spmi_free_node_resources(d_info); } of_spmi_populate_node_resources(d_info, &r_info); rc = of_spmi_create_device(d_info, container); if (rc) { dev_err(&ctrl->dev, "%s: unable to create device for" " node %s\n", __func__, container->full_name); of_spmi_free_devnode_resources(d_info); return; } } /* * Walks all children of a node containing the spmi-slave-container * binding. This indicates that all spmi_devices created from this * point all share the same slave_id. */ static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info, struct device_node *container) { struct spmi_controller *ctrl = d_info->ctrl; struct device_node *node; int rc; for_each_child_of_node(container, node) { struct of_spmi_res_info r_info; if (!of_device_is_available(node)) continue; /** * Check to see if this node contains children which * should be all created as the same spmi_device. */ if (of_get_property(node, "spmi-dev-container", NULL)) { of_spmi_walk_dev_container(d_info, node); continue; } of_spmi_init_resource(&r_info, node); of_spmi_sum_resources(&r_info, true); rc = of_spmi_allocate_node_resources(d_info, &r_info); if (rc) { dev_err(&ctrl->dev, "%s: unable to allocate" " resources\n", __func__); goto slave_err; } of_spmi_populate_node_resources(d_info, &r_info); rc = of_spmi_create_device(d_info, node); if (rc) { dev_err(&ctrl->dev, "%s: unable to create device for" " node %s\n", __func__, node->full_name); goto slave_err; } } return; slave_err: of_spmi_free_node_resources(d_info); } int of_spmi_register_devices(struct spmi_controller *ctrl) { struct device_node *node = ctrl->dev.of_node; /* Only register child devices if the ctrl has a node pointer set */ if (!node) return -ENODEV; if (of_get_property(node, "spmi-slave-container", NULL)) { dev_err(&ctrl->dev, "%s: structural error: spmi-slave-container" " is prohibited at the root level\n", __func__); return -EINVAL; } else if (of_get_property(node, "spmi-dev-container", NULL)) { dev_err(&ctrl->dev, "%s: structural error: spmi-dev-container" " is prohibited at the root level\n", __func__); return -EINVAL; } /** * Make best effort to launch as many nodes as possible. If there are * syntax errors, we will simply ignore that subtree and keep going. */ for_each_child_of_node(ctrl->dev.of_node, node) { struct of_spmi_dev_info d_info = {}; const __be32 *slave_id; int len, rc, have_dev_container = 0; slave_id = of_get_property(node, "reg", &len); if (!slave_id) { dev_err(&ctrl->dev, "%s: invalid sid " "on %s\n", __func__, node->full_name); continue; } d_info.b_info.slave_id = be32_to_cpup(slave_id); d_info.ctrl = ctrl; if (of_get_property(node, "spmi-dev-container", NULL)) have_dev_container = 1; if (of_get_property(node, "spmi-slave-container", NULL)) { if (have_dev_container) of_spmi_walk_dev_container(&d_info, node); else of_spmi_walk_slave_container(&d_info, node); } else { struct of_spmi_res_info r_info; /** * A dev container at the second level without a slave * container is considered an error. */ if (have_dev_container) { dev_err(&ctrl->dev, "%s: structural error," " node %s has spmi-dev-container without" " specifying spmi-slave-container\n", __func__, node->full_name); continue; } if (!of_device_is_available(node)) continue; of_spmi_init_resource(&r_info, node); of_spmi_sum_resources(&r_info, false); rc = of_spmi_allocate_node_resources(&d_info, &r_info); if (rc) { dev_err(&ctrl->dev, "%s: unable to allocate" " resources\n", __func__); of_spmi_free_node_resources(&d_info); continue; } of_spmi_populate_node_resources(&d_info, &r_info); rc = of_spmi_create_device(&d_info, node); if (rc) { dev_err(&ctrl->dev, "%s: unable to create" " device\n", __func__); of_spmi_free_node_resources(&d_info); continue; } } } return 0; } EXPORT_SYMBOL(of_spmi_register_devices); MODULE_LICENSE("GPL v2");