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