1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2015 Linaro Ltd. 4 * Copyright (c) 2015 HiSilicon Limited. 5 */ 6 7 #include <linux/mfd/syscon.h> 8 #include <linux/mod_devicetable.h> 9 #include <linux/module.h> 10 #include <linux/platform_device.h> 11 #include <linux/phy/phy.h> 12 #include <linux/regmap.h> 13 14 #define SC_PERIPH_CTRL4 0x00c 15 16 #define CTRL4_PICO_SIDDQ BIT(6) 17 #define CTRL4_PICO_OGDISABLE BIT(8) 18 #define CTRL4_PICO_VBUSVLDEXT BIT(10) 19 #define CTRL4_PICO_VBUSVLDEXTSEL BIT(11) 20 #define CTRL4_OTG_PHY_SEL BIT(21) 21 22 #define SC_PERIPH_CTRL5 0x010 23 24 #define CTRL5_USBOTG_RES_SEL BIT(3) 25 #define CTRL5_PICOPHY_ACAENB BIT(4) 26 #define CTRL5_PICOPHY_BC_MODE BIT(5) 27 #define CTRL5_PICOPHY_CHRGSEL BIT(6) 28 #define CTRL5_PICOPHY_VDATSRCEND BIT(7) 29 #define CTRL5_PICOPHY_VDATDETENB BIT(8) 30 #define CTRL5_PICOPHY_DCDENB BIT(9) 31 #define CTRL5_PICOPHY_IDDIG BIT(10) 32 33 #define SC_PERIPH_CTRL8 0x018 34 #define SC_PERIPH_RSTEN0 0x300 35 #define SC_PERIPH_RSTDIS0 0x304 36 37 #define RST0_USBOTG_BUS BIT(4) 38 #define RST0_POR_PICOPHY BIT(5) 39 #define RST0_USBOTG BIT(6) 40 #define RST0_USBOTG_32K BIT(7) 41 42 #define EYE_PATTERN_PARA 0x7053348c 43 44 struct hi6220_priv { 45 struct regmap *reg; 46 struct device *dev; 47 }; 48 49 static void hi6220_phy_init(struct hi6220_priv *priv) 50 { 51 struct regmap *reg = priv->reg; 52 u32 val, mask; 53 54 val = RST0_USBOTG_BUS | RST0_POR_PICOPHY | 55 RST0_USBOTG | RST0_USBOTG_32K; 56 mask = val; 57 regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val); 58 regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val); 59 } 60 61 static int hi6220_phy_setup(struct hi6220_priv *priv, bool on) 62 { 63 struct regmap *reg = priv->reg; 64 u32 val, mask; 65 int ret; 66 67 if (on) { 68 val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB; 69 mask = val | CTRL5_PICOPHY_BC_MODE; 70 ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val); 71 if (ret) 72 goto out; 73 74 val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL | 75 CTRL4_OTG_PHY_SEL; 76 mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE; 77 ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); 78 if (ret) 79 goto out; 80 81 ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA); 82 if (ret) 83 goto out; 84 } else { 85 val = CTRL4_PICO_SIDDQ; 86 mask = val; 87 ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); 88 if (ret) 89 goto out; 90 } 91 92 return 0; 93 out: 94 dev_err(priv->dev, "failed to setup phy ret: %d\n", ret); 95 return ret; 96 } 97 98 static int hi6220_phy_start(struct phy *phy) 99 { 100 struct hi6220_priv *priv = phy_get_drvdata(phy); 101 102 return hi6220_phy_setup(priv, true); 103 } 104 105 static int hi6220_phy_exit(struct phy *phy) 106 { 107 struct hi6220_priv *priv = phy_get_drvdata(phy); 108 109 return hi6220_phy_setup(priv, false); 110 } 111 112 static const struct phy_ops hi6220_phy_ops = { 113 .init = hi6220_phy_start, 114 .exit = hi6220_phy_exit, 115 .owner = THIS_MODULE, 116 }; 117 118 static int hi6220_phy_probe(struct platform_device *pdev) 119 { 120 struct phy_provider *phy_provider; 121 struct device *dev = &pdev->dev; 122 struct phy *phy; 123 struct hi6220_priv *priv; 124 125 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 126 if (!priv) 127 return -ENOMEM; 128 129 priv->dev = dev; 130 priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node, 131 "hisilicon,peripheral-syscon"); 132 if (IS_ERR(priv->reg)) { 133 dev_err(dev, "no hisilicon,peripheral-syscon\n"); 134 return PTR_ERR(priv->reg); 135 } 136 137 hi6220_phy_init(priv); 138 139 phy = devm_phy_create(dev, NULL, &hi6220_phy_ops); 140 if (IS_ERR(phy)) 141 return PTR_ERR(phy); 142 143 phy_set_drvdata(phy, priv); 144 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 145 return PTR_ERR_OR_ZERO(phy_provider); 146 } 147 148 static const struct of_device_id hi6220_phy_of_match[] = { 149 {.compatible = "hisilicon,hi6220-usb-phy",}, 150 { }, 151 }; 152 MODULE_DEVICE_TABLE(of, hi6220_phy_of_match); 153 154 static struct platform_driver hi6220_phy_driver = { 155 .probe = hi6220_phy_probe, 156 .driver = { 157 .name = "hi6220-usb-phy", 158 .of_match_table = hi6220_phy_of_match, 159 } 160 }; 161 module_platform_driver(hi6220_phy_driver); 162 163 MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); 164 MODULE_ALIAS("platform:hi6220-usb-phy"); 165 MODULE_LICENSE("GPL"); 166