From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A9C11C433EF for ; Tue, 28 Sep 2021 17:13:19 +0000 (UTC) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id DC765611BD for ; Tue, 28 Sep 2021 17:13:18 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org DC765611BD Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=lists.denx.de Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 1866B82DA7; Tue, 28 Sep 2021 19:13:17 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pjgwwNby"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id EF22382DA7; Tue, 28 Sep 2021 15:37:27 +0200 (CEST) Received: from mail-oo1-xc34.google.com (mail-oo1-xc34.google.com [IPv6:2607:f8b0:4864:20::c34]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id A5D4C831D3 for ; Tue, 28 Sep 2021 15:35:58 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=rfried.dev@gmail.com Received: by mail-oo1-xc34.google.com with SMTP id k11-20020a4abd8b000000b002b5c622a4ddso1834148oop.1 for ; Tue, 28 Sep 2021 06:35:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=bPdMCj9HxOrbjwJkhclR0cbGyCEwsc4RlhGlry2yjPc=; b=pjgwwNbykFjC0tkyDgFe1x+WAJ91LdiPPmW18vuVeR4fJSRkIDvnZgtjPW/wRrteol y+uqEAD8y6dLutV2ZYsubhUboCkR3KgUFTLZykW2ye9An80UCaJ5ucEjXwD4YVtXIXU9 2vlQEYcebeIbfbWz6vRfmX1WWzNe7l6FQO3B0AydiKW0dpf+/XySJ8Pk+gAAyPgjSh0N VMxd9gsLSt8fasjqydfFRyeWag8ZgVMdnG7rGw049OsMVjqxXja6QK/tnfAOaBdueMRz ETp5IbGtE3aDUQH8FfQ4H04Ho27ohUUkENJ4oC9eFzommwRnpM4XcNPqDXwgnQP2XgxP rS3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=bPdMCj9HxOrbjwJkhclR0cbGyCEwsc4RlhGlry2yjPc=; b=3XVCmUP/TgVXIWSI2hcLk27r1zox0x2SJIzTHt8aXaGVY744n113p3U1Wye4NTRkwj kf4kpoZJuBKxnG3p/sLuZL+mO46DMkNegrjGL819r3EsNubIEhMQLnkm9qnFfU+/OSre iKG6E3LXznk24VwMDDbj+9tB4UXfoAjQv4YkWFnveeY1Q5a87YgT3/gJ7crx2+d4xDJs YvICqwp1HQV/7mpaJLY3AIpYz0kMCWQ6CRIiqjrz+7GnamO9tMDDzNtt9ayIneLk2DHc orpJZ7HkOBV9mz0optJ731+7x2lhRJ2cMopQfTaBYjjVVsIKQ8bWwnPQeILL/87PMGW8 qPkQ== X-Gm-Message-State: AOAM533l1ROjjW+QnW5XbIgTM9ZbsSkdDRRcFA2f+y/ujZh2aa++ARQ6 gxlvUfo50j5XEg/R7B8shcrGGJcKKCKxWKKp614= X-Google-Smtp-Source: ABdhPJyQNrPRCwdRe1/wlQSVVZINYDq+74sLSMviCYOYIPdrz61Yyas1uGJtKNVRG+bg2KWmFj8fL3JPz5WuH1DwLfw= X-Received: by 2002:a05:6820:1018:: with SMTP id v24mr5065077oor.27.1632836156323; Tue, 28 Sep 2021 06:35:56 -0700 (PDT) MIME-Version: 1.0 References: <20210927234825.823582-1-vladimir.oltean@nxp.com> <20210927234825.823582-7-vladimir.oltean@nxp.com> In-Reply-To: <20210927234825.823582-7-vladimir.oltean@nxp.com> From: Ramon Fried Date: Tue, 28 Sep 2021 16:35:44 +0300 Message-ID: Subject: Re: [PATCH 6/9] net: add driver for NXP SJA1105 DSA L2 switch To: Vladimir Oltean Cc: U-Boot Mailing List , Joe Hershberger , Priyanka Jain Content-Type: text/plain; charset="UTF-8" X-Mailman-Approved-At: Tue, 28 Sep 2021 19:13:15 +0200 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean On Tue, Sep 28, 2021 at 2:48 AM Vladimir Oltean 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 > --- > 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 > + * Copyright 2020-2021 NXP > + * > + * Ported from Linux (drivers/net/dsa/sja1105/). > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +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