1 // SPDX-License-Identifier: GPL-2.0-only
3 * Lantiq XWAY SoC RCU module based USB 1.1/2.0 PHY driver
5 * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
6 * Copyright (C) 2017 Hauke Mehrtens <hauke@hauke-m.de>
10 #include <linux/delay.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/module.h>
14 #include <linux/of_address.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/property.h>
18 #include <linux/regmap.h>
19 #include <linux/reset.h>
21 /* Transmitter HS Pre-Emphasis Enable */
22 #define RCU_CFG1_TX_PEE BIT(0)
23 /* Disconnect Threshold */
24 #define RCU_CFG1_DIS_THR_MASK 0x00038000
25 #define RCU_CFG1_DIS_THR_SHIFT 15
27 struct ltq_rcu_usb2_bits
{
34 struct ltq_rcu_usb2_priv
{
35 struct regmap
*regmap
;
36 unsigned int phy_reg_offset
;
37 unsigned int ana_cfg1_reg_offset
;
38 const struct ltq_rcu_usb2_bits
*reg_bits
;
41 struct clk
*phy_gate_clk
;
42 struct reset_control
*ctrl_reset
;
43 struct reset_control
*phy_reset
;
46 static const struct ltq_rcu_usb2_bits xway_rcu_usb2_reg_bits
= {
48 .slave_endianness
= 9,
49 .host_endianness
= 10,
50 .have_ana_cfg
= false,
53 static const struct ltq_rcu_usb2_bits xrx100_rcu_usb2_reg_bits
= {
55 .slave_endianness
= 17,
56 .host_endianness
= 10,
57 .have_ana_cfg
= false,
60 static const struct ltq_rcu_usb2_bits xrx200_rcu_usb2_reg_bits
= {
62 .slave_endianness
= 9,
63 .host_endianness
= 10,
67 static const struct of_device_id ltq_rcu_usb2_phy_of_match
[] = {
69 .compatible
= "lantiq,ase-usb2-phy",
70 .data
= &xway_rcu_usb2_reg_bits
,
73 .compatible
= "lantiq,danube-usb2-phy",
74 .data
= &xway_rcu_usb2_reg_bits
,
77 .compatible
= "lantiq,xrx100-usb2-phy",
78 .data
= &xrx100_rcu_usb2_reg_bits
,
81 .compatible
= "lantiq,xrx200-usb2-phy",
82 .data
= &xrx200_rcu_usb2_reg_bits
,
85 .compatible
= "lantiq,xrx300-usb2-phy",
86 .data
= &xrx200_rcu_usb2_reg_bits
,
90 MODULE_DEVICE_TABLE(of
, ltq_rcu_usb2_phy_of_match
);
92 static int ltq_rcu_usb2_phy_init(struct phy
*phy
)
94 struct ltq_rcu_usb2_priv
*priv
= phy_get_drvdata(phy
);
96 if (priv
->reg_bits
->have_ana_cfg
) {
97 regmap_update_bits(priv
->regmap
, priv
->ana_cfg1_reg_offset
,
98 RCU_CFG1_TX_PEE
, RCU_CFG1_TX_PEE
);
99 regmap_update_bits(priv
->regmap
, priv
->ana_cfg1_reg_offset
,
100 RCU_CFG1_DIS_THR_MASK
, 7 << RCU_CFG1_DIS_THR_SHIFT
);
103 /* Configure core to host mode */
104 regmap_update_bits(priv
->regmap
, priv
->phy_reg_offset
,
105 BIT(priv
->reg_bits
->hostmode
), 0);
107 /* Select DMA endianness (Host-endian: big-endian) */
108 regmap_update_bits(priv
->regmap
, priv
->phy_reg_offset
,
109 BIT(priv
->reg_bits
->slave_endianness
), 0);
110 regmap_update_bits(priv
->regmap
, priv
->phy_reg_offset
,
111 BIT(priv
->reg_bits
->host_endianness
),
112 BIT(priv
->reg_bits
->host_endianness
));
117 static int ltq_rcu_usb2_phy_power_on(struct phy
*phy
)
119 struct ltq_rcu_usb2_priv
*priv
= phy_get_drvdata(phy
);
120 struct device
*dev
= priv
->dev
;
123 reset_control_deassert(priv
->phy_reset
);
125 ret
= clk_prepare_enable(priv
->phy_gate_clk
);
127 dev_err(dev
, "failed to enable PHY gate\n");
132 * at least the xrx200 usb2 phy requires some extra time to be
133 * operational after enabling the clock
135 usleep_range(100, 200);
140 static int ltq_rcu_usb2_phy_power_off(struct phy
*phy
)
142 struct ltq_rcu_usb2_priv
*priv
= phy_get_drvdata(phy
);
144 reset_control_assert(priv
->phy_reset
);
146 clk_disable_unprepare(priv
->phy_gate_clk
);
151 static const struct phy_ops ltq_rcu_usb2_phy_ops
= {
152 .init
= ltq_rcu_usb2_phy_init
,
153 .power_on
= ltq_rcu_usb2_phy_power_on
,
154 .power_off
= ltq_rcu_usb2_phy_power_off
,
155 .owner
= THIS_MODULE
,
158 static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv
*priv
,
159 struct platform_device
*pdev
)
161 struct device
*dev
= priv
->dev
;
162 const __be32
*offset
;
164 priv
->reg_bits
= of_device_get_match_data(dev
);
166 priv
->regmap
= syscon_node_to_regmap(dev
->of_node
->parent
);
167 if (IS_ERR(priv
->regmap
)) {
168 dev_err(dev
, "Failed to lookup RCU regmap\n");
169 return PTR_ERR(priv
->regmap
);
172 offset
= of_get_address(dev
->of_node
, 0, NULL
, NULL
);
174 dev_err(dev
, "Failed to get RCU PHY reg offset\n");
177 priv
->phy_reg_offset
= __be32_to_cpu(*offset
);
179 if (priv
->reg_bits
->have_ana_cfg
) {
180 offset
= of_get_address(dev
->of_node
, 1, NULL
, NULL
);
182 dev_err(dev
, "Failed to get RCU ANA CFG1 reg offset\n");
185 priv
->ana_cfg1_reg_offset
= __be32_to_cpu(*offset
);
188 priv
->phy_gate_clk
= devm_clk_get(dev
, "phy");
189 if (IS_ERR(priv
->phy_gate_clk
)) {
190 dev_err(dev
, "Unable to get USB phy gate clk\n");
191 return PTR_ERR(priv
->phy_gate_clk
);
194 priv
->ctrl_reset
= devm_reset_control_get_shared(dev
, "ctrl");
195 if (IS_ERR(priv
->ctrl_reset
)) {
196 if (PTR_ERR(priv
->ctrl_reset
) != -EPROBE_DEFER
)
197 dev_err(dev
, "failed to get 'ctrl' reset\n");
198 return PTR_ERR(priv
->ctrl_reset
);
201 priv
->phy_reset
= devm_reset_control_get_optional(dev
, "phy");
203 return PTR_ERR_OR_ZERO(priv
->phy_reset
);
206 static int ltq_rcu_usb2_phy_probe(struct platform_device
*pdev
)
208 struct device
*dev
= &pdev
->dev
;
209 struct ltq_rcu_usb2_priv
*priv
;
210 struct phy_provider
*provider
;
213 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
219 ret
= ltq_rcu_usb2_of_parse(priv
, pdev
);
223 /* Reset USB core through reset controller */
224 reset_control_deassert(priv
->ctrl_reset
);
226 reset_control_assert(priv
->phy_reset
);
228 priv
->phy
= devm_phy_create(dev
, dev
->of_node
, <q_rcu_usb2_phy_ops
);
229 if (IS_ERR(priv
->phy
)) {
230 dev_err(dev
, "failed to create PHY\n");
231 return PTR_ERR(priv
->phy
);
234 phy_set_drvdata(priv
->phy
, priv
);
236 provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
237 if (IS_ERR(provider
))
238 return PTR_ERR(provider
);
240 dev_set_drvdata(priv
->dev
, priv
);
244 static struct platform_driver ltq_rcu_usb2_phy_driver
= {
245 .probe
= ltq_rcu_usb2_phy_probe
,
247 .name
= "lantiq-rcu-usb2-phy",
248 .of_match_table
= ltq_rcu_usb2_phy_of_match
,
251 module_platform_driver(ltq_rcu_usb2_phy_driver
);
253 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
254 MODULE_DESCRIPTION("Lantiq XWAY USB2 PHY driver");
255 MODULE_LICENSE("GPL v2");