1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * COMBPHY driver for HiSilicon STB SoCs |
4 | * |
5 | * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com |
6 | * |
7 | * Authors: Jianguo Sun <sunjianguo1@huawei.com> |
8 | */ |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/io.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 | #include <linux/reset.h> |
21 | #include <dt-bindings/phy/phy.h> |
22 | |
23 | #define COMBPHY_MODE_PCIE 0 |
24 | #define COMBPHY_MODE_USB3 1 |
25 | #define COMBPHY_MODE_SATA 2 |
26 | |
27 | #define COMBPHY_CFG_REG 0x0 |
28 | #define COMBPHY_BYPASS_CODEC BIT(31) |
29 | #define COMBPHY_TEST_WRITE BIT(24) |
30 | #define COMBPHY_TEST_DATA_SHIFT 20 |
31 | #define COMBPHY_TEST_DATA_MASK GENMASK(23, 20) |
32 | #define COMBPHY_TEST_ADDR_SHIFT 12 |
33 | #define COMBPHY_TEST_ADDR_MASK GENMASK(16, 12) |
34 | #define COMBPHY_CLKREF_OUT_OEN BIT(0) |
35 | |
36 | struct histb_combphy_mode { |
37 | int fixed; |
38 | int select; |
39 | u32 reg; |
40 | u32 shift; |
41 | u32 mask; |
42 | }; |
43 | |
44 | struct histb_combphy_priv { |
45 | void __iomem *mmio; |
46 | struct regmap *syscon; |
47 | struct reset_control *por_rst; |
48 | struct clk *ref_clk; |
49 | struct phy *phy; |
50 | struct histb_combphy_mode mode; |
51 | }; |
52 | |
53 | static void nano_register_write(struct histb_combphy_priv *priv, |
54 | u32 addr, u32 data) |
55 | { |
56 | void __iomem *reg = priv->mmio + COMBPHY_CFG_REG; |
57 | u32 val; |
58 | |
59 | /* Set up address and data for the write */ |
60 | val = readl(addr: reg); |
61 | val &= ~COMBPHY_TEST_ADDR_MASK; |
62 | val |= addr << COMBPHY_TEST_ADDR_SHIFT; |
63 | val &= ~COMBPHY_TEST_DATA_MASK; |
64 | val |= data << COMBPHY_TEST_DATA_SHIFT; |
65 | writel(val, addr: reg); |
66 | |
67 | /* Flip strobe control to trigger the write */ |
68 | val &= ~COMBPHY_TEST_WRITE; |
69 | writel(val, addr: reg); |
70 | val |= COMBPHY_TEST_WRITE; |
71 | writel(val, addr: reg); |
72 | } |
73 | |
74 | static int is_mode_fixed(struct histb_combphy_mode *mode) |
75 | { |
76 | return (mode->fixed != PHY_NONE) ? true : false; |
77 | } |
78 | |
79 | static int histb_combphy_set_mode(struct histb_combphy_priv *priv) |
80 | { |
81 | struct histb_combphy_mode *mode = &priv->mode; |
82 | struct regmap *syscon = priv->syscon; |
83 | u32 hw_sel; |
84 | |
85 | if (is_mode_fixed(mode)) |
86 | return 0; |
87 | |
88 | switch (mode->select) { |
89 | case PHY_TYPE_SATA: |
90 | hw_sel = COMBPHY_MODE_SATA; |
91 | break; |
92 | case PHY_TYPE_PCIE: |
93 | hw_sel = COMBPHY_MODE_PCIE; |
94 | break; |
95 | case PHY_TYPE_USB3: |
96 | hw_sel = COMBPHY_MODE_USB3; |
97 | break; |
98 | default: |
99 | return -EINVAL; |
100 | } |
101 | |
102 | return regmap_update_bits(map: syscon, reg: mode->reg, mask: mode->mask, |
103 | val: hw_sel << mode->shift); |
104 | } |
105 | |
106 | static int histb_combphy_init(struct phy *phy) |
107 | { |
108 | struct histb_combphy_priv *priv = phy_get_drvdata(phy); |
109 | u32 val; |
110 | int ret; |
111 | |
112 | ret = histb_combphy_set_mode(priv); |
113 | if (ret) |
114 | return ret; |
115 | |
116 | /* Clear bypass bit to enable encoding/decoding */ |
117 | val = readl(addr: priv->mmio + COMBPHY_CFG_REG); |
118 | val &= ~COMBPHY_BYPASS_CODEC; |
119 | writel(val, addr: priv->mmio + COMBPHY_CFG_REG); |
120 | |
121 | ret = clk_prepare_enable(clk: priv->ref_clk); |
122 | if (ret) |
123 | return ret; |
124 | |
125 | reset_control_deassert(rstc: priv->por_rst); |
126 | |
127 | /* Enable EP clock */ |
128 | val = readl(addr: priv->mmio + COMBPHY_CFG_REG); |
129 | val |= COMBPHY_CLKREF_OUT_OEN; |
130 | writel(val, addr: priv->mmio + COMBPHY_CFG_REG); |
131 | |
132 | /* Need to wait for EP clock stable */ |
133 | mdelay(5); |
134 | |
135 | /* Configure nano phy registers as suggested by vendor */ |
136 | nano_register_write(priv, addr: 0x1, data: 0x8); |
137 | nano_register_write(priv, addr: 0xc, data: 0x9); |
138 | nano_register_write(priv, addr: 0x1a, data: 0x4); |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static int histb_combphy_exit(struct phy *phy) |
144 | { |
145 | struct histb_combphy_priv *priv = phy_get_drvdata(phy); |
146 | u32 val; |
147 | |
148 | /* Disable EP clock */ |
149 | val = readl(addr: priv->mmio + COMBPHY_CFG_REG); |
150 | val &= ~COMBPHY_CLKREF_OUT_OEN; |
151 | writel(val, addr: priv->mmio + COMBPHY_CFG_REG); |
152 | |
153 | reset_control_assert(rstc: priv->por_rst); |
154 | clk_disable_unprepare(clk: priv->ref_clk); |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static const struct phy_ops histb_combphy_ops = { |
160 | .init = histb_combphy_init, |
161 | .exit = histb_combphy_exit, |
162 | .owner = THIS_MODULE, |
163 | }; |
164 | |
165 | static struct phy *histb_combphy_xlate(struct device *dev, |
166 | const struct of_phandle_args *args) |
167 | { |
168 | struct histb_combphy_priv *priv = dev_get_drvdata(dev); |
169 | struct histb_combphy_mode *mode = &priv->mode; |
170 | |
171 | if (args->args_count < 1) { |
172 | dev_err(dev, "invalid number of arguments\n" ); |
173 | return ERR_PTR(error: -EINVAL); |
174 | } |
175 | |
176 | mode->select = args->args[0]; |
177 | |
178 | if (mode->select < PHY_TYPE_SATA || mode->select > PHY_TYPE_USB3) { |
179 | dev_err(dev, "invalid phy mode select argument\n" ); |
180 | return ERR_PTR(error: -EINVAL); |
181 | } |
182 | |
183 | if (is_mode_fixed(mode) && mode->select != mode->fixed) { |
184 | dev_err(dev, "mode select %d mismatch fixed phy mode %d\n" , |
185 | mode->select, mode->fixed); |
186 | return ERR_PTR(error: -EINVAL); |
187 | } |
188 | |
189 | return priv->phy; |
190 | } |
191 | |
192 | static int histb_combphy_probe(struct platform_device *pdev) |
193 | { |
194 | struct phy_provider *phy_provider; |
195 | struct device *dev = &pdev->dev; |
196 | struct histb_combphy_priv *priv; |
197 | struct device_node *np = dev->of_node; |
198 | struct histb_combphy_mode *mode; |
199 | u32 vals[3]; |
200 | int ret; |
201 | |
202 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
203 | if (!priv) |
204 | return -ENOMEM; |
205 | |
206 | priv->mmio = devm_platform_ioremap_resource(pdev, index: 0); |
207 | if (IS_ERR(ptr: priv->mmio)) { |
208 | ret = PTR_ERR(ptr: priv->mmio); |
209 | return ret; |
210 | } |
211 | |
212 | priv->syscon = syscon_node_to_regmap(np: np->parent); |
213 | if (IS_ERR(ptr: priv->syscon)) { |
214 | dev_err(dev, "failed to find peri_ctrl syscon regmap\n" ); |
215 | return PTR_ERR(ptr: priv->syscon); |
216 | } |
217 | |
218 | mode = &priv->mode; |
219 | mode->fixed = PHY_NONE; |
220 | |
221 | ret = of_property_read_u32(np, propname: "hisilicon,fixed-mode" , out_value: &mode->fixed); |
222 | if (ret == 0) |
223 | dev_dbg(dev, "found fixed phy mode %d\n" , mode->fixed); |
224 | |
225 | ret = of_property_read_u32_array(np, propname: "hisilicon,mode-select-bits" , |
226 | out_values: vals, ARRAY_SIZE(vals)); |
227 | if (ret == 0) { |
228 | if (is_mode_fixed(mode)) { |
229 | dev_err(dev, "found select bits for fixed mode phy\n" ); |
230 | return -EINVAL; |
231 | } |
232 | |
233 | mode->reg = vals[0]; |
234 | mode->shift = vals[1]; |
235 | mode->mask = vals[2]; |
236 | dev_dbg(dev, "found mode select bits\n" ); |
237 | } else { |
238 | if (!is_mode_fixed(mode)) { |
239 | dev_err(dev, "no valid select bits found for non-fixed phy\n" ); |
240 | return -ENODEV; |
241 | } |
242 | } |
243 | |
244 | priv->ref_clk = devm_clk_get(dev, NULL); |
245 | if (IS_ERR(ptr: priv->ref_clk)) { |
246 | dev_err(dev, "failed to find ref clock\n" ); |
247 | return PTR_ERR(ptr: priv->ref_clk); |
248 | } |
249 | |
250 | priv->por_rst = devm_reset_control_get(dev, NULL); |
251 | if (IS_ERR(ptr: priv->por_rst)) { |
252 | dev_err(dev, "failed to get poweron reset\n" ); |
253 | return PTR_ERR(ptr: priv->por_rst); |
254 | } |
255 | |
256 | priv->phy = devm_phy_create(dev, NULL, ops: &histb_combphy_ops); |
257 | if (IS_ERR(ptr: priv->phy)) { |
258 | dev_err(dev, "failed to create combphy\n" ); |
259 | return PTR_ERR(ptr: priv->phy); |
260 | } |
261 | |
262 | dev_set_drvdata(dev, data: priv); |
263 | phy_set_drvdata(phy: priv->phy, data: priv); |
264 | |
265 | phy_provider = devm_of_phy_provider_register(dev, histb_combphy_xlate); |
266 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
267 | } |
268 | |
269 | static const struct of_device_id histb_combphy_of_match[] = { |
270 | { .compatible = "hisilicon,hi3798cv200-combphy" }, |
271 | { }, |
272 | }; |
273 | MODULE_DEVICE_TABLE(of, histb_combphy_of_match); |
274 | |
275 | static struct platform_driver histb_combphy_driver = { |
276 | .probe = histb_combphy_probe, |
277 | .driver = { |
278 | .name = "combphy" , |
279 | .of_match_table = histb_combphy_of_match, |
280 | }, |
281 | }; |
282 | module_platform_driver(histb_combphy_driver); |
283 | |
284 | MODULE_DESCRIPTION("HiSilicon STB COMBPHY driver" ); |
285 | MODULE_LICENSE("GPL v2" ); |
286 | |