#include <pkgconf/system.h>
#include <pkgconf/io_eth_drivers.h>
#include <pkgconf/devs_eth_davicom_dm9000.h>
+
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/hal/hal_arch.h>
#define DM9000_PKT_MAX 1536
+//#define DEBUG
+//#define DEBUG_DUMP
+
//
// Control and Status register offsets
//
#define IMR_PTM (1 << 1)
#define IMR_PRM (1 << 0)
+/* PHY registers */
+#define PHY_BMCR 0x00
+#define PHY_BMSR 0x01
+#define PHY_ANAR 0x04
+
+/* PHY BMCR (Basic Mode Control Register) */
+#define PHY_BMCR_AUTO_NEG_EN (1 << 12)
+#define PHY_BMCR_AUTO_NEG_START (1 << 12)
+
+/* PHY BMSR (Basic Mode Status Register) */
+#define PHY_BMSR_AUTO_NEG_COMPLETE (1 << 5)
// Read one datum from 8-bit bus
static int read_data_8(struct dm9000 *p, cyg_uint8 *dest)
putreg(p, DM_EPCR, EPCR_ERPRR);
while (getreg(p, DM_EPCR) & EPCR_ERRE)
;
- CYGACC_CALL_IF_DELAY_US(200);
+ CYGACC_CALL_IF_DELAY_US(8000);
putreg(p, DM_EPCR, 0);
return getreg(p, DM_EPDRL) | (getreg(p, DM_EPDRH) << 8);
}
putreg(p, DM_EPCR, EPCR_WEP | EPCR_ERPRW);
while (getreg(p, DM_EPCR) & EPCR_ERRE)
;
- CYGACC_CALL_IF_DELAY_US(200);
+ CYGACC_CALL_IF_DELAY_US(8000);
putreg(p, DM_EPCR, 0);
}
putreg(p, DM_EPCR, EPCR_REEP);
while (getreg(p, DM_EPCR) & EPCR_ERRE)
;
- CYGACC_CALL_IF_DELAY_US(200);
+ CYGACC_CALL_IF_DELAY_US(8000);
putreg(p, DM_EPCR, 0);
}
static void init_phy(struct dm9000 *p)
{
- phy_write(p, 4, 0x1e1); // Advertise 10/100 half/full duplex w/CSMA
- phy_write(p, 0, 0x1200); // enable autoneg
+ int t = 0;
+ cyg_uint16 r;
+
+ /* power on PHY */
+ putreg(p, DM_GPCR, 0x01);
+ putreg(p, DM_GPR, 0x00);
- // release reset
- putreg(p, DM_GPCR, 1);
- putreg(p, DM_GPR, 0);
+ phy_write(p, PHY_ANAR, 0x1e1); // Advertise 10/100 half/full duplex w/CSMA
+ phy_write(p, PHY_BMCR, PHY_BMCR_AUTO_NEG_EN | PHY_BMCR_AUTO_NEG_START);
+
+ /* wait for autonegotiation to complete */
+ do {
+ CYGACC_CALL_IF_DELAY_US(1000);
+ r = phy_read(p, PHY_BMSR);
+ } while (!(r & PHY_BMSR_AUTO_NEG_COMPLETE) && t++ < 2000);
}
return 1;
}
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+static cyg_uint32 dm9000_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
+ struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
+
+ cyg_drv_interrupt_mask(priv->interrupt);
+ cyg_drv_interrupt_acknowledge(priv->interrupt);
+
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+}
+#endif
+
// ------------------------------------------------------------------------
//
if (id != 0x90000A46)
return 0;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+ cyg_drv_interrupt_create(priv->interrupt,
+ 0,
+ (cyg_addrword_t)sc,
+ dm9000_isr,
+ eth_drv_dsr,
+ &priv->interrupt_handle,
+ &priv->interrupt_object);
+ cyg_drv_interrupt_attach(priv->interrupt_handle);
+ cyg_drv_interrupt_acknowledge(priv->interrupt);
+ cyg_drv_interrupt_unmask(priv->interrupt);
+#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
+
for (i = 0; i < 64; i++)
u16tab[i] = eeprom_read(priv, i);
++sg;
} while (nread < total_len);
-#if 0
- // dump packet
+#ifdef DEBUG_DUMP
for (sg = sg_list; sg < (sg_list + sg_len); sg++) {
diag_printf("\n");
diag_dump_buf(sg->buf, sg->len);
int total_len, unsigned long key)
{
struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
- struct eth_drv_sg *sg = sg_list;
+ struct eth_drv_sg *sg;
cyg_uint8 tmpbuf[4];
- int i, len, extra, n, save_len;
+ int i, len, n, save_len, tail_extra;
char *p;
- if (0) {
- diag_printf("dm9000_send: NCR[%02x] NSR[%02x] TPL[%02x]\n",
- getreg(priv, DM_NCR), getreg(priv, DM_NSR),
- getreg(priv, DM_TRPAL) | (getreg(priv, DM_TRPAH) << 8)
- );
+#ifdef DEBUG
+ diag_printf("dm9000_send: NCR[%02x] NSR[%02x] TRPA[%04x]\n",
+ getreg(priv, DM_NCR), getreg(priv, DM_NSR),
+ getreg(priv, DM_TRPAL) | (getreg(priv, DM_TRPAH) << 8)
+ );
+#endif
+#ifdef DEBUG_DUMP
+ for (sg = sg_list; sg < (sg_list + sg_len); sg++) {
+ diag_printf("\n");
+ diag_dump_buf(sg->buf, sg->len);
}
+#endif
priv->txbusy = 1;
+ sg = sg_list;
save_len = total_len;
- extra = 0;
+ tail_extra = 0;
+
+ /* Disable all interrupts */
+ putreg(priv, DM_IMR, IMR_PAR);
HAL_WRITE_UINT8(priv->io_addr, DM_MWCMD);
len = total_len;
p = (char *)sg->buf;
- if (extra) {
- n = sizeof(tmpbuf) - extra;
- memcpy(tmpbuf + extra, p, n);
- p += n;
- len -= n;
+ /* write any left over partial words by combining them with the start
+ * of this sg block */
+ if (tail_extra) {
+ int head_extra = sizeof(tmpbuf) - tail_extra;
+ memcpy(tmpbuf + tail_extra, p, head_extra);
+ p += head_extra;
+ len -= head_extra;
for (i = 0; i < sizeof(tmpbuf) && total_len > 0; i += n) {
n = priv->write_data(priv, tmpbuf + i);
total_len -= n;
}
- extra = 0;
- }
-
- while (len >= sizeof(tmpbuf) && total_len > 0) {
- n = priv->write_data(priv, p);
- len -= n;
- total_len -= n;
- p += n;
- }
+ tail_extra = 0;
+ }
- if (len > 0 && total_len > 0) {
- extra = len;
- memcpy(tmpbuf, p, extra);
-
- if ((total_len - extra) <= 0) {
- // go ahead and write it now
- for (i = 0; total_len > 0; i += n, total_len -= n) {
- n = priv->write_data(priv, tmpbuf + i);
- total_len = 0;
- }
- break;
- }
- }
- sg++;
+ /* write out whole words */
+ while (len >= priv->buswidth) {
+ n = priv->write_data(priv, p);
+ len -= n;
+ total_len -= n;
+ p += n;
+ }
+
+ /* if we have some left over partial words... */
+ if (len > 0) {
+ /* combine them with the next sg block if available */
+ if (total_len > len ) {
+ tail_extra = len;
+ memcpy(tmpbuf, p, tail_extra);
+ } else {
+ /* otherwise just write this last partial word */
+ n = priv->write_data(priv, p);
+ total_len -= n;
+ }
+ }
+ sg++;
}
priv->txkey = key;
putreg(priv, DM_TCR, TCR_TXREQ);
- return;
+ /* Re-enable interrupt */
+ putreg(priv, DM_IMR, IMR_PAR | IMR_PTM | IMR_PRM);
}
// ------------------------------------------------------------------------
dm9000_poll(struct eth_drv_sc *sc)
{
struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
- cyg_uint8 status, rxstat, rx1;
+ cyg_uint8 status, rxstat;
cyg_uint16 pkt_stat, pkt_len;
int i;
// check for rx done
if (1 /*status & ISR_PRS*/) {
+ cyg_uint8 hdr[4]; /* 4 byte Rx pkt hdr */
+
+ getreg(priv, DM_MRCMDX); /* dummy read */
- rx1 = getreg(priv, DM_MRCMDX);
- HAL_READ_UINT8(priv->io_data, rxstat);
+ HAL_READ_UINT8(priv->io_data, rxstat);
// check for packet ready
if (rxstat == 1) {
- cyg_uint16 u16[2];
- cyg_uint8 *cp;
-
- HAL_WRITE_UINT8(priv->io_addr, DM_MRCMD);
- for (i = 0, cp = (cyg_uint8 *)u16; i < 4; )
- i += priv->read_data(priv, cp + i);
-
- u16[0] = CYG_LE16_TO_CPU(u16[0]);
- u16[1] = CYG_LE16_TO_CPU(u16[1]);
+ HAL_WRITE_UINT8(priv->io_addr, DM_MRCMD);
+ for (i = 0; i < 4;)
+ i += priv->read_data(priv, hdr + i);
-#if (CYG_BYTEORDER == CYG_MSBFIRST)
- pkt_stat = u16[0];
- pkt_len = u16[1];
-#else
- pkt_stat = u16[1];
- pkt_len = u16[0];
-#endif
+ pkt_stat = hdr[0] | (hdr[1] << 8);
+ pkt_len = hdr[2] | (hdr[3] << 8);
#ifdef DEBUG
diag_printf("pkt_stat=%04x pkt_len=%04x\n", pkt_stat, pkt_len);
diag_printf("packet too short: %d (0x%04x)\n", pkt_len, pkt_len);
i = 0;
while (i < pkt_len)
- i += priv->read_data(priv, cp);
+ i += priv->read_data(priv, hdr);
} else if (pkt_len > 1536) {
priv->reset_pending = 1;
diag_printf("packet too long: %d (0x%04x)\n", pkt_len, pkt_len);
diag_printf("bad packet status: 0x%04x\n", pkt_stat);
i = 0;
while (i < pkt_len)
- i += priv->read_data(priv, cp);
+ i += priv->read_data(priv, hdr);
} else {
// receive packet
priv->rxlen = pkt_len;
static void
dm9000_deliver(struct eth_drv_sc *sc)
{
+ struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
+
dm9000_poll(sc);
+
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+ cyg_drv_interrupt_unmask(priv->interrupt);
+#endif
}
// ------------------------------------------------------------------------
static int
dm9000_int_vector(struct eth_drv_sc *sc)
{
- struct dm9000 *priv;
- priv = (struct dm9000 *)sc->driver_private;
+ struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
- return -1;
+ return priv->interrupt;
}
dm9000_ioctl(struct eth_drv_sc *sc, unsigned long key,
void *data, int data_length)
{
+ struct dm9000 *priv = (struct dm9000 *)sc->driver_private;
+ cyg_uint8 *esa = (cyg_uint8 *)data;
+ int i;
+
+ switch (key) {
+#ifdef ETH_DRV_GET_MAC_ADDRESS
+ case ETH_DRV_GET_MAC_ADDRESS:
+ memcpy(esa, priv->mac_address, sizeof(priv->mac_address));
+ return 0;
+#endif
+#ifdef ETH_DRV_SET_MAC_ADDRESS
+ case ETH_DRV_SET_MAC_ADDRESS:
+ for (i = 0; i < sizeof(priv->mac_address); i++) {
+ priv->mac_address[i] = esa[i];
+ putreg(priv, DM_PAR + i, priv->mac_address[i]);
+ }
+#if defined(CYGSEM_DEVS_ETH_DAVICOM_DM9000_WRITE_EEPROM)
+ for (i = 0; i < sizeof(priv->mac_address) / 2; i++)
+ eeprom_write(priv, i, priv->mac_address[2*i] | (priv->mac_address[2*i+1] << 8));
+#else
+ diag_printf("dm9000: eeprom write disabled\n");
+#endif
+ return 0;
+#endif
+ }
+
return -1;
}