Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

phy: usb: Add driver for Canaan K230 USB 2.0 PHY

Add driver for the USB 2.0 PHY in Canaan K230 SoC, which supports PHY
initialization and power management.

Add Kconfig/Makefile under drivers/phy/canaan/.

Signed-off-by: Jiayu Du <jiayu.riscv@isrc.iscas.ac.cn>
Link: https://patch.msgid.link/20260121145526.14672-4-jiayu.riscv@isrc.iscas.ac.cn
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Jiayu Du and committed by
Vinod Koul
8787fa1d 50357e7d

+302
+1
drivers/phy/Kconfig
··· 139 139 source "drivers/phy/apple/Kconfig" 140 140 source "drivers/phy/broadcom/Kconfig" 141 141 source "drivers/phy/cadence/Kconfig" 142 + source "drivers/phy/canaan/Kconfig" 142 143 source "drivers/phy/freescale/Kconfig" 143 144 source "drivers/phy/hisilicon/Kconfig" 144 145 source "drivers/phy/ingenic/Kconfig"
+1
drivers/phy/Makefile
··· 22 22 apple/ \ 23 23 broadcom/ \ 24 24 cadence/ \ 25 + canaan/ \ 25 26 freescale/ \ 26 27 hisilicon/ \ 27 28 ingenic/ \
+14
drivers/phy/canaan/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Phy drivers for Canaan platforms 4 + # 5 + config PHY_CANAAN_USB 6 + tristate "Canaan USB2 PHY Driver" 7 + depends on (ARCH_CANAAN || COMPILE_TEST) && OF 8 + select GENERIC_PHY 9 + help 10 + Enable this driver to support the USB 2.0 PHY controller 11 + on Canaan K230 RISC-V SoCs. This PHY controller 12 + provides physical layer functionality for USB 2.0 devices. 13 + If you have a Canaan K230 board and need USB 2.0 support, 14 + say Y or M here.
+2
drivers/phy/canaan/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_PHY_CANAAN_USB) += phy-k230-usb.o
+284
drivers/phy/canaan/phy-k230-usb.c
··· 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");