1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Marvell Berlin SATA PHY driver |
4 | * |
5 | * Copyright (C) 2014 Marvell Technology Group Ltd. |
6 | * |
7 | * Antoine Ténart <antoine.tenart@free-electrons.com> |
8 | */ |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/io.h> |
15 | #include <linux/platform_device.h> |
16 | |
17 | #define HOST_VSA_ADDR 0x0 |
18 | #define HOST_VSA_DATA 0x4 |
19 | #define PORT_SCR_CTL 0x2c |
20 | #define PORT_VSR_ADDR 0x78 |
21 | #define PORT_VSR_DATA 0x7c |
22 | |
23 | #define CONTROL_REGISTER 0x0 |
24 | #define MBUS_SIZE_CONTROL 0x4 |
25 | |
26 | #define POWER_DOWN_PHY0 BIT(6) |
27 | #define POWER_DOWN_PHY1 BIT(14) |
28 | #define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) |
29 | #define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) |
30 | |
31 | #define BG2_PHY_BASE 0x080 |
32 | #define BG2Q_PHY_BASE 0x200 |
33 | |
34 | /* register 0x01 */ |
35 | #define REF_FREF_SEL_25 BIT(0) |
36 | #define PHY_BERLIN_MODE_SATA (0x0 << 5) |
37 | |
38 | /* register 0x02 */ |
39 | #define USE_MAX_PLL_RATE BIT(12) |
40 | |
41 | /* register 0x23 */ |
42 | #define DATA_BIT_WIDTH_10 (0x0 << 10) |
43 | #define DATA_BIT_WIDTH_20 (0x1 << 10) |
44 | #define DATA_BIT_WIDTH_40 (0x2 << 10) |
45 | |
46 | /* register 0x25 */ |
47 | #define PHY_GEN_MAX_1_5 (0x0 << 10) |
48 | #define PHY_GEN_MAX_3_0 (0x1 << 10) |
49 | #define PHY_GEN_MAX_6_0 (0x2 << 10) |
50 | |
51 | struct phy_berlin_desc { |
52 | struct phy *phy; |
53 | u32 power_bit; |
54 | unsigned index; |
55 | }; |
56 | |
57 | struct phy_berlin_priv { |
58 | void __iomem *base; |
59 | spinlock_t lock; |
60 | struct clk *clk; |
61 | struct phy_berlin_desc **phys; |
62 | unsigned nphys; |
63 | u32 phy_base; |
64 | }; |
65 | |
66 | static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, |
67 | u32 phy_base, u32 reg, u32 mask, u32 val) |
68 | { |
69 | u32 regval; |
70 | |
71 | /* select register */ |
72 | writel(val: phy_base + reg, addr: ctrl_reg + PORT_VSR_ADDR); |
73 | |
74 | /* set bits */ |
75 | regval = readl(addr: ctrl_reg + PORT_VSR_DATA); |
76 | regval &= ~mask; |
77 | regval |= val; |
78 | writel(val: regval, addr: ctrl_reg + PORT_VSR_DATA); |
79 | } |
80 | |
81 | static int phy_berlin_sata_power_on(struct phy *phy) |
82 | { |
83 | struct phy_berlin_desc *desc = phy_get_drvdata(phy); |
84 | struct phy_berlin_priv *priv = dev_get_drvdata(dev: phy->dev.parent); |
85 | void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); |
86 | u32 regval; |
87 | |
88 | clk_prepare_enable(clk: priv->clk); |
89 | |
90 | spin_lock(lock: &priv->lock); |
91 | |
92 | /* Power on PHY */ |
93 | writel(CONTROL_REGISTER, addr: priv->base + HOST_VSA_ADDR); |
94 | regval = readl(addr: priv->base + HOST_VSA_DATA); |
95 | regval &= ~desc->power_bit; |
96 | writel(val: regval, addr: priv->base + HOST_VSA_DATA); |
97 | |
98 | /* Configure MBus */ |
99 | writel(MBUS_SIZE_CONTROL, addr: priv->base + HOST_VSA_ADDR); |
100 | regval = readl(addr: priv->base + HOST_VSA_DATA); |
101 | regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; |
102 | writel(val: regval, addr: priv->base + HOST_VSA_DATA); |
103 | |
104 | /* set PHY mode and ref freq to 25 MHz */ |
105 | phy_berlin_sata_reg_setbits(ctrl_reg, phy_base: priv->phy_base, reg: 0x01, |
106 | mask: 0x00ff, |
107 | REF_FREF_SEL_25 | PHY_BERLIN_MODE_SATA); |
108 | |
109 | /* set PHY up to 6 Gbps */ |
110 | phy_berlin_sata_reg_setbits(ctrl_reg, phy_base: priv->phy_base, reg: 0x25, |
111 | mask: 0x0c00, PHY_GEN_MAX_6_0); |
112 | |
113 | /* set 40 bits width */ |
114 | phy_berlin_sata_reg_setbits(ctrl_reg, phy_base: priv->phy_base, reg: 0x23, |
115 | mask: 0x0c00, DATA_BIT_WIDTH_40); |
116 | |
117 | /* use max pll rate */ |
118 | phy_berlin_sata_reg_setbits(ctrl_reg, phy_base: priv->phy_base, reg: 0x02, |
119 | mask: 0x0000, USE_MAX_PLL_RATE); |
120 | |
121 | /* set Gen3 controller speed */ |
122 | regval = readl(addr: ctrl_reg + PORT_SCR_CTL); |
123 | regval &= ~GENMASK(7, 4); |
124 | regval |= 0x30; |
125 | writel(val: regval, addr: ctrl_reg + PORT_SCR_CTL); |
126 | |
127 | spin_unlock(lock: &priv->lock); |
128 | |
129 | clk_disable_unprepare(clk: priv->clk); |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | static int phy_berlin_sata_power_off(struct phy *phy) |
135 | { |
136 | struct phy_berlin_desc *desc = phy_get_drvdata(phy); |
137 | struct phy_berlin_priv *priv = dev_get_drvdata(dev: phy->dev.parent); |
138 | u32 regval; |
139 | |
140 | clk_prepare_enable(clk: priv->clk); |
141 | |
142 | spin_lock(lock: &priv->lock); |
143 | |
144 | /* Power down PHY */ |
145 | writel(CONTROL_REGISTER, addr: priv->base + HOST_VSA_ADDR); |
146 | regval = readl(addr: priv->base + HOST_VSA_DATA); |
147 | regval |= desc->power_bit; |
148 | writel(val: regval, addr: priv->base + HOST_VSA_DATA); |
149 | |
150 | spin_unlock(lock: &priv->lock); |
151 | |
152 | clk_disable_unprepare(clk: priv->clk); |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, |
158 | const struct of_phandle_args *args) |
159 | { |
160 | struct phy_berlin_priv *priv = dev_get_drvdata(dev); |
161 | int i; |
162 | |
163 | if (WARN_ON(args->args[0] >= priv->nphys)) |
164 | return ERR_PTR(error: -ENODEV); |
165 | |
166 | for (i = 0; i < priv->nphys; i++) { |
167 | if (priv->phys[i]->index == args->args[0]) |
168 | break; |
169 | } |
170 | |
171 | if (i == priv->nphys) |
172 | return ERR_PTR(error: -ENODEV); |
173 | |
174 | return priv->phys[i]->phy; |
175 | } |
176 | |
177 | static const struct phy_ops phy_berlin_sata_ops = { |
178 | .power_on = phy_berlin_sata_power_on, |
179 | .power_off = phy_berlin_sata_power_off, |
180 | .owner = THIS_MODULE, |
181 | }; |
182 | |
183 | static u32 phy_berlin_power_down_bits[] = { |
184 | POWER_DOWN_PHY0, |
185 | POWER_DOWN_PHY1, |
186 | }; |
187 | |
188 | static int phy_berlin_sata_probe(struct platform_device *pdev) |
189 | { |
190 | struct device *dev = &pdev->dev; |
191 | struct device_node *child; |
192 | struct phy *phy; |
193 | struct phy_provider *phy_provider; |
194 | struct phy_berlin_priv *priv; |
195 | struct resource *res; |
196 | int ret, i = 0; |
197 | u32 phy_id; |
198 | |
199 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
200 | if (!priv) |
201 | return -ENOMEM; |
202 | |
203 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
204 | if (!res) |
205 | return -EINVAL; |
206 | |
207 | priv->base = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
208 | if (!priv->base) |
209 | return -ENOMEM; |
210 | |
211 | priv->clk = devm_clk_get(dev, NULL); |
212 | if (IS_ERR(ptr: priv->clk)) |
213 | return PTR_ERR(ptr: priv->clk); |
214 | |
215 | priv->nphys = of_get_child_count(np: dev->of_node); |
216 | if (priv->nphys == 0) |
217 | return -ENODEV; |
218 | |
219 | priv->phys = devm_kcalloc(dev, n: priv->nphys, size: sizeof(*priv->phys), |
220 | GFP_KERNEL); |
221 | if (!priv->phys) |
222 | return -ENOMEM; |
223 | |
224 | if (of_device_is_compatible(device: dev->of_node, "marvell,berlin2-sata-phy" )) |
225 | priv->phy_base = BG2_PHY_BASE; |
226 | else |
227 | priv->phy_base = BG2Q_PHY_BASE; |
228 | |
229 | dev_set_drvdata(dev, data: priv); |
230 | spin_lock_init(&priv->lock); |
231 | |
232 | for_each_available_child_of_node(dev->of_node, child) { |
233 | struct phy_berlin_desc *phy_desc; |
234 | |
235 | if (of_property_read_u32(np: child, propname: "reg" , out_value: &phy_id)) { |
236 | dev_err(dev, "missing reg property in node %pOFn\n" , |
237 | child); |
238 | ret = -EINVAL; |
239 | goto put_child; |
240 | } |
241 | |
242 | if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { |
243 | dev_err(dev, "invalid reg in node %pOFn\n" , child); |
244 | ret = -EINVAL; |
245 | goto put_child; |
246 | } |
247 | |
248 | phy_desc = devm_kzalloc(dev, size: sizeof(*phy_desc), GFP_KERNEL); |
249 | if (!phy_desc) { |
250 | ret = -ENOMEM; |
251 | goto put_child; |
252 | } |
253 | |
254 | phy = devm_phy_create(dev, NULL, ops: &phy_berlin_sata_ops); |
255 | if (IS_ERR(ptr: phy)) { |
256 | dev_err(dev, "failed to create PHY %d\n" , phy_id); |
257 | ret = PTR_ERR(ptr: phy); |
258 | goto put_child; |
259 | } |
260 | |
261 | phy_desc->phy = phy; |
262 | phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; |
263 | phy_desc->index = phy_id; |
264 | phy_set_drvdata(phy, data: phy_desc); |
265 | |
266 | priv->phys[i++] = phy_desc; |
267 | |
268 | /* Make sure the PHY is off */ |
269 | phy_berlin_sata_power_off(phy); |
270 | } |
271 | |
272 | phy_provider = |
273 | devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); |
274 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
275 | put_child: |
276 | of_node_put(node: child); |
277 | return ret; |
278 | } |
279 | |
280 | static const struct of_device_id phy_berlin_sata_of_match[] = { |
281 | { .compatible = "marvell,berlin2-sata-phy" }, |
282 | { .compatible = "marvell,berlin2q-sata-phy" }, |
283 | { }, |
284 | }; |
285 | MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); |
286 | |
287 | static struct platform_driver phy_berlin_sata_driver = { |
288 | .probe = phy_berlin_sata_probe, |
289 | .driver = { |
290 | .name = "phy-berlin-sata" , |
291 | .of_match_table = phy_berlin_sata_of_match, |
292 | }, |
293 | }; |
294 | module_platform_driver(phy_berlin_sata_driver); |
295 | |
296 | MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver" ); |
297 | MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>" ); |
298 | MODULE_LICENSE("GPL v2" ); |
299 | |