diff options
author | Lee Jones <lee.jones@linaro.org> | 2014-07-09 07:41:12 -0400 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2014-07-22 03:16:37 -0400 |
commit | 6e877fedb1cff0f4a0988d30418ad87abaefafcb (patch) | |
tree | 373d179fab4418caad7ce4fcf2e83e661dc4c5a7 /drivers/phy | |
parent | f5c9f3be608017577731ebe8be37e55f800586d3 (diff) |
phy: miphy365x: Provide support for the MiPHY356x Generic PHY
The MiPHY365x is a Generic PHY which can serve various SATA or PCIe
devices. It has 2 ports which it can use for either; both SATA, both
PCIe or one of each in any configuration.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Alexandre Torgue <alexandre.torgue@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 10 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-miphy365x.c | 616 |
3 files changed, 627 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 3e251aa64ffd..cc97c897945a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
@@ -38,6 +38,16 @@ config PHY_MVEBU_SATA | |||
38 | depends on OF | 38 | depends on OF |
39 | select GENERIC_PHY | 39 | select GENERIC_PHY |
40 | 40 | ||
41 | config PHY_MIPHY365X | ||
42 | tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series" | ||
43 | depends on ARCH_STI | ||
44 | depends on GENERIC_PHY | ||
45 | depends on HAS_IOMEM | ||
46 | depends on OF | ||
47 | help | ||
48 | Enable this to support the miphy transceiver (for SATA/PCIE) | ||
49 | that is part of STMicroelectronics STiH41x SoC series. | ||
50 | |||
41 | config OMAP_CONTROL_PHY | 51 | config OMAP_CONTROL_PHY |
42 | tristate "OMAP CONTROL PHY Driver" | 52 | tristate "OMAP CONTROL PHY Driver" |
43 | depends on ARCH_OMAP2PLUS || COMPILE_TEST | 53 | depends on ARCH_OMAP2PLUS || COMPILE_TEST |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54ab9785932c..971ad0aac388 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
@@ -8,6 +8,7 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o | |||
8 | obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o | 8 | obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o |
9 | obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o | 9 | obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o |
10 | obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o | 10 | obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o |
11 | obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o | ||
11 | obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o | 12 | obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o |
12 | obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o | 13 | obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o |
13 | obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o | 14 | obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o |
diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c new file mode 100644 index 000000000000..65ecd04da9bc --- /dev/null +++ b/drivers/phy/phy-miphy365x.c | |||
@@ -0,0 +1,616 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 STMicroelectronics – All Rights Reserved | ||
3 | * | ||
4 | * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416). | ||
5 | * | ||
6 | * Authors: Alexandre Torgue <alexandre.torgue@st.com> | ||
7 | * Lee Jones <lee.jones@linaro.org> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_platform.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/phy/phy.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/mfd/syscon.h> | ||
25 | #include <linux/regmap.h> | ||
26 | |||
27 | #include <dt-bindings/phy/phy-miphy365x.h> | ||
28 | |||
29 | #define HFC_TIMEOUT 100 | ||
30 | |||
31 | #define SYSCFG_2521 0x824 | ||
32 | #define SYSCFG_2522 0x828 | ||
33 | #define SYSCFG_PCIE_SATA_MASK BIT(1) | ||
34 | #define SYSCFG_PCIE_SATA_POS 1 | ||
35 | |||
36 | /* MiPHY365x register definitions */ | ||
37 | #define RESET_REG 0x00 | ||
38 | #define RST_PLL BIT(1) | ||
39 | #define RST_PLL_CAL BIT(2) | ||
40 | #define RST_RX BIT(4) | ||
41 | #define RST_MACRO BIT(7) | ||
42 | |||
43 | #define STATUS_REG 0x01 | ||
44 | #define IDLL_RDY BIT(0) | ||
45 | #define PLL_RDY BIT(1) | ||
46 | #define DES_BIT_LOCK BIT(2) | ||
47 | #define DES_SYMBOL_LOCK BIT(3) | ||
48 | |||
49 | #define CTRL_REG 0x02 | ||
50 | #define TERM_EN BIT(0) | ||
51 | #define PCI_EN BIT(2) | ||
52 | #define DES_BIT_LOCK_EN BIT(3) | ||
53 | #define TX_POL BIT(5) | ||
54 | |||
55 | #define INT_CTRL_REG 0x03 | ||
56 | |||
57 | #define BOUNDARY1_REG 0x10 | ||
58 | #define SPDSEL_SEL BIT(0) | ||
59 | |||
60 | #define BOUNDARY3_REG 0x12 | ||
61 | #define TX_SPDSEL_GEN1_VAL 0 | ||
62 | #define TX_SPDSEL_GEN2_VAL 0x01 | ||
63 | #define TX_SPDSEL_GEN3_VAL 0x02 | ||
64 | #define RX_SPDSEL_GEN1_VAL 0 | ||
65 | #define RX_SPDSEL_GEN2_VAL (0x01 << 3) | ||
66 | #define RX_SPDSEL_GEN3_VAL (0x02 << 3) | ||
67 | |||
68 | #define PCIE_REG 0x16 | ||
69 | |||
70 | #define BUF_SEL_REG 0x20 | ||
71 | #define CONF_GEN_SEL_GEN3 0x02 | ||
72 | #define CONF_GEN_SEL_GEN2 0x01 | ||
73 | #define PD_VDDTFILTER BIT(4) | ||
74 | |||
75 | #define TXBUF1_REG 0x21 | ||
76 | #define SWING_VAL 0x04 | ||
77 | #define SWING_VAL_GEN1 0x03 | ||
78 | #define PREEMPH_VAL (0x3 << 5) | ||
79 | |||
80 | #define TXBUF2_REG 0x22 | ||
81 | #define TXSLEW_VAL 0x2 | ||
82 | #define TXSLEW_VAL_GEN1 0x4 | ||
83 | |||
84 | #define RXBUF_OFFSET_CTRL_REG 0x23 | ||
85 | |||
86 | #define RXBUF_REG 0x25 | ||
87 | #define SDTHRES_VAL 0x01 | ||
88 | #define EQ_ON3 (0x03 << 4) | ||
89 | #define EQ_ON1 (0x01 << 4) | ||
90 | |||
91 | #define COMP_CTRL1_REG 0x40 | ||
92 | #define START_COMSR BIT(0) | ||
93 | #define START_COMZC BIT(1) | ||
94 | #define COMSR_DONE BIT(2) | ||
95 | #define COMZC_DONE BIT(3) | ||
96 | #define COMP_AUTO_LOAD BIT(4) | ||
97 | |||
98 | #define COMP_CTRL2_REG 0x41 | ||
99 | #define COMP_2MHZ_RAT_GEN1 0x1e | ||
100 | #define COMP_2MHZ_RAT 0xf | ||
101 | |||
102 | #define COMP_CTRL3_REG 0x42 | ||
103 | #define COMSR_COMP_REF 0x33 | ||
104 | |||
105 | #define COMP_IDLL_REG 0x47 | ||
106 | #define COMZC_IDLL 0x2a | ||
107 | |||
108 | #define PLL_CTRL1_REG 0x50 | ||
109 | #define PLL_START_CAL BIT(0) | ||
110 | #define BUF_EN BIT(2) | ||
111 | #define SYNCHRO_TX BIT(3) | ||
112 | #define SSC_EN BIT(6) | ||
113 | #define CONFIG_PLL BIT(7) | ||
114 | |||
115 | #define PLL_CTRL2_REG 0x51 | ||
116 | #define BYPASS_PLL_CAL BIT(1) | ||
117 | |||
118 | #define PLL_RAT_REG 0x52 | ||
119 | |||
120 | #define PLL_SSC_STEP_MSB_REG 0x56 | ||
121 | #define PLL_SSC_STEP_MSB_VAL 0x03 | ||
122 | |||
123 | #define PLL_SSC_STEP_LSB_REG 0x57 | ||
124 | #define PLL_SSC_STEP_LSB_VAL 0x63 | ||
125 | |||
126 | #define PLL_SSC_PER_MSB_REG 0x58 | ||
127 | #define PLL_SSC_PER_MSB_VAL 0 | ||
128 | |||
129 | #define PLL_SSC_PER_LSB_REG 0x59 | ||
130 | #define PLL_SSC_PER_LSB_VAL 0xf1 | ||
131 | |||
132 | #define IDLL_TEST_REG 0x72 | ||
133 | #define START_CLK_HF BIT(6) | ||
134 | |||
135 | #define DES_BITLOCK_REG 0x86 | ||
136 | #define BIT_LOCK_LEVEL 0x01 | ||
137 | #define BIT_LOCK_CNT_512 (0x03 << 5) | ||
138 | |||
139 | static u8 ports[] = { MIPHY_PORT_0, MIPHY_PORT_1 }; | ||
140 | |||
141 | struct miphy365x_phy { | ||
142 | struct phy *phy; | ||
143 | void __iomem *base; | ||
144 | void __iomem *sata; | ||
145 | void __iomem *pcie; | ||
146 | u8 type; | ||
147 | u8 port; | ||
148 | }; | ||
149 | |||
150 | struct miphy365x_dev { | ||
151 | struct device *dev; | ||
152 | struct regmap *regmap; | ||
153 | struct mutex miphy_mutex; | ||
154 | struct miphy365x phys[ARRAY_SIZE(ports)]; | ||
155 | bool pcie_tx_pol_inv; | ||
156 | bool sata_tx_pol_inv; | ||
157 | u32 sata_gen; | ||
158 | }; | ||
159 | |||
160 | /* | ||
161 | * These values are represented in Device tree. They are considered to be ABI | ||
162 | * and although they can be extended any existing values must not change. | ||
163 | */ | ||
164 | enum miphy_sata_gen { | ||
165 | SATA_GEN1 = 1, | ||
166 | SATA_GEN2, | ||
167 | SATA_GEN3 | ||
168 | }; | ||
169 | |||
170 | static u8 rx_tx_spd[] = { | ||
171 | TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL, | ||
172 | TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL, | ||
173 | TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL | ||
174 | }; | ||
175 | |||
176 | /* | ||
177 | * This function selects the system configuration, | ||
178 | * either two SATA, one SATA and one PCIe, or two PCIe lanes. | ||
179 | */ | ||
180 | static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, | ||
181 | struct miphy365x_dev *miphy_dev) | ||
182 | { | ||
183 | u8 config = miphy_phy->type | miphy_phy->port; | ||
184 | u32 mask = SYSCFG_PCIE_SATA_MASK; | ||
185 | u32 reg; | ||
186 | bool sata; | ||
187 | |||
188 | switch (config) { | ||
189 | case MIPHY_SATA_PORT0: | ||
190 | reg = SYSCFG_2521; | ||
191 | sata = true; | ||
192 | break; | ||
193 | case MIPHY_PCIE_PORT1: | ||
194 | reg = SYSCFG_2522; | ||
195 | sata = false; | ||
196 | break; | ||
197 | default: | ||
198 | dev_err(miphy_dev->dev, "Configuration not supported\n"); | ||
199 | return -EINVAL; | ||
200 | } | ||
201 | |||
202 | return regmap_update_bits(miphy_dev->regmap, reg, mask, | ||
203 | sata << SYSCFG_PCIE_SATA_POS); | ||
204 | } | ||
205 | |||
206 | static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy, | ||
207 | struct miphy365x_dev *miphy_dev) | ||
208 | { | ||
209 | u8 val; | ||
210 | |||
211 | if (miphy_phy->pcie_tx_pol_inv) { | ||
212 | /* Invert Tx polarity and clear pci_txdetect_pol bit */ | ||
213 | val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL; | ||
214 | writeb_relaxed(val, miphy_phy->base + CTRL_REG); | ||
215 | writeb_relaxed(0x00, miphy_phy->base + PCIE_REG); | ||
216 | } | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy, | ||
222 | struct miphy365x_dev *miphy_dev) | ||
223 | { | ||
224 | unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); | ||
225 | u8 mask = IDLL_RDY | PLL_RDY; | ||
226 | u8 regval; | ||
227 | |||
228 | do { | ||
229 | regval = readb_relaxed(miphy_phy->base + STATUS_REG); | ||
230 | if (!(regval & mask)) | ||
231 | return 0; | ||
232 | |||
233 | usleep_range(2000, 2500); | ||
234 | } while (time_before(jiffies, timeout)); | ||
235 | |||
236 | dev_err(miphy_dev->dev, "HFC ready timeout!\n"); | ||
237 | return -EBUSY; | ||
238 | } | ||
239 | |||
240 | static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy, | ||
241 | struct miphy365x_dev *miphy_dev) | ||
242 | { | ||
243 | unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); | ||
244 | u8 mask = IDLL_RDY | PLL_RDY; | ||
245 | u8 regval; | ||
246 | |||
247 | do { | ||
248 | regval = readb_relaxed(miphy_phy->base + STATUS_REG); | ||
249 | if ((regval & mask) == mask) | ||
250 | return 0; | ||
251 | |||
252 | usleep_range(2000, 2500); | ||
253 | } while (time_before(jiffies, timeout)); | ||
254 | |||
255 | dev_err(miphy_dev->dev, "PHY not ready timeout!\n"); | ||
256 | return -EBUSY; | ||
257 | } | ||
258 | |||
259 | static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy, | ||
260 | struct miphy365x_dev *miphy_dev) | ||
261 | { | ||
262 | u8 val, mask; | ||
263 | |||
264 | if (miphy_dev->sata_gen == SATA_GEN1) | ||
265 | writeb_relaxed(COMP_2MHZ_RAT_GEN1, | ||
266 | miphy_phy->base + COMP_CTRL2_REG); | ||
267 | else | ||
268 | writeb_relaxed(COMP_2MHZ_RAT, | ||
269 | miphy_phy->base + COMP_CTRL2_REG); | ||
270 | |||
271 | if (miphy_dev->sata_gen != SATA_GEN3) { | ||
272 | writeb_relaxed(COMSR_COMP_REF, | ||
273 | miphy_phy->base + COMP_CTRL3_REG); | ||
274 | /* | ||
275 | * Force VCO current to value defined by address 0x5A | ||
276 | * and disable PCIe100Mref bit | ||
277 | * Enable auto load compensation for pll_i_bias | ||
278 | */ | ||
279 | writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG); | ||
280 | writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG); | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * Force restart compensation and enable auto load | ||
285 | * for Comzc_Tx, Comzc_Rx and Comsr on macro | ||
286 | */ | ||
287 | val = START_COMSR | START_COMZC | COMP_AUTO_LOAD; | ||
288 | writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG); | ||
289 | |||
290 | mask = COMSR_DONE | COMZC_DONE; | ||
291 | while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask) | ||
292 | cpu_relax(); | ||
293 | } | ||
294 | |||
295 | static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy, | ||
296 | struct miphy365x_dev *miphy_dev) | ||
297 | { | ||
298 | u8 val; | ||
299 | |||
300 | /* | ||
301 | * SSC Settings. SSC will be enabled through Link | ||
302 | * SSC Ampl. = 0.4% | ||
303 | * SSC Freq = 31KHz | ||
304 | */ | ||
305 | writeb_relaxed(PLL_SSC_STEP_MSB_VAL, | ||
306 | miphy_phy->base + PLL_SSC_STEP_MSB_REG); | ||
307 | writeb_relaxed(PLL_SSC_STEP_LSB_VAL, | ||
308 | miphy_phy->base + PLL_SSC_STEP_LSB_REG); | ||
309 | writeb_relaxed(PLL_SSC_PER_MSB_VAL, | ||
310 | miphy_phy->base + PLL_SSC_PER_MSB_REG); | ||
311 | writeb_relaxed(PLL_SSC_PER_LSB_VAL, | ||
312 | miphy_phy->base + PLL_SSC_PER_LSB_REG); | ||
313 | |||
314 | /* SSC Settings complete */ | ||
315 | if (miphy_dev->sata_gen == SATA_GEN1) { | ||
316 | val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; | ||
317 | writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); | ||
318 | } else { | ||
319 | val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; | ||
320 | writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, | ||
325 | struct miphy365x_dev *miphy_dev) | ||
326 | { | ||
327 | int ret; | ||
328 | u8 val; | ||
329 | |||
330 | /* | ||
331 | * Force PHY macro reset, PLL calibration reset, PLL reset | ||
332 | * and assert Deserializer Reset | ||
333 | */ | ||
334 | val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO; | ||
335 | writeb_relaxed(val, miphy_phy->base + RESET_REG); | ||
336 | |||
337 | if (miphy_dev->sata_tx_pol_inv) | ||
338 | writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG); | ||
339 | |||
340 | /* | ||
341 | * Force macro1 to use rx_lspd, tx_lspd | ||
342 | * Force Rx_Clock on first I-DLL phase | ||
343 | * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3 | ||
344 | */ | ||
345 | writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG); | ||
346 | writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG); | ||
347 | val = rx_tx_spd[miphy_dev->sata_gen]; | ||
348 | writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG); | ||
349 | |||
350 | /* Wait for HFC_READY = 0 */ | ||
351 | ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev); | ||
352 | if (ret) | ||
353 | return ret; | ||
354 | |||
355 | /* Compensation Recalibration */ | ||
356 | miphy365x_set_comp(miphy_phy, miphy_dev); | ||
357 | |||
358 | switch (miphy_dev->sata_gen) { | ||
359 | case SATA_GEN3: | ||
360 | /* | ||
361 | * TX Swing target 550-600mv peak to peak diff | ||
362 | * Tx Slew target 90-110ps rising/falling time | ||
363 | * Rx Eq ON3, Sigdet threshold SDTH1 | ||
364 | */ | ||
365 | val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3; | ||
366 | writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG); | ||
367 | val = SWING_VAL | PREEMPH_VAL; | ||
368 | writeb_relaxed(val, miphy_phy->base + TXBUF1_REG); | ||
369 | writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); | ||
370 | writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG); | ||
371 | val = SDTHRES_VAL | EQ_ON3; | ||
372 | writeb_relaxed(val, miphy_phy->base + RXBUF_REG); | ||
373 | break; | ||
374 | case SATA_GEN2: | ||
375 | /* | ||
376 | * conf gen sel=0x1 to program Gen2 banked registers | ||
377 | * VDDT filter ON | ||
378 | * Tx Swing target 550-600mV peak-to-peak diff | ||
379 | * Tx Slew target 90-110 ps rising/falling time | ||
380 | * RX Equalization ON1, Sigdet threshold SDTH1 | ||
381 | */ | ||
382 | writeb_relaxed(CONF_GEN_SEL_GEN2, | ||
383 | miphy_phy->base + BUF_SEL_REG); | ||
384 | writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG); | ||
385 | writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); | ||
386 | val = SDTHRES_VAL | EQ_ON1; | ||
387 | writeb_relaxed(val, miphy_phy->base + RXBUF_REG); | ||
388 | break; | ||
389 | case SATA_GEN1: | ||
390 | /* | ||
391 | * conf gen sel = 00b to program Gen1 banked registers | ||
392 | * VDDT filter ON | ||
393 | * Tx Swing target 500-550mV peak-to-peak diff | ||
394 | * Tx Slew target120-140 ps rising/falling time | ||
395 | */ | ||
396 | writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG); | ||
397 | writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG); | ||
398 | writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG); | ||
399 | break; | ||
400 | default: | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | /* Force Macro1 in partial mode & release pll cal reset */ | ||
405 | writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); | ||
406 | usleep_range(100, 150); | ||
407 | |||
408 | miphy365x_set_ssc(miphy_phy, miphy_dev); | ||
409 | |||
410 | /* Wait for phy_ready */ | ||
411 | ret = miphy365x_rdy(miphy_phy, miphy_dev); | ||
412 | if (ret) | ||
413 | return ret; | ||
414 | |||
415 | /* | ||
416 | * Enable macro1 to use rx_lspd & tx_lspd | ||
417 | * Release Rx_Clock on first I-DLL phase on macro1 | ||
418 | * Assert deserializer reset | ||
419 | * des_bit_lock_en is set | ||
420 | * bit lock detection strength | ||
421 | * Deassert deserializer reset | ||
422 | */ | ||
423 | writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG); | ||
424 | writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG); | ||
425 | writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); | ||
426 | val = miphy_dev->sata_tx_pol_inv ? | ||
427 | (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN; | ||
428 | writeb_relaxed(val, miphy_phy->base + CTRL_REG); | ||
429 | |||
430 | val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL; | ||
431 | writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG); | ||
432 | writeb_relaxed(0x00, miphy_phy->base + RESET_REG); | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static int miphy365x_init(struct phy *phy) | ||
438 | { | ||
439 | struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy); | ||
440 | struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent); | ||
441 | int ret = 0; | ||
442 | |||
443 | mutex_lock(&miphy_dev->miphy_mutex); | ||
444 | |||
445 | ret = miphy365x_set_path(miphy_phy, miphy_dev); | ||
446 | if (ret) { | ||
447 | mutex_unlock(&miphy_dev->miphy_mutex); | ||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | /* Initialise Miphy for PCIe or SATA */ | ||
452 | if (miphy_phy->type == MIPHY_TYPE_PCIE) | ||
453 | ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev); | ||
454 | else | ||
455 | ret = miphy365x_init_sata_port(miphy_phy, miphy_dev); | ||
456 | |||
457 | mutex_unlock(&miphy_dev->miphy_mutex); | ||
458 | |||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | static struct phy *miphy365x_xlate(struct device *dev, | ||
463 | struct of_phandle_args *args) | ||
464 | { | ||
465 | struct miphy365x_dev *state = dev_get_drvdata(dev); | ||
466 | u8 port, type; | ||
467 | |||
468 | if (args->count != 2) { | ||
469 | dev_err(dev, "Invalid number of cells in 'phy' property\n"); | ||
470 | return ERR_PTR(-EINVAL); | ||
471 | } | ||
472 | |||
473 | if (args->args[0] & 0xFFFFFF00 || args->args[1] & 0xFFFFFF00) { | ||
474 | dev_err(dev, "Unsupported flags set in 'phy' property\n"); | ||
475 | return ERR_PTR(-EINVAL); | ||
476 | } | ||
477 | |||
478 | port = args->args[0]; | ||
479 | type = args->args[1]; | ||
480 | |||
481 | if (WARN_ON(port >= ARRAY_SIZE(ports))) | ||
482 | return ERR_PTR(-EINVAL); | ||
483 | |||
484 | if (type == MIPHY_TYPE_SATA) | ||
485 | state->phys[port].base = state->phys[port].sata; | ||
486 | else if (type == MIPHY_TYPE_PCIE) | ||
487 | state->phys[port].base = state->phys[port].pcie; | ||
488 | else { | ||
489 | WARN(1, "Invalid type specified in DT"); | ||
490 | return ERR_PTR(-EINVAL); | ||
491 | } | ||
492 | |||
493 | state->phys[port].type = type; | ||
494 | |||
495 | return state->phys[port].phy; | ||
496 | } | ||
497 | |||
498 | static struct phy_ops miphy365x_ops = { | ||
499 | .init = miphy365x_init, | ||
500 | .owner = THIS_MODULE, | ||
501 | }; | ||
502 | |||
503 | static int miphy365x_get_base_addr(struct platform_device *pdev, | ||
504 | struct miphy365x_phy *phy, u8 port) | ||
505 | { | ||
506 | struct resource *res; | ||
507 | char type[6]; | ||
508 | |||
509 | sprintf(type, "sata%d", port); | ||
510 | |||
511 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); | ||
512 | phy->sata = devm_ioremap_resource(&pdev->dev, res)); | ||
513 | if (!phy->sata) | ||
514 | return -ENOMEM; | ||
515 | |||
516 | sprintf(type, "pcie%d", port); | ||
517 | |||
518 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); | ||
519 | phy->pcie = devm_ioremap_resource(&pdev->dev, res)); | ||
520 | if (!phy->pcie) | ||
521 | return -ENOMEM; | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int miphy365x_of_probe(struct device_node *np, | ||
527 | struct miphy365x_dev *phy_dev) | ||
528 | { | ||
529 | phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); | ||
530 | if (IS_ERR(phy_dev->regmap)) { | ||
531 | dev_err(phy_dev->dev, "No syscfg phandle specified\n"); | ||
532 | return PTR_ERR(phy_dev->regmap); | ||
533 | } | ||
534 | |||
535 | of_property_read_u32(np, "st,sata-gen", &phy_dev->sata_gen); | ||
536 | if (!phy_dev->sata_gen) | ||
537 | phy_dev->sata_gen = SATA_GEN1; | ||
538 | |||
539 | phy_dev->pcie_tx_pol_inv = | ||
540 | of_property_read_bool(np, "st,pcie-tx-pol-inv"); | ||
541 | |||
542 | phy_dev->sata_tx_pol_inv = | ||
543 | of_property_read_bool(np, "st,sata-tx-pol-inv"); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static int miphy365x_probe(struct platform_device *pdev) | ||
549 | { | ||
550 | struct device_node *np = pdev->dev.of_node; | ||
551 | struct miphy365x_dev *phy_dev; | ||
552 | struct device *dev = &pdev->dev; | ||
553 | struct phy_provider *provider; | ||
554 | u8 port; | ||
555 | int ret; | ||
556 | |||
557 | phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); | ||
558 | if (!phy_dev) | ||
559 | return -ENOMEM; | ||
560 | |||
561 | ret = miphy365x_of_probe(np, phy_dev); | ||
562 | if (ret) | ||
563 | return ret; | ||
564 | |||
565 | phy_dev->dev = dev; | ||
566 | |||
567 | dev_set_drvdata(dev, phy_dev); | ||
568 | |||
569 | mutex_init(&phy_dev->miphy_mutex); | ||
570 | |||
571 | for (port = 0; port < ARRAY_SIZE(ports); port++) { | ||
572 | struct phy *phy; | ||
573 | |||
574 | phy = devm_phy_create(dev, &miphy365x_ops, NULL); | ||
575 | if (IS_ERR(phy)) { | ||
576 | dev_err(dev, "failed to create PHY on port %d\n", port); | ||
577 | return PTR_ERR(phy); | ||
578 | } | ||
579 | |||
580 | phy_dev->phys[port].phy = phy; | ||
581 | phy_dev->phys[port].port = port; | ||
582 | |||
583 | ret = miphy365x_get_base_addr(pdev, | ||
584 | &phy_dev->phys[port], port); | ||
585 | if (ret) | ||
586 | return ret; | ||
587 | |||
588 | phy_set_drvdata(phy, &phy_dev->phys[port]); | ||
589 | } | ||
590 | |||
591 | provider = devm_of_phy_provider_register(dev, miphy365x_xlate); | ||
592 | if (IS_ERR(provider)) | ||
593 | return PTR_ERR(provider); | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | static const struct of_device_id miphy365x_of_match[] = { | ||
599 | { .compatible = "st,miphy365x-phy", }, | ||
600 | { }, | ||
601 | }; | ||
602 | MODULE_DEVICE_TABLE(of, miphy365x_of_match); | ||
603 | |||
604 | static struct platform_driver miphy365x_driver = { | ||
605 | .probe = miphy365x_probe, | ||
606 | .driver = { | ||
607 | .name = "miphy365x-phy", | ||
608 | .owner = THIS_MODULE, | ||
609 | .of_match_table = miphy365x_of_match, | ||
610 | } | ||
611 | }; | ||
612 | module_platform_driver(miphy365x_driver); | ||
613 | |||
614 | MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>"); | ||
615 | MODULE_DESCRIPTION("STMicroelectronics miphy365x driver"); | ||
616 | MODULE_LICENSE("GPL v2"); | ||