[POWERPC] iSeries: Move detection of virtual tapes
[firefly-linux-kernel-4.4.55.git] / arch / powerpc / platforms / iseries / vio.c
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 struct vio_waitevent {
49         struct completion       com;
50         int                     rc;
51         u16                     sub_result;
52 };
53
54 struct vio_resource {
55         char    rsrcname[10];
56         char    type[4];
57         char    model[3];
58 };
59
60 static struct property * __init new_property(const char *name, int length,
61                 const void *value)
62 {
63         struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length,
64                         GFP_KERNEL);
65
66         if (!np)
67                 return NULL;
68         np->name = (char *)(np + 1);
69         np->value = np->name + strlen(name) + 1;
70         strcpy(np->name, name);
71         memcpy(np->value, value, length);
72         np->length = length;
73         return np;
74 }
75
76 static void __init free_property(struct property *np)
77 {
78         kfree(np);
79 }
80
81 static struct device_node * __init new_node(const char *path,
82                 struct device_node *parent)
83 {
84         struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL);
85
86         if (!np)
87                 return NULL;
88         np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL);
89         if (!np->full_name) {
90                 kfree(np);
91                 return NULL;
92         }
93         strcpy(np->full_name, path);
94         of_node_set_flag(np, OF_DYNAMIC);
95         kref_init(&np->kref);
96         np->parent = of_node_get(parent);
97         return np;
98 }
99
100 static void __init free_node(struct device_node *np)
101 {
102         struct property *next;
103         struct property *prop;
104
105         next = np->properties;
106         while (next) {
107                 prop = next;
108                 next = prop->next;
109                 free_property(prop);
110         }
111         of_node_put(np->parent);
112         kfree(np->full_name);
113         kfree(np);
114 }
115
116 static int __init add_string_property(struct device_node *np, const char *name,
117                 const char *value)
118 {
119         struct property *nprop = new_property(name, strlen(value) + 1, value);
120
121         if (!nprop)
122                 return 0;
123         prom_add_property(np, nprop);
124         return 1;
125 }
126
127 static int __init add_raw_property(struct device_node *np, const char *name,
128                 int length, const void *value)
129 {
130         struct property *nprop = new_property(name, length, value);
131
132         if (!nprop)
133                 return 0;
134         prom_add_property(np, nprop);
135         return 1;
136 }
137
138 static void __init handle_cd_event(struct HvLpEvent *event)
139 {
140         struct viocdlpevent *bevent;
141         struct vio_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 vio_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 vio_waitevent we;
183         struct vio_resource *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), &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 /* Handle interrupt events for tape */
290 static void __init handle_tape_event(struct HvLpEvent *event)
291 {
292         struct vio_waitevent *we;
293         struct viotapelpevent *tevent = (struct viotapelpevent *)event;
294
295         if (event == NULL)
296                 /* Notification that a partition went away! */
297                 return;
298
299         we = (struct vio_waitevent *)event->xCorrelationToken;
300         switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
301         case viotapegetinfo:
302                 we->rc = tevent->sub_type_result;
303                 complete(&we->com);
304                 break;
305         default:
306                 printk(KERN_WARNING "handle_tape_event: weird ack\n");
307         }
308 }
309
310 static void __init get_viotape_info(struct device_node *vio_root)
311 {
312         HvLpEvent_Rc hvrc;
313         u32 unit;
314         struct vio_resource *unitinfo;
315         dma_addr_t unitinfo_dmaaddr;
316         size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
317         struct vio_waitevent we;
318         int ret;
319
320         ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
321         if (ret) {
322                 printk(KERN_WARNING "get_viotape_info: "
323                         "error on viopath_open to hostlp %d\n", ret);
324                 return;
325         }
326
327         vio_setHandler(viomajorsubtype_tape, handle_tape_event);
328
329         unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
330         if (!unitinfo)
331                 goto clear_handler;
332
333         memset(unitinfo, 0, len);
334
335         hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
336                         HvLpEvent_Type_VirtualIo,
337                         viomajorsubtype_tape | viotapegetinfo,
338                         HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
339                         viopath_sourceinst(viopath_hostLp),
340                         viopath_targetinst(viopath_hostLp),
341                         (u64)(unsigned long)&we, VIOVERSION << 16,
342                         unitinfo_dmaaddr, len, 0, 0);
343         if (hvrc != HvLpEvent_Rc_Good) {
344                 printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
345                                 (int)hvrc);
346                 goto hv_free;
347         }
348
349         wait_for_completion(&we.com);
350
351         for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
352                         unitinfo[unit].rsrcname[0]; unit++) {
353                 struct device_node *np;
354                 char name[64];
355                 u32 reg = FIRST_VIOTAPE + unit;
356
357                 snprintf(name, sizeof(name), "/vdevice/viotape@%08x", reg);
358                 np = new_node(name, vio_root);
359                 if (!np)
360                         goto hv_free;
361                 if (!add_string_property(np, "name", "viotape") ||
362                         !add_string_property(np, "device_type", "byte") ||
363                         !add_string_property(np, "compatible",
364                                 "IBM,iSeries-viotape") ||
365                         !add_raw_property(np, "reg", sizeof(reg), &reg) ||
366                         !add_raw_property(np, "linux,unit_address",
367                                 sizeof(unit), &unit) ||
368                         !add_raw_property(np, "linux,vio_rsrcname",
369                                 sizeof(unitinfo[unit].rsrcname),
370                                 unitinfo[unit].rsrcname) ||
371                         !add_raw_property(np, "linux,vio_type",
372                                 sizeof(unitinfo[unit].type),
373                                 unitinfo[unit].type) ||
374                         !add_raw_property(np, "linux,vio_model",
375                                 sizeof(unitinfo[unit].model),
376                                 unitinfo[unit].model))
377                         goto node_free;
378                 np->name = of_get_property(np, "name", NULL);
379                 np->type = of_get_property(np, "device_type", NULL);
380                 of_attach_node(np);
381 #ifdef CONFIG_PROC_DEVICETREE
382                 if (vio_root->pde) {
383                         struct proc_dir_entry *ent;
384
385                         ent = proc_mkdir(strrchr(np->full_name, '/') + 1,
386                                         vio_root->pde);
387                         if (ent)
388                                 proc_device_tree_add_node(np, ent);
389                 }
390 #endif
391                 continue;
392
393  node_free:
394                 free_node(np);
395                 break;
396         }
397
398  hv_free:
399         iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
400  clear_handler:
401         vio_clearHandler(viomajorsubtype_tape);
402         viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
403 }
404
405 static int __init iseries_vio_init(void)
406 {
407         struct device_node *vio_root;
408
409         if (!firmware_has_feature(FW_FEATURE_ISERIES))
410                 return -ENODEV;
411
412         iommu_vio_init();
413
414         vio_root = of_find_node_by_path("/vdevice");
415         if (!vio_root)
416                 return -ENODEV;
417
418         if (viopath_hostLp == HvLpIndexInvalid) {
419                 vio_set_hostlp();
420                 /* If we don't have a host, bail out */
421                 if (viopath_hostLp == HvLpIndexInvalid)
422                         goto put_node;
423         }
424
425         get_viocd_info(vio_root);
426         get_viotape_info(vio_root);
427
428         return 0;
429
430  put_node:
431         of_node_put(vio_root);
432         return -ENODEV;
433 }
434 arch_initcall(iseries_vio_init);