summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/net/macsec.c151
-rw-r--r--drivers/net/netdevsim/macsec.c5
-rw-r--r--drivers/net/phy/Kconfig3
-rw-r--r--drivers/net/phy/Makefile6
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx-macsec.c1729
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.c77
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.h62
-rw-r--r--include/linux/skbuff.h1
-rw-r--r--include/net/macsec.h54
-rw-r--r--net/core/skbuff.c25
-rw-r--r--net/dsa/user.c29
12 files changed, 2043 insertions, 101 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index fc044884c472..2b916990d7f0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15444,7 +15444,7 @@ NXP C45 TJA11XX PHY DRIVER
M: Radu Pirea <radu-nicolae.pirea@oss.nxp.com>
L: netdev@vger.kernel.org
S: Maintained
-F: drivers/net/phy/nxp-c45-tja11xx.c
+F: drivers/net/phy/nxp-c45-tja11xx*
NXP FSPI DRIVER
M: Han Xu <han.xu@nxp.com>
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 9663050a852d..e34816638569 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -93,6 +93,8 @@ struct pcpu_secy_stats {
* @secys: linked list of SecY's on the underlying device
* @gro_cells: pointer to the Generic Receive Offload cell
* @offload: status of offloading on the MACsec device
+ * @insert_tx_tag: when offloading, device requires to insert an
+ * additional tag
*/
struct macsec_dev {
struct macsec_secy secy;
@@ -102,6 +104,7 @@ struct macsec_dev {
struct list_head secys;
struct gro_cells gro_cells;
enum macsec_offload offload;
+ bool insert_tx_tag;
};
/**
@@ -604,26 +607,11 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
return ERR_PTR(-EINVAL);
}
- if (unlikely(skb_headroom(skb) < MACSEC_NEEDED_HEADROOM ||
- skb_tailroom(skb) < MACSEC_NEEDED_TAILROOM)) {
- struct sk_buff *nskb = skb_copy_expand(skb,
- MACSEC_NEEDED_HEADROOM,
- MACSEC_NEEDED_TAILROOM,
- GFP_ATOMIC);
- if (likely(nskb)) {
- consume_skb(skb);
- skb = nskb;
- } else {
- macsec_txsa_put(tx_sa);
- kfree_skb(skb);
- return ERR_PTR(-ENOMEM);
- }
- } else {
- skb = skb_unshare(skb, GFP_ATOMIC);
- if (!skb) {
- macsec_txsa_put(tx_sa);
- return ERR_PTR(-ENOMEM);
- }
+ ret = skb_ensure_writable_head_tail(skb, dev);
+ if (unlikely(ret < 0)) {
+ macsec_txsa_put(tx_sa);
+ kfree_skb(skb);
+ return ERR_PTR(ret);
}
unprotected_len = skb->len;
@@ -2583,6 +2571,33 @@ static bool macsec_is_configured(struct macsec_dev *macsec)
return false;
}
+static bool macsec_needs_tx_tag(struct macsec_dev *macsec,
+ const struct macsec_ops *ops)
+{
+ return macsec->offload == MACSEC_OFFLOAD_PHY &&
+ ops->mdo_insert_tx_tag;
+}
+
+static void macsec_set_head_tail_room(struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ struct net_device *real_dev = macsec->real_dev;
+ int needed_headroom, needed_tailroom;
+ const struct macsec_ops *ops;
+
+ ops = macsec_get_ops(macsec, NULL);
+ if (ops) {
+ needed_headroom = ops->needed_headroom;
+ needed_tailroom = ops->needed_tailroom;
+ } else {
+ needed_headroom = MACSEC_NEEDED_HEADROOM;
+ needed_tailroom = MACSEC_NEEDED_TAILROOM;
+ }
+
+ dev->needed_headroom = real_dev->needed_headroom + needed_headroom;
+ dev->needed_tailroom = real_dev->needed_tailroom + needed_tailroom;
+}
+
static int macsec_update_offload(struct net_device *dev, enum macsec_offload offload)
{
enum macsec_offload prev_offload;
@@ -2620,8 +2635,13 @@ static int macsec_update_offload(struct net_device *dev, enum macsec_offload off
ctx.secy = &macsec->secy;
ret = offload == MACSEC_OFFLOAD_OFF ? macsec_offload(ops->mdo_del_secy, &ctx)
: macsec_offload(ops->mdo_add_secy, &ctx);
- if (ret)
+ if (ret) {
macsec->offload = prev_offload;
+ return ret;
+ }
+
+ macsec_set_head_tail_room(dev);
+ macsec->insert_tx_tag = macsec_needs_tx_tag(macsec, ops);
return ret;
}
@@ -3379,6 +3399,40 @@ static struct genl_family macsec_fam __ro_after_init = {
.resv_start_op = MACSEC_CMD_UPD_OFFLOAD + 1,
};
+static struct sk_buff *macsec_insert_tx_tag(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct macsec_dev *macsec = macsec_priv(dev);
+ const struct macsec_ops *ops;
+ struct phy_device *phydev;
+ struct macsec_context ctx;
+ int skb_final_len;
+ int err;
+
+ ops = macsec_get_ops(macsec, &ctx);
+ skb_final_len = skb->len - ETH_HLEN + ops->needed_headroom +
+ ops->needed_tailroom;
+ if (unlikely(skb_final_len > macsec->real_dev->mtu)) {
+ err = -EINVAL;
+ goto cleanup;
+ }
+
+ phydev = macsec->real_dev->phydev;
+
+ err = skb_ensure_writable_head_tail(skb, dev);
+ if (unlikely(err < 0))
+ goto cleanup;
+
+ err = ops->mdo_insert_tx_tag(phydev, skb);
+ if (unlikely(err))
+ goto cleanup;
+
+ return skb;
+cleanup:
+ kfree_skb(skb);
+ return ERR_PTR(err);
+}
+
static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -3393,6 +3447,15 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
skb_dst_drop(skb);
dst_hold(&md_dst->dst);
skb_dst_set(skb, &md_dst->dst);
+
+ if (macsec->insert_tx_tag) {
+ skb = macsec_insert_tx_tag(skb, dev);
+ if (IS_ERR(skb)) {
+ DEV_STATS_INC(dev, tx_dropped);
+ return NETDEV_TX_OK;
+ }
+ }
+
skb->dev = macsec->real_dev;
return dev_queue_xmit(skb);
}
@@ -3454,10 +3517,7 @@ static int macsec_dev_init(struct net_device *dev)
dev->features = real_dev->features & MACSEC_FEATURES;
dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
- dev->needed_headroom = real_dev->needed_headroom +
- MACSEC_NEEDED_HEADROOM;
- dev->needed_tailroom = real_dev->needed_tailroom +
- MACSEC_NEEDED_TAILROOM;
+ macsec_set_head_tail_room(dev);
if (is_zero_ether_addr(dev->dev_addr))
eth_hw_addr_inherit(dev, real_dev);
@@ -3604,21 +3664,19 @@ static int macsec_set_mac_address(struct net_device *dev, void *p)
struct macsec_dev *macsec = macsec_priv(dev);
struct net_device *real_dev = macsec->real_dev;
struct sockaddr *addr = p;
+ u8 old_addr[ETH_ALEN];
int err;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- if (!(dev->flags & IFF_UP))
- goto out;
-
- err = dev_uc_add(real_dev, addr->sa_data);
- if (err < 0)
- return err;
-
- dev_uc_del(real_dev, dev->dev_addr);
+ if (dev->flags & IFF_UP) {
+ err = dev_uc_add(real_dev, addr->sa_data);
+ if (err < 0)
+ return err;
+ }
-out:
+ ether_addr_copy(old_addr, dev->dev_addr);
eth_hw_addr_set(dev, addr->sa_data);
/* If h/w offloading is available, propagate to the device */
@@ -3627,13 +3685,29 @@ out:
struct macsec_context ctx;
ops = macsec_get_ops(macsec, &ctx);
- if (ops) {
- ctx.secy = &macsec->secy;
- macsec_offload(ops->mdo_upd_secy, &ctx);
+ if (!ops) {
+ err = -EOPNOTSUPP;
+ goto restore_old_addr;
}
+
+ ctx.secy = &macsec->secy;
+ err = macsec_offload(ops->mdo_upd_secy, &ctx);
+ if (err)
+ goto restore_old_addr;
}
+ if (dev->flags & IFF_UP)
+ dev_uc_del(real_dev, old_addr);
+
return 0;
+
+restore_old_addr:
+ if (dev->flags & IFF_UP)
+ dev_uc_del(real_dev, addr->sa_data);
+
+ eth_hw_addr_set(dev, old_addr);
+
+ return err;
}
static int macsec_change_mtu(struct net_device *dev, int new_mtu)
@@ -4126,6 +4200,9 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
err = macsec_offload(ops->mdo_add_secy, &ctx);
if (err)
goto del_dev;
+
+ macsec->insert_tx_tag =
+ macsec_needs_tx_tag(macsec, ops);
}
}
diff --git a/drivers/net/netdevsim/macsec.c b/drivers/net/netdevsim/macsec.c
index 0d5f50430dd3..aa007b1e4b78 100644
--- a/drivers/net/netdevsim/macsec.c
+++ b/drivers/net/netdevsim/macsec.c
@@ -3,11 +3,6 @@
#include <net/macsec.h>
#include "netdevsim.h"
-static inline u64 sci_to_cpu(sci_t sci)
-{
- return be64_to_cpu((__force __be64)sci);
-}
-
static int nsim_macsec_find_secy(struct netdevsim *ns, sci_t sci)
{
int i;
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 2e4667bf9ff5..9e2672800f0b 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -317,9 +317,10 @@ config NXP_CBTX_PHY
config NXP_C45_TJA11XX_PHY
tristate "NXP C45 TJA11XX PHYs"
depends on PTP_1588_CLOCK_OPTIONAL
+ depends on MACSEC || !MACSEC
help
Enable support for NXP C45 TJA11XX PHYs.
- Currently supports the TJA1103 and TJA1120 PHYs.
+ Currently supports the TJA1103, TJA1104 and TJA1120 PHYs.
config NXP_TJA11XX_PHY
tristate "NXP TJA11xx PHYs support"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index e35ea69d9cb4..6097afd44392 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -84,7 +84,11 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc/
obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o
obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_NCN26000_PHY) += ncn26000.o
-obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o
+nxp-c45-tja-objs += nxp-c45-tja11xx.o
+ifdef CONFIG_MACSEC
+nxp-c45-tja-objs += nxp-c45-tja11xx-macsec.o
+endif
+obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja.o
obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o
obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
diff --git a/drivers/net/phy/nxp-c45-tja11xx-macsec.c b/drivers/net/phy/nxp-c45-tja11xx-macsec.c
new file mode 100644
index 000000000000..550ef08970f4
--- /dev/null
+++ b/drivers/net/phy/nxp-c45-tja11xx-macsec.c
@@ -0,0 +1,1729 @@
+// SPDX-License-Identifier: GPL-2.0
+/* NXP C45 PTP PHY driver interface
+ * Copyright 2023 NXP
+ * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/ethtool_netlink.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/processor.h>
+#include <net/dst_metadata.h>
+#include <net/macsec.h>
+
+#include "nxp-c45-tja11xx.h"
+
+#define MACSEC_REG_SIZE 32
+#define TX_SC_MAX 4
+
+#define TX_SC_BIT(secy_id) BIT(MACSEC_REG_SIZE - (secy_id) - 1)
+
+#define VEND1_MACSEC_BASE 0x9000
+
+#define MACSEC_CFG 0x0000
+#define MACSEC_CFG_BYPASS BIT(1)
+#define MACSEC_CFG_S0I BIT(0)
+
+#define MACSEC_TPNET 0x0044
+#define PN_WRAP_THRESHOLD 0xffffffff
+
+#define MACSEC_RXSCA 0x0080
+#define MACSEC_RXSCKA 0x0084
+
+#define MACSEC_TXSCA 0x00C0
+#define MACSEC_TXSCKA 0x00C4
+
+#define MACSEC_RXSC_SCI_1H 0x0100
+
+#define MACSEC_RXSC_CFG 0x0128
+#define MACSEC_RXSC_CFG_XPN BIT(25)
+#define MACSEC_RXSC_CFG_AES_256 BIT(24)
+#define MACSEC_RXSC_CFG_SCI_EN BIT(11)
+#define MACSEC_RXSC_CFG_RP BIT(10)
+#define MACSEC_RXSC_CFG_VF_MASK GENMASK(9, 8)
+#define MACSEC_RXSC_CFG_VF_OFF 8
+
+#define MACSEC_RPW 0x012C
+
+#define MACSEC_RXSA_A_CS 0x0180
+#define MACSEC_RXSA_A_NPN 0x0184
+#define MACSEC_RXSA_A_XNPN 0x0188
+#define MACSEC_RXSA_A_LNPN 0x018C
+#define MACSEC_RXSA_A_LXNPN 0x0190
+
+#define MACSEC_RXSA_B_CS 0x01C0
+#define MACSEC_RXSA_B_NPN 0x01C4
+#define MACSEC_RXSA_B_XNPN 0x01C8
+#define MACSEC_RXSA_B_LNPN 0x01CC
+#define MACSEC_RXSA_B_LXNPN 0x01D0
+
+#define MACSEC_RXSA_CS_AN_OFF 1
+#define MACSEC_RXSA_CS_EN BIT(0)
+
+#define MACSEC_TXSC_SCI_1H 0x0200
+#define MACSEC_TXSC_CFG 0x0228
+#define MACSEC_TXSC_CFG_XPN BIT(25)
+#define MACSEC_TXSC_CFG_AES_256 BIT(24)
+#define MACSEC_TXSC_CFG_AN_MASK GENMASK(19, 18)
+#define MACSEC_TXSC_CFG_AN_OFF 18
+#define MACSEC_TXSC_CFG_ASA BIT(17)
+#define MACSEC_TXSC_CFG_SCE BIT(16)
+#define MACSEC_TXSC_CFG_ENCRYPT BIT(4)
+#define MACSEC_TXSC_CFG_PROTECT BIT(3)
+#define MACSEC_TXSC_CFG_SEND_SCI BIT(2)
+#define MACSEC_TXSC_CFG_END_STATION BIT(1)
+#define MACSEC_TXSC_CFG_SCB BIT(0)
+
+#define MACSEC_TXSA_A_CS 0x0280
+#define MACSEC_TXSA_A_NPN 0x0284
+#define MACSEC_TXSA_A_XNPN 0x0288
+
+#define MACSEC_TXSA_B_CS 0x02C0
+#define MACSEC_TXSA_B_NPN 0x02C4
+#define MACSEC_TXSA_B_XNPN 0x02C8
+
+#define MACSEC_SA_CS_A BIT(31)
+
+#define MACSEC_EVR 0x0400
+#define MACSEC_EVER 0x0404
+
+#define MACSEC_RXSA_A_KA 0x0700
+#define MACSEC_RXSA_A_SSCI 0x0720
+#define MACSEC_RXSA_A_SALT 0x0724
+
+#define MACSEC_RXSA_B_KA 0x0740
+#define MACSEC_RXSA_B_SSCI 0x0760
+#define MACSEC_RXSA_B_SALT 0x0764
+
+#define MACSEC_TXSA_A_KA 0x0780
+#define MACSEC_TXSA_A_SSCI 0x07A0
+#define MACSEC_TXSA_A_SALT 0x07A4
+
+#define MACSEC_TXSA_B_KA 0x07C0
+#define MACSEC_TXSA_B_SSCI 0x07E0
+#define MACSEC_TXSA_B_SALT 0x07E4
+
+#define MACSEC_UPFR0D2 0x0A08
+#define MACSEC_UPFR0M1 0x0A10
+#define MACSEC_OVP BIT(12)
+
+#define MACSEC_UPFR0M2 0x0A14
+#define ETYPE_MASK 0xffff
+
+#define MACSEC_UPFR0R 0x0A18
+#define MACSEC_UPFR_EN BIT(0)
+
+#define ADPTR_CNTRL 0x0F00
+#define ADPTR_CNTRL_CONFIG_EN BIT(14)
+#define ADPTR_CNTRL_ADPTR_EN BIT(12)
+#define ADPTR_TX_TAG_CNTRL 0x0F0C
+#define ADPTR_TX_TAG_CNTRL_ENA BIT(31)
+
+#define TX_SC_FLT_BASE 0x800
+#define TX_SC_FLT_SIZE 0x10
+#define TX_FLT_BASE(flt_id) (TX_SC_FLT_BASE + \
+ TX_SC_FLT_SIZE * (flt_id))
+
+#define TX_SC_FLT_OFF_MAC_DA_SA 0x04
+#define TX_SC_FLT_OFF_MAC_SA 0x08
+#define TX_SC_FLT_OFF_MAC_CFG 0x0C
+#define TX_SC_FLT_BY_SA BIT(14)
+#define TX_SC_FLT_EN BIT(8)
+
+#define TX_SC_FLT_MAC_DA_SA(base) ((base) + TX_SC_FLT_OFF_MAC_DA_SA)
+#define TX_SC_FLT_MAC_SA(base) ((base) + TX_SC_FLT_OFF_MAC_SA)
+#define TX_SC_FLT_MAC_CFG(base) ((base) + TX_SC_FLT_OFF_MAC_CFG)
+
+#define ADAPTER_EN BIT(6)
+#define MACSEC_EN BIT(5)
+
+#define MACSEC_INOV1HS 0x0140
+#define MACSEC_INOV2HS 0x0144
+#define MACSEC_INOD1HS 0x0148
+#define MACSEC_INOD2HS 0x014C
+#define MACSEC_RXSCIPUS 0x0150
+#define MACSEC_RXSCIPDS 0x0154
+#define MACSEC_RXSCIPLS 0x0158
+#define MACSEC_RXAN0INUSS 0x0160
+#define MACSEC_RXAN0IPUSS 0x0170
+#define MACSEC_RXSA_A_IPOS 0x0194
+#define MACSEC_RXSA_A_IPIS 0x01B0
+#define MACSEC_RXSA_A_IPNVS 0x01B4
+#define MACSEC_RXSA_B_IPOS 0x01D4
+#define MACSEC_RXSA_B_IPIS 0x01F0
+#define MACSEC_RXSA_B_IPNVS 0x01F4
+#define MACSEC_OPUS 0x021C
+#define MACSEC_OPTLS 0x022C
+#define MACSEC_OOP1HS 0x0240
+#define MACSEC_OOP2HS 0x0244
+#define MACSEC_OOE1HS 0x0248
+#define MACSEC_OOE2HS 0x024C
+#define MACSEC_TXSA_A_OPPS 0x028C
+#define MACSEC_TXSA_A_OPES 0x0290
+#define MACSEC_TXSA_B_OPPS 0x02CC
+#define MACSEC_TXSA_B_OPES 0x02D0
+#define MACSEC_INPWTS 0x0630
+#define MACSEC_INPBTS 0x0638
+#define MACSEC_IPSNFS 0x063C
+
+#define TJA11XX_TLV_TX_NEEDED_HEADROOM (32)
+#define TJA11XX_TLV_NEEDED_TAILROOM (0)
+
+#define ETH_P_TJA11XX_TLV (0x4e58)
+
+enum nxp_c45_sa_type {
+ TX_SA,
+ RX_SA,
+};
+
+struct nxp_c45_sa {
+ void *sa;
+ const struct nxp_c45_sa_regs *regs;
+ enum nxp_c45_sa_type type;
+ bool is_key_a;
+ u8 an;
+ struct list_head list;
+};
+
+struct nxp_c45_secy {
+ struct macsec_secy *secy;
+ struct macsec_rx_sc *rx_sc;
+ struct list_head sa_list;
+ int secy_id;
+ bool rx_sc0_impl;
+ struct list_head list;
+};
+
+struct nxp_c45_macsec {
+ struct list_head secy_list;
+ DECLARE_BITMAP(secy_bitmap, TX_SC_MAX);
+ DECLARE_BITMAP(tx_sc_bitmap, TX_SC_MAX);
+};
+
+struct nxp_c45_sa_regs {
+ u16 cs;
+ u16 npn;
+ u16 xnpn;
+ u16 lnpn;
+ u16 lxnpn;
+ u16 ka;
+ u16 ssci;
+ u16 salt;
+ u16 ipis;
+ u16 ipnvs;
+ u16 ipos;
+ u16 opps;
+ u16 opes;
+};
+
+static const struct nxp_c45_sa_regs rx_sa_a_regs = {
+ .cs = MACSEC_RXSA_A_CS,
+ .npn = MACSEC_RXSA_A_NPN,
+ .xnpn = MACSEC_RXSA_A_XNPN,
+ .lnpn = MACSEC_RXSA_A_LNPN,
+ .lxnpn = MACSEC_RXSA_A_LXNPN,
+ .ka = MACSEC_RXSA_A_KA,
+ .ssci = MACSEC_RXSA_A_SSCI,
+ .salt = MACSEC_RXSA_A_SALT,
+ .ipis = MACSEC_RXSA_A_IPIS,
+ .ipnvs = MACSEC_RXSA_A_IPNVS,
+ .ipos = MACSEC_RXSA_A_IPOS,
+};
+
+static const struct nxp_c45_sa_regs rx_sa_b_regs = {
+ .cs = MACSEC_RXSA_B_CS,
+ .npn = MACSEC_RXSA_B_NPN,
+ .xnpn = MACSEC_RXSA_B_XNPN,
+ .lnpn = MACSEC_RXSA_B_LNPN,
+ .lxnpn = MACSEC_RXSA_B_LXNPN,
+ .ka = MACSEC_RXSA_B_KA,
+ .ssci = MACSEC_RXSA_B_SSCI,
+ .salt = MACSEC_RXSA_B_SALT,
+ .ipis = MACSEC_RXSA_B_IPIS,
+ .ipnvs = MACSEC_RXSA_B_IPNVS,
+ .ipos = MACSEC_RXSA_B_IPOS,
+};
+
+static const struct nxp_c45_sa_regs tx_sa_a_regs = {
+ .cs = MACSEC_TXSA_A_CS,
+ .npn = MACSEC_TXSA_A_NPN,
+ .xnpn = MACSEC_TXSA_A_XNPN,
+ .ka = MACSEC_TXSA_A_KA,
+ .ssci = MACSEC_TXSA_A_SSCI,
+ .salt = MACSEC_TXSA_A_SALT,
+ .opps = MACSEC_TXSA_A_OPPS,
+ .opes = MACSEC_TXSA_A_OPES,
+};
+
+static const struct nxp_c45_sa_regs tx_sa_b_regs = {
+ .cs = MACSEC_TXSA_B_CS,
+ .npn = MACSEC_TXSA_B_NPN,
+ .xnpn = MACSEC_TXSA_B_XNPN,
+ .ka = MACSEC_TXSA_B_KA,
+ .ssci = MACSEC_TXSA_B_SSCI,
+ .salt = MACSEC_TXSA_B_SALT,
+ .opps = MACSEC_TXSA_B_OPPS,
+ .opes = MACSEC_TXSA_B_OPES,
+};
+
+static const
+struct nxp_c45_sa_regs *nxp_c45_sa_regs_get(enum nxp_c45_sa_type sa_type,
+ bool key_a)
+{
+ if (sa_type == RX_SA)
+ if (key_a)
+ return &rx_sa_a_regs;
+ else
+ return &rx_sa_b_regs;
+ else if (sa_type == TX_SA)
+ if (key_a)
+ return &tx_sa_a_regs;
+ else
+ return &tx_sa_b_regs;
+ else
+ return NULL;
+}
+
+static int nxp_c45_macsec_write(struct phy_device *phydev, u16 addr, u32 value)
+{
+ u32 lvalue = value;
+ u16 laddr;
+ int ret;
+
+ WARN_ON_ONCE(addr % 4);
+
+ phydev_dbg(phydev, "write addr 0x%x value 0x%x\n", addr, value);
+
+ laddr = VEND1_MACSEC_BASE + addr / 2;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, laddr, lvalue);
+ if (ret)
+ return ret;
+
+ laddr += 1;
+ lvalue >>= 16;
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, laddr, lvalue);
+
+ return ret;
+}
+
+static int nxp_c45_macsec_read(struct phy_device *phydev, u16 addr, u32 *value)
+{
+ u32 lvalue;
+ u16 laddr;
+ int ret;
+
+ WARN_ON_ONCE(addr % 4);
+
+ laddr = VEND1_MACSEC_BASE + addr / 2;
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, laddr);
+ if (ret < 0)
+ return ret;
+
+ laddr += 1;
+ lvalue = (u32)ret & 0xffff;
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, laddr);
+ if (ret < 0)
+ return ret;
+
+ lvalue |= (u32)ret << 16;
+ *value = lvalue;
+
+ phydev_dbg(phydev, "read addr 0x%x value 0x%x\n", addr, *value);
+
+ return 0;
+}
+
+static void nxp_c45_macsec_read32_64(struct phy_device *phydev, u16 addr,
+ u64 *value)
+{
+ u32 lvalue;
+
+ nxp_c45_macsec_read(phydev, addr, &lvalue);
+ *value = lvalue;
+}
+
+static void nxp_c45_macsec_read64(struct phy_device *phydev, u16 addr,
+ u64 *value)
+{
+ u32 lvalue;
+
+ nxp_c45_macsec_read(phydev, addr, &lvalue);
+ *value = (u64)lvalue << 32;
+ nxp_c45_macsec_read(phydev, addr + 4, &lvalue);
+ *value |= lvalue;
+}
+
+static void nxp_c45_secy_irq_en(struct phy_device *phydev,
+ struct nxp_c45_secy *phy_secy, bool en)
+{
+ u32 reg;
+
+ nxp_c45_macsec_read(phydev, MACSEC_EVER, &reg);
+ if (en)
+ reg |= TX_SC_BIT(phy_secy->secy_id);
+ else
+ reg &= ~TX_SC_BIT(phy_secy->secy_id);
+ nxp_c45_macsec_write(phydev, MACSEC_EVER, reg);
+}
+
+static struct nxp_c45_secy *nxp_c45_find_secy(struct list_head *secy_list,
+ sci_t sci)
+{
+ struct nxp_c45_secy *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, secy_list, list)
+ if (pos->secy->sci == sci)
+ return pos;
+
+ return ERR_PTR(-EINVAL);
+}
+
+static struct
+nxp_c45_secy *nxp_c45_find_secy_by_id(struct list_head *secy_list,
+ int id)
+{
+ struct nxp_c45_secy *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, secy_list, list)
+ if (pos->secy_id == id)
+ return pos;
+
+ return ERR_PTR(-EINVAL);
+}
+
+static void nxp_c45_secy_free(struct nxp_c45_secy *phy_secy)
+{
+ list_del(&phy_secy->list);
+ kfree(phy_secy);
+}
+
+static struct nxp_c45_sa *nxp_c45_find_sa(struct list_head *sa_list,
+ enum nxp_c45_sa_type sa_type, u8 an)
+{
+ struct nxp_c45_sa *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, sa_list, list)
+ if (pos->an == an && pos->type == sa_type)
+ return pos;
+
+ return ERR_PTR(-EINVAL);
+}
+
+static struct nxp_c45_sa *nxp_c45_sa_alloc(struct list_head *sa_list, void *sa,
+ enum nxp_c45_sa_type sa_type, u8 an)
+{
+ struct nxp_c45_sa *first = NULL, *pos, *tmp;
+ int occurrences = 0;
+
+ list_for_each_entry_safe(pos, tmp, sa_list, list) {
+ if (pos->type != sa_type)
+ continue;
+
+ if (pos->an == an)
+ return ERR_PTR(-EINVAL);
+
+ first = pos;
+ occurrences++;
+ if (occurrences >= 2)
+ return ERR_PTR(-ENOSPC);
+ }
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return ERR_PTR(-ENOMEM);
+
+ if (first)
+ tmp->is_key_a = !first->is_key_a;
+ else
+ tmp->is_key_a = true;
+
+ tmp->sa = sa;
+ tmp->type = sa_type;
+ tmp->an = an;
+ tmp->regs = nxp_c45_sa_regs_get(tmp->type, tmp->is_key_a);
+ list_add_tail(&tmp->list, sa_list);
+
+ return tmp;
+}
+
+static void nxp_c45_sa_free(struct nxp_c45_sa *sa)
+{
+ list_del(&sa->list);
+ kfree(sa);
+}
+
+static void nxp_c45_sa_list_free(struct list_head *sa_list)
+{
+ struct nxp_c45_sa *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, sa_list, list)
+ nxp_c45_sa_free(pos);
+}
+
+static void nxp_c45_sa_set_pn(struct phy_device *phydev,
+ struct nxp_c45_sa *sa, u64 pn,
+ u32 replay_window)
+{
+ const struct nxp_c45_sa_regs *sa_regs = sa->regs;
+ pn_t npn = {.full64 = pn};
+ pn_t lnpn;
+
+ nxp_c45_macsec_write(phydev, sa_regs->npn, npn.lower);
+ nxp_c45_macsec_write(phydev, sa_regs->xnpn, npn.upper);
+ if (sa->type != RX_SA)
+ return;
+
+ if (pn > replay_window)
+ lnpn.full64 = pn - replay_window;
+ else
+ lnpn.full64 = 1;
+
+ nxp_c45_macsec_write(phydev, sa_regs->lnpn, lnpn.lower);
+ nxp_c45_macsec_write(phydev, sa_regs->lxnpn, lnpn.upper);
+}
+
+static void nxp_c45_sa_set_key(struct macsec_context *ctx,
+ const struct nxp_c45_sa_regs *sa_regs,
+ u8 *salt, ssci_t ssci)
+{
+ struct phy_device *phydev = ctx->phydev;
+ u32 key_size = ctx->secy->key_len / 4;
+ u32 salt_size = MACSEC_SALT_LEN / 4;
+ u32 *key_u32 = (u32 *)ctx->sa.key;
+ u32 *salt_u32 = (u32 *)salt;
+ u32 reg, value;
+ int i;
+
+ for (i = 0; i < key_size; i++) {
+ reg = sa_regs->ka + i * 4;
+ value = (__force u32)cpu_to_be32(key_u32[i]);
+ nxp_c45_macsec_write(phydev, reg, value);
+ }
+
+ if (ctx->secy->xpn) {
+ for (i = 0; i < salt_size; i++) {
+ reg = sa_regs->salt + (2 - i) * 4;
+ value = (__force u32)cpu_to_be32(salt_u32[i]);
+ nxp_c45_macsec_write(phydev, reg, value);
+ }
+
+ value = (__force u32)cpu_to_be32((__force u32)ssci);
+ nxp_c45_macsec_write(phydev, sa_regs->ssci, value);
+ }
+
+ nxp_c45_macsec_write(phydev, sa_regs->cs, MACSEC_SA_CS_A);
+}
+
+static void nxp_c45_rx_sa_clear_stats(struct phy_device *phydev,
+ struct nxp_c45_sa *sa)
+{
+ nxp_c45_macsec_write(phydev, sa->regs->ipis, 0);
+ nxp_c45_macsec_write(phydev, sa->regs->ipnvs, 0);
+ nxp_c45_macsec_write(phydev, sa->regs->ipos, 0);
+
+ nxp_c45_macsec_write(phydev, MACSEC_RXAN0INUSS + sa->an * 4, 0);
+ nxp_c45_macsec_write(phydev, MACSEC_RXAN0IPUSS + sa->an * 4, 0);
+}
+
+static void nxp_c45_rx_sa_read_stats(struct phy_device *phydev,
+ struct nxp_c45_sa *sa,
+ struct macsec_rx_sa_stats *stats)
+{
+ nxp_c45_macsec_read(phydev, sa->regs->ipis, &stats->InPktsInvalid);
+ nxp_c45_macsec_read(phydev, sa->regs->ipnvs, &stats->InPktsNotValid);
+ nxp_c45_macsec_read(phydev, sa->regs->ipos, &stats->InPktsOK);
+}
+
+static void nxp_c45_tx_sa_clear_stats(struct phy_device *phydev,
+ struct nxp_c45_sa *sa)
+{
+ nxp_c45_macsec_write(phydev, sa->regs->opps, 0);
+ nxp_c45_macsec_write(phydev, sa->regs->opes, 0);
+}
+
+static void nxp_c45_tx_sa_read_stats(struct phy_device *phydev,
+ struct nxp_c45_sa *sa,
+ struct macsec_tx_sa_stats *stats)
+{
+ nxp_c45_macsec_read(phydev, sa->regs->opps, &stats->OutPktsProtected);
+ nxp_c45_macsec_read(phydev, sa->regs->opes, &stats->OutPktsEncrypted);
+}
+
+static void nxp_c45_rx_sa_update(struct phy_device *phydev,
+ struct nxp_c45_sa *sa, bool en)
+{
+ const struct nxp_c45_sa_regs *sa_regs = sa->regs;
+ u32 cfg;
+
+ cfg = sa->an << MACSEC_RXSA_CS_AN_OFF;
+ cfg |= en ? MACSEC_RXSA_CS_EN : 0;
+ nxp_c45_macsec_write(phydev, sa_regs->cs, cfg);
+}
+
+static void nxp_c45_tx_sa_update(struct phy_device *phydev,
+ struct nxp_c45_sa *sa, bool en)
+{
+ u32 cfg = 0;
+
+ nxp_c45_macsec_read(phydev, MACSEC_TXSC_CFG, &cfg);
+
+ cfg &= ~MACSEC_TXSC_CFG_AN_MASK;
+ cfg |= sa->an << MACSEC_TXSC_CFG_AN_OFF;
+
+ if (sa->is_key_a)
+ cfg &= ~MACSEC_TXSC_CFG_ASA;
+ else
+ cfg |= MACSEC_TXSC_CFG_ASA;
+
+ if (en)
+ cfg |= MACSEC_TXSC_CFG_SCE;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_SCE;
+
+ nxp_c45_macsec_write(phydev, MACSEC_TXSC_CFG, cfg);
+}
+
+static void nxp_c45_set_sci(struct phy_device *phydev, u16 sci_base_addr,
+ sci_t sci)
+{
+ u64 lsci = sci_to_cpu(sci);
+
+ nxp_c45_macsec_write(phydev, sci_base_addr, lsci >> 32);
+ nxp_c45_macsec_write(phydev, sci_base_addr + 4, lsci);
+}
+
+static bool nxp_c45_port_is_1(sci_t sci)
+{
+ u16 port = sci_to_cpu(sci);
+
+ return port == 1;
+}
+
+static void nxp_c45_select_secy(struct phy_device *phydev, u8 id)
+{
+ nxp_c45_macsec_write(phydev, MACSEC_RXSCA, id);
+ nxp_c45_macsec_write(phydev, MACSEC_RXSCKA, id);
+ nxp_c45_macsec_write(phydev, MACSEC_TXSCA, id);
+ nxp_c45_macsec_write(phydev, MACSEC_TXSCKA, id);
+}
+
+static bool nxp_c45_secy_valid(struct nxp_c45_secy *phy_secy,
+ bool can_rx_sc0_impl)
+{
+ bool end_station = phy_secy->secy->tx_sc.end_station;
+ bool scb = phy_secy->secy->tx_sc.scb;
+
+ phy_secy->rx_sc0_impl = false;
+
+ if (end_station) {
+ if (!nxp_c45_port_is_1(phy_secy->secy->sci))
+ return false;
+ if (!phy_secy->rx_sc)
+ return true;
+ return nxp_c45_port_is_1(phy_secy->rx_sc->sci);
+ }
+
+ if (scb)
+ return false;
+
+ if (!can_rx_sc0_impl)
+ return false;
+
+ if (phy_secy->secy_id != 0)
+ return false;
+
+ phy_secy->rx_sc0_impl = true;
+
+ return true;
+}
+
+static bool nxp_c45_rx_sc0_impl(struct nxp_c45_secy *phy_secy)
+{
+ bool end_station = phy_secy->secy->tx_sc.end_station;
+ bool send_sci = phy_secy->secy->tx_sc.send_sci;
+ bool scb = phy_secy->secy->tx_sc.scb;
+
+ return !end_station && !send_sci && !scb;
+}
+
+static bool nxp_c45_mac_addr_free(struct macsec_context *ctx)
+{
+ struct nxp_c45_phy *priv = ctx->phydev->priv;
+ struct nxp_c45_secy *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, &priv->macsec->secy_list, list) {
+ if (pos->secy == ctx->secy)
+ continue;
+
+ if (memcmp(pos->secy->netdev->dev_addr,
+ ctx->secy->netdev->dev_addr, ETH_ALEN) == 0)
+ return false;
+ }
+
+ return true;
+}
+
+static void nxp_c45_tx_sc_en_flt(struct phy_device *phydev, int secy_id,
+ bool en)
+{
+ u32 tx_flt_base = TX_FLT_BASE(secy_id);
+ u32 reg = 0;
+
+ nxp_c45_macsec_read(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), &reg);
+ if (en)
+ reg |= TX_SC_FLT_EN;
+ else
+ reg &= ~TX_SC_FLT_EN;
+ nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), reg);
+}
+
+static void nxp_c45_tx_sc_set_flt(struct phy_device *phydev,
+ struct nxp_c45_secy *phy_secy)
+{
+ const u8 *dev_addr = phy_secy->secy->netdev->dev_addr;
+ u32 tx_flt_base = TX_FLT_BASE(phy_secy->secy_id);
+ u32 reg;
+
+ reg = dev_addr[0] << 8 | dev_addr[1];
+ nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_DA_SA(tx_flt_base), reg);
+ reg = dev_addr[5] | dev_addr[4] << 8 | dev_addr[3] << 16 |
+ dev_addr[2] << 24;
+
+ nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_SA(tx_flt_base), reg);
+ nxp_c45_macsec_read(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), &reg);
+ reg &= TX_SC_FLT_EN;
+ reg |= TX_SC_FLT_BY_SA | phy_secy->secy_id;
+ nxp_c45_macsec_write(phydev, TX_SC_FLT_MAC_CFG(tx_flt_base), reg);
+}
+
+static void nxp_c45_tx_sc_update(struct phy_device *phydev,
+ struct nxp_c45_secy *phy_secy)
+{
+ u32 cfg = 0;
+
+ nxp_c45_macsec_read(phydev, MACSEC_TXSC_CFG, &cfg);
+
+ phydev_dbg(phydev, "XPN %s\n", phy_secy->secy->xpn ? "on" : "off");
+ if (phy_secy->secy->xpn)
+ cfg |= MACSEC_TXSC_CFG_XPN;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_XPN;
+
+ phydev_dbg(phydev, "key len %u\n", phy_secy->secy->key_len);
+ if (phy_secy->secy->key_len == 32)
+ cfg |= MACSEC_TXSC_CFG_AES_256;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_AES_256;
+
+ phydev_dbg(phydev, "encryption %s\n",
+ phy_secy->secy->tx_sc.encrypt ? "on" : "off");
+ if (phy_secy->secy->tx_sc.encrypt)
+ cfg |= MACSEC_TXSC_CFG_ENCRYPT;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_ENCRYPT;
+
+ phydev_dbg(phydev, "protect frames %s\n",
+ phy_secy->secy->protect_frames ? "on" : "off");
+ if (phy_secy->secy->protect_frames)
+ cfg |= MACSEC_TXSC_CFG_PROTECT;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_PROTECT;
+
+ phydev_dbg(phydev, "send sci %s\n",
+ phy_secy->secy->tx_sc.send_sci ? "on" : "off");
+ if (phy_secy->secy->tx_sc.send_sci)
+ cfg |= MACSEC_TXSC_CFG_SEND_SCI;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_SEND_SCI;
+
+ phydev_dbg(phydev, "end station %s\n",
+ phy_secy->secy->tx_sc.end_station ? "on" : "off");
+ if (phy_secy->secy->tx_sc.end_station)
+ cfg |= MACSEC_TXSC_CFG_END_STATION;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_END_STATION;
+
+ phydev_dbg(phydev, "scb %s\n",
+ phy_secy->secy->tx_sc.scb ? "on" : "off");
+ if (phy_secy->secy->tx_sc.scb)
+ cfg |= MACSEC_TXSC_CFG_SCB;
+ else
+ cfg &= ~MACSEC_TXSC_CFG_SCB;
+
+ nxp_c45_macsec_write(phydev, MACSEC_TXSC_CFG, cfg);
+}
+
+static void nxp_c45_tx_sc_clear_stats(struct phy_device *phydev,
+ struct nxp_c45_secy *phy_secy)
+{
+ struct nxp_c45_sa *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, &phy_secy->sa_list, list)
+ if (pos->type == TX_SA)
+ nxp_c45_tx_sa_clear_stats(phydev, pos);
+
+ nxp_c45_macsec_write(phydev, MACSEC_OPUS, 0);
+ nxp_c45_macsec_write(phydev, MACSEC_OPTLS, 0);
+ nxp_c45_macsec_write(phydev, MACSEC_OOP1HS, 0);
+ nxp_c45_macsec_write(phydev, MACSEC_OOP2HS, 0);
+ nxp_c45_macsec_write(phydev, MACSEC_OOE1HS, 0);
+ nxp_c45_macsec_write(phydev, MACSEC_OOE2HS, 0);
+}
+
+static void nxp_c45_set_rx_sc0_impl(struct phy_device *phydev,
+ bool enable)
+{
+ u32 reg = 0;
+
+ nxp_c45_macsec_read(phydev, MACSEC_CFG, &reg);
+ if (enable)
+ reg |= MACSEC_CFG_S0I;
+ else
+ reg &= ~MACSEC_CFG_S0I;
+ nxp_c45_macsec_write(phydev, MACSEC_CFG, reg);
+}
+
+static bool nxp_c45_is_rx_sc0_impl(struct list_head *secy_list)
+{
+ struct nxp_c45_secy *pos, *tmp;
+
+ list_for_each_entry_safe(pos, tmp, secy_list, list)
+ if (pos->rx_sc0_impl)<