1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Renesas R-Car Gen3 for USB3.0 PHY driver |
4 | * |
5 | * Copyright (C) 2017 Renesas Electronics Corporation |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/io.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/pm_runtime.h> |
16 | |
17 | #define USB30_CLKSET0 0x034 |
18 | #define USB30_CLKSET1 0x036 |
19 | #define USB30_SSC_SET 0x038 |
20 | #define USB30_PHY_ENABLE 0x060 |
21 | #define USB30_VBUS_EN 0x064 |
22 | |
23 | /* USB30_CLKSET0 */ |
24 | #define CLKSET0_PRIVATE 0x05c0 |
25 | #define CLKSET0_USB30_FSEL_USB_EXTAL 0x0002 |
26 | |
27 | /* USB30_CLKSET1 */ |
28 | #define CLKSET1_USB30_PLL_MULTI_SHIFT 6 |
29 | #define CLKSET1_USB30_PLL_MULTI_USB_EXTAL (0x64 << \ |
30 | CLKSET1_USB30_PLL_MULTI_SHIFT) |
31 | #define CLKSET1_PHYRESET BIT(4) /* 1: reset */ |
32 | #define CLKSET1_REF_CLKDIV BIT(3) /* 1: USB_EXTAL */ |
33 | #define CLKSET1_PRIVATE_2_1 BIT(1) /* Write B'01 */ |
34 | #define CLKSET1_REF_CLK_SEL BIT(0) /* 1: USB3S0_CLK_P */ |
35 | |
36 | /* USB30_SSC_SET */ |
37 | #define SSC_SET_SSC_EN BIT(12) |
38 | #define SSC_SET_RANGE_SHIFT 9 |
39 | #define SSC_SET_RANGE_4980 (0x0 << SSC_SET_RANGE_SHIFT) |
40 | #define SSC_SET_RANGE_4492 (0x1 << SSC_SET_RANGE_SHIFT) |
41 | #define SSC_SET_RANGE_4003 (0x2 << SSC_SET_RANGE_SHIFT) |
42 | |
43 | /* USB30_PHY_ENABLE */ |
44 | #define PHY_ENABLE_RESET_EN BIT(4) |
45 | |
46 | /* USB30_VBUS_EN */ |
47 | #define VBUS_EN_VBUS_EN BIT(1) |
48 | |
49 | struct rcar_gen3_usb3 { |
50 | void __iomem *base; |
51 | struct phy *phy; |
52 | u32 ssc_range; |
53 | bool usb3s_clk; |
54 | bool usb_extal; |
55 | }; |
56 | |
57 | static void write_clkset1_for_usb_extal(struct rcar_gen3_usb3 *r, bool reset) |
58 | { |
59 | u16 val = CLKSET1_USB30_PLL_MULTI_USB_EXTAL | |
60 | CLKSET1_REF_CLKDIV | CLKSET1_PRIVATE_2_1; |
61 | |
62 | if (reset) |
63 | val |= CLKSET1_PHYRESET; |
64 | |
65 | writew(val, addr: r->base + USB30_CLKSET1); |
66 | } |
67 | |
68 | static void rcar_gen3_phy_usb3_enable_ssc(struct rcar_gen3_usb3 *r) |
69 | { |
70 | u16 val = SSC_SET_SSC_EN; |
71 | |
72 | switch (r->ssc_range) { |
73 | case 4980: |
74 | val |= SSC_SET_RANGE_4980; |
75 | break; |
76 | case 4492: |
77 | val |= SSC_SET_RANGE_4492; |
78 | break; |
79 | case 4003: |
80 | val |= SSC_SET_RANGE_4003; |
81 | break; |
82 | default: |
83 | dev_err(&r->phy->dev, "%s: unsupported range (%x)\n" , __func__, |
84 | r->ssc_range); |
85 | return; |
86 | } |
87 | |
88 | writew(val, addr: r->base + USB30_SSC_SET); |
89 | } |
90 | |
91 | static void rcar_gen3_phy_usb3_select_usb_extal(struct rcar_gen3_usb3 *r) |
92 | { |
93 | write_clkset1_for_usb_extal(r, reset: false); |
94 | if (r->ssc_range) |
95 | rcar_gen3_phy_usb3_enable_ssc(r); |
96 | writew(CLKSET0_PRIVATE | CLKSET0_USB30_FSEL_USB_EXTAL, |
97 | addr: r->base + USB30_CLKSET0); |
98 | writew(PHY_ENABLE_RESET_EN, addr: r->base + USB30_PHY_ENABLE); |
99 | write_clkset1_for_usb_extal(r, reset: true); |
100 | usleep_range(min: 10, max: 20); |
101 | write_clkset1_for_usb_extal(r, reset: false); |
102 | } |
103 | |
104 | static int rcar_gen3_phy_usb3_init(struct phy *p) |
105 | { |
106 | struct rcar_gen3_usb3 *r = phy_get_drvdata(phy: p); |
107 | |
108 | dev_vdbg(&r->phy->dev, "%s: enter (%d, %d, %d)\n" , __func__, |
109 | r->usb3s_clk, r->usb_extal, r->ssc_range); |
110 | |
111 | if (!r->usb3s_clk && r->usb_extal) |
112 | rcar_gen3_phy_usb3_select_usb_extal(r); |
113 | |
114 | /* Enables VBUS detection anyway */ |
115 | writew(VBUS_EN_VBUS_EN, addr: r->base + USB30_VBUS_EN); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static const struct phy_ops rcar_gen3_phy_usb3_ops = { |
121 | .init = rcar_gen3_phy_usb3_init, |
122 | .owner = THIS_MODULE, |
123 | }; |
124 | |
125 | static const struct of_device_id rcar_gen3_phy_usb3_match_table[] = { |
126 | { .compatible = "renesas,rcar-gen3-usb3-phy" }, |
127 | { } |
128 | }; |
129 | MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb3_match_table); |
130 | |
131 | static int rcar_gen3_phy_usb3_probe(struct platform_device *pdev) |
132 | { |
133 | struct device *dev = &pdev->dev; |
134 | struct rcar_gen3_usb3 *r; |
135 | struct phy_provider *provider; |
136 | int ret = 0; |
137 | struct clk *clk; |
138 | |
139 | if (!dev->of_node) { |
140 | dev_err(dev, "This driver needs device tree\n" ); |
141 | return -EINVAL; |
142 | } |
143 | |
144 | r = devm_kzalloc(dev, size: sizeof(*r), GFP_KERNEL); |
145 | if (!r) |
146 | return -ENOMEM; |
147 | |
148 | r->base = devm_platform_ioremap_resource(pdev, index: 0); |
149 | if (IS_ERR(ptr: r->base)) |
150 | return PTR_ERR(ptr: r->base); |
151 | |
152 | clk = devm_clk_get(dev, id: "usb3s_clk" ); |
153 | if (!IS_ERR(ptr: clk) && !clk_prepare_enable(clk)) { |
154 | r->usb3s_clk = !!clk_get_rate(clk); |
155 | clk_disable_unprepare(clk); |
156 | } |
157 | clk = devm_clk_get(dev, id: "usb_extal" ); |
158 | if (!IS_ERR(ptr: clk) && !clk_prepare_enable(clk)) { |
159 | r->usb_extal = !!clk_get_rate(clk); |
160 | clk_disable_unprepare(clk); |
161 | } |
162 | |
163 | if (!r->usb3s_clk && !r->usb_extal) { |
164 | dev_err(dev, "This driver needs usb3s_clk and/or usb_extal\n" ); |
165 | ret = -EINVAL; |
166 | goto error; |
167 | } |
168 | |
169 | /* |
170 | * devm_phy_create() will call pm_runtime_enable(&phy->dev); |
171 | * And then, phy-core will manage runtime pm for this device. |
172 | */ |
173 | pm_runtime_enable(dev); |
174 | |
175 | r->phy = devm_phy_create(dev, NULL, ops: &rcar_gen3_phy_usb3_ops); |
176 | if (IS_ERR(ptr: r->phy)) { |
177 | dev_err(dev, "Failed to create USB3 PHY\n" ); |
178 | ret = PTR_ERR(ptr: r->phy); |
179 | goto error; |
180 | } |
181 | |
182 | of_property_read_u32(np: dev->of_node, propname: "renesas,ssc-range" , out_value: &r->ssc_range); |
183 | |
184 | platform_set_drvdata(pdev, data: r); |
185 | phy_set_drvdata(phy: r->phy, data: r); |
186 | |
187 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
188 | if (IS_ERR(ptr: provider)) { |
189 | dev_err(dev, "Failed to register PHY provider\n" ); |
190 | ret = PTR_ERR(ptr: provider); |
191 | goto error; |
192 | } |
193 | |
194 | return 0; |
195 | |
196 | error: |
197 | pm_runtime_disable(dev); |
198 | |
199 | return ret; |
200 | } |
201 | |
202 | static void rcar_gen3_phy_usb3_remove(struct platform_device *pdev) |
203 | { |
204 | pm_runtime_disable(dev: &pdev->dev); |
205 | }; |
206 | |
207 | static struct platform_driver rcar_gen3_phy_usb3_driver = { |
208 | .driver = { |
209 | .name = "phy_rcar_gen3_usb3" , |
210 | .of_match_table = rcar_gen3_phy_usb3_match_table, |
211 | }, |
212 | .probe = rcar_gen3_phy_usb3_probe, |
213 | .remove_new = rcar_gen3_phy_usb3_remove, |
214 | }; |
215 | module_platform_driver(rcar_gen3_phy_usb3_driver); |
216 | |
217 | MODULE_LICENSE("GPL v2" ); |
218 | MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 3.0 PHY" ); |
219 | MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>" ); |
220 | |