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 "mv88e6xxx.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, GLOBAL_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, GLOBAL_ATU_CONTROL, &val); 31 if (err) 32 return err; 33 34 if (learn2all) 35 val |= GLOBAL_ATU_CONTROL_LEARN2ALL; 36 else 37 val &= ~GLOBAL_ATU_CONTROL_LEARN2ALL; 38 39 return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, 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, GLOBAL_ATU_CONTROL, &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, GLOBAL_ATU_CONTROL, 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, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY); 81 } 82 83 static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) 84 { 85 u16 val; 86 int err; 87 88 /* FID bits are dispatched all around gradually as more are supported */ 89 if (mv88e6xxx_num_databases(chip) > 256) { 90 err = mv88e6xxx_g1_atu_fid_write(chip, fid); 91 if (err) 92 return err; 93 } else { 94 if (mv88e6xxx_num_databases(chip) > 16) { 95 /* ATU DBNum[7:4] are located in ATU Control 15:12 */ 96 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); 97 if (err) 98 return err; 99 100 val = (val & 0x0fff) | ((fid << 8) & 0xf000); 101 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); 102 if (err) 103 return err; 104 } 105 106 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ 107 op |= fid & 0xf; 108 } 109 110 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, op); 111 if (err) 112 return err; 113 114 return mv88e6xxx_g1_atu_op_wait(chip); 115 } 116 117 /* Offset 0x0C: ATU Data Register */ 118 119 static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, 120 struct mv88e6xxx_atu_entry *entry) 121 { 122 u16 val; 123 int err; 124 125 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val); 126 if (err) 127 return err; 128 129 entry->state = val & 0xf; 130 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { 131 entry->trunk = !!(val & GLOBAL_ATU_DATA_TRUNK); 132 entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip); 133 } 134 135 return 0; 136 } 137 138 static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, 139 struct mv88e6xxx_atu_entry *entry) 140 { 141 u16 data = entry->state & 0xf; 142 143 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { 144 if (entry->trunk) 145 data |= GLOBAL_ATU_DATA_TRUNK; 146 147 data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4; 148 } 149 150 return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data); 151 } 152 153 /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1 154 * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3 155 * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5 156 */ 157 158 static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip, 159 struct mv88e6xxx_atu_entry *entry) 160 { 161 u16 val; 162 int i, err; 163 164 for (i = 0; i < 3; i++) { 165 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val); 166 if (err) 167 return err; 168 169 entry->mac[i * 2] = val >> 8; 170 entry->mac[i * 2 + 1] = val & 0xff; 171 } 172 173 return 0; 174 } 175 176 static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip, 177 struct mv88e6xxx_atu_entry *entry) 178 { 179 u16 val; 180 int i, err; 181 182 for (i = 0; i < 3; i++) { 183 val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1]; 184 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, val); 185 if (err) 186 return err; 187 } 188 189 return 0; 190 } 191 192 /* Address Translation Unit operations */ 193 194 int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, 195 struct mv88e6xxx_atu_entry *entry) 196 { 197 int err; 198 199 err = mv88e6xxx_g1_atu_op_wait(chip); 200 if (err) 201 return err; 202 203 /* Write the MAC address to iterate from only once */ 204 if (entry->state == GLOBAL_ATU_DATA_STATE_UNUSED) { 205 err = mv88e6xxx_g1_atu_mac_write(chip, entry); 206 if (err) 207 return err; 208 } 209 210 err = mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB); 211 if (err) 212 return err; 213 214 err = mv88e6xxx_g1_atu_data_read(chip, entry); 215 if (err) 216 return err; 217 218 return mv88e6xxx_g1_atu_mac_read(chip, entry); 219 } 220 221 int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, 222 struct mv88e6xxx_atu_entry *entry) 223 { 224 int err; 225 226 err = mv88e6xxx_g1_atu_op_wait(chip); 227 if (err) 228 return err; 229 230 err = mv88e6xxx_g1_atu_mac_write(chip, entry); 231 if (err) 232 return err; 233 234 err = mv88e6xxx_g1_atu_data_write(chip, entry); 235 if (err) 236 return err; 237 238 return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB); 239 } 240 241 static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid, 242 struct mv88e6xxx_atu_entry *entry, 243 bool all) 244 { 245 u16 op; 246 int err; 247 248 err = mv88e6xxx_g1_atu_op_wait(chip); 249 if (err) 250 return err; 251 252 err = mv88e6xxx_g1_atu_data_write(chip, entry); 253 if (err) 254 return err; 255 256 /* Flush/Move all or non-static entries from all or a given database */ 257 if (all && fid) 258 op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB; 259 else if (fid) 260 op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; 261 else if (all) 262 op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL; 263 else 264 op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; 265 266 return mv88e6xxx_g1_atu_op(chip, fid, op); 267 } 268 269 int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all) 270 { 271 struct mv88e6xxx_atu_entry entry = { 272 .state = 0, /* Null EntryState means Flush */ 273 }; 274 275 return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); 276 } 277 278 static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid, 279 int from_port, int to_port, bool all) 280 { 281 struct mv88e6xxx_atu_entry entry = { 0 }; 282 unsigned long mask; 283 int shift; 284 285 if (!chip->info->atu_move_port_mask) 286 return -EOPNOTSUPP; 287 288 mask = chip->info->atu_move_port_mask; 289 shift = bitmap_weight(&mask, 16); 290 291 entry.state = 0xf, /* Full EntryState means Move */ 292 entry.portvec = from_port & mask; 293 entry.portvec |= (to_port & mask) << shift; 294 295 return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); 296 } 297 298 int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, 299 bool all) 300 { 301 int from_port = port; 302 int to_port = chip->info->atu_move_port_mask; 303 304 return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all); 305 } 306