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