1*8787fa1dSJiayu Du // SPDX-License-Identifier: GPL-2.0-only 2*8787fa1dSJiayu Du /* 3*8787fa1dSJiayu Du * Canaan usb PHY driver 4*8787fa1dSJiayu Du * 5*8787fa1dSJiayu Du * Copyright (C) 2026 Jiayu Du <jiayu.riscv@isrc.iscas.ac.cn> 6*8787fa1dSJiayu Du */ 7*8787fa1dSJiayu Du 8*8787fa1dSJiayu Du #include <linux/bitfield.h> 9*8787fa1dSJiayu Du #include <linux/io.h> 10*8787fa1dSJiayu Du #include <linux/of_address.h> 11*8787fa1dSJiayu Du #include <linux/phy/phy.h> 12*8787fa1dSJiayu Du #include <linux/platform_device.h> 13*8787fa1dSJiayu Du 14*8787fa1dSJiayu Du #define MAX_PHYS 2 15*8787fa1dSJiayu Du 16*8787fa1dSJiayu Du /* Register offsets within the HiSysConfig system controller */ 17*8787fa1dSJiayu Du #define K230_USB0_TEST_REG_BASE 0x70 18*8787fa1dSJiayu Du #define K230_USB0_CTL_REG_BASE 0xb0 19*8787fa1dSJiayu Du #define K230_USB1_TEST_REG_BASE 0x90 20*8787fa1dSJiayu Du #define K230_USB1_CTL_REG_BASE 0xb8 21*8787fa1dSJiayu Du 22*8787fa1dSJiayu Du /* Relative offsets within each PHY's control/test block */ 23*8787fa1dSJiayu Du #define CTL0_OFFSET 0x00 24*8787fa1dSJiayu Du #define CTL1_OFFSET 0x04 25*8787fa1dSJiayu Du #define TEST_CTL3_OFFSET 0x0c 26*8787fa1dSJiayu Du 27*8787fa1dSJiayu Du /* Bit definitions for TEST_CTL3 */ 28*8787fa1dSJiayu Du #define USB_IDPULLUP0 BIT(4) 29*8787fa1dSJiayu Du #define USB_DMPULLDOWN0 BIT(8) 30*8787fa1dSJiayu Du #define USB_DPPULLDOWN0 BIT(9) 31*8787fa1dSJiayu Du 32*8787fa1dSJiayu Du /* USB control register 0 in HiSysConfig system controller */ 33*8787fa1dSJiayu Du /* PLL Integral Path Tune */ 34*8787fa1dSJiayu Du #define USB_CTL0_PLLITUNE_MASK GENMASK(23, 22) 35*8787fa1dSJiayu Du 36*8787fa1dSJiayu Du /* PLL Proportional Path Tune */ 37*8787fa1dSJiayu Du #define USB_CTL0_PLLPTUNE_MASK GENMASK(21, 18) 38*8787fa1dSJiayu Du 39*8787fa1dSJiayu Du /* PLL Bandwidth Adjustment */ 40*8787fa1dSJiayu Du #define USB_CTL0_PLLBTUNE_MASK GENMASK(17, 17) 41*8787fa1dSJiayu Du 42*8787fa1dSJiayu Du /* VReg18 Bypass Control */ 43*8787fa1dSJiayu Du #define USB_CTL0_VREGBYPASS_MASK GENMASK(16, 16) 44*8787fa1dSJiayu Du 45*8787fa1dSJiayu Du /* Retention Mode Enable */ 46*8787fa1dSJiayu Du #define USB_CTL0_RETENABLEN_MASK GENMASK(15, 15) 47*8787fa1dSJiayu Du 48*8787fa1dSJiayu Du /* Reserved Request Input */ 49*8787fa1dSJiayu Du #define USB_CTL0_RESREQIN_MASK GENMASK(14, 14) 50*8787fa1dSJiayu Du 51*8787fa1dSJiayu Du /* External VBUS Valid Select */ 52*8787fa1dSJiayu Du #define USB_CTL0_VBUSVLDEXTSEL0_MASK GENMASK(13, 13) 53*8787fa1dSJiayu Du 54*8787fa1dSJiayu Du /* OTG Block Disable Control */ 55*8787fa1dSJiayu Du #define USB_CTL0_OTGDISABLE0_MASK GENMASK(12, 12) 56*8787fa1dSJiayu Du 57*8787fa1dSJiayu Du /* Drive VBUS Enable */ 58*8787fa1dSJiayu Du #define USB_CTL0_DRVVBUS0_MASK GENMASK(11, 11) 59*8787fa1dSJiayu Du 60*8787fa1dSJiayu Du /* Autoresume Mode Enable */ 61*8787fa1dSJiayu Du #define USB_CTL0_AUTORSMENB0_MASK GENMASK(10, 10) 62*8787fa1dSJiayu Du 63*8787fa1dSJiayu Du /* HS Transceiver Asynchronous Control */ 64*8787fa1dSJiayu Du #define USB_CTL0_HSXCVREXTCTL0_MASK GENMASK(9, 9) 65*8787fa1dSJiayu Du 66*8787fa1dSJiayu Du /* USB 1.1 Transmit Data */ 67*8787fa1dSJiayu Du #define USB_CTL0_FSDATAEXT0_MASK GENMASK(8, 8) 68*8787fa1dSJiayu Du 69*8787fa1dSJiayu Du /* USB 1.1 SE0 Generation */ 70*8787fa1dSJiayu Du #define USB_CTL0_FSSE0EXT0_MASK GENMASK(7, 7) 71*8787fa1dSJiayu Du 72*8787fa1dSJiayu Du /* USB 1.1 Data Enable */ 73*8787fa1dSJiayu Du #define USB_CTL0_TXENABLEN0_MASK GENMASK(6, 6) 74*8787fa1dSJiayu Du 75*8787fa1dSJiayu Du /* Disconnect Threshold */ 76*8787fa1dSJiayu Du #define USB_CTL0_COMPDISTUNE0_MASK GENMASK(5, 3) 77*8787fa1dSJiayu Du 78*8787fa1dSJiayu Du /* Squelch Threshold */ 79*8787fa1dSJiayu Du #define USB_CTL0_SQRXTUNE0_MASK GENMASK(2, 0) 80*8787fa1dSJiayu Du 81*8787fa1dSJiayu Du /* USB control register 1 in HiSysConfig system controller */ 82*8787fa1dSJiayu Du /* Data Detect Voltage */ 83*8787fa1dSJiayu Du #define USB_CTL1_VDATREFTUNE0_MASK GENMASK(23, 22) 84*8787fa1dSJiayu Du 85*8787fa1dSJiayu Du /* VBUS Valid Threshold */ 86*8787fa1dSJiayu Du #define USB_CTL1_OTGTUNE0_MASK GENMASK(21, 19) 87*8787fa1dSJiayu Du 88*8787fa1dSJiayu Du /* Transmitter High-Speed Crossover */ 89*8787fa1dSJiayu Du #define USB_CTL1_TXHSXVTUNE0_MASK GENMASK(18, 17) 90*8787fa1dSJiayu Du 91*8787fa1dSJiayu Du /* FS/LS Source Impedance */ 92*8787fa1dSJiayu Du #define USB_CTL1_TXFSLSTUNE0_MASK GENMASK(16, 13) 93*8787fa1dSJiayu Du 94*8787fa1dSJiayu Du /* HS DC Voltage Level */ 95*8787fa1dSJiayu Du #define USB_CTL1_TXVREFTUNE0_MASK GENMASK(12, 9) 96*8787fa1dSJiayu Du 97*8787fa1dSJiayu Du /* HS Transmitter Rise/Fall Time */ 98*8787fa1dSJiayu Du #define USB_CTL1_TXRISETUNE0_MASK GENMASK(8, 7) 99*8787fa1dSJiayu Du 100*8787fa1dSJiayu Du /* USB Source Impedance */ 101*8787fa1dSJiayu Du #define USB_CTL1_TXRESTUNE0_MASK GENMASK(6, 5) 102*8787fa1dSJiayu Du 103*8787fa1dSJiayu Du /* HS Transmitter Pre-Emphasis Current Control */ 104*8787fa1dSJiayu Du #define USB_CTL1_TXPREEMPAMPTUNE0_MASK GENMASK(4, 3) 105*8787fa1dSJiayu Du 106*8787fa1dSJiayu Du /* HS Transmitter Pre-Emphasis Duration Control */ 107*8787fa1dSJiayu Du #define USB_CTL1_TXPREEMPPULSETUNE0_MASK GENMASK(2, 2) 108*8787fa1dSJiayu Du 109*8787fa1dSJiayu Du /* charging detection */ 110*8787fa1dSJiayu Du #define USB_CTL1_CHRGSRCPUENB0_MASK GENMASK(1, 0) 111*8787fa1dSJiayu Du 112*8787fa1dSJiayu Du #define K230_PHY_CTL0_VAL \ 113*8787fa1dSJiayu Du ( \ 114*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_PLLITUNE_MASK, 0x0) | \ 115*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_PLLPTUNE_MASK, 0xc) | \ 116*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_PLLBTUNE_MASK, 0x1) | \ 117*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_VREGBYPASS_MASK, 0x1) | \ 118*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_RETENABLEN_MASK, 0x1) | \ 119*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_RESREQIN_MASK, 0x0) | \ 120*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_VBUSVLDEXTSEL0_MASK, 0x0) | \ 121*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_OTGDISABLE0_MASK, 0x0) | \ 122*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_DRVVBUS0_MASK, 0x1) | \ 123*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_AUTORSMENB0_MASK, 0x0) | \ 124*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_HSXCVREXTCTL0_MASK, 0x0) | \ 125*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_FSDATAEXT0_MASK, 0x0) | \ 126*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_FSSE0EXT0_MASK, 0x0) | \ 127*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_TXENABLEN0_MASK, 0x0) | \ 128*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_COMPDISTUNE0_MASK, 0x3) | \ 129*8787fa1dSJiayu Du FIELD_PREP(USB_CTL0_SQRXTUNE0_MASK, 0x3) \ 130*8787fa1dSJiayu Du ) 131*8787fa1dSJiayu Du 132*8787fa1dSJiayu Du #define K230_PHY_CTL1_VAL \ 133*8787fa1dSJiayu Du ( \ 134*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_VDATREFTUNE0_MASK, 0x1) | \ 135*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_OTGTUNE0_MASK, 0x3) | \ 136*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXHSXVTUNE0_MASK, 0x3) | \ 137*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXFSLSTUNE0_MASK, 0x3) | \ 138*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXVREFTUNE0_MASK, 0x3) | \ 139*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXRISETUNE0_MASK, 0x1) | \ 140*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXRESTUNE0_MASK, 0x1) | \ 141*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXPREEMPAMPTUNE0_MASK, 0x0) | \ 142*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_TXPREEMPPULSETUNE0_MASK, 0x0) | \ 143*8787fa1dSJiayu Du FIELD_PREP(USB_CTL1_CHRGSRCPUENB0_MASK, 0x0) \ 144*8787fa1dSJiayu Du ) 145*8787fa1dSJiayu Du 146*8787fa1dSJiayu Du struct k230_usb_phy_instance { 147*8787fa1dSJiayu Du struct k230_usb_phy_global *global; 148*8787fa1dSJiayu Du struct phy *phy; 149*8787fa1dSJiayu Du u32 test_offset; 150*8787fa1dSJiayu Du u32 ctl_offset; 151*8787fa1dSJiayu Du int index; 152*8787fa1dSJiayu Du }; 153*8787fa1dSJiayu Du 154*8787fa1dSJiayu Du struct k230_usb_phy_global { 155*8787fa1dSJiayu Du struct k230_usb_phy_instance phys[MAX_PHYS]; 156*8787fa1dSJiayu Du void __iomem *base; 157*8787fa1dSJiayu Du }; 158*8787fa1dSJiayu Du 159*8787fa1dSJiayu Du static int k230_usb_phy_power_on(struct phy *phy) 160*8787fa1dSJiayu Du { 161*8787fa1dSJiayu Du struct k230_usb_phy_instance *inst = phy_get_drvdata(phy); 162*8787fa1dSJiayu Du struct k230_usb_phy_global *global = inst->global; 163*8787fa1dSJiayu Du void __iomem *base = global->base; 164*8787fa1dSJiayu Du u32 val; 165*8787fa1dSJiayu Du 166*8787fa1dSJiayu Du /* Apply recommended settings */ 167*8787fa1dSJiayu Du writel(K230_PHY_CTL0_VAL, base + inst->ctl_offset + CTL0_OFFSET); 168*8787fa1dSJiayu Du writel(K230_PHY_CTL1_VAL, base + inst->ctl_offset + CTL1_OFFSET); 169*8787fa1dSJiayu Du 170*8787fa1dSJiayu Du /* Configure test register (pull-ups/pull-downs) */ 171*8787fa1dSJiayu Du val = readl(base + inst->test_offset + TEST_CTL3_OFFSET); 172*8787fa1dSJiayu Du val |= USB_IDPULLUP0; 173*8787fa1dSJiayu Du 174*8787fa1dSJiayu Du if (inst->index == 1) 175*8787fa1dSJiayu Du val |= (USB_DMPULLDOWN0 | USB_DPPULLDOWN0); 176*8787fa1dSJiayu Du else 177*8787fa1dSJiayu Du val &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0); 178*8787fa1dSJiayu Du 179*8787fa1dSJiayu Du writel(val, base + inst->test_offset + TEST_CTL3_OFFSET); 180*8787fa1dSJiayu Du 181*8787fa1dSJiayu Du return 0; 182*8787fa1dSJiayu Du } 183*8787fa1dSJiayu Du 184*8787fa1dSJiayu Du static int k230_usb_phy_power_off(struct phy *phy) 185*8787fa1dSJiayu Du { 186*8787fa1dSJiayu Du struct k230_usb_phy_instance *inst = phy_get_drvdata(phy); 187*8787fa1dSJiayu Du struct k230_usb_phy_global *global = inst->global; 188*8787fa1dSJiayu Du void __iomem *base = global->base; 189*8787fa1dSJiayu Du u32 val; 190*8787fa1dSJiayu Du 191*8787fa1dSJiayu Du val = readl(base + inst->test_offset + TEST_CTL3_OFFSET); 192*8787fa1dSJiayu Du val &= ~(USB_DMPULLDOWN0 | USB_DPPULLDOWN0); 193*8787fa1dSJiayu Du writel(val, base + inst->test_offset + TEST_CTL3_OFFSET); 194*8787fa1dSJiayu Du 195*8787fa1dSJiayu Du return 0; 196*8787fa1dSJiayu Du } 197*8787fa1dSJiayu Du 198*8787fa1dSJiayu Du static const struct phy_ops k230_usb_phy_ops = { 199*8787fa1dSJiayu Du .power_on = k230_usb_phy_power_on, 200*8787fa1dSJiayu Du .power_off = k230_usb_phy_power_off, 201*8787fa1dSJiayu Du .owner = THIS_MODULE, 202*8787fa1dSJiayu Du }; 203*8787fa1dSJiayu Du 204*8787fa1dSJiayu Du static struct phy *k230_usb_phy_xlate(struct device *dev, 205*8787fa1dSJiayu Du const struct of_phandle_args *args) 206*8787fa1dSJiayu Du { 207*8787fa1dSJiayu Du struct k230_usb_phy_global *global = dev_get_drvdata(dev); 208*8787fa1dSJiayu Du unsigned int idx = args->args[0]; 209*8787fa1dSJiayu Du 210*8787fa1dSJiayu Du if (idx >= MAX_PHYS) 211*8787fa1dSJiayu Du return ERR_PTR(-EINVAL); 212*8787fa1dSJiayu Du 213*8787fa1dSJiayu Du return global->phys[idx].phy; 214*8787fa1dSJiayu Du } 215*8787fa1dSJiayu Du 216*8787fa1dSJiayu Du static int k230_usb_phy_probe(struct platform_device *pdev) 217*8787fa1dSJiayu Du { 218*8787fa1dSJiayu Du struct k230_usb_phy_global *global; 219*8787fa1dSJiayu Du struct device *dev = &pdev->dev; 220*8787fa1dSJiayu Du struct phy_provider *provider; 221*8787fa1dSJiayu Du int i; 222*8787fa1dSJiayu Du 223*8787fa1dSJiayu Du global = devm_kzalloc(dev, sizeof(*global), GFP_KERNEL); 224*8787fa1dSJiayu Du if (!global) 225*8787fa1dSJiayu Du return -ENOMEM; 226*8787fa1dSJiayu Du dev_set_drvdata(dev, global); 227*8787fa1dSJiayu Du 228*8787fa1dSJiayu Du global->base = devm_platform_ioremap_resource(pdev, 0); 229*8787fa1dSJiayu Du if (IS_ERR(global->base)) 230*8787fa1dSJiayu Du return dev_err_probe(dev, PTR_ERR(global->base), 231*8787fa1dSJiayu Du "failed to map registers\n"); 232*8787fa1dSJiayu Du 233*8787fa1dSJiayu Du static const struct { 234*8787fa1dSJiayu Du u32 test_offset; 235*8787fa1dSJiayu Du u32 ctl_offset; 236*8787fa1dSJiayu Du } phy_reg_info[MAX_PHYS] = { 237*8787fa1dSJiayu Du [0] = { K230_USB0_TEST_REG_BASE, K230_USB0_CTL_REG_BASE }, 238*8787fa1dSJiayu Du [1] = { K230_USB1_TEST_REG_BASE, K230_USB1_CTL_REG_BASE }, 239*8787fa1dSJiayu Du }; 240*8787fa1dSJiayu Du 241*8787fa1dSJiayu Du for (i = 0; i < MAX_PHYS; i++) { 242*8787fa1dSJiayu Du struct k230_usb_phy_instance *inst = &global->phys[i]; 243*8787fa1dSJiayu Du struct phy *phy; 244*8787fa1dSJiayu Du 245*8787fa1dSJiayu Du inst->global = global; 246*8787fa1dSJiayu Du inst->index = i; 247*8787fa1dSJiayu Du inst->test_offset = phy_reg_info[i].test_offset; 248*8787fa1dSJiayu Du inst->ctl_offset = phy_reg_info[i].ctl_offset; 249*8787fa1dSJiayu Du 250*8787fa1dSJiayu Du phy = devm_phy_create(dev, NULL, &k230_usb_phy_ops); 251*8787fa1dSJiayu Du if (IS_ERR(phy)) { 252*8787fa1dSJiayu Du dev_err(dev, "failed to create phy%d\n", i); 253*8787fa1dSJiayu Du return PTR_ERR(phy); 254*8787fa1dSJiayu Du } 255*8787fa1dSJiayu Du 256*8787fa1dSJiayu Du phy_set_drvdata(phy, inst); 257*8787fa1dSJiayu Du inst->phy = phy; 258*8787fa1dSJiayu Du } 259*8787fa1dSJiayu Du 260*8787fa1dSJiayu Du provider = devm_of_phy_provider_register(dev, k230_usb_phy_xlate); 261*8787fa1dSJiayu Du if (IS_ERR(provider)) 262*8787fa1dSJiayu Du return PTR_ERR(provider); 263*8787fa1dSJiayu Du 264*8787fa1dSJiayu Du return 0; 265*8787fa1dSJiayu Du } 266*8787fa1dSJiayu Du 267*8787fa1dSJiayu Du static const struct of_device_id k230_usb_phy_of_match[] = { 268*8787fa1dSJiayu Du { .compatible = "canaan,k230-usb-phy" }, 269*8787fa1dSJiayu Du {} 270*8787fa1dSJiayu Du }; 271*8787fa1dSJiayu Du MODULE_DEVICE_TABLE(of, k230_usb_phy_of_match); 272*8787fa1dSJiayu Du 273*8787fa1dSJiayu Du static struct platform_driver k230_usb_phy_driver = { 274*8787fa1dSJiayu Du .probe = k230_usb_phy_probe, 275*8787fa1dSJiayu Du .driver = { 276*8787fa1dSJiayu Du .name = "k230-usb-phy", 277*8787fa1dSJiayu Du .of_match_table = k230_usb_phy_of_match, 278*8787fa1dSJiayu Du }, 279*8787fa1dSJiayu Du }; 280*8787fa1dSJiayu Du module_platform_driver(k230_usb_phy_driver); 281*8787fa1dSJiayu Du 282*8787fa1dSJiayu Du MODULE_DESCRIPTION("Canaan Kendryte K230 USB 2.0 PHY driver"); 283*8787fa1dSJiayu Du MODULE_AUTHOR("Jiayu Du <jiayu.riscv@isrc.iscas.ac.cn>"); 284*8787fa1dSJiayu Du MODULE_LICENSE("GPL"); 285