Commit | Line | Data |
---|---|---|
0d486806 BN |
1 | /* |
2 | * Broadcom SATA3 AHCI Controller PHY Driver | |
3 | * | |
037c4189 | 4 | * Copyright (C) 2016 Broadcom |
0d486806 BN |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
4faee9a4 | 17 | #include <linux/delay.h> |
0d486806 BN |
18 | #include <linux/device.h> |
19 | #include <linux/init.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/kernel.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/of.h> | |
25 | #include <linux/phy/phy.h> | |
26 | #include <linux/platform_device.h> | |
27 | ||
4faee9a4 AP |
28 | #define SATA_PCB_BANK_OFFSET 0x23c |
29 | #define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4) | |
0d486806 BN |
30 | |
31 | #define MAX_PORTS 2 | |
32 | ||
33 | /* Register offset between PHYs in PCB space */ | |
4faee9a4 | 34 | #define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000 |
810c6f16 | 35 | |
c1602a1a JS |
36 | /* The older SATA PHY registers duplicated per port registers within the map, |
37 | * rather than having a separate map per port. | |
38 | */ | |
4faee9a4 AP |
39 | #define SATA_PCB_REG_40NM_SPACE_SIZE 0x10 |
40 | ||
41 | /* Register offset between PHYs in PHY control space */ | |
42 | #define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 | |
c1602a1a | 43 | |
810c6f16 | 44 | enum brcm_sata_phy_version { |
4faee9a4 AP |
45 | BRCM_SATA_PHY_STB_28NM, |
46 | BRCM_SATA_PHY_STB_40NM, | |
47 | BRCM_SATA_PHY_IPROC_NS2, | |
02481288 | 48 | BRCM_SATA_PHY_IPROC_NSP, |
810c6f16 | 49 | }; |
0d486806 BN |
50 | |
51 | struct brcm_sata_port { | |
52 | int portnum; | |
53 | struct phy *phy; | |
54 | struct brcm_sata_phy *phy_priv; | |
55 | bool ssc_en; | |
56 | }; | |
57 | ||
58 | struct brcm_sata_phy { | |
59 | struct device *dev; | |
60 | void __iomem *phy_base; | |
4faee9a4 | 61 | void __iomem *ctrl_base; |
810c6f16 | 62 | enum brcm_sata_phy_version version; |
0d486806 BN |
63 | |
64 | struct brcm_sata_port phys[MAX_PORTS]; | |
65 | }; | |
66 | ||
4faee9a4 AP |
67 | enum sata_phy_regs { |
68 | BLOCK0_REG_BANK = 0x000, | |
69 | BLOCK0_XGXSSTATUS = 0x81, | |
70 | BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12), | |
71 | BLOCK0_SPARE = 0x8d, | |
72 | BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, | |
73 | BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, | |
74 | ||
75 | PLL_REG_BANK_0 = 0x050, | |
0d486806 | 76 | PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, |
02481288 YRDR |
77 | PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), |
78 | PLLCONTROL_0_FREQ_MONITOR = BIT(12), | |
79 | PLLCONTROL_0_SEQ_START = BIT(15), | |
80 | PLL_CAP_CONTROL = 0x85, | |
81 | PLL_ACTRL2 = 0x8b, | |
82 | PLL_ACTRL2_SELDIV_MASK = 0x1f, | |
83 | PLL_ACTRL2_SELDIV_SHIFT = 9, | |
0d486806 | 84 | |
4faee9a4 AP |
85 | PLL1_REG_BANK = 0x060, |
86 | PLL1_ACTRL2 = 0x82, | |
87 | PLL1_ACTRL3 = 0x83, | |
88 | PLL1_ACTRL4 = 0x84, | |
89 | ||
90 | OOB_REG_BANK = 0x150, | |
02481288 | 91 | OOB1_REG_BANK = 0x160, |
4faee9a4 AP |
92 | OOB_CTRL1 = 0x80, |
93 | OOB_CTRL1_BURST_MAX_MASK = 0xf, | |
94 | OOB_CTRL1_BURST_MAX_SHIFT = 12, | |
95 | OOB_CTRL1_BURST_MIN_MASK = 0xf, | |
96 | OOB_CTRL1_BURST_MIN_SHIFT = 8, | |
97 | OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf, | |
98 | OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4, | |
99 | OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf, | |
100 | OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0, | |
101 | OOB_CTRL2 = 0x81, | |
102 | OOB_CTRL2_SEL_ENA_SHIFT = 15, | |
103 | OOB_CTRL2_SEL_ENA_RC_SHIFT = 14, | |
104 | OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f, | |
105 | OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8, | |
106 | OOB_CTRL2_BURST_CNT_MASK = 0x3, | |
107 | OOB_CTRL2_BURST_CNT_SHIFT = 6, | |
108 | OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f, | |
109 | OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0, | |
110 | ||
0d486806 BN |
111 | TXPMD_REG_BANK = 0x1a0, |
112 | TXPMD_CONTROL1 = 0x81, | |
113 | TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), | |
114 | TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), | |
115 | TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, | |
116 | TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, | |
117 | TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, | |
118 | TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, | |
119 | TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, | |
120 | }; | |
121 | ||
4faee9a4 AP |
122 | enum sata_phy_ctrl_regs { |
123 | PHY_CTRL_1 = 0x0, | |
124 | PHY_CTRL_1_RESET = BIT(0), | |
125 | }; | |
126 | ||
127 | static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) | |
0d486806 BN |
128 | { |
129 | struct brcm_sata_phy *priv = port->phy_priv; | |
4faee9a4 AP |
130 | u32 size = 0; |
131 | ||
132 | switch (priv->version) { | |
133 | case BRCM_SATA_PHY_STB_28NM: | |
134 | case BRCM_SATA_PHY_IPROC_NS2: | |
135 | size = SATA_PCB_REG_28NM_SPACE_SIZE; | |
136 | break; | |
137 | case BRCM_SATA_PHY_STB_40NM: | |
138 | size = SATA_PCB_REG_40NM_SPACE_SIZE; | |
139 | break; | |
140 | default: | |
141 | dev_err(priv->dev, "invalid phy version\n"); | |
142 | break; | |
143 | }; | |
0d486806 | 144 | |
4faee9a4 AP |
145 | return priv->phy_base + (port->portnum * size); |
146 | } | |
147 | ||
148 | static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) | |
149 | { | |
150 | struct brcm_sata_phy *priv = port->phy_priv; | |
151 | u32 size = 0; | |
152 | ||
153 | switch (priv->version) { | |
154 | case BRCM_SATA_PHY_IPROC_NS2: | |
155 | size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE; | |
156 | break; | |
157 | default: | |
c1602a1a | 158 | dev_err(priv->dev, "invalid phy version\n"); |
4faee9a4 AP |
159 | break; |
160 | }; | |
810c6f16 | 161 | |
4faee9a4 | 162 | return priv->ctrl_base + (port->portnum * size); |
0d486806 BN |
163 | } |
164 | ||
4faee9a4 AP |
165 | static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank, |
166 | u32 ofs, u32 msk, u32 value) | |
0d486806 BN |
167 | { |
168 | u32 tmp; | |
169 | ||
4faee9a4 AP |
170 | writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); |
171 | tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); | |
0d486806 | 172 | tmp = (tmp & msk) | value; |
4faee9a4 AP |
173 | writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs)); |
174 | } | |
175 | ||
176 | static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) | |
177 | { | |
178 | writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); | |
179 | return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); | |
0d486806 BN |
180 | } |
181 | ||
182 | /* These defaults were characterized by H/W group */ | |
4faee9a4 AP |
183 | #define STB_FMIN_VAL_DEFAULT 0x3df |
184 | #define STB_FMAX_VAL_DEFAULT 0x3df | |
185 | #define STB_FMAX_VAL_SSC 0x83 | |
0d486806 | 186 | |
4faee9a4 | 187 | static int brcm_stb_sata_init(struct brcm_sata_port *port) |
0d486806 | 188 | { |
4faee9a4 | 189 | void __iomem *base = brcm_sata_pcb_base(port); |
0d486806 BN |
190 | struct brcm_sata_phy *priv = port->phy_priv; |
191 | u32 tmp; | |
192 | ||
193 | /* override the TX spread spectrum setting */ | |
194 | tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; | |
4faee9a4 | 195 | brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); |
0d486806 BN |
196 | |
197 | /* set fixed min freq */ | |
4faee9a4 AP |
198 | brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, |
199 | ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, | |
200 | STB_FMIN_VAL_DEFAULT); | |
0d486806 BN |
201 | |
202 | /* set fixed max freq depending on SSC config */ | |
203 | if (port->ssc_en) { | |
4faee9a4 AP |
204 | dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum); |
205 | tmp = STB_FMAX_VAL_SSC; | |
0d486806 | 206 | } else { |
4faee9a4 | 207 | tmp = STB_FMAX_VAL_DEFAULT; |
0d486806 BN |
208 | } |
209 | ||
4faee9a4 | 210 | brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, |
0d486806 | 211 | ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); |
4faee9a4 AP |
212 | |
213 | return 0; | |
214 | } | |
215 | ||
216 | /* NS2 SATA PLL1 defaults were characterized by H/W group */ | |
217 | #define NS2_PLL1_ACTRL2_MAGIC 0x1df8 | |
218 | #define NS2_PLL1_ACTRL3_MAGIC 0x2b00 | |
219 | #define NS2_PLL1_ACTRL4_MAGIC 0x8824 | |
220 | ||
221 | static int brcm_ns2_sata_init(struct brcm_sata_port *port) | |
222 | { | |
223 | int try; | |
224 | unsigned int val; | |
225 | void __iomem *base = brcm_sata_pcb_base(port); | |
226 | void __iomem *ctrl_base = brcm_sata_ctrl_base(port); | |
227 | struct device *dev = port->phy_priv->dev; | |
228 | ||
229 | /* Configure OOB control */ | |
230 | val = 0x0; | |
231 | val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT); | |
232 | val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT); | |
233 | val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); | |
234 | val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); | |
235 | brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val); | |
236 | val = 0x0; | |
237 | val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); | |
238 | val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT); | |
239 | val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); | |
240 | brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val); | |
241 | ||
242 | /* Configure PHY PLL register bank 1 */ | |
243 | val = NS2_PLL1_ACTRL2_MAGIC; | |
244 | brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val); | |
245 | val = NS2_PLL1_ACTRL3_MAGIC; | |
246 | brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val); | |
247 | val = NS2_PLL1_ACTRL4_MAGIC; | |
248 | brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val); | |
249 | ||
250 | /* Configure PHY BLOCK0 register bank */ | |
251 | /* Set oob_clk_sel to refclk/2 */ | |
252 | brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE, | |
253 | ~BLOCK0_SPARE_OOB_CLK_SEL_MASK, | |
254 | BLOCK0_SPARE_OOB_CLK_SEL_REFBY2); | |
255 | ||
256 | /* Strobe PHY reset using PHY control register */ | |
257 | writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1); | |
258 | mdelay(1); | |
259 | writel(0x0, ctrl_base + PHY_CTRL_1); | |
260 | mdelay(1); | |
261 | ||
262 | /* Wait for PHY PLL lock by polling pll_lock bit */ | |
263 | try = 50; | |
264 | while (try) { | |
265 | val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, | |
266 | BLOCK0_XGXSSTATUS); | |
267 | if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) | |
268 | break; | |
269 | msleep(20); | |
270 | try--; | |
271 | } | |
272 | if (!try) { | |
273 | /* PLL did not lock; give up */ | |
274 | dev_err(dev, "port%d PLL did not lock\n", port->portnum); | |
275 | return -ETIMEDOUT; | |
276 | } | |
277 | ||
278 | dev_dbg(dev, "port%d initialized\n", port->portnum); | |
279 | ||
280 | return 0; | |
0d486806 BN |
281 | } |
282 | ||
02481288 YRDR |
283 | static int brcm_nsp_sata_init(struct brcm_sata_port *port) |
284 | { | |
285 | struct brcm_sata_phy *priv = port->phy_priv; | |
286 | struct device *dev = port->phy_priv->dev; | |
287 | void __iomem *base = priv->phy_base; | |
288 | unsigned int oob_bank; | |
289 | unsigned int val, try; | |
290 | ||
291 | /* Configure OOB control */ | |
292 | if (port->portnum == 0) | |
293 | oob_bank = OOB_REG_BANK; | |
294 | else if (port->portnum == 1) | |
295 | oob_bank = OOB1_REG_BANK; | |
296 | else | |
297 | return -EINVAL; | |
298 | ||
299 | val = 0x0; | |
300 | val |= (0x0f << OOB_CTRL1_BURST_MAX_SHIFT); | |
301 | val |= (0x06 << OOB_CTRL1_BURST_MIN_SHIFT); | |
302 | val |= (0x0f << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); | |
303 | val |= (0x06 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); | |
304 | brcm_sata_phy_wr(base, oob_bank, OOB_CTRL1, 0x0, val); | |
305 | ||
306 | val = 0x0; | |
307 | val |= (0x2e << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); | |
308 | val |= (0x02 << OOB_CTRL2_BURST_CNT_SHIFT); | |
309 | val |= (0x16 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); | |
310 | brcm_sata_phy_wr(base, oob_bank, OOB_CTRL2, 0x0, val); | |
311 | ||
312 | ||
313 | brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_ACTRL2, | |
314 | ~(PLL_ACTRL2_SELDIV_MASK << PLL_ACTRL2_SELDIV_SHIFT), | |
315 | 0x0c << PLL_ACTRL2_SELDIV_SHIFT); | |
316 | ||
317 | brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CONTROL, | |
318 | 0xff0, 0x4f0); | |
319 | ||
320 | val = PLLCONTROL_0_FREQ_DET_RESTART | PLLCONTROL_0_FREQ_MONITOR; | |
321 | brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, | |
322 | ~val, val); | |
323 | val = PLLCONTROL_0_SEQ_START; | |
324 | brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, | |
325 | ~val, 0); | |
326 | mdelay(10); | |
327 | brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, | |
328 | ~val, val); | |
329 | ||
330 | /* Wait for pll_seq_done bit */ | |
331 | try = 50; | |
332 | while (try--) { | |
333 | val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, | |
334 | BLOCK0_XGXSSTATUS); | |
335 | if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) | |
336 | break; | |
337 | msleep(20); | |
338 | } | |
339 | if (!try) { | |
340 | /* PLL did not lock; give up */ | |
341 | dev_err(dev, "port%d PLL did not lock\n", port->portnum); | |
342 | return -ETIMEDOUT; | |
343 | } | |
344 | ||
345 | dev_dbg(dev, "port%d initialized\n", port->portnum); | |
346 | ||
347 | return 0; | |
348 | } | |
349 | ||
0d486806 BN |
350 | static int brcm_sata_phy_init(struct phy *phy) |
351 | { | |
4faee9a4 | 352 | int rc; |
0d486806 BN |
353 | struct brcm_sata_port *port = phy_get_drvdata(phy); |
354 | ||
4faee9a4 AP |
355 | switch (port->phy_priv->version) { |
356 | case BRCM_SATA_PHY_STB_28NM: | |
357 | case BRCM_SATA_PHY_STB_40NM: | |
358 | rc = brcm_stb_sata_init(port); | |
359 | break; | |
360 | case BRCM_SATA_PHY_IPROC_NS2: | |
361 | rc = brcm_ns2_sata_init(port); | |
362 | break; | |
02481288 YRDR |
363 | case BRCM_SATA_PHY_IPROC_NSP: |
364 | rc = brcm_nsp_sata_init(port); | |
365 | break; | |
4faee9a4 AP |
366 | default: |
367 | rc = -ENODEV; | |
368 | }; | |
0d486806 BN |
369 | |
370 | return 0; | |
371 | } | |
372 | ||
c1602a1a | 373 | static const struct phy_ops phy_ops = { |
0d486806 BN |
374 | .init = brcm_sata_phy_init, |
375 | .owner = THIS_MODULE, | |
376 | }; | |
377 | ||
378 | static const struct of_device_id brcm_sata_phy_of_match[] = { | |
810c6f16 | 379 | { .compatible = "brcm,bcm7445-sata-phy", |
4faee9a4 | 380 | .data = (void *)BRCM_SATA_PHY_STB_28NM }, |
c1602a1a | 381 | { .compatible = "brcm,bcm7425-sata-phy", |
4faee9a4 AP |
382 | .data = (void *)BRCM_SATA_PHY_STB_40NM }, |
383 | { .compatible = "brcm,iproc-ns2-sata-phy", | |
384 | .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, | |
02481288 YRDR |
385 | { .compatible = "brcm,iproc-nsp-sata-phy", |
386 | .data = (void *)BRCM_SATA_PHY_IPROC_NSP }, | |
0d486806 BN |
387 | {}, |
388 | }; | |
389 | MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); | |
390 | ||
391 | static int brcm_sata_phy_probe(struct platform_device *pdev) | |
392 | { | |
393 | struct device *dev = &pdev->dev; | |
394 | struct device_node *dn = dev->of_node, *child; | |
810c6f16 | 395 | const struct of_device_id *of_id; |
0d486806 BN |
396 | struct brcm_sata_phy *priv; |
397 | struct resource *res; | |
398 | struct phy_provider *provider; | |
0b25ff86 | 399 | int ret, count = 0; |
0d486806 BN |
400 | |
401 | if (of_get_child_count(dn) == 0) | |
402 | return -ENODEV; | |
403 | ||
404 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
405 | if (!priv) | |
406 | return -ENOMEM; | |
407 | dev_set_drvdata(dev, priv); | |
408 | priv->dev = dev; | |
409 | ||
410 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); | |
411 | priv->phy_base = devm_ioremap_resource(dev, res); | |
412 | if (IS_ERR(priv->phy_base)) | |
413 | return PTR_ERR(priv->phy_base); | |
414 | ||
810c6f16 JS |
415 | of_id = of_match_node(brcm_sata_phy_of_match, dn); |
416 | if (of_id) | |
417 | priv->version = (enum brcm_sata_phy_version)of_id->data; | |
418 | else | |
4faee9a4 AP |
419 | priv->version = BRCM_SATA_PHY_STB_28NM; |
420 | ||
421 | if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { | |
422 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
423 | "phy-ctrl"); | |
424 | priv->ctrl_base = devm_ioremap_resource(dev, res); | |
425 | if (IS_ERR(priv->ctrl_base)) | |
426 | return PTR_ERR(priv->ctrl_base); | |
427 | } | |
810c6f16 | 428 | |
0d486806 BN |
429 | for_each_available_child_of_node(dn, child) { |
430 | unsigned int id; | |
431 | struct brcm_sata_port *port; | |
432 | ||
433 | if (of_property_read_u32(child, "reg", &id)) { | |
434 | dev_err(dev, "missing reg property in node %s\n", | |
435 | child->name); | |
0b25ff86 JL |
436 | ret = -EINVAL; |
437 | goto put_child; | |
0d486806 BN |
438 | } |
439 | ||
440 | if (id >= MAX_PORTS) { | |
441 | dev_err(dev, "invalid reg: %u\n", id); | |
0b25ff86 JL |
442 | ret = -EINVAL; |
443 | goto put_child; | |
0d486806 BN |
444 | } |
445 | if (priv->phys[id].phy) { | |
446 | dev_err(dev, "already registered port %u\n", id); | |
0b25ff86 JL |
447 | ret = -EINVAL; |
448 | goto put_child; | |
0d486806 BN |
449 | } |
450 | ||
451 | port = &priv->phys[id]; | |
452 | port->portnum = id; | |
453 | port->phy_priv = priv; | |
c1602a1a | 454 | port->phy = devm_phy_create(dev, child, &phy_ops); |
0d486806 BN |
455 | port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); |
456 | if (IS_ERR(port->phy)) { | |
457 | dev_err(dev, "failed to create PHY\n"); | |
0b25ff86 JL |
458 | ret = PTR_ERR(port->phy); |
459 | goto put_child; | |
0d486806 BN |
460 | } |
461 | ||
462 | phy_set_drvdata(port->phy, port); | |
463 | count++; | |
464 | } | |
465 | ||
466 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | |
467 | if (IS_ERR(provider)) { | |
468 | dev_err(dev, "could not register PHY provider\n"); | |
469 | return PTR_ERR(provider); | |
470 | } | |
471 | ||
472 | dev_info(dev, "registered %d port(s)\n", count); | |
473 | ||
474 | return 0; | |
0b25ff86 JL |
475 | put_child: |
476 | of_node_put(child); | |
477 | return ret; | |
0d486806 BN |
478 | } |
479 | ||
480 | static struct platform_driver brcm_sata_phy_driver = { | |
481 | .probe = brcm_sata_phy_probe, | |
482 | .driver = { | |
483 | .of_match_table = brcm_sata_phy_of_match, | |
037c4189 | 484 | .name = "brcm-sata-phy", |
0d486806 BN |
485 | } |
486 | }; | |
487 | module_platform_driver(brcm_sata_phy_driver); | |
488 | ||
037c4189 | 489 | MODULE_DESCRIPTION("Broadcom SATA PHY driver"); |
0d486806 BN |
490 | MODULE_LICENSE("GPL"); |
491 | MODULE_AUTHOR("Marc Carino"); | |
492 | MODULE_AUTHOR("Brian Norris"); | |
037c4189 | 493 | MODULE_ALIAS("platform:phy-brcm-sata"); |