diff options
author | Andres Salomon <dilinger@queued.net> | 2010-12-30 23:27:33 -0500 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-01-14 06:38:15 -0500 |
commit | de8255ccd219267cfd34139022b197c1ef8f032f (patch) | |
tree | be7744be209561f07d41bde109897de7dbd20bbf /drivers/i2c | |
parent | 419cdc54ea597d307fade607a65e4885634eb8c8 (diff) |
i2c: Convert SCx200 driver from using raw PCI to platform device
The SCx200 ACB driver supports ISA hardware as well as PCI. The PCI
hardware is CS5535/CS5536 based, and the device that it grabs is handled by
the cs5535-mfd driver. This converts the SCx200 driver to use a
platform_driver rather than the previous PCI hackery.
The driver used to manually track the iface list (via linked list); now it
only does this for ISA devices. PCI ifaces are handled through standard
driver model lists.
It's unclear what happens in case of errors in the old ISA code; rather than
pretending the code actually cares, I've dropped the (implicit) ignorance
of return values and marked it with a comment.
Signed-off-by: Andres Salomon <dilinger@queued.net>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/scx200_acb.c | 200 |
1 files changed, 80 insertions, 120 deletions
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 53fab518b3da..986e5f62debe 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/init.h> | 29 | #include <linux/init.h> |
30 | #include <linux/i2c.h> | 30 | #include <linux/i2c.h> |
31 | #include <linux/pci.h> | 31 | #include <linux/pci.h> |
32 | #include <linux/platform_device.h> | ||
32 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
33 | #include <linux/mutex.h> | 34 | #include <linux/mutex.h> |
34 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
@@ -40,6 +41,7 @@ | |||
40 | 41 | ||
41 | MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>"); | 42 | MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>"); |
42 | MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver"); | 43 | MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver"); |
44 | MODULE_ALIAS("platform:cs5535-smb"); | ||
43 | MODULE_LICENSE("GPL"); | 45 | MODULE_LICENSE("GPL"); |
44 | 46 | ||
45 | #define MAX_DEVICES 4 | 47 | #define MAX_DEVICES 4 |
@@ -84,10 +86,6 @@ struct scx200_acb_iface { | |||
84 | u8 *ptr; | 86 | u8 *ptr; |
85 | char needs_reset; | 87 | char needs_reset; |
86 | unsigned len; | 88 | unsigned len; |
87 | |||
88 | /* PCI device info */ | ||
89 | struct pci_dev *pdev; | ||
90 | int bar; | ||
91 | }; | 89 | }; |
92 | 90 | ||
93 | /* Register Definitions */ | 91 | /* Register Definitions */ |
@@ -391,7 +389,7 @@ static const struct i2c_algorithm scx200_acb_algorithm = { | |||
391 | static struct scx200_acb_iface *scx200_acb_list; | 389 | static struct scx200_acb_iface *scx200_acb_list; |
392 | static DEFINE_MUTEX(scx200_acb_list_mutex); | 390 | static DEFINE_MUTEX(scx200_acb_list_mutex); |
393 | 391 | ||
394 | static __init int scx200_acb_probe(struct scx200_acb_iface *iface) | 392 | static __devinit int scx200_acb_probe(struct scx200_acb_iface *iface) |
395 | { | 393 | { |
396 | u8 val; | 394 | u8 val; |
397 | 395 | ||
@@ -427,7 +425,7 @@ static __init int scx200_acb_probe(struct scx200_acb_iface *iface) | |||
427 | return 0; | 425 | return 0; |
428 | } | 426 | } |
429 | 427 | ||
430 | static __init struct scx200_acb_iface *scx200_create_iface(const char *text, | 428 | static __devinit struct scx200_acb_iface *scx200_create_iface(const char *text, |
431 | struct device *dev, int index) | 429 | struct device *dev, int index) |
432 | { | 430 | { |
433 | struct scx200_acb_iface *iface; | 431 | struct scx200_acb_iface *iface; |
@@ -452,7 +450,7 @@ static __init struct scx200_acb_iface *scx200_create_iface(const char *text, | |||
452 | return iface; | 450 | return iface; |
453 | } | 451 | } |
454 | 452 | ||
455 | static int __init scx200_acb_create(struct scx200_acb_iface *iface) | 453 | static int __devinit scx200_acb_create(struct scx200_acb_iface *iface) |
456 | { | 454 | { |
457 | struct i2c_adapter *adapter; | 455 | struct i2c_adapter *adapter; |
458 | int rc; | 456 | int rc; |
@@ -472,183 +470,145 @@ static int __init scx200_acb_create(struct scx200_acb_iface *iface) | |||
472 | return -ENODEV; | 470 | return -ENODEV; |
473 | } | 471 | } |
474 | 472 | ||
475 | mutex_lock(&scx200_acb_list_mutex); | 473 | if (!adapter->dev.parent) { |
476 | iface->next = scx200_acb_list; | 474 | /* If there's no dev, we're tracking (ISA) ifaces manually */ |
477 | scx200_acb_list = iface; | 475 | mutex_lock(&scx200_acb_list_mutex); |
478 | mutex_unlock(&scx200_acb_list_mutex); | 476 | iface->next = scx200_acb_list; |
477 | scx200_acb_list = iface; | ||
478 | mutex_unlock(&scx200_acb_list_mutex); | ||
479 | } | ||
479 | 480 | ||
480 | return 0; | 481 | return 0; |
481 | } | 482 | } |
482 | 483 | ||
483 | static __init int scx200_create_pci(const char *text, struct pci_dev *pdev, | 484 | static struct scx200_acb_iface * __devinit scx200_create_dev(const char *text, |
484 | int bar) | 485 | unsigned long base, int index, struct device *dev) |
485 | { | 486 | { |
486 | struct scx200_acb_iface *iface; | 487 | struct scx200_acb_iface *iface; |
487 | int rc; | 488 | int rc; |
488 | 489 | ||
489 | iface = scx200_create_iface(text, &pdev->dev, 0); | 490 | iface = scx200_create_iface(text, dev, index); |
490 | 491 | ||
491 | if (iface == NULL) | 492 | if (iface == NULL) |
492 | return -ENOMEM; | 493 | return NULL; |
493 | |||
494 | iface->pdev = pdev; | ||
495 | iface->bar = bar; | ||
496 | |||
497 | rc = pci_enable_device_io(iface->pdev); | ||
498 | if (rc) | ||
499 | goto errout_free; | ||
500 | 494 | ||
501 | rc = pci_request_region(iface->pdev, iface->bar, iface->adapter.name); | 495 | if (!request_region(base, 8, iface->adapter.name)) { |
502 | if (rc) { | 496 | printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n", |
503 | printk(KERN_ERR NAME ": can't allocate PCI BAR %d\n", | 497 | base, base + 8 - 1); |
504 | iface->bar); | ||
505 | goto errout_free; | 498 | goto errout_free; |
506 | } | 499 | } |
507 | 500 | ||
508 | iface->base = pci_resource_start(iface->pdev, iface->bar); | 501 | iface->base = base; |
509 | rc = scx200_acb_create(iface); | 502 | rc = scx200_acb_create(iface); |
510 | 503 | ||
511 | if (rc == 0) | 504 | if (rc == 0) |
512 | return 0; | 505 | return iface; |
513 | 506 | ||
514 | pci_release_region(iface->pdev, iface->bar); | 507 | release_region(base, 8); |
515 | pci_dev_put(iface->pdev); | ||
516 | errout_free: | 508 | errout_free: |
517 | kfree(iface); | 509 | kfree(iface); |
518 | return rc; | 510 | return NULL; |
519 | } | 511 | } |
520 | 512 | ||
521 | static int __init scx200_create_isa(const char *text, unsigned long base, | 513 | static int __devinit scx200_probe(struct platform_device *pdev) |
522 | int index) | ||
523 | { | 514 | { |
524 | struct scx200_acb_iface *iface; | 515 | struct scx200_acb_iface *iface; |
525 | int rc; | 516 | struct resource *res; |
526 | |||
527 | iface = scx200_create_iface(text, NULL, index); | ||
528 | |||
529 | if (iface == NULL) | ||
530 | return -ENOMEM; | ||
531 | 517 | ||
532 | if (!request_region(base, 8, iface->adapter.name)) { | 518 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
533 | printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n", | 519 | if (!res) { |
534 | base, base + 8 - 1); | 520 | dev_err(&pdev->dev, "can't fetch device resource info\n"); |
535 | rc = -EBUSY; | 521 | return -ENODEV; |
536 | goto errout_free; | ||
537 | } | 522 | } |
538 | 523 | ||
539 | iface->base = base; | 524 | iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev); |
540 | rc = scx200_acb_create(iface); | 525 | if (!iface) |
526 | return -EIO; | ||
541 | 527 | ||
542 | if (rc == 0) | 528 | dev_info(&pdev->dev, "SCx200 device '%s' registered\n", |
543 | return 0; | 529 | iface->adapter.name); |
530 | platform_set_drvdata(pdev, iface); | ||
544 | 531 | ||
545 | release_region(base, 8); | 532 | return 0; |
546 | errout_free: | ||
547 | kfree(iface); | ||
548 | return rc; | ||
549 | } | 533 | } |
550 | 534 | ||
551 | /* Driver data is an index into the scx200_data array that indicates | 535 | static void __devexit scx200_cleanup_iface(struct scx200_acb_iface *iface) |
552 | * the name and the BAR where the I/O address resource is located. ISA | 536 | { |
553 | * devices are flagged with a bar value of -1 */ | 537 | i2c_del_adapter(&iface->adapter); |
554 | 538 | release_region(iface->base, 8); | |
555 | static const struct pci_device_id scx200_pci[] __initconst = { | 539 | kfree(iface); |
556 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE), | 540 | } |
557 | .driver_data = 0 }, | ||
558 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE), | ||
559 | .driver_data = 0 }, | ||
560 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA), | ||
561 | .driver_data = 1 }, | ||
562 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA), | ||
563 | .driver_data = 2 }, | ||
564 | { 0, } | ||
565 | }; | ||
566 | |||
567 | static struct { | ||
568 | const char *name; | ||
569 | int bar; | ||
570 | } scx200_data[] = { | ||
571 | { "SCx200", -1 }, | ||
572 | { "CS5535", 0 }, | ||
573 | { "CS5536", 0 } | ||
574 | }; | ||
575 | 541 | ||
576 | static __init int scx200_scan_pci(void) | 542 | static int __devexit scx200_remove(struct platform_device *pdev) |
577 | { | 543 | { |
578 | int data, dev; | 544 | struct scx200_acb_iface *iface; |
579 | int rc = -ENODEV; | ||
580 | struct pci_dev *pdev; | ||
581 | 545 | ||
582 | for(dev = 0; dev < ARRAY_SIZE(scx200_pci); dev++) { | 546 | iface = platform_get_drvdata(pdev); |
583 | pdev = pci_get_device(scx200_pci[dev].vendor, | 547 | platform_set_drvdata(pdev, NULL); |
584 | scx200_pci[dev].device, NULL); | 548 | scx200_cleanup_iface(iface); |
585 | 549 | ||
586 | if (pdev == NULL) | 550 | return 0; |
587 | continue; | 551 | } |
588 | 552 | ||
589 | data = scx200_pci[dev].driver_data; | 553 | static struct platform_driver scx200_pci_drv = { |
554 | .driver = { | ||
555 | .name = "cs5535-smb", | ||
556 | .owner = THIS_MODULE, | ||
557 | }, | ||
558 | .probe = scx200_probe, | ||
559 | .remove = __devexit_p(scx200_remove), | ||
560 | }; | ||
590 | 561 | ||
591 | /* if .bar is greater or equal to zero, this is a | 562 | static const struct pci_device_id scx200_isa[] __initconst = { |
592 | * PCI device - otherwise, we assume | 563 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) }, |
593 | that the ports are ISA based | 564 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) }, |
594 | */ | 565 | { 0, } |
566 | }; | ||
595 | 567 | ||
596 | if (scx200_data[data].bar >= 0) | 568 | static __init void scx200_scan_isa(void) |
597 | rc = scx200_create_pci(scx200_data[data].name, pdev, | 569 | { |
598 | scx200_data[data].bar); | 570 | int i; |
599 | else { | ||
600 | int i; | ||
601 | 571 | ||
602 | pci_dev_put(pdev); | 572 | if (!pci_dev_present(scx200_isa)) |
603 | for (i = 0; i < MAX_DEVICES; ++i) { | 573 | return; |
604 | if (base[i] == 0) | ||
605 | continue; | ||
606 | 574 | ||
607 | rc = scx200_create_isa(scx200_data[data].name, | 575 | for (i = 0; i < MAX_DEVICES; ++i) { |
608 | base[i], | 576 | if (base[i] == 0) |
609 | i); | 577 | continue; |
610 | } | ||
611 | } | ||
612 | 578 | ||
613 | break; | 579 | /* XXX: should we care about failures? */ |
580 | scx200_create_dev("SCx200", base[i], i, NULL); | ||
614 | } | 581 | } |
615 | |||
616 | return rc; | ||
617 | } | 582 | } |
618 | 583 | ||
619 | static int __init scx200_acb_init(void) | 584 | static int __init scx200_acb_init(void) |
620 | { | 585 | { |
621 | int rc; | ||
622 | |||
623 | pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); | 586 | pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); |
624 | 587 | ||
625 | rc = scx200_scan_pci(); | 588 | /* First scan for ISA-based devices */ |
589 | scx200_scan_isa(); /* XXX: should we care about errors? */ | ||
626 | 590 | ||
627 | /* If at least one bus was created, init must succeed */ | 591 | /* If at least one bus was created, init must succeed */ |
628 | if (scx200_acb_list) | 592 | if (scx200_acb_list) |
629 | return 0; | 593 | return 0; |
630 | return rc; | 594 | |
595 | /* No ISA devices; register the platform driver for PCI-based devices */ | ||
596 | return platform_driver_register(&scx200_pci_drv); | ||
631 | } | 597 | } |
632 | 598 | ||
633 | static void __exit scx200_acb_cleanup(void) | 599 | static void __exit scx200_acb_cleanup(void) |
634 | { | 600 | { |
635 | struct scx200_acb_iface *iface; | 601 | struct scx200_acb_iface *iface; |
636 | 602 | ||
603 | platform_driver_unregister(&scx200_pci_drv); | ||
604 | |||
637 | mutex_lock(&scx200_acb_list_mutex); | 605 | mutex_lock(&scx200_acb_list_mutex); |
638 | while ((iface = scx200_acb_list) != NULL) { | 606 | while ((iface = scx200_acb_list) != NULL) { |
639 | scx200_acb_list = iface->next; | 607 | scx200_acb_list = iface->next; |
640 | mutex_unlock(&scx200_acb_list_mutex); | 608 | mutex_unlock(&scx200_acb_list_mutex); |
641 | 609 | ||
642 | i2c_del_adapter(&iface->adapter); | 610 | scx200_cleanup_iface(iface); |
643 | |||
644 | if (iface->pdev) { | ||
645 | pci_release_region(iface->pdev, iface->bar); | ||
646 | pci_dev_put(iface->pdev); | ||
647 | } | ||
648 | else | ||
649 | release_region(iface->base, 8); | ||
650 | 611 | ||
651 | kfree(iface); | ||
652 | mutex_lock(&scx200_acb_list_mutex); | 612 | mutex_lock(&scx200_acb_list_mutex); |
653 | } | 613 | } |
654 | mutex_unlock(&scx200_acb_list_mutex); | 614 | mutex_unlock(&scx200_acb_list_mutex); |