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