/*- * Copyright (C) 2009 Semihalf, Michal Hajduk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "e6000swreg.h" #define MVSW_RETRIES 100 #define MV_PORT_MAX 5 #define VLAN_DEFAULT 0x1 #define NO_CONF 0x0 struct e6000sw_softc { device_t dev; switch_readreg_t readreg; switch_writereg_t writereg; int cpu_port; struct cdev *cdev; }; static int e6000sw_probe(device_t dev); static int e6000sw_attach(device_t dev); static void e6000sw_setup(device_t dev, struct e6000sw_softc *sc, int vlan_config); static void e6000sw_port_vlan_conf(struct e6000sw_softc *sc); static void e6000sw_set_atustat(device_t dev, struct e6000sw_softc *sc, int bin, int flag); static int e6000sw_atu_flush(device_t dev, struct e6000sw_softc *sc, int flag); static int e6000sw_atu_mac_table(device_t dev, struct e6000sw_softc *sc, struct atu_opt *atu, int flag); /* cdevsw interface */ static d_open_t e6000sw_open; static d_close_t e6000sw_close; static d_ioctl_t e6000sw_ioctl; /* * Switch character device interface */ static struct cdevsw e6000sw_cdevsw = { .d_version = D_VERSION, .d_open = e6000sw_open, .d_close = e6000sw_close, .d_ioctl = e6000sw_ioctl, .d_name = "e6000sw" }; static device_method_t e6000sw_methods[] = { DEVMETHOD(device_probe, e6000sw_probe), DEVMETHOD(device_attach, e6000sw_attach), { 0, 0 } }; static devclass_t e6000sw_devclass; static driver_t e6000sw_driver = { "e6000sw", e6000sw_methods, sizeof(struct e6000sw_softc), }; DRIVER_MODULE(e6000sw, mge, e6000sw_driver, e6000sw_devclass, 0, 0); static __inline int e6000sw_readreg(struct e6000sw_softc *sc, int addr, int reg) { return (sc->readreg(device_get_parent(sc->dev), addr, reg)); } static __inline void e6000sw_writereg(struct e6000sw_softc *sc, int addr, int reg, int val) { sc->writereg(device_get_parent(sc->dev), addr, reg, val); } static __inline void e6000sw_dump_reg(device_t dev, struct e6000sw_softc *sc) { int addr, temp; for (addr = 0; addr < 32; addr++) { temp = e6000sw_readreg(sc, addr, 0x1); device_printf(dev, "DEV_ADDR #0x%x = 0x%x\n", addr, temp); } } static __inline void e6000sw_print_atustat(device_t dev, struct e6000sw_softc *sc) { uint16_t ret; ret = e6000sw_readreg(sc, REG_GLOBAL2, ATU_STATS); device_printf(dev, "ATU statistic : active bin counter = 0x%x\n", 0xfff & ret); } static int e6000sw_probe(device_t dev) { struct e6000sw_softc *sc; struct ethsw_ivars *sw_ivars; char *description; int id; sc = device_get_softc(dev); sc->dev = dev; sw_ivars = device_get_ivars(dev); sc->readreg = sw_ivars->readreg; sc->writereg = sw_ivars->writereg; id = e6000sw_readreg(sc, REG_PORT(CPU_PORT), SWITCH_ID); if (id > 0) { switch (id & 0xfff0) { case 0x1210: description = "Marvell 88E6123 Switch"; break; case 0x1610: description = "Marvell 88E6161 Switch"; break; case 0x1650: description = "Marvell 88E6165: 6GbE Port Switch"; break; default: description = "Unknow Switch"; } } else description = "Unknow Switch"; device_set_desc(dev, description); return (BUS_PROBE_DEFAULT); } static int e6000sw_attach(device_t dev) { struct e6000sw_softc *sc; sc = device_get_softc(dev); sc->cdev = make_dev(&e6000sw_cdevsw, device_get_unit(dev), UID_ROOT, GID_WHEEL, 0600, "e6000sw%d", device_get_unit(dev)); sc->cdev->si_drv1 = sc; e6000sw_setup(dev, sc, VLAN_DEFAULT); return (0); } static void e6000sw_setup(device_t dev, struct e6000sw_softc *sc, int vlan_config) { /* Set aging time */ e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, 0x0148); /* CPU port config */ e6000sw_writereg(sc, REG_GLOBAL, MONITOR_CONTROL, (CPU_PORT * 0x1110)); /* Send all with specific mac address to cpu port */ e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_2x, 0xffff); e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_0x, 0xffff); /* Disable Remote Managment */ e6000sw_writereg(sc, REG_GLOBAL, SWITCH_GLOBAL_CONTROL2, 0x0000); /* Disable loopback filter and flow control messages */ e6000sw_writereg(sc, REG_GLOBAL2, SWITCH_MANG, 0x00ff); /* Vlan basic config */ if (vlan_config == VLAN_DEFAULT) e6000sw_port_vlan_conf(sc); e6000sw_atu_flush(dev, sc, NO_OPERATION); e6000sw_atu_mac_table(dev, sc, NULL, NO_OPERATION); e6000sw_set_atustat(dev, sc, 0x0, COUNT_ALL); } static void e6000sw_port_vlan_conf(struct e6000sw_softc *sc) { int port, ret; /* Setting VLANs just like in U-Boot driver*/ /* Disable all ports */ for (port = 0; port < MV_PORT_MAX; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), CONTROL); e6000sw_writereg(sc, REG_PORT(port), CONTROL, (ret & ~0x3)); } /* Set cpu port to VID 0x1 */ ret = e6000sw_readreg(sc, REG_PORT(CPU_PORT), PORT_VLAN_MAP); ret &= ~0xfff; ret |= 0x1; e6000sw_writereg(sc, REG_PORT(CPU_PORT), PORT_VLAN_MAP, ret); /* Setting port priority */ for (port = 0; port < MV_PORT_MAX; port++) { if (port != CPU_PORT) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VLAN_MAP); ret &= ~0xc000; e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP, ret); } } /* Setting VID map */ for (port = 0; port < MV_PORT_MAX; port++) { if (port != CPU_PORT) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); ret &= ~0x0fff; ret |= (port + 1); e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret); ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VLAN_MAP); ret |= 0x03f; ret &= ~(1 << port); e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP, ret); } } ret = e6000sw_readreg(sc, REG_PORT(CPU_PORT), PORT_VLAN_MAP); ret &= ~((1 << MV_PORT_MAX) - 1); ret |= ~(1 << CPU_PORT); e6000sw_writereg(sc, REG_PORT(CPU_PORT), PORT_VLAN_MAP, ret); /* Enable all port */ for (port = 0; port < MV_PORT_MAX; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), CONTROL); e6000sw_writereg(sc, REG_PORT(port), CONTROL, (ret | 0x3)); } } static void e6000sw_set_atustat(device_t dev, struct e6000sw_softc *sc, int bin, int flag) { uint16_t ret; ret = e6000sw_readreg(sc, REG_GLOBAL2, ATU_STATS); e6000sw_writereg(sc, REG_GLOBAL2, ATU_STATS, (bin << 14 ) | (flag << 12)); } static int e6000sw_atu_mac_table(device_t dev, struct e6000sw_softc *sc, struct atu_opt *atu, int flag) { uint16_t ret_opt; uint16_t ret_data; int retries; if (flag == NO_OPERATION) return (0); else if ((flag & (LOAD_FROM_FIB | PURGE_FROM_FIB | GET_NEXT_IN_FIB | GET_VIOLATION_DATA | CLEAR_VIOLATION_DATA)) == 0) { device_printf(dev, "Wrong Opcode for ATU operation\n"); return (-2); } ret_opt = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION); if (ret_opt & ATU_UNIT_BUSY) { device_printf(dev, "ATU unit is busy, cannot access" "register\n"); return (-1); } else { if(flag & LOAD_FROM_FIB) { ret_data = e6000sw_readreg(sc, REG_GLOBAL, ATU_DATA); e6000sw_writereg(sc, REG_GLOBAL2, ATU_DATA, (ret_data & ~ENTRY_STATE)); } e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR01, atu->mac_01); e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR23, atu->mac_23); e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR45, atu->mac_45); e6000sw_writereg(sc, REG_GLOBAL, ATU_FID, atu->fid); e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret_opt | ATU_UNIT_BUSY | flag)); retries = MVSW_RETRIES; while (--retries & (e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION) & ATU_UNIT_BUSY)) DELAY(1); if (retries == 0) device_printf(dev, "Timeout while flushing\n"); else if (flag & GET_NEXT_IN_FIB) { atu->mac_01 = e6000sw_readreg(sc, REG_GLOBAL, ATU_MAC_ADDR01); atu->mac_23 = e6000sw_readreg(sc, REG_GLOBAL, ATU_MAC_ADDR23); atu->mac_45 = e6000sw_readreg(sc, REG_GLOBAL, ATU_MAC_ADDR45); } } return (0); } static int e6000sw_atu_flush(device_t dev, struct e6000sw_softc *sc, int flag) { uint16_t ret; int retries; if (flag == NO_OPERATION) return (0); ret = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION); if (ret & ATU_UNIT_BUSY) { device_printf(dev, "Atu unit is busy, cannot flush\n"); return (-1); } else { e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret | ATU_UNIT_BUSY | flag)); retries = MVSW_RETRIES; while (--retries & (e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION) & ATU_UNIT_BUSY)) DELAY(1); if (retries == 0) device_printf(dev, "Timeout while flushing\n"); } return (0); } /* cdevsw interface */ static int e6000sw_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { return (0); } static int e6000sw_close(struct cdev *dev, int oflags, int devtype, struct thread *td) { return (0); } static int e6000sw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { return (0); }