Commit | Line | Data |
---|---|---|
e66f840c TH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Microchip KSZ8795 switch driver | |
4 | * | |
5 | * Copyright (C) 2017 Microchip Technology Inc. | |
6 | * Tristram Ha <Tristram.Ha@microchip.com> | |
7 | */ | |
8 | ||
36838050 | 9 | #include <linux/bitfield.h> |
e66f840c TH |
10 | #include <linux/delay.h> |
11 | #include <linux/export.h> | |
12 | #include <linux/gpio.h> | |
b6459415 | 13 | #include <linux/if_vlan.h> |
e66f840c TH |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> | |
16 | #include <linux/platform_data/microchip-ksz.h> | |
17 | #include <linux/phy.h> | |
18 | #include <linux/etherdevice.h> | |
19 | #include <linux/if_bridge.h> | |
ec4b94f9 | 20 | #include <linux/micrel_phy.h> |
e66f840c TH |
21 | #include <net/dsa.h> |
22 | #include <net/switchdev.h> | |
2c709e0b | 23 | #include <linux/phylink.h> |
e66f840c | 24 | |
e66f840c TH |
25 | #include "ksz_common.h" |
26 | #include "ksz8795_reg.h" | |
9f73e112 MG |
27 | #include "ksz8.h" |
28 | ||
e66f840c TH |
29 | static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) |
30 | { | |
31 | regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); | |
32 | } | |
33 | ||
34 | static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, | |
35 | bool set) | |
36 | { | |
37 | regmap_update_bits(dev->regmap[0], PORT_CTRL_ADDR(port, offset), | |
38 | bits, set ? bits : 0); | |
39 | } | |
40 | ||
7b6e6235 OR |
41 | static int ksz8_ind_write8(struct ksz_device *dev, u8 table, u16 addr, u8 data) |
42 | { | |
a02579df | 43 | const u16 *regs; |
7b6e6235 OR |
44 | u16 ctrl_addr; |
45 | int ret = 0; | |
46 | ||
486f9ca7 AR |
47 | regs = dev->info->regs; |
48 | ||
7b6e6235 OR |
49 | mutex_lock(&dev->alu_mutex); |
50 | ||
51 | ctrl_addr = IND_ACC_TABLE(table) | addr; | |
52 | ret = ksz_write8(dev, regs[REG_IND_BYTE], data); | |
53 | if (!ret) | |
54 | ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); | |
55 | ||
56 | mutex_unlock(&dev->alu_mutex); | |
57 | ||
58 | return ret; | |
59 | } | |
60 | ||
6ec23aaa | 61 | int ksz8_reset_switch(struct ksz_device *dev) |
e66f840c | 62 | { |
4b20a07e OR |
63 | if (ksz_is_ksz88x3(dev)) { |
64 | /* reset switch */ | |
65 | ksz_cfg(dev, KSZ8863_REG_SW_RESET, | |
66 | KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true); | |
67 | ksz_cfg(dev, KSZ8863_REG_SW_RESET, | |
68 | KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false); | |
69 | } else { | |
70 | /* reset switch */ | |
71 | ksz_write8(dev, REG_POWER_MANAGEMENT_1, | |
72 | SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S); | |
73 | ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0); | |
74 | } | |
e66f840c TH |
75 | |
76 | return 0; | |
77 | } | |
78 | ||
79 | static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue) | |
80 | { | |
81 | u8 hi, lo; | |
82 | ||
83 | /* Number of queues can only be 1, 2, or 4. */ | |
84 | switch (queue) { | |
85 | case 4: | |
86 | case 3: | |
87 | queue = PORT_QUEUE_SPLIT_4; | |
88 | break; | |
89 | case 2: | |
90 | queue = PORT_QUEUE_SPLIT_2; | |
91 | break; | |
92 | default: | |
93 | queue = PORT_QUEUE_SPLIT_1; | |
94 | } | |
95 | ksz_pread8(dev, port, REG_PORT_CTRL_0, &lo); | |
96 | ksz_pread8(dev, port, P_DROP_TAG_CTRL, &hi); | |
97 | lo &= ~PORT_QUEUE_SPLIT_L; | |
98 | if (queue & PORT_QUEUE_SPLIT_2) | |
99 | lo |= PORT_QUEUE_SPLIT_L; | |
100 | hi &= ~PORT_QUEUE_SPLIT_H; | |
101 | if (queue & PORT_QUEUE_SPLIT_4) | |
102 | hi |= PORT_QUEUE_SPLIT_H; | |
103 | ksz_pwrite8(dev, port, REG_PORT_CTRL_0, lo); | |
104 | ksz_pwrite8(dev, port, P_DROP_TAG_CTRL, hi); | |
105 | ||
106 | /* Default is port based for egress rate limit. */ | |
107 | if (queue != PORT_QUEUE_SPLIT_1) | |
108 | ksz_cfg(dev, REG_SW_CTRL_19, SW_OUT_RATE_LIMIT_QUEUE_BASED, | |
109 | true); | |
110 | } | |
111 | ||
6ec23aaa | 112 | void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) |
e66f840c | 113 | { |
9f73e112 | 114 | const u32 *masks; |
a02579df | 115 | const u16 *regs; |
e66f840c TH |
116 | u16 ctrl_addr; |
117 | u32 data; | |
118 | u8 check; | |
119 | int loop; | |
120 | ||
d23a5e18 | 121 | masks = dev->info->masks; |
486f9ca7 | 122 | regs = dev->info->regs; |
9f73e112 | 123 | |
a530e6f2 | 124 | ctrl_addr = addr + dev->info->reg_mib_cnt * port; |
e66f840c TH |
125 | ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); |
126 | ||
127 | mutex_lock(&dev->alu_mutex); | |
9f73e112 | 128 | ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); |
e66f840c TH |
129 | |
130 | /* It is almost guaranteed to always read the valid bit because of | |
131 | * slow SPI speed. | |
132 | */ | |
133 | for (loop = 2; loop > 0; loop--) { | |
9f73e112 | 134 | ksz_read8(dev, regs[REG_IND_MIB_CHECK], &check); |
e66f840c | 135 | |
9f73e112 MG |
136 | if (check & masks[MIB_COUNTER_VALID]) { |
137 | ksz_read32(dev, regs[REG_IND_DATA_LO], &data); | |
138 | if (check & masks[MIB_COUNTER_OVERFLOW]) | |
e66f840c TH |
139 | *cnt += MIB_COUNTER_VALUE + 1; |
140 | *cnt += data & MIB_COUNTER_VALUE; | |
141 | break; | |
142 | } | |
143 | } | |
144 | mutex_unlock(&dev->alu_mutex); | |
145 | } | |
146 | ||
4b20a07e OR |
147 | static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, |
148 | u64 *dropped, u64 *cnt) | |
e66f840c | 149 | { |
9f73e112 | 150 | const u32 *masks; |
a02579df | 151 | const u16 *regs; |
e66f840c TH |
152 | u16 ctrl_addr; |
153 | u32 data; | |
154 | u8 check; | |
155 | int loop; | |
156 | ||
d23a5e18 | 157 | masks = dev->info->masks; |
486f9ca7 | 158 | regs = dev->info->regs; |
9f73e112 | 159 | |
a530e6f2 | 160 | addr -= dev->info->reg_mib_cnt; |
4b20a07e OR |
161 | ctrl_addr = (KSZ8795_MIB_TOTAL_RX_1 - KSZ8795_MIB_TOTAL_RX_0) * port; |
162 | ctrl_addr += addr + KSZ8795_MIB_TOTAL_RX_0; | |
e66f840c TH |
163 | ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); |
164 | ||
165 | mutex_lock(&dev->alu_mutex); | |
9f73e112 | 166 | ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); |
e66f840c TH |
167 | |
168 | /* It is almost guaranteed to always read the valid bit because of | |
169 | * slow SPI speed. | |
170 | */ | |
171 | for (loop = 2; loop > 0; loop--) { | |
9f73e112 | 172 | ksz_read8(dev, regs[REG_IND_MIB_CHECK], &check); |
e66f840c | 173 | |
9f73e112 MG |
174 | if (check & masks[MIB_COUNTER_VALID]) { |
175 | ksz_read32(dev, regs[REG_IND_DATA_LO], &data); | |
e66f840c TH |
176 | if (addr < 2) { |
177 | u64 total; | |
178 | ||
179 | total = check & MIB_TOTAL_BYTES_H; | |
180 | total <<= 32; | |
181 | *cnt += total; | |
182 | *cnt += data; | |
9f73e112 | 183 | if (check & masks[MIB_COUNTER_OVERFLOW]) { |
e66f840c TH |
184 | total = MIB_TOTAL_BYTES_H + 1; |
185 | total <<= 32; | |
186 | *cnt += total; | |
187 | } | |
188 | } else { | |
9f73e112 | 189 | if (check & masks[MIB_COUNTER_OVERFLOW]) |
e66f840c TH |
190 | *cnt += MIB_PACKET_DROPPED + 1; |
191 | *cnt += data & MIB_PACKET_DROPPED; | |
192 | } | |
193 | break; | |
194 | } | |
195 | } | |
196 | mutex_unlock(&dev->alu_mutex); | |
197 | } | |
198 | ||
4b20a07e OR |
199 | static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, |
200 | u64 *dropped, u64 *cnt) | |
201 | { | |
4b20a07e | 202 | u32 *last = (u32 *)dropped; |
a02579df | 203 | const u16 *regs; |
4b20a07e OR |
204 | u16 ctrl_addr; |
205 | u32 data; | |
206 | u32 cur; | |
207 | ||
486f9ca7 AR |
208 | regs = dev->info->regs; |
209 | ||
a530e6f2 | 210 | addr -= dev->info->reg_mib_cnt; |
4b20a07e OR |
211 | ctrl_addr = addr ? KSZ8863_MIB_PACKET_DROPPED_TX_0 : |
212 | KSZ8863_MIB_PACKET_DROPPED_RX_0; | |
213 | ctrl_addr += port; | |
214 | ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); | |
215 | ||
216 | mutex_lock(&dev->alu_mutex); | |
217 | ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); | |
218 | ksz_read32(dev, regs[REG_IND_DATA_LO], &data); | |
219 | mutex_unlock(&dev->alu_mutex); | |
220 | ||
221 | data &= MIB_PACKET_DROPPED; | |
222 | cur = last[addr]; | |
223 | if (data != cur) { | |
224 | last[addr] = data; | |
225 | if (data < cur) | |
226 | data += MIB_PACKET_DROPPED + 1; | |
227 | data -= cur; | |
228 | *cnt += data; | |
229 | } | |
230 | } | |
231 | ||
6ec23aaa AR |
232 | void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, |
233 | u64 *dropped, u64 *cnt) | |
4b20a07e OR |
234 | { |
235 | if (ksz_is_ksz88x3(dev)) | |
236 | ksz8863_r_mib_pkt(dev, port, addr, dropped, cnt); | |
237 | else | |
238 | ksz8795_r_mib_pkt(dev, port, addr, dropped, cnt); | |
239 | } | |
240 | ||
6ec23aaa | 241 | void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) |
e66f840c | 242 | { |
4b20a07e OR |
243 | if (ksz_is_ksz88x3(dev)) |
244 | return; | |
245 | ||
e66f840c TH |
246 | /* enable the port for flush/freeze function */ |
247 | if (freeze) | |
248 | ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true); | |
249 | ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FREEZE, freeze); | |
250 | ||
251 | /* disable the port after freeze is done */ | |
252 | if (!freeze) | |
253 | ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false); | |
254 | } | |
255 | ||
6ec23aaa | 256 | void ksz8_port_init_cnt(struct ksz_device *dev, int port) |
e66f840c TH |
257 | { |
258 | struct ksz_port_mib *mib = &dev->ports[port].mib; | |
4b20a07e | 259 | u64 *dropped; |
e66f840c | 260 | |
4b20a07e OR |
261 | if (!ksz_is_ksz88x3(dev)) { |
262 | /* flush all enabled port MIB counters */ | |
263 | ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true); | |
264 | ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true); | |
265 | ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false); | |
266 | } | |
e66f840c TH |
267 | |
268 | mib->cnt_ptr = 0; | |
269 | ||
270 | /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ | |
a530e6f2 | 271 | while (mib->cnt_ptr < dev->info->reg_mib_cnt) { |
e66f840c TH |
272 | dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, |
273 | &mib->counters[mib->cnt_ptr]); | |
274 | ++mib->cnt_ptr; | |
275 | } | |
276 | ||
4b20a07e | 277 | /* last one in storage */ |
a530e6f2 | 278 | dropped = &mib->counters[dev->info->mib_cnt]; |
4b20a07e | 279 | |
e66f840c | 280 | /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ |
a530e6f2 | 281 | while (mib->cnt_ptr < dev->info->mib_cnt) { |
e66f840c | 282 | dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, |
4b20a07e | 283 | dropped, &mib->counters[mib->cnt_ptr]); |
e66f840c TH |
284 | ++mib->cnt_ptr; |
285 | } | |
e66f840c TH |
286 | } |
287 | ||
4b5baca0 | 288 | static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) |
e66f840c | 289 | { |
a02579df | 290 | const u16 *regs; |
e66f840c TH |
291 | u16 ctrl_addr; |
292 | ||
486f9ca7 AR |
293 | regs = dev->info->regs; |
294 | ||
e66f840c TH |
295 | ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr; |
296 | ||
297 | mutex_lock(&dev->alu_mutex); | |
9f73e112 MG |
298 | ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); |
299 | ksz_read64(dev, regs[REG_IND_DATA_HI], data); | |
e66f840c TH |
300 | mutex_unlock(&dev->alu_mutex); |
301 | } | |
302 | ||
4b5baca0 | 303 | static void ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) |
e66f840c | 304 | { |
a02579df | 305 | const u16 *regs; |
e66f840c TH |
306 | u16 ctrl_addr; |
307 | ||
486f9ca7 AR |
308 | regs = dev->info->regs; |
309 | ||
e66f840c TH |
310 | ctrl_addr = IND_ACC_TABLE(table) | addr; |
311 | ||
312 | mutex_lock(&dev->alu_mutex); | |
9f73e112 MG |
313 | ksz_write64(dev, regs[REG_IND_DATA_HI], data); |
314 | ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); | |
e66f840c TH |
315 | mutex_unlock(&dev->alu_mutex); |
316 | } | |
317 | ||
4b5baca0 | 318 | static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data) |
e66f840c TH |
319 | { |
320 | int timeout = 100; | |
9f73e112 | 321 | const u32 *masks; |
a02579df | 322 | const u16 *regs; |
9f73e112 | 323 | |
d23a5e18 | 324 | masks = dev->info->masks; |
486f9ca7 | 325 | regs = dev->info->regs; |
e66f840c TH |
326 | |
327 | do { | |
9f73e112 | 328 | ksz_read8(dev, regs[REG_IND_DATA_CHECK], data); |
e66f840c | 329 | timeout--; |
9f73e112 | 330 | } while ((*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) && timeout); |
e66f840c TH |
331 | |
332 | /* Entry is not ready for accessing. */ | |
9f73e112 | 333 | if (*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) { |
e66f840c TH |
334 | return -EAGAIN; |
335 | /* Entry is ready for accessing. */ | |
336 | } else { | |
9f73e112 | 337 | ksz_read8(dev, regs[REG_IND_DATA_8], data); |
e66f840c TH |
338 | |
339 | /* There is no valid entry in the table. */ | |
9f73e112 | 340 | if (*data & masks[DYNAMIC_MAC_TABLE_MAC_EMPTY]) |
e66f840c TH |
341 | return -ENXIO; |
342 | } | |
343 | return 0; | |
344 | } | |
345 | ||
6ec23aaa AR |
346 | int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, |
347 | u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries) | |
e66f840c TH |
348 | { |
349 | u32 data_hi, data_lo; | |
9f73e112 MG |
350 | const u8 *shifts; |
351 | const u32 *masks; | |
a02579df | 352 | const u16 *regs; |
e66f840c TH |
353 | u16 ctrl_addr; |
354 | u8 data; | |
355 | int rc; | |
356 | ||
34e48383 | 357 | shifts = dev->info->shifts; |
d23a5e18 | 358 | masks = dev->info->masks; |
486f9ca7 | 359 | regs = dev->info->regs; |
9f73e112 | 360 | |
e66f840c TH |
361 | ctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr; |
362 | ||
363 | mutex_lock(&dev->alu_mutex); | |
9f73e112 | 364 | ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); |
e66f840c | 365 | |
4b5baca0 | 366 | rc = ksz8_valid_dyn_entry(dev, &data); |
e66f840c TH |
367 | if (rc == -EAGAIN) { |
368 | if (addr == 0) | |
369 | *entries = 0; | |
370 | } else if (rc == -ENXIO) { | |
371 | *entries = 0; | |
372 | /* At least one valid entry in the table. */ | |
373 | } else { | |
374 | u64 buf = 0; | |
375 | int cnt; | |
376 | ||
9f73e112 | 377 | ksz_read64(dev, regs[REG_IND_DATA_HI], &buf); |
e66f840c TH |
378 | data_hi = (u32)(buf >> 32); |
379 | data_lo = (u32)buf; | |
380 | ||
381 | /* Check out how many valid entry in the table. */ | |
9f73e112 MG |
382 | cnt = data & masks[DYNAMIC_MAC_TABLE_ENTRIES_H]; |
383 | cnt <<= shifts[DYNAMIC_MAC_ENTRIES_H]; | |
384 | cnt |= (data_hi & masks[DYNAMIC_MAC_TABLE_ENTRIES]) >> | |
385 | shifts[DYNAMIC_MAC_ENTRIES]; | |
e66f840c TH |
386 | *entries = cnt + 1; |
387 | ||
9f73e112 MG |
388 | *fid = (data_hi & masks[DYNAMIC_MAC_TABLE_FID]) >> |
389 | shifts[DYNAMIC_MAC_FID]; | |
390 | *src_port = (data_hi & masks[DYNAMIC_MAC_TABLE_SRC_PORT]) >> | |
391 | shifts[DYNAMIC_MAC_SRC_PORT]; | |
392 | *timestamp = (data_hi & masks[DYNAMIC_MAC_TABLE_TIMESTAMP]) >> | |
393 | shifts[DYNAMIC_MAC_TIMESTAMP]; | |
e66f840c TH |
394 | |
395 | mac_addr[5] = (u8)data_lo; | |
396 | mac_addr[4] = (u8)(data_lo >> 8); | |
397 | mac_addr[3] = (u8)(data_lo >> 16); | |
398 | mac_addr[2] = (u8)(data_lo >> 24); | |
399 | ||
400 | mac_addr[1] = (u8)data_hi; | |
401 | mac_addr[0] = (u8)(data_hi >> 8); | |
402 | rc = 0; | |
403 | } | |
404 | mutex_unlock(&dev->alu_mutex); | |
405 | ||
406 | return rc; | |
407 | } | |
408 | ||
6ec23aaa AR |
409 | int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, |
410 | struct alu_struct *alu) | |
e66f840c TH |
411 | { |
412 | u32 data_hi, data_lo; | |
9f73e112 MG |
413 | const u8 *shifts; |
414 | const u32 *masks; | |
e66f840c TH |
415 | u64 data; |
416 | ||
34e48383 | 417 | shifts = dev->info->shifts; |
d23a5e18 | 418 | masks = dev->info->masks; |
9f73e112 | 419 | |
4b5baca0 | 420 | ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data); |
e66f840c TH |
421 | data_hi = data >> 32; |
422 | data_lo = (u32)data; | |
9f73e112 MG |
423 | if (data_hi & (masks[STATIC_MAC_TABLE_VALID] | |
424 | masks[STATIC_MAC_TABLE_OVERRIDE])) { | |
e66f840c TH |
425 | alu->mac[5] = (u8)data_lo; |
426 | alu->mac[4] = (u8)(data_lo >> 8); | |
427 | alu->mac[3] = (u8)(data_lo >> 16); | |
428 | alu->mac[2] = (u8)(data_lo >> 24); | |
429 | alu->mac[1] = (u8)data_hi; | |
430 | alu->mac[0] = (u8)(data_hi >> 8); | |
9f73e112 MG |
431 | alu->port_forward = |
432 | (data_hi & masks[STATIC_MAC_TABLE_FWD_PORTS]) >> | |
433 | shifts[STATIC_MAC_FWD_PORTS]; | |
e66f840c | 434 | alu->is_override = |
9f73e112 | 435 | (data_hi & masks[STATIC_MAC_TABLE_OVERRIDE]) ? 1 : 0; |
e66f840c | 436 | data_hi >>= 1; |
9f73e112 MG |
437 | alu->is_static = true; |
438 | alu->is_use_fid = | |
439 | (data_hi & masks[STATIC_MAC_TABLE_USE_FID]) ? 1 : 0; | |
440 | alu->fid = (data_hi & masks[STATIC_MAC_TABLE_FID]) >> | |
441 | shifts[STATIC_MAC_FID]; | |
e66f840c TH |
442 | return 0; |
443 | } | |
444 | return -ENXIO; | |
445 | } | |
446 | ||
6ec23aaa AR |
447 | void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, |
448 | struct alu_struct *alu) | |
e66f840c TH |
449 | { |
450 | u32 data_hi, data_lo; | |
9f73e112 MG |
451 | const u8 *shifts; |
452 | const u32 *masks; | |
e66f840c TH |
453 | u64 data; |
454 | ||
34e48383 | 455 | shifts = dev->info->shifts; |
d23a5e18 | 456 | masks = dev->info->masks; |
9f73e112 | 457 | |
e66f840c TH |
458 | data_lo = ((u32)alu->mac[2] << 24) | |
459 | ((u32)alu->mac[3] << 16) | | |
460 | ((u32)alu->mac[4] << 8) | alu->mac[5]; | |
461 | data_hi = ((u32)alu->mac[0] << 8) | alu->mac[1]; | |
9f73e112 | 462 | data_hi |= (u32)alu->port_forward << shifts[STATIC_MAC_FWD_PORTS]; |
e66f840c TH |
463 | |
464 | if (alu->is_override) | |
9f73e112 | 465 | data_hi |= masks[STATIC_MAC_TABLE_OVERRIDE]; |
e66f840c | 466 | if (alu->is_use_fid) { |
9f73e112 MG |
467 | data_hi |= masks[STATIC_MAC_TABLE_USE_FID]; |
468 | data_hi |= (u32)alu->fid << shifts[STATIC_MAC_FID]; | |
e66f840c TH |
469 | } |
470 | if (alu->is_static) | |
9f73e112 | 471 | data_hi |= masks[STATIC_MAC_TABLE_VALID]; |
e66f840c | 472 | else |
9f73e112 | 473 | data_hi &= ~masks[STATIC_MAC_TABLE_OVERRIDE]; |
e66f840c TH |
474 | |
475 | data = (u64)data_hi << 32 | data_lo; | |
4b5baca0 | 476 | ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data); |
e66f840c TH |
477 | } |
478 | ||
9f73e112 MG |
479 | static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid, |
480 | u8 *member, u8 *valid) | |
e66f840c | 481 | { |
9f73e112 MG |
482 | const u8 *shifts; |
483 | const u32 *masks; | |
484 | ||
34e48383 | 485 | shifts = dev->info->shifts; |
d23a5e18 | 486 | masks = dev->info->masks; |
9f73e112 MG |
487 | |
488 | *fid = vlan & masks[VLAN_TABLE_FID]; | |
489 | *member = (vlan & masks[VLAN_TABLE_MEMBERSHIP]) >> | |
490 | shifts[VLAN_TABLE_MEMBERSHIP_S]; | |
491 | *valid = !!(vlan & masks[VLAN_TABLE_VALID]); | |
e66f840c TH |
492 | } |
493 | ||
9f73e112 MG |
494 | static void ksz8_to_vlan(struct ksz_device *dev, u8 fid, u8 member, u8 valid, |
495 | u16 *vlan) | |
e66f840c | 496 | { |
9f73e112 MG |
497 | const u8 *shifts; |
498 | const u32 *masks; | |
499 | ||
34e48383 | 500 | shifts = dev->info->shifts; |
d23a5e18 | 501 | masks = dev->info->masks; |
9f73e112 | 502 | |
e66f840c | 503 | *vlan = fid; |
9f73e112 | 504 | *vlan |= (u16)member << shifts[VLAN_TABLE_MEMBERSHIP_S]; |
e66f840c | 505 | if (valid) |
9f73e112 | 506 | *vlan |= masks[VLAN_TABLE_VALID]; |
e66f840c TH |
507 | } |
508 | ||
4b5baca0 | 509 | static void ksz8_r_vlan_entries(struct ksz_device *dev, u16 addr) |
e66f840c | 510 | { |
9f73e112 | 511 | const u8 *shifts; |
e66f840c TH |
512 | u64 data; |
513 | int i; | |
514 | ||
34e48383 | 515 | shifts = dev->info->shifts; |
9f73e112 | 516 | |
4b5baca0 | 517 | ksz8_r_table(dev, TABLE_VLAN, addr, &data); |
411d466d BH |
518 | addr *= 4; |
519 | for (i = 0; i < 4; i++) { | |
e66f840c | 520 | dev->vlan_cache[addr + i].table[0] = (u16)data; |
9f73e112 | 521 | data >>= shifts[VLAN_TABLE]; |
e66f840c TH |
522 | } |
523 | } | |
524 | ||
4b5baca0 | 525 | static void ksz8_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan) |
e66f840c TH |
526 | { |
527 | int index; | |
528 | u16 *data; | |
529 | u16 addr; | |
530 | u64 buf; | |
531 | ||
532 | data = (u16 *)&buf; | |
411d466d | 533 | addr = vid / 4; |
e66f840c | 534 | index = vid & 3; |
4b5baca0 | 535 | ksz8_r_table(dev, TABLE_VLAN, addr, &buf); |
e66f840c TH |
536 | *vlan = data[index]; |
537 | } | |
538 | ||
4b5baca0 | 539 | static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan) |
e66f840c TH |
540 | { |
541 | int index; | |
542 | u16 *data; | |
543 | u16 addr; | |
544 | u64 buf; | |
545 | ||
546 | data = (u16 *)&buf; | |
411d466d | 547 | addr = vid / 4; |
e66f840c | 548 | index = vid & 3; |
4b5baca0 | 549 | ksz8_r_table(dev, TABLE_VLAN, addr, &buf); |
e66f840c TH |
550 | data[index] = vlan; |
551 | dev->vlan_cache[vid].table[0] = vlan; | |
4b5baca0 | 552 | ksz8_w_table(dev, TABLE_VLAN, addr, buf); |
e66f840c TH |
553 | } |
554 | ||
8f420456 | 555 | int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) |
e66f840c TH |
556 | { |
557 | u8 restart, speed, ctrl, link; | |
558 | int processed = true; | |
a02579df | 559 | const u16 *regs; |
36838050 | 560 | u8 val1, val2; |
e66f840c TH |
561 | u16 data = 0; |
562 | u8 p = phy; | |
9590fc4a | 563 | int ret; |
e66f840c | 564 | |
486f9ca7 AR |
565 | regs = dev->info->regs; |
566 | ||
e66f840c | 567 | switch (reg) { |
ec4b94f9 | 568 | case MII_BMCR: |
9590fc4a OR |
569 | ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); |
570 | if (ret) | |
571 | return ret; | |
572 | ||
573 | ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); | |
574 | if (ret) | |
575 | return ret; | |
576 | ||
577 | ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); | |
578 | if (ret) | |
579 | return ret; | |
580 | ||
e66f840c | 581 | if (restart & PORT_PHY_LOOPBACK) |
ec4b94f9 | 582 | data |= BMCR_LOOPBACK; |
e66f840c | 583 | if (ctrl & PORT_FORCE_100_MBIT) |
ec4b94f9 | 584 | data |= BMCR_SPEED100; |
4b20a07e OR |
585 | if (ksz_is_ksz88x3(dev)) { |
586 | if ((ctrl & PORT_AUTO_NEG_ENABLE)) | |
ec4b94f9 | 587 | data |= BMCR_ANENABLE; |
4b20a07e OR |
588 | } else { |
589 | if (!(ctrl & PORT_AUTO_NEG_DISABLE)) | |
ec4b94f9 | 590 | data |= BMCR_ANENABLE; |
4b20a07e | 591 | } |
e66f840c | 592 | if (restart & PORT_POWER_DOWN) |
ec4b94f9 | 593 | data |= BMCR_PDOWN; |
e66f840c | 594 | if (restart & PORT_AUTO_NEG_RESTART) |
ec4b94f9 | 595 | data |= BMCR_ANRESTART; |
e66f840c | 596 | if (ctrl & PORT_FORCE_FULL_DUPLEX) |
ec4b94f9 | 597 | data |= BMCR_FULLDPLX; |
e66f840c | 598 | if (speed & PORT_HP_MDIX) |
ec4b94f9 | 599 | data |= KSZ886X_BMCR_HP_MDIX; |
e66f840c | 600 | if (restart & PORT_FORCE_MDIX) |
ec4b94f9 | 601 | data |= KSZ886X_BMCR_FORCE_MDI; |
e66f840c | 602 | if (restart & PORT_AUTO_MDIX_DISABLE) |
ec4b94f9 | 603 | data |= KSZ886X_BMCR_DISABLE_AUTO_MDIX; |
e66f840c | 604 | if (restart & PORT_TX_DISABLE) |
ec4b94f9 | 605 | data |= KSZ886X_BMCR_DISABLE_TRANSMIT; |
e66f840c | 606 | if (restart & PORT_LED_OFF) |
ec4b94f9 | 607 | data |= KSZ886X_BMCR_DISABLE_LED; |
e66f840c | 608 | break; |
ec4b94f9 | 609 | case MII_BMSR: |
9590fc4a OR |
610 | ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); |
611 | if (ret) | |
612 | return ret; | |
613 | ||
ec4b94f9 MG |
614 | data = BMSR_100FULL | |
615 | BMSR_100HALF | | |
616 | BMSR_10FULL | | |
617 | BMSR_10HALF | | |
618 | BMSR_ANEGCAPABLE; | |
e66f840c | 619 | if (link & PORT_AUTO_NEG_COMPLETE) |
ec4b94f9 | 620 | data |= BMSR_ANEGCOMPLETE; |
e66f840c | 621 | if (link & PORT_STAT_LINK_GOOD) |
ec4b94f9 | 622 | data |= BMSR_LSTATUS; |
e66f840c | 623 | break; |
ec4b94f9 | 624 | case MII_PHYSID1: |
e66f840c TH |
625 | data = KSZ8795_ID_HI; |
626 | break; | |
ec4b94f9 | 627 | case MII_PHYSID2: |
4b20a07e OR |
628 | if (ksz_is_ksz88x3(dev)) |
629 | data = KSZ8863_ID_LO; | |
630 | else | |
631 | data = KSZ8795_ID_LO; | |
e66f840c | 632 | break; |
ec4b94f9 | 633 | case MII_ADVERTISE: |
9590fc4a OR |
634 | ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); |
635 | if (ret) | |
636 | return ret; | |
637 | ||
ec4b94f9 | 638 | data = ADVERTISE_CSMA; |
e66f840c | 639 | if (ctrl & PORT_AUTO_NEG_SYM_PAUSE) |
ec4b94f9 | 640 | data |= ADVERTISE_PAUSE_CAP; |
e66f840c | 641 | if (ctrl & PORT_AUTO_NEG_100BTX_FD) |
ec4b94f9 | 642 | data |= ADVERTISE_100FULL; |
e66f840c | 643 | if (ctrl & PORT_AUTO_NEG_100BTX) |
ec4b94f9 | 644 | data |= ADVERTISE_100HALF; |
e66f840c | 645 | if (ctrl & PORT_AUTO_NEG_10BT_FD) |
ec4b94f9 | 646 | data |= ADVERTISE_10FULL; |
e66f840c | 647 | if (ctrl & PORT_AUTO_NEG_10BT) |
ec4b94f9 | 648 | data |= ADVERTISE_10HALF; |
e66f840c | 649 | break; |
ec4b94f9 | 650 | case MII_LPA: |
9590fc4a OR |
651 | ret = ksz_pread8(dev, p, regs[P_REMOTE_STATUS], &link); |
652 | if (ret) | |
653 | return ret; | |
654 | ||
ec4b94f9 | 655 | data = LPA_SLCT; |
e66f840c | 656 | if (link & PORT_REMOTE_SYM_PAUSE) |
ec4b94f9 | 657 | data |= LPA_PAUSE_CAP; |
e66f840c | 658 | if (link & PORT_REMOTE_100BTX_FD) |
ec4b94f9 | 659 | data |= LPA_100FULL; |
e66f840c | 660 | if (link & PORT_REMOTE_100BTX) |
ec4b94f9 | 661 | data |= LPA_100HALF; |
e66f840c | 662 | if (link & PORT_REMOTE_10BT_FD) |
ec4b94f9 | 663 | data |= LPA_10FULL; |
e66f840c | 664 | if (link & PORT_REMOTE_10BT) |
ec4b94f9 MG |
665 | data |= LPA_10HALF; |
666 | if (data & ~LPA_SLCT) | |
667 | data |= LPA_LPACK; | |
e66f840c | 668 | break; |
36838050 | 669 | case PHY_REG_LINK_MD: |
9590fc4a OR |
670 | ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_CTRL, &val1); |
671 | if (ret) | |
672 | return ret; | |
673 | ||
674 | ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_RESULT, &val2); | |
675 | if (ret) | |
676 | return ret; | |
677 | ||
36838050 OR |
678 | if (val1 & PORT_START_CABLE_DIAG) |
679 | data |= PHY_START_CABLE_DIAG; | |
680 | ||
681 | if (val1 & PORT_CABLE_10M_SHORT) | |
682 | data |= PHY_CABLE_10M_SHORT; | |
683 | ||
684 | data |= FIELD_PREP(PHY_CABLE_DIAG_RESULT_M, | |
685 | FIELD_GET(PORT_CABLE_DIAG_RESULT_M, val1)); | |
686 | ||
687 | data |= FIELD_PREP(PHY_CABLE_FAULT_COUNTER_M, | |
688 | (FIELD_GET(PORT_CABLE_FAULT_COUNTER_H, val1) << 8) | | |
689 | FIELD_GET(PORT_CABLE_FAULT_COUNTER_L, val2)); | |
690 | break; | |
52939393 | 691 | case PHY_REG_PHY_CTRL: |
9590fc4a OR |
692 | ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); |
693 | if (ret) | |
694 | return ret; | |
695 | ||
52939393 OR |
696 | if (link & PORT_MDIX_STATUS) |
697 | data |= KSZ886X_CTRL_MDIX_STAT; | |
698 | break; | |
e66f840c TH |
699 | default: |
700 | processed = false; | |
701 | break; | |
702 | } | |
703 | if (processed) | |
704 | *val = data; | |
8f420456 OR |
705 | |
706 | return 0; | |
e66f840c TH |
707 | } |
708 | ||
8f420456 | 709 | int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) |
e66f840c | 710 | { |
e66f840c | 711 | u8 restart, speed, ctrl, data; |
a02579df | 712 | const u16 *regs; |
9f73e112 | 713 | u8 p = phy; |
9590fc4a | 714 | int ret; |
e66f840c | 715 | |
486f9ca7 AR |
716 | regs = dev->info->regs; |
717 | ||
e66f840c | 718 | switch (reg) { |
ec4b94f9 | 719 | case MII_BMCR: |
e66f840c TH |
720 | |
721 | /* Do not support PHY reset function. */ | |
ec4b94f9 | 722 | if (val & BMCR_RESET) |
e66f840c | 723 | break; |
9590fc4a OR |
724 | ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); |
725 | if (ret) | |
726 | return ret; | |
727 | ||
e66f840c | 728 | data = speed; |
ec4b94f9 | 729 | if (val & KSZ886X_BMCR_HP_MDIX) |
e66f840c TH |
730 | data |= PORT_HP_MDIX; |
731 | else | |
732 | data &= ~PORT_HP_MDIX; | |
9590fc4a OR |
733 | |
734 | if (data != speed) { | |
735 | ret = ksz_pwrite8(dev, p, regs[P_SPEED_STATUS], data); | |
736 | if (ret) | |
737 | return ret; | |
738 | } | |
739 | ||
740 | ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); | |
741 | if (ret) | |
742 | return ret; | |
743 | ||
e66f840c | 744 | data = ctrl; |
4b20a07e | 745 | if (ksz_is_ksz88x3(dev)) { |
ec4b94f9 | 746 | if ((val & BMCR_ANENABLE)) |
4b20a07e OR |
747 | data |= PORT_AUTO_NEG_ENABLE; |
748 | else | |
749 | data &= ~PORT_AUTO_NEG_ENABLE; | |
750 | } else { | |
ec4b94f9 | 751 | if (!(val & BMCR_ANENABLE)) |
4b20a07e OR |
752 | data |= PORT_AUTO_NEG_DISABLE; |
753 | else | |
754 | data &= ~PORT_AUTO_NEG_DISABLE; | |
755 | ||
756 | /* Fiber port does not support auto-negotiation. */ | |
757 | if (dev->ports[p].fiber) | |
758 | data |= PORT_AUTO_NEG_DISABLE; | |
759 | } | |
e66f840c | 760 | |
ec4b94f9 | 761 | if (val & BMCR_SPEED100) |
e66f840c TH |
762 | data |= PORT_FORCE_100_MBIT; |
763 | else | |
764 | data &= ~PORT_FORCE_100_MBIT; | |
ec4b94f9 | 765 | if (val & BMCR_FULLDPLX) |
e66f840c TH |
766 | data |= PORT_FORCE_FULL_DUPLEX; |
767 | else | |
768 | data &= ~PORT_FORCE_FULL_DUPLEX; | |
9590fc4a OR |
769 | |
770 | if (data != ctrl) { | |
771 | ret = ksz_pwrite8(dev, p, regs[P_FORCE_CTRL], data); | |
772 | if (ret) | |
773 | return ret; | |
774 | } | |
775 | ||
776 | ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); | |
777 | if (ret) | |
778 | return ret; | |
779 | ||
e66f840c | 780 | data = restart; |
ec4b94f9 | 781 | if (val & KSZ886X_BMCR_DISABLE_LED) |
e66f840c TH |
782 | data |= PORT_LED_OFF; |
783 | else | |
784 | data &= ~PORT_LED_OFF; | |
ec4b94f9 | 785 | if (val & KSZ886X_BMCR_DISABLE_TRANSMIT) |
e66f840c TH |
786 | data |= PORT_TX_DISABLE; |
787 | else | |
788 | data &= ~PORT_TX_DISABLE; | |
ec4b94f9 | 789 | if (val & BMCR_ANRESTART) |
e66f840c TH |
790 | data |= PORT_AUTO_NEG_RESTART; |
791 | else | |
792 | data &= ~(PORT_AUTO_NEG_RESTART); | |
ec4b94f9 | 793 | if (val & BMCR_PDOWN) |
e66f840c TH |
794 | data |= PORT_POWER_DOWN; |
795 | else | |
796 | data &= ~PORT_POWER_DOWN; | |
ec4b94f9 | 797 | if (val & KSZ886X_BMCR_DISABLE_AUTO_MDIX) |
e66f840c TH |
798 | data |= PORT_AUTO_MDIX_DISABLE; |
799 | else | |
800 | data &= ~PORT_AUTO_MDIX_DISABLE; | |
ec4b94f9 | 801 | if (val & KSZ886X_BMCR_FORCE_MDI) |
e66f840c TH |
802 | data |= PORT_FORCE_MDIX; |
803 | else | |
804 | data &= ~PORT_FORCE_MDIX; | |
ec4b94f9 | 805 | if (val & BMCR_LOOPBACK) |
e66f840c TH |
806 | data |= PORT_PHY_LOOPBACK; |
807 | else | |
808 | data &= ~PORT_PHY_LOOPBACK; | |
9590fc4a OR |
809 | |
810 | if (data != restart) { | |
811 | ret = ksz_pwrite8(dev, p, regs[P_NEG_RESTART_CTRL], | |
812 | data); | |
813 | if (ret) | |
814 | return ret; | |
815 | } | |
e66f840c | 816 | break; |
ec4b94f9 | 817 | case MII_ADVERTISE: |
9590fc4a OR |
818 | ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); |
819 | if (ret) | |
820 | return ret; | |
821 | ||
e66f840c TH |
822 | data = ctrl; |
823 | data &= ~(PORT_AUTO_NEG_SYM_PAUSE | | |
824 | PORT_AUTO_NEG_100BTX_FD | | |
825 | PORT_AUTO_NEG_100BTX | | |
826 | PORT_AUTO_NEG_10BT_FD | | |
827 | PORT_AUTO_NEG_10BT); | |
ec4b94f9 | 828 | if (val & ADVERTISE_PAUSE_CAP) |
e66f840c | 829 | data |= PORT_AUTO_NEG_SYM_PAUSE; |
ec4b94f9 | 830 | if (val & ADVERTISE_100FULL) |
e66f840c | 831 | data |= PORT_AUTO_NEG_100BTX_FD; |
ec4b94f9 | 832 | if (val & ADVERTISE_100HALF) |
e66f840c | 833 | data |= PORT_AUTO_NEG_100BTX; |
ec4b94f9 | 834 | if (val & ADVERTISE_10FULL) |
e66f840c | 835 | data |= PORT_AUTO_NEG_10BT_FD; |
ec4b94f9 | 836 | if (val & ADVERTISE_10HALF) |
e66f840c | 837 | data |= PORT_AUTO_NEG_10BT; |
9590fc4a OR |
838 | |
839 | if (data != ctrl) { | |
840 | ret = ksz_pwrite8(dev, p, regs[P_LOCAL_CTRL], data); | |
841 | if (ret) | |
842 | return ret; | |
843 | } | |
e66f840c | 844 | break; |
36838050 OR |
845 | case PHY_REG_LINK_MD: |
846 | if (val & PHY_START_CABLE_DIAG) | |
847 | ksz_port_cfg(dev, p, REG_PORT_LINK_MD_CTRL, PORT_START_CABLE_DIAG, true); | |
848 | break; | |
e66f840c TH |
849 | default: |
850 | break; | |
851 | } | |
8f420456 OR |
852 | |
853 | return 0; | |
e66f840c TH |
854 | } |
855 | ||
6ec23aaa | 856 | void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) |
e66f840c TH |
857 | { |
858 | u8 data; | |
859 | ||
860 | ksz_pread8(dev, port, P_MIRROR_CTRL, &data); | |
861 | data &= ~PORT_VLAN_MEMBERSHIP; | |
862 | data |= (member & dev->port_mask); | |
863 | ksz_pwrite8(dev, port, P_MIRROR_CTRL, data); | |
e66f840c TH |
864 | } |
865 | ||
6ec23aaa | 866 | void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) |
e66f840c | 867 | { |
241ed719 | 868 | u8 learn[DSA_MAX_PORTS]; |
e66f840c TH |
869 | int first, index, cnt; |
870 | struct ksz_port *p; | |
6877102f AR |
871 | const u16 *regs; |
872 | ||
873 | regs = dev->info->regs; | |
e66f840c | 874 | |
462d5250 | 875 | if ((uint)port < dev->info->port_cnt) { |
e66f840c TH |
876 | first = port; |
877 | cnt = port + 1; | |
878 | } else { | |
879 | /* Flush all ports. */ | |
880 | first = 0; | |
462d5250 | 881 | cnt = dev->info->port_cnt; |
e66f840c TH |
882 | } |
883 | for (index = first; index < cnt; index++) { | |
884 | p = &dev->ports[index]; | |
885 | if (!p->on) | |
886 | continue; | |
6877102f | 887 | ksz_pread8(dev, index, regs[P_STP_CTRL], &learn[index]); |
e66f840c | 888 | if (!(learn[index] & PORT_LEARN_DISABLE)) |
6877102f | 889 | ksz_pwrite8(dev, index, regs[P_STP_CTRL], |
e66f840c TH |
890 | learn[index] | PORT_LEARN_DISABLE); |
891 | } | |
892 | ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true); | |
893 | for (index = first; index < cnt; index++) { | |
894 | p = &dev->ports[index]; | |
895 | if (!p->on) | |
896 | continue; | |
897 | if (!(learn[index] & PORT_LEARN_DISABLE)) | |
6877102f | 898 | ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]); |
e66f840c TH |
899 | } |
900 | } | |
901 | ||
6ec23aaa AR |
902 | int ksz8_fdb_dump(struct ksz_device *dev, int port, |
903 | dsa_fdb_dump_cb_t *cb, void *data) | |
e587be75 AR |
904 | { |
905 | int ret = 0; | |
906 | u16 i = 0; | |
907 | u16 entries = 0; | |
908 | u8 timestamp = 0; | |
909 | u8 fid; | |
910 | u8 member; | |
911 | struct alu_struct alu; | |
912 | ||
913 | do { | |
914 | alu.is_static = false; | |
915 | ret = ksz8_r_dyn_mac_table(dev, i, alu.mac, &fid, &member, | |
916 | ×tamp, &entries); | |
917 | if (!ret && (member & BIT(port))) { | |
918 | ret = cb(alu.mac, alu.fid, alu.is_static, data); | |
919 | if (ret) | |
920 | break; | |
921 | } | |
922 | i++; | |
923 | } while (i < entries); | |
924 | if (i >= entries) | |
925 | ret = 0; | |
926 | ||
927 | return ret; | |
928 | } | |
929 | ||
6ec23aaa AR |
930 | int ksz8_mdb_add(struct ksz_device *dev, int port, |
931 | const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) | |
980c7d17 AR |
932 | { |
933 | struct alu_struct alu; | |
934 | int index; | |
935 | int empty = 0; | |
936 | ||
937 | alu.port_forward = 0; | |
938 | for (index = 0; index < dev->info->num_statics; index++) { | |
e587be75 | 939 | if (!ksz8_r_sta_mac_table(dev, index, &alu)) { |
980c7d17 AR |
940 | /* Found one already in static MAC table. */ |
941 | if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && | |
942 | alu.fid == mdb->vid) | |
943 | break; | |
944 | /* Remember the first empty entry. */ | |
945 | } else if (!empty) { | |
946 | empty = index + 1; | |
947 | } | |
948 | } | |
949 | ||
950 | /* no available entry */ | |
951 | if (index == dev->info->num_statics && !empty) | |
952 | return -ENOSPC; | |
953 | ||
954 | /* add entry */ | |
955 | if (index == dev->info->num_statics) { | |
956 | index = empty - 1; | |
957 | memset(&alu, 0, sizeof(alu)); | |
958 | memcpy(alu.mac, mdb->addr, ETH_ALEN); | |
959 | alu.is_static = true; | |
960 | } | |
961 | alu.port_forward |= BIT(port); | |
962 | if (mdb->vid) { | |
963 | alu.is_use_fid = true; | |
964 | ||
965 | /* Need a way to map VID to FID. */ | |
966 | alu.fid = mdb->vid; | |
967 | } | |
e587be75 | 968 | ksz8_w_sta_mac_table(dev, index, &alu); |
980c7d17 AR |
969 | |
970 | return 0; | |
971 | } | |
972 | ||
6ec23aaa AR |
973 | int ksz8_mdb_del(struct ksz_device *dev, int port, |
974 | const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) | |
980c7d17 AR |
975 | { |
976 | struct alu_struct alu; | |
977 | int index; | |
978 | ||
979 | for (index = 0; index < dev->info->num_statics; index++) { | |
e587be75 | 980 | if (!ksz8_r_sta_mac_table(dev, index, &alu)) { |
980c7d17 AR |
981 | /* Found one already in static MAC table. */ |
982 | if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && | |
983 | alu.fid == mdb->vid) | |
984 | break; | |
985 | } | |
986 | } | |
987 | ||
988 | /* no available entry */ | |
989 | if (index == dev->info->num_statics) | |
990 | goto exit; | |
991 | ||
992 | /* clear port */ | |
993 | alu.port_forward &= ~BIT(port); | |
994 | if (!alu.port_forward) | |
995 | alu.is_static = false; | |
e587be75 | 996 | ksz8_w_sta_mac_table(dev, index, &alu); |
980c7d17 AR |
997 | |
998 | exit: | |
999 | return 0; | |
1000 | } | |
1001 | ||
6ec23aaa AR |
1002 | int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, |
1003 | struct netlink_ext_ack *extack) | |
e66f840c | 1004 | { |
4b20a07e OR |
1005 | if (ksz_is_ksz88x3(dev)) |
1006 | return -ENOTSUPP; | |
1007 | ||
16484413 | 1008 | /* Discard packets with VID not enabled on the switch */ |
e66f840c TH |
1009 | ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag); |
1010 | ||
16484413 BH |
1011 | /* Discard packets with VID not enabled on the ingress port */ |
1012 | for (port = 0; port < dev->phy_port_cnt; ++port) | |
1013 | ksz_port_cfg(dev, port, REG_PORT_CTRL_2, PORT_INGRESS_FILTER, | |
1014 | flag); | |
1015 | ||
e66f840c TH |
1016 | return 0; |
1017 | } | |
1018 | ||
ef3b02a1 BH |
1019 | static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state) |
1020 | { | |
1021 | if (ksz_is_ksz88x3(dev)) { | |
1022 | ksz_cfg(dev, REG_SW_INSERT_SRC_PVID, | |
1023 | 0x03 << (4 - 2 * port), state); | |
1024 | } else { | |
1025 | ksz_pwrite8(dev, port, REG_PORT_CTRL_12, state ? 0x0f : 0x00); | |
1026 | } | |
1027 | } | |
1028 | ||
6ec23aaa AR |
1029 | int ksz8_port_vlan_add(struct ksz_device *dev, int port, |
1030 | const struct switchdev_obj_port_vlan *vlan, | |
1031 | struct netlink_ext_ack *extack) | |
e66f840c TH |
1032 | { |
1033 | bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; | |
8f4f58f8 | 1034 | struct ksz_port *p = &dev->ports[port]; |
b7a9e0da | 1035 | u16 data, new_pvid = 0; |
e66f840c TH |
1036 | u8 fid, member, valid; |
1037 | ||
4b20a07e OR |
1038 | if (ksz_is_ksz88x3(dev)) |
1039 | return -ENOTSUPP; | |
1040 | ||
8f4f58f8 BH |
1041 | /* If a VLAN is added with untagged flag different from the |
1042 | * port's Remove Tag flag, we need to change the latter. | |
1043 | * Ignore VID 0, which is always untagged. | |
9130c2d3 | 1044 | * Ignore CPU port, which will always be tagged. |
8f4f58f8 | 1045 | */ |
9130c2d3 BH |
1046 | if (untagged != p->remove_tag && vlan->vid != 0 && |
1047 | port != dev->cpu_port) { | |
8f4f58f8 BH |
1048 | unsigned int vid; |
1049 | ||
1050 | /* Reject attempts to add a VLAN that requires the | |
1051 | * Remove Tag flag to be changed, unless there are no | |
1052 | * other VLANs currently configured. | |
1053 | */ | |
462d5250 | 1054 | for (vid = 1; vid < dev->info->num_vlans; ++vid) { |
8f4f58f8 BH |
1055 | /* Skip the VID we are going to add or reconfigure */ |
1056 | if (vid == vlan->vid) | |
1057 | continue; | |
1058 | ||
1059 | ksz8_from_vlan(dev, dev->vlan_cache[vid].table[0], | |
1060 | &fid, &member, &valid); | |
1061 | if (valid && (member & BIT(port))) | |
1062 | return -EINVAL; | |
1063 | } | |
1064 | ||
1065 | ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged); | |
1066 | p->remove_tag = untagged; | |
1067 | } | |
e66f840c | 1068 | |
4b5baca0 | 1069 | ksz8_r_vlan_table(dev, vlan->vid, &data); |
9f73e112 | 1070 | ksz8_from_vlan(dev, data, &fid, &member, &valid); |
e66f840c | 1071 | |
b7a9e0da VO |
1072 | /* First time to setup the VLAN entry. */ |
1073 | if (!valid) { | |
1074 | /* Need to find a way to map VID to FID. */ | |
1075 | fid = 1; | |
1076 | valid = 1; | |
1077 | } | |
1078 | member |= BIT(port); | |
e66f840c | 1079 | |
9f73e112 | 1080 | ksz8_to_vlan(dev, fid, member, valid, &data); |
4b5baca0 | 1081 | ksz8_w_vlan_table(dev, vlan->vid, data); |
e66f840c | 1082 | |
b7a9e0da VO |
1083 | /* change PVID */ |
1084 | if (vlan->flags & BRIDGE_VLAN_INFO_PVID) | |
1085 | new_pvid = vlan->vid; | |
e66f840c TH |
1086 | |
1087 | if (new_pvid) { | |
b7a9e0da VO |
1088 | u16 vid; |
1089 | ||
e66f840c | 1090 | ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid); |
ef3b02a1 | 1091 | vid &= ~VLAN_VID_MASK; |
e66f840c TH |
1092 | vid |= new_pvid; |
1093 | ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid); | |
ef3b02a1 BH |
1094 | |
1095 | ksz8_port_enable_pvid(dev, port, true); | |
e66f840c | 1096 | } |
1958d581 VO |
1097 | |
1098 | return 0; | |
e66f840c TH |
1099 | } |
1100 | ||
6ec23aaa AR |
1101 | int ksz8_port_vlan_del(struct ksz_device *dev, int port, |
1102 | const struct switchdev_obj_port_vlan *vlan) | |
e66f840c | 1103 | { |
ef3b02a1 | 1104 | u16 data, pvid; |
e66f840c TH |
1105 | u8 fid, member, valid; |
1106 | ||
4b20a07e OR |
1107 | if (ksz_is_ksz88x3(dev)) |
1108 | return -ENOTSUPP; | |
1109 | ||
e66f840c TH |
1110 | ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid); |
1111 | pvid = pvid & 0xFFF; | |
1112 | ||
4b5baca0 | 1113 | ksz8_r_vlan_table(dev, vlan->vid, &data); |
9f73e112 | 1114 | ksz8_from_vlan(dev, data, &fid, &member, &valid); |
e66f840c | 1115 | |
b7a9e0da | 1116 | member &= ~BIT(port); |
e66f840c | 1117 | |
b7a9e0da VO |
1118 | /* Invalidate the entry if no more member. */ |
1119 | if (!member) { | |
1120 | fid = 0; | |
1121 | valid = 0; | |
1122 | } | |
e66f840c | 1123 | |
9f73e112 | 1124 | ksz8_to_vlan(dev, fid, member, valid, &data); |
4b5baca0 | 1125 | ksz8_w_vlan_table(dev, vlan->vid, data); |
e66f840c | 1126 | |
ef3b02a1 BH |
1127 | if (pvid == vlan->vid) |
1128 | ksz8_port_enable_pvid(dev, port, false); | |
e66f840c TH |
1129 | |
1130 | return 0; | |
1131 | } | |
1132 | ||
6ec23aaa AR |
1133 | int ksz8_port_mirror_add(struct ksz_device *dev, int port, |
1134 | struct dsa_mall_mirror_tc_entry *mirror, | |
1135 | bool ingress, struct netlink_ext_ack *extack) | |
e66f840c | 1136 | { |
e66f840c TH |
1137 | if (ingress) { |
1138 | ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true); | |
1139 | dev->mirror_rx |= BIT(port); | |
1140 | } else { | |
1141 | ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true); | |
1142 | dev->mirror_tx |= BIT(port); | |
1143 | } | |
1144 | ||
1145 | ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false); | |
1146 | ||
1147 | /* configure mirror port */ | |
1148 | if (dev->mirror_rx || dev->mirror_tx) | |
1149 | ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, | |
1150 | PORT_MIRROR_SNIFFER, true); | |
1151 | ||
1152 | return 0; | |
1153 | } | |
1154 | ||
6ec23aaa AR |
1155 | void ksz8_port_mirror_del(struct ksz_device *dev, int port, |
1156 | struct dsa_mall_mirror_tc_entry *mirror) | |
e66f840c | 1157 | { |
e66f840c TH |
1158 | u8 data; |
1159 | ||
1160 | if (mirror->ingress) { | |
1161 | ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false); | |
1162 | dev->mirror_rx &= ~BIT(port); | |
1163 | } else { | |
1164 | ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false); | |
1165 | dev->mirror_tx &= ~BIT(port); | |
1166 | } | |
1167 | ||
1168 | ksz_pread8(dev, port, P_MIRROR_CTRL, &data); | |
1169 | ||
1170 | if (!dev->mirror_rx && !dev->mirror_tx) | |
1171 | ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, | |
1172 | PORT_MIRROR_SNIFFER, false); | |
1173 | } | |
1174 | ||
c2ac4d2a MG |
1175 | static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) |
1176 | { | |
1177 | struct ksz_port *p = &dev->ports[port]; | |
c2ac4d2a MG |
1178 | |
1179 | if (!p->interface && dev->compat_interface) { | |
1180 | dev_warn(dev->dev, | |
1181 | "Using legacy switch \"phy-mode\" property, because it is missing on port %d node. " | |
1182 | "Please update your device tree.\n", | |
1183 | port); | |
1184 | p->interface = dev->compat_interface; | |
1185 | } | |
c2ac4d2a MG |
1186 | } |
1187 | ||
6ec23aaa | 1188 | void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) |
e66f840c | 1189 | { |
b3612ccd | 1190 | struct dsa_switch *ds = dev->ds; |
9f73e112 | 1191 | const u32 *masks; |
c2ac4d2a | 1192 | u8 member; |
e66f840c | 1193 | |
d23a5e18 | 1194 | masks = dev->info->masks; |
9f73e112 | 1195 | |
e66f840c TH |
1196 | /* enable broadcast storm limit */ |
1197 | ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true); | |
1198 | ||
4b20a07e OR |
1199 | if (!ksz_is_ksz88x3(dev)) |
1200 | ksz8795_set_prio_queue(dev, port, 4); | |
e66f840c TH |
1201 | |
1202 | /* disable DiffServ priority */ | |
1203 | ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false); | |
1204 | ||
1205 | /* replace priority */ | |
9f73e112 MG |
1206 | ksz_port_cfg(dev, port, P_802_1P_CTRL, |
1207 | masks[PORT_802_1P_REMAPPING], false); | |
e66f840c TH |
1208 | |
1209 | /* enable 802.1p priority */ | |
1210 | ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true); | |
1211 | ||
1212 | if (cpu_port) { | |
4b20a07e OR |
1213 | if (!ksz_is_ksz88x3(dev)) |
1214 | ksz8795_cpu_interface_select(dev, port); | |
e66f840c | 1215 | |
b3612ccd | 1216 | member = dsa_user_ports(ds); |
e66f840c | 1217 | } else { |
b3612ccd | 1218 | member = BIT(dsa_upstream_port(ds, port)); |
e66f840c | 1219 | } |
b3612ccd | 1220 | |
4b5baca0 | 1221 | ksz8_cfg_port_member(dev, port, member); |
e66f840c TH |
1222 | } |
1223 | ||
6ec23aaa | 1224 | void ksz8_config_cpu_port(struct dsa_switch *ds) |
e66f840c TH |
1225 | { |
1226 | struct ksz_device *dev = ds->priv; | |
1227 | struct ksz_port *p; | |
9f73e112 | 1228 | const u32 *masks; |
a02579df | 1229 | const u16 *regs; |
e66f840c TH |
1230 | u8 remote; |
1231 | int i; | |
1232 | ||
d23a5e18 | 1233 | masks = dev->info->masks; |
486f9ca7 | 1234 | regs = dev->info->regs; |
9f73e112 | 1235 | |
e66f840c TH |
1236 | /* Switch marks the maximum frame with extra byte as oversize. */ |
1237 | ksz_cfg(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, true); | |
9f73e112 | 1238 | ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true); |
e66f840c TH |
1239 | |
1240 | p = &dev->ports[dev->cpu_port]; | |
e66f840c TH |
1241 | p->on = 1; |
1242 | ||
4b5baca0 | 1243 | ksz8_port_setup(dev, dev->cpu_port, true); |
e66f840c | 1244 | |
4ce2a984 | 1245 | for (i = 0; i < dev->phy_port_cnt; i++) { |
e66f840c TH |
1246 | p = &dev->ports[i]; |
1247 | ||
e593df51 | 1248 | ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED); |
e66f840c TH |
1249 | |
1250 | /* Last port may be disabled. */ | |
4ce2a984 | 1251 | if (i == dev->phy_port_cnt) |
e66f840c TH |
1252 | break; |
1253 | p->on = 1; | |
1254 | p->phy = 1; | |
1255 | } | |
1256 | for (i = 0; i < dev->phy_port_cnt; i++) { | |
1257 | p = &dev->ports[i]; | |
1258 | if (!p->on) | |
1259 | continue; | |
4b20a07e OR |
1260 | if (!ksz_is_ksz88x3(dev)) { |
1261 | ksz_pread8(dev, i, regs[P_REMOTE_STATUS], &remote); | |
91a98917 | 1262 | if (remote & KSZ8_PORT_FIBER_MODE) |
4b20a07e OR |
1263 | p->fiber = 1; |
1264 | } | |
e66f840c | 1265 | if (p->fiber) |
6877102f AR |
1266 | ksz_port_cfg(dev, i, regs[P_STP_CTRL], |
1267 | PORT_FORCE_FLOW_CTRL, true); | |
e66f840c | 1268 | else |
6877102f AR |
1269 | ksz_port_cfg(dev, i, regs[P_STP_CTRL], |
1270 | PORT_FORCE_FLOW_CTRL, false); | |
e66f840c TH |
1271 | } |
1272 | } | |
1273 | ||
7b6e6235 OR |
1274 | static int ksz8_handle_global_errata(struct dsa_switch *ds) |
1275 | { | |
1276 | struct ksz_device *dev = ds->priv; | |
1277 | int ret = 0; | |
1278 | ||
1279 | /* KSZ87xx Errata DS80000687C. | |
1280 | * Module 2: Link drops with some EEE link partners. | |
1281 | * An issue with the EEE next page exchange between the | |
1282 | * KSZ879x/KSZ877x/KSZ876x and some EEE link partners may result in | |
1283 | * the link dropping. | |
1284 | */ | |
462d5250 | 1285 | if (dev->info->ksz87xx_eee_link_erratum) |
7b6e6235 OR |
1286 | ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0); |
1287 | ||
1288 | return ret; | |
1289 | } | |
1290 | ||
6ec23aaa | 1291 | int ksz8_enable_stp_addr(struct ksz_device *dev) |
331d64f7 AR |
1292 | { |
1293 | struct alu_struct alu; | |
1294 | ||
1295 | /* Setup STP address for STP operation. */ | |
1296 | memset(&alu, 0, sizeof(alu)); | |
1297 | ether_addr_copy(alu.mac, eth_stp_addr); | |
1298 | alu.is_static = true; | |
1299 | alu.is_override = true; | |
1300 | alu.port_forward = dev->info->cpu_ports; | |
1301 | ||
1302 | ksz8_w_sta_mac_table(dev, 0, &alu); | |
1303 | ||
1304 | return 0; | |
1305 | } | |
1306 | ||
6ec23aaa | 1307 | int ksz8_setup(struct dsa_switch *ds) |
e66f840c TH |
1308 | { |
1309 | struct ksz_device *dev = ds->priv; | |
d2822e68 | 1310 | int i; |
e66f840c TH |
1311 | |
1312 | ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true); | |
1313 | ||
1314 | /* Enable automatic fast aging when link changed detected. */ | |
1315 | ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true); | |
1316 | ||
1317 | /* Enable aggressive back off algorithm in half duplex mode. */ | |
1318 | regmap_update_bits(dev->regmap[0], REG_SW_CTRL_1, | |
1319 | SW_AGGR_BACKOFF, SW_AGGR_BACKOFF); | |
1320 | ||
1321 | /* | |
1322 | * Make sure unicast VLAN boundary is set as default and | |
1323 | * enable no excessive collision drop. | |
1324 | */ | |
1325 | regmap_update_bits(dev->regmap[0], REG_SW_CTRL_2, | |
1326 | UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP, | |
1327 | UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP); | |
1328 | ||
e66f840c TH |
1329 | ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false); |
1330 | ||
1331 | ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false); | |
1332 | ||
ef3b02a1 BH |
1333 | if (!ksz_is_ksz88x3(dev)) |
1334 | ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true); | |
1335 | ||
462d5250 | 1336 | for (i = 0; i < (dev->info->num_vlans / 4); i++) |
4b5baca0 | 1337 | ksz8_r_vlan_entries(dev, i); |
e66f840c | 1338 | |
7b6e6235 | 1339 | return ksz8_handle_global_errata(ds); |
e66f840c TH |
1340 | } |
1341 | ||
6ec23aaa AR |
1342 | void ksz8_get_caps(struct ksz_device *dev, int port, |
1343 | struct phylink_config *config) | |
2c709e0b | 1344 | { |
82fdbb91 | 1345 | config->mac_capabilities = MAC_10 | MAC_100; |
2c709e0b MG |
1346 | |
1347 | /* Silicon Errata Sheet (DS80000830A): | |
1348 | * "Port 1 does not respond to received flow control PAUSE frames" | |
1349 | * So, disable Pause support on "Port 1" (port == 0) for all ksz88x3 | |
1350 | * switches. | |
1351 | */ | |
1352 | if (!ksz_is_ksz88x3(dev) || port) | |
82fdbb91 | 1353 | config->mac_capabilities |= MAC_SYM_PAUSE; |
2c709e0b MG |
1354 | |
1355 | /* Asym pause is not supported on KSZ8863 and KSZ8873 */ | |
1356 | if (!ksz_is_ksz88x3(dev)) | |
82fdbb91 | 1357 | config->mac_capabilities |= MAC_ASYM_PAUSE; |
2c709e0b MG |
1358 | } |
1359 | ||
6ec23aaa | 1360 | u32 ksz8_get_port_addr(int port, int offset) |
e66f840c TH |
1361 | { |
1362 | return PORT_CTRL_ADDR(port, offset); | |
1363 | } | |
1364 | ||
6ec23aaa | 1365 | int ksz8_switch_init(struct ksz_device *dev) |
e66f840c | 1366 | { |
462d5250 | 1367 | dev->cpu_port = fls(dev->info->cpu_ports) - 1; |
462d5250 AR |
1368 | dev->phy_port_cnt = dev->info->port_cnt - 1; |
1369 | dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | dev->info->cpu_ports; | |
e66f840c | 1370 | |
9130c2d3 BH |
1371 | /* We rely on software untagging on the CPU port, so that we |
1372 | * can support both tagged and untagged VLANs | |
1373 | */ | |
1374 | dev->ds->untag_bridge_pvid = true; | |
16484413 BH |
1375 | |
1376 | /* VLAN filtering is partly controlled by the global VLAN | |
1377 | * Enable flag | |
1378 | */ | |
1379 | dev->ds->vlan_filtering_is_global = true; | |
9130c2d3 | 1380 | |
e66f840c TH |
1381 | return 0; |
1382 | } | |
1383 | ||
6ec23aaa | 1384 | void ksz8_switch_exit(struct ksz_device *dev) |
e66f840c | 1385 | { |
4b5baca0 | 1386 | ksz8_reset_switch(dev); |
e66f840c TH |
1387 | } |
1388 | ||
e66f840c TH |
1389 | MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>"); |
1390 | MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver"); | |
1391 | MODULE_LICENSE("GPL"); |