Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | // SPDX-License-Identifier: GPL-2.0+ /* * StarFive JH7110 PCIe 2.0 PHY driver * * Copyright (C) 2024 StarFive Technology Co., Ltd. * Author: Minda Chen <minda.chen@starfivetech.com> */ #include <asm/io.h> #include <dm.h> #include <dm/device_compat.h> #include <errno.h> #include <generic-phy.h> #include <regmap.h> #include <soc.h> #include <syscon.h> #include <linux/bitops.h> #include <linux/err.h> #include "phy-jh7110-usb-syscon.h" #define PCIE_KVCO_LEVEL_OFF 0x28 #define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c #define PCIE_USB3_PHY_SS_MODE BIT(4) #define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80 #define PHY_KVCO_FINE_TUNE_LEVEL 0x91 #define PHY_KVCO_FINE_TUNE_SIGNALS 0xc #define PCIE_USB3_PHY_MODE 0x1 #define PCIE_BUS_WIDTH 0x2 #define PCIE_USB3_PHY_ENABLE 0x1 #define PCIE_USB3_PHY_SPLIT 0x1 struct jh7110_pcie_phy { struct phy *phy; struct regmap *stg_syscon; struct regmap *sys_syscon; void __iomem *regs; struct regmap_field *phy_mode; struct regmap_field *bus_width; struct regmap_field *usb3_phy_en; struct regmap_field *usb_split; enum phy_mode mode; }; static int phy_pcie_mode_set(struct jh7110_pcie_phy *data, bool usb_mode) { unsigned int phy_mode, width, usb3_phy, ss_mode, split; /* default is PCIe mode */ if (!data->stg_syscon || !data->sys_syscon) { if (usb_mode) { dev_err(data->phy->dev, "doesn't support USB3 mode\n"); return -EINVAL; } return 0; } if (usb_mode) { phy_mode = PCIE_USB3_PHY_MODE; width = 0; usb3_phy = PCIE_USB3_PHY_ENABLE; ss_mode = PCIE_USB3_PHY_SS_MODE; split = 0; } else { phy_mode = 0; width = PCIE_BUS_WIDTH; usb3_phy = 0; ss_mode = 0; split = PCIE_USB3_PHY_SPLIT; } regmap_field_write(data->phy_mode, phy_mode); regmap_field_write(data->bus_width, width); regmap_field_write(data->usb3_phy_en, usb3_phy); clrsetbits_le32(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF, PCIE_USB3_PHY_SS_MODE, ss_mode); regmap_field_write(data->usb_split, split); return 0; } static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy) { /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); } static int jh7110_pcie_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct udevice *dev = phy->dev; struct jh7110_pcie_phy *pcie_phy = dev_get_priv(dev); int ret; if (mode == pcie_phy->mode) return 0; switch (mode) { case PHY_MODE_USB_HOST: case PHY_MODE_USB_DEVICE: case PHY_MODE_USB_OTG: ret = phy_pcie_mode_set(pcie_phy, 1); if (ret) return ret; break; case PHY_MODE_PCIE: phy_pcie_mode_set(pcie_phy, 0); break; default: return -EINVAL; } dev_dbg(phy->dev, "Changing PHY mode to %d\n", mode); pcie_phy->mode = mode; return 0; } static const struct phy_ops jh7110_pcie_phy_ops = { .set_mode = jh7110_pcie_phy_set_mode, }; static int phy_stg_regfield_init(struct udevice *dev, int mode, int usb3) { struct jh7110_pcie_phy *phy = dev_get_priv(dev); struct reg_field phy_mode = REG_FIELD(mode, 20, 21); struct reg_field bus_width = REG_FIELD(usb3, 2, 3); struct reg_field usb3_phy_en = REG_FIELD(usb3, 4, 4); phy->phy_mode = devm_regmap_field_alloc(dev, phy->stg_syscon, phy_mode); if (IS_ERR(phy->phy_mode)) { dev_err(dev, "PHY mode reg field init failed\n"); return PTR_ERR(phy->phy_mode); } phy->bus_width = devm_regmap_field_alloc(dev, phy->stg_syscon, bus_width); if (IS_ERR(phy->bus_width)) { dev_err(dev, "PHY bus width reg field init failed\n"); return PTR_ERR(phy->bus_width); } phy->usb3_phy_en = devm_regmap_field_alloc(dev, phy->stg_syscon, usb3_phy_en); if (IS_ERR(phy->usb3_phy_en)) { dev_err(dev, "USB3 PHY enable field init failed\n"); return PTR_ERR(phy->bus_width); } return 0; } static int phy_sys_regfield_init(struct udevice *dev, int split) { struct jh7110_pcie_phy *phy = dev_get_priv(dev); struct reg_field usb_split = REG_FIELD(split, USB_PDRSTN_SPLIT_BIT, USB_PDRSTN_SPLIT_BIT); phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); if (IS_ERR(phy->usb_split)) { dev_err(dev, "USB split field init failed\n"); return PTR_ERR(phy->usb_split); } return 0; } static int starfive_pcie_phy_get_syscon(struct udevice *dev) { struct jh7110_pcie_phy *phy = dev_get_priv(dev); struct ofnode_phandle_args sys_phandle, stg_phandle; int ret; /* get corresponding syscon phandle */ ret = dev_read_phandle_with_args(dev, "starfive,sys-syscon", NULL, 1, 0, &sys_phandle); if (ret < 0) { dev_err(dev, "Can't get sys cfg phandle: %d\n", ret); return ret; } ret = dev_read_phandle_with_args(dev, "starfive,stg-syscon", NULL, 2, 0, &stg_phandle); if (ret < 0) { dev_err(dev, "Can't get stg cfg phandle: %d\n", ret); return ret; } phy->sys_syscon = syscon_node_to_regmap(sys_phandle.node); /* get syscon register offset */ if (!IS_ERR(phy->sys_syscon)) { ret = phy_sys_regfield_init(dev, SYSCON_USB_PDRSTN_REG_OFFSET); if (ret) return ret; } else { phy->sys_syscon = NULL; } phy->stg_syscon = syscon_node_to_regmap(stg_phandle.node); if (!IS_ERR(phy->stg_syscon)) return phy_stg_regfield_init(dev, stg_phandle.args[0], stg_phandle.args[1]); else phy->stg_syscon = NULL; return 0; } int jh7110_pcie_phy_probe(struct udevice *dev) { struct jh7110_pcie_phy *phy = dev_get_priv(dev); int rc; phy->regs = dev_read_addr_ptr(dev); if (!phy->regs) return -EINVAL; rc = starfive_pcie_phy_get_syscon(dev); if (rc) return rc; phy_kvco_gain_set(phy); return 0; } static const struct udevice_id jh7110_pcie_phy[] = { { .compatible = "starfive,jh7110-pcie-phy"}, {}, }; U_BOOT_DRIVER(jh7110_pcie_phy) = { .name = "jh7110_pcie_phy", .id = UCLASS_PHY, .of_match = jh7110_pcie_phy, .probe = jh7110_pcie_phy_probe, .ops = &jh7110_pcie_phy_ops, .priv_auto = sizeof(struct jh7110_pcie_phy), }; |