aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig10
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/broadcom.c175
-rw-r--r--drivers/net/phy/phy.c113
-rw-r--r--drivers/net/phy/phy_device.c30
5 files changed, 270 insertions, 59 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index b79ec0d7480f..f994f129f3d8 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -56,13 +56,19 @@ config SMSC_PHY
56 ---help--- 56 ---help---
57 Currently supports the LAN83C185 PHY 57 Currently supports the LAN83C185 PHY
58 58
59config BROADCOM_PHY
60 tristate "Drivers for Broadcom PHYs"
61 depends on PHYLIB
62 ---help---
63 Currently supports the BCM5411, BCM5421 and BCM5461 PHYs.
64
59config FIXED_PHY 65config FIXED_PHY
60 tristate "Drivers for PHY emulation on fixed speed/link" 66 tristate "Drivers for PHY emulation on fixed speed/link"
61 depends on PHYLIB 67 depends on PHYLIB
62 ---help--- 68 ---help---
63 Adds the driver to PHY layer to cover the boards that do not have any PHY bound, 69 Adds the driver to PHY layer to cover the boards that do not have any PHY bound,
64 but with the ability to manipulate with speed/link in software. The relavant MII 70 but with the ability to manipulate the speed/link in software. The relevant MII
65 speed/duplex parameters could be effectively handled in user-specified fuction. 71 speed/duplex parameters could be effectively handled in a user-specified function.
66 Currently tested with mpc866ads. 72 Currently tested with mpc866ads.
67 73
68config FIXED_MII_10_FDX 74config FIXED_MII_10_FDX
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 320f8323123f..bcd1efbd2a18 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -10,4 +10,5 @@ obj-$(CONFIG_LXT_PHY) += lxt.o
10obj-$(CONFIG_QSEMI_PHY) += qsemi.o 10obj-$(CONFIG_QSEMI_PHY) += qsemi.o
11obj-$(CONFIG_SMSC_PHY) += smsc.o 11obj-$(CONFIG_SMSC_PHY) += smsc.o
12obj-$(CONFIG_VITESSE_PHY) += vitesse.o 12obj-$(CONFIG_VITESSE_PHY) += vitesse.o
13obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
13obj-$(CONFIG_FIXED_PHY) += fixed.o 14obj-$(CONFIG_FIXED_PHY) += fixed.o
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
new file mode 100644
index 000000000000..29666c85ed55
--- /dev/null
+++ b/drivers/net/phy/broadcom.c
@@ -0,0 +1,175 @@
1/*
2 * drivers/net/phy/broadcom.c
3 *
4 * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
5 * transceivers.
6 *
7 * Copyright (c) 2006 Maciej W. Rozycki
8 *
9 * Inspired by code written by Amy Fong.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version
14 * 2 of the License, or (at your option) any later version.
15 */
16
17#include <linux/module.h>
18#include <linux/phy.h>
19
20#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
21#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
22#define MII_BCM54XX_ECR_IF 0x0800 /* Interrupt force */
23
24#define MII_BCM54XX_ESR 0x11 /* BCM54xx extended status register */
25#define MII_BCM54XX_ESR_IS 0x1000 /* Interrupt status */
26
27#define MII_BCM54XX_ISR 0x1a /* BCM54xx interrupt status register */
28#define MII_BCM54XX_IMR 0x1b /* BCM54xx interrupt mask register */
29#define MII_BCM54XX_INT_CRCERR 0x0001 /* CRC error */
30#define MII_BCM54XX_INT_LINK 0x0002 /* Link status changed */
31#define MII_BCM54XX_INT_SPEED 0x0004 /* Link speed change */
32#define MII_BCM54XX_INT_DUPLEX 0x0008 /* Duplex mode changed */
33#define MII_BCM54XX_INT_LRS 0x0010 /* Local receiver status changed */
34#define MII_BCM54XX_INT_RRS 0x0020 /* Remote receiver status changed */
35#define MII_BCM54XX_INT_SSERR 0x0040 /* Scrambler synchronization error */
36#define MII_BCM54XX_INT_UHCD 0x0080 /* Unsupported HCD negotiated */
37#define MII_BCM54XX_INT_NHCD 0x0100 /* No HCD */
38#define MII_BCM54XX_INT_NHCDL 0x0200 /* No HCD link */
39#define MII_BCM54XX_INT_ANPR 0x0400 /* Auto-negotiation page received */
40#define MII_BCM54XX_INT_LC 0x0800 /* All counters below 128 */
41#define MII_BCM54XX_INT_HC 0x1000 /* Counter above 32768 */
42#define MII_BCM54XX_INT_MDIX 0x2000 /* MDIX status change */
43#define MII_BCM54XX_INT_PSERR 0x4000 /* Pair swap error */
44
45MODULE_DESCRIPTION("Broadcom PHY driver");
46MODULE_AUTHOR("Maciej W. Rozycki");
47MODULE_LICENSE("GPL");
48
49static int bcm54xx_config_init(struct phy_device *phydev)
50{
51 int reg, err;
52
53 reg = phy_read(phydev, MII_BCM54XX_ECR);
54 if (reg < 0)
55 return reg;
56
57 /* Mask interrupts globally. */
58 reg |= MII_BCM54XX_ECR_IM;
59 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
60 if (err < 0)
61 return err;
62
63 /* Unmask events we are interested in. */
64 reg = ~(MII_BCM54XX_INT_DUPLEX |
65 MII_BCM54XX_INT_SPEED |
66 MII_BCM54XX_INT_LINK);
67 err = phy_write(phydev, MII_BCM54XX_IMR, reg);
68 if (err < 0)
69 return err;
70 return 0;
71}
72
73static int bcm54xx_ack_interrupt(struct phy_device *phydev)
74{
75 int reg;
76
77 /* Clear pending interrupts. */
78 reg = phy_read(phydev, MII_BCM54XX_ISR);
79 if (reg < 0)
80 return reg;
81
82 return 0;
83}
84
85static int bcm54xx_config_intr(struct phy_device *phydev)
86{
87 int reg, err;
88
89 reg = phy_read(phydev, MII_BCM54XX_ECR);
90 if (reg < 0)
91 return reg;
92
93 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
94 reg &= ~MII_BCM54XX_ECR_IM;
95 else
96 reg |= MII_BCM54XX_ECR_IM;
97
98 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
99 return err;
100}
101
102static struct phy_driver bcm5411_driver = {
103 .phy_id = 0x00206070,
104 .phy_id_mask = 0xfffffff0,
105 .name = "Broadcom BCM5411",
106 .features = PHY_GBIT_FEATURES,
107 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
108 .config_init = bcm54xx_config_init,
109 .config_aneg = genphy_config_aneg,
110 .read_status = genphy_read_status,
111 .ack_interrupt = bcm54xx_ack_interrupt,
112 .config_intr = bcm54xx_config_intr,
113 .driver = { .owner = THIS_MODULE },
114};
115
116static struct phy_driver bcm5421_driver = {
117 .phy_id = 0x002060e0,
118 .phy_id_mask = 0xfffffff0,
119 .name = "Broadcom BCM5421",
120 .features = PHY_GBIT_FEATURES,
121 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
122 .config_init = bcm54xx_config_init,
123 .config_aneg = genphy_config_aneg,
124 .read_status = genphy_read_status,
125 .ack_interrupt = bcm54xx_ack_interrupt,
126 .config_intr = bcm54xx_config_intr,
127 .driver = { .owner = THIS_MODULE },
128};
129
130static struct phy_driver bcm5461_driver = {
131 .phy_id = 0x002060c0,
132 .phy_id_mask = 0xfffffff0,
133 .name = "Broadcom BCM5461",
134 .features = PHY_GBIT_FEATURES,
135 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
136 .config_init = bcm54xx_config_init,
137 .config_aneg = genphy_config_aneg,
138 .read_status = genphy_read_status,
139 .ack_interrupt = bcm54xx_ack_interrupt,
140 .config_intr = bcm54xx_config_intr,
141 .driver = { .owner = THIS_MODULE },
142};
143
144static int __init broadcom_init(void)
145{
146 int ret;
147
148 ret = phy_driver_register(&bcm5411_driver);
149 if (ret)
150 goto out_5411;
151 ret = phy_driver_register(&bcm5421_driver);
152 if (ret)
153 goto out_5421;
154 ret = phy_driver_register(&bcm5461_driver);
155 if (ret)
156 goto out_5461;
157 return ret;
158
159out_5461:
160 phy_driver_unregister(&bcm5421_driver);
161out_5421:
162 phy_driver_unregister(&bcm5411_driver);
163out_5411:
164 return ret;
165}
166
167static void __exit broadcom_exit(void)
168{
169 phy_driver_unregister(&bcm5461_driver);
170 phy_driver_unregister(&bcm5421_driver);
171 phy_driver_unregister(&bcm5411_driver);
172}
173
174module_init(broadcom_init);
175module_exit(broadcom_exit);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 3af9fcf76c81..88237bdb5255 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -7,6 +7,7 @@
7 * Author: Andy Fleming 7 * Author: Andy Fleming
8 * 8 *
9 * Copyright (c) 2004 Freescale Semiconductor, Inc. 9 * Copyright (c) 2004 Freescale Semiconductor, Inc.
10 * Copyright (c) 2006 Maciej W. Rozycki
10 * 11 *
11 * This program is free software; you can redistribute it and/or modify it 12 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the 13 * under the terms of the GNU General Public License as published by the
@@ -32,6 +33,8 @@
32#include <linux/mii.h> 33#include <linux/mii.h>
33#include <linux/ethtool.h> 34#include <linux/ethtool.h>
34#include <linux/phy.h> 35#include <linux/phy.h>
36#include <linux/timer.h>
37#include <linux/workqueue.h>
35 38
36#include <asm/io.h> 39#include <asm/io.h>
37#include <asm/irq.h> 40#include <asm/irq.h>
@@ -484,6 +487,9 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
484{ 487{
485 struct phy_device *phydev = phy_dat; 488 struct phy_device *phydev = phy_dat;
486 489
490 if (PHY_HALTED == phydev->state)
491 return IRQ_NONE; /* It can't be ours. */
492
487 /* The MDIO bus is not allowed to be written in interrupt 493 /* The MDIO bus is not allowed to be written in interrupt
488 * context, so we need to disable the irq here. A work 494 * context, so we need to disable the irq here. A work
489 * queue will write the PHY to disable and clear the 495 * queue will write the PHY to disable and clear the
@@ -577,6 +583,13 @@ int phy_stop_interrupts(struct phy_device *phydev)
577 if (err) 583 if (err)
578 phy_error(phydev); 584 phy_error(phydev);
579 585
586 /*
587 * Finish any pending work; we might have been scheduled
588 * to be called from keventd ourselves, though.
589 */
590 if (!current_is_keventd())
591 flush_scheduled_work();
592
580 free_irq(phydev->irq, phydev); 593 free_irq(phydev->irq, phydev);
581 594
582 return err; 595 return err;
@@ -603,7 +616,8 @@ static void phy_change(void *data)
603 enable_irq(phydev->irq); 616 enable_irq(phydev->irq);
604 617
605 /* Reenable interrupts */ 618 /* Reenable interrupts */
606 err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); 619 if (PHY_HALTED != phydev->state)
620 err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
607 621
608 if (err) 622 if (err)
609 goto irq_enable_err; 623 goto irq_enable_err;
@@ -624,18 +638,24 @@ void phy_stop(struct phy_device *phydev)
624 if (PHY_HALTED == phydev->state) 638 if (PHY_HALTED == phydev->state)
625 goto out_unlock; 639 goto out_unlock;
626 640
627 if (phydev->irq != PHY_POLL) { 641 phydev->state = PHY_HALTED;
628 /* Clear any pending interrupts */
629 phy_clear_interrupt(phydev);
630 642
643 if (phydev->irq != PHY_POLL) {
631 /* Disable PHY Interrupts */ 644 /* Disable PHY Interrupts */
632 phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); 645 phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
633 }
634 646
635 phydev->state = PHY_HALTED; 647 /* Clear any pending interrupts */
648 phy_clear_interrupt(phydev);
649 }
636 650
637out_unlock: 651out_unlock:
638 spin_unlock(&phydev->lock); 652 spin_unlock(&phydev->lock);
653
654 /*
655 * Cannot call flush_scheduled_work() here as desired because
656 * of rtnl_lock(), but PHY_HALTED shall guarantee phy_change()
657 * will not reenable interrupts.
658 */
639} 659}
640 660
641 661
@@ -693,60 +713,57 @@ static void phy_timer(unsigned long data)
693 713
694 break; 714 break;
695 case PHY_AN: 715 case PHY_AN:
716 err = phy_read_status(phydev);
717
718 if (err < 0)
719 break;
720
721 /* If the link is down, give up on
722 * negotiation for now */
723 if (!phydev->link) {
724 phydev->state = PHY_NOLINK;
725 netif_carrier_off(phydev->attached_dev);
726 phydev->adjust_link(phydev->attached_dev);
727 break;
728 }
729
696 /* Check if negotiation is done. Break 730 /* Check if negotiation is done. Break
697 * if there's an error */ 731 * if there's an error */
698 err = phy_aneg_done(phydev); 732 err = phy_aneg_done(phydev);
699 if (err < 0) 733 if (err < 0)
700 break; 734 break;
701 735
702 /* If auto-negotiation is done, we change to 736 /* If AN is done, we're running */
703 * either RUNNING, or NOLINK */
704 if (err > 0) { 737 if (err > 0) {
705 err = phy_read_status(phydev); 738 phydev->state = PHY_RUNNING;
739 netif_carrier_on(phydev->attached_dev);
740 phydev->adjust_link(phydev->attached_dev);
741
742 } else if (0 == phydev->link_timeout--) {
743 int idx;
706 744
707 if (err) 745 needs_aneg = 1;
746 /* If we have the magic_aneg bit,
747 * we try again */
748 if (phydev->drv->flags & PHY_HAS_MAGICANEG)
708 break; 749 break;
709 750
710 if (phydev->link) { 751 /* The timer expired, and we still
711 phydev->state = PHY_RUNNING; 752 * don't have a setting, so we try
712 netif_carrier_on(phydev->attached_dev); 753 * forcing it until we find one that
713 } else { 754 * works, starting from the fastest speed,
714 phydev->state = PHY_NOLINK; 755 * and working our way down */
715 netif_carrier_off(phydev->attached_dev); 756 idx = phy_find_valid(0, phydev->supported);
716 }
717 757
718 phydev->adjust_link(phydev->attached_dev); 758 phydev->speed = settings[idx].speed;
759 phydev->duplex = settings[idx].duplex;
719 760
720 } else if (0 == phydev->link_timeout--) { 761 phydev->autoneg = AUTONEG_DISABLE;
721 /* The counter expired, so either we
722 * switch to forced mode, or the
723 * magic_aneg bit exists, and we try aneg
724 * again */
725 if (!(phydev->drv->flags & PHY_HAS_MAGICANEG)) {
726 int idx;
727
728 /* We'll start from the
729 * fastest speed, and work
730 * our way down */
731 idx = phy_find_valid(0,
732 phydev->supported);
733
734 phydev->speed = settings[idx].speed;
735 phydev->duplex = settings[idx].duplex;
736
737 phydev->autoneg = AUTONEG_DISABLE;
738 phydev->state = PHY_FORCING;
739 phydev->link_timeout =
740 PHY_FORCE_TIMEOUT;
741
742 pr_info("Trying %d/%s\n",
743 phydev->speed,
744 DUPLEX_FULL ==
745 phydev->duplex ?
746 "FULL" : "HALF");
747 }
748 762
749 needs_aneg = 1; 763 pr_info("Trying %d/%s\n", phydev->speed,
764 DUPLEX_FULL ==
765 phydev->duplex ?
766 "FULL" : "HALF");
750 } 767 }
751 break; 768 break;
752 case PHY_NOLINK: 769 case PHY_NOLINK:
@@ -762,7 +779,7 @@ static void phy_timer(unsigned long data)
762 } 779 }
763 break; 780 break;
764 case PHY_FORCING: 781 case PHY_FORCING:
765 err = phy_read_status(phydev); 782 err = genphy_update_link(phydev);
766 783
767 if (err) 784 if (err)
768 break; 785 break;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 3bbd5e70c209..b01fc70a57db 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -59,6 +59,7 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
59 dev->duplex = -1; 59 dev->duplex = -1;
60 dev->pause = dev->asym_pause = 0; 60 dev->pause = dev->asym_pause = 0;
61 dev->link = 1; 61 dev->link = 1;
62 dev->interface = PHY_INTERFACE_MODE_GMII;
62 63
63 dev->autoneg = AUTONEG_ENABLE; 64 dev->autoneg = AUTONEG_ENABLE;
64 65
@@ -137,11 +138,12 @@ void phy_prepare_link(struct phy_device *phydev,
137 * the desired functionality. 138 * the desired functionality.
138 */ 139 */
139struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, 140struct phy_device * phy_connect(struct net_device *dev, const char *phy_id,
140 void (*handler)(struct net_device *), u32 flags) 141 void (*handler)(struct net_device *), u32 flags,
142 u32 interface)
141{ 143{
142 struct phy_device *phydev; 144 struct phy_device *phydev;
143 145
144 phydev = phy_attach(dev, phy_id, flags); 146 phydev = phy_attach(dev, phy_id, flags, interface);
145 147
146 if (IS_ERR(phydev)) 148 if (IS_ERR(phydev))
147 return phydev; 149 return phydev;
@@ -186,7 +188,7 @@ static int phy_compare_id(struct device *dev, void *data)
186} 188}
187 189
188struct phy_device *phy_attach(struct net_device *dev, 190struct phy_device *phy_attach(struct net_device *dev,
189 const char *phy_id, u32 flags) 191 const char *phy_id, u32 flags, u32 interface)
190{ 192{
191 struct bus_type *bus = &mdio_bus_type; 193 struct bus_type *bus = &mdio_bus_type;
192 struct phy_device *phydev; 194 struct phy_device *phydev;
@@ -231,6 +233,20 @@ struct phy_device *phy_attach(struct net_device *dev,
231 233
232 phydev->dev_flags = flags; 234 phydev->dev_flags = flags;
233 235
236 phydev->interface = interface;
237
238 /* Do initial configuration here, now that
239 * we have certain key parameters
240 * (dev_flags and interface) */
241 if (phydev->drv->config_init) {
242 int err;
243
244 err = phydev->drv->config_init(phydev);
245
246 if (err < 0)
247 return ERR_PTR(err);
248 }
249
234 return phydev; 250 return phydev;
235} 251}
236EXPORT_SYMBOL(phy_attach); 252EXPORT_SYMBOL(phy_attach);
@@ -427,6 +443,7 @@ int genphy_update_link(struct phy_device *phydev)
427 443
428 return 0; 444 return 0;
429} 445}
446EXPORT_SYMBOL(genphy_update_link);
430 447
431/* genphy_read_status 448/* genphy_read_status
432 * 449 *
@@ -611,13 +628,8 @@ static int phy_probe(struct device *dev)
611 628
612 spin_unlock(&phydev->lock); 629 spin_unlock(&phydev->lock);
613 630
614 if (err < 0)
615 return err;
616
617 if (phydev->drv->config_init)
618 err = phydev->drv->config_init(phydev);
619
620 return err; 631 return err;
632
621} 633}
622 634
623static int phy_remove(struct device *dev) 635static int phy_remove(struct device *dev)