* (at your option) any later version.
*
* Additional credits:
- * This code is taken from John Taylor's Sibyte driver and then
+ * This code is taken from John Taylor's Sibyte driver and then
* modified for the AR2313.
*/
#include <linux/pkt_sched.h>
#include <linux/compile.h>
#include <linux/mii.h>
+#include <linux/phy.h>
#include <linux/ethtool.h>
#include <linux/ctype.h>
#include <linux/platform_device.h>
#define CRC_LEN 4
#define RX_OFFSET 2
-#define AR2313_BUFSIZE (AR2313_MTU + ETH_HLEN + CRC_LEN + RX_OFFSET)
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define VLAN_HDR 4
+#else
+#define VLAN_HDR 0
+#endif
+
+#define AR2313_BUFSIZE (AR2313_MTU + VLAN_HDR + ETH_HLEN + CRC_LEN + RX_OFFSET)
#ifdef MODULE
MODULE_LICENSE("GPL");
#define virt_to_phys(x) ((u32)(x) & 0x1fffffff)
// prototypes
-static short armiiread(struct net_device *dev, short phy, short reg);
-static void armiiwrite(struct net_device *dev, short phy, short reg,
- short data);
#ifdef TX_TIMEOUT
static void ar2313_tx_timeout(struct net_device *dev);
#endif
static void ar2313_halt(struct net_device *dev);
static void rx_tasklet_func(unsigned long data);
+static void rx_tasklet_cleanup(struct net_device *dev);
static void ar2313_multicast_list(struct net_device *dev);
+static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum);
+static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value);
+static int mdiobus_reset(struct mii_bus *bus);
+static int mdiobus_probe (struct net_device *dev);
+static void ar2313_adjust_link(struct net_device *dev);
+
#ifndef ERR
#define ERR(fmt, args...) printk("%s: " fmt, __func__, ##args)
#endif
return -ENOMEM;
}
- SET_MODULE_OWNER(dev);
platform_set_drvdata(pdev, dev);
- sp = dev->priv;
+ sp = netdev_priv(dev);
sp->dev = dev;
sp->cfg = pdev->dev.platform_data;
dev->stop = &ar2313_close;
dev->hard_start_xmit = &ar2313_start_xmit;
- dev->get_stats = &ar2313_get_stats;
dev->set_multicast_list = &ar2313_multicast_list;
#ifdef TX_TIMEOUT
dev->tx_timeout = ar2313_tx_timeout;
dev->do_ioctl = &ar2313_ioctl;
// SAMEER: do we need this?
- dev->features |= NETIF_F_SG | NETIF_F_HIGHDMA;
+ dev->features |= NETIF_F_HIGHDMA;
tasklet_init(&sp->rx_tasklet, rx_tasklet_func, (unsigned long) dev);
tasklet_disable(&sp->rx_tasklet);
return (-ENXIO);
}
- /*
- * When there's only one MAC, PHY regs are typically on ENET0,
+ /*
+ * When there's only one MAC, PHY regs are typically on ENET0,
* even though the MAC might be on ENET1.
* Needto remap PHY regs separately in this case
*/
sp->board_idx = BOARD_IDX_STATIC;
if (ar2313_init(dev)) {
- /*
+ /*
* ar2313_init() calls ar2313_init_cleanup() on error.
*/
kfree(dev);
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], dev->irq);
- /* start link poll timer */
- ar2313_setup_timer(dev);
+ sp->mii_bus.priv = dev;
+ sp->mii_bus.read = mdiobus_read;
+ sp->mii_bus.write = mdiobus_write;
+ sp->mii_bus.reset = mdiobus_reset;
+ sp->mii_bus.name = "ar2313_eth_mii";
+ sp->mii_bus.id = 0;
+ sp->mii_bus.irq = kmalloc(sizeof(int), GFP_KERNEL);
+ *sp->mii_bus.irq = PHY_POLL;
+
+ mdiobus_register(&sp->mii_bus);
+
+ if (mdiobus_probe(dev) != 0) {
+ printk(KERN_ERR "ar2313: mdiobus_probe failed");
+ rx_tasklet_cleanup(dev);
+ ar2313_init_cleanup(dev);
+ unregister_netdev(dev);
+ kfree(dev);
+ } else {
+ /* start link poll timer */
+ ar2313_setup_timer(dev);
+ }
return 0;
}
static void ar2313_dump_regs(struct net_device *dev)
{
unsigned int *ptr, i;
- struct ar2313_private *sp = (struct ar2313_private *) dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
ptr = (unsigned int *) sp->eth_regs;
for (i = 0; i < (sizeof(ETHERNET_STRUCT) / sizeof(unsigned int));
#ifdef TX_TIMEOUT
static void ar2313_tx_timeout(struct net_device *dev)
{
- struct ar2313_private *sp = (struct ar2313_private *) dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
unsigned long flags;
#if DEBUG_TX
*/
static void ar2313_multicast_list(struct net_device *dev)
{
- /*
- * Always listen to broadcasts and
- * treat IFF bits independently
+ /*
+ * Always listen to broadcasts and
+ * treat IFF bits independently
*/
- struct ar2313_private *sp = (struct ar2313_private *) dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
unsigned int recognise;
recognise = sp->eth_regs->mac_control;
static void rx_tasklet_cleanup(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
- /*
+ /*
* Tasklet may be scheduled. Need to get it removed from the list
* since we're about to free the struct.
*/
/*
- * Restart the AR2313 ethernet controller.
+ * Restart the AR2313 ethernet controller.
*/
static int ar2313_restart(struct net_device *dev)
{
static void ar2313_free_descriptors(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
if (sp->rx_ring != NULL) {
kfree((void *) KSEG0ADDR(sp->rx_ring));
sp->rx_ring = NULL;
static int ar2313_allocate_descriptors(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
int size;
int j;
ar2313_descr_t *space;
*/
static void ar2313_init_cleanup(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
struct sk_buff *skb;
int j;
static int ar2313_setup_timer(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
init_timer(&sp->link_timer);
static void ar2313_link_timer_fn(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
// see if the link status changed
// This was needed to make sure we set the PHY to the
// autonegotiated value of half or full duplex.
ar2313_check_link(dev);
- // Loop faster when we don't have link.
+ // Loop faster when we don't have link.
// This was needed to speed up the AP bootstrap time.
if (sp->link == 0) {
mod_timer(&sp->link_timer, jiffies + HZ / 2);
static void ar2313_check_link(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
u16 phyData;
- phyData = armiiread(dev, sp->phy, MII_BMSR);
+ phyData = mdiobus_read(&sp->mii_bus, sp->phy, MII_BMSR);
if (sp->phyData != phyData) {
if (phyData & BMSR_LSTATUS) {
/* link is present, ready link partner ability to deterine
u16 reg;
sp->link = 1;
- reg = armiiread(dev, sp->phy, MII_BMCR);
+ reg = mdiobus_read(&sp->mii_bus, sp->phy, MII_BMCR);
if (reg & BMCR_ANENABLE) {
/* auto neg enabled */
- reg = armiiread(dev, sp->phy, MII_LPA);
+ reg = mdiobus_read(&sp->mii_bus, sp->phy, MII_LPA);
duplex = (reg & (LPA_100FULL | LPA_10FULL)) ? 1 : 0;
} else {
/* no auto neg, just read duplex config */
static int ar2313_reset_reg(struct net_device *dev)
{
- struct ar2313_private *sp = (struct ar2313_private *) dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
unsigned int ethsal, ethsah;
unsigned int flags;
static int ar2313_init(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
int ecode = 0;
- /*
+ /*
* Allocate descriptors
*/
if (ar2313_allocate_descriptors(dev)) {
goto init_error;
}
- /*
+ /*
* Get the memory for the skb rings.
*/
if (sp->rx_skb == NULL) {
}
memset(sp->tx_skb, 0, sizeof(struct sk_buff *) * AR2313_DESCR_ENTRIES);
- /*
+ /*
* Set tx_csm before we start receiving interrupts, otherwise
* the interrupt handler might think it is supposed to process
* tx ints before we are up and running, which may cause a null
sp->tx_prd = 0;
sp->tx_csm = 0;
- /*
+ /*
* Zero the stats before starting the interface
*/
- memset(&sp->stats, 0, sizeof(sp->stats));
+ memset(&dev->stats, 0, sizeof(dev->stats));
- /*
+ /*
* We load the ring here as there seem to be no way to tell the
* firmware to wipe the ring without re-initializing it.
*/
ar2313_load_rx_ring(dev, RX_RING_SIZE);
- /*
+ /*
* Init hardware
*/
ar2313_reset_reg(dev);
- /*
+ /*
* Get the IRQ
*/
ecode =
static void ar2313_load_rx_ring(struct net_device *dev, int nr_bufs)
{
- struct ar2313_private *sp = ((struct net_device *) dev)->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
short i, idx;
idx = sp->rx_skbprd;
// partha: create additional room in the front for tx pkt capture
skb_reserve(skb, 32);
- /*
+ /*
* Make sure IP header starts on a fresh cache line.
*/
skb->dev = dev;
static int ar2313_rx_int(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
struct sk_buff *skb, *skb_new;
ar2313_descr_t *rxdesc;
unsigned int status;
idx = sp->cur_rx;
- /* process at most the entire ring and then wait for another interrupt
+ /* process at most the entire ring and then wait for another interrupt
*/
while (1) {
#if DEBUG_RX
printk("%s: rx ERROR %08x\n", __FUNCTION__, status);
#endif
- sp->stats.rx_errors++;
- sp->stats.rx_dropped++;
+ dev->stats.rx_errors++;
+ dev->stats.rx_dropped++;
/* add statistics counters */
if (status & DMA_RX_ERR_CRC)
- sp->stats.rx_crc_errors++;
+ dev->stats.rx_crc_errors++;
if (status & DMA_RX_ERR_COL)
- sp->stats.rx_over_errors++;
+ dev->stats.rx_over_errors++;
if (status & DMA_RX_ERR_LENGTH)
- sp->stats.rx_length_errors++;
+ dev->stats.rx_length_errors++;
if (status & DMA_RX_ERR_RUNT)
- sp->stats.rx_over_errors++;
+ dev->stats.rx_over_errors++;
if (status & DMA_RX_ERR_DESC)
- sp->stats.rx_over_errors++;
+ dev->stats.rx_over_errors++;
} else {
/* alloc new buffer. */
skb_put(skb,
((status >> DMA_RX_LEN_SHIFT) & 0x3fff) - CRC_LEN);
- sp->stats.rx_bytes += skb->len;
+ dev->stats.rx_bytes += skb->len;
skb->protocol = eth_type_trans(skb, dev);
/* pass the packet to upper layers */
netif_rx(skb);
/* reset descriptor's curr_addr */
rxdesc->addr = virt_to_phys(skb_new->data);
- sp->stats.rx_packets++;
+ dev->stats.rx_packets++;
sp->rx_skb[idx] = skb_new;
} else {
- sp->stats.rx_dropped++;
+ dev->stats.rx_dropped++;
}
}
static void ar2313_tx_int(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
u32 idx;
struct sk_buff *skb;
ar2313_descr_t *txdesc;
txdesc->status = 0;
if (status & DMA_TX_ERROR) {
- sp->stats.tx_errors++;
- sp->stats.tx_dropped++;
+ dev->stats.tx_errors++;
+ dev->stats.tx_dropped++;
if (status & DMA_TX_ERR_UNDER)
- sp->stats.tx_fifo_errors++;
+ dev->stats.tx_fifo_errors++;
if (status & DMA_TX_ERR_HB)
- sp->stats.tx_heartbeat_errors++;
+ dev->stats.tx_heartbeat_errors++;
if (status & (DMA_TX_ERR_LOSS | DMA_TX_ERR_LINK))
- sp->stats.tx_carrier_errors++;
+ dev->stats.tx_carrier_errors++;
if (status & (DMA_TX_ERR_LATE |
DMA_TX_ERR_COL |
DMA_TX_ERR_JABBER | DMA_TX_ERR_DEFER))
- sp->stats.tx_aborted_errors++;
+ dev->stats.tx_aborted_errors++;
} else {
/* transmit OK */
- sp->stats.tx_packets++;
+ dev->stats.tx_packets++;
}
skb = sp->tx_skb[idx];
sp->tx_skb[idx] = NULL;
idx = DSC_NEXT(idx);
- sp->stats.tx_bytes += skb->len;
+ dev->stats.tx_bytes += skb->len;
dev_kfree_skb_irq(skb);
}
static void rx_tasklet_func(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
if (sp->unloading) {
return;
static void rx_schedule(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
sp->dma_regs->intr_ena &= ~DMA_STATUS_RI;
static irqreturn_t ar2313_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
unsigned int status, enabled;
/* clear interrupt */
- /*
+ /*
* Don't clear RI bit if currently disabled.
*/
status = sp->dma_regs->status;
if (status & DMA_STATUS_NIS) {
/* normal status */
- /*
+ /*
* Don't schedule rx processing if interrupt
* is already disabled.
*/
static int ar2313_open(struct net_device *dev)
{
- struct ar2313_private *sp;
-
- sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
dev->mtu = 1500;
netif_start_queue(dev);
static void ar2313_halt(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
int j;
tasklet_disable(&sp->rx_tasklet);
static int ar2313_close(struct net_device *dev)
{
#if 0
- /*
+ /*
* Disable interrupts
*/
disable_irq(dev->irq);
- /*
+ /*
* Without (or before) releasing irq and stopping hardware, this
* is an absolute non-sense, by the way. It will be reset instantly
* by the first irq.
static int ar2313_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
ar2313_descr_t *td;
u32 idx;
printk("%s: No space left to Tx\n", __FUNCTION__);
#endif
/* free skbuf and lie to the caller that we sent it out */
- sp->stats.tx_dropped++;
+ dev->stats.tx_dropped++;
dev_kfree_skb(skb);
/* restart transmitter in case locked */
return 0;
}
-static int netdev_get_ecmd(struct net_device *dev,
- struct ethtool_cmd *ecmd)
-{
- struct ar2313_private *np = dev->priv;
- u32 tmp;
-
- ecmd->supported =
- (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
- SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
- SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
-
- ecmd->port = PORT_TP;
- /* only supports internal transceiver */
- ecmd->transceiver = XCVR_INTERNAL;
- /* not sure what this is for */
- ecmd->phy_address = 1;
-
- ecmd->advertising = ADVERTISED_MII;
- tmp = armiiread(dev, np->phy, MII_ADVERTISE);
- if (tmp & ADVERTISE_10HALF)
- ecmd->advertising |= ADVERTISED_10baseT_Half;
- if (tmp & ADVERTISE_10FULL)
- ecmd->advertising |= ADVERTISED_10baseT_Full;
- if (tmp & ADVERTISE_100HALF)
- ecmd->advertising |= ADVERTISED_100baseT_Half;
- if (tmp & ADVERTISE_100FULL)
- ecmd->advertising |= ADVERTISED_100baseT_Full;
-
- tmp = armiiread(dev, np->phy, MII_BMCR);
- if (tmp & BMCR_ANENABLE) {
- ecmd->advertising |= ADVERTISED_Autoneg;
- ecmd->autoneg = AUTONEG_ENABLE;
- } else {
- ecmd->autoneg = AUTONEG_DISABLE;
- }
-
- if (ecmd->autoneg == AUTONEG_ENABLE) {
- tmp = armiiread(dev, np->phy, MII_LPA);
- if (tmp & (LPA_100FULL | LPA_10FULL)) {
- ecmd->duplex = DUPLEX_FULL;
- } else {
- ecmd->duplex = DUPLEX_HALF;
- }
- if (tmp & (LPA_100FULL | LPA_100HALF)) {
- ecmd->speed = SPEED_100;
- } else {
- ecmd->speed = SPEED_10;
- }
- } else {
- if (tmp & BMCR_FULLDPLX) {
- ecmd->duplex = DUPLEX_FULL;
- } else {
- ecmd->duplex = DUPLEX_HALF;
- }
- if (tmp & BMCR_SPEED100) {
- ecmd->speed = SPEED_100;
- } else {
- ecmd->speed = SPEED_10;
- }
- }
-
- /* ignore maxtxpkt, maxrxpkt for now */
-
- return 0;
-}
-
-static int netdev_set_ecmd(struct net_device *dev,
- struct ethtool_cmd *ecmd)
-{
- struct ar2313_private *np = dev->priv;
- u32 tmp;
-
- if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
- return -EINVAL;
- if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
- return -EINVAL;
- if (ecmd->port != PORT_TP)
- return -EINVAL;
- if (ecmd->transceiver != XCVR_INTERNAL)
- return -EINVAL;
- if (ecmd->autoneg != AUTONEG_DISABLE
- && ecmd->autoneg != AUTONEG_ENABLE)
- return -EINVAL;
- /* ignore phy_address, maxtxpkt, maxrxpkt for now */
-
- /* WHEW! now lets bang some bits */
-
- tmp = armiiread(dev, np->phy, MII_BMCR);
- if (ecmd->autoneg == AUTONEG_ENABLE) {
- /* turn on autonegotiation */
- tmp |= BMCR_ANENABLE;
- printk("%s: Enabling auto-neg\n", dev->name);
- } else {
- /* turn off auto negotiation, set speed and duplexity */
- tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
- if (ecmd->speed == SPEED_100)
- tmp |= BMCR_SPEED100;
- if (ecmd->duplex == DUPLEX_FULL)
- tmp |= BMCR_FULLDPLX;
- printk("%s: Hard coding %d/%s\n", dev->name,
- (ecmd->speed == SPEED_100) ? 100 : 10,
- (ecmd->duplex == DUPLEX_FULL) ? "full" : "half");
- }
- armiiwrite(dev, np->phy, MII_BMCR, tmp);
- np->phyData = 0;
- return 0;
-}
-
-static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
-{
- struct ar2313_private *np = dev->priv;
- u32 cmd;
-
- if (get_user(cmd, (u32 *) useraddr))
- return -EFAULT;
-
- switch (cmd) {
- /* get settings */
- case ETHTOOL_GSET:{
- struct ethtool_cmd ecmd = { ETHTOOL_GSET };
- spin_lock_irq(&np->lock);
- netdev_get_ecmd(dev, &ecmd);
- spin_unlock_irq(&np->lock);
- if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
- return -EFAULT;
- return 0;
- }
- /* set settings */
- case ETHTOOL_SSET:{
- struct ethtool_cmd ecmd;
- int r;
- if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
- return -EFAULT;
- spin_lock_irq(&np->lock);
- r = netdev_set_ecmd(dev, &ecmd);
- spin_unlock_irq(&np->lock);
- return r;
- }
- /* restart autonegotiation */
- case ETHTOOL_NWAY_RST:{
- int tmp;
- int r = -EINVAL;
- /* if autoneg is off, it's an error */
- tmp = armiiread(dev, np->phy, MII_BMCR);
- if (tmp & BMCR_ANENABLE) {
- tmp |= (BMCR_ANRESTART);
- armiiwrite(dev, np->phy, MII_BMCR, tmp);
- r = 0;
- }
- return r;
- }
- /* get link status */
- case ETHTOOL_GLINK:{
- struct ethtool_value edata = { ETHTOOL_GLINK };
- edata.data =
- (armiiread(dev, np->phy, MII_BMSR) & BMSR_LSTATUS) ? 1 : 0;
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
- }
- }
-
- return -EOPNOTSUPP;
-}
-
static int ar2313_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct mii_ioctl_data *data = (struct mii_ioctl_data *) &ifr->ifr_data;
+ struct ar2313_private *sp = netdev_priv(dev);
+ int ret;
switch (cmd) {
case SIOCETHTOOL:
- return netdev_ethtool_ioctl(dev, (void *) ifr->ifr_data);
-
- case SIOCGMIIPHY: /* Get address of MII PHY in use. */
- data->phy_id = 1;
- /* Fall Through */
-
- case SIOCGMIIREG: /* Read MII PHY register. */
- data->val_out = armiiread(dev, data->phy_id & 0x1f,
- data->reg_num & 0x1f);
- return 0;
- case SIOCSMIIREG: /* Write MII PHY register. */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- armiiwrite(dev, data->phy_id & 0x1f,
- data->reg_num & 0x1f, data->val_in);
- return 0;
+ spin_lock_irq(&sp->lock);
+ ret = phy_ethtool_ioctl(sp->phy_dev, (void *) ifr->ifr_data);
+ spin_unlock_irq(&sp->lock);
+ return ret;
case SIOCSIFHWADDR:
if (copy_from_user
return -EFAULT;
return 0;
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return phy_mii_ioctl(sp->phy_dev, data, cmd);
+
default:
break;
}
return -EOPNOTSUPP;
}
-static struct net_device_stats *ar2313_get_stats(struct net_device *dev)
+static void ar2313_adjust_link(struct net_device *dev)
{
- struct ar2313_private *sp = dev->priv;
- return &sp->stats;
-}
+ struct ar2313_private *sp = netdev_priv(dev);
+ unsigned int mc;
+ if (!sp->phy_dev->link)
+ return;
+
+ if (sp->phy_dev->duplex != sp->oldduplex) {
+ mc = readl(&sp->eth_regs->mac_control);
+ mc &= ~(MAC_CONTROL_F | MAC_CONTROL_DRO);
+ if (sp->phy_dev->duplex)
+ mc |= MAC_CONTROL_F;
+ else
+ mc |= MAC_CONTROL_DRO;
+ writel(mc, &sp->eth_regs->mac_control);
+ sp->oldduplex = sp->phy_dev->duplex;
+ }
+}
#define MII_ADDR(phy, reg) \
((reg << MII_ADDR_REG_SHIFT) | (phy << MII_ADDR_PHY_SHIFT))
-static short armiiread(struct net_device *dev, short phy, short reg)
+static int
+mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
{
- struct ar2313_private *sp = (struct ar2313_private *) dev->priv;
+ struct net_device *const dev = bus->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
volatile ETHERNET_STRUCT *ethernet = sp->phy_regs;
- ethernet->mii_addr = MII_ADDR(phy, reg);
+ ethernet->mii_addr = MII_ADDR(phy_addr, regnum);
while (ethernet->mii_addr & MII_ADDR_BUSY);
return (ethernet->mii_data >> MII_DATA_SHIFT);
}
-static void
-armiiwrite(struct net_device *dev, short phy, short reg, short data)
+static int
+mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
+ u16 value)
{
- struct ar2313_private *sp = (struct ar2313_private *) dev->priv;
+ struct net_device *const dev = bus->priv;
+ struct ar2313_private *sp = netdev_priv(dev);
volatile ETHERNET_STRUCT *ethernet = sp->phy_regs;
while (ethernet->mii_addr & MII_ADDR_BUSY);
- ethernet->mii_data = data << MII_DATA_SHIFT;
- ethernet->mii_addr = MII_ADDR(phy, reg) | MII_ADDR_WRITE;
+ ethernet->mii_data = value << MII_DATA_SHIFT;
+ ethernet->mii_addr = MII_ADDR(phy_addr, regnum) | MII_ADDR_WRITE;
+
+ return 0;
+}
+
+static int mdiobus_reset(struct mii_bus *bus)
+{
+ struct net_device *const dev = bus->priv;
+
+ ar2313_reset_reg(dev);
+
+ return 0;
}
+
+static int mdiobus_probe (struct net_device *dev)
+{
+ struct ar2313_private *const sp = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ int phy_addr;
+
+ /* find the first (lowest address) PHY on the current MAC's MII bus */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
+ if (sp->mii_bus.phy_map[phy_addr]) {
+ phydev = sp->mii_bus.phy_map[phy_addr];
+ break; /* break out with first one found */
+ }
+
+ if (!phydev) {
+ printk (KERN_ERR "ar2313:%s: no PHY found\n", dev->name);
+ return -1;
+ }
+
+ /* now we are supposed to have a proper phydev, to attach to... */
+ BUG_ON(!phydev);
+ BUG_ON(phydev->attached_dev);
+
+ phydev = phy_connect(dev, phydev->dev.bus_id, &ar2313_adjust_link, 0,
+ PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(phydev)) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(phydev);
+ }
+
+ /* mask with MAC supported features */
+ phydev->supported &= (SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg
+ /* | SUPPORTED_Pause | SUPPORTED_Asym_Pause */
+ | SUPPORTED_MII
+ | SUPPORTED_TP);
+
+ phydev->advertising = phydev->supported;
+
+ sp->oldduplex = -1;
+ sp->phy_dev = phydev;
+
+ printk(KERN_INFO "%s: attached PHY driver [%s] "
+ "(mii_bus:phy_addr=%s)\n",
+ dev->name, phydev->drv->name, phydev->dev.bus_id);
+
+ return 0;
+}
+