aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFrancois Romieu <romieu@fr.zoreil.com>2005-07-30 07:15:22 -0400
committerJeff Garzik <jgarzik@pobox.com>2005-07-30 18:21:00 -0400
commitfcb9821d3dd62ede360e7991734ac22b79e9a4f0 (patch)
tree9c0818820beb5f6660dcb21ea820a25c179f7bd5 /drivers
parentfc10c39d7920b1db9ad2d80fa845896e529355dc (diff)
[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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/sis190.c216
1 files changed, 209 insertions, 7 deletions
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index 392110ede7b7..fe2ab6fd1384 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -43,6 +43,10 @@
43#define net_tx_err(p, arg...) if (netif_msg_tx_err(p)) \ 43#define net_tx_err(p, arg...) if (netif_msg_tx_err(p)) \
44 printk(arg) 44 printk(arg)
45 45
46#define PHY_MAX_ADDR 32
47#define PHY_ID_ANY 0x1f
48#define MII_REG_ANY 0x1f
49
46#ifdef CONFIG_SIS190_NAPI 50#ifdef CONFIG_SIS190_NAPI
47#define NAPI_SUFFIX "-NAPI" 51#define NAPI_SUFFIX "-NAPI"
48#else 52#else
@@ -295,6 +299,33 @@ struct sis190_private {
295 struct timer_list timer; 299 struct timer_list timer;
296 u32 msg_enable; 300 u32 msg_enable;
297 struct mii_if_info mii_if; 301 struct mii_if_info mii_if;
302 struct list_head first_phy;
303};
304
305struct sis190_phy {
306 struct list_head list;
307 int phy_id;
308 u16 id[2];
309 u16 status;
310 u8 type;
311};
312
313enum sis190_phy_type {
314 UNKNOWN = 0x00,
315 HOME = 0x01,
316 LAN = 0x02,
317 MIX = 0x03
318};
319
320static struct mii_chip_info {
321 const char *name;
322 u16 id[2];
323 unsigned int type;
324} mii_chip_table[] = {
325 { "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN },
326 { "Agere PHY ET1101B", { 0x0282, 0xf010 }, LAN },
327 { "Marvell PHY 88E1111", { 0x0141, 0x0cc0 }, LAN },
328 { NULL, }
298}; 329};
299 330
300const static struct { 331const static struct {
@@ -1174,6 +1205,177 @@ static struct net_device_stats *sis190_get_stats(struct net_device *dev)
1174 return &tp->stats; 1205 return &tp->stats;
1175} 1206}
1176 1207
1208static void sis190_free_phy(struct list_head *first_phy)
1209{
1210 struct sis190_phy *cur, *next;
1211
1212 list_for_each_entry_safe(cur, next, first_phy, list) {
1213 kfree(cur);
1214 }
1215}
1216
1217/**
1218 * sis190_default_phy - Select default PHY for sis190 mac.
1219 * @dev: the net device to probe for
1220 *
1221 * Select first detected PHY with link as default.
1222 * If no one is link on, select PHY whose types is HOME as default.
1223 * If HOME doesn't exist, select LAN.
1224 */
1225static u16 sis190_default_phy(struct net_device *dev)
1226{
1227 struct sis190_phy *phy, *phy_home, *phy_default, *phy_lan;
1228 struct sis190_private *tp = netdev_priv(dev);
1229 struct mii_if_info *mii_if = &tp->mii_if;
1230 void __iomem *ioaddr = tp->mmio_addr;
1231 u16 status;
1232
1233 phy_home = phy_default = phy_lan = NULL;
1234
1235 list_for_each_entry(phy, &tp->first_phy, list) {
1236 status = mdio_read_latched(ioaddr, phy->phy_id, MII_BMSR);
1237
1238 // Link ON & Not select default PHY & not ghost PHY.
1239 if ((status & BMSR_LSTATUS) &&
1240 !phy_default &&
1241 (phy->type != UNKNOWN)) {
1242 phy_default = phy;
1243 } else {
1244 status = mdio_read(ioaddr, phy->phy_id, MII_BMCR);
1245 mdio_write(ioaddr, phy->phy_id, MII_BMCR,
1246 status | BMCR_ANENABLE | BMCR_ISOLATE);
1247 if (phy->type == HOME)
1248 phy_home = phy;
1249 else if (phy->type == LAN)
1250 phy_lan = phy;
1251 }
1252 }
1253
1254 if (!phy_default) {
1255 if (phy_home)
1256 phy_default = phy_home;
1257 else if (phy_lan)
1258 phy_default = phy_lan;
1259 else
1260 phy_default = list_entry(&tp->first_phy,
1261 struct sis190_phy, list);
1262 }
1263
1264 if (mii_if->phy_id != phy_default->phy_id) {
1265 mii_if->phy_id = phy_default->phy_id;
1266 net_probe(tp, KERN_INFO
1267 "%s: Using transceiver at address %d as default.\n",
1268 dev->name, mii_if->phy_id);
1269 }
1270
1271 status = mdio_read(ioaddr, mii_if->phy_id, MII_BMCR);
1272 status &= (~BMCR_ISOLATE);
1273
1274 mdio_write(ioaddr, mii_if->phy_id, MII_BMCR, status);
1275 status = mdio_read_latched(ioaddr, mii_if->phy_id, MII_BMSR);
1276
1277 return status;
1278}
1279
1280static void sis190_init_phy(struct net_device *dev, struct sis190_private *tp,
1281 struct sis190_phy *phy, unsigned int phy_id,
1282 u16 mii_status)
1283{
1284 void __iomem *ioaddr = tp->mmio_addr;
1285 struct mii_chip_info *p;
1286
1287 INIT_LIST_HEAD(&phy->list);
1288 phy->status = mii_status;
1289 phy->phy_id = phy_id;
1290
1291 phy->id[0] = mdio_read(ioaddr, phy_id, MII_PHYSID1);
1292 phy->id[1] = mdio_read(ioaddr, phy_id, MII_PHYSID2);
1293
1294 for (p = mii_chip_table; p->type; p++) {
1295 if ((p->id[0] == phy->id[0]) &&
1296 (p->id[1] == (phy->id[1] & 0xfff0))) {
1297 break;
1298 }
1299 }
1300
1301 if (p->id[1]) {
1302 phy->type = (p->type == MIX) ?
1303 ((mii_status & (BMSR_100FULL | BMSR_100HALF)) ?
1304 LAN : HOME) : p->type;
1305 } else
1306 phy->type = UNKNOWN;
1307
1308 net_probe(tp, KERN_INFO "%s: %s transceiver at address %d.\n",
1309 dev->name, (phy->type == UNKNOWN) ? "Unknown PHY" : p->name,
1310 phy_id);
1311}
1312
1313/**
1314 * sis190_mii_probe - Probe MII PHY for sis190
1315 * @dev: the net device to probe for
1316 *
1317 * Search for total of 32 possible mii phy addresses.
1318 * Identify and set current phy if found one,
1319 * return error if it failed to found.
1320 */
1321static int __devinit sis190_mii_probe(struct net_device *dev)
1322{
1323 struct sis190_private *tp = netdev_priv(dev);
1324 struct mii_if_info *mii_if = &tp->mii_if;
1325 void __iomem *ioaddr = tp->mmio_addr;
1326 int phy_id;
1327 int rc = 0;
1328
1329 INIT_LIST_HEAD(&tp->first_phy);
1330
1331 for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
1332 struct sis190_phy *phy;
1333 u16 status;
1334
1335 status = mdio_read_latched(ioaddr, phy_id, MII_BMSR);
1336
1337 // Try next mii if the current one is not accessible.
1338 if (status == 0xffff || status == 0x0000)
1339 continue;
1340
1341 phy = kmalloc(sizeof(*phy), GFP_KERNEL);
1342 if (!phy) {
1343 sis190_free_phy(&tp->first_phy);
1344 rc = -ENOMEM;
1345 goto out;
1346 }
1347
1348 sis190_init_phy(dev, tp, phy, phy_id, status);
1349
1350 list_add(&tp->first_phy, &phy->list);
1351 }
1352
1353 if (list_empty(&tp->first_phy)) {
1354 net_probe(tp, KERN_INFO "%s: No MII transceivers found!\n",
1355 dev->name);
1356 rc = -EIO;
1357 goto out;
1358 }
1359
1360 /* Select default PHY for mac */
1361 sis190_default_phy(dev);
1362
1363 mii_if->dev = dev;
1364 mii_if->mdio_read = __mdio_read;
1365 mii_if->mdio_write = __mdio_write;
1366 mii_if->phy_id_mask = PHY_ID_ANY;
1367 mii_if->reg_num_mask = MII_REG_ANY;
1368out:
1369 return rc;
1370}
1371
1372static void __devexit sis190_mii_remove(struct net_device *dev)
1373{
1374 struct sis190_private *tp = netdev_priv(dev);
1375
1376 sis190_free_phy(&tp->first_phy);
1377}
1378
1177static void sis190_release_board(struct pci_dev *pdev) 1379static void sis190_release_board(struct pci_dev *pdev)
1178{ 1380{
1179 struct net_device *dev = pci_get_drvdata(pdev); 1381 struct net_device *dev = pci_get_drvdata(pdev);
@@ -1251,13 +1453,6 @@ static struct net_device * __devinit sis190_init_board(struct pci_dev *pdev)
1251 tp->pci_dev = pdev; 1453 tp->pci_dev = pdev;
1252 tp->mmio_addr = ioaddr; 1454 tp->mmio_addr = ioaddr;
1253 1455
1254 tp->mii_if.dev = dev;
1255 tp->mii_if.mdio_read = __mdio_read;
1256 tp->mii_if.mdio_write = __mdio_write;
1257 tp->mii_if.phy_id = 1;
1258 tp->mii_if.phy_id_mask = 0x1f;
1259 tp->mii_if.reg_num_mask = 0x1f;
1260
1261 sis190_irq_mask_and_ack(ioaddr); 1456 sis190_irq_mask_and_ack(ioaddr);
1262 1457
1263 sis190_soft_reset(ioaddr); 1458 sis190_soft_reset(ioaddr);
@@ -1585,6 +1780,10 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
1585 1780
1586 pci_set_drvdata(pdev, dev); 1781 pci_set_drvdata(pdev, dev);
1587 1782
1783 rc = sis190_mii_probe(dev);
1784 if (rc < 0)
1785 goto err_unregister_dev;
1786
1588 net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), " 1787 net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), "
1589 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", 1788 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
1590 pci_name(pdev), sis_chip_info[ent->driver_data].name, 1789 pci_name(pdev), sis_chip_info[ent->driver_data].name,
@@ -1599,6 +1798,8 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
1599out: 1798out:
1600 return rc; 1799 return rc;
1601 1800
1801err_unregister_dev:
1802 unregister_netdev(dev);
1602err_release_board: 1803err_release_board:
1603 sis190_release_board(pdev); 1804 sis190_release_board(pdev);
1604 goto out; 1805 goto out;
@@ -1608,6 +1809,7 @@ static void __devexit sis190_remove_one(struct pci_dev *pdev)
1608{ 1809{
1609 struct net_device *dev = pci_get_drvdata(pdev); 1810 struct net_device *dev = pci_get_drvdata(pdev);
1610 1811
1812 sis190_mii_remove(dev);
1611 unregister_netdev(dev); 1813 unregister_netdev(dev);
1612 sis190_release_board(pdev); 1814 sis190_release_board(pdev);
1613 pci_set_drvdata(pdev, NULL); 1815 pci_set_drvdata(pdev, NULL);