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 
1129c13c026SVivien Didelot static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
1139c13c026SVivien Didelot 				       struct mv88e6xxx_atu_entry *entry)
1149c13c026SVivien Didelot {
1159c13c026SVivien Didelot 	u16 data = entry->state & 0xf;
1169c13c026SVivien Didelot 
1179c13c026SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1189c13c026SVivien Didelot 		if (entry->trunk)
1199c13c026SVivien Didelot 			data |= GLOBAL_ATU_DATA_TRUNK;
1209c13c026SVivien Didelot 
1219c13c026SVivien Didelot 		data |= (entry->portv_trunkid & mv88e6xxx_port_mask(chip)) << 4;
1229c13c026SVivien Didelot 	}
1239c13c026SVivien Didelot 
1249c13c026SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
1259c13c026SVivien Didelot }
1269c13c026SVivien Didelot 
1279c13c026SVivien Didelot /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
1289c13c026SVivien Didelot  * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
1299c13c026SVivien Didelot  * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
1309c13c026SVivien Didelot  */
1319c13c026SVivien Didelot 
1329c13c026SVivien Didelot static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip,
1339c13c026SVivien Didelot 				      struct mv88e6xxx_atu_entry *entry)
1349c13c026SVivien Didelot {
1359c13c026SVivien Didelot 	u16 val;
1369c13c026SVivien Didelot 	int i, err;
1379c13c026SVivien Didelot 
1389c13c026SVivien Didelot 	for (i = 0; i < 3; i++) {
1399c13c026SVivien Didelot 		val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1];
1409c13c026SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, val);
1419c13c026SVivien Didelot 		if (err)
1429c13c026SVivien Didelot 			return err;
1439c13c026SVivien Didelot 	}
1449c13c026SVivien Didelot 
1459c13c026SVivien Didelot 	return 0;
1469c13c026SVivien Didelot }
1479c13c026SVivien Didelot 
1489c13c026SVivien Didelot /* Address Translation Unit operations */
1499c13c026SVivien Didelot 
1509c13c026SVivien Didelot int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
1519c13c026SVivien Didelot 			       struct mv88e6xxx_atu_entry *entry)
1529c13c026SVivien Didelot {
1539c13c026SVivien Didelot 	int err;
1549c13c026SVivien Didelot 
1559c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_op_wait(chip);
1569c13c026SVivien Didelot 	if (err)
1579c13c026SVivien Didelot 		return err;
1589c13c026SVivien Didelot 
1599c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_mac_write(chip, entry);
1609c13c026SVivien Didelot 	if (err)
1619c13c026SVivien Didelot 		return err;
1629c13c026SVivien Didelot 
1639c13c026SVivien Didelot 	err = mv88e6xxx_g1_atu_data_write(chip, entry);
1649c13c026SVivien Didelot 	if (err)
1659c13c026SVivien Didelot 		return err;
1669c13c026SVivien Didelot 
1679c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB);
1689c13c026SVivien Didelot }
169