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 
13720c6343SVivien Didelot #include "mv88e6xxx.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 {
209c13c026SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_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 
30c3a7d4adSVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
31c3a7d4adSVivien Didelot 	if (err)
32c3a7d4adSVivien Didelot 		return err;
33c3a7d4adSVivien Didelot 
34c3a7d4adSVivien Didelot 	if (learn2all)
35c3a7d4adSVivien Didelot 		val |= GLOBAL_ATU_CONTROL_LEARN2ALL;
36c3a7d4adSVivien Didelot 	else
37c3a7d4adSVivien Didelot 		val &= ~GLOBAL_ATU_CONTROL_LEARN2ALL;
38c3a7d4adSVivien Didelot 
39c3a7d4adSVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 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 
58720c6343SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &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 
66bae76dd9SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 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 {
809c13c026SVivien Didelot 	return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
819c13c026SVivien Didelot }
829c13c026SVivien Didelot 
839c13c026SVivien Didelot static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
849c13c026SVivien Didelot {
859c13c026SVivien Didelot 	u16 val;
869c13c026SVivien Didelot 	int err;
879c13c026SVivien Didelot 
889c13c026SVivien Didelot 	/* FID bits are dispatched all around gradually as more are supported */
899c13c026SVivien Didelot 	if (mv88e6xxx_num_databases(chip) > 256) {
909c13c026SVivien Didelot 		err = mv88e6xxx_g1_atu_fid_write(chip, fid);
919c13c026SVivien Didelot 		if (err)
929c13c026SVivien Didelot 			return err;
939c13c026SVivien Didelot 	} else {
949c13c026SVivien Didelot 		if (mv88e6xxx_num_databases(chip) > 16) {
959c13c026SVivien Didelot 			/* ATU DBNum[7:4] are located in ATU Control 15:12 */
969c13c026SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
979c13c026SVivien Didelot 			if (err)
989c13c026SVivien Didelot 				return err;
999c13c026SVivien Didelot 
1009c13c026SVivien Didelot 			val = (val & 0x0fff) | ((fid << 8) & 0xf000);
1019c13c026SVivien Didelot 			err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
1029c13c026SVivien Didelot 			if (err)
1039c13c026SVivien Didelot 				return err;
1049c13c026SVivien Didelot 		}
1059c13c026SVivien Didelot 
1069c13c026SVivien Didelot 		/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1079c13c026SVivien Didelot 		op |= fid & 0xf;
1089c13c026SVivien Didelot 	}
1099c13c026SVivien Didelot 
1109c13c026SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, op);
1119c13c026SVivien Didelot 	if (err)
1129c13c026SVivien Didelot 		return err;
1139c13c026SVivien Didelot 
1149c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_op_wait(chip);
1159c13c026SVivien Didelot }
1169c13c026SVivien Didelot 
1179c13c026SVivien Didelot /* Offset 0x0C: ATU Data Register */
1189c13c026SVivien Didelot 
119dabc1a96SVivien Didelot static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip,
120dabc1a96SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
121dabc1a96SVivien Didelot {
122dabc1a96SVivien Didelot 	u16 val;
123dabc1a96SVivien Didelot 	int err;
124dabc1a96SVivien Didelot 
125dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
126dabc1a96SVivien Didelot 	if (err)
127dabc1a96SVivien Didelot 		return err;
128dabc1a96SVivien Didelot 
129dabc1a96SVivien Didelot 	entry->state = val & 0xf;
130dabc1a96SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
13164014fe6SVivien Didelot 		entry->trunk = !!(val & GLOBAL_ATU_DATA_TRUNK);
13201bd96c8SVivien Didelot 		entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip);
133dabc1a96SVivien Didelot 	}
134dabc1a96SVivien Didelot 
135dabc1a96SVivien Didelot 	return 0;
136dabc1a96SVivien Didelot }
137dabc1a96SVivien Didelot 
1389c13c026SVivien Didelot static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
1399c13c026SVivien Didelot 				       struct mv88e6xxx_atu_entry *entry)
1409c13c026SVivien Didelot {
1419c13c026SVivien Didelot 	u16 data = entry->state & 0xf;
1429c13c026SVivien Didelot 
1439c13c026SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1449c13c026SVivien Didelot 		if (entry->trunk)
1459c13c026SVivien Didelot 			data |= GLOBAL_ATU_DATA_TRUNK;
1469c13c026SVivien Didelot 
14701bd96c8SVivien Didelot 		data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4;
1489c13c026SVivien Didelot 	}
1499c13c026SVivien Didelot 
1509c13c026SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
1519c13c026SVivien Didelot }
1529c13c026SVivien Didelot 
1539c13c026SVivien Didelot /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
1549c13c026SVivien Didelot  * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
1559c13c026SVivien Didelot  * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
1569c13c026SVivien Didelot  */
1579c13c026SVivien Didelot 
158dabc1a96SVivien Didelot static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip,
159dabc1a96SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry)
160dabc1a96SVivien Didelot {
161dabc1a96SVivien Didelot 	u16 val;
162dabc1a96SVivien Didelot 	int i, err;
163dabc1a96SVivien Didelot 
164dabc1a96SVivien Didelot 	for (i = 0; i < 3; i++) {
165dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
166dabc1a96SVivien Didelot 		if (err)
167dabc1a96SVivien Didelot 			return err;
168dabc1a96SVivien Didelot 
169dabc1a96SVivien Didelot 		entry->mac[i * 2] = val >> 8;
170dabc1a96SVivien Didelot 		entry->mac[i * 2 + 1] = val & 0xff;
171dabc1a96SVivien Didelot 	}
172dabc1a96SVivien Didelot 
173dabc1a96SVivien Didelot 	return 0;
174dabc1a96SVivien Didelot }
175dabc1a96SVivien Didelot 
1769c13c026SVivien Didelot static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip,
1779c13c026SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
1789c13c026SVivien Didelot {
1799c13c026SVivien Didelot 	u16 val;
1809c13c026SVivien Didelot 	int i, err;
1819c13c026SVivien Didelot 
1829c13c026SVivien Didelot 	for (i = 0; i < 3; i++) {
1839c13c026SVivien Didelot 		val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1];
1849c13c026SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, val);
1859c13c026SVivien Didelot 		if (err)
1869c13c026SVivien Didelot 			return err;
1879c13c026SVivien Didelot 	}
1889c13c026SVivien Didelot 
1899c13c026SVivien Didelot 	return 0;
1909c13c026SVivien Didelot }
1919c13c026SVivien Didelot 
1929c13c026SVivien Didelot /* Address Translation Unit operations */
1939c13c026SVivien Didelot 
194dabc1a96SVivien Didelot int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
195dabc1a96SVivien Didelot 			     struct mv88e6xxx_atu_entry *entry)
196dabc1a96SVivien Didelot {
197dabc1a96SVivien Didelot 	int err;
198dabc1a96SVivien Didelot 
199dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
200dabc1a96SVivien Didelot 	if (err)
201dabc1a96SVivien Didelot 		return err;
202dabc1a96SVivien Didelot 
203dabc1a96SVivien Didelot 	/* Write the MAC address to iterate from only once */
204dabc1a96SVivien Didelot 	if (entry->state == GLOBAL_ATU_DATA_STATE_UNUSED) {
205dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_atu_mac_write(chip, entry);
206dabc1a96SVivien Didelot 		if (err)
207dabc1a96SVivien Didelot 			return err;
208dabc1a96SVivien Didelot 	}
209dabc1a96SVivien Didelot 
210dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
211dabc1a96SVivien Didelot 	if (err)
212dabc1a96SVivien Didelot 		return err;
213dabc1a96SVivien Didelot 
214dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_data_read(chip, entry);
215dabc1a96SVivien Didelot 	if (err)
216dabc1a96SVivien Didelot 		return err;
217dabc1a96SVivien Didelot 
218dabc1a96SVivien Didelot 	return mv88e6xxx_g1_atu_mac_read(chip, entry);
219dabc1a96SVivien Didelot }
220dabc1a96SVivien Didelot 
2219c13c026SVivien Didelot int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
2229c13c026SVivien Didelot 			       struct mv88e6xxx_atu_entry *entry)
2239c13c026SVivien Didelot {
2249c13c026SVivien Didelot 	int err;
2259c13c026SVivien Didelot 
2269c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
2279c13c026SVivien Didelot 	if (err)
2289c13c026SVivien Didelot 		return err;
2299c13c026SVivien Didelot 
2309c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_mac_write(chip, entry);
2319c13c026SVivien Didelot 	if (err)
2329c13c026SVivien Didelot 		return err;
2339c13c026SVivien Didelot 
2349c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
2359c13c026SVivien Didelot 	if (err)
2369c13c026SVivien Didelot 		return err;
2379c13c026SVivien Didelot 
2389c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB);
2399c13c026SVivien Didelot }
240daefc943SVivien Didelot 
241daefc943SVivien Didelot static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid,
242daefc943SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry,
243daefc943SVivien Didelot 				      bool all)
244daefc943SVivien Didelot {
245daefc943SVivien Didelot 	u16 op;
246daefc943SVivien Didelot 	int err;
247daefc943SVivien Didelot 
248daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
249daefc943SVivien Didelot 	if (err)
250daefc943SVivien Didelot 		return err;
251daefc943SVivien Didelot 
252daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
253daefc943SVivien Didelot 	if (err)
254daefc943SVivien Didelot 		return err;
255daefc943SVivien Didelot 
256daefc943SVivien Didelot 	/* Flush/Move all or non-static entries from all or a given database */
257daefc943SVivien Didelot 	if (all && fid)
258daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB;
259daefc943SVivien Didelot 	else if (fid)
260daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
261daefc943SVivien Didelot 	else if (all)
262daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL;
263daefc943SVivien Didelot 	else
264daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
265daefc943SVivien Didelot 
266daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, op);
267daefc943SVivien Didelot }
268daefc943SVivien Didelot 
269daefc943SVivien Didelot int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all)
270daefc943SVivien Didelot {
271daefc943SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
272daefc943SVivien Didelot 		.state = 0, /* Null EntryState means Flush */
273daefc943SVivien Didelot 	};
274daefc943SVivien Didelot 
275daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
276daefc943SVivien Didelot }
277e606ca36SVivien Didelot 
278e606ca36SVivien Didelot static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
279e606ca36SVivien Didelot 				 int from_port, int to_port, bool all)
280e606ca36SVivien Didelot {
281e606ca36SVivien Didelot 	struct mv88e6xxx_atu_entry entry = { 0 };
282e606ca36SVivien Didelot 	unsigned long mask;
283e606ca36SVivien Didelot 	int shift;
284e606ca36SVivien Didelot 
285e606ca36SVivien Didelot 	if (!chip->info->atu_move_port_mask)
286e606ca36SVivien Didelot 		return -EOPNOTSUPP;
287e606ca36SVivien Didelot 
288e606ca36SVivien Didelot 	mask = chip->info->atu_move_port_mask;
289e606ca36SVivien Didelot 	shift = bitmap_weight(&mask, 16);
290e606ca36SVivien Didelot 
291e606ca36SVivien Didelot 	entry.state = 0xf, /* Full EntryState means Move */
29201bd96c8SVivien Didelot 	entry.portvec = from_port & mask;
29301bd96c8SVivien Didelot 	entry.portvec |= (to_port & mask) << shift;
294e606ca36SVivien Didelot 
295e606ca36SVivien Didelot 	return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
296e606ca36SVivien Didelot }
297e606ca36SVivien Didelot 
298e606ca36SVivien Didelot int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
299e606ca36SVivien Didelot 			    bool all)
300e606ca36SVivien Didelot {
301e606ca36SVivien Didelot 	int from_port = port;
302e606ca36SVivien Didelot 	int to_port = chip->info->atu_move_port_mask;
303e606ca36SVivien Didelot 
304e606ca36SVivien Didelot 	return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all);
305e606ca36SVivien Didelot }
306