Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: usb: Add USB2.0 phy driver for Sunplus SP7021

Add USB2.0 phy driver for Sunplus SP7021

Signed-off-by: Vincent Shih <vincent.sunplus@gmail.com>
Link: https://lore.kernel.org/r/1658717052-26142-2-git-send-email-vincent.sunplus@gmail.com
[vkoul: remove trailing line in driver file]
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Vincent Shih and committed by
Vinod Koul
99d9ccd9 c8c5d5e8

+320
+8
MAINTAINERS
··· 19490 19490 F: Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml 19491 19491 F: drivers/nvmem/sunplus-ocotp.c 19492 19492 19493 + SUNPLUS USB2 PHY DRIVER 19494 + M: Vincent Shih <vincent.sunplus@gmail.com> 19495 + L: linux-usb@vger.kernel.org 19496 + S: Maintained 19497 + F: drivers/phy/sunplus/Kconfig 19498 + F: drivers/phy/sunplus/Makefile 19499 + F: drivers/phy/sunplus/phy-sunplus-usb2.c 19500 + 19493 19501 SUNPLUS PWM DRIVER 19494 19502 M: Hammer Hsieh <hammerh0314@gmail.com> 19495 19503 S: Maintained
+1
drivers/phy/Kconfig
··· 91 91 source "drivers/phy/samsung/Kconfig" 92 92 source "drivers/phy/socionext/Kconfig" 93 93 source "drivers/phy/st/Kconfig" 94 + source "drivers/phy/sunplus/Kconfig" 94 95 source "drivers/phy/tegra/Kconfig" 95 96 source "drivers/phy/ti/Kconfig" 96 97 source "drivers/phy/intel/Kconfig"
+1
drivers/phy/Makefile
··· 31 31 samsung/ \ 32 32 socionext/ \ 33 33 st/ \ 34 + sunplus/ \ 34 35 tegra/ \ 35 36 ti/ \ 36 37 xilinx/
+12
drivers/phy/sunplus/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + 3 + config PHY_SUNPLUS_USB 4 + tristate "Sunplus SP7021 USB 2.0 PHY driver" 5 + depends on OF && (SOC_SP7021 || COMPILE_TEST) 6 + select GENERIC_PHY 7 + help 8 + Enable this to support the USB 2.0 PHY on Sunplus SP7021 9 + SoC. The USB 2.0 PHY controller supports battery charger 10 + and synchronous signals, various power down modes including 11 + operating, partial and suspend modes, and high-speed, 12 + full-speed and low-speed data transfer.
+2
drivers/phy/sunplus/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + obj-$(CONFIG_PHY_SUNPLUS_USB) += phy-sunplus-usb2.o
+296
drivers/phy/sunplus/phy-sunplus-usb2.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Sunplus SP7021 USB 2.0 phy driver 5 + * 6 + * Copyright (C) 2022 Sunplus Technology Inc., All rights reserved. 7 + * 8 + * Note 1 : non-posted write command for the registers accesses of 9 + * Sunplus SP7021. 10 + * 11 + */ 12 + 13 + #include <linux/bitfield.h> 14 + #include <linux/clk.h> 15 + #include <linux/delay.h> 16 + #include <linux/io.h> 17 + #include <linux/module.h> 18 + #include <linux/nvmem-consumer.h> 19 + #include <linux/of_platform.h> 20 + #include <linux/phy/phy.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/reset.h> 23 + 24 + #define HIGH_MASK_BITS GENMASK(31, 16) 25 + #define LOW_MASK_BITS GENMASK(15, 0) 26 + #define OTP_DISC_LEVEL_DEFAULT 0xd 27 + 28 + /* GROUP UPHY */ 29 + #define CONFIG1 0x4 30 + #define J_HS_TX_PWRSAV BIT(5) 31 + #define CONFIG3 0xc 32 + #define J_FORCE_DISC_ON BIT(5) 33 + #define J_DEBUG_CTRL_ADDR_MACRO BIT(0) 34 + #define CONFIG7 0x1c 35 + #define J_DISC 0X1f 36 + #define CONFIG9 0x24 37 + #define J_ECO_PATH BIT(6) 38 + #define CONFIG16 0x40 39 + #define J_TBCWAIT_MASK GENMASK(6, 5) 40 + #define J_TBCWAIT_1P1_MS FIELD_PREP(J_TBCWAIT_MASK, 0) 41 + #define J_TVDM_SRC_DIS_MASK GENMASK(4, 3) 42 + #define J_TVDM_SRC_DIS_8P2_MS FIELD_PREP(J_TVDM_SRC_DIS_MASK, 3) 43 + #define J_TVDM_SRC_EN_MASK GENMASK(2, 1) 44 + #define J_TVDM_SRC_EN_1P6_MS FIELD_PREP(J_TVDM_SRC_EN_MASK, 0) 45 + #define J_BC_EN BIT(0) 46 + #define CONFIG17 0x44 47 + #define IBG_TRIM0_MASK GENMASK(7, 5) 48 + #define IBG_TRIM0_SSLVHT FIELD_PREP(IBG_TRIM0_MASK, 4) 49 + #define J_VDATREE_TRIM_MASK GENMASK(4, 1) 50 + #define J_VDATREE_TRIM_DEFAULT FIELD_PREP(J_VDATREE_TRIM_MASK, 9) 51 + #define CONFIG23 0x5c 52 + #define PROB_MASK GENMASK(5, 3) 53 + #define PROB FIELD_PREP(PROB_MASK, 7) 54 + 55 + /* GROUP MOON4 */ 56 + #define UPHY_CONTROL0 0x0 57 + #define UPHY_CONTROL1 0x4 58 + #define UPHY_CONTROL2 0x8 59 + #define MO1_UPHY_RX_CLK_SEL BIT(6) 60 + #define MASK_MO1_UPHY_RX_CLK_SEL BIT(6 + 16) 61 + #define UPHY_CONTROL3 0xc 62 + #define MO1_UPHY_PLL_POWER_OFF_SEL BIT(7) 63 + #define MASK_MO1_UPHY_PLL_POWER_OFF_SEL BIT(7 + 16) 64 + #define MO1_UPHY_PLL_POWER_OFF BIT(3) 65 + #define MASK_UPHY_PLL_POWER_OFF BIT(3 + 16) 66 + 67 + struct sp_usbphy { 68 + struct device *dev; 69 + struct resource *phy_res_mem; 70 + struct resource *moon4_res_mem; 71 + struct reset_control *rstc; 72 + struct clk *phy_clk; 73 + void __iomem *phy_regs; 74 + void __iomem *moon4_regs; 75 + u32 disc_vol_addr_off; 76 + }; 77 + 78 + static int update_disc_vol(struct sp_usbphy *usbphy) 79 + { 80 + struct nvmem_cell *cell; 81 + char *disc_name = "disc_vol"; 82 + ssize_t otp_l = 0; 83 + char *otp_v; 84 + u32 val, set; 85 + 86 + cell = nvmem_cell_get(usbphy->dev, disc_name); 87 + if (IS_ERR_OR_NULL(cell)) { 88 + if (PTR_ERR(cell) == -EPROBE_DEFER) 89 + return -EPROBE_DEFER; 90 + } 91 + 92 + otp_v = nvmem_cell_read(cell, &otp_l); 93 + nvmem_cell_put(cell); 94 + 95 + if (otp_v) { 96 + set = *(otp_v + 1); 97 + set = (set << (sizeof(char) * 8)) | *otp_v; 98 + set = (set >> usbphy->disc_vol_addr_off) & J_DISC; 99 + } 100 + 101 + if (!otp_v || set == 0) 102 + set = OTP_DISC_LEVEL_DEFAULT; 103 + 104 + val = readl(usbphy->phy_regs + CONFIG7); 105 + val = (val & ~J_DISC) | set; 106 + writel(val, usbphy->phy_regs + CONFIG7); 107 + 108 + return 0; 109 + } 110 + 111 + static int sp_uphy_init(struct phy *phy) 112 + { 113 + struct sp_usbphy *usbphy = phy_get_drvdata(phy); 114 + u32 val; 115 + int ret; 116 + 117 + ret = clk_prepare_enable(usbphy->phy_clk); 118 + if (ret) 119 + goto err_clk; 120 + 121 + ret = reset_control_deassert(usbphy->rstc); 122 + if (ret) 123 + goto err_reset; 124 + 125 + /* Default value modification */ 126 + writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0); 127 + writel(HIGH_MASK_BITS | 0x8747, usbphy->moon4_regs + UPHY_CONTROL1); 128 + 129 + /* disconnect voltage */ 130 + ret = update_disc_vol(usbphy); 131 + if (ret < 0) 132 + return ret; 133 + 134 + /* board uphy 0 internal register modification for tid certification */ 135 + val = readl(usbphy->phy_regs + CONFIG9); 136 + val &= ~(J_ECO_PATH); 137 + writel(val, usbphy->phy_regs + CONFIG9); 138 + 139 + val = readl(usbphy->phy_regs + CONFIG1); 140 + val &= ~(J_HS_TX_PWRSAV); 141 + writel(val, usbphy->phy_regs + CONFIG1); 142 + 143 + val = readl(usbphy->phy_regs + CONFIG23); 144 + val = (val & ~PROB) | PROB; 145 + writel(val, usbphy->phy_regs + CONFIG23); 146 + 147 + /* port 0 uphy clk fix */ 148 + writel(MASK_MO1_UPHY_RX_CLK_SEL | MO1_UPHY_RX_CLK_SEL, 149 + usbphy->moon4_regs + UPHY_CONTROL2); 150 + 151 + /* battery charger */ 152 + writel(J_TBCWAIT_1P1_MS | J_TVDM_SRC_DIS_8P2_MS | J_TVDM_SRC_EN_1P6_MS | J_BC_EN, 153 + usbphy->phy_regs + CONFIG16); 154 + writel(IBG_TRIM0_SSLVHT | J_VDATREE_TRIM_DEFAULT, usbphy->phy_regs + CONFIG17); 155 + 156 + /* chirp mode */ 157 + writel(J_FORCE_DISC_ON | J_DEBUG_CTRL_ADDR_MACRO, usbphy->phy_regs + CONFIG3); 158 + 159 + return 0; 160 + 161 + err_reset: 162 + reset_control_assert(usbphy->rstc); 163 + err_clk: 164 + clk_disable_unprepare(usbphy->phy_clk); 165 + 166 + return ret; 167 + } 168 + 169 + static int sp_uphy_power_on(struct phy *phy) 170 + { 171 + struct sp_usbphy *usbphy = phy_get_drvdata(phy); 172 + u32 pll_pwr_on, pll_pwr_off; 173 + 174 + /* PLL power off/on twice */ 175 + pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) 176 + | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF; 177 + pll_pwr_on = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) 178 + | MO1_UPHY_PLL_POWER_OFF_SEL; 179 + 180 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, 181 + usbphy->moon4_regs + UPHY_CONTROL3); 182 + mdelay(1); 183 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on, 184 + usbphy->moon4_regs + UPHY_CONTROL3); 185 + mdelay(1); 186 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, 187 + usbphy->moon4_regs + UPHY_CONTROL3); 188 + mdelay(1); 189 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_on, 190 + usbphy->moon4_regs + UPHY_CONTROL3); 191 + mdelay(1); 192 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0, 193 + usbphy->moon4_regs + UPHY_CONTROL3); 194 + 195 + return 0; 196 + } 197 + 198 + static int sp_uphy_power_off(struct phy *phy) 199 + { 200 + struct sp_usbphy *usbphy = phy_get_drvdata(phy); 201 + u32 pll_pwr_off; 202 + 203 + pll_pwr_off = (readl(usbphy->moon4_regs + UPHY_CONTROL3) & ~LOW_MASK_BITS) 204 + | MO1_UPHY_PLL_POWER_OFF_SEL | MO1_UPHY_PLL_POWER_OFF; 205 + 206 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | pll_pwr_off, 207 + usbphy->moon4_regs + UPHY_CONTROL3); 208 + mdelay(1); 209 + writel(MASK_MO1_UPHY_PLL_POWER_OFF_SEL | MASK_UPHY_PLL_POWER_OFF | 0x0, 210 + usbphy->moon4_regs + UPHY_CONTROL3); 211 + 212 + return 0; 213 + } 214 + 215 + static int sp_uphy_exit(struct phy *phy) 216 + { 217 + struct sp_usbphy *usbphy = phy_get_drvdata(phy); 218 + 219 + reset_control_assert(usbphy->rstc); 220 + clk_disable_unprepare(usbphy->phy_clk); 221 + 222 + return 0; 223 + } 224 + 225 + static const struct phy_ops sp_uphy_ops = { 226 + .init = sp_uphy_init, 227 + .power_on = sp_uphy_power_on, 228 + .power_off = sp_uphy_power_off, 229 + .exit = sp_uphy_exit, 230 + }; 231 + 232 + static const struct of_device_id sp_uphy_dt_ids[] = { 233 + {.compatible = "sunplus,sp7021-usb2-phy", }, 234 + { } 235 + }; 236 + MODULE_DEVICE_TABLE(of, sp_uphy_dt_ids); 237 + 238 + static int sp_usb_phy_probe(struct platform_device *pdev) 239 + { 240 + struct sp_usbphy *usbphy; 241 + struct phy_provider *phy_provider; 242 + struct phy *phy; 243 + int ret; 244 + 245 + usbphy = devm_kzalloc(&pdev->dev, sizeof(*usbphy), GFP_KERNEL); 246 + if (!usbphy) 247 + return -ENOMEM; 248 + 249 + usbphy->dev = &pdev->dev; 250 + 251 + usbphy->phy_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); 252 + usbphy->phy_regs = devm_ioremap_resource(&pdev->dev, usbphy->phy_res_mem); 253 + if (IS_ERR(usbphy->phy_regs)) 254 + return PTR_ERR(usbphy->phy_regs); 255 + 256 + usbphy->moon4_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon4"); 257 + usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start, 258 + resource_size(usbphy->moon4_res_mem)); 259 + if (IS_ERR(usbphy->moon4_regs)) 260 + return PTR_ERR(usbphy->moon4_regs); 261 + 262 + usbphy->phy_clk = devm_clk_get(&pdev->dev, NULL); 263 + if (IS_ERR(usbphy->phy_clk)) 264 + return PTR_ERR(usbphy->phy_clk); 265 + 266 + usbphy->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 267 + if (IS_ERR(usbphy->rstc)) 268 + return PTR_ERR(usbphy->rstc); 269 + 270 + of_property_read_u32(pdev->dev.of_node, "sunplus,disc-vol-addr-off", 271 + &usbphy->disc_vol_addr_off); 272 + 273 + phy = devm_phy_create(&pdev->dev, NULL, &sp_uphy_ops); 274 + if (IS_ERR(phy)) { 275 + ret = -PTR_ERR(phy); 276 + return ret; 277 + } 278 + 279 + phy_set_drvdata(phy, usbphy); 280 + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 281 + 282 + return PTR_ERR_OR_ZERO(phy_provider); 283 + } 284 + 285 + static struct platform_driver sunplus_usb_phy_driver = { 286 + .probe = sp_usb_phy_probe, 287 + .driver = { 288 + .name = "sunplus-usb2-phy", 289 + .of_match_table = sp_uphy_dt_ids, 290 + }, 291 + }; 292 + module_platform_driver(sunplus_usb_phy_driver); 293 + 294 + MODULE_AUTHOR("Vincent Shih <vincent.shih@sunplus.com>"); 295 + MODULE_DESCRIPTION("Sunplus USB 2.0 phy driver"); 296 + MODULE_LICENSE("GPL");