1 /* 2 * Marvell 88E6xxx Address Translation Unit (ATU) support 3 * 4 * Copyright (c) 2008 Marvell Semiconductor 5 * Copyright (c) 2017 Savoir-faire Linux, Inc. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 */ 12 13 #include "chip.h" 14 #include "global1.h" 15 16 /* Offset 0x01: ATU FID Register */ 17 18 static int mv88e6xxx_g1_atu_fid_write(struct mv88e6xxx_chip *chip, u16 fid) 19 { 20 return mv88e6xxx_g1_write(chip, MV88E6352_G1_ATU_FID, fid & 0xfff); 21 } 22 23 /* Offset 0x0A: ATU Control Register */ 24 25 int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all) 26 { 27 u16 val; 28 int err; 29 30 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val); 31 if (err) 32 return err; 33 34 if (learn2all) 35 val |= MV88E6XXX_G1_ATU_CTL_LEARN2ALL; 36 else 37 val &= ~MV88E6XXX_G1_ATU_CTL_LEARN2ALL; 38 39 return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val); 40 } 41 42 int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, 43 unsigned int msecs) 44 { 45 const unsigned int coeff = chip->info->age_time_coeff; 46 const unsigned int min = 0x01 * coeff; 47 const unsigned int max = 0xff * coeff; 48 u8 age_time; 49 u16 val; 50 int err; 51 52 if (msecs < min || msecs > max) 53 return -ERANGE; 54 55 /* Round to nearest multiple of coeff */ 56 age_time = (msecs + coeff / 2) / coeff; 57 58 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val); 59 if (err) 60 return err; 61 62 /* AgeTime is 11:4 bits */ 63 val &= ~0xff0; 64 val |= age_time << 4; 65 66 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val); 67 if (err) 68 return err; 69 70 dev_dbg(chip->dev, "AgeTime set to 0x%02x (%d ms)\n", age_time, 71 age_time * coeff); 72 73 return 0; 74 } 75 76 /* Offset 0x0B: ATU Operation Register */ 77 78 static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip) 79 { 80 return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_ATU_OP, 81 MV88E6XXX_G1_ATU_OP_BUSY); 82 } 83 84 static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) 85 { 86 u16 val; 87 int err; 88 89 /* FID bits are dispatched all around gradually as more are supported */ 90 if (mv88e6xxx_num_databases(chip) > 256) { 91 err = mv88e6xxx_g1_atu_fid_write(chip, fid); 92 if (err) 93 return err; 94 } else { 95 if (mv88e6xxx_num_databases(chip) > 16) { 96 /* ATU DBNum[7:4] are located in ATU Control 15:12 */ 97 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, 98 &val); 99 if (err) 100 return err; 101 102 val = (val & 0x0fff) | ((fid << 8) & 0xf000); 103 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, 104 val); 105 if (err) 106 return err; 107 } 108 109 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ 110 op |= fid & 0xf; 111 } 112 113 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP, 114 MV88E6XXX_G1_ATU_OP_BUSY | op); 115 if (err) 116 return err; 117 118 return mv88e6xxx_g1_atu_op_wait(chip); 119 } 120 121 /* Offset 0x0C: ATU Data Register */ 122 123 static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, 124 struct mv88e6xxx_atu_entry *entry) 125 { 126 u16 val; 127 int err; 128 129 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &val); 130 if (err) 131 return err; 132 133 entry->state = val & 0xf; 134 if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { 135 entry->trunk = !!(val & MV88E6XXX_G1_ATU_DATA_TRUNK); 136 entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip); 137 } 138 139 return 0; 140 } 141 142 static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, 143 struct mv88e6xxx_atu_entry *entry) 144 { 145 u16 data = entry->state & 0xf; 146 147 if (entry->state != MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { 148 if (entry->trunk) 149 data |= MV88E6XXX_G1_ATU_DATA_TRUNK; 150 151 data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4; 152 } 153 154 return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_DATA, data); 155 } 156 157 /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1 158 * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3 159 * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5 160 */ 161 162 static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip, 163 struct mv88e6xxx_atu_entry *entry) 164 { 165 u16 val; 166 int i, err; 167 168 for (i = 0; i < 3; i++) { 169 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01 + i, &val); 170 if (err) 171 return err; 172 173 entry->mac[i * 2] = val >> 8; 174 entry->mac[i * 2 + 1] = val & 0xff; 175 } 176 177 return 0; 178 } 179 180 static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip, 181 struct mv88e6xxx_atu_entry *entry) 182 { 183 u16 val; 184 int i, err; 185 186 for (i = 0; i < 3; i++) { 187 val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1]; 188 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_MAC01 + i, val); 189 if (err) 190 return err; 191 } 192 193 return 0; 194 } 195 196 /* Address Translation Unit operations */ 197 198 int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, 199 struct mv88e6xxx_atu_entry *entry) 200 { 201 int err; 202 203 err = mv88e6xxx_g1_atu_op_wait(chip); 204 if (err) 205 return err; 206 207 /* Write the MAC address to iterate from only once */ 208 if (entry->state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { 209 err = mv88e6xxx_g1_atu_mac_write(chip, entry); 210 if (err) 211 return err; 212 } 213 214 err = mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_GET_NEXT_DB); 215 if (err) 216 return err; 217 218 err = mv88e6xxx_g1_atu_data_read(chip, entry); 219 if (err) 220 return err; 221 222 return mv88e6xxx_g1_atu_mac_read(chip, entry); 223 } 224 225 int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, 226 struct mv88e6xxx_atu_entry *entry) 227 { 228 int err; 229 230 err = mv88e6xxx_g1_atu_op_wait(chip); 231 if (err) 232 return err; 233 234 err = mv88e6xxx_g1_atu_mac_write(chip, entry); 235 if (err) 236 return err; 237 238 err = mv88e6xxx_g1_atu_data_write(chip, entry); 239 if (err) 240 return err; 241 242 return mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_LOAD_DB); 243 } 244 245 static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid, 246 struct mv88e6xxx_atu_entry *entry, 247 bool all) 248 { 249 u16 op; 250 int err; 251 252 err = mv88e6xxx_g1_atu_op_wait(chip); 253 if (err) 254 return err; 255 256 err = mv88e6xxx_g1_atu_data_write(chip, entry); 257 if (err) 258 return err; 259 260 /* Flush/Move all or non-static entries from all or a given database */ 261 if (all && fid) 262 op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB; 263 else if (fid) 264 op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; 265 else if (all) 266 op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL; 267 else 268 op = MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC; 269 270 return mv88e6xxx_g1_atu_op(chip, fid, op); 271 } 272 273 int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all) 274 { 275 struct mv88e6xxx_atu_entry entry = { 276 .state = 0, /* Null EntryState means Flush */ 277 }; 278 279 return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); 280 } 281 282 static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid, 283 int from_port, int to_port, bool all) 284 { 285 struct mv88e6xxx_atu_entry entry = { 0 }; 286 unsigned long mask; 287 int shift; 288 289 if (!chip->info->atu_move_port_mask) 290 return -EOPNOTSUPP; 291 292 mask = chip->info->atu_move_port_mask; 293 shift = bitmap_weight(&mask, 16); 294 295 entry.state = 0xf, /* Full EntryState means Move */ 296 entry.portvec = from_port & mask; 297 entry.portvec |= (to_port & mask) << shift; 298 299 return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); 300 } 301 302 int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, 303 bool all) 304 { 305 int from_port = port; 306 int to_port = chip->info->atu_move_port_mask; 307 308 return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all); 309 } 310