1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Marvell 88E6xxx Switch Global 2 Registers support |
4 | * |
5 | * Copyright (c) 2008 Marvell Semiconductor |
6 | * |
7 | * Copyright (c) 2016-2017 Savoir-faire Linux Inc. |
8 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
9 | * |
10 | * Copyright (c) 2017 National Instruments |
11 | * Brandon Streiff <brandon.streiff@ni.com> |
12 | */ |
13 | |
14 | #include <linux/bitfield.h> |
15 | |
16 | #include "global2.h" |
17 | |
18 | /* Offset 0x16: AVB Command Register |
19 | * Offset 0x17: AVB Data Register |
20 | * |
21 | * There are two different versions of this register interface: |
22 | * "6352": 3-bit "op" field, 4-bit "port" field. |
23 | * "6390": 2-bit "op" field, 5-bit "port" field. |
24 | * |
25 | * The "op" codes are different between the two, as well as the special |
26 | * port fields for global PTP and TAI configuration. |
27 | */ |
28 | |
29 | /* mv88e6xxx_g2_avb_read -- Read one or multiple 16-bit words. |
30 | * The hardware supports snapshotting up to four contiguous registers. |
31 | */ |
32 | static int mv88e6xxx_g2_avb_wait(struct mv88e6xxx_chip *chip) |
33 | { |
34 | int bit = __bf_shf(MV88E6352_G2_AVB_CMD_BUSY); |
35 | |
36 | return mv88e6xxx_g2_wait_bit(chip, MV88E6352_G2_AVB_CMD, bit, val: 0); |
37 | } |
38 | |
39 | static int mv88e6xxx_g2_avb_read(struct mv88e6xxx_chip *chip, u16 readop, |
40 | u16 *data, int len) |
41 | { |
42 | int err; |
43 | int i; |
44 | |
45 | err = mv88e6xxx_g2_avb_wait(chip); |
46 | if (err) |
47 | return err; |
48 | |
49 | /* Hardware can only snapshot four words. */ |
50 | if (len > 4) |
51 | return -E2BIG; |
52 | |
53 | err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_CMD, |
54 | MV88E6352_G2_AVB_CMD_BUSY | readop); |
55 | if (err) |
56 | return err; |
57 | |
58 | err = mv88e6xxx_g2_avb_wait(chip); |
59 | if (err) |
60 | return err; |
61 | |
62 | for (i = 0; i < len; ++i) { |
63 | err = mv88e6xxx_g2_read(chip, MV88E6352_G2_AVB_DATA, |
64 | val: &data[i]); |
65 | if (err) |
66 | return err; |
67 | } |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | /* mv88e6xxx_g2_avb_write -- Write one 16-bit word. */ |
73 | static int mv88e6xxx_g2_avb_write(struct mv88e6xxx_chip *chip, u16 writeop, |
74 | u16 data) |
75 | { |
76 | int err; |
77 | |
78 | err = mv88e6xxx_g2_avb_wait(chip); |
79 | if (err) |
80 | return err; |
81 | |
82 | err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_DATA, val: data); |
83 | if (err) |
84 | return err; |
85 | |
86 | err = mv88e6xxx_g2_write(chip, MV88E6352_G2_AVB_CMD, |
87 | MV88E6352_G2_AVB_CMD_BUSY | writeop); |
88 | |
89 | return mv88e6xxx_g2_avb_wait(chip); |
90 | } |
91 | |
92 | static int mv88e6352_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip, |
93 | int port, int addr, u16 *data, |
94 | int len) |
95 | { |
96 | u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ : |
97 | MV88E6352_G2_AVB_CMD_OP_READ_INCR) | |
98 | (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | |
99 | addr; |
100 | |
101 | return mv88e6xxx_g2_avb_read(chip, readop, data, len); |
102 | } |
103 | |
104 | static int mv88e6352_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip, |
105 | int port, int addr, u16 data) |
106 | { |
107 | u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE | (port << 8) | |
108 | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr; |
109 | |
110 | return mv88e6xxx_g2_avb_write(chip, writeop, data); |
111 | } |
112 | |
113 | static int mv88e6352_g2_avb_ptp_read(struct mv88e6xxx_chip *chip, int addr, |
114 | u16 *data, int len) |
115 | { |
116 | return mv88e6352_g2_avb_port_ptp_read(chip, |
117 | MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL, |
118 | addr, data, len); |
119 | } |
120 | |
121 | static int mv88e6352_g2_avb_ptp_write(struct mv88e6xxx_chip *chip, int addr, |
122 | u16 data) |
123 | { |
124 | return mv88e6352_g2_avb_port_ptp_write(chip, |
125 | MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL, |
126 | addr, data); |
127 | } |
128 | |
129 | static int mv88e6352_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr, |
130 | u16 *data, int len) |
131 | { |
132 | return mv88e6352_g2_avb_port_ptp_read(chip, |
133 | MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL, |
134 | addr, data, len); |
135 | } |
136 | |
137 | static int mv88e6352_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr, |
138 | u16 data) |
139 | { |
140 | return mv88e6352_g2_avb_port_ptp_write(chip, |
141 | MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL, |
142 | addr, data); |
143 | } |
144 | |
145 | const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = { |
146 | .port_ptp_read = mv88e6352_g2_avb_port_ptp_read, |
147 | .port_ptp_write = mv88e6352_g2_avb_port_ptp_write, |
148 | .ptp_read = mv88e6352_g2_avb_ptp_read, |
149 | .ptp_write = mv88e6352_g2_avb_ptp_write, |
150 | .tai_read = mv88e6352_g2_avb_tai_read, |
151 | .tai_write = mv88e6352_g2_avb_tai_write, |
152 | }; |
153 | |
154 | static int mv88e6165_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr, |
155 | u16 *data, int len) |
156 | { |
157 | return mv88e6352_g2_avb_port_ptp_read(chip, |
158 | MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL, |
159 | addr, data, len); |
160 | } |
161 | |
162 | static int mv88e6165_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr, |
163 | u16 data) |
164 | { |
165 | return mv88e6352_g2_avb_port_ptp_write(chip, |
166 | MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL, |
167 | addr, data); |
168 | } |
169 | |
170 | const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = { |
171 | .port_ptp_read = mv88e6352_g2_avb_port_ptp_read, |
172 | .port_ptp_write = mv88e6352_g2_avb_port_ptp_write, |
173 | .ptp_read = mv88e6352_g2_avb_ptp_read, |
174 | .ptp_write = mv88e6352_g2_avb_ptp_write, |
175 | .tai_read = mv88e6165_g2_avb_tai_read, |
176 | .tai_write = mv88e6165_g2_avb_tai_write, |
177 | }; |
178 | |
179 | static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip, |
180 | int port, int addr, u16 *data, |
181 | int len) |
182 | { |
183 | u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ : |
184 | MV88E6390_G2_AVB_CMD_OP_READ_INCR) | |
185 | (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | |
186 | addr; |
187 | |
188 | return mv88e6xxx_g2_avb_read(chip, readop, data, len); |
189 | } |
190 | |
191 | static int mv88e6390_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip, |
192 | int port, int addr, u16 data) |
193 | { |
194 | u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE | (port << 8) | |
195 | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr; |
196 | |
197 | return mv88e6xxx_g2_avb_write(chip, writeop, data); |
198 | } |
199 | |
200 | static int mv88e6390_g2_avb_ptp_read(struct mv88e6xxx_chip *chip, int addr, |
201 | u16 *data, int len) |
202 | { |
203 | return mv88e6390_g2_avb_port_ptp_read(chip, |
204 | MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL, |
205 | addr, data, len); |
206 | } |
207 | |
208 | static int mv88e6390_g2_avb_ptp_write(struct mv88e6xxx_chip *chip, int addr, |
209 | u16 data) |
210 | { |
211 | return mv88e6390_g2_avb_port_ptp_write(chip, |
212 | MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL, |
213 | addr, data); |
214 | } |
215 | |
216 | static int mv88e6390_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr, |
217 | u16 *data, int len) |
218 | { |
219 | return mv88e6390_g2_avb_port_ptp_read(chip, |
220 | MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL, |
221 | addr, data, len); |
222 | } |
223 | |
224 | static int mv88e6390_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr, |
225 | u16 data) |
226 | { |
227 | return mv88e6390_g2_avb_port_ptp_write(chip, |
228 | MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL, |
229 | addr, data); |
230 | } |
231 | |
232 | const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = { |
233 | .port_ptp_read = mv88e6390_g2_avb_port_ptp_read, |
234 | .port_ptp_write = mv88e6390_g2_avb_port_ptp_write, |
235 | .ptp_read = mv88e6390_g2_avb_ptp_read, |
236 | .ptp_write = mv88e6390_g2_avb_ptp_write, |
237 | .tai_read = mv88e6390_g2_avb_tai_read, |
238 | .tai_write = mv88e6390_g2_avb_tai_write, |
239 | }; |
240 | |