diff options
| -rw-r--r-- | drivers/net/sis190.c | 216 |
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 | |||
| 305 | struct sis190_phy { | ||
| 306 | struct list_head list; | ||
| 307 | int phy_id; | ||
| 308 | u16 id[2]; | ||
| 309 | u16 status; | ||
| 310 | u8 type; | ||
| 311 | }; | ||
| 312 | |||
| 313 | enum sis190_phy_type { | ||
| 314 | UNKNOWN = 0x00, | ||
| 315 | HOME = 0x01, | ||
| 316 | LAN = 0x02, | ||
| 317 | MIX = 0x03 | ||
| 318 | }; | ||
| 319 | |||
| 320 | static 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 | ||
| 300 | const static struct { | 331 | const 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 | ||
| 1208 | static 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 | */ | ||
| 1225 | static 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 | |||
| 1280 | static 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 | */ | ||
| 1321 | static 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; | ||
| 1368 | out: | ||
| 1369 | return rc; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | static 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 | |||
| 1177 | static void sis190_release_board(struct pci_dev *pdev) | 1379 | static 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, | |||
| 1599 | out: | 1798 | out: |
| 1600 | return rc; | 1799 | return rc; |
| 1601 | 1800 | ||
| 1801 | err_unregister_dev: | ||
| 1802 | unregister_netdev(dev); | ||
| 1602 | err_release_board: | 1803 | err_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); |
