From 1c2a49f61785ebbcbfb481a2aab659020f0457f7 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 4 Mar 2010 20:06:06 +0300 Subject: [PATCH] ahci: Add platform driver This can be used for AHCI-compatible interfaces implemented inside System-On-Chip solutions, or AHCI devices connected via localbus. Signed-off-by: Anton Vorontsov Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 8 ++ drivers/ata/Makefile | 1 + drivers/ata/ahci_platform.c | 191 ++++++++++++++++++++++++++++++++++++++++++ include/linux/ahci_platform.h | 29 +++++++ 4 files changed, 229 insertions(+) create mode 100644 drivers/ata/ahci_platform.c create mode 100644 include/linux/ahci_platform.h diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 01c52c4..cbadb9f 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -65,6 +65,14 @@ config SATA_AHCI If unsure, say N. +config SATA_AHCI_PLATFORM + tristate "Platform AHCI SATA support" + help + This option enables support for Platform AHCI Serial ATA + controllers. + + If unsure, say N. + config SATA_SIL24 tristate "Silicon Image 3124/3132 SATA support" depends on PCI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 20c5251..d0a93c4 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_ATA) += libata.o obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o +obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o obj-$(CONFIG_SATA_SVW) += sata_svw.o obj-$(CONFIG_ATA_PIIX) += ata_piix.o obj-$(CONFIG_SATA_PROMISE) += sata_promise.o diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c new file mode 100644 index 0000000..42cdd73 --- /dev/null +++ b/drivers/ata/ahci_platform.c @@ -0,0 +1,191 @@ +/* + * AHCI SATA platform driver + * + * Copyright 2004-2005 Red Hat, Inc. + * Jeff Garzik + * Copyright 2010 MontaVista Software, LLC. + * Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ahci.h" + +static int __init ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev->platform_data; + struct ata_port_info pi = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, + }; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; + struct ata_host *host; + struct resource *mem; + int irq; + int n_ports; + int i; + int rc; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(dev, "no mmio space\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } + + if (pdata && pdata->init) { + rc = pdata->init(dev); + if (rc) + return rc; + } + + if (pdata && pdata->ata_port_info) + pi = *pdata->ata_port_info; + + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto err0; + } + + hpriv->flags |= (unsigned long)pi.private_data; + + hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); + if (!hpriv->mmio) { + dev_err(dev, "can't map %pR\n", mem); + rc = -ENOMEM; + goto err0; + } + + ahci_save_initial_config(dev, hpriv, + pdata ? pdata->force_port_map : 0, + pdata ? pdata->mask_port_map : 0); + + /* prepare host */ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) { + rc = -ENOMEM; + goto err0; + } + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", mem); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set initial link pm policy */ + ap->pm_policy = NOT_AVAILABLE; + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = ahci_em_messages; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + rc = ahci_reset_controller(host); + if (rc) + goto err0; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, + &ahci_sht); + if (rc) + goto err0; + + return 0; +err0: + if (pdata && pdata->exit) + pdata->exit(dev); + return rc; +} + +static int __devexit ahci_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_platform_data *pdata = dev->platform_data; + struct ata_host *host = dev_get_drvdata(dev); + + ata_host_detach(host); + + if (pdata && pdata->exit) + pdata->exit(dev); + + return 0; +} + +static struct platform_driver ahci_driver = { + .probe = ahci_probe, + .remove = __devexit_p(ahci_remove), + .driver = { + .name = "ahci", + .owner = THIS_MODULE, + }, +}; + +static int __init ahci_init(void) +{ + return platform_driver_probe(&ahci_driver, ahci_probe); +} +module_init(ahci_init); + +static void __exit ahci_exit(void) +{ + platform_driver_unregister(&ahci_driver); +} +module_exit(ahci_exit); + +MODULE_DESCRIPTION("AHCI SATA platform driver"); +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ahci"); diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h new file mode 100644 index 0000000..f7dd576 --- /dev/null +++ b/include/linux/ahci_platform.h @@ -0,0 +1,29 @@ +/* + * AHCI SATA platform driver + * + * Copyright 2004-2005 Red Hat, Inc. + * Jeff Garzik + * Copyright 2010 MontaVista Software, LLC. + * Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ + +#ifndef _AHCI_PLATFORM_H +#define _AHCI_PLATFORM_H + +struct device; +struct ata_port_info; + +struct ahci_platform_data { + int (*init)(struct device *dev); + void (*exit)(struct device *dev); + const struct ata_port_info *ata_port_info; + unsigned int force_port_map; + unsigned int mask_port_map; +}; + +#endif /* _AHCI_PLATFORM_H */ -- 1.8.2.3