blob: 9f2a396cdc4629f282c71f28f0dcb7eae3e535c7 [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
22/**
23 * Allocate resources for a child of a spmi-container node.
24 */
25static int of_spmi_allocate_resources(struct spmi_controller *ctrl,
26 struct spmi_boardinfo *info,
27 struct device_node *node,
28 uint32_t num_reg)
29{
30 int i, num_irq = 0;
31 uint64_t size;
32 uint32_t flags;
33 struct resource *res;
34 const __be32 *addrp;
35 struct of_irq oirq;
36
37 while (of_irq_map_one(node, num_irq, &oirq) == 0)
38 num_irq++;
39
40 if (num_irq || num_reg) {
41 res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
42 if (!res)
43 return -ENOMEM;
44
45 info->num_resources = num_reg + num_irq;
46 info->resource = res;
47 for (i = 0; i < num_reg; i++, res++) {
48 /* Addresses are always 16 bits */
49 addrp = of_get_address(node, i, &size, &flags);
50 BUG_ON(!addrp);
51 res->start = be32_to_cpup(addrp);
52 res->end = res->start + size - 1;
53 res->flags = flags;
54 }
55 WARN_ON(of_irq_to_resource_table(node, res, num_irq) !=
56 num_irq);
57 }
58
59 return 0;
60}
61
62static int of_spmi_create_device(struct spmi_controller *ctrl,
63 struct spmi_boardinfo *info,
64 struct device_node *node)
65{
66 void *result;
67 int rc;
68
69 rc = of_modalias_node(node, info->name, sizeof(info->name));
70 if (rc < 0) {
71 dev_err(&ctrl->dev, "of_spmi modalias failure on %s\n",
72 node->full_name);
73 return rc;
74 }
75
76 info->of_node = of_node_get(node);
77 result = spmi_new_device(ctrl, info);
78
79 if (result == NULL) {
80 dev_err(&ctrl->dev, "of_spmi: Failure registering %s\n",
81 node->full_name);
82 of_node_put(node);
83 return -ENODEV;
84 }
85
86 return 0;
87}
88
89static void of_spmi_walk_container_children(struct spmi_controller *ctrl,
90 struct spmi_boardinfo *info,
91 struct device_node *container)
92{
93 struct device_node *node;
94 uint64_t size;
95 uint32_t flags, num_reg = 0;
96 int rc;
97
98 for_each_child_of_node(container, node) {
99 /*
100 * We can't use of_address_to_resource here since it includes
101 * address translation; and address translation assumes that no
102 * parent buses have a size-cell of 0. But SPMI does have a
103 * size-cell of 0.
104 */
105 while (of_get_address(node, num_reg, &size, &flags) != NULL)
106 num_reg++;
107
108 rc = of_spmi_allocate_resources(ctrl, info, node, num_reg);
109 if (rc) {
110 dev_err(&ctrl->dev, "%s: unable to allocate"
111 " resources\n", __func__);
112 return;
113 }
114 rc = of_spmi_create_device(ctrl, info, node);
115 if (rc) {
116 dev_err(&ctrl->dev, "%s: unable to create device for"
117 " node %s\n", __func__, node->full_name);
118 return;
119 }
120 }
121}
122
123int of_spmi_register_devices(struct spmi_controller *ctrl)
124{
125 struct device_node *node;
126
127 /* Only register child devices if the ctrl has a node pointer set */
128 if (!ctrl->dev.of_node)
129 return -ENODEV;
130
131 for_each_child_of_node(ctrl->dev.of_node, node) {
132 struct spmi_boardinfo info = {};
133 const __be32 *slave_id;
134 int len, rc;
135
136 slave_id = of_get_property(node, "reg", &len);
137 if (!slave_id) {
138 dev_err(&ctrl->dev, "of_spmi: invalid sid "
139 "on %s\n", node->full_name);
140 continue;
141 }
142
143 info.slave_id = be32_to_cpup(slave_id);
144
145 if (of_get_property(node, "spmi-dev-container", NULL)) {
146 of_spmi_walk_container_children(ctrl, &info, node);
147 continue;
148 } else {
149 rc = of_spmi_allocate_resources(ctrl, &info, node, 0);
150 if (rc)
151 continue;
152 of_spmi_create_device(ctrl, &info, node);
153 }
154 }
155
156 return 0;
157}
158EXPORT_SYMBOL(of_spmi_register_devices);
159
160MODULE_LICENSE("GPL");