Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / phy / socionext / phy-uniphier-pcie.c
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");