ax88796: use generic mdio_bitbang driver

..instead of using hand-crafted and not proper working version.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde 2011-02-21 12:41:55 +01:00
parent c9218c3a82
commit f6d7f2c60d
2 changed files with 201 additions and 205 deletions

View File

@ -238,8 +238,8 @@ source "drivers/net/arm/Kconfig"
config AX88796 config AX88796
tristate "ASIX AX88796 NE2000 clone support" tristate "ASIX AX88796 NE2000 clone support"
depends on ARM || MIPS || SUPERH depends on ARM || MIPS || SUPERH
select CRC32 select PHYLIB
select MII select MDIO_BITBANG
help help
AX88796 driver, using platform bus to provide AX88796 driver, using platform bus to provide
chip detection and resources chip detection and resources

View File

@ -24,7 +24,8 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/mii.h> #include <linux/mdio-bitbang.h>
#include <linux/phy.h>
#include <linux/eeprom_93cx6.h> #include <linux/eeprom_93cx6.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -32,8 +33,6 @@
#include <asm/system.h> #include <asm/system.h>
static int phy_debug;
/* Rename the lib8390.c functions to show that they are in this driver */ /* Rename the lib8390.c functions to show that they are in this driver */
#define __ei_open ax_ei_open #define __ei_open ax_ei_open
#define __ei_close ax_ei_close #define __ei_close ax_ei_close
@ -78,14 +77,20 @@ static unsigned char version[] = "ax88796.c: Copyright 2005,2007 Simtec Electron
#define NESM_START_PG 0x40 /* First page of TX buffer */ #define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ #define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
#define AX_GPOC_PPDSET BIT(6)
/* device private data */ /* device private data */
struct ax_device { struct ax_device {
struct timer_list mii_timer; struct mii_bus *mii_bus;
spinlock_t mii_lock; struct mdiobb_ctrl bb_ctrl;
struct mii_if_info mii; struct phy_device *phy_dev;
void __iomem *addr_memr;
u8 reg_memr;
int link;
int speed;
int duplex;
u32 msg_enable;
void __iomem *map2; void __iomem *map2;
const struct ax_plat_data *plat; const struct ax_plat_data *plat;
@ -313,159 +318,84 @@ static void ax_block_output(struct net_device *dev, int count,
#define AX_MEMR_EEO BIT(6) #define AX_MEMR_EEO BIT(6)
#define AX_MEMR_EECLK BIT(7) #define AX_MEMR_EECLK BIT(7)
/* static void ax_handle_link_change(struct net_device *dev)
* ax_mii_ei_outbits
*
* write the specified set of bits to the phy
*/
static void
ax_mii_ei_outbits(struct net_device *dev, unsigned int bits, int len)
{ {
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *memr_addr = (void __iomem *)dev->base_addr + AX_MEMR;
unsigned int memr;
/* clock low, data to output mode */
memr = ei_inb(memr_addr);
memr &= ~(AX_MEMR_MDC | AX_MEMR_MDIR);
ei_outb(memr, memr_addr);
for (len--; len >= 0; len--) {
if (bits & (1 << len))
memr |= AX_MEMR_MDO;
else
memr &= ~AX_MEMR_MDO;
ei_outb(memr, memr_addr);
/* clock high */
ei_outb(memr | AX_MEMR_MDC, memr_addr);
udelay(1);
/* clock low */
ei_outb(memr, memr_addr);
}
/* leaves the clock line low, mdir input */
memr |= AX_MEMR_MDIR;
ei_outb(memr, (void __iomem *)dev->base_addr + AX_MEMR);
}
/*
* ax_phy_ei_inbits
*
* read a specified number of bits from the phy
*/
static unsigned int
ax_phy_ei_inbits(struct net_device *dev, int no)
{
struct ei_device *ei_local = netdev_priv(dev);
void __iomem *memr_addr = (void __iomem *)dev->base_addr + AX_MEMR;
unsigned int memr;
unsigned int result = 0;
/* clock low, data to input mode */
memr = ei_inb(memr_addr);
memr &= ~AX_MEMR_MDC;
memr |= AX_MEMR_MDIR;
ei_outb(memr, memr_addr);
for (no--; no >= 0; no--) {
ei_outb(memr | AX_MEMR_MDC, memr_addr);
udelay(1);
if (ei_inb(memr_addr) & AX_MEMR_MDI)
result |= (1 << no);
ei_outb(memr, memr_addr);
}
return result;
}
/*
* ax_phy_issueaddr
*
* use the low level bit shifting routines to send the address
* and command to the specified phy
*/
static void
ax_phy_issueaddr(struct net_device *dev, int phy_addr, int reg, int opc)
{
if (phy_debug)
netdev_dbg(dev, "%s: dev %p, %04x, %04x, %d\n",
__func__, dev, phy_addr, reg, opc);
ax_mii_ei_outbits(dev, 0x3f, 6); /* pre-amble */
ax_mii_ei_outbits(dev, 1, 2); /* frame-start */
ax_mii_ei_outbits(dev, opc, 2); /* op code */
ax_mii_ei_outbits(dev, phy_addr, 5); /* phy address */
ax_mii_ei_outbits(dev, reg, 5); /* reg address */
}
static int
ax_phy_read(struct net_device *dev, int phy_addr, int reg)
{
struct ei_device *ei_local = netdev_priv(dev);
unsigned long flags;
unsigned int result;
spin_lock_irqsave(&ei_local->page_lock, flags);
ax_phy_issueaddr(dev, phy_addr, reg, 2);
result = ax_phy_ei_inbits(dev, 17);
result &= ~(3 << 16);
spin_unlock_irqrestore(&ei_local->page_lock, flags);
if (phy_debug)
netdev_dbg(dev, "%s: %04x.%04x => read %04x\n", __func__,
phy_addr, reg, result);
return result;
}
static void
ax_phy_write(struct net_device *dev, int phy_addr, int reg, int value)
{
struct ei_device *ei = netdev_priv(dev);
unsigned long flags;
netdev_dbg(dev, "%s: %p, %04x, %04x %04x\n",
__func__, dev, phy_addr, reg, value);
spin_lock_irqsave(&ei->page_lock, flags);
ax_phy_issueaddr(dev, phy_addr, reg, 1);
ax_mii_ei_outbits(dev, 2, 2); /* send TA */
ax_mii_ei_outbits(dev, value, 16);
spin_unlock_irqrestore(&ei->page_lock, flags);
}
static void ax_mii_expiry(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct ax_device *ax = to_ax_dev(dev); struct ax_device *ax = to_ax_dev(dev);
unsigned long flags; struct phy_device *phy_dev = ax->phy_dev;
int status_change = 0;
spin_lock_irqsave(&ax->mii_lock, flags); if (phy_dev->link && ((ax->speed != phy_dev->speed) ||
mii_check_media(&ax->mii, netif_msg_link(ax), 0); (ax->duplex != phy_dev->duplex))) {
spin_unlock_irqrestore(&ax->mii_lock, flags);
if (ax->running) { ax->speed = phy_dev->speed;
ax->mii_timer.expires = jiffies + HZ*2; ax->duplex = phy_dev->duplex;
add_timer(&ax->mii_timer); status_change = 1;
} }
if (phy_dev->link != ax->link) {
if (!phy_dev->link) {
ax->speed = 0;
ax->duplex = -1;
}
ax->link = phy_dev->link;
status_change = 1;
}
if (status_change)
phy_print_status(phy_dev);
}
static int ax_mii_probe(struct net_device *dev)
{
struct ax_device *ax = to_ax_dev(dev);
struct phy_device *phy_dev = NULL;
int ret;
/* find the first phy */
phy_dev = phy_find_first(ax->mii_bus);
if (!phy_dev) {
netdev_err(dev, "no PHY found\n");
return -ENODEV;
}
ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, 0,
PHY_INTERFACE_MODE_MII);
if (ret) {
netdev_err(dev, "Could not attach to PHY\n");
return ret;
}
/* mask with MAC supported features */
phy_dev->supported &= PHY_BASIC_FEATURES;
phy_dev->advertising = phy_dev->supported;
ax->phy_dev = phy_dev;
netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq);
return 0;
}
static void ax_phy_switch(struct net_device *dev, int on)
{
struct ei_device *ei_local = netdev_priv(dev);
struct ax_device *ax = to_ax_dev(dev);
u8 reg_gpoc = ax->plat->gpoc_val;
if (!!on)
reg_gpoc &= ~AX_GPOC_PPDSET;
else
reg_gpoc |= AX_GPOC_PPDSET;
ei_outb(reg_gpoc, ei_local->mem + EI_SHIFT(0x17));
} }
static int ax_open(struct net_device *dev) static int ax_open(struct net_device *dev)
{ {
struct ax_device *ax = to_ax_dev(dev); struct ax_device *ax = to_ax_dev(dev);
struct ei_device *ei_local = netdev_priv(dev);
int ret; int ret;
netdev_dbg(dev, "open\n"); netdev_dbg(dev, "open\n");
@ -473,50 +403,48 @@ static int ax_open(struct net_device *dev)
ret = request_irq(dev->irq, ax_ei_interrupt, ax->irqflags, ret = request_irq(dev->irq, ax_ei_interrupt, ax->irqflags,
dev->name, dev); dev->name, dev);
if (ret) if (ret)
return ret; goto failed_request_irq;
ret = ax_ei_open(dev);
if (ret) {
free_irq(dev->irq, dev);
return ret;
}
/* turn the phy on (if turned off) */ /* turn the phy on (if turned off) */
ax_phy_switch(dev, 1);
ret = ax_mii_probe(dev);
if (ret)
goto failed_mii_probe;
phy_start(ax->phy_dev);
ret = ax_ei_open(dev);
if (ret)
goto failed_ax_ei_open;
ei_outb(ax->plat->gpoc_val, ei_local->mem + EI_SHIFT(0x17));
ax->running = 1; ax->running = 1;
/* start the MII timer */
init_timer(&ax->mii_timer);
ax->mii_timer.expires = jiffies + 1;
ax->mii_timer.data = (unsigned long) dev;
ax->mii_timer.function = ax_mii_expiry;
add_timer(&ax->mii_timer);
return 0; return 0;
failed_ax_ei_open:
phy_disconnect(ax->phy_dev);
failed_mii_probe:
ax_phy_switch(dev, 0);
free_irq(dev->irq, dev);
failed_request_irq:
return ret;
} }
static int ax_close(struct net_device *dev) static int ax_close(struct net_device *dev)
{ {
struct ax_device *ax = to_ax_dev(dev); struct ax_device *ax = to_ax_dev(dev);
struct ei_device *ei_local = netdev_priv(dev);
netdev_dbg(dev, "close\n"); netdev_dbg(dev, "close\n");
/* turn the phy off */
ei_outb(ax->plat->gpoc_val | (1 << 6),
ei_local->mem + EI_SHIFT(0x17));
ax->running = 0; ax->running = 0;
wmb(); wmb();
del_timer_sync(&ax->mii_timer);
ax_ei_close(dev); ax_ei_close(dev);
/* turn the phy off */
ax_phy_switch(dev, 0);
phy_disconnect(ax->phy_dev);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
return 0; return 0;
} }
@ -524,17 +452,15 @@ static int ax_close(struct net_device *dev)
static int ax_ioctl(struct net_device *dev, struct ifreq *req, int cmd) static int ax_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{ {
struct ax_device *ax = to_ax_dev(dev); struct ax_device *ax = to_ax_dev(dev);
unsigned long flags; struct phy_device *phy_dev = ax->phy_dev;
int rc;
if (!netif_running(dev)) if (!netif_running(dev))
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&ax->mii_lock, flags); if (!phy_dev)
rc = generic_mii_ioctl(&ax->mii, if_mii(req), cmd, NULL); return -ENODEV;
spin_unlock_irqrestore(&ax->mii_lock, flags);
return rc; return phy_mii_ioctl(phy_dev, req, cmd);
} }
/* ethtool ops */ /* ethtool ops */
@ -552,46 +478,30 @@ static void ax_get_drvinfo(struct net_device *dev,
static int ax_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int ax_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
struct ax_device *ax = to_ax_dev(dev); struct ax_device *ax = to_ax_dev(dev);
unsigned long flags; struct phy_device *phy_dev = ax->phy_dev;
spin_lock_irqsave(&ax->mii_lock, flags); if (!phy_dev)
mii_ethtool_gset(&ax->mii, cmd); return -ENODEV;
spin_unlock_irqrestore(&ax->mii_lock, flags);
return 0; return phy_ethtool_gset(phy_dev, cmd);
} }
static int ax_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int ax_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
struct ax_device *ax = to_ax_dev(dev); struct ax_device *ax = to_ax_dev(dev);
unsigned long flags; struct phy_device *phy_dev = ax->phy_dev;
int rc;
spin_lock_irqsave(&ax->mii_lock, flags); if (!phy_dev)
rc = mii_ethtool_sset(&ax->mii, cmd); return -ENODEV;
spin_unlock_irqrestore(&ax->mii_lock, flags);
return rc; return phy_ethtool_sset(phy_dev, cmd);
}
static int ax_nway_reset(struct net_device *dev)
{
struct ax_device *ax = to_ax_dev(dev);
return mii_nway_restart(&ax->mii);
}
static u32 ax_get_link(struct net_device *dev)
{
struct ax_device *ax = to_ax_dev(dev);
return mii_link_ok(&ax->mii);
} }
static const struct ethtool_ops ax_ethtool_ops = { static const struct ethtool_ops ax_ethtool_ops = {
.get_drvinfo = ax_get_drvinfo, .get_drvinfo = ax_get_drvinfo,
.get_settings = ax_get_settings, .get_settings = ax_get_settings,
.set_settings = ax_set_settings, .set_settings = ax_set_settings,
.nway_reset = ax_nway_reset, .get_link = ethtool_op_get_link,
.get_link = ax_get_link,
}; };
#ifdef CONFIG_AX88796_93CX6 #ifdef CONFIG_AX88796_93CX6
@ -642,8 +552,102 @@ static const struct net_device_ops ax_netdev_ops = {
#endif #endif
}; };
static void ax_bb_mdc(struct mdiobb_ctrl *ctrl, int level)
{
struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
if (level)
ax->reg_memr |= AX_MEMR_MDC;
else
ax->reg_memr &= ~AX_MEMR_MDC;
ei_outb(ax->reg_memr, ax->addr_memr);
}
static void ax_bb_dir(struct mdiobb_ctrl *ctrl, int output)
{
struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
if (output)
ax->reg_memr &= ~AX_MEMR_MDIR;
else
ax->reg_memr |= AX_MEMR_MDIR;
ei_outb(ax->reg_memr, ax->addr_memr);
}
static void ax_bb_set_data(struct mdiobb_ctrl *ctrl, int value)
{
struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
if (value)
ax->reg_memr |= AX_MEMR_MDO;
else
ax->reg_memr &= ~AX_MEMR_MDO;
ei_outb(ax->reg_memr, ax->addr_memr);
}
static int ax_bb_get_data(struct mdiobb_ctrl *ctrl)
{
struct ax_device *ax = container_of(ctrl, struct ax_device, bb_ctrl);
int reg_memr = ei_inb(ax->addr_memr);
return reg_memr & AX_MEMR_MDI ? 1 : 0;
}
static struct mdiobb_ops bb_ops = {
.owner = THIS_MODULE,
.set_mdc = ax_bb_mdc,
.set_mdio_dir = ax_bb_dir,
.set_mdio_data = ax_bb_set_data,
.get_mdio_data = ax_bb_get_data,
};
/* setup code */ /* setup code */
static int ax_mii_init(struct net_device *dev)
{
struct platform_device *pdev = to_platform_device(dev->dev.parent);
struct ei_device *ei_local = netdev_priv(dev);
struct ax_device *ax = to_ax_dev(dev);
int err, i;
ax->bb_ctrl.ops = &bb_ops;
ax->addr_memr = ei_local->mem + AX_MEMR;
ax->mii_bus = alloc_mdio_bitbang(&ax->bb_ctrl);
if (!ax->mii_bus) {
err = -ENOMEM;
goto out;
}
ax->mii_bus->name = "ax88796_mii_bus";
ax->mii_bus->parent = dev->dev.parent;
snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
if (!ax->mii_bus->irq) {
err = -ENOMEM;
goto out_free_mdio_bitbang;
}
for (i = 0; i < PHY_MAX_ADDR; i++)
ax->mii_bus->irq[i] = PHY_POLL;
err = mdiobus_register(ax->mii_bus);
if (err)
goto out_free_irq;
return 0;
out_free_irq:
kfree(ax->mii_bus->irq);
out_free_mdio_bitbang:
free_mdio_bitbang(ax->mii_bus);
out:
return err;
}
static void ax_initial_setup(struct net_device *dev, struct ei_device *ei_local) static void ax_initial_setup(struct net_device *dev, struct ei_device *ei_local)
{ {
void __iomem *ioaddr = ei_local->mem; void __iomem *ioaddr = ei_local->mem;
@ -763,15 +767,9 @@ static int ax_init_dev(struct net_device *dev)
dev->netdev_ops = &ax_netdev_ops; dev->netdev_ops = &ax_netdev_ops;
dev->ethtool_ops = &ax_ethtool_ops; dev->ethtool_ops = &ax_ethtool_ops;
ax->msg_enable = NETIF_MSG_LINK; ret = ax_mii_init(dev);
ax->mii.phy_id_mask = 0x1f; if (ret)
ax->mii.reg_num_mask = 0x1f; goto out_irq;
ax->mii.phy_id = 0x10; /* onboard phy */
ax->mii.force_media = 0;
ax->mii.full_duplex = 0;
ax->mii.mdio_read = ax_phy_read;
ax->mii.mdio_write = ax_phy_write;
ax->mii.dev = dev;
ax_NS8390_init(dev, 0); ax_NS8390_init(dev, 0);
@ -842,8 +840,6 @@ static int ax_probe(struct platform_device *pdev)
ei_local = netdev_priv(dev); ei_local = netdev_priv(dev);
ax = to_ax_dev(dev); ax = to_ax_dev(dev);
spin_lock_init(&ax->mii_lock);
ax->plat = pdev->dev.platform_data; ax->plat = pdev->dev.platform_data;
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);