 1246dce0b3
			
		
	
	1246dce0b3
	
	
	
		
			
			Patches created from tree: git@github.com:danielschwierzeck/u-boot-lantiq.git v2013.10..u-boot-lantiq-v2013.10-openwrt4 Signed-off-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> SVN-Revision: 40482
		
			
				
	
	
		
			388 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 42cb399df978a33539b95d668b3f973d927cb902 Mon Sep 17 00:00:00 2001
 | |
| From: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
 | |
| Date: Mon, 17 Dec 2012 23:37:57 +0100
 | |
| Subject: net: switchlib: add driver for REALTEK RTL8306
 | |
| 
 | |
| Signed-off-by: Oliver Muth <dr.o.muth@gmx.de>
 | |
| Signed-off-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
 | |
| 
 | |
| diff --git a/drivers/net/switch/Makefile b/drivers/net/switch/Makefile
 | |
| index 7400897..08c6972 100644
 | |
| --- a/drivers/net/switch/Makefile
 | |
| +++ b/drivers/net/switch/Makefile
 | |
| @@ -13,6 +13,7 @@ COBJS-$(CONFIG_SWITCH_MULTI) += switch.o
 | |
|  COBJS-$(CONFIG_SWITCH_PSB697X) += psb697x.o
 | |
|  COBJS-$(CONFIG_SWITCH_ADM6996I) += adm6996i.o
 | |
|  COBJS-$(CONFIG_SWITCH_AR8216) += ar8216.o
 | |
| +COBJS-$(CONFIG_SWITCH_RTL8306) += rtl8306.o
 | |
|  
 | |
|  COBJS	:= $(COBJS-y)
 | |
|  SRCS	:= $(COBJS:.o=.c)
 | |
| diff --git a/drivers/net/switch/rtl8306.c b/drivers/net/switch/rtl8306.c
 | |
| new file mode 100644
 | |
| index 0000000..7a6a917
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/switch/rtl8306.c
 | |
| @@ -0,0 +1,332 @@
 | |
| +/*
 | |
| + * Based on OpenWRT linux driver
 | |
| + *
 | |
| + * Copyright (C) 2011-2012 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
 | |
| + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
 | |
| + *
 | |
| + * SPDX-License-Identifier:	GPL-2.0+
 | |
| + */
 | |
| +#define DEBUG
 | |
| +#include <common.h>
 | |
| +#include <malloc.h>
 | |
| +#include <switch.h>
 | |
| +#include <miiphy.h>
 | |
| +
 | |
| +#define RTL8306_REG_PAGE		16
 | |
| +#define RTL8306_REG_PAGE_LO		(1 << 15)
 | |
| +#define RTL8306_REG_PAGE_HI		(1 << 1) /* inverted */
 | |
| +#define RTL8306_CHIPID			0x5988
 | |
| +
 | |
| +#define RTL8306_NUM_VLANS		16
 | |
| +#define RTL8306_NUM_PORTS		6
 | |
| +#define RTL8306_PORT_CPU		5
 | |
| +#define RTL8306_NUM_PAGES		4
 | |
| +#define RTL8306_NUM_REGS		32
 | |
| +
 | |
| +enum {
 | |
| +	RTL_TYPE_S,
 | |
| +	RTL_TYPE_SD,
 | |
| +	RTL_TYPE_SDM,
 | |
| +};
 | |
| +
 | |
| +struct rtl_reg {
 | |
| +	int page;
 | |
| +	int phy;
 | |
| +	int reg;
 | |
| +	int bits;
 | |
| +	int shift;
 | |
| +	int inverted;
 | |
| +};
 | |
| +
 | |
| +enum rtl_regidx {
 | |
| +	RTL_REG_CHIPID,
 | |
| +	RTL_REG_CHIPVER,
 | |
| +	RTL_REG_CHIPTYPE,
 | |
| +	RTL_REG_CPUPORT,
 | |
| +
 | |
| +	RTL_REG_EN_CPUPORT,
 | |
| +	RTL_REG_EN_TAG_OUT,
 | |
| +	RTL_REG_EN_TAG_CLR,
 | |
| +	RTL_REG_EN_TAG_IN,
 | |
| +	RTL_REG_TRAP_CPU,
 | |
| +	RTL_REG_TRUNK_PORTSEL,
 | |
| +	RTL_REG_EN_TRUNK,
 | |
| +	RTL_REG_RESET,
 | |
| +	RTL_REG_PHY_RESET,
 | |
| +	RTL_REG_CPU_LINKUP,
 | |
| +
 | |
| +	RTL_REG_VLAN_ENABLE,
 | |
| +	RTL_REG_VLAN_FILTER,
 | |
| +	RTL_REG_VLAN_TAG_ONLY,
 | |
| +	RTL_REG_VLAN_TAG_AWARE,
 | |
| +#define RTL_VLAN_ENUM(id) \
 | |
| +	RTL_REG_VLAN##id##_VID, \
 | |
| +	RTL_REG_VLAN##id##_PORTMASK
 | |
| +	RTL_VLAN_ENUM(0),
 | |
| +	RTL_VLAN_ENUM(1),
 | |
| +	RTL_VLAN_ENUM(2),
 | |
| +	RTL_VLAN_ENUM(3),
 | |
| +	RTL_VLAN_ENUM(4),
 | |
| +	RTL_VLAN_ENUM(5),
 | |
| +	RTL_VLAN_ENUM(6),
 | |
| +	RTL_VLAN_ENUM(7),
 | |
| +	RTL_VLAN_ENUM(8),
 | |
| +	RTL_VLAN_ENUM(9),
 | |
| +	RTL_VLAN_ENUM(10),
 | |
| +	RTL_VLAN_ENUM(11),
 | |
| +	RTL_VLAN_ENUM(12),
 | |
| +	RTL_VLAN_ENUM(13),
 | |
| +	RTL_VLAN_ENUM(14),
 | |
| +	RTL_VLAN_ENUM(15),
 | |
| +#define RTL_PORT_ENUM(id) \
 | |
| +	RTL_REG_PORT##id##_PVID, \
 | |
| +	RTL_REG_PORT##id##_NULL_VID_REPLACE, \
 | |
| +	RTL_REG_PORT##id##_NON_PVID_DISCARD, \
 | |
| +	RTL_REG_PORT##id##_VID_INSERT, \
 | |
| +	RTL_REG_PORT##id##_TAG_INSERT, \
 | |
| +	RTL_REG_PORT##id##_LINK, \
 | |
| +	RTL_REG_PORT##id##_SPEED, \
 | |
| +	RTL_REG_PORT##id##_NWAY, \
 | |
| +	RTL_REG_PORT##id##_NRESTART, \
 | |
| +	RTL_REG_PORT##id##_DUPLEX, \
 | |
| +	RTL_REG_PORT##id##_RXEN, \
 | |
| +	RTL_REG_PORT##id##_TXEN, \
 | |
| +	RTL_REG_PORT##id##_LRNEN
 | |
| +	RTL_PORT_ENUM(0),
 | |
| +	RTL_PORT_ENUM(1),
 | |
| +	RTL_PORT_ENUM(2),
 | |
| +	RTL_PORT_ENUM(3),
 | |
| +	RTL_PORT_ENUM(4),
 | |
| +	RTL_PORT_ENUM(5),
 | |
| +};
 | |
| +
 | |
| +static const struct rtl_reg rtl_regs[] = {
 | |
| +	[RTL_REG_CHIPID]         = { 0, 4, 30, 16,  0, 0 },
 | |
| +	[RTL_REG_CHIPVER]        = { 0, 4, 31,  8,  0, 0 },
 | |
| +	[RTL_REG_CHIPTYPE]       = { 0, 4, 31,  2,  8, 0 },
 | |
| +
 | |
| +	/* CPU port number */
 | |
| +	[RTL_REG_CPUPORT]        = { 2, 4, 21,  3,  0, 0 },
 | |
| +	/* Enable CPU port function */
 | |
| +	[RTL_REG_EN_CPUPORT]     = { 3, 2, 21,  1, 15, 1 },
 | |
| +	/* Enable CPU port tag insertion */
 | |
| +	[RTL_REG_EN_TAG_OUT]     = { 3, 2, 21,  1, 12, 0 },
 | |
| +	/* Enable CPU port tag removal */
 | |
| +	[RTL_REG_EN_TAG_CLR]     = { 3, 2, 21,  1, 11, 0 },
 | |
| +	/* Enable CPU port tag checking */
 | |
| +	[RTL_REG_EN_TAG_IN]      = { 0, 4, 21,  1,  7, 0 },
 | |
| +	[RTL_REG_EN_TRUNK]       = { 0, 0, 19,  1, 11, 1 },
 | |
| +	[RTL_REG_TRUNK_PORTSEL]  = { 0, 0, 16,  1,  6, 1 },
 | |
| +	[RTL_REG_RESET]          = { 0, 0, 16,  1, 12, 0 },
 | |
| +	[RTL_REG_PHY_RESET]	 = { 0, 0,  0,  1, 15, 0 },
 | |
| +	[RTL_REG_CPU_LINKUP]	 = { 0, 6, 22,  1, 15, 0 },
 | |
| +	[RTL_REG_TRAP_CPU]       = { 3, 2, 22,  1,  6, 0 },
 | |
| +
 | |
| +	[RTL_REG_VLAN_TAG_ONLY]  = { 0, 0, 16,  1,  8, 1 },
 | |
| +	[RTL_REG_VLAN_FILTER]    = { 0, 0, 16,  1,  9, 1 },
 | |
| +	[RTL_REG_VLAN_TAG_AWARE] = { 0, 0, 16,  1, 10, 1 },
 | |
| +	[RTL_REG_VLAN_ENABLE]    = { 0, 0, 18,  1,  8, 1 },
 | |
| +
 | |
| +#define RTL_VLAN_REGS(id, phy, page, regofs) \
 | |
| +	[RTL_REG_VLAN##id##_VID] = { page, phy, 25 + regofs, 12, 0, 0 }, \
 | |
| +	[RTL_REG_VLAN##id##_PORTMASK] = { page, phy, 24 + regofs, 6, 0, 0 }
 | |
| +	RTL_VLAN_REGS( 0, 0, 0, 0),
 | |
| +	RTL_VLAN_REGS( 1, 1, 0, 0),
 | |
| +	RTL_VLAN_REGS( 2, 2, 0, 0),
 | |
| +	RTL_VLAN_REGS( 3, 3, 0, 0),
 | |
| +	RTL_VLAN_REGS( 4, 4, 0, 0),
 | |
| +	RTL_VLAN_REGS( 5, 0, 1, 2),
 | |
| +	RTL_VLAN_REGS( 6, 1, 1, 2),
 | |
| +	RTL_VLAN_REGS( 7, 2, 1, 2),
 | |
| +	RTL_VLAN_REGS( 8, 3, 1, 2),
 | |
| +	RTL_VLAN_REGS( 9, 4, 1, 2),
 | |
| +	RTL_VLAN_REGS(10, 0, 1, 4),
 | |
| +	RTL_VLAN_REGS(11, 1, 1, 4),
 | |
| +	RTL_VLAN_REGS(12, 2, 1, 4),
 | |
| +	RTL_VLAN_REGS(13, 3, 1, 4),
 | |
| +	RTL_VLAN_REGS(14, 4, 1, 4),
 | |
| +	RTL_VLAN_REGS(15, 0, 1, 6),
 | |
| +
 | |
| +#define REG_PORT_SETTING(port, phy) \
 | |
| +	[RTL_REG_PORT##port##_SPEED] = { 0, phy, 0, 1, 13, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_NWAY] = { 0, phy, 0, 1, 12, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_NRESTART] = { 0, phy, 0, 1, 9, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_DUPLEX] = { 0, phy, 0, 1, 8, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_TXEN] = { 0, phy, 24, 1, 11, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_RXEN] = { 0, phy, 24, 1, 10, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_LRNEN] = { 0, phy, 24, 1, 9, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_LINK] = { 0, phy, 1, 1, 2, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_NULL_VID_REPLACE] = { 0, phy, 22, 1, 12, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_NON_PVID_DISCARD] = { 0, phy, 22, 1, 11, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_VID_INSERT] = { 0, phy, 22, 2, 9, 0 }, \
 | |
| +	[RTL_REG_PORT##port##_TAG_INSERT] = { 0, phy, 22, 2, 0, 0 }
 | |
| +
 | |
| +	REG_PORT_SETTING(0, 0),
 | |
| +	REG_PORT_SETTING(1, 1),
 | |
| +	REG_PORT_SETTING(2, 2),
 | |
| +	REG_PORT_SETTING(3, 3),
 | |
| +	REG_PORT_SETTING(4, 4),
 | |
| +	REG_PORT_SETTING(5, 6),
 | |
| +
 | |
| +#define REG_PORT_PVID(phy, page, regofs) \
 | |
| +	{ page, phy, 24 + regofs, 4, 12, 0 }
 | |
| +	[RTL_REG_PORT0_PVID] = REG_PORT_PVID(0, 0, 0),
 | |
| +	[RTL_REG_PORT1_PVID] = REG_PORT_PVID(1, 0, 0),
 | |
| +	[RTL_REG_PORT2_PVID] = REG_PORT_PVID(2, 0, 0),
 | |
| +	[RTL_REG_PORT3_PVID] = REG_PORT_PVID(3, 0, 0),
 | |
| +	[RTL_REG_PORT4_PVID] = REG_PORT_PVID(4, 0, 0),
 | |
| +	[RTL_REG_PORT5_PVID] = REG_PORT_PVID(0, 1, 2),
 | |
| +};
 | |
| +
 | |
| +static void rtl_set_page(struct mii_dev *bus, unsigned int page)
 | |
| +{
 | |
| +	u16 pgsel;
 | |
| +
 | |
| +	BUG_ON(page > RTL8306_NUM_PAGES);
 | |
| +
 | |
| +	pgsel = bus->read(bus, 0, MDIO_DEVAD_NONE, RTL8306_REG_PAGE);
 | |
| +	pgsel &= ~(RTL8306_REG_PAGE_LO | RTL8306_REG_PAGE_HI);
 | |
| +
 | |
| +	if (page & (1 << 0))
 | |
| +		pgsel |= RTL8306_REG_PAGE_LO;
 | |
| +
 | |
| +	if (!(page & (1 << 1))) /* bit is inverted */
 | |
| +		pgsel |= RTL8306_REG_PAGE_HI;
 | |
| +
 | |
| +	bus->write(bus, 0, MDIO_DEVAD_NONE, RTL8306_REG_PAGE, pgsel);
 | |
| +
 | |
| +}
 | |
| +
 | |
| +static __maybe_unused int rtl_w16(struct mii_dev *bus, unsigned int page, unsigned int phy,
 | |
| +			unsigned int reg, u16 val)
 | |
| +{
 | |
| +	rtl_set_page(bus, page);
 | |
| +
 | |
| +	bus->write(bus, phy, MDIO_DEVAD_NONE, reg, val);
 | |
| +	bus->read(bus, phy, MDIO_DEVAD_NONE, reg); /* flush */
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int rtl_r16(struct mii_dev *bus, unsigned int page, unsigned int phy,
 | |
| +			unsigned int reg)
 | |
| +{
 | |
| +	rtl_set_page(bus, page);
 | |
| +
 | |
| +	return bus->read(bus, phy, MDIO_DEVAD_NONE, reg);
 | |
| +}
 | |
| +
 | |
| +static u16 rtl_rmw(struct mii_dev *bus, unsigned int page, unsigned int phy,
 | |
| +			unsigned int reg, u16 mask, u16 val)
 | |
| +{
 | |
| +	u16 r;
 | |
| +
 | |
| +	rtl_set_page(bus, page);
 | |
| +
 | |
| +	r = bus->read(bus, phy, MDIO_DEVAD_NONE, reg);
 | |
| +	r &= ~mask;
 | |
| +	r |= val;
 | |
| +	bus->write(bus, phy, MDIO_DEVAD_NONE, reg, r);
 | |
| +
 | |
| +	return bus->read(bus, phy, MDIO_DEVAD_NONE, reg); /* flush */
 | |
| +}
 | |
| +
 | |
| +static int rtl_get(struct mii_dev *bus, enum rtl_regidx s)
 | |
| +{
 | |
| +	const struct rtl_reg *r = &rtl_regs[s];
 | |
| +	u16 val;
 | |
| +
 | |
| +	BUG_ON(s >= ARRAY_SIZE(rtl_regs));
 | |
| +
 | |
| +	if (r->bits == 0) /* unimplemented */
 | |
| +		return 0;
 | |
| +
 | |
| +	val = rtl_r16(bus, r->page, r->phy, r->reg);
 | |
| +
 | |
| +	if (r->shift > 0)
 | |
| +		val >>= r->shift;
 | |
| +
 | |
| +	if (r->inverted)
 | |
| +		val = ~val;
 | |
| +
 | |
| +	val &= (1 << r->bits) - 1;
 | |
| +
 | |
| +	return val;
 | |
| +}
 | |
| +
 | |
| +static __maybe_unused int rtl_set(struct mii_dev *bus, enum rtl_regidx s, unsigned int val)
 | |
| +{
 | |
| +	const struct rtl_reg *r = &rtl_regs[s];
 | |
| +	u16 mask = 0xffff;
 | |
| +
 | |
| +	BUG_ON(s >= ARRAY_SIZE(rtl_regs));
 | |
| +
 | |
| +	if (r->bits == 0) /* unimplemented */
 | |
| +		return 0;
 | |
| +
 | |
| +	if (r->shift > 0)
 | |
| +		val <<= r->shift;
 | |
| +
 | |
| +	if (r->inverted)
 | |
| +		val = ~val;
 | |
| +
 | |
| +	if (r->bits != 16) {
 | |
| +		mask = (1 << r->bits) - 1;
 | |
| +		mask <<= r->shift;
 | |
| +	}
 | |
| +
 | |
| +	val &= mask;
 | |
| +
 | |
| +	return rtl_rmw(bus, r->page, r->phy, r->reg, mask, val);
 | |
| +}
 | |
| +
 | |
| +static int rtl8306_probe(struct switch_device *dev)
 | |
| +{
 | |
| +	struct mii_dev *bus = dev->bus;
 | |
| +	unsigned int chipid, chipver, chiptype;
 | |
| +
 | |
| +	chipid = rtl_get(bus, RTL_REG_CHIPID);
 | |
| +	chipver = rtl_get(bus, RTL_REG_CHIPVER);
 | |
| +	chiptype = rtl_get(bus, RTL_REG_CHIPTYPE);
 | |
| +
 | |
| +	debug("%s: chipid %x, chipver %x, chiptype %x\n",
 | |
| +		__func__, chipid, chipver, chiptype);
 | |
| +
 | |
| +	if (chipid == RTL8306_CHIPID)
 | |
| +		return 0;
 | |
| +
 | |
| +	return 1;
 | |
| +}
 | |
| +
 | |
| +static void rtl8306_setup(struct switch_device *dev)
 | |
| +{
 | |
| +	struct mii_dev *bus = dev->bus;
 | |
| +
 | |
| +	/* initialize cpu port settings */
 | |
| +	rtl_set(bus, RTL_REG_CPUPORT, dev->cpu_port);
 | |
| +	rtl_set(bus, RTL_REG_EN_CPUPORT, 1);
 | |
| +
 | |
| +	/* enable phy 5 link status */
 | |
| +	rtl_set(bus, RTL_REG_CPU_LINKUP, 1);
 | |
| +//	rtl_set(bus, RTL_REG_PORT5_TXEN, 1);
 | |
| +//	rtl_set(bus, RTL_REG_PORT5_RXEN, 1);
 | |
| +//	rtl_set(bus, RTL_REG_PORT5_LRNEN, 1);
 | |
| +#ifdef DEBUG
 | |
| + debug("%s: CPU link up: %i\n",
 | |
| +		__func__, rtl_get(bus, RTL_REG_PORT5_LINK));
 | |
| +#endif
 | |
| +
 | |
| +}
 | |
| +
 | |
| +static struct switch_driver rtl8306_drv = {
 | |
| +	.name = "rtl8306",
 | |
| +};
 | |
| +
 | |
| +void switch_rtl8306_init(void)
 | |
| +{
 | |
| +	/* For archs with manual relocation */
 | |
| +	rtl8306_drv.probe = rtl8306_probe;
 | |
| +	rtl8306_drv.setup = rtl8306_setup;
 | |
| +
 | |
| +	switch_driver_register(&rtl8306_drv);
 | |
| +}
 | |
| diff --git a/drivers/net/switch/switch.c b/drivers/net/switch/switch.c
 | |
| index 3e34a7f..2e1c668 100644
 | |
| --- a/drivers/net/switch/switch.c
 | |
| +++ b/drivers/net/switch/switch.c
 | |
| @@ -26,6 +26,9 @@ void switch_init(void)
 | |
|  #if defined(CONFIG_SWITCH_AR8216)
 | |
|  	switch_ar8216_init();
 | |
|  #endif
 | |
| +#if defined(CONFIG_SWITCH_RTL8306)
 | |
| +	switch_rtl8306_init();
 | |
| +#endif
 | |
|  
 | |
|  	board_switch_init();
 | |
|  }
 | |
| diff --git a/include/switch.h b/include/switch.h
 | |
| index ae7b123..927b1d2 100644
 | |
| --- a/include/switch.h
 | |
| +++ b/include/switch.h
 | |
| @@ -100,6 +100,7 @@ static inline void switch_setup(struct switch_device *dev)
 | |
|  extern void switch_psb697x_init(void);
 | |
|  extern void switch_adm6996i_init(void);
 | |
|  extern void switch_ar8216_init(void);
 | |
| +extern void switch_rtl8306_init(void);
 | |
|  
 | |
|  #endif /* __SWITCH_H */
 | |
|  
 | |
| -- 
 | |
| 1.8.3.2
 | |
| 
 |