summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Herring <r.herring@freescale.com>2009-09-16 11:48:45 -0500
committerAlan Tull <r80115@freescale.com>2010-08-25 11:51:44 -0500
commit5d0f46df1f23a855311fe7fcf4cf91fae44558a3 (patch)
treeb9caa8f97534b79604592430ca01e9513f09de6d
parent129810dc73a840e8c88f95fa5b26bf2870de5116 (diff)
enc28j60: enhancements for stmp3xxx boards
Get mac addr from otp bits Add suspend/resume Signed-off-by: Rob Herring <r.herring@freescale.com>
-rw-r--r--drivers/net/enc28j60.c154
1 files changed, 146 insertions, 8 deletions
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index 112c5aa9af7f..750d89bde890 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -29,12 +29,15 @@
#include <linux/delay.h>
#include <linux/spi/spi.h>
+#include <mach/platform.h>
+
#include "enc28j60_hw.h"
#define DRV_NAME "enc28j60"
#define DRV_VERSION "1.01"
#define SPI_OPLEN 1
+#define MAX_ENC_CARDS 1
#define ENC28J60_MSG_DEFAULT \
(NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
@@ -48,6 +51,10 @@
/* Max TX retries in case of collision as suggested by errata datasheet */
#define MAX_TX_RETRYCOUNT 16
+#ifdef CONFIG_ARCH_STMP3XXX
+#include <mach/stmp3xxx.h>
+#include <mach/regs-ocotp.h>
+#endif
enum {
RXFILTER_NORMAL,
RXFILTER_MULTI,
@@ -80,6 +87,69 @@ static struct {
u32 msg_enable;
} debug = { -1 };
+static int random_mac; /* = 0 */
+static char *mac[MAX_ENC_CARDS];
+
+static int enc28j60_get_mac(unsigned char *dev_addr, int idx)
+{
+ int i, r;
+ char *p, *item;
+ unsigned long v;
+ unsigned char sv[10];
+
+ if (idx < 0)
+ idx = 0;
+ if (idx > MAX_ENC_CARDS)
+ return false;
+
+ if (!mac[idx]) {
+#ifdef CONFIG_ARCH_STMP3XXX
+ if (get_evk_board_version() >= 1) {
+ int mac1 , mac2 , retry = 0;
+
+ stmp3xxx_setl(BM_OCOTP_CTRL_RD_BANK_OPEN, REGS_OCOTP_BASE + HW_OCOTP_CTRL);
+ while (__raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL) & BM_OCOTP_CTRL_BUSY) {
+ msleep(10);
+ retry++;
+ if (retry > 10)
+ return false;
+ }
+
+ mac1 = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CUSTn(0));
+ mac2 = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CUSTn(1));
+ if (MAX_ADDR_LEN < 6)
+ return false;
+
+ dev_addr[0] = (mac1 >> 24) & 0xFF;
+ dev_addr[1] = (mac1 >> 16) & 0xFF;
+ dev_addr[2] = (mac1 >> 8) & 0xFF;
+ dev_addr[3] = (mac1 >> 0) & 0xFF;
+ dev_addr[4] = (mac2 >> 8) & 0xFF;
+ dev_addr[5] = (mac2 >> 0) & 0xFF;
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ item = mac[idx];
+ for (i = 0; i < MAX_ADDR_LEN; i++) {
+ p = strchr(item, ':');
+ if (!p)
+ sprintf(sv, "0x%s", item);
+ else
+ sprintf(sv, "0x%*.*s", p - item, p-item, item);
+ r = strict_strtoul(sv, 0, &v);
+ dev_addr[i] = v;
+ if (p)
+ item = p + 1;
+ else
+ break;
+ if (r < 0)
+ return false;
+ }
+ return true;
+}
/*
* SPI read buffer
* wait for the SPI transfer and copy received data to destination
@@ -89,10 +159,13 @@ spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
{
u8 *rx_buf = priv->spi_transfer_buf + 4;
u8 *tx_buf = priv->spi_transfer_buf;
- struct spi_transfer t = {
- .tx_buf = tx_buf,
- .rx_buf = rx_buf,
- .len = SPI_OPLEN + len,
+ struct spi_transfer tt = {
+ .tx_buf = tx_buf,
+ .len = SPI_OPLEN,
+ };
+ struct spi_transfer tr = {
+ .rx_buf = rx_buf,
+ .len = len,
};
struct spi_message msg;
int ret;
@@ -101,10 +174,11 @@ spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
spi_message_init(&msg);
- spi_message_add_tail(&t, &msg);
+ spi_message_add_tail(&tt, &msg);
+ spi_message_add_tail(&tr, &msg);
ret = spi_sync(priv->spi, &msg);
if (ret == 0) {
- memcpy(data, &rx_buf[SPI_OPLEN], len);
+ memcpy(data, rx_buf, len);
ret = msg.status;
}
if (ret && netif_msg_drv(priv))
@@ -1107,8 +1181,24 @@ static int enc28j60_rx_interrupt(struct net_device *ndev)
priv->max_pk_counter);
}
ret = pk_counter;
- while (pk_counter-- > 0)
+ while (pk_counter-- > 0) {
+ if (!priv->full_duplex) {
+ /*
+ * This works only in HALF DUPLEX mode:
+ * when more than 2 packets are available, start
+ * transmission of 11111.. frame by setting
+ * FCON0 (0x01) in EFLOCON
+ *
+ * This bit can be cleared either explicitly, or by
+ * trasmitting the packet in enc28j60_hw_tx.
+ */
+ if (pk_counter > 2)
+ locked_reg_bfset(priv, EFLOCON, 0x01);
+ if (pk_counter == 1)
+ locked_reg_bfclr(priv, EFLOCON, 0x01);
+ }
enc28j60_hw_rx(ndev);
+ }
return ret;
}
@@ -1234,6 +1324,11 @@ static void enc28j60_irq_work_handler(struct work_struct *work)
*/
static void enc28j60_hw_tx(struct enc28j60_net *priv)
{
+ if (!priv->tx_skb) {
+ enc28j60_tx_clear(priv->netdev, false);
+ return;
+ }
+
if (netif_msg_tx_queued(priv))
printk(KERN_DEBUG DRV_NAME
": Tx Packet Len:%d\n", priv->tx_skb->len);
@@ -1545,6 +1640,7 @@ static int __devinit enc28j60_probe(struct spi_device *spi)
struct net_device *dev;
struct enc28j60_net *priv;
int ret = 0;
+ int set;
if (netif_msg_drv(&debug))
dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
@@ -1578,7 +1674,11 @@ static int __devinit enc28j60_probe(struct spi_device *spi)
ret = -EIO;
goto error_irq;
}
- random_ether_addr(dev->dev_addr);
+
+ /* need a counter here, to count instances of enc28j60 devices */
+ set = enc28j60_get_mac(dev->dev_addr, -1);
+ if (!set || random_mac)
+ random_ether_addr(dev->dev_addr);
enc28j60_set_hw_macaddr(dev);
/* Board setup must set the relevant edge trigger type;
@@ -1633,6 +1733,40 @@ static int __devexit enc28j60_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM
+static int
+enc28j60_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net_dev = priv ? priv->netdev : NULL;
+
+ if (net_dev && netif_running(net_dev)) {
+ netif_stop_queue(net_dev);
+ netif_device_detach(net_dev);
+ disable_irq(spi->irq);
+ }
+ return 0;
+}
+
+static int
+enc28j60_resume(struct spi_device *spi)
+{
+ struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net_dev = priv ? priv->netdev : NULL;
+
+ if (net_dev && netif_running(net_dev)) {
+ enable_irq(spi->irq);
+ netif_device_attach(net_dev);
+ netif_start_queue(net_dev);
+ schedule_work(&priv->restart_work);
+ }
+ return 0;
+}
+#else
+#define enc28j60_resume NULL
+#define enc28j60_suspend NULL
+#endif
+
static struct spi_driver enc28j60_driver = {
.driver = {
.name = DRV_NAME,
@@ -1640,6 +1774,8 @@ static struct spi_driver enc28j60_driver = {
},
.probe = enc28j60_probe,
.remove = __devexit_p(enc28j60_remove),
+ .suspend = enc28j60_suspend,
+ .resume = enc28j60_resume,
};
static int __init enc28j60_init(void)
@@ -1662,5 +1798,7 @@ MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_LICENSE("GPL");
module_param_named(debug, debug.msg_enable, int, 0);
+module_param(random_mac, int, 0444);
+module_param_array(mac, charp, NULL, 0);
MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
MODULE_ALIAS("spi:" DRV_NAME);