Message ID | 1279719442-10174-2-git-send-email-vapier@gentoo.org |
---|---|
State | Superseded, archived |
Delegated to: | David Miller |
Headers | show |
On Wed, Jul 21, 2010 at 09:37:22AM -0400, Mike Frysinger wrote: > From: Graf Yang <graf.yang@analog.com> Hurray for the first non-Marvell switch chip support in net/dsa! > +#define pr_fmt(fmt) "ksz8893m: " fmt This has no users. > +static int ksz8893m_setup(struct dsa_switch *ds) > +{ > + int i; > + int ret; > + > + ret = ksz8893m_switch_reset(ds); > + if (ret < 0) > + return ret; It's pretty ugly that the mdiobus is passed in via the normal means, but a reference to the SPI bus to use is just stuffed into some global variable. Can you not access all registers via MII? (If not, struct dsa_chip_data will need go be extended with another struct device pointer that we can use to find the spi bus with.) > +static int ksz8893m_port_to_phy_addr(int port) > +{ > + if (port >= 1 && port <= KSZ8893M_PORT_NUM) > + return port; > + > + pr_warning("use default phy addr 3\n"); > + return 3; Does this ever happen? You should just be able to return -1 here, IMHO. > +static int __devinit spi_switch_probe(struct spi_device *spi) > +{ > + if (sw.dev) { > + pr_err("only one instance supported at a time\n"); > + return 1; > + } > + memset(&sw.xfer, 0, sizeof(sw.xfer)); > + sw.dev = spi; > + return 0; > +} > + > +static int __devexit spi_switch_remove(struct spi_device *spi) > +{ > + sw.dev = NULL; > + return 0; > +} > + > +static struct spi_driver spi_switch_driver = { > + .driver = { > + .name = "ksz8893m", > + .bus = &spi_bus_type, > + .owner = THIS_MODULE, > + }, > + .probe = spi_switch_probe, > + .remove = __devexit_p(spi_switch_remove), > +}; I'm not entirely happy with this. Then again, there isn't really a clean way to deal with devices that have multiple different host interfaces as far as I know.. > +#define KSZ8893M_PORT_NUM 3 > +#define KSZ8893M_CPU_PORT 3 > + > +#define DEFAULT_PORT_VID 0 > + > +#define SPI_READ 3 > +#define SPI_WRITE 2 > > [snip] Something tells me that half the defines (and register index definitions) in this file aren't used. Also: > +/* MII Basic Control */ > +#define SOFT_RESET 0x8000 > +#define LOOPBACK 0x4000 > +#define FORCE_100 0x2000 > +#define AN_ENABLE 0x1000 > +#define POWER_DOWN 0x0800 > +#define ISOLATE 0x0400 > +#define RESTART_AN 0x0200 > +#define FORCE_FULL_DUPLEX 0x0100 > +#define COLLISION_TEST 0x0080 This part of the MII register is standard, and there are BMCR_* definitions for this in mii.h, no need to duplicate them. > +#define Force_MDI 0x0010 And please don't use mixed case. > +#define FAMILY_ID 0x88 > +#define START_SWITCH 0x01 > +#define TAG_INSERTION 0x04 > +#define SPECIAL_TPID_MODE 0x01 It would be nice to mention what registers these bitfields are part of. > +enum switch_phy_reg { > + /* Global Registers: 0-15 */ > + ChipID0 = 0, > + ChipID1_StartSwitch, > + GlobalControl0, And here, just define the registers you need only. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Le mercredi 21 juillet 2010 à 17:16 +0200, Lennert Buytenhek a écrit : > > +#define pr_fmt(fmt) "ksz8893m: " fmt > > This has no users. > Dont tell this to Joe Perches ;) It is used by all pr_err() friends #define pr_emerg(fmt, ...) \ printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) #define pr_alert(fmt, ...) \ printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) #define pr_crit(fmt, ...) \ printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) #define pr_err(fmt, ...) \ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) #define pr_warning(fmt, ...) \ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) #define pr_warn pr_warning #define pr_notice(fmt, ...) \ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) #define pr_cont(fmt, ...) \ printk(KERN_CONT fmt, ##__VA_ARGS__) -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Jul 21, 2010 at 05:33:13PM +0200, Eric Dumazet wrote: > > > +#define pr_fmt(fmt) "ksz8893m: " fmt > > > > This has no users. > > Dont tell this to Joe Perches ;) > > It is used by all pr_err() friends Oops, my bad! -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Jul 21, 2010 at 11:16, Lennert Buytenhek wrote: > On Wed, Jul 21, 2010 at 09:37:22AM -0400, Mike Frysinger wrote: >> +static int ksz8893m_setup(struct dsa_switch *ds) >> +{ >> + int i; >> + int ret; >> + >> + ret = ksz8893m_switch_reset(ds); >> + if (ret < 0) >> + return ret; > > It's pretty ugly that the mdiobus is passed in via the normal means, > but a reference to the SPI bus to use is just stuffed into some global > variable. > > Can you not access all registers via MII? it depends on the host mdio bus. if it supports the semi-standard behavior of toggling the OP field of MDIO frames, then yes, you can do it via MII. but i dont think the current mdio framework in the kernel keeps track of that functionality, so there isnt a way in the driver to say "is this possible, else fall back to SPI". certainly the part that was used to develop this driver does not support this behavior thus SPI is the only way of accessing the extended registers. i guess the driver could be extended so that people could pick which mode they want to program the registers via platform resources, but we have no way of testing that, so i say let the person who actually can use & wants that functionality implement it. > (If not, struct dsa_chip_data will need go be extended with another > struct device pointer that we can use to find the spi bus with.) if the framework supports, i can convert the driver to it, but i'm not sure we have the time atm to tackle reworking common frameworks. >> +static int ksz8893m_port_to_phy_addr(int port) >> +{ >> + if (port >= 1 && port <= KSZ8893M_PORT_NUM) >> + return port; >> + >> + pr_warning("use default phy addr 3\n"); >> + return 3; > > Does this ever happen? You should just be able to return -1 here, IMHO. i dont recall seeing a warning, but presumably if it it did occur, something else needs fixing. so -1 is OK. >> +/* MII Basic Control */ >> +#define SOFT_RESET 0x8000 >> +#define LOOPBACK 0x4000 >> +#define FORCE_100 0x2000 >> +#define AN_ENABLE 0x1000 >> +#define POWER_DOWN 0x0800 >> +#define ISOLATE 0x0400 >> +#define RESTART_AN 0x0200 >> +#define FORCE_FULL_DUPLEX 0x0100 >> +#define COLLISION_TEST 0x0080 > > This part of the MII register is standard, and there are BMCR_* > definitions for this in mii.h, no need to duplicate them. most are copies of the MII headers, so i just converted them all and punted the rest >> +#define FAMILY_ID 0x88 >> +#define START_SWITCH 0x01 >> +#define TAG_INSERTION 0x04 >> +#define SPECIAL_TPID_MODE 0x01 > > It would be nice to mention what registers these bitfields are part of. done >> +enum switch_phy_reg { >> + /* Global Registers: 0-15 */ >> + ChipID0 = 0, >> + ChipID1_StartSwitch, >> + GlobalControl0, > > And here, just define the registers you need only. i'm not sure that's necessary ... having the complete register layout is useful for people who want to extend in the future -mike -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Lennert Buytenhek <buytenh@wantstofly.org> Date: Wed, 21 Jul 2010 17:16:08 +0200 > On Wed, Jul 21, 2010 at 09:37:22AM -0400, Mike Frysinger wrote: > >> +#define pr_fmt(fmt) "ksz8893m: " fmt > > This has no users. egrep pr_fmt include/linux/kernel.h It feeds into all of the various kernel logging interfaces. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Jul 21, 2010 at 6:05 PM, Mike Frysinger <vapier.adi@gmail.com> wrote: > On Wed, Jul 21, 2010 at 11:16, Lennert Buytenhek wrote: >> On Wed, Jul 21, 2010 at 09:37:22AM -0400, Mike Frysinger wrote: >>> +static int ksz8893m_setup(struct dsa_switch *ds) >>> +{ >>> + int i; >>> + int ret; >>> + >>> + ret = ksz8893m_switch_reset(ds); >>> + if (ret < 0) >>> + return ret; >> >> It's pretty ugly that the mdiobus is passed in via the normal means, >> but a reference to the SPI bus to use is just stuffed into some global >> variable. >> >> Can you not access all registers via MII? > > it depends on the host mdio bus. if it supports the semi-standard > behavior of toggling the OP field of MDIO frames, then yes, you can do > it via MII. but i dont think the current mdio framework in the kernel > keeps track of that functionality, so there isnt a way in the driver > to say "is this possible, else fall back to SPI". > Are you referring to SMI ? > certainly the part that was used to develop this driver does not > support this behavior thus SPI is the only way of accessing the > extended registers. i guess the driver could be extended so that > people could pick which mode they want to program the registers via > platform resources, but we have no way of testing that, so i say let > the person who actually can use & wants that functionality implement > it. > >> (If not, struct dsa_chip_data will need go be extended with another >> struct device pointer that we can use to find the spi bus with.) > > if the framework supports, i can convert the driver to it, but i'm not > sure we have the time atm to tackle reworking common frameworks. > If someone tackles this, it would be nice that they bear in mind that phylib's drivers also need spi/smi/i2c register access. >>> +static int ksz8893m_port_to_phy_addr(int port) >>> +{ >>> + if (port >= 1 && port <= KSZ8893M_PORT_NUM) >>> + return port; >>> + >>> + pr_warning("use default phy addr 3\n"); >>> + return 3; >> >> Does this ever happen? You should just be able to return -1 here, IMHO. > > i dont recall seeing a warning, but presumably if it it did occur, > something else needs fixing. so -1 is OK. > I would remove this.
On Wed, Jul 21, 2010 at 16:31, Karl Beldan wrote: > On Wed, Jul 21, 2010 at 6:05 PM, Mike Frysinger wrote: >> On Wed, Jul 21, 2010 at 11:16, Lennert Buytenhek wrote: >>> On Wed, Jul 21, 2010 at 09:37:22AM -0400, Mike Frysinger wrote: >>>> +static int ksz8893m_setup(struct dsa_switch *ds) >>>> +{ >>>> + int i; >>>> + int ret; >>>> + >>>> + ret = ksz8893m_switch_reset(ds); >>>> + if (ret < 0) >>>> + return ret; >>> >>> It's pretty ugly that the mdiobus is passed in via the normal means, >>> but a reference to the SPI bus to use is just stuffed into some global >>> variable. >>> >>> Can you not access all registers via MII? >> >> it depends on the host mdio bus. if it supports the semi-standard >> behavior of toggling the OP field of MDIO frames, then yes, you can do >> it via MII. but i dont think the current mdio framework in the kernel >> keeps track of that functionality, so there isnt a way in the driver >> to say "is this possible, else fall back to SPI". > > Are you referring to SMI ? that is the term the ksz datasheet uses (Serial Management Interface), but i'm not aware of it being a standardized term. even the ksz datasheet admits that its behavior is a bit non-standard. the Blackfin MAC doesnt support it so we have no way of testing the it as that is the MAC on the board with the ksz switch. -mike -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Jul 21, 2010 at 11:00 PM, Mike Frysinger <vapier.adi@gmail.com> wrote: > On Wed, Jul 21, 2010 at 16:31, Karl Beldan wrote: >> On Wed, Jul 21, 2010 at 6:05 PM, Mike Frysinger wrote: >>> On Wed, Jul 21, 2010 at 11:16, Lennert Buytenhek wrote: >>>> On Wed, Jul 21, 2010 at 09:37:22AM -0400, Mike Frysinger wrote: >>>>> +static int ksz8893m_setup(struct dsa_switch *ds) >>>>> +{ >>>>> + int i; >>>>> + int ret; >>>>> + >>>>> + ret = ksz8893m_switch_reset(ds); >>>>> + if (ret < 0) >>>>> + return ret; >>>> >>>> It's pretty ugly that the mdiobus is passed in via the normal means, >>>> but a reference to the SPI bus to use is just stuffed into some global >>>> variable. >>>> >>>> Can you not access all registers via MII? >>> >>> it depends on the host mdio bus. if it supports the semi-standard >>> behavior of toggling the OP field of MDIO frames, then yes, you can do >>> it via MII. but i dont think the current mdio framework in the kernel >>> keeps track of that functionality, so there isnt a way in the driver >>> to say "is this possible, else fall back to SPI". >> >> Are you referring to SMI ? > > that is the term the ksz datasheet uses (Serial Management Interface), > but i'm not aware of it being a standardized term. even the ksz > datasheet admits that its behavior is a bit non-standard. the > Blackfin MAC doesnt support it so we have no way of testing the it as > that is the MAC on the board with the ksz switch. > Can't either, we don't even have mdio, only spi. Speaking of which, maybe you can replace this while you are at it : --- net/dsa/slave.c 2010-05-22 09:58:34.000000000 +0200 void dsa_slave_mii_bus_init(struct dsa_switch *ds) { ds->slave_mii_bus->priv = (void *)ds; - ds->slave_mii_bus->name = "dsa slave smi";
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index ee8d705..4a87436 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -60,4 +60,11 @@ config NET_DSA_MV88E6123_61_65 This enables support for the Marvell 88E6123/6161/6165 ethernet switch chips. +config NET_DSA_KSZ8893M + bool "MICREL KSZ8893MQL/BL ethernet switch chip support" + select NET_DSA_TAG_STPID + ---help--- + This enables support for the Micrel KSZ8893MQL/BL + ethernet switch chips. + endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 4881577..c4295e3 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_MV88E6123_61_65) += mv88e6123_61_65.o obj-$(CONFIG_NET_DSA_MV88E6131) += mv88e6131.o +obj-$(CONFIG_NET_DSA_KSZ8893M) += ksz8893m.o # the core obj-$(CONFIG_NET_DSA) += dsa.o slave.o diff --git a/net/dsa/ksz8893m.c b/net/dsa/ksz8893m.c new file mode 100644 index 0000000..98cce04 --- /dev/null +++ b/net/dsa/ksz8893m.c @@ -0,0 +1,359 @@ +/* + * Integrated 3-Port 10/100 Managed Switch with PHYs + * + * - KSZ8893M support + * + * Copyright 2008-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#define pr_fmt(fmt) "ksz8893m: " fmt + +#include <linux/list.h> +#include <linux/netdevice.h> +#include <linux/phy.h> +#include <linux/spi/spi.h> +#include "dsa_priv.h" +#include "ksz8893m.h" + +#define BUF_LEN 6 + +static struct _spi_switch { + struct spi_transfer xfer; + struct spi_device *dev; +} sw; + +static int ksz8893m_read(unsigned char *din, unsigned char reg, int len) +{ + int i, ret; + struct spi_message message; + unsigned char dout[BUF_LEN]; + struct spi_transfer *t = &sw.xfer; + + t->len = len; + t->tx_buf = dout; + t->rx_buf = din; + dout[0] = SPI_READ; + dout[1] = reg; + for (i = 2; i < len; i++) + dout[i] = 0; + + spi_message_init(&message); + spi_message_add_tail(t, &message); + ret = spi_sync(sw.dev, &message); + if (!ret) + return message.status; + + pr_err("read reg%d failed, ret=%d\n", reg, ret); + return ret; +} + +static int ksz8893m_write(unsigned char *dout, unsigned char reg, int len) +{ + int ret; + struct spi_message message; + unsigned char din[BUF_LEN]; + struct spi_transfer *t = &sw.xfer; + + t->len = len; + t->tx_buf = dout; + t->rx_buf = din; + dout[0] = SPI_WRITE; + dout[1] = reg; + + spi_message_init(&message); + spi_message_add_tail(t, &message); + ret = spi_sync(sw.dev, &message); + if (!ret) + return message.status; + + pr_err("write reg%d failed, ret=%d\n", reg, ret); + return ret; +} + +static char *ksz8893m_probe(struct mii_bus *bus, int sw_addr) +{ + int ret, phyid_low, phyid_high; + unsigned char din[BUF_LEN]; + + phyid_high = mdiobus_read(bus, KSZ8893M_CPU_PORT, MII_PHYSID1); + phyid_low = mdiobus_read(bus, KSZ8893M_CPU_PORT, MII_PHYSID2); + if (phyid_high != PHYID_HIGH || phyid_low != PHYID_LOW) + return NULL; + + ret = ksz8893m_read(din, ChipID0, 3); + + if (!ret && FAMILY_ID == din[2]) + return "KSZ8893M"; + + return NULL; +} + +static int ksz8893m_switch_reset(struct dsa_switch *ds) +{ + return 0; +} + +static int ksz8893m_setup_global(struct dsa_switch *ds) +{ + int ret; + unsigned char dout[BUF_LEN]; + unsigned char din[BUF_LEN]; + + /* Set VLAN VID of port1 */ + ret = ksz8893m_read(din, Port1Control3, 3); + if (ret) + return ret; + din[2] &= 0xf0; + dout[2] = (DEFAULT_PORT_VID & 0xfff) >> 8 | din[2]; + dout[3] = DEFAULT_PORT_VID & 0xff; + ret = ksz8893m_write(dout, Port1Control3, 4); + if (ret) + return ret; + + /* Set VLAN VID of port2 */ + ret = ksz8893m_read(din, Port2Control3, 3); + if (ret) + return ret; + din[2] &= 0xf0; + dout[2] = (DEFAULT_PORT_VID & 0xfff) >> 8 | din[2]; + dout[3] = DEFAULT_PORT_VID & 0xff; + ret = ksz8893m_write(dout, Port2Control3, 4); + if (ret) + return ret; + + /* Set VLAN VID of port3 */ + ret = ksz8893m_read(din, Port3Control3, 3); + if (ret) + return ret; + din[2] &= 0xf0; + dout[2] = (DEFAULT_PORT_VID & 0xfff) >> 8 | din[2]; + dout[3] = DEFAULT_PORT_VID & 0xff; + ret = ksz8893m_write(dout, Port3Control3, 4); + if (ret) + return ret; + + /* Insert VLAN tag that egress Port3 */ + ret = ksz8893m_read(din, Port3Control0, 3); + if (ret) + return ret; + dout[2] = TAG_INSERTION | din[2]; + ret = ksz8893m_write(dout, Port3Control0, 3); + if (ret) + return ret; + + /* Enable STPID Mode */ + ret = ksz8893m_read(din, GlobalControl9, 3); + if (ret) + return ret; + dout[2] = SPECIAL_TPID_MODE | din[2]; + ret = ksz8893m_write(dout, GlobalControl9, 3); + if (ret) + return ret; + + /* Start switch */ + dout[2] = START_SWITCH; + ret = ksz8893m_write(dout, ChipID1_StartSwitch, 3); + if (ret) + return ret; + + return 0; +} + +static int ksz8893m_setup_port(struct dsa_switch *ds, int p) +{ + int val, ret; + val = mdiobus_read(ds->master_mii_bus, p, MII_BMCR); + if (val < 0) + return val; + val |= AN_ENABLE | FORCE_100 | FORCE_FULL_DUPLEX; + val &= ~(POWER_DOWN | DISABLE_MDIX | DIS_FAR_END_FAULT |\ + DISABLE_TRANSMIT | DISABLE_LED); + ret = mdiobus_write(ds->master_mii_bus, p, MII_BMCR, val); + if (ret < 0) + return ret; + + val = mdiobus_read(ds->master_mii_bus, p, MII_ADVERTISE); + if (val < 0) + return val; + val |= ADV_10_HALF | ADV_10_FULL | ADV_100_HALF | ADV_100_FULL; + ret = mdiobus_write(ds->master_mii_bus, p, MII_ADVERTISE, val); + if (ret < 0) + return ret; + return 0; +} + +static int ksz8893m_setup(struct dsa_switch *ds) +{ + int i; + int ret; + + ret = ksz8893m_switch_reset(ds); + if (ret < 0) + return ret; + + ret = ksz8893m_setup_global(ds); + if (ret < 0) + return ret; + + for (i = 1; i < KSZ8893M_PORT_NUM; i++) { + ret = ksz8893m_setup_port(ds, i); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ksz8893m_set_addr(struct dsa_switch *ds, u8 *addr) +{ + return 0; +} + +static int ksz8893m_port_to_phy_addr(int port) +{ + if (port >= 1 && port <= KSZ8893M_PORT_NUM) + return port; + + pr_warning("use default phy addr 3\n"); + return 3; +} + +static int +ksz8893m_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + int phy_addr = ksz8893m_port_to_phy_addr(port); + return mdiobus_read(ds->master_mii_bus, phy_addr, regnum); +} + +static int +ksz8893m_phy_write(struct dsa_switch *ds, + int port, int regnum, u16 val) +{ + int phy_addr = ksz8893m_port_to_phy_addr(port); + return mdiobus_write(ds->master_mii_bus, phy_addr, regnum, val); +} + +static void ksz8893m_poll_link(struct dsa_switch *ds) +{ + int i; + + for (i = 1; i < KSZ8893M_PORT_NUM; i++) { + struct net_device *dev; + int val; + int link; + int speed; + int duplex; + int anc; + + dev = ds->ports[i]; + if (dev == NULL) + continue; + + link = 0; + if (dev->flags & IFF_UP) { + val = mdiobus_read(ds->master_mii_bus, i, MII_BMSR); + if (val < 0) + continue; + + link = !!(val & LINK_STATUS); + anc = !!(val & AN_COMPLETE); + } + + if (!link) { + if (netif_carrier_ok(dev)) { + printk(KERN_INFO "%s: link down\n", dev->name); + netif_carrier_off(dev); + } + continue; + } + + speed = 10; + duplex = 0; + val = mdiobus_read(ds->master_mii_bus, i, MII_BMSR); + if (val < 0) + continue; + val &= HALF_10_CAPABLE | FULL_10_CAPABLE |\ + HALF_100_CAPABLE | FULL_100_CAPABLE; + if (val & FULL_100_CAPABLE) { + speed = 100; + duplex = 1; + } else if (val & HALF_100_CAPABLE) { + speed = 100; + duplex = 0; + } else if (val & FULL_10_CAPABLE) { + speed = 10; + duplex = 1; + } + + if (!netif_carrier_ok(dev)) { + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex\n", + dev->name, speed, duplex ? "full" : "half"); + netif_carrier_on(dev); + } + } +} + +static int __devinit spi_switch_probe(struct spi_device *spi) +{ + if (sw.dev) { + pr_err("only one instance supported at a time\n"); + return 1; + } + memset(&sw.xfer, 0, sizeof(sw.xfer)); + sw.dev = spi; + return 0; +} + +static int __devexit spi_switch_remove(struct spi_device *spi) +{ + sw.dev = NULL; + return 0; +} + +static struct spi_driver spi_switch_driver = { + .driver = { + .name = "ksz8893m", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = spi_switch_probe, + .remove = __devexit_p(spi_switch_remove), +}; + +static struct dsa_switch_driver ksz8893m_switch_driver = { + .tag_protocol = __constant_htons(ETH_P_STPID), + .probe = ksz8893m_probe, + .setup = ksz8893m_setup, + .set_addr = ksz8893m_set_addr, + .phy_read = ksz8893m_phy_read, + .phy_write = ksz8893m_phy_write, + .poll_link = ksz8893m_poll_link, +}; + +static int __init ksz8893m_init(void) +{ + int ret; + + ret = spi_register_driver(&spi_switch_driver); + if (ret) { + pr_err("can't register driver\n"); + return ret; + } + + register_switch_driver(&ksz8893m_switch_driver); + return 0; +} +module_init(ksz8893m_init); + +static void __exit ksz8893m_cleanup(void) +{ + spi_unregister_driver(&spi_switch_driver); + unregister_switch_driver(&ksz8893m_switch_driver); +} +module_exit(ksz8893m_cleanup); + +MODULE_AUTHOR("Graf Yang <graf.yang@analog.com>"); +MODULE_DESCRIPTION("KSZ8893M driver for DSA"); +MODULE_LICENSE("GPL"); diff --git a/net/dsa/ksz8893m.h b/net/dsa/ksz8893m.h new file mode 100644 index 0000000..84f44e9 --- /dev/null +++ b/net/dsa/ksz8893m.h @@ -0,0 +1,223 @@ +/* + * Integrated 3-Port 10/100 Managed Switch with PHYs + * + * - KSZ8893M support + * + * Copyright 2008-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __KSZ8893M_H__ +#define __KSZ8893M_H__ + +#include <linux/netdevice.h> + +#define KSZ8893M_PORT_NUM 3 +#define KSZ8893M_CPU_PORT 3 + +#define DEFAULT_PORT_VID 0 + +#define SPI_READ 3 +#define SPI_WRITE 2 + +/* PHYID High */ +#define PHYID_HIGH 0x22 +/* PHYID Low */ +#define PHYID_LOW 0x1430 + +/* MII Basic Control */ +#define SOFT_RESET 0x8000 +#define LOOPBACK 0x4000 +#define FORCE_100 0x2000 +#define AN_ENABLE 0x1000 +#define POWER_DOWN 0x0800 +#define ISOLATE 0x0400 +#define RESTART_AN 0x0200 +#define FORCE_FULL_DUPLEX 0x0100 +#define COLLISION_TEST 0x0080 +/* Bit Reserved */ +#define HP_MDIX 0x0020 +#define Force_MDI 0x0010 +#define DISABLE_MDIX 0x0008 +#define DIS_FAR_END_FAULT 0x0004 +#define DISABLE_TRANSMIT 0x0002 +#define DISABLE_LED 0x0001 + +/* MII Basic Status */ +#define T4_CAPABLE 0x8000 +#define FULL_100_CAPABLE 0x4000 +#define HALF_100_CAPABLE 0x2000 +#define FULL_10_CAPABLE 0x1000 +#define HALF_10_CAPABLE 0x0800 +/* 4 Bits Reserved */ +#define PREAMBLE_SUPPRESS 0x0040 +#define AN_COMPLETE 0x0020 +#define FAR_END_FAULT 0x0010 +#define AN_CAPABLE 0x0008 +#define LINK_STATUS 0x0004 +#define JABBER_TEST 0x0002 +#define EXTENDED_CAPABLE 0x0001 + +/* Auto-Negotiation Advertisement Ability */ +#define NEXT_PAGE 0x8000 +/* Bit Reserved */ +#define REMOTE_FAULT 0x2000 +/* 2 Bits Reserved */ +#define PAUSE 0x0400 +/* Bit Reserved */ +#define ADV_100_FULL 0x0100 +#define ADV_100_HALF 0x0080 +#define ADV_10_FULL 0x0040 +#define ADV_10_HALF 0x0020 +#define SELECTOR_FIELD 0x001F + +/* Auto-Negotiation Link Partner Ability */ +#define NEXT_PAGE 0x8000 +#define LP_ACK 0x4000 +#define REMOTE_FAULT 0x2000 +/* 2 Bits Reserved */ +#define PAUSE 0x0400 +/* Bit Reserved */ +#define ADV_100_FULL 0x0100 +#define ADV_100_HALF 0x0080 +#define ADV_10_FULL 0x0040 +#define ADV_10_HALF 0x0020 +/* 5 Bits Reserved */ + +/* LinkMD Control/Status */ +#define VCT_ENABLE 0x8000 +#define VCT_RESULT 0x6000 +#define VCT_10M_SHORT 0x1000 +/* 3 Bits Reserved */ +#define VCT_FAULT_COUNT 0x01FF + +/* PHY Special Control/Status */ +/* 10 Bits Reserved */ +#define POLRVS 0x0020 +#define MDI_X_STATUS 0x0010 +#define FORCE_LNK 0x0008 +#define PWRSAVE 0x0004 +#define REMOTE_LOOPBACK 0x0002 +/* Bit Reserved */ + + +#define FAMILY_ID 0x88 +#define START_SWITCH 0x01 +#define TAG_INSERTION 0x04 +#define SPECIAL_TPID_MODE 0x01 + + +enum switch_phy_reg { + /* Global Registers: 0-15 */ + ChipID0 = 0, + ChipID1_StartSwitch, + GlobalControl0, + GlobalControl1, + GlobalControl2, /* 4 */ + GlobalControl3, + GlobalControl4, + GlobalControl5, + GlobalControl6, /* 8 */ + GlobalControl7, + GlobalControl8, + GlobalControl9, + GlobalControl10, /* 12 */ + GlobalControl11, + GlobalControl12, + GlobalControl13, + /* Port Registers: 16-95 */ + Port1Control0 = 16, + Port1Control1, + Port1Control2, + Port1Control3, + Port1Control4, /* 20 */ + Port1Control5, + Port1Control6, + Port1Control7, + Port1Control8, /* 24 */ + Port1Control9, + Port1PHYSpecialControl_Status, + Port1LinkMDResult, + Port1Control12, /* 28 */ + Port1Control13, + Port1Status0, + Port1Status1, + Port2Control0, /* 32 */ + Port2Control1, + Port2Control2, + Port2Control3, + Port2Control4, /* 36 */ + Port2Control5, + Port2Control6, + Port2Control7, + Port2Control8, /* 40 */ + Port2Control9, + Port2PHYSpecialControl_Status, + Port2LinkMDResult, + Port2Control12, /* 44 */ + Port2Control13, + Port2Status0, + Port2Status1, + Port3Control0, /* 48 */ + Port3Control1, + Port3Control2, + Port3Control3, + Port3Control4, /* 52 */ + Port3Control5, + Port3Control6, + Port3Control7, + Port3Control8, /* 56 */ + Port3Control9, + Reservednotappliedtoport3, /* 58-62 */ + Port3Status1 = 63, + /* Advanced Control Registers: 96-141 */ + TOSPriorityControlRegister0 = 96, + TOSPriorityControlRegister1, + TOSPriorityControlRegister2, + TOSPriorityControlRegister3, + TOSPriorityControlRegister4, /* 100 */ + TOSPriorityControlRegister5, + TOSPriorityControlRegister6, + TOSPriorityControlRegister7, + TOSPriorityControlRegister8, /* 104 */ + TOSPriorityControlRegister9, + TOSPriorityControlRegister10, + TOSPriorityControlRegister11, + TOSPriorityControlRegister12, /* 108 */ + TOSPriorityControlRegister13, + TOSPriorityControlRegister14, + TOSPriorityControlRegister15, + MACAddressRegister0 = 112, + MACAddressRegister1, + MACAddressRegister2, + MACAddressRegister3, + MACAddressRegister4, + MACAddressRegister5, + UserDefinedRegister1 = 118, + UserDefinedRegister2, + UserDefinedRegister3, + IndirectAccessControl0 = 121, + IndirectAccessControl1, + IndirectDataRegister8 = 123, + IndirectDataRegister7, + IndirectDataRegister6, + IndirectDataRegister5, + IndirectDataRegister4, + IndirectDataRegister3, + IndirectDataRegister2, + IndirectDataRegister1, + IndirectDataRegister0, + DigitalTestingStatus0 = 132, + DigitalTestingControl0, + AnalogTestingControl0, + AnalogTestingControl1, + AnalogTestingControl2, + AnalogTestingControl3, + AnalogTestingStatus, + AnalogTestingControl4, + QMDebug1, + QMDebug2, +}; + +#endif