From f8a97780af94fdc1e02f19fa3bf513a0d071f268 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 1 Feb 2015 21:30:40 +0000 Subject: [PATCH] socz80: intial merge of the ethernet driver --- Kernel/platform-socz80/Makefile | 2 +- Kernel/platform-socz80/deveth.h | 12 + Kernel/platform-socz80/devices.c | 7 + Kernel/platform-socz80/ethernet.c | 410 +++++++++++++++++------------- Kernel/platform-socz80/fuzix.lnk | 1 + 5 files changed, 253 insertions(+), 179 deletions(-) create mode 100644 Kernel/platform-socz80/deveth.h diff --git a/Kernel/platform-socz80/Makefile b/Kernel/platform-socz80/Makefile index fca48645..fe786d97 100644 --- a/Kernel/platform-socz80/Makefile +++ b/Kernel/platform-socz80/Makefile @@ -1,5 +1,5 @@ -CSRCS = devlpr.c devtty.c devrd.c devsdspi.c +CSRCS = devlpr.c devtty.c devrd.c devsdspi.c ethernet.c CSRCS += devices.c main.c ASRCS = crt0.s devrd_hw.s socz80.s diff --git a/Kernel/platform-socz80/deveth.h b/Kernel/platform-socz80/deveth.h new file mode 100644 index 00000000..36a43610 --- /dev/null +++ b/Kernel/platform-socz80/deveth.h @@ -0,0 +1,12 @@ +#ifndef _SOCZ80_DEVETH_H +#define _SOCZ80_DEVETH_H + +extern int eth_write(uint8_t minor, uint8_t rawflag, uint8_t flag); +extern int eth_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +extern int eth_open(uint8_t minor, uint16_t flag); +extern int eth_close(uint8_t minor); +extern int eth_ioctl(uint8_t minor, uarg_t arg, char *data); + +extern void deveth_init(void); + +#endif \ No newline at end of file diff --git a/Kernel/platform-socz80/devices.c b/Kernel/platform-socz80/devices.c index 4eb4c845..cdf1ef8e 100644 --- a/Kernel/platform-socz80/devices.c +++ b/Kernel/platform-socz80/devices.c @@ -8,6 +8,7 @@ #include #include #include +#include struct devsw dev_tab[] = /* The device driver switch table */ { @@ -24,6 +25,11 @@ struct devsw dev_tab[] = /* The device driver switch table */ /* 4: /dev/mem etc System devices (one offs) */ { no_open, no_close, sys_read, sys_write, sys_ioctl }, /* Pack to 7 with nxio if adding private devices and start at 8 */ + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + { no_open, no_close, sys_read, sys_write, sys_ioctl }, + /* 8: Ethernet */ + { eth_open, eth_close, eth_read, eth_write, eth_ioctl }, }; bool validdev(uint16_t dev) @@ -39,4 +45,5 @@ bool validdev(uint16_t dev) void device_init(void) { devsd_init(); + deveth_init(); } diff --git a/Kernel/platform-socz80/ethernet.c b/Kernel/platform-socz80/ethernet.c index 537a62fb..23021ee7 100644 --- a/Kernel/platform-socz80/ethernet.c +++ b/Kernel/platform-socz80/ethernet.c @@ -11,7 +11,9 @@ #include - +#include +#include +#include __sfr __at 0x38 eth_chipselect; __sfr __at 0x39 eth_status; @@ -21,6 +23,8 @@ __sfr __at 0x3c eth_divisor; __sfr __at 0x3d eth_gpio; __sfr __at 0x3e eth_spimode; +static int eth_present; + /* In bank 0 */ #define ERDPTL 0x00 #define ERDPTH 0x01 @@ -120,244 +124,294 @@ __sfr __at 0x3e eth_spimode; #define BFC 0xA0 #define RST 0xFF -#define REG_SLOW 0x100 /* Driver only flag */ +#define REG_SLOW 0x100 /* Driver only flag */ struct encrxhdr { - uint16_t next; /* Little endian */ - uint16_t length; - uint16_t status; /* Status - high 16 */ + uint16_t next; /* Little endian */ + uint16_t length; + uint16_t status; /* Status - high 16 */ #define FRAME_GOOD 0x80 /* Double check 0x80 or 0x01 */ }; static uint8_t ethernap(void) __naked { - __asm - push bc - ld bc, #0x00F8 ; 0x0028 ought to work - check and tune -dellp:djnz dellp ; 255 * 13 + 8 (3323 clocks) per cycle - dec c - jr nz,dellp - pop bc - ret - __endasm + __asm + push bc + ld bc,#0x00F8 ; 0x0028 ought to work - check and tune + dellp: djnz dellp ;255 *13 + 8(3323 clocks) per cycle + dec c + jr nz, dellp + pop bc + ret + __endasm; } - + /* Don't inline these, the call/return is budgeted into the delay requirements! */ static void eth_select(void) { - eth_chipselect = 0xFE; + eth_chipselect = 0xFE; } static void eth_deselect(void) { - eth_rx; - eth_rx; - eth_chipselect = 0xFF; + eth_rx; + eth_rx; + eth_chipselect = 0xFF; } /* Write a command to the registers */ static void eth_cmd(uint8_t reg, uint8_t val) { - eth_select(); - eth_tx = reg; - eth_tx = val; - eth_deselect(); + eth_select(); + eth_tx = reg; + eth_tx = val; + eth_deselect(); } /* Write a 16bit command to a register pair */ static void eth_cmd16(uint8_t reg, uint16_t val) { - eth_cmd(reg++, val & 0xFF); - eth_cmd(reg, val >> 8); + eth_cmd(reg++, val & 0xFF); + eth_cmd(reg, val >> 8); } -static uint8_t eth_read(uint16_t reg) +static uint8_t eth_get(uint16_t reg) { - uint8_t r; - eth_select(); - eth_tx = reg & 0xFF; - eth_tx = 0; - if (reg & REG_SLOW) - eth_tx = 0; - r = eth_rx; - eth_deselect(); + uint8_t r; + eth_select(); + eth_tx = reg & 0xFF; + eth_tx = 0; + if (reg & REG_SLOW) + eth_tx = 0; + r = eth_rx; + eth_deselect(); + return r; } -static uint16_t phy_cmd16(uint8_t r, uint16_t cmd) +/* Select banks */ +static void eth_bank(uint8_t bank) { - eth_bank(2); - eth_cmd(WCR|MIREGADR, r); - eth_cmd16(WCR|MIWRL, cmd); - eth_bank(3); - while(eth_read(REG_SLOW|MISTAT) & 1); + if (bank != 3) + eth_cmd(BFC | ECON1, 0x03); /* Clear bank bits */ + if (bank) + eth_cmd(BFS | ECON1, bank); /* Set needed bits */ } -static uint16_t phy_read(uint8_t r) + +static void phy_cmd16(uint8_t r, uint16_t cmd) { - eth_bank(2); - eth_cmd(WCR|MIREGADR, r); - eth_cmd(WCR|MICMD, 0x01); - eth_bank(3); - while(eth_read(REG_SLOW|MISTAT) & 1); - eth_bank(2); - eth_cmd(WCR|MICMD, 0x00); - return eth_read(REG_SLOW|MIRDL) | (eth_readb(REG_SLOW|MIRDH) << 8); + eth_bank(2); + eth_cmd(WCR | MIREGADR, r); + eth_cmd16(WCR | MIWRL, cmd); + eth_bank(3); + while (eth_get(REG_SLOW | MISTAT) & 1); } - -/* Select banks */ -static void eth_bank(uint8_t bank) + +static uint16_t phy_read(uint8_t r) { - if (bank != 3) - ethercmd(BFC|ECON1, 0x03); /* Clear bank bits */ - if (bank) - ethercmd(BFS|ECON1, bank); /* Set needed bits */ + eth_bank(2); + eth_cmd(WCR | MIREGADR, r); + eth_cmd(WCR | MICMD, 0x01); + eth_bank(3); + while (eth_get(REG_SLOW | MISTAT) & 1); + eth_bank(2); + eth_cmd(WCR | MICMD, 0x00); + return eth_get(REG_SLOW | MIRDL) | (eth_get(REG_SLOW | MIRDH) << + 8); } /* Reset the controller by waggling the GPIO */ static void ethernet_reset(void) { - eth_gpio = 0; - ethernap(); - eth_gpio = 1; + eth_gpio = 0; + ethernap(); + eth_gpio = 1; } -/* Initialize the Ethernet wing */ - -int dev_eth_init(void) -{ - eth_divisor = 6; /* 9.3MHz (device limit is 10) */ - if (eth_divisor == 0xFF) /* Hardware absent */ - return -1; - eth_spimode = 0; - eth_chipselect = 0xff; - eth_gpio = 1; - eth_cmd(RST|ECON1, 0xff); - ethernap(); - - /* Wait for the NIC to stabilize */ - while(!(eth_read(ESTAT) & 1); - - eth_bank(0); - eth_cmd16(WCR|ERXSTL, 0); - eth_cmd16(WCR|ERXRDPTL, 0); - eth_cmd16(WCR|ERXNDL, 0x0BFF); - eth_cmd16(WCR|ETXSTL, 0x0C00); - eth_cmd16(WCR|ETXNDL, 0x11FF); - eth_bank(1); - eth_cmd16(WCR|EPMM0, 0x303F); - eth_cmd16(WCR|EPMCSL, 0xF7F9); - eth_cmd(BFS|ERXFCON, 0x01); - eth_bank(2); - eth_cmd(WCR|MACON2, 0x00); - - if (eth_read(REG_SLOW | MACON2)) { - kputs("eth: stuck in reset.\n"); - return -1; - } - eth_cmd(WCR|MACON1, 0x0d); - if (eth_read(REG_SLOW | MACON1) != 0x0d) { - kputs("eth: macon1 fail.\n"); - return -1; - } - eth_cmd(BFS|MACON3, 0x32); - eth_cmd16(WCR|MAIPGL, 0x0C12); - eth_cmd(WCR|MABBIPG, 0x12); - eth_cmd16(WCR|MAMXFL, 0x05DC); - - if (eth_read(REG_SLOW | MACON1) != 0x0d) { - kputs("eth: macon1 fail2.\n"); - return -1; - } - eth_bank(3); - /* Set the mac to AAAAAAC0FFEE for now FIXME */ - eth_cmd16(WCR|MAADR5, 0xAAAA); - eth_cmd16(WCR|MAADR3, 0xC0AA); - eth_cmd16(WCR|MAADR1, 0xEEFF); - phy_cmd16(0x01, 0x1000); - eth_bank(0); - eth_cmd(BFS|EIE, 0xc0); - eth_cmd(BFS|ECON1, 0x04); - return 0; -} /* * Wait until free and then send a packet. We don't have an interrupt * mechanism for the SPI ethernet on the SocZ80 although we could add * one via a GPIO if it turns out useful */ -int dev_eth_send(uint8_t *packet, int len) +int eth_write(uint8_t minor, uint8_t rawflag, uint8_t flag) { - eth_bank(0); - while (eth_read(ECON1) & 0x8) { - /* Not ready */ - if (!(eth_read(EIR) & 2)) { - /* Errata fix */ - eth_cmd(BFS|ECON1, 0x80); - eth_cmd(BFC|ECON1, 0x80); - } - _yield(); - /* May have switched bank */ - eth_bank(0); - } - /* - * Load the packet up - */ - eth_cmd16(WCR|EWRPTL, 0x0C00); - eth_cmd16(WCR|ETXNDL, 0x0C00 + len); - eth_cmd(WBM, 0x07); - eth_chipselect = 0xFE; - eth_tx = WBM - /* should do this bit in asm as an otir */ - while(len--) { - eth_tx = *packet; - packet++; - } - eth_deselect(); - /* - * Start transmit - */ - eth_cmd(BFS|ECON1, 0x08); - return 0; + uint8_t *packet = (uint8_t *)udata.u_offset; + uint16_t len = udata.u_count; + + used(minor); + used(rawflag); + used(flag); + + if (len > 1514) { + udata.u_error = EFBIG; + return -1; + } + eth_bank(0); + while (eth_get(ECON1) & 0x8) { + /* Not ready */ + if (!(eth_get(EIR) & 2)) { + /* Errata fix */ + eth_cmd(BFS | ECON1, 0x80); + eth_cmd(BFC | ECON1, 0x80); + } + _sched_yield(); + /* FIXME: check link up */ + /* May have switched bank */ + eth_bank(0); + } + /* + * Load the packet up + */ + eth_cmd16(WCR | EWRPTL, 0x0C00); + eth_cmd16(WCR | ETXNDL, 0x0C00 + len); + eth_cmd(WBM, 0x07); + eth_chipselect = 0xFE; + eth_tx = WBM; + + while (len--) + eth_tx = ugetc(packet++); + eth_deselect(); + /* + * Start transmit + */ + eth_cmd(BFS | ECON1, 0x08); + return len; } /* * Check with the phy if the link is up */ -int dev_eth_up(void) +int eth_up(void) { - return phyread(0x11) & 4); + return phy_read(0x11) & 4; } -static int eth_readpkt(uint8_t *packet, int len) +static void eth_readpkt(uint8_t * packet, int len) { - eth_select(); - eth_tx = RBM; - while (len--) - eth_tx = 0; - *packet++ = eth_rx; - } - eth_deselect(); + uint8_t c; + eth_select(); + eth_tx = RBM; + while (len--) { + eth_tx = 0; + c = eth_rx; /* SDCC 3.4.0 will miscompile I/O as arguments */ + uputc(c, packet++); + } + eth_deselect(); } -int dev_eth_read(struct encrxhdr *eth, uint8_t *packet, int len) +int eth_read(uint8_t minor, uint8_t rawflag, uint8_t flag) { - int r = -EIO; - eth_bank(1); - if (!eth_read(EPKTCNT)) /* Any packets waiting ? */ - return -EAGAIN; - eth_bank(0); - eth_cmd16(WCR|ERDPTL, eth_rxnext); - eth_readpkt(hdr, 6); - eth_rxnext = hdr->next; - if (hdr->status & FRAME_GOOD) { - len = min(len, hdr->length - 4); - eth_readpkt(packet, len); - r = len; - } - if (eth_rxnext > 0x0BFF) - eth_rxnext = 0x0BFF; - eth_cmd16(WCR|RXRDPTL, eth_rxnext); - eth_cmd(BFS|ECON2. 0x40); - return r; + static struct encrxhdr eth; + static uint16_t eth_rxnext = 0; + uint8_t *packet = (uint8_t *)udata.u_offset; + uint16_t len = udata.u_count; + int r = -EIO; + + used(flag); + used(rawflag); + used(minor); + + /* Check carrier ?? */ + + eth_bank(1); + + /* We have no IRQ so no sane blocking model .. yet */ + if (!eth_get(EPKTCNT)) /* Any packets waiting ? */ + return -EAGAIN; + eth_bank(0); + eth_cmd16(WCR | ERDPTL, eth_rxnext); + eth_readpkt((uint8_t *)ð, 6); + eth_rxnext = eth.next; + if (eth.status & FRAME_GOOD) { + len = min(len, eth.length - 4); + eth_readpkt(packet, len); + r = len; + } + if (eth_rxnext > 0x0BFF) + eth_rxnext = 0x0BFF; + eth_cmd16(WCR | ERXRDPTL, eth_rxnext); + eth_cmd(BFS | ECON2, 0x40); + return r; +} + +int eth_open(uint8_t minor, uint16_t flag) +{ + used(flag); + if (minor || !eth_present) + return -ENXIO; + return 0; +} + +int eth_close(uint8_t minor) +{ + used(minor); + return 0; +} + +int eth_ioctl(uint8_t minor, uarg_t arg, char *data) +{ + used(minor); + used(arg); + used(data); + return -1; +} + +DISCARDABLE +/* Initialize the Ethernet wing */ +void deveth_init(void) +{ + eth_divisor = 6; /* 9.3MHz (device limit is 10) */ + if (eth_divisor == 0xFF) /* Hardware absent */ + return; + eth_spimode = 0; + eth_chipselect = 0xff; + eth_gpio = 1; + eth_cmd(RST | ECON1, 0xff); + ethernap(); + + /* Wait for the NIC to stabilize */ + while (!(eth_get(ESTAT) & 1)); + + eth_bank(0); + eth_cmd16(WCR | ERXSTL, 0); + eth_cmd16(WCR | ERXRDPTL, 0); + eth_cmd16(WCR | ERXNDL, 0x0BFF); + eth_cmd16(WCR | ETXSTL, 0x0C00); + eth_cmd16(WCR | ETXNDL, 0x11FF); + eth_bank(1); + eth_cmd16(WCR | EPMM0, 0x303F); + eth_cmd16(WCR | EPMCSL, 0xF7F9); + eth_cmd(BFS | ERXFCON, 0x01); + eth_bank(2); + eth_cmd(WCR | MACON2, 0x00); + if (eth_get(REG_SLOW | MACON2)) { + kputs("eth: stuck in reset.\n"); + return; + } + eth_cmd(WCR | MACON1, 0x0d); + if (eth_get(REG_SLOW | MACON1) != 0x0d) { + kputs("eth: macon1 fail.\n"); + return; + } + eth_cmd(BFS | MACON3, 0x32); + eth_cmd16(WCR | MAIPGL, 0x0C12); + eth_cmd(WCR | MABBIPG, 0x12); + eth_cmd16(WCR | MAMXFLL, 0x05DC); + if (eth_get(REG_SLOW | MACON1) != 0x0d) { + kputs("eth: macon1 fail2.\n"); + return; + } + eth_bank(3); + /* Set the mac to AAAAAAC0FFEE for now FIXME */ + eth_cmd16(WCR | MAADR5, 0xAAAA); + eth_cmd16(WCR | MAADR3, 0xC0AA); + eth_cmd16(WCR | MAADR1, 0xEEFF); + phy_cmd16(0x01, 0x1000); + eth_bank(0); + eth_cmd(BFS | EIE, 0xc0); + eth_cmd(BFS | ECON1, 0x04); + eth_present = 1; + kputs("eth: ethernet detected.\n"); } diff --git a/Kernel/platform-socz80/fuzix.lnk b/Kernel/platform-socz80/fuzix.lnk index 2244a9d5..bc7fb7fd 100644 --- a/Kernel/platform-socz80/fuzix.lnk +++ b/Kernel/platform-socz80/fuzix.lnk @@ -38,4 +38,5 @@ platform-socz80/devsd.rel platform-socz80/blkdev.rel platform-socz80/mbr.rel platform-socz80/devsdspi.rel +platform-socz80/ethernet.rel -e -- 2.34.1