300 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/*4 * Sunplus SP7021 USB 2.0 phy driver5 *6 * Copyright (C) 2022 Sunplus Technology Inc., All rights reserved.7 *8 * Note 1 : non-posted write command for the registers accesses of9 * 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.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 0xd27 28/* GROUP UPHY */29#define CONFIG1 0x430#define J_HS_TX_PWRSAV BIT(5)31#define CONFIG3 0xc32#define J_FORCE_DISC_ON BIT(5)33#define J_DEBUG_CTRL_ADDR_MACRO BIT(0)34#define CONFIG7 0x1c35#define J_DISC 0X1f36#define CONFIG9 0x2437#define J_ECO_PATH BIT(6)38#define CONFIG16 0x4039#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 0x4447#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 0x5c52#define PROB_MASK GENMASK(5, 3)53#define PROB FIELD_PREP(PROB_MASK, 7)54 55/* GROUP MOON4 */56#define UPHY_CONTROL0 0x057#define UPHY_CONTROL1 0x458#define UPHY_CONTROL2 0x859#define MO1_UPHY_RX_CLK_SEL BIT(6)60#define MASK_MO1_UPHY_RX_CLK_SEL BIT(6 + 16)61#define UPHY_CONTROL3 0xc62#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 67struct 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 78static 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 (!IS_ERR(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 (IS_ERR(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 111static 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 161err_reset:162 reset_control_assert(usbphy->rstc);163err_clk:164 clk_disable_unprepare(usbphy->phy_clk);165 166 return ret;167}168 169static 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 198static 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 215static 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 225static 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 232static const struct of_device_id sp_uphy_dt_ids[] = {233 {.compatible = "sunplus,sp7021-usb2-phy", },234 { }235};236MODULE_DEVICE_TABLE(of, sp_uphy_dt_ids);237 238static 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 if (!usbphy->moon4_res_mem)258 return -EINVAL;259 260 usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start,261 resource_size(usbphy->moon4_res_mem));262 if (!usbphy->moon4_regs)263 return -ENOMEM;264 265 usbphy->phy_clk = devm_clk_get(&pdev->dev, NULL);266 if (IS_ERR(usbphy->phy_clk))267 return PTR_ERR(usbphy->phy_clk);268 269 usbphy->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);270 if (IS_ERR(usbphy->rstc))271 return PTR_ERR(usbphy->rstc);272 273 of_property_read_u32(pdev->dev.of_node, "sunplus,disc-vol-addr-off",274 &usbphy->disc_vol_addr_off);275 276 phy = devm_phy_create(&pdev->dev, NULL, &sp_uphy_ops);277 if (IS_ERR(phy)) {278 ret = PTR_ERR(phy);279 return ret;280 }281 282 phy_set_drvdata(phy, usbphy);283 phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);284 285 return PTR_ERR_OR_ZERO(phy_provider);286}287 288static struct platform_driver sunplus_usb_phy_driver = {289 .probe = sp_usb_phy_probe,290 .driver = {291 .name = "sunplus-usb2-phy",292 .of_match_table = sp_uphy_dt_ids,293 },294};295module_platform_driver(sunplus_usb_phy_driver);296 297MODULE_AUTHOR("Vincent Shih <vincent.shih@sunplus.com>");298MODULE_DESCRIPTION("Sunplus USB 2.0 phy driver");299MODULE_LICENSE("GPL");300