.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Meson8, Meson8b and GXBB USB2 PHY driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com> |
---|
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 version 2 as |
---|
8 | | - * published by the Free Software Foundation. |
---|
9 | | - * |
---|
10 | | - * You should have received a copy of the GNU General Public License |
---|
11 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
12 | 6 | */ |
---|
13 | 7 | |
---|
14 | 8 | #include <linux/clk.h> |
---|
.. | .. |
---|
16 | 10 | #include <linux/io.h> |
---|
17 | 11 | #include <linux/module.h> |
---|
18 | 12 | #include <linux/of_device.h> |
---|
| 13 | +#include <linux/property.h> |
---|
| 14 | +#include <linux/regmap.h> |
---|
19 | 15 | #include <linux/reset.h> |
---|
20 | 16 | #include <linux/phy/phy.h> |
---|
21 | 17 | #include <linux/platform_device.h> |
---|
.. | .. |
---|
82 | 78 | #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) |
---|
83 | 79 | |
---|
84 | 80 | #define REG_DBG_UART 0x10 |
---|
| 81 | + #define REG_DBG_UART_BYPASS_SEL BIT(0) |
---|
| 82 | + #define REG_DBG_UART_BYPASS_DM_EN BIT(1) |
---|
| 83 | + #define REG_DBG_UART_BYPASS_DP_EN BIT(2) |
---|
| 84 | + #define REG_DBG_UART_BYPASS_DM_DATA BIT(3) |
---|
| 85 | + #define REG_DBG_UART_BYPASS_DP_DATA BIT(4) |
---|
| 86 | + #define REG_DBG_UART_FSV_MINUS BIT(5) |
---|
| 87 | + #define REG_DBG_UART_FSV_PLUS BIT(6) |
---|
| 88 | + #define REG_DBG_UART_FSV_BURN_IN_TEST BIT(7) |
---|
| 89 | + #define REG_DBG_UART_LOOPBACK_EN_B BIT(8) |
---|
| 90 | + #define REG_DBG_UART_SET_IDDQ BIT(9) |
---|
| 91 | + #define REG_DBG_UART_ATE_RESET BIT(10) |
---|
85 | 92 | |
---|
86 | 93 | #define REG_TEST 0x14 |
---|
87 | 94 | #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) |
---|
.. | .. |
---|
110 | 117 | #define RESET_COMPLETE_TIME 500 |
---|
111 | 118 | #define ACA_ENABLE_COMPLETE_TIME 50 |
---|
112 | 119 | |
---|
113 | | -struct phy_meson8b_usb2_priv { |
---|
114 | | - void __iomem *regs; |
---|
115 | | - enum usb_dr_mode dr_mode; |
---|
116 | | - struct clk *clk_usb_general; |
---|
117 | | - struct clk *clk_usb; |
---|
118 | | - struct reset_control *reset; |
---|
| 120 | +struct phy_meson8b_usb2_match_data { |
---|
| 121 | + bool host_enable_aca; |
---|
119 | 122 | }; |
---|
120 | 123 | |
---|
121 | | -static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, |
---|
122 | | - u32 reg) |
---|
123 | | -{ |
---|
124 | | - return readl(phy_priv->regs + reg); |
---|
125 | | -} |
---|
| 124 | +struct phy_meson8b_usb2_priv { |
---|
| 125 | + struct regmap *regmap; |
---|
| 126 | + enum usb_dr_mode dr_mode; |
---|
| 127 | + struct clk *clk_usb_general; |
---|
| 128 | + struct clk *clk_usb; |
---|
| 129 | + struct reset_control *reset; |
---|
| 130 | + const struct phy_meson8b_usb2_match_data *match; |
---|
| 131 | +}; |
---|
126 | 132 | |
---|
127 | | -static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, |
---|
128 | | - u32 reg, u32 mask, u32 value) |
---|
129 | | -{ |
---|
130 | | - u32 data; |
---|
131 | | - |
---|
132 | | - data = phy_meson8b_usb2_read(phy_priv, reg); |
---|
133 | | - data &= ~mask; |
---|
134 | | - data |= (value & mask); |
---|
135 | | - |
---|
136 | | - writel(data, phy_priv->regs + reg); |
---|
137 | | -} |
---|
| 133 | +static const struct regmap_config phy_meson8b_usb2_regmap_conf = { |
---|
| 134 | + .reg_bits = 8, |
---|
| 135 | + .val_bits = 32, |
---|
| 136 | + .reg_stride = 4, |
---|
| 137 | + .max_register = REG_TUNE, |
---|
| 138 | +}; |
---|
138 | 139 | |
---|
139 | 140 | static int phy_meson8b_usb2_power_on(struct phy *phy) |
---|
140 | 141 | { |
---|
141 | 142 | struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); |
---|
| 143 | + u32 reg; |
---|
142 | 144 | int ret; |
---|
143 | 145 | |
---|
144 | 146 | if (!IS_ERR_OR_NULL(priv->reset)) { |
---|
.. | .. |
---|
162 | 164 | return ret; |
---|
163 | 165 | } |
---|
164 | 166 | |
---|
165 | | - phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, |
---|
166 | | - REG_CONFIG_CLK_32k_ALTSEL); |
---|
| 167 | + regmap_update_bits(priv->regmap, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, |
---|
| 168 | + REG_CONFIG_CLK_32k_ALTSEL); |
---|
167 | 169 | |
---|
168 | | - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, |
---|
169 | | - 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); |
---|
| 170 | + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, |
---|
| 171 | + 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); |
---|
170 | 172 | |
---|
171 | | - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, |
---|
172 | | - 0x5 << REG_CTRL_FSEL_SHIFT); |
---|
| 173 | + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_FSEL_MASK, |
---|
| 174 | + 0x5 << REG_CTRL_FSEL_SHIFT); |
---|
173 | 175 | |
---|
174 | 176 | /* reset the PHY */ |
---|
175 | | - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, |
---|
176 | | - REG_CTRL_POWER_ON_RESET); |
---|
| 177 | + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, |
---|
| 178 | + REG_CTRL_POWER_ON_RESET); |
---|
177 | 179 | udelay(RESET_COMPLETE_TIME); |
---|
178 | | - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); |
---|
| 180 | + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); |
---|
179 | 181 | udelay(RESET_COMPLETE_TIME); |
---|
180 | 182 | |
---|
181 | | - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, |
---|
182 | | - REG_CTRL_SOF_TOGGLE_OUT); |
---|
| 183 | + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, |
---|
| 184 | + REG_CTRL_SOF_TOGGLE_OUT); |
---|
183 | 185 | |
---|
184 | 186 | if (priv->dr_mode == USB_DR_MODE_HOST) { |
---|
185 | | - phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, |
---|
| 187 | + regmap_update_bits(priv->regmap, REG_DBG_UART, |
---|
| 188 | + REG_DBG_UART_SET_IDDQ, 0); |
---|
| 189 | + |
---|
| 190 | + if (priv->match->host_enable_aca) { |
---|
| 191 | + regmap_update_bits(priv->regmap, REG_ADP_BC, |
---|
186 | 192 | REG_ADP_BC_ACA_ENABLE, |
---|
187 | 193 | REG_ADP_BC_ACA_ENABLE); |
---|
188 | 194 | |
---|
189 | | - udelay(ACA_ENABLE_COMPLETE_TIME); |
---|
| 195 | + udelay(ACA_ENABLE_COMPLETE_TIME); |
---|
190 | 196 | |
---|
191 | | - if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & |
---|
192 | | - REG_ADP_BC_ACA_PIN_FLOAT) { |
---|
193 | | - dev_warn(&phy->dev, "USB ID detect failed!\n"); |
---|
194 | | - clk_disable_unprepare(priv->clk_usb); |
---|
195 | | - clk_disable_unprepare(priv->clk_usb_general); |
---|
196 | | - return -EINVAL; |
---|
| 197 | + regmap_read(priv->regmap, REG_ADP_BC, ®); |
---|
| 198 | + if (reg & REG_ADP_BC_ACA_PIN_FLOAT) { |
---|
| 199 | + dev_warn(&phy->dev, "USB ID detect failed!\n"); |
---|
| 200 | + clk_disable_unprepare(priv->clk_usb); |
---|
| 201 | + clk_disable_unprepare(priv->clk_usb_general); |
---|
| 202 | + return -EINVAL; |
---|
| 203 | + } |
---|
197 | 204 | } |
---|
198 | 205 | } |
---|
199 | 206 | |
---|
.. | .. |
---|
203 | 210 | static int phy_meson8b_usb2_power_off(struct phy *phy) |
---|
204 | 211 | { |
---|
205 | 212 | struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); |
---|
| 213 | + |
---|
| 214 | + if (priv->dr_mode == USB_DR_MODE_HOST) |
---|
| 215 | + regmap_update_bits(priv->regmap, REG_DBG_UART, |
---|
| 216 | + REG_DBG_UART_SET_IDDQ, |
---|
| 217 | + REG_DBG_UART_SET_IDDQ); |
---|
206 | 218 | |
---|
207 | 219 | clk_disable_unprepare(priv->clk_usb); |
---|
208 | 220 | clk_disable_unprepare(priv->clk_usb_general); |
---|
.. | .. |
---|
219 | 231 | static int phy_meson8b_usb2_probe(struct platform_device *pdev) |
---|
220 | 232 | { |
---|
221 | 233 | struct phy_meson8b_usb2_priv *priv; |
---|
222 | | - struct resource *res; |
---|
223 | 234 | struct phy *phy; |
---|
224 | 235 | struct phy_provider *phy_provider; |
---|
| 236 | + void __iomem *base; |
---|
225 | 237 | |
---|
226 | 238 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
---|
227 | 239 | if (!priv) |
---|
228 | 240 | return -ENOMEM; |
---|
229 | 241 | |
---|
230 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
231 | | - priv->regs = devm_ioremap_resource(&pdev->dev, res); |
---|
232 | | - if (IS_ERR(priv->regs)) |
---|
233 | | - return PTR_ERR(priv->regs); |
---|
| 242 | + base = devm_platform_ioremap_resource(pdev, 0); |
---|
| 243 | + if (IS_ERR(base)) |
---|
| 244 | + return PTR_ERR(base); |
---|
| 245 | + |
---|
| 246 | + priv->match = device_get_match_data(&pdev->dev); |
---|
| 247 | + if (!priv->match) |
---|
| 248 | + return -ENODEV; |
---|
| 249 | + |
---|
| 250 | + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
---|
| 251 | + &phy_meson8b_usb2_regmap_conf); |
---|
| 252 | + if (IS_ERR(priv->regmap)) |
---|
| 253 | + return PTR_ERR(priv->regmap); |
---|
234 | 254 | |
---|
235 | 255 | priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); |
---|
236 | 256 | if (IS_ERR(priv->clk_usb_general)) |
---|
.. | .. |
---|
241 | 261 | return PTR_ERR(priv->clk_usb); |
---|
242 | 262 | |
---|
243 | 263 | priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); |
---|
244 | | - if (PTR_ERR(priv->reset) == -EPROBE_DEFER) |
---|
245 | | - return PTR_ERR(priv->reset); |
---|
| 264 | + if (IS_ERR(priv->reset)) |
---|
| 265 | + return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset), |
---|
| 266 | + "Failed to get the reset line"); |
---|
246 | 267 | |
---|
247 | 268 | priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); |
---|
248 | 269 | if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { |
---|
.. | .. |
---|
265 | 286 | return PTR_ERR_OR_ZERO(phy_provider); |
---|
266 | 287 | } |
---|
267 | 288 | |
---|
| 289 | +static const struct phy_meson8b_usb2_match_data phy_meson8_usb2_match_data = { |
---|
| 290 | + .host_enable_aca = false, |
---|
| 291 | +}; |
---|
| 292 | + |
---|
| 293 | +static const struct phy_meson8b_usb2_match_data phy_meson8b_usb2_match_data = { |
---|
| 294 | + .host_enable_aca = true, |
---|
| 295 | +}; |
---|
| 296 | + |
---|
268 | 297 | static const struct of_device_id phy_meson8b_usb2_of_match[] = { |
---|
269 | | - { .compatible = "amlogic,meson8-usb2-phy", }, |
---|
270 | | - { .compatible = "amlogic,meson8b-usb2-phy", }, |
---|
271 | | - { .compatible = "amlogic,meson-gxbb-usb2-phy", }, |
---|
272 | | - { }, |
---|
| 298 | + { |
---|
| 299 | + .compatible = "amlogic,meson8-usb2-phy", |
---|
| 300 | + .data = &phy_meson8_usb2_match_data |
---|
| 301 | + }, |
---|
| 302 | + { |
---|
| 303 | + .compatible = "amlogic,meson8b-usb2-phy", |
---|
| 304 | + .data = &phy_meson8b_usb2_match_data |
---|
| 305 | + }, |
---|
| 306 | + { |
---|
| 307 | + .compatible = "amlogic,meson8m2-usb2-phy", |
---|
| 308 | + .data = &phy_meson8b_usb2_match_data |
---|
| 309 | + }, |
---|
| 310 | + { |
---|
| 311 | + .compatible = "amlogic,meson-gxbb-usb2-phy", |
---|
| 312 | + .data = &phy_meson8b_usb2_match_data |
---|
| 313 | + }, |
---|
| 314 | + { /* sentinel */ } |
---|
273 | 315 | }; |
---|
274 | 316 | MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); |
---|
275 | 317 | |
---|
.. | .. |
---|
283 | 325 | module_platform_driver(phy_meson8b_usb2_driver); |
---|
284 | 326 | |
---|
285 | 327 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); |
---|
286 | | -MODULE_DESCRIPTION("Meson8, Meson8b and GXBB USB2 PHY driver"); |
---|
| 328 | +MODULE_DESCRIPTION("Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver"); |
---|
287 | 329 | MODULE_LICENSE("GPL"); |
---|