Commit 0c907754 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-phy-split-the-mscc-driver'

Antoine Tenart says:

====================
net: phy: split the mscc driver

This is a proposal to split the MSCC PHY driver, as its code base grew a
lot lately (it's already 3800+ lines). It also supports features
requiring a lot of code (MACsec), which would gain in being split from
the driver core, for readability and maintenance. This is also done as
other features should be coming later, which will also need lots of code
addition.

This series shouldn't change the way the driver works.

I checked, and there were no patch pending on this driver. This change
was done on top of all the modifications done on this driver in net-next.

Since v2:
  - Defined inline functions as static inline.
  - Fixed a locking issue reported by Kbuild.

Since v1:
  - Moved more definitions into the mscc_macsec.h header.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3d572b23 0b92f897
...@@ -89,7 +89,7 @@ obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o ...@@ -89,7 +89,7 @@ obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o
obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MICREL_PHY) += micrel.o
obj-$(CONFIG_MICROCHIP_PHY) += microchip.o obj-$(CONFIG_MICROCHIP_PHY) += microchip.o
obj-$(CONFIG_MICROCHIP_T1_PHY) += microchip_t1.o obj-$(CONFIG_MICROCHIP_T1_PHY) += microchip_t1.o
obj-$(CONFIG_MICROSEMI_PHY) += mscc.o obj-$(CONFIG_MICROSEMI_PHY) += mscc/
obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o
......
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for MSCC networking PHY driver
obj-$(CONFIG_MICROSEMI_PHY) := mscc.o
mscc-objs := mscc_main.o
ifdef CONFIG_MACSEC
mscc-objs += mscc_macsec.o
endif
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Driver for Microsemi VSC85xx PHYs
*
* Copyright (c) 2016 Microsemi Corporation
*/
#ifndef _MSCC_PHY_H_
#define _MSCC_PHY_H_
#if IS_ENABLED(CONFIG_MACSEC)
#include "mscc_macsec.h"
#endif
enum rgmii_rx_clock_delay {
RGMII_RX_CLK_DELAY_0_2_NS = 0,
RGMII_RX_CLK_DELAY_0_8_NS = 1,
RGMII_RX_CLK_DELAY_1_1_NS = 2,
RGMII_RX_CLK_DELAY_1_7_NS = 3,
RGMII_RX_CLK_DELAY_2_0_NS = 4,
RGMII_RX_CLK_DELAY_2_3_NS = 5,
RGMII_RX_CLK_DELAY_2_6_NS = 6,
RGMII_RX_CLK_DELAY_3_4_NS = 7
};
/* Microsemi VSC85xx PHY registers */
/* IEEE 802. Std Registers */
#define MSCC_PHY_BYPASS_CONTROL 18
#define DISABLE_HP_AUTO_MDIX_MASK 0x0080
#define DISABLE_PAIR_SWAP_CORR_MASK 0x0020
#define DISABLE_POLARITY_CORR_MASK 0x0010
#define PARALLEL_DET_IGNORE_ADVERTISED 0x0008
#define MSCC_PHY_EXT_CNTL_STATUS 22
#define SMI_BROADCAST_WR_EN 0x0001
#define MSCC_PHY_ERR_RX_CNT 19
#define MSCC_PHY_ERR_FALSE_CARRIER_CNT 20
#define MSCC_PHY_ERR_LINK_DISCONNECT_CNT 21
#define ERR_CNT_MASK GENMASK(7, 0)
#define MSCC_PHY_EXT_PHY_CNTL_1 23
#define MAC_IF_SELECTION_MASK 0x1800
#define MAC_IF_SELECTION_GMII 0
#define MAC_IF_SELECTION_RMII 1
#define MAC_IF_SELECTION_RGMII 2
#define MAC_IF_SELECTION_POS 11
#define VSC8584_MAC_IF_SELECTION_MASK 0x1000
#define VSC8584_MAC_IF_SELECTION_SGMII 0
#define VSC8584_MAC_IF_SELECTION_1000BASEX 1
#define VSC8584_MAC_IF_SELECTION_POS 12
#define FAR_END_LOOPBACK_MODE_MASK 0x0008
#define MEDIA_OP_MODE_MASK 0x0700
#define MEDIA_OP_MODE_COPPER 0
#define MEDIA_OP_MODE_SERDES 1
#define MEDIA_OP_MODE_1000BASEX 2
#define MEDIA_OP_MODE_100BASEFX 3
#define MEDIA_OP_MODE_AMS_COPPER_SERDES 5
#define MEDIA_OP_MODE_AMS_COPPER_1000BASEX 6
#define MEDIA_OP_MODE_AMS_COPPER_100BASEFX 7
#define MEDIA_OP_MODE_POS 8
#define MSCC_PHY_EXT_PHY_CNTL_2 24
#define MII_VSC85XX_INT_MASK 25
#define MII_VSC85XX_INT_MASK_MDINT BIT(15)
#define MII_VSC85XX_INT_MASK_LINK_CHG BIT(13)
#define MII_VSC85XX_INT_MASK_WOL BIT(6)
#define MII_VSC85XX_INT_MASK_EXT BIT(5)
#define MII_VSC85XX_INT_STATUS 26
#define MII_VSC85XX_INT_MASK_MASK (MII_VSC85XX_INT_MASK_MDINT | \
MII_VSC85XX_INT_MASK_LINK_CHG | \
MII_VSC85XX_INT_MASK_EXT)
#define MSCC_PHY_WOL_MAC_CONTROL 27
#define EDGE_RATE_CNTL_POS 5
#define EDGE_RATE_CNTL_MASK 0x00E0
#define MSCC_PHY_DEV_AUX_CNTL 28
#define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000
#define MSCC_PHY_LED_MODE_SEL 29
#define LED_MODE_SEL_POS(x) ((x) * 4)
#define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x))
#define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x))
#define MSCC_EXT_PAGE_CSR_CNTL_17 17
#define MSCC_EXT_PAGE_CSR_CNTL_18 18
#define MSCC_EXT_PAGE_CSR_CNTL_19 19
#define MSCC_PHY_CSR_CNTL_19_REG_ADDR(x) (x)
#define MSCC_PHY_CSR_CNTL_19_TARGET(x) ((x) << 12)
#define MSCC_PHY_CSR_CNTL_19_READ BIT(14)
#define MSCC_PHY_CSR_CNTL_19_CMD BIT(15)
#define MSCC_EXT_PAGE_CSR_CNTL_20 20
#define MSCC_PHY_CSR_CNTL_20_TARGET(x) (x)
#define PHY_MCB_TARGET 0x07
#define PHY_MCB_S6G_WRITE BIT(31)
#define PHY_MCB_S6G_READ BIT(30)
#define PHY_S6G_PLL5G_CFG0 0x06
#define PHY_S6G_LCPLL_CFG 0x11
#define PHY_S6G_PLL_CFG 0x2b
#define PHY_S6G_COMMON_CFG 0x2c
#define PHY_S6G_GPC_CFG 0x2e
#define PHY_S6G_MISC_CFG 0x3b
#define PHY_MCB_S6G_CFG 0x3f
#define PHY_S6G_DFT_CFG2 0x3e
#define PHY_S6G_PLL_STATUS 0x31
#define PHY_S6G_IB_STATUS0 0x2f
#define PHY_S6G_SYS_RST_POS 31
#define PHY_S6G_ENA_LANE_POS 18
#define PHY_S6G_ENA_LOOP_POS 8
#define PHY_S6G_QRATE_POS 6
#define PHY_S6G_IF_MODE_POS 4
#define PHY_S6G_PLL_ENA_OFFS_POS 21
#define PHY_S6G_PLL_FSM_CTRL_DATA_POS 8
#define PHY_S6G_PLL_FSM_ENA_POS 7
#define MSCC_EXT_PAGE_ACCESS 31
#define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
#define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */
#define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
#define MSCC_PHY_PAGE_EXTENDED_3 0x0003 /* Extended reg - page 3 */
#define MSCC_PHY_PAGE_EXTENDED_4 0x0004 /* Extended reg - page 4 */
#define MSCC_PHY_PAGE_CSR_CNTL MSCC_PHY_PAGE_EXTENDED_4
#define MSCC_PHY_PAGE_MACSEC MSCC_PHY_PAGE_EXTENDED_4
/* Extended reg - GPIO; this is a bank of registers that are shared for all PHYs
* in the same package.
*/
#define MSCC_PHY_PAGE_EXTENDED_GPIO 0x0010 /* Extended reg - GPIO */
#define MSCC_PHY_PAGE_TEST 0x2a30 /* Test reg */
#define MSCC_PHY_PAGE_TR 0x52b5 /* Token ring registers */
/* Extended Page 1 Registers */
#define MSCC_PHY_CU_MEDIA_CRC_VALID_CNT 18
#define VALID_CRC_CNT_CRC_MASK GENMASK(13, 0)
#define MSCC_PHY_EXT_MODE_CNTL 19
#define FORCE_MDI_CROSSOVER_MASK 0x000C
#define FORCE_MDI_CROSSOVER_MDIX 0x000C
#define FORCE_MDI_CROSSOVER_MDI 0x0008
#define MSCC_PHY_ACTIPHY_CNTL 20
#define PHY_ADDR_REVERSED 0x0200
#define DOWNSHIFT_CNTL_MASK 0x001C
#define DOWNSHIFT_EN 0x0010
#define DOWNSHIFT_CNTL_POS 2
#define MSCC_PHY_EXT_PHY_CNTL_4 23
#define PHY_CNTL_4_ADDR_POS 11
#define MSCC_PHY_VERIPHY_CNTL_2 25
#define MSCC_PHY_VERIPHY_CNTL_3 26
/* Extended Page 2 Registers */
#define MSCC_PHY_CU_PMD_TX_CNTL 16
#define MSCC_PHY_RGMII_CNTL 20
#define RGMII_RX_CLK_DELAY_MASK 0x0070
#define RGMII_RX_CLK_DELAY_POS 4
#define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
#define MSCC_PHY_WOL_MID_MAC_ADDR 22
#define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
#define MSCC_PHY_WOL_LOWER_PASSWD 24
#define MSCC_PHY_WOL_MID_PASSWD 25
#define MSCC_PHY_WOL_UPPER_PASSWD 26
#define MSCC_PHY_WOL_MAC_CONTROL 27
#define SECURE_ON_ENABLE 0x8000
#define SECURE_ON_PASSWD_LEN_4 0x4000
#define MSCC_PHY_EXTENDED_INT 28
#define MSCC_PHY_EXTENDED_INT_MS_EGR BIT(9)
/* Extended Page 3 Registers */
#define MSCC_PHY_SERDES_TX_VALID_CNT 21
#define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22
#define MSCC_PHY_SERDES_RX_VALID_CNT 28
#define MSCC_PHY_SERDES_RX_CRC_ERR_CNT 29
/* Extended page GPIO Registers */
#define MSCC_DW8051_CNTL_STATUS 0
#define MICRO_NSOFT_RESET 0x8000
#define RUN_FROM_INT_ROM 0x4000
#define AUTOINC_ADDR 0x2000
#define PATCH_RAM_CLK 0x1000
#define MICRO_PATCH_EN 0x0080
#define DW8051_CLK_EN 0x0010
#define MICRO_CLK_EN 0x0008
#define MICRO_CLK_DIVIDE(x) ((x) >> 1)
#define MSCC_DW8051_VLD_MASK 0xf1ff
/* x Address in range 1-4 */
#define MSCC_TRAP_ROM_ADDR(x) ((x) * 2 + 1)
#define MSCC_PATCH_RAM_ADDR(x) (((x) + 1) * 2)
#define MSCC_INT_MEM_ADDR 11
#define MSCC_INT_MEM_CNTL 12
#define READ_SFR 0x6000
#define READ_PRAM 0x4000
#define READ_ROM 0x2000
#define READ_RAM 0x0000
#define INT_MEM_WRITE_EN 0x1000
#define EN_PATCH_RAM_TRAP_ADDR(x) (0x0100 << ((x) - 1))
#define INT_MEM_DATA_M 0x00ff
#define INT_MEM_DATA(x) (INT_MEM_DATA_M & (x))
#define MSCC_PHY_PROC_CMD 18
#define PROC_CMD_NCOMPLETED 0x8000
#define PROC_CMD_FAILED 0x4000
#define PROC_CMD_SGMII_PORT(x) ((x) << 8)
#define PROC_CMD_FIBER_PORT(x) (0x0100 << (x) % 4)
#define PROC_CMD_QSGMII_PORT 0x0c00
#define PROC_CMD_RST_CONF_PORT 0x0080
#define PROC_CMD_RECONF_PORT 0x0000
#define PROC_CMD_READ_MOD_WRITE_PORT 0x0040
#define PROC_CMD_WRITE 0x0040
#define PROC_CMD_READ 0x0000
#define PROC_CMD_FIBER_DISABLE 0x0020
#define PROC_CMD_FIBER_100BASE_FX 0x0010
#define PROC_CMD_FIBER_1000BASE_X 0x0000
#define PROC_CMD_SGMII_MAC 0x0030
#define PROC_CMD_QSGMII_MAC 0x0020
#define PROC_CMD_NO_MAC_CONF 0x0000
#define PROC_CMD_1588_DEFAULT_INIT 0x0010
#define PROC_CMD_NOP 0x000f
#define PROC_CMD_PHY_INIT 0x000a
#define PROC_CMD_CRC16 0x0008
#define PROC_CMD_FIBER_MEDIA_CONF 0x0001
#define PROC_CMD_MCB_ACCESS_MAC_CONF 0x0000
#define PROC_CMD_NCOMPLETED_TIMEOUT_MS 500
#define MSCC_PHY_MAC_CFG_FASTLINK 19
#define MAC_CFG_MASK 0xc000
#define MAC_CFG_SGMII 0x0000
#define MAC_CFG_QSGMII 0x4000
/* Test page Registers */
#define MSCC_PHY_TEST_PAGE_5 5
#define MSCC_PHY_TEST_PAGE_8 8
#define MSCC_PHY_TEST_PAGE_9 9
#define MSCC_PHY_TEST_PAGE_20 20
#define MSCC_PHY_TEST_PAGE_24 24
/* Token ring page Registers */
#define MSCC_PHY_TR_CNTL 16
#define TR_WRITE 0x8000
#define TR_ADDR(x) (0x7fff & (x))
#define MSCC_PHY_TR_LSB 17
#define MSCC_PHY_TR_MSB 18
/* Microsemi PHY ID's
* Code assumes lowest nibble is 0
*/
#define PHY_ID_VSC8504 0x000704c0
#define PHY_ID_VSC8514 0x00070670
#define PHY_ID_VSC8530 0x00070560
#define PHY_ID_VSC8531 0x00070570
#define PHY_ID_VSC8540 0x00070760
#define PHY_ID_VSC8541 0x00070770
#define PHY_ID_VSC8552 0x000704e0
#define PHY_ID_VSC856X 0x000707e0
#define PHY_ID_VSC8572 0x000704d0
#define PHY_ID_VSC8574 0x000704a0
#define PHY_ID_VSC8575 0x000707d0
#define PHY_ID_VSC8582 0x000707b0
#define PHY_ID_VSC8584 0x000707c0
#define MSCC_VDDMAC_1500 1500
#define MSCC_VDDMAC_1800 1800
#define MSCC_VDDMAC_2500 2500
#define MSCC_VDDMAC_3300 3300
#define DOWNSHIFT_COUNT_MAX 5
#define MAX_LEDS 4
#define VSC8584_SUPP_LED_MODES (BIT(VSC8531_LINK_ACTIVITY) | \
BIT(VSC8531_LINK_1000_ACTIVITY) | \
BIT(VSC8531_LINK_100_ACTIVITY) | \
BIT(VSC8531_LINK_10_ACTIVITY) | \
BIT(VSC8531_LINK_100_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_100_ACTIVITY) | \
BIT(VSC8584_LINK_100FX_1000X_ACTIVITY) | \
BIT(VSC8531_DUPLEX_COLLISION) | \
BIT(VSC8531_COLLISION) | \
BIT(VSC8531_ACTIVITY) | \
BIT(VSC8584_100FX_1000X_ACTIVITY) | \
BIT(VSC8531_AUTONEG_FAULT) | \
BIT(VSC8531_SERIAL_MODE) | \
BIT(VSC8531_FORCE_LED_OFF) | \
BIT(VSC8531_FORCE_LED_ON))
#define VSC85XX_SUPP_LED_MODES (BIT(VSC8531_LINK_ACTIVITY) | \
BIT(VSC8531_LINK_1000_ACTIVITY) | \
BIT(VSC8531_LINK_100_ACTIVITY) | \
BIT(VSC8531_LINK_10_ACTIVITY) | \
BIT(VSC8531_LINK_100_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_100_ACTIVITY) | \
BIT(VSC8531_DUPLEX_COLLISION) | \
BIT(VSC8531_COLLISION) | \
BIT(VSC8531_ACTIVITY) | \
BIT(VSC8531_AUTONEG_FAULT) | \
BIT(VSC8531_SERIAL_MODE) | \
BIT(VSC8531_FORCE_LED_OFF) | \
BIT(VSC8531_FORCE_LED_ON))
#define MSCC_VSC8584_REVB_INT8051_FW "microchip/mscc_vsc8584_revb_int8051_fb48.bin"
#define MSCC_VSC8584_REVB_INT8051_FW_START_ADDR 0xe800
#define MSCC_VSC8584_REVB_INT8051_FW_CRC 0xfb48
#define MSCC_VSC8574_REVB_INT8051_FW "microchip/mscc_vsc8574_revb_int8051_29e8.bin"
#define MSCC_VSC8574_REVB_INT8051_FW_START_ADDR 0x4000
#define MSCC_VSC8574_REVB_INT8051_FW_CRC 0x29e8
#define VSC8584_REVB 0x0001
#define MSCC_DEV_REV_MASK GENMASK(3, 0)
struct reg_val {
u16 reg;
u32 val;
};
struct vsc85xx_hw_stat {
const char *string;
u8 reg;
u16 page;
u16 mask;
};
struct vsc8531_private {
int rate_magic;
u16 supp_led_modes;
u32 leds_mode[MAX_LEDS];
u8 nleds;
const struct vsc85xx_hw_stat *hw_stats;
u64 *stats;
int nstats;
bool pkg_init;
/* For multiple port PHYs; the MDIO address of the base PHY in the
* package.
*/
unsigned int base_addr;
#if IS_ENABLED(CONFIG_MACSEC)
/* MACsec fields:
* - One SecY per device (enforced at the s/w implementation level)
* - macsec_flows: list of h/w flows
* - ingr_flows: bitmap of ingress flows
* - egr_flows: bitmap of egress flows
*/
struct macsec_secy *secy;
struct list_head macsec_flows;
unsigned long ingr_flows;
unsigned long egr_flows;
#endif
};
#ifdef CONFIG_OF_MDIO
struct vsc8531_edge_rate_table {
u32 vddmac;
u32 slowdown[8];
};
#endif /* CONFIG_OF_MDIO */
#if IS_ENABLED(CONFIG_MACSEC)
int vsc8584_macsec_init(struct phy_device *phydev);
void vsc8584_handle_macsec_interrupt(struct phy_device *phydev);
void vsc8584_config_macsec_intr(struct phy_device *phydev);
#else
static inline int vsc8584_macsec_init(struct phy_device *phydev)
{
return 0;
}
static inline void vsc8584_handle_macsec_interrupt(struct phy_device *phydev)
{
}
static inline void vsc8584_config_macsec_intr(struct phy_device *phydev)
{
}
#endif
#endif /* _MSCC_PHY_H_ */
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ /* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/* /*
* Microsemi Ocelot Switch driver * Driver for Microsemi VSC85xx PHYs
* *
* Copyright (C) 2019 Microsemi Corporation * Copyright (C) 2019 Microsemi Corporation
*/ */
#ifndef _MSCC_OCELOT_FC_BUFFER_H_ #ifndef _MSCC_PHY_FC_BUFFER_H_
#define _MSCC_OCELOT_FC_BUFFER_H_ #define _MSCC_PHY_FC_BUFFER_H_
#define MSCC_FCBUF_ENA_CFG 0x00 #define MSCC_FCBUF_ENA_CFG 0x00
#define MSCC_FCBUF_MODE_CFG 0x01 #define MSCC_FCBUF_MODE_CFG 0x01
...@@ -61,4 +61,4 @@ ...@@ -61,4 +61,4 @@
#define MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH(x) ((x) << 16) #define MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH(x) ((x) << 16)
#define MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH_M GENMASK(31, 16) #define MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH_M GENMASK(31, 16)
#endif #endif /* _MSCC_PHY_FC_BUFFER_H_ */
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ /* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/* /*
* Microsemi Ocelot Switch driver * Driver for Microsemi VSC85xx PHYs
* *
* Copyright (c) 2017 Microsemi Corporation * Copyright (c) 2017 Microsemi Corporation
*/ */
#ifndef _MSCC_OCELOT_LINE_MAC_H_ #ifndef _MSCC_PHY_LINE_MAC_H_
#define _MSCC_OCELOT_LINE_MAC_H_ #define _MSCC_PHY_LINE_MAC_H_
#define MSCC_MAC_CFG_ENA_CFG 0x00 #define MSCC_MAC_CFG_ENA_CFG 0x00
#define MSCC_MAC_CFG_MODE_CFG 0x01 #define MSCC_MAC_CFG_MODE_CFG 0x01
...@@ -156,4 +156,4 @@ ...@@ -156,4 +156,4 @@
#define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE(x) (x) #define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE(x) (x)
#define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE_M GENMASK(2, 0) #define MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE_M GENMASK(2, 0)
#endif /* _MSCC_OCELOT_LINE_MAC_H_ */ #endif /* _MSCC_PHY_LINE_MAC_H_ */
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Driver for Microsemi VSC85xx PHYs
*
* Author: Nagaraju Lakkaraju
* License: Dual MIT/GPL
* Copyright (c) 2016 Microsemi Corporation
*/
#include <linux/phy.h>
#include <dt-bindings/net/mscc-phy-vsc8531.h>
#include <crypto/skcipher.h>
#include <net/macsec.h>
#include "mscc.h"
#include "mscc_mac.h"
#include "mscc_macsec.h"
#include "mscc_fc_buffer.h"
static u32 vsc8584_macsec_phy_read(struct phy_device *phydev,
enum macsec_bank bank, u32 reg)
{
u32 val, val_l = 0, val_h = 0;
unsigned long deadline;
int rc;
rc = phy_select_page(phydev, MSCC_PHY_PAGE_MACSEC);
if (rc < 0)
goto failed;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_20,
MSCC_PHY_MACSEC_20_TARGET(bank >> 2));
if (bank >> 2 == 0x1)
/* non-MACsec access */
bank &= 0x3;
else
bank = 0;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_19,
MSCC_PHY_MACSEC_19_CMD | MSCC_PHY_MACSEC_19_READ |
MSCC_PHY_MACSEC_19_REG_ADDR(reg) |
MSCC_PHY_MACSEC_19_TARGET(bank));
deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS);
do {
val = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_19);
} while (time_before(jiffies, deadline) && !(val & MSCC_PHY_MACSEC_19_CMD));
val_l = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_17);
val_h = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_18);
failed:
phy_restore_page(phydev, rc, rc);
return (val_h << 16) | val_l;
}
static void vsc8584_macsec_phy_write(struct phy_device *phydev,
enum macsec_bank bank, u32 reg, u32 val)
{
unsigned long deadline;
int rc;
rc = phy_select_page(phydev, MSCC_PHY_PAGE_MACSEC);
if (rc < 0)
goto failed;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_20,
MSCC_PHY_MACSEC_20_TARGET(bank >> 2));
if ((bank >> 2 == 0x1) || (bank >> 2 == 0x3))
bank &= 0x3;
else
/* MACsec access */
bank = 0;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_17, (u16)val);
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_18, (u16)(val >> 16));
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_19,
MSCC_PHY_MACSEC_19_CMD | MSCC_PHY_MACSEC_19_REG_ADDR(reg) |
MSCC_PHY_MACSEC_19_TARGET(bank));
deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS);
do {
val = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_19);
} while (time_before(jiffies, deadline) && !(val & MSCC_PHY_MACSEC_19_CMD));
failed:
phy_restore_page(phydev, rc, rc);
}
static void vsc8584_macsec_classification(struct phy_device *phydev,
enum macsec_bank bank)
{
/* enable VLAN tag parsing */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_CP_TAG,
MSCC_MS_SAM_CP_TAG_PARSE_STAG |
MSCC_MS_SAM_CP_TAG_PARSE_QTAG |
MSCC_MS_SAM_CP_TAG_PARSE_QINQ);
}
static void vsc8584_macsec_flow_default_action(struct phy_device *phydev,
enum macsec_bank bank,
bool block)
{
u32 port = (bank == MACSEC_INGR) ?
MSCC_MS_PORT_UNCONTROLLED : MSCC_MS_PORT_COMMON;
u32 action = MSCC_MS_FLOW_BYPASS;
if (block)
action = MSCC_MS_FLOW_DROP;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_NM_FLOW_NCP,
/* MACsec untagged */
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DEST_PORT(port) |
/* MACsec tagged */
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DEST_PORT(port) |
/* Bad tag */
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DEST_PORT(port) |
/* Kay tag */
MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_KAY_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_KAY_DEST_PORT(port));
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_NM_FLOW_CP,
/* MACsec untagged */
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DEST_PORT(port) |
/* MACsec tagged */
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DEST_PORT(port) |
/* Bad tag */
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DEST_PORT(port) |
/* Kay tag */
MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_KAY_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_KAY_DEST_PORT(port));
}
static void vsc8584_macsec_integrity_checks(struct phy_device *phydev,
enum macsec_bank bank)
{
u32 val;
if (bank != MACSEC_INGR)
return;
/* Set default rules to pass unmatched frames */
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MS_PARAMS2_IG_CC_CONTROL);
val |= MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_CTRL_ACT |
MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_ACT;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PARAMS2_IG_CC_CONTROL,
val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PARAMS2_IG_CP_TAG,
MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_STAG |
MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QTAG |
MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QINQ);
}
static void vsc8584_macsec_block_init(struct phy_device *phydev,
enum macsec_bank bank)
{
u32 val;
int i;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG,
MSCC_MS_ENA_CFG_SW_RST |
MSCC_MS_ENA_CFG_MACSEC_BYPASS_ENA);
/* Set the MACsec block out of s/w reset and enable clocks */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG,
MSCC_MS_ENA_CFG_CLK_ENA);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_STATUS_CONTEXT_CTRL,
bank == MACSEC_INGR ? 0xe5880214 : 0xe5880218);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_MISC_CONTROL,
MSCC_MS_MISC_CONTROL_MC_LATENCY_FIX(bank == MACSEC_INGR ? 57 : 40) |
MSCC_MS_MISC_CONTROL_XFORM_REC_SIZE(bank == MACSEC_INGR ? 1 : 2));
/* Clear the counters */
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_COUNT_CONTROL);
val |= MSCC_MS_COUNT_CONTROL_AUTO_CNTR_RESET;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_COUNT_CONTROL, val);
/* Enable octet increment mode */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PP_CTRL,
MSCC_MS_PP_CTRL_MACSEC_OCTET_INCR_MODE);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_BLOCK_CTX_UPDATE, 0x3);
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_COUNT_CONTROL);
val |= MSCC_MS_COUNT_CONTROL_RESET_ALL;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_COUNT_CONTROL, val);
/* Set the MTU */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_NON_VLAN_MTU_CHECK,
MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMPARE(32761) |
MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMP_DROP);
for (i = 0; i < 8; i++)
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_VLAN_MTU_CHECK(i),
MSCC_MS_VLAN_MTU_CHECK_MTU_COMPARE(32761) |
MSCC_MS_VLAN_MTU_CHECK_MTU_COMP_DROP);
if (bank == MACSEC_EGR) {
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_INTR_CTRL_STATUS);
val &= ~MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE_M;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_INTR_CTRL_STATUS, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_FC_CFG,
MSCC_MS_FC_CFG_FCBUF_ENA |
MSCC_MS_FC_CFG_LOW_THRESH(0x1) |
MSCC_MS_FC_CFG_HIGH_THRESH(0x4) |
MSCC_MS_FC_CFG_LOW_BYTES_VAL(0x4) |
MSCC_MS_FC_CFG_HIGH_BYTES_VAL(0x6));
}
vsc8584_macsec_classification(phydev, bank);
vsc8584_macsec_flow_default_action(phydev, bank, false);
vsc8584_macsec_integrity_checks(phydev, bank);
/* Enable the MACsec block */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG,
MSCC_MS_ENA_CFG_CLK_ENA |
MSCC_MS_ENA_CFG_MACSEC_ENA |
MSCC_MS_ENA_CFG_MACSEC_SPEED_MODE(0x5));
}
static void vsc8584_macsec_mac_init(struct phy_device *phydev,
enum macsec_bank bank)
{
u32 val;
int i;
/* Clear host & line stats */
for (i = 0; i < 36; i++)
vsc8584_macsec_phy_write(phydev, bank, 0x1c + i, 0);
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL);
val &= ~MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE_M;
val |= MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE(2) |
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_VALUE(0xffff);
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL, val);
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2);
val |= 0xffff;
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2, val);
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL);
if (bank == HOST_MAC)
val |= MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_TIMER_ENA |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA;
else
val |= MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_REACT_ENA |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_MODE |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_EARLY_PAUSE_DETECT_ENA;
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_PKTINF_CFG,
MSCC_MAC_CFG_PKTINF_CFG_STRIP_FCS_ENA |
MSCC_MAC_CFG_PKTINF_CFG_INSERT_FCS_ENA |
MSCC_MAC_CFG_PKTINF_CFG_LPI_RELAY_ENA |
MSCC_MAC_CFG_PKTINF_CFG_STRIP_PREAMBLE_ENA |
MSCC_MAC_CFG_PKTINF_CFG_INSERT_PREAMBLE_ENA |
(bank == HOST_MAC ?
MSCC_MAC_CFG_PKTINF_CFG_ENABLE_TX_PADDING : 0));
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MODE_CFG);
val &= ~MSCC_MAC_CFG_MODE_CFG_DISABLE_DIC;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_MODE_CFG, val);
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MAXLEN_CFG);
val &= ~MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN_M;
val |= MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN(10240);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_MAXLEN_CFG, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_ADV_CHK_CFG,
MSCC_MAC_CFG_ADV_CHK_CFG_SFD_CHK_ENA |
MSCC_MAC_CFG_ADV_CHK_CFG_PRM_CHK_ENA |
MSCC_MAC_CFG_ADV_CHK_CFG_OOR_ERR_ENA |
MSCC_MAC_CFG_ADV_CHK_CFG_INR_ERR_ENA);
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_LFS_CFG);
val &= ~MSCC_MAC_CFG_LFS_CFG_LFS_MODE_ENA;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_LFS_CFG, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_ENA_CFG,
MSCC_MAC_CFG_ENA_CFG_RX_CLK_ENA |
MSCC_MAC_CFG_ENA_CFG_TX_CLK_ENA |
MSCC_MAC_CFG_ENA_CFG_RX_ENA |
MSCC_MAC_CFG_ENA_CFG_TX_ENA);
}
/* Must be called with mdio_lock taken */
static int __vsc8584_macsec_init(struct phy_device *phydev)
{
u32 val;
vsc8584_macsec_block_init(phydev, MACSEC_INGR);
vsc8584_macsec_block_init(phydev, MACSEC_EGR);
vsc8584_macsec_mac_init(phydev, HOST_MAC);
vsc8584_macsec_mac_init(phydev, LINE_MAC);
vsc8584_macsec_phy_write(phydev, FC_BUFFER,
MSCC_FCBUF_FC_READ_THRESH_CFG,
MSCC_FCBUF_FC_READ_THRESH_CFG_TX_THRESH(4) |
MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH(5));
val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, MSCC_FCBUF_MODE_CFG);
val |= MSCC_FCBUF_MODE_CFG_PAUSE_GEN_ENA |
MSCC_FCBUF_MODE_CFG_RX_PPM_RATE_ADAPT_ENA |
MSCC_FCBUF_MODE_CFG_TX_PPM_RATE_ADAPT_ENA;
vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_MODE_CFG, val);
vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG,
MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_THRESH(8) |
MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_OFFSET(9));
val = vsc8584_macsec_phy_read(phydev, FC_BUFFER,
MSCC_FCBUF_TX_DATA_QUEUE_CFG);
val &= ~(MSCC_FCBUF_TX_DATA_QUEUE_CFG_START_M |
MSCC_FCBUF_TX_DATA_QUEUE_CFG_END_M);
val |= MSCC_FCBUF_TX_DATA_QUEUE_CFG_START(0) |
MSCC_FCBUF_TX_DATA_QUEUE_CFG_END(5119);
vsc8584_macsec_phy_write(phydev, FC_BUFFER,
MSCC_FCBUF_TX_DATA_QUEUE_CFG, val);
val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, MSCC_FCBUF_ENA_CFG);
val |= MSCC_FCBUF_ENA_CFG_TX_ENA | MSCC_FCBUF_ENA_CFG_RX_ENA;
vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_ENA_CFG, val);
val = vsc8584_macsec_phy_read(phydev, IP_1588,
MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL);
val &= ~MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE_M;
val |= MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE(4);
vsc8584_macsec_phy_write(phydev, IP_1588,
MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL, val);
return 0;
}
static void vsc8584_macsec_flow(struct phy_device *phydev,
struct macsec_flow *flow)
{
struct vsc8531_private *priv = phydev->priv;
enum macsec_bank bank = flow->bank;
u32 val, match = 0, mask = 0, action = 0, idx = flow->index;
if (flow->match.tagged)
match |= MSCC_MS_SAM_MISC_MATCH_TAGGED;
if (flow->match.untagged)
match |= MSCC_MS_SAM_MISC_MATCH_UNTAGGED;
if (bank == MACSEC_INGR && flow->assoc_num >= 0) {
match |= MSCC_MS_SAM_MISC_MATCH_AN(flow->assoc_num);
mask |= MSCC_MS_SAM_MASK_AN_MASK(0x3);
}
if (bank == MACSEC_INGR && flow->match.sci && flow->rx_sa->sc->sci) {
match |= MSCC_MS_SAM_MISC_MATCH_TCI(BIT(3));
mask |= MSCC_MS_SAM_MASK_TCI_MASK(BIT(3)) |
MSCC_MS_SAM_MASK_SCI_MASK;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_LO(idx),
lower_32_bits(flow->rx_sa->sc->sci));
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_HI(idx),
upper_32_bits(flow->rx_sa->sc->sci));
}
if (flow->match.etype) {
mask |= MSCC_MS_SAM_MASK_MAC_ETYPE_MASK;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MAC_SA_MATCH_HI(idx),
MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE(htons(flow->etype)));
}
match |= MSCC_MS_SAM_MISC_MATCH_PRIORITY(flow->priority);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MISC_MATCH(idx), match);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MASK(idx), mask);
/* Action for matching packets */
if (flow->action.drop)
action = MSCC_MS_FLOW_DROP;
else if (flow->action.bypass || flow->port == MSCC_MS_PORT_UNCONTROLLED)
action = MSCC_MS_FLOW_BYPASS;
else
action = (bank == MACSEC_INGR) ?
MSCC_MS_FLOW_INGRESS : MSCC_MS_FLOW_EGRESS;
val = MSCC_MS_SAM_FLOW_CTRL_FLOW_TYPE(action) |
MSCC_MS_SAM_FLOW_CTRL_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_FLOW_CTRL_DEST_PORT(flow->port);
if (action == MSCC_MS_FLOW_BYPASS)
goto write_ctrl;
if (bank == MACSEC_INGR) {
if (priv->secy->replay_protect)
val |= MSCC_MS_SAM_FLOW_CTRL_REPLAY_PROTECT;
if (priv->secy->validate_frames == MACSEC_VALIDATE_STRICT)
val |= MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(MSCC_MS_VALIDATE_STRICT);
else if (priv->secy->validate_frames == MACSEC_VALIDATE_CHECK)
val |= MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(MSCC_MS_VALIDATE_CHECK);
} else if (bank == MACSEC_EGR) {
if (priv->secy->protect_frames)
val |= MSCC_MS_SAM_FLOW_CTRL_PROTECT_FRAME;
if (priv->secy->tx_sc.encrypt)
val |= MSCC_MS_SAM_FLOW_CTRL_CONF_PROTECT;
if (priv->secy->tx_sc.send_sci)
val |= MSCC_MS_SAM_FLOW_CTRL_INCLUDE_SCI;
}
write_ctrl:
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val);
}
static struct macsec_flow *vsc8584_macsec_find_flow(struct macsec_context *ctx,
enum macsec_bank bank)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *pos, *tmp;
list_for_each_entry_safe(pos, tmp, &priv->macsec_flows, list)
if (pos->assoc_num == ctx->sa.assoc_num && pos->bank == bank)
return pos;
return ERR_PTR(-ENOENT);
}
static void vsc8584_macsec_flow_enable(struct phy_device *phydev,
struct macsec_flow *flow)
{
enum macsec_bank bank = flow->bank;
u32 val, idx = flow->index;
if ((flow->bank == MACSEC_INGR && flow->rx_sa && !flow->rx_sa->active) ||
(flow->bank == MACSEC_EGR && flow->tx_sa && !flow->tx_sa->active))
return;
/* Enable */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_ENTRY_SET1, BIT(idx));
/* Set in-use */
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx));
val |= MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val);
}
static void vsc8584_macsec_flow_disable(struct phy_device *phydev,
struct macsec_flow *flow)
{
enum macsec_bank bank = flow->bank;
u32 val, idx = flow->index;
/* Disable */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_ENTRY_CLEAR1, BIT(idx));
/* Clear in-use */
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx));
val &= ~MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val);
}
static u32 vsc8584_macsec_flow_context_id(struct macsec_flow *flow)
{
if (flow->bank == MACSEC_INGR)
return flow->index + MSCC_MS_MAX_FLOWS;
return flow->index;
}
/* Derive the AES key to get a key for the hash autentication */
static int vsc8584_macsec_derive_key(const u8 key[MACSEC_KEYID_LEN],
u16 key_len, u8 hkey[16])
{
struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
struct skcipher_request *req = NULL;
struct scatterlist src, dst;
DECLARE_CRYPTO_WAIT(wait);
u32 input[4] = {0};
int ret;
if (IS_ERR(tfm))
return PTR_ERR(tfm);
req = skcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) {
ret = -ENOMEM;
goto out;
}
skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP, crypto_req_done,
&wait);
ret = crypto_skcipher_setkey(tfm, key, key_len);
if (ret < 0)
goto out;
sg_init_one(&src, input, 16);
sg_init_one(&dst, hkey, 16);
skcipher_request_set_crypt(req, &src, &dst, 16, NULL);
ret = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
out:
skcipher_request_free(req);
crypto_free_skcipher(tfm);
return ret;
}
static int vsc8584_macsec_transformation(struct phy_device *phydev,
struct macsec_flow *flow)
{
struct vsc8531_private *priv = phydev->priv;
enum macsec_bank bank = flow->bank;
int i, ret, index = flow->index;
u32 rec = 0, control = 0;
u8 hkey[16];
sci_t sci;
ret = vsc8584_macsec_derive_key(flow->key, priv->secy->key_len, hkey);
if (ret)
return ret;
switch (priv->secy->key_len) {
case 16:
control |= CONTROL_CRYPTO_ALG(CTRYPTO_ALG_AES_CTR_128);
break;
case 32:
control |= CONTROL_CRYPTO_ALG(CTRYPTO_ALG_AES_CTR_256);
break;
default:
return -EINVAL;
}
control |= (bank == MACSEC_EGR) ?
(CONTROL_TYPE_EGRESS | CONTROL_AN(priv->secy->tx_sc.encoding_sa)) :
(CONTROL_TYPE_INGRESS | CONTROL_SEQ_MASK);
control |= CONTROL_UPDATE_SEQ | CONTROL_ENCRYPT_AUTH | CONTROL_KEY_IN_CTX |
CONTROL_IV0 | CONTROL_IV1 | CONTROL_IV_IN_SEQ |
CONTROL_DIGEST_TYPE(0x2) | CONTROL_SEQ_TYPE(0x1) |
CONTROL_AUTH_ALG(AUTH_ALG_AES_GHAS) | CONTROL_CONTEXT_ID;
/* Set the control word */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
control);
/* Set the context ID. Must be unique. */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
vsc8584_macsec_flow_context_id(flow));
/* Set the encryption/decryption key */
for (i = 0; i < priv->secy->key_len / sizeof(u32); i++)
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MS_XFORM_REC(index, rec++),
((u32 *)flow->key)[i]);
/* Set the authentication key */
for (i = 0; i < 4; i++)
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MS_XFORM_REC(index, rec++),
((u32 *)hkey)[i]);
/* Initial sequence number */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
bank == MACSEC_INGR ?
flow->rx_sa->next_pn : flow->tx_sa->next_pn);
if (bank == MACSEC_INGR)
/* Set the mask (replay window size) */
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MS_XFORM_REC(index, rec++),
priv->secy->replay_window);
/* Set the input vectors */
sci = bank == MACSEC_INGR ? flow->rx_sa->sc->sci : priv->secy->sci;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
lower_32_bits(sci));
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
upper_32_bits(sci));
while (rec < 20)
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
0);
flow->has_transformation = true;
return 0;
}
static struct macsec_flow *vsc8584_macsec_alloc_flow(struct vsc8531_private *priv,
enum macsec_bank bank)
{
unsigned long *bitmap = bank == MACSEC_INGR ?
&priv->ingr_flows : &priv->egr_flows;
struct macsec_flow *flow;
int index;
index = find_first_zero_bit(bitmap, MSCC_MS_MAX_FLOWS);
if (index == MSCC_MS_MAX_FLOWS)
return ERR_PTR(-ENOMEM);
flow = kzalloc(sizeof(*flow), GFP_KERNEL);
if (!flow)
return ERR_PTR(-ENOMEM);
set_bit(index, bitmap);
flow->index = index;
flow->bank = bank;
flow->priority = 8;
flow->assoc_num = -1;
list_add_tail(&flow->list, &priv->macsec_flows);
return flow;
}
static void vsc8584_macsec_free_flow(struct vsc8531_private *priv,
struct macsec_flow *flow)
{
unsigned long *bitmap = flow->bank == MACSEC_INGR ?
&priv->ingr_flows : &priv->egr_flows;
list_del(&flow->list);
clear_bit(flow->index, bitmap);
kfree(flow);
}
static int vsc8584_macsec_add_flow(struct phy_device *phydev,
struct macsec_flow *flow, bool update)
{
int ret;
flow->port = MSCC_MS_PORT_CONTROLLED;
vsc8584_macsec_flow(phydev, flow);
if (update)
return 0;
ret = vsc8584_macsec_transformation(phydev, flow);
if (ret) {
vsc8584_macsec_free_flow(phydev->priv, flow);
return ret;
}
return 0;
}
static int vsc8584_macsec_default_flows(struct phy_device *phydev)
{
struct macsec_flow *flow;
/* Add a rule to let the MKA traffic go through, ingress */
flow = vsc8584_macsec_alloc_flow(phydev->priv, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
flow->priority = 15;
flow->port = MSCC_MS_PORT_UNCONTROLLED;
flow->match.tagged = 1;
flow->match.untagged = 1;
flow->match.etype = 1;
flow->etype = ETH_P_PAE;
flow->action.bypass = 1;
vsc8584_macsec_flow(phydev, flow);
vsc8584_macsec_flow_enable(phydev, flow);
/* Add a rule to let the MKA traffic go through, egress */
flow = vsc8584_macsec_alloc_flow(phydev->priv, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
flow->priority = 15;
flow->port = MSCC_MS_PORT_COMMON;
flow->match.untagged = 1;
flow->match.etype = 1;
flow->etype = ETH_P_PAE;
flow->action.bypass = 1;
vsc8584_macsec_flow(phydev, flow);
vsc8584_macsec_flow_enable(phydev, flow);
return 0;
}
static void vsc8584_macsec_del_flow(struct phy_device *phydev,
struct macsec_flow *flow)
{
vsc8584_macsec_flow_disable(phydev, flow);
vsc8584_macsec_free_flow(phydev->priv, flow);
}
static int __vsc8584_macsec_add_rxsa(struct macsec_context *ctx,
struct macsec_flow *flow, bool update)
{
struct phy_device *phydev = ctx->phydev;
struct vsc8531_private *priv = phydev->priv;
if (!flow) {
flow = vsc8584_macsec_alloc_flow(priv, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
memcpy(flow->key, ctx->sa.key, priv->secy->key_len);
}
flow->assoc_num = ctx->sa.assoc_num;
flow->rx_sa = ctx->sa.rx_sa;
/* Always match tagged packets on ingress */
flow->match.tagged = 1;
flow->match.sci = 1;
if (priv->secy->validate_frames != MACSEC_VALIDATE_DISABLED)
flow->match.untagged = 1;
return vsc8584_macsec_add_flow(phydev, flow, update);
}
static int __vsc8584_macsec_add_txsa(struct macsec_context *ctx,
struct macsec_flow *flow, bool update)
{
struct phy_device *phydev = ctx->phydev;
struct vsc8531_private *priv = phydev->priv;
if (!flow) {
flow = vsc8584_macsec_alloc_flow(priv, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
memcpy(flow->key, ctx->sa.key, priv->secy->key_len);
}
flow->assoc_num = ctx->sa.assoc_num;
flow->tx_sa = ctx->sa.tx_sa;
/* Always match untagged packets on egress */
flow->match.untagged = 1;
return vsc8584_macsec_add_flow(phydev, flow, update);
}
static int vsc8584_macsec_dev_open(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list)
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_dev_stop(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list)
vsc8584_macsec_flow_disable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_add_secy(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_secy *secy = ctx->secy;
if (ctx->prepare) {
if (priv->secy)
return -EEXIST;
return 0;
}
priv->secy = secy;
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_EGR,
secy->validate_frames != MACSEC_VALIDATE_DISABLED);
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_INGR,
secy->validate_frames != MACSEC_VALIDATE_DISABLED);
return vsc8584_macsec_default_flows(ctx->phydev);
}
static int vsc8584_macsec_del_secy(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list)
vsc8584_macsec_del_flow(ctx->phydev, flow);
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_EGR, false);
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_INGR, false);
priv->secy = NULL;
return 0;
}
static int vsc8584_macsec_upd_secy(struct macsec_context *ctx)
{
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
vsc8584_macsec_del_secy(ctx);
return vsc8584_macsec_add_secy(ctx);
}
static int vsc8584_macsec_add_rxsc(struct macsec_context *ctx)
{
/* Nothing to do */
return 0;
}
static int vsc8584_macsec_upd_rxsc(struct macsec_context *ctx)
{
return -EOPNOTSUPP;
}
static int vsc8584_macsec_del_rxsc(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) {
if (flow->bank == MACSEC_INGR && flow->rx_sa &&
flow->rx_sa->sc->sci == ctx->rx_sc->sci)
vsc8584_macsec_del_flow(ctx->phydev, flow);
}
return 0;
}
static int vsc8584_macsec_add_rxsa(struct macsec_context *ctx)
{
struct macsec_flow *flow = NULL;
if (ctx->prepare)
return __vsc8584_macsec_add_rxsa(ctx, flow, false);
flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_upd_rxsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare) {
/* Make sure the flow is disabled before updating it */
vsc8584_macsec_flow_disable(ctx->phydev, flow);
return __vsc8584_macsec_add_rxsa(ctx, flow, true);
}
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_del_rxsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare)
return 0;
vsc8584_macsec_del_flow(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_add_txsa(struct macsec_context *ctx)
{
struct macsec_flow *flow = NULL;
if (ctx->prepare)
return __vsc8584_macsec_add_txsa(ctx, flow, false);
flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_upd_txsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare) {
/* Make sure the flow is disabled before updating it */
vsc8584_macsec_flow_disable(ctx->phydev, flow);
return __vsc8584_macsec_add_txsa(ctx, flow, true);
}
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_del_txsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare)
return 0;
vsc8584_macsec_del_flow(ctx->phydev, flow);
return 0;
}
static struct macsec_ops vsc8584_macsec_ops = {
.mdo_dev_open = vsc8584_macsec_dev_open,
.mdo_dev_stop = vsc8584_macsec_dev_stop,
.mdo_add_secy = vsc8584_macsec_add_secy,
.mdo_upd_secy = vsc8584_macsec_upd_secy,
.mdo_del_secy = vsc8584_macsec_del_secy,
.mdo_add_rxsc = vsc8584_macsec_add_rxsc,
.mdo_upd_rxsc = vsc8584_macsec_upd_rxsc,
.mdo_del_rxsc = vsc8584_macsec_del_rxsc,
.mdo_add_rxsa = vsc8584_macsec_add_rxsa,
.mdo_upd_rxsa = vsc8584_macsec_upd_rxsa,
.mdo_del_rxsa = vsc8584_macsec_del_rxsa,
.mdo_add_txsa = vsc8584_macsec_add_txsa,
.mdo_upd_txsa = vsc8584_macsec_upd_txsa,
.mdo_del_txsa = vsc8584_macsec_del_txsa,
};
int vsc8584_macsec_init(struct phy_device *phydev)
{
struct vsc8531_private *vsc8531 = phydev->priv;
switch (phydev->phy_id & phydev->drv->phy_id_mask) {
case PHY_ID_VSC856X:
case PHY_ID_VSC8575:
case PHY_ID_VSC8582:
case PHY_ID_VSC8584:
INIT_LIST_HEAD(&vsc8531->macsec_flows);
vsc8531->secy = NULL;
phydev->macsec_ops = &vsc8584_macsec_ops;
return __vsc8584_macsec_init(phydev);
}
return 0;
}
void vsc8584_handle_macsec_interrupt(struct phy_device *phydev)
{
struct vsc8531_private *priv = phydev->priv;
struct macsec_flow *flow, *tmp;
u32 cause, rec;
/* Check MACsec PN rollover */
cause = vsc8584_macsec_phy_read(phydev, MACSEC_EGR,
MSCC_MS_INTR_CTRL_STATUS);
cause &= MSCC_MS_INTR_CTRL_STATUS_INTR_CLR_STATUS_M;
if (!(cause & MACSEC_INTR_CTRL_STATUS_ROLLOVER))
return;
rec = 6 + priv->secy->key_len / sizeof(u32);
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) {
u32 val;
if (flow->bank != MACSEC_EGR || !flow->has_transformation)
continue;
val = vsc8584_macsec_phy_read(phydev, MACSEC_EGR,
MSCC_MS_XFORM_REC(flow->index, rec));
if (val == 0xffffffff) {
vsc8584_macsec_flow_disable(phydev, flow);
macsec_pn_wrapped(priv->secy, flow->tx_sa);
return;
}
}
}
void vsc8584_config_macsec_intr(struct phy_device *phydev)
{
phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_2);
phy_write(phydev, MSCC_PHY_EXTENDED_INT, MSCC_PHY_EXTENDED_INT_MS_EGR);
phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
vsc8584_macsec_phy_write(phydev, MACSEC_EGR, MSCC_MS_AIC_CTRL, 0xf);
vsc8584_macsec_phy_write(phydev, MACSEC_EGR, MSCC_MS_INTR_CTRL_STATUS,
MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE(MACSEC_INTR_CTRL_STATUS_ROLLOVER));
}
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ /* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/* /*
* Microsemi Ocelot Switch driver * Driver for Microsemi VSC85xx PHYs
* *
* Copyright (c) 2018 Microsemi Corporation * Copyright (c) 2018 Microsemi Corporation
*/ */
#ifndef _MSCC_OCELOT_MACSEC_H_ #ifndef _MSCC_PHY_MACSEC_H_
#define _MSCC_OCELOT_MACSEC_H_ #define _MSCC_PHY_MACSEC_H_
#include <net/macsec.h>
#define MSCC_MS_MAX_FLOWS 16 #define MSCC_MS_MAX_FLOWS 16
...@@ -58,6 +60,62 @@ enum mscc_macsec_validate_levels { ...@@ -58,6 +60,62 @@ enum mscc_macsec_validate_levels {
MSCC_MS_VALIDATE_STRICT = 2, MSCC_MS_VALIDATE_STRICT = 2,
}; };
enum macsec_bank {
FC_BUFFER = 0x04,
HOST_MAC = 0x05,
LINE_MAC = 0x06,
IP_1588 = 0x0e,
MACSEC_INGR = 0x38,
MACSEC_EGR = 0x3c,
};
struct macsec_flow {
struct list_head list;
enum mscc_macsec_destination_ports port;
enum macsec_bank bank;
u32 index;
int assoc_num;
bool has_transformation;
/* Highest takes precedence [0..15] */
u8 priority;
u8 key[MACSEC_KEYID_LEN];
union {
struct macsec_rx_sa *rx_sa;
struct macsec_tx_sa *tx_sa;
};
/* Matching */
struct {
u8 sci:1;
u8 tagged:1;
u8 untagged:1;
u8 etype:1;
} match;
u16 etype;
/* Action */
struct {
u8 bypass:1;
u8 drop:1;
} action;
};
#define MSCC_EXT_PAGE_MACSEC_17 17
#define MSCC_EXT_PAGE_MACSEC_18 18
#define MSCC_EXT_PAGE_MACSEC_19 19
#define MSCC_PHY_MACSEC_19_REG_ADDR(x) (x)
#define MSCC_PHY_MACSEC_19_TARGET(x) ((x) << 12)
#define MSCC_PHY_MACSEC_19_READ BIT(14)
#define MSCC_PHY_MACSEC_19_CMD BIT(15)
#define MSCC_EXT_PAGE_MACSEC_20 20
#define MSCC_PHY_MACSEC_20_TARGET(x) (x)
#define MSCC_MS_XFORM_REC(x, y) (((x) << 5) + (y)) #define MSCC_MS_XFORM_REC(x, y) (((x) << 5) + (y))
#define MSCC_MS_ENA_CFG 0x800 #define MSCC_MS_ENA_CFG 0x800
#define MSCC_MS_FC_CFG 0x804 #define MSCC_MS_FC_CFG 0x804
...@@ -263,4 +321,4 @@ enum mscc_macsec_validate_levels { ...@@ -263,4 +321,4 @@ enum mscc_macsec_validate_levels {
#define MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE_M GENMASK(31, 16) #define MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE_M GENMASK(31, 16)
#define MACSEC_INTR_CTRL_STATUS_ROLLOVER BIT(5) #define MACSEC_INTR_CTRL_STATUS_ROLLOVER BIT(5)
#endif #endif /* _MSCC_PHY_MACSEC_H_ */
...@@ -18,361 +18,7 @@ ...@@ -18,361 +18,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <dt-bindings/net/mscc-phy-vsc8531.h> #include <dt-bindings/net/mscc-phy-vsc8531.h>
#include <linux/scatterlist.h> #include "mscc.h"
#include <crypto/skcipher.h>
#if IS_ENABLED(CONFIG_MACSEC)
#include <net/macsec.h>
#endif
#include "mscc_macsec.h"
#include "mscc_mac.h"
#include "mscc_fc_buffer.h"
enum rgmii_rx_clock_delay {
RGMII_RX_CLK_DELAY_0_2_NS = 0,
RGMII_RX_CLK_DELAY_0_8_NS = 1,
RGMII_RX_CLK_DELAY_1_1_NS = 2,
RGMII_RX_CLK_DELAY_1_7_NS = 3,
RGMII_RX_CLK_DELAY_2_0_NS = 4,
RGMII_RX_CLK_DELAY_2_3_NS = 5,
RGMII_RX_CLK_DELAY_2_6_NS = 6,
RGMII_RX_CLK_DELAY_3_4_NS = 7
};
/* Microsemi VSC85xx PHY registers */
/* IEEE 802. Std Registers */
#define MSCC_PHY_BYPASS_CONTROL 18
#define DISABLE_HP_AUTO_MDIX_MASK 0x0080
#define DISABLE_PAIR_SWAP_CORR_MASK 0x0020
#define DISABLE_POLARITY_CORR_MASK 0x0010
#define PARALLEL_DET_IGNORE_ADVERTISED 0x0008
#define MSCC_PHY_EXT_CNTL_STATUS 22
#define SMI_BROADCAST_WR_EN 0x0001
#define MSCC_PHY_ERR_RX_CNT 19
#define MSCC_PHY_ERR_FALSE_CARRIER_CNT 20
#define MSCC_PHY_ERR_LINK_DISCONNECT_CNT 21
#define ERR_CNT_MASK GENMASK(7, 0)
#define MSCC_PHY_EXT_PHY_CNTL_1 23
#define MAC_IF_SELECTION_MASK 0x1800
#define MAC_IF_SELECTION_GMII 0
#define MAC_IF_SELECTION_RMII 1
#define MAC_IF_SELECTION_RGMII 2
#define MAC_IF_SELECTION_POS 11
#define VSC8584_MAC_IF_SELECTION_MASK 0x1000
#define VSC8584_MAC_IF_SELECTION_SGMII 0
#define VSC8584_MAC_IF_SELECTION_1000BASEX 1
#define VSC8584_MAC_IF_SELECTION_POS 12
#define FAR_END_LOOPBACK_MODE_MASK 0x0008
#define MEDIA_OP_MODE_MASK 0x0700
#define MEDIA_OP_MODE_COPPER 0
#define MEDIA_OP_MODE_SERDES 1
#define MEDIA_OP_MODE_1000BASEX 2
#define MEDIA_OP_MODE_100BASEFX 3
#define MEDIA_OP_MODE_AMS_COPPER_SERDES 5
#define MEDIA_OP_MODE_AMS_COPPER_1000BASEX 6
#define MEDIA_OP_MODE_AMS_COPPER_100BASEFX 7
#define MEDIA_OP_MODE_POS 8
#define MSCC_PHY_EXT_PHY_CNTL_2 24
#define MII_VSC85XX_INT_MASK 25
#define MII_VSC85XX_INT_MASK_MDINT BIT(15)
#define MII_VSC85XX_INT_MASK_LINK_CHG BIT(13)
#define MII_VSC85XX_INT_MASK_WOL BIT(6)
#define MII_VSC85XX_INT_MASK_EXT BIT(5)
#define MII_VSC85XX_INT_STATUS 26
#define MII_VSC85XX_INT_MASK_MASK (MII_VSC85XX_INT_MASK_MDINT | \
MII_VSC85XX_INT_MASK_LINK_CHG | \
MII_VSC85XX_INT_MASK_EXT)
#define MSCC_PHY_WOL_MAC_CONTROL 27
#define EDGE_RATE_CNTL_POS 5
#define EDGE_RATE_CNTL_MASK 0x00E0
#define MSCC_PHY_DEV_AUX_CNTL 28
#define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000
#define MSCC_PHY_LED_MODE_SEL 29
#define LED_MODE_SEL_POS(x) ((x) * 4)
#define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x))
#define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x))
#define MSCC_EXT_PAGE_CSR_CNTL_17 17
#define MSCC_EXT_PAGE_CSR_CNTL_18 18
#define MSCC_EXT_PAGE_CSR_CNTL_19 19
#define MSCC_PHY_CSR_CNTL_19_REG_ADDR(x) (x)
#define MSCC_PHY_CSR_CNTL_19_TARGET(x) ((x) << 12)
#define MSCC_PHY_CSR_CNTL_19_READ BIT(14)
#define MSCC_PHY_CSR_CNTL_19_CMD BIT(15)
#define MSCC_EXT_PAGE_CSR_CNTL_20 20
#define MSCC_PHY_CSR_CNTL_20_TARGET(x) (x)
#define PHY_MCB_TARGET 0x07
#define PHY_MCB_S6G_WRITE BIT(31)
#define PHY_MCB_S6G_READ BIT(30)
#define PHY_S6G_PLL5G_CFG0 0x06
#define PHY_S6G_LCPLL_CFG 0x11
#define PHY_S6G_PLL_CFG 0x2b
#define PHY_S6G_COMMON_CFG 0x2c
#define PHY_S6G_GPC_CFG 0x2e
#define PHY_S6G_MISC_CFG 0x3b
#define PHY_MCB_S6G_CFG 0x3f
#define PHY_S6G_DFT_CFG2 0x3e
#define PHY_S6G_PLL_STATUS 0x31
#define PHY_S6G_IB_STATUS0 0x2f
#define PHY_S6G_SYS_RST_POS 31
#define PHY_S6G_ENA_LANE_POS 18
#define PHY_S6G_ENA_LOOP_POS 8
#define PHY_S6G_QRATE_POS 6
#define PHY_S6G_IF_MODE_POS 4
#define PHY_S6G_PLL_ENA_OFFS_POS 21
#define PHY_S6G_PLL_FSM_CTRL_DATA_POS 8
#define PHY_S6G_PLL_FSM_ENA_POS 7
#define MSCC_EXT_PAGE_MACSEC_17 17
#define MSCC_EXT_PAGE_MACSEC_18 18
#define MSCC_EXT_PAGE_MACSEC_19 19
#define MSCC_PHY_MACSEC_19_REG_ADDR(x) (x)
#define MSCC_PHY_MACSEC_19_TARGET(x) ((x) << 12)
#define MSCC_PHY_MACSEC_19_READ BIT(14)
#define MSCC_PHY_MACSEC_19_CMD BIT(15)
#define MSCC_EXT_PAGE_MACSEC_20 20
#define MSCC_PHY_MACSEC_20_TARGET(x) (x)
enum macsec_bank {
FC_BUFFER = 0x04,
HOST_MAC = 0x05,
LINE_MAC = 0x06,
IP_1588 = 0x0e,
MACSEC_INGR = 0x38,
MACSEC_EGR = 0x3c,
};
#define MSCC_EXT_PAGE_ACCESS 31
#define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
#define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */
#define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
#define MSCC_PHY_PAGE_EXTENDED_3 0x0003 /* Extended reg - page 3 */
#define MSCC_PHY_PAGE_EXTENDED_4 0x0004 /* Extended reg - page 4 */
#define MSCC_PHY_PAGE_CSR_CNTL MSCC_PHY_PAGE_EXTENDED_4
#define MSCC_PHY_PAGE_MACSEC MSCC_PHY_PAGE_EXTENDED_4
/* Extended reg - GPIO; this is a bank of registers that are shared for all PHYs
* in the same package.
*/
#define MSCC_PHY_PAGE_EXTENDED_GPIO 0x0010 /* Extended reg - GPIO */
#define MSCC_PHY_PAGE_TEST 0x2a30 /* Test reg */
#define MSCC_PHY_PAGE_TR 0x52b5 /* Token ring registers */
/* Extended Page 1 Registers */
#define MSCC_PHY_CU_MEDIA_CRC_VALID_CNT 18
#define VALID_CRC_CNT_CRC_MASK GENMASK(13, 0)
#define MSCC_PHY_EXT_MODE_CNTL 19
#define FORCE_MDI_CROSSOVER_MASK 0x000C
#define FORCE_MDI_CROSSOVER_MDIX 0x000C
#define FORCE_MDI_CROSSOVER_MDI 0x0008
#define MSCC_PHY_ACTIPHY_CNTL 20
#define PHY_ADDR_REVERSED 0x0200
#define DOWNSHIFT_CNTL_MASK 0x001C
#define DOWNSHIFT_EN 0x0010
#define DOWNSHIFT_CNTL_POS 2
#define MSCC_PHY_EXT_PHY_CNTL_4 23
#define PHY_CNTL_4_ADDR_POS 11
#define MSCC_PHY_VERIPHY_CNTL_2 25
#define MSCC_PHY_VERIPHY_CNTL_3 26
/* Extended Page 2 Registers */
#define MSCC_PHY_CU_PMD_TX_CNTL 16
#define MSCC_PHY_RGMII_CNTL 20
#define RGMII_RX_CLK_DELAY_MASK 0x0070
#define RGMII_RX_CLK_DELAY_POS 4
#define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
#define MSCC_PHY_WOL_MID_MAC_ADDR 22
#define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
#define MSCC_PHY_WOL_LOWER_PASSWD 24
#define MSCC_PHY_WOL_MID_PASSWD 25
#define MSCC_PHY_WOL_UPPER_PASSWD 26
#define MSCC_PHY_WOL_MAC_CONTROL 27
#define SECURE_ON_ENABLE 0x8000
#define SECURE_ON_PASSWD_LEN_4 0x4000
#define MSCC_PHY_EXTENDED_INT 28
#define MSCC_PHY_EXTENDED_INT_MS_EGR BIT(9)
/* Extended Page 3 Registers */
#define MSCC_PHY_SERDES_TX_VALID_CNT 21
#define MSCC_PHY_SERDES_TX_CRC_ERR_CNT 22
#define MSCC_PHY_SERDES_RX_VALID_CNT 28
#define MSCC_PHY_SERDES_RX_CRC_ERR_CNT 29
/* Extended page GPIO Registers */
#define MSCC_DW8051_CNTL_STATUS 0
#define MICRO_NSOFT_RESET 0x8000
#define RUN_FROM_INT_ROM 0x4000
#define AUTOINC_ADDR 0x2000
#define PATCH_RAM_CLK 0x1000
#define MICRO_PATCH_EN 0x0080
#define DW8051_CLK_EN 0x0010
#define MICRO_CLK_EN 0x0008
#define MICRO_CLK_DIVIDE(x) ((x) >> 1)
#define MSCC_DW8051_VLD_MASK 0xf1ff
/* x Address in range 1-4 */
#define MSCC_TRAP_ROM_ADDR(x) ((x) * 2 + 1)
#define MSCC_PATCH_RAM_ADDR(x) (((x) + 1) * 2)
#define MSCC_INT_MEM_ADDR 11
#define MSCC_INT_MEM_CNTL 12
#define READ_SFR 0x6000
#define READ_PRAM 0x4000
#define READ_ROM 0x2000
#define READ_RAM 0x0000
#define INT_MEM_WRITE_EN 0x1000
#define EN_PATCH_RAM_TRAP_ADDR(x) (0x0100 << ((x) - 1))
#define INT_MEM_DATA_M 0x00ff
#define INT_MEM_DATA(x) (INT_MEM_DATA_M & (x))
#define MSCC_PHY_PROC_CMD 18
#define PROC_CMD_NCOMPLETED 0x8000
#define PROC_CMD_FAILED 0x4000
#define PROC_CMD_SGMII_PORT(x) ((x) << 8)
#define PROC_CMD_FIBER_PORT(x) (0x0100 << (x) % 4)
#define PROC_CMD_QSGMII_PORT 0x0c00
#define PROC_CMD_RST_CONF_PORT 0x0080
#define PROC_CMD_RECONF_PORT 0x0000
#define PROC_CMD_READ_MOD_WRITE_PORT 0x0040
#define PROC_CMD_WRITE 0x0040
#define PROC_CMD_READ 0x0000
#define PROC_CMD_FIBER_DISABLE 0x0020
#define PROC_CMD_FIBER_100BASE_FX 0x0010
#define PROC_CMD_FIBER_1000BASE_X 0x0000
#define PROC_CMD_SGMII_MAC 0x0030
#define PROC_CMD_QSGMII_MAC 0x0020
#define PROC_CMD_NO_MAC_CONF 0x0000
#define PROC_CMD_1588_DEFAULT_INIT 0x0010
#define PROC_CMD_NOP 0x000f
#define PROC_CMD_PHY_INIT 0x000a
#define PROC_CMD_CRC16 0x0008
#define PROC_CMD_FIBER_MEDIA_CONF 0x0001
#define PROC_CMD_MCB_ACCESS_MAC_CONF 0x0000
#define PROC_CMD_NCOMPLETED_TIMEOUT_MS 500
#define MSCC_PHY_MAC_CFG_FASTLINK 19
#define MAC_CFG_MASK 0xc000
#define MAC_CFG_SGMII 0x0000
#define MAC_CFG_QSGMII 0x4000
/* Test page Registers */
#define MSCC_PHY_TEST_PAGE_5 5
#define MSCC_PHY_TEST_PAGE_8 8
#define MSCC_PHY_TEST_PAGE_9 9
#define MSCC_PHY_TEST_PAGE_20 20
#define MSCC_PHY_TEST_PAGE_24 24
/* Token ring page Registers */
#define MSCC_PHY_TR_CNTL 16
#define TR_WRITE 0x8000
#define TR_ADDR(x) (0x7fff & (x))
#define MSCC_PHY_TR_LSB 17
#define MSCC_PHY_TR_MSB 18
/* Microsemi PHY ID's
* Code assumes lowest nibble is 0
*/
#define PHY_ID_VSC8504 0x000704c0
#define PHY_ID_VSC8514 0x00070670
#define PHY_ID_VSC8530 0x00070560
#define PHY_ID_VSC8531 0x00070570
#define PHY_ID_VSC8540 0x00070760
#define PHY_ID_VSC8541 0x00070770
#define PHY_ID_VSC8552 0x000704e0
#define PHY_ID_VSC856X 0x000707e0
#define PHY_ID_VSC8572 0x000704d0
#define PHY_ID_VSC8574 0x000704a0
#define PHY_ID_VSC8575 0x000707d0
#define PHY_ID_VSC8582 0x000707b0
#define PHY_ID_VSC8584 0x000707c0
#define MSCC_VDDMAC_1500 1500
#define MSCC_VDDMAC_1800 1800
#define MSCC_VDDMAC_2500 2500
#define MSCC_VDDMAC_3300 3300
#define DOWNSHIFT_COUNT_MAX 5
#define MAX_LEDS 4
#define VSC8584_SUPP_LED_MODES (BIT(VSC8531_LINK_ACTIVITY) | \
BIT(VSC8531_LINK_1000_ACTIVITY) | \
BIT(VSC8531_LINK_100_ACTIVITY) | \
BIT(VSC8531_LINK_10_ACTIVITY) | \
BIT(VSC8531_LINK_100_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_100_ACTIVITY) | \
BIT(VSC8584_LINK_100FX_1000X_ACTIVITY) | \
BIT(VSC8531_DUPLEX_COLLISION) | \
BIT(VSC8531_COLLISION) | \
BIT(VSC8531_ACTIVITY) | \
BIT(VSC8584_100FX_1000X_ACTIVITY) | \
BIT(VSC8531_AUTONEG_FAULT) | \
BIT(VSC8531_SERIAL_MODE) | \
BIT(VSC8531_FORCE_LED_OFF) | \
BIT(VSC8531_FORCE_LED_ON))
#define VSC85XX_SUPP_LED_MODES (BIT(VSC8531_LINK_ACTIVITY) | \
BIT(VSC8531_LINK_1000_ACTIVITY) | \
BIT(VSC8531_LINK_100_ACTIVITY) | \
BIT(VSC8531_LINK_10_ACTIVITY) | \
BIT(VSC8531_LINK_100_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_1000_ACTIVITY) | \
BIT(VSC8531_LINK_10_100_ACTIVITY) | \
BIT(VSC8531_DUPLEX_COLLISION) | \
BIT(VSC8531_COLLISION) | \
BIT(VSC8531_ACTIVITY) | \
BIT(VSC8531_AUTONEG_FAULT) | \
BIT(VSC8531_SERIAL_MODE) | \
BIT(VSC8531_FORCE_LED_OFF) | \
BIT(VSC8531_FORCE_LED_ON))
#define MSCC_VSC8584_REVB_INT8051_FW "microchip/mscc_vsc8584_revb_int8051_fb48.bin"
#define MSCC_VSC8584_REVB_INT8051_FW_START_ADDR 0xe800
#define MSCC_VSC8584_REVB_INT8051_FW_CRC 0xfb48
#define MSCC_VSC8574_REVB_INT8051_FW "microchip/mscc_vsc8574_revb_int8051_29e8.bin"
#define MSCC_VSC8574_REVB_INT8051_FW_START_ADDR 0x4000
#define MSCC_VSC8574_REVB_INT8051_FW_CRC 0x29e8
#define VSC8584_REVB 0x0001
#define MSCC_DEV_REV_MASK GENMASK(3, 0)
struct reg_val {
u16 reg;
u32 val;
};
struct vsc85xx_hw_stat {
const char *string;
u8 reg;
u16 page;
u16 mask;
};
static const struct vsc85xx_hw_stat vsc85xx_hw_stats[] = { static const struct vsc85xx_hw_stat vsc85xx_hw_stats[] = {
{ {
...@@ -452,85 +98,14 @@ static const struct vsc85xx_hw_stat vsc8584_hw_stats[] = { ...@@ -452,85 +98,14 @@ static const struct vsc85xx_hw_stat vsc8584_hw_stats[] = {
}, },
}; };
#if IS_ENABLED(CONFIG_MACSEC)
struct macsec_flow {
struct list_head list;
enum mscc_macsec_destination_ports port;
enum macsec_bank bank;
u32 index;
int assoc_num;
bool has_transformation;
/* Highest takes precedence [0..15] */
u8 priority;
u8 key[MACSEC_KEYID_LEN];
union {
struct macsec_rx_sa *rx_sa;
struct macsec_tx_sa *tx_sa;
};
/* Matching */
struct {
u8 sci:1;
u8 tagged:1;
u8 untagged:1;
u8 etype:1;
} match;
u16 etype;
/* Action */
struct {
u8 bypass:1;
u8 drop:1;
} action;
};
#endif
struct vsc8531_private {
int rate_magic;
u16 supp_led_modes;
u32 leds_mode[MAX_LEDS];
u8 nleds;
const struct vsc85xx_hw_stat *hw_stats;
u64 *stats;
int nstats;
bool pkg_init;
/* For multiple port PHYs; the MDIO address of the base PHY in the
* package.
*/
unsigned int base_addr;
#if IS_ENABLED(CONFIG_MACSEC)
/* MACsec fields:
* - One SecY per device (enforced at the s/w implementation level)
* - macsec_flows: list of h/w flows
* - ingr_flows: bitmap of ingress flows
* - egr_flows: bitmap of egress flows
*/
struct macsec_secy *secy;
struct list_head macsec_flows;
unsigned long ingr_flows;
unsigned long egr_flows;
#endif
};
#ifdef CONFIG_OF_MDIO #ifdef CONFIG_OF_MDIO
struct vsc8531_edge_rate_table {
u32 vddmac;
u32 slowdown[8];
};
static const struct vsc8531_edge_rate_table edge_table[] = { static const struct vsc8531_edge_rate_table edge_table[] = {
{MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} }, {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} },
{MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} }, {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} },
{MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} }, {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} },
{MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} }, {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} },
}; };
#endif /* CONFIG_OF_MDIO */ #endif
static int vsc85xx_phy_read_page(struct phy_device *phydev) static int vsc85xx_phy_read_page(struct phy_device *phydev)
{ {
...@@ -1676,978 +1251,6 @@ static int vsc8584_config_pre_init(struct phy_device *phydev) ...@@ -1676,978 +1251,6 @@ static int vsc8584_config_pre_init(struct phy_device *phydev)
return ret; return ret;
} }
#if IS_ENABLED(CONFIG_MACSEC)
static u32 vsc8584_macsec_phy_read(struct phy_device *phydev,
enum macsec_bank bank, u32 reg)
{
u32 val, val_l = 0, val_h = 0;
unsigned long deadline;
int rc;
rc = phy_select_page(phydev, MSCC_PHY_PAGE_MACSEC);
if (rc < 0)
goto failed;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_20,
MSCC_PHY_MACSEC_20_TARGET(bank >> 2));
if (bank >> 2 == 0x1)
/* non-MACsec access */
bank &= 0x3;
else
bank = 0;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_19,
MSCC_PHY_MACSEC_19_CMD | MSCC_PHY_MACSEC_19_READ |
MSCC_PHY_MACSEC_19_REG_ADDR(reg) |
MSCC_PHY_MACSEC_19_TARGET(bank));
deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS);
do {
val = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_19);
} while (time_before(jiffies, deadline) && !(val & MSCC_PHY_MACSEC_19_CMD));
val_l = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_17);
val_h = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_18);
failed:
phy_restore_page(phydev, rc, rc);
return (val_h << 16) | val_l;
}
static void vsc8584_macsec_phy_write(struct phy_device *phydev,
enum macsec_bank bank, u32 reg, u32 val)
{
unsigned long deadline;
int rc;
rc = phy_select_page(phydev, MSCC_PHY_PAGE_MACSEC);
if (rc < 0)
goto failed;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_20,
MSCC_PHY_MACSEC_20_TARGET(bank >> 2));
if ((bank >> 2 == 0x1) || (bank >> 2 == 0x3))
bank &= 0x3;
else
/* MACsec access */
bank = 0;
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_17, (u16)val);
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_18, (u16)(val >> 16));
__phy_write(phydev, MSCC_EXT_PAGE_MACSEC_19,
MSCC_PHY_MACSEC_19_CMD | MSCC_PHY_MACSEC_19_REG_ADDR(reg) |
MSCC_PHY_MACSEC_19_TARGET(bank));
deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS);
do {
val = __phy_read(phydev, MSCC_EXT_PAGE_MACSEC_19);
} while (time_before(jiffies, deadline) && !(val & MSCC_PHY_MACSEC_19_CMD));
failed:
phy_restore_page(phydev, rc, rc);
}
static void vsc8584_macsec_classification(struct phy_device *phydev,
enum macsec_bank bank)
{
/* enable VLAN tag parsing */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_CP_TAG,
MSCC_MS_SAM_CP_TAG_PARSE_STAG |
MSCC_MS_SAM_CP_TAG_PARSE_QTAG |
MSCC_MS_SAM_CP_TAG_PARSE_QINQ);
}
static void vsc8584_macsec_flow_default_action(struct phy_device *phydev,
enum macsec_bank bank,
bool block)
{
u32 port = (bank == MACSEC_INGR) ?
MSCC_MS_PORT_UNCONTROLLED : MSCC_MS_PORT_COMMON;
u32 action = MSCC_MS_FLOW_BYPASS;
if (block)
action = MSCC_MS_FLOW_DROP;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_NM_FLOW_NCP,
/* MACsec untagged */
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_DEST_PORT(port) |
/* MACsec tagged */
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_DEST_PORT(port) |
/* Bad tag */
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_DEST_PORT(port) |
/* Kay tag */
MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_NCP_KAY_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_NCP_KAY_DEST_PORT(port));
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_NM_FLOW_CP,
/* MACsec untagged */
MSCC_MS_SAM_NM_FLOW_NCP_UNTAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_UNTAGGED_DEST_PORT(port) |
/* MACsec tagged */
MSCC_MS_SAM_NM_FLOW_NCP_TAGGED_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_TAGGED_DEST_PORT(port) |
/* Bad tag */
MSCC_MS_SAM_NM_FLOW_NCP_BADTAG_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_BADTAG_DEST_PORT(port) |
/* Kay tag */
MSCC_MS_SAM_NM_FLOW_NCP_KAY_FLOW_TYPE(action) |
MSCC_MS_SAM_NM_FLOW_CP_KAY_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_NM_FLOW_CP_KAY_DEST_PORT(port));
}
static void vsc8584_macsec_integrity_checks(struct phy_device *phydev,
enum macsec_bank bank)
{
u32 val;
if (bank != MACSEC_INGR)
return;
/* Set default rules to pass unmatched frames */
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MS_PARAMS2_IG_CC_CONTROL);
val |= MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_CTRL_ACT |
MSCC_MS_PARAMS2_IG_CC_CONTROL_NON_MATCH_ACT;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PARAMS2_IG_CC_CONTROL,
val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PARAMS2_IG_CP_TAG,
MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_STAG |
MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QTAG |
MSCC_MS_PARAMS2_IG_CP_TAG_PARSE_QINQ);
}
static void vsc8584_macsec_block_init(struct phy_device *phydev,
enum macsec_bank bank)
{
u32 val;
int i;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG,
MSCC_MS_ENA_CFG_SW_RST |
MSCC_MS_ENA_CFG_MACSEC_BYPASS_ENA);
/* Set the MACsec block out of s/w reset and enable clocks */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG,
MSCC_MS_ENA_CFG_CLK_ENA);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_STATUS_CONTEXT_CTRL,
bank == MACSEC_INGR ? 0xe5880214 : 0xe5880218);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_MISC_CONTROL,
MSCC_MS_MISC_CONTROL_MC_LATENCY_FIX(bank == MACSEC_INGR ? 57 : 40) |
MSCC_MS_MISC_CONTROL_XFORM_REC_SIZE(bank == MACSEC_INGR ? 1 : 2));
/* Clear the counters */
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_COUNT_CONTROL);
val |= MSCC_MS_COUNT_CONTROL_AUTO_CNTR_RESET;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_COUNT_CONTROL, val);
/* Enable octet increment mode */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_PP_CTRL,
MSCC_MS_PP_CTRL_MACSEC_OCTET_INCR_MODE);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_BLOCK_CTX_UPDATE, 0x3);
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_COUNT_CONTROL);
val |= MSCC_MS_COUNT_CONTROL_RESET_ALL;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_COUNT_CONTROL, val);
/* Set the MTU */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_NON_VLAN_MTU_CHECK,
MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMPARE(32761) |
MSCC_MS_NON_VLAN_MTU_CHECK_NV_MTU_COMP_DROP);
for (i = 0; i < 8; i++)
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_VLAN_MTU_CHECK(i),
MSCC_MS_VLAN_MTU_CHECK_MTU_COMPARE(32761) |
MSCC_MS_VLAN_MTU_CHECK_MTU_COMP_DROP);
if (bank == MACSEC_EGR) {
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_INTR_CTRL_STATUS);
val &= ~MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE_M;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_INTR_CTRL_STATUS, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_FC_CFG,
MSCC_MS_FC_CFG_FCBUF_ENA |
MSCC_MS_FC_CFG_LOW_THRESH(0x1) |
MSCC_MS_FC_CFG_HIGH_THRESH(0x4) |
MSCC_MS_FC_CFG_LOW_BYTES_VAL(0x4) |
MSCC_MS_FC_CFG_HIGH_BYTES_VAL(0x6));
}
vsc8584_macsec_classification(phydev, bank);
vsc8584_macsec_flow_default_action(phydev, bank, false);
vsc8584_macsec_integrity_checks(phydev, bank);
/* Enable the MACsec block */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_ENA_CFG,
MSCC_MS_ENA_CFG_CLK_ENA |
MSCC_MS_ENA_CFG_MACSEC_ENA |
MSCC_MS_ENA_CFG_MACSEC_SPEED_MODE(0x5));
}
static void vsc8584_macsec_mac_init(struct phy_device *phydev,
enum macsec_bank bank)
{
u32 val;
int i;
/* Clear host & line stats */
for (i = 0; i < 36; i++)
vsc8584_macsec_phy_write(phydev, bank, 0x1c + i, 0);
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL);
val &= ~MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE_M;
val |= MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_MODE(2) |
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_PAUSE_VALUE(0xffff);
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL, val);
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2);
val |= 0xffff;
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MAC_PAUSE_CFG_TX_FRAME_CTRL_2, val);
val = vsc8584_macsec_phy_read(phydev, bank,
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL);
if (bank == HOST_MAC)
val |= MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_TIMER_ENA |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA;
else
val |= MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_REACT_ENA |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_FRAME_DROP_ENA |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_PAUSE_MODE |
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL_EARLY_PAUSE_DETECT_ENA;
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MAC_PAUSE_CFG_RX_FRAME_CTRL, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_PKTINF_CFG,
MSCC_MAC_CFG_PKTINF_CFG_STRIP_FCS_ENA |
MSCC_MAC_CFG_PKTINF_CFG_INSERT_FCS_ENA |
MSCC_MAC_CFG_PKTINF_CFG_LPI_RELAY_ENA |
MSCC_MAC_CFG_PKTINF_CFG_STRIP_PREAMBLE_ENA |
MSCC_MAC_CFG_PKTINF_CFG_INSERT_PREAMBLE_ENA |
(bank == HOST_MAC ?
MSCC_MAC_CFG_PKTINF_CFG_ENABLE_TX_PADDING : 0));
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MODE_CFG);
val &= ~MSCC_MAC_CFG_MODE_CFG_DISABLE_DIC;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_MODE_CFG, val);
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_MAXLEN_CFG);
val &= ~MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN_M;
val |= MSCC_MAC_CFG_MAXLEN_CFG_MAX_LEN(10240);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_MAXLEN_CFG, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_ADV_CHK_CFG,
MSCC_MAC_CFG_ADV_CHK_CFG_SFD_CHK_ENA |
MSCC_MAC_CFG_ADV_CHK_CFG_PRM_CHK_ENA |
MSCC_MAC_CFG_ADV_CHK_CFG_OOR_ERR_ENA |
MSCC_MAC_CFG_ADV_CHK_CFG_INR_ERR_ENA);
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MAC_CFG_LFS_CFG);
val &= ~MSCC_MAC_CFG_LFS_CFG_LFS_MODE_ENA;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_LFS_CFG, val);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MAC_CFG_ENA_CFG,
MSCC_MAC_CFG_ENA_CFG_RX_CLK_ENA |
MSCC_MAC_CFG_ENA_CFG_TX_CLK_ENA |
MSCC_MAC_CFG_ENA_CFG_RX_ENA |
MSCC_MAC_CFG_ENA_CFG_TX_ENA);
}
/* Must be called with mdio_lock taken */
static int vsc8584_macsec_init(struct phy_device *phydev)
{
u32 val;
vsc8584_macsec_block_init(phydev, MACSEC_INGR);
vsc8584_macsec_block_init(phydev, MACSEC_EGR);
vsc8584_macsec_mac_init(phydev, HOST_MAC);
vsc8584_macsec_mac_init(phydev, LINE_MAC);
vsc8584_macsec_phy_write(phydev, FC_BUFFER,
MSCC_FCBUF_FC_READ_THRESH_CFG,
MSCC_FCBUF_FC_READ_THRESH_CFG_TX_THRESH(4) |
MSCC_FCBUF_FC_READ_THRESH_CFG_RX_THRESH(5));
val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, MSCC_FCBUF_MODE_CFG);
val |= MSCC_FCBUF_MODE_CFG_PAUSE_GEN_ENA |
MSCC_FCBUF_MODE_CFG_RX_PPM_RATE_ADAPT_ENA |
MSCC_FCBUF_MODE_CFG_TX_PPM_RATE_ADAPT_ENA;
vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_MODE_CFG, val);
vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG,
MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_THRESH(8) |
MSCC_FCBUF_PPM_RATE_ADAPT_THRESH_CFG_TX_OFFSET(9));
val = vsc8584_macsec_phy_read(phydev, FC_BUFFER,
MSCC_FCBUF_TX_DATA_QUEUE_CFG);
val &= ~(MSCC_FCBUF_TX_DATA_QUEUE_CFG_START_M |
MSCC_FCBUF_TX_DATA_QUEUE_CFG_END_M);
val |= MSCC_FCBUF_TX_DATA_QUEUE_CFG_START(0) |
MSCC_FCBUF_TX_DATA_QUEUE_CFG_END(5119);
vsc8584_macsec_phy_write(phydev, FC_BUFFER,
MSCC_FCBUF_TX_DATA_QUEUE_CFG, val);
val = vsc8584_macsec_phy_read(phydev, FC_BUFFER, MSCC_FCBUF_ENA_CFG);
val |= MSCC_FCBUF_ENA_CFG_TX_ENA | MSCC_FCBUF_ENA_CFG_RX_ENA;
vsc8584_macsec_phy_write(phydev, FC_BUFFER, MSCC_FCBUF_ENA_CFG, val);
val = vsc8584_macsec_phy_read(phydev, IP_1588,
MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL);
val &= ~MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE_M;
val |= MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL_PROTOCOL_MODE(4);
vsc8584_macsec_phy_write(phydev, IP_1588,
MSCC_PROC_0_IP_1588_TOP_CFG_STAT_MODE_CTL, val);
return 0;
}
static void vsc8584_macsec_flow(struct phy_device *phydev,
struct macsec_flow *flow)
{
struct vsc8531_private *priv = phydev->priv;
enum macsec_bank bank = flow->bank;
u32 val, match = 0, mask = 0, action = 0, idx = flow->index;
if (flow->match.tagged)
match |= MSCC_MS_SAM_MISC_MATCH_TAGGED;
if (flow->match.untagged)
match |= MSCC_MS_SAM_MISC_MATCH_UNTAGGED;
if (bank == MACSEC_INGR && flow->assoc_num >= 0) {
match |= MSCC_MS_SAM_MISC_MATCH_AN(flow->assoc_num);
mask |= MSCC_MS_SAM_MASK_AN_MASK(0x3);
}
if (bank == MACSEC_INGR && flow->match.sci && flow->rx_sa->sc->sci) {
match |= MSCC_MS_SAM_MISC_MATCH_TCI(BIT(3));
mask |= MSCC_MS_SAM_MASK_TCI_MASK(BIT(3)) |
MSCC_MS_SAM_MASK_SCI_MASK;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_LO(idx),
lower_32_bits(flow->rx_sa->sc->sci));
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MATCH_SCI_HI(idx),
upper_32_bits(flow->rx_sa->sc->sci));
}
if (flow->match.etype) {
mask |= MSCC_MS_SAM_MASK_MAC_ETYPE_MASK;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MAC_SA_MATCH_HI(idx),
MSCC_MS_SAM_MAC_SA_MATCH_HI_ETYPE(htons(flow->etype)));
}
match |= MSCC_MS_SAM_MISC_MATCH_PRIORITY(flow->priority);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MISC_MATCH(idx), match);
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_MASK(idx), mask);
/* Action for matching packets */
if (flow->action.drop)
action = MSCC_MS_FLOW_DROP;
else if (flow->action.bypass || flow->port == MSCC_MS_PORT_UNCONTROLLED)
action = MSCC_MS_FLOW_BYPASS;
else
action = (bank == MACSEC_INGR) ?
MSCC_MS_FLOW_INGRESS : MSCC_MS_FLOW_EGRESS;
val = MSCC_MS_SAM_FLOW_CTRL_FLOW_TYPE(action) |
MSCC_MS_SAM_FLOW_CTRL_DROP_ACTION(MSCC_MS_ACTION_DROP) |
MSCC_MS_SAM_FLOW_CTRL_DEST_PORT(flow->port);
if (action == MSCC_MS_FLOW_BYPASS)
goto write_ctrl;
if (bank == MACSEC_INGR) {
if (priv->secy->replay_protect)
val |= MSCC_MS_SAM_FLOW_CTRL_REPLAY_PROTECT;
if (priv->secy->validate_frames == MACSEC_VALIDATE_STRICT)
val |= MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(MSCC_MS_VALIDATE_STRICT);
else if (priv->secy->validate_frames == MACSEC_VALIDATE_CHECK)
val |= MSCC_MS_SAM_FLOW_CTRL_VALIDATE_FRAMES(MSCC_MS_VALIDATE_CHECK);
} else if (bank == MACSEC_EGR) {
if (priv->secy->protect_frames)
val |= MSCC_MS_SAM_FLOW_CTRL_PROTECT_FRAME;
if (priv->secy->tx_sc.encrypt)
val |= MSCC_MS_SAM_FLOW_CTRL_CONF_PROTECT;
if (priv->secy->tx_sc.send_sci)
val |= MSCC_MS_SAM_FLOW_CTRL_INCLUDE_SCI;
}
write_ctrl:
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val);
}
static struct macsec_flow *vsc8584_macsec_find_flow(struct macsec_context *ctx,
enum macsec_bank bank)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *pos, *tmp;
list_for_each_entry_safe(pos, tmp, &priv->macsec_flows, list)
if (pos->assoc_num == ctx->sa.assoc_num && pos->bank == bank)
return pos;
return ERR_PTR(-ENOENT);
}
static void vsc8584_macsec_flow_enable(struct phy_device *phydev,
struct macsec_flow *flow)
{
enum macsec_bank bank = flow->bank;
u32 val, idx = flow->index;
if ((flow->bank == MACSEC_INGR && flow->rx_sa && !flow->rx_sa->active) ||
(flow->bank == MACSEC_EGR && flow->tx_sa && !flow->tx_sa->active))
return;
/* Enable */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_ENTRY_SET1, BIT(idx));
/* Set in-use */
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx));
val |= MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val);
}
static void vsc8584_macsec_flow_disable(struct phy_device *phydev,
struct macsec_flow *flow)
{
enum macsec_bank bank = flow->bank;
u32 val, idx = flow->index;
/* Disable */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_ENTRY_CLEAR1, BIT(idx));
/* Clear in-use */
val = vsc8584_macsec_phy_read(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx));
val &= ~MSCC_MS_SAM_FLOW_CTRL_SA_IN_USE;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_SAM_FLOW_CTRL(idx), val);
}
static u32 vsc8584_macsec_flow_context_id(struct macsec_flow *flow)
{
if (flow->bank == MACSEC_INGR)
return flow->index + MSCC_MS_MAX_FLOWS;
return flow->index;
}
/* Derive the AES key to get a key for the hash autentication */
static int vsc8584_macsec_derive_key(const u8 key[MACSEC_KEYID_LEN],
u16 key_len, u8 hkey[16])
{
struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
struct skcipher_request *req = NULL;
struct scatterlist src, dst;
DECLARE_CRYPTO_WAIT(wait);
u32 input[4] = {0};
int ret;
if (IS_ERR(tfm))
return PTR_ERR(tfm);
req = skcipher_request_alloc(tfm, GFP_KERNEL);
if (!req) {
ret = -ENOMEM;
goto out;
}
skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP, crypto_req_done,
&wait);
ret = crypto_skcipher_setkey(tfm, key, key_len);
if (ret < 0)
goto out;
sg_init_one(&src, input, 16);
sg_init_one(&dst, hkey, 16);
skcipher_request_set_crypt(req, &src, &dst, 16, NULL);
ret = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
out:
skcipher_request_free(req);
crypto_free_skcipher(tfm);
return ret;
}
static int vsc8584_macsec_transformation(struct phy_device *phydev,
struct macsec_flow *flow)
{
struct vsc8531_private *priv = phydev->priv;
enum macsec_bank bank = flow->bank;
int i, ret, index = flow->index;
u32 rec = 0, control = 0;
u8 hkey[16];
sci_t sci;
ret = vsc8584_macsec_derive_key(flow->key, priv->secy->key_len, hkey);
if (ret)
return ret;
switch (priv->secy->key_len) {
case 16:
control |= CONTROL_CRYPTO_ALG(CTRYPTO_ALG_AES_CTR_128);
break;
case 32:
control |= CONTROL_CRYPTO_ALG(CTRYPTO_ALG_AES_CTR_256);
break;
default:
return -EINVAL;
}
control |= (bank == MACSEC_EGR) ?
(CONTROL_TYPE_EGRESS | CONTROL_AN(priv->secy->tx_sc.encoding_sa)) :
(CONTROL_TYPE_INGRESS | CONTROL_SEQ_MASK);
control |= CONTROL_UPDATE_SEQ | CONTROL_ENCRYPT_AUTH | CONTROL_KEY_IN_CTX |
CONTROL_IV0 | CONTROL_IV1 | CONTROL_IV_IN_SEQ |
CONTROL_DIGEST_TYPE(0x2) | CONTROL_SEQ_TYPE(0x1) |
CONTROL_AUTH_ALG(AUTH_ALG_AES_GHAS) | CONTROL_CONTEXT_ID;
/* Set the control word */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
control);
/* Set the context ID. Must be unique. */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
vsc8584_macsec_flow_context_id(flow));
/* Set the encryption/decryption key */
for (i = 0; i < priv->secy->key_len / sizeof(u32); i++)
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MS_XFORM_REC(index, rec++),
((u32 *)flow->key)[i]);
/* Set the authentication key */
for (i = 0; i < 4; i++)
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MS_XFORM_REC(index, rec++),
((u32 *)hkey)[i]);
/* Initial sequence number */
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
bank == MACSEC_INGR ?
flow->rx_sa->next_pn : flow->tx_sa->next_pn);
if (bank == MACSEC_INGR)
/* Set the mask (replay window size) */
vsc8584_macsec_phy_write(phydev, bank,
MSCC_MS_XFORM_REC(index, rec++),
priv->secy->replay_window);
/* Set the input vectors */
sci = bank == MACSEC_INGR ? flow->rx_sa->sc->sci : priv->secy->sci;
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
lower_32_bits(sci));
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
upper_32_bits(sci));
while (rec < 20)
vsc8584_macsec_phy_write(phydev, bank, MSCC_MS_XFORM_REC(index, rec++),
0);
flow->has_transformation = true;
return 0;
}
static struct macsec_flow *vsc8584_macsec_alloc_flow(struct vsc8531_private *priv,
enum macsec_bank bank)
{
unsigned long *bitmap = bank == MACSEC_INGR ?
&priv->ingr_flows : &priv->egr_flows;
struct macsec_flow *flow;
int index;
index = find_first_zero_bit(bitmap, MSCC_MS_MAX_FLOWS);
if (index == MSCC_MS_MAX_FLOWS)
return ERR_PTR(-ENOMEM);
flow = kzalloc(sizeof(*flow), GFP_KERNEL);
if (!flow)
return ERR_PTR(-ENOMEM);
set_bit(index, bitmap);
flow->index = index;
flow->bank = bank;
flow->priority = 8;
flow->assoc_num = -1;
list_add_tail(&flow->list, &priv->macsec_flows);
return flow;
}
static void vsc8584_macsec_free_flow(struct vsc8531_private *priv,
struct macsec_flow *flow)
{
unsigned long *bitmap = flow->bank == MACSEC_INGR ?
&priv->ingr_flows : &priv->egr_flows;
list_del(&flow->list);
clear_bit(flow->index, bitmap);
kfree(flow);
}
static int vsc8584_macsec_add_flow(struct phy_device *phydev,
struct macsec_flow *flow, bool update)
{
int ret;
flow->port = MSCC_MS_PORT_CONTROLLED;
vsc8584_macsec_flow(phydev, flow);
if (update)
return 0;
ret = vsc8584_macsec_transformation(phydev, flow);
if (ret) {
vsc8584_macsec_free_flow(phydev->priv, flow);
return ret;
}
return 0;
}
static int vsc8584_macsec_default_flows(struct phy_device *phydev)
{
struct macsec_flow *flow;
/* Add a rule to let the MKA traffic go through, ingress */
flow = vsc8584_macsec_alloc_flow(phydev->priv, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
flow->priority = 15;
flow->port = MSCC_MS_PORT_UNCONTROLLED;
flow->match.tagged = 1;
flow->match.untagged = 1;
flow->match.etype = 1;
flow->etype = ETH_P_PAE;
flow->action.bypass = 1;
vsc8584_macsec_flow(phydev, flow);
vsc8584_macsec_flow_enable(phydev, flow);
/* Add a rule to let the MKA traffic go through, egress */
flow = vsc8584_macsec_alloc_flow(phydev->priv, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
flow->priority = 15;
flow->port = MSCC_MS_PORT_COMMON;
flow->match.untagged = 1;
flow->match.etype = 1;
flow->etype = ETH_P_PAE;
flow->action.bypass = 1;
vsc8584_macsec_flow(phydev, flow);
vsc8584_macsec_flow_enable(phydev, flow);
return 0;
}
static void vsc8584_macsec_del_flow(struct phy_device *phydev,
struct macsec_flow *flow)
{
vsc8584_macsec_flow_disable(phydev, flow);
vsc8584_macsec_free_flow(phydev->priv, flow);
}
static int __vsc8584_macsec_add_rxsa(struct macsec_context *ctx,
struct macsec_flow *flow, bool update)
{
struct phy_device *phydev = ctx->phydev;
struct vsc8531_private *priv = phydev->priv;
if (!flow) {
flow = vsc8584_macsec_alloc_flow(priv, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
memcpy(flow->key, ctx->sa.key, priv->secy->key_len);
}
flow->assoc_num = ctx->sa.assoc_num;
flow->rx_sa = ctx->sa.rx_sa;
/* Always match tagged packets on ingress */
flow->match.tagged = 1;
flow->match.sci = 1;
if (priv->secy->validate_frames != MACSEC_VALIDATE_DISABLED)
flow->match.untagged = 1;
return vsc8584_macsec_add_flow(phydev, flow, update);
}
static int __vsc8584_macsec_add_txsa(struct macsec_context *ctx,
struct macsec_flow *flow, bool update)
{
struct phy_device *phydev = ctx->phydev;
struct vsc8531_private *priv = phydev->priv;
if (!flow) {
flow = vsc8584_macsec_alloc_flow(priv, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
memcpy(flow->key, ctx->sa.key, priv->secy->key_len);
}
flow->assoc_num = ctx->sa.assoc_num;
flow->tx_sa = ctx->sa.tx_sa;
/* Always match untagged packets on egress */
flow->match.untagged = 1;
return vsc8584_macsec_add_flow(phydev, flow, update);
}
static int vsc8584_macsec_dev_open(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list)
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_dev_stop(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list)
vsc8584_macsec_flow_disable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_add_secy(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_secy *secy = ctx->secy;
if (ctx->prepare) {
if (priv->secy)
return -EEXIST;
return 0;
}
priv->secy = secy;
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_EGR,
secy->validate_frames != MACSEC_VALIDATE_DISABLED);
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_INGR,
secy->validate_frames != MACSEC_VALIDATE_DISABLED);
return vsc8584_macsec_default_flows(ctx->phydev);
}
static int vsc8584_macsec_del_secy(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list)
vsc8584_macsec_del_flow(ctx->phydev, flow);
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_EGR, false);
vsc8584_macsec_flow_default_action(ctx->phydev, MACSEC_INGR, false);
priv->secy = NULL;
return 0;
}
static int vsc8584_macsec_upd_secy(struct macsec_context *ctx)
{
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
vsc8584_macsec_del_secy(ctx);
return vsc8584_macsec_add_secy(ctx);
}
static int vsc8584_macsec_add_rxsc(struct macsec_context *ctx)
{
/* Nothing to do */
return 0;
}
static int vsc8584_macsec_upd_rxsc(struct macsec_context *ctx)
{
return -EOPNOTSUPP;
}
static int vsc8584_macsec_del_rxsc(struct macsec_context *ctx)
{
struct vsc8531_private *priv = ctx->phydev->priv;
struct macsec_flow *flow, *tmp;
/* No operation to perform before the commit step */
if (ctx->prepare)
return 0;
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) {
if (flow->bank == MACSEC_INGR && flow->rx_sa &&
flow->rx_sa->sc->sci == ctx->rx_sc->sci)
vsc8584_macsec_del_flow(ctx->phydev, flow);
}
return 0;
}
static int vsc8584_macsec_add_rxsa(struct macsec_context *ctx)
{
struct macsec_flow *flow = NULL;
if (ctx->prepare)
return __vsc8584_macsec_add_rxsa(ctx, flow, false);
flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_upd_rxsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare) {
/* Make sure the flow is disabled before updating it */
vsc8584_macsec_flow_disable(ctx->phydev, flow);
return __vsc8584_macsec_add_rxsa(ctx, flow, true);
}
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_del_rxsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_INGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare)
return 0;
vsc8584_macsec_del_flow(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_add_txsa(struct macsec_context *ctx)
{
struct macsec_flow *flow = NULL;
if (ctx->prepare)
return __vsc8584_macsec_add_txsa(ctx, flow, false);
flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_upd_txsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare) {
/* Make sure the flow is disabled before updating it */
vsc8584_macsec_flow_disable(ctx->phydev, flow);
return __vsc8584_macsec_add_txsa(ctx, flow, true);
}
vsc8584_macsec_flow_enable(ctx->phydev, flow);
return 0;
}
static int vsc8584_macsec_del_txsa(struct macsec_context *ctx)
{
struct macsec_flow *flow;
flow = vsc8584_macsec_find_flow(ctx, MACSEC_EGR);
if (IS_ERR(flow))
return PTR_ERR(flow);
if (ctx->prepare)
return 0;
vsc8584_macsec_del_flow(ctx->phydev, flow);
return 0;
}
static struct macsec_ops vsc8584_macsec_ops = {
.mdo_dev_open = vsc8584_macsec_dev_open,
.mdo_dev_stop = vsc8584_macsec_dev_stop,
.mdo_add_secy = vsc8584_macsec_add_secy,
.mdo_upd_secy = vsc8584_macsec_upd_secy,
.mdo_del_secy = vsc8584_macsec_del_secy,
.mdo_add_rxsc = vsc8584_macsec_add_rxsc,
.mdo_upd_rxsc = vsc8584_macsec_upd_rxsc,
.mdo_del_rxsc = vsc8584_macsec_del_rxsc,
.mdo_add_rxsa = vsc8584_macsec_add_rxsa,
.mdo_upd_rxsa = vsc8584_macsec_upd_rxsa,
.mdo_del_rxsa = vsc8584_macsec_del_rxsa,
.mdo_add_txsa = vsc8584_macsec_add_txsa,
.mdo_upd_txsa = vsc8584_macsec_upd_txsa,
.mdo_del_txsa = vsc8584_macsec_del_txsa,
};
#endif /* CONFIG_MACSEC */
/* Check if one PHY has already done the init of the parts common to all PHYs /* Check if one PHY has already done the init of the parts common to all PHYs
* in the Quad PHY package. * in the Quad PHY package.
*/ */
...@@ -2797,23 +1400,9 @@ static int vsc8584_config_init(struct phy_device *phydev) ...@@ -2797,23 +1400,9 @@ static int vsc8584_config_init(struct phy_device *phydev)
mutex_unlock(&phydev->mdio.bus->mdio_lock); mutex_unlock(&phydev->mdio.bus->mdio_lock);
#if IS_ENABLED(CONFIG_MACSEC)
/* MACsec */
switch (phydev->phy_id & phydev->drv->phy_id_mask) {
case PHY_ID_VSC856X:
case PHY_ID_VSC8575:
case PHY_ID_VSC8582:
case PHY_ID_VSC8584:
INIT_LIST_HEAD(&vsc8531->macsec_flows);
vsc8531->secy = NULL;
phydev->macsec_ops = &vsc8584_macsec_ops;
ret = vsc8584_macsec_init(phydev); ret = vsc8584_macsec_init(phydev);
if (ret) if (ret)
goto err; return ret;
}
#endif
phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
...@@ -2842,37 +1431,7 @@ static int vsc8584_config_init(struct phy_device *phydev) ...@@ -2842,37 +1431,7 @@ static int vsc8584_config_init(struct phy_device *phydev)
static int vsc8584_handle_interrupt(struct phy_device *phydev) static int vsc8584_handle_interrupt(struct phy_device *phydev)
{ {
#if IS_ENABLED(CONFIG_MACSEC) vsc8584_handle_macsec_interrupt(phydev);
struct vsc8531_private *priv = phydev->priv;
struct macsec_flow *flow, *tmp;
u32 cause, rec;
/* Check MACsec PN rollover */
cause = vsc8584_macsec_phy_read(phydev, MACSEC_EGR,
MSCC_MS_INTR_CTRL_STATUS);
cause &= MSCC_MS_INTR_CTRL_STATUS_INTR_CLR_STATUS_M;
if (!(cause & MACSEC_INTR_CTRL_STATUS_ROLLOVER))
goto skip_rollover;
rec = 6 + priv->secy->key_len / sizeof(u32);
list_for_each_entry_safe(flow, tmp, &priv->macsec_flows, list) {
u32 val;
if (flow->bank != MACSEC_EGR || !flow->has_transformation)
continue;
val = vsc8584_macsec_phy_read(phydev, MACSEC_EGR,
MSCC_MS_XFORM_REC(flow->index, rec));
if (val == 0xffffffff) {
vsc8584_macsec_flow_disable(phydev, flow);
macsec_pn_wrapped(priv->secy, flow->tx_sa);
break;
}
}
skip_rollover:
#endif
phy_mac_interrupt(phydev); phy_mac_interrupt(phydev);
return 0; return 0;
} }
...@@ -3320,20 +1879,8 @@ static int vsc85xx_config_intr(struct phy_device *phydev) ...@@ -3320,20 +1879,8 @@ static int vsc85xx_config_intr(struct phy_device *phydev)
int rc; int rc;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
#if IS_ENABLED(CONFIG_MACSEC) vsc8584_config_macsec_intr(phydev);
phy_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_EXTENDED_2);
phy_write(phydev, MSCC_PHY_EXTENDED_INT,
MSCC_PHY_EXTENDED_INT_MS_EGR);
phy_write(phydev, MSCC_EXT_PAGE_ACCESS,
MSCC_PHY_PAGE_STANDARD);
vsc8584_macsec_phy_write(phydev, MACSEC_EGR,
MSCC_MS_AIC_CTRL, 0xf);
vsc8584_macsec_phy_write(phydev, MACSEC_EGR,
MSCC_MS_INTR_CTRL_STATUS,
MSCC_MS_INTR_CTRL_STATUS_INTR_ENABLE(MACSEC_INTR_CTRL_STATUS_ROLLOVER));
#endif
rc = phy_write(phydev, MII_VSC85XX_INT_MASK, rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
MII_VSC85XX_INT_MASK_MASK); MII_VSC85XX_INT_MASK_MASK);
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment