307 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Allwinner sunxi AHCI SATA platform driver4 * Copyright 2013 Olliver Schinagl <oliver@schinagl.nl>5 * Copyright 2014 Hans de Goede <hdegoede@redhat.com>6 *7 * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov8 * Based on code from Allwinner Technology Co., Ltd. <www.allwinnertech.com>,9 * Daniel Wang <danielwang@allwinnertech.com>10 */11 12#include <linux/ahci_platform.h>13#include <linux/clk.h>14#include <linux/errno.h>15#include <linux/kernel.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/platform_device.h>19#include <linux/regulator/consumer.h>20#include "ahci.h"21 22#define DRV_NAME "ahci-sunxi"23 24/* Insmod parameters */25static bool enable_pmp;26module_param(enable_pmp, bool, 0);27MODULE_PARM_DESC(enable_pmp,28 "Enable support for sata port multipliers, only use if you use a pmp!");29 30#define AHCI_BISTAFR 0x00a031#define AHCI_BISTCR 0x00a432#define AHCI_BISTFCTR 0x00a833#define AHCI_BISTSR 0x00ac34#define AHCI_BISTDECR 0x00b035#define AHCI_DIAGNR0 0x00b436#define AHCI_DIAGNR1 0x00b837#define AHCI_OOBR 0x00bc38#define AHCI_PHYCS0R 0x00c039#define AHCI_PHYCS1R 0x00c440#define AHCI_PHYCS2R 0x00c841#define AHCI_TIMER1MS 0x00e042#define AHCI_GPARAM1R 0x00e843#define AHCI_GPARAM2R 0x00ec44#define AHCI_PPARAMR 0x00f045#define AHCI_TESTR 0x00f446#define AHCI_VERSIONR 0x00f847#define AHCI_IDR 0x00fc48#define AHCI_RWCR 0x00fc49#define AHCI_P0DMACR 0x017050#define AHCI_P0PHYCR 0x017851#define AHCI_P0PHYSR 0x017c52 53static void sunxi_clrbits(void __iomem *reg, u32 clr_val)54{55 u32 reg_val;56 57 reg_val = readl(reg);58 reg_val &= ~(clr_val);59 writel(reg_val, reg);60}61 62static void sunxi_setbits(void __iomem *reg, u32 set_val)63{64 u32 reg_val;65 66 reg_val = readl(reg);67 reg_val |= set_val;68 writel(reg_val, reg);69}70 71static void sunxi_clrsetbits(void __iomem *reg, u32 clr_val, u32 set_val)72{73 u32 reg_val;74 75 reg_val = readl(reg);76 reg_val &= ~(clr_val);77 reg_val |= set_val;78 writel(reg_val, reg);79}80 81static u32 sunxi_getbits(void __iomem *reg, u8 mask, u8 shift)82{83 return (readl(reg) >> shift) & mask;84}85 86static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base)87{88 u32 reg_val;89 int timeout;90 91 /* This magic is from the original code */92 writel(0, reg_base + AHCI_RWCR);93 msleep(5);94 95 sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19));96 sunxi_clrsetbits(reg_base + AHCI_PHYCS0R,97 (0x7 << 24),98 (0x5 << 24) | BIT(23) | BIT(18));99 sunxi_clrsetbits(reg_base + AHCI_PHYCS1R,100 (0x3 << 16) | (0x1f << 8) | (0x3 << 6),101 (0x2 << 16) | (0x6 << 8) | (0x2 << 6));102 sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15));103 sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19));104 sunxi_clrsetbits(reg_base + AHCI_PHYCS0R,105 (0x7 << 20), (0x3 << 20));106 sunxi_clrsetbits(reg_base + AHCI_PHYCS2R,107 (0x1f << 5), (0x19 << 5));108 msleep(5);109 110 sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19));111 112 timeout = 250; /* Power up takes aprox 50 us */113 do {114 reg_val = sunxi_getbits(reg_base + AHCI_PHYCS0R, 0x7, 28);115 if (reg_val == 0x02)116 break;117 118 if (--timeout == 0) {119 dev_err(dev, "PHY power up failed.\n");120 return -EIO;121 }122 udelay(1);123 } while (1);124 125 sunxi_setbits(reg_base + AHCI_PHYCS2R, (0x1 << 24));126 127 timeout = 100; /* Calibration takes aprox 10 us */128 do {129 reg_val = sunxi_getbits(reg_base + AHCI_PHYCS2R, 0x1, 24);130 if (reg_val == 0x00)131 break;132 133 if (--timeout == 0) {134 dev_err(dev, "PHY calibration failed.\n");135 return -EIO;136 }137 udelay(1);138 } while (1);139 140 msleep(15);141 142 writel(0x7, reg_base + AHCI_RWCR);143 144 return 0;145}146 147static void ahci_sunxi_start_engine(struct ata_port *ap)148{149 void __iomem *port_mmio = ahci_port_base(ap);150 struct ahci_host_priv *hpriv = ap->host->private_data;151 152 /* Setup DMA before DMA start153 *154 * NOTE: A similar SoC with SATA/AHCI by Texas Instruments documents155 * this Vendor Specific Port (P0DMACR, aka PxDMACR) in its156 * User's Guide document (TMS320C674x/OMAP-L1x Processor157 * Serial ATA (SATA) Controller, Literature Number: SPRUGJ8C,158 * March 2011, Chapter 4.33 Port DMA Control Register (P0DMACR),159 * p.68, https://www.ti.com/lit/ug/sprugj8c/sprugj8c.pdf)160 * as equivalent to the following struct:161 *162 * struct AHCI_P0DMACR_t163 * {164 * unsigned TXTS : 4;165 * unsigned RXTS : 4;166 * unsigned TXABL : 4;167 * unsigned RXABL : 4;168 * unsigned Reserved : 16;169 * };170 *171 * TXTS: Transmit Transaction Size (TX_TRANSACTION_SIZE).172 * This field defines the DMA transaction size in DWORDs for173 * transmit (system bus read, device write) operation. [...]174 *175 * RXTS: Receive Transaction Size (RX_TRANSACTION_SIZE).176 * This field defines the Port DMA transaction size in DWORDs177 * for receive (system bus write, device read) operation. [...]178 *179 * TXABL: Transmit Burst Limit.180 * This field allows software to limit the VBUSP master read181 * burst size. [...]182 *183 * RXABL: Receive Burst Limit.184 * Allows software to limit the VBUSP master write burst185 * size. [...]186 *187 * Reserved: Reserved.188 *189 *190 * NOTE: According to the above document, the following alternative191 * to the code below could perhaps be a better option192 * (or preparation) for possible further improvements later:193 * sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ffff,194 * 0x00000033);195 */196 sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ffff, 0x00004433);197 198 /* Start DMA */199 sunxi_setbits(port_mmio + PORT_CMD, PORT_CMD_START);200}201 202static const struct ata_port_info ahci_sunxi_port_info = {203 .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ | ATA_FLAG_NO_DIPM,204 .pio_mask = ATA_PIO4,205 .udma_mask = ATA_UDMA6,206 .port_ops = &ahci_platform_ops,207};208 209static const struct scsi_host_template ahci_platform_sht = {210 AHCI_SHT(DRV_NAME),211};212 213static int ahci_sunxi_probe(struct platform_device *pdev)214{215 struct device *dev = &pdev->dev;216 struct ahci_host_priv *hpriv;217 int rc;218 219 hpriv = ahci_platform_get_resources(pdev, AHCI_PLATFORM_GET_RESETS);220 if (IS_ERR(hpriv))221 return PTR_ERR(hpriv);222 223 hpriv->start_engine = ahci_sunxi_start_engine;224 225 rc = ahci_platform_enable_resources(hpriv);226 if (rc)227 return rc;228 229 rc = ahci_sunxi_phy_init(dev, hpriv->mmio);230 if (rc)231 goto disable_resources;232 233 hpriv->flags = AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI |234 AHCI_HFLAG_YES_NCQ;235 236 /*237 * The sunxi sata controller seems to be unable to successfully do a238 * soft reset if no pmp is attached, so disable pmp use unless239 * requested, otherwise directly attached disks do not work.240 */241 if (!enable_pmp)242 hpriv->flags |= AHCI_HFLAG_NO_PMP;243 244 rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info,245 &ahci_platform_sht);246 if (rc)247 goto disable_resources;248 249 return 0;250 251disable_resources:252 ahci_platform_disable_resources(hpriv);253 return rc;254}255 256#ifdef CONFIG_PM_SLEEP257static int ahci_sunxi_resume(struct device *dev)258{259 struct ata_host *host = dev_get_drvdata(dev);260 struct ahci_host_priv *hpriv = host->private_data;261 int rc;262 263 rc = ahci_platform_enable_resources(hpriv);264 if (rc)265 return rc;266 267 rc = ahci_sunxi_phy_init(dev, hpriv->mmio);268 if (rc)269 goto disable_resources;270 271 rc = ahci_platform_resume_host(dev);272 if (rc)273 goto disable_resources;274 275 return 0;276 277disable_resources:278 ahci_platform_disable_resources(hpriv);279 return rc;280}281#endif282 283static SIMPLE_DEV_PM_OPS(ahci_sunxi_pm_ops, ahci_platform_suspend,284 ahci_sunxi_resume);285 286static const struct of_device_id ahci_sunxi_of_match[] = {287 { .compatible = "allwinner,sun4i-a10-ahci", },288 { .compatible = "allwinner,sun8i-r40-ahci", },289 { /* sentinel */ }290};291MODULE_DEVICE_TABLE(of, ahci_sunxi_of_match);292 293static struct platform_driver ahci_sunxi_driver = {294 .probe = ahci_sunxi_probe,295 .remove_new = ata_platform_remove_one,296 .driver = {297 .name = DRV_NAME,298 .of_match_table = ahci_sunxi_of_match,299 .pm = &ahci_sunxi_pm_ops,300 },301};302module_platform_driver(ahci_sunxi_driver);303 304MODULE_DESCRIPTION("Allwinner sunxi AHCI SATA driver");305MODULE_AUTHOR("Olliver Schinagl <oliver@schinagl.nl>");306MODULE_LICENSE("GPL");307