mirror of
https://github.com/adulau/aha.git
synced 2025-01-04 07:03:38 +00:00
[PATCH] sis190: new PHY detection code.
New PHY detection code. Signed-off-by: Francois Romieu <romieu@fr.zoreil.com> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
This commit is contained in:
parent
fc10c39d79
commit
fcb9821d3d
1 changed files with 209 additions and 7 deletions
|
@ -43,6 +43,10 @@
|
||||||
#define net_tx_err(p, arg...) if (netif_msg_tx_err(p)) \
|
#define net_tx_err(p, arg...) if (netif_msg_tx_err(p)) \
|
||||||
printk(arg)
|
printk(arg)
|
||||||
|
|
||||||
|
#define PHY_MAX_ADDR 32
|
||||||
|
#define PHY_ID_ANY 0x1f
|
||||||
|
#define MII_REG_ANY 0x1f
|
||||||
|
|
||||||
#ifdef CONFIG_SIS190_NAPI
|
#ifdef CONFIG_SIS190_NAPI
|
||||||
#define NAPI_SUFFIX "-NAPI"
|
#define NAPI_SUFFIX "-NAPI"
|
||||||
#else
|
#else
|
||||||
|
@ -295,6 +299,33 @@ struct sis190_private {
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
u32 msg_enable;
|
u32 msg_enable;
|
||||||
struct mii_if_info mii_if;
|
struct mii_if_info mii_if;
|
||||||
|
struct list_head first_phy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sis190_phy {
|
||||||
|
struct list_head list;
|
||||||
|
int phy_id;
|
||||||
|
u16 id[2];
|
||||||
|
u16 status;
|
||||||
|
u8 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sis190_phy_type {
|
||||||
|
UNKNOWN = 0x00,
|
||||||
|
HOME = 0x01,
|
||||||
|
LAN = 0x02,
|
||||||
|
MIX = 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mii_chip_info {
|
||||||
|
const char *name;
|
||||||
|
u16 id[2];
|
||||||
|
unsigned int type;
|
||||||
|
} mii_chip_table[] = {
|
||||||
|
{ "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN },
|
||||||
|
{ "Agere PHY ET1101B", { 0x0282, 0xf010 }, LAN },
|
||||||
|
{ "Marvell PHY 88E1111", { 0x0141, 0x0cc0 }, LAN },
|
||||||
|
{ NULL, }
|
||||||
};
|
};
|
||||||
|
|
||||||
const static struct {
|
const static struct {
|
||||||
|
@ -1174,6 +1205,177 @@ static struct net_device_stats *sis190_get_stats(struct net_device *dev)
|
||||||
return &tp->stats;
|
return &tp->stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sis190_free_phy(struct list_head *first_phy)
|
||||||
|
{
|
||||||
|
struct sis190_phy *cur, *next;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(cur, next, first_phy, list) {
|
||||||
|
kfree(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sis190_default_phy - Select default PHY for sis190 mac.
|
||||||
|
* @dev: the net device to probe for
|
||||||
|
*
|
||||||
|
* Select first detected PHY with link as default.
|
||||||
|
* If no one is link on, select PHY whose types is HOME as default.
|
||||||
|
* If HOME doesn't exist, select LAN.
|
||||||
|
*/
|
||||||
|
static u16 sis190_default_phy(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sis190_phy *phy, *phy_home, *phy_default, *phy_lan;
|
||||||
|
struct sis190_private *tp = netdev_priv(dev);
|
||||||
|
struct mii_if_info *mii_if = &tp->mii_if;
|
||||||
|
void __iomem *ioaddr = tp->mmio_addr;
|
||||||
|
u16 status;
|
||||||
|
|
||||||
|
phy_home = phy_default = phy_lan = NULL;
|
||||||
|
|
||||||
|
list_for_each_entry(phy, &tp->first_phy, list) {
|
||||||
|
status = mdio_read_latched(ioaddr, phy->phy_id, MII_BMSR);
|
||||||
|
|
||||||
|
// Link ON & Not select default PHY & not ghost PHY.
|
||||||
|
if ((status & BMSR_LSTATUS) &&
|
||||||
|
!phy_default &&
|
||||||
|
(phy->type != UNKNOWN)) {
|
||||||
|
phy_default = phy;
|
||||||
|
} else {
|
||||||
|
status = mdio_read(ioaddr, phy->phy_id, MII_BMCR);
|
||||||
|
mdio_write(ioaddr, phy->phy_id, MII_BMCR,
|
||||||
|
status | BMCR_ANENABLE | BMCR_ISOLATE);
|
||||||
|
if (phy->type == HOME)
|
||||||
|
phy_home = phy;
|
||||||
|
else if (phy->type == LAN)
|
||||||
|
phy_lan = phy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!phy_default) {
|
||||||
|
if (phy_home)
|
||||||
|
phy_default = phy_home;
|
||||||
|
else if (phy_lan)
|
||||||
|
phy_default = phy_lan;
|
||||||
|
else
|
||||||
|
phy_default = list_entry(&tp->first_phy,
|
||||||
|
struct sis190_phy, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mii_if->phy_id != phy_default->phy_id) {
|
||||||
|
mii_if->phy_id = phy_default->phy_id;
|
||||||
|
net_probe(tp, KERN_INFO
|
||||||
|
"%s: Using transceiver at address %d as default.\n",
|
||||||
|
dev->name, mii_if->phy_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
status = mdio_read(ioaddr, mii_if->phy_id, MII_BMCR);
|
||||||
|
status &= (~BMCR_ISOLATE);
|
||||||
|
|
||||||
|
mdio_write(ioaddr, mii_if->phy_id, MII_BMCR, status);
|
||||||
|
status = mdio_read_latched(ioaddr, mii_if->phy_id, MII_BMSR);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sis190_init_phy(struct net_device *dev, struct sis190_private *tp,
|
||||||
|
struct sis190_phy *phy, unsigned int phy_id,
|
||||||
|
u16 mii_status)
|
||||||
|
{
|
||||||
|
void __iomem *ioaddr = tp->mmio_addr;
|
||||||
|
struct mii_chip_info *p;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&phy->list);
|
||||||
|
phy->status = mii_status;
|
||||||
|
phy->phy_id = phy_id;
|
||||||
|
|
||||||
|
phy->id[0] = mdio_read(ioaddr, phy_id, MII_PHYSID1);
|
||||||
|
phy->id[1] = mdio_read(ioaddr, phy_id, MII_PHYSID2);
|
||||||
|
|
||||||
|
for (p = mii_chip_table; p->type; p++) {
|
||||||
|
if ((p->id[0] == phy->id[0]) &&
|
||||||
|
(p->id[1] == (phy->id[1] & 0xfff0))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->id[1]) {
|
||||||
|
phy->type = (p->type == MIX) ?
|
||||||
|
((mii_status & (BMSR_100FULL | BMSR_100HALF)) ?
|
||||||
|
LAN : HOME) : p->type;
|
||||||
|
} else
|
||||||
|
phy->type = UNKNOWN;
|
||||||
|
|
||||||
|
net_probe(tp, KERN_INFO "%s: %s transceiver at address %d.\n",
|
||||||
|
dev->name, (phy->type == UNKNOWN) ? "Unknown PHY" : p->name,
|
||||||
|
phy_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sis190_mii_probe - Probe MII PHY for sis190
|
||||||
|
* @dev: the net device to probe for
|
||||||
|
*
|
||||||
|
* Search for total of 32 possible mii phy addresses.
|
||||||
|
* Identify and set current phy if found one,
|
||||||
|
* return error if it failed to found.
|
||||||
|
*/
|
||||||
|
static int __devinit sis190_mii_probe(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sis190_private *tp = netdev_priv(dev);
|
||||||
|
struct mii_if_info *mii_if = &tp->mii_if;
|
||||||
|
void __iomem *ioaddr = tp->mmio_addr;
|
||||||
|
int phy_id;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&tp->first_phy);
|
||||||
|
|
||||||
|
for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
|
||||||
|
struct sis190_phy *phy;
|
||||||
|
u16 status;
|
||||||
|
|
||||||
|
status = mdio_read_latched(ioaddr, phy_id, MII_BMSR);
|
||||||
|
|
||||||
|
// Try next mii if the current one is not accessible.
|
||||||
|
if (status == 0xffff || status == 0x0000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
phy = kmalloc(sizeof(*phy), GFP_KERNEL);
|
||||||
|
if (!phy) {
|
||||||
|
sis190_free_phy(&tp->first_phy);
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sis190_init_phy(dev, tp, phy, phy_id, status);
|
||||||
|
|
||||||
|
list_add(&tp->first_phy, &phy->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_empty(&tp->first_phy)) {
|
||||||
|
net_probe(tp, KERN_INFO "%s: No MII transceivers found!\n",
|
||||||
|
dev->name);
|
||||||
|
rc = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select default PHY for mac */
|
||||||
|
sis190_default_phy(dev);
|
||||||
|
|
||||||
|
mii_if->dev = dev;
|
||||||
|
mii_if->mdio_read = __mdio_read;
|
||||||
|
mii_if->mdio_write = __mdio_write;
|
||||||
|
mii_if->phy_id_mask = PHY_ID_ANY;
|
||||||
|
mii_if->reg_num_mask = MII_REG_ANY;
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __devexit sis190_mii_remove(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct sis190_private *tp = netdev_priv(dev);
|
||||||
|
|
||||||
|
sis190_free_phy(&tp->first_phy);
|
||||||
|
}
|
||||||
|
|
||||||
static void sis190_release_board(struct pci_dev *pdev)
|
static void sis190_release_board(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct net_device *dev = pci_get_drvdata(pdev);
|
struct net_device *dev = pci_get_drvdata(pdev);
|
||||||
|
@ -1251,13 +1453,6 @@ static struct net_device * __devinit sis190_init_board(struct pci_dev *pdev)
|
||||||
tp->pci_dev = pdev;
|
tp->pci_dev = pdev;
|
||||||
tp->mmio_addr = ioaddr;
|
tp->mmio_addr = ioaddr;
|
||||||
|
|
||||||
tp->mii_if.dev = dev;
|
|
||||||
tp->mii_if.mdio_read = __mdio_read;
|
|
||||||
tp->mii_if.mdio_write = __mdio_write;
|
|
||||||
tp->mii_if.phy_id = 1;
|
|
||||||
tp->mii_if.phy_id_mask = 0x1f;
|
|
||||||
tp->mii_if.reg_num_mask = 0x1f;
|
|
||||||
|
|
||||||
sis190_irq_mask_and_ack(ioaddr);
|
sis190_irq_mask_and_ack(ioaddr);
|
||||||
|
|
||||||
sis190_soft_reset(ioaddr);
|
sis190_soft_reset(ioaddr);
|
||||||
|
@ -1585,6 +1780,10 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
|
||||||
|
|
||||||
pci_set_drvdata(pdev, dev);
|
pci_set_drvdata(pdev, dev);
|
||||||
|
|
||||||
|
rc = sis190_mii_probe(dev);
|
||||||
|
if (rc < 0)
|
||||||
|
goto err_unregister_dev;
|
||||||
|
|
||||||
net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), "
|
net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), "
|
||||||
"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
|
"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
|
||||||
pci_name(pdev), sis_chip_info[ent->driver_data].name,
|
pci_name(pdev), sis_chip_info[ent->driver_data].name,
|
||||||
|
@ -1599,6 +1798,8 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
|
||||||
out:
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
err_unregister_dev:
|
||||||
|
unregister_netdev(dev);
|
||||||
err_release_board:
|
err_release_board:
|
||||||
sis190_release_board(pdev);
|
sis190_release_board(pdev);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1608,6 +1809,7 @@ static void __devexit sis190_remove_one(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct net_device *dev = pci_get_drvdata(pdev);
|
struct net_device *dev = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
sis190_mii_remove(dev);
|
||||||
unregister_netdev(dev);
|
unregister_netdev(dev);
|
||||||
sis190_release_board(pdev);
|
sis190_release_board(pdev);
|
||||||
pci_set_drvdata(pdev, NULL);
|
pci_set_drvdata(pdev, NULL);
|
||||||
|
|
Loading…
Reference in a new issue