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: Add Google Tensor SoC USB PHY driver

Support the USB PHY found on Google Tensor G5 (Laguna). This
particular USB PHY supports both high-speed and super-speed
operations, and is integrated with the SNPS DWC3 controller that's
also on the SoC. This initial patch specifically adds functionality
for high-speed.

Co-developed-by: Joy Chakraborty <joychakr@google.com>
Signed-off-by: Joy Chakraborty <joychakr@google.com>
Co-developed-by: Naveen Kumar <mnkumar@google.com>
Signed-off-by: Naveen Kumar <mnkumar@google.com>
Signed-off-by: Roy Luo <royluo@google.com>
Link: https://patch.msgid.link/20251227-phyb4-v10-2-e8caf6b93fe7@google.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Roy Luo and committed by
Vinod Koul
cbce6666 876dc58c

+308
+1
MAINTAINERS
··· 10727 10727 F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml 10728 10728 F: arch/arm64/boot/dts/exynos/google/ 10729 10729 F: drivers/clk/samsung/clk-gs101.c 10730 + F: drivers/phy/phy-google-usb.c 10730 10731 F: drivers/soc/samsung/gs101-pmu.c 10731 10732 F: drivers/phy/samsung/phy-gs101-ufs.c 10732 10733 F: include/dt-bindings/clock/google,gs101*
+10
drivers/phy/Kconfig
··· 47 47 Provides a number of helpers a core functions for MIPI D-PHY 48 48 drivers to us. 49 49 50 + config PHY_GOOGLE_USB 51 + tristate "Google Tensor SoC USB PHY driver" 52 + select GENERIC_PHY 53 + help 54 + Enable support for the USB PHY on Google Tensor SoCs, starting with 55 + the G5 generation (Laguna). This driver provides the PHY interfaces 56 + to interact with the SNPS eUSB2 and USB 3.2/DisplayPort Combo PHY, 57 + both of which are integrated with the DWC3 USB DRD controller. 58 + This driver currently supports USB high-speed. 59 + 50 60 config PHY_LPC18XX_USB_OTG 51 61 tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" 52 62 depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
+1
drivers/phy/Makefile
··· 8 8 obj-$(CONFIG_GENERIC_PHY) += phy-core.o 9 9 obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o 10 10 obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o 11 + obj-$(CONFIG_PHY_GOOGLE_USB) += phy-google-usb.o 11 12 obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o 12 13 obj-$(CONFIG_PHY_XGENE) += phy-xgene.o 13 14 obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
+296
drivers/phy/phy-google-usb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * phy-google-usb.c - Google USB PHY driver 4 + * 5 + * Copyright (C) 2025, Google LLC 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/cleanup.h> 10 + #include <linux/clk.h> 11 + #include <linux/io.h> 12 + #include <linux/kernel.h> 13 + #include <linux/mfd/syscon.h> 14 + #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/of.h> 17 + #include <linux/phy/phy.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/regmap.h> 20 + #include <linux/reset.h> 21 + #include <linux/usb/typec_mux.h> 22 + 23 + #define USBCS_USB2PHY_CFG19_OFFSET 0x0 24 + #define USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV GENMASK(19, 8) 25 + 26 + #define USBCS_USB2PHY_CFG21_OFFSET 0x8 27 + #define USBCS_USB2PHY_CFG21_PHY_ENABLE BIT(12) 28 + #define USBCS_USB2PHY_CFG21_REF_FREQ_SEL GENMASK(15, 13) 29 + #define USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL BIT(19) 30 + 31 + #define USBCS_PHY_CFG1_OFFSET 0x28 32 + #define USBCS_PHY_CFG1_SYS_VBUSVALID BIT(17) 33 + 34 + enum google_usb_phy_id { 35 + GOOGLE_USB2_PHY, 36 + GOOGLE_USB_PHY_NUM, 37 + }; 38 + 39 + struct google_usb_phy_instance { 40 + struct google_usb_phy *parent; 41 + unsigned int index; 42 + struct phy *phy; 43 + unsigned int num_clks; 44 + struct clk_bulk_data *clks; 45 + unsigned int num_rsts; 46 + struct reset_control_bulk_data *rsts; 47 + }; 48 + 49 + struct google_usb_phy { 50 + struct device *dev; 51 + struct regmap *usb_cfg_regmap; 52 + unsigned int usb2_cfg_offset; 53 + void __iomem *usbdp_top_base; 54 + struct google_usb_phy_instance *insts; 55 + /* 56 + * Protect phy registers from concurrent access, specifically via 57 + * google_usb_set_orientation callback. 58 + */ 59 + struct mutex phy_mutex; 60 + struct typec_switch_dev *sw; 61 + enum typec_orientation orientation; 62 + }; 63 + 64 + static void set_vbus_valid(struct google_usb_phy *gphy) 65 + { 66 + u32 reg; 67 + 68 + if (gphy->orientation == TYPEC_ORIENTATION_NONE) { 69 + reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET); 70 + reg &= ~USBCS_PHY_CFG1_SYS_VBUSVALID; 71 + writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET); 72 + } else { 73 + reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET); 74 + reg |= USBCS_PHY_CFG1_SYS_VBUSVALID; 75 + writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET); 76 + } 77 + } 78 + 79 + static int google_usb_set_orientation(struct typec_switch_dev *sw, 80 + enum typec_orientation orientation) 81 + { 82 + struct google_usb_phy *gphy = typec_switch_get_drvdata(sw); 83 + 84 + dev_dbg(gphy->dev, "set orientation %d\n", orientation); 85 + 86 + gphy->orientation = orientation; 87 + 88 + if (pm_runtime_suspended(gphy->dev)) 89 + return 0; 90 + 91 + guard(mutex)(&gphy->phy_mutex); 92 + 93 + set_vbus_valid(gphy); 94 + 95 + return 0; 96 + } 97 + 98 + static int google_usb2_phy_init(struct phy *_phy) 99 + { 100 + struct google_usb_phy_instance *inst = phy_get_drvdata(_phy); 101 + struct google_usb_phy *gphy = inst->parent; 102 + u32 reg; 103 + int ret; 104 + 105 + dev_dbg(gphy->dev, "initializing usb2 phy\n"); 106 + 107 + guard(mutex)(&gphy->phy_mutex); 108 + 109 + regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg); 110 + reg &= ~USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL; 111 + reg &= ~USBCS_USB2PHY_CFG21_REF_FREQ_SEL; 112 + reg |= FIELD_PREP(USBCS_USB2PHY_CFG21_REF_FREQ_SEL, 0); 113 + regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg); 114 + 115 + regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG19_OFFSET, &reg); 116 + reg &= ~USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV; 117 + reg |= FIELD_PREP(USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV, 368); 118 + regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG19_OFFSET, reg); 119 + 120 + set_vbus_valid(gphy); 121 + 122 + ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks); 123 + if (ret) 124 + return ret; 125 + 126 + ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts); 127 + if (ret) { 128 + clk_bulk_disable_unprepare(inst->num_clks, inst->clks); 129 + return ret; 130 + } 131 + 132 + regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg); 133 + reg |= USBCS_USB2PHY_CFG21_PHY_ENABLE; 134 + regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg); 135 + 136 + return 0; 137 + } 138 + 139 + static int google_usb2_phy_exit(struct phy *_phy) 140 + { 141 + struct google_usb_phy_instance *inst = phy_get_drvdata(_phy); 142 + struct google_usb_phy *gphy = inst->parent; 143 + u32 reg; 144 + 145 + dev_dbg(gphy->dev, "exiting usb2 phy\n"); 146 + 147 + guard(mutex)(&gphy->phy_mutex); 148 + 149 + regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg); 150 + reg &= ~USBCS_USB2PHY_CFG21_PHY_ENABLE; 151 + regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg); 152 + 153 + reset_control_bulk_assert(inst->num_rsts, inst->rsts); 154 + clk_bulk_disable_unprepare(inst->num_clks, inst->clks); 155 + 156 + return 0; 157 + } 158 + 159 + static const struct phy_ops google_usb2_phy_ops = { 160 + .init = google_usb2_phy_init, 161 + .exit = google_usb2_phy_exit, 162 + }; 163 + 164 + static struct phy *google_usb_phy_xlate(struct device *dev, 165 + const struct of_phandle_args *args) 166 + { 167 + struct google_usb_phy *gphy = dev_get_drvdata(dev); 168 + 169 + if (args->args[0] >= GOOGLE_USB_PHY_NUM) { 170 + dev_err(dev, "invalid PHY index requested from DT\n"); 171 + return ERR_PTR(-ENODEV); 172 + } 173 + return gphy->insts[args->args[0]].phy; 174 + } 175 + 176 + static int google_usb_phy_probe(struct platform_device *pdev) 177 + { 178 + struct typec_switch_desc sw_desc = { }; 179 + struct google_usb_phy_instance *inst; 180 + struct phy_provider *phy_provider; 181 + struct device *dev = &pdev->dev; 182 + struct google_usb_phy *gphy; 183 + struct phy *phy; 184 + u32 args[1]; 185 + int ret; 186 + 187 + gphy = devm_kzalloc(dev, sizeof(*gphy), GFP_KERNEL); 188 + if (!gphy) 189 + return -ENOMEM; 190 + 191 + dev_set_drvdata(dev, gphy); 192 + gphy->dev = dev; 193 + 194 + ret = devm_mutex_init(dev, &gphy->phy_mutex); 195 + if (ret) 196 + return ret; 197 + 198 + gphy->usb_cfg_regmap = 199 + syscon_regmap_lookup_by_phandle_args(dev->of_node, 200 + "google,usb-cfg-csr", 201 + ARRAY_SIZE(args), args); 202 + if (IS_ERR(gphy->usb_cfg_regmap)) { 203 + return dev_err_probe(dev, PTR_ERR(gphy->usb_cfg_regmap), 204 + "invalid usb cfg csr\n"); 205 + } 206 + 207 + gphy->usb2_cfg_offset = args[0]; 208 + 209 + gphy->usbdp_top_base = devm_platform_ioremap_resource_byname(pdev, 210 + "usbdp_top"); 211 + if (IS_ERR(gphy->usbdp_top_base)) 212 + return dev_err_probe(dev, PTR_ERR(gphy->usbdp_top_base), 213 + "invalid usbdp top\n"); 214 + 215 + gphy->insts = devm_kcalloc(dev, GOOGLE_USB_PHY_NUM, sizeof(*gphy->insts), GFP_KERNEL); 216 + if (!gphy->insts) 217 + return -ENOMEM; 218 + 219 + inst = &gphy->insts[GOOGLE_USB2_PHY]; 220 + inst->parent = gphy; 221 + inst->index = GOOGLE_USB2_PHY; 222 + phy = devm_phy_create(dev, NULL, &google_usb2_phy_ops); 223 + if (IS_ERR(phy)) 224 + return dev_err_probe(dev, PTR_ERR(phy), 225 + "failed to create usb2 phy instance\n"); 226 + inst->phy = phy; 227 + phy_set_drvdata(phy, inst); 228 + 229 + inst->num_clks = 2; 230 + inst->clks = devm_kcalloc(dev, inst->num_clks, sizeof(*inst->clks), GFP_KERNEL); 231 + if (!inst->clks) 232 + return -ENOMEM; 233 + inst->clks[0].id = "usb2"; 234 + inst->clks[1].id = "usb2_apb"; 235 + ret = devm_clk_bulk_get(dev, inst->num_clks, inst->clks); 236 + if (ret) 237 + return dev_err_probe(dev, ret, "failed to get u2 phy clks\n"); 238 + 239 + inst->num_rsts = 2; 240 + inst->rsts = devm_kcalloc(dev, inst->num_rsts, sizeof(*inst->rsts), GFP_KERNEL); 241 + if (!inst->rsts) 242 + return -ENOMEM; 243 + inst->rsts[0].id = "usb2"; 244 + inst->rsts[1].id = "usb2_apb"; 245 + ret = devm_reset_control_bulk_get_exclusive(dev, inst->num_rsts, inst->rsts); 246 + if (ret) 247 + return dev_err_probe(dev, ret, "failed to get u2 phy resets\n"); 248 + 249 + phy_provider = devm_of_phy_provider_register(dev, google_usb_phy_xlate); 250 + if (IS_ERR(phy_provider)) 251 + return dev_err_probe(dev, PTR_ERR(phy_provider), 252 + "failed to register phy provider\n"); 253 + 254 + pm_runtime_enable(dev); 255 + 256 + sw_desc.fwnode = dev_fwnode(dev); 257 + sw_desc.drvdata = gphy; 258 + sw_desc.name = fwnode_get_name(dev_fwnode(dev)); 259 + sw_desc.set = google_usb_set_orientation; 260 + 261 + gphy->sw = typec_switch_register(dev, &sw_desc); 262 + if (IS_ERR(gphy->sw)) 263 + return dev_err_probe(dev, PTR_ERR(gphy->sw), 264 + "failed to register typec switch\n"); 265 + 266 + return 0; 267 + } 268 + 269 + static void google_usb_phy_remove(struct platform_device *pdev) 270 + { 271 + struct google_usb_phy *gphy = dev_get_drvdata(&pdev->dev); 272 + 273 + typec_switch_unregister(gphy->sw); 274 + pm_runtime_disable(&pdev->dev); 275 + } 276 + 277 + static const struct of_device_id google_usb_phy_of_match[] = { 278 + { 279 + .compatible = "google,lga-usb-phy", 280 + }, 281 + { } 282 + }; 283 + MODULE_DEVICE_TABLE(of, google_usb_phy_of_match); 284 + 285 + static struct platform_driver google_usb_phy = { 286 + .probe = google_usb_phy_probe, 287 + .remove = google_usb_phy_remove, 288 + .driver = { 289 + .name = "google-usb-phy", 290 + .of_match_table = google_usb_phy_of_match, 291 + } 292 + }; 293 + 294 + module_platform_driver(google_usb_phy); 295 + MODULE_LICENSE("GPL"); 296 + MODULE_DESCRIPTION("Google USB phy driver");