blob: 2032b46270837d7b6397d3287dd445f38c4f8377 [file] [log] [blame]
Michael Bohan86e30dc2012-01-05 14:16:43 -08001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/spmi.h>
14#include <linux/irq.h>
15#include <linux/of.h>
16#include <linux/of_address.h>
17#include <linux/of_irq.h>
18#include <linux/of_spmi.h>
19#include <linux/slab.h>
20#include <linux/module.h>
21
Michael Bohan11926c92012-02-08 11:01:24 -080022struct of_spmi_dev_info {
23 struct spmi_controller *ctrl;
24 struct spmi_boardinfo b_info;
25};
26
27struct of_spmi_res_info {
28 struct device_node *node;
29 uint32_t num_reg;
30 uint32_t num_irq;
31};
32
33static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg)
Michael Bohan86e30dc2012-01-05 14:16:43 -080034{
Michael Bohan11926c92012-02-08 11:01:24 -080035 struct of_irq oirq;
Michael Bohan86e30dc2012-01-05 14:16:43 -080036 uint64_t size;
37 uint32_t flags;
Michael Bohan11926c92012-02-08 11:01:24 -080038
39 while (of_irq_map_one(r_info->node, r_info->num_irq, &oirq) == 0)
40 r_info->num_irq++;
41
42 if (!has_reg)
43 return;
44
45 /*
46 * We can't use of_address_to_resource here since it includes
47 * address translation; and address translation assumes that no
48 * parent buses have a size-cell of 0. But SPMI does have a
49 * size-cell of 0.
50 */
51 while (of_get_address(r_info->node, r_info->num_reg,
52 &size, &flags) != NULL)
53 r_info->num_reg++;
54}
55
56/**
57 * Allocate resources for a child of a spmi-slave node.
58 */
59static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info,
60 struct of_spmi_res_info *r_info)
61
62{
63 uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg;
64 int i;
Michael Bohan86e30dc2012-01-05 14:16:43 -080065 struct resource *res;
66 const __be32 *addrp;
Michael Bohan11926c92012-02-08 11:01:24 -080067 uint64_t size;
68 uint32_t flags;
Michael Bohan86e30dc2012-01-05 14:16:43 -080069
70 if (num_irq || num_reg) {
71 res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
72 if (!res)
73 return -ENOMEM;
74
Michael Bohan11926c92012-02-08 11:01:24 -080075 d_info->b_info.num_resources = num_reg + num_irq;
76 d_info->b_info.resource = res;
Michael Bohan86e30dc2012-01-05 14:16:43 -080077 for (i = 0; i < num_reg; i++, res++) {
78 /* Addresses are always 16 bits */
Michael Bohan11926c92012-02-08 11:01:24 -080079 addrp = of_get_address(r_info->node, i, &size, &flags);
Michael Bohan86e30dc2012-01-05 14:16:43 -080080 BUG_ON(!addrp);
81 res->start = be32_to_cpup(addrp);
82 res->end = res->start + size - 1;
83 res->flags = flags;
84 }
Michael Bohan11926c92012-02-08 11:01:24 -080085 WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) !=
Michael Bohan86e30dc2012-01-05 14:16:43 -080086 num_irq);
87 }
88
89 return 0;
90}
91
Michael Bohan11926c92012-02-08 11:01:24 -080092static int of_spmi_create_device(struct of_spmi_dev_info *d_info,
93 struct device_node *node)
Michael Bohan86e30dc2012-01-05 14:16:43 -080094{
Michael Bohan11926c92012-02-08 11:01:24 -080095 struct spmi_controller *ctrl = d_info->ctrl;
96 struct spmi_boardinfo *b_info = &d_info->b_info;
Michael Bohan86e30dc2012-01-05 14:16:43 -080097 void *result;
98 int rc;
99
Michael Bohan11926c92012-02-08 11:01:24 -0800100 rc = of_modalias_node(node, b_info->name, sizeof(b_info->name));
Michael Bohan86e30dc2012-01-05 14:16:43 -0800101 if (rc < 0) {
102 dev_err(&ctrl->dev, "of_spmi modalias failure on %s\n",
103 node->full_name);
104 return rc;
105 }
106
Michael Bohan11926c92012-02-08 11:01:24 -0800107 b_info->of_node = of_node_get(node);
108 result = spmi_new_device(ctrl, b_info);
Michael Bohan86e30dc2012-01-05 14:16:43 -0800109
110 if (result == NULL) {
111 dev_err(&ctrl->dev, "of_spmi: Failure registering %s\n",
112 node->full_name);
113 of_node_put(node);
114 return -ENODEV;
115 }
116
117 return 0;
118}
119
Michael Bohan11926c92012-02-08 11:01:24 -0800120static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info,
121 struct device_node *container)
Michael Bohan86e30dc2012-01-05 14:16:43 -0800122{
Michael Bohan11926c92012-02-08 11:01:24 -0800123 struct spmi_controller *ctrl = d_info->ctrl;
Michael Bohan86e30dc2012-01-05 14:16:43 -0800124 struct device_node *node;
Michael Bohan86e30dc2012-01-05 14:16:43 -0800125 int rc;
126
127 for_each_child_of_node(container, node) {
Michael Bohan11926c92012-02-08 11:01:24 -0800128 struct of_spmi_res_info r_info;
Michael Bohan86e30dc2012-01-05 14:16:43 -0800129
Michael Bohan11926c92012-02-08 11:01:24 -0800130 r_info.node = node;
131 of_spmi_sum_resources(&r_info, 1);
132
133 rc = of_spmi_allocate_resources(d_info, &r_info);
Michael Bohan86e30dc2012-01-05 14:16:43 -0800134 if (rc) {
135 dev_err(&ctrl->dev, "%s: unable to allocate"
136 " resources\n", __func__);
137 return;
138 }
Michael Bohan11926c92012-02-08 11:01:24 -0800139 rc = of_spmi_create_device(d_info, node);
Michael Bohan86e30dc2012-01-05 14:16:43 -0800140 if (rc) {
141 dev_err(&ctrl->dev, "%s: unable to create device for"
142 " node %s\n", __func__, node->full_name);
143 return;
144 }
145 }
146}
147
148int of_spmi_register_devices(struct spmi_controller *ctrl)
149{
150 struct device_node *node;
151
152 /* Only register child devices if the ctrl has a node pointer set */
153 if (!ctrl->dev.of_node)
154 return -ENODEV;
155
156 for_each_child_of_node(ctrl->dev.of_node, node) {
Michael Bohan11926c92012-02-08 11:01:24 -0800157 struct of_spmi_dev_info d_info = {};
Michael Bohan86e30dc2012-01-05 14:16:43 -0800158 const __be32 *slave_id;
159 int len, rc;
160
161 slave_id = of_get_property(node, "reg", &len);
162 if (!slave_id) {
163 dev_err(&ctrl->dev, "of_spmi: invalid sid "
164 "on %s\n", node->full_name);
165 continue;
166 }
167
Michael Bohan11926c92012-02-08 11:01:24 -0800168 d_info.b_info.slave_id = be32_to_cpup(slave_id);
169 d_info.ctrl = ctrl;
Michael Bohan86e30dc2012-01-05 14:16:43 -0800170
Michael Bohan11926c92012-02-08 11:01:24 -0800171 if (of_get_property(node, "spmi-slave-container", NULL)) {
172 of_spmi_walk_slave_container(&d_info, node);
Michael Bohan86e30dc2012-01-05 14:16:43 -0800173 continue;
174 } else {
Michael Bohan11926c92012-02-08 11:01:24 -0800175 struct of_spmi_res_info r_info;
176
177 r_info.node = node;
178 of_spmi_sum_resources(&r_info, 0);
179 rc = of_spmi_allocate_resources(&d_info, &r_info);
Michael Bohan86e30dc2012-01-05 14:16:43 -0800180 if (rc)
181 continue;
Michael Bohan11926c92012-02-08 11:01:24 -0800182 of_spmi_create_device(&d_info, node);
Michael Bohan86e30dc2012-01-05 14:16:43 -0800183 }
184 }
185
186 return 0;
187}
188EXPORT_SYMBOL(of_spmi_register_devices);
189
190MODULE_LICENSE("GPL");