1e66f840cSTristram Ha // SPDX-License-Identifier: GPL-2.0
2e66f840cSTristram Ha /*
3e66f840cSTristram Ha * Microchip KSZ8795 switch driver
4e66f840cSTristram Ha *
5e66f840cSTristram Ha * Copyright (C) 2017 Microchip Technology Inc.
6e66f840cSTristram Ha * Tristram Ha <Tristram.Ha@microchip.com>
7e66f840cSTristram Ha */
8e66f840cSTristram Ha
936838050SOleksij Rempel #include <linux/bitfield.h>
10e66f840cSTristram Ha #include <linux/delay.h>
11e66f840cSTristram Ha #include <linux/export.h>
12e66f840cSTristram Ha #include <linux/gpio.h>
13b6459415SJakub Kicinski #include <linux/if_vlan.h>
14e66f840cSTristram Ha #include <linux/kernel.h>
15e66f840cSTristram Ha #include <linux/module.h>
16e66f840cSTristram Ha #include <linux/platform_data/microchip-ksz.h>
17e66f840cSTristram Ha #include <linux/phy.h>
18e66f840cSTristram Ha #include <linux/etherdevice.h>
19e66f840cSTristram Ha #include <linux/if_bridge.h>
20ec4b94f9SMichael Grzeschik #include <linux/micrel_phy.h>
21e66f840cSTristram Ha #include <net/dsa.h>
22e66f840cSTristram Ha #include <net/switchdev.h>
232c709e0bSMichael Grzeschik #include <linux/phylink.h>
24e66f840cSTristram Ha
25e66f840cSTristram Ha #include "ksz_common.h"
26e66f840cSTristram Ha #include "ksz8795_reg.h"
279f73e112SMichael Grzeschik #include "ksz8.h"
289f73e112SMichael Grzeschik
ksz_cfg(struct ksz_device * dev,u32 addr,u8 bits,bool set)29e66f840cSTristram Ha static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
30e66f840cSTristram Ha {
31b8311f46SVladimir Oltean regmap_update_bits(ksz_regmap_8(dev), addr, bits, set ? bits : 0);
32e66f840cSTristram Ha }
33e66f840cSTristram Ha
ksz_port_cfg(struct ksz_device * dev,int port,int offset,u8 bits,bool set)34e66f840cSTristram Ha static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
35e66f840cSTristram Ha bool set)
36e66f840cSTristram Ha {
37b8311f46SVladimir Oltean regmap_update_bits(ksz_regmap_8(dev), PORT_CTRL_ADDR(port, offset),
38e66f840cSTristram Ha bits, set ? bits : 0);
39e66f840cSTristram Ha }
40e66f840cSTristram Ha
ksz8_ind_write8(struct ksz_device * dev,u8 table,u16 addr,u8 data)417b6e6235SOleksij Rempel static int ksz8_ind_write8(struct ksz_device *dev, u8 table, u16 addr, u8 data)
427b6e6235SOleksij Rempel {
43a02579dfSArun Ramadoss const u16 *regs;
447b6e6235SOleksij Rempel u16 ctrl_addr;
457b6e6235SOleksij Rempel int ret = 0;
467b6e6235SOleksij Rempel
47486f9ca7SArun Ramadoss regs = dev->info->regs;
48486f9ca7SArun Ramadoss
497b6e6235SOleksij Rempel mutex_lock(&dev->alu_mutex);
507b6e6235SOleksij Rempel
517b6e6235SOleksij Rempel ctrl_addr = IND_ACC_TABLE(table) | addr;
527b6e6235SOleksij Rempel ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
53*782c5fa6STobias Jakobi (Compleo) if (!ret)
54*782c5fa6STobias Jakobi (Compleo) ret = ksz_write8(dev, regs[REG_IND_BYTE], data);
557b6e6235SOleksij Rempel
567b6e6235SOleksij Rempel mutex_unlock(&dev->alu_mutex);
577b6e6235SOleksij Rempel
587b6e6235SOleksij Rempel return ret;
597b6e6235SOleksij Rempel }
607b6e6235SOleksij Rempel
ksz8_reset_switch(struct ksz_device * dev)616ec23aaaSArun Ramadoss int ksz8_reset_switch(struct ksz_device *dev)
62e66f840cSTristram Ha {
634b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev)) {
644b20a07eSOleksij Rempel /* reset switch */
654b20a07eSOleksij Rempel ksz_cfg(dev, KSZ8863_REG_SW_RESET,
664b20a07eSOleksij Rempel KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
674b20a07eSOleksij Rempel ksz_cfg(dev, KSZ8863_REG_SW_RESET,
684b20a07eSOleksij Rempel KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
694b20a07eSOleksij Rempel } else {
70e66f840cSTristram Ha /* reset switch */
71e66f840cSTristram Ha ksz_write8(dev, REG_POWER_MANAGEMENT_1,
72e66f840cSTristram Ha SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S);
73e66f840cSTristram Ha ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0);
744b20a07eSOleksij Rempel }
75e66f840cSTristram Ha
76e66f840cSTristram Ha return 0;
77e66f840cSTristram Ha }
78e66f840cSTristram Ha
ksz8863_change_mtu(struct ksz_device * dev,int frame_size)7929d1e85fSOleksij Rempel static int ksz8863_change_mtu(struct ksz_device *dev, int frame_size)
8029d1e85fSOleksij Rempel {
8129d1e85fSOleksij Rempel u8 ctrl2 = 0;
8229d1e85fSOleksij Rempel
8329d1e85fSOleksij Rempel if (frame_size <= KSZ8_LEGAL_PACKET_SIZE)
8429d1e85fSOleksij Rempel ctrl2 |= KSZ8863_LEGAL_PACKET_ENABLE;
8529d1e85fSOleksij Rempel else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE)
8629d1e85fSOleksij Rempel ctrl2 |= KSZ8863_HUGE_PACKET_ENABLE;
8729d1e85fSOleksij Rempel
8829d1e85fSOleksij Rempel return ksz_rmw8(dev, REG_SW_CTRL_2, KSZ8863_LEGAL_PACKET_ENABLE |
8929d1e85fSOleksij Rempel KSZ8863_HUGE_PACKET_ENABLE, ctrl2);
9029d1e85fSOleksij Rempel }
9129d1e85fSOleksij Rempel
ksz8795_change_mtu(struct ksz_device * dev,int frame_size)9229d1e85fSOleksij Rempel static int ksz8795_change_mtu(struct ksz_device *dev, int frame_size)
9329d1e85fSOleksij Rempel {
9429d1e85fSOleksij Rempel u8 ctrl1 = 0, ctrl2 = 0;
9529d1e85fSOleksij Rempel int ret;
9629d1e85fSOleksij Rempel
9729d1e85fSOleksij Rempel if (frame_size > KSZ8_LEGAL_PACKET_SIZE)
9829d1e85fSOleksij Rempel ctrl2 |= SW_LEGAL_PACKET_DISABLE;
993d2f8f1fSChristophe JAILLET if (frame_size > KSZ8863_NORMAL_PACKET_SIZE)
10029d1e85fSOleksij Rempel ctrl1 |= SW_HUGE_PACKET;
10129d1e85fSOleksij Rempel
10229d1e85fSOleksij Rempel ret = ksz_rmw8(dev, REG_SW_CTRL_1, SW_HUGE_PACKET, ctrl1);
10329d1e85fSOleksij Rempel if (ret)
10429d1e85fSOleksij Rempel return ret;
10529d1e85fSOleksij Rempel
10629d1e85fSOleksij Rempel return ksz_rmw8(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, ctrl2);
10729d1e85fSOleksij Rempel }
10829d1e85fSOleksij Rempel
ksz8_change_mtu(struct ksz_device * dev,int port,int mtu)10929d1e85fSOleksij Rempel int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu)
11029d1e85fSOleksij Rempel {
11129d1e85fSOleksij Rempel u16 frame_size;
11229d1e85fSOleksij Rempel
11329d1e85fSOleksij Rempel if (!dsa_is_cpu_port(dev->ds, port))
11429d1e85fSOleksij Rempel return 0;
11529d1e85fSOleksij Rempel
11629d1e85fSOleksij Rempel frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
11729d1e85fSOleksij Rempel
11829d1e85fSOleksij Rempel switch (dev->chip_id) {
11929d1e85fSOleksij Rempel case KSZ8795_CHIP_ID:
12029d1e85fSOleksij Rempel case KSZ8794_CHIP_ID:
12129d1e85fSOleksij Rempel case KSZ8765_CHIP_ID:
12229d1e85fSOleksij Rempel return ksz8795_change_mtu(dev, frame_size);
12329d1e85fSOleksij Rempel case KSZ8830_CHIP_ID:
12429d1e85fSOleksij Rempel return ksz8863_change_mtu(dev, frame_size);
12529d1e85fSOleksij Rempel }
12629d1e85fSOleksij Rempel
12729d1e85fSOleksij Rempel return -EOPNOTSUPP;
12829d1e85fSOleksij Rempel }
12929d1e85fSOleksij Rempel
ksz8795_set_prio_queue(struct ksz_device * dev,int port,int queue)130e66f840cSTristram Ha static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue)
131e66f840cSTristram Ha {
132e66f840cSTristram Ha u8 hi, lo;
133e66f840cSTristram Ha
134e66f840cSTristram Ha /* Number of queues can only be 1, 2, or 4. */
135e66f840cSTristram Ha switch (queue) {
136e66f840cSTristram Ha case 4:
137e66f840cSTristram Ha case 3:
138e66f840cSTristram Ha queue = PORT_QUEUE_SPLIT_4;
139e66f840cSTristram Ha break;
140e66f840cSTristram Ha case 2:
141e66f840cSTristram Ha queue = PORT_QUEUE_SPLIT_2;
142e66f840cSTristram Ha break;
143e66f840cSTristram Ha default:
144e66f840cSTristram Ha queue = PORT_QUEUE_SPLIT_1;
145e66f840cSTristram Ha }
146e66f840cSTristram Ha ksz_pread8(dev, port, REG_PORT_CTRL_0, &lo);
147e66f840cSTristram Ha ksz_pread8(dev, port, P_DROP_TAG_CTRL, &hi);
148e66f840cSTristram Ha lo &= ~PORT_QUEUE_SPLIT_L;
149e66f840cSTristram Ha if (queue & PORT_QUEUE_SPLIT_2)
150e66f840cSTristram Ha lo |= PORT_QUEUE_SPLIT_L;
151e66f840cSTristram Ha hi &= ~PORT_QUEUE_SPLIT_H;
152e66f840cSTristram Ha if (queue & PORT_QUEUE_SPLIT_4)
153e66f840cSTristram Ha hi |= PORT_QUEUE_SPLIT_H;
154e66f840cSTristram Ha ksz_pwrite8(dev, port, REG_PORT_CTRL_0, lo);
155e66f840cSTristram Ha ksz_pwrite8(dev, port, P_DROP_TAG_CTRL, hi);
156e66f840cSTristram Ha
157e66f840cSTristram Ha /* Default is port based for egress rate limit. */
158e66f840cSTristram Ha if (queue != PORT_QUEUE_SPLIT_1)
159e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_19, SW_OUT_RATE_LIMIT_QUEUE_BASED,
160e66f840cSTristram Ha true);
161e66f840cSTristram Ha }
162e66f840cSTristram Ha
ksz8_r_mib_cnt(struct ksz_device * dev,int port,u16 addr,u64 * cnt)1636ec23aaaSArun Ramadoss void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt)
164e66f840cSTristram Ha {
1659f73e112SMichael Grzeschik const u32 *masks;
166a02579dfSArun Ramadoss const u16 *regs;
167e66f840cSTristram Ha u16 ctrl_addr;
168e66f840cSTristram Ha u32 data;
169e66f840cSTristram Ha u8 check;
170e66f840cSTristram Ha int loop;
171e66f840cSTristram Ha
172d23a5e18SArun Ramadoss masks = dev->info->masks;
173486f9ca7SArun Ramadoss regs = dev->info->regs;
1749f73e112SMichael Grzeschik
175a530e6f2SArun Ramadoss ctrl_addr = addr + dev->info->reg_mib_cnt * port;
176e66f840cSTristram Ha ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
177e66f840cSTristram Ha
178e66f840cSTristram Ha mutex_lock(&dev->alu_mutex);
1799f73e112SMichael Grzeschik ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
180e66f840cSTristram Ha
181e66f840cSTristram Ha /* It is almost guaranteed to always read the valid bit because of
182e66f840cSTristram Ha * slow SPI speed.
183e66f840cSTristram Ha */
184e66f840cSTristram Ha for (loop = 2; loop > 0; loop--) {
1859f73e112SMichael Grzeschik ksz_read8(dev, regs[REG_IND_MIB_CHECK], &check);
186e66f840cSTristram Ha
1879f73e112SMichael Grzeschik if (check & masks[MIB_COUNTER_VALID]) {
1889f73e112SMichael Grzeschik ksz_read32(dev, regs[REG_IND_DATA_LO], &data);
1899f73e112SMichael Grzeschik if (check & masks[MIB_COUNTER_OVERFLOW])
190e66f840cSTristram Ha *cnt += MIB_COUNTER_VALUE + 1;
191e66f840cSTristram Ha *cnt += data & MIB_COUNTER_VALUE;
192e66f840cSTristram Ha break;
193e66f840cSTristram Ha }
194e66f840cSTristram Ha }
195e66f840cSTristram Ha mutex_unlock(&dev->alu_mutex);
196e66f840cSTristram Ha }
197e66f840cSTristram Ha
ksz8795_r_mib_pkt(struct ksz_device * dev,int port,u16 addr,u64 * dropped,u64 * cnt)1984b20a07eSOleksij Rempel static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
199e66f840cSTristram Ha u64 *dropped, u64 *cnt)
200e66f840cSTristram Ha {
2019f73e112SMichael Grzeschik const u32 *masks;
202a02579dfSArun Ramadoss const u16 *regs;
203e66f840cSTristram Ha u16 ctrl_addr;
204e66f840cSTristram Ha u32 data;
205e66f840cSTristram Ha u8 check;
206e66f840cSTristram Ha int loop;
207e66f840cSTristram Ha
208d23a5e18SArun Ramadoss masks = dev->info->masks;
209486f9ca7SArun Ramadoss regs = dev->info->regs;
2109f73e112SMichael Grzeschik
211a530e6f2SArun Ramadoss addr -= dev->info->reg_mib_cnt;
2124b20a07eSOleksij Rempel ctrl_addr = (KSZ8795_MIB_TOTAL_RX_1 - KSZ8795_MIB_TOTAL_RX_0) * port;
2134b20a07eSOleksij Rempel ctrl_addr += addr + KSZ8795_MIB_TOTAL_RX_0;
214e66f840cSTristram Ha ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
215e66f840cSTristram Ha
216e66f840cSTristram Ha mutex_lock(&dev->alu_mutex);
2179f73e112SMichael Grzeschik ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
218e66f840cSTristram Ha
219e66f840cSTristram Ha /* It is almost guaranteed to always read the valid bit because of
220e66f840cSTristram Ha * slow SPI speed.
221e66f840cSTristram Ha */
222e66f840cSTristram Ha for (loop = 2; loop > 0; loop--) {
2239f73e112SMichael Grzeschik ksz_read8(dev, regs[REG_IND_MIB_CHECK], &check);
224e66f840cSTristram Ha
2259f73e112SMichael Grzeschik if (check & masks[MIB_COUNTER_VALID]) {
2269f73e112SMichael Grzeschik ksz_read32(dev, regs[REG_IND_DATA_LO], &data);
227e66f840cSTristram Ha if (addr < 2) {
228e66f840cSTristram Ha u64 total;
229e66f840cSTristram Ha
230e66f840cSTristram Ha total = check & MIB_TOTAL_BYTES_H;
231e66f840cSTristram Ha total <<= 32;
232e66f840cSTristram Ha *cnt += total;
233e66f840cSTristram Ha *cnt += data;
2349f73e112SMichael Grzeschik if (check & masks[MIB_COUNTER_OVERFLOW]) {
235e66f840cSTristram Ha total = MIB_TOTAL_BYTES_H + 1;
236e66f840cSTristram Ha total <<= 32;
237e66f840cSTristram Ha *cnt += total;
238e66f840cSTristram Ha }
239e66f840cSTristram Ha } else {
2409f73e112SMichael Grzeschik if (check & masks[MIB_COUNTER_OVERFLOW])
241e66f840cSTristram Ha *cnt += MIB_PACKET_DROPPED + 1;
242e66f840cSTristram Ha *cnt += data & MIB_PACKET_DROPPED;
243e66f840cSTristram Ha }
244e66f840cSTristram Ha break;
245e66f840cSTristram Ha }
246e66f840cSTristram Ha }
247e66f840cSTristram Ha mutex_unlock(&dev->alu_mutex);
248e66f840cSTristram Ha }
249e66f840cSTristram Ha
ksz8863_r_mib_pkt(struct ksz_device * dev,int port,u16 addr,u64 * dropped,u64 * cnt)2504b20a07eSOleksij Rempel static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
2514b20a07eSOleksij Rempel u64 *dropped, u64 *cnt)
2524b20a07eSOleksij Rempel {
2534b20a07eSOleksij Rempel u32 *last = (u32 *)dropped;
254a02579dfSArun Ramadoss const u16 *regs;
2554b20a07eSOleksij Rempel u16 ctrl_addr;
2564b20a07eSOleksij Rempel u32 data;
2574b20a07eSOleksij Rempel u32 cur;
2584b20a07eSOleksij Rempel
259486f9ca7SArun Ramadoss regs = dev->info->regs;
260486f9ca7SArun Ramadoss
261a530e6f2SArun Ramadoss addr -= dev->info->reg_mib_cnt;
2624b20a07eSOleksij Rempel ctrl_addr = addr ? KSZ8863_MIB_PACKET_DROPPED_TX_0 :
2634b20a07eSOleksij Rempel KSZ8863_MIB_PACKET_DROPPED_RX_0;
2644b20a07eSOleksij Rempel ctrl_addr += port;
2654b20a07eSOleksij Rempel ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
2664b20a07eSOleksij Rempel
2674b20a07eSOleksij Rempel mutex_lock(&dev->alu_mutex);
2684b20a07eSOleksij Rempel ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
2694b20a07eSOleksij Rempel ksz_read32(dev, regs[REG_IND_DATA_LO], &data);
2704b20a07eSOleksij Rempel mutex_unlock(&dev->alu_mutex);
2714b20a07eSOleksij Rempel
2724b20a07eSOleksij Rempel data &= MIB_PACKET_DROPPED;
2734b20a07eSOleksij Rempel cur = last[addr];
2744b20a07eSOleksij Rempel if (data != cur) {
2754b20a07eSOleksij Rempel last[addr] = data;
2764b20a07eSOleksij Rempel if (data < cur)
2774b20a07eSOleksij Rempel data += MIB_PACKET_DROPPED + 1;
2784b20a07eSOleksij Rempel data -= cur;
2794b20a07eSOleksij Rempel *cnt += data;
2804b20a07eSOleksij Rempel }
2814b20a07eSOleksij Rempel }
2824b20a07eSOleksij Rempel
ksz8_r_mib_pkt(struct ksz_device * dev,int port,u16 addr,u64 * dropped,u64 * cnt)2836ec23aaaSArun Ramadoss void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
2844b20a07eSOleksij Rempel u64 *dropped, u64 *cnt)
2854b20a07eSOleksij Rempel {
2864b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev))
2874b20a07eSOleksij Rempel ksz8863_r_mib_pkt(dev, port, addr, dropped, cnt);
2884b20a07eSOleksij Rempel else
2894b20a07eSOleksij Rempel ksz8795_r_mib_pkt(dev, port, addr, dropped, cnt);
2904b20a07eSOleksij Rempel }
2914b20a07eSOleksij Rempel
ksz8_freeze_mib(struct ksz_device * dev,int port,bool freeze)2926ec23aaaSArun Ramadoss void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze)
293e66f840cSTristram Ha {
2944b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev))
2954b20a07eSOleksij Rempel return;
2964b20a07eSOleksij Rempel
297e66f840cSTristram Ha /* enable the port for flush/freeze function */
298e66f840cSTristram Ha if (freeze)
299e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
300e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FREEZE, freeze);
301e66f840cSTristram Ha
302e66f840cSTristram Ha /* disable the port after freeze is done */
303e66f840cSTristram Ha if (!freeze)
304e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
305e66f840cSTristram Ha }
306e66f840cSTristram Ha
ksz8_port_init_cnt(struct ksz_device * dev,int port)3076ec23aaaSArun Ramadoss void ksz8_port_init_cnt(struct ksz_device *dev, int port)
308e66f840cSTristram Ha {
309e66f840cSTristram Ha struct ksz_port_mib *mib = &dev->ports[port].mib;
3104b20a07eSOleksij Rempel u64 *dropped;
311e66f840cSTristram Ha
3124b20a07eSOleksij Rempel if (!ksz_is_ksz88x3(dev)) {
313e66f840cSTristram Ha /* flush all enabled port MIB counters */
314e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
315e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true);
316e66f840cSTristram Ha ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
3174b20a07eSOleksij Rempel }
318e66f840cSTristram Ha
319e66f840cSTristram Ha mib->cnt_ptr = 0;
320e66f840cSTristram Ha
321e66f840cSTristram Ha /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */
322a530e6f2SArun Ramadoss while (mib->cnt_ptr < dev->info->reg_mib_cnt) {
323e66f840cSTristram Ha dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr,
324e66f840cSTristram Ha &mib->counters[mib->cnt_ptr]);
325e66f840cSTristram Ha ++mib->cnt_ptr;
326e66f840cSTristram Ha }
327e66f840cSTristram Ha
3284b20a07eSOleksij Rempel /* last one in storage */
329a530e6f2SArun Ramadoss dropped = &mib->counters[dev->info->mib_cnt];
3304b20a07eSOleksij Rempel
331e66f840cSTristram Ha /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */
332a530e6f2SArun Ramadoss while (mib->cnt_ptr < dev->info->mib_cnt) {
333e66f840cSTristram Ha dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr,
3344b20a07eSOleksij Rempel dropped, &mib->counters[mib->cnt_ptr]);
335e66f840cSTristram Ha ++mib->cnt_ptr;
336e66f840cSTristram Ha }
337e66f840cSTristram Ha }
338e66f840cSTristram Ha
ksz8_r_table(struct ksz_device * dev,int table,u16 addr,u64 * data)339ec2312f3SOleksij Rempel static int ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data)
340e66f840cSTristram Ha {
341a02579dfSArun Ramadoss const u16 *regs;
342e66f840cSTristram Ha u16 ctrl_addr;
343ec2312f3SOleksij Rempel int ret;
344e66f840cSTristram Ha
345486f9ca7SArun Ramadoss regs = dev->info->regs;
346486f9ca7SArun Ramadoss
347e66f840cSTristram Ha ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr;
348e66f840cSTristram Ha
349e66f840cSTristram Ha mutex_lock(&dev->alu_mutex);
350ec2312f3SOleksij Rempel ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
351ec2312f3SOleksij Rempel if (ret)
352ec2312f3SOleksij Rempel goto unlock_alu;
353ec2312f3SOleksij Rempel
354ec2312f3SOleksij Rempel ret = ksz_read64(dev, regs[REG_IND_DATA_HI], data);
355ec2312f3SOleksij Rempel unlock_alu:
356e66f840cSTristram Ha mutex_unlock(&dev->alu_mutex);
357ec2312f3SOleksij Rempel
358ec2312f3SOleksij Rempel return ret;
359e66f840cSTristram Ha }
360e66f840cSTristram Ha
ksz8_w_table(struct ksz_device * dev,int table,u16 addr,u64 data)3613c2e6b54SOleksij Rempel static int ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data)
362e66f840cSTristram Ha {
363a02579dfSArun Ramadoss const u16 *regs;
364e66f840cSTristram Ha u16 ctrl_addr;
3653c2e6b54SOleksij Rempel int ret;
366e66f840cSTristram Ha
367486f9ca7SArun Ramadoss regs = dev->info->regs;
368486f9ca7SArun Ramadoss
369e66f840cSTristram Ha ctrl_addr = IND_ACC_TABLE(table) | addr;
370e66f840cSTristram Ha
371e66f840cSTristram Ha mutex_lock(&dev->alu_mutex);
3723c2e6b54SOleksij Rempel ret = ksz_write64(dev, regs[REG_IND_DATA_HI], data);
3733c2e6b54SOleksij Rempel if (ret)
3743c2e6b54SOleksij Rempel goto unlock_alu;
3753c2e6b54SOleksij Rempel
3763c2e6b54SOleksij Rempel ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
3773c2e6b54SOleksij Rempel unlock_alu:
378e66f840cSTristram Ha mutex_unlock(&dev->alu_mutex);
3793c2e6b54SOleksij Rempel
3803c2e6b54SOleksij Rempel return ret;
381e66f840cSTristram Ha }
382e66f840cSTristram Ha
ksz8_valid_dyn_entry(struct ksz_device * dev,u8 * data)3834b5baca0SMichael Grzeschik static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data)
384e66f840cSTristram Ha {
385e66f840cSTristram Ha int timeout = 100;
3869f73e112SMichael Grzeschik const u32 *masks;
387a02579dfSArun Ramadoss const u16 *regs;
3889f73e112SMichael Grzeschik
389d23a5e18SArun Ramadoss masks = dev->info->masks;
390486f9ca7SArun Ramadoss regs = dev->info->regs;
391e66f840cSTristram Ha
392e66f840cSTristram Ha do {
3939f73e112SMichael Grzeschik ksz_read8(dev, regs[REG_IND_DATA_CHECK], data);
394e66f840cSTristram Ha timeout--;
3959f73e112SMichael Grzeschik } while ((*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) && timeout);
396e66f840cSTristram Ha
397e66f840cSTristram Ha /* Entry is not ready for accessing. */
3989f73e112SMichael Grzeschik if (*data & masks[DYNAMIC_MAC_TABLE_NOT_READY]) {
399e66f840cSTristram Ha return -EAGAIN;
400e66f840cSTristram Ha /* Entry is ready for accessing. */
401e66f840cSTristram Ha } else {
4029f73e112SMichael Grzeschik ksz_read8(dev, regs[REG_IND_DATA_8], data);
403e66f840cSTristram Ha
404e66f840cSTristram Ha /* There is no valid entry in the table. */
4059f73e112SMichael Grzeschik if (*data & masks[DYNAMIC_MAC_TABLE_MAC_EMPTY])
406e66f840cSTristram Ha return -ENXIO;
407e66f840cSTristram Ha }
408e66f840cSTristram Ha return 0;
409e66f840cSTristram Ha }
410e66f840cSTristram Ha
ksz8_r_dyn_mac_table(struct ksz_device * dev,u16 addr,u8 * mac_addr,u8 * fid,u8 * src_port,u8 * timestamp,u16 * entries)4116ec23aaaSArun Ramadoss int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr,
4126ec23aaaSArun Ramadoss u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries)
413e66f840cSTristram Ha {
414e66f840cSTristram Ha u32 data_hi, data_lo;
4159f73e112SMichael Grzeschik const u8 *shifts;
4169f73e112SMichael Grzeschik const u32 *masks;
417a02579dfSArun Ramadoss const u16 *regs;
418e66f840cSTristram Ha u16 ctrl_addr;
419e66f840cSTristram Ha u8 data;
420e66f840cSTristram Ha int rc;
421e66f840cSTristram Ha
42234e48383SArun Ramadoss shifts = dev->info->shifts;
423d23a5e18SArun Ramadoss masks = dev->info->masks;
424486f9ca7SArun Ramadoss regs = dev->info->regs;
4259f73e112SMichael Grzeschik
426e66f840cSTristram Ha ctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr;
427e66f840cSTristram Ha
428e66f840cSTristram Ha mutex_lock(&dev->alu_mutex);
4299f73e112SMichael Grzeschik ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
430e66f840cSTristram Ha
4314b5baca0SMichael Grzeschik rc = ksz8_valid_dyn_entry(dev, &data);
432e66f840cSTristram Ha if (rc == -EAGAIN) {
433e66f840cSTristram Ha if (addr == 0)
434e66f840cSTristram Ha *entries = 0;
435e66f840cSTristram Ha } else if (rc == -ENXIO) {
436e66f840cSTristram Ha *entries = 0;
437e66f840cSTristram Ha /* At least one valid entry in the table. */
438e66f840cSTristram Ha } else {
439e66f840cSTristram Ha u64 buf = 0;
440e66f840cSTristram Ha int cnt;
441e66f840cSTristram Ha
4429f73e112SMichael Grzeschik ksz_read64(dev, regs[REG_IND_DATA_HI], &buf);
443e66f840cSTristram Ha data_hi = (u32)(buf >> 32);
444e66f840cSTristram Ha data_lo = (u32)buf;
445e66f840cSTristram Ha
446e66f840cSTristram Ha /* Check out how many valid entry in the table. */
4479f73e112SMichael Grzeschik cnt = data & masks[DYNAMIC_MAC_TABLE_ENTRIES_H];
4489f73e112SMichael Grzeschik cnt <<= shifts[DYNAMIC_MAC_ENTRIES_H];
4499f73e112SMichael Grzeschik cnt |= (data_hi & masks[DYNAMIC_MAC_TABLE_ENTRIES]) >>
4509f73e112SMichael Grzeschik shifts[DYNAMIC_MAC_ENTRIES];
451e66f840cSTristram Ha *entries = cnt + 1;
452e66f840cSTristram Ha
4539f73e112SMichael Grzeschik *fid = (data_hi & masks[DYNAMIC_MAC_TABLE_FID]) >>
4549f73e112SMichael Grzeschik shifts[DYNAMIC_MAC_FID];
4559f73e112SMichael Grzeschik *src_port = (data_hi & masks[DYNAMIC_MAC_TABLE_SRC_PORT]) >>
4569f73e112SMichael Grzeschik shifts[DYNAMIC_MAC_SRC_PORT];
4579f73e112SMichael Grzeschik *timestamp = (data_hi & masks[DYNAMIC_MAC_TABLE_TIMESTAMP]) >>
4589f73e112SMichael Grzeschik shifts[DYNAMIC_MAC_TIMESTAMP];
459e66f840cSTristram Ha
460e66f840cSTristram Ha mac_addr[5] = (u8)data_lo;
461e66f840cSTristram Ha mac_addr[4] = (u8)(data_lo >> 8);
462e66f840cSTristram Ha mac_addr[3] = (u8)(data_lo >> 16);
463e66f840cSTristram Ha mac_addr[2] = (u8)(data_lo >> 24);
464e66f840cSTristram Ha
465e66f840cSTristram Ha mac_addr[1] = (u8)data_hi;
466e66f840cSTristram Ha mac_addr[0] = (u8)(data_hi >> 8);
467e66f840cSTristram Ha rc = 0;
468e66f840cSTristram Ha }
469e66f840cSTristram Ha mutex_unlock(&dev->alu_mutex);
470e66f840cSTristram Ha
471e66f840cSTristram Ha return rc;
472e66f840cSTristram Ha }
473e66f840cSTristram Ha
ksz8_r_sta_mac_table(struct ksz_device * dev,u16 addr,struct alu_struct * alu,bool * valid)474b5751cddSOleksij Rempel static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr,
475559901b4SOleksij Rempel struct alu_struct *alu, bool *valid)
476e66f840cSTristram Ha {
477e66f840cSTristram Ha u32 data_hi, data_lo;
4789f73e112SMichael Grzeschik const u8 *shifts;
4799f73e112SMichael Grzeschik const u32 *masks;
480e66f840cSTristram Ha u64 data;
481ec2312f3SOleksij Rempel int ret;
482e66f840cSTristram Ha
48334e48383SArun Ramadoss shifts = dev->info->shifts;
484d23a5e18SArun Ramadoss masks = dev->info->masks;
4859f73e112SMichael Grzeschik
486ec2312f3SOleksij Rempel ret = ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data);
487ec2312f3SOleksij Rempel if (ret)
488ec2312f3SOleksij Rempel return ret;
489ec2312f3SOleksij Rempel
490e66f840cSTristram Ha data_hi = data >> 32;
491e66f840cSTristram Ha data_lo = (u32)data;
492559901b4SOleksij Rempel
493559901b4SOleksij Rempel if (!(data_hi & (masks[STATIC_MAC_TABLE_VALID] |
494559901b4SOleksij Rempel masks[STATIC_MAC_TABLE_OVERRIDE]))) {
495559901b4SOleksij Rempel *valid = false;
496559901b4SOleksij Rempel return 0;
497559901b4SOleksij Rempel }
498559901b4SOleksij Rempel
499e66f840cSTristram Ha alu->mac[5] = (u8)data_lo;
500e66f840cSTristram Ha alu->mac[4] = (u8)(data_lo >> 8);
501e66f840cSTristram Ha alu->mac[3] = (u8)(data_lo >> 16);
502e66f840cSTristram Ha alu->mac[2] = (u8)(data_lo >> 24);
503e66f840cSTristram Ha alu->mac[1] = (u8)data_hi;
504e66f840cSTristram Ha alu->mac[0] = (u8)(data_hi >> 8);
5059f73e112SMichael Grzeschik alu->port_forward =
5069f73e112SMichael Grzeschik (data_hi & masks[STATIC_MAC_TABLE_FWD_PORTS]) >>
5079f73e112SMichael Grzeschik shifts[STATIC_MAC_FWD_PORTS];
508559901b4SOleksij Rempel alu->is_override = (data_hi & masks[STATIC_MAC_TABLE_OVERRIDE]) ? 1 : 0;
5094bdf79d6STristram Ha
5104bdf79d6STristram Ha /* KSZ8795 family switches have STATIC_MAC_TABLE_USE_FID and
5114bdf79d6STristram Ha * STATIC_MAC_TABLE_FID definitions off by 1 when doing read on the
5124bdf79d6STristram Ha * static MAC table compared to doing write.
5134bdf79d6STristram Ha */
5144bdf79d6STristram Ha if (ksz_is_ksz87xx(dev))
515e66f840cSTristram Ha data_hi >>= 1;
5169f73e112SMichael Grzeschik alu->is_static = true;
517559901b4SOleksij Rempel alu->is_use_fid = (data_hi & masks[STATIC_MAC_TABLE_USE_FID]) ? 1 : 0;
5189f73e112SMichael Grzeschik alu->fid = (data_hi & masks[STATIC_MAC_TABLE_FID]) >>
5199f73e112SMichael Grzeschik shifts[STATIC_MAC_FID];
520559901b4SOleksij Rempel
521559901b4SOleksij Rempel *valid = true;
522559901b4SOleksij Rempel
523e66f840cSTristram Ha return 0;
524e66f840cSTristram Ha }
525e66f840cSTristram Ha
ksz8_w_sta_mac_table(struct ksz_device * dev,u16 addr,struct alu_struct * alu)5263c2e6b54SOleksij Rempel static int ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr,
527e66f840cSTristram Ha struct alu_struct *alu)
528e66f840cSTristram Ha {
529e66f840cSTristram Ha u32 data_hi, data_lo;
5309f73e112SMichael Grzeschik const u8 *shifts;
5319f73e112SMichael Grzeschik const u32 *masks;
532e66f840cSTristram Ha u64 data;
533e66f840cSTristram Ha
53434e48383SArun Ramadoss shifts = dev->info->shifts;
535d23a5e18SArun Ramadoss masks = dev->info->masks;
5369f73e112SMichael Grzeschik
537e66f840cSTristram Ha data_lo = ((u32)alu->mac[2] << 24) |
538e66f840cSTristram Ha ((u32)alu->mac[3] << 16) |
539e66f840cSTristram Ha ((u32)alu->mac[4] << 8) | alu->mac[5];
540e66f840cSTristram Ha data_hi = ((u32)alu->mac[0] << 8) | alu->mac[1];
5419f73e112SMichael Grzeschik data_hi |= (u32)alu->port_forward << shifts[STATIC_MAC_FWD_PORTS];
542e66f840cSTristram Ha
543e66f840cSTristram Ha if (alu->is_override)
5449f73e112SMichael Grzeschik data_hi |= masks[STATIC_MAC_TABLE_OVERRIDE];
545e66f840cSTristram Ha if (alu->is_use_fid) {
5469f73e112SMichael Grzeschik data_hi |= masks[STATIC_MAC_TABLE_USE_FID];
5479f73e112SMichael Grzeschik data_hi |= (u32)alu->fid << shifts[STATIC_MAC_FID];
548e66f840cSTristram Ha }
549e66f840cSTristram Ha if (alu->is_static)
5509f73e112SMichael Grzeschik data_hi |= masks[STATIC_MAC_TABLE_VALID];
551e66f840cSTristram Ha else
5529f73e112SMichael Grzeschik data_hi &= ~masks[STATIC_MAC_TABLE_OVERRIDE];
553e66f840cSTristram Ha
554e66f840cSTristram Ha data = (u64)data_hi << 32 | data_lo;
5553c2e6b54SOleksij Rempel
5563c2e6b54SOleksij Rempel return ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data);
557e66f840cSTristram Ha }
558e66f840cSTristram Ha
ksz8_from_vlan(struct ksz_device * dev,u32 vlan,u8 * fid,u8 * member,u8 * valid)5599f73e112SMichael Grzeschik static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid,
5609f73e112SMichael Grzeschik u8 *member, u8 *valid)
561e66f840cSTristram Ha {
5629f73e112SMichael Grzeschik const u8 *shifts;
5639f73e112SMichael Grzeschik const u32 *masks;
5649f73e112SMichael Grzeschik
56534e48383SArun Ramadoss shifts = dev->info->shifts;
566d23a5e18SArun Ramadoss masks = dev->info->masks;
5679f73e112SMichael Grzeschik
5689f73e112SMichael Grzeschik *fid = vlan & masks[VLAN_TABLE_FID];
5699f73e112SMichael Grzeschik *member = (vlan & masks[VLAN_TABLE_MEMBERSHIP]) >>
5709f73e112SMichael Grzeschik shifts[VLAN_TABLE_MEMBERSHIP_S];
5719f73e112SMichael Grzeschik *valid = !!(vlan & masks[VLAN_TABLE_VALID]);
572e66f840cSTristram Ha }
573e66f840cSTristram Ha
ksz8_to_vlan(struct ksz_device * dev,u8 fid,u8 member,u8 valid,u16 * vlan)5749f73e112SMichael Grzeschik static void ksz8_to_vlan(struct ksz_device *dev, u8 fid, u8 member, u8 valid,
5759f73e112SMichael Grzeschik u16 *vlan)
576e66f840cSTristram Ha {
5779f73e112SMichael Grzeschik const u8 *shifts;
5789f73e112SMichael Grzeschik const u32 *masks;
5799f73e112SMichael Grzeschik
58034e48383SArun Ramadoss shifts = dev->info->shifts;
581d23a5e18SArun Ramadoss masks = dev->info->masks;
5829f73e112SMichael Grzeschik
583e66f840cSTristram Ha *vlan = fid;
5849f73e112SMichael Grzeschik *vlan |= (u16)member << shifts[VLAN_TABLE_MEMBERSHIP_S];
585e66f840cSTristram Ha if (valid)
5869f73e112SMichael Grzeschik *vlan |= masks[VLAN_TABLE_VALID];
587e66f840cSTristram Ha }
588e66f840cSTristram Ha
ksz8_r_vlan_entries(struct ksz_device * dev,u16 addr)5894b5baca0SMichael Grzeschik static void ksz8_r_vlan_entries(struct ksz_device *dev, u16 addr)
590e66f840cSTristram Ha {
5919f73e112SMichael Grzeschik const u8 *shifts;
592e66f840cSTristram Ha u64 data;
593e66f840cSTristram Ha int i;
594e66f840cSTristram Ha
59534e48383SArun Ramadoss shifts = dev->info->shifts;
5969f73e112SMichael Grzeschik
5974b5baca0SMichael Grzeschik ksz8_r_table(dev, TABLE_VLAN, addr, &data);
598411d466dSBen Hutchings addr *= 4;
599411d466dSBen Hutchings for (i = 0; i < 4; i++) {
600e66f840cSTristram Ha dev->vlan_cache[addr + i].table[0] = (u16)data;
6019f73e112SMichael Grzeschik data >>= shifts[VLAN_TABLE];
602e66f840cSTristram Ha }
603e66f840cSTristram Ha }
604e66f840cSTristram Ha
ksz8_r_vlan_table(struct ksz_device * dev,u16 vid,u16 * vlan)6054b5baca0SMichael Grzeschik static void ksz8_r_vlan_table(struct ksz_device *dev, u16 vid, u16 *vlan)
606e66f840cSTristram Ha {
607e66f840cSTristram Ha int index;
608e66f840cSTristram Ha u16 *data;
609e66f840cSTristram Ha u16 addr;
610e66f840cSTristram Ha u64 buf;
611e66f840cSTristram Ha
612e66f840cSTristram Ha data = (u16 *)&buf;
613411d466dSBen Hutchings addr = vid / 4;
614e66f840cSTristram Ha index = vid & 3;
6154b5baca0SMichael Grzeschik ksz8_r_table(dev, TABLE_VLAN, addr, &buf);
616e66f840cSTristram Ha *vlan = data[index];
617e66f840cSTristram Ha }
618e66f840cSTristram Ha
ksz8_w_vlan_table(struct ksz_device * dev,u16 vid,u16 vlan)6194b5baca0SMichael Grzeschik static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan)
620e66f840cSTristram Ha {
621e66f840cSTristram Ha int index;
622e66f840cSTristram Ha u16 *data;
623e66f840cSTristram Ha u16 addr;
624e66f840cSTristram Ha u64 buf;
625e66f840cSTristram Ha
626e66f840cSTristram Ha data = (u16 *)&buf;
627411d466dSBen Hutchings addr = vid / 4;
628e66f840cSTristram Ha index = vid & 3;
6294b5baca0SMichael Grzeschik ksz8_r_table(dev, TABLE_VLAN, addr, &buf);
630e66f840cSTristram Ha data[index] = vlan;
631e66f840cSTristram Ha dev->vlan_cache[vid].table[0] = vlan;
6324b5baca0SMichael Grzeschik ksz8_w_table(dev, TABLE_VLAN, addr, buf);
633e66f840cSTristram Ha }
634e66f840cSTristram Ha
ksz8_r_phy(struct ksz_device * dev,u16 phy,u16 reg,u16 * val)6358f420456SOleksij Rempel int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
636e66f840cSTristram Ha {
637e66f840cSTristram Ha u8 restart, speed, ctrl, link;
638e66f840cSTristram Ha int processed = true;
639a02579dfSArun Ramadoss const u16 *regs;
64036838050SOleksij Rempel u8 val1, val2;
641e66f840cSTristram Ha u16 data = 0;
642e66f840cSTristram Ha u8 p = phy;
6439590fc4aSOleksij Rempel int ret;
644e66f840cSTristram Ha
645486f9ca7SArun Ramadoss regs = dev->info->regs;
646486f9ca7SArun Ramadoss
647e66f840cSTristram Ha switch (reg) {
648ec4b94f9SMichael Grzeschik case MII_BMCR:
6499590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart);
6509590fc4aSOleksij Rempel if (ret)
6519590fc4aSOleksij Rempel return ret;
6529590fc4aSOleksij Rempel
6539590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed);
6549590fc4aSOleksij Rempel if (ret)
6559590fc4aSOleksij Rempel return ret;
6569590fc4aSOleksij Rempel
6579590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl);
6589590fc4aSOleksij Rempel if (ret)
6599590fc4aSOleksij Rempel return ret;
6609590fc4aSOleksij Rempel
661e66f840cSTristram Ha if (restart & PORT_PHY_LOOPBACK)
662ec4b94f9SMichael Grzeschik data |= BMCR_LOOPBACK;
663e66f840cSTristram Ha if (ctrl & PORT_FORCE_100_MBIT)
664ec4b94f9SMichael Grzeschik data |= BMCR_SPEED100;
6654b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev)) {
6664b20a07eSOleksij Rempel if ((ctrl & PORT_AUTO_NEG_ENABLE))
667ec4b94f9SMichael Grzeschik data |= BMCR_ANENABLE;
6684b20a07eSOleksij Rempel } else {
669e66f840cSTristram Ha if (!(ctrl & PORT_AUTO_NEG_DISABLE))
670ec4b94f9SMichael Grzeschik data |= BMCR_ANENABLE;
6714b20a07eSOleksij Rempel }
672e66f840cSTristram Ha if (restart & PORT_POWER_DOWN)
673ec4b94f9SMichael Grzeschik data |= BMCR_PDOWN;
674e66f840cSTristram Ha if (restart & PORT_AUTO_NEG_RESTART)
675ec4b94f9SMichael Grzeschik data |= BMCR_ANRESTART;
676e66f840cSTristram Ha if (ctrl & PORT_FORCE_FULL_DUPLEX)
677ec4b94f9SMichael Grzeschik data |= BMCR_FULLDPLX;
678e66f840cSTristram Ha if (speed & PORT_HP_MDIX)
679ec4b94f9SMichael Grzeschik data |= KSZ886X_BMCR_HP_MDIX;
680e66f840cSTristram Ha if (restart & PORT_FORCE_MDIX)
681ec4b94f9SMichael Grzeschik data |= KSZ886X_BMCR_FORCE_MDI;
682e66f840cSTristram Ha if (restart & PORT_AUTO_MDIX_DISABLE)
683ec4b94f9SMichael Grzeschik data |= KSZ886X_BMCR_DISABLE_AUTO_MDIX;
684e66f840cSTristram Ha if (restart & PORT_TX_DISABLE)
685ec4b94f9SMichael Grzeschik data |= KSZ886X_BMCR_DISABLE_TRANSMIT;
686e66f840cSTristram Ha if (restart & PORT_LED_OFF)
687ec4b94f9SMichael Grzeschik data |= KSZ886X_BMCR_DISABLE_LED;
688e66f840cSTristram Ha break;
689ec4b94f9SMichael Grzeschik case MII_BMSR:
6909590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link);
6919590fc4aSOleksij Rempel if (ret)
6929590fc4aSOleksij Rempel return ret;
6939590fc4aSOleksij Rempel
694ec4b94f9SMichael Grzeschik data = BMSR_100FULL |
695ec4b94f9SMichael Grzeschik BMSR_100HALF |
696ec4b94f9SMichael Grzeschik BMSR_10FULL |
697ec4b94f9SMichael Grzeschik BMSR_10HALF |
698ec4b94f9SMichael Grzeschik BMSR_ANEGCAPABLE;
699e66f840cSTristram Ha if (link & PORT_AUTO_NEG_COMPLETE)
700ec4b94f9SMichael Grzeschik data |= BMSR_ANEGCOMPLETE;
701e66f840cSTristram Ha if (link & PORT_STAT_LINK_GOOD)
702ec4b94f9SMichael Grzeschik data |= BMSR_LSTATUS;
703e66f840cSTristram Ha break;
704ec4b94f9SMichael Grzeschik case MII_PHYSID1:
705e66f840cSTristram Ha data = KSZ8795_ID_HI;
706e66f840cSTristram Ha break;
707ec4b94f9SMichael Grzeschik case MII_PHYSID2:
7084b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev))
7094b20a07eSOleksij Rempel data = KSZ8863_ID_LO;
7104b20a07eSOleksij Rempel else
711e66f840cSTristram Ha data = KSZ8795_ID_LO;
712e66f840cSTristram Ha break;
713ec4b94f9SMichael Grzeschik case MII_ADVERTISE:
7149590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl);
7159590fc4aSOleksij Rempel if (ret)
7169590fc4aSOleksij Rempel return ret;
7179590fc4aSOleksij Rempel
718ec4b94f9SMichael Grzeschik data = ADVERTISE_CSMA;
719e66f840cSTristram Ha if (ctrl & PORT_AUTO_NEG_SYM_PAUSE)
720ec4b94f9SMichael Grzeschik data |= ADVERTISE_PAUSE_CAP;
721e66f840cSTristram Ha if (ctrl & PORT_AUTO_NEG_100BTX_FD)
722ec4b94f9SMichael Grzeschik data |= ADVERTISE_100FULL;
723e66f840cSTristram Ha if (ctrl & PORT_AUTO_NEG_100BTX)
724ec4b94f9SMichael Grzeschik data |= ADVERTISE_100HALF;
725e66f840cSTristram Ha if (ctrl & PORT_AUTO_NEG_10BT_FD)
726ec4b94f9SMichael Grzeschik data |= ADVERTISE_10FULL;
727e66f840cSTristram Ha if (ctrl & PORT_AUTO_NEG_10BT)
728ec4b94f9SMichael Grzeschik data |= ADVERTISE_10HALF;
729e66f840cSTristram Ha break;
730ec4b94f9SMichael Grzeschik case MII_LPA:
7319590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_REMOTE_STATUS], &link);
7329590fc4aSOleksij Rempel if (ret)
7339590fc4aSOleksij Rempel return ret;
7349590fc4aSOleksij Rempel
735ec4b94f9SMichael Grzeschik data = LPA_SLCT;
736e66f840cSTristram Ha if (link & PORT_REMOTE_SYM_PAUSE)
737ec4b94f9SMichael Grzeschik data |= LPA_PAUSE_CAP;
738e66f840cSTristram Ha if (link & PORT_REMOTE_100BTX_FD)
739ec4b94f9SMichael Grzeschik data |= LPA_100FULL;
740e66f840cSTristram Ha if (link & PORT_REMOTE_100BTX)
741ec4b94f9SMichael Grzeschik data |= LPA_100HALF;
742e66f840cSTristram Ha if (link & PORT_REMOTE_10BT_FD)
743ec4b94f9SMichael Grzeschik data |= LPA_10FULL;
744e66f840cSTristram Ha if (link & PORT_REMOTE_10BT)
745ec4b94f9SMichael Grzeschik data |= LPA_10HALF;
746ec4b94f9SMichael Grzeschik if (data & ~LPA_SLCT)
747ec4b94f9SMichael Grzeschik data |= LPA_LPACK;
748e66f840cSTristram Ha break;
74936838050SOleksij Rempel case PHY_REG_LINK_MD:
7509590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_CTRL, &val1);
7519590fc4aSOleksij Rempel if (ret)
7529590fc4aSOleksij Rempel return ret;
7539590fc4aSOleksij Rempel
7549590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_RESULT, &val2);
7559590fc4aSOleksij Rempel if (ret)
7569590fc4aSOleksij Rempel return ret;
7579590fc4aSOleksij Rempel
75836838050SOleksij Rempel if (val1 & PORT_START_CABLE_DIAG)
75936838050SOleksij Rempel data |= PHY_START_CABLE_DIAG;
76036838050SOleksij Rempel
76136838050SOleksij Rempel if (val1 & PORT_CABLE_10M_SHORT)
76236838050SOleksij Rempel data |= PHY_CABLE_10M_SHORT;
76336838050SOleksij Rempel
76436838050SOleksij Rempel data |= FIELD_PREP(PHY_CABLE_DIAG_RESULT_M,
76536838050SOleksij Rempel FIELD_GET(PORT_CABLE_DIAG_RESULT_M, val1));
76636838050SOleksij Rempel
76736838050SOleksij Rempel data |= FIELD_PREP(PHY_CABLE_FAULT_COUNTER_M,
76836838050SOleksij Rempel (FIELD_GET(PORT_CABLE_FAULT_COUNTER_H, val1) << 8) |
76936838050SOleksij Rempel FIELD_GET(PORT_CABLE_FAULT_COUNTER_L, val2));
77036838050SOleksij Rempel break;
77152939393SOleksij Rempel case PHY_REG_PHY_CTRL:
7729590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link);
7739590fc4aSOleksij Rempel if (ret)
7749590fc4aSOleksij Rempel return ret;
7759590fc4aSOleksij Rempel
77652939393SOleksij Rempel if (link & PORT_MDIX_STATUS)
77752939393SOleksij Rempel data |= KSZ886X_CTRL_MDIX_STAT;
77852939393SOleksij Rempel break;
779e66f840cSTristram Ha default:
780e66f840cSTristram Ha processed = false;
781e66f840cSTristram Ha break;
782e66f840cSTristram Ha }
783e66f840cSTristram Ha if (processed)
784e66f840cSTristram Ha *val = data;
7858f420456SOleksij Rempel
7868f420456SOleksij Rempel return 0;
787e66f840cSTristram Ha }
788e66f840cSTristram Ha
ksz8_w_phy(struct ksz_device * dev,u16 phy,u16 reg,u16 val)7898f420456SOleksij Rempel int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
790e66f840cSTristram Ha {
791e66f840cSTristram Ha u8 restart, speed, ctrl, data;
792a02579dfSArun Ramadoss const u16 *regs;
7939f73e112SMichael Grzeschik u8 p = phy;
7949590fc4aSOleksij Rempel int ret;
795e66f840cSTristram Ha
796486f9ca7SArun Ramadoss regs = dev->info->regs;
797486f9ca7SArun Ramadoss
798e66f840cSTristram Ha switch (reg) {
799ec4b94f9SMichael Grzeschik case MII_BMCR:
800e66f840cSTristram Ha
801e66f840cSTristram Ha /* Do not support PHY reset function. */
802ec4b94f9SMichael Grzeschik if (val & BMCR_RESET)
803e66f840cSTristram Ha break;
8049590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed);
8059590fc4aSOleksij Rempel if (ret)
8069590fc4aSOleksij Rempel return ret;
8079590fc4aSOleksij Rempel
808e66f840cSTristram Ha data = speed;
809ec4b94f9SMichael Grzeschik if (val & KSZ886X_BMCR_HP_MDIX)
810e66f840cSTristram Ha data |= PORT_HP_MDIX;
811e66f840cSTristram Ha else
812e66f840cSTristram Ha data &= ~PORT_HP_MDIX;
8139590fc4aSOleksij Rempel
8149590fc4aSOleksij Rempel if (data != speed) {
8159590fc4aSOleksij Rempel ret = ksz_pwrite8(dev, p, regs[P_SPEED_STATUS], data);
8169590fc4aSOleksij Rempel if (ret)
8179590fc4aSOleksij Rempel return ret;
8189590fc4aSOleksij Rempel }
8199590fc4aSOleksij Rempel
8209590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl);
8219590fc4aSOleksij Rempel if (ret)
8229590fc4aSOleksij Rempel return ret;
8239590fc4aSOleksij Rempel
824e66f840cSTristram Ha data = ctrl;
8254b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev)) {
826ec4b94f9SMichael Grzeschik if ((val & BMCR_ANENABLE))
8274b20a07eSOleksij Rempel data |= PORT_AUTO_NEG_ENABLE;
8284b20a07eSOleksij Rempel else
8294b20a07eSOleksij Rempel data &= ~PORT_AUTO_NEG_ENABLE;
8304b20a07eSOleksij Rempel } else {
831ec4b94f9SMichael Grzeschik if (!(val & BMCR_ANENABLE))
832e66f840cSTristram Ha data |= PORT_AUTO_NEG_DISABLE;
833e66f840cSTristram Ha else
834e66f840cSTristram Ha data &= ~PORT_AUTO_NEG_DISABLE;
835e66f840cSTristram Ha
836e66f840cSTristram Ha /* Fiber port does not support auto-negotiation. */
837e66f840cSTristram Ha if (dev->ports[p].fiber)
838e66f840cSTristram Ha data |= PORT_AUTO_NEG_DISABLE;
8394b20a07eSOleksij Rempel }
8404b20a07eSOleksij Rempel
841ec4b94f9SMichael Grzeschik if (val & BMCR_SPEED100)
842e66f840cSTristram Ha data |= PORT_FORCE_100_MBIT;
843e66f840cSTristram Ha else
844e66f840cSTristram Ha data &= ~PORT_FORCE_100_MBIT;
845ec4b94f9SMichael Grzeschik if (val & BMCR_FULLDPLX)
846e66f840cSTristram Ha data |= PORT_FORCE_FULL_DUPLEX;
847e66f840cSTristram Ha else
848e66f840cSTristram Ha data &= ~PORT_FORCE_FULL_DUPLEX;
8499590fc4aSOleksij Rempel
8509590fc4aSOleksij Rempel if (data != ctrl) {
8519590fc4aSOleksij Rempel ret = ksz_pwrite8(dev, p, regs[P_FORCE_CTRL], data);
8529590fc4aSOleksij Rempel if (ret)
8539590fc4aSOleksij Rempel return ret;
8549590fc4aSOleksij Rempel }
8559590fc4aSOleksij Rempel
8569590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart);
8579590fc4aSOleksij Rempel if (ret)
8589590fc4aSOleksij Rempel return ret;
8599590fc4aSOleksij Rempel
860e66f840cSTristram Ha data = restart;
861ec4b94f9SMichael Grzeschik if (val & KSZ886X_BMCR_DISABLE_LED)
862e66f840cSTristram Ha data |= PORT_LED_OFF;
863e66f840cSTristram Ha else
864e66f840cSTristram Ha data &= ~PORT_LED_OFF;
865ec4b94f9SMichael Grzeschik if (val & KSZ886X_BMCR_DISABLE_TRANSMIT)
866e66f840cSTristram Ha data |= PORT_TX_DISABLE;
867e66f840cSTristram Ha else
868e66f840cSTristram Ha data &= ~PORT_TX_DISABLE;
869ec4b94f9SMichael Grzeschik if (val & BMCR_ANRESTART)
870e66f840cSTristram Ha data |= PORT_AUTO_NEG_RESTART;
871e66f840cSTristram Ha else
872e66f840cSTristram Ha data &= ~(PORT_AUTO_NEG_RESTART);
873ec4b94f9SMichael Grzeschik if (val & BMCR_PDOWN)
874e66f840cSTristram Ha data |= PORT_POWER_DOWN;
875e66f840cSTristram Ha else
876e66f840cSTristram Ha data &= ~PORT_POWER_DOWN;
877ec4b94f9SMichael Grzeschik if (val & KSZ886X_BMCR_DISABLE_AUTO_MDIX)
878e66f840cSTristram Ha data |= PORT_AUTO_MDIX_DISABLE;
879e66f840cSTristram Ha else
880e66f840cSTristram Ha data &= ~PORT_AUTO_MDIX_DISABLE;
881ec4b94f9SMichael Grzeschik if (val & KSZ886X_BMCR_FORCE_MDI)
882e66f840cSTristram Ha data |= PORT_FORCE_MDIX;
883e66f840cSTristram Ha else
884e66f840cSTristram Ha data &= ~PORT_FORCE_MDIX;
885ec4b94f9SMichael Grzeschik if (val & BMCR_LOOPBACK)
886e66f840cSTristram Ha data |= PORT_PHY_LOOPBACK;
887e66f840cSTristram Ha else
888e66f840cSTristram Ha data &= ~PORT_PHY_LOOPBACK;
8899590fc4aSOleksij Rempel
8909590fc4aSOleksij Rempel if (data != restart) {
8919590fc4aSOleksij Rempel ret = ksz_pwrite8(dev, p, regs[P_NEG_RESTART_CTRL],
8929590fc4aSOleksij Rempel data);
8939590fc4aSOleksij Rempel if (ret)
8949590fc4aSOleksij Rempel return ret;
8959590fc4aSOleksij Rempel }
896e66f840cSTristram Ha break;
897ec4b94f9SMichael Grzeschik case MII_ADVERTISE:
8989590fc4aSOleksij Rempel ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl);
8999590fc4aSOleksij Rempel if (ret)
9009590fc4aSOleksij Rempel return ret;
9019590fc4aSOleksij Rempel
902e66f840cSTristram Ha data = ctrl;
903e66f840cSTristram Ha data &= ~(PORT_AUTO_NEG_SYM_PAUSE |
904e66f840cSTristram Ha PORT_AUTO_NEG_100BTX_FD |
905e66f840cSTristram Ha PORT_AUTO_NEG_100BTX |
906e66f840cSTristram Ha PORT_AUTO_NEG_10BT_FD |
907e66f840cSTristram Ha PORT_AUTO_NEG_10BT);
908ec4b94f9SMichael Grzeschik if (val & ADVERTISE_PAUSE_CAP)
909e66f840cSTristram Ha data |= PORT_AUTO_NEG_SYM_PAUSE;
910ec4b94f9SMichael Grzeschik if (val & ADVERTISE_100FULL)
911e66f840cSTristram Ha data |= PORT_AUTO_NEG_100BTX_FD;
912ec4b94f9SMichael Grzeschik if (val & ADVERTISE_100HALF)
913e66f840cSTristram Ha data |= PORT_AUTO_NEG_100BTX;
914ec4b94f9SMichael Grzeschik if (val & ADVERTISE_10FULL)
915e66f840cSTristram Ha data |= PORT_AUTO_NEG_10BT_FD;
916ec4b94f9SMichael Grzeschik if (val & ADVERTISE_10HALF)
917e66f840cSTristram Ha data |= PORT_AUTO_NEG_10BT;
9189590fc4aSOleksij Rempel
9199590fc4aSOleksij Rempel if (data != ctrl) {
9209590fc4aSOleksij Rempel ret = ksz_pwrite8(dev, p, regs[P_LOCAL_CTRL], data);
9219590fc4aSOleksij Rempel if (ret)
9229590fc4aSOleksij Rempel return ret;
9239590fc4aSOleksij Rempel }
924e66f840cSTristram Ha break;
92536838050SOleksij Rempel case PHY_REG_LINK_MD:
92636838050SOleksij Rempel if (val & PHY_START_CABLE_DIAG)
92736838050SOleksij Rempel ksz_port_cfg(dev, p, REG_PORT_LINK_MD_CTRL, PORT_START_CABLE_DIAG, true);
92836838050SOleksij Rempel break;
929e66f840cSTristram Ha default:
930e66f840cSTristram Ha break;
931e66f840cSTristram Ha }
9328f420456SOleksij Rempel
9338f420456SOleksij Rempel return 0;
934e66f840cSTristram Ha }
935e66f840cSTristram Ha
ksz8_cfg_port_member(struct ksz_device * dev,int port,u8 member)9366ec23aaaSArun Ramadoss void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member)
937e66f840cSTristram Ha {
938e66f840cSTristram Ha u8 data;
939e66f840cSTristram Ha
940e66f840cSTristram Ha ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
941e66f840cSTristram Ha data &= ~PORT_VLAN_MEMBERSHIP;
942e66f840cSTristram Ha data |= (member & dev->port_mask);
943e66f840cSTristram Ha ksz_pwrite8(dev, port, P_MIRROR_CTRL, data);
944e66f840cSTristram Ha }
945e66f840cSTristram Ha
ksz8_flush_dyn_mac_table(struct ksz_device * dev,int port)9466ec23aaaSArun Ramadoss void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
947e66f840cSTristram Ha {
948241ed719SMichael Grzeschik u8 learn[DSA_MAX_PORTS];
949e66f840cSTristram Ha int first, index, cnt;
9506877102fSArun Ramadoss const u16 *regs;
9516877102fSArun Ramadoss
9526877102fSArun Ramadoss regs = dev->info->regs;
953e66f840cSTristram Ha
954462d5250SArun Ramadoss if ((uint)port < dev->info->port_cnt) {
955e66f840cSTristram Ha first = port;
956e66f840cSTristram Ha cnt = port + 1;
957e66f840cSTristram Ha } else {
958e66f840cSTristram Ha /* Flush all ports. */
959e66f840cSTristram Ha first = 0;
960462d5250SArun Ramadoss cnt = dev->info->port_cnt;
961e66f840cSTristram Ha }
962e66f840cSTristram Ha for (index = first; index < cnt; index++) {
9636877102fSArun Ramadoss ksz_pread8(dev, index, regs[P_STP_CTRL], &learn[index]);
964e66f840cSTristram Ha if (!(learn[index] & PORT_LEARN_DISABLE))
9656877102fSArun Ramadoss ksz_pwrite8(dev, index, regs[P_STP_CTRL],
966e66f840cSTristram Ha learn[index] | PORT_LEARN_DISABLE);
967e66f840cSTristram Ha }
968e66f840cSTristram Ha ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);
969e66f840cSTristram Ha for (index = first; index < cnt; index++) {
970e66f840cSTristram Ha if (!(learn[index] & PORT_LEARN_DISABLE))
9716877102fSArun Ramadoss ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]);
972e66f840cSTristram Ha }
973e66f840cSTristram Ha }
974e66f840cSTristram Ha
ksz8_fdb_dump(struct ksz_device * dev,int port,dsa_fdb_dump_cb_t * cb,void * data)9756ec23aaaSArun Ramadoss int ksz8_fdb_dump(struct ksz_device *dev, int port,
976e587be75SArun Ramadoss dsa_fdb_dump_cb_t *cb, void *data)
977e587be75SArun Ramadoss {
978e587be75SArun Ramadoss int ret = 0;
979e587be75SArun Ramadoss u16 i = 0;
980e587be75SArun Ramadoss u16 entries = 0;
981e587be75SArun Ramadoss u8 timestamp = 0;
982e587be75SArun Ramadoss u8 fid;
98388e943e8SOleksij Rempel u8 src_port;
98488e943e8SOleksij Rempel u8 mac[ETH_ALEN];
985e587be75SArun Ramadoss
986e587be75SArun Ramadoss do {
98788e943e8SOleksij Rempel ret = ksz8_r_dyn_mac_table(dev, i, mac, &fid, &src_port,
988e587be75SArun Ramadoss ×tamp, &entries);
98988e943e8SOleksij Rempel if (!ret && port == src_port) {
99088e943e8SOleksij Rempel ret = cb(mac, fid, false, data);
991e587be75SArun Ramadoss if (ret)
992e587be75SArun Ramadoss break;
993e587be75SArun Ramadoss }
994e587be75SArun Ramadoss i++;
995e587be75SArun Ramadoss } while (i < entries);
996e587be75SArun Ramadoss if (i >= entries)
997e587be75SArun Ramadoss ret = 0;
998e587be75SArun Ramadoss
999e587be75SArun Ramadoss return ret;
1000e587be75SArun Ramadoss }
1001e587be75SArun Ramadoss
ksz8_add_sta_mac(struct ksz_device * dev,int port,const unsigned char * addr,u16 vid)1002f6636ff6SOleksij Rempel static int ksz8_add_sta_mac(struct ksz_device *dev, int port,
1003f6636ff6SOleksij Rempel const unsigned char *addr, u16 vid)
1004980c7d17SArun Ramadoss {
1005980c7d17SArun Ramadoss struct alu_struct alu;
1006559901b4SOleksij Rempel int index, ret;
1007980c7d17SArun Ramadoss int empty = 0;
1008980c7d17SArun Ramadoss
1009980c7d17SArun Ramadoss alu.port_forward = 0;
1010980c7d17SArun Ramadoss for (index = 0; index < dev->info->num_statics; index++) {
1011559901b4SOleksij Rempel bool valid;
1012559901b4SOleksij Rempel
1013559901b4SOleksij Rempel ret = ksz8_r_sta_mac_table(dev, index, &alu, &valid);
1014559901b4SOleksij Rempel if (ret)
1015559901b4SOleksij Rempel return ret;
1016559901b4SOleksij Rempel if (!valid) {
1017980c7d17SArun Ramadoss /* Remember the first empty entry. */
1018559901b4SOleksij Rempel if (!empty)
1019980c7d17SArun Ramadoss empty = index + 1;
1020559901b4SOleksij Rempel continue;
1021980c7d17SArun Ramadoss }
1022559901b4SOleksij Rempel
1023559901b4SOleksij Rempel if (!memcmp(alu.mac, addr, ETH_ALEN) && alu.fid == vid)
1024559901b4SOleksij Rempel break;
1025980c7d17SArun Ramadoss }
1026980c7d17SArun Ramadoss
1027980c7d17SArun Ramadoss /* no available entry */
1028980c7d17SArun Ramadoss if (index == dev->info->num_statics && !empty)
1029980c7d17SArun Ramadoss return -ENOSPC;
1030980c7d17SArun Ramadoss
1031980c7d17SArun Ramadoss /* add entry */
1032980c7d17SArun Ramadoss if (index == dev->info->num_statics) {
1033980c7d17SArun Ramadoss index = empty - 1;
1034980c7d17SArun Ramadoss memset(&alu, 0, sizeof(alu));
1035f6636ff6SOleksij Rempel memcpy(alu.mac, addr, ETH_ALEN);
1036980c7d17SArun Ramadoss alu.is_static = true;
1037980c7d17SArun Ramadoss }
1038980c7d17SArun Ramadoss alu.port_forward |= BIT(port);
1039f6636ff6SOleksij Rempel if (vid) {
1040980c7d17SArun Ramadoss alu.is_use_fid = true;
1041980c7d17SArun Ramadoss
1042980c7d17SArun Ramadoss /* Need a way to map VID to FID. */
1043f6636ff6SOleksij Rempel alu.fid = vid;
1044980c7d17SArun Ramadoss }
1045980c7d17SArun Ramadoss
10463c2e6b54SOleksij Rempel return ksz8_w_sta_mac_table(dev, index, &alu);
1047980c7d17SArun Ramadoss }
1048980c7d17SArun Ramadoss
ksz8_del_sta_mac(struct ksz_device * dev,int port,const unsigned char * addr,u16 vid)1049f6636ff6SOleksij Rempel static int ksz8_del_sta_mac(struct ksz_device *dev, int port,
1050f6636ff6SOleksij Rempel const unsigned char *addr, u16 vid)
1051980c7d17SArun Ramadoss {
1052980c7d17SArun Ramadoss struct alu_struct alu;
1053559901b4SOleksij Rempel int index, ret;
1054980c7d17SArun Ramadoss
1055980c7d17SArun Ramadoss for (index = 0; index < dev->info->num_statics; index++) {
1056559901b4SOleksij Rempel bool valid;
1057559901b4SOleksij Rempel
1058559901b4SOleksij Rempel ret = ksz8_r_sta_mac_table(dev, index, &alu, &valid);
1059559901b4SOleksij Rempel if (ret)
1060559901b4SOleksij Rempel return ret;
1061559901b4SOleksij Rempel if (!valid)
1062559901b4SOleksij Rempel continue;
1063559901b4SOleksij Rempel
1064559901b4SOleksij Rempel if (!memcmp(alu.mac, addr, ETH_ALEN) && alu.fid == vid)
1065980c7d17SArun Ramadoss break;
1066980c7d17SArun Ramadoss }
1067980c7d17SArun Ramadoss
1068980c7d17SArun Ramadoss /* no available entry */
1069980c7d17SArun Ramadoss if (index == dev->info->num_statics)
10703c2e6b54SOleksij Rempel return 0;
1071980c7d17SArun Ramadoss
1072980c7d17SArun Ramadoss /* clear port */
1073980c7d17SArun Ramadoss alu.port_forward &= ~BIT(port);
1074980c7d17SArun Ramadoss if (!alu.port_forward)
1075980c7d17SArun Ramadoss alu.is_static = false;
1076980c7d17SArun Ramadoss
10773c2e6b54SOleksij Rempel return ksz8_w_sta_mac_table(dev, index, &alu);
1078980c7d17SArun Ramadoss }
1079980c7d17SArun Ramadoss
ksz8_mdb_add(struct ksz_device * dev,int port,const struct switchdev_obj_port_mdb * mdb,struct dsa_db db)1080f6636ff6SOleksij Rempel int ksz8_mdb_add(struct ksz_device *dev, int port,
1081f6636ff6SOleksij Rempel const struct switchdev_obj_port_mdb *mdb, struct dsa_db db)
1082f6636ff6SOleksij Rempel {
1083f6636ff6SOleksij Rempel return ksz8_add_sta_mac(dev, port, mdb->addr, mdb->vid);
1084f6636ff6SOleksij Rempel }
1085f6636ff6SOleksij Rempel
ksz8_mdb_del(struct ksz_device * dev,int port,const struct switchdev_obj_port_mdb * mdb,struct dsa_db db)1086f6636ff6SOleksij Rempel int ksz8_mdb_del(struct ksz_device *dev, int port,
1087f6636ff6SOleksij Rempel const struct switchdev_obj_port_mdb *mdb, struct dsa_db db)
1088f6636ff6SOleksij Rempel {
1089f6636ff6SOleksij Rempel return ksz8_del_sta_mac(dev, port, mdb->addr, mdb->vid);
1090f6636ff6SOleksij Rempel }
1091f6636ff6SOleksij Rempel
ksz8_fdb_add(struct ksz_device * dev,int port,const unsigned char * addr,u16 vid,struct dsa_db db)109257795412SOleksij Rempel int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr,
109357795412SOleksij Rempel u16 vid, struct dsa_db db)
109457795412SOleksij Rempel {
109557795412SOleksij Rempel return ksz8_add_sta_mac(dev, port, addr, vid);
109657795412SOleksij Rempel }
109757795412SOleksij Rempel
ksz8_fdb_del(struct ksz_device * dev,int port,const unsigned char * addr,u16 vid,struct dsa_db db)109857795412SOleksij Rempel int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr,
109957795412SOleksij Rempel u16 vid, struct dsa_db db)
110057795412SOleksij Rempel {
110157795412SOleksij Rempel return ksz8_del_sta_mac(dev, port, addr, vid);
110257795412SOleksij Rempel }
110357795412SOleksij Rempel
ksz8_port_vlan_filtering(struct ksz_device * dev,int port,bool flag,struct netlink_ext_ack * extack)11046ec23aaaSArun Ramadoss int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag,
110589153ed6SVladimir Oltean struct netlink_ext_ack *extack)
1106e66f840cSTristram Ha {
11074b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev))
11084b20a07eSOleksij Rempel return -ENOTSUPP;
11094b20a07eSOleksij Rempel
111016484413SBen Hutchings /* Discard packets with VID not enabled on the switch */
1111e66f840cSTristram Ha ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag);
1112e66f840cSTristram Ha
111316484413SBen Hutchings /* Discard packets with VID not enabled on the ingress port */
111416484413SBen Hutchings for (port = 0; port < dev->phy_port_cnt; ++port)
111516484413SBen Hutchings ksz_port_cfg(dev, port, REG_PORT_CTRL_2, PORT_INGRESS_FILTER,
111616484413SBen Hutchings flag);
111716484413SBen Hutchings
1118e66f840cSTristram Ha return 0;
1119e66f840cSTristram Ha }
1120e66f840cSTristram Ha
ksz8_port_enable_pvid(struct ksz_device * dev,int port,bool state)1121ef3b02a1SBen Hutchings static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state)
1122ef3b02a1SBen Hutchings {
1123ef3b02a1SBen Hutchings if (ksz_is_ksz88x3(dev)) {
1124ef3b02a1SBen Hutchings ksz_cfg(dev, REG_SW_INSERT_SRC_PVID,
1125ef3b02a1SBen Hutchings 0x03 << (4 - 2 * port), state);
1126ef3b02a1SBen Hutchings } else {
1127ef3b02a1SBen Hutchings ksz_pwrite8(dev, port, REG_PORT_CTRL_12, state ? 0x0f : 0x00);
1128ef3b02a1SBen Hutchings }
1129ef3b02a1SBen Hutchings }
1130ef3b02a1SBen Hutchings
ksz8_port_vlan_add(struct ksz_device * dev,int port,const struct switchdev_obj_port_vlan * vlan,struct netlink_ext_ack * extack)11316ec23aaaSArun Ramadoss int ksz8_port_vlan_add(struct ksz_device *dev, int port,
113231046a5fSVladimir Oltean const struct switchdev_obj_port_vlan *vlan,
113331046a5fSVladimir Oltean struct netlink_ext_ack *extack)
1134e66f840cSTristram Ha {
1135e66f840cSTristram Ha bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
11368f4f58f8SBen Hutchings struct ksz_port *p = &dev->ports[port];
1137b7a9e0daSVladimir Oltean u16 data, new_pvid = 0;
1138e66f840cSTristram Ha u8 fid, member, valid;
1139e66f840cSTristram Ha
11404b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev))
11414b20a07eSOleksij Rempel return -ENOTSUPP;
11424b20a07eSOleksij Rempel
11438f4f58f8SBen Hutchings /* If a VLAN is added with untagged flag different from the
11448f4f58f8SBen Hutchings * port's Remove Tag flag, we need to change the latter.
11458f4f58f8SBen Hutchings * Ignore VID 0, which is always untagged.
11469130c2d3SBen Hutchings * Ignore CPU port, which will always be tagged.
11478f4f58f8SBen Hutchings */
11489130c2d3SBen Hutchings if (untagged != p->remove_tag && vlan->vid != 0 &&
11499130c2d3SBen Hutchings port != dev->cpu_port) {
11508f4f58f8SBen Hutchings unsigned int vid;
11518f4f58f8SBen Hutchings
11528f4f58f8SBen Hutchings /* Reject attempts to add a VLAN that requires the
11538f4f58f8SBen Hutchings * Remove Tag flag to be changed, unless there are no
11548f4f58f8SBen Hutchings * other VLANs currently configured.
11558f4f58f8SBen Hutchings */
1156462d5250SArun Ramadoss for (vid = 1; vid < dev->info->num_vlans; ++vid) {
11578f4f58f8SBen Hutchings /* Skip the VID we are going to add or reconfigure */
11588f4f58f8SBen Hutchings if (vid == vlan->vid)
11598f4f58f8SBen Hutchings continue;
11608f4f58f8SBen Hutchings
11618f4f58f8SBen Hutchings ksz8_from_vlan(dev, dev->vlan_cache[vid].table[0],
11628f4f58f8SBen Hutchings &fid, &member, &valid);
11638f4f58f8SBen Hutchings if (valid && (member & BIT(port)))
11648f4f58f8SBen Hutchings return -EINVAL;
11658f4f58f8SBen Hutchings }
11668f4f58f8SBen Hutchings
1167e66f840cSTristram Ha ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
11688f4f58f8SBen Hutchings p->remove_tag = untagged;
11698f4f58f8SBen Hutchings }
1170e66f840cSTristram Ha
11714b5baca0SMichael Grzeschik ksz8_r_vlan_table(dev, vlan->vid, &data);
11729f73e112SMichael Grzeschik ksz8_from_vlan(dev, data, &fid, &member, &valid);
1173e66f840cSTristram Ha
1174e66f840cSTristram Ha /* First time to setup the VLAN entry. */
1175e66f840cSTristram Ha if (!valid) {
1176e66f840cSTristram Ha /* Need to find a way to map VID to FID. */
1177e66f840cSTristram Ha fid = 1;
1178e66f840cSTristram Ha valid = 1;
1179e66f840cSTristram Ha }
1180e66f840cSTristram Ha member |= BIT(port);
1181e66f840cSTristram Ha
11829f73e112SMichael Grzeschik ksz8_to_vlan(dev, fid, member, valid, &data);
11834b5baca0SMichael Grzeschik ksz8_w_vlan_table(dev, vlan->vid, data);
1184e66f840cSTristram Ha
1185e66f840cSTristram Ha /* change PVID */
1186e66f840cSTristram Ha if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
1187b7a9e0daSVladimir Oltean new_pvid = vlan->vid;
1188e66f840cSTristram Ha
1189e66f840cSTristram Ha if (new_pvid) {
1190b7a9e0daSVladimir Oltean u16 vid;
1191b7a9e0daSVladimir Oltean
1192e66f840cSTristram Ha ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid);
1193ef3b02a1SBen Hutchings vid &= ~VLAN_VID_MASK;
1194e66f840cSTristram Ha vid |= new_pvid;
1195e66f840cSTristram Ha ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid);
1196ef3b02a1SBen Hutchings
1197ef3b02a1SBen Hutchings ksz8_port_enable_pvid(dev, port, true);
1198e66f840cSTristram Ha }
11991958d581SVladimir Oltean
12001958d581SVladimir Oltean return 0;
1201e66f840cSTristram Ha }
1202e66f840cSTristram Ha
ksz8_port_vlan_del(struct ksz_device * dev,int port,const struct switchdev_obj_port_vlan * vlan)12036ec23aaaSArun Ramadoss int ksz8_port_vlan_del(struct ksz_device *dev, int port,
1204e66f840cSTristram Ha const struct switchdev_obj_port_vlan *vlan)
1205e66f840cSTristram Ha {
1206ef3b02a1SBen Hutchings u16 data, pvid;
1207e66f840cSTristram Ha u8 fid, member, valid;
1208e66f840cSTristram Ha
12094b20a07eSOleksij Rempel if (ksz_is_ksz88x3(dev))
12104b20a07eSOleksij Rempel return -ENOTSUPP;
12114b20a07eSOleksij Rempel
1212e66f840cSTristram Ha ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
1213e66f840cSTristram Ha pvid = pvid & 0xFFF;
1214e66f840cSTristram Ha
12154b5baca0SMichael Grzeschik ksz8_r_vlan_table(dev, vlan->vid, &data);
12169f73e112SMichael Grzeschik ksz8_from_vlan(dev, data, &fid, &member, &valid);
1217e66f840cSTristram Ha
1218e66f840cSTristram Ha member &= ~BIT(port);
1219e66f840cSTristram Ha
1220e66f840cSTristram Ha /* Invalidate the entry if no more member. */
1221e66f840cSTristram Ha if (!member) {
1222e66f840cSTristram Ha fid = 0;
1223e66f840cSTristram Ha valid = 0;
1224e66f840cSTristram Ha }
1225e66f840cSTristram Ha
12269f73e112SMichael Grzeschik ksz8_to_vlan(dev, fid, member, valid, &data);
12274b5baca0SMichael Grzeschik ksz8_w_vlan_table(dev, vlan->vid, data);
1228e66f840cSTristram Ha
1229ef3b02a1SBen Hutchings if (pvid == vlan->vid)
1230ef3b02a1SBen Hutchings ksz8_port_enable_pvid(dev, port, false);
1231e66f840cSTristram Ha
1232e66f840cSTristram Ha return 0;
1233e66f840cSTristram Ha }
1234e66f840cSTristram Ha
ksz8_port_mirror_add(struct ksz_device * dev,int port,struct dsa_mall_mirror_tc_entry * mirror,bool ingress,struct netlink_ext_ack * extack)12356ec23aaaSArun Ramadoss int ksz8_port_mirror_add(struct ksz_device *dev, int port,
1236e66f840cSTristram Ha struct dsa_mall_mirror_tc_entry *mirror,
12370148bb50SVladimir Oltean bool ingress, struct netlink_ext_ack *extack)
1238e66f840cSTristram Ha {
1239e66f840cSTristram Ha if (ingress) {
1240e66f840cSTristram Ha ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
1241e66f840cSTristram Ha dev->mirror_rx |= BIT(port);
1242e66f840cSTristram Ha } else {
1243e66f840cSTristram Ha ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
1244e66f840cSTristram Ha dev->mirror_tx |= BIT(port);
1245e66f840cSTristram Ha }
1246e66f840cSTristram Ha
1247e66f840cSTristram Ha ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
1248e66f840cSTristram Ha
1249e66f840cSTristram Ha /* configure mirror port */
1250e66f840cSTristram Ha if (dev->mirror_rx || dev->mirror_tx)
1251e66f840cSTristram Ha ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
1252e66f840cSTristram Ha PORT_MIRROR_SNIFFER, true);
1253e66f840cSTristram Ha
1254e66f840cSTristram Ha return 0;
1255e66f840cSTristram Ha }
1256e66f840cSTristram Ha
ksz8_port_mirror_del(struct ksz_device * dev,int port,struct dsa_mall_mirror_tc_entry * mirror)12576ec23aaaSArun Ramadoss void ksz8_port_mirror_del(struct ksz_device *dev, int port,
1258e66f840cSTristram Ha struct dsa_mall_mirror_tc_entry *mirror)
1259e66f840cSTristram Ha {
1260e66f840cSTristram Ha u8 data;
1261e66f840cSTristram Ha
1262e66f840cSTristram Ha if (mirror->ingress) {
1263e66f840cSTristram Ha ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
1264e66f840cSTristram Ha dev->mirror_rx &= ~BIT(port);
1265e66f840cSTristram Ha } else {
1266e66f840cSTristram Ha ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
1267e66f840cSTristram Ha dev->mirror_tx &= ~BIT(port);
1268e66f840cSTristram Ha }
1269e66f840cSTristram Ha
1270e66f840cSTristram Ha ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
1271e66f840cSTristram Ha
1272e66f840cSTristram Ha if (!dev->mirror_rx && !dev->mirror_tx)
1273e66f840cSTristram Ha ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
1274e66f840cSTristram Ha PORT_MIRROR_SNIFFER, false);
1275e66f840cSTristram Ha }
1276e66f840cSTristram Ha
ksz8795_cpu_interface_select(struct ksz_device * dev,int port)1277c2ac4d2aSMichael Grzeschik static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port)
1278e66f840cSTristram Ha {
1279e66f840cSTristram Ha struct ksz_port *p = &dev->ports[port];
1280e66f840cSTristram Ha
1281edecfa98SHelmut Grohne if (!p->interface && dev->compat_interface) {
1282edecfa98SHelmut Grohne dev_warn(dev->dev,
1283edecfa98SHelmut Grohne "Using legacy switch \"phy-mode\" property, because it is missing on port %d node. "
1284edecfa98SHelmut Grohne "Please update your device tree.\n",
1285edecfa98SHelmut Grohne port);
1286edecfa98SHelmut Grohne p->interface = dev->compat_interface;
1287edecfa98SHelmut Grohne }
1288c2ac4d2aSMichael Grzeschik }
1289c2ac4d2aSMichael Grzeschik
ksz8_port_setup(struct ksz_device * dev,int port,bool cpu_port)12906ec23aaaSArun Ramadoss void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
1291c2ac4d2aSMichael Grzeschik {
1292b3612ccdSOleksij Rempel struct dsa_switch *ds = dev->ds;
12939f73e112SMichael Grzeschik const u32 *masks;
1294c2ac4d2aSMichael Grzeschik u8 member;
1295c2ac4d2aSMichael Grzeschik
1296d23a5e18SArun Ramadoss masks = dev->info->masks;
12979f73e112SMichael Grzeschik
1298c2ac4d2aSMichael Grzeschik /* enable broadcast storm limit */
1299c2ac4d2aSMichael Grzeschik ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
1300c2ac4d2aSMichael Grzeschik
13014b20a07eSOleksij Rempel if (!ksz_is_ksz88x3(dev))
1302c2ac4d2aSMichael Grzeschik ksz8795_set_prio_queue(dev, port, 4);
1303c2ac4d2aSMichael Grzeschik
1304c2ac4d2aSMichael Grzeschik /* disable DiffServ priority */
1305c2ac4d2aSMichael Grzeschik ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false);
1306c2ac4d2aSMichael Grzeschik
1307c2ac4d2aSMichael Grzeschik /* replace priority */
13089f73e112SMichael Grzeschik ksz_port_cfg(dev, port, P_802_1P_CTRL,
13099f73e112SMichael Grzeschik masks[PORT_802_1P_REMAPPING], false);
1310c2ac4d2aSMichael Grzeschik
1311c2ac4d2aSMichael Grzeschik /* enable 802.1p priority */
1312c2ac4d2aSMichael Grzeschik ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true);
1313c2ac4d2aSMichael Grzeschik
1314c2ac4d2aSMichael Grzeschik if (cpu_port) {
13154b20a07eSOleksij Rempel if (!ksz_is_ksz88x3(dev))
1316c2ac4d2aSMichael Grzeschik ksz8795_cpu_interface_select(dev, port);
1317e66f840cSTristram Ha
1318b3612ccdSOleksij Rempel member = dsa_user_ports(ds);
1319e66f840cSTristram Ha } else {
1320b3612ccdSOleksij Rempel member = BIT(dsa_upstream_port(ds, port));
1321e66f840cSTristram Ha }
1322b3612ccdSOleksij Rempel
13234b5baca0SMichael Grzeschik ksz8_cfg_port_member(dev, port, member);
1324e66f840cSTristram Ha }
1325e66f840cSTristram Ha
ksz8_config_cpu_port(struct dsa_switch * ds)13266ec23aaaSArun Ramadoss void ksz8_config_cpu_port(struct dsa_switch *ds)
1327e66f840cSTristram Ha {
1328e66f840cSTristram Ha struct ksz_device *dev = ds->priv;
1329e66f840cSTristram Ha struct ksz_port *p;
13309f73e112SMichael Grzeschik const u32 *masks;
1331a02579dfSArun Ramadoss const u16 *regs;
1332e66f840cSTristram Ha u8 remote;
1333e66f840cSTristram Ha int i;
1334e66f840cSTristram Ha
1335d23a5e18SArun Ramadoss masks = dev->info->masks;
1336486f9ca7SArun Ramadoss regs = dev->info->regs;
13379f73e112SMichael Grzeschik
13389f73e112SMichael Grzeschik ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true);
1339e66f840cSTristram Ha
13404b5baca0SMichael Grzeschik ksz8_port_setup(dev, dev->cpu_port, true);
1341e66f840cSTristram Ha
13424ce2a984SMichael Grzeschik for (i = 0; i < dev->phy_port_cnt; i++) {
1343e593df51SArun Ramadoss ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);
1344e66f840cSTristram Ha }
1345e66f840cSTristram Ha for (i = 0; i < dev->phy_port_cnt; i++) {
1346e66f840cSTristram Ha p = &dev->ports[i];
1347bb4609d2SOleksij Rempel
13484b20a07eSOleksij Rempel if (!ksz_is_ksz88x3(dev)) {
13499f73e112SMichael Grzeschik ksz_pread8(dev, i, regs[P_REMOTE_STATUS], &remote);
135091a98917SArun Ramadoss if (remote & KSZ8_PORT_FIBER_MODE)
1351e66f840cSTristram Ha p->fiber = 1;
13524b20a07eSOleksij Rempel }
1353e66f840cSTristram Ha if (p->fiber)
13546877102fSArun Ramadoss ksz_port_cfg(dev, i, regs[P_STP_CTRL],
13556877102fSArun Ramadoss PORT_FORCE_FLOW_CTRL, true);
1356e66f840cSTristram Ha else
13576877102fSArun Ramadoss ksz_port_cfg(dev, i, regs[P_STP_CTRL],
13586877102fSArun Ramadoss PORT_FORCE_FLOW_CTRL, false);
1359e66f840cSTristram Ha }
1360e66f840cSTristram Ha }
1361e66f840cSTristram Ha
ksz8_handle_global_errata(struct dsa_switch * ds)13627b6e6235SOleksij Rempel static int ksz8_handle_global_errata(struct dsa_switch *ds)
13637b6e6235SOleksij Rempel {
13647b6e6235SOleksij Rempel struct ksz_device *dev = ds->priv;
13657b6e6235SOleksij Rempel int ret = 0;
13667b6e6235SOleksij Rempel
13677b6e6235SOleksij Rempel /* KSZ87xx Errata DS80000687C.
13687b6e6235SOleksij Rempel * Module 2: Link drops with some EEE link partners.
13697b6e6235SOleksij Rempel * An issue with the EEE next page exchange between the
13707b6e6235SOleksij Rempel * KSZ879x/KSZ877x/KSZ876x and some EEE link partners may result in
13717b6e6235SOleksij Rempel * the link dropping.
13727b6e6235SOleksij Rempel */
1373462d5250SArun Ramadoss if (dev->info->ksz87xx_eee_link_erratum)
13747b6e6235SOleksij Rempel ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0);
13757b6e6235SOleksij Rempel
13767b6e6235SOleksij Rempel return ret;
13777b6e6235SOleksij Rempel }
13787b6e6235SOleksij Rempel
ksz8_enable_stp_addr(struct ksz_device * dev)13796ec23aaaSArun Ramadoss int ksz8_enable_stp_addr(struct ksz_device *dev)
1380331d64f7SArun Ramadoss {
1381331d64f7SArun Ramadoss struct alu_struct alu;
1382331d64f7SArun Ramadoss
1383331d64f7SArun Ramadoss /* Setup STP address for STP operation. */
1384331d64f7SArun Ramadoss memset(&alu, 0, sizeof(alu));
1385331d64f7SArun Ramadoss ether_addr_copy(alu.mac, eth_stp_addr);
1386331d64f7SArun Ramadoss alu.is_static = true;
1387331d64f7SArun Ramadoss alu.is_override = true;
1388331d64f7SArun Ramadoss alu.port_forward = dev->info->cpu_ports;
1389331d64f7SArun Ramadoss
13903c2e6b54SOleksij Rempel return ksz8_w_sta_mac_table(dev, 0, &alu);
1391331d64f7SArun Ramadoss }
1392331d64f7SArun Ramadoss
ksz8_setup(struct dsa_switch * ds)13936ec23aaaSArun Ramadoss int ksz8_setup(struct dsa_switch *ds)
1394e66f840cSTristram Ha {
1395e66f840cSTristram Ha struct ksz_device *dev = ds->priv;
1396d2822e68SArun Ramadoss int i;
1397e66f840cSTristram Ha
13986b30cfa8SOleksij Rempel ds->mtu_enforcement_ingress = true;
13996b30cfa8SOleksij Rempel
140055a952eeSOleksij Rempel /* We rely on software untagging on the CPU port, so that we
140155a952eeSOleksij Rempel * can support both tagged and untagged VLANs
140255a952eeSOleksij Rempel */
140355a952eeSOleksij Rempel ds->untag_bridge_pvid = true;
140455a952eeSOleksij Rempel
140555a952eeSOleksij Rempel /* VLAN filtering is partly controlled by the global VLAN
140655a952eeSOleksij Rempel * Enable flag
140755a952eeSOleksij Rempel */
140855a952eeSOleksij Rempel ds->vlan_filtering_is_global = true;
140955a952eeSOleksij Rempel
1410e66f840cSTristram Ha ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true);
1411e66f840cSTristram Ha
1412e66f840cSTristram Ha /* Enable automatic fast aging when link changed detected. */
1413e66f840cSTristram Ha ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true);
1414e66f840cSTristram Ha
1415e66f840cSTristram Ha /* Enable aggressive back off algorithm in half duplex mode. */
1416b8311f46SVladimir Oltean regmap_update_bits(ksz_regmap_8(dev), REG_SW_CTRL_1,
1417e66f840cSTristram Ha SW_AGGR_BACKOFF, SW_AGGR_BACKOFF);
1418e66f840cSTristram Ha
1419e66f840cSTristram Ha /*
1420e66f840cSTristram Ha * Make sure unicast VLAN boundary is set as default and
1421e66f840cSTristram Ha * enable no excessive collision drop.
1422e66f840cSTristram Ha */
1423b8311f46SVladimir Oltean regmap_update_bits(ksz_regmap_8(dev), REG_SW_CTRL_2,
1424e66f840cSTristram Ha UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP,
1425e66f840cSTristram Ha UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP);
1426e66f840cSTristram Ha
1427e66f840cSTristram Ha ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false);
1428e66f840cSTristram Ha
1429e66f840cSTristram Ha ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
1430e66f840cSTristram Ha
1431ef3b02a1SBen Hutchings if (!ksz_is_ksz88x3(dev))
1432ef3b02a1SBen Hutchings ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true);
1433ef3b02a1SBen Hutchings
1434462d5250SArun Ramadoss for (i = 0; i < (dev->info->num_vlans / 4); i++)
14354b5baca0SMichael Grzeschik ksz8_r_vlan_entries(dev, i);
1436e66f840cSTristram Ha
14377b6e6235SOleksij Rempel return ksz8_handle_global_errata(ds);
1438e66f840cSTristram Ha }
1439e66f840cSTristram Ha
ksz8_get_caps(struct ksz_device * dev,int port,struct phylink_config * config)14406ec23aaaSArun Ramadoss void ksz8_get_caps(struct ksz_device *dev, int port,
144182fdbb91SRussell King (Oracle) struct phylink_config *config)
14422c709e0bSMichael Grzeschik {
144382fdbb91SRussell King (Oracle) config->mac_capabilities = MAC_10 | MAC_100;
14442c709e0bSMichael Grzeschik
14452c709e0bSMichael Grzeschik /* Silicon Errata Sheet (DS80000830A):
14462c709e0bSMichael Grzeschik * "Port 1 does not respond to received flow control PAUSE frames"
14472c709e0bSMichael Grzeschik * So, disable Pause support on "Port 1" (port == 0) for all ksz88x3
14482c709e0bSMichael Grzeschik * switches.
14492c709e0bSMichael Grzeschik */
14502c709e0bSMichael Grzeschik if (!ksz_is_ksz88x3(dev) || port)
145182fdbb91SRussell King (Oracle) config->mac_capabilities |= MAC_SYM_PAUSE;
14522c709e0bSMichael Grzeschik
14532c709e0bSMichael Grzeschik /* Asym pause is not supported on KSZ8863 and KSZ8873 */
14542c709e0bSMichael Grzeschik if (!ksz_is_ksz88x3(dev))
145582fdbb91SRussell King (Oracle) config->mac_capabilities |= MAC_ASYM_PAUSE;
14562c709e0bSMichael Grzeschik }
14572c709e0bSMichael Grzeschik
ksz8_get_port_addr(int port,int offset)14586ec23aaaSArun Ramadoss u32 ksz8_get_port_addr(int port, int offset)
1459e66f840cSTristram Ha {
1460e66f840cSTristram Ha return PORT_CTRL_ADDR(port, offset);
1461e66f840cSTristram Ha }
1462e66f840cSTristram Ha
ksz8_switch_init(struct ksz_device * dev)14636ec23aaaSArun Ramadoss int ksz8_switch_init(struct ksz_device *dev)
1464e66f840cSTristram Ha {
1465462d5250SArun Ramadoss dev->cpu_port = fls(dev->info->cpu_ports) - 1;
1466462d5250SArun Ramadoss dev->phy_port_cnt = dev->info->port_cnt - 1;
1467462d5250SArun Ramadoss dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | dev->info->cpu_ports;
1468e66f840cSTristram Ha
1469e66f840cSTristram Ha return 0;
1470e66f840cSTristram Ha }
1471e66f840cSTristram Ha
ksz8_switch_exit(struct ksz_device * dev)14726ec23aaaSArun Ramadoss void ksz8_switch_exit(struct ksz_device *dev)
1473e66f840cSTristram Ha {
14744b5baca0SMichael Grzeschik ksz8_reset_switch(dev);
1475e66f840cSTristram Ha }
1476e66f840cSTristram Ha
1477e66f840cSTristram Ha MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>");
1478e66f840cSTristram Ha MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver");
1479e66f840cSTristram Ha MODULE_LICENSE("GPL");
1480