forked from mirrors/linux
		
	net: dsa: b53: Add support for Broadcom RoboSwitch
This patch adds support for Broadcom's BCM53xx switch family, also known as RoboSwitch. Some of these switches are ubiquituous, found in home routers, Wi-Fi routers, DSL and cable modem gateways and other networking related products. This drivers adds the library driver (b53_common.c) as well as a few bus glue drivers for MDIO, SPI, Switch Register Access Block (SRAB) and memory-mapped I/O into a SoC's address space (Broadcom BCM63xx/33xx). Basic operations are supported to bring the Layer 1/2 up and running, but not much more at this point, subsequent patches add the remaining features. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									409a5f27ed
								
							
						
					
					
						commit
						967dd82ffc
					
				
					 14 changed files with 3397 additions and 0 deletions
				
			
		
							
								
								
									
										88
									
								
								Documentation/devicetree/bindings/net/dsa/b53.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								Documentation/devicetree/bindings/net/dsa/b53.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | |||
| Broadcom BCM53xx Ethernet switches | ||||
| ================================== | ||||
| 
 | ||||
| Required properties: | ||||
| 
 | ||||
| - compatible: For external switch chips, compatible string must be exactly one | ||||
|   of: "brcm,bcm5325" | ||||
|       "brcm,bcm53115" | ||||
|       "brcm,bcm53125" | ||||
|       "brcm,bcm53128" | ||||
|       "brcm,bcm5365" | ||||
|       "brcm,bcm5395" | ||||
|       "brcm,bcm5397" | ||||
|       "brcm,bcm5398" | ||||
| 
 | ||||
|   For the BCM5310x SoCs with an integrated switch, must be one of: | ||||
|       "brcm,bcm53010-srab" | ||||
|       "brcm,bcm53011-srab" | ||||
|       "brcm,bcm53012-srab" | ||||
|       "brcm,bcm53018-srab" | ||||
|       "brcm,bcm53019-srab" and the mandatory "brcm,bcm5301x-srab" string | ||||
| 
 | ||||
|   For the BCM63xx/33xx SoCs with an integrated switch, must be one of: | ||||
|       "brcm,bcm3384-switch" | ||||
|       "brcm,bcm6328-switch" | ||||
|       "brcm,bcm6368-switch" and the mandatory "brcm,bcm63xx-switch" | ||||
| 
 | ||||
| See Documentation/devicetree/bindings/dsa/dsa.txt for a list of additional | ||||
| required and optional properties. | ||||
| 
 | ||||
| Examples: | ||||
| 
 | ||||
| Ethernet switch connected via MDIO to the host, CPU port wired to eth0: | ||||
| 
 | ||||
| 	eth0: ethernet@10001000 { | ||||
| 		compatible = "brcm,unimac"; | ||||
| 		reg = <0x10001000 0x1000>; | ||||
| 
 | ||||
| 		fixed-link { | ||||
| 			speed = <1000>; | ||||
| 			duplex-full; | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	mdio0: mdio@10000000 { | ||||
| 		compatible = "brcm,unimac-mdio"; | ||||
| 		#address-cells = <1>; | ||||
| 		#size-cells = <0>; | ||||
| 
 | ||||
| 		switch0: ethernet-switch@30 { | ||||
| 			compatible = "brcm,bcm53125"; | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
| 
 | ||||
| 			ports { | ||||
| 				port0@0 { | ||||
| 					reg = <0>; | ||||
| 					label = "lan1"; | ||||
| 				}; | ||||
| 
 | ||||
| 				port1@1 { | ||||
| 					reg = <1>; | ||||
| 					label = "lan2"; | ||||
| 				}; | ||||
| 
 | ||||
| 				port5@5 { | ||||
| 					reg = <5>; | ||||
| 					label = "cable-modem"; | ||||
| 					fixed-link { | ||||
| 						speed = <1000>; | ||||
| 						duplex-full; | ||||
| 					}; | ||||
| 					phy-mode = "rgmii-txid"; | ||||
| 				}; | ||||
| 
 | ||||
| 				port8@8 { | ||||
| 					reg = <8>; | ||||
| 					label = "cpu"; | ||||
| 					fixed-link { | ||||
| 						speed = <1000>; | ||||
| 						duplex-full; | ||||
| 					}; | ||||
| 					phy-mode = "rgmii-txid"; | ||||
| 					ethernet = <ð0>; | ||||
| 				}; | ||||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
|  | @ -2454,6 +2454,14 @@ L:	netdev@vger.kernel.org | |||
| S:	Supported | ||||
| F:	drivers/net/ethernet/broadcom/b44.* | ||||
| 
 | ||||
| BROADCOM B53 ETHERNET SWITCH DRIVER | ||||
| M:	Florian Fainelli <f.fainelli@gmail.com> | ||||
| L:	netdev@vger.kernel.org | ||||
| L:	openwrt-devel@lists.openwrt.org (subscribers-only) | ||||
| S:	Supported | ||||
| F:	drivers/net/dsa/b53/* | ||||
| F:	include/linux/platform_data/b53.h | ||||
| 
 | ||||
| BROADCOM GENET ETHERNET DRIVER | ||||
| M:	Florian Fainelli <f.fainelli@gmail.com> | ||||
| L:	netdev@vger.kernel.org | ||||
|  |  | |||
|  | @ -28,4 +28,6 @@ config NET_DSA_BCM_SF2 | |||
| 	  This enables support for the Broadcom Starfighter 2 Ethernet | ||||
| 	  switch chips. | ||||
| 
 | ||||
| source "drivers/net/dsa/b53/Kconfig" | ||||
| 
 | ||||
| endmenu | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o | ||||
| obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o | ||||
| obj-$(CONFIG_NET_DSA_BCM_SF2)	+= bcm_sf2.o | ||||
| 
 | ||||
| obj-y				+= b53/ | ||||
|  |  | |||
							
								
								
									
										33
									
								
								drivers/net/dsa/b53/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								drivers/net/dsa/b53/Kconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| menuconfig B53 | ||||
| 	tristate "Broadcom BCM53xx managed switch support" | ||||
| 	depends on NET_DSA | ||||
| 	help | ||||
| 	  This driver adds support for Broadcom managed switch chips. It supports | ||||
| 	  BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX | ||||
| 	  integrated switches. | ||||
| 
 | ||||
| config B53_SPI_DRIVER | ||||
| 	tristate "B53 SPI connected switch driver" | ||||
| 	depends on B53 && SPI | ||||
| 	help | ||||
| 	  Select to enable support for registering switches configured through SPI. | ||||
| 
 | ||||
| config B53_MDIO_DRIVER | ||||
| 	tristate "B53 MDIO connected switch driver" | ||||
| 	depends on B53 | ||||
| 	help | ||||
| 	  Select to enable support for registering switches configured through MDIO. | ||||
| 
 | ||||
| config B53_MMAP_DRIVER | ||||
| 	tristate "B53 MMAP connected switch driver" | ||||
| 	depends on B53 && HAS_IOMEM | ||||
| 	help | ||||
| 	  Select to enable support for memory-mapped switches like the BCM63XX | ||||
| 	  integrated switches. | ||||
| 
 | ||||
| config B53_SRAB_DRIVER | ||||
| 	tristate "B53 SRAB connected switch driver" | ||||
| 	depends on B53 && HAS_IOMEM | ||||
| 	help | ||||
| 	  Select to enable support for memory-mapped Switch Register Access | ||||
| 	  Bridge Registers (SRAB) like it is found on the BCM53010 | ||||
							
								
								
									
										6
									
								
								drivers/net/dsa/b53/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								drivers/net/dsa/b53/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| obj-$(CONFIG_B53)		+= b53_common.o | ||||
| 
 | ||||
| obj-$(CONFIG_B53_SPI_DRIVER)	+= b53_spi.o | ||||
| obj-$(CONFIG_B53_MDIO_DRIVER)	+= b53_mdio.o | ||||
| obj-$(CONFIG_B53_MMAP_DRIVER)	+= b53_mmap.o | ||||
| obj-$(CONFIG_B53_SRAB_DRIVER)	+= b53_srab.o | ||||
							
								
								
									
										1158
									
								
								drivers/net/dsa/b53/b53_common.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1158
									
								
								drivers/net/dsa/b53/b53_common.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										381
									
								
								drivers/net/dsa/b53/b53_mdio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								drivers/net/dsa/b53/b53_mdio.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,381 @@ | |||
| /*
 | ||||
|  * B53 register access through MII registers | ||||
|  * | ||||
|  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/phy.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/brcmphy.h> | ||||
| #include <linux/rtnetlink.h> | ||||
| #include <net/dsa.h> | ||||
| 
 | ||||
| #include "b53_priv.h" | ||||
| 
 | ||||
| /* MII registers */ | ||||
| #define REG_MII_PAGE    0x10    /* MII Page register */ | ||||
| #define REG_MII_ADDR    0x11    /* MII Address register */ | ||||
| #define REG_MII_DATA0   0x18    /* MII Data register 0 */ | ||||
| #define REG_MII_DATA1   0x19    /* MII Data register 1 */ | ||||
| #define REG_MII_DATA2   0x1a    /* MII Data register 2 */ | ||||
| #define REG_MII_DATA3   0x1b    /* MII Data register 3 */ | ||||
| 
 | ||||
| #define REG_MII_PAGE_ENABLE     BIT(0) | ||||
| #define REG_MII_ADDR_WRITE      BIT(0) | ||||
| #define REG_MII_ADDR_READ       BIT(1) | ||||
| 
 | ||||
| static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op) | ||||
| { | ||||
| 	int i; | ||||
| 	u16 v; | ||||
| 	int ret; | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 
 | ||||
| 	if (dev->current_page != page) { | ||||
| 		/* set page number */ | ||||
| 		v = (page << 8) | REG_MII_PAGE_ENABLE; | ||||
| 		ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 					   REG_MII_PAGE, v); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		dev->current_page = page; | ||||
| 	} | ||||
| 
 | ||||
| 	/* set register address */ | ||||
| 	v = (reg << 8) | op; | ||||
| 	ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_ADDR, v); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* check if operation completed */ | ||||
| 	for (i = 0; i < 5; ++i) { | ||||
| 		v = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 					REG_MII_ADDR); | ||||
| 		if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ))) | ||||
| 			break; | ||||
| 		usleep_range(10, 100); | ||||
| 	} | ||||
| 
 | ||||
| 	if (WARN_ON(i == 5)) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	*val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 				   REG_MII_DATA0) & 0xff; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	*val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	*val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0); | ||||
| 	*val |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 				    REG_MII_DATA1) << 16; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	u64 temp = 0; | ||||
| 	int i; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for (i = 2; i >= 0; i--) { | ||||
| 		temp <<= 16; | ||||
| 		temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 				     REG_MII_DATA0 + i); | ||||
| 	} | ||||
| 
 | ||||
| 	*val = temp; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	u64 temp = 0; | ||||
| 	int i; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for (i = 3; i >= 0; i--) { | ||||
| 		temp <<= 16; | ||||
| 		temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 					    REG_MII_DATA0 + i); | ||||
| 	} | ||||
| 
 | ||||
| 	*val = temp; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 				   REG_MII_DATA0, value); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u16 value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 				   REG_MII_DATA0, value); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u32 value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	unsigned int i; | ||||
| 	u32 temp = value; | ||||
| 
 | ||||
| 	for (i = 0; i < 2; i++) { | ||||
| 		int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 					       REG_MII_DATA0 + i, | ||||
| 					       temp & 0xffff); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		temp >>= 16; | ||||
| 	} | ||||
| 
 | ||||
| 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u64 value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	unsigned int i; | ||||
| 	u64 temp = value; | ||||
| 
 | ||||
| 	for (i = 0; i < 3; i++) { | ||||
| 		int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 					       REG_MII_DATA0 + i, | ||||
| 					       temp & 0xffff); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		temp >>= 16; | ||||
| 	} | ||||
| 
 | ||||
| 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u64 value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 	unsigned int i; | ||||
| 	u64 temp = value; | ||||
| 
 | ||||
| 	for (i = 0; i < 4; i++) { | ||||
| 		int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, | ||||
| 					       REG_MII_DATA0 + i, | ||||
| 					       temp & 0xffff); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		temp >>= 16; | ||||
| 	} | ||||
| 
 | ||||
| 	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_phy_read16(struct b53_device *dev, int addr, int reg, | ||||
| 			       u16 *value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->priv; | ||||
| 
 | ||||
| 	*value = mdiobus_read_nested(bus, addr, reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mdio_phy_write16(struct b53_device *dev, int addr, int reg, | ||||
| 				u16 value) | ||||
| { | ||||
| 	struct mii_bus *bus = dev->bus; | ||||
| 
 | ||||
| 	return mdiobus_write_nested(bus, addr, reg, value); | ||||
| } | ||||
| 
 | ||||
| static struct b53_io_ops b53_mdio_ops = { | ||||
| 	.read8 = b53_mdio_read8, | ||||
| 	.read16 = b53_mdio_read16, | ||||
| 	.read32 = b53_mdio_read32, | ||||
| 	.read48 = b53_mdio_read48, | ||||
| 	.read64 = b53_mdio_read64, | ||||
| 	.write8 = b53_mdio_write8, | ||||
| 	.write16 = b53_mdio_write16, | ||||
| 	.write32 = b53_mdio_write32, | ||||
| 	.write48 = b53_mdio_write48, | ||||
| 	.write64 = b53_mdio_write64, | ||||
| 	.phy_read16 = b53_mdio_phy_read16, | ||||
| 	.phy_write16 = b53_mdio_phy_write16, | ||||
| }; | ||||
| 
 | ||||
| #define B53_BRCM_OUI_1	0x0143bc00 | ||||
| #define B53_BRCM_OUI_2	0x03625c00 | ||||
| #define B53_BRCM_OUI_3	0x00406000 | ||||
| 
 | ||||
| static int b53_mdio_probe(struct mdio_device *mdiodev) | ||||
| { | ||||
| 	struct b53_device *dev; | ||||
| 	u32 phy_id; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* allow the generic PHY driver to take over the non-management MDIO
 | ||||
| 	 * addresses | ||||
| 	 */ | ||||
| 	if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) { | ||||
| 		dev_err(&mdiodev->dev, "leaving address %d to PHY\n", | ||||
| 			mdiodev->addr); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	/* read the first port's id */ | ||||
| 	phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16; | ||||
| 	phy_id |= mdiobus_read(mdiodev->bus, 0, 3); | ||||
| 
 | ||||
| 	/* BCM5325, BCM539x (OUI_1)
 | ||||
| 	 * BCM53125, BCM53128 (OUI_2) | ||||
| 	 * BCM5365 (OUI_3) | ||||
| 	 */ | ||||
| 	if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 && | ||||
| 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 && | ||||
| 	    (phy_id & 0xfffffc00) != B53_BRCM_OUI_3) { | ||||
| 		dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus); | ||||
| 	if (!dev) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* we don't use page 0xff, so force a page set */ | ||||
| 	dev->current_page = 0xff; | ||||
| 	dev->bus = mdiodev->bus; | ||||
| 
 | ||||
| 	dev_set_drvdata(&mdiodev->dev, dev); | ||||
| 
 | ||||
| 	ret = b53_switch_register(dev); | ||||
| 	if (ret) { | ||||
| 		dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void b53_mdio_remove(struct mdio_device *mdiodev) | ||||
| { | ||||
| 	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev); | ||||
| 	struct dsa_switch *ds = dev->ds; | ||||
| 
 | ||||
| 	dsa_unregister_switch(ds); | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id b53_of_match[] = { | ||||
| 	{ .compatible = "brcm,bcm5325" }, | ||||
| 	{ .compatible = "brcm,bcm53115" }, | ||||
| 	{ .compatible = "brcm,bcm53125" }, | ||||
| 	{ .compatible = "brcm,bcm53128" }, | ||||
| 	{ .compatible = "brcm,bcm5365" }, | ||||
| 	{ .compatible = "brcm,bcm5395" }, | ||||
| 	{ .compatible = "brcm,bcm5397" }, | ||||
| 	{ .compatible = "brcm,bcm5398" }, | ||||
| 	{ /* sentinel */ }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, b53_of_match); | ||||
| 
 | ||||
| static struct mdio_driver b53_mdio_driver = { | ||||
| 	.probe	= b53_mdio_probe, | ||||
| 	.remove	= b53_mdio_remove, | ||||
| 	.mdiodrv.driver = { | ||||
| 		.name = "bcm53xx", | ||||
| 		.of_match_table = b53_of_match, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int __init b53_mdio_driver_register(void) | ||||
| { | ||||
| 	return mdio_driver_register(&b53_mdio_driver); | ||||
| } | ||||
| module_init(b53_mdio_driver_register); | ||||
| 
 | ||||
| static void __exit b53_mdio_driver_unregister(void) | ||||
| { | ||||
| 	mdio_driver_unregister(&b53_mdio_driver); | ||||
| } | ||||
| module_exit(b53_mdio_driver_unregister); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("B53 MDIO access driver"); | ||||
| MODULE_LICENSE("Dual BSD/GPL"); | ||||
							
								
								
									
										260
									
								
								drivers/net/dsa/b53/b53_mmap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								drivers/net/dsa/b53/b53_mmap.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,260 @@ | |||
| /*
 | ||||
|  * B53 register access through memory mapped registers | ||||
|  * | ||||
|  * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kconfig.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/platform_data/b53.h> | ||||
| 
 | ||||
| #include "b53_priv.h" | ||||
| 
 | ||||
| struct b53_mmap_priv { | ||||
| 	void __iomem *regs; | ||||
| }; | ||||
| 
 | ||||
| static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) | ||||
| { | ||||
| 	u8 __iomem *regs = dev->priv; | ||||
| 
 | ||||
| 	*val = readb(regs + (page << 8) + reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) | ||||
| { | ||||
| 	u8 __iomem *regs = dev->priv; | ||||
| 
 | ||||
| 	if (WARN_ON(reg % 2)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && dev->pdata && | ||||
| 	    dev->pdata->big_endian) | ||||
| 		*val = __raw_readw(regs + (page << 8) + reg); | ||||
| 	else | ||||
| 		*val = readw(regs + (page << 8) + reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) | ||||
| { | ||||
| 	u8 __iomem *regs = dev->priv; | ||||
| 
 | ||||
| 	if (WARN_ON(reg % 4)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && dev->pdata && | ||||
| 	    dev->pdata->big_endian) | ||||
| 		*val = __raw_readl(regs + (page << 8) + reg); | ||||
| 	else | ||||
| 		*val = readl(regs + (page << 8) + reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	if (WARN_ON(reg % 2)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (reg % 4) { | ||||
| 		u16 lo; | ||||
| 		u32 hi; | ||||
| 
 | ||||
| 		b53_mmap_read16(dev, page, reg, &lo); | ||||
| 		b53_mmap_read32(dev, page, reg + 2, &hi); | ||||
| 
 | ||||
| 		*val = ((u64)hi << 16) | lo; | ||||
| 	} else { | ||||
| 		u32 lo; | ||||
| 		u16 hi; | ||||
| 
 | ||||
| 		b53_mmap_read32(dev, page, reg, &lo); | ||||
| 		b53_mmap_read16(dev, page, reg + 4, &hi); | ||||
| 
 | ||||
| 		*val = ((u64)hi << 32) | lo; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	u32 hi, lo; | ||||
| 
 | ||||
| 	if (WARN_ON(reg % 4)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	b53_mmap_read32(dev, page, reg, &lo); | ||||
| 	b53_mmap_read32(dev, page, reg + 4, &hi); | ||||
| 
 | ||||
| 	*val = ((u64)hi << 32) | lo; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) | ||||
| { | ||||
| 	u8 __iomem *regs = dev->priv; | ||||
| 
 | ||||
| 	writeb(value, regs + (page << 8) + reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u16 value) | ||||
| { | ||||
| 	u8 __iomem *regs = dev->priv; | ||||
| 
 | ||||
| 	if (WARN_ON(reg % 2)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && dev->pdata && | ||||
| 	    dev->pdata->big_endian) | ||||
| 		__raw_writew(value, regs + (page << 8) + reg); | ||||
| 	else | ||||
| 		writew(value, regs + (page << 8) + reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u32 value) | ||||
| { | ||||
| 	u8 __iomem *regs = dev->priv; | ||||
| 
 | ||||
| 	if (WARN_ON(reg % 4)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && dev->pdata && | ||||
| 	    dev->pdata->big_endian) | ||||
| 		__raw_writel(value, regs + (page << 8) + reg); | ||||
| 	else | ||||
| 		writel(value, regs + (page << 8) + reg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u64 value) | ||||
| { | ||||
| 	if (WARN_ON(reg % 2)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (reg % 4) { | ||||
| 		u32 hi = (u32)(value >> 16); | ||||
| 		u16 lo = (u16)value; | ||||
| 
 | ||||
| 		b53_mmap_write16(dev, page, reg, lo); | ||||
| 		b53_mmap_write32(dev, page, reg + 2, hi); | ||||
| 	} else { | ||||
| 		u16 hi = (u16)(value >> 32); | ||||
| 		u32 lo = (u32)value; | ||||
| 
 | ||||
| 		b53_mmap_write32(dev, page, reg, lo); | ||||
| 		b53_mmap_write16(dev, page, reg + 4, hi); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u64 value) | ||||
| { | ||||
| 	u32 hi, lo; | ||||
| 
 | ||||
| 	hi = upper_32_bits(value); | ||||
| 	lo = lower_32_bits(value); | ||||
| 
 | ||||
| 	if (WARN_ON(reg % 4)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	b53_mmap_write32(dev, page, reg, lo); | ||||
| 	b53_mmap_write32(dev, page, reg + 4, hi); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct b53_io_ops b53_mmap_ops = { | ||||
| 	.read8 = b53_mmap_read8, | ||||
| 	.read16 = b53_mmap_read16, | ||||
| 	.read32 = b53_mmap_read32, | ||||
| 	.read48 = b53_mmap_read48, | ||||
| 	.read64 = b53_mmap_read64, | ||||
| 	.write8 = b53_mmap_write8, | ||||
| 	.write16 = b53_mmap_write16, | ||||
| 	.write32 = b53_mmap_write32, | ||||
| 	.write48 = b53_mmap_write48, | ||||
| 	.write64 = b53_mmap_write64, | ||||
| }; | ||||
| 
 | ||||
| static int b53_mmap_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct b53_platform_data *pdata = pdev->dev.platform_data; | ||||
| 	struct b53_device *dev; | ||||
| 
 | ||||
| 	if (!pdata) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs); | ||||
| 	if (!dev) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	if (pdata) | ||||
| 		dev->pdata = pdata; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, dev); | ||||
| 
 | ||||
| 	return b53_switch_register(dev); | ||||
| } | ||||
| 
 | ||||
| static int b53_mmap_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct b53_device *dev = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	if (dev) | ||||
| 		b53_switch_remove(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id b53_mmap_of_table[] = { | ||||
| 	{ .compatible = "brcm,bcm3384-switch" }, | ||||
| 	{ .compatible = "brcm,bcm6328-switch" }, | ||||
| 	{ .compatible = "brcm,bcm6368-switch" }, | ||||
| 	{ .compatible = "brcm,bcm63xx-switch" }, | ||||
| 	{ /* sentinel */ }, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver b53_mmap_driver = { | ||||
| 	.probe = b53_mmap_probe, | ||||
| 	.remove = b53_mmap_remove, | ||||
| 	.driver = { | ||||
| 		.name = "b53-switch", | ||||
| 		.of_match_table = b53_mmap_of_table, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| module_platform_driver(b53_mmap_driver); | ||||
| MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); | ||||
| MODULE_DESCRIPTION("B53 MMAP access driver"); | ||||
| MODULE_LICENSE("Dual BSD/GPL"); | ||||
							
								
								
									
										322
									
								
								drivers/net/dsa/b53/b53_priv.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								drivers/net/dsa/b53/b53_priv.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,322 @@ | |||
| /*
 | ||||
|  * B53 common definitions | ||||
|  * | ||||
|  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __B53_PRIV_H | ||||
| #define __B53_PRIV_H | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/phy.h> | ||||
| #include <net/dsa.h> | ||||
| 
 | ||||
| struct b53_device; | ||||
| 
 | ||||
| struct b53_io_ops { | ||||
| 	int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value); | ||||
| 	int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value); | ||||
| 	int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value); | ||||
| 	int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value); | ||||
| 	int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value); | ||||
| 	int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value); | ||||
| 	int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value); | ||||
| 	int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value); | ||||
| 	int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value); | ||||
| 	int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value); | ||||
| 	int (*phy_read16)(struct b53_device *dev, int addr, int reg, u16 *value); | ||||
| 	int (*phy_write16)(struct b53_device *dev, int addr, int reg, u16 value); | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	BCM5325_DEVICE_ID = 0x25, | ||||
| 	BCM5365_DEVICE_ID = 0x65, | ||||
| 	BCM5395_DEVICE_ID = 0x95, | ||||
| 	BCM5397_DEVICE_ID = 0x97, | ||||
| 	BCM5398_DEVICE_ID = 0x98, | ||||
| 	BCM53115_DEVICE_ID = 0x53115, | ||||
| 	BCM53125_DEVICE_ID = 0x53125, | ||||
| 	BCM53128_DEVICE_ID = 0x53128, | ||||
| 	BCM63XX_DEVICE_ID = 0x6300, | ||||
| 	BCM53010_DEVICE_ID = 0x53010, | ||||
| 	BCM53011_DEVICE_ID = 0x53011, | ||||
| 	BCM53012_DEVICE_ID = 0x53012, | ||||
| 	BCM53018_DEVICE_ID = 0x53018, | ||||
| 	BCM53019_DEVICE_ID = 0x53019, | ||||
| }; | ||||
| 
 | ||||
| #define B53_N_PORTS	9 | ||||
| #define B53_N_PORTS_25	6 | ||||
| 
 | ||||
| struct b53_port { | ||||
| }; | ||||
| 
 | ||||
| struct b53_device { | ||||
| 	struct dsa_switch *ds; | ||||
| 	struct b53_platform_data *pdata; | ||||
| 	const char *name; | ||||
| 
 | ||||
| 	struct mutex reg_mutex; | ||||
| 	struct mutex stats_mutex; | ||||
| 	const struct b53_io_ops *ops; | ||||
| 
 | ||||
| 	/* chip specific data */ | ||||
| 	u32 chip_id; | ||||
| 	u8 core_rev; | ||||
| 	u8 vta_regs[3]; | ||||
| 	u8 duplex_reg; | ||||
| 	u8 jumbo_pm_reg; | ||||
| 	u8 jumbo_size_reg; | ||||
| 	int reset_gpio; | ||||
| 
 | ||||
| 	/* used ports mask */ | ||||
| 	u16 enabled_ports; | ||||
| 	unsigned int cpu_port; | ||||
| 
 | ||||
| 	/* connect specific data */ | ||||
| 	u8 current_page; | ||||
| 	struct device *dev; | ||||
| 
 | ||||
| 	/* Master MDIO bus we got probed from */ | ||||
| 	struct mii_bus *bus; | ||||
| 
 | ||||
| 	/* Slave MDIO bus we created */ | ||||
| 	struct mii_bus *slave_bus; | ||||
| 	void *priv; | ||||
| 
 | ||||
| 	/* run time configuration */ | ||||
| 	unsigned enable_jumbo:1; | ||||
| 	unsigned allow_vid_4095:1; | ||||
| 	unsigned int num_vlans; | ||||
| 	unsigned int num_ports; | ||||
| 	struct b53_port *ports; | ||||
| }; | ||||
| 
 | ||||
| #define b53_for_each_port(dev, i) \ | ||||
| 	for (i = 0; i < B53_N_PORTS; i++) \ | ||||
| 		if (dev->enabled_ports & BIT(i)) | ||||
| 
 | ||||
| 
 | ||||
| static inline int is5325(struct b53_device *dev) | ||||
| { | ||||
| 	return dev->chip_id == BCM5325_DEVICE_ID; | ||||
| } | ||||
| 
 | ||||
| static inline int is5365(struct b53_device *dev) | ||||
| { | ||||
| #ifdef CONFIG_BCM47XX | ||||
| 	return dev->chip_id == BCM5365_DEVICE_ID; | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static inline int is5397_98(struct b53_device *dev) | ||||
| { | ||||
| 	return dev->chip_id == BCM5397_DEVICE_ID || | ||||
| 		dev->chip_id == BCM5398_DEVICE_ID; | ||||
| } | ||||
| 
 | ||||
| static inline int is539x(struct b53_device *dev) | ||||
| { | ||||
| 	return dev->chip_id == BCM5395_DEVICE_ID || | ||||
| 		dev->chip_id == BCM5397_DEVICE_ID || | ||||
| 		dev->chip_id == BCM5398_DEVICE_ID; | ||||
| } | ||||
| 
 | ||||
| static inline int is531x5(struct b53_device *dev) | ||||
| { | ||||
| 	return dev->chip_id == BCM53115_DEVICE_ID || | ||||
| 		dev->chip_id == BCM53125_DEVICE_ID || | ||||
| 		dev->chip_id == BCM53128_DEVICE_ID; | ||||
| } | ||||
| 
 | ||||
| static inline int is63xx(struct b53_device *dev) | ||||
| { | ||||
| #ifdef CONFIG_BCM63XX | ||||
| 	return dev->chip_id == BCM63XX_DEVICE_ID; | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static inline int is5301x(struct b53_device *dev) | ||||
| { | ||||
| 	return dev->chip_id == BCM53010_DEVICE_ID || | ||||
| 		dev->chip_id == BCM53011_DEVICE_ID || | ||||
| 		dev->chip_id == BCM53012_DEVICE_ID || | ||||
| 		dev->chip_id == BCM53018_DEVICE_ID || | ||||
| 		dev->chip_id == BCM53019_DEVICE_ID; | ||||
| } | ||||
| 
 | ||||
| #define B53_CPU_PORT_25	5 | ||||
| #define B53_CPU_PORT	8 | ||||
| 
 | ||||
| static inline int is_cpu_port(struct b53_device *dev, int port) | ||||
| { | ||||
| 	return dev->cpu_port; | ||||
| } | ||||
| 
 | ||||
| struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, | ||||
| 				    void *priv); | ||||
| 
 | ||||
| int b53_switch_detect(struct b53_device *dev); | ||||
| 
 | ||||
| int b53_switch_register(struct b53_device *dev); | ||||
| 
 | ||||
| static inline void b53_switch_remove(struct b53_device *dev) | ||||
| { | ||||
| 	dsa_unregister_switch(dev->ds); | ||||
| } | ||||
| 
 | ||||
| static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->read8(dev, page, reg, val); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->read16(dev, page, reg, val); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->read32(dev, page, reg, val); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->read48(dev, page, reg, val); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->read64(dev, page, reg, val); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->write8(dev, page, reg, value); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			      u16 value) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->write16(dev, page, reg, value); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			      u32 value) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->write32(dev, page, reg, value); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			      u64 value) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->write48(dev, page, reg, value); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			       u64 value) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&dev->reg_mutex); | ||||
| 	ret = dev->ops->write64(dev, page, reg, value); | ||||
| 	mutex_unlock(&dev->reg_mutex); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_BCM47XX | ||||
| 
 | ||||
| #include <linux/version.h> | ||||
| #include <linux/bcm47xx_nvram.h> | ||||
| #include <bcm47xx_board.h> | ||||
| static inline int b53_switch_get_reset_gpio(struct b53_device *dev) | ||||
| { | ||||
| 	enum bcm47xx_board board = bcm47xx_board_get(); | ||||
| 
 | ||||
| 	switch (board) { | ||||
| 	case BCM47XX_BOARD_LINKSYS_WRT300NV11: | ||||
| 	case BCM47XX_BOARD_LINKSYS_WRT310NV1: | ||||
| 		return 8; | ||||
| 	default: | ||||
| 		return bcm47xx_nvram_gpio_pin("robo_reset"); | ||||
| 	} | ||||
| } | ||||
| #else | ||||
| static inline int b53_switch_get_reset_gpio(struct b53_device *dev) | ||||
| { | ||||
| 	return -ENOENT; | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
							
								
								
									
										358
									
								
								drivers/net/dsa/b53/b53_regs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								drivers/net/dsa/b53/b53_regs.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,358 @@ | |||
| /*
 | ||||
|  * B53 register definitions | ||||
|  * | ||||
|  * Copyright (C) 2004 Broadcom Corporation | ||||
|  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __B53_REGS_H | ||||
| #define __B53_REGS_H | ||||
| 
 | ||||
| /* Management Port (SMP) Page offsets */ | ||||
| #define B53_CTRL_PAGE			0x00 /* Control */ | ||||
| #define B53_STAT_PAGE			0x01 /* Status */ | ||||
| #define B53_MGMT_PAGE			0x02 /* Management Mode */ | ||||
| #define B53_MIB_AC_PAGE			0x03 /* MIB Autocast */ | ||||
| #define B53_ARLCTRL_PAGE		0x04 /* ARL Control */ | ||||
| #define B53_ARLIO_PAGE			0x05 /* ARL Access */ | ||||
| #define B53_FRAMEBUF_PAGE		0x06 /* Management frame access */ | ||||
| #define B53_MEM_ACCESS_PAGE		0x08 /* Memory access */ | ||||
| 
 | ||||
| /* PHY Registers */ | ||||
| #define B53_PORT_MII_PAGE(i)		(0x10 + (i)) /* Port i MII Registers */ | ||||
| #define B53_IM_PORT_PAGE		0x18 /* Inverse MII Port (to EMAC) */ | ||||
| #define B53_ALL_PORT_PAGE		0x19 /* All ports MII (broadcast) */ | ||||
| 
 | ||||
| /* MIB registers */ | ||||
| #define B53_MIB_PAGE(i)			(0x20 + (i)) | ||||
| 
 | ||||
| /* Quality of Service (QoS) Registers */ | ||||
| #define B53_QOS_PAGE			0x30 | ||||
| 
 | ||||
| /* Port VLAN Page */ | ||||
| #define B53_PVLAN_PAGE			0x31 | ||||
| 
 | ||||
| /* VLAN Registers */ | ||||
| #define B53_VLAN_PAGE			0x34 | ||||
| 
 | ||||
| /* Jumbo Frame Registers */ | ||||
| #define B53_JUMBO_PAGE			0x40 | ||||
| 
 | ||||
| /* CFP Configuration Registers Page */ | ||||
| #define B53_CFP_PAGE			0xa1 | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * Control Page registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* Port Control Register (8 bit) */ | ||||
| #define B53_PORT_CTRL(i)		(0x00 + (i)) | ||||
| #define   PORT_CTRL_RX_DISABLE		BIT(0) | ||||
| #define   PORT_CTRL_TX_DISABLE		BIT(1) | ||||
| #define   PORT_CTRL_RX_BCST_EN		BIT(2) /* Broadcast RX (P8 only) */ | ||||
| #define   PORT_CTRL_RX_MCST_EN		BIT(3) /* Multicast RX (P8 only) */ | ||||
| #define   PORT_CTRL_RX_UCST_EN		BIT(4) /* Unicast RX (P8 only) */ | ||||
| #define	  PORT_CTRL_STP_STATE_S		5 | ||||
| #define   PORT_CTRL_STP_STATE_MASK	(0x7 << PORT_CTRL_STP_STATE_S) | ||||
| 
 | ||||
| /* SMP Control Register (8 bit) */ | ||||
| #define B53_SMP_CTRL			0x0a | ||||
| 
 | ||||
| /* Switch Mode Control Register (8 bit) */ | ||||
| #define B53_SWITCH_MODE			0x0b | ||||
| #define   SM_SW_FWD_MODE		BIT(0)	/* 1 = Managed Mode */ | ||||
| #define   SM_SW_FWD_EN			BIT(1)	/* Forwarding Enable */ | ||||
| 
 | ||||
| /* IMP Port state override register (8 bit) */ | ||||
| #define B53_PORT_OVERRIDE_CTRL		0x0e | ||||
| #define   PORT_OVERRIDE_LINK		BIT(0) | ||||
| #define   PORT_OVERRIDE_FULL_DUPLEX	BIT(1) /* 0 = Half Duplex */ | ||||
| #define   PORT_OVERRIDE_SPEED_S		2 | ||||
| #define   PORT_OVERRIDE_SPEED_10M	(0 << PORT_OVERRIDE_SPEED_S) | ||||
| #define   PORT_OVERRIDE_SPEED_100M	(1 << PORT_OVERRIDE_SPEED_S) | ||||
| #define   PORT_OVERRIDE_SPEED_1000M	(2 << PORT_OVERRIDE_SPEED_S) | ||||
| #define   PORT_OVERRIDE_RV_MII_25	BIT(4) /* BCM5325 only */ | ||||
| #define   PORT_OVERRIDE_RX_FLOW		BIT(4) | ||||
| #define   PORT_OVERRIDE_TX_FLOW		BIT(5) | ||||
| #define   PORT_OVERRIDE_SPEED_2000M	BIT(6) /* BCM5301X only, requires setting 1000M */ | ||||
| #define   PORT_OVERRIDE_EN		BIT(7) /* Use the register contents */ | ||||
| 
 | ||||
| /* Power-down mode control */ | ||||
| #define B53_PD_MODE_CTRL_25		0x0f | ||||
| 
 | ||||
| /* IP Multicast control (8 bit) */ | ||||
| #define B53_IP_MULTICAST_CTRL		0x21 | ||||
| #define  B53_IPMC_FWD_EN		BIT(1) | ||||
| #define  B53_UC_FWD_EN			BIT(6) | ||||
| #define  B53_MC_FWD_EN			BIT(7) | ||||
| 
 | ||||
| /* (16 bit) */ | ||||
| #define B53_UC_FLOOD_MASK		0x32 | ||||
| #define B53_MC_FLOOD_MASK		0x34 | ||||
| #define B53_IPMC_FLOOD_MASK		0x36 | ||||
| 
 | ||||
| /*
 | ||||
|  * Override Ports 0-7 State on devices with xMII interfaces (8 bit) | ||||
|  * | ||||
|  * For port 8 still use B53_PORT_OVERRIDE_CTRL | ||||
|  * Please note that not all ports are available on every hardware, e.g. BCM5301X | ||||
|  * don't include overriding port 6, BCM63xx also have some limitations. | ||||
|  */ | ||||
| #define B53_GMII_PORT_OVERRIDE_CTRL(i)	(0x58 + (i)) | ||||
| #define   GMII_PO_LINK			BIT(0) | ||||
| #define   GMII_PO_FULL_DUPLEX		BIT(1) /* 0 = Half Duplex */ | ||||
| #define   GMII_PO_SPEED_S		2 | ||||
| #define   GMII_PO_SPEED_10M		(0 << GMII_PO_SPEED_S) | ||||
| #define   GMII_PO_SPEED_100M		(1 << GMII_PO_SPEED_S) | ||||
| #define   GMII_PO_SPEED_1000M		(2 << GMII_PO_SPEED_S) | ||||
| #define   GMII_PO_RX_FLOW		BIT(4) | ||||
| #define   GMII_PO_TX_FLOW		BIT(5) | ||||
| #define   GMII_PO_EN			BIT(6) /* Use the register contents */ | ||||
| #define   GMII_PO_SPEED_2000M		BIT(7) /* BCM5301X only, requires setting 1000M */ | ||||
| 
 | ||||
| #define B53_RGMII_CTRL_IMP		0x60 | ||||
| #define   RGMII_CTRL_ENABLE_GMII	BIT(7) | ||||
| #define   RGMII_CTRL_TIMING_SEL		BIT(2) | ||||
| #define   RGMII_CTRL_DLL_RXC		BIT(1) | ||||
| #define   RGMII_CTRL_DLL_TXC		BIT(0) | ||||
| 
 | ||||
| #define B53_RGMII_CTRL_P(i)		(B53_RGMII_CTRL_IMP + (i)) | ||||
| 
 | ||||
| /* Software reset register (8 bit) */ | ||||
| #define B53_SOFTRESET			0x79 | ||||
| #define   SW_RST			BIT(7) | ||||
| #define   EN_SW_RST			BIT(4) | ||||
| 
 | ||||
| /* Fast Aging Control register (8 bit) */ | ||||
| #define B53_FAST_AGE_CTRL		0x88 | ||||
| #define   FAST_AGE_STATIC		BIT(0) | ||||
| #define   FAST_AGE_DYNAMIC		BIT(1) | ||||
| #define   FAST_AGE_PORT			BIT(2) | ||||
| #define   FAST_AGE_VLAN			BIT(3) | ||||
| #define   FAST_AGE_STP			BIT(4) | ||||
| #define   FAST_AGE_MC			BIT(5) | ||||
| #define   FAST_AGE_DONE			BIT(7) | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * Status Page registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* Link Status Summary Register (16bit) */ | ||||
| #define B53_LINK_STAT			0x00 | ||||
| 
 | ||||
| /* Link Status Change Register (16 bit) */ | ||||
| #define B53_LINK_STAT_CHANGE		0x02 | ||||
| 
 | ||||
| /* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */ | ||||
| #define B53_SPEED_STAT			0x04 | ||||
| #define  SPEED_PORT_FE(reg, port)	(((reg) >> (port)) & 1) | ||||
| #define  SPEED_PORT_GE(reg, port)	(((reg) >> 2 * (port)) & 3) | ||||
| #define  SPEED_STAT_10M			0 | ||||
| #define  SPEED_STAT_100M		1 | ||||
| #define  SPEED_STAT_1000M		2 | ||||
| 
 | ||||
| /* Duplex Status Summary (16 bit) */ | ||||
| #define B53_DUPLEX_STAT_FE		0x06 | ||||
| #define B53_DUPLEX_STAT_GE		0x08 | ||||
| #define B53_DUPLEX_STAT_63XX		0x0c | ||||
| 
 | ||||
| /* Revision ID register for BCM5325 */ | ||||
| #define B53_REV_ID_25			0x50 | ||||
| 
 | ||||
| /* Strap Value (48 bit) */ | ||||
| #define B53_STRAP_VALUE			0x70 | ||||
| #define   SV_GMII_CTRL_115		BIT(27) | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * Management Mode Page Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* Global Management Config Register (8 bit) */ | ||||
| #define B53_GLOBAL_CONFIG		0x00 | ||||
| #define   GC_RESET_MIB			0x01 | ||||
| #define   GC_RX_BPDU_EN			0x02 | ||||
| #define   GC_MIB_AC_HDR_EN		0x10 | ||||
| #define   GC_MIB_AC_EN			0x20 | ||||
| #define   GC_FRM_MGMT_PORT_M		0xC0 | ||||
| #define   GC_FRM_MGMT_PORT_04		0x00 | ||||
| #define   GC_FRM_MGMT_PORT_MII		0x80 | ||||
| 
 | ||||
| /* Broadcom Header control register (8 bit) */ | ||||
| #define B53_BRCM_HDR			0x03 | ||||
| #define   BRCM_HDR_P8_EN		BIT(0) /* Enable tagging on port 8 */ | ||||
| #define   BRCM_HDR_P5_EN		BIT(1) /* Enable tagging on port 5 */ | ||||
| 
 | ||||
| /* Device ID register (8 or 32 bit) */ | ||||
| #define B53_DEVICE_ID			0x30 | ||||
| 
 | ||||
| /* Revision ID register (8 bit) */ | ||||
| #define B53_REV_ID			0x40 | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * ARL Access Page Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* VLAN Table Access Register (8 bit) */ | ||||
| #define B53_VT_ACCESS			0x80 | ||||
| #define B53_VT_ACCESS_9798		0x60 /* for BCM5397/BCM5398 */ | ||||
| #define B53_VT_ACCESS_63XX		0x60 /* for BCM6328/62/68 */ | ||||
| #define   VTA_CMD_WRITE			0 | ||||
| #define   VTA_CMD_READ			1 | ||||
| #define   VTA_CMD_CLEAR			2 | ||||
| #define   VTA_START_CMD			BIT(7) | ||||
| 
 | ||||
| /* VLAN Table Index Register (16 bit) */ | ||||
| #define B53_VT_INDEX			0x81 | ||||
| #define B53_VT_INDEX_9798		0x61 | ||||
| #define B53_VT_INDEX_63XX		0x62 | ||||
| 
 | ||||
| /* VLAN Table Entry Register (32 bit) */ | ||||
| #define B53_VT_ENTRY			0x83 | ||||
| #define B53_VT_ENTRY_9798		0x63 | ||||
| #define B53_VT_ENTRY_63XX		0x64 | ||||
| #define   VTE_MEMBERS			0x1ff | ||||
| #define   VTE_UNTAG_S			9 | ||||
| #define   VTE_UNTAG			(0x1ff << 9) | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * Port VLAN Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */ | ||||
| #define B53_PVLAN_PORT_MASK(i)		((i) * 2) | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * 802.1Q Page Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* Global QoS Control (8 bit) */ | ||||
| #define B53_QOS_GLOBAL_CTL		0x00 | ||||
| 
 | ||||
| /* Enable 802.1Q for individual Ports (16 bit) */ | ||||
| #define B53_802_1P_EN			0x04 | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * VLAN Page Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* VLAN Control 0 (8 bit) */ | ||||
| #define B53_VLAN_CTRL0			0x00 | ||||
| #define   VC0_8021PF_CTRL_MASK		0x3 | ||||
| #define   VC0_8021PF_CTRL_NONE		0x0 | ||||
| #define   VC0_8021PF_CTRL_CHANGE_PRI	0x1 | ||||
| #define   VC0_8021PF_CTRL_CHANGE_VID	0x2 | ||||
| #define   VC0_8021PF_CTRL_CHANGE_BOTH	0x3 | ||||
| #define   VC0_8021QF_CTRL_MASK		0xc | ||||
| #define   VC0_8021QF_CTRL_CHANGE_PRI	0x1 | ||||
| #define   VC0_8021QF_CTRL_CHANGE_VID	0x2 | ||||
| #define   VC0_8021QF_CTRL_CHANGE_BOTH	0x3 | ||||
| #define   VC0_RESERVED_1		BIT(1) | ||||
| #define   VC0_DROP_VID_MISS		BIT(4) | ||||
| #define   VC0_VID_HASH_VID		BIT(5) | ||||
| #define   VC0_VID_CHK_EN		BIT(6)	/* Use VID,DA or VID,SA */ | ||||
| #define   VC0_VLAN_EN			BIT(7)	/* 802.1Q VLAN Enabled */ | ||||
| 
 | ||||
| /* VLAN Control 1 (8 bit) */ | ||||
| #define B53_VLAN_CTRL1			0x01 | ||||
| #define   VC1_RX_MCST_TAG_EN		BIT(1) | ||||
| #define   VC1_RX_MCST_FWD_EN		BIT(2) | ||||
| #define   VC1_RX_MCST_UNTAG_EN		BIT(3) | ||||
| 
 | ||||
| /* VLAN Control 2 (8 bit) */ | ||||
| #define B53_VLAN_CTRL2			0x02 | ||||
| 
 | ||||
| /* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */ | ||||
| #define B53_VLAN_CTRL3			0x03 | ||||
| #define B53_VLAN_CTRL3_63XX		0x04 | ||||
| #define   VC3_MAXSIZE_1532		BIT(6) /* 5325 only */ | ||||
| #define   VC3_HIGH_8BIT_EN		BIT(7) /* 5325 only */ | ||||
| 
 | ||||
| /* VLAN Control 4 (8 bit) */ | ||||
| #define B53_VLAN_CTRL4			0x05 | ||||
| #define B53_VLAN_CTRL4_25		0x04 | ||||
| #define B53_VLAN_CTRL4_63XX		0x06 | ||||
| #define   VC4_ING_VID_CHECK_S		6 | ||||
| #define   VC4_ING_VID_CHECK_MASK	(0x3 << VC4_ING_VID_CHECK_S) | ||||
| #define   VC4_ING_VID_VIO_FWD		0 /* forward, but do not learn */ | ||||
| #define   VC4_ING_VID_VIO_DROP		1 /* drop VID violations */ | ||||
| #define   VC4_NO_ING_VID_CHK		2 /* do not check */ | ||||
| #define   VC4_ING_VID_VIO_TO_IMP	3 /* redirect to MII port */ | ||||
| 
 | ||||
| /* VLAN Control 5 (8 bit) */ | ||||
| #define B53_VLAN_CTRL5			0x06 | ||||
| #define B53_VLAN_CTRL5_25		0x05 | ||||
| #define B53_VLAN_CTRL5_63XX		0x07 | ||||
| #define   VC5_VID_FFF_EN		BIT(2) | ||||
| #define   VC5_DROP_VTABLE_MISS		BIT(3) | ||||
| 
 | ||||
| /* VLAN Control 6 (8 bit) */ | ||||
| #define B53_VLAN_CTRL6			0x07 | ||||
| #define B53_VLAN_CTRL6_63XX		0x08 | ||||
| 
 | ||||
| /* VLAN Table Access Register (16 bit) */ | ||||
| #define B53_VLAN_TABLE_ACCESS_25	0x06	/* BCM5325E/5350 */ | ||||
| #define B53_VLAN_TABLE_ACCESS_65	0x08	/* BCM5365 */ | ||||
| #define   VTA_VID_LOW_MASK_25		0xf | ||||
| #define   VTA_VID_LOW_MASK_65		0xff | ||||
| #define   VTA_VID_HIGH_S_25		4 | ||||
| #define   VTA_VID_HIGH_S_65		8 | ||||
| #define   VTA_VID_HIGH_MASK_25		(0xff << VTA_VID_HIGH_S_25E) | ||||
| #define   VTA_VID_HIGH_MASK_65		(0xf << VTA_VID_HIGH_S_65) | ||||
| #define   VTA_RW_STATE			BIT(12) | ||||
| #define   VTA_RW_STATE_RD		0 | ||||
| #define   VTA_RW_STATE_WR		BIT(12) | ||||
| #define   VTA_RW_OP_EN			BIT(13) | ||||
| 
 | ||||
| /* VLAN Read/Write Registers for (16/32 bit) */ | ||||
| #define B53_VLAN_WRITE_25		0x08 | ||||
| #define B53_VLAN_WRITE_65		0x0a | ||||
| #define B53_VLAN_READ			0x0c | ||||
| #define   VA_MEMBER_MASK		0x3f | ||||
| #define   VA_UNTAG_S_25			6 | ||||
| #define   VA_UNTAG_MASK_25		0x3f | ||||
| #define   VA_UNTAG_S_65			7 | ||||
| #define   VA_UNTAG_MASK_65		0x1f | ||||
| #define   VA_VID_HIGH_S			12 | ||||
| #define   VA_VID_HIGH_MASK		(0xffff << VA_VID_HIGH_S) | ||||
| #define   VA_VALID_25			BIT(20) | ||||
| #define   VA_VALID_25_R4		BIT(24) | ||||
| #define   VA_VALID_65			BIT(14) | ||||
| 
 | ||||
| /* VLAN Port Default Tag (16 bit) */ | ||||
| #define B53_VLAN_PORT_DEF_TAG(i)	(0x10 + 2 * (i)) | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * Jumbo Frame Page Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */ | ||||
| #define B53_JUMBO_PORT_MASK		0x01 | ||||
| #define B53_JUMBO_PORT_MASK_63XX	0x04 | ||||
| #define   JPM_10_100_JUMBO_EN		BIT(24) /* GigE always enabled */ | ||||
| 
 | ||||
| /* Good Frame Max Size without 802.1Q TAG (16 bit) */ | ||||
| #define B53_JUMBO_MAX_SIZE		0x05 | ||||
| #define B53_JUMBO_MAX_SIZE_63XX		0x08 | ||||
| #define   JMS_MIN_SIZE			1518 | ||||
| #define   JMS_MAX_SIZE			9724 | ||||
| 
 | ||||
| /*************************************************************************
 | ||||
|  * CFP Configuration Page Registers | ||||
|  *************************************************************************/ | ||||
| 
 | ||||
| /* CFP Control Register with ports map (8 bit) */ | ||||
| #define B53_CFP_CTRL			0x00 | ||||
| 
 | ||||
| #endif /* !__B53_REGS_H */ | ||||
							
								
								
									
										331
									
								
								drivers/net/dsa/b53/b53_spi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								drivers/net/dsa/b53/b53_spi.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,331 @@ | |||
| /*
 | ||||
|  * B53 register access through SPI | ||||
|  * | ||||
|  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <asm/unaligned.h> | ||||
| 
 | ||||
| #include <linux/delay.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/spi/spi.h> | ||||
| #include <linux/platform_data/b53.h> | ||||
| 
 | ||||
| #include "b53_priv.h" | ||||
| 
 | ||||
| #define B53_SPI_DATA		0xf0 | ||||
| 
 | ||||
| #define B53_SPI_STATUS		0xfe | ||||
| #define B53_SPI_CMD_SPIF	BIT(7) | ||||
| #define B53_SPI_CMD_RACK	BIT(5) | ||||
| 
 | ||||
| #define B53_SPI_CMD_READ	0x00 | ||||
| #define B53_SPI_CMD_WRITE	0x01 | ||||
| #define B53_SPI_CMD_NORMAL	0x60 | ||||
| #define B53_SPI_CMD_FAST	0x10 | ||||
| 
 | ||||
| #define B53_SPI_PAGE_SELECT	0xff | ||||
| 
 | ||||
| static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val, | ||||
| 				   unsigned int len) | ||||
| { | ||||
| 	u8 txbuf[2]; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ; | ||||
| 	txbuf[1] = reg; | ||||
| 
 | ||||
| 	return spi_write_then_read(spi, txbuf, 2, val, len); | ||||
| } | ||||
| 
 | ||||
| static inline int b53_spi_clear_status(struct spi_device *spi) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	u8 rxbuf; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	for (i = 0; i < 10; i++) { | ||||
| 		ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		if (!(rxbuf & B53_SPI_CMD_SPIF)) | ||||
| 			break; | ||||
| 
 | ||||
| 		mdelay(1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (i == 10) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int b53_spi_set_page(struct spi_device *spi, u8 page) | ||||
| { | ||||
| 	u8 txbuf[3]; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; | ||||
| 	txbuf[1] = B53_SPI_PAGE_SELECT; | ||||
| 	txbuf[2] = page; | ||||
| 
 | ||||
| 	return spi_write(spi, txbuf, sizeof(txbuf)); | ||||
| } | ||||
| 
 | ||||
| static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page) | ||||
| { | ||||
| 	int ret = b53_spi_clear_status(spi); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return b53_spi_set_page(spi, page); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg) | ||||
| { | ||||
| 	u8 rxbuf; | ||||
| 	int retry_count; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_spi_read_reg(spi, reg, &rxbuf, 1); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for (retry_count = 0; retry_count < 10; retry_count++) { | ||||
| 		ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		if (rxbuf & B53_SPI_CMD_RACK) | ||||
| 			break; | ||||
| 
 | ||||
| 		mdelay(1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (retry_count == 10) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data, | ||||
| 			unsigned int len) | ||||
| { | ||||
| 	struct spi_device *spi = dev->priv; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = b53_prepare_reg_access(spi, page); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = b53_spi_prepare_reg_read(spi, reg); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return b53_spi_read_reg(spi, B53_SPI_DATA, data, len); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) | ||||
| { | ||||
| 	return b53_spi_read(dev, page, reg, val, 1); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) | ||||
| { | ||||
| 	int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2); | ||||
| 
 | ||||
| 	if (!ret) | ||||
| 		*val = le16_to_cpu(*val); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) | ||||
| { | ||||
| 	int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4); | ||||
| 
 | ||||
| 	if (!ret) | ||||
| 		*val = le32_to_cpu(*val); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	*val = 0; | ||||
| 	ret = b53_spi_read(dev, page, reg, (u8 *)val, 6); | ||||
| 	if (!ret) | ||||
| 		*val = le64_to_cpu(*val); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8); | ||||
| 
 | ||||
| 	if (!ret) | ||||
| 		*val = le64_to_cpu(*val); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) | ||||
| { | ||||
| 	struct spi_device *spi = dev->priv; | ||||
| 	int ret; | ||||
| 	u8 txbuf[3]; | ||||
| 
 | ||||
| 	ret = b53_prepare_reg_access(spi, page); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; | ||||
| 	txbuf[1] = reg; | ||||
| 	txbuf[2] = value; | ||||
| 
 | ||||
| 	return spi_write(spi, txbuf, sizeof(txbuf)); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value) | ||||
| { | ||||
| 	struct spi_device *spi = dev->priv; | ||||
| 	int ret; | ||||
| 	u8 txbuf[4]; | ||||
| 
 | ||||
| 	ret = b53_prepare_reg_access(spi, page); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; | ||||
| 	txbuf[1] = reg; | ||||
| 	put_unaligned_le16(value, &txbuf[2]); | ||||
| 
 | ||||
| 	return spi_write(spi, txbuf, sizeof(txbuf)); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value) | ||||
| { | ||||
| 	struct spi_device *spi = dev->priv; | ||||
| 	int ret; | ||||
| 	u8 txbuf[6]; | ||||
| 
 | ||||
| 	ret = b53_prepare_reg_access(spi, page); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; | ||||
| 	txbuf[1] = reg; | ||||
| 	put_unaligned_le32(value, &txbuf[2]); | ||||
| 
 | ||||
| 	return spi_write(spi, txbuf, sizeof(txbuf)); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value) | ||||
| { | ||||
| 	struct spi_device *spi = dev->priv; | ||||
| 	int ret; | ||||
| 	u8 txbuf[10]; | ||||
| 
 | ||||
| 	ret = b53_prepare_reg_access(spi, page); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; | ||||
| 	txbuf[1] = reg; | ||||
| 	put_unaligned_le64(value, &txbuf[2]); | ||||
| 
 | ||||
| 	return spi_write(spi, txbuf, sizeof(txbuf) - 2); | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value) | ||||
| { | ||||
| 	struct spi_device *spi = dev->priv; | ||||
| 	int ret; | ||||
| 	u8 txbuf[10]; | ||||
| 
 | ||||
| 	ret = b53_prepare_reg_access(spi, page); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE; | ||||
| 	txbuf[1] = reg; | ||||
| 	put_unaligned_le64(value, &txbuf[2]); | ||||
| 
 | ||||
| 	return spi_write(spi, txbuf, sizeof(txbuf)); | ||||
| } | ||||
| 
 | ||||
| static struct b53_io_ops b53_spi_ops = { | ||||
| 	.read8 = b53_spi_read8, | ||||
| 	.read16 = b53_spi_read16, | ||||
| 	.read32 = b53_spi_read32, | ||||
| 	.read48 = b53_spi_read48, | ||||
| 	.read64 = b53_spi_read64, | ||||
| 	.write8 = b53_spi_write8, | ||||
| 	.write16 = b53_spi_write16, | ||||
| 	.write32 = b53_spi_write32, | ||||
| 	.write48 = b53_spi_write48, | ||||
| 	.write64 = b53_spi_write64, | ||||
| }; | ||||
| 
 | ||||
| static int b53_spi_probe(struct spi_device *spi) | ||||
| { | ||||
| 	struct b53_device *dev; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi); | ||||
| 	if (!dev) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	if (spi->dev.platform_data) | ||||
| 		dev->pdata = spi->dev.platform_data; | ||||
| 
 | ||||
| 	ret = b53_switch_register(dev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	spi_set_drvdata(spi, dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_spi_remove(struct spi_device *spi) | ||||
| { | ||||
| 	struct b53_device *dev = spi_get_drvdata(spi); | ||||
| 
 | ||||
| 	if (dev) | ||||
| 		b53_switch_remove(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct spi_driver b53_spi_driver = { | ||||
| 	.driver = { | ||||
| 		.name	= "b53-switch", | ||||
| 		.bus	= &spi_bus_type, | ||||
| 		.owner	= THIS_MODULE, | ||||
| 	}, | ||||
| 	.probe	= b53_spi_probe, | ||||
| 	.remove	= b53_spi_remove, | ||||
| }; | ||||
| 
 | ||||
| module_spi_driver(b53_spi_driver); | ||||
| 
 | ||||
| MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); | ||||
| MODULE_DESCRIPTION("B53 SPI access driver"); | ||||
| MODULE_LICENSE("Dual BSD/GPL"); | ||||
							
								
								
									
										415
									
								
								drivers/net/dsa/b53/b53_srab.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								drivers/net/dsa/b53/b53_srab.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,415 @@ | |||
| /*
 | ||||
|  * B53 register access through Switch Register Access Bridge Registers | ||||
|  * | ||||
|  * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/platform_data/b53.h> | ||||
| 
 | ||||
| #include "b53_priv.h" | ||||
| 
 | ||||
| /* command and status register of the SRAB */ | ||||
| #define B53_SRAB_CMDSTAT		0x2c | ||||
| #define  B53_SRAB_CMDSTAT_RST		BIT(2) | ||||
| #define  B53_SRAB_CMDSTAT_WRITE		BIT(1) | ||||
| #define  B53_SRAB_CMDSTAT_GORDYN	BIT(0) | ||||
| #define  B53_SRAB_CMDSTAT_PAGE		24 | ||||
| #define  B53_SRAB_CMDSTAT_REG		16 | ||||
| 
 | ||||
| /* high order word of write data to switch registe */ | ||||
| #define B53_SRAB_WD_H			0x30 | ||||
| 
 | ||||
| /* low order word of write data to switch registe */ | ||||
| #define B53_SRAB_WD_L			0x34 | ||||
| 
 | ||||
| /* high order word of read data from switch register */ | ||||
| #define B53_SRAB_RD_H			0x38 | ||||
| 
 | ||||
| /* low order word of read data from switch register */ | ||||
| #define B53_SRAB_RD_L			0x3c | ||||
| 
 | ||||
| /* command and status register of the SRAB */ | ||||
| #define B53_SRAB_CTRLS			0x40 | ||||
| #define  B53_SRAB_CTRLS_RCAREQ		BIT(3) | ||||
| #define  B53_SRAB_CTRLS_RCAGNT		BIT(4) | ||||
| #define  B53_SRAB_CTRLS_SW_INIT_DONE	BIT(6) | ||||
| 
 | ||||
| /* the register captures interrupt pulses from the switch */ | ||||
| #define B53_SRAB_INTR			0x44 | ||||
| #define  B53_SRAB_INTR_P(x)		BIT(x) | ||||
| #define  B53_SRAB_SWITCH_PHY		BIT(8) | ||||
| #define  B53_SRAB_1588_SYNC		BIT(9) | ||||
| #define  B53_SRAB_IMP1_SLEEP_TIMER	BIT(10) | ||||
| #define  B53_SRAB_P7_SLEEP_TIMER	BIT(11) | ||||
| #define  B53_SRAB_IMP0_SLEEP_TIMER	BIT(12) | ||||
| 
 | ||||
| struct b53_srab_priv { | ||||
| 	void __iomem *regs; | ||||
| }; | ||||
| 
 | ||||
| static int b53_srab_request_grant(struct b53_device *dev) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	u32 ctrls; | ||||
| 	int i; | ||||
| 
 | ||||
| 	ctrls = readl(regs + B53_SRAB_CTRLS); | ||||
| 	ctrls |= B53_SRAB_CTRLS_RCAREQ; | ||||
| 	writel(ctrls, regs + B53_SRAB_CTRLS); | ||||
| 
 | ||||
| 	for (i = 0; i < 20; i++) { | ||||
| 		ctrls = readl(regs + B53_SRAB_CTRLS); | ||||
| 		if (ctrls & B53_SRAB_CTRLS_RCAGNT) | ||||
| 			break; | ||||
| 		usleep_range(10, 100); | ||||
| 	} | ||||
| 	if (WARN_ON(i == 5)) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void b53_srab_release_grant(struct b53_device *dev) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	u32 ctrls; | ||||
| 
 | ||||
| 	ctrls = readl(regs + B53_SRAB_CTRLS); | ||||
| 	ctrls &= ~B53_SRAB_CTRLS_RCAREQ; | ||||
| 	writel(ctrls, regs + B53_SRAB_CTRLS); | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int i; | ||||
| 	u32 cmdstat; | ||||
| 
 | ||||
| 	/* set register address */ | ||||
| 	cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) | | ||||
| 		  (reg << B53_SRAB_CMDSTAT_REG) | | ||||
| 		  B53_SRAB_CMDSTAT_GORDYN | | ||||
| 		  op; | ||||
| 	writel(cmdstat, regs + B53_SRAB_CMDSTAT); | ||||
| 
 | ||||
| 	/* check if operation completed */ | ||||
| 	for (i = 0; i < 5; ++i) { | ||||
| 		cmdstat = readl(regs + B53_SRAB_CMDSTAT); | ||||
| 		if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN)) | ||||
| 			break; | ||||
| 		usleep_range(10, 100); | ||||
| 	} | ||||
| 
 | ||||
| 	if (WARN_ON(i == 5)) | ||||
| 		return -EIO; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, 0); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	*val = readl(regs + B53_SRAB_RD_L) & 0xff; | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, 0); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	*val = readl(regs + B53_SRAB_RD_L) & 0xffff; | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, 0); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	*val = readl(regs + B53_SRAB_RD_L); | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, 0); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	*val = readl(regs + B53_SRAB_RD_L); | ||||
| 	*val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32; | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, 0); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	*val = readl(regs + B53_SRAB_RD_L); | ||||
| 	*val += (u64)readl(regs + B53_SRAB_RD_H) << 32; | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	writel(value, regs + B53_SRAB_WD_L); | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u16 value) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	writel(value, regs + B53_SRAB_WD_L); | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u32 value) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	writel(value, regs + B53_SRAB_WD_L); | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u64 value) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	writel((u32)value, regs + B53_SRAB_WD_L); | ||||
| 	writel((u16)(value >> 32), regs + B53_SRAB_WD_H); | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg, | ||||
| 			    u64 value) | ||||
| { | ||||
| 	struct b53_srab_priv *priv = dev->priv; | ||||
| 	u8 __iomem *regs = priv->regs; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	ret = b53_srab_request_grant(dev); | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	writel((u32)value, regs + B53_SRAB_WD_L); | ||||
| 	writel((u32)(value >> 32), regs + B53_SRAB_WD_H); | ||||
| 
 | ||||
| 	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE); | ||||
| 
 | ||||
| err: | ||||
| 	b53_srab_release_grant(dev); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct b53_io_ops b53_srab_ops = { | ||||
| 	.read8 = b53_srab_read8, | ||||
| 	.read16 = b53_srab_read16, | ||||
| 	.read32 = b53_srab_read32, | ||||
| 	.read48 = b53_srab_read48, | ||||
| 	.read64 = b53_srab_read64, | ||||
| 	.write8 = b53_srab_write8, | ||||
| 	.write16 = b53_srab_write16, | ||||
| 	.write32 = b53_srab_write32, | ||||
| 	.write48 = b53_srab_write48, | ||||
| 	.write64 = b53_srab_write64, | ||||
| }; | ||||
| 
 | ||||
| static int b53_srab_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct b53_srab_priv *priv; | ||||
| 	struct b53_device *dev; | ||||
| 	struct resource *r; | ||||
| 
 | ||||
| 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||||
| 	if (!priv) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	priv->regs = devm_ioremap_resource(&pdev->dev, r); | ||||
| 	if (IS_ERR(priv->regs)) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, priv); | ||||
| 	if (!dev) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, dev); | ||||
| 
 | ||||
| 	return b53_switch_register(dev); | ||||
| } | ||||
| 
 | ||||
| static int b53_srab_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct b53_device *dev = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	if (dev) | ||||
| 		b53_switch_remove(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id b53_srab_of_match[] = { | ||||
| 	{ .compatible = "brcm,bcm53010-srab" }, | ||||
| 	{ .compatible = "brcm,bcm53011-srab" }, | ||||
| 	{ .compatible = "brcm,bcm53012-srab" }, | ||||
| 	{ .compatible = "brcm,bcm53018-srab" }, | ||||
| 	{ .compatible = "brcm,bcm53019-srab" }, | ||||
| 	{ .compatible = "brcm,bcm5301x-srab" }, | ||||
| 	{ /* sentinel */ }, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver b53_srab_driver = { | ||||
| 	.probe = b53_srab_probe, | ||||
| 	.remove = b53_srab_remove, | ||||
| 	.driver = { | ||||
| 		.name = "b53-srab-switch", | ||||
| 		.of_match_table = b53_srab_of_match, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| module_platform_driver(b53_srab_driver); | ||||
| MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>"); | ||||
| MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver"); | ||||
| MODULE_LICENSE("Dual BSD/GPL"); | ||||
							
								
								
									
										33
									
								
								include/linux/platform_data/b53.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/linux/platform_data/b53.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| /*
 | ||||
|  * B53 platform data | ||||
|  * | ||||
|  * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __B53_H | ||||
| #define __B53_H | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| struct b53_platform_data { | ||||
| 	u32 chip_id; | ||||
| 	u16 enabled_ports; | ||||
| 
 | ||||
| 	/* only used by MMAP'd driver */ | ||||
| 	unsigned big_endian:1; | ||||
| 	void __iomem *regs; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
		Reference in a new issue
	
	 Florian Fainelli
						Florian Fainelli