drivers/net: Add Micrel KS8841/42 support to ks8842 driver

Body of the explanation:
   -support 16bit and 32bit bus width.
   -add device reset for ks8842/8841 Micrel device.
   -set 100Mbps as a default for Micrel device.
   -set MAC address in both MAC/Switch layer with different sequence for Micrel
    device, as mentioned in data sheet.
   -use private data to set options both 16/32bit bus width and Micrel device/
    Timberdale(FPGA).
   -update Kconfig in order to put more information about ks8842 device.

Signed-off-by: David J. Choi <david.choi@micrel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David J. Choi 2010-07-13 10:09:19 -07:00 committed by David S. Miller
parent 242647bcf8
commit 28bd620c7a
2 changed files with 132 additions and 47 deletions

View File

@ -1750,11 +1750,12 @@ config TLAN
Please email feedback to <torben.mathiasen@compaq.com>.
config KS8842
tristate "Micrel KSZ8842"
tristate "Micrel KSZ8841/42 with generic bus interface"
depends on HAS_IOMEM
help
This platform driver is for Micrel KSZ8842 / KS8842
2-port ethernet switch chip (managed, VLAN, QoS).
This platform driver is for KSZ8841(1-port) / KS8842(2-port)
ethernet switch chip (managed, VLAN, QoS) from Micrel or
Timberdale(FPGA).
config KS8851
tristate "Micrel KS8851 SPI"

View File

@ -18,6 +18,7 @@
/* Supports:
* The Micrel KS8842 behind the timberdale FPGA
* The genuine Micrel KS8841/42 device with ISA 16/32bit bus interface
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -114,9 +115,14 @@
#define REG_P1CR4 0x02
#define REG_P1SR 0x04
/* flags passed by platform_device for configuration */
#define MICREL_KS884X 0x01 /* 0=Timeberdale(FPGA), 1=Micrel */
#define KS884X_16BIT 0x02 /* 1=16bit, 0=32bit */
struct ks8842_adapter {
void __iomem *hw_addr;
int irq;
unsigned long conf_flags; /* copy of platform_device config */
struct tasklet_struct tasklet;
spinlock_t lock; /* spinlock to be interrupt safe */
struct work_struct timeout_work;
@ -192,15 +198,21 @@ static inline u32 ks8842_read32(struct ks8842_adapter *adapter, u16 bank,
static void ks8842_reset(struct ks8842_adapter *adapter)
{
/* The KS8842 goes haywire when doing softare reset
* a work around in the timberdale IP is implemented to
* do a hardware reset instead
ks8842_write16(adapter, 3, 1, REG_GRR);
msleep(10);
iowrite16(0, adapter->hw_addr + REG_GRR);
*/
iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST);
msleep(20);
if (adapter->conf_flags & MICREL_KS884X) {
ks8842_write16(adapter, 3, 1, REG_GRR);
msleep(10);
iowrite16(0, adapter->hw_addr + REG_GRR);
} else {
/* The KS8842 goes haywire when doing softare reset
* a work around in the timberdale IP is implemented to
* do a hardware reset instead
ks8842_write16(adapter, 3, 1, REG_GRR);
msleep(10);
iowrite16(0, adapter->hw_addr + REG_GRR);
*/
iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST);
msleep(20);
}
}
static void ks8842_update_link_status(struct net_device *netdev,
@ -269,8 +281,10 @@ static void ks8842_reset_hw(struct ks8842_adapter *adapter)
/* restart port auto-negotiation */
ks8842_enable_bits(adapter, 49, 1 << 13, REG_P1CR4);
/* only advertise 10Mbps */
ks8842_clear_bits(adapter, 49, 3 << 2, REG_P1CR4);
if (!(adapter->conf_flags & MICREL_KS884X))
/* only advertise 10Mbps */
ks8842_clear_bits(adapter, 49, 3 << 2, REG_P1CR4);
/* Enable the transmitter */
ks8842_enable_tx(adapter);
@ -296,13 +310,28 @@ static void ks8842_read_mac_addr(struct ks8842_adapter *adapter, u8 *dest)
for (i = 0; i < ETH_ALEN; i++)
dest[ETH_ALEN - i - 1] = ks8842_read8(adapter, 2, REG_MARL + i);
/* make sure the switch port uses the same MAC as the QMU */
mac = ks8842_read16(adapter, 2, REG_MARL);
ks8842_write16(adapter, 39, mac, REG_MACAR1);
mac = ks8842_read16(adapter, 2, REG_MARM);
ks8842_write16(adapter, 39, mac, REG_MACAR2);
mac = ks8842_read16(adapter, 2, REG_MARH);
ks8842_write16(adapter, 39, mac, REG_MACAR3);
if (adapter->conf_flags & MICREL_KS884X) {
/*
the sequence of saving mac addr between MAC and Switch is
different.
*/
mac = ks8842_read16(adapter, 2, REG_MARL);
ks8842_write16(adapter, 39, mac, REG_MACAR3);
mac = ks8842_read16(adapter, 2, REG_MARM);
ks8842_write16(adapter, 39, mac, REG_MACAR2);
mac = ks8842_read16(adapter, 2, REG_MARH);
ks8842_write16(adapter, 39, mac, REG_MACAR1);
} else {
/* make sure the switch port uses the same MAC as the QMU */
mac = ks8842_read16(adapter, 2, REG_MARL);
ks8842_write16(adapter, 39, mac, REG_MACAR1);
mac = ks8842_read16(adapter, 2, REG_MARM);
ks8842_write16(adapter, 39, mac, REG_MACAR2);
mac = ks8842_read16(adapter, 2, REG_MARH);
ks8842_write16(adapter, 39, mac, REG_MACAR3);
}
}
static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac)
@ -313,8 +342,25 @@ static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac)
spin_lock_irqsave(&adapter->lock, flags);
for (i = 0; i < ETH_ALEN; i++) {
ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i);
ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1],
REG_MACAR1 + i);
if (!(adapter->conf_flags & MICREL_KS884X))
ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1],
REG_MACAR1 + i);
}
if (adapter->conf_flags & MICREL_KS884X) {
/*
the sequence of saving mac addr between MAC and Switch is
different.
*/
u16 mac;
mac = ks8842_read16(adapter, 2, REG_MARL);
ks8842_write16(adapter, 39, mac, REG_MACAR3);
mac = ks8842_read16(adapter, 2, REG_MARM);
ks8842_write16(adapter, 39, mac, REG_MACAR2);
mac = ks8842_read16(adapter, 2, REG_MARH);
ks8842_write16(adapter, 39, mac, REG_MACAR1);
}
spin_unlock_irqrestore(&adapter->lock, flags);
}
@ -328,8 +374,6 @@ static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct ks8842_adapter *adapter = netdev_priv(netdev);
int len = skb->len;
u32 *ptr = (u32 *)skb->data;
u32 ctrl;
netdev_dbg(netdev, "%s: len %u head %p data %p tail %p end %p\n",
__func__, skb->len, skb->head, skb->data,
@ -339,17 +383,34 @@ static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
if (ks8842_tx_fifo_space(adapter) < len + 8)
return NETDEV_TX_BUSY;
/* the control word, enable IRQ, port 1 and the length */
ctrl = 0x8000 | 0x100 | (len << 16);
ks8842_write32(adapter, 17, ctrl, REG_QMU_DATA_LO);
if (adapter->conf_flags & KS884X_16BIT) {
u16 *ptr16 = (u16 *)skb->data;
ks8842_write16(adapter, 17, 0x8000 | 0x100, REG_QMU_DATA_LO);
ks8842_write16(adapter, 17, (u16)len, REG_QMU_DATA_HI);
netdev->stats.tx_bytes += len;
netdev->stats.tx_bytes += len;
/* copy buffer */
while (len > 0) {
iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_LO);
iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_HI);
len -= sizeof(u32);
}
} else {
/* copy buffer */
while (len > 0) {
iowrite32(*ptr, adapter->hw_addr + REG_QMU_DATA_LO);
len -= sizeof(u32);
ptr++;
u32 *ptr = (u32 *)skb->data;
u32 ctrl;
/* the control word, enable IRQ, port 1 and the length */
ctrl = 0x8000 | 0x100 | (len << 16);
ks8842_write32(adapter, 17, ctrl, REG_QMU_DATA_LO);
netdev->stats.tx_bytes += len;
/* copy buffer */
while (len > 0) {
iowrite32(*ptr, adapter->hw_addr + REG_QMU_DATA_LO);
len -= sizeof(u32);
ptr++;
}
}
/* enqueue packet */
@ -363,12 +424,23 @@ static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
static void ks8842_rx_frame(struct net_device *netdev,
struct ks8842_adapter *adapter)
{
u32 status = ks8842_read32(adapter, 17, REG_QMU_DATA_LO);
int len = (status >> 16) & 0x7ff;
u16 status16;
u32 status;
int len;
status &= 0xffff;
netdev_dbg(netdev, "%s - rx_data: status: %x\n", __func__, status);
if (adapter->conf_flags & KS884X_16BIT) {
status16 = ks8842_read16(adapter, 17, REG_QMU_DATA_LO);
len = (int)ks8842_read16(adapter, 17, REG_QMU_DATA_HI);
len &= 0xffff;
netdev_dbg(netdev, "%s - rx_data: status: %x\n",
__func__, status16);
} else {
status = ks8842_read32(adapter, 17, REG_QMU_DATA_LO);
len = (status >> 16) & 0x7ff;
status &= 0xffff;
netdev_dbg(netdev, "%s - rx_data: status: %x\n",
__func__, status);
}
/* check the status */
if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
@ -376,22 +448,32 @@ static void ks8842_rx_frame(struct net_device *netdev,
netdev_dbg(netdev, "%s, got package, len: %d\n", __func__, len);
if (skb) {
u32 *data;
netdev->stats.rx_packets++;
netdev->stats.rx_bytes += len;
if (status & RXSR_MULTICAST)
netdev->stats.multicast++;
data = (u32 *)skb_put(skb, len);
if (adapter->conf_flags & KS884X_16BIT) {
u16 *data16 = (u16 *)skb_put(skb, len);
ks8842_select_bank(adapter, 17);
while (len > 0) {
*data16++ = ioread16(adapter->hw_addr +
REG_QMU_DATA_LO);
*data16++ = ioread16(adapter->hw_addr +
REG_QMU_DATA_HI);
len -= sizeof(u32);
}
} else {
u32 *data = (u32 *)skb_put(skb, len);
ks8842_select_bank(adapter, 17);
while (len > 0) {
*data++ = ioread32(adapter->hw_addr +
REG_QMU_DATA_LO);
len -= sizeof(u32);
ks8842_select_bank(adapter, 17);
while (len > 0) {
*data++ = ioread32(adapter->hw_addr +
REG_QMU_DATA_LO);
len -= sizeof(u32);
}
}
skb->protocol = eth_type_trans(skb, netdev);
netif_rx(skb);
} else
@ -669,6 +751,8 @@ static int __devinit ks8842_probe(struct platform_device *pdev)
adapter->netdev = netdev;
INIT_WORK(&adapter->timeout_work, ks8842_tx_timeout_work);
adapter->hw_addr = ioremap(iomem->start, resource_size(iomem));
adapter->conf_flags = iomem->flags;
if (!adapter->hw_addr)
goto err_ioremap;