1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2009-2016 Cavium, Inc. |
4 | */ |
5 | |
6 | #include <linux/delay.h> |
7 | #include <linux/io.h> |
8 | #include <linux/module.h> |
9 | #include <linux/phy.h> |
10 | |
11 | #include "mdio-cavium.h" |
12 | |
13 | static void cavium_mdiobus_set_mode(struct cavium_mdiobus *p, |
14 | enum cavium_mdiobus_mode m) |
15 | { |
16 | union cvmx_smix_clk smi_clk; |
17 | |
18 | if (m == p->mode) |
19 | return; |
20 | |
21 | smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK); |
22 | smi_clk.s.mode = (m == C45) ? 1 : 0; |
23 | smi_clk.s.preamble = 1; |
24 | oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK); |
25 | p->mode = m; |
26 | } |
27 | |
28 | static int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p, |
29 | int phy_id, int devad, int regnum) |
30 | { |
31 | union cvmx_smix_cmd smi_cmd; |
32 | union cvmx_smix_wr_dat smi_wr; |
33 | int timeout = 1000; |
34 | |
35 | cavium_mdiobus_set_mode(p, m: C45); |
36 | |
37 | smi_wr.u64 = 0; |
38 | smi_wr.s.dat = regnum & 0xffff; |
39 | oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT); |
40 | |
41 | smi_cmd.u64 = 0; |
42 | smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */ |
43 | smi_cmd.s.phy_adr = phy_id; |
44 | smi_cmd.s.reg_adr = devad; |
45 | oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); |
46 | |
47 | do { |
48 | /* Wait 1000 clocks so we don't saturate the RSL bus |
49 | * doing reads. |
50 | */ |
51 | __delay(loops: 1000); |
52 | smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT); |
53 | } while (smi_wr.s.pending && --timeout); |
54 | |
55 | if (timeout <= 0) |
56 | return -EIO; |
57 | return 0; |
58 | } |
59 | |
60 | int cavium_mdiobus_read_c22(struct mii_bus *bus, int phy_id, int regnum) |
61 | { |
62 | struct cavium_mdiobus *p = bus->priv; |
63 | union cvmx_smix_cmd smi_cmd; |
64 | union cvmx_smix_rd_dat smi_rd; |
65 | int timeout = 1000; |
66 | |
67 | cavium_mdiobus_set_mode(p, m: C22); |
68 | |
69 | smi_cmd.u64 = 0; |
70 | smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */ |
71 | smi_cmd.s.phy_adr = phy_id; |
72 | smi_cmd.s.reg_adr = regnum; |
73 | oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); |
74 | |
75 | do { |
76 | /* Wait 1000 clocks so we don't saturate the RSL bus |
77 | * doing reads. |
78 | */ |
79 | __delay(loops: 1000); |
80 | smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT); |
81 | } while (smi_rd.s.pending && --timeout); |
82 | |
83 | if (smi_rd.s.val) |
84 | return smi_rd.s.dat; |
85 | else |
86 | return -EIO; |
87 | } |
88 | EXPORT_SYMBOL(cavium_mdiobus_read_c22); |
89 | |
90 | int cavium_mdiobus_read_c45(struct mii_bus *bus, int phy_id, int devad, |
91 | int regnum) |
92 | { |
93 | struct cavium_mdiobus *p = bus->priv; |
94 | union cvmx_smix_cmd smi_cmd; |
95 | union cvmx_smix_rd_dat smi_rd; |
96 | int timeout = 1000; |
97 | int r; |
98 | |
99 | r = cavium_mdiobus_c45_addr(p, phy_id, devad, regnum); |
100 | if (r < 0) |
101 | return r; |
102 | |
103 | smi_cmd.u64 = 0; |
104 | smi_cmd.s.phy_op = 3; /* MDIO_CLAUSE_45_READ */ |
105 | smi_cmd.s.phy_adr = phy_id; |
106 | smi_cmd.s.reg_adr = regnum; |
107 | oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); |
108 | |
109 | do { |
110 | /* Wait 1000 clocks so we don't saturate the RSL bus |
111 | * doing reads. |
112 | */ |
113 | __delay(loops: 1000); |
114 | smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT); |
115 | } while (smi_rd.s.pending && --timeout); |
116 | |
117 | if (smi_rd.s.val) |
118 | return smi_rd.s.dat; |
119 | else |
120 | return -EIO; |
121 | } |
122 | EXPORT_SYMBOL(cavium_mdiobus_read_c45); |
123 | |
124 | int cavium_mdiobus_write_c22(struct mii_bus *bus, int phy_id, int regnum, |
125 | u16 val) |
126 | { |
127 | struct cavium_mdiobus *p = bus->priv; |
128 | union cvmx_smix_cmd smi_cmd; |
129 | union cvmx_smix_wr_dat smi_wr; |
130 | int timeout = 1000; |
131 | |
132 | cavium_mdiobus_set_mode(p, m: C22); |
133 | |
134 | smi_wr.u64 = 0; |
135 | smi_wr.s.dat = val; |
136 | oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT); |
137 | |
138 | smi_cmd.u64 = 0; |
139 | smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */ |
140 | smi_cmd.s.phy_adr = phy_id; |
141 | smi_cmd.s.reg_adr = regnum; |
142 | oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); |
143 | |
144 | do { |
145 | /* Wait 1000 clocks so we don't saturate the RSL bus |
146 | * doing reads. |
147 | */ |
148 | __delay(loops: 1000); |
149 | smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT); |
150 | } while (smi_wr.s.pending && --timeout); |
151 | |
152 | if (timeout <= 0) |
153 | return -EIO; |
154 | |
155 | return 0; |
156 | } |
157 | EXPORT_SYMBOL(cavium_mdiobus_write_c22); |
158 | |
159 | int cavium_mdiobus_write_c45(struct mii_bus *bus, int phy_id, int devad, |
160 | int regnum, u16 val) |
161 | { |
162 | struct cavium_mdiobus *p = bus->priv; |
163 | union cvmx_smix_cmd smi_cmd; |
164 | union cvmx_smix_wr_dat smi_wr; |
165 | int timeout = 1000; |
166 | int r; |
167 | |
168 | r = cavium_mdiobus_c45_addr(p, phy_id, devad, regnum); |
169 | if (r < 0) |
170 | return r; |
171 | |
172 | smi_wr.u64 = 0; |
173 | smi_wr.s.dat = val; |
174 | oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT); |
175 | |
176 | smi_cmd.u64 = 0; |
177 | smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_45_WRITE */ |
178 | smi_cmd.s.phy_adr = phy_id; |
179 | smi_cmd.s.reg_adr = devad; |
180 | oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); |
181 | |
182 | do { |
183 | /* Wait 1000 clocks so we don't saturate the RSL bus |
184 | * doing reads. |
185 | */ |
186 | __delay(loops: 1000); |
187 | smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT); |
188 | } while (smi_wr.s.pending && --timeout); |
189 | |
190 | if (timeout <= 0) |
191 | return -EIO; |
192 | |
193 | return 0; |
194 | } |
195 | EXPORT_SYMBOL(cavium_mdiobus_write_c45); |
196 | |
197 | MODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers" ); |
198 | MODULE_AUTHOR("David Daney" ); |
199 | MODULE_LICENSE("GPL v2" ); |
200 | |