diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2007-10-11 00:57:26 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-10-11 06:40:47 -0400 |
commit | b833b481c10cf591b15cc674948cc514e55d3b94 (patch) | |
tree | 6e02bb29bff6eea2716c0b02c0a2779d2b80480c /arch | |
parent | dd9b67ab37d57da67840276d28957498512d4dd8 (diff) |
[POWERPC] iSeries: Move detection of virtual cdroms
Now we will only have entries in the device tree for the actual existing
devices (including their OS/400 properties). This way viocd.c gets all
the information about the devices from the device tree.
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Acked-by: Jens Axboe <jens.axboe@oracle.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/kernel/vio.c | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/dt.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/vio.c | 317 |
4 files changed, 318 insertions, 8 deletions
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index b7c9e44ef180..cb22a3557c4e 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c | |||
@@ -247,9 +247,6 @@ static int __init vio_bus_init(void) | |||
247 | int err; | 247 | int err; |
248 | struct device_node *node_vroot; | 248 | struct device_node *node_vroot; |
249 | 249 | ||
250 | if (firmware_has_feature(FW_FEATURE_ISERIES)) | ||
251 | iommu_vio_init(); | ||
252 | |||
253 | err = bus_register(&vio_bus_type); | 250 | err = bus_register(&vio_bus_type); |
254 | if (err) { | 251 | if (err) { |
255 | printk(KERN_ERR "failed to register VIO bus\n"); | 252 | printk(KERN_ERR "failed to register VIO bus\n"); |
diff --git a/arch/powerpc/platforms/iseries/Makefile b/arch/powerpc/platforms/iseries/Makefile index 60db509638f1..a65f1b44abf8 100644 --- a/arch/powerpc/platforms/iseries/Makefile +++ b/arch/powerpc/platforms/iseries/Makefile | |||
@@ -7,7 +7,7 @@ obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt_mod.o mf.o lpevents.o \ | |||
7 | hvcall.o proc.o htab.o iommu.o misc.o irq.o | 7 | hvcall.o proc.o htab.o iommu.o misc.o irq.o |
8 | obj-$(CONFIG_PCI) += pci.o vpdinfo.o | 8 | obj-$(CONFIG_PCI) += pci.o vpdinfo.o |
9 | obj-$(CONFIG_SMP) += smp.o | 9 | obj-$(CONFIG_SMP) += smp.o |
10 | obj-$(CONFIG_VIOPATH) += viopath.o | 10 | obj-$(CONFIG_VIOPATH) += viopath.o vio.o |
11 | obj-$(CONFIG_MODULES) += ksyms.o | 11 | obj-$(CONFIG_MODULES) += ksyms.o |
12 | 12 | ||
13 | quiet_cmd_dt_strings = DT_STR $@ | 13 | quiet_cmd_dt_strings = DT_STR $@ |
diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c index 9e8a334a518a..84fcee15eb25 100644 --- a/arch/powerpc/platforms/iseries/dt.c +++ b/arch/powerpc/platforms/iseries/dt.c | |||
@@ -381,10 +381,6 @@ static void __init dt_vdevices(struct iseries_flat_dt *dt) | |||
381 | dt_do_vdevice(dt, "viodasd", reg, i, device_type_block, | 381 | dt_do_vdevice(dt, "viodasd", reg, i, device_type_block, |
382 | "IBM,iSeries-viodasd", 1); | 382 | "IBM,iSeries-viodasd", 1); |
383 | reg += HVMAXARCHITECTEDVIRTUALDISKS; | 383 | reg += HVMAXARCHITECTEDVIRTUALDISKS; |
384 | |||
385 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++) | ||
386 | dt_do_vdevice(dt, "viocd", reg, i, device_type_block, | ||
387 | "IBM,iSeries-viocd", 1); | ||
388 | reg += HVMAXARCHITECTEDVIRTUALCDROMS; | 384 | reg += HVMAXARCHITECTEDVIRTUALCDROMS; |
389 | 385 | ||
390 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++) | 386 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++) |
diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c new file mode 100644 index 000000000000..f61a97441c3d --- /dev/null +++ b/arch/powerpc/platforms/iseries/vio.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | * Legacy iSeries specific vio initialisation | ||
3 | * that needs to be built in (not a module). | ||
4 | * | ||
5 | * © Copyright 2007 IBM Corporation | ||
6 | * Author: Stephen Rothwell | ||
7 | * Some parts collected from various other files | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation; either version 2 of the | ||
12 | * License, or (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software Foundation, | ||
21 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/gfp.h> | ||
26 | #include <linux/completion.h> | ||
27 | #include <linux/proc_fs.h> | ||
28 | |||
29 | #include <asm/firmware.h> | ||
30 | #include <asm/iseries/vio.h> | ||
31 | #include <asm/iseries/iommu.h> | ||
32 | #include <asm/iseries/hv_types.h> | ||
33 | #include <asm/iseries/hv_lp_event.h> | ||
34 | |||
35 | #define FIRST_VTY 0 | ||
36 | #define NUM_VTYS 1 | ||
37 | #define FIRST_VSCSI (FIRST_VTY + NUM_VTYS) | ||
38 | #define NUM_VSCSIS 1 | ||
39 | #define FIRST_VLAN (FIRST_VSCSI + NUM_VSCSIS) | ||
40 | #define NUM_VLANS HVMAXARCHITECTEDVIRTUALLANS | ||
41 | #define FIRST_VIODASD (FIRST_VLAN + NUM_VLANS) | ||
42 | #define NUM_VIODASDS HVMAXARCHITECTEDVIRTUALDISKS | ||
43 | #define FIRST_VIOCD (FIRST_VIODASD + NUM_VIODASDS) | ||
44 | #define NUM_VIOCDS HVMAXARCHITECTEDVIRTUALCDROMS | ||
45 | #define FIRST_VIOTAPE (FIRST_VIOCD + NUM_VIOCDS) | ||
46 | #define NUM_VIOTAPES HVMAXARCHITECTEDVIRTUALTAPES | ||
47 | |||
48 | static struct property * __init new_property(const char *name, int length, | ||
49 | const void *value) | ||
50 | { | ||
51 | struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length, | ||
52 | GFP_KERNEL); | ||
53 | |||
54 | if (!np) | ||
55 | return NULL; | ||
56 | np->name = (char *)(np + 1); | ||
57 | np->value = np->name + strlen(name) + 1; | ||
58 | strcpy(np->name, name); | ||
59 | memcpy(np->value, value, length); | ||
60 | np->length = length; | ||
61 | return np; | ||
62 | } | ||
63 | |||
64 | static void __init free_property(struct property *np) | ||
65 | { | ||
66 | kfree(np); | ||
67 | } | ||
68 | |||
69 | static struct device_node * __init new_node(const char *path, | ||
70 | struct device_node *parent) | ||
71 | { | ||
72 | struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL); | ||
73 | |||
74 | if (!np) | ||
75 | return NULL; | ||
76 | np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL); | ||
77 | if (!np->full_name) { | ||
78 | kfree(np); | ||
79 | return NULL; | ||
80 | } | ||
81 | strcpy(np->full_name, path); | ||
82 | of_node_set_flag(np, OF_DYNAMIC); | ||
83 | kref_init(&np->kref); | ||
84 | np->parent = of_node_get(parent); | ||
85 | return np; | ||
86 | } | ||
87 | |||
88 | static void __init free_node(struct device_node *np) | ||
89 | { | ||
90 | struct property *next; | ||
91 | struct property *prop; | ||
92 | |||
93 | next = np->properties; | ||
94 | while (next) { | ||
95 | prop = next; | ||
96 | next = prop->next; | ||
97 | free_property(prop); | ||
98 | } | ||
99 | of_node_put(np->parent); | ||
100 | kfree(np->full_name); | ||
101 | kfree(np); | ||
102 | } | ||
103 | |||
104 | static int __init add_string_property(struct device_node *np, const char *name, | ||
105 | const char *value) | ||
106 | { | ||
107 | struct property *nprop = new_property(name, strlen(value) + 1, value); | ||
108 | |||
109 | if (!nprop) | ||
110 | return 0; | ||
111 | prom_add_property(np, nprop); | ||
112 | return 1; | ||
113 | } | ||
114 | |||
115 | static int __init add_raw_property(struct device_node *np, const char *name, | ||
116 | int length, const void *value) | ||
117 | { | ||
118 | struct property *nprop = new_property(name, length, value); | ||
119 | |||
120 | if (!nprop) | ||
121 | return 0; | ||
122 | prom_add_property(np, nprop); | ||
123 | return 1; | ||
124 | } | ||
125 | |||
126 | struct viocd_waitevent { | ||
127 | struct completion com; | ||
128 | int rc; | ||
129 | u16 sub_result; | ||
130 | }; | ||
131 | |||
132 | struct cdrom_info { | ||
133 | char rsrcname[10]; | ||
134 | char type[4]; | ||
135 | char model[3]; | ||
136 | }; | ||
137 | |||
138 | static void __init handle_cd_event(struct HvLpEvent *event) | ||
139 | { | ||
140 | struct viocdlpevent *bevent; | ||
141 | struct viocd_waitevent *pwe; | ||
142 | |||
143 | if (!event) | ||
144 | /* Notification that a partition went away! */ | ||
145 | return; | ||
146 | |||
147 | /* First, we should NEVER get an int here...only acks */ | ||
148 | if (hvlpevent_is_int(event)) { | ||
149 | printk(KERN_WARNING "handle_cd_event: got an unexpected int\n"); | ||
150 | if (hvlpevent_need_ack(event)) { | ||
151 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
152 | HvCallEvent_ackLpEvent(event); | ||
153 | } | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | bevent = (struct viocdlpevent *)event; | ||
158 | |||
159 | switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { | ||
160 | case viocdgetinfo: | ||
161 | pwe = (struct viocd_waitevent *)event->xCorrelationToken; | ||
162 | pwe->rc = event->xRc; | ||
163 | pwe->sub_result = bevent->sub_result; | ||
164 | complete(&pwe->com); | ||
165 | break; | ||
166 | |||
167 | default: | ||
168 | printk(KERN_WARNING "handle_cd_event: " | ||
169 | "message with unexpected subtype %0x04X!\n", | ||
170 | event->xSubtype & VIOMINOR_SUBTYPE_MASK); | ||
171 | if (hvlpevent_need_ack(event)) { | ||
172 | event->xRc = HvLpEvent_Rc_InvalidSubtype; | ||
173 | HvCallEvent_ackLpEvent(event); | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static void __init get_viocd_info(struct device_node *vio_root) | ||
179 | { | ||
180 | HvLpEvent_Rc hvrc; | ||
181 | u32 unit; | ||
182 | struct viocd_waitevent we; | ||
183 | struct cdrom_info *unitinfo; | ||
184 | dma_addr_t unitinfo_dmaaddr; | ||
185 | int ret; | ||
186 | |||
187 | ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2); | ||
188 | if (ret) { | ||
189 | printk(KERN_WARNING | ||
190 | "get_viocd_info: error opening path to host partition %d\n", | ||
191 | viopath_hostLp); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | /* Initialize our request handler */ | ||
196 | vio_setHandler(viomajorsubtype_cdio, handle_cd_event); | ||
197 | |||
198 | unitinfo = iseries_hv_alloc( | ||
199 | sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, | ||
200 | &unitinfo_dmaaddr, GFP_ATOMIC); | ||
201 | if (!unitinfo) { | ||
202 | printk(KERN_WARNING | ||
203 | "get_viocd_info: error allocating unitinfo\n"); | ||
204 | goto clear_handler; | ||
205 | } | ||
206 | |||
207 | memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS); | ||
208 | |||
209 | init_completion(&we.com); | ||
210 | |||
211 | hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, | ||
212 | HvLpEvent_Type_VirtualIo, | ||
213 | viomajorsubtype_cdio | viocdgetinfo, | ||
214 | HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, | ||
215 | viopath_sourceinst(viopath_hostLp), | ||
216 | viopath_targetinst(viopath_hostLp), | ||
217 | (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0, | ||
218 | sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0); | ||
219 | if (hvrc != HvLpEvent_Rc_Good) { | ||
220 | printk(KERN_WARNING | ||
221 | "get_viocd_info: cdrom error sending event. rc %d\n", | ||
222 | (int)hvrc); | ||
223 | goto hv_free; | ||
224 | } | ||
225 | |||
226 | wait_for_completion(&we.com); | ||
227 | |||
228 | if (we.rc) { | ||
229 | printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n", | ||
230 | we.rc, we.sub_result); | ||
231 | goto hv_free; | ||
232 | } | ||
233 | |||
234 | for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) && | ||
235 | unitinfo[unit].rsrcname[0]; unit++) { | ||
236 | struct device_node *np; | ||
237 | char name[64]; | ||
238 | u32 reg = FIRST_VIOCD + unit; | ||
239 | |||
240 | snprintf(name, sizeof(name), "/vdevice/viocd@%08x", reg); | ||
241 | np = new_node(name, vio_root); | ||
242 | if (!np) | ||
243 | goto hv_free; | ||
244 | if (!add_string_property(np, "name", "viocd") || | ||
245 | !add_string_property(np, "device_type", "block") || | ||
246 | !add_string_property(np, "compatible", | ||
247 | "IBM,iSeries-viocd") || | ||
248 | !add_raw_property(np, "reg", sizeof(reg), ®) || | ||
249 | !add_raw_property(np, "linux,unit_address", | ||
250 | sizeof(unit), &unit) || | ||
251 | !add_raw_property(np, "linux,vio_rsrcname", | ||
252 | sizeof(unitinfo[unit].rsrcname), | ||
253 | unitinfo[unit].rsrcname) || | ||
254 | !add_raw_property(np, "linux,vio_type", | ||
255 | sizeof(unitinfo[unit].type), | ||
256 | unitinfo[unit].type) || | ||
257 | !add_raw_property(np, "linux,vio_model", | ||
258 | sizeof(unitinfo[unit].model), | ||
259 | unitinfo[unit].model)) | ||
260 | goto node_free; | ||
261 | np->name = of_get_property(np, "name", NULL); | ||
262 | np->type = of_get_property(np, "device_type", NULL); | ||
263 | of_attach_node(np); | ||
264 | #ifdef CONFIG_PROC_DEVICETREE | ||
265 | if (vio_root->pde) { | ||
266 | struct proc_dir_entry *ent; | ||
267 | |||
268 | ent = proc_mkdir(strrchr(np->full_name, '/') + 1, | ||
269 | vio_root->pde); | ||
270 | if (ent) | ||
271 | proc_device_tree_add_node(np, ent); | ||
272 | } | ||
273 | #endif | ||
274 | continue; | ||
275 | |||
276 | node_free: | ||
277 | free_node(np); | ||
278 | break; | ||
279 | } | ||
280 | |||
281 | hv_free: | ||
282 | iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, | ||
283 | unitinfo, unitinfo_dmaaddr); | ||
284 | clear_handler: | ||
285 | vio_clearHandler(viomajorsubtype_cdio); | ||
286 | viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2); | ||
287 | } | ||
288 | |||
289 | static int __init iseries_vio_init(void) | ||
290 | { | ||
291 | struct device_node *vio_root; | ||
292 | |||
293 | if (!firmware_has_feature(FW_FEATURE_ISERIES)) | ||
294 | return -ENODEV; | ||
295 | |||
296 | iommu_vio_init(); | ||
297 | |||
298 | vio_root = of_find_node_by_path("/vdevice"); | ||
299 | if (!vio_root) | ||
300 | return -ENODEV; | ||
301 | |||
302 | if (viopath_hostLp == HvLpIndexInvalid) { | ||
303 | vio_set_hostlp(); | ||
304 | /* If we don't have a host, bail out */ | ||
305 | if (viopath_hostLp == HvLpIndexInvalid) | ||
306 | goto put_node; | ||
307 | } | ||
308 | |||
309 | get_viocd_info(vio_root); | ||
310 | |||
311 | return 0; | ||
312 | |||
313 | put_node: | ||
314 | of_node_put(vio_root); | ||
315 | return -ENODEV; | ||
316 | } | ||
317 | arch_initcall(iseries_vio_init); | ||