1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Broadcom Northstar USB 3.0 PHY Driver |
4 | * |
5 | * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl> |
6 | * Copyright (C) 2016 Broadcom |
7 | * |
8 | * All magic values used for initialization (and related comments) were obtained |
9 | * from Broadcom's SDK: |
10 | * Copyright (c) Broadcom Corp, 2012 |
11 | */ |
12 | |
13 | #include <linux/bcma/bcma.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/err.h> |
16 | #include <linux/iopoll.h> |
17 | #include <linux/mdio.h> |
18 | #include <linux/module.h> |
19 | #include <linux/of.h> |
20 | #include <linux/of_address.h> |
21 | #include <linux/platform_device.h> |
22 | #include <linux/phy/phy.h> |
23 | #include <linux/property.h> |
24 | #include <linux/slab.h> |
25 | |
26 | #define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f |
27 | #define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 |
28 | #define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 |
29 | #define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 |
30 | |
31 | /* Registers of PLL30 block */ |
32 | #define BCM_NS_USB3_PLL_CONTROL 0x01 |
33 | #define BCM_NS_USB3_PLLA_CONTROL0 0x0a |
34 | #define BCM_NS_USB3_PLLA_CONTROL1 0x0b |
35 | |
36 | /* Registers of TX PMD block */ |
37 | #define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 |
38 | |
39 | /* Registers of PIPE block */ |
40 | #define BCM_NS_USB3_LFPS_CMP 0x02 |
41 | #define BCM_NS_USB3_LFPS_DEGLITCH 0x03 |
42 | |
43 | enum bcm_ns_family { |
44 | BCM_NS_UNKNOWN, |
45 | BCM_NS_AX, |
46 | BCM_NS_BX, |
47 | }; |
48 | |
49 | struct bcm_ns_usb3 { |
50 | struct device *dev; |
51 | enum bcm_ns_family family; |
52 | void __iomem *dmp; |
53 | struct mdio_device *mdiodev; |
54 | struct phy *phy; |
55 | }; |
56 | |
57 | static const struct of_device_id bcm_ns_usb3_id_table[] = { |
58 | { |
59 | .compatible = "brcm,ns-ax-usb3-phy" , |
60 | .data = (int *)BCM_NS_AX, |
61 | }, |
62 | { |
63 | .compatible = "brcm,ns-bx-usb3-phy" , |
64 | .data = (int *)BCM_NS_BX, |
65 | }, |
66 | {}, |
67 | }; |
68 | |
69 | static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, |
70 | u16 value); |
71 | |
72 | static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) |
73 | { |
74 | int err; |
75 | |
76 | /* USB3 PLL Block */ |
77 | err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, |
78 | BCM_NS_USB3_PHY_PLL30_BLOCK); |
79 | if (err < 0) |
80 | return err; |
81 | |
82 | /* Assert Ana_Pllseq start */ |
83 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, value: 0x1000); |
84 | |
85 | /* Assert CML Divider ratio to 26 */ |
86 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, value: 0x6400); |
87 | |
88 | /* Asserting PLL Reset */ |
89 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, value: 0xc000); |
90 | |
91 | /* Deaaserting PLL Reset */ |
92 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, value: 0x8000); |
93 | |
94 | /* Deasserting USB3 system reset */ |
95 | writel(val: 0, addr: usb3->dmp + BCMA_RESET_CTL); |
96 | |
97 | /* PLL frequency monitor enable */ |
98 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, value: 0x9000); |
99 | |
100 | /* PIPE Block */ |
101 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, |
102 | BCM_NS_USB3_PHY_PIPE_BLOCK); |
103 | |
104 | /* CMPMAX & CMPMINTH setting */ |
105 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, value: 0xf30d); |
106 | |
107 | /* DEGLITCH MIN & MAX setting */ |
108 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, value: 0x6302); |
109 | |
110 | /* TXPMD block */ |
111 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, |
112 | BCM_NS_USB3_PHY_TX_PMD_BLOCK); |
113 | |
114 | /* Enabling SSC */ |
115 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, value: 0x1003); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) |
121 | { |
122 | int err; |
123 | |
124 | /* PLL30 block */ |
125 | err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, |
126 | BCM_NS_USB3_PHY_PLL30_BLOCK); |
127 | if (err < 0) |
128 | return err; |
129 | |
130 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, value: 0x6400); |
131 | |
132 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, value: 0x80e0); |
133 | |
134 | bcm_ns_usb3_mdio_phy_write(usb3, reg: 0x02, value: 0x009c); |
135 | |
136 | /* Enable SSC */ |
137 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, |
138 | BCM_NS_USB3_PHY_TX_PMD_BLOCK); |
139 | |
140 | bcm_ns_usb3_mdio_phy_write(usb3, reg: 0x02, value: 0x21d3); |
141 | |
142 | bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, value: 0x1003); |
143 | |
144 | /* Deasserting USB3 system reset */ |
145 | writel(val: 0, addr: usb3->dmp + BCMA_RESET_CTL); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int bcm_ns_usb3_phy_init(struct phy *phy) |
151 | { |
152 | struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); |
153 | int err; |
154 | |
155 | /* Perform USB3 system soft reset */ |
156 | writel(BCMA_RESET_CTL_RESET, addr: usb3->dmp + BCMA_RESET_CTL); |
157 | |
158 | switch (usb3->family) { |
159 | case BCM_NS_AX: |
160 | err = bcm_ns_usb3_phy_init_ns_ax(usb3); |
161 | break; |
162 | case BCM_NS_BX: |
163 | err = bcm_ns_usb3_phy_init_ns_bx(usb3); |
164 | break; |
165 | default: |
166 | WARN_ON(1); |
167 | err = -ENOTSUPP; |
168 | } |
169 | |
170 | return err; |
171 | } |
172 | |
173 | static const struct phy_ops ops = { |
174 | .init = bcm_ns_usb3_phy_init, |
175 | .owner = THIS_MODULE, |
176 | }; |
177 | |
178 | /************************************************** |
179 | * MDIO driver code |
180 | **************************************************/ |
181 | |
182 | static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, |
183 | u16 value) |
184 | { |
185 | struct mdio_device *mdiodev = usb3->mdiodev; |
186 | |
187 | return mdiodev_write(mdiodev, regnum: reg, val: value); |
188 | } |
189 | |
190 | static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) |
191 | { |
192 | struct device *dev = &mdiodev->dev; |
193 | struct phy_provider *phy_provider; |
194 | struct device_node *syscon_np; |
195 | struct bcm_ns_usb3 *usb3; |
196 | struct resource res; |
197 | int err; |
198 | |
199 | usb3 = devm_kzalloc(dev, size: sizeof(*usb3), GFP_KERNEL); |
200 | if (!usb3) |
201 | return -ENOMEM; |
202 | |
203 | usb3->dev = dev; |
204 | usb3->mdiodev = mdiodev; |
205 | |
206 | usb3->family = (enum bcm_ns_family)device_get_match_data(dev); |
207 | |
208 | syscon_np = of_parse_phandle(np: dev->of_node, phandle_name: "usb3-dmp-syscon" , index: 0); |
209 | err = of_address_to_resource(dev: syscon_np, index: 0, r: &res); |
210 | of_node_put(node: syscon_np); |
211 | if (err) |
212 | return err; |
213 | |
214 | usb3->dmp = devm_ioremap_resource(dev, res: &res); |
215 | if (IS_ERR(ptr: usb3->dmp)) |
216 | return PTR_ERR(ptr: usb3->dmp); |
217 | |
218 | usb3->phy = devm_phy_create(dev, NULL, ops: &ops); |
219 | if (IS_ERR(ptr: usb3->phy)) { |
220 | dev_err(dev, "Failed to create PHY\n" ); |
221 | return PTR_ERR(ptr: usb3->phy); |
222 | } |
223 | |
224 | phy_set_drvdata(phy: usb3->phy, data: usb3); |
225 | |
226 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
227 | |
228 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
229 | } |
230 | |
231 | static struct mdio_driver bcm_ns_usb3_mdio_driver = { |
232 | .mdiodrv = { |
233 | .driver = { |
234 | .name = "bcm_ns_mdio_usb3" , |
235 | .of_match_table = bcm_ns_usb3_id_table, |
236 | }, |
237 | }, |
238 | .probe = bcm_ns_usb3_mdio_probe, |
239 | }; |
240 | |
241 | mdio_module_driver(bcm_ns_usb3_mdio_driver); |
242 | |
243 | MODULE_LICENSE("GPL v2" ); |
244 | MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); |
245 | |