1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * phy-k3-combo.c - SpacemiT K3 PCIe/USB3 Combo PHY Driver 4 * 5 * Copyright (c) 2025 SpacemiT Technology Co. Ltd 6 */ 7 8 #include <linux/bitfield.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/mfd/syscon.h> 12 #include <linux/iopoll.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/regmap.h> 16 #include <linux/phy/phy.h> 17 18 #include <dt-bindings/phy/phy.h> 19 20 #include "phy-k3-common.h" 21 22 /* 23 * The PCIE/USB Subsystem on SpacemiT K3 have 3 single lane PIPE3 PHYs 24 * (PHY2/3/4) shared by PCIE PortC/D and USB3 PortB/C/D. 25 * 26 * PMUA_PCIE_SUBSYS_MGMT[4:0] 27 * 28 * bit4 = 0 : PCIe A X8 mode, all 8 lanes dedicated to PCIe Port A 29 * 1 : PHY lanes shared between PCIe or USB according to [3:0] 30 * 31 * All PHY matrix combinations according to [4:0]: 32 * 33 * 0x0X : PCIe-A X8 34 * 0x10 : PCIe-C x2 (PHY2+PHY3) + PCIe-D x1 (PHY4) 35 * 0x11 : PCIe-C x2 (PHY2+PHY3) + USB-D (PHY4) 36 * 0x12 : PCIe-C x1 (PHY2) + USB-C (PHY3) 37 * 0x13 : PCIe-C x1 (PHY2) + USB-C (PHY3) + USB-D (PHY4) 38 * 0x14 : PCIe-C x1 (PHY3) + USB-B (PHY2) 39 * 0x15 : PCIe-C x1 (PHY3) + USB-B (PHY2) + USB-D (PHY4) 40 * 0x16 : USB-B (PHY2) + USB-C (PHY3) + PCIe D x1 (PHY4) 41 * 0x17 : USB-B (PHY2) + USB-C (PHY3) + USB-D (PHY4) 42 * 43 * So any USB Port B/C/D operation requires PCIe A X8 mode to be disabled. 44 */ 45 #define PMUA_PCIE_SUBSYS_MGMT 0x1d8 46 #define PU_MATRIX_CONF_MASK GENMASK(4, 0) 47 48 #define COMBPHY_MAX_SUBPHYS 6 49 50 struct k3_combo_phy { 51 struct device *dev; 52 struct k3_lane_group groups[COMBPHY_MAX_SUBPHYS]; 53 void __iomem *base; 54 struct regmap *apb_spare; 55 }; 56 57 static const struct k3_phy_lane_group_data k3_combphy_lane_group0 = { 58 .lanes = 2, 59 .config = 0xff, 60 .mask = 0x00, 61 .offsets = { 62 0x0, 0x400 63 }, 64 }; 65 66 static const struct k3_phy_lane_group_data k3_combphy_lane_group1 = { 67 .lanes = 2, 68 .config = 0xff, 69 .mask = 0x00, 70 .offsets = { 71 0x100000, 0x100400 72 }, 73 }; 74 75 static const struct k3_phy_lane_group_data k3_combphy_lane_group2 = { 76 .lanes = 1, 77 .config = 0x14, 78 .mask = 0x14, 79 .offsets = { 80 0x200000 81 }, 82 }; 83 84 static const struct k3_phy_lane_group_data k3_combphy_lane_group3 = { 85 .lanes = 1, 86 .config = 0x12, 87 .mask = 0x12, 88 .offsets = { 89 0x300000 90 }, 91 }; 92 93 static const struct k3_phy_lane_group_data k3_combphy_lane_group4 = { 94 .lanes = 1, 95 .config = 0x11, 96 .mask = 0x11, 97 .offsets = { 98 0x400000 99 }, 100 }; 101 102 static const struct k3_phy_lane_group_data k3_combphy_lane_group5 = { 103 .lanes = 1, 104 .config = 0xff, 105 .mask = 0x00, 106 .offsets = { 107 0x500000 108 }, 109 }; 110 111 static const struct k3_phy_lane_group_data *k3_combphy_lane_datas[] = { 112 &k3_combphy_lane_group0, 113 &k3_combphy_lane_group1, 114 &k3_combphy_lane_group2, 115 &k3_combphy_lane_group3, 116 &k3_combphy_lane_group4, 117 &k3_combphy_lane_group5, 118 }; 119 120 static int k3_combo_phy_init_lanes(struct k3_combo_phy *phy, unsigned int config) 121 { 122 int i; 123 124 for (i = 0; i < ARRAY_SIZE(k3_combphy_lane_datas); i++) { 125 const struct k3_phy_lane_group_data *data = k3_combphy_lane_datas[i]; 126 struct k3_lane_group *lg = &phy->groups[i]; 127 const struct phy_ops *ops; 128 bool is_usb; 129 130 is_usb = (data->mask & config) == data->config; 131 if (is_usb) 132 ops = &k3_usb3_phy_ops; 133 else 134 ops = &k3_pcie_phy_ops; 135 136 dev_dbg(phy->dev, "phy %d is %s\n", i, is_usb ? "usb" : "pcie"); 137 138 lg->phy = devm_phy_create(phy->dev, NULL, ops); 139 if (IS_ERR(lg->phy)) 140 return PTR_ERR(lg->phy); 141 142 lg->is_pcie = !is_usb; 143 lg->data = data; 144 lg->base = phy->base; 145 phy_set_drvdata(lg->phy, lg); 146 } 147 148 return 0; 149 } 150 151 static int k3_combo_phy_update_config(struct regmap *apmu, unsigned int config) 152 { 153 if (config & ~PU_MATRIX_CONF_MASK) 154 return -EINVAL; 155 156 return regmap_update_bits(apmu, PMUA_PCIE_SUBSYS_MGMT, PU_MATRIX_CONF_MASK, config); 157 } 158 159 static struct phy *k3_combo_phy_xlate(struct device *dev, const struct of_phandle_args *args) 160 { 161 struct k3_combo_phy *phy = dev_get_drvdata(dev); 162 struct k3_lane_group *lg; 163 164 if (args->args_count != 2) { 165 dev_err(dev, "Invalid number of arguments\n"); 166 return ERR_PTR(-EINVAL); 167 } 168 169 if (args->args[0] >= ARRAY_SIZE(k3_combphy_lane_datas)) { 170 dev_err(dev, "Invalid PHY id\n"); 171 return ERR_PTR(-EINVAL); 172 } 173 174 lg = &phy->groups[args->args[0]]; 175 176 if ((lg->is_pcie && args->args[1] != PHY_TYPE_PCIE) || 177 (!lg->is_pcie && args->args[1] != PHY_TYPE_USB3)) { 178 dev_err(dev, "Invalid PHY mode\n"); 179 return ERR_PTR(-EINVAL); 180 } 181 182 return lg->phy; 183 } 184 185 static int k3_combo_phy_probe(struct platform_device *pdev) 186 { 187 struct device *dev = &pdev->dev; 188 struct device_node *node = dev->of_node; 189 struct phy_provider *provider; 190 struct k3_combo_phy *phy; 191 struct regmap *apmu; 192 u32 config = 0; 193 int ret; 194 195 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 196 if (!phy) 197 return -ENOMEM; 198 199 phy->base = devm_platform_ioremap_resource(pdev, 0); 200 if (IS_ERR(phy->base)) 201 return PTR_ERR(phy->base); 202 203 phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare"); 204 if (IS_ERR(phy->apb_spare)) 205 return dev_err_probe(dev, PTR_ERR(phy->apb_spare), 206 "Failed to find APB SPARE syscon"); 207 208 apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config); 209 if (IS_ERR(apmu)) 210 return dev_err_probe(dev, PTR_ERR(apmu), 211 "Failed to find APMU syscon"); 212 213 ret = k3_combo_phy_update_config(apmu, config); 214 if (ret < 0) 215 return dev_err_probe(dev, ret, "Failed to set lane configuration"); 216 217 phy->dev = dev; 218 platform_set_drvdata(pdev, phy); 219 220 ret = k3_phy_calibrate(phy->apb_spare); 221 if (ret < 0) 222 return dev_err_probe(dev, ret, "Failed to calibrate phy"); 223 224 ret = k3_combo_phy_init_lanes(phy, config); 225 if (ret < 0) 226 return dev_err_probe(dev, ret, "Failed to init lanes"); 227 228 provider = devm_of_phy_provider_register(dev, k3_combo_phy_xlate); 229 if (IS_ERR(provider)) 230 return dev_err_probe(dev, PTR_ERR(provider), 231 "Failed to register provider\n"); 232 233 return 0; 234 } 235 236 static const struct of_device_id k3_combo_phy_of_match[] = { 237 { .compatible = "spacemit,k3-combo-phy" }, 238 { }, 239 }; 240 MODULE_DEVICE_TABLE(of, k3_combo_phy_of_match); 241 242 static struct platform_driver k3_combo_phy_driver = { 243 .probe = k3_combo_phy_probe, 244 .driver = { 245 .name = "spacemit,k3-combo-phy", 246 .of_match_table = k3_combo_phy_of_match, 247 }, 248 }; 249 module_platform_driver(k3_combo_phy_driver); 250 251 MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe combo PHY driver"); 252 MODULE_LICENSE("GPL"); 253