mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	This function is also available as ofnode_is_enabled(), so use that instead. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			3371 lines
		
	
	
		
			97 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3371 lines
		
	
	
		
			97 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright 2016-2018 NXP
 | |
|  * Copyright 2018, Sensor-Technik Wiedemann GmbH
 | |
|  * Copyright 2018-2019, Vladimir Oltean <olteanv@gmail.com>
 | |
|  * Copyright 2020-2021 NXP
 | |
|  *
 | |
|  * Ported from Linux (drivers/net/dsa/sja1105/).
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm/device_compat.h>
 | |
| #include <linux/bitops.h>
 | |
| #include <linux/bitrev.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/if_ether.h>
 | |
| #include <linux/if_vlan.h>
 | |
| #include <linux/types.h>
 | |
| #include <net/dsa.h>
 | |
| #include <stdlib.h>
 | |
| #include <spi.h>
 | |
| #include <miiphy.h>
 | |
| #include <dm/of_extra.h>
 | |
| 
 | |
| enum packing_op {
 | |
| 	PACK,
 | |
| 	UNPACK,
 | |
| };
 | |
| 
 | |
| #define ETHER_CRC32_POLY				0x04C11DB7
 | |
| #define ETH_P_SJA1105					0xdadb
 | |
| #define SJA1105_NUM_PORTS				5
 | |
| #define SJA1110_NUM_PORTS				11
 | |
| #define SJA1105_MAX_NUM_PORTS				SJA1110_NUM_PORTS
 | |
| #define SJA1105_NUM_TC					8
 | |
| #define SJA1105ET_FDB_BIN_SIZE				4
 | |
| #define SJA1105_SIZE_CGU_CMD				4
 | |
| #define SJA1105_SIZE_RESET_CMD				4
 | |
| #define SJA1105_SIZE_MDIO_CMD				4
 | |
| #define SJA1105_SIZE_SPI_MSG_HEADER			4
 | |
| #define SJA1105_SIZE_SPI_MSG_MAXLEN			(64 * 4)
 | |
| #define SJA1105_SIZE_DEVICE_ID				4
 | |
| #define SJA1105_SIZE_TABLE_HEADER			12
 | |
| #define SJA1105_SIZE_L2_POLICING_ENTRY			8
 | |
| #define SJA1105_SIZE_VLAN_LOOKUP_ENTRY			8
 | |
| #define SJA1110_SIZE_VLAN_LOOKUP_ENTRY			12
 | |
| #define SJA1105_SIZE_L2_FORWARDING_ENTRY		8
 | |
| #define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY		12
 | |
| #define SJA1105_SIZE_XMII_PARAMS_ENTRY			4
 | |
| #define SJA1110_SIZE_XMII_PARAMS_ENTRY			8
 | |
| #define SJA1105ET_SIZE_MAC_CONFIG_ENTRY			28
 | |
| #define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY		40
 | |
| #define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY		32
 | |
| #define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY		44
 | |
| #define SJA1110_SIZE_GENERAL_PARAMS_ENTRY		56
 | |
| 
 | |
| #define SJA1105_MAX_L2_LOOKUP_COUNT			1024
 | |
| #define SJA1105_MAX_L2_POLICING_COUNT			45
 | |
| #define SJA1110_MAX_L2_POLICING_COUNT			110
 | |
| #define SJA1105_MAX_VLAN_LOOKUP_COUNT			4096
 | |
| #define SJA1105_MAX_L2_FORWARDING_COUNT			13
 | |
| #define SJA1110_MAX_L2_FORWARDING_COUNT			19
 | |
| #define SJA1105_MAX_MAC_CONFIG_COUNT			5
 | |
| #define SJA1110_MAX_MAC_CONFIG_COUNT			11
 | |
| #define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT		1
 | |
| #define SJA1105_MAX_GENERAL_PARAMS_COUNT		1
 | |
| #define SJA1105_MAX_XMII_PARAMS_COUNT			1
 | |
| 
 | |
| #define SJA1105_MAX_FRAME_MEMORY			929
 | |
| 
 | |
| #define SJA1105E_DEVICE_ID				0x9C00000Cull
 | |
| #define SJA1105T_DEVICE_ID				0x9E00030Eull
 | |
| #define SJA1105PR_DEVICE_ID				0xAF00030Eull
 | |
| #define SJA1105QS_DEVICE_ID				0xAE00030Eull
 | |
| #define SJA1110_DEVICE_ID				0xB700030Full
 | |
| 
 | |
| #define SJA1105ET_PART_NO				0x9A83
 | |
| #define SJA1105P_PART_NO				0x9A84
 | |
| #define SJA1105Q_PART_NO				0x9A85
 | |
| #define SJA1105R_PART_NO				0x9A86
 | |
| #define SJA1105S_PART_NO				0x9A87
 | |
| #define SJA1110A_PART_NO				0x1110
 | |
| #define SJA1110B_PART_NO				0x1111
 | |
| #define SJA1110C_PART_NO				0x1112
 | |
| #define SJA1110D_PART_NO				0x1113
 | |
| 
 | |
| #define SJA1110_ACU			0x1c4400
 | |
| #define SJA1110_RGU			0x1c6000
 | |
| #define SJA1110_CGU			0x1c6400
 | |
| 
 | |
| #define SJA1110_SPI_ADDR(x)		((x) / 4)
 | |
| #define SJA1110_ACU_ADDR(x)		(SJA1110_ACU + SJA1110_SPI_ADDR(x))
 | |
| #define SJA1110_CGU_ADDR(x)		(SJA1110_CGU + SJA1110_SPI_ADDR(x))
 | |
| #define SJA1110_RGU_ADDR(x)		(SJA1110_RGU + SJA1110_SPI_ADDR(x))
 | |
| 
 | |
| #define SJA1105_RSV_ADDR		0xffffffffffffffffull
 | |
| 
 | |
| #define SJA1110_PCS_BANK_REG		SJA1110_SPI_ADDR(0x3fc)
 | |
| 
 | |
| #define DSA_8021Q_DIR_TX		BIT(11)
 | |
| #define DSA_8021Q_PORT_SHIFT		0
 | |
| #define DSA_8021Q_PORT_MASK		GENMASK(3, 0)
 | |
| #define DSA_8021Q_PORT(x)		(((x) << DSA_8021Q_PORT_SHIFT) & \
 | |
| 						 DSA_8021Q_PORT_MASK)
 | |
| 
 | |
| #define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000)
 | |
| 
 | |
| /* XPCS registers */
 | |
| 
 | |
| /* VR MII MMD registers offsets */
 | |
| #define DW_VR_MII_DIG_CTRL1		0x8000
 | |
| #define DW_VR_MII_AN_CTRL		0x8001
 | |
| #define DW_VR_MII_DIG_CTRL2		0x80e1
 | |
| 
 | |
| /* VR_MII_DIG_CTRL1 */
 | |
| #define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW		BIT(9)
 | |
| 
 | |
| /* VR_MII_DIG_CTRL2 */
 | |
| #define DW_VR_MII_DIG_CTRL2_TX_POL_INV		BIT(4)
 | |
| 
 | |
| /* VR_MII_AN_CTRL */
 | |
| #define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT	3
 | |
| #define DW_VR_MII_TX_CONFIG_MASK		BIT(3)
 | |
| #define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII	0x0
 | |
| #define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT	1
 | |
| #define DW_VR_MII_PCS_MODE_MASK			GENMASK(2, 1)
 | |
| #define DW_VR_MII_PCS_MODE_C37_SGMII		0x2
 | |
| 
 | |
| /* PMA registers */
 | |
| 
 | |
| /* LANE_DRIVER1_0 register */
 | |
| #define SJA1110_LANE_DRIVER1_0		0x8038
 | |
| #define SJA1110_TXDRV(x)		(((x) << 12) & GENMASK(14, 12))
 | |
| 
 | |
| /* LANE_DRIVER2_0 register */
 | |
| #define SJA1110_LANE_DRIVER2_0		0x803a
 | |
| #define SJA1110_TXDRVTRIM_LSB(x)	((x) & GENMASK_ULL(15, 0))
 | |
| 
 | |
| /* LANE_DRIVER2_1 register */
 | |
| #define SJA1110_LANE_DRIVER2_1		0x803b
 | |
| #define SJA1110_LANE_DRIVER2_1_RSV	BIT(9)
 | |
| #define SJA1110_TXDRVTRIM_MSB(x)	(((x) & GENMASK_ULL(23, 16)) >> 16)
 | |
| 
 | |
| /* LANE_TRIM register */
 | |
| #define SJA1110_LANE_TRIM		0x8040
 | |
| #define SJA1110_TXTEN			BIT(11)
 | |
| #define SJA1110_TXRTRIM(x)		(((x) << 8) & GENMASK(10, 8))
 | |
| #define SJA1110_TXPLL_BWSEL		BIT(7)
 | |
| #define SJA1110_RXTEN			BIT(6)
 | |
| #define SJA1110_RXRTRIM(x)		(((x) << 3) & GENMASK(5, 3))
 | |
| #define SJA1110_CDR_GAIN		BIT(2)
 | |
| #define SJA1110_ACCOUPLE_RXVCM_EN	BIT(0)
 | |
| 
 | |
| /* LANE_DATAPATH_1 register */
 | |
| #define SJA1110_LANE_DATAPATH_1		0x8037
 | |
| 
 | |
| /* POWERDOWN_ENABLE register */
 | |
| #define SJA1110_POWERDOWN_ENABLE	0x8041
 | |
| #define SJA1110_TXPLL_PD		BIT(12)
 | |
| #define SJA1110_TXPD			BIT(11)
 | |
| #define SJA1110_RXPKDETEN		BIT(10)
 | |
| #define SJA1110_RXCH_PD			BIT(9)
 | |
| #define SJA1110_RXBIAS_PD		BIT(8)
 | |
| #define SJA1110_RESET_SER_EN		BIT(7)
 | |
| #define SJA1110_RESET_SER		BIT(6)
 | |
| #define SJA1110_RESET_DES		BIT(5)
 | |
| #define SJA1110_RCVEN			BIT(4)
 | |
| 
 | |
| /* RXPLL_CTRL0 register */
 | |
| #define SJA1110_RXPLL_CTRL0		0x8065
 | |
| #define SJA1110_RXPLL_FBDIV(x)		(((x) << 2) & GENMASK(9, 2))
 | |
| 
 | |
| /* RXPLL_CTRL1 register */
 | |
| #define SJA1110_RXPLL_CTRL1		0x8066
 | |
| #define SJA1110_RXPLL_REFDIV(x)		((x) & GENMASK(4, 0))
 | |
| 
 | |
| /* TXPLL_CTRL0 register */
 | |
| #define SJA1110_TXPLL_CTRL0		0x806d
 | |
| #define SJA1110_TXPLL_FBDIV(x)		((x) & GENMASK(11, 0))
 | |
| 
 | |
| /* TXPLL_CTRL1 register */
 | |
| #define SJA1110_TXPLL_CTRL1		0x806e
 | |
| #define SJA1110_TXPLL_REFDIV(x)		((x) & GENMASK(5, 0))
 | |
| 
 | |
| /* RX_DATA_DETECT register */
 | |
| #define SJA1110_RX_DATA_DETECT		0x8045
 | |
| 
 | |
| /* RX_CDR_CTLE register */
 | |
| #define SJA1110_RX_CDR_CTLE		0x8042
 | |
| 
 | |
| /* UM10944.pdf Page 11, Table 2. Configuration Blocks */
 | |
| enum {
 | |
| 	BLKID_L2_POLICING				= 0x06,
 | |
| 	BLKID_VLAN_LOOKUP				= 0x07,
 | |
| 	BLKID_L2_FORWARDING				= 0x08,
 | |
| 	BLKID_MAC_CONFIG				= 0x09,
 | |
| 	BLKID_L2_FORWARDING_PARAMS			= 0x0E,
 | |
| 	BLKID_GENERAL_PARAMS				= 0x11,
 | |
| 	BLKID_XMII_PARAMS				= 0x4E,
 | |
| };
 | |
| 
 | |
| enum sja1105_blk_idx {
 | |
| 	BLK_IDX_L2_POLICING = 0,
 | |
| 	BLK_IDX_VLAN_LOOKUP,
 | |
| 	BLK_IDX_L2_FORWARDING,
 | |
| 	BLK_IDX_MAC_CONFIG,
 | |
| 	BLK_IDX_L2_FORWARDING_PARAMS,
 | |
| 	BLK_IDX_GENERAL_PARAMS,
 | |
| 	BLK_IDX_XMII_PARAMS,
 | |
| 	BLK_IDX_MAX,
 | |
| };
 | |
| 
 | |
| struct sja1105_general_params_entry {
 | |
| 	u64 mac_fltres1;
 | |
| 	u64 mac_fltres0;
 | |
| 	u64 mac_flt1;
 | |
| 	u64 mac_flt0;
 | |
| 	u64 casc_port;
 | |
| 	u64 host_port;
 | |
| 	u64 mirr_port;
 | |
| 	u64 tpid;
 | |
| 	u64 tpid2;
 | |
| };
 | |
| 
 | |
| struct sja1105_vlan_lookup_entry {
 | |
| 	u64 vmemb_port;
 | |
| 	u64 vlan_bc;
 | |
| 	u64 tag_port;
 | |
| 	u64 vlanid;
 | |
| 	u64 type_entry; /* SJA1110 only */
 | |
| };
 | |
| 
 | |
| struct sja1105_l2_forwarding_entry {
 | |
| 	u64 bc_domain;
 | |
| 	u64 reach_port;
 | |
| 	u64 fl_domain;
 | |
| };
 | |
| 
 | |
| struct sja1105_l2_forwarding_params_entry {
 | |
| 	u64 part_spc[SJA1105_NUM_TC];
 | |
| };
 | |
| 
 | |
| struct sja1105_l2_policing_entry {
 | |
| 	u64 sharindx;
 | |
| 	u64 smax;
 | |
| 	u64 rate;
 | |
| 	u64 maxlen;
 | |
| 	u64 partition;
 | |
| };
 | |
| 
 | |
| struct sja1105_mac_config_entry {
 | |
| 	u64 top[SJA1105_NUM_TC];
 | |
| 	u64 base[SJA1105_NUM_TC];
 | |
| 	u64 enabled[SJA1105_NUM_TC];
 | |
| 	u64 speed;
 | |
| 	u64 vlanid;
 | |
| 	u64 egress;
 | |
| 	u64 ingress;
 | |
| };
 | |
| 
 | |
| struct sja1105_xmii_params_entry {
 | |
| 	u64 phy_mac[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 xmii_mode[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 special[SJA1105_MAX_NUM_PORTS];
 | |
| };
 | |
| 
 | |
| struct sja1105_table_header {
 | |
| 	u64 block_id;
 | |
| 	u64 len;
 | |
| 	u64 crc;
 | |
| };
 | |
| 
 | |
| struct sja1105_table_ops {
 | |
| 	size_t (*packing)(void *buf, void *entry_ptr, enum packing_op op);
 | |
| 	size_t unpacked_entry_size;
 | |
| 	size_t packed_entry_size;
 | |
| 	size_t max_entry_count;
 | |
| };
 | |
| 
 | |
| struct sja1105_table {
 | |
| 	const struct sja1105_table_ops *ops;
 | |
| 	size_t entry_count;
 | |
| 	void *entries;
 | |
| };
 | |
| 
 | |
| struct sja1105_static_config {
 | |
| 	u64 device_id;
 | |
| 	struct sja1105_table tables[BLK_IDX_MAX];
 | |
| };
 | |
| 
 | |
| struct sja1105_xpcs_cfg {
 | |
| 	bool inband_an;
 | |
| 	int speed;
 | |
| };
 | |
| 
 | |
| struct sja1105_private {
 | |
| 	struct sja1105_static_config static_config;
 | |
| 	bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS];
 | |
| 	bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS];
 | |
| 	u16 pvid[SJA1105_MAX_NUM_PORTS];
 | |
| 	struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS];
 | |
| 	struct mii_dev *mdio_pcs;
 | |
| 	const struct sja1105_info *info;
 | |
| 	struct udevice *dev;
 | |
| };
 | |
| 
 | |
| typedef enum {
 | |
| 	SPI_READ = 0,
 | |
| 	SPI_WRITE = 1,
 | |
| } sja1105_spi_rw_mode_t;
 | |
| 
 | |
| typedef enum {
 | |
| 	XMII_MAC = 0,
 | |
| 	XMII_PHY = 1,
 | |
| } sja1105_mii_role_t;
 | |
| 
 | |
| typedef enum {
 | |
| 	XMII_MODE_MII		= 0,
 | |
| 	XMII_MODE_RMII		= 1,
 | |
| 	XMII_MODE_RGMII		= 2,
 | |
| 	XMII_MODE_SGMII		= 3,
 | |
| } sja1105_phy_interface_t;
 | |
| 
 | |
| enum {
 | |
| 	SJA1105_SPEED_AUTO,
 | |
| 	SJA1105_SPEED_10MBPS,
 | |
| 	SJA1105_SPEED_100MBPS,
 | |
| 	SJA1105_SPEED_1000MBPS,
 | |
| 	SJA1105_SPEED_MAX,
 | |
| };
 | |
| 
 | |
| enum sja1110_vlan_type {
 | |
| 	SJA1110_VLAN_INVALID = 0,
 | |
| 	SJA1110_VLAN_C_TAG = 1, /* Single inner VLAN tag */
 | |
| 	SJA1110_VLAN_S_TAG = 2, /* Single outer VLAN tag */
 | |
| 	SJA1110_VLAN_D_TAG = 3, /* Double tagged, use outer tag for lookup */
 | |
| };
 | |
| 
 | |
| /* Keeps the different addresses between E/T and P/Q/R/S */
 | |
| struct sja1105_regs {
 | |
| 	u64 device_id;
 | |
| 	u64 prod_id;
 | |
| 	u64 status;
 | |
| 	u64 port_control;
 | |
| 	u64 rgu;
 | |
| 	u64 config;
 | |
| 	u64 rmii_pll1;
 | |
| 	u64 pad_mii_tx[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 pad_mii_rx[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 pad_mii_id[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 cgu_idiv[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 mii_tx_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 mii_rx_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 mii_ext_tx_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 mii_ext_rx_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 rgmii_tx_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS];
 | |
| 	u64 pcs_base[SJA1105_MAX_NUM_PORTS];
 | |
| };
 | |
| 
 | |
| struct sja1105_info {
 | |
| 	u64 device_id;
 | |
| 	u64 part_no;
 | |
| 	const struct sja1105_table_ops *static_ops;
 | |
| 	const struct sja1105_regs *regs;
 | |
| 	int (*reset_cmd)(struct sja1105_private *priv);
 | |
| 	int (*setup_rgmii_delay)(struct sja1105_private *priv, int port);
 | |
| 	int (*pcs_mdio_read)(struct mii_dev *bus, int phy, int mmd, int reg);
 | |
| 	int (*pcs_mdio_write)(struct mii_dev *bus, int phy, int mmd, int reg,
 | |
| 			      u16 val);
 | |
| 	int (*pma_config)(struct sja1105_private *priv, int port);
 | |
| 	const char *name;
 | |
| 	bool supports_mii[SJA1105_MAX_NUM_PORTS];
 | |
| 	bool supports_rmii[SJA1105_MAX_NUM_PORTS];
 | |
| 	bool supports_rgmii[SJA1105_MAX_NUM_PORTS];
 | |
| 	bool supports_sgmii[SJA1105_MAX_NUM_PORTS];
 | |
| 	const u64 port_speed[SJA1105_SPEED_MAX];
 | |
| };
 | |
| 
 | |
| struct sja1105_chunk {
 | |
| 	u8	*buf;
 | |
| 	size_t	len;
 | |
| 	u64	reg_addr;
 | |
| };
 | |
| 
 | |
| struct sja1105_spi_message {
 | |
| 	u64 access;
 | |
| 	u64 read_count;
 | |
| 	u64 address;
 | |
| };
 | |
| 
 | |
| /* Common structure for CFG_PAD_MIIx_RX and CFG_PAD_MIIx_TX */
 | |
| struct sja1105_cfg_pad_mii {
 | |
| 	u64 d32_os;
 | |
| 	u64 d32_ih;
 | |
| 	u64 d32_ipud;
 | |
| 	u64 d10_ih;
 | |
| 	u64 d10_os;
 | |
| 	u64 d10_ipud;
 | |
| 	u64 ctrl_os;
 | |
| 	u64 ctrl_ih;
 | |
| 	u64 ctrl_ipud;
 | |
| 	u64 clk_os;
 | |
| 	u64 clk_ih;
 | |
| 	u64 clk_ipud;
 | |
| };
 | |
| 
 | |
| struct sja1105_cfg_pad_mii_id {
 | |
| 	u64 rxc_stable_ovr;
 | |
| 	u64 rxc_delay;
 | |
| 	u64 rxc_bypass;
 | |
| 	u64 rxc_pd;
 | |
| 	u64 txc_stable_ovr;
 | |
| 	u64 txc_delay;
 | |
| 	u64 txc_bypass;
 | |
| 	u64 txc_pd;
 | |
| };
 | |
| 
 | |
| struct sja1105_cgu_idiv {
 | |
| 	u64 clksrc;
 | |
| 	u64 autoblock;
 | |
| 	u64 idiv;
 | |
| 	u64 pd;
 | |
| };
 | |
| 
 | |
| struct sja1105_cgu_pll_ctrl {
 | |
| 	u64 pllclksrc;
 | |
| 	u64 msel;
 | |
| 	u64 autoblock;
 | |
| 	u64 psel;
 | |
| 	u64 direct;
 | |
| 	u64 fbsel;
 | |
| 	u64 bypass;
 | |
| 	u64 pd;
 | |
| };
 | |
| 
 | |
| enum {
 | |
| 	CLKSRC_MII0_TX_CLK	= 0x00,
 | |
| 	CLKSRC_MII0_RX_CLK	= 0x01,
 | |
| 	CLKSRC_MII1_TX_CLK	= 0x02,
 | |
| 	CLKSRC_MII1_RX_CLK	= 0x03,
 | |
| 	CLKSRC_MII2_TX_CLK	= 0x04,
 | |
| 	CLKSRC_MII2_RX_CLK	= 0x05,
 | |
| 	CLKSRC_MII3_TX_CLK	= 0x06,
 | |
| 	CLKSRC_MII3_RX_CLK	= 0x07,
 | |
| 	CLKSRC_MII4_TX_CLK	= 0x08,
 | |
| 	CLKSRC_MII4_RX_CLK	= 0x09,
 | |
| 	CLKSRC_PLL0		= 0x0B,
 | |
| 	CLKSRC_PLL1		= 0x0E,
 | |
| 	CLKSRC_IDIV0		= 0x11,
 | |
| 	CLKSRC_IDIV1		= 0x12,
 | |
| 	CLKSRC_IDIV2		= 0x13,
 | |
| 	CLKSRC_IDIV3		= 0x14,
 | |
| 	CLKSRC_IDIV4		= 0x15,
 | |
| };
 | |
| 
 | |
| struct sja1105_cgu_mii_ctrl {
 | |
| 	u64 clksrc;
 | |
| 	u64 autoblock;
 | |
| 	u64 pd;
 | |
| };
 | |
| 
 | |
| static int get_reverse_lsw32_offset(int offset, size_t len)
 | |
| {
 | |
| 	int closest_multiple_of_4;
 | |
| 	int word_index;
 | |
| 
 | |
| 	word_index = offset / 4;
 | |
| 	closest_multiple_of_4 = word_index * 4;
 | |
| 	offset -= closest_multiple_of_4;
 | |
| 	word_index = (len / 4) - word_index - 1;
 | |
| 	return word_index * 4 + offset;
 | |
| }
 | |
| 
 | |
| /* Simplified version of the "packing" function from Linux, adapted
 | |
|  * to support only sja1105's quirk: QUIRK_LSW32_IS_FIRST
 | |
|  */
 | |
| static void sja1105_packing(void *pbuf, u64 *uval, int startbit, int endbit,
 | |
| 			    size_t pbuflen, enum packing_op op)
 | |
| {
 | |
| 	int plogical_first_u8, plogical_last_u8, box;
 | |
| 
 | |
| 	if (op == UNPACK)
 | |
| 		*uval = 0;
 | |
| 
 | |
| 	plogical_first_u8 = startbit / 8;
 | |
| 	plogical_last_u8  = endbit / 8;
 | |
| 
 | |
| 	for (box = plogical_first_u8; box >= plogical_last_u8; box--) {
 | |
| 		int box_start_bit, box_end_bit, box_addr;
 | |
| 		int proj_start_bit, proj_end_bit;
 | |
| 		u64 proj_mask;
 | |
| 		u8  box_mask;
 | |
| 
 | |
| 		if (box == plogical_first_u8)
 | |
| 			box_start_bit = startbit % 8;
 | |
| 		else
 | |
| 			box_start_bit = 7;
 | |
| 		if (box == plogical_last_u8)
 | |
| 			box_end_bit = endbit % 8;
 | |
| 		else
 | |
| 			box_end_bit = 0;
 | |
| 
 | |
| 		proj_start_bit = ((box * 8) + box_start_bit) - endbit;
 | |
| 		proj_end_bit   = ((box * 8) + box_end_bit) - endbit;
 | |
| 		proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit);
 | |
| 		box_mask  = GENMASK_ULL(box_start_bit, box_end_bit);
 | |
| 
 | |
| 		box_addr = pbuflen - box - 1;
 | |
| 		box_addr = get_reverse_lsw32_offset(box_addr, pbuflen);
 | |
| 
 | |
| 		if (op == UNPACK) {
 | |
| 			u64 pval;
 | |
| 
 | |
| 			/* Read from pbuf, write to uval */
 | |
| 			pval = ((u8 *)pbuf)[box_addr] & box_mask;
 | |
| 
 | |
| 			pval >>= box_end_bit;
 | |
| 			pval <<= proj_end_bit;
 | |
| 			*uval &= ~proj_mask;
 | |
| 			*uval |= pval;
 | |
| 		} else {
 | |
| 			u64 pval;
 | |
| 
 | |
| 			/* Write to pbuf, read from uval */
 | |
| 			pval = (*uval) & proj_mask;
 | |
| 			pval >>= proj_end_bit;
 | |
| 
 | |
| 			pval <<= box_end_bit;
 | |
| 			((u8 *)pbuf)[box_addr] &= ~box_mask;
 | |
| 			((u8 *)pbuf)[box_addr] |= pval;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static u32 crc32_add(u32 crc, u8 byte)
 | |
| {
 | |
| 	u32 byte32 = bitrev32(byte);
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < 8; i++) {
 | |
| 		if ((crc ^ byte32) & BIT(31)) {
 | |
| 			crc <<= 1;
 | |
| 			crc ^= ETHER_CRC32_POLY;
 | |
| 		} else {
 | |
| 			crc <<= 1;
 | |
| 		}
 | |
| 		byte32 <<= 1;
 | |
| 	}
 | |
| 	return crc;
 | |
| }
 | |
| 
 | |
| /* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
 | |
| static uint32_t sja1105_crc32(void *buf, size_t len)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	u64 chunk;
 | |
| 	u32 crc;
 | |
| 
 | |
| 	/* seed */
 | |
| 	crc = 0xFFFFFFFF;
 | |
| 	for (i = 0; i < len; i += 4) {
 | |
| 		sja1105_packing(buf + i, &chunk, 31, 0, 4, UNPACK);
 | |
| 		crc = crc32_add(crc, chunk & 0xFF);
 | |
| 		crc = crc32_add(crc, (chunk >> 8) & 0xFF);
 | |
| 		crc = crc32_add(crc, (chunk >> 16) & 0xFF);
 | |
| 		crc = crc32_add(crc, (chunk >> 24) & 0xFF);
 | |
| 	}
 | |
| 	return bitrev32(~crc);
 | |
| }
 | |
| 
 | |
| static void sja1105_spi_message_pack(void *buf, struct sja1105_spi_message *msg)
 | |
| {
 | |
| 	const int size = SJA1105_SIZE_SPI_MSG_HEADER;
 | |
| 
 | |
| 	memset(buf, 0, size);
 | |
| 
 | |
| 	sja1105_packing(buf, &msg->access,     31, 31, size, PACK);
 | |
| 	sja1105_packing(buf, &msg->read_count, 30, 25, size, PACK);
 | |
| 	sja1105_packing(buf, &msg->address,    24,  4, size, PACK);
 | |
| }
 | |
| 
 | |
| static int sja1105_xfer_buf(const struct sja1105_private *priv,
 | |
| 			    sja1105_spi_rw_mode_t rw, u64 reg_addr,
 | |
| 			    u8 *buf, size_t len)
 | |
| {
 | |
| 	struct udevice *dev = priv->dev;
 | |
| 	struct sja1105_chunk chunk = {
 | |
| 		.len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
 | |
| 		.reg_addr = reg_addr,
 | |
| 		.buf = buf,
 | |
| 	};
 | |
| 	int num_chunks;
 | |
| 	int rc, i;
 | |
| 
 | |
| 	rc = dm_spi_claim_bus(dev);
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
 | |
| 
 | |
| 	for (i = 0; i < num_chunks; i++) {
 | |
| 		u8 hdr_buf[SJA1105_SIZE_SPI_MSG_HEADER];
 | |
| 		struct sja1105_spi_message msg;
 | |
| 		u8 *rx_buf = NULL;
 | |
| 		u8 *tx_buf = NULL;
 | |
| 
 | |
| 		/* Populate the transfer's header buffer */
 | |
| 		msg.address = chunk.reg_addr;
 | |
| 		msg.access = rw;
 | |
| 		if (rw == SPI_READ)
 | |
| 			msg.read_count = chunk.len / 4;
 | |
| 		else
 | |
| 			/* Ignored */
 | |
| 			msg.read_count = 0;
 | |
| 		sja1105_spi_message_pack(hdr_buf, &msg);
 | |
| 		rc = dm_spi_xfer(dev, SJA1105_SIZE_SPI_MSG_HEADER * 8, hdr_buf,
 | |
| 				 NULL, SPI_XFER_BEGIN);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 		/* Populate the transfer's data buffer */
 | |
| 		if (rw == SPI_READ)
 | |
| 			rx_buf = chunk.buf;
 | |
| 		else
 | |
| 			tx_buf = chunk.buf;
 | |
| 		rc = dm_spi_xfer(dev, chunk.len * 8, tx_buf, rx_buf,
 | |
| 				 SPI_XFER_END);
 | |
| 		if (rc)
 | |
| 			goto out;
 | |
| 
 | |
| 		/* Calculate next chunk */
 | |
| 		chunk.buf += chunk.len;
 | |
| 		chunk.reg_addr += chunk.len / 4;
 | |
| 		chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf),
 | |
| 				  SJA1105_SIZE_SPI_MSG_MAXLEN);
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	dm_spi_release_bus(dev);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static int sja1105et_reset_cmd(struct sja1105_private *priv)
 | |
| {
 | |
| 	const struct sja1105_regs *regs = priv->info->regs;
 | |
| 	u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
 | |
| 	const int size = SJA1105_SIZE_RESET_CMD;
 | |
| 	u64 cold_rst = 1;
 | |
| 
 | |
| 	sja1105_packing(packed_buf, &cold_rst, 3, 3, size, PACK);
 | |
| 
 | |
| 	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
 | |
| 				SJA1105_SIZE_RESET_CMD);
 | |
| }
 | |
| 
 | |
| static int sja1105pqrs_reset_cmd(struct sja1105_private *priv)
 | |
| {
 | |
| 	const struct sja1105_regs *regs = priv->info->regs;
 | |
| 	u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
 | |
| 	const int size = SJA1105_SIZE_RESET_CMD;
 | |
| 	u64 cold_rst = 1;
 | |
| 
 | |
| 	sja1105_packing(packed_buf, &cold_rst, 2, 2, size, PACK);
 | |
| 
 | |
| 	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
 | |
| 				SJA1105_SIZE_RESET_CMD);
 | |
| }
 | |
| 
 | |
| static int sja1110_reset_cmd(struct sja1105_private *priv)
 | |
| {
 | |
| 	const struct sja1105_regs *regs = priv->info->regs;
 | |
| 	u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0};
 | |
| 	const int size = SJA1105_SIZE_RESET_CMD;
 | |
| 	u64 switch_rst = 1;
 | |
| 
 | |
| 	/* Only reset the switch core.
 | |
| 	 * A full cold reset would re-enable the BASE_MCSS_CLOCK PLL which
 | |
| 	 * would turn on the microcontroller, potentially letting it execute
 | |
| 	 * code which could interfere with our configuration.
 | |
| 	 */
 | |
| 	sja1105_packing(packed_buf, &switch_rst, 20, 20, size, PACK);
 | |
| 
 | |
| 	return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf,
 | |
| 				SJA1105_SIZE_RESET_CMD);
 | |
| }
 | |
| 
 | |
| static size_t sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,
 | |
| 						     enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY;
 | |
| 	struct sja1105_general_params_entry *entry = entry_ptr;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->mac_fltres1, 311, 264, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_fltres0, 263, 216, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_flt1,    215, 168, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_flt0,    167, 120, size, op);
 | |
| 	sja1105_packing(buf, &entry->casc_port,   115, 113, size, op);
 | |
| 	sja1105_packing(buf, &entry->host_port,   112, 110, size, op);
 | |
| 	sja1105_packing(buf, &entry->mirr_port,   109, 107, size, op);
 | |
| 	sja1105_packing(buf, &entry->tpid,         42,  27, size, op);
 | |
| 	sja1105_packing(buf, &entry->tpid2,        25,  10, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1110_general_params_entry_packing(void *buf, void *entry_ptr,
 | |
| 						   enum packing_op op)
 | |
| {
 | |
| 	struct sja1105_general_params_entry *entry = entry_ptr;
 | |
| 	const size_t size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->mac_fltres1,  438, 391, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_fltres0,  390, 343, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_flt1,     342, 295, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_flt0,     294, 247, size, op);
 | |
| 	sja1105_packing(buf, &entry->casc_port,    242, 232, size, op);
 | |
| 	sja1105_packing(buf, &entry->host_port,    231, 228, size, op);
 | |
| 	sja1105_packing(buf, &entry->mirr_port,    227, 224, size, op);
 | |
| 	sja1105_packing(buf, &entry->tpid2,        159, 144, size, op);
 | |
| 	sja1105_packing(buf, &entry->tpid,         142, 127, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t
 | |
| sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr,
 | |
| 					 enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY;
 | |
| 	struct sja1105_general_params_entry *entry = entry_ptr;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->mac_fltres1, 343, 296, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_fltres0, 295, 248, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_flt1,    247, 200, size, op);
 | |
| 	sja1105_packing(buf, &entry->mac_flt0,    199, 152, size, op);
 | |
| 	sja1105_packing(buf, &entry->casc_port,   147, 145, size, op);
 | |
| 	sja1105_packing(buf, &entry->host_port,   144, 142, size, op);
 | |
| 	sja1105_packing(buf, &entry->mirr_port,   141, 139, size, op);
 | |
| 	sja1105_packing(buf, &entry->tpid,         74,  59, size, op);
 | |
| 	sja1105_packing(buf, &entry->tpid2,        57,  42, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t
 | |
| sja1105_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr,
 | |
| 					   enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY;
 | |
| 	struct sja1105_l2_forwarding_params_entry *entry = entry_ptr;
 | |
| 	int offset, i;
 | |
| 
 | |
| 	for (i = 0, offset = 13; i < SJA1105_NUM_TC; i++, offset += 10)
 | |
| 		sja1105_packing(buf, &entry->part_spc[i],
 | |
| 				offset + 9, offset + 0, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t
 | |
| sja1110_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr,
 | |
| 					   enum packing_op op)
 | |
| {
 | |
| 	struct sja1105_l2_forwarding_params_entry *entry = entry_ptr;
 | |
| 	const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY;
 | |
| 	int offset, i;
 | |
| 
 | |
| 	for (i = 0, offset = 5; i < 8; i++, offset += 11)
 | |
| 		sja1105_packing(buf, &entry->part_spc[i],
 | |
| 				offset + 10, offset + 0, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
 | |
| 						  enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY;
 | |
| 	struct sja1105_l2_forwarding_entry *entry = entry_ptr;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->bc_domain,  63, 59, size, op);
 | |
| 	sja1105_packing(buf, &entry->reach_port, 58, 54, size, op);
 | |
| 	sja1105_packing(buf, &entry->fl_domain,  53, 49, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1110_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
 | |
| 						  enum packing_op op)
 | |
| {
 | |
| 	struct sja1105_l2_forwarding_entry *entry = entry_ptr;
 | |
| 	const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->bc_domain,  63, 53, size, op);
 | |
| 	sja1105_packing(buf, &entry->reach_port, 52, 42, size, op);
 | |
| 	sja1105_packing(buf, &entry->fl_domain,  41, 31, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1105_l2_policing_entry_packing(void *buf, void *entry_ptr,
 | |
| 						enum packing_op op)
 | |
| {
 | |
| 	struct sja1105_l2_policing_entry *entry = entry_ptr;
 | |
| 	const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->sharindx,  63, 58, size, op);
 | |
| 	sja1105_packing(buf, &entry->smax,      57, 42, size, op);
 | |
| 	sja1105_packing(buf, &entry->rate,      41, 26, size, op);
 | |
| 	sja1105_packing(buf, &entry->maxlen,    25, 15, size, op);
 | |
| 	sja1105_packing(buf, &entry->partition, 14, 12, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1110_l2_policing_entry_packing(void *buf, void *entry_ptr,
 | |
| 						enum packing_op op)
 | |
| {
 | |
| 	struct sja1105_l2_policing_entry *entry = entry_ptr;
 | |
| 	const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->sharindx, 63, 57, size, op);
 | |
| 	sja1105_packing(buf, &entry->smax,     56, 39, size, op);
 | |
| 	sja1105_packing(buf, &entry->rate,     38, 21, size, op);
 | |
| 	sja1105_packing(buf, &entry->maxlen,   20, 10, size, op);
 | |
| 	sja1105_packing(buf, &entry->partition, 9,  7, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr,
 | |
| 						 enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY;
 | |
| 	struct sja1105_mac_config_entry *entry = entry_ptr;
 | |
| 	int offset, i;
 | |
| 
 | |
| 	for (i = 0, offset = 72; i < SJA1105_NUM_TC; i++, offset += 19) {
 | |
| 		sja1105_packing(buf, &entry->enabled[i],
 | |
| 				offset +  0, offset +  0, size, op);
 | |
| 		sja1105_packing(buf, &entry->base[i],
 | |
| 				offset +  9, offset +  1, size, op);
 | |
| 		sja1105_packing(buf, &entry->top[i],
 | |
| 				offset + 18, offset + 10, size, op);
 | |
| 	}
 | |
| 	sja1105_packing(buf, &entry->speed,     66, 65, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlanid,    21, 10, size, op);
 | |
| 	sja1105_packing(buf, &entry->egress,     2,  2, size, op);
 | |
| 	sja1105_packing(buf, &entry->ingress,    1,  1, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,
 | |
| 						   enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY;
 | |
| 	struct sja1105_mac_config_entry *entry = entry_ptr;
 | |
| 	int offset, i;
 | |
| 
 | |
| 	for (i = 0, offset = 104; i < SJA1105_NUM_TC; i++, offset += 19) {
 | |
| 		sja1105_packing(buf, &entry->enabled[i],
 | |
| 				offset +  0, offset +  0, size, op);
 | |
| 		sja1105_packing(buf, &entry->base[i],
 | |
| 				offset +  9, offset +  1, size, op);
 | |
| 		sja1105_packing(buf, &entry->top[i],
 | |
| 				offset + 18, offset + 10, size, op);
 | |
| 	}
 | |
| 	sja1105_packing(buf, &entry->speed,      98, 97, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlanid,     53, 42, size, op);
 | |
| 	sja1105_packing(buf, &entry->egress,     32, 32, size, op);
 | |
| 	sja1105_packing(buf, &entry->ingress,    31, 31, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1110_mac_config_entry_packing(void *buf, void *entry_ptr,
 | |
| 					       enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY;
 | |
| 	struct sja1105_mac_config_entry *entry = entry_ptr;
 | |
| 	int offset, i;
 | |
| 
 | |
| 	for (i = 0, offset = 104; i < 8; i++, offset += 19) {
 | |
| 		sja1105_packing(buf, &entry->enabled[i],
 | |
| 				offset +  0, offset +  0, size, op);
 | |
| 		sja1105_packing(buf, &entry->base[i],
 | |
| 				offset +  9, offset +  1, size, op);
 | |
| 		sja1105_packing(buf, &entry->top[i],
 | |
| 				offset + 18, offset + 10, size, op);
 | |
| 	}
 | |
| 	sja1105_packing(buf, &entry->speed,      98, 96, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlanid,     52, 41, size, op);
 | |
| 	sja1105_packing(buf, &entry->egress,     31, 31, size, op);
 | |
| 	sja1105_packing(buf, &entry->ingress,    30, 30, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
 | |
| 						enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY;
 | |
| 	struct sja1105_vlan_lookup_entry *entry = entry_ptr;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->vmemb_port, 53, 49, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlan_bc,    48, 44, size, op);
 | |
| 	sja1105_packing(buf, &entry->tag_port,   43, 39, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlanid,     38, 27, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1110_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
 | |
| 						enum packing_op op)
 | |
| {
 | |
| 	struct sja1105_vlan_lookup_entry *entry = entry_ptr;
 | |
| 	const size_t size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY;
 | |
| 
 | |
| 	sja1105_packing(buf, &entry->vmemb_port, 73, 63, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlan_bc,    62, 52, size, op);
 | |
| 	sja1105_packing(buf, &entry->tag_port,   51, 41, size, op);
 | |
| 	sja1105_packing(buf, &entry->type_entry, 40, 39, size, op);
 | |
| 	sja1105_packing(buf, &entry->vlanid,     38, 27, size, op);
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static size_t sja1105_xmii_params_entry_packing(void *buf, void *entry_ptr,
 | |
| 						enum packing_op op)
 | |
| {
 | |
| 	const size_t size = SJA1105_SIZE_XMII_PARAMS_ENTRY;
 | |
| 	struct sja1105_xmii_params_entry *entry = entry_ptr;
 | |
| 	int offset, i;
 | |
| 
 | |
| 	for (i = 0, offset = 17; i < SJA1105_NUM_PORTS; i++, offset += 3) {
 | |
| 		sja1105_packing(buf, &entry->xmii_mode[i],
 | |
| 				offset + 1, offset + 0, size, op);
 | |
| 		sja1105_packing(buf, &entry->phy_mac[i],
 | |
| 				offset + 2, offset + 2, size, op);
 | |
| 	}
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| static 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);
 | |
| }
 | |
| 
 | |
| static 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, MDIO_CTRL1);
 | |
| 	if (rc < 0)
 | |
| 		return rc;
 | |
| 	rc &= ~MDIO_AN_CTRL1_ENABLE;
 | |
| 	rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1,
 | |
| 			       rc);
 | |
| 	if (rc < 0)
 | |
| 		return 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;
 | |
| 
 | |
| 	rc = sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1,
 | |
| 			       rc);
 | |
| 	if (rc < 0)
 | |
| 		return rc;
 | |
| 
 | |
| 	rc = sja1105_pcs_read(priv, port, MDIO_MMD_VEND2, MDIO_CTRL1);
 | |
| 	if (rc < 0)
 | |
| 		return rc;
 | |
| 
 | |
| 	if (priv->xpcs_cfg[port].inband_an)
 | |
| 		rc |= MDIO_AN_CTRL1_ENABLE;
 | |
| 	else
 | |
| 		rc &= ~MDIO_AN_CTRL1_ENABLE;
 | |
| 
 | |
| 	return sja1105_pcs_write(priv, port, MDIO_MMD_VEND2, MDIO_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);
 | |
| 
 | |
| 	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_enabled(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),
 | |
| };
 |