1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Marvell 88E6xxx Switch Global 2 Scratch & Misc Registers support |
4 | * |
5 | * Copyright (c) 2008 Marvell Semiconductor |
6 | * |
7 | * Copyright (c) 2017 National Instruments |
8 | * Brandon Streiff <brandon.streiff@ni.com> |
9 | */ |
10 | |
11 | #include "chip.h" |
12 | #include "global2.h" |
13 | |
14 | /* Offset 0x1A: Scratch and Misc. Register */ |
15 | static int mv88e6xxx_g2_scratch_read(struct mv88e6xxx_chip *chip, int reg, |
16 | u8 *data) |
17 | { |
18 | u16 value; |
19 | int err; |
20 | |
21 | err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, |
22 | val: reg << 8); |
23 | if (err) |
24 | return err; |
25 | |
26 | err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, val: &value); |
27 | if (err) |
28 | return err; |
29 | |
30 | *data = (value & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK); |
31 | |
32 | return 0; |
33 | } |
34 | |
35 | static int mv88e6xxx_g2_scratch_write(struct mv88e6xxx_chip *chip, int reg, |
36 | u8 data) |
37 | { |
38 | u16 value = (reg << 8) | data; |
39 | |
40 | return mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, |
41 | MV88E6XXX_G2_SCRATCH_MISC_UPDATE | value); |
42 | } |
43 | |
44 | /** |
45 | * mv88e6xxx_g2_scratch_get_bit - get a bit |
46 | * @chip: chip private data |
47 | * @base_reg: base of scratch bits |
48 | * @offset: index of bit within the register |
49 | * @set: is bit set? |
50 | */ |
51 | static int mv88e6xxx_g2_scratch_get_bit(struct mv88e6xxx_chip *chip, |
52 | int base_reg, unsigned int offset, |
53 | int *set) |
54 | { |
55 | int reg = base_reg + (offset / 8); |
56 | u8 mask = (1 << (offset & 0x7)); |
57 | u8 val; |
58 | int err; |
59 | |
60 | err = mv88e6xxx_g2_scratch_read(chip, reg, data: &val); |
61 | if (err) |
62 | return err; |
63 | |
64 | *set = !!(mask & val); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | /** |
70 | * mv88e6xxx_g2_scratch_set_bit - set (or clear) a bit |
71 | * @chip: chip private data |
72 | * @base_reg: base of scratch bits |
73 | * @offset: index of bit within the register |
74 | * @set: should this bit be set? |
75 | * |
76 | * Helper function for dealing with the direction and data registers. |
77 | */ |
78 | static int mv88e6xxx_g2_scratch_set_bit(struct mv88e6xxx_chip *chip, |
79 | int base_reg, unsigned int offset, |
80 | int set) |
81 | { |
82 | int reg = base_reg + (offset / 8); |
83 | u8 mask = (1 << (offset & 0x7)); |
84 | u8 val; |
85 | int err; |
86 | |
87 | err = mv88e6xxx_g2_scratch_read(chip, reg, data: &val); |
88 | if (err) |
89 | return err; |
90 | |
91 | if (set) |
92 | val |= mask; |
93 | else |
94 | val &= ~mask; |
95 | |
96 | return mv88e6xxx_g2_scratch_write(chip, reg, data: val); |
97 | } |
98 | |
99 | /** |
100 | * mv88e6352_g2_scratch_gpio_get_data - get data on gpio pin |
101 | * @chip: chip private data |
102 | * @pin: gpio index |
103 | * |
104 | * Return: 0 for low, 1 for high, negative error |
105 | */ |
106 | static int mv88e6352_g2_scratch_gpio_get_data(struct mv88e6xxx_chip *chip, |
107 | unsigned int pin) |
108 | { |
109 | int val = 0; |
110 | int err; |
111 | |
112 | err = mv88e6xxx_g2_scratch_get_bit(chip, |
113 | MV88E6352_G2_SCRATCH_GPIO_DATA0, |
114 | offset: pin, set: &val); |
115 | if (err) |
116 | return err; |
117 | |
118 | return val; |
119 | } |
120 | |
121 | /** |
122 | * mv88e6352_g2_scratch_gpio_set_data - set data on gpio pin |
123 | * @chip: chip private data |
124 | * @pin: gpio index |
125 | * @value: value to set |
126 | */ |
127 | static int mv88e6352_g2_scratch_gpio_set_data(struct mv88e6xxx_chip *chip, |
128 | unsigned int pin, int value) |
129 | { |
130 | u8 mask = (1 << (pin & 0x7)); |
131 | int offset = (pin / 8); |
132 | int reg; |
133 | |
134 | reg = MV88E6352_G2_SCRATCH_GPIO_DATA0 + offset; |
135 | |
136 | if (value) |
137 | chip->gpio_data[offset] |= mask; |
138 | else |
139 | chip->gpio_data[offset] &= ~mask; |
140 | |
141 | return mv88e6xxx_g2_scratch_write(chip, reg, data: chip->gpio_data[offset]); |
142 | } |
143 | |
144 | /** |
145 | * mv88e6352_g2_scratch_gpio_get_dir - get direction of gpio pin |
146 | * @chip: chip private data |
147 | * @pin: gpio index |
148 | * |
149 | * Return: 0 for output, 1 for input (same as GPIOF_DIR_XXX). |
150 | */ |
151 | static int mv88e6352_g2_scratch_gpio_get_dir(struct mv88e6xxx_chip *chip, |
152 | unsigned int pin) |
153 | { |
154 | int val = 0; |
155 | int err; |
156 | |
157 | err = mv88e6xxx_g2_scratch_get_bit(chip, |
158 | MV88E6352_G2_SCRATCH_GPIO_DIR0, |
159 | offset: pin, set: &val); |
160 | if (err) |
161 | return err; |
162 | |
163 | return val; |
164 | } |
165 | |
166 | /** |
167 | * mv88e6352_g2_scratch_gpio_set_dir - set direction of gpio pin |
168 | * @chip: chip private data |
169 | * @pin: gpio index |
170 | * @input: should the gpio be an input, or an output? |
171 | */ |
172 | static int mv88e6352_g2_scratch_gpio_set_dir(struct mv88e6xxx_chip *chip, |
173 | unsigned int pin, bool input) |
174 | { |
175 | int value = (input ? MV88E6352_G2_SCRATCH_GPIO_DIR_IN : |
176 | MV88E6352_G2_SCRATCH_GPIO_DIR_OUT); |
177 | |
178 | return mv88e6xxx_g2_scratch_set_bit(chip, |
179 | MV88E6352_G2_SCRATCH_GPIO_DIR0, |
180 | offset: pin, set: value); |
181 | } |
182 | |
183 | /** |
184 | * mv88e6352_g2_scratch_gpio_get_pctl - get pin control setting |
185 | * @chip: chip private data |
186 | * @pin: gpio index |
187 | * @func: function number |
188 | * |
189 | * Note that the function numbers themselves may vary by chipset. |
190 | */ |
191 | static int mv88e6352_g2_scratch_gpio_get_pctl(struct mv88e6xxx_chip *chip, |
192 | unsigned int pin, int *func) |
193 | { |
194 | int reg = MV88E6352_G2_SCRATCH_GPIO_PCTL0 + (pin / 2); |
195 | int offset = (pin & 0x1) ? 4 : 0; |
196 | u8 mask = (0x7 << offset); |
197 | int err; |
198 | u8 val; |
199 | |
200 | err = mv88e6xxx_g2_scratch_read(chip, reg, data: &val); |
201 | if (err) |
202 | return err; |
203 | |
204 | *func = (val & mask) >> offset; |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | /** |
210 | * mv88e6352_g2_scratch_gpio_set_pctl - set pin control setting |
211 | * @chip: chip private data |
212 | * @pin: gpio index |
213 | * @func: function number |
214 | */ |
215 | static int mv88e6352_g2_scratch_gpio_set_pctl(struct mv88e6xxx_chip *chip, |
216 | unsigned int pin, int func) |
217 | { |
218 | int reg = MV88E6352_G2_SCRATCH_GPIO_PCTL0 + (pin / 2); |
219 | int offset = (pin & 0x1) ? 4 : 0; |
220 | u8 mask = (0x7 << offset); |
221 | int err; |
222 | u8 val; |
223 | |
224 | err = mv88e6xxx_g2_scratch_read(chip, reg, data: &val); |
225 | if (err) |
226 | return err; |
227 | |
228 | val = (val & ~mask) | ((func & mask) << offset); |
229 | |
230 | return mv88e6xxx_g2_scratch_write(chip, reg, data: val); |
231 | } |
232 | |
233 | const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = { |
234 | .get_data = mv88e6352_g2_scratch_gpio_get_data, |
235 | .set_data = mv88e6352_g2_scratch_gpio_set_data, |
236 | .get_dir = mv88e6352_g2_scratch_gpio_get_dir, |
237 | .set_dir = mv88e6352_g2_scratch_gpio_set_dir, |
238 | .get_pctl = mv88e6352_g2_scratch_gpio_get_pctl, |
239 | .set_pctl = mv88e6352_g2_scratch_gpio_set_pctl, |
240 | }; |
241 | |
242 | /** |
243 | * mv88e6390_g2_scratch_gpio_set_smi - set gpio muxing for external smi |
244 | * @chip: chip private data |
245 | * @external: set mux for external smi, or free for gpio usage |
246 | * |
247 | * Some mv88e6xxx models have GPIO pins that may be configured as |
248 | * an external SMI interface, or they may be made free for other |
249 | * GPIO uses. |
250 | */ |
251 | int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, |
252 | bool external) |
253 | { |
254 | int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG; |
255 | int config_data1 = MV88E6352_G2_SCRATCH_CONFIG_DATA1; |
256 | int config_data2 = MV88E6352_G2_SCRATCH_CONFIG_DATA2; |
257 | bool no_cpu; |
258 | u8 p0_mode; |
259 | int err; |
260 | u8 val; |
261 | |
262 | err = mv88e6xxx_g2_scratch_read(chip, reg: config_data2, data: &val); |
263 | if (err) |
264 | return err; |
265 | |
266 | p0_mode = val & MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK; |
267 | |
268 | if (p0_mode == 0x01 || p0_mode == 0x02) |
269 | return -EBUSY; |
270 | |
271 | err = mv88e6xxx_g2_scratch_read(chip, reg: config_data1, data: &val); |
272 | if (err) |
273 | return err; |
274 | |
275 | no_cpu = !!(val & MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU); |
276 | |
277 | err = mv88e6xxx_g2_scratch_read(chip, reg: misc_cfg, data: &val); |
278 | if (err) |
279 | return err; |
280 | |
281 | /* NO_CPU being 0 inverts the meaning of the bit */ |
282 | if (!no_cpu) |
283 | external = !external; |
284 | |
285 | if (external) |
286 | val |= MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI; |
287 | else |
288 | val &= ~MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI; |
289 | |
290 | return mv88e6xxx_g2_scratch_write(chip, reg: misc_cfg, data: val); |
291 | } |
292 | |
293 | /** |
294 | * mv88e6393x_g2_scratch_gpio_set_smi - set gpio muxing for external smi |
295 | * @chip: chip private data |
296 | * @external: set mux for external smi, or free for gpio usage |
297 | * |
298 | * MV88E6191X/6193X/6393X GPIO pins 9 and 10 can be configured as an |
299 | * external SMI interface or as regular GPIO-s. |
300 | * |
301 | * They however have a different register layout then the existing |
302 | * function. |
303 | */ |
304 | |
305 | int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, |
306 | bool external) |
307 | { |
308 | int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG; |
309 | int err; |
310 | u8 val; |
311 | |
312 | err = mv88e6xxx_g2_scratch_read(chip, reg: misc_cfg, data: &val); |
313 | if (err) |
314 | return err; |
315 | |
316 | if (external) |
317 | val &= ~MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI; |
318 | else |
319 | val |= MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI; |
320 | |
321 | return mv88e6xxx_g2_scratch_write(chip, reg: misc_cfg, data: val); |
322 | } |
323 | |
324 | /** |
325 | * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes |
326 | * @chip: chip private data |
327 | * @port: port number to check for serdes |
328 | * |
329 | * Indicates whether the port may have a serdes attached according to the |
330 | * pin strapping. Returns negative error number, 0 if the port is not |
331 | * configured to have a serdes, and 1 if the port is configured to have a |
332 | * serdes attached. |
333 | */ |
334 | int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port) |
335 | { |
336 | u8 config3, p; |
337 | int err; |
338 | |
339 | err = mv88e6xxx_g2_scratch_read(chip, MV88E6352_G2_SCRATCH_CONFIG_DATA3, |
340 | data: &config3); |
341 | if (err) |
342 | return err; |
343 | |
344 | if (config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL) |
345 | p = 5; |
346 | else |
347 | p = 4; |
348 | |
349 | return port == p; |
350 | } |
351 | |