1 /* 2 * copyright (c) 2013 Freescale Semiconductor, Inc. 3 * Freescale IMX AHCI SATA platform driver 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 <linux/libata.h> 29 #include "ahci.h" 30 31 enum { 32 PORT_PHY_CTL = 0x178, /* Port0 PHY Control */ 33 PORT_PHY_CTL_PDDQ_LOC = 0x100000, /* PORT_PHY_CTL bits */ 34 HOST_TIMER1MS = 0xe0, /* Timer 1-ms */ 35 }; 36 37 enum ahci_imx_type { 38 AHCI_IMX53, 39 AHCI_IMX6Q, 40 }; 41 42 struct imx_ahci_priv { 43 struct platform_device *ahci_pdev; 44 enum ahci_imx_type type; 45 struct clk *ahb_clk; 46 struct regmap *gpr; 47 bool no_device; 48 bool first_time; 49 }; 50 51 static int ahci_imx_hotplug; 52 module_param_named(hotplug, ahci_imx_hotplug, int, 0644); 53 MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); 54 55 static void ahci_imx_host_stop(struct ata_host *host); 56 57 static int imx_sata_enable(struct ahci_host_priv *hpriv) 58 { 59 struct imx_ahci_priv *imxpriv = hpriv->plat_data; 60 int ret; 61 62 if (imxpriv->no_device) 63 return 0; 64 65 if (hpriv->target_pwr) { 66 ret = regulator_enable(hpriv->target_pwr); 67 if (ret) 68 return ret; 69 } 70 71 ret = ahci_platform_enable_clks(hpriv); 72 if (ret < 0) 73 goto disable_regulator; 74 75 if (imxpriv->type == AHCI_IMX6Q) { 76 /* 77 * set PHY Paremeters, two steps to configure the GPR13, 78 * one write for rest of parameters, mask of first write 79 * is 0x07ffffff, and the other one write for setting 80 * the mpll_clk_en. 81 */ 82 regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, 83 IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | 84 IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | 85 IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | 86 IMX6Q_GPR13_SATA_SPD_MODE_MASK | 87 IMX6Q_GPR13_SATA_MPLL_SS_EN | 88 IMX6Q_GPR13_SATA_TX_ATTEN_MASK | 89 IMX6Q_GPR13_SATA_TX_BOOST_MASK | 90 IMX6Q_GPR13_SATA_TX_LVL_MASK | 91 IMX6Q_GPR13_SATA_MPLL_CLK_EN | 92 IMX6Q_GPR13_SATA_TX_EDGE_RATE, 93 IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB | 94 IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M | 95 IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | 96 IMX6Q_GPR13_SATA_SPD_MODE_3P0G | 97 IMX6Q_GPR13_SATA_MPLL_SS_EN | 98 IMX6Q_GPR13_SATA_TX_ATTEN_9_16 | 99 IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB | 100 IMX6Q_GPR13_SATA_TX_LVL_1_025_V); 101 regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, 102 IMX6Q_GPR13_SATA_MPLL_CLK_EN, 103 IMX6Q_GPR13_SATA_MPLL_CLK_EN); 104 } 105 106 usleep_range(1000, 2000); 107 108 return 0; 109 110 disable_regulator: 111 if (hpriv->target_pwr) 112 regulator_disable(hpriv->target_pwr); 113 114 return ret; 115 } 116 117 static void imx_sata_disable(struct ahci_host_priv *hpriv) 118 { 119 struct imx_ahci_priv *imxpriv = hpriv->plat_data; 120 121 if (imxpriv->no_device) 122 return; 123 124 if (imxpriv->type == AHCI_IMX6Q) { 125 regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, 126 IMX6Q_GPR13_SATA_MPLL_CLK_EN, 127 !IMX6Q_GPR13_SATA_MPLL_CLK_EN); 128 } 129 130 ahci_platform_disable_clks(hpriv); 131 132 if (hpriv->target_pwr) 133 regulator_disable(hpriv->target_pwr); 134 } 135 136 static void ahci_imx_error_handler(struct ata_port *ap) 137 { 138 u32 reg_val; 139 struct ata_device *dev; 140 struct ata_host *host = dev_get_drvdata(ap->dev); 141 struct ahci_host_priv *hpriv = host->private_data; 142 void __iomem *mmio = hpriv->mmio; 143 struct imx_ahci_priv *imxpriv = hpriv->plat_data; 144 145 ahci_error_handler(ap); 146 147 if (!(imxpriv->first_time) || ahci_imx_hotplug) 148 return; 149 150 imxpriv->first_time = false; 151 152 ata_for_each_dev(dev, &ap->link, ENABLED) 153 return; 154 /* 155 * Disable link to save power. An imx ahci port can't be recovered 156 * without full reset once the pddq mode is enabled making it 157 * impossible to use as part of libata LPM. 158 */ 159 reg_val = readl(mmio + PORT_PHY_CTL); 160 writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL); 161 imx_sata_disable(hpriv); 162 imxpriv->no_device = true; 163 } 164 165 static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, 166 unsigned long deadline) 167 { 168 struct ata_port *ap = link->ap; 169 struct ata_host *host = dev_get_drvdata(ap->dev); 170 struct ahci_host_priv *hpriv = host->private_data; 171 struct imx_ahci_priv *imxpriv = hpriv->plat_data; 172 int ret = -EIO; 173 174 if (imxpriv->type == AHCI_IMX53) 175 ret = ahci_pmp_retry_srst_ops.softreset(link, class, deadline); 176 else if (imxpriv->type == AHCI_IMX6Q) 177 ret = ahci_ops.softreset(link, class, deadline); 178 179 return ret; 180 } 181 182 static struct ata_port_operations ahci_imx_ops = { 183 .inherits = &ahci_ops, 184 .host_stop = ahci_imx_host_stop, 185 .error_handler = ahci_imx_error_handler, 186 .softreset = ahci_imx_softreset, 187 }; 188 189 static const struct ata_port_info ahci_imx_port_info = { 190 .flags = AHCI_FLAG_COMMON, 191 .pio_mask = ATA_PIO4, 192 .udma_mask = ATA_UDMA6, 193 .port_ops = &ahci_imx_ops, 194 }; 195 196 static const struct of_device_id imx_ahci_of_match[] = { 197 { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, 198 { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, 199 {}, 200 }; 201 MODULE_DEVICE_TABLE(of, imx_ahci_of_match); 202 203 static int imx_ahci_probe(struct platform_device *pdev) 204 { 205 struct device *dev = &pdev->dev; 206 const struct of_device_id *of_id; 207 struct ahci_host_priv *hpriv; 208 struct imx_ahci_priv *imxpriv; 209 unsigned int reg_val; 210 int ret; 211 212 of_id = of_match_device(imx_ahci_of_match, dev); 213 if (!of_id) 214 return -EINVAL; 215 216 imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); 217 if (!imxpriv) 218 return -ENOMEM; 219 220 imxpriv->no_device = false; 221 imxpriv->first_time = true; 222 imxpriv->type = (enum ahci_imx_type)of_id->data; 223 imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); 224 if (IS_ERR(imxpriv->ahb_clk)) { 225 dev_err(dev, "can't get ahb clock.\n"); 226 return PTR_ERR(imxpriv->ahb_clk); 227 } 228 229 if (imxpriv->type == AHCI_IMX6Q) { 230 imxpriv->gpr = syscon_regmap_lookup_by_compatible( 231 "fsl,imx6q-iomuxc-gpr"); 232 if (IS_ERR(imxpriv->gpr)) { 233 dev_err(dev, 234 "failed to find fsl,imx6q-iomux-gpr regmap\n"); 235 return PTR_ERR(imxpriv->gpr); 236 } 237 } 238 239 hpriv = ahci_platform_get_resources(pdev); 240 if (IS_ERR(hpriv)) 241 return PTR_ERR(hpriv); 242 243 hpriv->plat_data = imxpriv; 244 245 ret = imx_sata_enable(hpriv); 246 if (ret) 247 return ret; 248 249 /* 250 * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, 251 * and IP vendor specific register HOST_TIMER1MS. 252 * Configure CAP_SSS (support stagered spin up). 253 * Implement the port0. 254 * Get the ahb clock rate, and configure the TIMER1MS register. 255 */ 256 reg_val = readl(hpriv->mmio + HOST_CAP); 257 if (!(reg_val & HOST_CAP_SSS)) { 258 reg_val |= HOST_CAP_SSS; 259 writel(reg_val, hpriv->mmio + HOST_CAP); 260 } 261 reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL); 262 if (!(reg_val & 0x1)) { 263 reg_val |= 0x1; 264 writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL); 265 } 266 267 reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; 268 writel(reg_val, hpriv->mmio + HOST_TIMER1MS); 269 270 ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, 0, 0); 271 if (ret) 272 imx_sata_disable(hpriv); 273 274 return ret; 275 } 276 277 static void ahci_imx_host_stop(struct ata_host *host) 278 { 279 struct ahci_host_priv *hpriv = host->private_data; 280 281 imx_sata_disable(hpriv); 282 } 283 284 #ifdef CONFIG_PM_SLEEP 285 static int imx_ahci_suspend(struct device *dev) 286 { 287 struct ata_host *host = dev_get_drvdata(dev); 288 struct ahci_host_priv *hpriv = host->private_data; 289 int ret; 290 291 ret = ahci_platform_suspend_host(dev); 292 if (ret) 293 return ret; 294 295 imx_sata_disable(hpriv); 296 297 return 0; 298 } 299 300 static int imx_ahci_resume(struct device *dev) 301 { 302 struct ata_host *host = dev_get_drvdata(dev); 303 struct ahci_host_priv *hpriv = host->private_data; 304 int ret; 305 306 ret = imx_sata_enable(hpriv); 307 if (ret) 308 return ret; 309 310 return ahci_platform_resume_host(dev); 311 } 312 #endif 313 314 static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops, imx_ahci_suspend, imx_ahci_resume); 315 316 static struct platform_driver imx_ahci_driver = { 317 .probe = imx_ahci_probe, 318 .remove = ata_platform_remove_one, 319 .driver = { 320 .name = "ahci-imx", 321 .owner = THIS_MODULE, 322 .of_match_table = imx_ahci_of_match, 323 .pm = &ahci_imx_pm_ops, 324 }, 325 }; 326 module_platform_driver(imx_ahci_driver); 327 328 MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); 329 MODULE_AUTHOR("Richard Zhu <Hong-Xing.Zhu@freescale.com>"); 330 MODULE_LICENSE("GPL"); 331 MODULE_ALIAS("ahci:imx"); 332