1 /* 2 * Freescale IMX AHCI SATA platform driver 3 * Copyright 2013 Freescale Semiconductor, Inc. 4 * 5 * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms and conditions of the GNU General Public License, 9 * version 2, as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/kernel.h> 21 #include <linux/module.h> 22 #include <linux/platform_device.h> 23 #include <linux/regmap.h> 24 #include <linux/ahci_platform.h> 25 #include <linux/of_device.h> 26 #include <linux/mfd/syscon.h> 27 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 28 #include "ahci.h" 29 30 enum { 31 HOST_TIMER1MS = 0xe0, /* Timer 1-ms */ 32 }; 33 34 struct imx_ahci_priv { 35 struct platform_device *ahci_pdev; 36 struct clk *sata_ref_clk; 37 struct clk *ahb_clk; 38 struct regmap *gpr; 39 }; 40 41 static int imx6q_sata_init(struct device *dev, void __iomem *mmio) 42 { 43 int ret = 0; 44 unsigned int reg_val; 45 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); 46 47 imxpriv->gpr = 48 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); 49 if (IS_ERR(imxpriv->gpr)) { 50 dev_err(dev, "failed to find fsl,imx6q-iomux-gpr regmap\n"); 51 return PTR_ERR(imxpriv->gpr); 52 } 53 54 ret = clk_prepare_enable(imxpriv->sata_ref_clk); 55 if (ret < 0) { 56 dev_err(dev, "prepare-enable sata_ref clock err:%d\n", ret); 57 return ret; 58 } 59 60 /* 61 * set PHY Paremeters, two steps to configure the GPR13, 62 * one write for rest of parameters, mask of first write 63 * is 0x07fffffd, and the other one write for setting 64 * the mpll_clk_en. 65 */ 66 regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK 67 | IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK 68 | IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK 69 | IMX6Q_GPR13_SATA_SPD_MODE_MASK 70 | IMX6Q_GPR13_SATA_MPLL_SS_EN 71 | IMX6Q_GPR13_SATA_TX_ATTEN_MASK 72 | IMX6Q_GPR13_SATA_TX_BOOST_MASK 73 | IMX6Q_GPR13_SATA_TX_LVL_MASK 74 | IMX6Q_GPR13_SATA_TX_EDGE_RATE 75 , IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB 76 | IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M 77 | IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F 78 | IMX6Q_GPR13_SATA_SPD_MODE_3P0G 79 | IMX6Q_GPR13_SATA_MPLL_SS_EN 80 | IMX6Q_GPR13_SATA_TX_ATTEN_9_16 81 | IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB 82 | IMX6Q_GPR13_SATA_TX_LVL_1_025_V); 83 regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN, 84 IMX6Q_GPR13_SATA_MPLL_CLK_EN); 85 usleep_range(100, 200); 86 87 /* 88 * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, 89 * and IP vendor specific register HOST_TIMER1MS. 90 * Configure CAP_SSS (support stagered spin up). 91 * Implement the port0. 92 * Get the ahb clock rate, and configure the TIMER1MS register. 93 */ 94 reg_val = readl(mmio + HOST_CAP); 95 if (!(reg_val & HOST_CAP_SSS)) { 96 reg_val |= HOST_CAP_SSS; 97 writel(reg_val, mmio + HOST_CAP); 98 } 99 reg_val = readl(mmio + HOST_PORTS_IMPL); 100 if (!(reg_val & 0x1)) { 101 reg_val |= 0x1; 102 writel(reg_val, mmio + HOST_PORTS_IMPL); 103 } 104 105 reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; 106 writel(reg_val, mmio + HOST_TIMER1MS); 107 108 return 0; 109 } 110 111 static void imx6q_sata_exit(struct device *dev) 112 { 113 struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); 114 115 regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN, 116 !IMX6Q_GPR13_SATA_MPLL_CLK_EN); 117 clk_disable_unprepare(imxpriv->sata_ref_clk); 118 } 119 120 static struct ahci_platform_data imx6q_sata_pdata = { 121 .init = imx6q_sata_init, 122 .exit = imx6q_sata_exit, 123 }; 124 125 static const struct of_device_id imx_ahci_of_match[] = { 126 { .compatible = "fsl,imx6q-ahci", .data = &imx6q_sata_pdata}, 127 {}, 128 }; 129 MODULE_DEVICE_TABLE(of, imx_ahci_of_match); 130 131 static int imx_ahci_probe(struct platform_device *pdev) 132 { 133 struct device *dev = &pdev->dev; 134 struct resource *mem, *irq, res[2]; 135 const struct of_device_id *of_id; 136 const struct ahci_platform_data *pdata = NULL; 137 struct imx_ahci_priv *imxpriv; 138 struct device *ahci_dev; 139 struct platform_device *ahci_pdev; 140 int ret; 141 142 imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); 143 if (!imxpriv) { 144 dev_err(dev, "can't alloc ahci_host_priv\n"); 145 return -ENOMEM; 146 } 147 148 ahci_pdev = platform_device_alloc("ahci", -1); 149 if (!ahci_pdev) 150 return -ENODEV; 151 152 ahci_dev = &ahci_pdev->dev; 153 ahci_dev->parent = dev; 154 155 imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); 156 if (IS_ERR(imxpriv->ahb_clk)) { 157 dev_err(dev, "can't get ahb clock.\n"); 158 ret = PTR_ERR(imxpriv->ahb_clk); 159 goto err_out; 160 } 161 162 imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); 163 if (IS_ERR(imxpriv->sata_ref_clk)) { 164 dev_err(dev, "can't get sata_ref clock.\n"); 165 ret = PTR_ERR(imxpriv->sata_ref_clk); 166 goto err_out; 167 } 168 169 imxpriv->ahci_pdev = ahci_pdev; 170 platform_set_drvdata(pdev, imxpriv); 171 172 of_id = of_match_device(imx_ahci_of_match, dev); 173 if (of_id) { 174 pdata = of_id->data; 175 } else { 176 ret = -EINVAL; 177 goto err_out; 178 } 179 180 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 181 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 182 if (!mem || !irq) { 183 dev_err(dev, "no mmio/irq resource\n"); 184 ret = -ENOMEM; 185 goto err_out; 186 } 187 188 res[0] = *mem; 189 res[1] = *irq; 190 191 ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32); 192 ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask; 193 ahci_dev->of_node = dev->of_node; 194 195 ret = platform_device_add_resources(ahci_pdev, res, 2); 196 if (ret) 197 goto err_out; 198 199 ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata)); 200 if (ret) 201 goto err_out; 202 203 ret = platform_device_add(ahci_pdev); 204 if (ret) { 205 err_out: 206 platform_device_put(ahci_pdev); 207 return ret; 208 } 209 210 return 0; 211 } 212 213 static int imx_ahci_remove(struct platform_device *pdev) 214 { 215 struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev); 216 struct platform_device *ahci_pdev = imxpriv->ahci_pdev; 217 218 platform_device_unregister(ahci_pdev); 219 return 0; 220 } 221 222 static struct platform_driver imx_ahci_driver = { 223 .probe = imx_ahci_probe, 224 .remove = imx_ahci_remove, 225 .driver = { 226 .name = "ahci-imx", 227 .owner = THIS_MODULE, 228 .of_match_table = imx_ahci_of_match, 229 }, 230 }; 231 module_platform_driver(imx_ahci_driver); 232 233 MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); 234 MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>"); 235 MODULE_LICENSE("GPL"); 236 MODULE_ALIAS("ahci:imx"); 237