diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-07-11 21:51:31 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-16 07:04:20 -0400 |
commit | e450992d13ffaec6dde4bbbd308b834ae9fc3708 (patch) | |
tree | a42083aff5e9435c60ced16846fd14b9475ad96b | |
parent | 13077d80286205e02eebe1c2786a914a4bbd2588 (diff) |
[SPARC64]: Initial domain-services driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc64/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/sparc64/kernel/ds.c | 654 |
2 files changed, 655 insertions, 1 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 719ab23b1938..70e6c501392a 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile | |||
@@ -26,7 +26,7 @@ obj-$(CONFIG_MODULES) += module.o | |||
26 | obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o | 26 | obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o |
27 | obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o | 27 | obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o |
28 | obj-$(CONFIG_KPROBES) += kprobes.o | 28 | obj-$(CONFIG_KPROBES) += kprobes.o |
29 | obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o | 29 | obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o |
30 | obj-$(CONFIG_AUDIT) += audit.o | 30 | obj-$(CONFIG_AUDIT) += audit.o |
31 | obj-$(CONFIG_AUDIT)$(CONFIG_SPARC32_COMPAT) += compat_audit.o | 31 | obj-$(CONFIG_AUDIT)$(CONFIG_SPARC32_COMPAT) += compat_audit.o |
32 | obj-y += $(obj-yy) | 32 | obj-y += $(obj-yy) |
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c new file mode 100644 index 000000000000..22517dfd021d --- /dev/null +++ b/arch/sparc64/kernel/ds.c | |||
@@ -0,0 +1,654 @@ | |||
1 | /* ds.c: Domain Services driver for Logical Domains | ||
2 | * | ||
3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | |||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/delay.h> | ||
14 | |||
15 | #include <asm/ldc.h> | ||
16 | #include <asm/vio.h> | ||
17 | #include <asm/power.h> | ||
18 | |||
19 | #define DRV_MODULE_NAME "ds" | ||
20 | #define PFX DRV_MODULE_NAME ": " | ||
21 | #define DRV_MODULE_VERSION "1.0" | ||
22 | #define DRV_MODULE_RELDATE "Jul 11, 2007" | ||
23 | |||
24 | static char version[] __devinitdata = | ||
25 | DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; | ||
26 | MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); | ||
27 | MODULE_DESCRIPTION("Sun LDOM domain services driver"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | MODULE_VERSION(DRV_MODULE_VERSION); | ||
30 | |||
31 | struct ds_msg_tag { | ||
32 | __u32 type; | ||
33 | #define DS_INIT_REQ 0x00 | ||
34 | #define DS_INIT_ACK 0x01 | ||
35 | #define DS_INIT_NACK 0x02 | ||
36 | #define DS_REG_REQ 0x03 | ||
37 | #define DS_REG_ACK 0x04 | ||
38 | #define DS_REG_NACK 0x05 | ||
39 | #define DS_UNREG_REQ 0x06 | ||
40 | #define DS_UNREG_ACK 0x07 | ||
41 | #define DS_UNREG_NACK 0x08 | ||
42 | #define DS_DATA 0x09 | ||
43 | #define DS_NACK 0x0a | ||
44 | |||
45 | __u32 len; | ||
46 | }; | ||
47 | |||
48 | /* Result codes */ | ||
49 | #define DS_OK 0x00 | ||
50 | #define DS_REG_VER_NACK 0x01 | ||
51 | #define DS_REG_DUP 0x02 | ||
52 | #define DS_INV_HDL 0x03 | ||
53 | #define DS_TYPE_UNKNOWN 0x04 | ||
54 | |||
55 | struct ds_version { | ||
56 | __u16 major; | ||
57 | __u16 minor; | ||
58 | }; | ||
59 | |||
60 | struct ds_ver_req { | ||
61 | struct ds_msg_tag tag; | ||
62 | struct ds_version ver; | ||
63 | }; | ||
64 | |||
65 | struct ds_ver_ack { | ||
66 | struct ds_msg_tag tag; | ||
67 | __u16 minor; | ||
68 | }; | ||
69 | |||
70 | struct ds_ver_nack { | ||
71 | struct ds_msg_tag tag; | ||
72 | __u16 major; | ||
73 | }; | ||
74 | |||
75 | struct ds_reg_req { | ||
76 | struct ds_msg_tag tag; | ||
77 | __u64 handle; | ||
78 | __u16 major; | ||
79 | __u16 minor; | ||
80 | char svc_id[0]; | ||
81 | }; | ||
82 | |||
83 | struct ds_reg_ack { | ||
84 | struct ds_msg_tag tag; | ||
85 | __u64 handle; | ||
86 | __u16 minor; | ||
87 | }; | ||
88 | |||
89 | struct ds_reg_nack { | ||
90 | struct ds_msg_tag tag; | ||
91 | __u64 handle; | ||
92 | __u16 major; | ||
93 | }; | ||
94 | |||
95 | struct ds_unreg_req { | ||
96 | struct ds_msg_tag tag; | ||
97 | __u64 handle; | ||
98 | }; | ||
99 | |||
100 | struct ds_unreg_ack { | ||
101 | struct ds_msg_tag tag; | ||
102 | __u64 handle; | ||
103 | }; | ||
104 | |||
105 | struct ds_unreg_nack { | ||
106 | struct ds_msg_tag tag; | ||
107 | __u64 handle; | ||
108 | }; | ||
109 | |||
110 | struct ds_data { | ||
111 | struct ds_msg_tag tag; | ||
112 | __u64 handle; | ||
113 | }; | ||
114 | |||
115 | struct ds_data_nack { | ||
116 | struct ds_msg_tag tag; | ||
117 | __u64 handle; | ||
118 | __u64 result; | ||
119 | }; | ||
120 | |||
121 | struct ds_cap_state { | ||
122 | __u64 handle; | ||
123 | |||
124 | void (*data)(struct ldc_channel *lp, | ||
125 | struct ds_cap_state *dp, | ||
126 | void *buf, int len); | ||
127 | |||
128 | const char *service_id; | ||
129 | |||
130 | u8 state; | ||
131 | #define CAP_STATE_UNKNOWN 0x00 | ||
132 | #define CAP_STATE_REG_SENT 0x01 | ||
133 | #define CAP_STATE_REGISTERED 0x02 | ||
134 | }; | ||
135 | |||
136 | static int ds_send(struct ldc_channel *lp, void *data, int len) | ||
137 | { | ||
138 | int err, limit = 1000; | ||
139 | |||
140 | err = -EINVAL; | ||
141 | while (limit-- > 0) { | ||
142 | err = ldc_write(lp, data, len); | ||
143 | if (!err || (err != -EAGAIN)) | ||
144 | break; | ||
145 | udelay(1); | ||
146 | } | ||
147 | |||
148 | return err; | ||
149 | } | ||
150 | |||
151 | struct ds_md_update_req { | ||
152 | __u64 req_num; | ||
153 | }; | ||
154 | |||
155 | struct ds_md_update_res { | ||
156 | __u64 req_num; | ||
157 | __u32 result; | ||
158 | }; | ||
159 | |||
160 | static void md_update_data(struct ldc_channel *lp, | ||
161 | struct ds_cap_state *dp, | ||
162 | void *buf, int len) | ||
163 | { | ||
164 | struct ds_data *dpkt = buf; | ||
165 | struct ds_md_update_req *rp; | ||
166 | struct { | ||
167 | struct ds_data data; | ||
168 | struct ds_md_update_res res; | ||
169 | } pkt; | ||
170 | |||
171 | rp = (struct ds_md_update_req *) (dpkt + 1); | ||
172 | |||
173 | printk(KERN_ERR PFX "MD update REQ [%lx] len=%d\n", | ||
174 | rp->req_num, len); | ||
175 | |||
176 | memset(&pkt, 0, sizeof(pkt)); | ||
177 | pkt.data.tag.type = DS_DATA; | ||
178 | pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); | ||
179 | pkt.data.handle = dp->handle; | ||
180 | pkt.res.req_num = rp->req_num; | ||
181 | pkt.res.result = DS_OK; | ||
182 | |||
183 | ds_send(lp, &pkt, sizeof(pkt)); | ||
184 | } | ||
185 | |||
186 | struct ds_shutdown_req { | ||
187 | __u64 req_num; | ||
188 | __u32 ms_delay; | ||
189 | }; | ||
190 | |||
191 | struct ds_shutdown_res { | ||
192 | __u64 req_num; | ||
193 | __u32 result; | ||
194 | char reason[1]; | ||
195 | }; | ||
196 | |||
197 | static void domain_shutdown_data(struct ldc_channel *lp, | ||
198 | struct ds_cap_state *dp, | ||
199 | void *buf, int len) | ||
200 | { | ||
201 | struct ds_data *dpkt = buf; | ||
202 | struct ds_shutdown_req *rp; | ||
203 | struct { | ||
204 | struct ds_data data; | ||
205 | struct ds_shutdown_res res; | ||
206 | } pkt; | ||
207 | |||
208 | rp = (struct ds_shutdown_req *) (dpkt + 1); | ||
209 | |||
210 | printk(KERN_ALERT PFX "Shutdown request from " | ||
211 | "LDOM manager received.\n"); | ||
212 | |||
213 | memset(&pkt, 0, sizeof(pkt)); | ||
214 | pkt.data.tag.type = DS_DATA; | ||
215 | pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); | ||
216 | pkt.data.handle = dp->handle; | ||
217 | pkt.res.req_num = rp->req_num; | ||
218 | pkt.res.result = DS_OK; | ||
219 | pkt.res.reason[0] = 0; | ||
220 | |||
221 | ds_send(lp, &pkt, sizeof(pkt)); | ||
222 | |||
223 | wake_up_powerd(); | ||
224 | } | ||
225 | |||
226 | struct ds_panic_req { | ||
227 | __u64 req_num; | ||
228 | }; | ||
229 | |||
230 | struct ds_panic_res { | ||
231 | __u64 req_num; | ||
232 | __u32 result; | ||
233 | char reason[1]; | ||
234 | }; | ||
235 | |||
236 | static void domain_panic_data(struct ldc_channel *lp, | ||
237 | struct ds_cap_state *dp, | ||
238 | void *buf, int len) | ||
239 | { | ||
240 | struct ds_data *dpkt = buf; | ||
241 | struct ds_panic_req *rp; | ||
242 | struct { | ||
243 | struct ds_data data; | ||
244 | struct ds_panic_res res; | ||
245 | } pkt; | ||
246 | |||
247 | rp = (struct ds_panic_req *) (dpkt + 1); | ||
248 | |||
249 | printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n", | ||
250 | rp->req_num, len); | ||
251 | |||
252 | memset(&pkt, 0, sizeof(pkt)); | ||
253 | pkt.data.tag.type = DS_DATA; | ||
254 | pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); | ||
255 | pkt.data.handle = dp->handle; | ||
256 | pkt.res.req_num = rp->req_num; | ||
257 | pkt.res.result = DS_OK; | ||
258 | pkt.res.reason[0] = 0; | ||
259 | |||
260 | ds_send(lp, &pkt, sizeof(pkt)); | ||
261 | |||
262 | panic("PANIC requested by LDOM manager."); | ||
263 | } | ||
264 | |||
265 | struct ds_cpu_tag { | ||
266 | __u64 req_num; | ||
267 | __u32 type; | ||
268 | #define DS_CPU_CONFIGURE 0x43 | ||
269 | #define DS_CPU_UNCONFIGURE 0x55 | ||
270 | #define DS_CPU_FORCE_UNCONFIGURE 0x46 | ||
271 | #define DS_CPU_STATUS 0x53 | ||
272 | |||
273 | /* Responses */ | ||
274 | #define DS_CPU_OK 0x6f | ||
275 | #define DS_CPU_ERROR 0x65 | ||
276 | |||
277 | __u32 num_records; | ||
278 | }; | ||
279 | |||
280 | struct ds_cpu_record { | ||
281 | __u32 cpu_id; | ||
282 | }; | ||
283 | |||
284 | static void dr_cpu_data(struct ldc_channel *lp, | ||
285 | struct ds_cap_state *dp, | ||
286 | void *buf, int len) | ||
287 | { | ||
288 | struct ds_data *dpkt = buf; | ||
289 | struct ds_cpu_tag *rp; | ||
290 | |||
291 | rp = (struct ds_cpu_tag *) (dpkt + 1); | ||
292 | |||
293 | printk(KERN_ERR PFX "CPU REQ [%lx:%x], len=%d\n", | ||
294 | rp->req_num, rp->type, len); | ||
295 | } | ||
296 | |||
297 | struct ds_pri_msg { | ||
298 | __u64 req_num; | ||
299 | __u64 type; | ||
300 | #define DS_PRI_REQUEST 0x00 | ||
301 | #define DS_PRI_DATA 0x01 | ||
302 | #define DS_PRI_UPDATE 0x02 | ||
303 | }; | ||
304 | |||
305 | static void ds_pri_data(struct ldc_channel *lp, | ||
306 | struct ds_cap_state *dp, | ||
307 | void *buf, int len) | ||
308 | { | ||
309 | struct ds_data *dpkt = buf; | ||
310 | struct ds_pri_msg *rp; | ||
311 | |||
312 | rp = (struct ds_pri_msg *) (dpkt + 1); | ||
313 | |||
314 | printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n", | ||
315 | rp->req_num, rp->type, len); | ||
316 | } | ||
317 | |||
318 | struct ds_cap_state ds_states[] = { | ||
319 | { | ||
320 | .service_id = "md-update", | ||
321 | .data = md_update_data, | ||
322 | }, | ||
323 | { | ||
324 | .service_id = "domain-shutdown", | ||
325 | .data = domain_shutdown_data, | ||
326 | }, | ||
327 | { | ||
328 | .service_id = "domain-panic", | ||
329 | .data = domain_panic_data, | ||
330 | }, | ||
331 | { | ||
332 | .service_id = "dr-cpu", | ||
333 | .data = dr_cpu_data, | ||
334 | }, | ||
335 | { | ||
336 | .service_id = "pri", | ||
337 | .data = ds_pri_data, | ||
338 | }, | ||
339 | }; | ||
340 | |||
341 | static struct ds_cap_state *find_cap(u64 handle) | ||
342 | { | ||
343 | unsigned int index = handle >> 32; | ||
344 | |||
345 | if (index >= ARRAY_SIZE(ds_states)) | ||
346 | return NULL; | ||
347 | return &ds_states[index]; | ||
348 | } | ||
349 | |||
350 | static DEFINE_SPINLOCK(ds_lock); | ||
351 | |||
352 | struct ds_info { | ||
353 | struct ldc_channel *lp; | ||
354 | u8 hs_state; | ||
355 | #define DS_HS_START 0x01 | ||
356 | #define DS_HS_DONE 0x02 | ||
357 | |||
358 | void *rcv_buf; | ||
359 | int rcv_buf_len; | ||
360 | }; | ||
361 | |||
362 | static void ds_conn_reset(struct ds_info *dp) | ||
363 | { | ||
364 | printk(KERN_ERR PFX "ds_conn_reset() from %p\n", | ||
365 | __builtin_return_address(0)); | ||
366 | } | ||
367 | |||
368 | static int register_services(struct ds_info *dp) | ||
369 | { | ||
370 | struct ldc_channel *lp = dp->lp; | ||
371 | int i; | ||
372 | |||
373 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) { | ||
374 | struct { | ||
375 | struct ds_reg_req req; | ||
376 | u8 id_buf[256]; | ||
377 | } pbuf; | ||
378 | struct ds_cap_state *cp = &ds_states[i]; | ||
379 | int err, msg_len; | ||
380 | u64 new_count; | ||
381 | |||
382 | if (cp->state == CAP_STATE_REGISTERED) | ||
383 | continue; | ||
384 | |||
385 | new_count = sched_clock() & 0xffffffff; | ||
386 | cp->handle = ((u64) i << 32) | new_count; | ||
387 | |||
388 | msg_len = (sizeof(struct ds_reg_req) + | ||
389 | strlen(cp->service_id)); | ||
390 | |||
391 | memset(&pbuf, 0, sizeof(pbuf)); | ||
392 | pbuf.req.tag.type = DS_REG_REQ; | ||
393 | pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag)); | ||
394 | pbuf.req.handle = cp->handle; | ||
395 | pbuf.req.major = 1; | ||
396 | pbuf.req.minor = 0; | ||
397 | strcpy(pbuf.req.svc_id, cp->service_id); | ||
398 | |||
399 | err = ds_send(lp, &pbuf, msg_len); | ||
400 | if (err > 0) | ||
401 | cp->state = CAP_STATE_REG_SENT; | ||
402 | } | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt) | ||
407 | { | ||
408 | |||
409 | if (dp->hs_state == DS_HS_START) { | ||
410 | if (pkt->type != DS_INIT_ACK) | ||
411 | goto conn_reset; | ||
412 | |||
413 | dp->hs_state = DS_HS_DONE; | ||
414 | |||
415 | return register_services(dp); | ||
416 | } | ||
417 | |||
418 | if (dp->hs_state != DS_HS_DONE) | ||
419 | goto conn_reset; | ||
420 | |||
421 | if (pkt->type == DS_REG_ACK) { | ||
422 | struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt; | ||
423 | struct ds_cap_state *cp = find_cap(ap->handle); | ||
424 | |||
425 | if (!cp) { | ||
426 | printk(KERN_ERR PFX "REG ACK for unknown handle %lx\n", | ||
427 | ap->handle); | ||
428 | return 0; | ||
429 | } | ||
430 | printk(KERN_INFO PFX "Registered %s service.\n", | ||
431 | cp->service_id); | ||
432 | cp->state = CAP_STATE_REGISTERED; | ||
433 | } else if (pkt->type == DS_REG_NACK) { | ||
434 | struct ds_reg_nack *np = (struct ds_reg_nack *) pkt; | ||
435 | struct ds_cap_state *cp = find_cap(np->handle); | ||
436 | |||
437 | if (!cp) { | ||
438 | printk(KERN_ERR PFX "REG NACK for " | ||
439 | "unknown handle %lx\n", | ||
440 | np->handle); | ||
441 | return 0; | ||
442 | } | ||
443 | printk(KERN_ERR PFX "Could not register %s service\n", | ||
444 | cp->service_id); | ||
445 | cp->state = CAP_STATE_UNKNOWN; | ||
446 | } | ||
447 | |||
448 | return 0; | ||
449 | |||
450 | conn_reset: | ||
451 | ds_conn_reset(dp); | ||
452 | return -ECONNRESET; | ||
453 | } | ||
454 | |||
455 | static int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len) | ||
456 | { | ||
457 | struct ds_data *dpkt = (struct ds_data *) pkt; | ||
458 | struct ds_cap_state *cp = find_cap(dpkt->handle); | ||
459 | |||
460 | if (!cp) { | ||
461 | struct ds_data_nack nack = { | ||
462 | .tag = { | ||
463 | .type = DS_NACK, | ||
464 | .len = (sizeof(struct ds_data_nack) - | ||
465 | sizeof(struct ds_msg_tag)), | ||
466 | }, | ||
467 | .handle = dpkt->handle, | ||
468 | .result = DS_INV_HDL, | ||
469 | }; | ||
470 | |||
471 | printk(KERN_ERR PFX "Data for unknown handle %lu\n", | ||
472 | dpkt->handle); | ||
473 | ds_send(dp->lp, &nack, sizeof(nack)); | ||
474 | } else { | ||
475 | cp->data(dp->lp, cp, dpkt, len); | ||
476 | } | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static void ds_up(struct ds_info *dp) | ||
481 | { | ||
482 | struct ldc_channel *lp = dp->lp; | ||
483 | struct ds_ver_req req; | ||
484 | int err; | ||
485 | |||
486 | req.tag.type = DS_INIT_REQ; | ||
487 | req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag); | ||
488 | req.ver.major = 1; | ||
489 | req.ver.minor = 0; | ||
490 | |||
491 | err = ds_send(lp, &req, sizeof(req)); | ||
492 | if (err > 0) | ||
493 | dp->hs_state = DS_HS_START; | ||
494 | } | ||
495 | |||
496 | static void ds_event(void *arg, int event) | ||
497 | { | ||
498 | struct ds_info *dp = arg; | ||
499 | struct ldc_channel *lp = dp->lp; | ||
500 | unsigned long flags; | ||
501 | int err; | ||
502 | |||
503 | spin_lock_irqsave(&ds_lock, flags); | ||
504 | |||
505 | if (event == LDC_EVENT_UP) { | ||
506 | ds_up(dp); | ||
507 | spin_unlock_irqrestore(&ds_lock, flags); | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | if (event != LDC_EVENT_DATA_READY) { | ||
512 | printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); | ||
513 | spin_unlock_irqrestore(&ds_lock, flags); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | err = 0; | ||
518 | while (1) { | ||
519 | struct ds_msg_tag *tag; | ||
520 | |||
521 | err = ldc_read(lp, dp->rcv_buf, sizeof(*tag)); | ||
522 | |||
523 | if (unlikely(err < 0)) { | ||
524 | if (err == -ECONNRESET) | ||
525 | ds_conn_reset(dp); | ||
526 | break; | ||
527 | } | ||
528 | if (err == 0) | ||
529 | break; | ||
530 | |||
531 | tag = dp->rcv_buf; | ||
532 | err = ldc_read(lp, tag + 1, tag->len); | ||
533 | |||
534 | if (unlikely(err < 0)) { | ||
535 | if (err == -ECONNRESET) | ||
536 | ds_conn_reset(dp); | ||
537 | break; | ||
538 | } | ||
539 | if (err < tag->len) | ||
540 | break; | ||
541 | |||
542 | if (tag->type < DS_DATA) | ||
543 | err = ds_handshake(dp, dp->rcv_buf); | ||
544 | else | ||
545 | err = ds_data(dp, dp->rcv_buf, | ||
546 | sizeof(*tag) + err); | ||
547 | if (err == -ECONNRESET) | ||
548 | break; | ||
549 | } | ||
550 | |||
551 | spin_unlock_irqrestore(&ds_lock, flags); | ||
552 | } | ||
553 | |||
554 | static int __devinit ds_probe(struct vio_dev *vdev, | ||
555 | const struct vio_device_id *id) | ||
556 | { | ||
557 | static int ds_version_printed; | ||
558 | struct mdesc_node *endp; | ||
559 | struct ldc_channel_config ds_cfg = { | ||
560 | .event = ds_event, | ||
561 | .mtu = 4096, | ||
562 | .mode = LDC_MODE_STREAM, | ||
563 | }; | ||
564 | struct ldc_channel *lp; | ||
565 | struct ds_info *dp; | ||
566 | const u64 *chan_id; | ||
567 | int err; | ||
568 | |||
569 | if (ds_version_printed++ == 0) | ||
570 | printk(KERN_INFO "%s", version); | ||
571 | |||
572 | endp = vio_find_endpoint(vdev); | ||
573 | if (!endp) | ||
574 | return -ENODEV; | ||
575 | |||
576 | chan_id = md_get_property(endp, "id", NULL); | ||
577 | if (!chan_id) | ||
578 | return -ENODEV; | ||
579 | |||
580 | dp = kzalloc(sizeof(*dp), GFP_KERNEL); | ||
581 | err = -ENOMEM; | ||
582 | if (!dp) | ||
583 | goto out_err; | ||
584 | |||
585 | dp->rcv_buf = kzalloc(4096, GFP_KERNEL); | ||
586 | if (!dp->rcv_buf) | ||
587 | goto out_free_dp; | ||
588 | |||
589 | dp->rcv_buf_len = 4096; | ||
590 | |||
591 | ds_cfg.tx_irq = endp->irqs[0]; | ||
592 | ds_cfg.rx_irq = endp->irqs[1]; | ||
593 | |||
594 | lp = ldc_alloc(*chan_id, &ds_cfg, dp); | ||
595 | if (IS_ERR(lp)) { | ||
596 | err = PTR_ERR(lp); | ||
597 | goto out_free_rcv_buf; | ||
598 | } | ||
599 | dp->lp = lp; | ||
600 | |||
601 | err = ldc_bind(lp); | ||
602 | if (err) | ||
603 | goto out_free_ldc; | ||
604 | |||
605 | start_powerd(); | ||
606 | |||
607 | return err; | ||
608 | |||
609 | out_free_ldc: | ||
610 | ldc_free(dp->lp); | ||
611 | |||
612 | out_free_rcv_buf: | ||
613 | kfree(dp->rcv_buf); | ||
614 | |||
615 | out_free_dp: | ||
616 | kfree(dp); | ||
617 | |||
618 | out_err: | ||
619 | return err; | ||
620 | } | ||
621 | |||
622 | static int ds_remove(struct vio_dev *vdev) | ||
623 | { | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static struct vio_device_id ds_match[] = { | ||
628 | { | ||
629 | .type = "domain-services-port", | ||
630 | }, | ||
631 | {}, | ||
632 | }; | ||
633 | |||
634 | static struct vio_driver ds_driver = { | ||
635 | .id_table = ds_match, | ||
636 | .probe = ds_probe, | ||
637 | .remove = ds_remove, | ||
638 | .driver = { | ||
639 | .name = "ds", | ||
640 | .owner = THIS_MODULE, | ||
641 | } | ||
642 | }; | ||
643 | |||
644 | static int __init ds_init(void) | ||
645 | { | ||
646 | int i; | ||
647 | |||
648 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) | ||
649 | ds_states[i].handle = ((u64)i << 32); | ||
650 | |||
651 | return vio_register_driver(&ds_driver); | ||
652 | } | ||
653 | |||
654 | subsys_initcall(ds_init); | ||