linux/drivers/ata/ahci_platform.c
<<
>>
Prefs
   1/*
   2 * AHCI SATA platform driver
   3 *
   4 * Copyright 2004-2005  Red Hat, Inc.
   5 *   Jeff Garzik <jgarzik@pobox.com>
   6 * Copyright 2010  MontaVista Software, LLC.
   7 *   Anton Vorontsov <avorontsov@ru.mvista.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2, or (at your option)
  12 * any later version.
  13 */
  14
  15#include <linux/kernel.h>
  16#include <linux/gfp.h>
  17#include <linux/module.h>
  18#include <linux/pm.h>
  19#include <linux/init.h>
  20#include <linux/interrupt.h>
  21#include <linux/device.h>
  22#include <linux/platform_device.h>
  23#include <linux/libata.h>
  24#include <linux/ahci_platform.h>
  25#include "ahci.h"
  26
  27enum ahci_type {
  28        AHCI,           /* standard platform ahci */
  29        IMX53_AHCI,     /* ahci on i.mx53 */
  30        STRICT_AHCI,    /* delayed DMA engine start */
  31};
  32
  33static struct platform_device_id ahci_devtype[] = {
  34        {
  35                .name = "ahci",
  36                .driver_data = AHCI,
  37        }, {
  38                .name = "imx53-ahci",
  39                .driver_data = IMX53_AHCI,
  40        }, {
  41                .name = "strict-ahci",
  42                .driver_data = STRICT_AHCI,
  43        }, {
  44                /* sentinel */
  45        }
  46};
  47MODULE_DEVICE_TABLE(platform, ahci_devtype);
  48
  49
  50static const struct ata_port_info ahci_port_info[] = {
  51        /* by features */
  52        [AHCI] = {
  53                .flags          = AHCI_FLAG_COMMON,
  54                .pio_mask       = ATA_PIO4,
  55                .udma_mask      = ATA_UDMA6,
  56                .port_ops       = &ahci_ops,
  57        },
  58        [IMX53_AHCI] = {
  59                .flags          = AHCI_FLAG_COMMON,
  60                .pio_mask       = ATA_PIO4,
  61                .udma_mask      = ATA_UDMA6,
  62                .port_ops       = &ahci_pmp_retry_srst_ops,
  63        },
  64        [STRICT_AHCI] = {
  65                AHCI_HFLAGS     (AHCI_HFLAG_DELAY_ENGINE),
  66                .flags          = AHCI_FLAG_COMMON,
  67                .pio_mask       = ATA_PIO4,
  68                .udma_mask      = ATA_UDMA6,
  69                .port_ops       = &ahci_ops,
  70        },
  71};
  72
  73static struct scsi_host_template ahci_platform_sht = {
  74        AHCI_SHT("ahci_platform"),
  75};
  76
  77static int __init ahci_probe(struct platform_device *pdev)
  78{
  79        struct device *dev = &pdev->dev;
  80        struct ahci_platform_data *pdata = dev_get_platdata(dev);
  81        const struct platform_device_id *id = platform_get_device_id(pdev);
  82        struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
  83        const struct ata_port_info *ppi[] = { &pi, NULL };
  84        struct ahci_host_priv *hpriv;
  85        struct ata_host *host;
  86        struct resource *mem;
  87        int irq;
  88        int n_ports;
  89        int i;
  90        int rc;
  91
  92        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  93        if (!mem) {
  94                dev_err(dev, "no mmio space\n");
  95                return -EINVAL;
  96        }
  97
  98        irq = platform_get_irq(pdev, 0);
  99        if (irq <= 0) {
 100                dev_err(dev, "no irq\n");
 101                return -EINVAL;
 102        }
 103
 104        if (pdata && pdata->ata_port_info)
 105                pi = *pdata->ata_port_info;
 106
 107        hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
 108        if (!hpriv) {
 109                dev_err(dev, "can't alloc ahci_host_priv\n");
 110                return -ENOMEM;
 111        }
 112
 113        hpriv->flags |= (unsigned long)pi.private_data;
 114
 115        hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
 116        if (!hpriv->mmio) {
 117                dev_err(dev, "can't map %pR\n", mem);
 118                return -ENOMEM;
 119        }
 120
 121        /*
 122         * Some platforms might need to prepare for mmio region access,
 123         * which could be done in the following init call. So, the mmio
 124         * region shouldn't be accessed before init (if provided) has
 125         * returned successfully.
 126         */
 127        if (pdata && pdata->init) {
 128                rc = pdata->init(dev, hpriv->mmio);
 129                if (rc)
 130                        return rc;
 131        }
 132
 133        ahci_save_initial_config(dev, hpriv,
 134                pdata ? pdata->force_port_map : 0,
 135                pdata ? pdata->mask_port_map  : 0);
 136
 137        /* prepare host */
 138        if (hpriv->cap & HOST_CAP_NCQ)
 139                pi.flags |= ATA_FLAG_NCQ;
 140
 141        if (hpriv->cap & HOST_CAP_PMP)
 142                pi.flags |= ATA_FLAG_PMP;
 143
 144        ahci_set_em_messages(hpriv, &pi);
 145
 146        /* CAP.NP sometimes indicate the index of the last enabled
 147         * port, at other times, that of the last possible port, so
 148         * determining the maximum port number requires looking at
 149         * both CAP.NP and port_map.
 150         */
 151        n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
 152
 153        host = ata_host_alloc_pinfo(dev, ppi, n_ports);
 154        if (!host) {
 155                rc = -ENOMEM;
 156                goto err0;
 157        }
 158
 159        host->private_data = hpriv;
 160
 161        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
 162                host->flags |= ATA_HOST_PARALLEL_SCAN;
 163        else
 164                printk(KERN_INFO "ahci: SSS flag set, parallel bus scan disabled\n");
 165
 166        if (pi.flags & ATA_FLAG_EM)
 167                ahci_reset_em(host);
 168
 169        for (i = 0; i < host->n_ports; i++) {
 170                struct ata_port *ap = host->ports[i];
 171
 172                ata_port_desc(ap, "mmio %pR", mem);
 173                ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
 174
 175                /* set enclosure management message type */
 176                if (ap->flags & ATA_FLAG_EM)
 177                        ap->em_message_type = hpriv->em_msg_type;
 178
 179                /* disabled/not-implemented port */
 180                if (!(hpriv->port_map & (1 << i)))
 181                        ap->ops = &ata_dummy_port_ops;
 182        }
 183
 184        rc = ahci_reset_controller(host);
 185        if (rc)
 186                goto err0;
 187
 188        ahci_init_controller(host);
 189        ahci_print_info(host, "platform");
 190
 191        rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
 192                               &ahci_platform_sht);
 193        if (rc)
 194                goto err0;
 195
 196        return 0;
 197err0:
 198        if (pdata && pdata->exit)
 199                pdata->exit(dev);
 200        return rc;
 201}
 202
 203static int __devexit ahci_remove(struct platform_device *pdev)
 204{
 205        struct device *dev = &pdev->dev;
 206        struct ahci_platform_data *pdata = dev_get_platdata(dev);
 207        struct ata_host *host = dev_get_drvdata(dev);
 208
 209        ata_host_detach(host);
 210
 211        if (pdata && pdata->exit)
 212                pdata->exit(dev);
 213
 214        return 0;
 215}
 216
 217#ifdef CONFIG_PM
 218static int ahci_suspend(struct device *dev)
 219{
 220        struct ahci_platform_data *pdata = dev_get_platdata(dev);
 221        struct ata_host *host = dev_get_drvdata(dev);
 222        struct ahci_host_priv *hpriv = host->private_data;
 223        void __iomem *mmio = hpriv->mmio;
 224        u32 ctl;
 225        int rc;
 226
 227        if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
 228                dev_err(dev, "firmware update required for suspend/resume\n");
 229                return -EIO;
 230        }
 231
 232        /*
 233         * AHCI spec rev1.1 section 8.3.3:
 234         * Software must disable interrupts prior to requesting a
 235         * transition of the HBA to D3 state.
 236         */
 237        ctl = readl(mmio + HOST_CTL);
 238        ctl &= ~HOST_IRQ_EN;
 239        writel(ctl, mmio + HOST_CTL);
 240        readl(mmio + HOST_CTL); /* flush */
 241
 242        rc = ata_host_suspend(host, PMSG_SUSPEND);
 243        if (rc)
 244                return rc;
 245
 246        if (pdata && pdata->suspend)
 247                return pdata->suspend(dev);
 248        return 0;
 249}
 250
 251static int ahci_resume(struct device *dev)
 252{
 253        struct ahci_platform_data *pdata = dev_get_platdata(dev);
 254        struct ata_host *host = dev_get_drvdata(dev);
 255        int rc;
 256
 257        if (pdata && pdata->resume) {
 258                rc = pdata->resume(dev);
 259                if (rc)
 260                        return rc;
 261        }
 262
 263        if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
 264                rc = ahci_reset_controller(host);
 265                if (rc)
 266                        return rc;
 267
 268                ahci_init_controller(host);
 269        }
 270
 271        ata_host_resume(host);
 272
 273        return 0;
 274}
 275#endif
 276
 277SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
 278
 279static const struct of_device_id ahci_of_match[] = {
 280        { .compatible = "calxeda,hb-ahci", },
 281        { .compatible = "snps,spear-ahci", },
 282        {},
 283};
 284MODULE_DEVICE_TABLE(of, ahci_of_match);
 285
 286static struct platform_driver ahci_driver = {
 287        .remove = __devexit_p(ahci_remove),
 288        .driver = {
 289                .name = "ahci",
 290                .owner = THIS_MODULE,
 291                .of_match_table = ahci_of_match,
 292                .pm = &ahci_pm_ops,
 293        },
 294        .id_table       = ahci_devtype,
 295};
 296
 297static int __init ahci_init(void)
 298{
 299        return platform_driver_probe(&ahci_driver, ahci_probe);
 300}
 301module_init(ahci_init);
 302
 303static void __exit ahci_exit(void)
 304{
 305        platform_driver_unregister(&ahci_driver);
 306}
 307module_exit(ahci_exit);
 308
 309MODULE_DESCRIPTION("AHCI SATA platform driver");
 310MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
 311MODULE_LICENSE("GPL");
 312MODULE_ALIAS("platform:ahci");
 313
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.