The spmi-dev-container binding is intended for SPMI configurations that have multiple device nodes associated with only one spmi_device. By default, if this flag is not specified, each device node will create a new spmi_device. Sometimes having multiple spmi_devices for SPMI device nodes is superfluous. One example of this is gpios. In some architectures, a single gpio is treated as a unique device. But from a gpio_chip perspective, the chip is comprised of many gpios. Beyond wasting memory allocating a unique spmi_device per gpio, the implication of not coalescing spmi_devices is that the clients probe() routine would be called N number of times. But this sort of behavior makes it difficult to realize when a gpio_chip starts and stops. If we assume that one gpio_chip represents one call to probe(), then this problem is solved, since all gpios in that chip will be passed as resources. In order to support multiple device nodes per spmi_device, we also need to extend the data structures for spmi_resources. This change also makes an effort to cleanup some of the error handling for illegal combinations of device bindings, as well as adding some additional documentation. Change-Id: If3ce2aaaa07bdf79e0d9fdedf16419e74a00fbec Signed-off-by: Michael Bohan <mbohan@codeaurora.org>
403 lines
10 KiB
C
403 lines
10 KiB
C
/* Copyright (c) 2012, Code Aurora Forum. 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 <linux/spmi.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_spmi.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Allocate dev_node array for spmi_device
|
|
*/
|
|
static inline int of_spmi_alloc_device_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;
|
|
}
|
|
|
|
/*
|
|
* Calculate the number of resources to allocate
|
|
*
|
|
* The caller is responsible for initializing the of_spmi_res_info structure.
|
|
*/
|
|
static void of_spmi_sum_node_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;
|
|
}
|
|
|
|
/*
|
|
* free spmi_resource for the spmi_device
|
|
*/
|
|
static void of_spmi_free_device_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);
|
|
}
|
|
|
|
/*
|
|
* Gather 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,
|
|
int idx)
|
|
|
|
{
|
|
uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg;
|
|
int i;
|
|
struct resource *res;
|
|
const __be32 *addrp;
|
|
uint64_t size;
|
|
uint32_t flags;
|
|
|
|
res = d_info->b_info.dev_node[idx].resource;
|
|
d_info->b_info.dev_node[idx].of_node = r_info->node;
|
|
|
|
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;
|
|
}
|
|
WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) !=
|
|
num_irq);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate enough memory to handle the resources associated with the
|
|
* device_node. The number of device nodes included in this allocation
|
|
* depends on whether the spmi-dev-container flag is specified or not.
|
|
*/
|
|
static int of_spmi_allocate_node_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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/* first count the total number of device_nodes */
|
|
for_each_child_of_node(container, node)
|
|
num_dev_node++;
|
|
|
|
rc = of_spmi_alloc_device_store(d_info, num_dev_node);
|
|
if (rc) {
|
|
dev_err(&ctrl->dev, "%s: unable to allocate"
|
|
" device resources\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* allocate resources per device_node */
|
|
i = 0;
|
|
for_each_child_of_node(container, node) {
|
|
of_spmi_init_resource(&r_info, node);
|
|
of_spmi_sum_node_resources(&r_info, 1);
|
|
rc = of_spmi_allocate_node_resources(d_info, &r_info, i);
|
|
if (rc) {
|
|
dev_err(&ctrl->dev, "%s: unable to allocate"
|
|
" resources\n", __func__);
|
|
of_spmi_free_device_resources(d_info);
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
/**
|
|
* Now we need to cycle through again and actually populate
|
|
* the resources for each node.
|
|
*/
|
|
i = 0;
|
|
for_each_child_of_node(container, node) {
|
|
of_spmi_init_resource(&r_info, node);
|
|
of_spmi_sum_node_resources(&r_info, 1);
|
|
of_spmi_populate_node_resources(d_info, &r_info, i);
|
|
i++;
|
|
}
|
|
|
|
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_device_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;
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
rc = of_spmi_alloc_device_store(d_info, 1);
|
|
if (rc) {
|
|
dev_err(&ctrl->dev, "%s: unable to allocate"
|
|
" device resources\n", __func__);
|
|
goto slave_err;
|
|
}
|
|
|
|
of_spmi_init_resource(&r_info, node);
|
|
of_spmi_sum_node_resources(&r_info, 1);
|
|
|
|
rc = of_spmi_allocate_node_resources(d_info, &r_info, 0);
|
|
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, 0);
|
|
|
|
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_device_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;
|
|
}
|
|
|
|
rc = of_spmi_alloc_device_store(&d_info, 1);
|
|
if (rc) {
|
|
dev_err(&ctrl->dev, "%s: unable to allocate"
|
|
" device resources\n", __func__);
|
|
continue;
|
|
}
|
|
|
|
of_spmi_init_resource(&r_info, node);
|
|
of_spmi_sum_node_resources(&r_info, 0);
|
|
rc = of_spmi_allocate_node_resources(&d_info,
|
|
&r_info, 0);
|
|
if (rc) {
|
|
dev_err(&ctrl->dev, "%s: unable to allocate"
|
|
" resources\n", __func__);
|
|
of_spmi_free_device_resources(&d_info);
|
|
continue;
|
|
}
|
|
|
|
of_spmi_populate_node_resources(&d_info, &r_info, 0);
|
|
|
|
rc = of_spmi_create_device(&d_info, node);
|
|
if (rc) {
|
|
dev_err(&ctrl->dev, "%s: unable to create"
|
|
" device\n", __func__);
|
|
of_spmi_free_device_resources(&d_info);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(of_spmi_register_devices);
|
|
|
|
MODULE_LICENSE("GPL");
|