302 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Meson GXL and GXM USB2 PHY driver4 *5 * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>6 */7 8#include <linux/clk.h>9#include <linux/delay.h>10#include <linux/io.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/regmap.h>14#include <linux/reset.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17 18/* bits [31:27] are read-only */19#define U2P_R0 0x020 #define U2P_R0_BYPASS_SEL BIT(0)21 #define U2P_R0_BYPASS_DM_EN BIT(1)22 #define U2P_R0_BYPASS_DP_EN BIT(2)23 #define U2P_R0_TXBITSTUFF_ENH BIT(3)24 #define U2P_R0_TXBITSTUFF_EN BIT(4)25 #define U2P_R0_DM_PULLDOWN BIT(5)26 #define U2P_R0_DP_PULLDOWN BIT(6)27 #define U2P_R0_DP_VBUS_VLD_EXT_SEL BIT(7)28 #define U2P_R0_DP_VBUS_VLD_EXT BIT(8)29 #define U2P_R0_ADP_PRB_EN BIT(9)30 #define U2P_R0_ADP_DISCHARGE BIT(10)31 #define U2P_R0_ADP_CHARGE BIT(11)32 #define U2P_R0_DRV_VBUS BIT(12)33 #define U2P_R0_ID_PULLUP BIT(13)34 #define U2P_R0_LOOPBACK_EN_B BIT(14)35 #define U2P_R0_OTG_DISABLE BIT(15)36 #define U2P_R0_COMMON_ONN BIT(16)37 #define U2P_R0_FSEL_MASK GENMASK(19, 17)38 #define U2P_R0_REF_CLK_SEL_MASK GENMASK(21, 20)39 #define U2P_R0_POWER_ON_RESET BIT(22)40 #define U2P_R0_V_ATE_TEST_EN_B_MASK GENMASK(24, 23)41 #define U2P_R0_ID_SET_ID_DQ BIT(25)42 #define U2P_R0_ATE_RESET BIT(26)43 #define U2P_R0_FSV_MINUS BIT(27)44 #define U2P_R0_FSV_PLUS BIT(28)45 #define U2P_R0_BYPASS_DM_DATA BIT(29)46 #define U2P_R0_BYPASS_DP_DATA BIT(30)47 48#define U2P_R1 0x449 #define U2P_R1_BURN_IN_TEST BIT(0)50 #define U2P_R1_ACA_ENABLE BIT(1)51 #define U2P_R1_DCD_ENABLE BIT(2)52 #define U2P_R1_VDAT_SRC_EN_B BIT(3)53 #define U2P_R1_VDAT_DET_EN_B BIT(4)54 #define U2P_R1_CHARGES_SEL BIT(5)55 #define U2P_R1_TX_PREEMP_PULSE_TUNE BIT(6)56 #define U2P_R1_TX_PREEMP_AMP_TUNE_MASK GENMASK(8, 7)57 #define U2P_R1_TX_RES_TUNE_MASK GENMASK(10, 9)58 #define U2P_R1_TX_RISE_TUNE_MASK GENMASK(12, 11)59 #define U2P_R1_TX_VREF_TUNE_MASK GENMASK(16, 13)60 #define U2P_R1_TX_FSLS_TUNE_MASK GENMASK(20, 17)61 #define U2P_R1_TX_HSXV_TUNE_MASK GENMASK(22, 21)62 #define U2P_R1_OTG_TUNE_MASK GENMASK(25, 23)63 #define U2P_R1_SQRX_TUNE_MASK GENMASK(28, 26)64 #define U2P_R1_COMP_DIS_TUNE_MASK GENMASK(31, 29)65 66/* bits [31:14] are read-only */67#define U2P_R2 0x868 #define U2P_R2_TESTDATA_IN_MASK GENMASK(7, 0)69 #define U2P_R2_TESTADDR_MASK GENMASK(11, 8)70 #define U2P_R2_TESTDATA_OUT_SEL BIT(12)71 #define U2P_R2_TESTCLK BIT(13)72 #define U2P_R2_TESTDATA_OUT_MASK GENMASK(17, 14)73 #define U2P_R2_ACA_PIN_RANGE_C BIT(18)74 #define U2P_R2_ACA_PIN_RANGE_B BIT(19)75 #define U2P_R2_ACA_PIN_RANGE_A BIT(20)76 #define U2P_R2_ACA_PIN_GND BIT(21)77 #define U2P_R2_ACA_PIN_FLOAT BIT(22)78 #define U2P_R2_CHARGE_DETECT BIT(23)79 #define U2P_R2_DEVICE_SESSION_VALID BIT(24)80 #define U2P_R2_ADP_PROBE BIT(25)81 #define U2P_R2_ADP_SENSE BIT(26)82 #define U2P_R2_SESSION_END BIT(27)83 #define U2P_R2_VBUS_VALID BIT(28)84 #define U2P_R2_B_VALID BIT(29)85 #define U2P_R2_A_VALID BIT(30)86 #define U2P_R2_ID_DIG BIT(31)87 88#define U2P_R3 0xc89 90#define RESET_COMPLETE_TIME 50091 92struct phy_meson_gxl_usb2_priv {93 struct regmap *regmap;94 enum phy_mode mode;95 int is_enabled;96 struct clk *clk;97 struct reset_control *reset;98};99 100static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {101 .reg_bits = 8,102 .val_bits = 32,103 .reg_stride = 4,104 .max_register = U2P_R3,105};106 107static int phy_meson_gxl_usb2_init(struct phy *phy)108{109 struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);110 int ret;111 112 ret = reset_control_reset(priv->reset);113 if (ret)114 return ret;115 116 ret = clk_prepare_enable(priv->clk);117 if (ret) {118 reset_control_rearm(priv->reset);119 return ret;120 }121 122 return 0;123}124 125static int phy_meson_gxl_usb2_exit(struct phy *phy)126{127 struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);128 129 clk_disable_unprepare(priv->clk);130 reset_control_rearm(priv->reset);131 132 return 0;133}134 135static int phy_meson_gxl_usb2_reset(struct phy *phy)136{137 struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);138 139 if (priv->is_enabled) {140 /* reset the PHY and wait until settings are stabilized */141 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,142 U2P_R0_POWER_ON_RESET);143 udelay(RESET_COMPLETE_TIME);144 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,145 0);146 udelay(RESET_COMPLETE_TIME);147 }148 149 return 0;150}151 152static int phy_meson_gxl_usb2_set_mode(struct phy *phy,153 enum phy_mode mode, int submode)154{155 struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);156 157 switch (mode) {158 case PHY_MODE_USB_HOST:159 case PHY_MODE_USB_OTG:160 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,161 U2P_R0_DM_PULLDOWN);162 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,163 U2P_R0_DP_PULLDOWN);164 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,165 U2P_R0_ID_PULLUP);166 break;167 168 case PHY_MODE_USB_DEVICE:169 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,170 0);171 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,172 0);173 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,174 U2P_R0_ID_PULLUP);175 break;176 177 default:178 return -EINVAL;179 }180 181 phy_meson_gxl_usb2_reset(phy);182 183 priv->mode = mode;184 185 return 0;186}187 188static int phy_meson_gxl_usb2_power_off(struct phy *phy)189{190 struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);191 192 priv->is_enabled = 0;193 194 /* power off the PHY by putting it into reset mode */195 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,196 U2P_R0_POWER_ON_RESET);197 198 return 0;199}200 201static int phy_meson_gxl_usb2_power_on(struct phy *phy)202{203 struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);204 int ret;205 206 priv->is_enabled = 1;207 208 /* power on the PHY by taking it out of reset mode */209 regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);210 211 ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode, 0);212 if (ret) {213 phy_meson_gxl_usb2_power_off(phy);214 215 dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",216 priv->mode);217 return ret;218 }219 220 return 0;221}222 223static const struct phy_ops phy_meson_gxl_usb2_ops = {224 .init = phy_meson_gxl_usb2_init,225 .exit = phy_meson_gxl_usb2_exit,226 .power_on = phy_meson_gxl_usb2_power_on,227 .power_off = phy_meson_gxl_usb2_power_off,228 .set_mode = phy_meson_gxl_usb2_set_mode,229 .reset = phy_meson_gxl_usb2_reset,230 .owner = THIS_MODULE,231};232 233static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)234{235 struct device *dev = &pdev->dev;236 struct phy_provider *phy_provider;237 struct phy_meson_gxl_usb2_priv *priv;238 struct phy *phy;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 platform_set_drvdata(pdev, priv);247 248 base = devm_platform_ioremap_resource(pdev, 0);249 if (IS_ERR(base))250 return PTR_ERR(base);251 252 /* start in host mode */253 priv->mode = PHY_MODE_USB_HOST;254 255 priv->regmap = devm_regmap_init_mmio(dev, base,256 &phy_meson_gxl_usb2_regmap_conf);257 if (IS_ERR(priv->regmap))258 return PTR_ERR(priv->regmap);259 260 priv->clk = devm_clk_get_optional(dev, "phy");261 if (IS_ERR(priv->clk))262 return PTR_ERR(priv->clk);263 264 priv->reset = devm_reset_control_get_optional_shared(dev, "phy");265 if (IS_ERR(priv->reset))266 return PTR_ERR(priv->reset);267 268 phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);269 if (IS_ERR(phy)) {270 ret = PTR_ERR(phy);271 if (ret != -EPROBE_DEFER)272 dev_err(dev, "failed to create PHY\n");273 274 return ret;275 }276 277 phy_set_drvdata(phy, priv);278 279 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);280 281 return PTR_ERR_OR_ZERO(phy_provider);282}283 284static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {285 { .compatible = "amlogic,meson-gxl-usb2-phy", },286 { },287};288MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);289 290static struct platform_driver phy_meson_gxl_usb2_driver = {291 .probe = phy_meson_gxl_usb2_probe,292 .driver = {293 .name = "phy-meson-gxl-usb2",294 .of_match_table = phy_meson_gxl_usb2_of_match,295 },296};297module_platform_driver(phy_meson_gxl_usb2_driver);298 299MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");300MODULE_DESCRIPTION("Meson GXL and GXM USB2 PHY driver");301MODULE_LICENSE("GPL v2");302