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