1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller 4 * Copyright 2018, Socionext Inc. 5 * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> 6 */ 7 8 #include <linux/bitops.h> 9 #include <linux/bitfield.h> 10 #include <linux/clk.h> 11 #include <linux/iopoll.h> 12 #include <linux/mfd/syscon.h> 13 #include <linux/module.h> 14 #include <linux/of_device.h> 15 #include <linux/phy/phy.h> 16 #include <linux/platform_device.h> 17 #include <linux/regmap.h> 18 #include <linux/reset.h> 19 #include <linux/resource.h> 20 21 /* PHY */ 22 #define PCL_PHY_TEST_I 0x2000 23 #define PCL_PHY_TEST_O 0x2004 24 #define TESTI_DAT_MASK GENMASK(13, 6) 25 #define TESTI_ADR_MASK GENMASK(5, 1) 26 #define TESTI_WR_EN BIT(0) 27 28 #define PCL_PHY_RESET 0x200c 29 #define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */ 30 #define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */ 31 32 /* SG */ 33 #define SG_USBPCIESEL 0x590 34 #define SG_USBPCIESEL_PCIE BIT(0) 35 36 #define PCL_PHY_R00 0 37 #define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */ 38 #define PCL_PHY_R06 6 39 #define RX_EQ_ADJ GENMASK(5, 0) /* EQ adjustment value */ 40 #define RX_EQ_ADJ_VAL 0 41 #define PCL_PHY_R26 26 42 #define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */ 43 #define VCO_CTRL_INIT_VAL 5 44 45 struct uniphier_pciephy_priv { 46 void __iomem *base; 47 struct device *dev; 48 struct clk *clk; 49 struct reset_control *rst; 50 const struct uniphier_pciephy_soc_data *data; 51 }; 52 53 struct uniphier_pciephy_soc_data { 54 bool has_syscon; 55 }; 56 57 static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv, 58 u32 data) 59 { 60 /* need to read TESTO twice after accessing TESTI */ 61 writel(data, priv->base + PCL_PHY_TEST_I); 62 readl(priv->base + PCL_PHY_TEST_O); 63 readl(priv->base + PCL_PHY_TEST_O); 64 } 65 66 static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv, 67 u32 reg, u32 mask, u32 param) 68 { 69 u32 val; 70 71 /* read previous data */ 72 val = FIELD_PREP(TESTI_DAT_MASK, 1); 73 val |= FIELD_PREP(TESTI_ADR_MASK, reg); 74 uniphier_pciephy_testio_write(priv, val); 75 val = readl(priv->base + PCL_PHY_TEST_O); 76 77 /* update value */ 78 val &= ~FIELD_PREP(TESTI_DAT_MASK, mask); 79 val = FIELD_PREP(TESTI_DAT_MASK, mask & param); 80 val |= FIELD_PREP(TESTI_ADR_MASK, reg); 81 uniphier_pciephy_testio_write(priv, val); 82 uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN); 83 uniphier_pciephy_testio_write(priv, val); 84 85 /* read current data as dummy */ 86 val = FIELD_PREP(TESTI_DAT_MASK, 1); 87 val |= FIELD_PREP(TESTI_ADR_MASK, reg); 88 uniphier_pciephy_testio_write(priv, val); 89 readl(priv->base + PCL_PHY_TEST_O); 90 } 91 92 static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv) 93 { 94 u32 val; 95 96 val = readl(priv->base + PCL_PHY_RESET); 97 val &= ~PCL_PHY_RESET_N; 98 val |= PCL_PHY_RESET_N_MNMODE; 99 writel(val, priv->base + PCL_PHY_RESET); 100 } 101 102 static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv) 103 { 104 u32 val; 105 106 val = readl(priv->base + PCL_PHY_RESET); 107 val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N; 108 writel(val, priv->base + PCL_PHY_RESET); 109 } 110 111 static int uniphier_pciephy_init(struct phy *phy) 112 { 113 struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); 114 int ret; 115 116 ret = clk_prepare_enable(priv->clk); 117 if (ret) 118 return ret; 119 120 ret = reset_control_deassert(priv->rst); 121 if (ret) 122 goto out_clk_disable; 123 124 uniphier_pciephy_set_param(priv, PCL_PHY_R00, 125 RX_EQ_ADJ_EN, RX_EQ_ADJ_EN); 126 uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ, 127 FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL)); 128 uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL, 129 FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL)); 130 usleep_range(1, 10); 131 132 uniphier_pciephy_deassert(priv); 133 usleep_range(1, 10); 134 135 return 0; 136 137 out_clk_disable: 138 clk_disable_unprepare(priv->clk); 139 140 return ret; 141 } 142 143 static int uniphier_pciephy_exit(struct phy *phy) 144 { 145 struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); 146 147 uniphier_pciephy_assert(priv); 148 reset_control_assert(priv->rst); 149 clk_disable_unprepare(priv->clk); 150 151 return 0; 152 } 153 154 static const struct phy_ops uniphier_pciephy_ops = { 155 .init = uniphier_pciephy_init, 156 .exit = uniphier_pciephy_exit, 157 .owner = THIS_MODULE, 158 }; 159 160 static int uniphier_pciephy_probe(struct platform_device *pdev) 161 { 162 struct uniphier_pciephy_priv *priv; 163 struct phy_provider *phy_provider; 164 struct device *dev = &pdev->dev; 165 struct regmap *regmap; 166 struct resource *res; 167 struct phy *phy; 168 169 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 170 if (!priv) 171 return -ENOMEM; 172 173 priv->data = of_device_get_match_data(dev); 174 if (WARN_ON(!priv->data)) 175 return -EINVAL; 176 177 priv->dev = dev; 178 179 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 180 priv->base = devm_ioremap_resource(dev, res); 181 if (IS_ERR(priv->base)) 182 return PTR_ERR(priv->base); 183 184 priv->clk = devm_clk_get(dev, NULL); 185 if (IS_ERR(priv->clk)) 186 return PTR_ERR(priv->clk); 187 188 priv->rst = devm_reset_control_get_shared(dev, NULL); 189 if (IS_ERR(priv->rst)) 190 return PTR_ERR(priv->rst); 191 192 phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops); 193 if (IS_ERR(phy)) 194 return PTR_ERR(phy); 195 196 regmap = syscon_regmap_lookup_by_phandle(dev->of_node, 197 "socionext,syscon"); 198 if (!IS_ERR(regmap) && priv->data->has_syscon) 199 regmap_update_bits(regmap, SG_USBPCIESEL, 200 SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE); 201 202 phy_set_drvdata(phy, priv); 203 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 204 205 return PTR_ERR_OR_ZERO(phy_provider); 206 } 207 208 static const struct uniphier_pciephy_soc_data uniphier_ld20_data = { 209 .has_syscon = true, 210 }; 211 212 static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = { 213 .has_syscon = false, 214 }; 215 216 static const struct of_device_id uniphier_pciephy_match[] = { 217 { 218 .compatible = "socionext,uniphier-ld20-pcie-phy", 219 .data = &uniphier_ld20_data, 220 }, 221 { 222 .compatible = "socionext,uniphier-pxs3-pcie-phy", 223 .data = &uniphier_pxs3_data, 224 }, 225 { /* sentinel */ }, 226 }; 227 MODULE_DEVICE_TABLE(of, uniphier_pciephy_match); 228 229 static struct platform_driver uniphier_pciephy_driver = { 230 .probe = uniphier_pciephy_probe, 231 .driver = { 232 .name = "uniphier-pcie-phy", 233 .of_match_table = uniphier_pciephy_match, 234 }, 235 }; 236 module_platform_driver(uniphier_pciephy_driver); 237 238 MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 239 MODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller"); 240 MODULE_LICENSE("GPL v2"); 241