| .. | .. |
|---|
| 13 | 13 | #include <linux/mfd/syscon.h> |
|---|
| 14 | 14 | #include <linux/module.h> |
|---|
| 15 | 15 | #include <linux/of_device.h> |
|---|
| 16 | +#include <linux/phy/pcie.h> |
|---|
| 16 | 17 | #include <linux/phy/phy.h> |
|---|
| 17 | 18 | #include <linux/regmap.h> |
|---|
| 18 | 19 | #include <linux/reset.h> |
|---|
| 19 | | -#include <dt-bindings/phy/phy.h> |
|---|
| 20 | +#include <dt-bindings/phy/phy-snps-pcie3.h> |
|---|
| 20 | 21 | |
|---|
| 22 | +/* Register for RK3568 */ |
|---|
| 21 | 23 | #define GRF_PCIE30PHY_CON1 0x4 |
|---|
| 22 | 24 | #define GRF_PCIE30PHY_CON4 0x10 |
|---|
| 23 | 25 | #define GRF_PCIE30PHY_CON6 0x18 |
|---|
| .. | .. |
|---|
| 25 | 27 | #define GRF_PCIE30PHY_STATUS0 0x80 |
|---|
| 26 | 28 | #define SRAM_INIT_DONE(reg) (reg & BIT(14)) |
|---|
| 27 | 29 | |
|---|
| 30 | +/* Register for RK3588 */ |
|---|
| 31 | +#define PHP_GRF_PCIESEL_CON 0x100 |
|---|
| 32 | +#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0 |
|---|
| 33 | +#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904 |
|---|
| 34 | +#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04 |
|---|
| 35 | +#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0)) |
|---|
| 36 | + |
|---|
| 37 | +struct rockchip_p3phy_ops; |
|---|
| 38 | + |
|---|
| 28 | 39 | struct rockchip_p3phy_priv { |
|---|
| 40 | + const struct rockchip_p3phy_ops *ops; |
|---|
| 29 | 41 | void __iomem *mmio; |
|---|
| 42 | + /* mode: RC, EP */ |
|---|
| 30 | 43 | int mode; |
|---|
| 44 | + /* pcie30_phymode: Aggregation, Bifurcation */ |
|---|
| 45 | + int pcie30_phymode; |
|---|
| 31 | 46 | struct regmap *phy_grf; |
|---|
| 47 | + struct regmap *pipe_grf; |
|---|
| 32 | 48 | struct reset_control *p30phy; |
|---|
| 33 | | - struct clk *ref_clk_m; |
|---|
| 34 | | - struct clk *ref_clk_n; |
|---|
| 35 | | - struct clk *pclk; |
|---|
| 36 | 49 | struct phy *phy; |
|---|
| 50 | + struct clk_bulk_data *clks; |
|---|
| 51 | + int num_clks; |
|---|
| 37 | 52 | bool is_bifurcation; |
|---|
| 38 | 53 | }; |
|---|
| 39 | 54 | |
|---|
| 40 | | -static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode) |
|---|
| 55 | +struct rockchip_p3phy_ops { |
|---|
| 56 | + int (*phy_init)(struct rockchip_p3phy_priv *priv); |
|---|
| 57 | +}; |
|---|
| 58 | + |
|---|
| 59 | +static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) |
|---|
| 41 | 60 | { |
|---|
| 42 | 61 | struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); |
|---|
| 43 | 62 | |
|---|
| 44 | 63 | /* Acutally We don't care EP/RC mode, but just record it */ |
|---|
| 45 | | - switch (mode) { |
|---|
| 64 | + switch (submode) { |
|---|
| 46 | 65 | case PHY_MODE_PCIE_RC: |
|---|
| 47 | 66 | priv->mode = PHY_MODE_PCIE_RC; |
|---|
| 48 | 67 | break; |
|---|
| .. | .. |
|---|
| 64 | 83 | #include "phy-rockchip-snps-pcie3.fw" |
|---|
| 65 | 84 | }; |
|---|
| 66 | 85 | |
|---|
| 67 | | -static int rochchip_p3phy_init(struct phy *phy) |
|---|
| 86 | +static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv) |
|---|
| 68 | 87 | { |
|---|
| 69 | | - struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); |
|---|
| 70 | 88 | int i; |
|---|
| 71 | 89 | int ret = 0; |
|---|
| 72 | 90 | u32 reg; |
|---|
| 73 | | - |
|---|
| 74 | | - ret = clk_prepare_enable(priv->ref_clk_m); |
|---|
| 75 | | - if (ret < 0) |
|---|
| 76 | | - return ret; |
|---|
| 77 | | - |
|---|
| 78 | | - ret = clk_prepare_enable(priv->ref_clk_n); |
|---|
| 79 | | - if (ret < 0) |
|---|
| 80 | | - goto err_ref; |
|---|
| 81 | | - |
|---|
| 82 | | - ret = clk_prepare_enable(priv->pclk); |
|---|
| 83 | | - if (ret < 0) |
|---|
| 84 | | - goto err_pclk; |
|---|
| 85 | | - |
|---|
| 86 | | - reset_control_assert(priv->p30phy); |
|---|
| 87 | | - udelay(1); |
|---|
| 88 | 91 | |
|---|
| 89 | 92 | /* Deassert PCIe PMA output clamp mode */ |
|---|
| 90 | 93 | regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, |
|---|
| .. | .. |
|---|
| 96 | 99 | regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1, |
|---|
| 97 | 100 | (0x1 << 15) | (0x1 << 31)); |
|---|
| 98 | 101 | } |
|---|
| 99 | | - |
|---|
| 100 | 102 | regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON4, |
|---|
| 101 | 103 | (0x0 << 14) | (0x1 << (14 + 16))); //sdram_ld_done |
|---|
| 102 | 104 | regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON4, |
|---|
| 103 | 105 | (0x0 << 13) | (0x1 << (13 + 16))); //sdram_bypass |
|---|
| 104 | | - reset_control_deassert(priv->p30phy); |
|---|
| 105 | 106 | |
|---|
| 107 | + reset_control_deassert(priv->p30phy); |
|---|
| 106 | 108 | ret = regmap_read_poll_timeout(priv->phy_grf, |
|---|
| 107 | 109 | GRF_PCIE30PHY_STATUS0, |
|---|
| 108 | 110 | reg, SRAM_INIT_DONE(reg), |
|---|
| .. | .. |
|---|
| 110 | 112 | if (ret) { |
|---|
| 111 | 113 | pr_err("%s: lock failed 0x%x, check input refclk and power supply\n", |
|---|
| 112 | 114 | __func__, reg); |
|---|
| 113 | | - goto err_pclk; |
|---|
| 115 | + goto out; |
|---|
| 114 | 116 | } |
|---|
| 115 | 117 | |
|---|
| 116 | 118 | regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, |
|---|
| .. | .. |
|---|
| 124 | 126 | regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON4, |
|---|
| 125 | 127 | (0x1 << 14) | (0x1 << (14 + 16))); //sdram_ld_done |
|---|
| 126 | 128 | |
|---|
| 127 | | - return 0; |
|---|
| 128 | | -err_pclk: |
|---|
| 129 | | - clk_disable_unprepare(priv->ref_clk_n); |
|---|
| 130 | | -err_ref: |
|---|
| 131 | | - clk_disable_unprepare(priv->ref_clk_m); |
|---|
| 129 | +out: |
|---|
| 130 | + return ret; |
|---|
| 131 | +} |
|---|
| 132 | + |
|---|
| 133 | +static const struct rockchip_p3phy_ops rk3568_ops = { |
|---|
| 134 | + .phy_init = rockchip_p3phy_rk3568_init, |
|---|
| 135 | +}; |
|---|
| 136 | + |
|---|
| 137 | +static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv) |
|---|
| 138 | +{ |
|---|
| 139 | + int ret = 0; |
|---|
| 140 | + u32 reg; |
|---|
| 141 | + |
|---|
| 142 | + /* Deassert PCIe PMA output clamp mode */ |
|---|
| 143 | + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, |
|---|
| 144 | + (0x1 << 8) | (0x1 << 24)); |
|---|
| 145 | + |
|---|
| 146 | + reset_control_deassert(priv->p30phy); |
|---|
| 147 | + |
|---|
| 148 | + ret = regmap_read_poll_timeout(priv->phy_grf, |
|---|
| 149 | + RK3588_PCIE3PHY_GRF_PHY0_STATUS1, |
|---|
| 150 | + reg, RK3588_SRAM_INIT_DONE(reg), |
|---|
| 151 | + 0, 500); |
|---|
| 152 | + ret |= regmap_read_poll_timeout(priv->phy_grf, |
|---|
| 153 | + RK3588_PCIE3PHY_GRF_PHY1_STATUS1, |
|---|
| 154 | + reg, RK3588_SRAM_INIT_DONE(reg), |
|---|
| 155 | + 0, 500); |
|---|
| 156 | + if (ret) |
|---|
| 157 | + pr_err("%s: lock failed 0x%x, check input refclk and power supply\n", |
|---|
| 158 | + __func__, reg); |
|---|
| 159 | + return ret; |
|---|
| 160 | +} |
|---|
| 161 | + |
|---|
| 162 | +static const struct rockchip_p3phy_ops rk3588_ops = { |
|---|
| 163 | + .phy_init = rockchip_p3phy_rk3588_init, |
|---|
| 164 | +}; |
|---|
| 165 | + |
|---|
| 166 | +static int rochchip_p3phy_init(struct phy *phy) |
|---|
| 167 | +{ |
|---|
| 168 | + struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); |
|---|
| 169 | + int ret; |
|---|
| 170 | + |
|---|
| 171 | + ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); |
|---|
| 172 | + if (ret) { |
|---|
| 173 | + pr_err("failed to enable PCIe bulk clks %d\n", ret); |
|---|
| 174 | + return ret; |
|---|
| 175 | + } |
|---|
| 176 | + |
|---|
| 177 | + reset_control_assert(priv->p30phy); |
|---|
| 178 | + udelay(1); |
|---|
| 179 | + |
|---|
| 180 | + if (priv->ops->phy_init) { |
|---|
| 181 | + ret = priv->ops->phy_init(priv); |
|---|
| 182 | + if (ret) |
|---|
| 183 | + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); |
|---|
| 184 | + }; |
|---|
| 185 | + |
|---|
| 132 | 186 | return ret; |
|---|
| 133 | 187 | } |
|---|
| 134 | 188 | |
|---|
| 135 | 189 | static int rochchip_p3phy_exit(struct phy *phy) |
|---|
| 136 | 190 | { |
|---|
| 137 | 191 | struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy); |
|---|
| 138 | | - clk_disable_unprepare(priv->ref_clk_m); |
|---|
| 139 | | - clk_disable_unprepare(priv->ref_clk_n); |
|---|
| 140 | | - clk_disable_unprepare(priv->pclk); |
|---|
| 192 | + clk_bulk_disable_unprepare(priv->num_clks, priv->clks); |
|---|
| 141 | 193 | reset_control_assert(priv->p30phy); |
|---|
| 142 | 194 | return 0; |
|---|
| 143 | 195 | } |
|---|
| .. | .. |
|---|
| 157 | 209 | struct device_node *np = dev->of_node; |
|---|
| 158 | 210 | struct resource *res; |
|---|
| 159 | 211 | int ret; |
|---|
| 212 | + u32 val, reg; |
|---|
| 160 | 213 | |
|---|
| 161 | 214 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 162 | 215 | if (!priv) |
|---|
| .. | .. |
|---|
| 169 | 222 | return ret; |
|---|
| 170 | 223 | } |
|---|
| 171 | 224 | |
|---|
| 225 | + priv->ops = of_device_get_match_data(&pdev->dev); |
|---|
| 226 | + if (!priv->ops) { |
|---|
| 227 | + dev_err(&pdev->dev, "no of match data provided\n"); |
|---|
| 228 | + return -EINVAL; |
|---|
| 229 | + } |
|---|
| 230 | + |
|---|
| 172 | 231 | priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf"); |
|---|
| 173 | 232 | if (IS_ERR(priv->phy_grf)) { |
|---|
| 174 | 233 | dev_err(dev, "failed to find rockchip,phy_grf regmap\n"); |
|---|
| 175 | 234 | return PTR_ERR(priv->phy_grf); |
|---|
| 176 | 235 | } |
|---|
| 236 | + |
|---|
| 237 | + priv->pipe_grf = syscon_regmap_lookup_by_phandle(dev->of_node, |
|---|
| 238 | + "rockchip,pipe-grf"); |
|---|
| 239 | + if (IS_ERR(priv->pipe_grf)) |
|---|
| 240 | + dev_info(dev, "failed to find rockchip,pipe_grf regmap\n"); |
|---|
| 241 | + |
|---|
| 242 | + ret = device_property_read_u32(dev, "rockchip,pcie30-phymode", &val); |
|---|
| 243 | + if (!ret) |
|---|
| 244 | + priv->pcie30_phymode = val; |
|---|
| 245 | + else |
|---|
| 246 | + priv->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION; |
|---|
| 247 | + |
|---|
| 248 | + /* Select correct pcie30_phymode */ |
|---|
| 249 | + if (priv->pcie30_phymode > 4) |
|---|
| 250 | + priv->pcie30_phymode = PHY_MODE_PCIE_AGGREGATION; |
|---|
| 251 | + |
|---|
| 252 | + regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, |
|---|
| 253 | + (0x7<<16) | priv->pcie30_phymode); |
|---|
| 254 | + |
|---|
| 255 | + /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */ |
|---|
| 256 | + if (!IS_ERR(priv->pipe_grf)) { |
|---|
| 257 | + reg = priv->pcie30_phymode & 3; |
|---|
| 258 | + if (reg) |
|---|
| 259 | + regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON, |
|---|
| 260 | + (reg << 16) | reg); |
|---|
| 261 | + }; |
|---|
| 177 | 262 | |
|---|
| 178 | 263 | priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops); |
|---|
| 179 | 264 | if (IS_ERR(priv->phy)) { |
|---|
| .. | .. |
|---|
| 187 | 272 | priv->p30phy = NULL; |
|---|
| 188 | 273 | } |
|---|
| 189 | 274 | |
|---|
| 190 | | - priv->ref_clk_m = devm_clk_get(dev, "refclk_m"); |
|---|
| 191 | | - if (IS_ERR(priv->ref_clk_m)) { |
|---|
| 192 | | - dev_err(dev, "failed to find ref clock M\n"); |
|---|
| 193 | | - return PTR_ERR(priv->ref_clk_m); |
|---|
| 194 | | - } |
|---|
| 195 | | - |
|---|
| 196 | | - priv->ref_clk_n = devm_clk_get(dev, "refclk_n"); |
|---|
| 197 | | - if (IS_ERR(priv->ref_clk_n)) { |
|---|
| 198 | | - dev_err(dev, "failed to find ref clock N\n"); |
|---|
| 199 | | - return PTR_ERR(priv->ref_clk_n); |
|---|
| 200 | | - } |
|---|
| 201 | | - |
|---|
| 202 | | - priv->pclk = devm_clk_get(dev, "pclk"); |
|---|
| 203 | | - if (IS_ERR(priv->pclk)) { |
|---|
| 204 | | - dev_err(dev, "failed to find pclk\n"); |
|---|
| 205 | | - return PTR_ERR(priv->pclk); |
|---|
| 206 | | - } |
|---|
| 275 | + priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); |
|---|
| 276 | + if (priv->num_clks < 1) |
|---|
| 277 | + return -ENODEV; |
|---|
| 207 | 278 | |
|---|
| 208 | 279 | dev_set_drvdata(dev, priv); |
|---|
| 209 | 280 | phy_set_drvdata(priv->phy, priv); |
|---|
| .. | .. |
|---|
| 212 | 283 | } |
|---|
| 213 | 284 | |
|---|
| 214 | 285 | static const struct of_device_id rockchip_p3phy_of_match[] = { |
|---|
| 215 | | - { .compatible = "rockchip,rk3568-pcie3-phy" }, |
|---|
| 286 | + { .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops }, |
|---|
| 287 | + { .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops }, |
|---|
| 216 | 288 | { }, |
|---|
| 217 | 289 | }; |
|---|
| 218 | 290 | MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match); |
|---|