1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Update 6.12 kernel patches

This commit is contained in:
Ycarus (Yannick Chabanois) 2024-12-26 18:19:04 +01:00
parent bdb9b0046f
commit 9d83c70ced
247 changed files with 53301 additions and 589 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
/*
* ADM6996 switch driver
*
* Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
* Copyright (c) 2010,2011 Peter Lebbing <peter@digitalbrains.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
* Free Software Foundation
*/
#ifndef __ADM6996_H
#define __ADM6996_H
/*
* ADM_PHY_PORTS: Number of ports with a PHY.
* We only control ports 0 to 3, because if 4 is connected, it is most likely
* not connected to the switch but to a separate MII and MAC for the WAN port.
*/
#define ADM_PHY_PORTS 4
#define ADM_NUM_PORTS 6
#define ADM_CPU_PORT 5
#define ADM_NUM_VLANS 16
#define ADM_VLAN_MAX_ID 4094
enum admreg {
ADM_EEPROM_BASE = 0x0,
ADM_P0_CFG = ADM_EEPROM_BASE + 1,
ADM_P1_CFG = ADM_EEPROM_BASE + 3,
ADM_P2_CFG = ADM_EEPROM_BASE + 5,
ADM_P3_CFG = ADM_EEPROM_BASE + 7,
ADM_P4_CFG = ADM_EEPROM_BASE + 8,
ADM_P5_CFG = ADM_EEPROM_BASE + 9,
ADM_SYSC0 = ADM_EEPROM_BASE + 0xa,
ADM_VLAN_PRIOMAP = ADM_EEPROM_BASE + 0xe,
ADM_SYSC3 = ADM_EEPROM_BASE + 0x11,
/* Input Force No Tag Enable */
ADM_IFNTE = ADM_EEPROM_BASE + 0x20,
ADM_VID_CHECK = ADM_EEPROM_BASE + 0x26,
ADM_P0_PVID = ADM_EEPROM_BASE + 0x28,
ADM_P1_PVID = ADM_EEPROM_BASE + 0x29,
/* Output Tag Bypass Enable and P2 PVID */
ADM_OTBE_P2_PVID = ADM_EEPROM_BASE + 0x2a,
ADM_P3_P4_PVID = ADM_EEPROM_BASE + 0x2b,
ADM_P5_PVID = ADM_EEPROM_BASE + 0x2c,
ADM_EEPROM_EXT_BASE = 0x40,
#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n))
#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n))
#define ADM_VLAN_MAP(n) (ADM_EEPROM_BASE + 0x13 + n)
ADM_COUNTER_BASE = 0xa0,
ADM_SIG0 = ADM_COUNTER_BASE + 0,
ADM_SIG1 = ADM_COUNTER_BASE + 1,
ADM_PS0 = ADM_COUNTER_BASE + 2,
ADM_PS1 = ADM_COUNTER_BASE + 3,
ADM_PS2 = ADM_COUNTER_BASE + 4,
ADM_CL0 = ADM_COUNTER_BASE + 8, /* RxPacket */
ADM_CL6 = ADM_COUNTER_BASE + 0x1a, /* RxByte */
ADM_CL12 = ADM_COUNTER_BASE + 0x2c, /* TxPacket */
ADM_CL18 = ADM_COUNTER_BASE + 0x3e, /* TxByte */
ADM_CL24 = ADM_COUNTER_BASE + 0x50, /* Coll */
ADM_CL30 = ADM_COUNTER_BASE + 0x62, /* Err */
#define ADM_OFFSET_PORT(n) ((n * 4) - (n / 4) * 2 - (n / 5) * 2)
ADM_PHY_BASE = 0x200,
#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
};
/* Chip identification patterns */
#define ADM_SIG0_MASK 0xffff
#define ADM_SIG0_VAL 0x1023
#define ADM_SIG1_MASK 0xffff
#define ADM_SIG1_VAL 0x0007
enum {
ADM_PHYCFG_COLTST = (1 << 7), /* Enable collision test */
ADM_PHYCFG_DPLX = (1 << 8), /* Enable full duplex */
ADM_PHYCFG_ANEN_RST = (1 << 9), /* Restart auto negotiation (self clear) */
ADM_PHYCFG_ISO = (1 << 10), /* Isolate PHY */
ADM_PHYCFG_PDN = (1 << 11), /* Power down PHY */
ADM_PHYCFG_ANEN = (1 << 12), /* Enable auto negotiation */
ADM_PHYCFG_SPEED_100 = (1 << 13), /* Enable 100 Mbit/s */
ADM_PHYCFG_LPBK = (1 << 14), /* Enable loopback operation */
ADM_PHYCFG_RST = (1 << 15), /* Reset the port (self clear) */
ADM_PHYCFG_INIT = (
ADM_PHYCFG_RST |
ADM_PHYCFG_SPEED_100 |
ADM_PHYCFG_ANEN |
ADM_PHYCFG_ANEN_RST
)
};
enum {
ADM_PORTCFG_FC = (1 << 0), /* Enable 802.x flow control */
ADM_PORTCFG_AN = (1 << 1), /* Enable auto-negotiation */
ADM_PORTCFG_SPEED_100 = (1 << 2), /* Enable 100 Mbit/s */
ADM_PORTCFG_DPLX = (1 << 3), /* Enable full duplex */
ADM_PORTCFG_OT = (1 << 4), /* Output tagged packets */
ADM_PORTCFG_PD = (1 << 5), /* Port disable */
ADM_PORTCFG_TV_PRIO = (1 << 6), /* 0 = VLAN based priority
* 1 = TOS based priority */
ADM_PORTCFG_PPE = (1 << 7), /* Port based priority enable */
ADM_PORTCFG_PP_S = (1 << 8), /* Port based priority, 2 bits */
ADM_PORTCFG_PVID_BASE = (1 << 10), /* Primary VLAN id, 4 bits */
ADM_PORTCFG_FSE = (1 << 14), /* Fx select enable */
ADM_PORTCFG_CAM = (1 << 15), /* Crossover Auto MDIX */
ADM_PORTCFG_INIT = (
ADM_PORTCFG_FC |
ADM_PORTCFG_AN |
ADM_PORTCFG_SPEED_100 |
ADM_PORTCFG_DPLX |
ADM_PORTCFG_CAM
),
ADM_PORTCFG_CPU = (
ADM_PORTCFG_FC |
ADM_PORTCFG_SPEED_100 |
ADM_PORTCFG_OT |
ADM_PORTCFG_DPLX
),
};
#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8)
#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
#define ADM_PORTCFG_PVID_MASK (0xf << 10)
#define ADM_IFNTE_MASK (0x3f << 9)
#define ADM_VID_CHECK_MASK (0x3f << 6)
#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8)
#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
#define ADM_P2_PVID_MASK 0xff
#define ADM_OTBE(n) (((n) & 0x3f) << 8)
#define ADM_OTBE_MASK (0x3f << 8)
/* ADM_SYSC0 */
enum {
ADM_NTTE = (1 << 2), /* New Tag Transmit Enable */
ADM_RVID1 = (1 << 8) /* Replace VLAN ID 1 */
};
/* Tag Based VLAN in ADM_SYSC3 */
#define ADM_MAC_CLONE BIT(4)
#define ADM_TBV BIT(5)
static const u8 adm_portcfg[] = {
[0] = ADM_P0_CFG,
[1] = ADM_P1_CFG,
[2] = ADM_P2_CFG,
[3] = ADM_P3_CFG,
[4] = ADM_P4_CFG,
[5] = ADM_P5_CFG,
};
/* Fields in ADM_VLAN_FILT_L(x) */
#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12)
#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6)
#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0)
#define ADM_VLAN_FILT_MEMBER_MASK 0x3f
/* Fields in ADM_VLAN_FILT_H(x) */
#define ADM_VLAN_FILT_VALID (1 << 15)
#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0)
/* Convert ports to a form for ADM6996L VLAN map */
#define ADM_VLAN_FILT(ports) ((ports & 0x01) | ((ports & 0x02) << 1) | \
((ports & 0x04) << 2) | ((ports & 0x08) << 3) | \
((ports & 0x10) << 3) | ((ports & 0x20) << 3))
/* Port status register */
enum {
ADM_PS_LS = (1 << 0), /* Link status */
ADM_PS_SS = (1 << 1), /* Speed status */
ADM_PS_DS = (1 << 2), /* Duplex status */
ADM_PS_FCS = (1 << 3) /* Flow control status */
};
/*
* Split the register address in phy id and register
* it will get combined again by the mdio bus op
*/
#define PHYADDR(_reg) ((_reg >> 5) & 0xff), (_reg & 0x1f)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,725 @@
/*
* ar8216.h: AR8216 switch driver
*
* Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __AR8216_H
#define __AR8216_H
#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s)
#define AR8XXX_CAP_GIGE BIT(0)
#define AR8XXX_CAP_MIB_COUNTERS BIT(1)
#define AR8XXX_NUM_PHYS 5
#define AR8216_PORT_CPU 0
#define AR8216_NUM_PORTS 6
#define AR8216_NUM_VLANS 16
#define AR7240SW_NUM_PORTS 5
#define AR8316_NUM_VLANS 4096
/* size of the vlan table */
#define AR8X16_MAX_VLANS 128
#define AR83X7_MAX_VLANS 4096
#define AR8XXX_MAX_VLANS AR83X7_MAX_VLANS
#define AR8X16_PROBE_RETRIES 10
#define AR8X16_MAX_PORTS 8
#define AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS 7
#define AR8XXX_DEFAULT_ARL_AGE_TIME 300
/* Atheros specific MII registers */
#define MII_ATH_MMD_ADDR 0x0d
#define MII_ATH_MMD_DATA 0x0e
#define MII_ATH_DBG_ADDR 0x1d
#define MII_ATH_DBG_DATA 0x1e
#define AR8216_REG_CTRL 0x0000
#define AR8216_CTRL_REVISION BITS(0, 8)
#define AR8216_CTRL_REVISION_S 0
#define AR8216_CTRL_VERSION BITS(8, 8)
#define AR8216_CTRL_VERSION_S 8
#define AR8216_CTRL_RESET BIT(31)
#define AR8216_REG_FLOOD_MASK 0x002C
#define AR8216_FM_UNI_DEST_PORTS BITS(0, 6)
#define AR8216_FM_MULTI_DEST_PORTS BITS(16, 6)
#define AR8216_FM_CPU_BROADCAST_EN BIT(26)
#define AR8229_FLOOD_MASK_UC_DP(_p) BIT(_p)
#define AR8229_FLOOD_MASK_MC_DP(_p) BIT(16 + (_p))
#define AR8229_FLOOD_MASK_BC_DP(_p) BIT(25 + (_p))
#define AR8216_REG_GLOBAL_CTRL 0x0030
#define AR8216_GCTRL_MTU BITS(0, 11)
#define AR8236_GCTRL_MTU BITS(0, 14)
#define AR8316_GCTRL_MTU BITS(0, 14)
#define AR8216_REG_VTU 0x0040
#define AR8216_VTU_OP BITS(0, 3)
#define AR8216_VTU_OP_NOOP 0x0
#define AR8216_VTU_OP_FLUSH 0x1
#define AR8216_VTU_OP_LOAD 0x2
#define AR8216_VTU_OP_PURGE 0x3
#define AR8216_VTU_OP_REMOVE_PORT 0x4
#define AR8216_VTU_ACTIVE BIT(3)
#define AR8216_VTU_FULL BIT(4)
#define AR8216_VTU_PORT BITS(8, 4)
#define AR8216_VTU_PORT_S 8
#define AR8216_VTU_VID BITS(16, 12)
#define AR8216_VTU_VID_S 16
#define AR8216_VTU_PRIO BITS(28, 3)
#define AR8216_VTU_PRIO_S 28
#define AR8216_VTU_PRIO_EN BIT(31)
#define AR8216_REG_VTU_DATA 0x0044
#define AR8216_VTUDATA_MEMBER BITS(0, 10)
#define AR8236_VTUDATA_MEMBER BITS(0, 7)
#define AR8216_VTUDATA_VALID BIT(11)
#define AR8216_REG_ATU_FUNC0 0x0050
#define AR8216_ATU_OP BITS(0, 3)
#define AR8216_ATU_OP_NOOP 0x0
#define AR8216_ATU_OP_FLUSH 0x1
#define AR8216_ATU_OP_LOAD 0x2
#define AR8216_ATU_OP_PURGE 0x3
#define AR8216_ATU_OP_FLUSH_UNLOCKED 0x4
#define AR8216_ATU_OP_FLUSH_PORT 0x5
#define AR8216_ATU_OP_GET_NEXT 0x6
#define AR8216_ATU_ACTIVE BIT(3)
#define AR8216_ATU_PORT_NUM BITS(8, 4)
#define AR8216_ATU_PORT_NUM_S 8
#define AR8216_ATU_FULL_VIO BIT(12)
#define AR8216_ATU_ADDR5 BITS(16, 8)
#define AR8216_ATU_ADDR5_S 16
#define AR8216_ATU_ADDR4 BITS(24, 8)
#define AR8216_ATU_ADDR4_S 24
#define AR8216_REG_ATU_FUNC1 0x0054
#define AR8216_ATU_ADDR3 BITS(0, 8)
#define AR8216_ATU_ADDR3_S 0
#define AR8216_ATU_ADDR2 BITS(8, 8)
#define AR8216_ATU_ADDR2_S 8
#define AR8216_ATU_ADDR1 BITS(16, 8)
#define AR8216_ATU_ADDR1_S 16
#define AR8216_ATU_ADDR0 BITS(24, 8)
#define AR8216_ATU_ADDR0_S 24
#define AR8216_REG_ATU_FUNC2 0x0058
#define AR8216_ATU_PORTS BITS(0, 6)
#define AR8216_ATU_PORTS_S 0
#define AR8216_ATU_PORT0 BIT(0)
#define AR8216_ATU_PORT1 BIT(1)
#define AR8216_ATU_PORT2 BIT(2)
#define AR8216_ATU_PORT3 BIT(3)
#define AR8216_ATU_PORT4 BIT(4)
#define AR8216_ATU_PORT5 BIT(5)
#define AR8216_ATU_STATUS BITS(16, 4)
#define AR8216_ATU_STATUS_S 16
#define AR8216_REG_ATU_CTRL 0x005C
#define AR8216_ATU_CTRL_AGE_EN BIT(17)
#define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16)
#define AR8216_ATU_CTRL_AGE_TIME_S 0
#define AR8236_ATU_CTRL_RES BIT(20)
#define AR8216_ATU_CTRL_LEARN_CHANGE BIT(18)
#define AR8216_ATU_CTRL_RESERVED BIT(19)
#define AR8216_ATU_CTRL_ARP_EN BIT(20)
#define AR8216_REG_TAG_PRIORITY 0x0070
#define AR8216_REG_SERVICE_TAG 0x0074
#define AR8216_SERVICE_TAG_M BITS(0, 16)
#define AR8216_REG_MIB_FUNC 0x0080
#define AR8216_MIB_TIMER BITS(0, 16)
#define AR8216_MIB_AT_HALF_EN BIT(16)
#define AR8216_MIB_BUSY BIT(17)
#define AR8216_MIB_FUNC BITS(24, 3)
#define AR8216_MIB_FUNC_S 24
#define AR8216_MIB_FUNC_NO_OP 0x0
#define AR8216_MIB_FUNC_FLUSH 0x1
#define AR8216_MIB_FUNC_CAPTURE 0x3
#define AR8236_MIB_EN BIT(30)
#define AR8216_REG_GLOBAL_CPUPORT 0x0078
#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT BITS(4, 4)
#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S 4
#define AR8216_GLOBAL_CPUPORT_EN BIT(8)
#define AR8216_REG_MDIO_CTRL 0x98
#define AR8216_MDIO_CTRL_DATA_M BITS(0, 16)
#define AR8216_MDIO_CTRL_REG_ADDR_S 16
#define AR8216_MDIO_CTRL_PHY_ADDR_S 21
#define AR8216_MDIO_CTRL_CMD_WRITE 0
#define AR8216_MDIO_CTRL_CMD_READ BIT(27)
#define AR8216_MDIO_CTRL_MASTER_EN BIT(30)
#define AR8216_MDIO_CTRL_BUSY BIT(31)
#define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1))
#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000)
#define AR8216_PORT_STATUS_SPEED BITS(0,2)
#define AR8216_PORT_STATUS_SPEED_S 0
#define AR8216_PORT_STATUS_TXMAC BIT(2)
#define AR8216_PORT_STATUS_RXMAC BIT(3)
#define AR8216_PORT_STATUS_TXFLOW BIT(4)
#define AR8216_PORT_STATUS_RXFLOW BIT(5)
#define AR8216_PORT_STATUS_DUPLEX BIT(6)
#define AR8216_PORT_STATUS_LINK_UP BIT(8)
#define AR8216_PORT_STATUS_LINK_AUTO BIT(9)
#define AR8216_PORT_STATUS_LINK_PAUSE BIT(10)
#define AR8216_PORT_STATUS_FLOW_CONTROL BIT(12)
#define AR8216_REG_PORT_CTRL(_i) (AR8216_PORT_OFFSET(_i) + 0x0004)
/* port forwarding state */
#define AR8216_PORT_CTRL_STATE BITS(0, 3)
#define AR8216_PORT_CTRL_STATE_S 0
#define AR8216_PORT_CTRL_LEARN_LOCK BIT(7)
/* egress 802.1q mode */
#define AR8216_PORT_CTRL_VLAN_MODE BITS(8, 2)
#define AR8216_PORT_CTRL_VLAN_MODE_S 8
#define AR8216_PORT_CTRL_IGMP_SNOOP BIT(10)
#define AR8216_PORT_CTRL_HEADER BIT(11)
#define AR8216_PORT_CTRL_MAC_LOOP BIT(12)
#define AR8216_PORT_CTRL_SINGLE_VLAN BIT(13)
#define AR8216_PORT_CTRL_LEARN BIT(14)
#define AR8216_PORT_CTRL_MIRROR_TX BIT(16)
#define AR8216_PORT_CTRL_MIRROR_RX BIT(17)
#define AR8216_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET(_i) + 0x0008)
#define AR8216_PORT_VLAN_DEFAULT_ID BITS(0, 12)
#define AR8216_PORT_VLAN_DEFAULT_ID_S 0
#define AR8216_PORT_VLAN_DEST_PORTS BITS(16, 9)
#define AR8216_PORT_VLAN_DEST_PORTS_S 16
/* bit0 added to the priority field of egress frames */
#define AR8216_PORT_VLAN_TX_PRIO BIT(27)
/* port default priority */
#define AR8216_PORT_VLAN_PRIORITY BITS(28, 2)
#define AR8216_PORT_VLAN_PRIORITY_S 28
/* ingress 802.1q mode */
#define AR8216_PORT_VLAN_MODE BITS(30, 2)
#define AR8216_PORT_VLAN_MODE_S 30
#define AR8216_REG_PORT_RATE(_i) (AR8216_PORT_OFFSET(_i) + 0x000c)
#define AR8216_REG_PORT_PRIO(_i) (AR8216_PORT_OFFSET(_i) + 0x0010)
#define AR8216_STATS_RXBROAD 0x00
#define AR8216_STATS_RXPAUSE 0x04
#define AR8216_STATS_RXMULTI 0x08
#define AR8216_STATS_RXFCSERR 0x0c
#define AR8216_STATS_RXALIGNERR 0x10
#define AR8216_STATS_RXRUNT 0x14
#define AR8216_STATS_RXFRAGMENT 0x18
#define AR8216_STATS_RX64BYTE 0x1c
#define AR8216_STATS_RX128BYTE 0x20
#define AR8216_STATS_RX256BYTE 0x24
#define AR8216_STATS_RX512BYTE 0x28
#define AR8216_STATS_RX1024BYTE 0x2c
#define AR8216_STATS_RXMAXBYTE 0x30
#define AR8216_STATS_RXTOOLONG 0x34
#define AR8216_STATS_RXGOODBYTE 0x38
#define AR8216_STATS_RXBADBYTE 0x40
#define AR8216_STATS_RXOVERFLOW 0x48
#define AR8216_STATS_FILTERED 0x4c
#define AR8216_STATS_TXBROAD 0x50
#define AR8216_STATS_TXPAUSE 0x54
#define AR8216_STATS_TXMULTI 0x58
#define AR8216_STATS_TXUNDERRUN 0x5c
#define AR8216_STATS_TX64BYTE 0x60
#define AR8216_STATS_TX128BYTE 0x64
#define AR8216_STATS_TX256BYTE 0x68
#define AR8216_STATS_TX512BYTE 0x6c
#define AR8216_STATS_TX1024BYTE 0x70
#define AR8216_STATS_TXMAXBYTE 0x74
#define AR8216_STATS_TXOVERSIZE 0x78
#define AR8216_STATS_TXBYTE 0x7c
#define AR8216_STATS_TXCOLLISION 0x84
#define AR8216_STATS_TXABORTCOL 0x88
#define AR8216_STATS_TXMULTICOL 0x8c
#define AR8216_STATS_TXSINGLECOL 0x90
#define AR8216_STATS_TXEXCDEFER 0x94
#define AR8216_STATS_TXDEFER 0x98
#define AR8216_STATS_TXLATECOL 0x9c
#define AR8216_MIB_RXB_ID 14 /* RxGoodByte */
#define AR8216_MIB_TXB_ID 29 /* TxByte */
#define AR8229_REG_OPER_MODE0 0x04
#define AR8229_OPER_MODE0_MAC_GMII_EN BIT(6)
#define AR8229_OPER_MODE0_PHY_MII_EN BIT(10)
#define AR8229_REG_OPER_MODE1 0x08
#define AR8229_REG_OPER_MODE1_PHY4_MII_EN BIT(28)
#define AR8229_REG_QM_CTRL 0x3c
#define AR8229_QM_CTRL_ARP_EN BIT(15)
#define AR8236_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET((_i)) + 0x0008)
#define AR8236_PORT_VLAN_DEFAULT_ID BITS(16, 12)
#define AR8236_PORT_VLAN_DEFAULT_ID_S 16
#define AR8236_PORT_VLAN_PRIORITY BITS(29, 3)
#define AR8236_PORT_VLAN_PRIORITY_S 28
#define AR8236_REG_PORT_VLAN2(_i) (AR8216_PORT_OFFSET((_i)) + 0x000c)
#define AR8236_PORT_VLAN2_MEMBER BITS(16, 7)
#define AR8236_PORT_VLAN2_MEMBER_S 16
#define AR8236_PORT_VLAN2_TX_PRIO BIT(23)
#define AR8236_PORT_VLAN2_VLAN_MODE BITS(30, 2)
#define AR8236_PORT_VLAN2_VLAN_MODE_S 30
#define AR8236_STATS_RXBROAD 0x00
#define AR8236_STATS_RXPAUSE 0x04
#define AR8236_STATS_RXMULTI 0x08
#define AR8236_STATS_RXFCSERR 0x0c
#define AR8236_STATS_RXALIGNERR 0x10
#define AR8236_STATS_RXRUNT 0x14
#define AR8236_STATS_RXFRAGMENT 0x18
#define AR8236_STATS_RX64BYTE 0x1c
#define AR8236_STATS_RX128BYTE 0x20
#define AR8236_STATS_RX256BYTE 0x24
#define AR8236_STATS_RX512BYTE 0x28
#define AR8236_STATS_RX1024BYTE 0x2c
#define AR8236_STATS_RX1518BYTE 0x30
#define AR8236_STATS_RXMAXBYTE 0x34
#define AR8236_STATS_RXTOOLONG 0x38
#define AR8236_STATS_RXGOODBYTE 0x3c
#define AR8236_STATS_RXBADBYTE 0x44
#define AR8236_STATS_RXOVERFLOW 0x4c
#define AR8236_STATS_FILTERED 0x50
#define AR8236_STATS_TXBROAD 0x54
#define AR8236_STATS_TXPAUSE 0x58
#define AR8236_STATS_TXMULTI 0x5c
#define AR8236_STATS_TXUNDERRUN 0x60
#define AR8236_STATS_TX64BYTE 0x64
#define AR8236_STATS_TX128BYTE 0x68
#define AR8236_STATS_TX256BYTE 0x6c
#define AR8236_STATS_TX512BYTE 0x70
#define AR8236_STATS_TX1024BYTE 0x74
#define AR8236_STATS_TX1518BYTE 0x78
#define AR8236_STATS_TXMAXBYTE 0x7c
#define AR8236_STATS_TXOVERSIZE 0x80
#define AR8236_STATS_TXBYTE 0x84
#define AR8236_STATS_TXCOLLISION 0x8c
#define AR8236_STATS_TXABORTCOL 0x90
#define AR8236_STATS_TXMULTICOL 0x94
#define AR8236_STATS_TXSINGLECOL 0x98
#define AR8236_STATS_TXEXCDEFER 0x9c
#define AR8236_STATS_TXDEFER 0xa0
#define AR8236_STATS_TXLATECOL 0xa4
#define AR8236_MIB_RXB_ID 15 /* RxGoodByte */
#define AR8236_MIB_TXB_ID 31 /* TxByte */
#define AR8316_REG_POSTRIP 0x0008
#define AR8316_POSTRIP_MAC0_GMII_EN BIT(0)
#define AR8316_POSTRIP_MAC0_RGMII_EN BIT(1)
#define AR8316_POSTRIP_PHY4_GMII_EN BIT(2)
#define AR8316_POSTRIP_PHY4_RGMII_EN BIT(3)
#define AR8316_POSTRIP_MAC0_MAC_MODE BIT(4)
#define AR8316_POSTRIP_RTL_MODE BIT(5)
#define AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN BIT(6)
#define AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN BIT(7)
#define AR8316_POSTRIP_SERDES_EN BIT(8)
#define AR8316_POSTRIP_SEL_ANA_RST BIT(9)
#define AR8316_POSTRIP_GATE_25M_EN BIT(10)
#define AR8316_POSTRIP_SEL_CLK25M BIT(11)
#define AR8316_POSTRIP_HIB_PULSE_HW BIT(12)
#define AR8316_POSTRIP_DBG_MODE_I BIT(13)
#define AR8316_POSTRIP_MAC5_MAC_MODE BIT(14)
#define AR8316_POSTRIP_MAC5_PHY_MODE BIT(15)
#define AR8316_POSTRIP_POWER_DOWN_HW BIT(16)
#define AR8316_POSTRIP_LPW_STATE_EN BIT(17)
#define AR8316_POSTRIP_MAN_EN BIT(18)
#define AR8316_POSTRIP_PHY_PLL_ON BIT(19)
#define AR8316_POSTRIP_LPW_EXIT BIT(20)
#define AR8316_POSTRIP_TXDELAY_S0 BIT(21)
#define AR8316_POSTRIP_TXDELAY_S1 BIT(22)
#define AR8316_POSTRIP_RXDELAY_S0 BIT(23)
#define AR8316_POSTRIP_LED_OPEN_EN BIT(24)
#define AR8316_POSTRIP_SPI_EN BIT(25)
#define AR8316_POSTRIP_RXDELAY_S1 BIT(26)
#define AR8316_POSTRIP_POWER_ON_SEL BIT(31)
/* port speed */
enum {
AR8216_PORT_SPEED_10M = 0,
AR8216_PORT_SPEED_100M = 1,
AR8216_PORT_SPEED_1000M = 2,
AR8216_PORT_SPEED_ERR = 3,
};
/* ingress 802.1q mode */
enum {
AR8216_IN_PORT_ONLY = 0,
AR8216_IN_PORT_FALLBACK = 1,
AR8216_IN_VLAN_ONLY = 2,
AR8216_IN_SECURE = 3
};
/* egress 802.1q mode */
enum {
AR8216_OUT_KEEP = 0,
AR8216_OUT_STRIP_VLAN = 1,
AR8216_OUT_ADD_VLAN = 2
};
/* port forwarding state */
enum {
AR8216_PORT_STATE_DISABLED = 0,
AR8216_PORT_STATE_BLOCK = 1,
AR8216_PORT_STATE_LISTEN = 2,
AR8216_PORT_STATE_LEARN = 3,
AR8216_PORT_STATE_FORWARD = 4
};
/* mib counter type */
enum {
AR8XXX_MIB_BASIC = 0,
AR8XXX_MIB_EXTENDED = 1
};
enum {
AR8XXX_VER_AR8216 = 0x01,
AR8XXX_VER_AR8236 = 0x03,
AR8XXX_VER_AR8316 = 0x10,
AR8XXX_VER_AR8327 = 0x12,
AR8XXX_VER_AR8337 = 0x13,
};
#define AR8XXX_NUM_ARL_RECORDS 100
enum arl_op {
AR8XXX_ARL_INITIALIZE,
AR8XXX_ARL_GET_NEXT
};
struct arl_entry {
u16 portmap;
u8 mac[6];
};
struct ar8xxx_priv;
struct ar8xxx_mib_desc {
unsigned int size;
unsigned int offset;
const char *name;
u8 type;
};
struct ar8xxx_chip {
unsigned long caps;
bool config_at_probe;
bool mii_lo_first;
/* parameters to calculate REG_PORT_STATS_BASE */
unsigned reg_port_stats_start;
unsigned reg_port_stats_length;
unsigned reg_arl_ctrl;
int (*hw_init)(struct ar8xxx_priv *priv);
void (*cleanup)(struct ar8xxx_priv *priv);
const char *name;
int vlans;
int ports;
const struct switch_dev_ops *swops;
void (*init_globals)(struct ar8xxx_priv *priv);
void (*init_port)(struct ar8xxx_priv *priv, int port);
void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
u32 (*read_port_eee_status)(struct ar8xxx_priv *priv, int port);
int (*atu_flush)(struct ar8xxx_priv *priv);
int (*atu_flush_port)(struct ar8xxx_priv *priv, int port);
void (*vtu_flush)(struct ar8xxx_priv *priv);
void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
void (*set_mirror_regs)(struct ar8xxx_priv *priv);
void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
u32 *status, enum arl_op op);
int (*sw_hw_apply)(struct switch_dev *dev);
void (*phy_rgmii_set)(struct ar8xxx_priv *priv, struct phy_device *phydev);
int (*phy_read)(struct ar8xxx_priv *priv, int addr, int regnum);
int (*phy_write)(struct ar8xxx_priv *priv, int addr, int regnum, u16 val);
const struct ar8xxx_mib_desc *mib_decs;
unsigned num_mibs;
unsigned mib_func;
int mib_rxb_id;
int mib_txb_id;
};
struct ar8xxx_priv {
struct switch_dev dev;
struct mii_bus *mii_bus;
struct mii_bus *sw_mii_bus;
struct phy_device *phy;
struct device *pdev;
int (*get_port_link)(unsigned port);
const struct net_device_ops *ndo_old;
struct net_device_ops ndo;
struct mutex reg_mutex;
u8 chip_ver;
u8 chip_rev;
const struct ar8xxx_chip *chip;
void *chip_data;
bool initialized;
bool port4_phy;
char buf[2048];
struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
bool link_up[AR8X16_MAX_PORTS];
bool init;
struct mutex mib_lock;
struct delayed_work mib_work;
u64 *mib_stats;
u32 mib_poll_interval;
u8 mib_type;
struct list_head list;
unsigned int use_count;
/* all fields below are cleared on reset */
struct_group(ar8xxx_priv_volatile,
bool vlan;
u16 vlan_id[AR8XXX_MAX_VLANS];
u8 vlan_table[AR8XXX_MAX_VLANS];
u8 vlan_tagged;
u16 pvid[AR8X16_MAX_PORTS];
int arl_age_time;
/* mirroring */
bool mirror_rx;
bool mirror_tx;
int source_port;
int monitor_port;
u8 port_vlan_prio[AR8X16_MAX_PORTS];
);
};
u32
ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
void
ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
u32
ar8xxx_read(struct ar8xxx_priv *priv, int reg);
void
ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val);
u32
ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
void
ar8xxx_phy_dbg_read(struct ar8xxx_priv *priv, int phy_addr,
u16 dbg_addr, u16 *dbg_data);
void
ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
u16 dbg_addr, u16 dbg_data);
void
ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data);
u16
ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg);
void
ar8xxx_phy_init(struct ar8xxx_priv *priv);
int
ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_mib_poll_interval(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_mib_poll_interval(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_mib_type(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_mib_type(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan);
int
ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan);
int
ar8xxx_sw_hw_apply(struct switch_dev *dev);
int
ar8xxx_sw_reset_switch(struct switch_dev *dev);
int
ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
struct switch_port_link *link);
int
ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_port_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_arl_age_time(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_arl_age_time(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_arl_table(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_port_stats(struct switch_dev *dev, int port,
struct switch_port_stats *stats);
int
ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
static inline struct ar8xxx_priv *
swdev_to_ar8xxx(struct switch_dev *swdev)
{
return container_of(swdev, struct ar8xxx_priv, dev);
}
static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv)
{
return priv->chip->caps & AR8XXX_CAP_GIGE;
}
static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv)
{
return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS;
}
static inline bool chip_is_ar8216(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8216;
}
static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8236;
}
static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8316;
}
static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8327;
}
static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8337;
}
static inline void
ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val)
{
ar8xxx_rmw(priv, reg, 0, val);
}
static inline void
ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
{
ar8xxx_rmw(priv, reg, val, 0);
}
static inline void
split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
{
regaddr >>= 1;
*r1 = regaddr & 0x1e;
regaddr >>= 5;
*r2 = regaddr & 0x7;
regaddr >>= 3;
*page = regaddr & 0x1ff;
}
static inline void
wait_for_page_switch(void)
{
udelay(5);
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,333 @@
/*
* ar8327.h: AR8216 switch driver
*
* Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __AR8327_H
#define __AR8327_H
#define AR8327_NUM_PORTS 7
#define AR8327_NUM_LEDS 15
#define AR8327_PORTS_ALL 0x7f
#define AR8327_NUM_LED_CTRL_REGS 4
#define AR8327_REG_MASK 0x000
#define AR8327_REG_PAD0_MODE 0x004
#define AR8327_REG_PAD5_MODE 0x008
#define AR8327_REG_PAD6_MODE 0x00c
#define AR8327_PAD_MAC_MII_RXCLK_SEL BIT(0)
#define AR8327_PAD_MAC_MII_TXCLK_SEL BIT(1)
#define AR8327_PAD_MAC_MII_EN BIT(2)
#define AR8327_PAD_MAC_GMII_RXCLK_SEL BIT(4)
#define AR8327_PAD_MAC_GMII_TXCLK_SEL BIT(5)
#define AR8327_PAD_MAC_GMII_EN BIT(6)
#define AR8327_PAD_SGMII_EN BIT(7)
#define AR8327_PAD_PHY_MII_RXCLK_SEL BIT(8)
#define AR8327_PAD_PHY_MII_TXCLK_SEL BIT(9)
#define AR8327_PAD_PHY_MII_EN BIT(10)
#define AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL BIT(11)
#define AR8327_PAD_PHY_GMII_RXCLK_SEL BIT(12)
#define AR8327_PAD_PHY_GMII_TXCLK_SEL BIT(13)
#define AR8327_PAD_PHY_GMII_EN BIT(14)
#define AR8327_PAD_PHYX_GMII_EN BIT(16)
#define AR8327_PAD_PHYX_RGMII_EN BIT(17)
#define AR8327_PAD_PHYX_MII_EN BIT(18)
#define AR8327_PAD_SGMII_DELAY_EN BIT(19)
#define AR8327_PAD_RGMII_RXCLK_DELAY_SEL BITS(20, 2)
#define AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S 20
#define AR8327_PAD_RGMII_TXCLK_DELAY_SEL BITS(22, 2)
#define AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S 22
#define AR8327_PAD_RGMII_RXCLK_DELAY_EN BIT(24)
#define AR8327_PAD_RGMII_TXCLK_DELAY_EN BIT(25)
#define AR8327_PAD_RGMII_EN BIT(26)
#define AR8327_REG_POWER_ON_STRAP 0x010
#define AR8327_POWER_ON_STRAP_POWER_ON_SEL BIT(31)
#define AR8327_POWER_ON_STRAP_LED_OPEN_EN BIT(24)
#define AR8327_POWER_ON_STRAP_SERDES_AEN BIT(7)
#define AR8327_REG_INT_STATUS0 0x020
#define AR8327_INT0_VT_DONE BIT(20)
#define AR8327_REG_INT_STATUS1 0x024
#define AR8327_REG_INT_MASK0 0x028
#define AR8327_REG_INT_MASK1 0x02c
#define AR8327_REG_MODULE_EN 0x030
#define AR8327_MODULE_EN_MIB BIT(0)
#define AR8327_REG_MIB_FUNC 0x034
#define AR8327_MIB_CPU_KEEP BIT(20)
#define AR8327_REG_SERVICE_TAG 0x048
#define AR8327_REG_LED_CTRL(_i) (0x050 + (_i) * 4)
#define AR8327_REG_LED_CTRL0 0x050
#define AR8327_REG_LED_CTRL1 0x054
#define AR8327_REG_LED_CTRL2 0x058
#define AR8327_REG_LED_CTRL3 0x05c
#define AR8327_REG_MAC_ADDR0 0x060
#define AR8327_REG_MAC_ADDR1 0x064
#define AR8327_REG_MAX_FRAME_SIZE 0x078
#define AR8327_MAX_FRAME_SIZE_MTU BITS(0, 14)
#define AR8327_REG_PORT_STATUS(_i) (0x07c + (_i) * 4)
#define AR8327_PORT_STATUS_TXFLOW_AUTO BIT(10)
#define AR8327_PORT_STATUS_RXFLOW_AUTO BIT(11)
#define AR8327_REG_HEADER_CTRL 0x098
#define AR8327_REG_PORT_HEADER(_i) (0x09c + (_i) * 4)
#define AR8327_REG_SGMII_CTRL 0x0e0
#define AR8327_SGMII_CTRL_EN_PLL BIT(1)
#define AR8327_SGMII_CTRL_EN_RX BIT(2)
#define AR8327_SGMII_CTRL_EN_TX BIT(3)
#define AR8327_REG_EEE_CTRL 0x100
#define AR8327_EEE_CTRL_DISABLE_PHY(_i) BIT(4 + (_i) * 2)
#define AR8327_REG_FRAME_ACK_CTRL0 0x210
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0 BIT(0)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0 BIT(1)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0 BIT(2)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN0 BIT(3)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN0 BIT(4)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0 BIT(5)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0 BIT(6)
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1 BIT(8)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1 BIT(9)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1 BIT(10)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN1 BIT(11)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN1 BIT(12)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1 BIT(13)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1 BIT(14)
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2 BIT(16)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2 BIT(17)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2 BIT(18)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN2 BIT(19)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN2 BIT(20)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2 BIT(21)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2 BIT(22)
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3 BIT(24)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3 BIT(25)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3 BIT(26)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN3 BIT(27)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN3 BIT(28)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3 BIT(29)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3 BIT(30)
#define AR8327_REG_FRAME_ACK_CTRL1 0x214
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4 BIT(0)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4 BIT(1)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4 BIT(2)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN4 BIT(3)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN4 BIT(4)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4 BIT(5)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4 BIT(6)
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5 BIT(8)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5 BIT(9)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5 BIT(10)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN5 BIT(11)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN5 BIT(12)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5 BIT(13)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5 BIT(14)
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6 BIT(16)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6 BIT(17)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6 BIT(18)
#define AR8327_FRAME_ACK_CTRL_EAPOL_EN6 BIT(19)
#define AR8327_FRAME_ACK_CTRL_DHCP_EN6 BIT(20)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6 BIT(21)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6 BIT(22)
#define AR8327_FRAME_ACK_CTRL_IGMP_V3_EN BIT(24)
#define AR8327_FRAME_ACK_CTRL_PPPOE_EN BIT(25)
#define AR8327_REG_FRAME_ACK_CTRL(_i) (0x210 + ((_i) / 4) * 0x4)
#define AR8327_FRAME_ACK_CTRL_IGMP_MLD BIT(0)
#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN BIT(1)
#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE BIT(2)
#define AR8327_FRAME_ACK_CTRL_EAPOL BIT(3)
#define AR8327_FRAME_ACK_CTRL_DHCP BIT(4)
#define AR8327_FRAME_ACK_CTRL_ARP_ACK BIT(5)
#define AR8327_FRAME_ACK_CTRL_ARP_REQ BIT(6)
#define AR8327_FRAME_ACK_CTRL_S(_i) (((_i) % 4) * 8)
#define AR8327_REG_PORT_VLAN0(_i) (0x420 + (_i) * 0x8)
#define AR8327_PORT_VLAN0_DEF_PRI_MASK BITS(0, 3)
#define AR8327_PORT_VLAN0_DEF_SVID BITS(0, 12)
#define AR8327_PORT_VLAN0_DEF_SVID_S 0
#define AR8327_PORT_VLAN0_DEF_SPRI BITS(13, 3)
#define AR8327_PORT_VLAN0_DEF_SPRI_S 13
#define AR8327_PORT_VLAN0_DEF_CVID BITS(16, 12)
#define AR8327_PORT_VLAN0_DEF_CVID_S 16
#define AR8327_PORT_VLAN0_DEF_CPRI BITS(29, 3)
#define AR8327_PORT_VLAN0_DEF_CPRI_S 29
#define AR8327_REG_PORT_VLAN1(_i) (0x424 + (_i) * 0x8)
#define AR8327_PORT_VLAN1_VLAN_PRI_PROP BIT(4)
#define AR8327_PORT_VLAN1_PORT_VLAN_PROP BIT(6)
#define AR8327_PORT_VLAN1_OUT_MODE BITS(12, 2)
#define AR8327_PORT_VLAN1_OUT_MODE_S 12
#define AR8327_PORT_VLAN1_OUT_MODE_UNMOD 0
#define AR8327_PORT_VLAN1_OUT_MODE_UNTAG 1
#define AR8327_PORT_VLAN1_OUT_MODE_TAG 2
#define AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH 3
#define AR8327_REG_ATU_DATA0 0x600
#define AR8327_ATU_ADDR0 BITS(0, 8)
#define AR8327_ATU_ADDR0_S 0
#define AR8327_ATU_ADDR1 BITS(8, 8)
#define AR8327_ATU_ADDR1_S 8
#define AR8327_ATU_ADDR2 BITS(16, 8)
#define AR8327_ATU_ADDR2_S 16
#define AR8327_ATU_ADDR3 BITS(24, 8)
#define AR8327_ATU_ADDR3_S 24
#define AR8327_REG_ATU_DATA1 0x604
#define AR8327_ATU_ADDR4 BITS(0, 8)
#define AR8327_ATU_ADDR4_S 0
#define AR8327_ATU_ADDR5 BITS(8, 8)
#define AR8327_ATU_ADDR5_S 8
#define AR8327_ATU_PORTS BITS(16, 7)
#define AR8327_ATU_PORTS_S 16
#define AR8327_ATU_PORT0 BIT(16)
#define AR8327_ATU_PORT1 BIT(17)
#define AR8327_ATU_PORT2 BIT(18)
#define AR8327_ATU_PORT3 BIT(19)
#define AR8327_ATU_PORT4 BIT(20)
#define AR8327_ATU_PORT5 BIT(21)
#define AR8327_ATU_PORT6 BIT(22)
#define AR8327_REG_ATU_DATA2 0x608
#define AR8327_ATU_STATUS BITS(0, 4)
#define AR8327_REG_ATU_FUNC 0x60c
#define AR8327_ATU_FUNC_OP BITS(0, 4)
#define AR8327_ATU_FUNC_OP_NOOP 0x0
#define AR8327_ATU_FUNC_OP_FLUSH 0x1
#define AR8327_ATU_FUNC_OP_LOAD 0x2
#define AR8327_ATU_FUNC_OP_PURGE 0x3
#define AR8327_ATU_FUNC_OP_FLUSH_UNLOCKED 0x4
#define AR8327_ATU_FUNC_OP_FLUSH_PORT 0x5
#define AR8327_ATU_FUNC_OP_GET_NEXT 0x6
#define AR8327_ATU_FUNC_OP_SEARCH_MAC 0x7
#define AR8327_ATU_FUNC_OP_CHANGE_TRUNK 0x8
#define AR8327_ATU_PORT_NUM BITS(8, 4)
#define AR8327_ATU_PORT_NUM_S 8
#define AR8327_ATU_FUNC_BUSY BIT(31)
#define AR8327_REG_VTU_FUNC0 0x0610
#define AR8327_VTU_FUNC0_EG_MODE BITS(4, 14)
#define AR8327_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2)
#define AR8327_VTU_FUNC0_EG_MODE_KEEP 0
#define AR8327_VTU_FUNC0_EG_MODE_UNTAG 1
#define AR8327_VTU_FUNC0_EG_MODE_TAG 2
#define AR8327_VTU_FUNC0_EG_MODE_NOT 3
#define AR8327_VTU_FUNC0_IVL BIT(19)
#define AR8327_VTU_FUNC0_VALID BIT(20)
#define AR8327_REG_VTU_FUNC1 0x0614
#define AR8327_VTU_FUNC1_OP BITS(0, 3)
#define AR8327_VTU_FUNC1_OP_NOOP 0
#define AR8327_VTU_FUNC1_OP_FLUSH 1
#define AR8327_VTU_FUNC1_OP_LOAD 2
#define AR8327_VTU_FUNC1_OP_PURGE 3
#define AR8327_VTU_FUNC1_OP_REMOVE_PORT 4
#define AR8327_VTU_FUNC1_OP_GET_NEXT 5
#define AR8327_VTU_FUNC1_OP_GET_ONE 6
#define AR8327_VTU_FUNC1_FULL BIT(4)
#define AR8327_VTU_FUNC1_PORT BIT(8, 4)
#define AR8327_VTU_FUNC1_PORT_S 8
#define AR8327_VTU_FUNC1_VID BIT(16, 12)
#define AR8327_VTU_FUNC1_VID_S 16
#define AR8327_VTU_FUNC1_BUSY BIT(31)
#define AR8327_REG_ARL_CTRL 0x0618
#define AR8327_REG_FWD_CTRL0 0x620
#define AR8327_FWD_CTRL0_CPU_PORT_EN BIT(10)
#define AR8327_FWD_CTRL0_MIRROR_PORT BITS(4, 4)
#define AR8327_FWD_CTRL0_MIRROR_PORT_S 4
#define AR8327_REG_FWD_CTRL1 0x624
#define AR8327_FWD_CTRL1_UC_FLOOD BITS(0, 7)
#define AR8327_FWD_CTRL1_UC_FLOOD_S 0
#define AR8327_FWD_CTRL1_MC_FLOOD BITS(8, 7)
#define AR8327_FWD_CTRL1_MC_FLOOD_S 8
#define AR8327_FWD_CTRL1_BC_FLOOD BITS(16, 7)
#define AR8327_FWD_CTRL1_BC_FLOOD_S 16
#define AR8327_FWD_CTRL1_IGMP BITS(24, 7)
#define AR8327_FWD_CTRL1_IGMP_S 24
#define AR8327_REG_PORT_LOOKUP(_i) (0x660 + (_i) * 0xc)
#define AR8327_PORT_LOOKUP_MEMBER BITS(0, 7)
#define AR8327_PORT_LOOKUP_IN_MODE BITS(8, 2)
#define AR8327_PORT_LOOKUP_IN_MODE_S 8
#define AR8327_PORT_LOOKUP_STATE BITS(16, 3)
#define AR8327_PORT_LOOKUP_STATE_S 16
#define AR8327_PORT_LOOKUP_LEARN BIT(20)
#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define AR8327_REG_PORT_PRIO(_i) (0x664 + (_i) * 0xc)
#define AR8327_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
#define AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
#define AR8337_PAD_MAC06_EXCHANGE_EN BIT(31)
#define AR8327_PHY_MODE_SEL 0x12
#define AR8327_PHY_MODE_SEL_RGMII BIT(3)
#define AR8327_PHY_TEST_CTRL 0x0
#define AR8327_PHY_TEST_CTRL_RGMII_RX_DELAY BIT(15)
#define AR8327_PHY_SYS_CTRL 0x5
#define AR8327_PHY_SYS_CTRL_RGMII_TX_DELAY BIT(8)
enum ar8327_led_pattern {
AR8327_LED_PATTERN_OFF = 0,
AR8327_LED_PATTERN_BLINK,
AR8327_LED_PATTERN_ON,
AR8327_LED_PATTERN_RULE,
};
struct ar8327_led_entry {
unsigned reg;
unsigned shift;
};
struct ar8327_led {
struct led_classdev cdev;
struct ar8xxx_priv *sw_priv;
char *name;
bool active_low;
u8 led_num;
enum ar8327_led_mode mode;
struct mutex mutex;
spinlock_t lock;
struct work_struct led_work;
bool enable_hw_mode;
enum ar8327_led_pattern pattern;
};
struct ar8327_data {
u32 port0_status;
u32 port6_status;
struct ar8327_led **leds;
unsigned int num_leds;
/* all fields below are cleared on reset */
bool eee[AR8XXX_NUM_PHYS];
};
#endif

View file

@ -0,0 +1,37 @@
menuconfig SWCONFIG_B53
tristate "Broadcom bcm53xx managed switch support"
depends on SWCONFIG
help
This driver adds support for Broadcom managed switch chips. It supports
BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
integrated switches.
config SWCONFIG_B53_SPI_DRIVER
tristate "B53 SPI connected switch driver"
depends on SWCONFIG_B53 && SPI
help
Select to enable support for registering switches configured through SPI.
config SWCONFIG_B53_PHY_DRIVER
tristate "B53 MDIO connected switch driver"
depends on SWCONFIG_B53
select SWCONFIG_B53_PHY_FIXUP
help
Select to enable support for registering switches configured through MDIO.
config SWCONFIG_B53_MMAP_DRIVER
tristate "B53 MMAP connected switch driver"
depends on SWCONFIG_B53
help
Select to enable support for memory-mapped switches like the BCM63XX
integrated switches.
config SWCONFIG_B53_SRAB_DRIVER
tristate "B53 SRAB connected switch driver"
depends on SWCONFIG_B53
help
Select to enable support for memory-mapped Switch Register Access
Bridge Registers (SRAB) like it is found on the BCM53010
config SWCONFIG_B53_PHY_FIXUP
bool

View file

@ -0,0 +1,10 @@
obj-$(CONFIG_SWCONFIG_B53) += b53_common.o
obj-$(CONFIG_SWCONFIG_B53_PHY_FIXUP) += b53_phy_fixup.o
obj-$(CONFIG_SWCONFIG_B53_MMAP_DRIVER) += b53_mmap.o
obj-$(CONFIG_SWCONFIG_B53_SRAB_DRIVER) += b53_srab.o
obj-$(CONFIG_SWCONFIG_B53_PHY_DRIVER) += b53_mdio.o
obj-$(CONFIG_SWCONFIG_B53_SPI_DRIVER) += b53_spi.o
ccflags-y += -Werror

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,436 @@
/*
* B53 register access through MII registers
*
* Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/phy.h>
#include <linux/module.h>
#include "b53_priv.h"
#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
/* MII registers */
#define REG_MII_PAGE 0x10 /* MII Page register */
#define REG_MII_ADDR 0x11 /* MII Address register */
#define REG_MII_DATA0 0x18 /* MII Data register 0 */
#define REG_MII_DATA1 0x19 /* MII Data register 1 */
#define REG_MII_DATA2 0x1a /* MII Data register 2 */
#define REG_MII_DATA3 0x1b /* MII Data register 3 */
#define REG_MII_PAGE_ENABLE BIT(0)
#define REG_MII_ADDR_WRITE BIT(0)
#define REG_MII_ADDR_READ BIT(1)
static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
{
int i;
u16 v;
int ret;
struct mii_bus *bus = dev->priv;
if (dev->current_page != page) {
/* set page number */
v = (page << 8) | REG_MII_PAGE_ENABLE;
ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
if (ret)
return ret;
dev->current_page = page;
}
/* set register address */
v = (reg << 8) | op;
ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
if (ret)
return ret;
/* check if operation completed */
for (i = 0; i < 5; ++i) {
v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
break;
usleep_range(10, 100);
}
if (WARN_ON(i == 5))
return -EIO;
return 0;
}
static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
{
struct mii_bus *bus = dev->priv;
int ret;
ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
if (ret)
return ret;
*val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
return 0;
}
static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
{
struct mii_bus *bus = dev->priv;
int ret;
ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
if (ret)
return ret;
*val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
return 0;
}
static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
{
struct mii_bus *bus = dev->priv;
int ret;
ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
if (ret)
return ret;
*val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
*val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
return 0;
}
static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
struct mii_bus *bus = dev->priv;
u64 temp = 0;
int i;
int ret;
ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
if (ret)
return ret;
for (i = 2; i >= 0; i--) {
temp <<= 16;
temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
}
*val = temp;
return 0;
}
static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
struct mii_bus *bus = dev->priv;
u64 temp = 0;
int i;
int ret;
ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
if (ret)
return ret;
for (i = 3; i >= 0; i--) {
temp <<= 16;
temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
}
*val = temp;
return 0;
}
static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
{
struct mii_bus *bus = dev->priv;
int ret;
ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
if (ret)
return ret;
return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
}
static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
u16 value)
{
struct mii_bus *bus = dev->priv;
int ret;
ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
if (ret)
return ret;
return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
}
static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
u32 value)
{
struct mii_bus *bus = dev->priv;
unsigned int i;
u32 temp = value;
for (i = 0; i < 2; i++) {
int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
temp & 0xffff);
if (ret)
return ret;
temp >>= 16;
}
return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
}
static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
struct mii_bus *bus = dev->priv;
unsigned i;
u64 temp = value;
for (i = 0; i < 3; i++) {
int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
temp & 0xffff);
if (ret)
return ret;
temp >>= 16;
}
return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
}
static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
struct mii_bus *bus = dev->priv;
unsigned i;
u64 temp = value;
for (i = 0; i < 4; i++) {
int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
temp & 0xffff);
if (ret)
return ret;
temp >>= 16;
}
return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
}
static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg,
u16 *value)
{
struct mii_bus *bus = dev->priv;
*value = mdiobus_read(bus, addr, reg);
return 0;
}
static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg,
u16 value)
{
struct mii_bus *bus = dev->priv;
return mdiobus_write(bus, addr, reg, value);
}
static struct b53_io_ops b53_mdio_ops = {
.read8 = b53_mdio_read8,
.read16 = b53_mdio_read16,
.read32 = b53_mdio_read32,
.read48 = b53_mdio_read48,
.read64 = b53_mdio_read64,
.write8 = b53_mdio_write8,
.write16 = b53_mdio_write16,
.write32 = b53_mdio_write32,
.write48 = b53_mdio_write48,
.write64 = b53_mdio_write64,
.phy_read16 = b53_mdio_phy_read16,
.phy_write16 = b53_mdio_phy_write16,
};
static int b53_phy_probe(struct phy_device *phydev)
{
struct b53_device *dev;
int ret;
/* allow the generic phy driver to take over */
if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
return -ENODEV;
dev = b53_swconfig_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
if (!dev)
return -ENOMEM;
dev->current_page = 0xff;
dev->priv = phydev->mdio.bus;
dev->ops = &b53_mdio_ops;
dev->pdata = NULL;
mutex_init(&dev->reg_mutex);
ret = b53_swconfig_switch_detect(dev);
if (ret)
return ret;
linkmode_zero(phydev->supported);
if (is5325(dev) || is5365(dev))
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported);
else
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported);
linkmode_copy(phydev->advertising, phydev->supported);
ret = b53_swconfig_switch_register(dev);
if (ret) {
dev_err(dev->dev, "failed to register switch: %i\n", ret);
return ret;
}
phydev->priv = dev;
return 0;
}
static int b53_phy_config_init(struct phy_device *phydev)
{
struct b53_device *dev = phydev->priv;
/* we don't use page 0xff, so force a page set */
dev->current_page = 0xff;
/* force the ethX as alias */
dev->sw_dev.alias = phydev->attached_dev->name;
return 0;
}
static void b53_phy_remove(struct phy_device *phydev)
{
struct b53_device *priv = phydev->priv;
if (!priv)
return;
b53_switch_remove(priv);
phydev->priv = NULL;
}
static int b53_phy_config_aneg(struct phy_device *phydev)
{
return 0;
}
static int b53_phy_read_status(struct phy_device *phydev)
{
struct b53_device *priv = phydev->priv;
if (is5325(priv) || is5365(priv))
phydev->speed = 100;
else
phydev->speed = 1000;
phydev->duplex = DUPLEX_FULL;
phydev->link = 1;
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
return 0;
}
/* BCM5325, BCM539x */
static struct phy_driver b53_phy_driver_id1 = {
.phy_id = 0x0143bc00,
.name = "Broadcom B53 (1)",
.phy_id_mask = 0x1ffffc00,
.features = 0,
.probe = b53_phy_probe,
.remove = b53_phy_remove,
.config_aneg = b53_phy_config_aneg,
.config_init = b53_phy_config_init,
.read_status = b53_phy_read_status,
};
/* BCM53125, BCM53128 */
static struct phy_driver b53_phy_driver_id2 = {
.phy_id = 0x03625c00,
.name = "Broadcom B53 (2)",
.phy_id_mask = 0x1ffffc00,
.features = 0,
.probe = b53_phy_probe,
.remove = b53_phy_remove,
.config_aneg = b53_phy_config_aneg,
.config_init = b53_phy_config_init,
.read_status = b53_phy_read_status,
};
/* BCM5365 */
static struct phy_driver b53_phy_driver_id3 = {
.phy_id = 0x00406300,
.name = "Broadcom B53 (3)",
.phy_id_mask = 0x1fffff00,
.features = 0,
.probe = b53_phy_probe,
.remove = b53_phy_remove,
.config_aneg = b53_phy_config_aneg,
.config_init = b53_phy_config_init,
.read_status = b53_phy_read_status,
};
int __init b53_phy_driver_register(void)
{
int ret;
ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE);
if (ret)
return ret;
ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE);
if (ret)
goto err1;
ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE);
if (!ret)
return 0;
phy_driver_unregister(&b53_phy_driver_id2);
err1:
phy_driver_unregister(&b53_phy_driver_id1);
return ret;
}
void __exit b53_phy_driver_unregister(void)
{
phy_driver_unregister(&b53_phy_driver_id3);
phy_driver_unregister(&b53_phy_driver_id2);
phy_driver_unregister(&b53_phy_driver_id1);
}
module_init(b53_phy_driver_register);
module_exit(b53_phy_driver_unregister);
MODULE_DESCRIPTION("B53 MDIO access driver");
MODULE_LICENSE("Dual BSD/GPL");

View file

@ -0,0 +1,248 @@
/*
* B53 register access through memory mapped registers
*
* Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/b53.h>
#include <linux/version.h>
#include "b53_priv.h"
static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
{
u8 __iomem *regs = dev->priv;
*val = readb(regs + (page << 8) + reg);
return 0;
}
static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
{
u8 __iomem *regs = dev->priv;
if (WARN_ON(reg % 2))
return -EINVAL;
if (dev->pdata && dev->pdata->big_endian)
*val = readw_be(regs + (page << 8) + reg);
else
*val = readw(regs + (page << 8) + reg);
return 0;
}
static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
{
u8 __iomem *regs = dev->priv;
if (WARN_ON(reg % 4))
return -EINVAL;
if (dev->pdata && dev->pdata->big_endian)
*val = readl_be(regs + (page << 8) + reg);
else
*val = readl(regs + (page << 8) + reg);
return 0;
}
static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
if (WARN_ON(reg % 2))
return -EINVAL;
if (reg % 4) {
u16 lo;
u32 hi;
b53_mmap_read16(dev, page, reg, &lo);
b53_mmap_read32(dev, page, reg + 2, &hi);
*val = ((u64)hi << 16) | lo;
} else {
u32 lo;
u16 hi;
b53_mmap_read32(dev, page, reg, &lo);
b53_mmap_read16(dev, page, reg + 4, &hi);
*val = ((u64)hi << 32) | lo;
}
return 0;
}
static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
u32 hi, lo;
if (WARN_ON(reg % 4))
return -EINVAL;
b53_mmap_read32(dev, page, reg, &lo);
b53_mmap_read32(dev, page, reg + 4, &hi);
*val = ((u64)hi << 32) | lo;
return 0;
}
static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
{
u8 __iomem *regs = dev->priv;
writeb(value, regs + (page << 8) + reg);
return 0;
}
static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
u16 value)
{
u8 __iomem *regs = dev->priv;
if (WARN_ON(reg % 2))
return -EINVAL;
if (dev->pdata && dev->pdata->big_endian)
writew_be(value, regs + (page << 8) + reg);
else
writew(value, regs + (page << 8) + reg);
return 0;
}
static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
u32 value)
{
u8 __iomem *regs = dev->priv;
if (WARN_ON(reg % 4))
return -EINVAL;
if (dev->pdata && dev->pdata->big_endian)
writel_be(value, regs + (page << 8) + reg);
else
writel(value, regs + (page << 8) + reg);
return 0;
}
static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
if (WARN_ON(reg % 2))
return -EINVAL;
if (reg % 4) {
u32 hi = (u32)(value >> 16);
u16 lo = (u16)value;
b53_mmap_write16(dev, page, reg, lo);
b53_mmap_write32(dev, page, reg + 2, hi);
} else {
u16 hi = (u16)(value >> 32);
u32 lo = (u32)value;
b53_mmap_write32(dev, page, reg, lo);
b53_mmap_write16(dev, page, reg + 4, hi);
}
return 0;
}
static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
u32 hi, lo;
hi = (u32)(value >> 32);
lo = (u32)value;
if (WARN_ON(reg % 4))
return -EINVAL;
b53_mmap_write32(dev, page, reg, lo);
b53_mmap_write32(dev, page, reg + 4, hi);
return 0;
}
static struct b53_io_ops b53_mmap_ops = {
.read8 = b53_mmap_read8,
.read16 = b53_mmap_read16,
.read32 = b53_mmap_read32,
.read48 = b53_mmap_read48,
.read64 = b53_mmap_read64,
.write8 = b53_mmap_write8,
.write16 = b53_mmap_write16,
.write32 = b53_mmap_write32,
.write48 = b53_mmap_write48,
.write64 = b53_mmap_write64,
};
static int b53_mmap_probe(struct platform_device *pdev)
{
struct b53_platform_data *pdata = pdev->dev.platform_data;
struct b53_device *dev;
if (!pdata)
return -EINVAL;
dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
if (!dev)
return -ENOMEM;
if (pdata)
dev->pdata = pdata;
platform_set_drvdata(pdev, dev);
return b53_swconfig_switch_register(dev);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
static int b53_mmap_remove(struct platform_device *pdev)
#else
static void b53_mmap_remove(struct platform_device *pdev)
#endif
{
struct b53_device *dev = platform_get_drvdata(pdev);
if (dev)
b53_switch_remove(dev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
return 0;
#endif
}
static struct platform_driver b53_mmap_driver = {
.probe = b53_mmap_probe,
.remove = b53_mmap_remove,
.driver = {
.name = "b53-switch",
},
};
module_platform_driver(b53_mmap_driver);
MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
MODULE_DESCRIPTION("B53 MMAP access driver");
MODULE_LICENSE("Dual BSD/GPL");

View file

@ -0,0 +1,55 @@
/*
* B53 PHY Fixup call
*
* Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/phy.h>
#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
#define B53_BRCM_OUI_1 0x0143bc00
#define B53_BRCM_OUI_2 0x03625c00
#define B53_BRCM_OUI_3 0x00406300
static int b53_phy_fixup(struct phy_device *dev)
{
struct mii_bus *bus = dev->mdio.bus;
u32 phy_id;
if (dev->mdio.addr != B53_PSEUDO_PHY)
return 0;
/* read the first port's id */
phy_id = mdiobus_read(bus, 0, 2) << 16;
phy_id |= mdiobus_read(bus, 0, 3);
if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
(phy_id & 0xfffffc00) == B53_BRCM_OUI_2 ||
(phy_id & 0xffffff00) == B53_BRCM_OUI_3) {
dev->phy_id = phy_id;
}
return 0;
}
int __init b53_phy_fixup_register(void)
{
return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
}
subsys_initcall(b53_phy_fixup_register);

View file

@ -0,0 +1,336 @@
/*
* B53 common definitions
*
* Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __B53_PRIV_H
#define __B53_PRIV_H
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/switch.h>
struct b53_device;
struct b53_io_ops {
int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
int (*phy_read16)(struct b53_device *dev, int addr, u8 reg, u16 *value);
int (*phy_write16)(struct b53_device *dev, int addr, u8 reg, u16 value);
};
enum {
BCM5325_DEVICE_ID = 0x25,
BCM5365_DEVICE_ID = 0x65,
BCM5395_DEVICE_ID = 0x95,
BCM5397_DEVICE_ID = 0x97,
BCM5398_DEVICE_ID = 0x98,
BCM53115_DEVICE_ID = 0x53115,
BCM53125_DEVICE_ID = 0x53125,
BCM53128_DEVICE_ID = 0x53128,
BCM63XX_DEVICE_ID = 0x6300,
BCM53010_DEVICE_ID = 0x53010,
BCM53011_DEVICE_ID = 0x53011,
BCM53012_DEVICE_ID = 0x53012,
BCM53018_DEVICE_ID = 0x53018,
BCM53019_DEVICE_ID = 0x53019,
};
#define B53_N_PORTS 9
#define B53_N_PORTS_25 6
struct b53_vlan {
unsigned int members:B53_N_PORTS;
unsigned int untag:B53_N_PORTS;
};
struct b53_port {
unsigned int pvid:12;
};
struct b53_device {
struct switch_dev sw_dev;
struct b53_platform_data *pdata;
struct mutex reg_mutex;
const struct b53_io_ops *ops;
/* chip specific data */
u32 chip_id;
u8 core_rev;
u8 vta_regs[3];
u8 duplex_reg;
u8 jumbo_pm_reg;
u8 jumbo_size_reg;
int reset_gpio;
/* used ports mask */
u16 enabled_ports;
/* connect specific data */
u8 current_page;
struct device *dev;
void *priv;
/* run time configuration */
unsigned enable_vlan:1;
unsigned enable_jumbo:1;
unsigned allow_vid_4095:1;
struct b53_port *ports;
struct b53_vlan *vlans;
char *buf;
};
#define b53_for_each_port(dev, i) \
for (i = 0; i < B53_N_PORTS; i++) \
if (dev->enabled_ports & BIT(i))
static inline int is5325(struct b53_device *dev)
{
return dev->chip_id == BCM5325_DEVICE_ID;
}
static inline int is5365(struct b53_device *dev)
{
#ifdef CONFIG_BCM47XX
return dev->chip_id == BCM5365_DEVICE_ID;
#else
return 0;
#endif
}
static inline int is5397_98(struct b53_device *dev)
{
return dev->chip_id == BCM5397_DEVICE_ID ||
dev->chip_id == BCM5398_DEVICE_ID;
}
static inline int is539x(struct b53_device *dev)
{
return dev->chip_id == BCM5395_DEVICE_ID ||
dev->chip_id == BCM5397_DEVICE_ID ||
dev->chip_id == BCM5398_DEVICE_ID;
}
static inline int is531x5(struct b53_device *dev)
{
return dev->chip_id == BCM53115_DEVICE_ID ||
dev->chip_id == BCM53125_DEVICE_ID ||
dev->chip_id == BCM53128_DEVICE_ID;
}
static inline int is63xx(struct b53_device *dev)
{
#ifdef CONFIG_BCM63XX
return dev->chip_id == BCM63XX_DEVICE_ID;
#else
return 0;
#endif
}
static inline int is5301x(struct b53_device *dev)
{
return dev->chip_id == BCM53010_DEVICE_ID ||
dev->chip_id == BCM53011_DEVICE_ID ||
dev->chip_id == BCM53012_DEVICE_ID ||
dev->chip_id == BCM53018_DEVICE_ID ||
dev->chip_id == BCM53019_DEVICE_ID;
}
#define B53_CPU_PORT_25 5
#define B53_CPU_PORT 8
static inline int is_cpu_port(struct b53_device *dev, int port)
{
return dev->sw_dev.cpu_port == port;
}
static inline int is_imp_port(struct b53_device *dev, int port)
{
if (is5325(dev) || is5365(dev))
return port == B53_CPU_PORT_25;
else
return port == B53_CPU_PORT;
}
static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
{
return container_of(sw, struct b53_device, sw_dev);
}
struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_ops *ops,
void *priv);
int b53_swconfig_switch_detect(struct b53_device *dev);
int b53_swconfig_switch_register(struct b53_device *dev);
static inline void b53_switch_remove(struct b53_device *dev)
{
unregister_switch(&dev->sw_dev);
}
static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->read8(dev, page, reg, val);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->read16(dev, page, reg, val);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->read32(dev, page, reg, val);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->read48(dev, page, reg, val);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->read64(dev, page, reg, val);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->write8(dev, page, reg, value);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
u16 value)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->write16(dev, page, reg, value);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
u32 value)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->write32(dev, page, reg, value);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->write48(dev, page, reg, value);
mutex_unlock(&dev->reg_mutex);
return ret;
}
static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
int ret;
mutex_lock(&dev->reg_mutex);
ret = dev->ops->write64(dev, page, reg, value);
mutex_unlock(&dev->reg_mutex);
return ret;
}
#ifdef CONFIG_BCM47XX
#include <bcm47xx_board.h>
#endif
#include <linux/version.h>
#include <linux/bcm47xx_nvram.h>
static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
{
#ifdef CONFIG_BCM47XX
enum bcm47xx_board board = bcm47xx_board_get();
switch (board) {
case BCM47XX_BOARD_LINKSYS_WRT300NV11:
case BCM47XX_BOARD_LINKSYS_WRT310NV1:
return 8;
default:
break;
}
#endif
return bcm47xx_nvram_gpio_pin("robo_reset");
}
#endif

View file

@ -0,0 +1,348 @@
/*
* B53 register definitions
*
* Copyright (C) 2004 Broadcom Corporation
* Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __B53_REGS_H
#define __B53_REGS_H
/* Management Port (SMP) Page offsets */
#define B53_CTRL_PAGE 0x00 /* Control */
#define B53_STAT_PAGE 0x01 /* Status */
#define B53_MGMT_PAGE 0x02 /* Management Mode */
#define B53_MIB_AC_PAGE 0x03 /* MIB Autocast */
#define B53_ARLCTRL_PAGE 0x04 /* ARL Control */
#define B53_ARLIO_PAGE 0x05 /* ARL Access */
#define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */
#define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */
/* PHY Registers */
#define B53_PORT_MII_PAGE(i) (0x10 + (i)) /* Port i MII Registers */
#define B53_IM_PORT_PAGE 0x18 /* Inverse MII Port (to EMAC) */
#define B53_ALL_PORT_PAGE 0x19 /* All ports MII (broadcast) */
/* MIB registers */
#define B53_MIB_PAGE(i) (0x20 + (i))
/* Quality of Service (QoS) Registers */
#define B53_QOS_PAGE 0x30
/* Port VLAN Page */
#define B53_PVLAN_PAGE 0x31
/* VLAN Registers */
#define B53_VLAN_PAGE 0x34
/* Jumbo Frame Registers */
#define B53_JUMBO_PAGE 0x40
/* CFP Configuration Registers Page */
#define B53_CFP_PAGE 0xa1
/*************************************************************************
* Control Page registers
*************************************************************************/
/* Port Control Register (8 bit) */
#define B53_PORT_CTRL(i) (0x00 + (i))
#define PORT_CTRL_RX_DISABLE BIT(0)
#define PORT_CTRL_TX_DISABLE BIT(1)
#define PORT_CTRL_RX_BCST_EN BIT(2) /* Broadcast RX (P8 only) */
#define PORT_CTRL_RX_MCST_EN BIT(3) /* Multicast RX (P8 only) */
#define PORT_CTRL_RX_UCST_EN BIT(4) /* Unicast RX (P8 only) */
#define PORT_CTRL_STP_STATE_S 5
#define PORT_CTRL_STP_STATE_MASK (0x7 << PORT_CTRL_STP_STATE_S)
/* SMP Control Register (8 bit) */
#define B53_SMP_CTRL 0x0a
/* Switch Mode Control Register (8 bit) */
#define B53_SWITCH_MODE 0x0b
#define SM_SW_FWD_MODE BIT(0) /* 1 = Managed Mode */
#define SM_SW_FWD_EN BIT(1) /* Forwarding Enable */
/* IMP Port state override register (8 bit) */
#define B53_PORT_OVERRIDE_CTRL 0x0e
#define PORT_OVERRIDE_LINK BIT(0)
#define PORT_OVERRIDE_FULL_DUPLEX BIT(1) /* 0 = Half Duplex */
#define PORT_OVERRIDE_SPEED_S 2
#define PORT_OVERRIDE_SPEED_10M (0 << PORT_OVERRIDE_SPEED_S)
#define PORT_OVERRIDE_SPEED_100M (1 << PORT_OVERRIDE_SPEED_S)
#define PORT_OVERRIDE_SPEED_1000M (2 << PORT_OVERRIDE_SPEED_S)
#define PORT_OVERRIDE_RV_MII_25 BIT(4) /* BCM5325 only */
#define PORT_OVERRIDE_RX_FLOW BIT(4)
#define PORT_OVERRIDE_TX_FLOW BIT(5)
#define PORT_OVERRIDE_SPEED_2000M BIT(6) /* BCM5301X only, requires setting 1000M */
#define PORT_OVERRIDE_EN BIT(7) /* Use the register contents */
/* Power-down mode control */
#define B53_PD_MODE_CTRL_25 0x0f
/* IP Multicast control (8 bit) */
#define B53_IP_MULTICAST_CTRL 0x21
#define B53_IPMC_FWD_EN BIT(1)
#define B53_UC_FWD_EN BIT(6)
#define B53_MC_FWD_EN BIT(7)
/* (16 bit) */
#define B53_UC_FLOOD_MASK 0x32
#define B53_MC_FLOOD_MASK 0x34
#define B53_IPMC_FLOOD_MASK 0x36
/*
* Override Ports 0-7 State on devices with xMII interfaces (8 bit)
*
* For port 8 still use B53_PORT_OVERRIDE_CTRL
* Please note that not all ports are available on every hardware, e.g. BCM5301X
* don't include overriding port 6, BCM63xx also have some limitations.
*/
#define B53_GMII_PORT_OVERRIDE_CTRL(i) (0x58 + (i))
#define GMII_PO_LINK BIT(0)
#define GMII_PO_FULL_DUPLEX BIT(1) /* 0 = Half Duplex */
#define GMII_PO_SPEED_S 2
#define GMII_PO_SPEED_10M (0 << GMII_PO_SPEED_S)
#define GMII_PO_SPEED_100M (1 << GMII_PO_SPEED_S)
#define GMII_PO_SPEED_1000M (2 << GMII_PO_SPEED_S)
#define GMII_PO_RX_FLOW BIT(4)
#define GMII_PO_TX_FLOW BIT(5)
#define GMII_PO_EN BIT(6) /* Use the register contents */
#define GMII_PO_SPEED_2000M BIT(7) /* BCM5301X only, requires setting 1000M */
/* Software reset register (8 bit) */
#define B53_SOFTRESET 0x79
/* Fast Aging Control register (8 bit) */
#define B53_FAST_AGE_CTRL 0x88
#define FAST_AGE_STATIC BIT(0)
#define FAST_AGE_DYNAMIC BIT(1)
#define FAST_AGE_PORT BIT(2)
#define FAST_AGE_VLAN BIT(3)
#define FAST_AGE_STP BIT(4)
#define FAST_AGE_MC BIT(5)
#define FAST_AGE_DONE BIT(7)
/*************************************************************************
* Status Page registers
*************************************************************************/
/* Link Status Summary Register (16bit) */
#define B53_LINK_STAT 0x00
/* Link Status Change Register (16 bit) */
#define B53_LINK_STAT_CHANGE 0x02
/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
#define B53_SPEED_STAT 0x04
#define SPEED_PORT_FE(reg, port) (((reg) >> (port)) & 1)
#define SPEED_PORT_GE(reg, port) (((reg) >> 2 * (port)) & 3)
#define SPEED_STAT_10M 0
#define SPEED_STAT_100M 1
#define SPEED_STAT_1000M 2
/* Duplex Status Summary (16 bit) */
#define B53_DUPLEX_STAT_FE 0x06
#define B53_DUPLEX_STAT_GE 0x08
#define B53_DUPLEX_STAT_63XX 0x0c
/* Revision ID register for BCM5325 */
#define B53_REV_ID_25 0x50
/* Strap Value (48 bit) */
#define B53_STRAP_VALUE 0x70
#define SV_GMII_CTRL_115 BIT(27)
/*************************************************************************
* Management Mode Page Registers
*************************************************************************/
/* Global Management Config Register (8 bit) */
#define B53_GLOBAL_CONFIG 0x00
#define GC_RESET_MIB 0x01
#define GC_RX_BPDU_EN 0x02
#define GC_MIB_AC_HDR_EN 0x10
#define GC_MIB_AC_EN 0x20
#define GC_FRM_MGMT_PORT_M 0xC0
#define GC_FRM_MGMT_PORT_04 0x00
#define GC_FRM_MGMT_PORT_MII 0x80
/* Broadcom Header control register (8 bit) */
#define B53_BRCM_HDR 0x03
#define BRCM_HDR_P8_EN BIT(0) /* Enable tagging on port 8 */
#define BRCM_HDR_P5_EN BIT(1) /* Enable tagging on port 5 */
/* Device ID register (8 or 32 bit) */
#define B53_DEVICE_ID 0x30
/* Revision ID register (8 bit) */
#define B53_REV_ID 0x40
/*************************************************************************
* ARL Access Page Registers
*************************************************************************/
/* VLAN Table Access Register (8 bit) */
#define B53_VT_ACCESS 0x80
#define B53_VT_ACCESS_9798 0x60 /* for BCM5397/BCM5398 */
#define B53_VT_ACCESS_63XX 0x60 /* for BCM6328/62/68 */
#define VTA_CMD_WRITE 0
#define VTA_CMD_READ 1
#define VTA_CMD_CLEAR 2
#define VTA_START_CMD BIT(7)
/* VLAN Table Index Register (16 bit) */
#define B53_VT_INDEX 0x81
#define B53_VT_INDEX_9798 0x61
#define B53_VT_INDEX_63XX 0x62
/* VLAN Table Entry Register (32 bit) */
#define B53_VT_ENTRY 0x83
#define B53_VT_ENTRY_9798 0x63
#define B53_VT_ENTRY_63XX 0x64
#define VTE_MEMBERS 0x1ff
#define VTE_UNTAG_S 9
#define VTE_UNTAG (0x1ff << 9)
/*************************************************************************
* Port VLAN Registers
*************************************************************************/
/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
#define B53_PVLAN_PORT_MASK(i) ((i) * 2)
/*************************************************************************
* 802.1Q Page Registers
*************************************************************************/
/* Global QoS Control (8 bit) */
#define B53_QOS_GLOBAL_CTL 0x00
/* Enable 802.1Q for individual Ports (16 bit) */
#define B53_802_1P_EN 0x04
/*************************************************************************
* VLAN Page Registers
*************************************************************************/
/* VLAN Control 0 (8 bit) */
#define B53_VLAN_CTRL0 0x00
#define VC0_8021PF_CTRL_MASK 0x3
#define VC0_8021PF_CTRL_NONE 0x0
#define VC0_8021PF_CTRL_CHANGE_PRI 0x1
#define VC0_8021PF_CTRL_CHANGE_VID 0x2
#define VC0_8021PF_CTRL_CHANGE_BOTH 0x3
#define VC0_8021QF_CTRL_MASK 0xc
#define VC0_8021QF_CTRL_CHANGE_PRI 0x1
#define VC0_8021QF_CTRL_CHANGE_VID 0x2
#define VC0_8021QF_CTRL_CHANGE_BOTH 0x3
#define VC0_RESERVED_1 BIT(1)
#define VC0_DROP_VID_MISS BIT(4)
#define VC0_VID_HASH_VID BIT(5)
#define VC0_VID_CHK_EN BIT(6) /* Use VID,DA or VID,SA */
#define VC0_VLAN_EN BIT(7) /* 802.1Q VLAN Enabled */
/* VLAN Control 1 (8 bit) */
#define B53_VLAN_CTRL1 0x01
#define VC1_RX_MCST_TAG_EN BIT(1)
#define VC1_RX_MCST_FWD_EN BIT(2)
#define VC1_RX_MCST_UNTAG_EN BIT(3)
/* VLAN Control 2 (8 bit) */
#define B53_VLAN_CTRL2 0x02
/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
#define B53_VLAN_CTRL3 0x03
#define B53_VLAN_CTRL3_63XX 0x04
#define VC3_MAXSIZE_1532 BIT(6) /* 5325 only */
#define VC3_HIGH_8BIT_EN BIT(7) /* 5325 only */
/* VLAN Control 4 (8 bit) */
#define B53_VLAN_CTRL4 0x05
#define B53_VLAN_CTRL4_25 0x04
#define B53_VLAN_CTRL4_63XX 0x06
#define VC4_ING_VID_CHECK_S 6
#define VC4_ING_VID_CHECK_MASK (0x3 << VC4_ING_VID_CHECK_S)
#define VC4_ING_VID_VIO_FWD 0 /* forward, but do not learn */
#define VC4_ING_VID_VIO_DROP 1 /* drop VID violations */
#define VC4_NO_ING_VID_CHK 2 /* do not check */
#define VC4_ING_VID_VIO_TO_IMP 3 /* redirect to MII port */
/* VLAN Control 5 (8 bit) */
#define B53_VLAN_CTRL5 0x06
#define B53_VLAN_CTRL5_25 0x05
#define B53_VLAN_CTRL5_63XX 0x07
#define VC5_VID_FFF_EN BIT(2)
#define VC5_DROP_VTABLE_MISS BIT(3)
/* VLAN Control 6 (8 bit) */
#define B53_VLAN_CTRL6 0x07
#define B53_VLAN_CTRL6_63XX 0x08
/* VLAN Table Access Register (16 bit) */
#define B53_VLAN_TABLE_ACCESS_25 0x06 /* BCM5325E/5350 */
#define B53_VLAN_TABLE_ACCESS_65 0x08 /* BCM5365 */
#define VTA_VID_LOW_MASK_25 0xf
#define VTA_VID_LOW_MASK_65 0xff
#define VTA_VID_HIGH_S_25 4
#define VTA_VID_HIGH_S_65 8
#define VTA_VID_HIGH_MASK_25 (0xff << VTA_VID_HIGH_S_25E)
#define VTA_VID_HIGH_MASK_65 (0xf << VTA_VID_HIGH_S_65)
#define VTA_RW_STATE BIT(12)
#define VTA_RW_STATE_RD 0
#define VTA_RW_STATE_WR BIT(12)
#define VTA_RW_OP_EN BIT(13)
/* VLAN Read/Write Registers for (16/32 bit) */
#define B53_VLAN_WRITE_25 0x08
#define B53_VLAN_WRITE_65 0x0a
#define B53_VLAN_READ 0x0c
#define VA_MEMBER_MASK 0x3f
#define VA_UNTAG_S_25 6
#define VA_UNTAG_MASK_25 0x3f
#define VA_UNTAG_S_65 7
#define VA_UNTAG_MASK_65 0x1f
#define VA_VID_HIGH_S 12
#define VA_VID_HIGH_MASK (0xffff << VA_VID_HIGH_S)
#define VA_VALID_25 BIT(20)
#define VA_VALID_25_R4 BIT(24)
#define VA_VALID_65 BIT(14)
/* VLAN Port Default Tag (16 bit) */
#define B53_VLAN_PORT_DEF_TAG(i) (0x10 + 2 * (i))
/*************************************************************************
* Jumbo Frame Page Registers
*************************************************************************/
/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
#define B53_JUMBO_PORT_MASK 0x01
#define B53_JUMBO_PORT_MASK_63XX 0x04
#define JPM_10_100_JUMBO_EN BIT(24) /* GigE always enabled */
/* Good Frame Max Size without 802.1Q TAG (16 bit) */
#define B53_JUMBO_MAX_SIZE 0x05
#define B53_JUMBO_MAX_SIZE_63XX 0x08
#define JMS_MIN_SIZE 1518
#define JMS_MAX_SIZE 9724
/*************************************************************************
* CFP Configuration Page Registers
*************************************************************************/
/* CFP Control Register with ports map (8 bit) */
#define B53_CFP_CTRL 0x00
#endif /* !__B53_REGS_H */

View file

@ -0,0 +1,344 @@
/*
* B53 register access through SPI
*
* Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <asm/unaligned.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/platform_data/b53.h>
#include "b53_priv.h"
#define B53_SPI_DATA 0xf0
#define B53_SPI_STATUS 0xfe
#define B53_SPI_CMD_SPIF BIT(7)
#define B53_SPI_CMD_RACK BIT(5)
#define B53_SPI_CMD_READ 0x00
#define B53_SPI_CMD_WRITE 0x01
#define B53_SPI_CMD_NORMAL 0x60
#define B53_SPI_CMD_FAST 0x10
#define B53_SPI_PAGE_SELECT 0xff
static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
unsigned len)
{
u8 txbuf[2];
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
txbuf[1] = reg;
return spi_write_then_read(spi, txbuf, 2, val, len);
}
static inline int b53_spi_clear_status(struct spi_device *spi)
{
unsigned int i;
u8 rxbuf;
int ret;
for (i = 0; i < 10; i++) {
ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
if (ret)
return ret;
if (!(rxbuf & B53_SPI_CMD_SPIF))
break;
mdelay(1);
}
if (i == 10)
return -EIO;
return 0;
}
static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
{
u8 txbuf[3];
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
txbuf[1] = B53_SPI_PAGE_SELECT;
txbuf[2] = page;
return spi_write(spi, txbuf, sizeof(txbuf));
}
static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
{
int ret = b53_spi_clear_status(spi);
if (ret)
return ret;
return b53_spi_set_page(spi, page);
}
static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
{
u8 rxbuf;
int retry_count;
int ret;
ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
if (ret)
return ret;
for (retry_count = 0; retry_count < 10; retry_count++) {
ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
if (ret)
return ret;
if (rxbuf & B53_SPI_CMD_RACK)
break;
mdelay(1);
}
if (retry_count == 10)
return -EIO;
return 0;
}
static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
unsigned len)
{
struct spi_device *spi = dev->priv;
int ret;
ret = b53_prepare_reg_access(spi, page);
if (ret)
return ret;
ret = b53_spi_prepare_reg_read(spi, reg);
if (ret)
return ret;
return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
}
static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
{
return b53_spi_read(dev, page, reg, val, 1);
}
static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
{
int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
if (!ret)
*val = le16_to_cpu(*val);
return ret;
}
static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
{
int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
if (!ret)
*val = le32_to_cpu(*val);
return ret;
}
static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
int ret;
*val = 0;
ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
if (!ret)
*val = le64_to_cpu(*val);
return ret;
}
static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
if (!ret)
*val = le64_to_cpu(*val);
return ret;
}
static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
{
struct spi_device *spi = dev->priv;
int ret;
u8 txbuf[3];
ret = b53_prepare_reg_access(spi, page);
if (ret)
return ret;
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
txbuf[1] = reg;
txbuf[2] = value;
return spi_write(spi, txbuf, sizeof(txbuf));
}
static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
{
struct spi_device *spi = dev->priv;
int ret;
u8 txbuf[4];
ret = b53_prepare_reg_access(spi, page);
if (ret)
return ret;
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
txbuf[1] = reg;
put_unaligned_le16(value, &txbuf[2]);
return spi_write(spi, txbuf, sizeof(txbuf));
}
static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
{
struct spi_device *spi = dev->priv;
int ret;
u8 txbuf[6];
ret = b53_prepare_reg_access(spi, page);
if (ret)
return ret;
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
txbuf[1] = reg;
put_unaligned_le32(value, &txbuf[2]);
return spi_write(spi, txbuf, sizeof(txbuf));
}
static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
{
struct spi_device *spi = dev->priv;
int ret;
u8 txbuf[10];
ret = b53_prepare_reg_access(spi, page);
if (ret)
return ret;
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
txbuf[1] = reg;
put_unaligned_le64(value, &txbuf[2]);
return spi_write(spi, txbuf, sizeof(txbuf) - 2);
}
static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
{
struct spi_device *spi = dev->priv;
int ret;
u8 txbuf[10];
ret = b53_prepare_reg_access(spi, page);
if (ret)
return ret;
txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
txbuf[1] = reg;
put_unaligned_le64(value, &txbuf[2]);
return spi_write(spi, txbuf, sizeof(txbuf));
}
static struct b53_io_ops b53_spi_ops = {
.read8 = b53_spi_read8,
.read16 = b53_spi_read16,
.read32 = b53_spi_read32,
.read48 = b53_spi_read48,
.read64 = b53_spi_read64,
.write8 = b53_spi_write8,
.write16 = b53_spi_write16,
.write32 = b53_spi_write32,
.write48 = b53_spi_write48,
.write64 = b53_spi_write64,
};
static int b53_spi_probe(struct spi_device *spi)
{
struct b53_device *dev;
int ret;
dev = b53_swconfig_switch_alloc(&spi->dev, &b53_spi_ops, spi);
if (!dev)
return -ENOMEM;
if (spi->dev.platform_data)
dev->pdata = spi->dev.platform_data;
ret = b53_swconfig_switch_register(dev);
if (ret)
return ret;
spi_set_drvdata(spi, dev);
return 0;
}
static int b53_spi_remove(struct spi_device *spi)
{
struct b53_device *dev = spi_get_drvdata(spi);
if (dev)
b53_switch_remove(dev);
return 0;
}
static const struct of_device_id b53_of_match[] = {
{ .compatible = "brcm,bcm5325" },
{ .compatible = "brcm,bcm53115" },
{ .compatible = "brcm,bcm53125" },
{ .compatible = "brcm,bcm53128" },
{ .compatible = "brcm,bcm5365" },
{ .compatible = "brcm,bcm5395" },
{ .compatible = "brcm,bcm5397" },
{ .compatible = "brcm,bcm5398" },
{ /* sentinel */ },
};
static struct spi_driver b53_spi_driver = {
.driver = {
.name = "b53-switch",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = b53_of_match,
},
.probe = b53_spi_probe,
.remove = b53_spi_remove,
};
module_spi_driver(b53_spi_driver);
MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
MODULE_DESCRIPTION("B53 SPI access driver");
MODULE_LICENSE("Dual BSD/GPL");

View file

@ -0,0 +1,385 @@
/*
* B53 register access through Switch Register Access Bridge Registers
*
* Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/b53.h>
#include <linux/version.h>
#include "b53_priv.h"
/* command and status register of the SRAB */
#define B53_SRAB_CMDSTAT 0x2c
#define B53_SRAB_CMDSTAT_RST BIT(2)
#define B53_SRAB_CMDSTAT_WRITE BIT(1)
#define B53_SRAB_CMDSTAT_GORDYN BIT(0)
#define B53_SRAB_CMDSTAT_PAGE 24
#define B53_SRAB_CMDSTAT_REG 16
/* high order word of write data to switch registe */
#define B53_SRAB_WD_H 0x30
/* low order word of write data to switch registe */
#define B53_SRAB_WD_L 0x34
/* high order word of read data from switch register */
#define B53_SRAB_RD_H 0x38
/* low order word of read data from switch register */
#define B53_SRAB_RD_L 0x3c
/* command and status register of the SRAB */
#define B53_SRAB_CTRLS 0x40
#define B53_SRAB_CTRLS_RCAREQ BIT(3)
#define B53_SRAB_CTRLS_RCAGNT BIT(4)
#define B53_SRAB_CTRLS_SW_INIT_DONE BIT(6)
/* the register captures interrupt pulses from the switch */
#define B53_SRAB_INTR 0x44
static int b53_srab_request_grant(struct b53_device *dev)
{
u8 __iomem *regs = dev->priv;
u32 ctrls;
int i;
ctrls = readl(regs + B53_SRAB_CTRLS);
ctrls |= B53_SRAB_CTRLS_RCAREQ;
writel(ctrls, regs + B53_SRAB_CTRLS);
for (i = 0; i < 20; i++) {
ctrls = readl(regs + B53_SRAB_CTRLS);
if (ctrls & B53_SRAB_CTRLS_RCAGNT)
break;
usleep_range(10, 100);
}
if (WARN_ON(i == 5))
return -EIO;
return 0;
}
static void b53_srab_release_grant(struct b53_device *dev)
{
u8 __iomem *regs = dev->priv;
u32 ctrls;
ctrls = readl(regs + B53_SRAB_CTRLS);
ctrls &= ~B53_SRAB_CTRLS_RCAREQ;
writel(ctrls, regs + B53_SRAB_CTRLS);
}
static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)
{
int i;
u32 cmdstat;
u8 __iomem *regs = dev->priv;
/* set register address */
cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |
(reg << B53_SRAB_CMDSTAT_REG) |
B53_SRAB_CMDSTAT_GORDYN |
op;
writel(cmdstat, regs + B53_SRAB_CMDSTAT);
/* check if operation completed */
for (i = 0; i < 5; ++i) {
cmdstat = readl(regs + B53_SRAB_CMDSTAT);
if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))
break;
usleep_range(10, 100);
}
if (WARN_ON(i == 5))
return -EIO;
return 0;
}
static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
ret = b53_srab_op(dev, page, reg, 0);
if (ret)
goto err;
*val = readl(regs + B53_SRAB_RD_L) & 0xff;
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
ret = b53_srab_op(dev, page, reg, 0);
if (ret)
goto err;
*val = readl(regs + B53_SRAB_RD_L) & 0xffff;
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
ret = b53_srab_op(dev, page, reg, 0);
if (ret)
goto err;
*val = readl(regs + B53_SRAB_RD_L);
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
ret = b53_srab_op(dev, page, reg, 0);
if (ret)
goto err;
*val = readl(regs + B53_SRAB_RD_L);
*val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
ret = b53_srab_op(dev, page, reg, 0);
if (ret)
goto err;
*val = readl(regs + B53_SRAB_RD_L);
*val += (u64)readl(regs + B53_SRAB_RD_H) << 32;
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
writel(value, regs + B53_SRAB_WD_L);
ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,
u16 value)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
writel(value, regs + B53_SRAB_WD_L);
ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,
u32 value)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
writel(value, regs + B53_SRAB_WD_L);
ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
writel((u32)value, regs + B53_SRAB_WD_L);
writel((u16)(value >> 32), regs + B53_SRAB_WD_H);
ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
err:
b53_srab_release_grant(dev);
return ret;
}
static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,
u64 value)
{
u8 __iomem *regs = dev->priv;
int ret = 0;
ret = b53_srab_request_grant(dev);
if (ret)
goto err;
writel((u32)value, regs + B53_SRAB_WD_L);
writel((u32)(value >> 32), regs + B53_SRAB_WD_H);
ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
err:
b53_srab_release_grant(dev);
return ret;
}
static struct b53_io_ops b53_srab_ops = {
.read8 = b53_srab_read8,
.read16 = b53_srab_read16,
.read32 = b53_srab_read32,
.read48 = b53_srab_read48,
.read64 = b53_srab_read64,
.write8 = b53_srab_write8,
.write16 = b53_srab_write16,
.write32 = b53_srab_write32,
.write48 = b53_srab_write48,
.write64 = b53_srab_write64,
};
static int b53_srab_probe(struct platform_device *pdev)
{
struct b53_platform_data *pdata = pdev->dev.platform_data;
struct b53_device *dev;
if (!pdata)
return -EINVAL;
dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
if (!dev)
return -ENOMEM;
if (pdata)
dev->pdata = pdata;
platform_set_drvdata(pdev, dev);
return b53_swconfig_switch_register(dev);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
static int b53_srab_remove(struct platform_device *pdev)
#else
static void b53_srab_remove(struct platform_device *pdev)
#endif
{
struct b53_device *dev = platform_get_drvdata(pdev);
if (dev)
b53_switch_remove(dev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
return 0;
#endif
}
static struct platform_driver b53_srab_driver = {
.probe = b53_srab_probe,
.remove = b53_srab_remove,
.driver = {
.name = "b53-srab-switch",
},
};
module_platform_driver(b53_srab_driver);
MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");
MODULE_LICENSE("Dual BSD/GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,443 @@
/*
* Lantiq PSB6970 (Tantos) Switch driver
*
* Copyright (c) 2009,2010 Team Embedded.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
* Free Software Foundation.
*
* The switch programming done in this driver follows the
* "Ethernet Traffic Separation using VLAN" Application Note as
* published by Lantiq.
*/
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/switch.h>
#include <linux/phy.h>
#include <linux/version.h>
#define PSB6970_MAX_VLANS 16
#define PSB6970_NUM_PORTS 7
#define PSB6970_DEFAULT_PORT_CPU 6
#define PSB6970_IS_CPU_PORT(x) ((x) > 4)
#define PHYADDR(_reg) ((_reg >> 5) & 0xff), (_reg & 0x1f)
/* --- Identification --- */
#define PSB6970_CI0 0x0100
#define PSB6970_CI0_MASK 0x000f
#define PSB6970_CI1 0x0101
#define PSB6970_CI1_VAL 0x2599
#define PSB6970_CI1_MASK 0xffff
/* --- VLAN filter table --- */
#define PSB6970_VFxL(i) ((i)*2+0x10) /* VLAN Filter Low */
#define PSB6970_VFxL_VV (1 << 15) /* VLAN_Valid */
#define PSB6970_VFxH(i) ((i)*2+0x11) /* VLAN Filter High */
#define PSB6970_VFxH_TM_SHIFT 7 /* Tagged Member */
/* --- Port registers --- */
#define PSB6970_EC(p) ((p)*0x20+2) /* Extended Control */
#define PSB6970_EC_IFNTE (1 << 1) /* Input Force No Tag Enable */
#define PSB6970_PBVM(p) ((p)*0x20+3) /* Port Base VLAN Map */
#define PSB6970_PBVM_VMCE (1 << 8)
#define PSB6970_PBVM_AOVTP (1 << 9)
#define PSB6970_PBVM_VSD (1 << 10)
#define PSB6970_PBVM_VC (1 << 11) /* VID Check with VID table */
#define PSB6970_PBVM_TBVE (1 << 13) /* Tag-Based VLAN enable */
#define PSB6970_DVID(p) ((p)*0x20+4) /* Default VLAN ID & Priority */
struct psb6970_priv {
struct switch_dev dev;
struct phy_device *phy;
u16 (*read) (struct phy_device* phydev, int reg);
void (*write) (struct phy_device* phydev, int reg, u16 val);
struct mutex reg_mutex;
/* all fields below are cleared on reset */
struct_group(psb6970_priv_volatile,
bool vlan;
u16 vlan_id[PSB6970_MAX_VLANS];
u8 vlan_table[PSB6970_MAX_VLANS];
u8 vlan_tagged;
u16 pvid[PSB6970_NUM_PORTS];
);
};
#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
{
struct mii_bus *bus = phydev->mdio.bus;
return bus->read(bus, PHYADDR(reg));
}
static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
{
struct mii_bus *bus = phydev->mdio.bus;
bus->write(bus, PHYADDR(reg), val);
}
static int
psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct psb6970_priv *priv = to_psb6970(dev);
priv->vlan = !!val->value.i;
return 0;
}
static int
psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct psb6970_priv *priv = to_psb6970(dev);
val->value.i = priv->vlan;
return 0;
}
static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
{
struct psb6970_priv *priv = to_psb6970(dev);
/* make sure no invalid PVIDs get set */
if (vlan >= dev->vlans)
return -EINVAL;
priv->pvid[port] = vlan;
return 0;
}
static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
{
struct psb6970_priv *priv = to_psb6970(dev);
*vlan = priv->pvid[port];
return 0;
}
static int
psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct psb6970_priv *priv = to_psb6970(dev);
priv->vlan_id[val->port_vlan] = val->value.i;
return 0;
}
static int
psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct psb6970_priv *priv = to_psb6970(dev);
val->value.i = priv->vlan_id[val->port_vlan];
return 0;
}
static struct switch_attr psb6970_globals[] = {
{
.type = SWITCH_TYPE_INT,
.name = "enable_vlan",
.description = "Enable VLAN mode",
.set = psb6970_set_vlan,
.get = psb6970_get_vlan,
.max = 1},
};
static struct switch_attr psb6970_port[] = {
};
static struct switch_attr psb6970_vlan[] = {
{
.type = SWITCH_TYPE_INT,
.name = "vid",
.description = "VLAN ID (0-4094)",
.set = psb6970_set_vid,
.get = psb6970_get_vid,
.max = 4094,
},
};
static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
{
struct psb6970_priv *priv = to_psb6970(dev);
u8 ports = priv->vlan_table[val->port_vlan];
int i;
val->len = 0;
for (i = 0; i < PSB6970_NUM_PORTS; i++) {
struct switch_port *p;
if (!(ports & (1 << i)))
continue;
p = &val->value.ports[val->len++];
p->id = i;
if (priv->vlan_tagged & (1 << i))
p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
else
p->flags = 0;
}
return 0;
}
static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
{
struct psb6970_priv *priv = to_psb6970(dev);
u8 *vt = &priv->vlan_table[val->port_vlan];
int i, j;
*vt = 0;
for (i = 0; i < val->len; i++) {
struct switch_port *p = &val->value.ports[i];
if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
priv->vlan_tagged |= (1 << p->id);
else {
priv->vlan_tagged &= ~(1 << p->id);
priv->pvid[p->id] = val->port_vlan;
/* make sure that an untagged port does not
* appear in other vlans */
for (j = 0; j < PSB6970_MAX_VLANS; j++) {
if (j == val->port_vlan)
continue;
priv->vlan_table[j] &= ~(1 << p->id);
}
}
*vt |= 1 << p->id;
}
return 0;
}
static int psb6970_hw_apply(struct switch_dev *dev)
{
struct psb6970_priv *priv = to_psb6970(dev);
int i, j;
mutex_lock(&priv->reg_mutex);
if (priv->vlan) {
/* into the vlan translation unit */
for (j = 0; j < PSB6970_MAX_VLANS; j++) {
u8 vp = priv->vlan_table[j];
if (vp) {
priv->write(priv->phy, PSB6970_VFxL(j),
PSB6970_VFxL_VV | priv->vlan_id[j]);
priv->write(priv->phy, PSB6970_VFxH(j),
((vp & priv->
vlan_tagged) <<
PSB6970_VFxH_TM_SHIFT) | vp);
} else /* clear VLAN Valid flag for unused vlans */
priv->write(priv->phy, PSB6970_VFxL(j), 0);
}
}
/* update the port destination mask registers and tag settings */
for (i = 0; i < PSB6970_NUM_PORTS; i++) {
int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
if (priv->vlan) {
ec = PSB6970_EC_IFNTE;
dvid = priv->vlan_id[priv->pvid[i]];
pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
if ((i << 1) & priv->vlan_tagged)
pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
}
priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
if (!PSB6970_IS_CPU_PORT(i)) {
priv->write(priv->phy, PSB6970_EC(i), ec);
priv->write(priv->phy, PSB6970_DVID(i), dvid);
}
}
mutex_unlock(&priv->reg_mutex);
return 0;
}
static int psb6970_reset_switch(struct switch_dev *dev)
{
struct psb6970_priv *priv = to_psb6970(dev);
int i;
mutex_lock(&priv->reg_mutex);
memset(&priv->psb6970_priv_volatile, 0,
sizeof(priv->psb6970_priv_volatile));
for (i = 0; i < PSB6970_MAX_VLANS; i++)
priv->vlan_id[i] = i;
mutex_unlock(&priv->reg_mutex);
return psb6970_hw_apply(dev);
}
static const struct switch_dev_ops psb6970_ops = {
.attr_global = {
.attr = psb6970_globals,
.n_attr = ARRAY_SIZE(psb6970_globals),
},
.attr_port = {
.attr = psb6970_port,
.n_attr = ARRAY_SIZE(psb6970_port),
},
.attr_vlan = {
.attr = psb6970_vlan,
.n_attr = ARRAY_SIZE(psb6970_vlan),
},
.get_port_pvid = psb6970_get_pvid,
.set_port_pvid = psb6970_set_pvid,
.get_vlan_ports = psb6970_get_ports,
.set_vlan_ports = psb6970_set_ports,
.apply_config = psb6970_hw_apply,
.reset_switch = psb6970_reset_switch,
};
static int psb6970_config_init(struct phy_device *pdev)
{
struct psb6970_priv *priv;
struct switch_dev *swdev;
int ret;
priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
priv->phy = pdev;
if (pdev->mdio.addr == 0)
printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
pdev->attached_dev->name);
if (pdev->mdio.addr != 0) {
kfree(priv);
return 0;
}
linkmode_zero(pdev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pdev->supported);
linkmode_copy(pdev->advertising, pdev->supported);
mutex_init(&priv->reg_mutex);
priv->read = psb6970_mii_read;
priv->write = psb6970_mii_write;
pdev->priv = priv;
swdev = &priv->dev;
swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
swdev->ops = &psb6970_ops;
swdev->name = "Lantiq PSB6970";
swdev->vlans = PSB6970_MAX_VLANS;
swdev->ports = PSB6970_NUM_PORTS;
if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
kfree(priv);
goto done;
}
ret = psb6970_reset_switch(&priv->dev);
if (ret) {
kfree(priv);
goto done;
}
done:
return ret;
}
static int psb6970_read_status(struct phy_device *phydev)
{
phydev->speed = SPEED_100;
phydev->duplex = DUPLEX_FULL;
phydev->link = 1;
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
return 0;
}
static int psb6970_config_aneg(struct phy_device *phydev)
{
return 0;
}
static int psb6970_probe(struct phy_device *pdev)
{
return 0;
}
static void psb6970_remove(struct phy_device *pdev)
{
struct psb6970_priv *priv = pdev->priv;
if (!priv)
return;
if (pdev->mdio.addr == 0)
unregister_switch(&priv->dev);
kfree(priv);
}
static int psb6970_fixup(struct phy_device *dev)
{
struct mii_bus *bus = dev->mdio.bus;
u16 reg;
/* look for the switch on the bus */
reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
if (reg != PSB6970_CI1_VAL)
return 0;
dev->phy_id = (reg << 16);
dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
return 0;
}
static struct phy_driver psb6970_driver = {
.name = "Lantiq PSB6970",
.phy_id = PSB6970_CI1_VAL << 16,
.phy_id_mask = 0xffff0000,
.features = PHY_BASIC_FEATURES,
.probe = psb6970_probe,
.remove = psb6970_remove,
.config_init = &psb6970_config_init,
.config_aneg = &psb6970_config_aneg,
.read_status = &psb6970_read_status,
};
int __init psb6970_init(void)
{
phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
return phy_driver_register(&psb6970_driver, THIS_MODULE);
}
module_init(psb6970_init);
void __exit psb6970_exit(void)
{
phy_driver_unregister(&psb6970_driver);
}
module_exit(psb6970_exit);
MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
/*
* Realtek RTL8366 SMI interface driver defines
*
* Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef _RTL8366_SMI_H
#define _RTL8366_SMI_H
#include <linux/phy.h>
#include <linux/switch.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
struct rtl8366_smi_ops;
struct rtl8366_vlan_ops;
struct mii_bus;
struct dentry;
struct inode;
struct file;
typedef enum rtl8367b_chip_e {
RTL8367B_CHIP_UNKNOWN,
/* Family B */
RTL8367B_CHIP_RTL8367RB,
RTL8367B_CHIP_RTL8367R_VB, /* chip with exception in extif assignment */
/* Family C */
RTL8367B_CHIP_RTL8367RB_VB,
RTL8367B_CHIP_RTL8367S,
/* Family D */
RTL8367B_CHIP_RTL8367S_VB /* chip with exception in extif assignment */
} rtl8367b_chip_t;
struct rtl8366_mib_counter {
unsigned base;
unsigned offset;
unsigned length;
const char *name;
};
struct rtl8366_smi {
struct device *parent;
unsigned int gpio_sda;
unsigned int gpio_sck;
void (*hw_reset)(struct rtl8366_smi *smi, bool active);
unsigned int clk_delay; /* ns */
u8 cmd_read;
u8 cmd_write;
spinlock_t lock;
struct mii_bus *mii_bus;
int mii_irq[PHY_MAX_ADDR];
struct switch_dev sw_dev;
unsigned int cpu_port;
unsigned int num_ports;
unsigned int num_vlan_mc;
unsigned int num_mib_counters;
struct rtl8366_mib_counter *mib_counters;
struct rtl8366_smi_ops *ops;
int vlan_enabled;
int vlan4k_enabled;
char buf[4096];
struct reset_control *reset;
#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
struct dentry *debugfs_root;
u16 dbg_reg;
u8 dbg_vlan_4k_page;
#endif
u32 phy_id;
rtl8367b_chip_t rtl8367b_chip;
struct mii_bus *ext_mbus;
struct rtl8366_vlan_mc *emu_vlanmc;
};
struct rtl8366_vlan_mc {
u16 vid;
u16 untag;
u16 member;
u8 fid;
u8 priority;
};
struct rtl8366_vlan_4k {
u16 vid;
u16 untag;
u16 member;
u8 fid;
};
struct rtl8366_smi_ops {
int (*detect)(struct rtl8366_smi *smi);
int (*reset_chip)(struct rtl8366_smi *smi);
int (*setup)(struct rtl8366_smi *smi);
int (*mii_read)(struct mii_bus *bus, int addr, int reg);
int (*mii_write)(struct mii_bus *bus, int addr, int reg, u16 val);
int (*get_vlan_mc)(struct rtl8366_smi *smi, u32 index,
struct rtl8366_vlan_mc *vlanmc);
int (*set_vlan_mc)(struct rtl8366_smi *smi, u32 index,
const struct rtl8366_vlan_mc *vlanmc);
int (*get_vlan_4k)(struct rtl8366_smi *smi, u32 vid,
struct rtl8366_vlan_4k *vlan4k);
int (*set_vlan_4k)(struct rtl8366_smi *smi,
const struct rtl8366_vlan_4k *vlan4k);
int (*get_mc_index)(struct rtl8366_smi *smi, int port, int *val);
int (*set_mc_index)(struct rtl8366_smi *smi, int port, int index);
int (*get_mib_counter)(struct rtl8366_smi *smi, int counter,
int port, unsigned long long *val);
int (*is_vlan_valid)(struct rtl8366_smi *smi, unsigned vlan);
int (*enable_vlan)(struct rtl8366_smi *smi, int enable);
int (*enable_vlan4k)(struct rtl8366_smi *smi, int enable);
int (*enable_port)(struct rtl8366_smi *smi, int port, int enable);
};
struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent);
int rtl8366_smi_init(struct rtl8366_smi *smi);
void rtl8366_smi_cleanup(struct rtl8366_smi *smi);
int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data);
int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data);
int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data);
int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data);
#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
int rtl8366_debugfs_open(struct inode *inode, struct file *file);
#endif
static inline struct rtl8366_smi *sw_to_rtl8366_smi(struct switch_dev *sw)
{
return container_of(sw, struct rtl8366_smi, sw_dev);
}
int rtl8366_sw_reset_switch(struct switch_dev *dev);
int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
int rtl8366_sw_get_port_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val);
int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val);
int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
struct switch_port_stats *stats,
int txb_id, int rxb_id);
struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
#endif /* _RTL8366_SMI_H */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,555 @@
/*
* swconfig_led.c: LED trigger support for the switch configuration API
*
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
*/
#ifdef CONFIG_SWCONFIG_LEDS
#include <linux/leds.h>
#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#define SWCONFIG_LED_TIMER_INTERVAL (HZ / 10)
#define SWCONFIG_LED_NUM_PORTS 32
#define SWCONFIG_LED_PORT_SPEED_NA 0x01 /* unknown speed */
#define SWCONFIG_LED_PORT_SPEED_10 0x02 /* 10 Mbps */
#define SWCONFIG_LED_PORT_SPEED_100 0x04 /* 100 Mbps */
#define SWCONFIG_LED_PORT_SPEED_1000 0x08 /* 1000 Mbps */
#define SWCONFIG_LED_PORT_SPEED_ALL (SWCONFIG_LED_PORT_SPEED_NA | \
SWCONFIG_LED_PORT_SPEED_10 | \
SWCONFIG_LED_PORT_SPEED_100 | \
SWCONFIG_LED_PORT_SPEED_1000)
#define SWCONFIG_LED_MODE_LINK 0x01
#define SWCONFIG_LED_MODE_TX 0x02
#define SWCONFIG_LED_MODE_RX 0x04
#define SWCONFIG_LED_MODE_TXRX (SWCONFIG_LED_MODE_TX | \
SWCONFIG_LED_MODE_RX)
#define SWCONFIG_LED_MODE_ALL (SWCONFIG_LED_MODE_LINK | \
SWCONFIG_LED_MODE_TX | \
SWCONFIG_LED_MODE_RX)
struct switch_led_trigger {
struct led_trigger trig;
struct switch_dev *swdev;
struct delayed_work sw_led_work;
u32 port_mask;
u32 port_link;
unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
u8 link_speed[SWCONFIG_LED_NUM_PORTS];
};
struct swconfig_trig_data {
struct led_classdev *led_cdev;
struct switch_dev *swdev;
rwlock_t lock;
u32 port_mask;
bool prev_link;
unsigned long prev_traffic;
enum led_brightness prev_brightness;
u8 mode;
u8 speed_mask;
};
static void
swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
enum led_brightness brightness)
{
led_set_brightness(trig_data->led_cdev, brightness);
trig_data->prev_brightness = brightness;
}
static void
swconfig_trig_update_port_mask(struct led_trigger *trigger)
{
struct list_head *entry;
struct switch_led_trigger *sw_trig;
u32 port_mask;
if (!trigger)
return;
sw_trig = (void *) trigger;
port_mask = 0;
spin_lock(&trigger->leddev_list_lock);
list_for_each(entry, &trigger->led_cdevs) {
struct led_classdev *led_cdev;
struct swconfig_trig_data *trig_data;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
trig_data = led_cdev->trigger_data;
if (trig_data) {
read_lock(&trig_data->lock);
port_mask |= trig_data->port_mask;
read_unlock(&trig_data->lock);
}
}
spin_unlock(&trigger->leddev_list_lock);
sw_trig->port_mask = port_mask;
if (port_mask)
schedule_delayed_work(&sw_trig->sw_led_work,
SWCONFIG_LED_TIMER_INTERVAL);
else
cancel_delayed_work_sync(&sw_trig->sw_led_work);
}
static ssize_t
swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
unsigned long port_mask;
int ret;
bool changed;
ret = kstrtoul(buf, 0, &port_mask);
if (ret)
return ret;
write_lock(&trig_data->lock);
changed = (trig_data->port_mask != port_mask);
trig_data->port_mask = port_mask;
write_unlock(&trig_data->lock);
if (changed) {
if (port_mask == 0)
swconfig_trig_set_brightness(trig_data, LED_OFF);
swconfig_trig_update_port_mask(led_cdev->trigger);
}
return size;
}
static ssize_t
swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
u32 port_mask;
read_lock(&trig_data->lock);
port_mask = trig_data->port_mask;
read_unlock(&trig_data->lock);
sprintf(buf, "%#x\n", port_mask);
return strlen(buf) + 1;
}
static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
swconfig_trig_port_mask_store);
/* speed_mask file handler - display value */
static ssize_t swconfig_trig_speed_mask_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
u8 speed_mask;
read_lock(&trig_data->lock);
speed_mask = trig_data->speed_mask;
read_unlock(&trig_data->lock);
sprintf(buf, "%#x\n", speed_mask);
return strlen(buf) + 1;
}
/* speed_mask file handler - store value */
static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
u8 speed_mask;
int ret;
ret = kstrtou8(buf, 0, &speed_mask);
if (ret)
return ret;
write_lock(&trig_data->lock);
trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
write_unlock(&trig_data->lock);
return size;
}
/* speed_mask special file */
static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
swconfig_trig_speed_mask_store);
static ssize_t swconfig_trig_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
u8 mode;
read_lock(&trig_data->lock);
mode = trig_data->mode;
read_unlock(&trig_data->lock);
if (mode == 0) {
strcpy(buf, "none\n");
} else {
if (mode & SWCONFIG_LED_MODE_LINK)
strcat(buf, "link ");
if (mode & SWCONFIG_LED_MODE_TX)
strcat(buf, "tx ");
if (mode & SWCONFIG_LED_MODE_RX)
strcat(buf, "rx ");
strcat(buf, "\n");
}
return strlen(buf)+1;
}
static ssize_t swconfig_trig_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
char copybuf[128];
int new_mode = -1;
char *p, *token;
/* take a copy since we don't want to trash the inbound buffer when using strsep */
strncpy(copybuf, buf, sizeof(copybuf));
copybuf[sizeof(copybuf) - 1] = 0;
p = copybuf;
while ((token = strsep(&p, " \t\n")) != NULL) {
if (!*token)
continue;
if (new_mode < 0)
new_mode = 0;
if (!strcmp(token, "none"))
new_mode = 0;
else if (!strcmp(token, "tx"))
new_mode |= SWCONFIG_LED_MODE_TX;
else if (!strcmp(token, "rx"))
new_mode |= SWCONFIG_LED_MODE_RX;
else if (!strcmp(token, "link"))
new_mode |= SWCONFIG_LED_MODE_LINK;
else
return -EINVAL;
}
if (new_mode < 0)
return -EINVAL;
write_lock(&trig_data->lock);
trig_data->mode = (u8)new_mode;
write_unlock(&trig_data->lock);
return size;
}
/* mode special file */
static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
swconfig_trig_mode_store);
static int
swconfig_trig_activate(struct led_classdev *led_cdev)
{
struct switch_led_trigger *sw_trig;
struct swconfig_trig_data *trig_data;
int err;
trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
if (!trig_data)
return -ENOMEM;
sw_trig = (void *) led_cdev->trigger;
rwlock_init(&trig_data->lock);
trig_data->led_cdev = led_cdev;
trig_data->swdev = sw_trig->swdev;
trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
trig_data->mode = SWCONFIG_LED_MODE_ALL;
led_cdev->trigger_data = trig_data;
err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
if (err)
goto err_free;
err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
if (err)
goto err_dev_free;
err = device_create_file(led_cdev->dev, &dev_attr_mode);
if (err)
goto err_mode_free;
return 0;
err_mode_free:
device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
err_dev_free:
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
err_free:
led_cdev->trigger_data = NULL;
kfree(trig_data);
return err;
}
static void
swconfig_trig_deactivate(struct led_classdev *led_cdev)
{
struct swconfig_trig_data *trig_data;
swconfig_trig_update_port_mask(led_cdev->trigger);
trig_data = (void *) led_cdev->trigger_data;
if (trig_data) {
device_remove_file(led_cdev->dev, &dev_attr_port_mask);
device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
device_remove_file(led_cdev->dev, &dev_attr_mode);
kfree(trig_data);
}
}
/*
* link off -> led off (can't be any other reason to turn it on)
* link on:
* mode link: led on by default only if speed matches, else off
* mode txrx: blink only if speed matches, else off
*/
static void
swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
struct led_classdev *led_cdev)
{
struct swconfig_trig_data *trig_data;
u32 port_mask;
bool link;
u8 speed_mask, mode;
enum led_brightness led_base, led_blink;
trig_data = led_cdev->trigger_data;
if (!trig_data)
return;
read_lock(&trig_data->lock);
port_mask = trig_data->port_mask;
speed_mask = trig_data->speed_mask;
mode = trig_data->mode;
read_unlock(&trig_data->lock);
link = !!(sw_trig->port_link & port_mask);
if (!link) {
if (trig_data->prev_brightness != LED_OFF)
swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
}
else {
unsigned long traffic;
int speedok; /* link speed flag */
int i;
led_base = LED_FULL;
led_blink = LED_OFF;
traffic = 0;
speedok = 0;
for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
if (port_mask & (1 << i)) {
if (sw_trig->link_speed[i] & speed_mask) {
traffic += ((mode & SWCONFIG_LED_MODE_TX) ?
sw_trig->port_tx_traffic[i] : 0) +
((mode & SWCONFIG_LED_MODE_RX) ?
sw_trig->port_rx_traffic[i] : 0);
speedok = 1;
}
}
}
if (speedok) {
/* At least one port speed matches speed_mask */
if (!(mode & SWCONFIG_LED_MODE_LINK)) {
led_base = LED_OFF;
led_blink = LED_FULL;
}
if (trig_data->prev_brightness != led_base)
swconfig_trig_set_brightness(trig_data,
led_base);
else if (traffic != trig_data->prev_traffic)
swconfig_trig_set_brightness(trig_data,
led_blink);
} else if (trig_data->prev_brightness != LED_OFF)
swconfig_trig_set_brightness(trig_data, LED_OFF);
trig_data->prev_traffic = traffic;
}
trig_data->prev_link = link;
}
static void
swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
{
struct list_head *entry;
struct led_trigger *trigger;
trigger = &sw_trig->trig;
spin_lock(&trigger->leddev_list_lock);
list_for_each(entry, &trigger->led_cdevs) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
swconfig_trig_led_event(sw_trig, led_cdev);
}
spin_unlock(&trigger->leddev_list_lock);
}
static void
swconfig_led_work_func(struct work_struct *work)
{
struct switch_led_trigger *sw_trig;
struct switch_dev *swdev;
u32 port_mask;
u32 link;
int i;
sw_trig = container_of(work, struct switch_led_trigger,
sw_led_work.work);
port_mask = sw_trig->port_mask;
swdev = sw_trig->swdev;
link = 0;
for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
u32 port_bit;
sw_trig->link_speed[i] = 0;
port_bit = BIT(i);
if ((port_mask & port_bit) == 0)
continue;
if (swdev->ops->get_port_link) {
struct switch_port_link port_link;
memset(&port_link, '\0', sizeof(port_link));
swdev->ops->get_port_link(swdev, i, &port_link);
if (port_link.link) {
link |= port_bit;
switch (port_link.speed) {
case SWITCH_PORT_SPEED_UNKNOWN:
sw_trig->link_speed[i] =
SWCONFIG_LED_PORT_SPEED_NA;
break;
case SWITCH_PORT_SPEED_10:
sw_trig->link_speed[i] =
SWCONFIG_LED_PORT_SPEED_10;
break;
case SWITCH_PORT_SPEED_100:
sw_trig->link_speed[i] =
SWCONFIG_LED_PORT_SPEED_100;
break;
case SWITCH_PORT_SPEED_1000:
sw_trig->link_speed[i] =
SWCONFIG_LED_PORT_SPEED_1000;
break;
}
}
}
if (swdev->ops->get_port_stats) {
struct switch_port_stats port_stats;
memset(&port_stats, '\0', sizeof(port_stats));
swdev->ops->get_port_stats(swdev, i, &port_stats);
sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
}
}
sw_trig->port_link = link;
swconfig_trig_update_leds(sw_trig);
schedule_delayed_work(&sw_trig->sw_led_work,
SWCONFIG_LED_TIMER_INTERVAL);
}
static int
swconfig_create_led_trigger(struct switch_dev *swdev)
{
struct switch_led_trigger *sw_trig;
int err;
if (!swdev->ops->get_port_link)
return 0;
sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL);
if (!sw_trig)
return -ENOMEM;
sw_trig->swdev = swdev;
sw_trig->trig.name = swdev->devname;
sw_trig->trig.activate = swconfig_trig_activate;
sw_trig->trig.deactivate = swconfig_trig_deactivate;
INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func);
err = led_trigger_register(&sw_trig->trig);
if (err)
goto err_free;
swdev->led_trigger = sw_trig;
return 0;
err_free:
kfree(sw_trig);
return err;
}
static void
swconfig_destroy_led_trigger(struct switch_dev *swdev)
{
struct switch_led_trigger *sw_trig;
sw_trig = swdev->led_trigger;
if (sw_trig) {
cancel_delayed_work_sync(&sw_trig->sw_led_work);
led_trigger_unregister(&sw_trig->trig);
kfree(sw_trig);
}
}
#else /* SWCONFIG_LEDS */
static inline int
swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; }
static inline void
swconfig_destroy_led_trigger(struct switch_dev *swdev) { }
#endif /* CONFIG_SWCONFIG_LEDS */