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 
66720c6343SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
67720c6343SVivien Didelot }
689c13c026SVivien Didelot 
699c13c026SVivien Didelot /* Offset 0x0B: ATU Operation Register */
709c13c026SVivien Didelot 
719c13c026SVivien Didelot static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
729c13c026SVivien Didelot {
739c13c026SVivien Didelot 	return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
749c13c026SVivien Didelot }
759c13c026SVivien Didelot 
769c13c026SVivien Didelot static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
779c13c026SVivien Didelot {
789c13c026SVivien Didelot 	u16 val;
799c13c026SVivien Didelot 	int err;
809c13c026SVivien Didelot 
819c13c026SVivien Didelot 	/* FID bits are dispatched all around gradually as more are supported */
829c13c026SVivien Didelot 	if (mv88e6xxx_num_databases(chip) > 256) {
839c13c026SVivien Didelot 		err = mv88e6xxx_g1_atu_fid_write(chip, fid);
849c13c026SVivien Didelot 		if (err)
859c13c026SVivien Didelot 			return err;
869c13c026SVivien Didelot 	} else {
879c13c026SVivien Didelot 		if (mv88e6xxx_num_databases(chip) > 16) {
889c13c026SVivien Didelot 			/* ATU DBNum[7:4] are located in ATU Control 15:12 */
899c13c026SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
909c13c026SVivien Didelot 			if (err)
919c13c026SVivien Didelot 				return err;
929c13c026SVivien Didelot 
939c13c026SVivien Didelot 			val = (val & 0x0fff) | ((fid << 8) & 0xf000);
949c13c026SVivien Didelot 			err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
959c13c026SVivien Didelot 			if (err)
969c13c026SVivien Didelot 				return err;
979c13c026SVivien Didelot 		}
989c13c026SVivien Didelot 
999c13c026SVivien Didelot 		/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1009c13c026SVivien Didelot 		op |= fid & 0xf;
1019c13c026SVivien Didelot 	}
1029c13c026SVivien Didelot 
1039c13c026SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, op);
1049c13c026SVivien Didelot 	if (err)
1059c13c026SVivien Didelot 		return err;
1069c13c026SVivien Didelot 
1079c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_op_wait(chip);
1089c13c026SVivien Didelot }
1099c13c026SVivien Didelot 
1109c13c026SVivien Didelot /* Offset 0x0C: ATU Data Register */
1119c13c026SVivien Didelot 
112dabc1a96SVivien Didelot static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip,
113dabc1a96SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
114dabc1a96SVivien Didelot {
115dabc1a96SVivien Didelot 	u16 val;
116dabc1a96SVivien Didelot 	int err;
117dabc1a96SVivien Didelot 
118dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
119dabc1a96SVivien Didelot 	if (err)
120dabc1a96SVivien Didelot 		return err;
121dabc1a96SVivien Didelot 
122dabc1a96SVivien Didelot 	entry->state = val & 0xf;
123dabc1a96SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
124dabc1a96SVivien Didelot 		if (val & GLOBAL_ATU_DATA_TRUNK)
125dabc1a96SVivien Didelot 			entry->trunk = true;
126dabc1a96SVivien Didelot 
127dabc1a96SVivien Didelot 		entry->portv_trunkid = (val >> 4) & mv88e6xxx_port_mask(chip);
128dabc1a96SVivien Didelot 	}
129dabc1a96SVivien Didelot 
130dabc1a96SVivien Didelot 	return 0;
131dabc1a96SVivien Didelot }
132dabc1a96SVivien Didelot 
1339c13c026SVivien Didelot static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
1349c13c026SVivien Didelot 				       struct mv88e6xxx_atu_entry *entry)
1359c13c026SVivien Didelot {
1369c13c026SVivien Didelot 	u16 data = entry->state & 0xf;
1379c13c026SVivien Didelot 
1389c13c026SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1399c13c026SVivien Didelot 		if (entry->trunk)
1409c13c026SVivien Didelot 			data |= GLOBAL_ATU_DATA_TRUNK;
1419c13c026SVivien Didelot 
1429c13c026SVivien Didelot 		data |= (entry->portv_trunkid & mv88e6xxx_port_mask(chip)) << 4;
1439c13c026SVivien Didelot 	}
1449c13c026SVivien Didelot 
1459c13c026SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
1469c13c026SVivien Didelot }
1479c13c026SVivien Didelot 
1489c13c026SVivien Didelot /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
1499c13c026SVivien Didelot  * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
1509c13c026SVivien Didelot  * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
1519c13c026SVivien Didelot  */
1529c13c026SVivien Didelot 
153dabc1a96SVivien Didelot static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip,
154dabc1a96SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry)
155dabc1a96SVivien Didelot {
156dabc1a96SVivien Didelot 	u16 val;
157dabc1a96SVivien Didelot 	int i, err;
158dabc1a96SVivien Didelot 
159dabc1a96SVivien Didelot 	for (i = 0; i < 3; i++) {
160dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
161dabc1a96SVivien Didelot 		if (err)
162dabc1a96SVivien Didelot 			return err;
163dabc1a96SVivien Didelot 
164dabc1a96SVivien Didelot 		entry->mac[i * 2] = val >> 8;
165dabc1a96SVivien Didelot 		entry->mac[i * 2 + 1] = val & 0xff;
166dabc1a96SVivien Didelot 	}
167dabc1a96SVivien Didelot 
168dabc1a96SVivien Didelot 	return 0;
169dabc1a96SVivien Didelot }
170dabc1a96SVivien Didelot 
1719c13c026SVivien Didelot static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip,
1729c13c026SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
1739c13c026SVivien Didelot {
1749c13c026SVivien Didelot 	u16 val;
1759c13c026SVivien Didelot 	int i, err;
1769c13c026SVivien Didelot 
1779c13c026SVivien Didelot 	for (i = 0; i < 3; i++) {
1789c13c026SVivien Didelot 		val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1];
1799c13c026SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, val);
1809c13c026SVivien Didelot 		if (err)
1819c13c026SVivien Didelot 			return err;
1829c13c026SVivien Didelot 	}
1839c13c026SVivien Didelot 
1849c13c026SVivien Didelot 	return 0;
1859c13c026SVivien Didelot }
1869c13c026SVivien Didelot 
1879c13c026SVivien Didelot /* Address Translation Unit operations */
1889c13c026SVivien Didelot 
189dabc1a96SVivien Didelot int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
190dabc1a96SVivien Didelot 			     struct mv88e6xxx_atu_entry *entry)
191dabc1a96SVivien Didelot {
192dabc1a96SVivien Didelot 	int err;
193dabc1a96SVivien Didelot 
194dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
195dabc1a96SVivien Didelot 	if (err)
196dabc1a96SVivien Didelot 		return err;
197dabc1a96SVivien Didelot 
198dabc1a96SVivien Didelot 	/* Write the MAC address to iterate from only once */
199dabc1a96SVivien Didelot 	if (entry->state == GLOBAL_ATU_DATA_STATE_UNUSED) {
200dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_atu_mac_write(chip, entry);
201dabc1a96SVivien Didelot 		if (err)
202dabc1a96SVivien Didelot 			return err;
203dabc1a96SVivien Didelot 	}
204dabc1a96SVivien Didelot 
205dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
206dabc1a96SVivien Didelot 	if (err)
207dabc1a96SVivien Didelot 		return err;
208dabc1a96SVivien Didelot 
209dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_data_read(chip, entry);
210dabc1a96SVivien Didelot 	if (err)
211dabc1a96SVivien Didelot 		return err;
212dabc1a96SVivien Didelot 
213dabc1a96SVivien Didelot 	return mv88e6xxx_g1_atu_mac_read(chip, entry);
214dabc1a96SVivien Didelot }
215dabc1a96SVivien Didelot 
2169c13c026SVivien Didelot int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
2179c13c026SVivien Didelot 			       struct mv88e6xxx_atu_entry *entry)
2189c13c026SVivien Didelot {
2199c13c026SVivien Didelot 	int err;
2209c13c026SVivien Didelot 
2219c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
2229c13c026SVivien Didelot 	if (err)
2239c13c026SVivien Didelot 		return err;
2249c13c026SVivien Didelot 
2259c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_mac_write(chip, entry);
2269c13c026SVivien Didelot 	if (err)
2279c13c026SVivien Didelot 		return err;
2289c13c026SVivien Didelot 
2299c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
2309c13c026SVivien Didelot 	if (err)
2319c13c026SVivien Didelot 		return err;
2329c13c026SVivien Didelot 
2339c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB);
2349c13c026SVivien Didelot }
235daefc943SVivien Didelot 
236daefc943SVivien Didelot static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid,
237daefc943SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry,
238daefc943SVivien Didelot 				      bool all)
239daefc943SVivien Didelot {
240daefc943SVivien Didelot 	u16 op;
241daefc943SVivien Didelot 	int err;
242daefc943SVivien Didelot 
243daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
244daefc943SVivien Didelot 	if (err)
245daefc943SVivien Didelot 		return err;
246daefc943SVivien Didelot 
247daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
248daefc943SVivien Didelot 	if (err)
249daefc943SVivien Didelot 		return err;
250daefc943SVivien Didelot 
251daefc943SVivien Didelot 	/* Flush/Move all or non-static entries from all or a given database */
252daefc943SVivien Didelot 	if (all && fid)
253daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB;
254daefc943SVivien Didelot 	else if (fid)
255daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
256daefc943SVivien Didelot 	else if (all)
257daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL;
258daefc943SVivien Didelot 	else
259daefc943SVivien Didelot 		op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
260daefc943SVivien Didelot 
261daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, op);
262daefc943SVivien Didelot }
263daefc943SVivien Didelot 
264daefc943SVivien Didelot int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all)
265daefc943SVivien Didelot {
266daefc943SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
267daefc943SVivien Didelot 		.state = 0, /* Null EntryState means Flush */
268daefc943SVivien Didelot 	};
269daefc943SVivien Didelot 
270daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
271daefc943SVivien Didelot }
272