1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ST spear1340-miphy driver |
4 | * |
5 | * Copyright (C) 2014 ST Microelectronics |
6 | * Pratyush Anand <pratyush.anand@gmail.com> |
7 | * Mohit Kumar <mohit.kumar.dhaka@gmail.com> |
8 | */ |
9 | |
10 | #include <linux/bitops.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/dma-mapping.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/phy/phy.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/regmap.h> |
20 | |
21 | /* SPEAr1340 Registers */ |
22 | /* Power Management Registers */ |
23 | #define SPEAR1340_PCM_CFG 0x100 |
24 | #define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11) |
25 | #define SPEAR1340_PCM_WKUP_CFG 0x104 |
26 | #define SPEAR1340_SWITCH_CTR 0x108 |
27 | |
28 | #define SPEAR1340_PERIP1_SW_RST 0x318 |
29 | #define SPEAR1340_PERIP1_SW_RSATA BIT(12) |
30 | #define SPEAR1340_PERIP2_SW_RST 0x31C |
31 | #define SPEAR1340_PERIP3_SW_RST 0x320 |
32 | |
33 | /* PCIE - SATA configuration registers */ |
34 | #define SPEAR1340_PCIE_SATA_CFG 0x424 |
35 | /* PCIE CFG MASks */ |
36 | #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11) |
37 | #define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10) |
38 | #define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9) |
39 | #define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8) |
40 | #define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4) |
41 | #define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3) |
42 | #define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2) |
43 | #define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1) |
44 | #define SPEAR1340_PCIE_SATA_SEL_PCIE (0) |
45 | #define SPEAR1340_PCIE_SATA_SEL_SATA (1) |
46 | #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F |
47 | #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \ |
48 | SPEAR1340_PCIE_CFG_AUX_CLK_EN | \ |
49 | SPEAR1340_PCIE_CFG_CORE_CLK_EN | \ |
50 | SPEAR1340_PCIE_CFG_POWERUP_RESET | \ |
51 | SPEAR1340_PCIE_CFG_DEVICE_PRESENT) |
52 | #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \ |
53 | SPEAR1340_SATA_CFG_PM_CLK_EN | \ |
54 | SPEAR1340_SATA_CFG_POWERUP_RESET | \ |
55 | SPEAR1340_SATA_CFG_RX_CLK_EN | \ |
56 | SPEAR1340_SATA_CFG_TX_CLK_EN) |
57 | |
58 | #define SPEAR1340_PCIE_MIPHY_CFG 0x428 |
59 | #define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31) |
60 | #define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27) |
61 | #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27) |
62 | #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27) |
63 | #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0) |
64 | #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF |
65 | #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \ |
66 | (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ |
67 | SPEAR1340_MIPHY_CLK_REF_DIV2 | \ |
68 | SPEAR1340_MIPHY_PLL_RATIO_TOP(60)) |
69 | #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ |
70 | (SPEAR1340_MIPHY_PLL_RATIO_TOP(120)) |
71 | #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \ |
72 | (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ |
73 | SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) |
74 | |
75 | enum spear1340_miphy_mode { |
76 | SATA, |
77 | PCIE, |
78 | }; |
79 | |
80 | struct spear1340_miphy_priv { |
81 | /* phy mode: 0 for SATA 1 for PCIe */ |
82 | enum spear1340_miphy_mode mode; |
83 | /* regmap for any soc specific misc registers */ |
84 | struct regmap *misc; |
85 | /* phy struct pointer */ |
86 | struct phy *phy; |
87 | }; |
88 | |
89 | static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv) |
90 | { |
91 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_SATA_CFG, |
92 | SPEAR1340_PCIE_SATA_CFG_MASK, |
93 | SPEAR1340_SATA_CFG_VAL); |
94 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_MIPHY_CFG, |
95 | SPEAR1340_PCIE_MIPHY_CFG_MASK, |
96 | SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK); |
97 | /* Switch on sata power domain */ |
98 | regmap_update_bits(map: priv->misc, SPEAR1340_PCM_CFG, |
99 | SPEAR1340_PCM_CFG_SATA_POWER_EN, |
100 | SPEAR1340_PCM_CFG_SATA_POWER_EN); |
101 | /* Wait for SATA power domain on */ |
102 | msleep(msecs: 20); |
103 | |
104 | /* Disable PCIE SATA Controller reset */ |
105 | regmap_update_bits(map: priv->misc, SPEAR1340_PERIP1_SW_RST, |
106 | SPEAR1340_PERIP1_SW_RSATA, val: 0); |
107 | /* Wait for SATA reset de-assert completion */ |
108 | msleep(msecs: 20); |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv) |
114 | { |
115 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_SATA_CFG, |
116 | SPEAR1340_PCIE_SATA_CFG_MASK, val: 0); |
117 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_MIPHY_CFG, |
118 | SPEAR1340_PCIE_MIPHY_CFG_MASK, val: 0); |
119 | |
120 | /* Enable PCIE SATA Controller reset */ |
121 | regmap_update_bits(map: priv->misc, SPEAR1340_PERIP1_SW_RST, |
122 | SPEAR1340_PERIP1_SW_RSATA, |
123 | SPEAR1340_PERIP1_SW_RSATA); |
124 | /* Wait for SATA power domain off */ |
125 | msleep(msecs: 20); |
126 | /* Switch off sata power domain */ |
127 | regmap_update_bits(map: priv->misc, SPEAR1340_PCM_CFG, |
128 | SPEAR1340_PCM_CFG_SATA_POWER_EN, val: 0); |
129 | /* Wait for SATA reset assert completion */ |
130 | msleep(msecs: 20); |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv) |
136 | { |
137 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_MIPHY_CFG, |
138 | SPEAR1340_PCIE_MIPHY_CFG_MASK, |
139 | SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE); |
140 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_SATA_CFG, |
141 | SPEAR1340_PCIE_SATA_CFG_MASK, |
142 | SPEAR1340_PCIE_CFG_VAL); |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv) |
148 | { |
149 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_MIPHY_CFG, |
150 | SPEAR1340_PCIE_MIPHY_CFG_MASK, val: 0); |
151 | regmap_update_bits(map: priv->misc, SPEAR1340_PCIE_SATA_CFG, |
152 | SPEAR1340_PCIE_SATA_CFG_MASK, val: 0); |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static int spear1340_miphy_init(struct phy *phy) |
158 | { |
159 | struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); |
160 | int ret = 0; |
161 | |
162 | if (priv->mode == SATA) |
163 | ret = spear1340_miphy_sata_init(priv); |
164 | else if (priv->mode == PCIE) |
165 | ret = spear1340_miphy_pcie_init(priv); |
166 | |
167 | return ret; |
168 | } |
169 | |
170 | static int spear1340_miphy_exit(struct phy *phy) |
171 | { |
172 | struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); |
173 | int ret = 0; |
174 | |
175 | if (priv->mode == SATA) |
176 | ret = spear1340_miphy_sata_exit(priv); |
177 | else if (priv->mode == PCIE) |
178 | ret = spear1340_miphy_pcie_exit(priv); |
179 | |
180 | return ret; |
181 | } |
182 | |
183 | static const struct of_device_id spear1340_miphy_of_match[] = { |
184 | { .compatible = "st,spear1340-miphy" }, |
185 | { }, |
186 | }; |
187 | MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); |
188 | |
189 | static const struct phy_ops spear1340_miphy_ops = { |
190 | .init = spear1340_miphy_init, |
191 | .exit = spear1340_miphy_exit, |
192 | .owner = THIS_MODULE, |
193 | }; |
194 | |
195 | #ifdef CONFIG_PM_SLEEP |
196 | static int spear1340_miphy_suspend(struct device *dev) |
197 | { |
198 | struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); |
199 | int ret = 0; |
200 | |
201 | if (priv->mode == SATA) |
202 | ret = spear1340_miphy_sata_exit(priv); |
203 | |
204 | return ret; |
205 | } |
206 | |
207 | static int spear1340_miphy_resume(struct device *dev) |
208 | { |
209 | struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); |
210 | int ret = 0; |
211 | |
212 | if (priv->mode == SATA) |
213 | ret = spear1340_miphy_sata_init(priv); |
214 | |
215 | return ret; |
216 | } |
217 | #endif |
218 | |
219 | static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend, |
220 | spear1340_miphy_resume); |
221 | |
222 | static struct phy *spear1340_miphy_xlate(struct device *dev, |
223 | const struct of_phandle_args *args) |
224 | { |
225 | struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); |
226 | |
227 | if (args->args_count < 1) { |
228 | dev_err(dev, "DT did not pass correct no of args\n" ); |
229 | return ERR_PTR(error: -ENODEV); |
230 | } |
231 | |
232 | priv->mode = args->args[0]; |
233 | |
234 | if (priv->mode != SATA && priv->mode != PCIE) { |
235 | dev_err(dev, "DT did not pass correct phy mode\n" ); |
236 | return ERR_PTR(error: -ENODEV); |
237 | } |
238 | |
239 | return priv->phy; |
240 | } |
241 | |
242 | static int spear1340_miphy_probe(struct platform_device *pdev) |
243 | { |
244 | struct device *dev = &pdev->dev; |
245 | struct spear1340_miphy_priv *priv; |
246 | struct phy_provider *phy_provider; |
247 | |
248 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
249 | if (!priv) |
250 | return -ENOMEM; |
251 | |
252 | priv->misc = |
253 | syscon_regmap_lookup_by_phandle(np: dev->of_node, property: "misc" ); |
254 | if (IS_ERR(ptr: priv->misc)) { |
255 | dev_err(dev, "failed to find misc regmap\n" ); |
256 | return PTR_ERR(ptr: priv->misc); |
257 | } |
258 | |
259 | priv->phy = devm_phy_create(dev, NULL, ops: &spear1340_miphy_ops); |
260 | if (IS_ERR(ptr: priv->phy)) { |
261 | dev_err(dev, "failed to create SATA PCIe PHY\n" ); |
262 | return PTR_ERR(ptr: priv->phy); |
263 | } |
264 | |
265 | dev_set_drvdata(dev, data: priv); |
266 | phy_set_drvdata(phy: priv->phy, data: priv); |
267 | |
268 | phy_provider = |
269 | devm_of_phy_provider_register(dev, spear1340_miphy_xlate); |
270 | if (IS_ERR(ptr: phy_provider)) { |
271 | dev_err(dev, "failed to register phy provider\n" ); |
272 | return PTR_ERR(ptr: phy_provider); |
273 | } |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | static struct platform_driver spear1340_miphy_driver = { |
279 | .probe = spear1340_miphy_probe, |
280 | .driver = { |
281 | .name = "spear1340-miphy" , |
282 | .pm = &spear1340_miphy_pm_ops, |
283 | .of_match_table = spear1340_miphy_of_match, |
284 | }, |
285 | }; |
286 | |
287 | module_platform_driver(spear1340_miphy_driver); |
288 | |
289 | MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver" ); |
290 | MODULE_AUTHOR("Pratyush Anand <pratyush.anand@gmail.com>" ); |
291 | MODULE_LICENSE("GPL v2" ); |
292 | |