root/drivers/ata/ahci_mvebu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ahci_mvebu_mbus_config
  2. ahci_mvebu_regret_option
  3. ahci_mvebu_armada_380_config
  4. ahci_mvebu_armada_3700_config
  5. ahci_mvebu_stop_engine
  6. ahci_mvebu_suspend
  7. ahci_mvebu_resume
  8. ahci_mvebu_probe

   1 /*
   2  * AHCI glue platform driver for Marvell EBU SOCs
   3  *
   4  * Copyright (C) 2014 Marvell
   5  *
   6  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   7  * Marcin Wojtas <mw@semihalf.com>
   8  *
   9  * This file is licensed under the terms of the GNU General Public
  10  * License version 2.  This program is licensed "as is" without any
  11  * warranty of any kind, whether express or implied.
  12  */
  13 
  14 #include <linux/ahci_platform.h>
  15 #include <linux/kernel.h>
  16 #include <linux/mbus.h>
  17 #include <linux/module.h>
  18 #include <linux/of_device.h>
  19 #include <linux/platform_device.h>
  20 #include "ahci.h"
  21 
  22 #define DRV_NAME "ahci-mvebu"
  23 
  24 #define AHCI_VENDOR_SPECIFIC_0_ADDR  0xa0
  25 #define AHCI_VENDOR_SPECIFIC_0_DATA  0xa4
  26 
  27 #define AHCI_WINDOW_CTRL(win)   (0x60 + ((win) << 4))
  28 #define AHCI_WINDOW_BASE(win)   (0x64 + ((win) << 4))
  29 #define AHCI_WINDOW_SIZE(win)   (0x68 + ((win) << 4))
  30 
  31 struct ahci_mvebu_plat_data {
  32         int (*plat_config)(struct ahci_host_priv *hpriv);
  33         unsigned int flags;
  34 };
  35 
  36 static void ahci_mvebu_mbus_config(struct ahci_host_priv *hpriv,
  37                                    const struct mbus_dram_target_info *dram)
  38 {
  39         int i;
  40 
  41         for (i = 0; i < 4; i++) {
  42                 writel(0, hpriv->mmio + AHCI_WINDOW_CTRL(i));
  43                 writel(0, hpriv->mmio + AHCI_WINDOW_BASE(i));
  44                 writel(0, hpriv->mmio + AHCI_WINDOW_SIZE(i));
  45         }
  46 
  47         for (i = 0; i < dram->num_cs; i++) {
  48                 const struct mbus_dram_window *cs = dram->cs + i;
  49 
  50                 writel((cs->mbus_attr << 8) |
  51                        (dram->mbus_dram_target_id << 4) | 1,
  52                        hpriv->mmio + AHCI_WINDOW_CTRL(i));
  53                 writel(cs->base >> 16, hpriv->mmio + AHCI_WINDOW_BASE(i));
  54                 writel(((cs->size - 1) & 0xffff0000),
  55                        hpriv->mmio + AHCI_WINDOW_SIZE(i));
  56         }
  57 }
  58 
  59 static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv)
  60 {
  61         /*
  62          * Enable the regret bit to allow the SATA unit to regret a
  63          * request that didn't receive an acknowlegde and avoid a
  64          * deadlock
  65          */
  66         writel(0x4, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_ADDR);
  67         writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
  68 }
  69 
  70 static int ahci_mvebu_armada_380_config(struct ahci_host_priv *hpriv)
  71 {
  72         const struct mbus_dram_target_info *dram;
  73         int rc = 0;
  74 
  75         dram = mv_mbus_dram_info();
  76         if (dram)
  77                 ahci_mvebu_mbus_config(hpriv, dram);
  78         else
  79                 rc = -ENODEV;
  80 
  81         ahci_mvebu_regret_option(hpriv);
  82 
  83         return rc;
  84 }
  85 
  86 static int ahci_mvebu_armada_3700_config(struct ahci_host_priv *hpriv)
  87 {
  88         u32 reg;
  89 
  90         writel(0, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_ADDR);
  91 
  92         reg = readl(hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
  93         reg |= BIT(6);
  94         writel(reg, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
  95 
  96         return 0;
  97 }
  98 
  99 /**
 100  * ahci_mvebu_stop_engine
 101  *
 102  * @ap: Target ata port
 103  *
 104  * Errata Ref#226 - SATA Disk HOT swap issue when connected through
 105  * Port Multiplier in FIS-based Switching mode.
 106  *
 107  * To avoid the issue, according to design, the bits[11:8, 0] of
 108  * register PxFBS are cleared when Port Command and Status (0x18) bit[0]
 109  * changes its value from 1 to 0, i.e. falling edge of Port
 110  * Command and Status bit[0] sends PULSE that resets PxFBS
 111  * bits[11:8; 0].
 112  *
 113  * This function is used to override function of "ahci_stop_engine"
 114  * from libahci.c by adding the mvebu work around(WA) to save PxFBS
 115  * value before the PxCMD ST write of 0, then restore PxFBS value.
 116  *
 117  * Return: 0 on success; Error code otherwise.
 118  */
 119 static int ahci_mvebu_stop_engine(struct ata_port *ap)
 120 {
 121         void __iomem *port_mmio = ahci_port_base(ap);
 122         u32 tmp, port_fbs;
 123 
 124         tmp = readl(port_mmio + PORT_CMD);
 125 
 126         /* check if the HBA is idle */
 127         if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0)
 128                 return 0;
 129 
 130         /* save the port PxFBS register for later restore */
 131         port_fbs = readl(port_mmio + PORT_FBS);
 132 
 133         /* setting HBA to idle */
 134         tmp &= ~PORT_CMD_START;
 135         writel(tmp, port_mmio + PORT_CMD);
 136 
 137         /*
 138          * bit #15 PxCMD signal doesn't clear PxFBS,
 139          * restore the PxFBS register right after clearing the PxCMD ST,
 140          * no need to wait for the PxCMD bit #15.
 141          */
 142         writel(port_fbs, port_mmio + PORT_FBS);
 143 
 144         /* wait for engine to stop. This could be as long as 500 msec */
 145         tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
 146                                 PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
 147         if (tmp & PORT_CMD_LIST_ON)
 148                 return -EIO;
 149 
 150         return 0;
 151 }
 152 
 153 #ifdef CONFIG_PM_SLEEP
 154 static int ahci_mvebu_suspend(struct platform_device *pdev, pm_message_t state)
 155 {
 156         return ahci_platform_suspend_host(&pdev->dev);
 157 }
 158 
 159 static int ahci_mvebu_resume(struct platform_device *pdev)
 160 {
 161         struct ata_host *host = platform_get_drvdata(pdev);
 162         struct ahci_host_priv *hpriv = host->private_data;
 163         const struct ahci_mvebu_plat_data *pdata = hpriv->plat_data;
 164 
 165         pdata->plat_config(hpriv);
 166 
 167         return ahci_platform_resume_host(&pdev->dev);
 168 }
 169 #else
 170 #define ahci_mvebu_suspend NULL
 171 #define ahci_mvebu_resume NULL
 172 #endif
 173 
 174 static const struct ata_port_info ahci_mvebu_port_info = {
 175         .flags     = AHCI_FLAG_COMMON,
 176         .pio_mask  = ATA_PIO4,
 177         .udma_mask = ATA_UDMA6,
 178         .port_ops  = &ahci_platform_ops,
 179 };
 180 
 181 static struct scsi_host_template ahci_platform_sht = {
 182         AHCI_SHT(DRV_NAME),
 183 };
 184 
 185 static int ahci_mvebu_probe(struct platform_device *pdev)
 186 {
 187         const struct ahci_mvebu_plat_data *pdata;
 188         struct ahci_host_priv *hpriv;
 189         int rc;
 190 
 191         pdata = of_device_get_match_data(&pdev->dev);
 192         if (!pdata)
 193                 return -EINVAL;
 194 
 195         hpriv = ahci_platform_get_resources(pdev, 0);
 196         if (IS_ERR(hpriv))
 197                 return PTR_ERR(hpriv);
 198 
 199         hpriv->flags |= pdata->flags;
 200         hpriv->plat_data = (void *)pdata;
 201 
 202         rc = ahci_platform_enable_resources(hpriv);
 203         if (rc)
 204                 return rc;
 205 
 206         hpriv->stop_engine = ahci_mvebu_stop_engine;
 207 
 208         rc = pdata->plat_config(hpriv);
 209         if (rc)
 210                 goto disable_resources;
 211 
 212         rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info,
 213                                      &ahci_platform_sht);
 214         if (rc)
 215                 goto disable_resources;
 216 
 217         return 0;
 218 
 219 disable_resources:
 220         ahci_platform_disable_resources(hpriv);
 221         return rc;
 222 }
 223 
 224 static const struct ahci_mvebu_plat_data ahci_mvebu_armada_380_plat_data = {
 225         .plat_config = ahci_mvebu_armada_380_config,
 226 };
 227 
 228 static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = {
 229         .plat_config = ahci_mvebu_armada_3700_config,
 230         .flags = AHCI_HFLAG_SUSPEND_PHYS,
 231 };
 232 
 233 static const struct of_device_id ahci_mvebu_of_match[] = {
 234         {
 235                 .compatible = "marvell,armada-380-ahci",
 236                 .data = &ahci_mvebu_armada_380_plat_data,
 237         },
 238         {
 239                 .compatible = "marvell,armada-3700-ahci",
 240                 .data = &ahci_mvebu_armada_3700_plat_data,
 241         },
 242         { },
 243 };
 244 MODULE_DEVICE_TABLE(of, ahci_mvebu_of_match);
 245 
 246 static struct platform_driver ahci_mvebu_driver = {
 247         .probe = ahci_mvebu_probe,
 248         .remove = ata_platform_remove_one,
 249         .suspend = ahci_mvebu_suspend,
 250         .resume = ahci_mvebu_resume,
 251         .driver = {
 252                 .name = DRV_NAME,
 253                 .of_match_table = ahci_mvebu_of_match,
 254         },
 255 };
 256 module_platform_driver(ahci_mvebu_driver);
 257 
 258 MODULE_DESCRIPTION("Marvell EBU AHCI SATA driver");
 259 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>, Marcin Wojtas <mw@semihalf.com>");
 260 MODULE_LICENSE("GPL");
 261 MODULE_ALIAS("platform:ahci_mvebu");

/* [<][>][^][v][top][bottom][index][help] */