mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
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>
This commit is contained in:
parent
419cdc54ea
commit
de8255ccd2
@ -29,6 +29,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
@ -40,6 +41,7 @@
|
||||
|
||||
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
|
||||
MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver");
|
||||
MODULE_ALIAS("platform:cs5535-smb");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define MAX_DEVICES 4
|
||||
@ -84,10 +86,6 @@ struct scx200_acb_iface {
|
||||
u8 *ptr;
|
||||
char needs_reset;
|
||||
unsigned len;
|
||||
|
||||
/* PCI device info */
|
||||
struct pci_dev *pdev;
|
||||
int bar;
|
||||
};
|
||||
|
||||
/* Register Definitions */
|
||||
@ -391,7 +389,7 @@ static const struct i2c_algorithm scx200_acb_algorithm = {
|
||||
static struct scx200_acb_iface *scx200_acb_list;
|
||||
static DEFINE_MUTEX(scx200_acb_list_mutex);
|
||||
|
||||
static __init int scx200_acb_probe(struct scx200_acb_iface *iface)
|
||||
static __devinit int scx200_acb_probe(struct scx200_acb_iface *iface)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
@ -427,7 +425,7 @@ static __init int scx200_acb_probe(struct scx200_acb_iface *iface)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init struct scx200_acb_iface *scx200_create_iface(const char *text,
|
||||
static __devinit struct scx200_acb_iface *scx200_create_iface(const char *text,
|
||||
struct device *dev, int index)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
@ -452,7 +450,7 @@ static __init struct scx200_acb_iface *scx200_create_iface(const char *text,
|
||||
return iface;
|
||||
}
|
||||
|
||||
static int __init scx200_acb_create(struct scx200_acb_iface *iface)
|
||||
static int __devinit scx200_acb_create(struct scx200_acb_iface *iface)
|
||||
{
|
||||
struct i2c_adapter *adapter;
|
||||
int rc;
|
||||
@ -472,67 +470,31 @@ static int __init scx200_acb_create(struct scx200_acb_iface *iface)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
iface->next = scx200_acb_list;
|
||||
scx200_acb_list = iface;
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
if (!adapter->dev.parent) {
|
||||
/* If there's no dev, we're tracking (ISA) ifaces manually */
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
iface->next = scx200_acb_list;
|
||||
scx200_acb_list = iface;
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init int scx200_create_pci(const char *text, struct pci_dev *pdev,
|
||||
int bar)
|
||||
static struct scx200_acb_iface * __devinit scx200_create_dev(const char *text,
|
||||
unsigned long base, int index, struct device *dev)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
int rc;
|
||||
|
||||
iface = scx200_create_iface(text, &pdev->dev, 0);
|
||||
iface = scx200_create_iface(text, dev, index);
|
||||
|
||||
if (iface == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
iface->pdev = pdev;
|
||||
iface->bar = bar;
|
||||
|
||||
rc = pci_enable_device_io(iface->pdev);
|
||||
if (rc)
|
||||
goto errout_free;
|
||||
|
||||
rc = pci_request_region(iface->pdev, iface->bar, iface->adapter.name);
|
||||
if (rc) {
|
||||
printk(KERN_ERR NAME ": can't allocate PCI BAR %d\n",
|
||||
iface->bar);
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
iface->base = pci_resource_start(iface->pdev, iface->bar);
|
||||
rc = scx200_acb_create(iface);
|
||||
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
|
||||
pci_release_region(iface->pdev, iface->bar);
|
||||
pci_dev_put(iface->pdev);
|
||||
errout_free:
|
||||
kfree(iface);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init scx200_create_isa(const char *text, unsigned long base,
|
||||
int index)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
int rc;
|
||||
|
||||
iface = scx200_create_iface(text, NULL, index);
|
||||
|
||||
if (iface == NULL)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
if (!request_region(base, 8, iface->adapter.name)) {
|
||||
printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n",
|
||||
base, base + 8 - 1);
|
||||
rc = -EBUSY;
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
@ -540,115 +502,113 @@ static int __init scx200_create_isa(const char *text, unsigned long base,
|
||||
rc = scx200_acb_create(iface);
|
||||
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
return iface;
|
||||
|
||||
release_region(base, 8);
|
||||
errout_free:
|
||||
kfree(iface);
|
||||
return rc;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Driver data is an index into the scx200_data array that indicates
|
||||
* the name and the BAR where the I/O address resource is located. ISA
|
||||
* devices are flagged with a bar value of -1 */
|
||||
static int __devinit scx200_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
struct resource *res;
|
||||
|
||||
static const struct pci_device_id scx200_pci[] __initconst = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE),
|
||||
.driver_data = 0 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA),
|
||||
.driver_data = 1 },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA),
|
||||
.driver_data = 2 },
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't fetch device resource info\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev);
|
||||
if (!iface)
|
||||
return -EIO;
|
||||
|
||||
dev_info(&pdev->dev, "SCx200 device '%s' registered\n",
|
||||
iface->adapter.name);
|
||||
platform_set_drvdata(pdev, iface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit scx200_cleanup_iface(struct scx200_acb_iface *iface)
|
||||
{
|
||||
i2c_del_adapter(&iface->adapter);
|
||||
release_region(iface->base, 8);
|
||||
kfree(iface);
|
||||
}
|
||||
|
||||
static int __devexit scx200_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
|
||||
iface = platform_get_drvdata(pdev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
scx200_cleanup_iface(iface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver scx200_pci_drv = {
|
||||
.driver = {
|
||||
.name = "cs5535-smb",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = scx200_probe,
|
||||
.remove = __devexit_p(scx200_remove),
|
||||
};
|
||||
|
||||
static const struct pci_device_id scx200_isa[] __initconst = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int bar;
|
||||
} scx200_data[] = {
|
||||
{ "SCx200", -1 },
|
||||
{ "CS5535", 0 },
|
||||
{ "CS5536", 0 }
|
||||
};
|
||||
|
||||
static __init int scx200_scan_pci(void)
|
||||
static __init void scx200_scan_isa(void)
|
||||
{
|
||||
int data, dev;
|
||||
int rc = -ENODEV;
|
||||
struct pci_dev *pdev;
|
||||
int i;
|
||||
|
||||
for(dev = 0; dev < ARRAY_SIZE(scx200_pci); dev++) {
|
||||
pdev = pci_get_device(scx200_pci[dev].vendor,
|
||||
scx200_pci[dev].device, NULL);
|
||||
if (!pci_dev_present(scx200_isa))
|
||||
return;
|
||||
|
||||
if (pdev == NULL)
|
||||
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||
if (base[i] == 0)
|
||||
continue;
|
||||
|
||||
data = scx200_pci[dev].driver_data;
|
||||
|
||||
/* if .bar is greater or equal to zero, this is a
|
||||
* PCI device - otherwise, we assume
|
||||
that the ports are ISA based
|
||||
*/
|
||||
|
||||
if (scx200_data[data].bar >= 0)
|
||||
rc = scx200_create_pci(scx200_data[data].name, pdev,
|
||||
scx200_data[data].bar);
|
||||
else {
|
||||
int i;
|
||||
|
||||
pci_dev_put(pdev);
|
||||
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||
if (base[i] == 0)
|
||||
continue;
|
||||
|
||||
rc = scx200_create_isa(scx200_data[data].name,
|
||||
base[i],
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
/* XXX: should we care about failures? */
|
||||
scx200_create_dev("SCx200", base[i], i, NULL);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init scx200_acb_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
|
||||
|
||||
rc = scx200_scan_pci();
|
||||
/* First scan for ISA-based devices */
|
||||
scx200_scan_isa(); /* XXX: should we care about errors? */
|
||||
|
||||
/* If at least one bus was created, init must succeed */
|
||||
if (scx200_acb_list)
|
||||
return 0;
|
||||
return rc;
|
||||
|
||||
/* No ISA devices; register the platform driver for PCI-based devices */
|
||||
return platform_driver_register(&scx200_pci_drv);
|
||||
}
|
||||
|
||||
static void __exit scx200_acb_cleanup(void)
|
||||
{
|
||||
struct scx200_acb_iface *iface;
|
||||
|
||||
platform_driver_unregister(&scx200_pci_drv);
|
||||
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
while ((iface = scx200_acb_list) != NULL) {
|
||||
scx200_acb_list = iface->next;
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
|
||||
i2c_del_adapter(&iface->adapter);
|
||||
scx200_cleanup_iface(iface);
|
||||
|
||||
if (iface->pdev) {
|
||||
pci_release_region(iface->pdev, iface->bar);
|
||||
pci_dev_put(iface->pdev);
|
||||
}
|
||||
else
|
||||
release_region(iface->base, 8);
|
||||
|
||||
kfree(iface);
|
||||
mutex_lock(&scx200_acb_list_mutex);
|
||||
}
|
||||
mutex_unlock(&scx200_acb_list_mutex);
|
||||
|
Loading…
Reference in New Issue
Block a user