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 */
15static 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				 reg << 8);
23	if (err)
24		return err;
25
26	err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SCRATCH_MISC_MISC, &value);
27	if (err)
28		return err;
29
30	*data = (value & MV88E6XXX_G2_SCRATCH_MISC_DATA_MASK);
31
32	return 0;
33}
34
35static 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 */
51static 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, &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 */
78static 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, &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, 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 */
106static 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					   pin, &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 */
127static 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, 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 */
151static 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					   pin, &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 */
172static 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					    pin, 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 */
191static 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, &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 */
215static 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, &val);
225	if (err)
226		return err;
227
228	val = (val & ~mask) | ((func & mask) << offset);
229
230	return mv88e6xxx_g2_scratch_write(chip, reg, val);
231}
232
233const 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 */
251int 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, config_data2, &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, config_data1, &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, misc_cfg, &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, misc_cfg, 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
305int 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, misc_cfg, &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, misc_cfg, 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 */
334int 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					&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