302 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Intel Keem Bay USB PHY driver4 * Copyright (C) 2020 Intel Corporation5 */6 7#include <linux/bitfield.h>8#include <linux/bits.h>9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16 17/* USS (USB Subsystem) clock control registers */18#define USS_CPR_CLK_EN 0x0019#define USS_CPR_CLK_SET 0x0420#define USS_CPR_CLK_CLR 0x0821#define USS_CPR_RST_EN 0x1022#define USS_CPR_RST_SET 0x1423#define USS_CPR_RST_CLR 0x1824 25/* USS clock/reset bit fields */26#define USS_CPR_PHY_TST BIT(6)27#define USS_CPR_LOW_JIT BIT(5)28#define USS_CPR_CORE BIT(4)29#define USS_CPR_SUSPEND BIT(3)30#define USS_CPR_ALT_REF BIT(2)31#define USS_CPR_REF BIT(1)32#define USS_CPR_SYS BIT(0)33#define USS_CPR_MASK GENMASK(6, 0)34 35/* USS APB slave registers */36#define USS_USB_CTRL_CFG0 0x1037#define VCC_RESET_N_MASK BIT(31)38 39#define USS_USB_PHY_CFG0 0x3040#define POR_MASK BIT(15)41#define PHY_RESET_MASK BIT(14)42#define PHY_REF_USE_PAD_MASK BIT(5)43 44#define USS_USB_PHY_CFG6 0x6445#define PHY0_SRAM_EXT_LD_DONE_MASK BIT(23)46 47#define USS_USB_PARALLEL_IF_CTRL 0xa048#define USB_PHY_CR_PARA_SEL_MASK BIT(2)49 50#define USS_USB_TSET_SIGNALS_AND_GLOB 0xac51#define USB_PHY_CR_PARA_CLK_EN_MASK BIT(7)52 53#define USS_USB_STATUS_REG 0xb854#define PHY0_SRAM_INIT_DONE_MASK BIT(3)55 56#define USS_USB_TIEOFFS_CONSTANTS_REG1 0xc057#define IDDQ_ENABLE_MASK BIT(10)58 59struct keembay_usb_phy {60 struct device *dev;61 struct regmap *regmap_cpr;62 struct regmap *regmap_slv;63};64 65static const struct regmap_config keembay_regmap_config = {66 .reg_bits = 32,67 .val_bits = 32,68 .reg_stride = 4,69 .max_register = USS_USB_TIEOFFS_CONSTANTS_REG1,70};71 72static int keembay_usb_clocks_on(struct keembay_usb_phy *priv)73{74 int ret;75 76 ret = regmap_update_bits(priv->regmap_cpr, USS_CPR_CLK_SET,77 USS_CPR_MASK, USS_CPR_MASK);78 if (ret) {79 dev_err(priv->dev, "error clock set: %d\n", ret);80 return ret;81 }82 83 ret = regmap_update_bits(priv->regmap_cpr, USS_CPR_RST_SET,84 USS_CPR_MASK, USS_CPR_MASK);85 if (ret) {86 dev_err(priv->dev, "error reset set: %d\n", ret);87 return ret;88 }89 90 ret = regmap_update_bits(priv->regmap_slv,91 USS_USB_TIEOFFS_CONSTANTS_REG1,92 IDDQ_ENABLE_MASK,93 FIELD_PREP(IDDQ_ENABLE_MASK, 0));94 if (ret) {95 dev_err(priv->dev, "error iddq disable: %d\n", ret);96 return ret;97 }98 99 /* Wait 30us to ensure all analog blocks are powered up. */100 usleep_range(30, 60);101 102 ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG0,103 PHY_REF_USE_PAD_MASK,104 FIELD_PREP(PHY_REF_USE_PAD_MASK, 1));105 if (ret)106 dev_err(priv->dev, "error ref clock select: %d\n", ret);107 108 return ret;109}110 111static int keembay_usb_core_off(struct keembay_usb_phy *priv)112{113 int ret;114 115 ret = regmap_update_bits(priv->regmap_slv, USS_USB_CTRL_CFG0,116 VCC_RESET_N_MASK,117 FIELD_PREP(VCC_RESET_N_MASK, 0));118 if (ret)119 dev_err(priv->dev, "error core reset: %d\n", ret);120 121 return ret;122}123 124static int keembay_usb_core_on(struct keembay_usb_phy *priv)125{126 int ret;127 128 ret = regmap_update_bits(priv->regmap_slv, USS_USB_CTRL_CFG0,129 VCC_RESET_N_MASK,130 FIELD_PREP(VCC_RESET_N_MASK, 1));131 if (ret)132 dev_err(priv->dev, "error core on: %d\n", ret);133 134 return ret;135}136 137static int keembay_usb_phys_on(struct keembay_usb_phy *priv)138{139 int ret;140 141 ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG0,142 POR_MASK | PHY_RESET_MASK,143 FIELD_PREP(POR_MASK | PHY_RESET_MASK, 0));144 if (ret)145 dev_err(priv->dev, "error phys on: %d\n", ret);146 147 return ret;148}149 150static int keembay_usb_phy_init(struct phy *phy)151{152 struct keembay_usb_phy *priv = phy_get_drvdata(phy);153 u32 val;154 int ret;155 156 ret = keembay_usb_core_off(priv);157 if (ret)158 return ret;159 160 /*161 * According to Keem Bay datasheet, wait minimum 20us after clock162 * enable before bringing PHYs out of reset.163 */164 usleep_range(20, 40);165 166 ret = keembay_usb_phys_on(priv);167 if (ret)168 return ret;169 170 ret = regmap_update_bits(priv->regmap_slv,171 USS_USB_TSET_SIGNALS_AND_GLOB,172 USB_PHY_CR_PARA_CLK_EN_MASK,173 FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 0));174 if (ret) {175 dev_err(priv->dev, "error cr clock disable: %d\n", ret);176 return ret;177 }178 179 /*180 * According to Keem Bay datasheet, wait 2us after disabling the181 * clock into the USB 3.x parallel interface.182 */183 udelay(2);184 185 ret = regmap_update_bits(priv->regmap_slv,186 USS_USB_PARALLEL_IF_CTRL,187 USB_PHY_CR_PARA_SEL_MASK,188 FIELD_PREP(USB_PHY_CR_PARA_SEL_MASK, 1));189 if (ret) {190 dev_err(priv->dev, "error cr select: %d\n", ret);191 return ret;192 }193 194 ret = regmap_update_bits(priv->regmap_slv,195 USS_USB_TSET_SIGNALS_AND_GLOB,196 USB_PHY_CR_PARA_CLK_EN_MASK,197 FIELD_PREP(USB_PHY_CR_PARA_CLK_EN_MASK, 1));198 if (ret) {199 dev_err(priv->dev, "error cr clock enable: %d\n", ret);200 return ret;201 }202 203 ret = regmap_read_poll_timeout(priv->regmap_slv, USS_USB_STATUS_REG,204 val, val & PHY0_SRAM_INIT_DONE_MASK,205 USEC_PER_MSEC, 10 * USEC_PER_MSEC);206 if (ret) {207 dev_err(priv->dev, "SRAM init not done: %d\n", ret);208 return ret;209 }210 211 ret = regmap_update_bits(priv->regmap_slv, USS_USB_PHY_CFG6,212 PHY0_SRAM_EXT_LD_DONE_MASK,213 FIELD_PREP(PHY0_SRAM_EXT_LD_DONE_MASK, 1));214 if (ret) {215 dev_err(priv->dev, "error SRAM init done set: %d\n", ret);216 return ret;217 }218 219 /*220 * According to Keem Bay datasheet, wait 20us after setting the221 * SRAM load done bit, before releasing the controller reset.222 */223 usleep_range(20, 40);224 225 return keembay_usb_core_on(priv);226}227 228static const struct phy_ops ops = {229 .init = keembay_usb_phy_init,230 .owner = THIS_MODULE,231};232 233static int keembay_usb_phy_probe(struct platform_device *pdev)234{235 struct device *dev = &pdev->dev;236 struct keembay_usb_phy *priv;237 struct phy *generic_phy;238 struct phy_provider *phy_provider;239 void __iomem *base;240 int ret;241 242 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);243 if (!priv)244 return -ENOMEM;245 246 base = devm_platform_ioremap_resource_byname(pdev, "cpr-apb-base");247 if (IS_ERR(base))248 return PTR_ERR(base);249 250 priv->regmap_cpr = devm_regmap_init_mmio(dev, base,251 &keembay_regmap_config);252 if (IS_ERR(priv->regmap_cpr))253 return PTR_ERR(priv->regmap_cpr);254 255 base = devm_platform_ioremap_resource_byname(pdev, "slv-apb-base");256 if (IS_ERR(base))257 return PTR_ERR(base);258 259 priv->regmap_slv = devm_regmap_init_mmio(dev, base,260 &keembay_regmap_config);261 if (IS_ERR(priv->regmap_slv))262 return PTR_ERR(priv->regmap_slv);263 264 generic_phy = devm_phy_create(dev, dev->of_node, &ops);265 if (IS_ERR(generic_phy))266 return dev_err_probe(dev, PTR_ERR(generic_phy),267 "failed to create PHY\n");268 269 phy_set_drvdata(generic_phy, priv);270 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);271 if (IS_ERR(phy_provider))272 return dev_err_probe(dev, PTR_ERR(phy_provider),273 "failed to register phy provider\n");274 275 /* Setup USB subsystem clocks */276 ret = keembay_usb_clocks_on(priv);277 if (ret)278 return ret;279 280 /* and turn on the DWC3 core, prior to DWC3 driver init. */281 return keembay_usb_core_on(priv);282}283 284static const struct of_device_id keembay_usb_phy_dt_ids[] = {285 { .compatible = "intel,keembay-usb-phy" },286 {}287};288MODULE_DEVICE_TABLE(of, keembay_usb_phy_dt_ids);289 290static struct platform_driver keembay_usb_phy_driver = {291 .probe = keembay_usb_phy_probe,292 .driver = {293 .name = "keembay-usb-phy",294 .of_match_table = keembay_usb_phy_dt_ids,295 },296};297module_platform_driver(keembay_usb_phy_driver);298 299MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>");300MODULE_DESCRIPTION("Intel Keem Bay USB PHY driver");301MODULE_LICENSE("GPL v2");302