1720c6343SVivien Didelot /*
2720c6343SVivien Didelot  * Marvell 88E6xxx Address Translation Unit (ATU) support
3720c6343SVivien Didelot  *
4720c6343SVivien Didelot  * Copyright (c) 2008 Marvell Semiconductor
5720c6343SVivien Didelot  * Copyright (c) 2017 Savoir-faire Linux, Inc.
6720c6343SVivien Didelot  *
7720c6343SVivien Didelot  * This program is free software; you can redistribute it and/or modify
8720c6343SVivien Didelot  * it under the terms of the GNU General Public License as published by
9720c6343SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
10720c6343SVivien Didelot  * (at your option) any later version.
11720c6343SVivien Didelot  */
12720c6343SVivien Didelot 
134d5f2ba7SVivien Didelot #include "chip.h"
14720c6343SVivien Didelot #include "global1.h"
15720c6343SVivien Didelot 
169c13c026SVivien Didelot /* Offset 0x01: ATU FID Register */
179c13c026SVivien Didelot 
189c13c026SVivien Didelot static int mv88e6xxx_g1_atu_fid_write(struct mv88e6xxx_chip *chip, u16 fid)
199c13c026SVivien Didelot {
2027c0e600SVivien Didelot 	return mv88e6xxx_g1_write(chip, MV88E6352_G1_ATU_FID, fid & 0xfff);
219c13c026SVivien Didelot }
229c13c026SVivien Didelot 
23720c6343SVivien Didelot /* Offset 0x0A: ATU Control Register */
24720c6343SVivien Didelot 
25c3a7d4adSVivien Didelot int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all)
26c3a7d4adSVivien Didelot {
27c3a7d4adSVivien Didelot 	u16 val;
28c3a7d4adSVivien Didelot 	int err;
29c3a7d4adSVivien Didelot 
3027c0e600SVivien Didelot 	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val);
31c3a7d4adSVivien Didelot 	if (err)
32c3a7d4adSVivien Didelot 		return err;
33c3a7d4adSVivien Didelot 
34c3a7d4adSVivien Didelot 	if (learn2all)
3527c0e600SVivien Didelot 		val |= MV88E6XXX_G1_ATU_CTL_LEARN2ALL;
36c3a7d4adSVivien Didelot 	else
3727c0e600SVivien Didelot 		val &= ~MV88E6XXX_G1_ATU_CTL_LEARN2ALL;
38c3a7d4adSVivien Didelot 
3927c0e600SVivien Didelot 	return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val);
40c3a7d4adSVivien Didelot }
41c3a7d4adSVivien Didelot 
42720c6343SVivien Didelot int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,
43720c6343SVivien Didelot 				  unsigned int msecs)
44720c6343SVivien Didelot {
45720c6343SVivien Didelot 	const unsigned int coeff = chip->info->age_time_coeff;
46720c6343SVivien Didelot 	const unsigned int min = 0x01 * coeff;
47720c6343SVivien Didelot 	const unsigned int max = 0xff * coeff;
48720c6343SVivien Didelot 	u8 age_time;
49720c6343SVivien Didelot 	u16 val;
50720c6343SVivien Didelot 	int err;
51720c6343SVivien Didelot 
52720c6343SVivien Didelot 	if (msecs < min || msecs > max)
53720c6343SVivien Didelot 		return -ERANGE;
54720c6343SVivien Didelot 
55720c6343SVivien Didelot 	/* Round to nearest multiple of coeff */
56720c6343SVivien Didelot 	age_time = (msecs + coeff / 2) / coeff;
57720c6343SVivien Didelot 
5827c0e600SVivien Didelot 	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val);
59720c6343SVivien Didelot 	if (err)
60720c6343SVivien Didelot 		return err;
61720c6343SVivien Didelot 
62720c6343SVivien Didelot 	/* AgeTime is 11:4 bits */
63720c6343SVivien Didelot 	val &= ~0xff0;
64720c6343SVivien Didelot 	val |= age_time << 4;
65720c6343SVivien Didelot 
6627c0e600SVivien Didelot 	err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val);
67bae76dd9SVivien Didelot 	if (err)
68bae76dd9SVivien Didelot 		return err;
69bae76dd9SVivien Didelot 
70bae76dd9SVivien Didelot 	dev_dbg(chip->dev, "AgeTime set to 0x%02x (%d ms)\n", age_time,
71bae76dd9SVivien Didelot 		age_time * coeff);
72bae76dd9SVivien Didelot 
73bae76dd9SVivien Didelot 	return 0;
74720c6343SVivien Didelot }
759c13c026SVivien Didelot 
769c13c026SVivien Didelot /* Offset 0x0B: ATU Operation Register */
779c13c026SVivien Didelot 
789c13c026SVivien Didelot static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
799c13c026SVivien Didelot {
8027c0e600SVivien Didelot 	return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_ATU_OP,
8127c0e600SVivien Didelot 				 MV88E6XXX_G1_ATU_OP_BUSY);
829c13c026SVivien Didelot }
839c13c026SVivien Didelot 
849c13c026SVivien Didelot static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
859c13c026SVivien Didelot {
869c13c026SVivien Didelot 	u16 val;
879c13c026SVivien Didelot 	int err;
889c13c026SVivien Didelot 
899c13c026SVivien Didelot 	/* FID bits are dispatched all around gradually as more are supported */
909c13c026SVivien Didelot 	if (mv88e6xxx_num_databases(chip) > 256) {
919c13c026SVivien Didelot 		err = mv88e6xxx_g1_atu_fid_write(chip, fid);
929c13c026SVivien Didelot 		if (err)
939c13c026SVivien Didelot 			return err;
949c13c026SVivien Didelot 	} else {
959c13c026SVivien Didelot 		if (mv88e6xxx_num_databases(chip) > 16) {
969c13c026SVivien Didelot 			/* ATU DBNum[7:4] are located in ATU Control 15:12 */
9727c0e600SVivien Didelot 			err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL,
9827c0e600SVivien Didelot 						&val);
999c13c026SVivien Didelot 			if (err)
1009c13c026SVivien Didelot 				return err;
1019c13c026SVivien Didelot 
1029c13c026SVivien Didelot 			val = (val & 0x0fff) | ((fid << 8) & 0xf000);
10327c0e600SVivien Didelot 			err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL,
10427c0e600SVivien Didelot 						 val);
1059c13c026SVivien Didelot 			if (err)
1069c13c026SVivien Didelot 				return err;
1079c13c026SVivien Didelot 		}
1089c13c026SVivien Didelot 
1099c13c026SVivien Didelot 		/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1109c13c026SVivien Didelot 		op |= fid & 0xf;
1119c13c026SVivien Didelot 	}
1129c13c026SVivien Didelot 
11327c0e600SVivien Didelot 	err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP,
11427c0e600SVivien Didelot 				 MV88E6XXX_G1_ATU_OP_BUSY | op);
1159c13c026SVivien Didelot 	if (err)
1169c13c026SVivien Didelot 		return err;
1179c13c026SVivien Didelot 
1189c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_op_wait(chip);
1199c13c026SVivien Didelot }
1209c13c026SVivien Didelot 
1219c13c026SVivien Didelot /* Offset 0x0C: ATU Data Register */
1229c13c026SVivien Didelot 
123dabc1a96SVivien Didelot static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip,
124dabc1a96SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
125dabc1a96SVivien Didelot {
126dabc1a96SVivien Didelot 	u16 val;
127dabc1a96SVivien Didelot 	int err;
128dabc1a96SVivien Didelot 
12927c0e600SVivien Didelot 	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &val);
130dabc1a96SVivien Didelot 	if (err)
131dabc1a96SVivien Didelot 		return err;
132dabc1a96SVivien Didelot 
133dabc1a96SVivien Didelot 	entry->state = val & 0xf;
13427c0e600SVivien Didelot 	if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
13527c0e600SVivien Didelot 		entry->trunk = !!(val & MV88E6XXX_G1_ATU_DATA_TRUNK);
13601bd96c8SVivien Didelot 		entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip);
137dabc1a96SVivien Didelot 	}
138dabc1a96SVivien Didelot 
139dabc1a96SVivien Didelot 	return 0;
140dabc1a96SVivien Didelot }
141dabc1a96SVivien Didelot 
1429c13c026SVivien Didelot static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
1439c13c026SVivien Didelot 				       struct mv88e6xxx_atu_entry *entry)
1449c13c026SVivien Didelot {
1459c13c026SVivien Didelot 	u16 data = entry->state & 0xf;
1469c13c026SVivien Didelot 
14727c0e600SVivien Didelot 	if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
1489c13c026SVivien Didelot 		if (entry->trunk)
14927c0e600SVivien Didelot 			data |= MV88E6XXX_G1_ATU_DATA_TRUNK;
1509c13c026SVivien Didelot 
15101bd96c8SVivien Didelot 		data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4;
1529c13c026SVivien Didelot 	}
1539c13c026SVivien Didelot 
15427c0e600SVivien Didelot 	return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_DATA, data);
1559c13c026SVivien Didelot }
1569c13c026SVivien Didelot 
1579c13c026SVivien Didelot /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
1589c13c026SVivien Didelot  * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
1599c13c026SVivien Didelot  * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
1609c13c026SVivien Didelot  */
1619c13c026SVivien Didelot 
162dabc1a96SVivien Didelot static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip,
163dabc1a96SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry)
164dabc1a96SVivien Didelot {
165dabc1a96SVivien Didelot 	u16 val;
166dabc1a96SVivien Didelot 	int i, err;
167dabc1a96SVivien Didelot 
168dabc1a96SVivien Didelot 	for (i = 0; i < 3; i++) {
16927c0e600SVivien Didelot 		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01 + i, &val);
170dabc1a96SVivien Didelot 		if (err)
171dabc1a96SVivien Didelot 			return err;
172dabc1a96SVivien Didelot 
173dabc1a96SVivien Didelot 		entry->mac[i * 2] = val >> 8;
174dabc1a96SVivien Didelot 		entry->mac[i * 2 + 1] = val & 0xff;
175dabc1a96SVivien Didelot 	}
176dabc1a96SVivien Didelot 
177dabc1a96SVivien Didelot 	return 0;
178dabc1a96SVivien Didelot }
179dabc1a96SVivien Didelot 
1809c13c026SVivien Didelot static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip,
1819c13c026SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
1829c13c026SVivien Didelot {
1839c13c026SVivien Didelot 	u16 val;
1849c13c026SVivien Didelot 	int i, err;
1859c13c026SVivien Didelot 
1869c13c026SVivien Didelot 	for (i = 0; i < 3; i++) {
1879c13c026SVivien Didelot 		val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1];
18827c0e600SVivien Didelot 		err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_MAC01 + i, val);
1899c13c026SVivien Didelot 		if (err)
1909c13c026SVivien Didelot 			return err;
1919c13c026SVivien Didelot 	}
1929c13c026SVivien Didelot 
1939c13c026SVivien Didelot 	return 0;
1949c13c026SVivien Didelot }
1959c13c026SVivien Didelot 
1969c13c026SVivien Didelot /* Address Translation Unit operations */
1979c13c026SVivien Didelot 
198dabc1a96SVivien Didelot int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
199dabc1a96SVivien Didelot 			     struct mv88e6xxx_atu_entry *entry)
200dabc1a96SVivien Didelot {
201dabc1a96SVivien Didelot 	int err;
202dabc1a96SVivien Didelot 
203dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
204dabc1a96SVivien Didelot 	if (err)
205dabc1a96SVivien Didelot 		return err;
206dabc1a96SVivien Didelot 
207dabc1a96SVivien Didelot 	/* Write the MAC address to iterate from only once */
20827c0e600SVivien Didelot 	if (entry->state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) {
209dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_atu_mac_write(chip, entry);
210dabc1a96SVivien Didelot 		if (err)
211dabc1a96SVivien Didelot 			return err;
212dabc1a96SVivien Didelot 	}
213dabc1a96SVivien Didelot 
21427c0e600SVivien Didelot 	err = mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_GET_NEXT_DB);
215dabc1a96SVivien Didelot 	if (err)
216dabc1a96SVivien Didelot 		return err;
217dabc1a96SVivien Didelot 
218dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_data_read(chip, entry);
219dabc1a96SVivien Didelot 	if (err)
220dabc1a96SVivien Didelot 		return err;
221dabc1a96SVivien Didelot 
222dabc1a96SVivien Didelot 	return mv88e6xxx_g1_atu_mac_read(chip, entry);
223dabc1a96SVivien Didelot }
224dabc1a96SVivien Didelot 
2259c13c026SVivien Didelot int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
2269c13c026SVivien Didelot 			       struct mv88e6xxx_atu_entry *entry)
2279c13c026SVivien Didelot {
2289c13c026SVivien Didelot 	int err;
2299c13c026SVivien Didelot 
2309c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
2319c13c026SVivien Didelot 	if (err)
2329c13c026SVivien Didelot 		return err;
2339c13c026SVivien Didelot 
2349c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_mac_write(chip, entry);
2359c13c026SVivien Didelot 	if (err)
2369c13c026SVivien Didelot 		return err;
2379c13c026SVivien Didelot 
2389c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
2399c13c026SVivien Didelot 	if (err)
2409c13c026SVivien Didelot 		return err;
2419c13c026SVivien Didelot 
24227c0e600SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_LOAD_DB);
2439c13c026SVivien Didelot }
244daefc943SVivien Didelot 
245daefc943SVivien Didelot static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid,
246daefc943SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry,
247daefc943SVivien Didelot 				      bool all)
248daefc943SVivien Didelot {
249daefc943SVivien Didelot 	u16 op;
250daefc943SVivien Didelot 	int err;
251daefc943SVivien Didelot 
252daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
253daefc943SVivien Didelot 	if (err)
254daefc943SVivien Didelot 		return err;
255daefc943SVivien Didelot 
256daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
257daefc943SVivien Didelot 	if (err)
258daefc943SVivien Didelot 		return err;
259daefc943SVivien Didelot 
260daefc943SVivien Didelot 	/* Flush/Move all or non-static entries from all or a given database */
261daefc943SVivien Didelot 	if (all && fid)
26227c0e600SVivien Didelot 		op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB;
263daefc943SVivien Didelot 	else if (fid)
26427c0e600SVivien Didelot 		op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
265daefc943SVivien Didelot 	else if (all)
26627c0e600SVivien Didelot 		op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL;
267daefc943SVivien Didelot 	else
26827c0e600SVivien Didelot 		op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC;
269daefc943SVivien Didelot 
270daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, op);
271daefc943SVivien Didelot }
272daefc943SVivien Didelot 
273daefc943SVivien Didelot int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all)
274daefc943SVivien Didelot {
275daefc943SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
276daefc943SVivien Didelot 		.state = 0, /* Null EntryState means Flush */
277daefc943SVivien Didelot 	};
278daefc943SVivien Didelot 
279daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
280daefc943SVivien Didelot }
281e606ca36SVivien Didelot 
282e606ca36SVivien Didelot static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
283e606ca36SVivien Didelot 				 int from_port, int to_port, bool all)
284e606ca36SVivien Didelot {
285e606ca36SVivien Didelot 	struct mv88e6xxx_atu_entry entry = { 0 };
286e606ca36SVivien Didelot 	unsigned long mask;
287e606ca36SVivien Didelot 	int shift;
288e606ca36SVivien Didelot 
289e606ca36SVivien Didelot 	if (!chip->info->atu_move_port_mask)
290e606ca36SVivien Didelot 		return -EOPNOTSUPP;
291e606ca36SVivien Didelot 
292e606ca36SVivien Didelot 	mask = chip->info->atu_move_port_mask;
293e606ca36SVivien Didelot 	shift = bitmap_weight(&mask, 16);
294e606ca36SVivien Didelot 
295e606ca36SVivien Didelot 	entry.state = 0xf, /* Full EntryState means Move */
29601bd96c8SVivien Didelot 	entry.portvec = from_port & mask;
29701bd96c8SVivien Didelot 	entry.portvec |= (to_port & mask) << shift;
298e606ca36SVivien Didelot 
299e606ca36SVivien Didelot 	return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
300e606ca36SVivien Didelot }
301e606ca36SVivien Didelot 
302e606ca36SVivien Didelot int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
303e606ca36SVivien Didelot 			    bool all)
304e606ca36SVivien Didelot {
305e606ca36SVivien Didelot 	int from_port = port;
306e606ca36SVivien Didelot 	int to_port = chip->info->atu_move_port_mask;
307e606ca36SVivien Didelot 
308e606ca36SVivien Didelot 	return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all);
309e606ca36SVivien Didelot }
310