summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Souza <luiz@netgate.com>2018-03-08 19:38:22 -0300
committerLuiz Souza <luiz@netgate.com>2018-03-08 21:51:34 -0600
commiteb062d93722e51c4cfd19786b208db635bc6688a (patch)
tree1fc7293e6b8543ac6ac66128ef5205d7ee1b8afd
parentb3b184d0afe6bd05367dd44abf060aeb94e626f3 (diff)
downloadFreeBSD-src-eb062d93722e51c4cfd19786b208db635bc6688a.zip
FreeBSD-src-eb062d93722e51c4cfd19786b208db635bc6688a.tar.gz
Add the backend IO support for the Marvell e6000 series switch.
This particular interface allows access to the switch eeprom. The eeprom is write protected by default. (cherry picked from commit 33b80ce1752172918dffa8d33662a465cc2b0c29)
-rw-r--r--sys/dev/etherswitch/e6000sw/e6000sw.c110
-rw-r--r--sys/dev/etherswitch/e6000sw/e6000swreg.h11
2 files changed, 121 insertions, 0 deletions
diff --git a/sys/dev/etherswitch/e6000sw/e6000sw.c b/sys/dev/etherswitch/e6000sw/e6000sw.c
index 11fe59e..b7e8fb1 100644
--- a/sys/dev/etherswitch/e6000sw/e6000sw.c
+++ b/sys/dev/etherswitch/e6000sw/e6000sw.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sockio.h>
+#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_media.h>
@@ -100,6 +101,9 @@ typedef struct e6000sw_softc {
int port_base; /* SMI base addr of port regs */
int sw_addr;
int num_ports;
+
+ ssize_t iosize;
+ void *iobuf;
} e6000sw_softc_t;
static etherswitch_info_t etherswitch_info = {
@@ -132,6 +136,11 @@ static int e6000sw_getvgroup_wrapper(device_t, etherswitch_vlangroup_t *);
static int e6000sw_setvgroup_wrapper(device_t, etherswitch_vlangroup_t *);
static int e6000sw_setvgroup(device_t, etherswitch_vlangroup_t *);
static int e6000sw_getvgroup(device_t, etherswitch_vlangroup_t *);
+static ssize_t e6000sw_getiosize(device_t);
+static ssize_t e6000sw_getioblksize(device_t);
+static void *e6000sw_getiobuf(device_t);
+static int e6000sw_ioread(device_t, off_t, ssize_t);
+static int e6000sw_iowrite(device_t, off_t, ssize_t);
static void e6000sw_setup(device_t, e6000sw_softc_t *);
static void e6000sw_tick(void *);
static void e6000sw_set_atustat(device_t, e6000sw_softc_t *, int, int);
@@ -183,6 +192,11 @@ static device_method_t e6000sw_methods[] = {
DEVMETHOD(etherswitch_writephyreg, e6000sw_writephy_wrapper),
DEVMETHOD(etherswitch_setvgroup, e6000sw_setvgroup_wrapper),
DEVMETHOD(etherswitch_getvgroup, e6000sw_getvgroup_wrapper),
+ DEVMETHOD(etherswitch_getioblksize, e6000sw_getioblksize),
+ DEVMETHOD(etherswitch_getiosize, e6000sw_getiosize),
+ DEVMETHOD(etherswitch_getiobuf, e6000sw_getiobuf),
+ DEVMETHOD(etherswitch_ioread, e6000sw_ioread),
+ DEVMETHOD(etherswitch_iowrite, e6000sw_iowrite),
DEVMETHOD_END
};
@@ -198,6 +212,13 @@ DRIVER_MODULE(etherswitch, e6000sw, etherswitch_driver, etherswitch_devclass, 0,
DRIVER_MODULE(miibus, e6000sw, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(e6000sw, mdio, 1, 1, 1);
+static SYSCTL_NODE(_hw, OID_AUTO, e6000sw, CTLFLAG_RD, 0,
+ "Marvell E6000 series Switch Parameters");
+
+static int e6000sw_eeprom_wp = TRUE;
+SYSCTL_INT(_hw_e6000sw, OID_AUTO, eeprom_wp, CTLFLAG_RDTUN, &e6000sw_eeprom_wp,
+ 0, "Enable eeprom write protect.");
+
#undef E6000SW_DEBUG
#if defined(E6000SW_DEBUG)
static void
@@ -349,6 +370,10 @@ e6000sw_probe(device_t dev)
sc = device_get_softc(dev);
sc->dev = dev;
+ /* Do not set iosize until iobuf is ready. */
+ sc->iosize = -1;
+ sc->iobuf = NULL;
+
#ifdef FDT
dsa_node = fdt_find_compatible(OF_finddevice("/"),
"marvell,dsa", 0);
@@ -585,6 +610,8 @@ e6000sw_attach(device_t dev)
device_printf(dev, "single-chip addressing mode\n");
sx_init(&sc->sx, "e6000sw");
+ sc->iobuf = malloc(E6000SW_IOBUF_BLKSIZE, M_E6000SW, M_WAITOK);
+ sc->iosize = E6000SW_IOBUF_SIZE;
E6000SW_LOCK(sc);
e6000sw_setup(dev, sc);
@@ -752,6 +779,8 @@ e6000sw_detach(device_t dev)
sc = device_get_softc(dev);
bus_generic_detach(dev);
+ if (sc->iobuf != NULL)
+ free(sc->iobuf, M_E6000SW);
sx_destroy(&sc->sx);
for (phy = 0; phy < sc->num_ports; phy++) {
if (sc->miibus[phy] != NULL)
@@ -1751,3 +1780,84 @@ e6000sw_vtu_update(e6000sw_softc_t *sc, int purge, int vid, int fid,
return (0);
}
+
+static ssize_t
+e6000sw_getiosize(device_t dev)
+{
+ e6000sw_softc_t *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->iosize);
+}
+
+static ssize_t
+e6000sw_getioblksize(device_t dev __unused)
+{
+
+ return (E6000SW_IOBUF_BLKSIZE);
+}
+
+static void *
+e6000sw_getiobuf(device_t dev)
+{
+ e6000sw_softc_t *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->iobuf);
+}
+
+static int
+e6000sw_ioread(device_t dev, off_t off, ssize_t len)
+{
+ e6000sw_softc_t *sc;
+ ssize_t resid;
+ uint8_t *iobuf;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ iobuf = (uint8_t *)sc->iobuf;
+ for (resid = 0; resid < len; resid++) {
+ if (E6000SW_WAITREADY2(sc, EEPROM_CMD, EEPROM_BUSY)) {
+ device_printf(sc->dev, "EEPROM is busy, cannot access\n");
+ return (ETIMEDOUT);
+ }
+ e6000sw_writereg(sc, REG_GLOBAL2, EEPROM_ADDR, off + resid);
+ e6000sw_writereg(sc, REG_GLOBAL2, EEPROM_CMD,
+ EEPROM_READ_CMD | EEPROM_BUSY);
+ if (E6000SW_WAITREADY2(sc, EEPROM_CMD, EEPROM_BUSY)) {
+ device_printf(sc->dev, "EEPROM is busy, cannot access\n");
+ return (ETIMEDOUT);
+ }
+ reg = e6000sw_readreg(sc, REG_GLOBAL2, EEPROM_CMD);
+ iobuf[resid] = reg & EEPROM_DATA_MASK;
+ }
+
+ return (0);
+}
+
+static int
+e6000sw_iowrite(device_t dev, off_t off, ssize_t len)
+{
+ e6000sw_softc_t *sc;
+ ssize_t resid;
+ uint8_t *iobuf;
+
+ if (e6000sw_eeprom_wp)
+ return (EPERM);
+ sc = device_get_softc(dev);
+ iobuf = (uint8_t *)sc->iobuf;
+ for (resid = 0; resid < len; resid++) {
+ if (E6000SW_WAITREADY2(sc, EEPROM_CMD, EEPROM_BUSY)) {
+ device_printf(sc->dev, "EEPROM is busy, cannot access\n");
+ return (ETIMEDOUT);
+ }
+ e6000sw_writereg(sc, REG_GLOBAL2, EEPROM_ADDR, off + resid);
+ e6000sw_writereg(sc, REG_GLOBAL2, EEPROM_CMD,
+ EEPROM_BUSY | EEPROM_WRITE_CMD | EEPROM_WRITE_EN |
+ (iobuf[resid] & EEPROM_DATA_MASK));
+ }
+
+ return (0);
+}
diff --git a/sys/dev/etherswitch/e6000sw/e6000swreg.h b/sys/dev/etherswitch/e6000sw/e6000swreg.h
index 80cbd88..72c6da2 100644
--- a/sys/dev/etherswitch/e6000sw/e6000swreg.h
+++ b/sys/dev/etherswitch/e6000sw/e6000swreg.h
@@ -228,6 +228,15 @@ struct atu_opt {
* 'Switch Global Registers 2' (REG_GLOBAL2).
*/
+/* EEPROM registers */
+#define EEPROM_CMD 0x14
+#define EEPROM_BUSY (1 << 15)
+#define EEPROM_READ_CMD (4 << 12)
+#define EEPROM_WRITE_CMD (3 << 12)
+#define EEPROM_WRITE_EN (1 << 10)
+#define EEPROM_DATA_MASK 0xff
+#define EEPROM_ADDR 0x15
+
/* PHY registers */
#define SMI_PHY_CMD_REG 0x18
#define SMI_CMD_BUSY (1 << 15)
@@ -262,5 +271,7 @@ struct atu_opt {
#define E6000SW_DEFAULT_AGETIME 20
#define E6000SW_RETRIES 100
#define E6000SW_SMI_TIMEOUT 16
+#define E6000SW_IOBUF_BLKSIZE (4 * 1024) /* 4 KiB block */
+#define E6000SW_IOBUF_SIZE (64 * 1024) /* 64 KiB max. */
#endif /* _E6000SWREG_H_ */
OpenPOWER on IntegriCloud