271 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.4 */5 6#include <linux/io.h>7#include <linux/iopoll.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/time.h>12#include <linux/delay.h>13#include <linux/clk.h>14#include <linux/slab.h>15#include <linux/platform_device.h>16#include <linux/phy/phy.h>17 18/* PHY registers */19#define UNIPHY_PLL_REFCLK_CFG 0x00020#define UNIPHY_PLL_PWRGEN_CFG 0x01421#define UNIPHY_PLL_GLB_CFG 0x02022#define UNIPHY_PLL_SDM_CFG0 0x03823#define UNIPHY_PLL_SDM_CFG1 0x03C24#define UNIPHY_PLL_SDM_CFG2 0x04025#define UNIPHY_PLL_SDM_CFG3 0x04426#define UNIPHY_PLL_SDM_CFG4 0x04827#define UNIPHY_PLL_SSC_CFG0 0x04C28#define UNIPHY_PLL_SSC_CFG1 0x05029#define UNIPHY_PLL_SSC_CFG2 0x05430#define UNIPHY_PLL_SSC_CFG3 0x05831#define UNIPHY_PLL_LKDET_CFG0 0x05C32#define UNIPHY_PLL_LKDET_CFG1 0x06033#define UNIPHY_PLL_LKDET_CFG2 0x06434#define UNIPHY_PLL_CAL_CFG0 0x06C35#define UNIPHY_PLL_CAL_CFG8 0x08C36#define UNIPHY_PLL_CAL_CFG9 0x09037#define UNIPHY_PLL_CAL_CFG10 0x09438#define UNIPHY_PLL_CAL_CFG11 0x09839#define UNIPHY_PLL_STATUS 0x0C040 41#define SATA_PHY_SER_CTRL 0x10042#define SATA_PHY_TX_DRIV_CTRL0 0x10443#define SATA_PHY_TX_DRIV_CTRL1 0x10844#define SATA_PHY_TX_IMCAL0 0x11C45#define SATA_PHY_TX_IMCAL2 0x12446#define SATA_PHY_RX_IMCAL0 0x12847#define SATA_PHY_EQUAL 0x13C48#define SATA_PHY_OOB_TERM 0x14449#define SATA_PHY_CDR_CTRL0 0x14850#define SATA_PHY_CDR_CTRL1 0x14C51#define SATA_PHY_CDR_CTRL2 0x15052#define SATA_PHY_CDR_CTRL3 0x15453#define SATA_PHY_PI_CTRL0 0x16854#define SATA_PHY_POW_DWN_CTRL0 0x18055#define SATA_PHY_POW_DWN_CTRL1 0x18456#define SATA_PHY_TX_DATA_CTRL 0x18857#define SATA_PHY_ALIGNP 0x1A458#define SATA_PHY_TX_IMCAL_STAT 0x1E459#define SATA_PHY_RX_IMCAL_STAT 0x1E860 61#define UNIPHY_PLL_LOCK BIT(0)62#define SATA_PHY_TX_CAL BIT(0)63#define SATA_PHY_RX_CAL BIT(0)64 65/* default timeout set to 1 sec */66#define TIMEOUT_MS 1000067#define DELAY_INTERVAL_US 10068 69struct qcom_apq8064_sata_phy {70 void __iomem *mmio;71 struct clk *cfg_clk;72 struct device *dev;73};74 75/* Helper function to do poll and timeout */76static int poll_timeout(void __iomem *addr, u32 mask)77{78 u32 val;79 80 return readl_relaxed_poll_timeout(addr, val, (val & mask),81 DELAY_INTERVAL_US, TIMEOUT_MS * 1000);82}83 84static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)85{86 struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);87 void __iomem *base = phy->mmio;88 int ret = 0;89 90 /* SATA phy initialization */91 writel_relaxed(0x01, base + SATA_PHY_SER_CTRL);92 writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0);93 /* Make sure the power down happens before power up */94 mb();95 usleep_range(10, 60);96 97 writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);98 writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);99 writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);100 writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);101 writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2);102 103 /* Write UNIPHYPLL registers to configure PLL */104 writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG);105 writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG);106 107 writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0);108 writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8);109 writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9);110 writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10);111 writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11);112 113 writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0);114 writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1);115 writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2);116 writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3);117 writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4);118 119 writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0);120 writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1);121 writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2);122 writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3);123 124 writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0);125 writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1);126 127 writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG);128 /* make sure global config LDO power down happens before power up */129 mb();130 131 writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG);132 writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);133 134 /* PLL Lock wait */135 ret = poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);136 if (ret) {137 dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");138 return ret;139 }140 141 /* TX Calibration */142 ret = poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);143 if (ret) {144 dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");145 return ret;146 }147 148 /* RX Calibration */149 ret = poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);150 if (ret) {151 dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");152 return ret;153 }154 155 /* SATA phy calibrated successfully, power up to functional mode */156 writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);157 writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);158 writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);159 160 writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1);161 writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0);162 writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1);163 writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2);164 writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0);165 writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3);166 writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);167 168 writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL);169 writel_relaxed(0x43, base + SATA_PHY_ALIGNP);170 writel_relaxed(0x04, base + SATA_PHY_OOB_TERM);171 172 writel_relaxed(0x01, base + SATA_PHY_EQUAL);173 writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0);174 writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1);175 176 return 0;177}178 179static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)180{181 struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);182 void __iomem *base = phy->mmio;183 184 /* Power down PHY */185 writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0);186 writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1);187 188 /* Power down PLL block */189 writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG);190 191 return 0;192}193 194static const struct phy_ops qcom_apq8064_sata_phy_ops = {195 .init = qcom_apq8064_sata_phy_init,196 .exit = qcom_apq8064_sata_phy_exit,197 .owner = THIS_MODULE,198};199 200static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)201{202 struct qcom_apq8064_sata_phy *phy;203 struct device *dev = &pdev->dev;204 struct phy_provider *phy_provider;205 struct phy *generic_phy;206 int ret;207 208 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);209 if (!phy)210 return -ENOMEM;211 212 phy->mmio = devm_platform_ioremap_resource(pdev, 0);213 if (IS_ERR(phy->mmio))214 return PTR_ERR(phy->mmio);215 216 generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops);217 if (IS_ERR(generic_phy)) {218 dev_err(dev, "%s: failed to create phy\n", __func__);219 return PTR_ERR(generic_phy);220 }221 222 phy->dev = dev;223 phy_set_drvdata(generic_phy, phy);224 platform_set_drvdata(pdev, phy);225 226 phy->cfg_clk = devm_clk_get(dev, "cfg");227 if (IS_ERR(phy->cfg_clk)) {228 dev_err(dev, "Failed to get sata cfg clock\n");229 return PTR_ERR(phy->cfg_clk);230 }231 232 ret = clk_prepare_enable(phy->cfg_clk);233 if (ret)234 return ret;235 236 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);237 if (IS_ERR(phy_provider)) {238 clk_disable_unprepare(phy->cfg_clk);239 dev_err(dev, "%s: failed to register phy\n", __func__);240 return PTR_ERR(phy_provider);241 }242 243 return 0;244}245 246static void qcom_apq8064_sata_phy_remove(struct platform_device *pdev)247{248 struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev);249 250 clk_disable_unprepare(phy->cfg_clk);251}252 253static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = {254 { .compatible = "qcom,apq8064-sata-phy" },255 { },256};257MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match);258 259static struct platform_driver qcom_apq8064_sata_phy_driver = {260 .probe = qcom_apq8064_sata_phy_probe,261 .remove_new = qcom_apq8064_sata_phy_remove,262 .driver = {263 .name = "qcom-apq8064-sata-phy",264 .of_match_table = qcom_apq8064_sata_phy_of_match,265 }266};267module_platform_driver(qcom_apq8064_sata_phy_driver);268 269MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver");270MODULE_LICENSE("GPL v2");271