1*fe4bc1a0SZe Huang // SPDX-License-Identifier: GPL-2.0-only 2*fe4bc1a0SZe Huang /* 3*fe4bc1a0SZe Huang * SpacemiT K1 USB 2.0 PHY driver 4*fe4bc1a0SZe Huang * 5*fe4bc1a0SZe Huang * Copyright (C) 2025 SpacemiT (Hangzhou) Technology Co. Ltd 6*fe4bc1a0SZe Huang * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev> 7*fe4bc1a0SZe Huang */ 8*fe4bc1a0SZe Huang 9*fe4bc1a0SZe Huang #include <linux/bitfield.h> 10*fe4bc1a0SZe Huang #include <linux/clk.h> 11*fe4bc1a0SZe Huang #include <linux/iopoll.h> 12*fe4bc1a0SZe Huang #include <linux/platform_device.h> 13*fe4bc1a0SZe Huang #include <linux/regmap.h> 14*fe4bc1a0SZe Huang #include <linux/usb/of.h> 15*fe4bc1a0SZe Huang 16*fe4bc1a0SZe Huang #define PHY_RST_MODE_CTRL 0x04 17*fe4bc1a0SZe Huang #define PHY_PLL_RDY BIT(0) 18*fe4bc1a0SZe Huang #define PHY_CLK_CDR_EN BIT(1) 19*fe4bc1a0SZe Huang #define PHY_CLK_PLL_EN BIT(2) 20*fe4bc1a0SZe Huang #define PHY_CLK_MAC_EN BIT(3) 21*fe4bc1a0SZe Huang #define PHY_MAC_RSTN BIT(5) 22*fe4bc1a0SZe Huang #define PHY_CDR_RSTN BIT(6) 23*fe4bc1a0SZe Huang #define PHY_PLL_RSTN BIT(7) 24*fe4bc1a0SZe Huang /* 25*fe4bc1a0SZe Huang * hs line state sel (Bit 13): 26*fe4bc1a0SZe Huang * - 1 (Default): Internal HS line state is set to 01 when usb_hs_tx_en is valid. 27*fe4bc1a0SZe Huang * - 0: Internal HS line state is always driven by usb_hs_lstate. 28*fe4bc1a0SZe Huang * 29*fe4bc1a0SZe Huang * fs line state sel (Bit 14): 30*fe4bc1a0SZe Huang * - 1 (Default): FS line state is determined by the output data 31*fe4bc1a0SZe Huang * (usb_fs_datain/b). 32*fe4bc1a0SZe Huang * - 0: FS line state is always determined by the input data (dmo/dpo). 33*fe4bc1a0SZe Huang */ 34*fe4bc1a0SZe Huang #define PHY_HS_LINE_TX_MODE BIT(13) 35*fe4bc1a0SZe Huang #define PHY_FS_LINE_TX_MODE BIT(14) 36*fe4bc1a0SZe Huang 37*fe4bc1a0SZe Huang #define PHY_INIT_MODE_BITS (PHY_FS_LINE_TX_MODE | PHY_HS_LINE_TX_MODE) 38*fe4bc1a0SZe Huang #define PHY_CLK_ENABLE_BITS (PHY_CLK_PLL_EN | PHY_CLK_CDR_EN | \ 39*fe4bc1a0SZe Huang PHY_CLK_MAC_EN) 40*fe4bc1a0SZe Huang #define PHY_DEASSERT_RST_BITS (PHY_PLL_RSTN | PHY_CDR_RSTN | \ 41*fe4bc1a0SZe Huang PHY_MAC_RSTN) 42*fe4bc1a0SZe Huang 43*fe4bc1a0SZe Huang #define PHY_TX_HOST_CTRL 0x10 44*fe4bc1a0SZe Huang #define PHY_HST_DISC_AUTO_CLR BIT(2) /* autoclear hs host disc when re-connect */ 45*fe4bc1a0SZe Huang 46*fe4bc1a0SZe Huang #define PHY_HSTXP_HW_CTRL 0x34 47*fe4bc1a0SZe Huang #define PHY_HSTXP_RSTN BIT(2) /* generate reset for clock hstxp */ 48*fe4bc1a0SZe Huang #define PHY_CLK_HSTXP_EN BIT(3) /* clock hstxp enable */ 49*fe4bc1a0SZe Huang #define PHY_HSTXP_MODE BIT(4) /* 0: force en_txp to be 1; 1: no force */ 50*fe4bc1a0SZe Huang 51*fe4bc1a0SZe Huang #define PHY_PLL_DIV_CFG 0x98 52*fe4bc1a0SZe Huang #define PHY_FDIV_FRACT_8_15 GENMASK(7, 0) 53*fe4bc1a0SZe Huang #define PHY_FDIV_FRACT_16_19 GENMASK(11, 8) 54*fe4bc1a0SZe Huang #define PHY_FDIV_FRACT_20_21 BIT(12) /* fdiv_reg<21>, <20>, bit21 == bit20 */ 55*fe4bc1a0SZe Huang /* 56*fe4bc1a0SZe Huang * freq_sel<1:0> 57*fe4bc1a0SZe Huang * if ref clk freq=24.0MHz-->freq_sel<2:0> == 3b'001, then internal divider value == 80 58*fe4bc1a0SZe Huang */ 59*fe4bc1a0SZe Huang #define PHY_FDIV_FRACT_0_1 GENMASK(14, 13) 60*fe4bc1a0SZe Huang /* 61*fe4bc1a0SZe Huang * pll divider value selection 62*fe4bc1a0SZe Huang * 1: divider value will choose internal default value ,dependent on freq_sel<1:0> 63*fe4bc1a0SZe Huang * 0: divider value will be over ride by fdiv_reg<21:0> 64*fe4bc1a0SZe Huang */ 65*fe4bc1a0SZe Huang #define PHY_DIV_LOCAL_EN BIT(15) 66*fe4bc1a0SZe Huang 67*fe4bc1a0SZe Huang #define PHY_SEL_FREQ_24MHZ 0x01 68*fe4bc1a0SZe Huang #define FDIV_REG_MASK (PHY_FDIV_FRACT_20_21 | PHY_FDIV_FRACT_16_19 | \ 69*fe4bc1a0SZe Huang PHY_FDIV_FRACT_8_15) 70*fe4bc1a0SZe Huang #define FDIV_REG_VAL 0x1ec4 /* 0x100 selects 24MHz, rest are default */ 71*fe4bc1a0SZe Huang 72*fe4bc1a0SZe Huang #define K1_USB2PHY_RESET_TIME_MS 50 73*fe4bc1a0SZe Huang 74*fe4bc1a0SZe Huang struct spacemit_usb2phy { 75*fe4bc1a0SZe Huang struct phy *phy; 76*fe4bc1a0SZe Huang struct clk *clk; 77*fe4bc1a0SZe Huang struct regmap *regmap_base; 78*fe4bc1a0SZe Huang }; 79*fe4bc1a0SZe Huang 80*fe4bc1a0SZe Huang static const struct regmap_config phy_regmap_config = { 81*fe4bc1a0SZe Huang .reg_bits = 32, 82*fe4bc1a0SZe Huang .val_bits = 32, 83*fe4bc1a0SZe Huang .reg_stride = 4, 84*fe4bc1a0SZe Huang .max_register = 0x200, 85*fe4bc1a0SZe Huang }; 86*fe4bc1a0SZe Huang 87*fe4bc1a0SZe Huang static int spacemit_usb2phy_init(struct phy *phy) 88*fe4bc1a0SZe Huang { 89*fe4bc1a0SZe Huang struct spacemit_usb2phy *sphy = phy_get_drvdata(phy); 90*fe4bc1a0SZe Huang struct regmap *map = sphy->regmap_base; 91*fe4bc1a0SZe Huang u32 val; 92*fe4bc1a0SZe Huang int ret; 93*fe4bc1a0SZe Huang 94*fe4bc1a0SZe Huang ret = clk_enable(sphy->clk); 95*fe4bc1a0SZe Huang if (ret) { 96*fe4bc1a0SZe Huang dev_err(&phy->dev, "failed to enable clock\n"); 97*fe4bc1a0SZe Huang clk_disable(sphy->clk); 98*fe4bc1a0SZe Huang return ret; 99*fe4bc1a0SZe Huang } 100*fe4bc1a0SZe Huang 101*fe4bc1a0SZe Huang /* 102*fe4bc1a0SZe Huang * make sure the usb controller is not under reset process before 103*fe4bc1a0SZe Huang * any configuration 104*fe4bc1a0SZe Huang */ 105*fe4bc1a0SZe Huang usleep_range(150, 200); 106*fe4bc1a0SZe Huang 107*fe4bc1a0SZe Huang /* 24M ref clk */ 108*fe4bc1a0SZe Huang val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) | 109*fe4bc1a0SZe Huang FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) | 110*fe4bc1a0SZe Huang PHY_DIV_LOCAL_EN; 111*fe4bc1a0SZe Huang regmap_write(map, PHY_PLL_DIV_CFG, val); 112*fe4bc1a0SZe Huang 113*fe4bc1a0SZe Huang ret = regmap_read_poll_timeout(map, PHY_RST_MODE_CTRL, val, 114*fe4bc1a0SZe Huang (val & PHY_PLL_RDY), 115*fe4bc1a0SZe Huang 500, K1_USB2PHY_RESET_TIME_MS * 1000); 116*fe4bc1a0SZe Huang if (ret) { 117*fe4bc1a0SZe Huang dev_err(&phy->dev, "wait PLLREADY timeout\n"); 118*fe4bc1a0SZe Huang clk_disable(sphy->clk); 119*fe4bc1a0SZe Huang return ret; 120*fe4bc1a0SZe Huang } 121*fe4bc1a0SZe Huang 122*fe4bc1a0SZe Huang /* release usb2 phy internal reset and enable clock gating */ 123*fe4bc1a0SZe Huang val = (PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS); 124*fe4bc1a0SZe Huang regmap_write(map, PHY_RST_MODE_CTRL, val); 125*fe4bc1a0SZe Huang 126*fe4bc1a0SZe Huang val = (PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE); 127*fe4bc1a0SZe Huang regmap_write(map, PHY_HSTXP_HW_CTRL, val); 128*fe4bc1a0SZe Huang 129*fe4bc1a0SZe Huang /* auto clear host disc */ 130*fe4bc1a0SZe Huang regmap_update_bits(map, PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR, 131*fe4bc1a0SZe Huang PHY_HST_DISC_AUTO_CLR); 132*fe4bc1a0SZe Huang 133*fe4bc1a0SZe Huang return 0; 134*fe4bc1a0SZe Huang } 135*fe4bc1a0SZe Huang 136*fe4bc1a0SZe Huang static int spacemit_usb2phy_exit(struct phy *phy) 137*fe4bc1a0SZe Huang { 138*fe4bc1a0SZe Huang struct spacemit_usb2phy *sphy = phy_get_drvdata(phy); 139*fe4bc1a0SZe Huang 140*fe4bc1a0SZe Huang clk_disable(sphy->clk); 141*fe4bc1a0SZe Huang 142*fe4bc1a0SZe Huang return 0; 143*fe4bc1a0SZe Huang } 144*fe4bc1a0SZe Huang 145*fe4bc1a0SZe Huang static const struct phy_ops spacemit_usb2phy_ops = { 146*fe4bc1a0SZe Huang .init = spacemit_usb2phy_init, 147*fe4bc1a0SZe Huang .exit = spacemit_usb2phy_exit, 148*fe4bc1a0SZe Huang .owner = THIS_MODULE, 149*fe4bc1a0SZe Huang }; 150*fe4bc1a0SZe Huang 151*fe4bc1a0SZe Huang static int spacemit_usb2phy_probe(struct platform_device *pdev) 152*fe4bc1a0SZe Huang { 153*fe4bc1a0SZe Huang struct phy_provider *phy_provider; 154*fe4bc1a0SZe Huang struct device *dev = &pdev->dev; 155*fe4bc1a0SZe Huang struct spacemit_usb2phy *sphy; 156*fe4bc1a0SZe Huang void __iomem *base; 157*fe4bc1a0SZe Huang 158*fe4bc1a0SZe Huang sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); 159*fe4bc1a0SZe Huang if (!sphy) 160*fe4bc1a0SZe Huang return -ENOMEM; 161*fe4bc1a0SZe Huang 162*fe4bc1a0SZe Huang sphy->clk = devm_clk_get_prepared(&pdev->dev, NULL); 163*fe4bc1a0SZe Huang if (IS_ERR(sphy->clk)) 164*fe4bc1a0SZe Huang return dev_err_probe(dev, PTR_ERR(sphy->clk), "Failed to get clock\n"); 165*fe4bc1a0SZe Huang 166*fe4bc1a0SZe Huang base = devm_platform_ioremap_resource(pdev, 0); 167*fe4bc1a0SZe Huang if (IS_ERR(base)) 168*fe4bc1a0SZe Huang return PTR_ERR(base); 169*fe4bc1a0SZe Huang 170*fe4bc1a0SZe Huang sphy->regmap_base = devm_regmap_init_mmio(dev, base, &phy_regmap_config); 171*fe4bc1a0SZe Huang if (IS_ERR(sphy->regmap_base)) 172*fe4bc1a0SZe Huang return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n"); 173*fe4bc1a0SZe Huang 174*fe4bc1a0SZe Huang sphy->phy = devm_phy_create(dev, NULL, &spacemit_usb2phy_ops); 175*fe4bc1a0SZe Huang if (IS_ERR(sphy->phy)) 176*fe4bc1a0SZe Huang return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n"); 177*fe4bc1a0SZe Huang 178*fe4bc1a0SZe Huang phy_set_drvdata(sphy->phy, sphy); 179*fe4bc1a0SZe Huang phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 180*fe4bc1a0SZe Huang 181*fe4bc1a0SZe Huang return PTR_ERR_OR_ZERO(phy_provider); 182*fe4bc1a0SZe Huang } 183*fe4bc1a0SZe Huang 184*fe4bc1a0SZe Huang static const struct of_device_id spacemit_usb2phy_dt_match[] = { 185*fe4bc1a0SZe Huang { .compatible = "spacemit,k1-usb2-phy", }, 186*fe4bc1a0SZe Huang { /* sentinel */ } 187*fe4bc1a0SZe Huang }; 188*fe4bc1a0SZe Huang MODULE_DEVICE_TABLE(of, spacemit_usb2phy_dt_match); 189*fe4bc1a0SZe Huang 190*fe4bc1a0SZe Huang static struct platform_driver spacemit_usb2_phy_driver = { 191*fe4bc1a0SZe Huang .probe = spacemit_usb2phy_probe, 192*fe4bc1a0SZe Huang .driver = { 193*fe4bc1a0SZe Huang .name = "spacemit-usb2-phy", 194*fe4bc1a0SZe Huang .of_match_table = spacemit_usb2phy_dt_match, 195*fe4bc1a0SZe Huang }, 196*fe4bc1a0SZe Huang }; 197*fe4bc1a0SZe Huang module_platform_driver(spacemit_usb2_phy_driver); 198*fe4bc1a0SZe Huang 199*fe4bc1a0SZe Huang MODULE_DESCRIPTION("Spacemit USB 2.0 PHY driver"); 200*fe4bc1a0SZe Huang MODULE_LICENSE("GPL"); 201