diff options
Diffstat (limited to 'drivers/sbus/char/bbc_i2c.c')
-rw-r--r-- | drivers/sbus/char/bbc_i2c.c | 267 |
1 files changed, 106 insertions, 161 deletions
diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index ac8ef2ce07fb..af7f4af6c5fb 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c | |||
@@ -1,8 +1,7 @@ | |||
1 | /* $Id: bbc_i2c.c,v 1.2 2001/04/02 09:59:08 davem Exp $ | 1 | /* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III |
2 | * bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III | ||
3 | * platforms. | 2 | * platforms. |
4 | * | 3 | * |
5 | * Copyright (C) 2001 David S. Miller (davem@redhat.com) | 4 | * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) |
6 | */ | 5 | */ |
7 | 6 | ||
8 | #include <linux/module.h> | 7 | #include <linux/module.h> |
@@ -14,9 +13,8 @@ | |||
14 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
15 | #include <linux/init.h> | 14 | #include <linux/init.h> |
16 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
17 | #include <asm/oplib.h> | 16 | #include <linux/of.h> |
18 | #include <asm/ebus.h> | 17 | #include <linux/of_device.h> |
19 | #include <asm/spitfire.h> | ||
20 | #include <asm/bbc.h> | 18 | #include <asm/bbc.h> |
21 | #include <asm/io.h> | 19 | #include <asm/io.h> |
22 | 20 | ||
@@ -53,54 +51,12 @@ | |||
53 | * The second controller also connects to the smartcard reader, if present. | 51 | * The second controller also connects to the smartcard reader, if present. |
54 | */ | 52 | */ |
55 | 53 | ||
56 | #define NUM_CHILDREN 8 | 54 | static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, int val) |
57 | struct bbc_i2c_bus { | ||
58 | struct bbc_i2c_bus *next; | ||
59 | int index; | ||
60 | spinlock_t lock; | ||
61 | void __iomem *i2c_bussel_reg; | ||
62 | void __iomem *i2c_control_regs; | ||
63 | unsigned char own, clock; | ||
64 | |||
65 | wait_queue_head_t wq; | ||
66 | volatile int waiting; | ||
67 | |||
68 | struct linux_ebus_device *bus_edev; | ||
69 | struct { | ||
70 | struct linux_ebus_child *device; | ||
71 | int client_claimed; | ||
72 | } devs[NUM_CHILDREN]; | ||
73 | }; | ||
74 | |||
75 | static struct bbc_i2c_bus *all_bbc_i2c; | ||
76 | |||
77 | struct bbc_i2c_client { | ||
78 | struct bbc_i2c_bus *bp; | ||
79 | struct linux_ebus_child *echild; | ||
80 | int bus; | ||
81 | int address; | ||
82 | }; | ||
83 | |||
84 | static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild) | ||
85 | { | 55 | { |
86 | int i; | 56 | int i; |
87 | 57 | ||
88 | for (i = 0; i < NUM_CHILDREN; i++) { | 58 | for (i = 0; i < NUM_CHILDREN; i++) { |
89 | if (bp->devs[i].device == echild) { | 59 | if (bp->devs[i].device == op) { |
90 | if (bp->devs[i].client_claimed) | ||
91 | return 0; | ||
92 | return 1; | ||
93 | } | ||
94 | } | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val) | ||
99 | { | ||
100 | int i; | ||
101 | |||
102 | for (i = 0; i < NUM_CHILDREN; i++) { | ||
103 | if (bp->devs[i].device == echild) { | ||
104 | bp->devs[i].client_claimed = val; | 60 | bp->devs[i].client_claimed = val; |
105 | return; | 61 | return; |
106 | } | 62 | } |
@@ -110,61 +66,47 @@ static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child | |||
110 | #define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) | 66 | #define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) |
111 | #define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) | 67 | #define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) |
112 | 68 | ||
113 | static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild) | 69 | struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index) |
114 | { | 70 | { |
115 | struct bbc_i2c_bus *bp = all_bbc_i2c; | 71 | struct of_device *op = NULL; |
72 | int curidx = 0, i; | ||
116 | 73 | ||
117 | while (bp != NULL) { | 74 | for (i = 0; i < NUM_CHILDREN; i++) { |
118 | if (find_device(bp, echild) != 0) | 75 | if (!(op = bp->devs[i].device)) |
119 | break; | 76 | break; |
120 | bp = bp->next; | 77 | if (curidx == index) |
78 | goto out; | ||
79 | op = NULL; | ||
80 | curidx++; | ||
121 | } | 81 | } |
122 | 82 | ||
123 | return bp; | ||
124 | } | ||
125 | |||
126 | struct linux_ebus_child *bbc_i2c_getdev(int index) | ||
127 | { | ||
128 | struct bbc_i2c_bus *bp = all_bbc_i2c; | ||
129 | struct linux_ebus_child *echild = NULL; | ||
130 | int curidx = 0; | ||
131 | |||
132 | while (bp != NULL) { | ||
133 | struct bbc_i2c_bus *next = bp->next; | ||
134 | int i; | ||
135 | |||
136 | for (i = 0; i < NUM_CHILDREN; i++) { | ||
137 | if (!(echild = bp->devs[i].device)) | ||
138 | break; | ||
139 | if (curidx == index) | ||
140 | goto out; | ||
141 | echild = NULL; | ||
142 | curidx++; | ||
143 | } | ||
144 | bp = next; | ||
145 | } | ||
146 | out: | 83 | out: |
147 | if (curidx == index) | 84 | if (curidx == index) |
148 | return echild; | 85 | return op; |
149 | return NULL; | 86 | return NULL; |
150 | } | 87 | } |
151 | 88 | ||
152 | struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild) | 89 | struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *op) |
153 | { | 90 | { |
154 | struct bbc_i2c_bus *bp = find_bus_for_device(echild); | ||
155 | struct bbc_i2c_client *client; | 91 | struct bbc_i2c_client *client; |
92 | const u32 *reg; | ||
156 | 93 | ||
157 | if (!bp) | ||
158 | return NULL; | ||
159 | client = kzalloc(sizeof(*client), GFP_KERNEL); | 94 | client = kzalloc(sizeof(*client), GFP_KERNEL); |
160 | if (!client) | 95 | if (!client) |
161 | return NULL; | 96 | return NULL; |
162 | client->bp = bp; | 97 | client->bp = bp; |
163 | client->echild = echild; | 98 | client->op = op; |
164 | client->bus = echild->resource[0].start; | 99 | |
165 | client->address = echild->resource[1].start; | 100 | reg = of_get_property(op->node, "reg", NULL); |
101 | if (!reg) { | ||
102 | kfree(client); | ||
103 | return NULL; | ||
104 | } | ||
166 | 105 | ||
167 | claim_device(bp, echild); | 106 | client->bus = reg[0]; |
107 | client->address = reg[1]; | ||
108 | |||
109 | claim_device(bp, op); | ||
168 | 110 | ||
169 | return client; | 111 | return client; |
170 | } | 112 | } |
@@ -172,9 +114,9 @@ struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild) | |||
172 | void bbc_i2c_detach(struct bbc_i2c_client *client) | 114 | void bbc_i2c_detach(struct bbc_i2c_client *client) |
173 | { | 115 | { |
174 | struct bbc_i2c_bus *bp = client->bp; | 116 | struct bbc_i2c_bus *bp = client->bp; |
175 | struct linux_ebus_child *echild = client->echild; | 117 | struct of_device *op = client->op; |
176 | 118 | ||
177 | release_device(bp, echild); | 119 | release_device(bp, op); |
178 | kfree(client); | 120 | kfree(client); |
179 | } | 121 | } |
180 | 122 | ||
@@ -355,44 +297,43 @@ static void __init reset_one_i2c(struct bbc_i2c_bus *bp) | |||
355 | writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); | 297 | writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); |
356 | } | 298 | } |
357 | 299 | ||
358 | static int __init attach_one_i2c(struct linux_ebus_device *edev, int index) | 300 | static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int index) |
359 | { | 301 | { |
360 | struct bbc_i2c_bus *bp; | 302 | struct bbc_i2c_bus *bp; |
361 | struct linux_ebus_child *echild; | 303 | struct device_node *dp; |
362 | int entry; | 304 | int entry; |
363 | 305 | ||
364 | bp = kzalloc(sizeof(*bp), GFP_KERNEL); | 306 | bp = kzalloc(sizeof(*bp), GFP_KERNEL); |
365 | if (!bp) | 307 | if (!bp) |
366 | return -ENOMEM; | 308 | return NULL; |
367 | 309 | ||
368 | bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2); | 310 | bp->i2c_control_regs = of_ioremap(&op->resource[0], 0, 0x2, "bbc_i2c_regs"); |
369 | if (!bp->i2c_control_regs) | 311 | if (!bp->i2c_control_regs) |
370 | goto fail; | 312 | goto fail; |
371 | 313 | ||
372 | if (edev->num_addrs == 2) { | 314 | bp->i2c_bussel_reg = of_ioremap(&op->resource[1], 0, 0x1, "bbc_i2c_bussel"); |
373 | bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1); | 315 | if (!bp->i2c_bussel_reg) |
374 | if (!bp->i2c_bussel_reg) | 316 | goto fail; |
375 | goto fail; | ||
376 | } | ||
377 | 317 | ||
378 | bp->waiting = 0; | 318 | bp->waiting = 0; |
379 | init_waitqueue_head(&bp->wq); | 319 | init_waitqueue_head(&bp->wq); |
380 | if (request_irq(edev->irqs[0], bbc_i2c_interrupt, | 320 | if (request_irq(op->irqs[0], bbc_i2c_interrupt, |
381 | IRQF_SHARED, "bbc_i2c", bp)) | 321 | IRQF_SHARED, "bbc_i2c", bp)) |
382 | goto fail; | 322 | goto fail; |
383 | 323 | ||
384 | bp->index = index; | 324 | bp->index = index; |
385 | bp->bus_edev = edev; | 325 | bp->op = op; |
386 | 326 | ||
387 | spin_lock_init(&bp->lock); | 327 | spin_lock_init(&bp->lock); |
388 | bp->next = all_bbc_i2c; | ||
389 | all_bbc_i2c = bp; | ||
390 | 328 | ||
391 | entry = 0; | 329 | entry = 0; |
392 | for (echild = edev->children; | 330 | for (dp = op->node->child; |
393 | echild && entry < 8; | 331 | dp && entry < 8; |
394 | echild = echild->next, entry++) { | 332 | dp = dp->sibling, entry++) { |
395 | bp->devs[entry].device = echild; | 333 | struct of_device *child_op; |
334 | |||
335 | child_op = of_find_device_by_node(dp); | ||
336 | bp->devs[entry].device = child_op; | ||
396 | bp->devs[entry].client_claimed = 0; | 337 | bp->devs[entry].client_claimed = 0; |
397 | } | 338 | } |
398 | 339 | ||
@@ -406,86 +347,90 @@ static int __init attach_one_i2c(struct linux_ebus_device *edev, int index) | |||
406 | 347 | ||
407 | reset_one_i2c(bp); | 348 | reset_one_i2c(bp); |
408 | 349 | ||
409 | return 0; | 350 | return bp; |
410 | 351 | ||
411 | fail: | 352 | fail: |
412 | if (bp->i2c_bussel_reg) | 353 | if (bp->i2c_bussel_reg) |
413 | iounmap(bp->i2c_bussel_reg); | 354 | of_iounmap(&op->resource[1], bp->i2c_bussel_reg, 1); |
414 | if (bp->i2c_control_regs) | 355 | if (bp->i2c_control_regs) |
415 | iounmap(bp->i2c_control_regs); | 356 | of_iounmap(&op->resource[0], bp->i2c_control_regs, 2); |
416 | kfree(bp); | 357 | kfree(bp); |
417 | return -EINVAL; | 358 | return NULL; |
418 | } | ||
419 | |||
420 | static int __init bbc_present(void) | ||
421 | { | ||
422 | struct linux_ebus *ebus = NULL; | ||
423 | struct linux_ebus_device *edev = NULL; | ||
424 | |||
425 | for_each_ebus(ebus) { | ||
426 | for_each_ebusdev(edev, ebus) { | ||
427 | if (!strcmp(edev->prom_node->name, "bbc")) | ||
428 | return 1; | ||
429 | } | ||
430 | } | ||
431 | return 0; | ||
432 | } | 359 | } |
433 | 360 | ||
434 | extern int bbc_envctrl_init(void); | 361 | extern int bbc_envctrl_init(struct bbc_i2c_bus *bp); |
435 | extern void bbc_envctrl_cleanup(void); | 362 | extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp); |
436 | static void bbc_i2c_cleanup(void); | ||
437 | 363 | ||
438 | static int __init bbc_i2c_init(void) | 364 | static int __devinit bbc_i2c_probe(struct of_device *op, |
365 | const struct of_device_id *match) | ||
439 | { | 366 | { |
440 | struct linux_ebus *ebus = NULL; | 367 | struct bbc_i2c_bus *bp; |
441 | struct linux_ebus_device *edev = NULL; | ||
442 | int err, index = 0; | 368 | int err, index = 0; |
443 | 369 | ||
444 | if ((tlb_type != cheetah && tlb_type != cheetah_plus) || | 370 | bp = attach_one_i2c(op, index); |
445 | !bbc_present()) | 371 | if (!bp) |
446 | return -ENODEV; | 372 | return -EINVAL; |
447 | 373 | ||
448 | for_each_ebus(ebus) { | 374 | err = bbc_envctrl_init(bp); |
449 | for_each_ebusdev(edev, ebus) { | 375 | if (err) { |
450 | if (!strcmp(edev->prom_node->name, "i2c")) { | 376 | free_irq(op->irqs[0], bp); |
451 | if (!attach_one_i2c(edev, index)) | 377 | if (bp->i2c_bussel_reg) |
452 | index++; | 378 | of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1); |
453 | } | 379 | if (bp->i2c_control_regs) |
454 | } | 380 | of_iounmap(&op->resource[1], bp->i2c_control_regs, 2); |
381 | kfree(bp); | ||
382 | } else { | ||
383 | dev_set_drvdata(&op->dev, bp); | ||
455 | } | 384 | } |
456 | 385 | ||
457 | if (!index) | ||
458 | return -ENODEV; | ||
459 | |||
460 | err = bbc_envctrl_init(); | ||
461 | if (err) | ||
462 | bbc_i2c_cleanup(); | ||
463 | return err; | 386 | return err; |
464 | } | 387 | } |
465 | 388 | ||
466 | static void bbc_i2c_cleanup(void) | 389 | static int __devexit bbc_i2c_remove(struct of_device *op) |
467 | { | 390 | { |
468 | struct bbc_i2c_bus *bp = all_bbc_i2c; | 391 | struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev); |
392 | |||
393 | bbc_envctrl_cleanup(bp); | ||
394 | |||
395 | free_irq(op->irqs[0], bp); | ||
469 | 396 | ||
470 | bbc_envctrl_cleanup(); | 397 | if (bp->i2c_bussel_reg) |
398 | of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1); | ||
399 | if (bp->i2c_control_regs) | ||
400 | of_iounmap(&op->resource[1], bp->i2c_control_regs, 2); | ||
471 | 401 | ||
472 | while (bp != NULL) { | 402 | kfree(bp); |
473 | struct bbc_i2c_bus *next = bp->next; | ||
474 | 403 | ||
475 | free_irq(bp->bus_edev->irqs[0], bp); | 404 | return 0; |
405 | } | ||
476 | 406 | ||
477 | if (bp->i2c_bussel_reg) | 407 | static struct of_device_id bbc_i2c_match[] = { |
478 | iounmap(bp->i2c_bussel_reg); | 408 | { |
479 | if (bp->i2c_control_regs) | 409 | .name = "i2c", |
480 | iounmap(bp->i2c_control_regs); | 410 | .compatible = "SUNW,bbc-i2c", |
411 | }, | ||
412 | {}, | ||
413 | }; | ||
414 | MODULE_DEVICE_TABLE(of, bbc_i2c_match); | ||
481 | 415 | ||
482 | kfree(bp); | 416 | static struct of_platform_driver bbc_i2c_driver = { |
417 | .name = "bbc_i2c", | ||
418 | .match_table = bbc_i2c_match, | ||
419 | .probe = bbc_i2c_probe, | ||
420 | .remove = __devexit_p(bbc_i2c_remove), | ||
421 | }; | ||
483 | 422 | ||
484 | bp = next; | 423 | static int __init bbc_i2c_init(void) |
485 | } | 424 | { |
486 | all_bbc_i2c = NULL; | 425 | return of_register_driver(&bbc_i2c_driver, &of_bus_type); |
426 | } | ||
427 | |||
428 | static void __exit bbc_i2c_exit(void) | ||
429 | { | ||
430 | of_unregister_driver(&bbc_i2c_driver); | ||
487 | } | 431 | } |
488 | 432 | ||
489 | module_init(bbc_i2c_init); | 433 | module_init(bbc_i2c_init); |
490 | module_exit(bbc_i2c_cleanup); | 434 | module_exit(bbc_i2c_exit); |
435 | |||
491 | MODULE_LICENSE("GPL"); | 436 | MODULE_LICENSE("GPL"); |