2012-01-05 22:16:43 +00:00
|
|
|
/* 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>
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg)
|
2012-01-05 22:16:43 +00:00
|
|
|
{
|
2012-02-08 19:01:24 +00:00
|
|
|
struct of_irq oirq;
|
2012-01-05 22:16:43 +00:00
|
|
|
uint64_t size;
|
|
|
|
uint32_t flags;
|
2012-02-08 19:01:24 +00:00
|
|
|
|
|
|
|
while (of_irq_map_one(r_info->node, r_info->num_irq, &oirq) == 0)
|
|
|
|
r_info->num_irq++;
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
while (of_get_address(r_info->node, r_info->num_reg,
|
|
|
|
&size, &flags) != NULL)
|
|
|
|
r_info->num_reg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate resources for a child of a spmi-slave node.
|
|
|
|
*/
|
|
|
|
static int of_spmi_allocate_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;
|
|
|
|
int i;
|
2012-01-05 22:16:43 +00:00
|
|
|
struct resource *res;
|
|
|
|
const __be32 *addrp;
|
2012-02-08 19:01:24 +00:00
|
|
|
uint64_t size;
|
|
|
|
uint32_t flags;
|
2012-01-05 22:16:43 +00:00
|
|
|
|
|
|
|
if (num_irq || num_reg) {
|
|
|
|
res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
|
|
|
|
if (!res)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
d_info->b_info.num_resources = num_reg + num_irq;
|
|
|
|
d_info->b_info.resource = res;
|
2012-01-05 22:16:43 +00:00
|
|
|
for (i = 0; i < num_reg; i++, res++) {
|
|
|
|
/* Addresses are always 16 bits */
|
2012-02-08 19:01:24 +00:00
|
|
|
addrp = of_get_address(r_info->node, i, &size, &flags);
|
2012-01-05 22:16:43 +00:00
|
|
|
BUG_ON(!addrp);
|
|
|
|
res->start = be32_to_cpup(addrp);
|
|
|
|
res->end = res->start + size - 1;
|
|
|
|
res->flags = flags;
|
|
|
|
}
|
2012-02-08 19:01:24 +00:00
|
|
|
WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) !=
|
2012-01-05 22:16:43 +00:00
|
|
|
num_irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
static int of_spmi_create_device(struct of_spmi_dev_info *d_info,
|
|
|
|
struct device_node *node)
|
2012-01-05 22:16:43 +00:00
|
|
|
{
|
2012-02-08 19:01:24 +00:00
|
|
|
struct spmi_controller *ctrl = d_info->ctrl;
|
|
|
|
struct spmi_boardinfo *b_info = &d_info->b_info;
|
2012-01-05 22:16:43 +00:00
|
|
|
void *result;
|
|
|
|
int rc;
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
rc = of_modalias_node(node, b_info->name, sizeof(b_info->name));
|
2012-01-05 22:16:43 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
dev_err(&ctrl->dev, "of_spmi modalias failure on %s\n",
|
|
|
|
node->full_name);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
b_info->of_node = of_node_get(node);
|
|
|
|
result = spmi_new_device(ctrl, b_info);
|
2012-01-05 22:16:43 +00:00
|
|
|
|
|
|
|
if (result == NULL) {
|
|
|
|
dev_err(&ctrl->dev, "of_spmi: Failure registering %s\n",
|
|
|
|
node->full_name);
|
|
|
|
of_node_put(node);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info,
|
|
|
|
struct device_node *container)
|
2012-01-05 22:16:43 +00:00
|
|
|
{
|
2012-02-08 19:01:24 +00:00
|
|
|
struct spmi_controller *ctrl = d_info->ctrl;
|
2012-01-05 22:16:43 +00:00
|
|
|
struct device_node *node;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
for_each_child_of_node(container, node) {
|
2012-02-08 19:01:24 +00:00
|
|
|
struct of_spmi_res_info r_info;
|
|
|
|
|
|
|
|
r_info.node = node;
|
|
|
|
of_spmi_sum_resources(&r_info, 1);
|
|
|
|
|
|
|
|
rc = of_spmi_allocate_resources(d_info, &r_info);
|
2012-01-05 22:16:43 +00:00
|
|
|
if (rc) {
|
|
|
|
dev_err(&ctrl->dev, "%s: unable to allocate"
|
|
|
|
" resources\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
2012-02-08 19:01:24 +00:00
|
|
|
rc = of_spmi_create_device(d_info, node);
|
2012-01-05 22:16:43 +00:00
|
|
|
if (rc) {
|
|
|
|
dev_err(&ctrl->dev, "%s: unable to create device for"
|
|
|
|
" node %s\n", __func__, node->full_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int of_spmi_register_devices(struct spmi_controller *ctrl)
|
|
|
|
{
|
|
|
|
struct device_node *node;
|
|
|
|
|
|
|
|
/* Only register child devices if the ctrl has a node pointer set */
|
|
|
|
if (!ctrl->dev.of_node)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
for_each_child_of_node(ctrl->dev.of_node, node) {
|
2012-02-08 19:01:24 +00:00
|
|
|
struct of_spmi_dev_info d_info = {};
|
2012-01-05 22:16:43 +00:00
|
|
|
const __be32 *slave_id;
|
|
|
|
int len, rc;
|
|
|
|
|
|
|
|
slave_id = of_get_property(node, "reg", &len);
|
|
|
|
if (!slave_id) {
|
|
|
|
dev_err(&ctrl->dev, "of_spmi: invalid sid "
|
|
|
|
"on %s\n", node->full_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
d_info.b_info.slave_id = be32_to_cpup(slave_id);
|
|
|
|
d_info.ctrl = ctrl;
|
2012-01-05 22:16:43 +00:00
|
|
|
|
2012-02-08 19:01:24 +00:00
|
|
|
if (of_get_property(node, "spmi-slave-container", NULL)) {
|
|
|
|
of_spmi_walk_slave_container(&d_info, node);
|
2012-01-05 22:16:43 +00:00
|
|
|
continue;
|
|
|
|
} else {
|
2012-02-08 19:01:24 +00:00
|
|
|
struct of_spmi_res_info r_info;
|
|
|
|
|
|
|
|
r_info.node = node;
|
|
|
|
of_spmi_sum_resources(&r_info, 0);
|
|
|
|
rc = of_spmi_allocate_resources(&d_info, &r_info);
|
2012-01-05 22:16:43 +00:00
|
|
|
if (rc)
|
|
|
|
continue;
|
2012-02-08 19:01:24 +00:00
|
|
|
of_spmi_create_device(&d_info, node);
|
2012-01-05 22:16:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(of_spmi_register_devices);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|