1a935c052SVivien Didelot /*
2a935c052SVivien Didelot  * Marvell 88E6xxx Switch Global (1) Registers support
3a935c052SVivien Didelot  *
4a935c052SVivien Didelot  * Copyright (c) 2008 Marvell Semiconductor
5a935c052SVivien Didelot  *
6a935c052SVivien Didelot  * Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7a935c052SVivien Didelot  *
8a935c052SVivien Didelot  * This program is free software; you can redistribute it and/or modify
9a935c052SVivien Didelot  * it under the terms of the GNU General Public License as published by
10a935c052SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
11a935c052SVivien Didelot  * (at your option) any later version.
12a935c052SVivien Didelot  */
13a935c052SVivien Didelot 
14a935c052SVivien Didelot #include "mv88e6xxx.h"
15a935c052SVivien Didelot #include "global1.h"
16a935c052SVivien Didelot 
17a935c052SVivien Didelot int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
18a935c052SVivien Didelot {
19a935c052SVivien Didelot 	int addr = chip->info->global1_addr;
20a935c052SVivien Didelot 
21a935c052SVivien Didelot 	return mv88e6xxx_read(chip, addr, reg, val);
22a935c052SVivien Didelot }
23a935c052SVivien Didelot 
24a935c052SVivien Didelot int mv88e6xxx_g1_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
25a935c052SVivien Didelot {
26a935c052SVivien Didelot 	int addr = chip->info->global1_addr;
27a935c052SVivien Didelot 
28a935c052SVivien Didelot 	return mv88e6xxx_write(chip, addr, reg, val);
29a935c052SVivien Didelot }
30a935c052SVivien Didelot 
31a935c052SVivien Didelot int mv88e6xxx_g1_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
32a935c052SVivien Didelot {
33a935c052SVivien Didelot 	return mv88e6xxx_wait(chip, chip->info->global1_addr, reg, mask);
34a935c052SVivien Didelot }
35a605a0feSAndrew Lunn 
3617e708baSVivien Didelot /* Offset 0x00: Switch Global Status Register */
3717e708baSVivien Didelot 
3817e708baSVivien Didelot static int mv88e6185_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
3917e708baSVivien Didelot {
4017e708baSVivien Didelot 	u16 state;
4117e708baSVivien Didelot 	int i, err;
4217e708baSVivien Didelot 
4317e708baSVivien Didelot 	for (i = 0; i < 16; ++i) {
4417e708baSVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
4517e708baSVivien Didelot 		if (err)
4617e708baSVivien Didelot 			return err;
4717e708baSVivien Didelot 
4817e708baSVivien Didelot 		/* Check the value of the PPUState bits 15:14 */
4917e708baSVivien Didelot 		state &= GLOBAL_STATUS_PPU_STATE_MASK;
5017e708baSVivien Didelot 		if (state == GLOBAL_STATUS_PPU_STATE_POLLING)
5117e708baSVivien Didelot 			return 0;
5217e708baSVivien Didelot 
5317e708baSVivien Didelot 		usleep_range(1000, 2000);
5417e708baSVivien Didelot 	}
5517e708baSVivien Didelot 
5617e708baSVivien Didelot 	return -ETIMEDOUT;
5717e708baSVivien Didelot }
5817e708baSVivien Didelot 
5917e708baSVivien Didelot static int mv88e6352_g1_wait_ppu_polling(struct mv88e6xxx_chip *chip)
6017e708baSVivien Didelot {
6117e708baSVivien Didelot 	u16 state;
6217e708baSVivien Didelot 	int i, err;
6317e708baSVivien Didelot 
6417e708baSVivien Didelot 	for (i = 0; i < 16; ++i) {
6517e708baSVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &state);
6617e708baSVivien Didelot 		if (err)
6717e708baSVivien Didelot 			return err;
6817e708baSVivien Didelot 
6917e708baSVivien Didelot 		/* Check the value of the PPUState (or InitState) bit 15 */
7017e708baSVivien Didelot 		if (state & GLOBAL_STATUS_PPU_STATE)
7117e708baSVivien Didelot 			return 0;
7217e708baSVivien Didelot 
7317e708baSVivien Didelot 		usleep_range(1000, 2000);
7417e708baSVivien Didelot 	}
7517e708baSVivien Didelot 
7617e708baSVivien Didelot 	return -ETIMEDOUT;
7717e708baSVivien Didelot }
7817e708baSVivien Didelot 
7917e708baSVivien Didelot static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip)
8017e708baSVivien Didelot {
8117e708baSVivien Didelot 	const unsigned long timeout = jiffies + 1 * HZ;
8217e708baSVivien Didelot 	u16 val;
8317e708baSVivien Didelot 	int err;
8417e708baSVivien Didelot 
8517e708baSVivien Didelot 	/* Wait up to 1 second for the switch to be ready. The InitReady bit 11
8617e708baSVivien Didelot 	 * is set to a one when all units inside the device (ATU, VTU, etc.)
8717e708baSVivien Didelot 	 * have finished their initialization and are ready to accept frames.
8817e708baSVivien Didelot 	 */
8917e708baSVivien Didelot 	while (time_before(jiffies, timeout)) {
9017e708baSVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
9117e708baSVivien Didelot 		if (err)
9217e708baSVivien Didelot 			return err;
9317e708baSVivien Didelot 
9417e708baSVivien Didelot 		if (val & GLOBAL_STATUS_INIT_READY)
9517e708baSVivien Didelot 			break;
9617e708baSVivien Didelot 
9717e708baSVivien Didelot 		usleep_range(1000, 2000);
9817e708baSVivien Didelot 	}
9917e708baSVivien Didelot 
10017e708baSVivien Didelot 	if (time_after(jiffies, timeout))
10117e708baSVivien Didelot 		return -ETIMEDOUT;
10217e708baSVivien Didelot 
10317e708baSVivien Didelot 	return 0;
10417e708baSVivien Didelot }
10517e708baSVivien Didelot 
10617e708baSVivien Didelot /* Offset 0x04: Switch Global Control Register */
10717e708baSVivien Didelot 
10817e708baSVivien Didelot int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip)
10917e708baSVivien Didelot {
11017e708baSVivien Didelot 	u16 val;
11117e708baSVivien Didelot 	int err;
11217e708baSVivien Didelot 
11317e708baSVivien Didelot 	/* Set the SWReset bit 15 along with the PPUEn bit 14, to also restart
11417e708baSVivien Didelot 	 * the PPU, including re-doing PHY detection and initialization
11517e708baSVivien Didelot 	 */
11617e708baSVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
11717e708baSVivien Didelot 	if (err)
11817e708baSVivien Didelot 		return err;
11917e708baSVivien Didelot 
12017e708baSVivien Didelot 	val |= GLOBAL_CONTROL_SW_RESET;
12117e708baSVivien Didelot 	val |= GLOBAL_CONTROL_PPU_ENABLE;
12217e708baSVivien Didelot 
12317e708baSVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
12417e708baSVivien Didelot 	if (err)
12517e708baSVivien Didelot 		return err;
12617e708baSVivien Didelot 
12717e708baSVivien Didelot 	err = mv88e6xxx_g1_wait_init_ready(chip);
12817e708baSVivien Didelot 	if (err)
12917e708baSVivien Didelot 		return err;
13017e708baSVivien Didelot 
13117e708baSVivien Didelot 	return mv88e6185_g1_wait_ppu_polling(chip);
13217e708baSVivien Didelot }
13317e708baSVivien Didelot 
13417e708baSVivien Didelot int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip)
13517e708baSVivien Didelot {
13617e708baSVivien Didelot 	u16 val;
13717e708baSVivien Didelot 	int err;
13817e708baSVivien Didelot 
13917e708baSVivien Didelot 	/* Set the SWReset bit 15 */
14017e708baSVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
14117e708baSVivien Didelot 	if (err)
14217e708baSVivien Didelot 		return err;
14317e708baSVivien Didelot 
14417e708baSVivien Didelot 	val |= GLOBAL_CONTROL_SW_RESET;
14517e708baSVivien Didelot 
14617e708baSVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, val);
14717e708baSVivien Didelot 	if (err)
14817e708baSVivien Didelot 		return err;
14917e708baSVivien Didelot 
15017e708baSVivien Didelot 	err = mv88e6xxx_g1_wait_init_ready(chip);
15117e708baSVivien Didelot 	if (err)
15217e708baSVivien Didelot 		return err;
15317e708baSVivien Didelot 
15417e708baSVivien Didelot 	return mv88e6352_g1_wait_ppu_polling(chip);
15517e708baSVivien Didelot }
15617e708baSVivien Didelot 
15733641994SAndrew Lunn /* Offset 0x1a: Monitor Control */
15833641994SAndrew Lunn /* Offset 0x1a: Monitor & MGMT Control on some devices */
15933641994SAndrew Lunn 
16033641994SAndrew Lunn int mv88e6095_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
16133641994SAndrew Lunn {
16233641994SAndrew Lunn 	u16 reg;
16333641994SAndrew Lunn 	int err;
16433641994SAndrew Lunn 
16533641994SAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, &reg);
16633641994SAndrew Lunn 	if (err)
16733641994SAndrew Lunn 		return err;
16833641994SAndrew Lunn 
16933641994SAndrew Lunn 	reg &= ~(GLOBAL_MONITOR_CONTROL_INGRESS_MASK |
17033641994SAndrew Lunn 		 GLOBAL_MONITOR_CONTROL_EGRESS_MASK);
17133641994SAndrew Lunn 
17233641994SAndrew Lunn 	reg |= port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
17333641994SAndrew Lunn 		port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT;
17433641994SAndrew Lunn 
17533641994SAndrew Lunn 	return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
17633641994SAndrew Lunn }
17733641994SAndrew Lunn 
17833641994SAndrew Lunn /* Older generations also call this the ARP destination. It has been
17933641994SAndrew Lunn  * generalized in more modern devices such that more than ARP can
18033641994SAndrew Lunn  * egress it
18133641994SAndrew Lunn  */
18233641994SAndrew Lunn int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
18333641994SAndrew Lunn {
18433641994SAndrew Lunn 	u16 reg;
18533641994SAndrew Lunn 	int err;
18633641994SAndrew Lunn 
18733641994SAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_MONITOR_CONTROL, &reg);
18833641994SAndrew Lunn 	if (err)
18933641994SAndrew Lunn 		return err;
19033641994SAndrew Lunn 
19133641994SAndrew Lunn 	reg &= ~GLOBAL_MONITOR_CONTROL_ARP_MASK;
19233641994SAndrew Lunn 	reg |= port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
19333641994SAndrew Lunn 
19433641994SAndrew Lunn 	return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
19533641994SAndrew Lunn }
19633641994SAndrew Lunn 
19733641994SAndrew Lunn static int mv88e6390_g1_monitor_write(struct mv88e6xxx_chip *chip,
19833641994SAndrew Lunn 				      u16 pointer, u8 data)
19933641994SAndrew Lunn {
20033641994SAndrew Lunn 	u16 reg;
20133641994SAndrew Lunn 
20233641994SAndrew Lunn 	reg = GLOBAL_MONITOR_CONTROL_UPDATE | pointer | data;
20333641994SAndrew Lunn 
20433641994SAndrew Lunn 	return mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
20533641994SAndrew Lunn }
20633641994SAndrew Lunn 
20733641994SAndrew Lunn int mv88e6390_g1_set_egress_port(struct mv88e6xxx_chip *chip, int port)
20833641994SAndrew Lunn {
20933641994SAndrew Lunn 	int err;
21033641994SAndrew Lunn 
21133641994SAndrew Lunn 	err = mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_INGRESS,
21233641994SAndrew Lunn 					 port);
21333641994SAndrew Lunn 	if (err)
21433641994SAndrew Lunn 		return err;
21533641994SAndrew Lunn 
21633641994SAndrew Lunn 	return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_EGRESS,
21733641994SAndrew Lunn 					  port);
21833641994SAndrew Lunn }
21933641994SAndrew Lunn 
22033641994SAndrew Lunn int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port)
22133641994SAndrew Lunn {
22233641994SAndrew Lunn 	return mv88e6390_g1_monitor_write(chip, GLOBAL_MONITOR_CONTROL_CPU_DEST,
22333641994SAndrew Lunn 					  port);
22433641994SAndrew Lunn }
22533641994SAndrew Lunn 
2266e55f698SAndrew Lunn int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
2276e55f698SAndrew Lunn {
2286e55f698SAndrew Lunn 	int err;
2296e55f698SAndrew Lunn 
2306e55f698SAndrew Lunn 	/* 01:c2:80:00:00:00:00-01:c2:80:00:00:00:07 are Management */
2316e55f698SAndrew Lunn 	err = mv88e6390_g1_monitor_write(
2326e55f698SAndrew Lunn 		chip, GLOBAL_MONITOR_CONTROL_0180C280000000XLO, 0xff);
2336e55f698SAndrew Lunn 	if (err)
2346e55f698SAndrew Lunn 		return err;
2356e55f698SAndrew Lunn 
2366e55f698SAndrew Lunn 	/* 01:c2:80:00:00:00:08-01:c2:80:00:00:00:0f are Management */
2376e55f698SAndrew Lunn 	err = mv88e6390_g1_monitor_write(
2386e55f698SAndrew Lunn 		chip, GLOBAL_MONITOR_CONTROL_0180C280000000XHI, 0xff);
2396e55f698SAndrew Lunn 	if (err)
2406e55f698SAndrew Lunn 		return err;
2416e55f698SAndrew Lunn 
2426e55f698SAndrew Lunn 	/* 01:c2:80:00:00:00:20-01:c2:80:00:00:00:27 are Management */
2436e55f698SAndrew Lunn 	err = mv88e6390_g1_monitor_write(
2446e55f698SAndrew Lunn 		chip, GLOBAL_MONITOR_CONTROL_0180C280000002XLO, 0xff);
2456e55f698SAndrew Lunn 	if (err)
2466e55f698SAndrew Lunn 		return err;
2476e55f698SAndrew Lunn 
2486e55f698SAndrew Lunn 	/* 01:c2:80:00:00:00:28-01:c2:80:00:00:00:2f are Management */
2496e55f698SAndrew Lunn 	return mv88e6390_g1_monitor_write(
2506e55f698SAndrew Lunn 		chip, GLOBAL_MONITOR_CONTROL_0180C280000002XHI, 0xff);
2516e55f698SAndrew Lunn }
2526e55f698SAndrew Lunn 
253de227387SAndrew Lunn /* Offset 0x1c: Global Control 2 */
254de227387SAndrew Lunn 
255de227387SAndrew Lunn int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
256de227387SAndrew Lunn {
257de227387SAndrew Lunn 	u16 val;
258de227387SAndrew Lunn 	int err;
259de227387SAndrew Lunn 
260de227387SAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL_2, &val);
261de227387SAndrew Lunn 	if (err)
262de227387SAndrew Lunn 		return err;
263de227387SAndrew Lunn 
264de227387SAndrew Lunn 	val |= GLOBAL_CONTROL_2_HIST_RX_TX;
265de227387SAndrew Lunn 
266de227387SAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2, val);
267de227387SAndrew Lunn 
268de227387SAndrew Lunn 	return err;
269de227387SAndrew Lunn }
270de227387SAndrew Lunn 
271de227387SAndrew Lunn /* Offset 0x1d: Statistics Operation 2 */
272de227387SAndrew Lunn 
2737f9ef3afSAndrew Lunn int mv88e6xxx_g1_stats_wait(struct mv88e6xxx_chip *chip)
274a605a0feSAndrew Lunn {
275a605a0feSAndrew Lunn 	return mv88e6xxx_g1_wait(chip, GLOBAL_STATS_OP, GLOBAL_STATS_OP_BUSY);
276a605a0feSAndrew Lunn }
277a605a0feSAndrew Lunn 
278a605a0feSAndrew Lunn int mv88e6xxx_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
279a605a0feSAndrew Lunn {
280a605a0feSAndrew Lunn 	int err;
281a605a0feSAndrew Lunn 
282a605a0feSAndrew Lunn 	/* Snapshot the hardware statistics counters for this port. */
283a605a0feSAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
284a605a0feSAndrew Lunn 				 GLOBAL_STATS_OP_CAPTURE_PORT |
285a605a0feSAndrew Lunn 				 GLOBAL_STATS_OP_HIST_RX_TX | port);
286a605a0feSAndrew Lunn 	if (err)
287a605a0feSAndrew Lunn 		return err;
288a605a0feSAndrew Lunn 
289a605a0feSAndrew Lunn 	/* Wait for the snapshotting to complete. */
290a605a0feSAndrew Lunn 	return mv88e6xxx_g1_stats_wait(chip);
291a605a0feSAndrew Lunn }
292a605a0feSAndrew Lunn 
293a605a0feSAndrew Lunn int mv88e6320_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
294a605a0feSAndrew Lunn {
295a605a0feSAndrew Lunn 	port = (port + 1) << 5;
296a605a0feSAndrew Lunn 
297a605a0feSAndrew Lunn 	return mv88e6xxx_g1_stats_snapshot(chip, port);
298a605a0feSAndrew Lunn }
29979523473SAndrew Lunn 
30079523473SAndrew Lunn int mv88e6390_g1_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
30179523473SAndrew Lunn {
30279523473SAndrew Lunn 	int err;
30379523473SAndrew Lunn 
30479523473SAndrew Lunn 	port = (port + 1) << 5;
30579523473SAndrew Lunn 
30679523473SAndrew Lunn 	/* Snapshot the hardware statistics counters for this port. */
30779523473SAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
30879523473SAndrew Lunn 				 GLOBAL_STATS_OP_CAPTURE_PORT | port);
30979523473SAndrew Lunn 	if (err)
31079523473SAndrew Lunn 		return err;
31179523473SAndrew Lunn 
31279523473SAndrew Lunn 	/* Wait for the snapshotting to complete. */
31379523473SAndrew Lunn 	return mv88e6xxx_g1_stats_wait(chip);
31479523473SAndrew Lunn }
3157f9ef3afSAndrew Lunn 
3167f9ef3afSAndrew Lunn void mv88e6xxx_g1_stats_read(struct mv88e6xxx_chip *chip, int stat, u32 *val)
3177f9ef3afSAndrew Lunn {
3187f9ef3afSAndrew Lunn 	u32 value;
3197f9ef3afSAndrew Lunn 	u16 reg;
3207f9ef3afSAndrew Lunn 	int err;
3217f9ef3afSAndrew Lunn 
3227f9ef3afSAndrew Lunn 	*val = 0;
3237f9ef3afSAndrew Lunn 
3247f9ef3afSAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
3257f9ef3afSAndrew Lunn 				 GLOBAL_STATS_OP_READ_CAPTURED | stat);
3267f9ef3afSAndrew Lunn 	if (err)
3277f9ef3afSAndrew Lunn 		return;
3287f9ef3afSAndrew Lunn 
3297f9ef3afSAndrew Lunn 	err = mv88e6xxx_g1_stats_wait(chip);
3307f9ef3afSAndrew Lunn 	if (err)
3317f9ef3afSAndrew Lunn 		return;
3327f9ef3afSAndrew Lunn 
3337f9ef3afSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, &reg);
3347f9ef3afSAndrew Lunn 	if (err)
3357f9ef3afSAndrew Lunn 		return;
3367f9ef3afSAndrew Lunn 
3377f9ef3afSAndrew Lunn 	value = reg << 16;
3387f9ef3afSAndrew Lunn 
3397f9ef3afSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, &reg);
3407f9ef3afSAndrew Lunn 	if (err)
3417f9ef3afSAndrew Lunn 		return;
3427f9ef3afSAndrew Lunn 
3437f9ef3afSAndrew Lunn 	*val = value | reg;
3447f9ef3afSAndrew Lunn }
345