162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * B53 switch driver main logic 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> 562306a36Sopenharmony_ci * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 862306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 962306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1262306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1362306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1462306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1562306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1662306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1762306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/export.h> 2262306a36Sopenharmony_ci#include <linux/gpio.h> 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/platform_data/b53.h> 2662306a36Sopenharmony_ci#include <linux/phy.h> 2762306a36Sopenharmony_ci#include <linux/phylink.h> 2862306a36Sopenharmony_ci#include <linux/etherdevice.h> 2962306a36Sopenharmony_ci#include <linux/if_bridge.h> 3062306a36Sopenharmony_ci#include <net/dsa.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "b53_regs.h" 3362306a36Sopenharmony_ci#include "b53_priv.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct b53_mib_desc { 3662306a36Sopenharmony_ci u8 size; 3762306a36Sopenharmony_ci u8 offset; 3862306a36Sopenharmony_ci const char *name; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* BCM5365 MIB counters */ 4262306a36Sopenharmony_cistatic const struct b53_mib_desc b53_mibs_65[] = { 4362306a36Sopenharmony_ci { 8, 0x00, "TxOctets" }, 4462306a36Sopenharmony_ci { 4, 0x08, "TxDropPkts" }, 4562306a36Sopenharmony_ci { 4, 0x10, "TxBroadcastPkts" }, 4662306a36Sopenharmony_ci { 4, 0x14, "TxMulticastPkts" }, 4762306a36Sopenharmony_ci { 4, 0x18, "TxUnicastPkts" }, 4862306a36Sopenharmony_ci { 4, 0x1c, "TxCollisions" }, 4962306a36Sopenharmony_ci { 4, 0x20, "TxSingleCollision" }, 5062306a36Sopenharmony_ci { 4, 0x24, "TxMultipleCollision" }, 5162306a36Sopenharmony_ci { 4, 0x28, "TxDeferredTransmit" }, 5262306a36Sopenharmony_ci { 4, 0x2c, "TxLateCollision" }, 5362306a36Sopenharmony_ci { 4, 0x30, "TxExcessiveCollision" }, 5462306a36Sopenharmony_ci { 4, 0x38, "TxPausePkts" }, 5562306a36Sopenharmony_ci { 8, 0x44, "RxOctets" }, 5662306a36Sopenharmony_ci { 4, 0x4c, "RxUndersizePkts" }, 5762306a36Sopenharmony_ci { 4, 0x50, "RxPausePkts" }, 5862306a36Sopenharmony_ci { 4, 0x54, "Pkts64Octets" }, 5962306a36Sopenharmony_ci { 4, 0x58, "Pkts65to127Octets" }, 6062306a36Sopenharmony_ci { 4, 0x5c, "Pkts128to255Octets" }, 6162306a36Sopenharmony_ci { 4, 0x60, "Pkts256to511Octets" }, 6262306a36Sopenharmony_ci { 4, 0x64, "Pkts512to1023Octets" }, 6362306a36Sopenharmony_ci { 4, 0x68, "Pkts1024to1522Octets" }, 6462306a36Sopenharmony_ci { 4, 0x6c, "RxOversizePkts" }, 6562306a36Sopenharmony_ci { 4, 0x70, "RxJabbers" }, 6662306a36Sopenharmony_ci { 4, 0x74, "RxAlignmentErrors" }, 6762306a36Sopenharmony_ci { 4, 0x78, "RxFCSErrors" }, 6862306a36Sopenharmony_ci { 8, 0x7c, "RxGoodOctets" }, 6962306a36Sopenharmony_ci { 4, 0x84, "RxDropPkts" }, 7062306a36Sopenharmony_ci { 4, 0x88, "RxUnicastPkts" }, 7162306a36Sopenharmony_ci { 4, 0x8c, "RxMulticastPkts" }, 7262306a36Sopenharmony_ci { 4, 0x90, "RxBroadcastPkts" }, 7362306a36Sopenharmony_ci { 4, 0x94, "RxSAChanges" }, 7462306a36Sopenharmony_ci { 4, 0x98, "RxFragments" }, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define B53_MIBS_65_SIZE ARRAY_SIZE(b53_mibs_65) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* BCM63xx MIB counters */ 8062306a36Sopenharmony_cistatic const struct b53_mib_desc b53_mibs_63xx[] = { 8162306a36Sopenharmony_ci { 8, 0x00, "TxOctets" }, 8262306a36Sopenharmony_ci { 4, 0x08, "TxDropPkts" }, 8362306a36Sopenharmony_ci { 4, 0x0c, "TxQoSPkts" }, 8462306a36Sopenharmony_ci { 4, 0x10, "TxBroadcastPkts" }, 8562306a36Sopenharmony_ci { 4, 0x14, "TxMulticastPkts" }, 8662306a36Sopenharmony_ci { 4, 0x18, "TxUnicastPkts" }, 8762306a36Sopenharmony_ci { 4, 0x1c, "TxCollisions" }, 8862306a36Sopenharmony_ci { 4, 0x20, "TxSingleCollision" }, 8962306a36Sopenharmony_ci { 4, 0x24, "TxMultipleCollision" }, 9062306a36Sopenharmony_ci { 4, 0x28, "TxDeferredTransmit" }, 9162306a36Sopenharmony_ci { 4, 0x2c, "TxLateCollision" }, 9262306a36Sopenharmony_ci { 4, 0x30, "TxExcessiveCollision" }, 9362306a36Sopenharmony_ci { 4, 0x38, "TxPausePkts" }, 9462306a36Sopenharmony_ci { 8, 0x3c, "TxQoSOctets" }, 9562306a36Sopenharmony_ci { 8, 0x44, "RxOctets" }, 9662306a36Sopenharmony_ci { 4, 0x4c, "RxUndersizePkts" }, 9762306a36Sopenharmony_ci { 4, 0x50, "RxPausePkts" }, 9862306a36Sopenharmony_ci { 4, 0x54, "Pkts64Octets" }, 9962306a36Sopenharmony_ci { 4, 0x58, "Pkts65to127Octets" }, 10062306a36Sopenharmony_ci { 4, 0x5c, "Pkts128to255Octets" }, 10162306a36Sopenharmony_ci { 4, 0x60, "Pkts256to511Octets" }, 10262306a36Sopenharmony_ci { 4, 0x64, "Pkts512to1023Octets" }, 10362306a36Sopenharmony_ci { 4, 0x68, "Pkts1024to1522Octets" }, 10462306a36Sopenharmony_ci { 4, 0x6c, "RxOversizePkts" }, 10562306a36Sopenharmony_ci { 4, 0x70, "RxJabbers" }, 10662306a36Sopenharmony_ci { 4, 0x74, "RxAlignmentErrors" }, 10762306a36Sopenharmony_ci { 4, 0x78, "RxFCSErrors" }, 10862306a36Sopenharmony_ci { 8, 0x7c, "RxGoodOctets" }, 10962306a36Sopenharmony_ci { 4, 0x84, "RxDropPkts" }, 11062306a36Sopenharmony_ci { 4, 0x88, "RxUnicastPkts" }, 11162306a36Sopenharmony_ci { 4, 0x8c, "RxMulticastPkts" }, 11262306a36Sopenharmony_ci { 4, 0x90, "RxBroadcastPkts" }, 11362306a36Sopenharmony_ci { 4, 0x94, "RxSAChanges" }, 11462306a36Sopenharmony_ci { 4, 0x98, "RxFragments" }, 11562306a36Sopenharmony_ci { 4, 0xa0, "RxSymbolErrors" }, 11662306a36Sopenharmony_ci { 4, 0xa4, "RxQoSPkts" }, 11762306a36Sopenharmony_ci { 8, 0xa8, "RxQoSOctets" }, 11862306a36Sopenharmony_ci { 4, 0xb0, "Pkts1523to2047Octets" }, 11962306a36Sopenharmony_ci { 4, 0xb4, "Pkts2048to4095Octets" }, 12062306a36Sopenharmony_ci { 4, 0xb8, "Pkts4096to8191Octets" }, 12162306a36Sopenharmony_ci { 4, 0xbc, "Pkts8192to9728Octets" }, 12262306a36Sopenharmony_ci { 4, 0xc0, "RxDiscarded" }, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define B53_MIBS_63XX_SIZE ARRAY_SIZE(b53_mibs_63xx) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* MIB counters */ 12862306a36Sopenharmony_cistatic const struct b53_mib_desc b53_mibs[] = { 12962306a36Sopenharmony_ci { 8, 0x00, "TxOctets" }, 13062306a36Sopenharmony_ci { 4, 0x08, "TxDropPkts" }, 13162306a36Sopenharmony_ci { 4, 0x10, "TxBroadcastPkts" }, 13262306a36Sopenharmony_ci { 4, 0x14, "TxMulticastPkts" }, 13362306a36Sopenharmony_ci { 4, 0x18, "TxUnicastPkts" }, 13462306a36Sopenharmony_ci { 4, 0x1c, "TxCollisions" }, 13562306a36Sopenharmony_ci { 4, 0x20, "TxSingleCollision" }, 13662306a36Sopenharmony_ci { 4, 0x24, "TxMultipleCollision" }, 13762306a36Sopenharmony_ci { 4, 0x28, "TxDeferredTransmit" }, 13862306a36Sopenharmony_ci { 4, 0x2c, "TxLateCollision" }, 13962306a36Sopenharmony_ci { 4, 0x30, "TxExcessiveCollision" }, 14062306a36Sopenharmony_ci { 4, 0x38, "TxPausePkts" }, 14162306a36Sopenharmony_ci { 8, 0x50, "RxOctets" }, 14262306a36Sopenharmony_ci { 4, 0x58, "RxUndersizePkts" }, 14362306a36Sopenharmony_ci { 4, 0x5c, "RxPausePkts" }, 14462306a36Sopenharmony_ci { 4, 0x60, "Pkts64Octets" }, 14562306a36Sopenharmony_ci { 4, 0x64, "Pkts65to127Octets" }, 14662306a36Sopenharmony_ci { 4, 0x68, "Pkts128to255Octets" }, 14762306a36Sopenharmony_ci { 4, 0x6c, "Pkts256to511Octets" }, 14862306a36Sopenharmony_ci { 4, 0x70, "Pkts512to1023Octets" }, 14962306a36Sopenharmony_ci { 4, 0x74, "Pkts1024to1522Octets" }, 15062306a36Sopenharmony_ci { 4, 0x78, "RxOversizePkts" }, 15162306a36Sopenharmony_ci { 4, 0x7c, "RxJabbers" }, 15262306a36Sopenharmony_ci { 4, 0x80, "RxAlignmentErrors" }, 15362306a36Sopenharmony_ci { 4, 0x84, "RxFCSErrors" }, 15462306a36Sopenharmony_ci { 8, 0x88, "RxGoodOctets" }, 15562306a36Sopenharmony_ci { 4, 0x90, "RxDropPkts" }, 15662306a36Sopenharmony_ci { 4, 0x94, "RxUnicastPkts" }, 15762306a36Sopenharmony_ci { 4, 0x98, "RxMulticastPkts" }, 15862306a36Sopenharmony_ci { 4, 0x9c, "RxBroadcastPkts" }, 15962306a36Sopenharmony_ci { 4, 0xa0, "RxSAChanges" }, 16062306a36Sopenharmony_ci { 4, 0xa4, "RxFragments" }, 16162306a36Sopenharmony_ci { 4, 0xa8, "RxJumboPkts" }, 16262306a36Sopenharmony_ci { 4, 0xac, "RxSymbolErrors" }, 16362306a36Sopenharmony_ci { 4, 0xc0, "RxDiscarded" }, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define B53_MIBS_SIZE ARRAY_SIZE(b53_mibs) 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic const struct b53_mib_desc b53_mibs_58xx[] = { 16962306a36Sopenharmony_ci { 8, 0x00, "TxOctets" }, 17062306a36Sopenharmony_ci { 4, 0x08, "TxDropPkts" }, 17162306a36Sopenharmony_ci { 4, 0x0c, "TxQPKTQ0" }, 17262306a36Sopenharmony_ci { 4, 0x10, "TxBroadcastPkts" }, 17362306a36Sopenharmony_ci { 4, 0x14, "TxMulticastPkts" }, 17462306a36Sopenharmony_ci { 4, 0x18, "TxUnicastPKts" }, 17562306a36Sopenharmony_ci { 4, 0x1c, "TxCollisions" }, 17662306a36Sopenharmony_ci { 4, 0x20, "TxSingleCollision" }, 17762306a36Sopenharmony_ci { 4, 0x24, "TxMultipleCollision" }, 17862306a36Sopenharmony_ci { 4, 0x28, "TxDeferredCollision" }, 17962306a36Sopenharmony_ci { 4, 0x2c, "TxLateCollision" }, 18062306a36Sopenharmony_ci { 4, 0x30, "TxExcessiveCollision" }, 18162306a36Sopenharmony_ci { 4, 0x34, "TxFrameInDisc" }, 18262306a36Sopenharmony_ci { 4, 0x38, "TxPausePkts" }, 18362306a36Sopenharmony_ci { 4, 0x3c, "TxQPKTQ1" }, 18462306a36Sopenharmony_ci { 4, 0x40, "TxQPKTQ2" }, 18562306a36Sopenharmony_ci { 4, 0x44, "TxQPKTQ3" }, 18662306a36Sopenharmony_ci { 4, 0x48, "TxQPKTQ4" }, 18762306a36Sopenharmony_ci { 4, 0x4c, "TxQPKTQ5" }, 18862306a36Sopenharmony_ci { 8, 0x50, "RxOctets" }, 18962306a36Sopenharmony_ci { 4, 0x58, "RxUndersizePkts" }, 19062306a36Sopenharmony_ci { 4, 0x5c, "RxPausePkts" }, 19162306a36Sopenharmony_ci { 4, 0x60, "RxPkts64Octets" }, 19262306a36Sopenharmony_ci { 4, 0x64, "RxPkts65to127Octets" }, 19362306a36Sopenharmony_ci { 4, 0x68, "RxPkts128to255Octets" }, 19462306a36Sopenharmony_ci { 4, 0x6c, "RxPkts256to511Octets" }, 19562306a36Sopenharmony_ci { 4, 0x70, "RxPkts512to1023Octets" }, 19662306a36Sopenharmony_ci { 4, 0x74, "RxPkts1024toMaxPktsOctets" }, 19762306a36Sopenharmony_ci { 4, 0x78, "RxOversizePkts" }, 19862306a36Sopenharmony_ci { 4, 0x7c, "RxJabbers" }, 19962306a36Sopenharmony_ci { 4, 0x80, "RxAlignmentErrors" }, 20062306a36Sopenharmony_ci { 4, 0x84, "RxFCSErrors" }, 20162306a36Sopenharmony_ci { 8, 0x88, "RxGoodOctets" }, 20262306a36Sopenharmony_ci { 4, 0x90, "RxDropPkts" }, 20362306a36Sopenharmony_ci { 4, 0x94, "RxUnicastPkts" }, 20462306a36Sopenharmony_ci { 4, 0x98, "RxMulticastPkts" }, 20562306a36Sopenharmony_ci { 4, 0x9c, "RxBroadcastPkts" }, 20662306a36Sopenharmony_ci { 4, 0xa0, "RxSAChanges" }, 20762306a36Sopenharmony_ci { 4, 0xa4, "RxFragments" }, 20862306a36Sopenharmony_ci { 4, 0xa8, "RxJumboPkt" }, 20962306a36Sopenharmony_ci { 4, 0xac, "RxSymblErr" }, 21062306a36Sopenharmony_ci { 4, 0xb0, "InRangeErrCount" }, 21162306a36Sopenharmony_ci { 4, 0xb4, "OutRangeErrCount" }, 21262306a36Sopenharmony_ci { 4, 0xb8, "EEELpiEvent" }, 21362306a36Sopenharmony_ci { 4, 0xbc, "EEELpiDuration" }, 21462306a36Sopenharmony_ci { 4, 0xc0, "RxDiscard" }, 21562306a36Sopenharmony_ci { 4, 0xc8, "TxQPKTQ6" }, 21662306a36Sopenharmony_ci { 4, 0xcc, "TxQPKTQ7" }, 21762306a36Sopenharmony_ci { 4, 0xd0, "TxPkts64Octets" }, 21862306a36Sopenharmony_ci { 4, 0xd4, "TxPkts65to127Octets" }, 21962306a36Sopenharmony_ci { 4, 0xd8, "TxPkts128to255Octets" }, 22062306a36Sopenharmony_ci { 4, 0xdc, "TxPkts256to511Ocets" }, 22162306a36Sopenharmony_ci { 4, 0xe0, "TxPkts512to1023Ocets" }, 22262306a36Sopenharmony_ci { 4, 0xe4, "TxPkts1024toMaxPktOcets" }, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci#define B53_MIBS_58XX_SIZE ARRAY_SIZE(b53_mibs_58xx) 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int b53_do_vlan_op(struct b53_device *dev, u8 op) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci unsigned int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 23462306a36Sopenharmony_ci u8 vta; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta); 23762306a36Sopenharmony_ci if (!(vta & VTA_START_CMD)) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci usleep_range(100, 200); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return -EIO; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void b53_set_vlan_entry(struct b53_device *dev, u16 vid, 24762306a36Sopenharmony_ci struct b53_vlan *vlan) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci if (is5325(dev)) { 25062306a36Sopenharmony_ci u32 entry = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (vlan->members) { 25362306a36Sopenharmony_ci entry = ((vlan->untag & VA_UNTAG_MASK_25) << 25462306a36Sopenharmony_ci VA_UNTAG_S_25) | vlan->members; 25562306a36Sopenharmony_ci if (dev->core_rev >= 3) 25662306a36Sopenharmony_ci entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S; 25762306a36Sopenharmony_ci else 25862306a36Sopenharmony_ci entry |= VA_VALID_25; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry); 26262306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | 26362306a36Sopenharmony_ci VTA_RW_STATE_WR | VTA_RW_OP_EN); 26462306a36Sopenharmony_ci } else if (is5365(dev)) { 26562306a36Sopenharmony_ci u16 entry = 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (vlan->members) 26862306a36Sopenharmony_ci entry = ((vlan->untag & VA_UNTAG_MASK_65) << 26962306a36Sopenharmony_ci VA_UNTAG_S_65) | vlan->members | VA_VALID_65; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry); 27262306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | 27362306a36Sopenharmony_ci VTA_RW_STATE_WR | VTA_RW_OP_EN); 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); 27662306a36Sopenharmony_ci b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], 27762306a36Sopenharmony_ci (vlan->untag << VTE_UNTAG_S) | vlan->members); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci b53_do_vlan_op(dev, VTA_CMD_WRITE); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dev_dbg(dev->ds->dev, "VID: %d, members: 0x%04x, untag: 0x%04x\n", 28362306a36Sopenharmony_ci vid, vlan->members, vlan->untag); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void b53_get_vlan_entry(struct b53_device *dev, u16 vid, 28762306a36Sopenharmony_ci struct b53_vlan *vlan) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci if (is5325(dev)) { 29062306a36Sopenharmony_ci u32 entry = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid | 29362306a36Sopenharmony_ci VTA_RW_STATE_RD | VTA_RW_OP_EN); 29462306a36Sopenharmony_ci b53_read32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, &entry); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (dev->core_rev >= 3) 29762306a36Sopenharmony_ci vlan->valid = !!(entry & VA_VALID_25_R4); 29862306a36Sopenharmony_ci else 29962306a36Sopenharmony_ci vlan->valid = !!(entry & VA_VALID_25); 30062306a36Sopenharmony_ci vlan->members = entry & VA_MEMBER_MASK; 30162306a36Sopenharmony_ci vlan->untag = (entry >> VA_UNTAG_S_25) & VA_UNTAG_MASK_25; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci } else if (is5365(dev)) { 30462306a36Sopenharmony_ci u16 entry = 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid | 30762306a36Sopenharmony_ci VTA_RW_STATE_WR | VTA_RW_OP_EN); 30862306a36Sopenharmony_ci b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, &entry); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci vlan->valid = !!(entry & VA_VALID_65); 31162306a36Sopenharmony_ci vlan->members = entry & VA_MEMBER_MASK; 31262306a36Sopenharmony_ci vlan->untag = (entry >> VA_UNTAG_S_65) & VA_UNTAG_MASK_65; 31362306a36Sopenharmony_ci } else { 31462306a36Sopenharmony_ci u32 entry = 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid); 31762306a36Sopenharmony_ci b53_do_vlan_op(dev, VTA_CMD_READ); 31862306a36Sopenharmony_ci b53_read32(dev, B53_ARLIO_PAGE, dev->vta_regs[2], &entry); 31962306a36Sopenharmony_ci vlan->members = entry & VTE_MEMBERS; 32062306a36Sopenharmony_ci vlan->untag = (entry >> VTE_UNTAG_S) & VTE_MEMBERS; 32162306a36Sopenharmony_ci vlan->valid = true; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void b53_set_forwarding(struct b53_device *dev, int enable) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci u8 mgmt; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (enable) 33262306a36Sopenharmony_ci mgmt |= SM_SW_FWD_EN; 33362306a36Sopenharmony_ci else 33462306a36Sopenharmony_ci mgmt &= ~SM_SW_FWD_EN; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Include IMP port in dumb forwarding mode 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt); 34162306a36Sopenharmony_ci mgmt |= B53_MII_DUMB_FWDG_EN; 34262306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Look at B53_UC_FWD_EN and B53_MC_FWD_EN to decide whether 34562306a36Sopenharmony_ci * frames should be flooded or not. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt); 34862306a36Sopenharmony_ci mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IPMC_FWD_EN; 34962306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void b53_enable_vlan(struct b53_device *dev, int port, bool enable, 35362306a36Sopenharmony_ci bool enable_filtering) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci u8 mgmt, vc0, vc1, vc4 = 0, vc5; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); 35862306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0); 35962306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) { 36262306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); 36362306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5); 36462306a36Sopenharmony_ci } else if (is63xx(dev)) { 36562306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4); 36662306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5); 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4); 36962306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (enable) { 37362306a36Sopenharmony_ci vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID; 37462306a36Sopenharmony_ci vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN; 37562306a36Sopenharmony_ci vc4 &= ~VC4_ING_VID_CHECK_MASK; 37662306a36Sopenharmony_ci if (enable_filtering) { 37762306a36Sopenharmony_ci vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S; 37862306a36Sopenharmony_ci vc5 |= VC5_DROP_VTABLE_MISS; 37962306a36Sopenharmony_ci } else { 38062306a36Sopenharmony_ci vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; 38162306a36Sopenharmony_ci vc5 &= ~VC5_DROP_VTABLE_MISS; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (is5325(dev)) 38562306a36Sopenharmony_ci vc0 &= ~VC0_RESERVED_1; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 38862306a36Sopenharmony_ci vc1 |= VC1_RX_MCST_TAG_EN; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID); 39262306a36Sopenharmony_ci vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN); 39362306a36Sopenharmony_ci vc4 &= ~VC4_ING_VID_CHECK_MASK; 39462306a36Sopenharmony_ci vc5 &= ~VC5_DROP_VTABLE_MISS; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 39762306a36Sopenharmony_ci vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S; 39862306a36Sopenharmony_ci else 39962306a36Sopenharmony_ci vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 40262306a36Sopenharmony_ci vc1 &= ~VC1_RX_MCST_TAG_EN; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!is5325(dev) && !is5365(dev)) 40662306a36Sopenharmony_ci vc5 &= ~VC5_VID_FFF_EN; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0); 40962306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) { 41262306a36Sopenharmony_ci /* enable the high 8 bit vid check on 5325 */ 41362306a36Sopenharmony_ci if (is5325(dev) && enable) 41462306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 41562306a36Sopenharmony_ci VC3_HIGH_8BIT_EN); 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4); 42062306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5); 42162306a36Sopenharmony_ci } else if (is63xx(dev)) { 42262306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0); 42362306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4); 42462306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5); 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0); 42762306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4); 42862306a36Sopenharmony_ci b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci dev->vlan_enabled = enable; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci dev_dbg(dev->dev, "Port %d VLAN enabled: %d, filtering: %d\n", 43662306a36Sopenharmony_ci port, enable, enable_filtering); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci u32 port_mask = 0; 44262306a36Sopenharmony_ci u16 max_size = JMS_MIN_SIZE; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (enable) { 44862306a36Sopenharmony_ci port_mask = dev->enabled_ports; 44962306a36Sopenharmony_ci max_size = JMS_MAX_SIZE; 45062306a36Sopenharmony_ci if (allow_10_100) 45162306a36Sopenharmony_ci port_mask |= JPM_10_100_JUMBO_EN; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask); 45562306a36Sopenharmony_ci return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int b53_flush_arl(struct b53_device *dev, u8 mask) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci unsigned int i; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, 46362306a36Sopenharmony_ci FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 46662306a36Sopenharmony_ci u8 fast_age_ctrl; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, 46962306a36Sopenharmony_ci &fast_age_ctrl); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (!(fast_age_ctrl & FAST_AGE_DONE)) 47262306a36Sopenharmony_ci goto out; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci msleep(1); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return -ETIMEDOUT; 47862306a36Sopenharmony_ciout: 47962306a36Sopenharmony_ci /* Only age dynamic entries (default behavior) */ 48062306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DYNAMIC); 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int b53_fast_age_port(struct b53_device *dev, int port) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_PORT_CTRL, port); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return b53_flush_arl(dev, FAST_AGE_PORT); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int b53_fast_age_vlan(struct b53_device *dev, u16 vid) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci b53_write16(dev, B53_CTRL_PAGE, B53_FAST_AGE_VID_CTRL, vid); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return b53_flush_arl(dev, FAST_AGE_VLAN); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_civoid b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 50162306a36Sopenharmony_ci unsigned int i; 50262306a36Sopenharmony_ci u16 pvlan; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* Enable the IMP port to be in the same VLAN as the other ports 50562306a36Sopenharmony_ci * on a per-port basis such that we only have Port i and IMP in 50662306a36Sopenharmony_ci * the same VLAN. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci b53_for_each_port(dev, i) { 50962306a36Sopenharmony_ci b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), &pvlan); 51062306a36Sopenharmony_ci pvlan |= BIT(cpu_port); 51162306a36Sopenharmony_ci b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), pvlan); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ciEXPORT_SYMBOL(b53_imp_vlan_setup); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void b53_port_set_ucast_flood(struct b53_device *dev, int port, 51762306a36Sopenharmony_ci bool unicast) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci u16 uc; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci b53_read16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, &uc); 52262306a36Sopenharmony_ci if (unicast) 52362306a36Sopenharmony_ci uc |= BIT(port); 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci uc &= ~BIT(port); 52662306a36Sopenharmony_ci b53_write16(dev, B53_CTRL_PAGE, B53_UC_FLOOD_MASK, uc); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic void b53_port_set_mcast_flood(struct b53_device *dev, int port, 53062306a36Sopenharmony_ci bool multicast) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci u16 mc; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci b53_read16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, &mc); 53562306a36Sopenharmony_ci if (multicast) 53662306a36Sopenharmony_ci mc |= BIT(port); 53762306a36Sopenharmony_ci else 53862306a36Sopenharmony_ci mc &= ~BIT(port); 53962306a36Sopenharmony_ci b53_write16(dev, B53_CTRL_PAGE, B53_MC_FLOOD_MASK, mc); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci b53_read16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, &mc); 54262306a36Sopenharmony_ci if (multicast) 54362306a36Sopenharmony_ci mc |= BIT(port); 54462306a36Sopenharmony_ci else 54562306a36Sopenharmony_ci mc &= ~BIT(port); 54662306a36Sopenharmony_ci b53_write16(dev, B53_CTRL_PAGE, B53_IPMC_FLOOD_MASK, mc); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void b53_port_set_learning(struct b53_device *dev, int port, 55062306a36Sopenharmony_ci bool learning) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci u16 reg; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci b53_read16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, ®); 55562306a36Sopenharmony_ci if (learning) 55662306a36Sopenharmony_ci reg &= ~BIT(port); 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci reg |= BIT(port); 55962306a36Sopenharmony_ci b53_write16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, reg); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciint b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 56562306a36Sopenharmony_ci unsigned int cpu_port; 56662306a36Sopenharmony_ci int ret = 0; 56762306a36Sopenharmony_ci u16 pvlan; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci cpu_port = dsa_to_port(ds, port)->cpu_dp->index; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci b53_port_set_ucast_flood(dev, port, true); 57562306a36Sopenharmony_ci b53_port_set_mcast_flood(dev, port, true); 57662306a36Sopenharmony_ci b53_port_set_learning(dev, port, false); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (dev->ops->irq_enable) 57962306a36Sopenharmony_ci ret = dev->ops->irq_enable(dev, port); 58062306a36Sopenharmony_ci if (ret) 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Clear the Rx and Tx disable bits and set to no spanning tree */ 58462306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Set this port, and only this one to be in the default VLAN, 58762306a36Sopenharmony_ci * if member of a bridge, restore its membership prior to 58862306a36Sopenharmony_ci * bringing down this port. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); 59162306a36Sopenharmony_ci pvlan &= ~0x1ff; 59262306a36Sopenharmony_ci pvlan |= BIT(port); 59362306a36Sopenharmony_ci pvlan |= dev->ports[port].vlan_ctl_mask; 59462306a36Sopenharmony_ci b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci b53_imp_vlan_setup(ds, cpu_port); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* If EEE was enabled, restore it */ 59962306a36Sopenharmony_ci if (dev->ports[port].eee.eee_enabled) 60062306a36Sopenharmony_ci b53_eee_enable_set(ds, port, true); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ciEXPORT_SYMBOL(b53_enable_port); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_civoid b53_disable_port(struct dsa_switch *ds, int port) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 60962306a36Sopenharmony_ci u8 reg; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Disable Tx/Rx for the port */ 61262306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), ®); 61362306a36Sopenharmony_ci reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE; 61462306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (dev->ops->irq_disable) 61762306a36Sopenharmony_ci dev->ops->irq_disable(dev, port); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ciEXPORT_SYMBOL(b53_disable_port); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_civoid b53_brcm_hdr_setup(struct dsa_switch *ds, int port) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 62462306a36Sopenharmony_ci bool tag_en = !(dev->tag_protocol == DSA_TAG_PROTO_NONE); 62562306a36Sopenharmony_ci u8 hdr_ctl, val; 62662306a36Sopenharmony_ci u16 reg; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Resolve which bit controls the Broadcom tag */ 62962306a36Sopenharmony_ci switch (port) { 63062306a36Sopenharmony_ci case 8: 63162306a36Sopenharmony_ci val = BRCM_HDR_P8_EN; 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci case 7: 63462306a36Sopenharmony_ci val = BRCM_HDR_P7_EN; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case 5: 63762306a36Sopenharmony_ci val = BRCM_HDR_P5_EN; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci default: 64062306a36Sopenharmony_ci val = 0; 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* Enable management mode if tagging is requested */ 64562306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &hdr_ctl); 64662306a36Sopenharmony_ci if (tag_en) 64762306a36Sopenharmony_ci hdr_ctl |= SM_SW_FWD_MODE; 64862306a36Sopenharmony_ci else 64962306a36Sopenharmony_ci hdr_ctl &= ~SM_SW_FWD_MODE; 65062306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, hdr_ctl); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Configure the appropriate IMP port */ 65362306a36Sopenharmony_ci b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &hdr_ctl); 65462306a36Sopenharmony_ci if (port == 8) 65562306a36Sopenharmony_ci hdr_ctl |= GC_FRM_MGMT_PORT_MII; 65662306a36Sopenharmony_ci else if (port == 5) 65762306a36Sopenharmony_ci hdr_ctl |= GC_FRM_MGMT_PORT_M; 65862306a36Sopenharmony_ci b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, hdr_ctl); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* Enable Broadcom tags for IMP port */ 66162306a36Sopenharmony_ci b53_read8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, &hdr_ctl); 66262306a36Sopenharmony_ci if (tag_en) 66362306a36Sopenharmony_ci hdr_ctl |= val; 66462306a36Sopenharmony_ci else 66562306a36Sopenharmony_ci hdr_ctl &= ~val; 66662306a36Sopenharmony_ci b53_write8(dev, B53_MGMT_PAGE, B53_BRCM_HDR, hdr_ctl); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Registers below are only accessible on newer devices */ 66962306a36Sopenharmony_ci if (!is58xx(dev)) 67062306a36Sopenharmony_ci return; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Enable reception Broadcom tag for CPU TX (switch RX) to 67362306a36Sopenharmony_ci * allow us to tag outgoing frames 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_RX_DIS, ®); 67662306a36Sopenharmony_ci if (tag_en) 67762306a36Sopenharmony_ci reg &= ~BIT(port); 67862306a36Sopenharmony_ci else 67962306a36Sopenharmony_ci reg |= BIT(port); 68062306a36Sopenharmony_ci b53_write16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_RX_DIS, reg); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Enable transmission of Broadcom tags from the switch (CPU RX) to 68362306a36Sopenharmony_ci * allow delivering frames to the per-port net_devices 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_TX_DIS, ®); 68662306a36Sopenharmony_ci if (tag_en) 68762306a36Sopenharmony_ci reg &= ~BIT(port); 68862306a36Sopenharmony_ci else 68962306a36Sopenharmony_ci reg |= BIT(port); 69062306a36Sopenharmony_ci b53_write16(dev, B53_MGMT_PAGE, B53_BRCM_HDR_TX_DIS, reg); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ciEXPORT_SYMBOL(b53_brcm_hdr_setup); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic void b53_enable_cpu_port(struct b53_device *dev, int port) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci u8 port_ctrl; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* BCM5325 CPU port is at 8 */ 69962306a36Sopenharmony_ci if ((is5325(dev) || is5365(dev)) && port == B53_CPU_PORT_25) 70062306a36Sopenharmony_ci port = B53_CPU_PORT; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci port_ctrl = PORT_CTRL_RX_BCST_EN | 70362306a36Sopenharmony_ci PORT_CTRL_RX_MCST_EN | 70462306a36Sopenharmony_ci PORT_CTRL_RX_UCST_EN; 70562306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), port_ctrl); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci b53_brcm_hdr_setup(dev->ds, port); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci b53_port_set_ucast_flood(dev, port, true); 71062306a36Sopenharmony_ci b53_port_set_mcast_flood(dev, port, true); 71162306a36Sopenharmony_ci b53_port_set_learning(dev, port, false); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic void b53_enable_mib(struct b53_device *dev) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci u8 gc; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc); 71962306a36Sopenharmony_ci gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN); 72062306a36Sopenharmony_ci b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic u16 b53_default_pvid(struct b53_device *dev) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 72662306a36Sopenharmony_ci return 1; 72762306a36Sopenharmony_ci else 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ciint b53_configure_vlan(struct dsa_switch *ds) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 74162306a36Sopenharmony_ci struct b53_vlan vl = { 0 }; 74262306a36Sopenharmony_ci struct b53_vlan *v; 74362306a36Sopenharmony_ci int i, def_vid; 74462306a36Sopenharmony_ci u16 vid; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci def_vid = b53_default_pvid(dev); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* clear all vlan entries */ 74962306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) { 75062306a36Sopenharmony_ci for (i = def_vid; i < dev->num_vlans; i++) 75162306a36Sopenharmony_ci b53_set_vlan_entry(dev, i, &vl); 75262306a36Sopenharmony_ci } else { 75362306a36Sopenharmony_ci b53_do_vlan_op(dev, VTA_CMD_CLEAR); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci b53_enable_vlan(dev, -1, dev->vlan_enabled, ds->vlan_filtering); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Create an untagged VLAN entry for the default PVID in case 75962306a36Sopenharmony_ci * CONFIG_VLAN_8021Q is disabled and there are no calls to 76062306a36Sopenharmony_ci * dsa_slave_vlan_rx_add_vid() to create the default VLAN 76162306a36Sopenharmony_ci * entry. Do this only when the tagging protocol is not 76262306a36Sopenharmony_ci * DSA_TAG_PROTO_NONE 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_ci b53_for_each_port(dev, i) { 76562306a36Sopenharmony_ci v = &dev->vlans[def_vid]; 76662306a36Sopenharmony_ci v->members |= BIT(i); 76762306a36Sopenharmony_ci if (!b53_vlan_port_needs_forced_tagged(ds, i)) 76862306a36Sopenharmony_ci v->untag = v->members; 76962306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, 77062306a36Sopenharmony_ci B53_VLAN_PORT_DEF_TAG(i), def_vid); 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* Upon initial call we have not set-up any VLANs, but upon 77462306a36Sopenharmony_ci * system resume, we need to restore all VLAN entries. 77562306a36Sopenharmony_ci */ 77662306a36Sopenharmony_ci for (vid = def_vid; vid < dev->num_vlans; vid++) { 77762306a36Sopenharmony_ci v = &dev->vlans[vid]; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (!v->members) 78062306a36Sopenharmony_ci continue; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci b53_set_vlan_entry(dev, vid, v); 78362306a36Sopenharmony_ci b53_fast_age_vlan(dev, vid); 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_configure_vlan); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic void b53_switch_reset_gpio(struct b53_device *dev) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci int gpio = dev->reset_gpio; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (gpio < 0) 79562306a36Sopenharmony_ci return; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* Reset sequence: RESET low(50ms)->high(20ms) 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ci gpio_set_value(gpio, 0); 80062306a36Sopenharmony_ci mdelay(50); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci gpio_set_value(gpio, 1); 80362306a36Sopenharmony_ci mdelay(20); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci dev->current_page = 0xff; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int b53_switch_reset(struct b53_device *dev) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci unsigned int timeout = 1000; 81162306a36Sopenharmony_ci u8 mgmt, reg; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci b53_switch_reset_gpio(dev); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (is539x(dev)) { 81662306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83); 81762306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* This is specific to 58xx devices here, do not use is58xx() which 82162306a36Sopenharmony_ci * covers the larger Starfigther 2 family, including 7445/7278 which 82262306a36Sopenharmony_ci * still use this driver as a library and need to perform the reset 82362306a36Sopenharmony_ci * earlier. 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_ci if (dev->chip_id == BCM58XX_DEVICE_ID || 82662306a36Sopenharmony_ci dev->chip_id == BCM583XX_DEVICE_ID) { 82762306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, ®); 82862306a36Sopenharmony_ci reg |= SW_RST | EN_SW_RST | EN_CH_RST; 82962306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, reg); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci do { 83262306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SOFTRESET, ®); 83362306a36Sopenharmony_ci if (!(reg & SW_RST)) 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci usleep_range(1000, 2000); 83762306a36Sopenharmony_ci } while (timeout-- > 0); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (timeout == 0) { 84062306a36Sopenharmony_ci dev_err(dev->dev, 84162306a36Sopenharmony_ci "Timeout waiting for SW_RST to clear!\n"); 84262306a36Sopenharmony_ci return -ETIMEDOUT; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (!(mgmt & SM_SW_FWD_EN)) { 84962306a36Sopenharmony_ci mgmt &= ~SM_SW_FWD_MODE; 85062306a36Sopenharmony_ci mgmt |= SM_SW_FWD_EN; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); 85362306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (!(mgmt & SM_SW_FWD_EN)) { 85662306a36Sopenharmony_ci dev_err(dev->dev, "Failed to enable switch!\n"); 85762306a36Sopenharmony_ci return -EINVAL; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci b53_enable_mib(dev); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return b53_flush_arl(dev, FAST_AGE_STATIC); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int b53_phy_read16(struct dsa_switch *ds, int addr, int reg) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 86962306a36Sopenharmony_ci u16 value = 0; 87062306a36Sopenharmony_ci int ret; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (priv->ops->phy_read16) 87362306a36Sopenharmony_ci ret = priv->ops->phy_read16(priv, addr, reg, &value); 87462306a36Sopenharmony_ci else 87562306a36Sopenharmony_ci ret = b53_read16(priv, B53_PORT_MII_PAGE(addr), 87662306a36Sopenharmony_ci reg * 2, &value); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return ret ? ret : value; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int b53_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (priv->ops->phy_write16) 88662306a36Sopenharmony_ci return priv->ops->phy_write16(priv, addr, reg, val); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg * 2, val); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int b53_reset_switch(struct b53_device *priv) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci /* reset vlans */ 89462306a36Sopenharmony_ci memset(priv->vlans, 0, sizeof(*priv->vlans) * priv->num_vlans); 89562306a36Sopenharmony_ci memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci priv->serdes_lane = B53_INVALID_LANE; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return b53_switch_reset(priv); 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int b53_apply_config(struct b53_device *priv) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci /* disable switching */ 90562306a36Sopenharmony_ci b53_set_forwarding(priv, 0); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci b53_configure_vlan(priv->ds); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* enable switching */ 91062306a36Sopenharmony_ci b53_set_forwarding(priv, 1); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic void b53_reset_mib(struct b53_device *priv) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci u8 gc; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB); 92262306a36Sopenharmony_ci msleep(1); 92362306a36Sopenharmony_ci b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB); 92462306a36Sopenharmony_ci msleep(1); 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic const struct b53_mib_desc *b53_get_mib(struct b53_device *dev) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci if (is5365(dev)) 93062306a36Sopenharmony_ci return b53_mibs_65; 93162306a36Sopenharmony_ci else if (is63xx(dev)) 93262306a36Sopenharmony_ci return b53_mibs_63xx; 93362306a36Sopenharmony_ci else if (is58xx(dev)) 93462306a36Sopenharmony_ci return b53_mibs_58xx; 93562306a36Sopenharmony_ci else 93662306a36Sopenharmony_ci return b53_mibs; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic unsigned int b53_get_mib_size(struct b53_device *dev) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci if (is5365(dev)) 94262306a36Sopenharmony_ci return B53_MIBS_65_SIZE; 94362306a36Sopenharmony_ci else if (is63xx(dev)) 94462306a36Sopenharmony_ci return B53_MIBS_63XX_SIZE; 94562306a36Sopenharmony_ci else if (is58xx(dev)) 94662306a36Sopenharmony_ci return B53_MIBS_58XX_SIZE; 94762306a36Sopenharmony_ci else 94862306a36Sopenharmony_ci return B53_MIBS_SIZE; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic struct phy_device *b53_get_phy_device(struct dsa_switch *ds, int port) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci /* These ports typically do not have built-in PHYs */ 95462306a36Sopenharmony_ci switch (port) { 95562306a36Sopenharmony_ci case B53_CPU_PORT_25: 95662306a36Sopenharmony_ci case 7: 95762306a36Sopenharmony_ci case B53_CPU_PORT: 95862306a36Sopenharmony_ci return NULL; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci return mdiobus_get_phy(ds->slave_mii_bus, port); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_civoid b53_get_strings(struct dsa_switch *ds, int port, u32 stringset, 96562306a36Sopenharmony_ci uint8_t *data) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 96862306a36Sopenharmony_ci const struct b53_mib_desc *mibs = b53_get_mib(dev); 96962306a36Sopenharmony_ci unsigned int mib_size = b53_get_mib_size(dev); 97062306a36Sopenharmony_ci struct phy_device *phydev; 97162306a36Sopenharmony_ci unsigned int i; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) { 97462306a36Sopenharmony_ci for (i = 0; i < mib_size; i++) 97562306a36Sopenharmony_ci strscpy(data + i * ETH_GSTRING_LEN, 97662306a36Sopenharmony_ci mibs[i].name, ETH_GSTRING_LEN); 97762306a36Sopenharmony_ci } else if (stringset == ETH_SS_PHY_STATS) { 97862306a36Sopenharmony_ci phydev = b53_get_phy_device(ds, port); 97962306a36Sopenharmony_ci if (!phydev) 98062306a36Sopenharmony_ci return; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci phy_ethtool_get_strings(phydev, data); 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ciEXPORT_SYMBOL(b53_get_strings); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_civoid b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 99062306a36Sopenharmony_ci const struct b53_mib_desc *mibs = b53_get_mib(dev); 99162306a36Sopenharmony_ci unsigned int mib_size = b53_get_mib_size(dev); 99262306a36Sopenharmony_ci const struct b53_mib_desc *s; 99362306a36Sopenharmony_ci unsigned int i; 99462306a36Sopenharmony_ci u64 val = 0; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (is5365(dev) && port == 5) 99762306a36Sopenharmony_ci port = 8; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci mutex_lock(&dev->stats_mutex); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci for (i = 0; i < mib_size; i++) { 100262306a36Sopenharmony_ci s = &mibs[i]; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (s->size == 8) { 100562306a36Sopenharmony_ci b53_read64(dev, B53_MIB_PAGE(port), s->offset, &val); 100662306a36Sopenharmony_ci } else { 100762306a36Sopenharmony_ci u32 val32; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci b53_read32(dev, B53_MIB_PAGE(port), s->offset, 101062306a36Sopenharmony_ci &val32); 101162306a36Sopenharmony_ci val = val32; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci data[i] = (u64)val; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci mutex_unlock(&dev->stats_mutex); 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_get_ethtool_stats); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_civoid b53_get_ethtool_phy_stats(struct dsa_switch *ds, int port, uint64_t *data) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct phy_device *phydev; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci phydev = b53_get_phy_device(ds, port); 102562306a36Sopenharmony_ci if (!phydev) 102662306a36Sopenharmony_ci return; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci phy_ethtool_get_stats(phydev, NULL, data); 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ciEXPORT_SYMBOL(b53_get_ethtool_phy_stats); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ciint b53_get_sset_count(struct dsa_switch *ds, int port, int sset) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 103562306a36Sopenharmony_ci struct phy_device *phydev; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (sset == ETH_SS_STATS) { 103862306a36Sopenharmony_ci return b53_get_mib_size(dev); 103962306a36Sopenharmony_ci } else if (sset == ETH_SS_PHY_STATS) { 104062306a36Sopenharmony_ci phydev = b53_get_phy_device(ds, port); 104162306a36Sopenharmony_ci if (!phydev) 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci return phy_ethtool_get_sset_count(phydev); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci return 0; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ciEXPORT_SYMBOL(b53_get_sset_count); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cienum b53_devlink_resource_id { 105262306a36Sopenharmony_ci B53_DEVLINK_PARAM_ID_VLAN_TABLE, 105362306a36Sopenharmony_ci}; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic u64 b53_devlink_vlan_table_get(void *priv) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct b53_device *dev = priv; 105862306a36Sopenharmony_ci struct b53_vlan *vl; 105962306a36Sopenharmony_ci unsigned int i; 106062306a36Sopenharmony_ci u64 count = 0; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci for (i = 0; i < dev->num_vlans; i++) { 106362306a36Sopenharmony_ci vl = &dev->vlans[i]; 106462306a36Sopenharmony_ci if (vl->members) 106562306a36Sopenharmony_ci count++; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci return count; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ciint b53_setup_devlink_resources(struct dsa_switch *ds) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct devlink_resource_size_params size_params; 107462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 107562306a36Sopenharmony_ci int err; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci devlink_resource_size_params_init(&size_params, dev->num_vlans, 107862306a36Sopenharmony_ci dev->num_vlans, 107962306a36Sopenharmony_ci 1, DEVLINK_RESOURCE_UNIT_ENTRY); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci err = dsa_devlink_resource_register(ds, "VLAN", dev->num_vlans, 108262306a36Sopenharmony_ci B53_DEVLINK_PARAM_ID_VLAN_TABLE, 108362306a36Sopenharmony_ci DEVLINK_RESOURCE_ID_PARENT_TOP, 108462306a36Sopenharmony_ci &size_params); 108562306a36Sopenharmony_ci if (err) 108662306a36Sopenharmony_ci goto out; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci dsa_devlink_resource_occ_get_register(ds, 108962306a36Sopenharmony_ci B53_DEVLINK_PARAM_ID_VLAN_TABLE, 109062306a36Sopenharmony_ci b53_devlink_vlan_table_get, dev); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ciout: 109462306a36Sopenharmony_ci dsa_devlink_resources_unregister(ds); 109562306a36Sopenharmony_ci return err; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ciEXPORT_SYMBOL(b53_setup_devlink_resources); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic int b53_setup(struct dsa_switch *ds) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 110262306a36Sopenharmony_ci unsigned int port; 110362306a36Sopenharmony_ci int ret; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci /* Request bridge PVID untagged when DSA_TAG_PROTO_NONE is set 110662306a36Sopenharmony_ci * which forces the CPU port to be tagged in all VLANs. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci ds->untag_bridge_pvid = dev->tag_protocol == DSA_TAG_PROTO_NONE; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci ret = b53_reset_switch(dev); 111162306a36Sopenharmony_ci if (ret) { 111262306a36Sopenharmony_ci dev_err(ds->dev, "failed to reset switch\n"); 111362306a36Sopenharmony_ci return ret; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci b53_reset_mib(dev); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ret = b53_apply_config(dev); 111962306a36Sopenharmony_ci if (ret) { 112062306a36Sopenharmony_ci dev_err(ds->dev, "failed to apply configuration\n"); 112162306a36Sopenharmony_ci return ret; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* Configure IMP/CPU port, disable all other ports. Enabled 112562306a36Sopenharmony_ci * ports will be configured with .port_enable 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci for (port = 0; port < dev->num_ports; port++) { 112862306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port)) 112962306a36Sopenharmony_ci b53_enable_cpu_port(dev, port); 113062306a36Sopenharmony_ci else 113162306a36Sopenharmony_ci b53_disable_port(ds, port); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci return b53_setup_devlink_resources(ds); 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_cistatic void b53_teardown(struct dsa_switch *ds) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci dsa_devlink_resources_unregister(ds); 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic void b53_force_link(struct b53_device *dev, int port, int link) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci u8 reg, val, off; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* Override the port settings */ 114762306a36Sopenharmony_ci if (port == dev->imp_port) { 114862306a36Sopenharmony_ci off = B53_PORT_OVERRIDE_CTRL; 114962306a36Sopenharmony_ci val = PORT_OVERRIDE_EN; 115062306a36Sopenharmony_ci } else { 115162306a36Sopenharmony_ci off = B53_GMII_PORT_OVERRIDE_CTRL(port); 115262306a36Sopenharmony_ci val = GMII_PO_EN; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, off, ®); 115662306a36Sopenharmony_ci reg |= val; 115762306a36Sopenharmony_ci if (link) 115862306a36Sopenharmony_ci reg |= PORT_OVERRIDE_LINK; 115962306a36Sopenharmony_ci else 116062306a36Sopenharmony_ci reg &= ~PORT_OVERRIDE_LINK; 116162306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, off, reg); 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic void b53_force_port_config(struct b53_device *dev, int port, 116562306a36Sopenharmony_ci int speed, int duplex, 116662306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci u8 reg, val, off; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* Override the port settings */ 117162306a36Sopenharmony_ci if (port == dev->imp_port) { 117262306a36Sopenharmony_ci off = B53_PORT_OVERRIDE_CTRL; 117362306a36Sopenharmony_ci val = PORT_OVERRIDE_EN; 117462306a36Sopenharmony_ci } else { 117562306a36Sopenharmony_ci off = B53_GMII_PORT_OVERRIDE_CTRL(port); 117662306a36Sopenharmony_ci val = GMII_PO_EN; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, off, ®); 118062306a36Sopenharmony_ci reg |= val; 118162306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 118262306a36Sopenharmony_ci reg |= PORT_OVERRIDE_FULL_DUPLEX; 118362306a36Sopenharmony_ci else 118462306a36Sopenharmony_ci reg &= ~PORT_OVERRIDE_FULL_DUPLEX; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci switch (speed) { 118762306a36Sopenharmony_ci case 2000: 118862306a36Sopenharmony_ci reg |= PORT_OVERRIDE_SPEED_2000M; 118962306a36Sopenharmony_ci fallthrough; 119062306a36Sopenharmony_ci case SPEED_1000: 119162306a36Sopenharmony_ci reg |= PORT_OVERRIDE_SPEED_1000M; 119262306a36Sopenharmony_ci break; 119362306a36Sopenharmony_ci case SPEED_100: 119462306a36Sopenharmony_ci reg |= PORT_OVERRIDE_SPEED_100M; 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci case SPEED_10: 119762306a36Sopenharmony_ci reg |= PORT_OVERRIDE_SPEED_10M; 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci default: 120062306a36Sopenharmony_ci dev_err(dev->dev, "unknown speed: %d\n", speed); 120162306a36Sopenharmony_ci return; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (rx_pause) 120562306a36Sopenharmony_ci reg |= PORT_OVERRIDE_RX_FLOW; 120662306a36Sopenharmony_ci if (tx_pause) 120762306a36Sopenharmony_ci reg |= PORT_OVERRIDE_TX_FLOW; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, off, reg); 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port, 121362306a36Sopenharmony_ci phy_interface_t interface) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 121662306a36Sopenharmony_ci u8 rgmii_ctrl = 0, off; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (port == dev->imp_port) 121962306a36Sopenharmony_ci off = B53_RGMII_CTRL_IMP; 122062306a36Sopenharmony_ci else 122162306a36Sopenharmony_ci off = B53_RGMII_CTRL_P(port); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci switch (interface) { 122662306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 122762306a36Sopenharmony_ci rgmii_ctrl |= (RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 123062306a36Sopenharmony_ci rgmii_ctrl &= ~(RGMII_CTRL_DLL_TXC); 123162306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_DLL_RXC; 123262306a36Sopenharmony_ci break; 123362306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 123462306a36Sopenharmony_ci rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC); 123562306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_DLL_TXC; 123662306a36Sopenharmony_ci break; 123762306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 123862306a36Sopenharmony_ci default: 123962306a36Sopenharmony_ci rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC); 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (port != dev->imp_port) { 124462306a36Sopenharmony_ci if (is63268(dev)) 124562306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci dev_dbg(ds->dev, "Configured port %d for %s\n", port, 125362306a36Sopenharmony_ci phy_modes(interface)); 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic void b53_adjust_link(struct dsa_switch *ds, int port, 125762306a36Sopenharmony_ci struct phy_device *phydev) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 126062306a36Sopenharmony_ci struct ethtool_eee *p = &dev->ports[port].eee; 126162306a36Sopenharmony_ci u8 rgmii_ctrl = 0, reg = 0, off; 126262306a36Sopenharmony_ci bool tx_pause = false; 126362306a36Sopenharmony_ci bool rx_pause = false; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (!phy_is_pseudo_fixed_link(phydev)) 126662306a36Sopenharmony_ci return; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci /* Enable flow control on BCM5301x's CPU port */ 126962306a36Sopenharmony_ci if (is5301x(dev) && dsa_is_cpu_port(ds, port)) 127062306a36Sopenharmony_ci tx_pause = rx_pause = true; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (phydev->pause) { 127362306a36Sopenharmony_ci if (phydev->asym_pause) 127462306a36Sopenharmony_ci tx_pause = true; 127562306a36Sopenharmony_ci rx_pause = true; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci b53_force_port_config(dev, port, phydev->speed, phydev->duplex, 127962306a36Sopenharmony_ci tx_pause, rx_pause); 128062306a36Sopenharmony_ci b53_force_link(dev, port, phydev->link); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (is63xx(dev) && port >= B53_63XX_RGMII0) 128362306a36Sopenharmony_ci b53_adjust_63xx_rgmii(ds, port, phydev->interface); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (is531x5(dev) && phy_interface_is_rgmii(phydev)) { 128662306a36Sopenharmony_ci if (port == dev->imp_port) 128762306a36Sopenharmony_ci off = B53_RGMII_CTRL_IMP; 128862306a36Sopenharmony_ci else 128962306a36Sopenharmony_ci off = B53_RGMII_CTRL_P(port); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* Configure the port RGMII clock delay by DLL disabled and 129262306a36Sopenharmony_ci * tx_clk aligned timing (restoring to reset defaults) 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl); 129562306a36Sopenharmony_ci rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC | 129662306a36Sopenharmony_ci RGMII_CTRL_TIMING_SEL); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make 129962306a36Sopenharmony_ci * sure that we enable the port TX clock internal delay to 130062306a36Sopenharmony_ci * account for this internal delay that is inserted, otherwise 130162306a36Sopenharmony_ci * the switch won't be able to receive correctly. 130262306a36Sopenharmony_ci * 130362306a36Sopenharmony_ci * PHY_INTERFACE_MODE_RGMII means that we are not introducing 130462306a36Sopenharmony_ci * any delay neither on transmission nor reception, so the 130562306a36Sopenharmony_ci * BCM53125 must also be configured accordingly to account for 130662306a36Sopenharmony_ci * the lack of delay and introduce 130762306a36Sopenharmony_ci * 130862306a36Sopenharmony_ci * The BCM53125 switch has its RX clock and TX clock control 130962306a36Sopenharmony_ci * swapped, hence the reason why we modify the TX clock path in 131062306a36Sopenharmony_ci * the "RGMII" case 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) 131362306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_DLL_TXC; 131462306a36Sopenharmony_ci if (phydev->interface == PHY_INTERFACE_MODE_RGMII) 131562306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC; 131662306a36Sopenharmony_ci rgmii_ctrl |= RGMII_CTRL_TIMING_SEL; 131762306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci dev_info(ds->dev, "Configured port %d for %s\n", port, 132062306a36Sopenharmony_ci phy_modes(phydev->interface)); 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci /* configure MII port if necessary */ 132462306a36Sopenharmony_ci if (is5325(dev)) { 132562306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, 132662306a36Sopenharmony_ci ®); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* reverse mii needs to be enabled */ 132962306a36Sopenharmony_ci if (!(reg & PORT_OVERRIDE_RV_MII_25)) { 133062306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, 133162306a36Sopenharmony_ci reg | PORT_OVERRIDE_RV_MII_25); 133262306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL, 133362306a36Sopenharmony_ci ®); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (!(reg & PORT_OVERRIDE_RV_MII_25)) { 133662306a36Sopenharmony_ci dev_err(ds->dev, 133762306a36Sopenharmony_ci "Failed to enable reverse MII mode\n"); 133862306a36Sopenharmony_ci return; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci /* Re-negotiate EEE if it was enabled already */ 134462306a36Sopenharmony_ci p->eee_enabled = b53_eee_init(ds, port, phydev); 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_civoid b53_port_event(struct dsa_switch *ds, int port) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 135062306a36Sopenharmony_ci bool link; 135162306a36Sopenharmony_ci u16 sts; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci b53_read16(dev, B53_STAT_PAGE, B53_LINK_STAT, &sts); 135462306a36Sopenharmony_ci link = !!(sts & BIT(port)); 135562306a36Sopenharmony_ci dsa_port_phylink_mac_change(ds, port, link); 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ciEXPORT_SYMBOL(b53_port_event); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic void b53_phylink_get_caps(struct dsa_switch *ds, int port, 136062306a36Sopenharmony_ci struct phylink_config *config) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Internal ports need GMII for PHYLIB */ 136562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* These switches appear to support MII and RevMII too, but beyond 136862306a36Sopenharmony_ci * this, the code gives very few clues. FIXME: We probably need more 136962306a36Sopenharmony_ci * interface modes here. 137062306a36Sopenharmony_ci * 137162306a36Sopenharmony_ci * According to b53_srab_mux_init(), ports 3..5 can support: 137262306a36Sopenharmony_ci * SGMII, MII, GMII, RGMII or INTERNAL depending on the MUX setting. 137362306a36Sopenharmony_ci * However, the interface mode read from the MUX configuration is 137462306a36Sopenharmony_ci * not passed back to DSA, so phylink uses NA. 137562306a36Sopenharmony_ci * DT can specify RGMII for ports 0, 1. 137662306a36Sopenharmony_ci * For MDIO, port 8 can be RGMII_TXID. 137762306a36Sopenharmony_ci */ 137862306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); 137962306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_REVMII, config->supported_interfaces); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 138262306a36Sopenharmony_ci MAC_10 | MAC_100; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* 5325/5365 are not capable of gigabit speeds, everything else is. 138562306a36Sopenharmony_ci * Note: the original code also exclulded Gigagbit for MII, RevMII 138662306a36Sopenharmony_ci * and 802.3z modes. MII and RevMII are not able to work above 100M, 138762306a36Sopenharmony_ci * so will be excluded by the generic validator implementation. 138862306a36Sopenharmony_ci * However, the exclusion of Gigabit for 802.3z just seems wrong. 138962306a36Sopenharmony_ci */ 139062306a36Sopenharmony_ci if (!(is5325(dev) || is5365(dev))) 139162306a36Sopenharmony_ci config->mac_capabilities |= MAC_1000; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* Get the implementation specific capabilities */ 139462306a36Sopenharmony_ci if (dev->ops->phylink_get_caps) 139562306a36Sopenharmony_ci dev->ops->phylink_get_caps(dev, port, config); 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistatic struct phylink_pcs *b53_phylink_mac_select_pcs(struct dsa_switch *ds, 139962306a36Sopenharmony_ci int port, 140062306a36Sopenharmony_ci phy_interface_t interface) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (!dev->ops->phylink_mac_select_pcs) 140562306a36Sopenharmony_ci return NULL; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return dev->ops->phylink_mac_select_pcs(dev, port, interface); 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_civoid b53_phylink_mac_config(struct dsa_switch *ds, int port, 141162306a36Sopenharmony_ci unsigned int mode, 141262306a36Sopenharmony_ci const struct phylink_link_state *state) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ciEXPORT_SYMBOL(b53_phylink_mac_config); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_civoid b53_phylink_mac_link_down(struct dsa_switch *ds, int port, 141862306a36Sopenharmony_ci unsigned int mode, 141962306a36Sopenharmony_ci phy_interface_t interface) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (mode == MLO_AN_PHY) 142462306a36Sopenharmony_ci return; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (mode == MLO_AN_FIXED) { 142762306a36Sopenharmony_ci b53_force_link(dev, port, false); 142862306a36Sopenharmony_ci return; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (phy_interface_mode_is_8023z(interface) && 143262306a36Sopenharmony_ci dev->ops->serdes_link_set) 143362306a36Sopenharmony_ci dev->ops->serdes_link_set(dev, port, mode, interface, false); 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ciEXPORT_SYMBOL(b53_phylink_mac_link_down); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_civoid b53_phylink_mac_link_up(struct dsa_switch *ds, int port, 143862306a36Sopenharmony_ci unsigned int mode, 143962306a36Sopenharmony_ci phy_interface_t interface, 144062306a36Sopenharmony_ci struct phy_device *phydev, 144162306a36Sopenharmony_ci int speed, int duplex, 144262306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 144362306a36Sopenharmony_ci{ 144462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci if (is63xx(dev) && port >= B53_63XX_RGMII0) 144762306a36Sopenharmony_ci b53_adjust_63xx_rgmii(ds, port, interface); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci if (mode == MLO_AN_PHY) 145062306a36Sopenharmony_ci return; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (mode == MLO_AN_FIXED) { 145362306a36Sopenharmony_ci b53_force_port_config(dev, port, speed, duplex, 145462306a36Sopenharmony_ci tx_pause, rx_pause); 145562306a36Sopenharmony_ci b53_force_link(dev, port, true); 145662306a36Sopenharmony_ci return; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if (phy_interface_mode_is_8023z(interface) && 146062306a36Sopenharmony_ci dev->ops->serdes_link_set) 146162306a36Sopenharmony_ci dev->ops->serdes_link_set(dev, port, mode, interface, true); 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ciEXPORT_SYMBOL(b53_phylink_mac_link_up); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ciint b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, 146662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 146762306a36Sopenharmony_ci{ 146862306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci b53_enable_vlan(dev, port, dev->vlan_enabled, vlan_filtering); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci return 0; 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ciEXPORT_SYMBOL(b53_vlan_filtering); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic int b53_vlan_prepare(struct dsa_switch *ds, int port, 147762306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if ((is5325(dev) || is5365(dev)) && vlan->vid == 0) 148262306a36Sopenharmony_ci return -EOPNOTSUPP; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of 148562306a36Sopenharmony_ci * receiving VLAN tagged frames at all, we can still allow the port to 148662306a36Sopenharmony_ci * be configured for egress untagged. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_ci if (dev->chip_id == BCM7278_DEVICE_ID && port == 7 && 148962306a36Sopenharmony_ci !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) 149062306a36Sopenharmony_ci return -EINVAL; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (vlan->vid >= dev->num_vlans) 149362306a36Sopenharmony_ci return -ERANGE; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci b53_enable_vlan(dev, port, true, ds->vlan_filtering); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return 0; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ciint b53_vlan_add(struct dsa_switch *ds, int port, 150162306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 150262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 150362306a36Sopenharmony_ci{ 150462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 150562306a36Sopenharmony_ci bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 150662306a36Sopenharmony_ci bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 150762306a36Sopenharmony_ci struct b53_vlan *vl; 150862306a36Sopenharmony_ci int err; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci err = b53_vlan_prepare(ds, port, vlan); 151162306a36Sopenharmony_ci if (err) 151262306a36Sopenharmony_ci return err; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci vl = &dev->vlans[vlan->vid]; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci b53_get_vlan_entry(dev, vlan->vid, vl); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (vlan->vid == 0 && vlan->vid == b53_default_pvid(dev)) 151962306a36Sopenharmony_ci untagged = true; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci vl->members |= BIT(port); 152262306a36Sopenharmony_ci if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port)) 152362306a36Sopenharmony_ci vl->untag |= BIT(port); 152462306a36Sopenharmony_ci else 152562306a36Sopenharmony_ci vl->untag &= ~BIT(port); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci b53_set_vlan_entry(dev, vlan->vid, vl); 152862306a36Sopenharmony_ci b53_fast_age_vlan(dev, vlan->vid); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (pvid && !dsa_is_cpu_port(ds, port)) { 153162306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), 153262306a36Sopenharmony_ci vlan->vid); 153362306a36Sopenharmony_ci b53_fast_age_vlan(dev, vlan->vid); 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci return 0; 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_vlan_add); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ciint b53_vlan_del(struct dsa_switch *ds, int port, 154162306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 154462306a36Sopenharmony_ci bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 154562306a36Sopenharmony_ci struct b53_vlan *vl; 154662306a36Sopenharmony_ci u16 pvid; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci vl = &dev->vlans[vlan->vid]; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci b53_get_vlan_entry(dev, vlan->vid, vl); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci vl->members &= ~BIT(port); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (pvid == vlan->vid) 155762306a36Sopenharmony_ci pvid = b53_default_pvid(dev); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port)) 156062306a36Sopenharmony_ci vl->untag &= ~(BIT(port)); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci b53_set_vlan_entry(dev, vlan->vid, vl); 156362306a36Sopenharmony_ci b53_fast_age_vlan(dev, vlan->vid); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), pvid); 156662306a36Sopenharmony_ci b53_fast_age_vlan(dev, pvid); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci return 0; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ciEXPORT_SYMBOL(b53_vlan_del); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci/* Address Resolution Logic routines. Caller must hold &dev->arl_mutex. */ 157362306a36Sopenharmony_cistatic int b53_arl_op_wait(struct b53_device *dev) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci unsigned int timeout = 10; 157662306a36Sopenharmony_ci u8 reg; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci do { 157962306a36Sopenharmony_ci b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, ®); 158062306a36Sopenharmony_ci if (!(reg & ARLTBL_START_DONE)) 158162306a36Sopenharmony_ci return 0; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci usleep_range(1000, 2000); 158462306a36Sopenharmony_ci } while (timeout--); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci dev_warn(dev->dev, "timeout waiting for ARL to finish: 0x%02x\n", reg); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci return -ETIMEDOUT; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_cistatic int b53_arl_rw_op(struct b53_device *dev, unsigned int op) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci u8 reg; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (op > ARLTBL_RW) 159662306a36Sopenharmony_ci return -EINVAL; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, ®); 159962306a36Sopenharmony_ci reg |= ARLTBL_START_DONE; 160062306a36Sopenharmony_ci if (op) 160162306a36Sopenharmony_ci reg |= ARLTBL_RW; 160262306a36Sopenharmony_ci else 160362306a36Sopenharmony_ci reg &= ~ARLTBL_RW; 160462306a36Sopenharmony_ci if (dev->vlan_enabled) 160562306a36Sopenharmony_ci reg &= ~ARLTBL_IVL_SVL_SELECT; 160662306a36Sopenharmony_ci else 160762306a36Sopenharmony_ci reg |= ARLTBL_IVL_SVL_SELECT; 160862306a36Sopenharmony_ci b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, reg); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci return b53_arl_op_wait(dev); 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic int b53_arl_read(struct b53_device *dev, u64 mac, 161462306a36Sopenharmony_ci u16 vid, struct b53_arl_entry *ent, u8 *idx) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES); 161762306a36Sopenharmony_ci unsigned int i; 161862306a36Sopenharmony_ci int ret; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci ret = b53_arl_op_wait(dev); 162162306a36Sopenharmony_ci if (ret) 162262306a36Sopenharmony_ci return ret; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci bitmap_zero(free_bins, dev->num_arl_bins); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci /* Read the bins */ 162762306a36Sopenharmony_ci for (i = 0; i < dev->num_arl_bins; i++) { 162862306a36Sopenharmony_ci u64 mac_vid; 162962306a36Sopenharmony_ci u32 fwd_entry; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci b53_read64(dev, B53_ARLIO_PAGE, 163262306a36Sopenharmony_ci B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid); 163362306a36Sopenharmony_ci b53_read32(dev, B53_ARLIO_PAGE, 163462306a36Sopenharmony_ci B53_ARLTBL_DATA_ENTRY(i), &fwd_entry); 163562306a36Sopenharmony_ci b53_arl_to_entry(ent, mac_vid, fwd_entry); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci if (!(fwd_entry & ARLTBL_VALID)) { 163862306a36Sopenharmony_ci set_bit(i, free_bins); 163962306a36Sopenharmony_ci continue; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci if ((mac_vid & ARLTBL_MAC_MASK) != mac) 164262306a36Sopenharmony_ci continue; 164362306a36Sopenharmony_ci if (dev->vlan_enabled && 164462306a36Sopenharmony_ci ((mac_vid >> ARLTBL_VID_S) & ARLTBL_VID_MASK) != vid) 164562306a36Sopenharmony_ci continue; 164662306a36Sopenharmony_ci *idx = i; 164762306a36Sopenharmony_ci return 0; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci *idx = find_first_bit(free_bins, dev->num_arl_bins); 165162306a36Sopenharmony_ci return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic int b53_arl_op(struct b53_device *dev, int op, int port, 165562306a36Sopenharmony_ci const unsigned char *addr, u16 vid, bool is_valid) 165662306a36Sopenharmony_ci{ 165762306a36Sopenharmony_ci struct b53_arl_entry ent; 165862306a36Sopenharmony_ci u32 fwd_entry; 165962306a36Sopenharmony_ci u64 mac, mac_vid = 0; 166062306a36Sopenharmony_ci u8 idx = 0; 166162306a36Sopenharmony_ci int ret; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci /* Convert the array into a 64-bit MAC */ 166462306a36Sopenharmony_ci mac = ether_addr_to_u64(addr); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci /* Perform a read for the given MAC and VID */ 166762306a36Sopenharmony_ci b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac); 166862306a36Sopenharmony_ci b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci /* Issue a read operation for this MAC */ 167162306a36Sopenharmony_ci ret = b53_arl_rw_op(dev, 1); 167262306a36Sopenharmony_ci if (ret) 167362306a36Sopenharmony_ci return ret; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci ret = b53_arl_read(dev, mac, vid, &ent, &idx); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci /* If this is a read, just finish now */ 167862306a36Sopenharmony_ci if (op) 167962306a36Sopenharmony_ci return ret; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci switch (ret) { 168262306a36Sopenharmony_ci case -ETIMEDOUT: 168362306a36Sopenharmony_ci return ret; 168462306a36Sopenharmony_ci case -ENOSPC: 168562306a36Sopenharmony_ci dev_dbg(dev->dev, "{%pM,%.4d} no space left in ARL\n", 168662306a36Sopenharmony_ci addr, vid); 168762306a36Sopenharmony_ci return is_valid ? ret : 0; 168862306a36Sopenharmony_ci case -ENOENT: 168962306a36Sopenharmony_ci /* We could not find a matching MAC, so reset to a new entry */ 169062306a36Sopenharmony_ci dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n", 169162306a36Sopenharmony_ci addr, vid, idx); 169262306a36Sopenharmony_ci fwd_entry = 0; 169362306a36Sopenharmony_ci break; 169462306a36Sopenharmony_ci default: 169562306a36Sopenharmony_ci dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n", 169662306a36Sopenharmony_ci addr, vid, idx); 169762306a36Sopenharmony_ci break; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci /* For multicast address, the port is a bitmask and the validity 170162306a36Sopenharmony_ci * is determined by having at least one port being still active 170262306a36Sopenharmony_ci */ 170362306a36Sopenharmony_ci if (!is_multicast_ether_addr(addr)) { 170462306a36Sopenharmony_ci ent.port = port; 170562306a36Sopenharmony_ci ent.is_valid = is_valid; 170662306a36Sopenharmony_ci } else { 170762306a36Sopenharmony_ci if (is_valid) 170862306a36Sopenharmony_ci ent.port |= BIT(port); 170962306a36Sopenharmony_ci else 171062306a36Sopenharmony_ci ent.port &= ~BIT(port); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci ent.is_valid = !!(ent.port); 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci ent.vid = vid; 171662306a36Sopenharmony_ci ent.is_static = true; 171762306a36Sopenharmony_ci ent.is_age = false; 171862306a36Sopenharmony_ci memcpy(ent.mac, addr, ETH_ALEN); 171962306a36Sopenharmony_ci b53_arl_from_entry(&mac_vid, &fwd_entry, &ent); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci b53_write64(dev, B53_ARLIO_PAGE, 172262306a36Sopenharmony_ci B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid); 172362306a36Sopenharmony_ci b53_write32(dev, B53_ARLIO_PAGE, 172462306a36Sopenharmony_ci B53_ARLTBL_DATA_ENTRY(idx), fwd_entry); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci return b53_arl_rw_op(dev, 0); 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ciint b53_fdb_add(struct dsa_switch *ds, int port, 173062306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 173162306a36Sopenharmony_ci struct dsa_db db) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 173462306a36Sopenharmony_ci int ret; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci /* 5325 and 5365 require some more massaging, but could 173762306a36Sopenharmony_ci * be supported eventually 173862306a36Sopenharmony_ci */ 173962306a36Sopenharmony_ci if (is5325(priv) || is5365(priv)) 174062306a36Sopenharmony_ci return -EOPNOTSUPP; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci mutex_lock(&priv->arl_mutex); 174362306a36Sopenharmony_ci ret = b53_arl_op(priv, 0, port, addr, vid, true); 174462306a36Sopenharmony_ci mutex_unlock(&priv->arl_mutex); 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci return ret; 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_fdb_add); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ciint b53_fdb_del(struct dsa_switch *ds, int port, 175162306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 175262306a36Sopenharmony_ci struct dsa_db db) 175362306a36Sopenharmony_ci{ 175462306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 175562306a36Sopenharmony_ci int ret; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci mutex_lock(&priv->arl_mutex); 175862306a36Sopenharmony_ci ret = b53_arl_op(priv, 0, port, addr, vid, false); 175962306a36Sopenharmony_ci mutex_unlock(&priv->arl_mutex); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci return ret; 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ciEXPORT_SYMBOL(b53_fdb_del); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic int b53_arl_search_wait(struct b53_device *dev) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci unsigned int timeout = 1000; 176862306a36Sopenharmony_ci u8 reg; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci do { 177162306a36Sopenharmony_ci b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, ®); 177262306a36Sopenharmony_ci if (!(reg & ARL_SRCH_STDN)) 177362306a36Sopenharmony_ci return 0; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (reg & ARL_SRCH_VLID) 177662306a36Sopenharmony_ci return 0; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci usleep_range(1000, 2000); 177962306a36Sopenharmony_ci } while (timeout--); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci return -ETIMEDOUT; 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic void b53_arl_search_rd(struct b53_device *dev, u8 idx, 178562306a36Sopenharmony_ci struct b53_arl_entry *ent) 178662306a36Sopenharmony_ci{ 178762306a36Sopenharmony_ci u64 mac_vid; 178862306a36Sopenharmony_ci u32 fwd_entry; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci b53_read64(dev, B53_ARLIO_PAGE, 179162306a36Sopenharmony_ci B53_ARL_SRCH_RSTL_MACVID(idx), &mac_vid); 179262306a36Sopenharmony_ci b53_read32(dev, B53_ARLIO_PAGE, 179362306a36Sopenharmony_ci B53_ARL_SRCH_RSTL(idx), &fwd_entry); 179462306a36Sopenharmony_ci b53_arl_to_entry(ent, mac_vid, fwd_entry); 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_cistatic int b53_fdb_copy(int port, const struct b53_arl_entry *ent, 179862306a36Sopenharmony_ci dsa_fdb_dump_cb_t *cb, void *data) 179962306a36Sopenharmony_ci{ 180062306a36Sopenharmony_ci if (!ent->is_valid) 180162306a36Sopenharmony_ci return 0; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (port != ent->port) 180462306a36Sopenharmony_ci return 0; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci return cb(ent->mac, ent->vid, ent->is_static, data); 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ciint b53_fdb_dump(struct dsa_switch *ds, int port, 181062306a36Sopenharmony_ci dsa_fdb_dump_cb_t *cb, void *data) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 181362306a36Sopenharmony_ci struct b53_arl_entry results[2]; 181462306a36Sopenharmony_ci unsigned int count = 0; 181562306a36Sopenharmony_ci int ret; 181662306a36Sopenharmony_ci u8 reg; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci mutex_lock(&priv->arl_mutex); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci /* Start search operation */ 182162306a36Sopenharmony_ci reg = ARL_SRCH_STDN; 182262306a36Sopenharmony_ci b53_write8(priv, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, reg); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci do { 182562306a36Sopenharmony_ci ret = b53_arl_search_wait(priv); 182662306a36Sopenharmony_ci if (ret) 182762306a36Sopenharmony_ci break; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci b53_arl_search_rd(priv, 0, &results[0]); 183062306a36Sopenharmony_ci ret = b53_fdb_copy(port, &results[0], cb, data); 183162306a36Sopenharmony_ci if (ret) 183262306a36Sopenharmony_ci break; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci if (priv->num_arl_bins > 2) { 183562306a36Sopenharmony_ci b53_arl_search_rd(priv, 1, &results[1]); 183662306a36Sopenharmony_ci ret = b53_fdb_copy(port, &results[1], cb, data); 183762306a36Sopenharmony_ci if (ret) 183862306a36Sopenharmony_ci break; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci if (!results[0].is_valid && !results[1].is_valid) 184162306a36Sopenharmony_ci break; 184262306a36Sopenharmony_ci } 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci } while (count++ < b53_max_arl_entries(priv) / 2); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci mutex_unlock(&priv->arl_mutex); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci return 0; 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_ciEXPORT_SYMBOL(b53_fdb_dump); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ciint b53_mdb_add(struct dsa_switch *ds, int port, 185362306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 185462306a36Sopenharmony_ci struct dsa_db db) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 185762306a36Sopenharmony_ci int ret; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci /* 5325 and 5365 require some more massaging, but could 186062306a36Sopenharmony_ci * be supported eventually 186162306a36Sopenharmony_ci */ 186262306a36Sopenharmony_ci if (is5325(priv) || is5365(priv)) 186362306a36Sopenharmony_ci return -EOPNOTSUPP; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci mutex_lock(&priv->arl_mutex); 186662306a36Sopenharmony_ci ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true); 186762306a36Sopenharmony_ci mutex_unlock(&priv->arl_mutex); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci return ret; 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ciEXPORT_SYMBOL(b53_mdb_add); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ciint b53_mdb_del(struct dsa_switch *ds, int port, 187462306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 187562306a36Sopenharmony_ci struct dsa_db db) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct b53_device *priv = ds->priv; 187862306a36Sopenharmony_ci int ret; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci mutex_lock(&priv->arl_mutex); 188162306a36Sopenharmony_ci ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, false); 188262306a36Sopenharmony_ci mutex_unlock(&priv->arl_mutex); 188362306a36Sopenharmony_ci if (ret) 188462306a36Sopenharmony_ci dev_err(ds->dev, "failed to delete MDB entry\n"); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci return ret; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_mdb_del); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ciint b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, 189162306a36Sopenharmony_ci bool *tx_fwd_offload, struct netlink_ext_ack *extack) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 189462306a36Sopenharmony_ci s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; 189562306a36Sopenharmony_ci u16 pvlan, reg; 189662306a36Sopenharmony_ci unsigned int i; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci /* On 7278, port 7 which connects to the ASP should only receive 189962306a36Sopenharmony_ci * traffic from matching CFP rules. 190062306a36Sopenharmony_ci */ 190162306a36Sopenharmony_ci if (dev->chip_id == BCM7278_DEVICE_ID && port == 7) 190262306a36Sopenharmony_ci return -EINVAL; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci /* Make this port leave the all VLANs join since we will have proper 190562306a36Sopenharmony_ci * VLAN entries from now on 190662306a36Sopenharmony_ci */ 190762306a36Sopenharmony_ci if (is58xx(dev)) { 190862306a36Sopenharmony_ci b53_read16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, ®); 190962306a36Sopenharmony_ci reg &= ~BIT(port); 191062306a36Sopenharmony_ci if ((reg & BIT(cpu_port)) == BIT(cpu_port)) 191162306a36Sopenharmony_ci reg &= ~BIT(cpu_port); 191262306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, reg); 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci b53_for_each_port(dev, i) { 191862306a36Sopenharmony_ci if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 191962306a36Sopenharmony_ci continue; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* Add this local port to the remote port VLAN control 192262306a36Sopenharmony_ci * membership and update the remote port bitmask 192362306a36Sopenharmony_ci */ 192462306a36Sopenharmony_ci b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); 192562306a36Sopenharmony_ci reg |= BIT(port); 192662306a36Sopenharmony_ci b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), reg); 192762306a36Sopenharmony_ci dev->ports[i].vlan_ctl_mask = reg; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci pvlan |= BIT(i); 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci /* Configure the local port VLAN control membership to include 193362306a36Sopenharmony_ci * remote ports and update the local port bitmask 193462306a36Sopenharmony_ci */ 193562306a36Sopenharmony_ci b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); 193662306a36Sopenharmony_ci dev->ports[port].vlan_ctl_mask = pvlan; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci return 0; 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ciEXPORT_SYMBOL(b53_br_join); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_civoid b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 194562306a36Sopenharmony_ci struct b53_vlan *vl = &dev->vlans[0]; 194662306a36Sopenharmony_ci s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; 194762306a36Sopenharmony_ci unsigned int i; 194862306a36Sopenharmony_ci u16 pvlan, reg, pvid; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci b53_for_each_port(dev, i) { 195362306a36Sopenharmony_ci /* Don't touch the remaining ports */ 195462306a36Sopenharmony_ci if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 195562306a36Sopenharmony_ci continue; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); 195862306a36Sopenharmony_ci reg &= ~BIT(port); 195962306a36Sopenharmony_ci b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), reg); 196062306a36Sopenharmony_ci dev->ports[port].vlan_ctl_mask = reg; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci /* Prevent self removal to preserve isolation */ 196362306a36Sopenharmony_ci if (port != i) 196462306a36Sopenharmony_ci pvlan &= ~BIT(i); 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan); 196862306a36Sopenharmony_ci dev->ports[port].vlan_ctl_mask = pvlan; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci pvid = b53_default_pvid(dev); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* Make this port join all VLANs without VLAN entries */ 197362306a36Sopenharmony_ci if (is58xx(dev)) { 197462306a36Sopenharmony_ci b53_read16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, ®); 197562306a36Sopenharmony_ci reg |= BIT(port); 197662306a36Sopenharmony_ci if (!(reg & BIT(cpu_port))) 197762306a36Sopenharmony_ci reg |= BIT(cpu_port); 197862306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_JOIN_ALL_VLAN_EN, reg); 197962306a36Sopenharmony_ci } else { 198062306a36Sopenharmony_ci b53_get_vlan_entry(dev, pvid, vl); 198162306a36Sopenharmony_ci vl->members |= BIT(port) | BIT(cpu_port); 198262306a36Sopenharmony_ci vl->untag |= BIT(port) | BIT(cpu_port); 198362306a36Sopenharmony_ci b53_set_vlan_entry(dev, pvid, vl); 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ciEXPORT_SYMBOL(b53_br_leave); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_civoid b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state) 198962306a36Sopenharmony_ci{ 199062306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 199162306a36Sopenharmony_ci u8 hw_state; 199262306a36Sopenharmony_ci u8 reg; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci switch (state) { 199562306a36Sopenharmony_ci case BR_STATE_DISABLED: 199662306a36Sopenharmony_ci hw_state = PORT_CTRL_DIS_STATE; 199762306a36Sopenharmony_ci break; 199862306a36Sopenharmony_ci case BR_STATE_LISTENING: 199962306a36Sopenharmony_ci hw_state = PORT_CTRL_LISTEN_STATE; 200062306a36Sopenharmony_ci break; 200162306a36Sopenharmony_ci case BR_STATE_LEARNING: 200262306a36Sopenharmony_ci hw_state = PORT_CTRL_LEARN_STATE; 200362306a36Sopenharmony_ci break; 200462306a36Sopenharmony_ci case BR_STATE_FORWARDING: 200562306a36Sopenharmony_ci hw_state = PORT_CTRL_FWD_STATE; 200662306a36Sopenharmony_ci break; 200762306a36Sopenharmony_ci case BR_STATE_BLOCKING: 200862306a36Sopenharmony_ci hw_state = PORT_CTRL_BLOCK_STATE; 200962306a36Sopenharmony_ci break; 201062306a36Sopenharmony_ci default: 201162306a36Sopenharmony_ci dev_err(ds->dev, "invalid STP state: %d\n", state); 201262306a36Sopenharmony_ci return; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), ®); 201662306a36Sopenharmony_ci reg &= ~PORT_CTRL_STP_STATE_MASK; 201762306a36Sopenharmony_ci reg |= hw_state; 201862306a36Sopenharmony_ci b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg); 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ciEXPORT_SYMBOL(b53_br_set_stp_state); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_civoid b53_br_fast_age(struct dsa_switch *ds, int port) 202362306a36Sopenharmony_ci{ 202462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci if (b53_fast_age_port(dev, port)) 202762306a36Sopenharmony_ci dev_err(ds->dev, "fast ageing failed\n"); 202862306a36Sopenharmony_ci} 202962306a36Sopenharmony_ciEXPORT_SYMBOL(b53_br_fast_age); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ciint b53_br_flags_pre(struct dsa_switch *ds, int port, 203262306a36Sopenharmony_ci struct switchdev_brport_flags flags, 203362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 203462306a36Sopenharmony_ci{ 203562306a36Sopenharmony_ci if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_LEARNING)) 203662306a36Sopenharmony_ci return -EINVAL; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci return 0; 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ciEXPORT_SYMBOL(b53_br_flags_pre); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ciint b53_br_flags(struct dsa_switch *ds, int port, 204362306a36Sopenharmony_ci struct switchdev_brport_flags flags, 204462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci if (flags.mask & BR_FLOOD) 204762306a36Sopenharmony_ci b53_port_set_ucast_flood(ds->priv, port, 204862306a36Sopenharmony_ci !!(flags.val & BR_FLOOD)); 204962306a36Sopenharmony_ci if (flags.mask & BR_MCAST_FLOOD) 205062306a36Sopenharmony_ci b53_port_set_mcast_flood(ds->priv, port, 205162306a36Sopenharmony_ci !!(flags.val & BR_MCAST_FLOOD)); 205262306a36Sopenharmony_ci if (flags.mask & BR_LEARNING) 205362306a36Sopenharmony_ci b53_port_set_learning(ds->priv, port, 205462306a36Sopenharmony_ci !!(flags.val & BR_LEARNING)); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci return 0; 205762306a36Sopenharmony_ci} 205862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_br_flags); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_cistatic bool b53_possible_cpu_port(struct dsa_switch *ds, int port) 206162306a36Sopenharmony_ci{ 206262306a36Sopenharmony_ci /* Broadcom switches will accept enabling Broadcom tags on the 206362306a36Sopenharmony_ci * following ports: 5, 7 and 8, any other port is not supported 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_ci switch (port) { 206662306a36Sopenharmony_ci case B53_CPU_PORT_25: 206762306a36Sopenharmony_ci case 7: 206862306a36Sopenharmony_ci case B53_CPU_PORT: 206962306a36Sopenharmony_ci return true; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci return false; 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cistatic bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port, 207662306a36Sopenharmony_ci enum dsa_tag_protocol tag_protocol) 207762306a36Sopenharmony_ci{ 207862306a36Sopenharmony_ci bool ret = b53_possible_cpu_port(ds, port); 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci if (!ret) { 208162306a36Sopenharmony_ci dev_warn(ds->dev, "Port %d is not Broadcom tag capable\n", 208262306a36Sopenharmony_ci port); 208362306a36Sopenharmony_ci return ret; 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci switch (tag_protocol) { 208762306a36Sopenharmony_ci case DSA_TAG_PROTO_BRCM: 208862306a36Sopenharmony_ci case DSA_TAG_PROTO_BRCM_PREPEND: 208962306a36Sopenharmony_ci dev_warn(ds->dev, 209062306a36Sopenharmony_ci "Port %d is stacked to Broadcom tag switch\n", port); 209162306a36Sopenharmony_ci ret = false; 209262306a36Sopenharmony_ci break; 209362306a36Sopenharmony_ci default: 209462306a36Sopenharmony_ci ret = true; 209562306a36Sopenharmony_ci break; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci return ret; 209962306a36Sopenharmony_ci} 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_cienum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port, 210262306a36Sopenharmony_ci enum dsa_tag_protocol mprot) 210362306a36Sopenharmony_ci{ 210462306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci if (!b53_can_enable_brcm_tags(ds, port, mprot)) { 210762306a36Sopenharmony_ci dev->tag_protocol = DSA_TAG_PROTO_NONE; 210862306a36Sopenharmony_ci goto out; 210962306a36Sopenharmony_ci } 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci /* Older models require a different 6 byte tag */ 211262306a36Sopenharmony_ci if (is5325(dev) || is5365(dev) || is63xx(dev)) { 211362306a36Sopenharmony_ci dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY; 211462306a36Sopenharmony_ci goto out; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci /* Broadcom BCM58xx chips have a flow accelerator on Port 8 211862306a36Sopenharmony_ci * which requires us to use the prepended Broadcom tag type 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_ci if (dev->chip_id == BCM58XX_DEVICE_ID && port == B53_CPU_PORT) { 212162306a36Sopenharmony_ci dev->tag_protocol = DSA_TAG_PROTO_BRCM_PREPEND; 212262306a36Sopenharmony_ci goto out; 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci dev->tag_protocol = DSA_TAG_PROTO_BRCM; 212662306a36Sopenharmony_ciout: 212762306a36Sopenharmony_ci return dev->tag_protocol; 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ciEXPORT_SYMBOL(b53_get_tag_protocol); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ciint b53_mirror_add(struct dsa_switch *ds, int port, 213262306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror, bool ingress, 213362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 213662306a36Sopenharmony_ci u16 reg, loc; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (ingress) 213962306a36Sopenharmony_ci loc = B53_IG_MIR_CTL; 214062306a36Sopenharmony_ci else 214162306a36Sopenharmony_ci loc = B53_EG_MIR_CTL; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, loc, ®); 214462306a36Sopenharmony_ci reg |= BIT(port); 214562306a36Sopenharmony_ci b53_write16(dev, B53_MGMT_PAGE, loc, reg); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, ®); 214862306a36Sopenharmony_ci reg &= ~CAP_PORT_MASK; 214962306a36Sopenharmony_ci reg |= mirror->to_local_port; 215062306a36Sopenharmony_ci reg |= MIRROR_EN; 215162306a36Sopenharmony_ci b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg); 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci return 0; 215462306a36Sopenharmony_ci} 215562306a36Sopenharmony_ciEXPORT_SYMBOL(b53_mirror_add); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_civoid b53_mirror_del(struct dsa_switch *ds, int port, 215862306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror) 215962306a36Sopenharmony_ci{ 216062306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 216162306a36Sopenharmony_ci bool loc_disable = false, other_loc_disable = false; 216262306a36Sopenharmony_ci u16 reg, loc; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci if (mirror->ingress) 216562306a36Sopenharmony_ci loc = B53_IG_MIR_CTL; 216662306a36Sopenharmony_ci else 216762306a36Sopenharmony_ci loc = B53_EG_MIR_CTL; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci /* Update the desired ingress/egress register */ 217062306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, loc, ®); 217162306a36Sopenharmony_ci reg &= ~BIT(port); 217262306a36Sopenharmony_ci if (!(reg & MIRROR_MASK)) 217362306a36Sopenharmony_ci loc_disable = true; 217462306a36Sopenharmony_ci b53_write16(dev, B53_MGMT_PAGE, loc, reg); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* Now look at the other one to know if we can disable mirroring 217762306a36Sopenharmony_ci * entirely 217862306a36Sopenharmony_ci */ 217962306a36Sopenharmony_ci if (mirror->ingress) 218062306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, ®); 218162306a36Sopenharmony_ci else 218262306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, ®); 218362306a36Sopenharmony_ci if (!(reg & MIRROR_MASK)) 218462306a36Sopenharmony_ci other_loc_disable = true; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, ®); 218762306a36Sopenharmony_ci /* Both no longer have ports, let's disable mirroring */ 218862306a36Sopenharmony_ci if (loc_disable && other_loc_disable) { 218962306a36Sopenharmony_ci reg &= ~MIRROR_EN; 219062306a36Sopenharmony_ci reg &= ~mirror->to_local_port; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg); 219362306a36Sopenharmony_ci} 219462306a36Sopenharmony_ciEXPORT_SYMBOL(b53_mirror_del); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_civoid b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable) 219762306a36Sopenharmony_ci{ 219862306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 219962306a36Sopenharmony_ci u16 reg; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci b53_read16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, ®); 220262306a36Sopenharmony_ci if (enable) 220362306a36Sopenharmony_ci reg |= BIT(port); 220462306a36Sopenharmony_ci else 220562306a36Sopenharmony_ci reg &= ~BIT(port); 220662306a36Sopenharmony_ci b53_write16(dev, B53_EEE_PAGE, B53_EEE_EN_CTRL, reg); 220762306a36Sopenharmony_ci} 220862306a36Sopenharmony_ciEXPORT_SYMBOL(b53_eee_enable_set); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci/* Returns 0 if EEE was not enabled, or 1 otherwise 221262306a36Sopenharmony_ci */ 221362306a36Sopenharmony_ciint b53_eee_init(struct dsa_switch *ds, int port, struct phy_device *phy) 221462306a36Sopenharmony_ci{ 221562306a36Sopenharmony_ci int ret; 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci ret = phy_init_eee(phy, false); 221862306a36Sopenharmony_ci if (ret) 221962306a36Sopenharmony_ci return 0; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci b53_eee_enable_set(ds, port, true); 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci return 1; 222462306a36Sopenharmony_ci} 222562306a36Sopenharmony_ciEXPORT_SYMBOL(b53_eee_init); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ciint b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) 222862306a36Sopenharmony_ci{ 222962306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 223062306a36Sopenharmony_ci struct ethtool_eee *p = &dev->ports[port].eee; 223162306a36Sopenharmony_ci u16 reg; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 223462306a36Sopenharmony_ci return -EOPNOTSUPP; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci b53_read16(dev, B53_EEE_PAGE, B53_EEE_LPI_INDICATE, ®); 223762306a36Sopenharmony_ci e->eee_enabled = p->eee_enabled; 223862306a36Sopenharmony_ci e->eee_active = !!(reg & BIT(port)); 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci return 0; 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ciEXPORT_SYMBOL(b53_get_mac_eee); 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ciint b53_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) 224562306a36Sopenharmony_ci{ 224662306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 224762306a36Sopenharmony_ci struct ethtool_eee *p = &dev->ports[port].eee; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 225062306a36Sopenharmony_ci return -EOPNOTSUPP; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci p->eee_enabled = e->eee_enabled; 225362306a36Sopenharmony_ci b53_eee_enable_set(ds, port, e->eee_enabled); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci return 0; 225662306a36Sopenharmony_ci} 225762306a36Sopenharmony_ciEXPORT_SYMBOL(b53_set_mac_eee); 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_cistatic int b53_change_mtu(struct dsa_switch *ds, int port, int mtu) 226062306a36Sopenharmony_ci{ 226162306a36Sopenharmony_ci struct b53_device *dev = ds->priv; 226262306a36Sopenharmony_ci bool enable_jumbo; 226362306a36Sopenharmony_ci bool allow_10_100; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci if (is5325(dev) || is5365(dev)) 226662306a36Sopenharmony_ci return -EOPNOTSUPP; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci enable_jumbo = (mtu >= JMS_MIN_SIZE); 226962306a36Sopenharmony_ci allow_10_100 = (dev->chip_id == BCM583XX_DEVICE_ID); 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci return b53_set_jumbo(dev, enable_jumbo, allow_10_100); 227262306a36Sopenharmony_ci} 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_cistatic int b53_get_max_mtu(struct dsa_switch *ds, int port) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci return JMS_MAX_SIZE; 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_cistatic const struct dsa_switch_ops b53_switch_ops = { 228062306a36Sopenharmony_ci .get_tag_protocol = b53_get_tag_protocol, 228162306a36Sopenharmony_ci .setup = b53_setup, 228262306a36Sopenharmony_ci .teardown = b53_teardown, 228362306a36Sopenharmony_ci .get_strings = b53_get_strings, 228462306a36Sopenharmony_ci .get_ethtool_stats = b53_get_ethtool_stats, 228562306a36Sopenharmony_ci .get_sset_count = b53_get_sset_count, 228662306a36Sopenharmony_ci .get_ethtool_phy_stats = b53_get_ethtool_phy_stats, 228762306a36Sopenharmony_ci .phy_read = b53_phy_read16, 228862306a36Sopenharmony_ci .phy_write = b53_phy_write16, 228962306a36Sopenharmony_ci .adjust_link = b53_adjust_link, 229062306a36Sopenharmony_ci .phylink_get_caps = b53_phylink_get_caps, 229162306a36Sopenharmony_ci .phylink_mac_select_pcs = b53_phylink_mac_select_pcs, 229262306a36Sopenharmony_ci .phylink_mac_config = b53_phylink_mac_config, 229362306a36Sopenharmony_ci .phylink_mac_link_down = b53_phylink_mac_link_down, 229462306a36Sopenharmony_ci .phylink_mac_link_up = b53_phylink_mac_link_up, 229562306a36Sopenharmony_ci .port_enable = b53_enable_port, 229662306a36Sopenharmony_ci .port_disable = b53_disable_port, 229762306a36Sopenharmony_ci .get_mac_eee = b53_get_mac_eee, 229862306a36Sopenharmony_ci .set_mac_eee = b53_set_mac_eee, 229962306a36Sopenharmony_ci .port_bridge_join = b53_br_join, 230062306a36Sopenharmony_ci .port_bridge_leave = b53_br_leave, 230162306a36Sopenharmony_ci .port_pre_bridge_flags = b53_br_flags_pre, 230262306a36Sopenharmony_ci .port_bridge_flags = b53_br_flags, 230362306a36Sopenharmony_ci .port_stp_state_set = b53_br_set_stp_state, 230462306a36Sopenharmony_ci .port_fast_age = b53_br_fast_age, 230562306a36Sopenharmony_ci .port_vlan_filtering = b53_vlan_filtering, 230662306a36Sopenharmony_ci .port_vlan_add = b53_vlan_add, 230762306a36Sopenharmony_ci .port_vlan_del = b53_vlan_del, 230862306a36Sopenharmony_ci .port_fdb_dump = b53_fdb_dump, 230962306a36Sopenharmony_ci .port_fdb_add = b53_fdb_add, 231062306a36Sopenharmony_ci .port_fdb_del = b53_fdb_del, 231162306a36Sopenharmony_ci .port_mirror_add = b53_mirror_add, 231262306a36Sopenharmony_ci .port_mirror_del = b53_mirror_del, 231362306a36Sopenharmony_ci .port_mdb_add = b53_mdb_add, 231462306a36Sopenharmony_ci .port_mdb_del = b53_mdb_del, 231562306a36Sopenharmony_ci .port_max_mtu = b53_get_max_mtu, 231662306a36Sopenharmony_ci .port_change_mtu = b53_change_mtu, 231762306a36Sopenharmony_ci}; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_cistruct b53_chip_data { 232062306a36Sopenharmony_ci u32 chip_id; 232162306a36Sopenharmony_ci const char *dev_name; 232262306a36Sopenharmony_ci u16 vlans; 232362306a36Sopenharmony_ci u16 enabled_ports; 232462306a36Sopenharmony_ci u8 imp_port; 232562306a36Sopenharmony_ci u8 cpu_port; 232662306a36Sopenharmony_ci u8 vta_regs[3]; 232762306a36Sopenharmony_ci u8 arl_bins; 232862306a36Sopenharmony_ci u16 arl_buckets; 232962306a36Sopenharmony_ci u8 duplex_reg; 233062306a36Sopenharmony_ci u8 jumbo_pm_reg; 233162306a36Sopenharmony_ci u8 jumbo_size_reg; 233262306a36Sopenharmony_ci}; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci#define B53_VTA_REGS \ 233562306a36Sopenharmony_ci { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY } 233662306a36Sopenharmony_ci#define B53_VTA_REGS_9798 \ 233762306a36Sopenharmony_ci { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 } 233862306a36Sopenharmony_ci#define B53_VTA_REGS_63XX \ 233962306a36Sopenharmony_ci { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX } 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_cistatic const struct b53_chip_data b53_switch_chips[] = { 234262306a36Sopenharmony_ci { 234362306a36Sopenharmony_ci .chip_id = BCM5325_DEVICE_ID, 234462306a36Sopenharmony_ci .dev_name = "BCM5325", 234562306a36Sopenharmony_ci .vlans = 16, 234662306a36Sopenharmony_ci .enabled_ports = 0x3f, 234762306a36Sopenharmony_ci .arl_bins = 2, 234862306a36Sopenharmony_ci .arl_buckets = 1024, 234962306a36Sopenharmony_ci .imp_port = 5, 235062306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_FE, 235162306a36Sopenharmony_ci }, 235262306a36Sopenharmony_ci { 235362306a36Sopenharmony_ci .chip_id = BCM5365_DEVICE_ID, 235462306a36Sopenharmony_ci .dev_name = "BCM5365", 235562306a36Sopenharmony_ci .vlans = 256, 235662306a36Sopenharmony_ci .enabled_ports = 0x3f, 235762306a36Sopenharmony_ci .arl_bins = 2, 235862306a36Sopenharmony_ci .arl_buckets = 1024, 235962306a36Sopenharmony_ci .imp_port = 5, 236062306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_FE, 236162306a36Sopenharmony_ci }, 236262306a36Sopenharmony_ci { 236362306a36Sopenharmony_ci .chip_id = BCM5389_DEVICE_ID, 236462306a36Sopenharmony_ci .dev_name = "BCM5389", 236562306a36Sopenharmony_ci .vlans = 4096, 236662306a36Sopenharmony_ci .enabled_ports = 0x11f, 236762306a36Sopenharmony_ci .arl_bins = 4, 236862306a36Sopenharmony_ci .arl_buckets = 1024, 236962306a36Sopenharmony_ci .imp_port = 8, 237062306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 237162306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 237262306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 237362306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 237462306a36Sopenharmony_ci }, 237562306a36Sopenharmony_ci { 237662306a36Sopenharmony_ci .chip_id = BCM5395_DEVICE_ID, 237762306a36Sopenharmony_ci .dev_name = "BCM5395", 237862306a36Sopenharmony_ci .vlans = 4096, 237962306a36Sopenharmony_ci .enabled_ports = 0x11f, 238062306a36Sopenharmony_ci .arl_bins = 4, 238162306a36Sopenharmony_ci .arl_buckets = 1024, 238262306a36Sopenharmony_ci .imp_port = 8, 238362306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 238462306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 238562306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 238662306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 238762306a36Sopenharmony_ci }, 238862306a36Sopenharmony_ci { 238962306a36Sopenharmony_ci .chip_id = BCM5397_DEVICE_ID, 239062306a36Sopenharmony_ci .dev_name = "BCM5397", 239162306a36Sopenharmony_ci .vlans = 4096, 239262306a36Sopenharmony_ci .enabled_ports = 0x11f, 239362306a36Sopenharmony_ci .arl_bins = 4, 239462306a36Sopenharmony_ci .arl_buckets = 1024, 239562306a36Sopenharmony_ci .imp_port = 8, 239662306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS_9798, 239762306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 239862306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 239962306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 240062306a36Sopenharmony_ci }, 240162306a36Sopenharmony_ci { 240262306a36Sopenharmony_ci .chip_id = BCM5398_DEVICE_ID, 240362306a36Sopenharmony_ci .dev_name = "BCM5398", 240462306a36Sopenharmony_ci .vlans = 4096, 240562306a36Sopenharmony_ci .enabled_ports = 0x17f, 240662306a36Sopenharmony_ci .arl_bins = 4, 240762306a36Sopenharmony_ci .arl_buckets = 1024, 240862306a36Sopenharmony_ci .imp_port = 8, 240962306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS_9798, 241062306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 241162306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 241262306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 241362306a36Sopenharmony_ci }, 241462306a36Sopenharmony_ci { 241562306a36Sopenharmony_ci .chip_id = BCM53115_DEVICE_ID, 241662306a36Sopenharmony_ci .dev_name = "BCM53115", 241762306a36Sopenharmony_ci .vlans = 4096, 241862306a36Sopenharmony_ci .enabled_ports = 0x11f, 241962306a36Sopenharmony_ci .arl_bins = 4, 242062306a36Sopenharmony_ci .arl_buckets = 1024, 242162306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 242262306a36Sopenharmony_ci .imp_port = 8, 242362306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 242462306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 242562306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 242662306a36Sopenharmony_ci }, 242762306a36Sopenharmony_ci { 242862306a36Sopenharmony_ci .chip_id = BCM53125_DEVICE_ID, 242962306a36Sopenharmony_ci .dev_name = "BCM53125", 243062306a36Sopenharmony_ci .vlans = 4096, 243162306a36Sopenharmony_ci .enabled_ports = 0x1ff, 243262306a36Sopenharmony_ci .arl_bins = 4, 243362306a36Sopenharmony_ci .arl_buckets = 1024, 243462306a36Sopenharmony_ci .imp_port = 8, 243562306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 243662306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 243762306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 243862306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 243962306a36Sopenharmony_ci }, 244062306a36Sopenharmony_ci { 244162306a36Sopenharmony_ci .chip_id = BCM53128_DEVICE_ID, 244262306a36Sopenharmony_ci .dev_name = "BCM53128", 244362306a36Sopenharmony_ci .vlans = 4096, 244462306a36Sopenharmony_ci .enabled_ports = 0x1ff, 244562306a36Sopenharmony_ci .arl_bins = 4, 244662306a36Sopenharmony_ci .arl_buckets = 1024, 244762306a36Sopenharmony_ci .imp_port = 8, 244862306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 244962306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 245062306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 245162306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 245262306a36Sopenharmony_ci }, 245362306a36Sopenharmony_ci { 245462306a36Sopenharmony_ci .chip_id = BCM63XX_DEVICE_ID, 245562306a36Sopenharmony_ci .dev_name = "BCM63xx", 245662306a36Sopenharmony_ci .vlans = 4096, 245762306a36Sopenharmony_ci .enabled_ports = 0, /* pdata must provide them */ 245862306a36Sopenharmony_ci .arl_bins = 4, 245962306a36Sopenharmony_ci .arl_buckets = 1024, 246062306a36Sopenharmony_ci .imp_port = 8, 246162306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS_63XX, 246262306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_63XX, 246362306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, 246462306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, 246562306a36Sopenharmony_ci }, 246662306a36Sopenharmony_ci { 246762306a36Sopenharmony_ci .chip_id = BCM63268_DEVICE_ID, 246862306a36Sopenharmony_ci .dev_name = "BCM63268", 246962306a36Sopenharmony_ci .vlans = 4096, 247062306a36Sopenharmony_ci .enabled_ports = 0, /* pdata must provide them */ 247162306a36Sopenharmony_ci .arl_bins = 4, 247262306a36Sopenharmony_ci .arl_buckets = 1024, 247362306a36Sopenharmony_ci .imp_port = 8, 247462306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS_63XX, 247562306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_63XX, 247662306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, 247762306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX, 247862306a36Sopenharmony_ci }, 247962306a36Sopenharmony_ci { 248062306a36Sopenharmony_ci .chip_id = BCM53010_DEVICE_ID, 248162306a36Sopenharmony_ci .dev_name = "BCM53010", 248262306a36Sopenharmony_ci .vlans = 4096, 248362306a36Sopenharmony_ci .enabled_ports = 0x1bf, 248462306a36Sopenharmony_ci .arl_bins = 4, 248562306a36Sopenharmony_ci .arl_buckets = 1024, 248662306a36Sopenharmony_ci .imp_port = 8, 248762306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 248862306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 248962306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 249062306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 249162306a36Sopenharmony_ci }, 249262306a36Sopenharmony_ci { 249362306a36Sopenharmony_ci .chip_id = BCM53011_DEVICE_ID, 249462306a36Sopenharmony_ci .dev_name = "BCM53011", 249562306a36Sopenharmony_ci .vlans = 4096, 249662306a36Sopenharmony_ci .enabled_ports = 0x1bf, 249762306a36Sopenharmony_ci .arl_bins = 4, 249862306a36Sopenharmony_ci .arl_buckets = 1024, 249962306a36Sopenharmony_ci .imp_port = 8, 250062306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 250162306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 250262306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 250362306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 250462306a36Sopenharmony_ci }, 250562306a36Sopenharmony_ci { 250662306a36Sopenharmony_ci .chip_id = BCM53012_DEVICE_ID, 250762306a36Sopenharmony_ci .dev_name = "BCM53012", 250862306a36Sopenharmony_ci .vlans = 4096, 250962306a36Sopenharmony_ci .enabled_ports = 0x1bf, 251062306a36Sopenharmony_ci .arl_bins = 4, 251162306a36Sopenharmony_ci .arl_buckets = 1024, 251262306a36Sopenharmony_ci .imp_port = 8, 251362306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 251462306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 251562306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 251662306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 251762306a36Sopenharmony_ci }, 251862306a36Sopenharmony_ci { 251962306a36Sopenharmony_ci .chip_id = BCM53018_DEVICE_ID, 252062306a36Sopenharmony_ci .dev_name = "BCM53018", 252162306a36Sopenharmony_ci .vlans = 4096, 252262306a36Sopenharmony_ci .enabled_ports = 0x1bf, 252362306a36Sopenharmony_ci .arl_bins = 4, 252462306a36Sopenharmony_ci .arl_buckets = 1024, 252562306a36Sopenharmony_ci .imp_port = 8, 252662306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 252762306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 252862306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 252962306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 253062306a36Sopenharmony_ci }, 253162306a36Sopenharmony_ci { 253262306a36Sopenharmony_ci .chip_id = BCM53019_DEVICE_ID, 253362306a36Sopenharmony_ci .dev_name = "BCM53019", 253462306a36Sopenharmony_ci .vlans = 4096, 253562306a36Sopenharmony_ci .enabled_ports = 0x1bf, 253662306a36Sopenharmony_ci .arl_bins = 4, 253762306a36Sopenharmony_ci .arl_buckets = 1024, 253862306a36Sopenharmony_ci .imp_port = 8, 253962306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 254062306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 254162306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 254262306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 254362306a36Sopenharmony_ci }, 254462306a36Sopenharmony_ci { 254562306a36Sopenharmony_ci .chip_id = BCM58XX_DEVICE_ID, 254662306a36Sopenharmony_ci .dev_name = "BCM585xx/586xx/88312", 254762306a36Sopenharmony_ci .vlans = 4096, 254862306a36Sopenharmony_ci .enabled_ports = 0x1ff, 254962306a36Sopenharmony_ci .arl_bins = 4, 255062306a36Sopenharmony_ci .arl_buckets = 1024, 255162306a36Sopenharmony_ci .imp_port = 8, 255262306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 255362306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 255462306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 255562306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 255662306a36Sopenharmony_ci }, 255762306a36Sopenharmony_ci { 255862306a36Sopenharmony_ci .chip_id = BCM583XX_DEVICE_ID, 255962306a36Sopenharmony_ci .dev_name = "BCM583xx/11360", 256062306a36Sopenharmony_ci .vlans = 4096, 256162306a36Sopenharmony_ci .enabled_ports = 0x103, 256262306a36Sopenharmony_ci .arl_bins = 4, 256362306a36Sopenharmony_ci .arl_buckets = 1024, 256462306a36Sopenharmony_ci .imp_port = 8, 256562306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 256662306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 256762306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 256862306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 256962306a36Sopenharmony_ci }, 257062306a36Sopenharmony_ci /* Starfighter 2 */ 257162306a36Sopenharmony_ci { 257262306a36Sopenharmony_ci .chip_id = BCM4908_DEVICE_ID, 257362306a36Sopenharmony_ci .dev_name = "BCM4908", 257462306a36Sopenharmony_ci .vlans = 4096, 257562306a36Sopenharmony_ci .enabled_ports = 0x1bf, 257662306a36Sopenharmony_ci .arl_bins = 4, 257762306a36Sopenharmony_ci .arl_buckets = 256, 257862306a36Sopenharmony_ci .imp_port = 8, 257962306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 258062306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 258162306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 258262306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 258362306a36Sopenharmony_ci }, 258462306a36Sopenharmony_ci { 258562306a36Sopenharmony_ci .chip_id = BCM7445_DEVICE_ID, 258662306a36Sopenharmony_ci .dev_name = "BCM7445", 258762306a36Sopenharmony_ci .vlans = 4096, 258862306a36Sopenharmony_ci .enabled_ports = 0x1ff, 258962306a36Sopenharmony_ci .arl_bins = 4, 259062306a36Sopenharmony_ci .arl_buckets = 1024, 259162306a36Sopenharmony_ci .imp_port = 8, 259262306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 259362306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 259462306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 259562306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 259662306a36Sopenharmony_ci }, 259762306a36Sopenharmony_ci { 259862306a36Sopenharmony_ci .chip_id = BCM7278_DEVICE_ID, 259962306a36Sopenharmony_ci .dev_name = "BCM7278", 260062306a36Sopenharmony_ci .vlans = 4096, 260162306a36Sopenharmony_ci .enabled_ports = 0x1ff, 260262306a36Sopenharmony_ci .arl_bins = 4, 260362306a36Sopenharmony_ci .arl_buckets = 256, 260462306a36Sopenharmony_ci .imp_port = 8, 260562306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 260662306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 260762306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 260862306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 260962306a36Sopenharmony_ci }, 261062306a36Sopenharmony_ci { 261162306a36Sopenharmony_ci .chip_id = BCM53134_DEVICE_ID, 261262306a36Sopenharmony_ci .dev_name = "BCM53134", 261362306a36Sopenharmony_ci .vlans = 4096, 261462306a36Sopenharmony_ci .enabled_ports = 0x12f, 261562306a36Sopenharmony_ci .imp_port = 8, 261662306a36Sopenharmony_ci .cpu_port = B53_CPU_PORT, 261762306a36Sopenharmony_ci .vta_regs = B53_VTA_REGS, 261862306a36Sopenharmony_ci .arl_bins = 4, 261962306a36Sopenharmony_ci .arl_buckets = 1024, 262062306a36Sopenharmony_ci .duplex_reg = B53_DUPLEX_STAT_GE, 262162306a36Sopenharmony_ci .jumbo_pm_reg = B53_JUMBO_PORT_MASK, 262262306a36Sopenharmony_ci .jumbo_size_reg = B53_JUMBO_MAX_SIZE, 262362306a36Sopenharmony_ci }, 262462306a36Sopenharmony_ci}; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_cistatic int b53_switch_init(struct b53_device *dev) 262762306a36Sopenharmony_ci{ 262862306a36Sopenharmony_ci unsigned int i; 262962306a36Sopenharmony_ci int ret; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) { 263262306a36Sopenharmony_ci const struct b53_chip_data *chip = &b53_switch_chips[i]; 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci if (chip->chip_id == dev->chip_id) { 263562306a36Sopenharmony_ci if (!dev->enabled_ports) 263662306a36Sopenharmony_ci dev->enabled_ports = chip->enabled_ports; 263762306a36Sopenharmony_ci dev->name = chip->dev_name; 263862306a36Sopenharmony_ci dev->duplex_reg = chip->duplex_reg; 263962306a36Sopenharmony_ci dev->vta_regs[0] = chip->vta_regs[0]; 264062306a36Sopenharmony_ci dev->vta_regs[1] = chip->vta_regs[1]; 264162306a36Sopenharmony_ci dev->vta_regs[2] = chip->vta_regs[2]; 264262306a36Sopenharmony_ci dev->jumbo_pm_reg = chip->jumbo_pm_reg; 264362306a36Sopenharmony_ci dev->imp_port = chip->imp_port; 264462306a36Sopenharmony_ci dev->num_vlans = chip->vlans; 264562306a36Sopenharmony_ci dev->num_arl_bins = chip->arl_bins; 264662306a36Sopenharmony_ci dev->num_arl_buckets = chip->arl_buckets; 264762306a36Sopenharmony_ci break; 264862306a36Sopenharmony_ci } 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci /* check which BCM5325x version we have */ 265262306a36Sopenharmony_ci if (is5325(dev)) { 265362306a36Sopenharmony_ci u8 vc4; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4); 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci /* check reserved bits */ 265862306a36Sopenharmony_ci switch (vc4 & 3) { 265962306a36Sopenharmony_ci case 1: 266062306a36Sopenharmony_ci /* BCM5325E */ 266162306a36Sopenharmony_ci break; 266262306a36Sopenharmony_ci case 3: 266362306a36Sopenharmony_ci /* BCM5325F - do not use port 4 */ 266462306a36Sopenharmony_ci dev->enabled_ports &= ~BIT(4); 266562306a36Sopenharmony_ci break; 266662306a36Sopenharmony_ci default: 266762306a36Sopenharmony_ci/* On the BCM47XX SoCs this is the supported internal switch.*/ 266862306a36Sopenharmony_ci#ifndef CONFIG_BCM47XX 266962306a36Sopenharmony_ci /* BCM5325M */ 267062306a36Sopenharmony_ci return -EINVAL; 267162306a36Sopenharmony_ci#else 267262306a36Sopenharmony_ci break; 267362306a36Sopenharmony_ci#endif 267462306a36Sopenharmony_ci } 267562306a36Sopenharmony_ci } 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci dev->num_ports = fls(dev->enabled_ports); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS); 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci /* Include non standard CPU port built-in PHYs to be probed */ 268262306a36Sopenharmony_ci if (is539x(dev) || is531x5(dev)) { 268362306a36Sopenharmony_ci for (i = 0; i < dev->num_ports; i++) { 268462306a36Sopenharmony_ci if (!(dev->ds->phys_mii_mask & BIT(i)) && 268562306a36Sopenharmony_ci !b53_possible_cpu_port(dev->ds, i)) 268662306a36Sopenharmony_ci dev->ds->phys_mii_mask |= BIT(i); 268762306a36Sopenharmony_ci } 268862306a36Sopenharmony_ci } 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci dev->ports = devm_kcalloc(dev->dev, 269162306a36Sopenharmony_ci dev->num_ports, sizeof(struct b53_port), 269262306a36Sopenharmony_ci GFP_KERNEL); 269362306a36Sopenharmony_ci if (!dev->ports) 269462306a36Sopenharmony_ci return -ENOMEM; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci dev->vlans = devm_kcalloc(dev->dev, 269762306a36Sopenharmony_ci dev->num_vlans, sizeof(struct b53_vlan), 269862306a36Sopenharmony_ci GFP_KERNEL); 269962306a36Sopenharmony_ci if (!dev->vlans) 270062306a36Sopenharmony_ci return -ENOMEM; 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci dev->reset_gpio = b53_switch_get_reset_gpio(dev); 270362306a36Sopenharmony_ci if (dev->reset_gpio >= 0) { 270462306a36Sopenharmony_ci ret = devm_gpio_request_one(dev->dev, dev->reset_gpio, 270562306a36Sopenharmony_ci GPIOF_OUT_INIT_HIGH, "robo_reset"); 270662306a36Sopenharmony_ci if (ret) 270762306a36Sopenharmony_ci return ret; 270862306a36Sopenharmony_ci } 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci return 0; 271162306a36Sopenharmony_ci} 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_cistruct b53_device *b53_switch_alloc(struct device *base, 271462306a36Sopenharmony_ci const struct b53_io_ops *ops, 271562306a36Sopenharmony_ci void *priv) 271662306a36Sopenharmony_ci{ 271762306a36Sopenharmony_ci struct dsa_switch *ds; 271862306a36Sopenharmony_ci struct b53_device *dev; 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL); 272162306a36Sopenharmony_ci if (!ds) 272262306a36Sopenharmony_ci return NULL; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci ds->dev = base; 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL); 272762306a36Sopenharmony_ci if (!dev) 272862306a36Sopenharmony_ci return NULL; 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci ds->priv = dev; 273162306a36Sopenharmony_ci dev->dev = base; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci dev->ds = ds; 273462306a36Sopenharmony_ci dev->priv = priv; 273562306a36Sopenharmony_ci dev->ops = ops; 273662306a36Sopenharmony_ci ds->ops = &b53_switch_ops; 273762306a36Sopenharmony_ci dev->vlan_enabled = true; 273862306a36Sopenharmony_ci /* Let DSA handle the case were multiple bridges span the same switch 273962306a36Sopenharmony_ci * device and different VLAN awareness settings are requested, which 274062306a36Sopenharmony_ci * would be breaking filtering semantics for any of the other bridge 274162306a36Sopenharmony_ci * devices. (not hardware supported) 274262306a36Sopenharmony_ci */ 274362306a36Sopenharmony_ci ds->vlan_filtering_is_global = true; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci mutex_init(&dev->reg_mutex); 274662306a36Sopenharmony_ci mutex_init(&dev->stats_mutex); 274762306a36Sopenharmony_ci mutex_init(&dev->arl_mutex); 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci return dev; 275062306a36Sopenharmony_ci} 275162306a36Sopenharmony_ciEXPORT_SYMBOL(b53_switch_alloc); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ciint b53_switch_detect(struct b53_device *dev) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci u32 id32; 275662306a36Sopenharmony_ci u16 tmp; 275762306a36Sopenharmony_ci u8 id8; 275862306a36Sopenharmony_ci int ret; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8); 276162306a36Sopenharmony_ci if (ret) 276262306a36Sopenharmony_ci return ret; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci switch (id8) { 276562306a36Sopenharmony_ci case 0: 276662306a36Sopenharmony_ci /* BCM5325 and BCM5365 do not have this register so reads 276762306a36Sopenharmony_ci * return 0. But the read operation did succeed, so assume this 276862306a36Sopenharmony_ci * is one of them. 276962306a36Sopenharmony_ci * 277062306a36Sopenharmony_ci * Next check if we can write to the 5325's VTA register; for 277162306a36Sopenharmony_ci * 5365 it is read only. 277262306a36Sopenharmony_ci */ 277362306a36Sopenharmony_ci b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf); 277462306a36Sopenharmony_ci b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp); 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci if (tmp == 0xf) 277762306a36Sopenharmony_ci dev->chip_id = BCM5325_DEVICE_ID; 277862306a36Sopenharmony_ci else 277962306a36Sopenharmony_ci dev->chip_id = BCM5365_DEVICE_ID; 278062306a36Sopenharmony_ci break; 278162306a36Sopenharmony_ci case BCM5389_DEVICE_ID: 278262306a36Sopenharmony_ci case BCM5395_DEVICE_ID: 278362306a36Sopenharmony_ci case BCM5397_DEVICE_ID: 278462306a36Sopenharmony_ci case BCM5398_DEVICE_ID: 278562306a36Sopenharmony_ci dev->chip_id = id8; 278662306a36Sopenharmony_ci break; 278762306a36Sopenharmony_ci default: 278862306a36Sopenharmony_ci ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32); 278962306a36Sopenharmony_ci if (ret) 279062306a36Sopenharmony_ci return ret; 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci switch (id32) { 279362306a36Sopenharmony_ci case BCM53115_DEVICE_ID: 279462306a36Sopenharmony_ci case BCM53125_DEVICE_ID: 279562306a36Sopenharmony_ci case BCM53128_DEVICE_ID: 279662306a36Sopenharmony_ci case BCM53010_DEVICE_ID: 279762306a36Sopenharmony_ci case BCM53011_DEVICE_ID: 279862306a36Sopenharmony_ci case BCM53012_DEVICE_ID: 279962306a36Sopenharmony_ci case BCM53018_DEVICE_ID: 280062306a36Sopenharmony_ci case BCM53019_DEVICE_ID: 280162306a36Sopenharmony_ci case BCM53134_DEVICE_ID: 280262306a36Sopenharmony_ci dev->chip_id = id32; 280362306a36Sopenharmony_ci break; 280462306a36Sopenharmony_ci default: 280562306a36Sopenharmony_ci dev_err(dev->dev, 280662306a36Sopenharmony_ci "unsupported switch detected (BCM53%02x/BCM%x)\n", 280762306a36Sopenharmony_ci id8, id32); 280862306a36Sopenharmony_ci return -ENODEV; 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci } 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci if (dev->chip_id == BCM5325_DEVICE_ID) 281362306a36Sopenharmony_ci return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25, 281462306a36Sopenharmony_ci &dev->core_rev); 281562306a36Sopenharmony_ci else 281662306a36Sopenharmony_ci return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID, 281762306a36Sopenharmony_ci &dev->core_rev); 281862306a36Sopenharmony_ci} 281962306a36Sopenharmony_ciEXPORT_SYMBOL(b53_switch_detect); 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ciint b53_switch_register(struct b53_device *dev) 282262306a36Sopenharmony_ci{ 282362306a36Sopenharmony_ci int ret; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci if (dev->pdata) { 282662306a36Sopenharmony_ci dev->chip_id = dev->pdata->chip_id; 282762306a36Sopenharmony_ci dev->enabled_ports = dev->pdata->enabled_ports; 282862306a36Sopenharmony_ci } 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_ci if (!dev->chip_id && b53_switch_detect(dev)) 283162306a36Sopenharmony_ci return -EINVAL; 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci ret = b53_switch_init(dev); 283462306a36Sopenharmony_ci if (ret) 283562306a36Sopenharmony_ci return ret; 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci dev_info(dev->dev, "found switch: %s, rev %i\n", 283862306a36Sopenharmony_ci dev->name, dev->core_rev); 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci return dsa_register_switch(dev->ds); 284162306a36Sopenharmony_ci} 284262306a36Sopenharmony_ciEXPORT_SYMBOL(b53_switch_register); 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ciMODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 284562306a36Sopenharmony_ciMODULE_DESCRIPTION("B53 switch library"); 284662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 2847