On Tue, Sep 28, 2021 at 2:48 AM Vladimir Oltean <vladimir.olt...@nxp.com> wrote:
>
> The SJA1105 driver is largely reused from Linux. Its programming model
> is that it is blank out of reset, and it waits for a static
> configuration stream over SPI, which contains all runtime parameters (it
> has no notion of "default values").
>
> Keeping a binary array for the configuration stream would have meant
> that aspects such as the CPU port and the MAC speeds could have not been
> configured easily, and would have been static and board-dependent.
> Live-patching the binary array means recalculating the static config
> table CRCs, which is not a fun process.
>
> So we create an abstraction over the static config tables, using the
> packing API, same as in Linux. The tables are kept as C structures, and
> the binary configuration stream is constructed on-the-go, with CRC and
> all.
>
> All static config tables instantiated in this driver are mandatory.
> The hardware reference manual can be found at:
> https://www.nxp.com/docs/en/user-guide/UM10944.pdf
>
> For tagging, a simplified version of tag_8021q from Linux is used. The
> VLAN EtherType is the same (0xdadb) but since we don't want switching in
> U-Boot, there is no reason to have a TX VLAN and an RX VLAN for each
> port. We just need the RX VLANs to act as the unique pvid of each
> front-panel port, to decode the switch port number. The RX VLAN is used
> for both RX and TX.
>
> The device tree bindings are the same as in Linux.
>
> Signed-off-by: Vladimir Oltean <vladimir.olt...@nxp.com>
> ---
> drivers/net/Kconfig | 16 +
> drivers/net/Makefile | 1 +
> drivers/net/sja1105.c | 3351 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 3368 insertions(+)
> create mode 100644 drivers/net/sja1105.c
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index d4dc72046c4e..a6e90dad0b4f 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -542,6 +542,22 @@ config RTL8169
> This driver supports Realtek 8169 series gigabit ethernet family of
> PCI/PCIe chipsets/adapters.
>
> +config SJA1105
> + bool "NXP SJA1105 Ethernet switch family driver"
> + depends on DM_DSA && DM_SPI
> + select BITREVERSE
> + help
> + This is the driver for the NXP SJA1105 automotive Ethernet switch
> + family. These are 5-port devices and are managed over an SPI
> + interface. Probing is handled based on OF bindings. The driver
> + supports the following revisions:
> + - SJA1105E (Gen. 1, No TT-Ethernet)
> + - SJA1105T (Gen. 1, TT-Ethernet)
> + - SJA1105P (Gen. 2, No SGMII, No TT-Ethernet)
> + - SJA1105Q (Gen. 2, No SGMII, TT-Ethernet)
> + - SJA1105R (Gen. 2, SGMII, No TT-Ethernet)
> + - SJA1105S (Gen. 2, SGMII, TT-Ethernet)
> +
> config SMC911X
> bool "SMSC LAN911x and LAN921x controller driver"
>
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index b94ccea1003b..d9c394ee3dab 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o
> obj-$(CONFIG_E1000) += e1000.o
> obj-$(CONFIG_E1000_SPI) += e1000_spi.o
> obj-$(CONFIG_EEPRO100) += eepro100.o
> +obj-$(CONFIG_SJA1105) += sja1105.o
> obj-$(CONFIG_SUN4I_EMAC) += sunxi_emac.o
> obj-$(CONFIG_SUN8I_EMAC) += sun8i_emac.o
> obj-$(CONFIG_EP93XX) += ep93xx_eth.o
> diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c
> new file mode 100644
> index 000000000000..99a73e41ef8e
> --- /dev/null
> +++ b/drivers/net/sja1105.c
> @@ -0,0 +1,3351 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2016-2018 NXP
> + * Copyright 2018, Sensor-Technik Wiedemann GmbH
> + * Copyright 2018-2019, Vladimir Oltean <olte...@gmail.com>
> + * Copyright 2020-2021 NXP
> + *
> + * Ported from Linux (drivers/net/dsa/sja1105/).
> + */
> +
> +#include <common.h>
> +#include <dm/device_compat.h>
> +#include <linux/bitops.h>
> +#include <linux/bitrev.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/if_ether.h>
> +#include <linux/if_vlan.h>
> +#include <linux/types.h>
> +#include <net/dsa.h>
> +#include <stdlib.h>
> +#include <spi.h>
> +#include <miiphy.h>
> +#include <dm/of_extra.h>
> +
> +enum packing_op {
> + PACK,
> + UNPACK,
> +};
> +
> +#define ETHER_CRC32_POLY 0x04C11DB7
> +#define ETH_P_SJA1105 0xdadb
> +#define SJA1105_NUM_PORTS 5
> +#define SJA1110_NUM_PORTS 11
> +#define SJA1105_MAX_NUM_PORTS SJA1110_NUM_PORTS
> +#define SJA1105_NUM_TC 8
> +#define SJA1105ET_FDB_BIN_SIZE 4
> +#define SJA1105_SIZE_CGU_CMD 4
> +#define SJA1105_SIZE_RESET_CMD 4
> +#define SJA1105_SIZE_MDIO_CMD 4
> +#define SJA1105_SIZE_SPI_MSG_HEADER 4
> +#define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 4)
> +#define SJA1105_SIZE_DEVICE_ID 4
> +#define SJA1105_SIZE_TABLE_HEADER 12
> +#define SJA1105_SIZE_L2_POLICING_ENTRY 8
> +#define SJA1105_SIZE_VLAN_LOOKUP_ENTRY 8
> +#define SJA1110_SIZE_VLAN_LOOKUP_ENTRY 12
> +#define SJA1105_SIZE_L2_FORWARDING_ENTRY 8
> +#define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY 12
> +#define SJA1105_SIZE_XMII_PARAMS_ENTRY 4
> +#define SJA1110_SIZE_XMII_PARAMS_ENTRY 8
> +#define SJA1105ET_SIZE_MAC_CONFIG_ENTRY 28
> +#define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY 40
> +#define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY 32
> +#define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY 44
> +#define SJA1110_SIZE_GENERAL_PARAMS_ENTRY 56
> +
> +#define SJA1105_MAX_L2_LOOKUP_COUNT 1024
> +#define SJA1105_MAX_L2_POLICING_COUNT 45
> +#define SJA1110_MAX_L2_POLICING_COUNT 110
> +#define SJA1105_MAX_VLAN_LOOKUP_COUNT 4096
> +#define SJA1105_MAX_L2_FORWARDING_COUNT 13
> +#define SJA1110_MAX_L2_FORWARDING_COUNT 19
> +#define SJA1105_MAX_MAC_CONFIG_COUNT 5
> +#define SJA1110_MAX_MAC_CONFIG_COUNT 11
> +#define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT 1
> +#define SJA1105_MAX_GENERAL_PARAMS_COUNT 1
> +#define SJA1105_MAX_XMII_PARAMS_COUNT 1
> +
> +#define SJA1105_MAX_FRAME_MEMORY 929
> +
> +#define SJA1105E_DEVICE_ID 0x9C00000Cull
> +#define SJA1105T_DEVICE_ID 0x9E00030Eull
> +#define SJA1105PR_DEVICE_ID 0xAF00030Eull
> +#define SJA1105QS_DEVICE_ID 0xAE00030Eull
> +#define SJA1110_DEVICE_ID 0xB700030Full
> +
> +#define SJA1105ET_PART_NO 0x9A83
> +#define SJA1105P_PART_NO 0x9A84
> +#define SJA1105Q_PART_NO 0x9A85
> +#define SJA1105R_PART_NO 0x9A86
> +#define SJA1105S_PART_NO 0x9A87
> +#define SJA1110A_PART_NO 0x1110
> +#define SJA1110B_PART_NO 0x1111
> +#define SJA1110C_PART_NO 0x1112
> +#define SJA1110D_PART_NO 0x1113
> +
> +#define SJA1110_ACU 0x1c4400
> +#define SJA1110_RGU 0x1c6000
> +#define SJA1110_CGU 0x1c6400
> +
> +#define SJA1110_SPI_ADDR(x) ((x) / 4)
> +#define SJA1110_ACU_ADDR(x) (SJA1110_ACU + SJA1110_SPI_ADDR(x))
> +#define SJA1110_CGU_ADDR(x) (SJA1110_CGU + SJA1110_SPI_ADDR(x))
> +#define SJA1110_RGU_ADDR(x) (SJA1110_RGU + SJA1110_SPI_ADDR(x))
> +
> +#define SJA1105_RSV_ADDR 0xffffffffffffffffull
> +
> +#define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc)
> +
> +#define DSA_8021Q_DIR_TX BIT(11)
> +#define DSA_8021Q_PORT_SHIFT 0
> +#define DSA_8021Q_PORT_MASK GENMASK(3, 0)
> +#define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \
> + DSA_8021Q_PORT_MASK)
> +
> +#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000)
> +
> +/* XPCS registers */
> +
> +/* VR MII MMD registers offsets */
> +#define DW_VR_MII_DIG_CTRL1 0x8000
> +#define DW_VR_MII_AN_CTRL 0x8001
> +#define DW_VR_MII_DIG_CTRL2 0x80e1
> +
> +/* VR_MII_DIG_CTRL1 */
> +#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
> +
> +/* VR_MII_DIG_CTRL2 */
> +#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4)
> +
> +/* VR_MII_AN_CTRL */
> +#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3
> +#define DW_VR_MII_TX_CONFIG_MASK BIT(3)
> +#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0
> +#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1
> +#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1)
> +#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2
> +
> +/* PMA registers */
> +
> +/* LANE_DRIVER1_0 register */
> +#define SJA1110_LANE_DRIVER1_0 0x8038
> +#define SJA1110_TXDRV(x) (((x) << 12) & GENMASK(14, 12))
> +
> +/* LANE_DRIVER2_0 register */
> +#define SJA1110_LANE_DRIVER2_0 0x803a
> +#define SJA1110_TXDRVTRIM_LSB(x) ((x) & GENMASK_ULL(15, 0))
> +
> +/* LANE_DRIVER2_1 register */
> +#define SJA1110_LANE_DRIVER2_1 0x803b
> +#define SJA1110_LANE_DRIVER2_1_RSV BIT(9)
> +#define SJA1110_TXDRVTRIM_MSB(x) (((x) & GENMASK_ULL(23, 16)) >> 16)
> +
> +/* LANE_TRIM register */
> +#define SJA1110_LANE_TRIM 0x8040
> +#define SJA1110_TXTEN BIT(11)
> +#define SJA1110_TXRTRIM(x) (((x) << 8) & GENMASK(10, 8))
> +#define SJA1110_TXPLL_BWSEL BIT(7)
> +#define SJA1110_RXTEN BIT(6)
> +#define SJA1110_RXRTRIM(x) (((x) << 3) & GENMASK(5, 3))
> +#define SJA1110_CDR_GAIN BIT(2)
> +#define SJA1110_ACCOUPLE_RXVCM_EN BIT(0)
> +
> +/* LANE_DATAPATH_1 register */
> +#define SJA1110_LANE_DATAPATH_1 0x8037
> +
> +/* POWERDOWN_ENABLE register */
> +#define SJA1110_POWERDOWN_ENABLE 0x8041
> +#define SJA1110_TXPLL_PD BIT(12)
> +#define SJA1110_TXPD BIT(11)
> +#define SJA1110_RXPKDETEN BIT(10)
> +#define SJA1110_RXCH_PD BIT(9)
> +#define SJA1110_RXBIAS_PD BIT(8)
> +#define SJA1110_RESET_SER_EN BIT(7)
> +#define SJA1110_RESET_SER BIT(6)
> +#define SJA1110_RESET_DES BIT(5)
> +#define SJA1110_RCVEN BIT(4)
> +
> +/* RXPLL_CTRL0 register */
> +#define SJA1110_RXPLL_CTRL0 0x8065
> +#define SJA1110_RXPLL_FBDIV(x) (((x) << 2) & GENMASK(9, 2))
> +
> +/* RXPLL_CTRL1 register */
> +#define SJA1110_RXPLL_CTRL1 0x8066
> +#define SJA1110_RXPLL_REFDIV(x) ((x) & GENMASK(4, 0))
> +
> +/* TXPLL_CTRL0 register */
> +#define SJA1110_TXPLL_CTRL0 0x806d
> +#define SJA1110_TXPLL_FBDIV(x) ((x) & GENMASK(11, 0))
> +
> +/* TXPLL_CTRL1 register */
> +#define SJA1110_TXPLL_CTRL1 0x806e
> +#define SJA1110_TXPLL_REFDIV(x) ((x) & GENMASK(5, 0))
> +
> +/* RX_DATA_DETECT register */
> +#define SJA1110_RX_DATA_DETECT 0x8045
> +
> +/* RX_CDR_CTLE register */
> +#define SJA1110_RX_CDR_CTLE 0x8042
> +
> +/* UM10944.pdf Page 11, Table 2. Configuration Blocks */
> +enum {
> + BLKID_L2_POLICING = 0x06,
> + BLKID_VLAN_LOOKUP = 0x07,
> + BLKID_L2_FORWARDING = 0x08,
> + BLKID_MAC_CONFIG = 0x09,
> + BLKID_L2_FORWARDING_PARAMS = 0x0E,
> + BLKID_GENERAL_PARAMS = 0x11,
> + BLKID_XMII_PARAMS = 0x4E,
> +};
> +
> +enum sja1105_blk_idx {
> + BLK_IDX_L2_POLICING = 0,
> + BLK_IDX_VLAN_LOOKUP,
> + BLK_IDX_L2_FORWARDING,
> + BLK_IDX_MAC_CONFIG,
> + BLK_IDX_L2_FORWARDING_PARAMS,
> + BLK_IDX_GENERAL_PARAMS,
> + BLK_IDX_XMII_PARAMS,
> + BLK_IDX_MAX,
> +};
> +
> +struct sja1105_general_params_entry {
> + u64 mac_fltres1;
> + u64 mac_fltres0;
> + u64 mac_flt1;
> + u64 mac_flt0;
> + u64 casc_port;
> + u64 host_port;
> + u64 mirr_port;
> + u64 tpid;
> + u64 tpid2;
> +};
> +
> +struct sja1105_vlan_lookup_entry {
> + u64 vmemb_port;
> + u64 vlan_bc;
> + u64 tag_port;
> + u64 vlanid;
> + u64 type_entry; /* SJA1110 only */
> +};
> +
> +struct sja1105_l2_forwarding_entry {
> + u64 bc_domain;
> + u64 reach_port;
> + u64 fl_domain;
> +};
> +
> +struct sja1105_l2_forwarding_params_entry {
> + u64 part_spc[SJA1105_NUM_TC];
> +};
> +
> +struct sja1105_l2_policing_entry {
> + u64 sharindx;
> + u64 smax;
> + u64 rate;
> + u64 maxlen;
> + u64 partition;
> +};
> +
> +struct sja1105_mac_config_entry {
> + u64 top[SJA1105_NUM_TC];
> + u64 base[SJA1105_NUM_TC];
> + u64 enabled[SJA1105_NUM_TC];
> + u64 speed;
> + u64 vlanid;
> + u64 egress;
> + u64 ingress;
> +};
> +
> +struct sja1105_xmii_params_entry {
> + u64 phy_mac[SJA1105_MAX_NUM_PORTS];
> + u64 xmii_mode[SJA1105_MAX_NUM_PORTS];
> + u64 special[SJA1105_MAX_NUM_PORTS];
> +};
> +
> +struct sja1105_table_header {
> + u64 block_id;
> + u64 len;
> + u64 crc;
> +};
> +
> +struct sja1105_table_ops {
> + size_t (*packing)(void *buf, void *entry_ptr, enum packing_op op);
> + size_t unpacked_entry_size;
> + size_t packed_entry_size;
> + size_t max_entry_count;
> +};
> +
> +struct sja1105_table {
> + const struct sja1105_table_ops *ops;
> + size_t entry_count;
> + void *entries;
> +};
> +
> +struct sja1105_static_config {
> + u64 device_id;
> + struct sja1105_table tables[BLK_IDX_MAX];
> +};
> +
> +struct sja1105_xpcs_cfg {
> + bool inband_an;
> + int speed;
> +};
> +
> +struct sja1105_private {
> + struct sja1105_static_config static_config;
> + bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS];
> + bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS];
> + u16 pvid[SJA1105_MAX_NUM_PORTS];
> + struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS];
> + struct mii_dev *mdio_pcs;
> + const struct sja1105_info *info;
> + struct udevice *dev;
> +};
> +
> +typedef enum {
> + SPI_READ = 0,
> + SPI_WRITE = 1,
> +} sja1105_spi_rw_mode_t;
> +
> +typedef enum {
> + XMII_MAC = 0,
> + XMII_PHY = 1,
> +} sja1105_mii_role_t;
> +
> +typedef enum {
> + XMII_MODE_MII = 0,
> + XMII_MODE_RMII = 1,
> + XMII_MODE_RGMII = 2,
> + XMII_MODE_SGMII = 3,
> +} sja1105_phy_interface_t;
> +
> +enum {
> + SJA1105_SPEED_AUTO,
> + SJA1105_SPEED_10MBPS,
> + SJA1105_SPEED_100MBPS,
> + SJA1105_SPEED_1000MBPS,
> + SJA1105_SPEED_MAX,
> +};
> +
> +enum sja1110_vlan_type {
> + SJA1110_VLAN_INVALID = 0,
> + SJA1110_VLAN_C_TAG = 1, /* Single inner VLAN tag */
> + SJA1110_VLAN_S_TAG = 2, /* Single outer VLAN tag */
> + SJA1110_VLAN_D_TAG = 3, /* Double tagged, use outer tag for lookup */
> +};
> +
> +/* Keeps the different addresses between E/T and P/Q/R/S */
> +struct sja1105_regs {
> + u64 device_id;
> + u64 prod_id;
> + u64 status;
> + u64 port_control;
> + u64 rgu;
> + u64 config;
> + u64 rmii_pll1;
> + u64 pad_mii_tx[SJA1105_MAX_NUM_PORTS];
> + u64 pad_mii_rx[SJA1105_MAX_NUM_PORTS];
> + u64 pad_mii_id[SJA1105_MAX_NUM_PORTS];
> + u64 cgu_idiv[SJA1105_MAX_NUM_PORTS];
> + u64 mii_tx_clk[SJA1105_MAX_NUM_PORTS];
> + u64 mii_rx_clk[SJA1105_MAX_NUM_PORTS];
> + u64 mii_ext_tx_clk[SJA1105_MAX_NUM_PORTS];
> + u64 mii_ext_rx_clk[SJA1105_MAX_NUM_PORTS];
> + u64 rgmii_tx_clk[SJA1105_MAX_NUM_PORTS];
> + u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS];
> + u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS];
> + u64 pcs_base[SJA1105_MAX_NUM_PORTS];
> +};
> +
> +struct sja1105_info {
> + u64 device_id;
> + u64 part_no;
> + const struct sja1105_table_ops *static_ops;
> + const struct sja1105_regs *regs;
> + int (*reset_cmd)(struct sja1105_private *priv);
> + int (*setup_rgmii_delay)(struct sja1105_private *priv, int port);
> + int (*pcs_mdio_read)(struct mii_dev *bus, int phy, int mmd, int reg);
> + int (*pcs_mdio_write)(struct mii_dev *bus, int phy, int mmd, int reg,
> + u16 val);
> + int (*pma_config)(struct sja1105_private *priv, int port);
> + const char *name;
> + bool supports_mii[SJA1105_MAX_NUM_PORTS];
> + bool supports_rmii[SJA1105_MAX_NUM_PORTS];
> + bool supports_rgmii[SJA1105_MAX_NUM_PORTS];
> + bool supports_sgmii[SJA1105_MAX_NUM_PORTS];
> + const u64 port_speed[SJA1105_SPEED_MAX];
> +};
> +
> +struct sja1105_chunk {
> + u8 *buf;
> + size_t len;
> + u64 reg_addr;
> +};
> +
> +struct sja1105_spi_message {
> + u64 access;
> + u64 read_count;
> + u64 address;
> +};
> +
> +/* Common structure for CFG_PAD_MIIx_RX and CFG_PAD_MIIx_TX */
> +struct sja1105_cfg_pad_mii {
> + u64 d32_os;
> + u64 d32_ih;
> + u64 d32_ipud;
> + u64 d10_ih;
> + u64 d10_os;
> + u64 d10_ipud;
> + u64 ctrl_os;
> + u64 ctrl_ih;
> + u64 ctrl_ipud;
> + u64 clk_os;
> + u64 clk_ih;
> + u64 clk_ipud;
> +};
> +
> +struct sja1105_cfg_pad_mii_id {
> + u64 rxc_stable_ovr;
> + u64 rxc_delay;
> + u64 rxc_bypass;
> + u64 rxc_pd;
> + u64 txc_stable_ovr;
> + u64 txc_delay;
> + u64 txc_bypass;
> + u64 txc_pd;
> +};
> +
> +struct sja1105_cgu_idiv {
> + u64 clksrc;
> + u64 autoblock;
> + u64 idiv;
> + u64 pd;
> +};
> +
> +struct sja1105_cgu_pll_ctrl {
> + u64 pllclksrc;
> + u64 msel;
> + u64 autoblock;
> + u64 psel;
> + u64 direct;
> + u64 fbsel;
> + u64 bypass;
> + u64 pd;
> +};
> +
> +enum {
> + CLKSRC_MII0_TX_CLK = 0x00,
> + CLKSRC_MII0_RX_CLK = 0x01,
> + CLKSRC_MII1_TX_CLK = 0x02,
> + CLKSRC_MII1_RX_CLK = 0x03,
> + CLKSRC_MII2_TX_CLK = 0x04,
> + CLKSRC_MII2_RX_CLK = 0x05,
> + CLKSRC_MII3_TX_CLK = 0x06,
> + CLKSRC_MII3_RX_CLK = 0x07,
> + CLKSRC_MII4_TX_CLK = 0x08,
> + CLKSRC_MII4_RX_CLK = 0x09,
> + CLKSRC_PLL0 = 0x0B,
> + CLKSRC_PLL1 = 0x0E,
> + CLKSRC_IDIV0 = 0x11,
> + CLKSRC_IDIV1 = 0x12,
> + CLKSRC_IDIV2 = 0x13,
> + CLKSRC_IDIV3 = 0x14,
> + CLKSRC_IDIV4 = 0x15,
> +};
> +
> +struct sja1105_cgu_mii_ctrl {
> + u64 clksrc;
> + u64 autoblock;
> + u64 pd;
> +};
> +
> +static int get_reverse_lsw32_offset(int offset, size_t len)
> +{
> + int closest_multiple_of_4;
> + int word_index;
> +
> + word_index = offset / 4;
> + closest_multiple_of_4 = word_index * 4;
> + offset -= closest_multiple_of_4;
> + word_index = (len / 4) - word_index - 1;
> + return word_index * 4 + offset;
> +}
> +
> +/* Simplified version of the "packing" function from Linux, adapted
> + * to support only sja1105's quirk: QUIRK_LSW32_IS_FIRST
> + */
> +static void sja1105_packing(void *pbuf, u64 *uval, int startbit, int endbit,
> + size_t pbuflen, enum packing_op op)
> +{
> + int plogical_first_u8, plogical_last_u8, box;
> +
> + if (op == UNPACK)
> + *uval = 0;
> +
> + plogical_first_u8 = startbit / 8;
> + plogical_last_u8 = endbit / 8;
> +
> + for (box = plogical_first_u8; box >= plogical_last_u8; box--) {
> + int box_start_bit, box_end_bit, box_addr;
> + int proj_start_bit, proj_end_bit;
> + u64 proj_mask;
> + u8 box_mask;
> +
> + if (box == plogical_first_u8)
> + box_start_bit = startbit % 8;
> + else
> + box_start_bit = 7;
> + if (box == plogical_last_u8)
> + box_end_bit = endbit % 8;
> + else
> + box_end_bit = 0;
> +
> + proj_start_bit = ((box * 8) + box_start_bit) - endbit;
> + proj_end_bit = ((box * 8) + box_end_bit) - endbit;
> + proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit);
> + box_mask = GENMASK_ULL(box_start_bit, box_end_bit);
> +
> + box_addr = pbuflen - box - 1;
> + box_addr = get_reverse_lsw32_offset(box_addr, pbuflen);
> +
> + if (op == UNPACK) {
> + u64 pval;
> +
> + /* Read from pbuf, write to uval */
> + pval = ((u8 *)pbuf)[box_addr] & box_mask;
> +
> + pval >>= box_end_bit;
> + pval <<= proj_end_bit;
> + *uval &= ~proj_mask;
> + *uval |= pval;
> + } else {
> + u64 pval;
> +
> + /* Write to pbuf, read from uval */
> + pval = (*uval) & proj_mask;
> + pval >>= proj_end_bit;
> +
> + pval <<= box_end_bit;
> + ((u8 *)pbuf)[box_addr] &= ~box_mask;
> + ((u8 *)pbuf)[box_addr] |= pval;
> + }
> + }
> +}
> +
> +static u32 crc32_add(u32 crc, u8 byte)
> +{
> + u32 byte32 = bitrev32(byte);
> + int i;
> +
> + for (i = 0; i < 8; i++) {
> + if ((crc ^ byte32) & BIT(31)) {
> + crc <<= 1;
> + crc ^= ETHER_CRC32_POLY;
> + } else {
> + crc <<= 1;
> + }
> + byte32 <<= 1;
> + }
> + return crc;
> +}
> +
> +/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
> +static uint32_t sja1105_crc32(void *buf, size_t len)
> +{
> + unsigned int i;
> + u64 chunk;
> + u32 crc;
> +
> + /* seed */
> + crc = 0xFFFFFFFF;
> + for (i = 0; i < len; i += 4) {
> + sja1105_packing(buf + i, &chunk, 31, 0, 4, UNPACK);
> + crc = crc32_add(crc, chunk & 0xFF);
> + crc = crc32_add(crc, (chunk >> 8) & 0xFF);
> + crc = crc32_add(crc, (chunk >> 16) & 0xFF);
> + crc = crc32_add(crc, (chunk >> 24) & 0xFF);
> + }
> + return bitrev32(~crc);
> +}
> +
> +static void sja1105_spi_message_pack(void *buf, struct sja1105_spi_message
> *msg)
> +{
> + const int size = SJA1105_SIZE_SPI_MSG_HEADER;
> +
> + memset(buf, 0, size);
> +
> + sja1105_packing(buf, &msg->access, 31, 31, size, PACK);
> + sja1105_packing(buf, &msg->read_count, 30, 25, size, PACK);
> + sja1105_packing(buf, &msg->address, 24, 4, size, PACK);
> +}
> +
> +static int sja1105_xfer_buf(const struct sja1105_private *priv,
> + sja1105_spi_rw_mode_t rw, u64 reg_addr,
> + u8 *buf, size_t len)
> +{
> + struct udevice *dev = priv->dev;
> + struct sja1105_chunk chunk = {
> + .len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
> + .reg_addr = reg_addr,
> + .buf = buf,
> + };
> + int num_chunks;
> + int rc, i;
> +
> + rc = dm_spi_claim_bus(dev);
> + if (rc)
> + return rc;
> +
> + num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
> +
> + for (i = 0; i < num_chunks; i++) {
> + u8 hdr_buf[SJA1105_SIZE_SPI_MSG_HEADER];
> + struct sja1105_spi_message msg;
> + u8 *rx_buf = NULL;
> + u8 *tx_buf = NULL;
> +
> + /* Populate the transfer's header buffer */
> + msg.address = chunk.reg_addr;
> + msg.access = rw;
> + if (rw == SPI_READ)
> + msg.read_count = chunk.len / 4;
> + else
> + /* Ignored */
> + msg.read_count = 0;
> + sja1105_spi_message_pack(hdr_buf, &msg);
> + rc = dm_spi_xfer(dev, SJA1105_SIZE_SPI_MSG_HEADER * 8,
> hdr_buf,
> + NULL, SPI_XFER_BEGIN);
> + if (rc)
> + goto out;
> +
> + /* Populate the transfer's data buffer */
> + if (rw == SPI_READ)
> + rx_buf = chunk.buf;
> + else
> + tx_buf = chunk.buf;
> + rc = dm_spi_xfer(dev, chunk.len * 8, tx_buf, rx_buf,
> + SPI_XFER_END);
> + if (rc)
> + goto out;
> +
> + /* Calculate next chunk */
> + chunk.buf += chunk.len;
> + chunk.reg_addr += chunk.len / 4;
> + chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf),
> + SJA1105_SIZE_SPI_MSG_MAXLEN);
> + }
> +
> +out:
> + dm_spi_release_bus(dev);
> +
> + return rc;
> +}
> +
> +static int sja1105et_reset_cmd(struct sja1105_private *priv)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
> + const int size = SJA1105_SIZE_RESET_CMD;
> + u64 cold_rst = 1;
> +
> + sja1105_packing(packed_buf, &cold_rst, 3, 3, size, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
> + SJA1105_SIZE_RESET_CMD);
> +}
> +
> +static int sja1105pqrs_reset_cmd(struct sja1105_private *priv)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
> + const int size = SJA1105_SIZE_RESET_CMD;
> + u64 cold_rst = 1;
> +
> + sja1105_packing(packed_buf, &cold_rst, 2, 2, size, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
> + SJA1105_SIZE_RESET_CMD);
> +}
> +
> +static int sja1110_reset_cmd(struct sja1105_private *priv)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
> + const int size = SJA1105_SIZE_RESET_CMD;
> + u64 switch_rst = 1;
> +
> + /* Only reset the switch core.
> + * A full cold reset would re-enable the BASE_MCSS_CLOCK PLL which
> + * would turn on the microcontroller, potentially letting it execute
> + * code which could interfere with our configuration.
> + */
> + sja1105_packing(packed_buf, &switch_rst, 20, 20, size, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
> + SJA1105_SIZE_RESET_CMD);
> +}
> +
> +static size_t sja1105et_general_params_entry_packing(void *buf, void
> *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY;
> + struct sja1105_general_params_entry *entry = entry_ptr;
> +
> + sja1105_packing(buf, &entry->mac_fltres1, 311, 264, size, op);
> + sja1105_packing(buf, &entry->mac_fltres0, 263, 216, size, op);
> + sja1105_packing(buf, &entry->mac_flt1, 215, 168, size, op);
> + sja1105_packing(buf, &entry->mac_flt0, 167, 120, size, op);
> + sja1105_packing(buf, &entry->casc_port, 115, 113, size, op);
> + sja1105_packing(buf, &entry->host_port, 112, 110, size, op);
> + sja1105_packing(buf, &entry->mirr_port, 109, 107, size, op);
> + sja1105_packing(buf, &entry->tpid, 42, 27, size, op);
> + sja1105_packing(buf, &entry->tpid2, 25, 10, size, op);
> + return size;
> +}
> +
> +static size_t sja1110_general_params_entry_packing(void *buf, void
> *entry_ptr,
> + enum packing_op op)
> +{
> + struct sja1105_general_params_entry *entry = entry_ptr;
> + const size_t size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY;
> +
> + sja1105_packing(buf, &entry->mac_fltres1, 438, 391, size, op);
> + sja1105_packing(buf, &entry->mac_fltres0, 390, 343, size, op);
> + sja1105_packing(buf, &entry->mac_flt1, 342, 295, size, op);
> + sja1105_packing(buf, &entry->mac_flt0, 294, 247, size, op);
> + sja1105_packing(buf, &entry->casc_port, 242, 232, size, op);
> + sja1105_packing(buf, &entry->host_port, 231, 228, size, op);
> + sja1105_packing(buf, &entry->mirr_port, 227, 224, size, op);
> + sja1105_packing(buf, &entry->tpid2, 159, 144, size, op);
> + sja1105_packing(buf, &entry->tpid, 142, 127, size, op);
> + return size;
> +}
> +
> +static size_t
> +sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY;
> + struct sja1105_general_params_entry *entry = entry_ptr;
> +
> + sja1105_packing(buf, &entry->mac_fltres1, 343, 296, size, op);
> + sja1105_packing(buf, &entry->mac_fltres0, 295, 248, size, op);
> + sja1105_packing(buf, &entry->mac_flt1, 247, 200, size, op);
> + sja1105_packing(buf, &entry->mac_flt0, 199, 152, size, op);
> + sja1105_packing(buf, &entry->casc_port, 147, 145, size, op);
> + sja1105_packing(buf, &entry->host_port, 144, 142, size, op);
> + sja1105_packing(buf, &entry->mirr_port, 141, 139, size, op);
> + sja1105_packing(buf, &entry->tpid, 74, 59, size, op);
> + sja1105_packing(buf, &entry->tpid2, 57, 42, size, op);
> + return size;
> +}
> +
> +static size_t
> +sja1105_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY;
> + struct sja1105_l2_forwarding_params_entry *entry = entry_ptr;
> + int offset, i;
> +
> + for (i = 0, offset = 13; i < SJA1105_NUM_TC; i++, offset += 10)
> + sja1105_packing(buf, &entry->part_spc[i],
> + offset + 9, offset + 0, size, op);
> + return size;
> +}
> +
> +static size_t
> +sja1110_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + struct sja1105_l2_forwarding_params_entry *entry = entry_ptr;
> + const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY;
> + int offset, i;
> +
> + for (i = 0, offset = 5; i < 8; i++, offset += 11)
> + sja1105_packing(buf, &entry->part_spc[i],
> + offset + 10, offset + 0, size, op);
> + return size;
> +}
> +
> +static size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY;
> + struct sja1105_l2_forwarding_entry *entry = entry_ptr;
> +
> + sja1105_packing(buf, &entry->bc_domain, 63, 59, size, op);
> + sja1105_packing(buf, &entry->reach_port, 58, 54, size, op);
> + sja1105_packing(buf, &entry->fl_domain, 53, 49, size, op);
> + return size;
> +}
> +
> +static size_t sja1110_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + struct sja1105_l2_forwarding_entry *entry = entry_ptr;
> + const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY;
> +
> + sja1105_packing(buf, &entry->bc_domain, 63, 53, size, op);
> + sja1105_packing(buf, &entry->reach_port, 52, 42, size, op);
> + sja1105_packing(buf, &entry->fl_domain, 41, 31, size, op);
> + return size;
> +}
> +
> +static size_t sja1105_l2_policing_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + struct sja1105_l2_policing_entry *entry = entry_ptr;
> + const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY;
> +
> + sja1105_packing(buf, &entry->sharindx, 63, 58, size, op);
> + sja1105_packing(buf, &entry->smax, 57, 42, size, op);
> + sja1105_packing(buf, &entry->rate, 41, 26, size, op);
> + sja1105_packing(buf, &entry->maxlen, 25, 15, size, op);
> + sja1105_packing(buf, &entry->partition, 14, 12, size, op);
> + return size;
> +}
> +
> +static size_t sja1110_l2_policing_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + struct sja1105_l2_policing_entry *entry = entry_ptr;
> + const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY;
> +
> + sja1105_packing(buf, &entry->sharindx, 63, 57, size, op);
> + sja1105_packing(buf, &entry->smax, 56, 39, size, op);
> + sja1105_packing(buf, &entry->rate, 38, 21, size, op);
> + sja1105_packing(buf, &entry->maxlen, 20, 10, size, op);
> + sja1105_packing(buf, &entry->partition, 9, 7, size, op);
> + return size;
> +}
> +
> +static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY;
> + struct sja1105_mac_config_entry *entry = entry_ptr;
> + int offset, i;
> +
> + for (i = 0, offset = 72; i < SJA1105_NUM_TC; i++, offset += 19) {
> + sja1105_packing(buf, &entry->enabled[i],
> + offset + 0, offset + 0, size, op);
> + sja1105_packing(buf, &entry->base[i],
> + offset + 9, offset + 1, size, op);
> + sja1105_packing(buf, &entry->top[i],
> + offset + 18, offset + 10, size, op);
> + }
> + sja1105_packing(buf, &entry->speed, 66, 65, size, op);
> + sja1105_packing(buf, &entry->vlanid, 21, 10, size, op);
> + sja1105_packing(buf, &entry->egress, 2, 2, size, op);
> + sja1105_packing(buf, &entry->ingress, 1, 1, size, op);
> + return size;
> +}
> +
> +static size_t sja1105pqrs_mac_config_entry_packing(void *buf, void
> *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY;
> + struct sja1105_mac_config_entry *entry = entry_ptr;
> + int offset, i;
> +
> + for (i = 0, offset = 104; i < SJA1105_NUM_TC; i++, offset += 19) {
> + sja1105_packing(buf, &entry->enabled[i],
> + offset + 0, offset + 0, size, op);
> + sja1105_packing(buf, &entry->base[i],
> + offset + 9, offset + 1, size, op);
> + sja1105_packing(buf, &entry->top[i],
> + offset + 18, offset + 10, size, op);
> + }
> + sja1105_packing(buf, &entry->speed, 98, 97, size, op);
> + sja1105_packing(buf, &entry->vlanid, 53, 42, size, op);
> + sja1105_packing(buf, &entry->egress, 32, 32, size, op);
> + sja1105_packing(buf, &entry->ingress, 31, 31, size, op);
> + return size;
> +}
> +
> +static size_t sja1110_mac_config_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY;
> + struct sja1105_mac_config_entry *entry = entry_ptr;
> + int offset, i;
> +
> + for (i = 0, offset = 104; i < 8; i++, offset += 19) {
> + sja1105_packing(buf, &entry->enabled[i],
> + offset + 0, offset + 0, size, op);
> + sja1105_packing(buf, &entry->base[i],
> + offset + 9, offset + 1, size, op);
> + sja1105_packing(buf, &entry->top[i],
> + offset + 18, offset + 10, size, op);
> + }
> + sja1105_packing(buf, &entry->speed, 98, 96, size, op);
> + sja1105_packing(buf, &entry->vlanid, 52, 41, size, op);
> + sja1105_packing(buf, &entry->egress, 31, 31, size, op);
> + sja1105_packing(buf, &entry->ingress, 30, 30, size, op);
> + return size;
> +}
> +
> +static size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY;
> + struct sja1105_vlan_lookup_entry *entry = entry_ptr;
> +
> + sja1105_packing(buf, &entry->vmemb_port, 53, 49, size, op);
> + sja1105_packing(buf, &entry->vlan_bc, 48, 44, size, op);
> + sja1105_packing(buf, &entry->tag_port, 43, 39, size, op);
> + sja1105_packing(buf, &entry->vlanid, 38, 27, size, op);
> + return size;
> +}
> +
> +static size_t sja1110_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + struct sja1105_vlan_lookup_entry *entry = entry_ptr;
> + const size_t size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY;
> +
> + sja1105_packing(buf, &entry->vmemb_port, 73, 63, size, op);
> + sja1105_packing(buf, &entry->vlan_bc, 62, 52, size, op);
> + sja1105_packing(buf, &entry->tag_port, 51, 41, size, op);
> + sja1105_packing(buf, &entry->type_entry, 40, 39, size, op);
> + sja1105_packing(buf, &entry->vlanid, 38, 27, size, op);
> + return size;
> +}
> +
> +static size_t sja1105_xmii_params_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105_SIZE_XMII_PARAMS_ENTRY;
> + struct sja1105_xmii_params_entry *entry = entry_ptr;
> + int offset, i;
> +
> + for (i = 0, offset = 17; i < SJA1105_NUM_PORTS; i++, offset += 3) {
> + sja1105_packing(buf, &entry->xmii_mode[i],
> + offset + 1, offset + 0, size, op);
> + sja1105_packing(buf, &entry->phy_mac[i],
> + offset + 2, offset + 2, size, op);
> + }
> + return size;
> +}
> +
> +size_t sja1110_xmii_params_entry_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1110_SIZE_XMII_PARAMS_ENTRY;
> + struct sja1105_xmii_params_entry *entry = entry_ptr;
> + int offset, i;
> +
> + for (i = 0, offset = 20; i < SJA1110_NUM_PORTS; i++, offset += 4) {
> + sja1105_packing(buf, &entry->xmii_mode[i],
> + offset + 1, offset + 0, size, op);
> + sja1105_packing(buf, &entry->phy_mac[i],
> + offset + 2, offset + 2, size, op);
> + sja1105_packing(buf, &entry->special[i],
> + offset + 3, offset + 3, size, op);
> + }
> + return size;
> +}
> +
> +static size_t sja1105_table_header_packing(void *buf, void *entry_ptr,
> + enum packing_op op)
> +{
> + const size_t size = SJA1105_SIZE_TABLE_HEADER;
> + struct sja1105_table_header *entry = entry_ptr;
> +
> + sja1105_packing(buf, &entry->block_id, 31, 24, size, op);
> + sja1105_packing(buf, &entry->len, 55, 32, size, op);
> + sja1105_packing(buf, &entry->crc, 95, 64, size, op);
> + return size;
> +}
> +
> +static void
> +sja1105_table_header_pack_with_crc(void *buf, struct sja1105_table_header
> *hdr)
> +{
> + /* First pack the table as-is, then calculate the CRC, and
> + * finally put the proper CRC into the packed buffer
> + */
> + memset(buf, 0, SJA1105_SIZE_TABLE_HEADER);
> + sja1105_table_header_packing(buf, hdr, PACK);
> + hdr->crc = sja1105_crc32(buf, SJA1105_SIZE_TABLE_HEADER - 4);
> + sja1105_packing(buf + SJA1105_SIZE_TABLE_HEADER - 4, &hdr->crc,
> + 31, 0, 4, PACK);
> +}
> +
> +static void sja1105_table_write_crc(u8 *table_start, u8 *crc_ptr)
> +{
> + u64 computed_crc;
> + int len_bytes;
> +
> + len_bytes = (uintptr_t)(crc_ptr - table_start);
> + computed_crc = sja1105_crc32(table_start, len_bytes);
> + sja1105_packing(crc_ptr, &computed_crc, 31, 0, 4, PACK);
> +}
> +
> +/* The block IDs that the switches support are unfortunately sparse, so keep
> a
> + * mapping table to "block indices" and translate back and forth.
> + */
> +static const u64 blk_id_map[BLK_IDX_MAX] = {
> + [BLK_IDX_L2_POLICING] = BLKID_L2_POLICING,
> + [BLK_IDX_VLAN_LOOKUP] = BLKID_VLAN_LOOKUP,
> + [BLK_IDX_L2_FORWARDING] = BLKID_L2_FORWARDING,
> + [BLK_IDX_MAC_CONFIG] = BLKID_MAC_CONFIG,
> + [BLK_IDX_L2_FORWARDING_PARAMS] = BLKID_L2_FORWARDING_PARAMS,
> + [BLK_IDX_GENERAL_PARAMS] = BLKID_GENERAL_PARAMS,
> + [BLK_IDX_XMII_PARAMS] = BLKID_XMII_PARAMS,
> +};
> +
> +static void
> +sja1105_static_config_pack(void *buf, struct sja1105_static_config *config)
> +{
> + struct sja1105_table_header header = {0};
> + enum sja1105_blk_idx i;
> + u8 *p = buf;
> + int j;
> +
> + sja1105_packing(p, &config->device_id, 31, 0, 4, PACK);
> + p += SJA1105_SIZE_DEVICE_ID;
> +
> + for (i = 0; i < BLK_IDX_MAX; i++) {
> + const struct sja1105_table *table;
> + u8 *table_start;
> +
> + table = &config->tables[i];
> + if (!table->entry_count)
> + continue;
> +
> + header.block_id = blk_id_map[i];
> + header.len = table->entry_count *
> + table->ops->packed_entry_size / 4;
> + sja1105_table_header_pack_with_crc(p, &header);
> + p += SJA1105_SIZE_TABLE_HEADER;
> + table_start = p;
> + for (j = 0; j < table->entry_count; j++) {
> + u8 *entry_ptr = table->entries;
> +
> + entry_ptr += j * table->ops->unpacked_entry_size;
> + memset(p, 0, table->ops->packed_entry_size);
> + table->ops->packing(p, entry_ptr, PACK);
> + p += table->ops->packed_entry_size;
> + }
> + sja1105_table_write_crc(table_start, p);
> + p += 4;
> + }
> + /* Final header:
> + * Block ID does not matter
> + * Length of 0 marks that header is final
> + * CRC will be replaced on-the-fly
> + */
> + header.block_id = 0;
> + header.len = 0;
> + header.crc = 0xDEADBEEF;
> + memset(p, 0, SJA1105_SIZE_TABLE_HEADER);
> + sja1105_table_header_packing(p, &header, PACK);
> +}
> +
> +static size_t
> +sja1105_static_config_get_length(const struct sja1105_static_config *config)
> +{
> + unsigned int header_count;
> + enum sja1105_blk_idx i;
> + unsigned int sum;
> +
> + /* Ending header */
> + header_count = 1;
> + sum = SJA1105_SIZE_DEVICE_ID;
> +
> + /* Tables (headers and entries) */
> + for (i = 0; i < BLK_IDX_MAX; i++) {
> + const struct sja1105_table *table;
> +
> + table = &config->tables[i];
> + if (table->entry_count)
> + header_count++;
> +
> + sum += table->ops->packed_entry_size * table->entry_count;
> + }
> + /* Headers have an additional CRC at the end */
> + sum += header_count * (SJA1105_SIZE_TABLE_HEADER + 4);
> + /* Last header does not have an extra CRC because there is no data */
> + sum -= 4;
> +
> + return sum;
> +}
> +
> +/* Compatibility matrices */
> +static const struct sja1105_table_ops sja1105et_table_ops[BLK_IDX_MAX] = {
> + [BLK_IDX_L2_POLICING] = {
> + .packing = sja1105_l2_policing_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_policing_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_POLICING_COUNT,
> + },
> + [BLK_IDX_VLAN_LOOKUP] = {
> + .packing = sja1105_vlan_lookup_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_vlan_lookup_entry),
> + .packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY,
> + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
> + },
> + [BLK_IDX_L2_FORWARDING] = {
> + .packing = sja1105_l2_forwarding_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_forwarding_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT,
> + },
> + [BLK_IDX_MAC_CONFIG] = {
> + .packing = sja1105et_mac_config_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_mac_config_entry),
> + .packed_entry_size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY,
> + .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT,
> + },
> + [BLK_IDX_L2_FORWARDING_PARAMS] = {
> + .packing = sja1105_l2_forwarding_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_forwarding_params_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
> + },
> + [BLK_IDX_GENERAL_PARAMS] = {
> + .packing = sja1105et_general_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_general_params_entry),
> + .packed_entry_size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
> + },
> + [BLK_IDX_XMII_PARAMS] = {
> + .packing = sja1105_xmii_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_xmii_params_entry),
> + .packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT,
> + },
> +};
> +
> +static const struct sja1105_table_ops sja1105pqrs_table_ops[BLK_IDX_MAX] = {
> + [BLK_IDX_L2_POLICING] = {
> + .packing = sja1105_l2_policing_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_policing_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_POLICING_COUNT,
> + },
> + [BLK_IDX_VLAN_LOOKUP] = {
> + .packing = sja1105_vlan_lookup_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_vlan_lookup_entry),
> + .packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY,
> + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
> + },
> + [BLK_IDX_L2_FORWARDING] = {
> + .packing = sja1105_l2_forwarding_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_forwarding_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT,
> + },
> + [BLK_IDX_MAC_CONFIG] = {
> + .packing = sja1105pqrs_mac_config_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_mac_config_entry),
> + .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY,
> + .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT,
> + },
> + [BLK_IDX_L2_FORWARDING_PARAMS] = {
> + .packing = sja1105_l2_forwarding_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_forwarding_params_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
> + },
> + [BLK_IDX_GENERAL_PARAMS] = {
> + .packing = sja1105pqrs_general_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_general_params_entry),
> + .packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
> + },
> + [BLK_IDX_XMII_PARAMS] = {
> + .packing = sja1105_xmii_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_xmii_params_entry),
> + .packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT,
> + },
> +};
> +
> +static const struct sja1105_table_ops sja1110_table_ops[BLK_IDX_MAX] = {
> + [BLK_IDX_L2_POLICING] = {
> + .packing = sja1110_l2_policing_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_policing_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY,
> + .max_entry_count = SJA1110_MAX_L2_POLICING_COUNT,
> + },
> + [BLK_IDX_VLAN_LOOKUP] = {
> + .packing = sja1110_vlan_lookup_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_vlan_lookup_entry),
> + .packed_entry_size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY,
> + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
> + },
> + [BLK_IDX_L2_FORWARDING] = {
> + .packing = sja1110_l2_forwarding_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_forwarding_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY,
> + .max_entry_count = SJA1110_MAX_L2_FORWARDING_COUNT,
> + },
> + [BLK_IDX_MAC_CONFIG] = {
> + .packing = sja1110_mac_config_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_mac_config_entry),
> + .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY,
> + .max_entry_count = SJA1110_MAX_MAC_CONFIG_COUNT,
> + },
> + [BLK_IDX_L2_FORWARDING_PARAMS] = {
> + .packing = sja1110_l2_forwarding_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_l2_forwarding_params_entry),
> + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
> + },
> + [BLK_IDX_GENERAL_PARAMS] = {
> + .packing = sja1110_general_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_general_params_entry),
> + .packed_entry_size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
> + },
> + [BLK_IDX_XMII_PARAMS] = {
> + .packing = sja1110_xmii_params_entry_packing,
> + .unpacked_entry_size = sizeof(struct
> sja1105_xmii_params_entry),
> + .packed_entry_size = SJA1110_SIZE_XMII_PARAMS_ENTRY,
> + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT,
> + },
> +};
> +
> +static int sja1105_init_mii_settings(struct sja1105_private *priv)
> +{
> + struct sja1105_table *table;
> +
> + table = &priv->static_config.tables[BLK_IDX_XMII_PARAMS];
> +
> + table->entries = calloc(SJA1105_MAX_XMII_PARAMS_COUNT,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + /* Table will be populated at runtime */
> + table->entry_count = SJA1105_MAX_XMII_PARAMS_COUNT;
> +
> + return 0;
> +}
> +
> +static void sja1105_setup_tagging(struct sja1105_private *priv, int port)
> +{
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + struct sja1105_vlan_lookup_entry *vlan;
> + int cpu = pdata->cpu_port;
> +
> + /* The CPU port is implicitly configured by
> + * configuring the front-panel ports
> + */
> + if (port == cpu)
> + return;
> +
> + vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries;
> +
> + priv->pvid[port] = DSA_8021Q_DIR_TX | DSA_8021Q_PORT(port);
> +
> + vlan[port].vmemb_port = BIT(port) | BIT(cpu);
> + vlan[port].vlan_bc = BIT(port) | BIT(cpu);
> + vlan[port].tag_port = BIT(cpu);
> + vlan[port].vlanid = priv->pvid[port];
> + vlan[port].type_entry = SJA1110_VLAN_D_TAG;
> +}
> +
> +static int sja1105_init_vlan(struct sja1105_private *priv)
> +{
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + struct sja1105_table *table;
> + int port;
> +
> + table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP];
> +
> + table->entries = calloc(pdata->num_ports,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + table->entry_count = pdata->num_ports;
> +
> + for (port = 0; port < pdata->num_ports; port++)
> + sja1105_setup_tagging(priv, port);
> +
> + return 0;
> +}
> +
> +static void
> +sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd,
> + int from, int to)
> +{
> + l2_fwd[from].bc_domain |= BIT(to);
> + l2_fwd[from].reach_port |= BIT(to);
> + l2_fwd[from].fl_domain |= BIT(to);
> +}
> +
> +static int sja1105_init_l2_forwarding(struct sja1105_private *priv)
> +{
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + struct sja1105_l2_forwarding_entry *l2fwd;
> + struct sja1105_table *table;
> + int cpu = pdata->cpu_port;
> + int i;
> +
> + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING];
> +
> + table->entries = calloc(SJA1105_MAX_L2_FORWARDING_COUNT,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT;
> +
> + l2fwd = table->entries;
> +
> + /* First 5 entries define the forwarding rules */
> + for (i = 0; i < pdata->num_ports; i++) {
> + if (i == cpu)
> + continue;
> +
> + sja1105_port_allow_traffic(l2fwd, i, cpu);
> + sja1105_port_allow_traffic(l2fwd, cpu, i);
> + }
> + /* Next 8 entries define VLAN PCP mapping from ingress to egress.
> + * Leave them unpopulated (implicitly 0) but present.
> + */
> + return 0;
> +}
> +
> +static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv)
> +{
> + struct sja1105_l2_forwarding_params_entry default_l2fwd_params = {
> + /* Use a single memory partition for all ingress queues */
> + .part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 },
> + };
> + struct sja1105_table *table;
> +
> + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS];
> +
> + table->entries = calloc(SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + table->entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT;
> +
> + /* This table only has a single entry */
> + ((struct sja1105_l2_forwarding_params_entry *)table->entries)[0] =
> + default_l2fwd_params;
> +
> + return 0;
> +}
> +
> +static int sja1105_init_general_params(struct sja1105_private *priv)
> +{
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + struct sja1105_general_params_entry default_general_params = {
> + /* No frame trapping */
> + .mac_fltres1 = 0x0,
> + .mac_flt1 = 0xffffffffffff,
> + .mac_fltres0 = 0x0,
> + .mac_flt0 = 0xffffffffffff,
> + .host_port = pdata->num_ports,
> + /* No mirroring => specify an out-of-range port value */
> + .mirr_port = pdata->num_ports,
> + /* No link-local trapping => specify an out-of-range port
> value
> + */
> + .casc_port = pdata->num_ports,
> + /* Force the switch to see all traffic as untagged. */
> + .tpid = ETH_P_SJA1105,
> + .tpid2 = ETH_P_SJA1105,
> + };
> + struct sja1105_table *table;
> +
> + table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
> +
> + table->entries = calloc(SJA1105_MAX_GENERAL_PARAMS_COUNT,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + table->entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT;
> +
> + /* This table only has a single entry */
> + ((struct sja1105_general_params_entry *)table->entries)[0] =
> + default_general_params;
> +
> + return 0;
> +}
> +
> +static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing,
> + int index, int mtu)
> +{
> + policing[index].sharindx = index;
> + policing[index].smax = 65535; /* Burst size in bytes */
> + policing[index].rate = SJA1105_RATE_MBPS(1000);
> + policing[index].maxlen = mtu;
> + policing[index].partition = 0;
> +}
> +
> +static int sja1105_init_l2_policing(struct sja1105_private *priv)
> +{
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + struct sja1105_l2_policing_entry *policing;
> + struct sja1105_table *table;
> + int cpu = pdata->cpu_port;
> + int i, j, k;
> +
> + table = &priv->static_config.tables[BLK_IDX_L2_POLICING];
> +
> + table->entries = calloc(SJA1105_MAX_L2_POLICING_COUNT,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + table->entry_count = SJA1105_MAX_L2_POLICING_COUNT;
> +
> + policing = table->entries;
> +
> + /* k sweeps through all unicast policers (0-39).
> + * bcast sweeps through policers 40-44.
> + */
> + for (i = 0, k = 0; i < pdata->num_ports; i++) {
> + int bcast = (pdata->num_ports * SJA1105_NUM_TC) + i;
> + int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN;
> +
> + if (i == cpu)
> + mtu += VLAN_HLEN;
> +
> + for (j = 0; j < SJA1105_NUM_TC; j++, k++)
> + sja1105_setup_policer(policing, k, mtu);
> +
> + /* Set up this port's policer for broadcast traffic */
> + sja1105_setup_policer(policing, bcast, mtu);
> + }
> + return 0;
> +}
> +
> +static int sja1105_init_mac_settings(struct sja1105_private *priv)
> +{
> + struct sja1105_mac_config_entry default_mac = {
> + /* Enable 1 priority queue on egress. */
> + .top = {0x1FF, 0, 0, 0, 0, 0, 0},
> + .base = {0x0, 0, 0, 0, 0, 0, 0, 0},
> + .enabled = {1, 0, 0, 0, 0, 0, 0, 0},
> + /* Will be overridden in sja1105_port_enable. */
> + .speed = priv->info->port_speed[SJA1105_SPEED_AUTO],
> + .egress = true,
> + .ingress = true,
> + };
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + struct sja1105_mac_config_entry *mac;
> + struct sja1105_table *table;
> + int port;
> +
> + table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG];
> +
> + table->entries = calloc(pdata->num_ports,
> + table->ops->unpacked_entry_size);
> + if (!table->entries)
> + return -ENOMEM;
> +
> + table->entry_count = pdata->num_ports;
> +
> + mac = table->entries;
> +
> + for (port = 0; port < pdata->num_ports; port++) {
> + mac[port] = default_mac;
> + /* Internal VLAN (pvid) to apply to untagged ingress */
> + mac[port].vlanid = priv->pvid[port];
> + }
> +
> + return 0;
> +}
> +
> +static int sja1105_static_config_init(struct sja1105_private *priv)
> +{
> + struct sja1105_static_config *config = &priv->static_config;
> + const struct sja1105_table_ops *static_ops = priv->info->static_ops;
> + u64 device_id = priv->info->device_id;
> + enum sja1105_blk_idx i;
> + int rc;
> +
> + *config = (struct sja1105_static_config) {0};
> +
> + /* Transfer static_ops array from priv into per-table ops
> + * for handier access
> + */
> + for (i = 0; i < BLK_IDX_MAX; i++)
> + config->tables[i].ops = &static_ops[i];
> +
> + config->device_id = device_id;
> +
> + /* Build initial static configuration, to be fixed up during runtime
> */
> + rc = sja1105_init_vlan(priv);
> + if (rc < 0)
> + return rc;
> + rc = sja1105_init_mac_settings(priv);
> + if (rc < 0)
> + return rc;
> + rc = sja1105_init_mii_settings(priv);
> + if (rc < 0)
> + return rc;
> + rc = sja1105_init_l2_forwarding(priv);
> + if (rc < 0)
> + return rc;
> + rc = sja1105_init_l2_forwarding_params(priv);
> + if (rc < 0)
> + return rc;
> + rc = sja1105_init_l2_policing(priv);
> + if (rc < 0)
> + return rc;
> + rc = sja1105_init_general_params(priv);
> + if (rc < 0)
> + return rc;
> +
> + return 0;
> +}
> +
> +static void sja1105_static_config_free(struct sja1105_static_config *config)
> +{
> + enum sja1105_blk_idx i;
> +
> + for (i = 0; i < BLK_IDX_MAX; i++) {
> + if (config->tables[i].entry_count) {
> + free(config->tables[i].entries);
> + config->tables[i].entry_count = 0;
> + }
> + }
> +}
> +
> +static void sja1105_cgu_idiv_packing(void *buf, struct sja1105_cgu_idiv
> *idiv,
> + enum packing_op op)
> +{
> + const int size = 4;
> +
> + sja1105_packing(buf, &idiv->clksrc, 28, 24, size, op);
> + sja1105_packing(buf, &idiv->autoblock, 11, 11, size, op);
> + sja1105_packing(buf, &idiv->idiv, 5, 2, size, op);
> + sja1105_packing(buf, &idiv->pd, 0, 0, size, op);
> +}
> +
> +static int sja1105_cgu_idiv_config(struct sja1105_private *priv, int port,
> + bool enabled, int factor)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + struct sja1105_cgu_idiv idiv;
> +
> + if (regs->cgu_idiv[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + if (enabled && factor != 1 && factor != 10)
> + return -ERANGE;
> +
> + /* Payload for packed_buf */
> + idiv.clksrc = 0x0A; /* 25MHz */
> + idiv.autoblock = 1; /* Block clk automatically */
> + idiv.idiv = factor - 1; /* Divide by 1 or 10 */
> + idiv.pd = enabled ? 0 : 1; /* Power down? */
> + sja1105_cgu_idiv_packing(packed_buf, &idiv, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->cgu_idiv[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static void
> +sja1105_cgu_mii_control_packing(void *buf, struct sja1105_cgu_mii_ctrl *cmd,
> + enum packing_op op)
> +{
> + const int size = 4;
> +
> + sja1105_packing(buf, &cmd->clksrc, 28, 24, size, op);
> + sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op);
> + sja1105_packing(buf, &cmd->pd, 0, 0, size, op);
> +}
> +
> +static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv,
> + int port, sja1105_mii_role_t role)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cgu_mii_ctrl mii_tx_clk;
> + const int mac_clk_sources[] = {
> + CLKSRC_MII0_TX_CLK,
> + CLKSRC_MII1_TX_CLK,
> + CLKSRC_MII2_TX_CLK,
> + CLKSRC_MII3_TX_CLK,
> + CLKSRC_MII4_TX_CLK,
> + };
> + const int phy_clk_sources[] = {
> + CLKSRC_IDIV0,
> + CLKSRC_IDIV1,
> + CLKSRC_IDIV2,
> + CLKSRC_IDIV3,
> + CLKSRC_IDIV4,
> + };
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + int clksrc;
> +
> + if (regs->mii_tx_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + if (role == XMII_MAC)
> + clksrc = mac_clk_sources[port];
> + else
> + clksrc = phy_clk_sources[port];
> +
> + /* Payload for packed_buf */
> + mii_tx_clk.clksrc = clksrc;
> + mii_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */
> + mii_tx_clk.pd = 0; /* Power Down off => enabled */
> + sja1105_cgu_mii_control_packing(packed_buf, &mii_tx_clk, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_tx_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int
> +sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + struct sja1105_cgu_mii_ctrl mii_rx_clk;
> + const int clk_sources[] = {
> + CLKSRC_MII0_RX_CLK,
> + CLKSRC_MII1_RX_CLK,
> + CLKSRC_MII2_RX_CLK,
> + CLKSRC_MII3_RX_CLK,
> + CLKSRC_MII4_RX_CLK,
> + };
> +
> + if (regs->mii_rx_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload for packed_buf */
> + mii_rx_clk.clksrc = clk_sources[port];
> + mii_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */
> + mii_rx_clk.pd = 0; /* Power Down off => enabled */
> + sja1105_cgu_mii_control_packing(packed_buf, &mii_rx_clk, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_rx_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int
> +sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cgu_mii_ctrl mii_ext_tx_clk;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + const int clk_sources[] = {
> + CLKSRC_IDIV0,
> + CLKSRC_IDIV1,
> + CLKSRC_IDIV2,
> + CLKSRC_IDIV3,
> + CLKSRC_IDIV4,
> + };
> +
> + if (regs->mii_ext_tx_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload for packed_buf */
> + mii_ext_tx_clk.clksrc = clk_sources[port];
> + mii_ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc
> */
> + mii_ext_tx_clk.pd = 0; /* Power Down off => enabled */
> + sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_tx_clk, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_tx_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int
> +sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cgu_mii_ctrl mii_ext_rx_clk;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + const int clk_sources[] = {
> + CLKSRC_IDIV0,
> + CLKSRC_IDIV1,
> + CLKSRC_IDIV2,
> + CLKSRC_IDIV3,
> + CLKSRC_IDIV4,
> + };
> +
> + if (regs->mii_ext_rx_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload for packed_buf */
> + mii_ext_rx_clk.clksrc = clk_sources[port];
> + mii_ext_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc
> */
> + mii_ext_rx_clk.pd = 0; /* Power Down off => enabled */
> + sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_rx_clk, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_rx_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int sja1105_mii_clocking_setup(struct sja1105_private *priv, int port,
> + sja1105_mii_role_t role)
> +{
> + int rc;
> +
> + rc = sja1105_cgu_idiv_config(priv, port, (role == XMII_PHY), 1);
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_cgu_mii_tx_clk_config(priv, port, role);
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_cgu_mii_rx_clk_config(priv, port);
> + if (rc < 0)
> + return rc;
> +
> + if (role == XMII_PHY) {
> + rc = sja1105_cgu_mii_ext_tx_clk_config(priv, port);
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_cgu_mii_ext_rx_clk_config(priv, port);
> + if (rc < 0)
> + return rc;
> + }
> + return 0;
> +}
> +
> +static void
> +sja1105_cgu_pll_control_packing(void *buf, struct sja1105_cgu_pll_ctrl *cmd,
> + enum packing_op op)
> +{
> + const int size = 4;
> +
> + sja1105_packing(buf, &cmd->pllclksrc, 28, 24, size, op);
> + sja1105_packing(buf, &cmd->msel, 23, 16, size, op);
> + sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op);
> + sja1105_packing(buf, &cmd->psel, 9, 8, size, op);
> + sja1105_packing(buf, &cmd->direct, 7, 7, size, op);
> + sja1105_packing(buf, &cmd->fbsel, 6, 6, size, op);
> + sja1105_packing(buf, &cmd->bypass, 1, 1, size, op);
> + sja1105_packing(buf, &cmd->pd, 0, 0, size, op);
> +}
> +
> +static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv,
> + int port, u64 speed)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cgu_mii_ctrl txc;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + int clksrc;
> +
> + if (regs->rgmii_tx_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) {
> + clksrc = CLKSRC_PLL0;
> + } else {
> + int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2,
> + CLKSRC_IDIV3, CLKSRC_IDIV4};
> + clksrc = clk_sources[port];
> + }
> +
> + /* RGMII: 125MHz for 1000, 25MHz for 100, 2.5MHz for 10 */
> + txc.clksrc = clksrc;
> + /* Autoblock clk while changing clksrc */
> + txc.autoblock = 1;
> + /* Power Down off => enabled */
> + txc.pd = 0;
> + sja1105_cgu_mii_control_packing(packed_buf, &txc, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgmii_tx_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +/* AGU */
> +static void
> +sja1105_cfg_pad_mii_packing(void *buf, struct sja1105_cfg_pad_mii *cmd,
> + enum packing_op op)
> +{
> + const int size = 4;
> +
> + sja1105_packing(buf, &cmd->d32_os, 28, 27, size, op);
> + sja1105_packing(buf, &cmd->d32_ih, 26, 26, size, op);
> + sja1105_packing(buf, &cmd->d32_ipud, 25, 24, size, op);
> + sja1105_packing(buf, &cmd->d10_os, 20, 19, size, op);
> + sja1105_packing(buf, &cmd->d10_ih, 18, 18, size, op);
> + sja1105_packing(buf, &cmd->d10_ipud, 17, 16, size, op);
> + sja1105_packing(buf, &cmd->ctrl_os, 12, 11, size, op);
> + sja1105_packing(buf, &cmd->ctrl_ih, 10, 10, size, op);
> + sja1105_packing(buf, &cmd->ctrl_ipud, 9, 8, size, op);
> + sja1105_packing(buf, &cmd->clk_os, 4, 3, size, op);
> + sja1105_packing(buf, &cmd->clk_ih, 2, 2, size, op);
> + sja1105_packing(buf, &cmd->clk_ipud, 1, 0, size, op);
> +}
> +
> +static void
> +sja1110_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd,
> + enum packing_op op)
> +{
> + const int size = SJA1105_SIZE_CGU_CMD;
> + u64 range = 4;
> +
> + /* Fields RXC_RANGE and TXC_RANGE select the input frequency range:
> + * 0 = 2.5MHz
> + * 1 = 25MHz
> + * 2 = 50MHz
> + * 3 = 125MHz
> + * 4 = Automatically determined by port speed.
> + * There's no point in defining a structure different than the one for
> + * SJA1105, so just hardcode the frequency range to automatic, just as
> + * before.
> + */
> + sja1105_packing(buf, &cmd->rxc_stable_ovr, 26, 26, size, op);
> + sja1105_packing(buf, &cmd->rxc_delay, 25, 21, size, op);
> + sja1105_packing(buf, &range, 20, 18, size, op);
> + sja1105_packing(buf, &cmd->rxc_bypass, 17, 17, size, op);
> + sja1105_packing(buf, &cmd->rxc_pd, 16, 16, size, op);
> + sja1105_packing(buf, &cmd->txc_stable_ovr, 10, 10, size, op);
> + sja1105_packing(buf, &cmd->txc_delay, 9, 5, size, op);
> + sja1105_packing(buf, &range, 4, 2, size, op);
> + sja1105_packing(buf, &cmd->txc_bypass, 1, 1, size, op);
> + sja1105_packing(buf, &cmd->txc_pd, 0, 0, size, op);
> +}
> +
> +static int sja1105_rgmii_cfg_pad_tx_config(struct sja1105_private *priv,
> + int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cfg_pad_mii pad_mii_tx = {0};
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> +
> + if (regs->pad_mii_tx[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload */
> + pad_mii_tx.d32_os = 3; /* TXD[3:2] output stage: */
> + /* high noise/high speed */
> + pad_mii_tx.d10_os = 3; /* TXD[1:0] output stage: */
> + /* high noise/high speed */
> + pad_mii_tx.d32_ipud = 2; /* TXD[3:2] input stage: */
> + /* plain input (default) */
> + pad_mii_tx.d10_ipud = 2; /* TXD[1:0] input stage: */
> + /* plain input (default) */
> + pad_mii_tx.ctrl_os = 3; /* TX_CTL / TX_ER output stage */
> + pad_mii_tx.ctrl_ipud = 2; /* TX_CTL / TX_ER input stage (default) */
> + pad_mii_tx.clk_os = 3; /* TX_CLK output stage */
> + pad_mii_tx.clk_ih = 0; /* TX_CLK input hysteresis (default) */
> + pad_mii_tx.clk_ipud = 2; /* TX_CLK input stage (default) */
> + sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_tx, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_tx[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int sja1105_cfg_pad_rx_config(struct sja1105_private *priv, int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cfg_pad_mii pad_mii_rx = {0};
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> +
> + if (regs->pad_mii_rx[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload */
> + pad_mii_rx.d32_ih = 0; /* RXD[3:2] input stage hysteresis: */
> + /* non-Schmitt (default) */
> + pad_mii_rx.d32_ipud = 2; /* RXD[3:2] input weak pull-up/down */
> + /* plain input (default) */
> + pad_mii_rx.d10_ih = 0; /* RXD[1:0] input stage hysteresis: */
> + /* non-Schmitt (default) */
> + pad_mii_rx.d10_ipud = 2; /* RXD[1:0] input weak pull-up/down */
> + /* plain input (default) */
> + pad_mii_rx.ctrl_ih = 0; /* RX_DV/CRS_DV/RX_CTL and RX_ER */
> + /* input stage hysteresis: */
> + /* non-Schmitt (default) */
> + pad_mii_rx.ctrl_ipud = 3; /* RX_DV/CRS_DV/RX_CTL and RX_ER */
> + /* input stage weak pull-up/down: */
> + /* pull-down */
> + pad_mii_rx.clk_os = 2; /* RX_CLK/RXC output stage: */
> + /* medium noise/fast speed (default) */
> + pad_mii_rx.clk_ih = 0; /* RX_CLK/RXC input hysteresis: */
> + /* non-Schmitt (default) */
> + pad_mii_rx.clk_ipud = 2; /* RX_CLK/RXC input pull-up/down: */
> + /* plain input (default) */
> + sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_rx, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_rx[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static void
> +sja1105_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd,
> + enum packing_op op)
> +{
> + const int size = SJA1105_SIZE_CGU_CMD;
> +
> + sja1105_packing(buf, &cmd->rxc_stable_ovr, 15, 15, size, op);
> + sja1105_packing(buf, &cmd->rxc_delay, 14, 10, size, op);
> + sja1105_packing(buf, &cmd->rxc_bypass, 9, 9, size, op);
> + sja1105_packing(buf, &cmd->rxc_pd, 8, 8, size, op);
> + sja1105_packing(buf, &cmd->txc_stable_ovr, 7, 7, size, op);
> + sja1105_packing(buf, &cmd->txc_delay, 6, 2, size, op);
> + sja1105_packing(buf, &cmd->txc_bypass, 1, 1, size, op);
> + sja1105_packing(buf, &cmd->txc_pd, 0, 0, size, op);
> +}
> +
> +/* Valid range in degrees is an integer between 73.8 and 101.7 */
> +static u64 sja1105_rgmii_delay(u64 phase)
> +{
> + /* UM11040.pdf: The delay in degree phase is 73.8 + delay_tune * 0.9.
> + * To avoid floating point operations we'll multiply by 10
> + * and get 1 decimal point precision.
> + */
> + phase *= 10;
> + return (phase - 738) / 9;
> +}
> +
> +static int sja1105pqrs_setup_rgmii_delay(struct sja1105_private *priv, int
> port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cfg_pad_mii_id pad_mii_id = {0};
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + int rc;
> +
> + if (priv->rgmii_rx_delay[port])
> + pad_mii_id.rxc_delay = sja1105_rgmii_delay(90);
> + if (priv->rgmii_tx_delay[port])
> + pad_mii_id.txc_delay = sja1105_rgmii_delay(90);
> +
> + /* Stage 1: Turn the RGMII delay lines off. */
> + pad_mii_id.rxc_bypass = 1;
> + pad_mii_id.rxc_pd = 1;
> + pad_mii_id.txc_bypass = 1;
> + pad_mii_id.txc_pd = 1;
> + sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK);
> +
> + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> + if (rc < 0)
> + return rc;
> +
> + /* Stage 2: Turn the RGMII delay lines on. */
> + if (priv->rgmii_rx_delay[port]) {
> + pad_mii_id.rxc_bypass = 0;
> + pad_mii_id.rxc_pd = 0;
> + }
> + if (priv->rgmii_tx_delay[port]) {
> + pad_mii_id.txc_bypass = 0;
> + pad_mii_id.txc_pd = 0;
> + }
> + sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +int sja1110_setup_rgmii_delay(struct sja1105_private *priv, int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cfg_pad_mii_id pad_mii_id = {0};
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> +
> + pad_mii_id.rxc_pd = 1;
> + pad_mii_id.txc_pd = 1;
> +
> + if (priv->rgmii_rx_delay[port]) {
> + pad_mii_id.rxc_delay = sja1105_rgmii_delay(90);
> + /* The "BYPASS" bit in SJA1110 is actually a "don't bypass" */
> + pad_mii_id.rxc_bypass = 1;
> + pad_mii_id.rxc_pd = 0;
> + }
> +
> + if (priv->rgmii_tx_delay[port]) {
> + pad_mii_id.txc_delay = sja1105_rgmii_delay(90);
> + pad_mii_id.txc_bypass = 1;
> + pad_mii_id.txc_pd = 0;
> + }
> +
> + sja1110_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int sja1105_rgmii_clocking_setup(struct sja1105_private *priv, int
> port,
> + sja1105_mii_role_t role)
> +{
> + struct sja1105_mac_config_entry *mac;
> + struct udevice *dev = priv->dev;
> + u64 speed;
> + int rc;
> +
> + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
> + speed = mac[port].speed;
> +
> + if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) {
> + /* 1000Mbps, IDIV disabled (125 MHz) */
> + rc = sja1105_cgu_idiv_config(priv, port, false, 1);
> + } else if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS]) {
> + /* 100Mbps, IDIV enabled, divide by 1 (25 MHz) */
> + rc = sja1105_cgu_idiv_config(priv, port, true, 1);
> + } else if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS]) {
> + /* 10Mbps, IDIV enabled, divide by 10 (2.5 MHz) */
> + rc = sja1105_cgu_idiv_config(priv, port, true, 10);
> + } else if (speed == priv->info->port_speed[SJA1105_SPEED_AUTO]) {
> + /* Skip CGU configuration if there is no speed available
> + * (e.g. link is not established yet)
> + */
> + dev_dbg(dev, "Speed not available, skipping CGU config\n");
> + return 0;
> + } else {
> + rc = -EINVAL;
> + }
> +
> + if (rc < 0) {
> + dev_err(dev, "Failed to configure idiv\n");
> + return rc;
> + }
> + rc = sja1105_cgu_rgmii_tx_clk_config(priv, port, speed);
> + if (rc < 0) {
> + dev_err(dev, "Failed to configure RGMII Tx clock\n");
> + return rc;
> + }
> + rc = sja1105_rgmii_cfg_pad_tx_config(priv, port);
> + if (rc < 0) {
> + dev_err(dev, "Failed to configure Tx pad registers\n");
> + return rc;
> + }
> +
> + if (!priv->info->setup_rgmii_delay)
> + return 0;
> +
> + return priv->info->setup_rgmii_delay(priv, port);
> +}
> +
> +static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv,
> + int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + struct sja1105_cgu_mii_ctrl ref_clk;
> + const int clk_sources[] = {
> + CLKSRC_MII0_TX_CLK,
> + CLKSRC_MII1_TX_CLK,
> + CLKSRC_MII2_TX_CLK,
> + CLKSRC_MII3_TX_CLK,
> + CLKSRC_MII4_TX_CLK,
> + };
> +
> + if (regs->rmii_ref_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload for packed_buf */
> + ref_clk.clksrc = clk_sources[port];
> + ref_clk.autoblock = 1; /* Autoblock clk while changing clksrc */
> + ref_clk.pd = 0; /* Power Down off => enabled */
> + sja1105_cgu_mii_control_packing(packed_buf, &ref_clk, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ref_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int
> +sja1105_cgu_rmii_ext_tx_clk_config(struct sja1105_private *priv, int port)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_cgu_mii_ctrl ext_tx_clk;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> +
> + if (regs->rmii_ext_tx_clk[port] == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Payload for packed_buf */
> + ext_tx_clk.clksrc = CLKSRC_PLL1;
> + ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */
> + ext_tx_clk.pd = 0; /* Power Down off => enabled */
> + sja1105_cgu_mii_control_packing(packed_buf, &ext_tx_clk, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ext_tx_clk[port],
> + packed_buf, SJA1105_SIZE_CGU_CMD);
> +}
> +
> +static int sja1105_cgu_rmii_pll_config(struct sja1105_private *priv)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
> + struct sja1105_cgu_pll_ctrl pll = {0};
> + int rc;
> +
> + if (regs->rmii_pll1 == SJA1105_RSV_ADDR)
> + return 0;
> +
> + /* Step 1: PLL1 setup for 50Mhz */
> + pll.pllclksrc = 0xA;
> + pll.msel = 0x1;
> + pll.autoblock = 0x1;
> + pll.psel = 0x1;
> + pll.direct = 0x0;
> + pll.fbsel = 0x1;
> + pll.bypass = 0x0;
> + pll.pd = 0x1;
> +
> + sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK);
> + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf,
> + SJA1105_SIZE_CGU_CMD);
> + if (rc < 0)
> + return rc;
> +
> + /* Step 2: Enable PLL1 */
> + pll.pd = 0x0;
> +
> + sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK);
> + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf,
> + SJA1105_SIZE_CGU_CMD);
> + return rc;
> +}
> +
> +static int sja1105_rmii_clocking_setup(struct sja1105_private *priv, int
> port,
> + sja1105_mii_role_t role)
> +{
> + int rc;
> +
> + /* AH1601.pdf chapter 2.5.1. Sources */
> + if (role == XMII_MAC) {
> + /* Configure and enable PLL1 for 50Mhz output */
> + rc = sja1105_cgu_rmii_pll_config(priv);
> + if (rc < 0)
> + return rc;
> + }
> + /* Disable IDIV for this port */
> + rc = sja1105_cgu_idiv_config(priv, port, false, 1);
> + if (rc < 0)
> + return rc;
> + /* Source to sink mappings */
> + rc = sja1105_cgu_rmii_ref_clk_config(priv, port);
> + if (rc < 0)
> + return rc;
> + if (role == XMII_MAC) {
> + rc = sja1105_cgu_rmii_ext_tx_clk_config(priv, port);
> + if (rc < 0)
> + return rc;
> + }
> + return 0;
> +}
> +
> +static int sja1105_pcs_read(struct sja1105_private *priv, int addr,
> + int devad, int regnum)
> +{
> + return priv->mdio_pcs->read(priv->mdio_pcs, addr, devad, regnum);
> +}
> +
> +static int sja1105_pcs_write(struct sja1105_private *priv, int addr,
> + int devad, int regnum, u16 val)
> +{
> + return priv->mdio_pcs->write(priv->mdio_pcs, addr, devad, regnum,
> val);
> +}
> +
> +/* In NXP SJA1105, the PCS is integrated with a PMA that has the TX lane
> + * polarity inverted by default (PLUS is MINUS, MINUS is PLUS). To obtain
> + * normal non-inverted behavior, the TX lane polarity must be inverted in the
> + * PCS, via the DIGITAL_CONTROL_2 register.
> + */
> +static int sja1105_pma_config(struct sja1105_private *priv, int port)
> +{
> + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + DW_VR_MII_DIG_CTRL2,
> + DW_VR_MII_DIG_CTRL2_TX_POL_INV);
> +}
> +
> +static int sja1110_pma_config(struct sja1105_private *priv, int port)
> +{
> + u16 txpll_fbdiv = 0x19, txpll_refdiv = 0x1;
> + u16 rxpll_fbdiv = 0x19, rxpll_refdiv = 0x1;
> + u16 rx_cdr_ctle = 0x212a;
> + u16 val;
> + int rc;
> +
> + /* Program TX PLL feedback divider and reference divider settings for
> + * correct oscillation frequency.
> + */
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> SJA1110_TXPLL_CTRL0,
> + SJA1110_TXPLL_FBDIV(txpll_fbdiv));
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> SJA1110_TXPLL_CTRL1,
> + SJA1110_TXPLL_REFDIV(txpll_refdiv));
> + if (rc < 0)
> + return rc;
> +
> + /* Program transmitter amplitude and disable amplitude trimming */
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + SJA1110_LANE_DRIVER1_0, SJA1110_TXDRV(0x5));
> + if (rc < 0)
> + return rc;
> +
> + val = SJA1110_TXDRVTRIM_LSB(0xffffffull);
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + SJA1110_LANE_DRIVER2_0, val);
> + if (rc < 0)
> + return rc;
> +
> + val = SJA1110_TXDRVTRIM_MSB(0xffffffull) | SJA1110_LANE_DRIVER2_1_RSV;
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + SJA1110_LANE_DRIVER2_1, val);
> + if (rc < 0)
> + return rc;
> +
> + /* Enable input and output resistor terminations for low BER. */
> + val = SJA1110_ACCOUPLE_RXVCM_EN | SJA1110_CDR_GAIN |
> + SJA1110_RXRTRIM(4) | SJA1110_RXTEN | SJA1110_TXPLL_BWSEL |
> + SJA1110_TXRTRIM(3) | SJA1110_TXTEN;
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, SJA1110_LANE_TRIM,
> + val);
> + if (rc < 0)
> + return rc;
> +
> + /* Select PCS as transmitter data source. */
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + SJA1110_LANE_DATAPATH_1, 0);
> + if (rc < 0)
> + return rc;
> +
> + /* Program RX PLL feedback divider and reference divider for correct
> + * oscillation frequency.
> + */
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> SJA1110_RXPLL_CTRL0,
> + SJA1110_RXPLL_FBDIV(rxpll_fbdiv));
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> SJA1110_RXPLL_CTRL1,
> + SJA1110_RXPLL_REFDIV(rxpll_refdiv));
> + if (rc < 0)
> + return rc;
> +
> + /* Program threshold for receiver signal detector.
> + * Enable control of RXPLL by receiver signal detector to disable
> RXPLL
> + * when an input signal is not present.
> + */
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + SJA1110_RX_DATA_DETECT, 0x0005);
> + if (rc < 0)
> + return rc;
> +
> + /* Enable TX and RX PLLs and circuits.
> + * Release reset of PMA to enable data flow to/from PCS.
> + */
> + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2,
> + SJA1110_POWERDOWN_ENABLE);
> + if (rc < 0)
> + return rc;
> +
> + val = rc & ~(SJA1110_TXPLL_PD | SJA1110_TXPD | SJA1110_RXCH_PD |
> + SJA1110_RXBIAS_PD | SJA1110_RESET_SER_EN |
> + SJA1110_RESET_SER | SJA1110_RESET_DES);
> + val |= SJA1110_RXPKDETEN | SJA1110_RCVEN;
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> + SJA1110_POWERDOWN_ENABLE, val);
> + if (rc < 0)
> + return rc;
> +
> + /* Program continuous-time linear equalizer (CTLE) settings. */
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> SJA1110_RX_CDR_CTLE,
> + rx_cdr_ctle);
> + if (rc < 0)
> + return rc;
> +
> + return 0;
> +}
> +
> +static int sja1105_xpcs_config_aneg_c37_sgmii(struct sja1105_private *priv,
> + int port)
> +{
> + int rc;
> +
> + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL);
> + if (rc < 0)
> + return rc;
> +
> + rc &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK);
> + rc |= (DW_VR_MII_PCS_MODE_C37_SGMII <<
> DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT &
> + DW_VR_MII_PCS_MODE_MASK);
> + rc |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII <<
> DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT &
> + DW_VR_MII_TX_CONFIG_MASK);
> +
> + rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL,
> rc);
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2,
> DW_VR_MII_DIG_CTRL1);
> + if (rc < 0)
> + return rc;
> +
> + if (priv->xpcs_cfg[port].inband_an)
> + rc |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
> + else
> + rc &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
> +
> + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2,
> DW_VR_MII_DIG_CTRL1, rc);
> +}
> +
> +static int sja1105_xpcs_link_up_sgmii(struct sja1105_private *priv, int port)
> +{
> + int val = BMCR_FULLDPLX;
> +
> + if (priv->xpcs_cfg[port].inband_an)
> + return 0;
> +
> + switch (priv->xpcs_cfg[port].speed) {
> + case SPEED_1000:
> + val = BMCR_SPEED1000;
> + break;
> + case SPEED_100:
> + val = BMCR_SPEED100;
> + break;
> + case SPEED_10:
> + val = BMCR_SPEED10;
> + break;
> + default:
> + dev_err(priv->dev, "Invalid PCS speed %d\n",
> + priv->xpcs_cfg[port].speed);
> + return -EINVAL;
> + }
> +
> + return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1, val);
> +}
> +
> +static int sja1105_sgmii_setup(struct sja1105_private *priv, int port)
> +{
> + int rc;
> +
> + rc = sja1105_xpcs_config_aneg_c37_sgmii(priv, port);
> + if (rc)
> + return rc;
> +
> + rc = sja1105_xpcs_link_up_sgmii(priv, port);
> + if (rc)
> + return rc;
> +
> + return priv->info->pma_config(priv, port);
> +}
> +
> +static int sja1105_clocking_setup_port(struct sja1105_private *priv, int
> port)
> +{
> + struct sja1105_xmii_params_entry *mii;
> + sja1105_phy_interface_t phy_mode;
> + sja1105_mii_role_t role;
> + int rc;
> +
> + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries;
> +
> + /* RGMII etc */
> + phy_mode = mii->xmii_mode[port];
> + /* MAC or PHY, for applicable types (not RGMII) */
> + role = mii->phy_mac[port];
> +
> + switch (phy_mode) {
> + case XMII_MODE_MII:
> + rc = sja1105_mii_clocking_setup(priv, port, role);
> + break;
> + case XMII_MODE_RMII:
> + rc = sja1105_rmii_clocking_setup(priv, port, role);
> + break;
> + case XMII_MODE_RGMII:
> + rc = sja1105_rgmii_clocking_setup(priv, port, role);
> + break;
> + case XMII_MODE_SGMII:
> + rc = sja1105_sgmii_setup(priv, port);
> + break;
> + default:
> + return -EINVAL;
> + }
> + if (rc)
> + return rc;
> +
> + /* Internally pull down the RX_DV/CRS_DV/RX_CTL and RX_ER inputs */
> + return sja1105_cfg_pad_rx_config(priv, port);
> +}
> +
> +static int sja1105_clocking_setup(struct sja1105_private *priv)
> +{
> + struct dsa_pdata *pdata = dev_get_uclass_plat(priv->dev);
> + int port, rc;
> +
> + for (port = 0; port < pdata->num_ports; port++) {
> + rc = sja1105_clocking_setup_port(priv, port);
> + if (rc < 0)
> + return rc;
> + }
> + return 0;
> +}
> +
> +static int sja1105_pcs_mdio_read(struct mii_dev *bus, int phy, int mmd, int
> reg)
> +{
> + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0};
> + struct sja1105_private *priv = bus->priv;
> + const int size = SJA1105_SIZE_MDIO_CMD;
> + u64 addr, tmp;
> + int rc;
> +
> + if (mmd == MDIO_DEVAD_NONE)
> + return -ENODEV;
> +
> + if (!priv->info->supports_sgmii[phy])
> + return -ENODEV;
> +
> + addr = (mmd << 16) | (reg & GENMASK(15, 0));
> +
> + if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
> + return 0xffff;
> +
> + rc = sja1105_xfer_buf(priv, SPI_READ, addr, packed_buf, size);
> + if (rc < 0)
> + return rc;
> +
> + sja1105_packing(packed_buf, &tmp, 31, 0, size, UNPACK);
> +
> + return tmp & 0xffff;
> +}
> +
> +static int sja1105_pcs_mdio_write(struct mii_dev *bus, int phy, int mmd,
> + int reg, u16 val)
> +{
> + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0};
> + struct sja1105_private *priv = bus->priv;
> + const int size = SJA1105_SIZE_MDIO_CMD;
> + u64 addr, tmp;
> +
> + if (mmd == MDIO_DEVAD_NONE)
> + return -ENODEV;
> +
> + if (!priv->info->supports_sgmii[phy])
> + return -ENODEV;
> +
> + addr = (mmd << 16) | (reg & GENMASK(15, 0));
> + tmp = val;
> +
> + if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
> + return -ENODEV;
> +
> + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, addr, packed_buf, size);
> +}
> +
> +static int sja1110_pcs_mdio_read(struct mii_dev *bus, int phy, int mmd, int
> reg)
> +{
> + struct sja1105_private *priv = bus->priv;
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0};
> + const int size = SJA1105_SIZE_MDIO_CMD;
> + int offset, bank;
> + u64 addr, tmp;
> + int rc;
> +
> + if (mmd == MDIO_DEVAD_NONE)
> + return -ENODEV;
> +
> + if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
> + return -ENODEV;
> +
> + addr = (mmd << 16) | (reg & GENMASK(15, 0));
> +
> + bank = addr >> 8;
> + offset = addr & GENMASK(7, 0);
> +
> + /* This addressing scheme reserves register 0xff for the bank address
> + * register, so that can never be addressed.
> + */
> + if (offset == 0xff)
> + return -ENODEV;
> +
> + tmp = bank;
> +
> + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK);
> +
> + rc = sja1105_xfer_buf(priv, SPI_WRITE,
> + regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
> + packed_buf, size);
> + if (rc < 0)
> + return rc;
> +
> + rc = sja1105_xfer_buf(priv, SPI_READ, regs->pcs_base[phy] + offset,
> + packed_buf, size);
> + if (rc < 0)
> + return rc;
> +
> + sja1105_packing(packed_buf, &tmp, 31, 0, size, UNPACK);
> +
> + return tmp & 0xffff;
> +}
> +
> +static int sja1110_pcs_mdio_write(struct mii_dev *bus, int phy, int mmd,
> + int reg, u16 val)
> +{
> + struct sja1105_private *priv = bus->priv;
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_MDIO_CMD] = {0};
> + const int size = SJA1105_SIZE_MDIO_CMD;
> + int offset, bank;
> + u64 addr, tmp;
> + int rc;
> +
> + if (mmd == MDIO_DEVAD_NONE)
> + return -ENODEV;
> +
> + if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
> + return -ENODEV;
> +
> + addr = (mmd << 16) | (reg & GENMASK(15, 0));
> +
> + bank = addr >> 8;
> + offset = addr & GENMASK(7, 0);
> +
> + /* This addressing scheme reserves register 0xff for the bank address
> + * register, so that can never be addressed.
> + */
> + if (offset == 0xff)
> + return -ENODEV;
> +
> + tmp = bank;
> + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK);
> +
> + rc = sja1105_xfer_buf(priv, SPI_WRITE,
> + regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
> + packed_buf, size);
> + if (rc < 0)
> + return rc;
> +
> + tmp = val;
> + sja1105_packing(packed_buf, &tmp, 31, 0, size, PACK);
> +
> + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pcs_base[phy] + offset,
> + packed_buf, size);
> +}
> +
> +static int sja1105_mdiobus_register(struct sja1105_private *priv)
> +{
> + struct udevice *dev = priv->dev;
> + struct mii_dev *bus;
> + int rc;
> +
> + if (!priv->info->pcs_mdio_read || !priv->info->pcs_mdio_write)
> + return 0;
> +
> + bus = mdio_alloc();
> + if (!bus)
> + return -ENOMEM;
> +
> + snprintf(bus->name, MDIO_NAME_LEN, "%s-pcs", dev->name);
> + bus->read = priv->info->pcs_mdio_read;
> + bus->write = priv->info->pcs_mdio_write;
> + bus->priv = priv;
> +
> + rc = mdio_register(bus);
> + if (rc) {
> + mdio_free(bus);
> + return rc;
> + }
> +
> + priv->mdio_pcs = bus;
> +
> + return 0;
> +}
> +
> +static void sja1105_mdiobus_unregister(struct sja1105_private *priv)
> +{
> + if (!priv->mdio_pcs)
> + return;
> +
> + mdio_unregister(priv->mdio_pcs);
> + mdio_free(priv->mdio_pcs);
> +}
> +
> +static const struct sja1105_regs sja1105et_regs = {
> + .device_id = 0x0,
> + .prod_id = 0x100BC3,
> + .status = 0x1,
> + .port_control = 0x11,
> + .config = 0x020000,
> + .rgu = 0x100440,
> + /* UM10944.pdf, Table 86, ACU Register overview */
> + .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},
> + .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809},
> + .rmii_pll1 = 0x10000A,
> + .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F},
> + /* UM10944.pdf, Table 78, CGU Register overview */
> + .mii_tx_clk = {0x100013, 0x10001A, 0x100021, 0x100028, 0x10002F},
> + .mii_rx_clk = {0x100014, 0x10001B, 0x100022, 0x100029, 0x100030},
> + .mii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
> + .mii_ext_rx_clk = {0x100019, 0x100020, 0x100027, 0x10002E, 0x100035},
> + .rgmii_tx_clk = {0x100016, 0x10001D, 0x100024, 0x10002B, 0x100032},
> + .rmii_ref_clk = {0x100015, 0x10001C, 0x100023, 0x10002A, 0x100031},
> + .rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
> +};
> +
> +static const struct sja1105_regs sja1105pqrs_regs = {
> + .device_id = 0x0,
> + .prod_id = 0x100BC3,
> + .status = 0x1,
> + .port_control = 0x12,
> + .config = 0x020000,
> + .rgu = 0x100440,
> + /* UM10944.pdf, Table 86, ACU Register overview */
> + .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},
> + .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809},
> + .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814},
> + .rmii_pll1 = 0x10000A,
> + .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F},
> + /* UM11040.pdf, Table 114 */
> + .mii_tx_clk = {0x100013, 0x100019, 0x10001F, 0x100025, 0x10002B},
> + .mii_rx_clk = {0x100014, 0x10001A, 0x100020, 0x100026, 0x10002C},
> + .mii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F},
> + .mii_ext_rx_clk = {0x100018, 0x10001E, 0x100024, 0x10002A, 0x100030},
> + .rgmii_tx_clk = {0x100016, 0x10001C, 0x100022, 0x100028, 0x10002E},
> + .rmii_ref_clk = {0x100015, 0x10001B, 0x100021, 0x100027, 0x10002D},
> + .rmii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F},
> +};
> +
> +static const struct sja1105_regs sja1110_regs = {
> + .device_id = SJA1110_SPI_ADDR(0x0),
> + .prod_id = SJA1110_ACU_ADDR(0xf00),
> + .status = SJA1110_SPI_ADDR(0x4),
> + .port_control = SJA1110_SPI_ADDR(0x50), /* actually INHIB_TX */
> + .config = 0x020000,
> + .rgu = SJA1110_RGU_ADDR(0x100), /* Reset Control Register 0 */
> + /* Ports 2 and 3 are capable of xMII, but there isn't anything to
> + * configure in the CGU/ACU for them.
> + */
> + .pad_mii_tx = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR},
> + .pad_mii_rx = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR},
> + .pad_mii_id = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1110_ACU_ADDR(0x18), SJA1110_ACU_ADDR(0x28),
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR},
> + .rmii_pll1 = SJA1105_RSV_ADDR,
> + .cgu_idiv = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .mii_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .mii_rx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .mii_ext_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .mii_ext_rx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .rgmii_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .rmii_ref_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> + .rmii_ext_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR},
> + .pcs_base = {SJA1105_RSV_ADDR, 0x1c1400, 0x1c1800, 0x1c1c00, 0x1c2000,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR,
> + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR},
> +};
> +
> +enum sja1105_switch_id {
> + SJA1105E = 0,
> + SJA1105T,
> + SJA1105P,
> + SJA1105Q,
> + SJA1105R,
> + SJA1105S,
> + SJA1110A,
> + SJA1110B,
> + SJA1110C,
> + SJA1110D,
> + SJA1105_MAX_SWITCH_ID,
> +};
> +
> +static const struct sja1105_info sja1105_info[] = {
> + [SJA1105E] = {
> + .device_id = SJA1105E_DEVICE_ID,
> + .part_no = SJA1105ET_PART_NO,
> + .static_ops = sja1105et_table_ops,
> + .reset_cmd = sja1105et_reset_cmd,
> + .regs = &sja1105et_regs,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 3,
> + [SJA1105_SPEED_100MBPS] = 2,
> + [SJA1105_SPEED_1000MBPS] = 1,
> + },
> + .supports_mii = {true, true, true, true, true},
> + .supports_rmii = {true, true, true, true, true},
> + .supports_rgmii = {true, true, true, true, true},
> + .name = "SJA1105E",
> + },
> + [SJA1105T] = {
> + .device_id = SJA1105T_DEVICE_ID,
> + .part_no = SJA1105ET_PART_NO,
> + .static_ops = sja1105et_table_ops,
> + .reset_cmd = sja1105et_reset_cmd,
> + .regs = &sja1105et_regs,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 3,
> + [SJA1105_SPEED_100MBPS] = 2,
> + [SJA1105_SPEED_1000MBPS] = 1,
> + },
> + .supports_mii = {true, true, true, true, true},
> + .supports_rmii = {true, true, true, true, true},
> + .supports_rgmii = {true, true, true, true, true},
> + .name = "SJA1105T",
> + },
> + [SJA1105P] = {
> + .device_id = SJA1105PR_DEVICE_ID,
> + .part_no = SJA1105P_PART_NO,
> + .static_ops = sja1105pqrs_table_ops,
> + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay,
> + .reset_cmd = sja1105pqrs_reset_cmd,
> + .regs = &sja1105pqrs_regs,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 3,
> + [SJA1105_SPEED_100MBPS] = 2,
> + [SJA1105_SPEED_1000MBPS] = 1,
> + },
> + .supports_mii = {true, true, true, true, true},
> + .supports_rmii = {true, true, true, true, true},
> + .supports_rgmii = {true, true, true, true, true},
> + .name = "SJA1105P",
> + },
> + [SJA1105Q] = {
> + .device_id = SJA1105QS_DEVICE_ID,
> + .part_no = SJA1105Q_PART_NO,
> + .static_ops = sja1105pqrs_table_ops,
> + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay,
> + .reset_cmd = sja1105pqrs_reset_cmd,
> + .regs = &sja1105pqrs_regs,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 3,
> + [SJA1105_SPEED_100MBPS] = 2,
> + [SJA1105_SPEED_1000MBPS] = 1,
> + },
> + .supports_mii = {true, true, true, true, true},
> + .supports_rmii = {true, true, true, true, true},
> + .supports_rgmii = {true, true, true, true, true},
> + .name = "SJA1105Q",
> + },
> + [SJA1105R] = {
> + .device_id = SJA1105PR_DEVICE_ID,
> + .part_no = SJA1105R_PART_NO,
> + .static_ops = sja1105pqrs_table_ops,
> + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay,
> + .reset_cmd = sja1105pqrs_reset_cmd,
> + .regs = &sja1105pqrs_regs,
> + .pcs_mdio_read = sja1105_pcs_mdio_read,
> + .pcs_mdio_write = sja1105_pcs_mdio_write,
> + .pma_config = sja1105_pma_config,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 3,
> + [SJA1105_SPEED_100MBPS] = 2,
> + [SJA1105_SPEED_1000MBPS] = 1,
> + },
> + .supports_mii = {true, true, true, true, true},
> + .supports_rmii = {true, true, true, true, true},
> + .supports_rgmii = {true, true, true, true, true},
> + .supports_sgmii = {false, false, false, false, true},
> + .name = "SJA1105R",
> + },
> + [SJA1105S] = {
> + .device_id = SJA1105QS_DEVICE_ID,
> + .part_no = SJA1105S_PART_NO,
> + .static_ops = sja1105pqrs_table_ops,
> + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay,
> + .reset_cmd = sja1105pqrs_reset_cmd,
> + .regs = &sja1105pqrs_regs,
> + .pcs_mdio_read = sja1105_pcs_mdio_read,
> + .pcs_mdio_write = sja1105_pcs_mdio_write,
> + .pma_config = sja1105_pma_config,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 3,
> + [SJA1105_SPEED_100MBPS] = 2,
> + [SJA1105_SPEED_1000MBPS] = 1,
> + },
> + .supports_mii = {true, true, true, true, true},
> + .supports_rmii = {true, true, true, true, true},
> + .supports_rgmii = {true, true, true, true, true},
> + .supports_sgmii = {false, false, false, false, true},
> + .name = "SJA1105S",
> + },
> + [SJA1110A] = {
> + .device_id = SJA1110_DEVICE_ID,
> + .part_no = SJA1110A_PART_NO,
> + .static_ops = sja1110_table_ops,
> + .setup_rgmii_delay = sja1110_setup_rgmii_delay,
> + .reset_cmd = sja1110_reset_cmd,
> + .regs = &sja1110_regs,
> + .pcs_mdio_read = sja1110_pcs_mdio_read,
> + .pcs_mdio_write = sja1110_pcs_mdio_write,
> + .pma_config = sja1110_pma_config,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 4,
> + [SJA1105_SPEED_100MBPS] = 3,
> + [SJA1105_SPEED_1000MBPS] = 2,
> + },
> + .supports_mii = {true, true, true, true, false,
> + true, true, true, true, true,
> true},
> + .supports_rmii = {false, false, true, true, false,
> + false, false, false, false, false,
> false},
> + .supports_rgmii = {false, false, true, true, false,
> + false, false, false, false, false,
> false},
> + .supports_sgmii = {false, true, true, true, true,
> + false, false, false, false, false,
> false},
> + .name = "SJA1110A",
> + },
> + [SJA1110B] = {
> + .device_id = SJA1110_DEVICE_ID,
> + .part_no = SJA1110B_PART_NO,
> + .static_ops = sja1110_table_ops,
> + .setup_rgmii_delay = sja1110_setup_rgmii_delay,
> + .reset_cmd = sja1110_reset_cmd,
> + .regs = &sja1110_regs,
> + .pcs_mdio_read = sja1110_pcs_mdio_read,
> + .pcs_mdio_write = sja1110_pcs_mdio_write,
> + .pma_config = sja1110_pma_config,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 4,
> + [SJA1105_SPEED_100MBPS] = 3,
> + [SJA1105_SPEED_1000MBPS] = 2,
> + },
> + .supports_mii = {true, true, true, true, false,
> + true, true, true, true, true,
> false},
> + .supports_rmii = {false, false, true, true, false,
> + false, false, false, false, false,
> false},
> + .supports_rgmii = {false, false, true, true, false,
> + false, false, false, false, false,
> false},
> + .supports_sgmii = {false, false, false, true, true,
> + false, false, false, false, false,
> false},
> + .name = "SJA1110B",
> + },
> + [SJA1110C] = {
> + .device_id = SJA1110_DEVICE_ID,
> + .part_no = SJA1110C_PART_NO,
> + .static_ops = sja1110_table_ops,
> + .setup_rgmii_delay = sja1110_setup_rgmii_delay,
> + .reset_cmd = sja1110_reset_cmd,
> + .regs = &sja1110_regs,
> + .pcs_mdio_read = sja1110_pcs_mdio_read,
> + .pcs_mdio_write = sja1110_pcs_mdio_write,
> + .pma_config = sja1110_pma_config,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 4,
> + [SJA1105_SPEED_100MBPS] = 3,
> + [SJA1105_SPEED_1000MBPS] = 2,
> + },
> + .supports_mii = {true, true, true, true, false,
> + true, true, true, false, false,
> false},
> + .supports_rmii = {false, false, true, true, false,
> + false, false, false, false, false,
> false},
> + .supports_rgmii = {false, false, true, true, false,
> + false, false, false, false, false,
> false},
> + .supports_sgmii = {false, false, false, false, true,
> + false, false, false, false, false,
> false},
> + .name = "SJA1110C",
> + },
> + [SJA1110D] = {
> + .device_id = SJA1110_DEVICE_ID,
> + .part_no = SJA1110D_PART_NO,
> + .static_ops = sja1110_table_ops,
> + .setup_rgmii_delay = sja1110_setup_rgmii_delay,
> + .reset_cmd = sja1110_reset_cmd,
> + .regs = &sja1110_regs,
> + .pcs_mdio_read = sja1110_pcs_mdio_read,
> + .pcs_mdio_write = sja1110_pcs_mdio_write,
> + .pma_config = sja1110_pma_config,
> + .port_speed = {
> + [SJA1105_SPEED_AUTO] = 0,
> + [SJA1105_SPEED_10MBPS] = 4,
> + [SJA1105_SPEED_100MBPS] = 3,
> + [SJA1105_SPEED_1000MBPS] = 2,
> + },
> + .supports_mii = {true, false, true, false, false,
> + true, true, true, false, false,
> false},
> + .supports_rmii = {false, false, true, false, false,
> + false, false, false, false, false,
> false},
> + .supports_rgmii = {false, false, true, false, false,
> + false, false, false, false, false,
> false},
> + .supports_sgmii = {false, true, true, true, true,
> + false, false, false, false, false,
> false},
> + .name = "SJA1110D",
> + },
> +};
> +
> +struct sja1105_status {
> + u64 configs;
> + u64 crcchkl;
> + u64 ids;
> + u64 crcchkg;
> +};
> +
> +static void sja1105_status_unpack(void *buf, struct sja1105_status *status)
> +{
> + sja1105_packing(buf, &status->configs, 31, 31, 4, UNPACK);
> + sja1105_packing(buf, &status->crcchkl, 30, 30, 4, UNPACK);
> + sja1105_packing(buf, &status->ids, 29, 29, 4, UNPACK);
> + sja1105_packing(buf, &status->crcchkg, 28, 28, 4, UNPACK);
> +}
> +
> +static int sja1105_status_get(struct sja1105_private *priv,
> + struct sja1105_status *status)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[4];
> + int rc;
> +
> + rc = sja1105_xfer_buf(priv, SPI_READ, regs->status, packed_buf, 4);
> + if (rc < 0)
> + return rc;
> +
> + sja1105_status_unpack(packed_buf, status);
> +
> + return 0;
> +}
> +
> +/* Not const because unpacking priv->static_config into buffers and preparing
> + * for upload requires the recalculation of table CRCs and updating the
> + * structures with these.
> + */
> +static int
> +static_config_buf_prepare_for_upload(struct sja1105_private *priv,
> + void *config_buf, int buf_len)
> +{
> + struct sja1105_static_config *config = &priv->static_config;
> + struct sja1105_table_header final_header;
> + char *final_header_ptr;
> + int crc_len;
> +
> + /* Write Device ID and config tables to config_buf */
> + sja1105_static_config_pack(config_buf, config);
> + /* Recalculate CRC of the last header (right now 0xDEADBEEF).
> + * Don't include the CRC field itself.
> + */
> + crc_len = buf_len - 4;
> + /* Read the whole table header */
> + final_header_ptr = config_buf + buf_len - SJA1105_SIZE_TABLE_HEADER;
> + sja1105_table_header_packing(final_header_ptr, &final_header, UNPACK);
> + /* Modify */
> + final_header.crc = sja1105_crc32(config_buf, crc_len);
> + /* Rewrite */
> + sja1105_table_header_packing(final_header_ptr, &final_header, PACK);
> +
> + return 0;
> +}
> +
> +static int sja1105_static_config_upload(struct sja1105_private *priv)
> +{
> + struct sja1105_static_config *config = &priv->static_config;
> + const struct sja1105_regs *regs = priv->info->regs;
> + struct sja1105_status status;
> + u8 *config_buf;
> + int buf_len;
> + int rc;
> +
> + buf_len = sja1105_static_config_get_length(config);
> + config_buf = calloc(buf_len, sizeof(char));
> + if (!config_buf)
> + return -ENOMEM;
> +
> + rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len);
> + if (rc < 0) {
> + printf("Invalid config, cannot upload\n");
> + rc = -EINVAL;
> + goto out;
> + }
> + /* Put the SJA1105 in programming mode */
> + rc = priv->info->reset_cmd(priv);
> + if (rc < 0) {
> + printf("Failed to reset switch\n");
> + goto out;
> + }
> + /* Wait for the switch to come out of reset */
> + udelay(1000);
> + /* Upload the static config to the device */
> + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->config,
> + config_buf, buf_len);
> + if (rc < 0) {
> + printf("Failed to upload config\n");
> + goto out;
> + }
> + /* Check that SJA1105 responded well to the config upload */
> + rc = sja1105_status_get(priv, &status);
> + if (rc < 0)
> + goto out;
> +
> + if (status.ids == 1) {
> + printf("Mismatch between hardware and static config device
> id. "
> + "Wrote 0x%llx, wants 0x%llx\n",
> + config->device_id, priv->info->device_id);
> + rc = -EIO;
> + goto out;
> + }
> + if (status.crcchkl == 1 || status.crcchkg == 1) {
> + printf("Switch reported invalid CRC on static config\n");
> + rc = -EIO;
> + goto out;
> + }
> + if (status.configs == 0) {
> + printf("Switch reported that config is invalid\n");
> + rc = -EIO;
> + goto out;
> + }
> +
> +out:
> + free(config_buf);
> + return rc;
> +}
> +
> +static int sja1105_static_config_reload(struct sja1105_private *priv)
> +{
> + int rc;
> +
> + rc = sja1105_static_config_upload(priv);
> + if (rc < 0) {
> + printf("Failed to load static config: %d\n", rc);
> + return rc;
> + }
> +
> + /* Configure the CGU (PHY link modes and speeds) */
> + rc = sja1105_clocking_setup(priv);
> + if (rc < 0) {
> + printf("Failed to configure MII clocking: %d\n", rc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +static int sja1105_port_probe(struct udevice *dev, int port,
> + struct phy_device *phy)
> +{
> + struct sja1105_private *priv = dev_get_priv(dev);
> + ofnode node = dsa_port_get_ofnode(dev, port);
> + phy_interface_t phy_mode = phy->interface;
> +
> + priv->xpcs_cfg[port].inband_an = ofnode_eth_uses_inband_aneg(node);
> +
> + if (phy_mode == PHY_INTERFACE_MODE_MII ||
> + phy_mode == PHY_INTERFACE_MODE_RMII) {
> + phy->supported &= PHY_BASIC_FEATURES;
> + phy->advertising &= PHY_BASIC_FEATURES;
> + } else {
> + phy->supported &= PHY_GBIT_FEATURES;
> + phy->advertising &= PHY_GBIT_FEATURES;
> + }
> +
> + return phy_config(phy);
> +}
> +
> +static int sja1105_port_enable(struct udevice *dev, int port,
> + struct phy_device *phy)
> +{
> + struct sja1105_private *priv = dev_get_priv(dev);
> + phy_interface_t phy_mode = phy->interface;
> + struct sja1105_xmii_params_entry *mii;
> + struct sja1105_mac_config_entry *mac;
> + int rc;
> +
> + rc = phy_startup(phy);
> + if (rc)
> + return rc;
> +
> + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries;
> + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
> +
> + switch (phy_mode) {
> + case PHY_INTERFACE_MODE_MII:
> + if (!priv->info->supports_mii[port])
> + goto unsupported;
> +
> + mii->xmii_mode[port] = XMII_MODE_MII;
> + break;
> + case PHY_INTERFACE_MODE_RMII:
> + if (!priv->info->supports_rmii[port])
> + goto unsupported;
> +
> + mii->xmii_mode[port] = XMII_MODE_RMII;
> + break;
> + case PHY_INTERFACE_MODE_RGMII:
> + case PHY_INTERFACE_MODE_RGMII_ID:
> + case PHY_INTERFACE_MODE_RGMII_RXID:
> + case PHY_INTERFACE_MODE_RGMII_TXID:
> + if (!priv->info->supports_rgmii[port])
> + goto unsupported;
> +
> + mii->xmii_mode[port] = XMII_MODE_RGMII;
> + break;
> + case PHY_INTERFACE_MODE_SGMII:
> + if (!priv->info->supports_sgmii[port])
> + goto unsupported;
> +
> + mii->xmii_mode[port] = XMII_MODE_SGMII;
> + mii->special[port] = true;
> + break;
> +unsupported:
> + default:
> + dev_err(dev, "Unsupported PHY mode %d on port %d!\n",
> + phy_mode, port);
> + return -EINVAL;
> + }
> +
> + /* RevMII, RevRMII not supported */
> + mii->phy_mac[port] = XMII_MAC;
> +
> + /* Let the PHY handle the RGMII delays, if present. */
> + if (phy->phy_id == PHY_FIXED_ID) {
> + if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID ||
> + phy_mode == PHY_INTERFACE_MODE_RGMII_ID)
> + priv->rgmii_rx_delay[port] = true;
> +
> + if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID ||
> + phy_mode == PHY_INTERFACE_MODE_RGMII_ID)
> + priv->rgmii_tx_delay[port] = true;
> +
> + if ((priv->rgmii_rx_delay[port] ||
> + priv->rgmii_tx_delay[port]) &&
> + !priv->info->setup_rgmii_delay) {
> + printf("Chip does not support internal RGMII
> delays\n");
> + return -EINVAL;
> + }
> + }
> +
> + if (mii->xmii_mode[port] == XMII_MODE_SGMII) {
> + mac[port].speed =
> priv->info->port_speed[SJA1105_SPEED_1000MBPS];
> + priv->xpcs_cfg[port].speed = phy->speed;
> + } else if (phy->speed == SPEED_1000) {
> + mac[port].speed =
> priv->info->port_speed[SJA1105_SPEED_1000MBPS];
> + } else if (phy->speed == SPEED_100) {
> + mac[port].speed =
> priv->info->port_speed[SJA1105_SPEED_100MBPS];
> + } else if (phy->speed == SPEED_10) {
> + mac[port].speed =
> priv->info->port_speed[SJA1105_SPEED_10MBPS];
> + } else {
> + printf("Invalid PHY speed %d on port %d\n", phy->speed, port);
> + return -EINVAL;
> + }
> +
> + return sja1105_static_config_reload(priv);
> +}
> +
> +static void sja1105_port_disable(struct udevice *dev, int port,
> + struct phy_device *phy)
> +{
> + phy_shutdown(phy);
> +}
> +
> +static int sja1105_xmit(struct udevice *dev, int port, void *packet, int
> length)
> +{
> + struct sja1105_private *priv = dev_get_priv(dev);
> + u8 *from = (u8 *)packet + VLAN_HLEN;
> + struct vlan_ethhdr *hdr = packet;
> + u8 *dest = (u8 *)packet;
> +
> + memmove(dest, from, 2 * ETH_ALEN);
> + hdr->h_vlan_proto = htons(ETH_P_SJA1105);
> + hdr->h_vlan_TCI = htons(priv->pvid[port]);
> +
> + return 0;
> +}
> +
> +static int sja1105_rcv(struct udevice *dev, int *port, void *packet, int
> length)
> +{
> + struct vlan_ethhdr *hdr = packet;
> + u8 *dest = packet + VLAN_HLEN;
> + u8 *from = packet;
> +
> + if (ntohs(hdr->h_vlan_proto) != ETH_P_SJA1105)
> + return -EINVAL;
> +
> + *port = ntohs(hdr->h_vlan_TCI) & DSA_8021Q_PORT_MASK;
> + memmove(dest, from, 2 * ETH_ALEN);
> +
> + return 0;
> +}
> +
> +static const struct dsa_ops sja1105_dsa_ops = {
> + .port_probe = sja1105_port_probe,
> + .port_enable = sja1105_port_enable,
> + .port_disable = sja1105_port_disable,
> + .xmit = sja1105_xmit,
> + .rcv = sja1105_rcv,
> +};
> +
> +static int sja1105_init(struct sja1105_private *priv)
> +{
> + int rc;
> +
> + rc = sja1105_static_config_init(priv);
> + if (rc) {
> + printf("Failed to initialize static config: %d\n", rc);
> + return rc;
> + }
> +
> + rc = sja1105_mdiobus_register(priv);
> + if (rc) {
> + printf("Failed to register MDIO bus: %d\n", rc);
> + goto err_mdiobus_register;
> + }
> +
> + return 0;
> +
> +err_mdiobus_register:
> + sja1105_static_config_free(&priv->static_config);
> +
> + return rc;
> +}
> +
> +static int sja1105_check_device_id(struct sja1105_private *priv)
> +{
> + const struct sja1105_regs *regs = priv->info->regs;
> + u8 packed_buf[SJA1105_SIZE_DEVICE_ID] = {0};
> + enum sja1105_switch_id id;
> + u64 device_id;
> + u64 part_no;
> + int rc;
> +
> + rc = sja1105_xfer_buf(priv, SPI_READ, regs->device_id, packed_buf,
> + SJA1105_SIZE_DEVICE_ID);
> + if (rc < 0)
> + return rc;
> +
> + sja1105_packing(packed_buf, &device_id, 31, 0, SJA1105_SIZE_DEVICE_ID,
> + UNPACK);
> +
> + if (device_id != priv->info->device_id) {
> + printf("Expected device ID 0x%llx but read 0x%llx\n",
> + priv->info->device_id, device_id);
> + return -ENODEV;
> + }
> +
> + rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, packed_buf,
> + SJA1105_SIZE_DEVICE_ID);
> + if (rc < 0)
> + return rc;
> +
> + sja1105_packing(packed_buf, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID,
> + UNPACK);
> +
> + for (id = 0; id < SJA1105_MAX_SWITCH_ID; id++) {
> + const struct sja1105_info *info = &sja1105_info[id];
> +
> + /* Is what's been probed in our match table at all? */
> + if (info->device_id != device_id || info->part_no != part_no)
> + continue;
> +
> + /* But is it what's in the device tree? */
> + if (priv->info->device_id != device_id ||
> + priv->info->part_no != part_no) {
> + printf("Device tree specifies chip %s but found %s,
> please fix it!\n",
> + priv->info->name, info->name);
> + /* It isn't. No problem, pick that up. */
> + priv->info = info;
> + }
> +
> + return 0;
> + }
> +
> + printf("Unexpected {device ID, part number}: 0x%llx 0x%llx\n",
> + device_id, part_no);
> +
> + return -ENODEV;
> +}
> +
> +static int sja1105_probe(struct udevice *dev)
> +{
> + enum sja1105_switch_id id = dev_get_driver_data(dev);
> + struct sja1105_private *priv = dev_get_priv(dev);
> + int rc;
> +
> + if (ofnode_valid(dev_ofnode(dev)) &&
> + !ofnode_is_available(dev_ofnode(dev))) {
> + dev_dbg(dev, "switch disabled\n");
> + return -ENODEV;
> + }
> +
> + priv->info = &sja1105_info[id];
> + priv->dev = dev;
> +
> + rc = sja1105_check_device_id(priv);
> + if (rc < 0) {
> + dev_err(dev, "Device ID check failed: %d\n", rc);
> + return rc;
> + }
> +
> + dsa_set_tagging(dev, VLAN_HLEN, 0);
> +
> + return sja1105_init(priv);
> +}
> +
> +static int sja1105_remove(struct udevice *dev)
> +{
> + struct sja1105_private *priv = dev_get_priv(dev);
> +
> + sja1105_mdiobus_unregister(priv);
> + sja1105_static_config_free(&priv->static_config);
> +
> + return 0;
> +}
> +
> +static const struct udevice_id sja1105_ids[] = {
> + { .compatible = "nxp,sja1105e", .data = SJA1105E },
> + { .compatible = "nxp,sja1105t", .data = SJA1105T },
> + { .compatible = "nxp,sja1105p", .data = SJA1105P },
> + { .compatible = "nxp,sja1105q", .data = SJA1105Q },
> + { .compatible = "nxp,sja1105r", .data = SJA1105R },
> + { .compatible = "nxp,sja1105s", .data = SJA1105S },
> + { .compatible = "nxp,sja1110a", .data = SJA1110A },
> + { .compatible = "nxp,sja1110b", .data = SJA1110B },
> + { .compatible = "nxp,sja1110c", .data = SJA1110C },
> + { .compatible = "nxp,sja1110d", .data = SJA1110D },
> + { }
> +};
> +
> +U_BOOT_DRIVER(sja1105) = {
> + .name = "sja1105",
> + .id = UCLASS_DSA,
> + .of_match = sja1105_ids,
> + .probe = sja1105_probe,
> + .remove = sja1105_remove,
> + .ops = &sja1105_dsa_ops,
> + .priv_auto = sizeof(struct sja1105_private),
> +};
> --
> 2.25.1
>
Reviewed-by: Ramon Fried <rfried....@gmail.com>