[PATCH] cpu hotplug: use hotplug version of registration in late inits
[linux-2.6] / drivers / sbus / sbus.c
1 /* sbus.c: SBus support routines.
2  *
3  * Copyright (C) 1995, 2006 David S. Miller (davem@davemloft.net)
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/slab.h>
8 #include <linux/init.h>
9 #include <linux/pci.h>
10
11 #include <asm/system.h>
12 #include <asm/sbus.h>
13 #include <asm/dma.h>
14 #include <asm/oplib.h>
15 #include <asm/prom.h>
16 #include <asm/of_device.h>
17 #include <asm/bpp.h>
18 #include <asm/irq.h>
19
20 struct sbus_bus *sbus_root;
21
22 static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev)
23 {
24         unsigned long base;
25         void *pval;
26         int len;
27
28         sdev->prom_node = dp->node;
29         strcpy(sdev->prom_name, dp->name);
30
31         pval = of_get_property(dp, "reg", &len);
32         sdev->num_registers = 0;
33         if (pval) {
34                 memcpy(sdev->reg_addrs, pval, len);
35
36                 sdev->num_registers =
37                         len / sizeof(struct linux_prom_registers);
38
39                 base = (unsigned long) sdev->reg_addrs[0].phys_addr;
40
41                 /* Compute the slot number. */
42                 if (base >= SUN_SBUS_BVADDR && sparc_cpu_model == sun4m)
43                         sdev->slot = sbus_dev_slot(base);
44                 else
45                         sdev->slot = sdev->reg_addrs[0].which_io;
46         }
47
48         pval = of_get_property(dp, "ranges", &len);
49         sdev->num_device_ranges = 0;
50         if (pval) {
51                 memcpy(sdev->device_ranges, pval, len);
52                 sdev->num_device_ranges =
53                         len / sizeof(struct linux_prom_ranges);
54         }
55
56         sbus_fill_device_irq(sdev);
57
58         sdev->ofdev.node = dp;
59         if (sdev->parent)
60                 sdev->ofdev.dev.parent = &sdev->parent->ofdev.dev;
61         else
62                 sdev->ofdev.dev.parent = &sdev->bus->ofdev.dev;
63         sdev->ofdev.dev.bus = &sbus_bus_type;
64         strcpy(sdev->ofdev.dev.bus_id, dp->path_component_name);
65
66         if (of_device_register(&sdev->ofdev) != 0)
67                 printk(KERN_DEBUG "sbus: device registration error for %s!\n",
68                        sdev->ofdev.dev.bus_id);
69 }
70
71 static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus)
72 {
73         void *pval;
74         int len;
75
76         pval = of_get_property(dp, "ranges", &len);
77         sbus->num_sbus_ranges = 0;
78         if (pval) {
79                 memcpy(sbus->sbus_ranges, pval, len);
80                 sbus->num_sbus_ranges =
81                         len / sizeof(struct linux_prom_ranges);
82
83                 sbus_arch_bus_ranges_init(dp->parent, sbus);
84         }
85 }
86
87 static void __init __apply_ranges_to_regs(struct linux_prom_ranges *ranges,
88                                           int num_ranges,
89                                           struct linux_prom_registers *regs,
90                                           int num_regs)
91 {
92         if (num_ranges) {
93                 int regnum;
94
95                 for (regnum = 0; regnum < num_regs; regnum++) {
96                         int rngnum;
97
98                         for (rngnum = 0; rngnum < num_ranges; rngnum++) {
99                                 if (regs[regnum].which_io == ranges[rngnum].ot_child_space)
100                                         break;
101                         }
102                         if (rngnum == num_ranges) {
103                                 /* We used to flag this as an error.  Actually
104                                  * some devices do not report the regs as we expect.
105                                  * For example, see SUNW,pln device.  In that case
106                                  * the reg property is in a format internal to that
107                                  * node, ie. it is not in the SBUS register space
108                                  * per se. -DaveM
109                                  */
110                                 return;
111                         }
112                         regs[regnum].which_io = ranges[rngnum].ot_parent_space;
113                         regs[regnum].phys_addr -= ranges[rngnum].ot_child_base;
114                         regs[regnum].phys_addr += ranges[rngnum].ot_parent_base;
115                 }
116         }
117 }
118
119 static void __init __fixup_regs_sdev(struct sbus_dev *sdev)
120 {
121         if (sdev->num_registers != 0) {
122                 struct sbus_dev *parent = sdev->parent;
123                 int i;
124
125                 while (parent != NULL) {
126                         __apply_ranges_to_regs(parent->device_ranges,
127                                                parent->num_device_ranges,
128                                                sdev->reg_addrs,
129                                                sdev->num_registers);
130
131                         parent = parent->parent;
132                 }
133
134                 __apply_ranges_to_regs(sdev->bus->sbus_ranges,
135                                        sdev->bus->num_sbus_ranges,
136                                        sdev->reg_addrs,
137                                        sdev->num_registers);
138
139                 for (i = 0; i < sdev->num_registers; i++) {
140                         struct resource *res = &sdev->resource[i];
141
142                         res->start = sdev->reg_addrs[i].phys_addr;
143                         res->end = (res->start +
144                                     (unsigned long)sdev->reg_addrs[i].reg_size - 1UL);
145                         res->flags = IORESOURCE_IO |
146                                 (sdev->reg_addrs[i].which_io & 0xff);
147                 }
148         }
149 }
150
151 static void __init sbus_fixup_all_regs(struct sbus_dev *first_sdev)
152 {
153         struct sbus_dev *sdev;
154
155         for (sdev = first_sdev; sdev; sdev = sdev->next) {
156                 if (sdev->child)
157                         sbus_fixup_all_regs(sdev->child);
158                 __fixup_regs_sdev(sdev);
159         }
160 }
161
162 /* We preserve the "probe order" of these bus and device lists to give
163  * the same ordering as the old code.
164  */
165 static void __init sbus_insert(struct sbus_bus *sbus, struct sbus_bus **root)
166 {
167         while (*root)
168                 root = &(*root)->next;
169         *root = sbus;
170         sbus->next = NULL;
171 }
172
173 static void __init sdev_insert(struct sbus_dev *sdev, struct sbus_dev **root)
174 {
175         while (*root)
176                 root = &(*root)->next;
177         *root = sdev;
178         sdev->next = NULL;
179 }
180
181 static void __init walk_children(struct device_node *dp, struct sbus_dev *parent, struct sbus_bus *sbus)
182 {
183         dp = dp->child;
184         while (dp) {
185                 struct sbus_dev *sdev;
186
187                 sdev = kzalloc(sizeof(struct sbus_dev), GFP_ATOMIC);
188                 if (sdev) {
189                         sdev_insert(sdev, &parent->child);
190
191                         sdev->bus = sbus;
192                         sdev->parent = parent;
193
194                         fill_sbus_device(dp, sdev);
195
196                         walk_children(dp, sdev, sbus);
197                 }
198                 dp = dp->sibling;
199         }
200 }
201
202 static void __init build_one_sbus(struct device_node *dp, int num_sbus)
203 {
204         struct sbus_bus *sbus;
205         unsigned int sbus_clock;
206         struct device_node *dev_dp;
207
208         sbus = kzalloc(sizeof(struct sbus_bus), GFP_ATOMIC);
209         if (!sbus)
210                 return;
211
212         sbus_insert(sbus, &sbus_root);
213         sbus->prom_node = dp->node;
214
215         sbus_setup_iommu(sbus, dp);
216
217         printk("sbus%d: ", num_sbus);
218
219         sbus_clock = of_getintprop_default(dp, "clock-frequency",
220                                            (25*1000*1000));
221         sbus->clock_freq = sbus_clock;
222
223         printk("Clock %d.%d MHz\n", (int) ((sbus_clock/1000)/1000),
224                (int) (((sbus_clock/1000)%1000 != 0) ? 
225                       (((sbus_clock/1000)%1000) + 1000) : 0));
226
227         strcpy(sbus->prom_name, dp->name);
228
229         sbus_setup_arch_props(sbus, dp);
230
231         sbus_bus_ranges_init(dp, sbus);
232
233         sbus->ofdev.node = dp;
234         sbus->ofdev.dev.parent = NULL;
235         sbus->ofdev.dev.bus = &sbus_bus_type;
236         sprintf(sbus->ofdev.dev.bus_id, "sbus%d", num_sbus);
237
238         if (of_device_register(&sbus->ofdev) != 0)
239                 printk(KERN_DEBUG "sbus: device registration error for %s!\n",
240                        sbus->ofdev.dev.bus_id);
241
242         dev_dp = dp->child;
243         while (dev_dp) {
244                 struct sbus_dev *sdev;
245
246                 sdev = kzalloc(sizeof(struct sbus_dev), GFP_ATOMIC);
247                 if (sdev) {
248                         sdev_insert(sdev, &sbus->devices);
249
250                         sdev->bus = sbus;
251                         sdev->parent = NULL;
252                         fill_sbus_device(dev_dp, sdev);
253
254                         walk_children(dev_dp, sdev, sbus);
255                 }
256                 dev_dp = dev_dp->sibling;
257         }
258
259         sbus_fixup_all_regs(sbus->devices);
260
261         dvma_init(sbus);
262 }
263
264 static int __init sbus_init(void)
265 {
266         struct device_node *dp;
267         const char *sbus_name = "sbus";
268         int num_sbus = 0;
269
270         if (sbus_arch_preinit())
271                 return 0;
272
273         if (sparc_cpu_model == sun4d)
274                 sbus_name = "sbi";
275
276         for_each_node_by_name(dp, sbus_name) {
277                 build_one_sbus(dp, num_sbus);
278                 num_sbus++;
279
280         }
281
282         sbus_arch_postinit();
283
284         return 0;
285 }
286
287 subsys_initcall(sbus_init);