1c5754b52SOlliver Schinagl /* 2c5754b52SOlliver Schinagl * Allwinner sunxi AHCI SATA platform driver 3c5754b52SOlliver Schinagl * Copyright 2013 Olliver Schinagl <oliver@schinagl.nl> 4c5754b52SOlliver Schinagl * Copyright 2014 Hans de Goede <hdegoede@redhat.com> 5c5754b52SOlliver Schinagl * 6c5754b52SOlliver Schinagl * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov 7c5754b52SOlliver Schinagl * Based on code from Allwinner Technology Co., Ltd. <www.allwinnertech.com>, 8c5754b52SOlliver Schinagl * Daniel Wang <danielwang@allwinnertech.com> 9c5754b52SOlliver Schinagl * 10c5754b52SOlliver Schinagl * This program is free software; you can redistribute it and/or modify it 11c5754b52SOlliver Schinagl * under the terms and conditions of the GNU General Public License, 12c5754b52SOlliver Schinagl * version 2, as published by the Free Software Foundation. 13c5754b52SOlliver Schinagl * 14c5754b52SOlliver Schinagl * This program is distributed in the hope it will be useful, but WITHOUT 15c5754b52SOlliver Schinagl * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16c5754b52SOlliver Schinagl * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17c5754b52SOlliver Schinagl * more details. 18c5754b52SOlliver Schinagl */ 19c5754b52SOlliver Schinagl 20c5754b52SOlliver Schinagl #include <linux/ahci_platform.h> 21c5754b52SOlliver Schinagl #include <linux/clk.h> 22c5754b52SOlliver Schinagl #include <linux/errno.h> 23c5754b52SOlliver Schinagl #include <linux/kernel.h> 24c5754b52SOlliver Schinagl #include <linux/module.h> 25c5754b52SOlliver Schinagl #include <linux/of_device.h> 26c5754b52SOlliver Schinagl #include <linux/platform_device.h> 27c5754b52SOlliver Schinagl #include <linux/regulator/consumer.h> 28c5754b52SOlliver Schinagl #include "ahci.h" 29c5754b52SOlliver Schinagl 30c5754b52SOlliver Schinagl #define AHCI_BISTAFR 0x00a0 31c5754b52SOlliver Schinagl #define AHCI_BISTCR 0x00a4 32c5754b52SOlliver Schinagl #define AHCI_BISTFCTR 0x00a8 33c5754b52SOlliver Schinagl #define AHCI_BISTSR 0x00ac 34c5754b52SOlliver Schinagl #define AHCI_BISTDECR 0x00b0 35c5754b52SOlliver Schinagl #define AHCI_DIAGNR0 0x00b4 36c5754b52SOlliver Schinagl #define AHCI_DIAGNR1 0x00b8 37c5754b52SOlliver Schinagl #define AHCI_OOBR 0x00bc 38c5754b52SOlliver Schinagl #define AHCI_PHYCS0R 0x00c0 39c5754b52SOlliver Schinagl #define AHCI_PHYCS1R 0x00c4 40c5754b52SOlliver Schinagl #define AHCI_PHYCS2R 0x00c8 41c5754b52SOlliver Schinagl #define AHCI_TIMER1MS 0x00e0 42c5754b52SOlliver Schinagl #define AHCI_GPARAM1R 0x00e8 43c5754b52SOlliver Schinagl #define AHCI_GPARAM2R 0x00ec 44c5754b52SOlliver Schinagl #define AHCI_PPARAMR 0x00f0 45c5754b52SOlliver Schinagl #define AHCI_TESTR 0x00f4 46c5754b52SOlliver Schinagl #define AHCI_VERSIONR 0x00f8 47c5754b52SOlliver Schinagl #define AHCI_IDR 0x00fc 48c5754b52SOlliver Schinagl #define AHCI_RWCR 0x00fc 49c5754b52SOlliver Schinagl #define AHCI_P0DMACR 0x0170 50c5754b52SOlliver Schinagl #define AHCI_P0PHYCR 0x0178 51c5754b52SOlliver Schinagl #define AHCI_P0PHYSR 0x017c 52c5754b52SOlliver Schinagl 53c5754b52SOlliver Schinagl static void sunxi_clrbits(void __iomem *reg, u32 clr_val) 54c5754b52SOlliver Schinagl { 55c5754b52SOlliver Schinagl u32 reg_val; 56c5754b52SOlliver Schinagl 57c5754b52SOlliver Schinagl reg_val = readl(reg); 58c5754b52SOlliver Schinagl reg_val &= ~(clr_val); 59c5754b52SOlliver Schinagl writel(reg_val, reg); 60c5754b52SOlliver Schinagl } 61c5754b52SOlliver Schinagl 62c5754b52SOlliver Schinagl static void sunxi_setbits(void __iomem *reg, u32 set_val) 63c5754b52SOlliver Schinagl { 64c5754b52SOlliver Schinagl u32 reg_val; 65c5754b52SOlliver Schinagl 66c5754b52SOlliver Schinagl reg_val = readl(reg); 67c5754b52SOlliver Schinagl reg_val |= set_val; 68c5754b52SOlliver Schinagl writel(reg_val, reg); 69c5754b52SOlliver Schinagl } 70c5754b52SOlliver Schinagl 71c5754b52SOlliver Schinagl static void sunxi_clrsetbits(void __iomem *reg, u32 clr_val, u32 set_val) 72c5754b52SOlliver Schinagl { 73c5754b52SOlliver Schinagl u32 reg_val; 74c5754b52SOlliver Schinagl 75c5754b52SOlliver Schinagl reg_val = readl(reg); 76c5754b52SOlliver Schinagl reg_val &= ~(clr_val); 77c5754b52SOlliver Schinagl reg_val |= set_val; 78c5754b52SOlliver Schinagl writel(reg_val, reg); 79c5754b52SOlliver Schinagl } 80c5754b52SOlliver Schinagl 81c5754b52SOlliver Schinagl static u32 sunxi_getbits(void __iomem *reg, u8 mask, u8 shift) 82c5754b52SOlliver Schinagl { 83c5754b52SOlliver Schinagl return (readl(reg) >> shift) & mask; 84c5754b52SOlliver Schinagl } 85c5754b52SOlliver Schinagl 86c5754b52SOlliver Schinagl static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) 87c5754b52SOlliver Schinagl { 88c5754b52SOlliver Schinagl u32 reg_val; 89c5754b52SOlliver Schinagl int timeout; 90c5754b52SOlliver Schinagl 91c5754b52SOlliver Schinagl /* This magic is from the original code */ 92c5754b52SOlliver Schinagl writel(0, reg_base + AHCI_RWCR); 93d2ec147aSHans de Goede msleep(5); 94c5754b52SOlliver Schinagl 95c5754b52SOlliver Schinagl sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19)); 96c5754b52SOlliver Schinagl sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, 97c5754b52SOlliver Schinagl (0x7 << 24), 98c5754b52SOlliver Schinagl (0x5 << 24) | BIT(23) | BIT(18)); 99c5754b52SOlliver Schinagl sunxi_clrsetbits(reg_base + AHCI_PHYCS1R, 100c5754b52SOlliver Schinagl (0x3 << 16) | (0x1f << 8) | (0x3 << 6), 101c5754b52SOlliver Schinagl (0x2 << 16) | (0x6 << 8) | (0x2 << 6)); 102c5754b52SOlliver Schinagl sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15)); 103c5754b52SOlliver Schinagl sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19)); 104c5754b52SOlliver Schinagl sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, 105c5754b52SOlliver Schinagl (0x7 << 20), (0x3 << 20)); 106c5754b52SOlliver Schinagl sunxi_clrsetbits(reg_base + AHCI_PHYCS2R, 107c5754b52SOlliver Schinagl (0x1f << 5), (0x19 << 5)); 108d2ec147aSHans de Goede msleep(5); 109c5754b52SOlliver Schinagl 110c5754b52SOlliver Schinagl sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19)); 111c5754b52SOlliver Schinagl 112c5754b52SOlliver Schinagl timeout = 250; /* Power up takes aprox 50 us */ 113c5754b52SOlliver Schinagl do { 114c5754b52SOlliver Schinagl reg_val = sunxi_getbits(reg_base + AHCI_PHYCS0R, 0x7, 28); 115c5754b52SOlliver Schinagl if (reg_val == 0x02) 116c5754b52SOlliver Schinagl break; 117c5754b52SOlliver Schinagl 118c5754b52SOlliver Schinagl if (--timeout == 0) { 119c5754b52SOlliver Schinagl dev_err(dev, "PHY power up failed.\n"); 120c5754b52SOlliver Schinagl return -EIO; 121c5754b52SOlliver Schinagl } 122c5754b52SOlliver Schinagl udelay(1); 123c5754b52SOlliver Schinagl } while (1); 124c5754b52SOlliver Schinagl 125c5754b52SOlliver Schinagl sunxi_setbits(reg_base + AHCI_PHYCS2R, (0x1 << 24)); 126c5754b52SOlliver Schinagl 127c5754b52SOlliver Schinagl timeout = 100; /* Calibration takes aprox 10 us */ 128c5754b52SOlliver Schinagl do { 129c5754b52SOlliver Schinagl reg_val = sunxi_getbits(reg_base + AHCI_PHYCS2R, 0x1, 24); 130c5754b52SOlliver Schinagl if (reg_val == 0x00) 131c5754b52SOlliver Schinagl break; 132c5754b52SOlliver Schinagl 133c5754b52SOlliver Schinagl if (--timeout == 0) { 134c5754b52SOlliver Schinagl dev_err(dev, "PHY calibration failed.\n"); 135c5754b52SOlliver Schinagl return -EIO; 136c5754b52SOlliver Schinagl } 137c5754b52SOlliver Schinagl udelay(1); 138c5754b52SOlliver Schinagl } while (1); 139c5754b52SOlliver Schinagl 140d2ec147aSHans de Goede msleep(15); 141c5754b52SOlliver Schinagl 142c5754b52SOlliver Schinagl writel(0x7, reg_base + AHCI_RWCR); 143c5754b52SOlliver Schinagl 144c5754b52SOlliver Schinagl return 0; 145c5754b52SOlliver Schinagl } 146c5754b52SOlliver Schinagl 147c5754b52SOlliver Schinagl static void ahci_sunxi_start_engine(struct ata_port *ap) 148c5754b52SOlliver Schinagl { 149c5754b52SOlliver Schinagl void __iomem *port_mmio = ahci_port_base(ap); 150c5754b52SOlliver Schinagl struct ahci_host_priv *hpriv = ap->host->private_data; 151c5754b52SOlliver Schinagl 152c5754b52SOlliver Schinagl /* Setup DMA before DMA start */ 153c5754b52SOlliver Schinagl sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ff00, 0x00004400); 154c5754b52SOlliver Schinagl 155c5754b52SOlliver Schinagl /* Start DMA */ 156c5754b52SOlliver Schinagl sunxi_setbits(port_mmio + PORT_CMD, PORT_CMD_START); 157c5754b52SOlliver Schinagl } 158c5754b52SOlliver Schinagl 159c5754b52SOlliver Schinagl static const struct ata_port_info ahci_sunxi_port_info = { 160c5754b52SOlliver Schinagl .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, 161c5754b52SOlliver Schinagl .pio_mask = ATA_PIO4, 162c5754b52SOlliver Schinagl .udma_mask = ATA_UDMA6, 163c5754b52SOlliver Schinagl .port_ops = &ahci_platform_ops, 164c5754b52SOlliver Schinagl }; 165c5754b52SOlliver Schinagl 166c5754b52SOlliver Schinagl static int ahci_sunxi_probe(struct platform_device *pdev) 167c5754b52SOlliver Schinagl { 168c5754b52SOlliver Schinagl struct device *dev = &pdev->dev; 169c5754b52SOlliver Schinagl struct ahci_host_priv *hpriv; 170*f9f36917SKefeng Wang unsigned long hflags; 171c5754b52SOlliver Schinagl int rc; 172c5754b52SOlliver Schinagl 173c5754b52SOlliver Schinagl hpriv = ahci_platform_get_resources(pdev); 174c5754b52SOlliver Schinagl if (IS_ERR(hpriv)) 175c5754b52SOlliver Schinagl return PTR_ERR(hpriv); 176c5754b52SOlliver Schinagl 177c5754b52SOlliver Schinagl hpriv->start_engine = ahci_sunxi_start_engine; 178c5754b52SOlliver Schinagl 179c5754b52SOlliver Schinagl rc = ahci_platform_enable_resources(hpriv); 180c5754b52SOlliver Schinagl if (rc) 181c5754b52SOlliver Schinagl return rc; 182c5754b52SOlliver Schinagl 183c5754b52SOlliver Schinagl rc = ahci_sunxi_phy_init(dev, hpriv->mmio); 184c5754b52SOlliver Schinagl if (rc) 185c5754b52SOlliver Schinagl goto disable_resources; 186c5754b52SOlliver Schinagl 187*f9f36917SKefeng Wang hflags = AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | 188*f9f36917SKefeng Wang AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; 189*f9f36917SKefeng Wang 190*f9f36917SKefeng Wang rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, 191*f9f36917SKefeng Wang hflags, 0, 0); 192c5754b52SOlliver Schinagl if (rc) 193c5754b52SOlliver Schinagl goto disable_resources; 194c5754b52SOlliver Schinagl 195c5754b52SOlliver Schinagl return 0; 196c5754b52SOlliver Schinagl 197c5754b52SOlliver Schinagl disable_resources: 198c5754b52SOlliver Schinagl ahci_platform_disable_resources(hpriv); 199c5754b52SOlliver Schinagl return rc; 200c5754b52SOlliver Schinagl } 201c5754b52SOlliver Schinagl 202c5754b52SOlliver Schinagl #ifdef CONFIG_PM_SLEEP 2031bf9d885SBartlomiej Zolnierkiewicz static int ahci_sunxi_resume(struct device *dev) 204c5754b52SOlliver Schinagl { 205c5754b52SOlliver Schinagl struct ata_host *host = dev_get_drvdata(dev); 206c5754b52SOlliver Schinagl struct ahci_host_priv *hpriv = host->private_data; 207c5754b52SOlliver Schinagl int rc; 208c5754b52SOlliver Schinagl 209c5754b52SOlliver Schinagl rc = ahci_platform_enable_resources(hpriv); 210c5754b52SOlliver Schinagl if (rc) 211c5754b52SOlliver Schinagl return rc; 212c5754b52SOlliver Schinagl 213c5754b52SOlliver Schinagl rc = ahci_sunxi_phy_init(dev, hpriv->mmio); 214c5754b52SOlliver Schinagl if (rc) 215c5754b52SOlliver Schinagl goto disable_resources; 216c5754b52SOlliver Schinagl 217c5754b52SOlliver Schinagl rc = ahci_platform_resume_host(dev); 218c5754b52SOlliver Schinagl if (rc) 219c5754b52SOlliver Schinagl goto disable_resources; 220c5754b52SOlliver Schinagl 221c5754b52SOlliver Schinagl return 0; 222c5754b52SOlliver Schinagl 223c5754b52SOlliver Schinagl disable_resources: 224c5754b52SOlliver Schinagl ahci_platform_disable_resources(hpriv); 225c5754b52SOlliver Schinagl return rc; 226c5754b52SOlliver Schinagl } 227c5754b52SOlliver Schinagl #endif 228c5754b52SOlliver Schinagl 229c5754b52SOlliver Schinagl static SIMPLE_DEV_PM_OPS(ahci_sunxi_pm_ops, ahci_platform_suspend, 230c5754b52SOlliver Schinagl ahci_sunxi_resume); 231c5754b52SOlliver Schinagl 232c5754b52SOlliver Schinagl static const struct of_device_id ahci_sunxi_of_match[] = { 233c5754b52SOlliver Schinagl { .compatible = "allwinner,sun4i-a10-ahci", }, 234c5754b52SOlliver Schinagl { }, 235c5754b52SOlliver Schinagl }; 236c5754b52SOlliver Schinagl MODULE_DEVICE_TABLE(of, ahci_sunxi_of_match); 237c5754b52SOlliver Schinagl 238c5754b52SOlliver Schinagl static struct platform_driver ahci_sunxi_driver = { 239c5754b52SOlliver Schinagl .probe = ahci_sunxi_probe, 240c5754b52SOlliver Schinagl .remove = ata_platform_remove_one, 241c5754b52SOlliver Schinagl .driver = { 242c5754b52SOlliver Schinagl .name = "ahci-sunxi", 243c5754b52SOlliver Schinagl .owner = THIS_MODULE, 244c5754b52SOlliver Schinagl .of_match_table = ahci_sunxi_of_match, 245c5754b52SOlliver Schinagl .pm = &ahci_sunxi_pm_ops, 246c5754b52SOlliver Schinagl }, 247c5754b52SOlliver Schinagl }; 248c5754b52SOlliver Schinagl module_platform_driver(ahci_sunxi_driver); 249c5754b52SOlliver Schinagl 250c5754b52SOlliver Schinagl MODULE_DESCRIPTION("Allwinner sunxi AHCI SATA driver"); 251c5754b52SOlliver Schinagl MODULE_AUTHOR("Olliver Schinagl <oliver@schinagl.nl>"); 252c5754b52SOlliver Schinagl MODULE_LICENSE("GPL"); 253