summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-10-24 15:49:25 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-24 15:49:25 -0400
commit5f3619f275adcca531c9bdbddb8583f4e07a2a10 (patch)
treece59b9cfc05e1b2343ed6d41c54dd7ed7455444f
parentd4c3363e84ba03f4f8c434145d352f44c2b2443b (diff)
parent9ff32fe1b9c5e125557766ea1723a7c42779f5a6 (diff)
Merge branch 'berlin_ethernet'
Sebastian Hesselbarth says: ==================== Marvell PXA168 libphy handling and Berlin Ethernet This patch series deals with a removing a IP feature that can be found on all currently supported Marvell Ethernet IP (pxa168_eth, mv643xx_eth, mvneta). The MAC IP allows to automatically perform PHY auto-negotiation without software interaction. However, this feature (a) fundamentally clashes with the way libphy works and (b) is unable to deal with quirky PHYs that require special treatment. In this series, pxa168_eth driver is rewritten to completely disable that feature and properly deal with libphy provided PHYs. As usual, a branch on top of v3.18-rc1 can be found at git://git.infradead.org/users/hesselba/linux-berlin.git devel/bg2-bg2cd-eth-v2 Patches 1-5 should go through David's net tree, I'll pick up the DT patches 6-9. There have been some changes, compared to the RFT - added phy-connection-type property to BG2Q PHY DT node - bail out from pxa168_eth_adjust_link when there is no change in PHY parameters. Also, add a call to phy_print_status. compared to v1 - move phy-connection-type to ethernet node instead of PHY node Patch 1 adds support for Marvell 88E3016 FastEthernet PHY that is also integrated in Marvell Berlin BG2/BG2CD SoCs. Patch 2 allows to pass phy_interface_t on pxa168_eth platform_data that is only used by mach-mmp/gplug. From the board setup, I guessed gplug's PHY is connected via RMII. The patch still isn't even compile tested. Patches 3-5 prepare proper libphy handling and finally remove all in-driver PHY mangling related to the feature explained above. Patches 6-9 add corresponding ethernet DT nodes to BG2, BG2CD, add a phy-connection-type property to BG2Q and enable ethernet on BG2-based Sony NSZ-GS7. I have tested all this on GS7 successfully with ip=dhcp on 100M FD. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/arm/mach-mmp/gplugd.c2
-rw-r--r--drivers/net/ethernet/marvell/pxa168_eth.c248
-rw-r--r--drivers/net/phy/marvell.c46
-rw-r--r--include/linux/marvell_phy.h1
-rw-r--r--include/linux/pxa168_eth.h1
5 files changed, 150 insertions, 148 deletions
diff --git a/arch/arm/mach-mmp/gplugd.c b/arch/arm/mach-mmp/gplugd.c
index d81b2475e67e..3b5794cd0357 100644
--- a/arch/arm/mach-mmp/gplugd.c
+++ b/arch/arm/mach-mmp/gplugd.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio-pxa.h>
+#include <linux/phy.h>
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
@@ -158,6 +159,7 @@ struct pxa168_eth_platform_data gplugd_eth_platform_data = {
.port_number = 0,
.phy_addr = 0,
.speed = 0, /* Autonagotiation */
+ .intf = PHY_INTERFACE_MODE_RMII,
.init = gplugd_eth_init,
};
diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c
index c3b209cd0660..bee4c369f0fe 100644
--- a/drivers/net/ethernet/marvell/pxa168_eth.c
+++ b/drivers/net/ethernet/marvell/pxa168_eth.c
@@ -106,6 +106,7 @@
#define SDMA_CMD_ERD (1 << 7)
/* Bit definitions of the Port Config Reg */
+#define PCR_DUPLEX_FULL (1 << 15)
#define PCR_HS (1 << 12)
#define PCR_EN (1 << 7)
#define PCR_PM (1 << 0)
@@ -113,11 +114,17 @@
/* Bit definitions of the Port Config Extend Reg */
#define PCXR_2BSM (1 << 28)
#define PCXR_DSCP_EN (1 << 21)
+#define PCXR_RMII_EN (1 << 20)
+#define PCXR_AN_SPEED_DIS (1 << 19)
+#define PCXR_SPEED_100 (1 << 18)
#define PCXR_MFL_1518 (0 << 14)
#define PCXR_MFL_1536 (1 << 14)
#define PCXR_MFL_2048 (2 << 14)
#define PCXR_MFL_64K (3 << 14)
+#define PCXR_FLOWCTL_DIS (1 << 12)
#define PCXR_FLP (1 << 11)
+#define PCXR_AN_FLOWCTL_DIS (1 << 10)
+#define PCXR_AN_DUPLEX_DIS (1 << 9)
#define PCXR_PRIO_TX_OFF 3
#define PCXR_TX_HIGH_PRI (7 << PCXR_PRIO_TX_OFF)
@@ -170,7 +177,6 @@
#define LINK_UP (1 << 3)
/* Bit definitions for work to be done */
-#define WORK_LINK (1 << 0)
#define WORK_TX_DONE (1 << 1)
/*
@@ -197,6 +203,9 @@ struct tx_desc {
struct pxa168_eth_private {
int port_num; /* User Ethernet port number */
int phy_addr;
+ int phy_speed;
+ int phy_duplex;
+ phy_interface_t phy_intf;
int rx_resource_err; /* Rx ring resource error flag */
@@ -269,11 +278,11 @@ enum hash_table_entry {
static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd);
static int pxa168_init_hw(struct pxa168_eth_private *pep);
+static int pxa168_init_phy(struct net_device *dev);
static void eth_port_reset(struct net_device *dev);
static void eth_port_start(struct net_device *dev);
static int pxa168_eth_open(struct net_device *dev);
static int pxa168_eth_stop(struct net_device *dev);
-static int ethernet_phy_setup(struct net_device *dev);
static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
{
@@ -305,26 +314,6 @@ static void abort_dma(struct pxa168_eth_private *pep)
netdev_err(pep->dev, "%s : DMA Stuck\n", __func__);
}
-static int ethernet_phy_get(struct pxa168_eth_private *pep)
-{
- unsigned int reg_data;
-
- reg_data = rdl(pep, PHY_ADDRESS);
-
- return (reg_data >> (5 * pep->port_num)) & 0x1f;
-}
-
-static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
-{
- u32 reg_data;
- int addr_shift = 5 * pep->port_num;
-
- reg_data = rdl(pep, PHY_ADDRESS);
- reg_data &= ~(0x1f << addr_shift);
- reg_data |= (phy_addr & 0x1f) << addr_shift;
- wrl(pep, PHY_ADDRESS, reg_data);
-}
-
static void rxq_refill(struct net_device *dev)
{
struct pxa168_eth_private *pep = netdev_priv(dev);
@@ -655,14 +644,7 @@ static void eth_port_start(struct net_device *dev)
struct pxa168_eth_private *pep = netdev_priv(dev);
int tx_curr_desc, rx_curr_desc;
- /* Perform PHY reset, if there is a PHY. */
- if (pep->phy != NULL) {
- struct ethtool_cmd cmd;
-
- pxa168_get_settings(pep->dev, &cmd);
- phy_init_hw(pep->phy);
- pxa168_set_settings(pep->dev, &cmd);
- }
+ phy_start(pep->phy);
/* Assignment of Tx CTRP of given queue */
tx_curr_desc = pep->tx_curr_desc_q;
@@ -717,6 +699,8 @@ static void eth_port_reset(struct net_device *dev)
val = rdl(pep, PORT_CONFIG);
val &= ~PCR_EN;
wrl(pep, PORT_CONFIG, val);
+
+ phy_stop(pep->phy);
}
/*
@@ -884,43 +868,9 @@ static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
}
if (icr & ICR_RXBUF)
ret = 1;
- if (icr & ICR_MII_CH) {
- pep->work_todo |= WORK_LINK;
- ret = 1;
- }
return ret;
}
-static void handle_link_event(struct pxa168_eth_private *pep)
-{
- struct net_device *dev = pep->dev;
- u32 port_status;
- int speed;
- int duplex;
- int fc;
-
- port_status = rdl(pep, PORT_STATUS);
- if (!(port_status & LINK_UP)) {
- if (netif_carrier_ok(dev)) {
- netdev_info(dev, "link down\n");
- netif_carrier_off(dev);
- txq_reclaim(dev, 1);
- }
- return;
- }
- if (port_status & PORT_SPEED_100)
- speed = 100;
- else
- speed = 10;
-
- duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
- fc = (port_status & FLOW_CONTROL_DISABLED) ? 0 : 1;
- netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n",
- speed, duplex ? "full" : "half", fc ? "en" : "dis");
- if (!netif_carrier_ok(dev))
- netif_carrier_on(dev);
-}
-
static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
@@ -978,8 +928,11 @@ static int set_port_config_ext(struct pxa168_eth_private *pep)
skb_size = PCXR_MFL_64K;
/* Extended Port Configuration */
- wrl(pep,
- PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte prefix aligns IP hdr */
+ wrl(pep, PORT_CONFIG_EXT,
+ PCXR_AN_SPEED_DIS | /* Disable HW AN */
+ PCXR_AN_DUPLEX_DIS |
+ PCXR_AN_FLOWCTL_DIS |
+ PCXR_2BSM | /* Two byte prefix aligns IP hdr */
PCXR_DSCP_EN | /* Enable DSCP in IP */
skb_size | PCXR_FLP | /* do not force link pass */
PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */
@@ -987,6 +940,69 @@ static int set_port_config_ext(struct pxa168_eth_private *pep)
return 0;
}
+static void pxa168_eth_adjust_link(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct phy_device *phy = pep->phy;
+ u32 cfg, cfg_o = rdl(pep, PORT_CONFIG);
+ u32 cfgext, cfgext_o = rdl(pep, PORT_CONFIG_EXT);
+
+ cfg = cfg_o & ~PCR_DUPLEX_FULL;
+ cfgext = cfgext_o & ~(PCXR_SPEED_100 | PCXR_FLOWCTL_DIS | PCXR_RMII_EN);
+
+ if (phy->interface == PHY_INTERFACE_MODE_RMII)
+ cfgext |= PCXR_RMII_EN;
+ if (phy->speed == SPEED_100)
+ cfgext |= PCXR_SPEED_100;
+ if (phy->duplex)
+ cfg |= PCR_DUPLEX_FULL;
+ if (!phy->pause)
+ cfgext |= PCXR_FLOWCTL_DIS;
+
+ /* Bail out if there has nothing changed */
+ if (cfg == cfg_o && cfgext == cfgext_o)
+ return;
+
+ wrl(pep, PORT_CONFIG, cfg);
+ wrl(pep, PORT_CONFIG_EXT, cfgext);
+
+ phy_print_status(phy);
+}
+
+static int pxa168_init_phy(struct net_device *dev)
+{
+ struct pxa168_eth_private *pep = netdev_priv(dev);
+ struct ethtool_cmd cmd;
+ int err;
+
+ if (pep->phy)
+ return 0;
+
+ pep->phy = mdiobus_scan(pep->smi_bus, pep->phy_addr);
+ if (!pep->phy)
+ return -ENODEV;
+
+ err = phy_connect_direct(dev, pep->phy, pxa168_eth_adjust_link,
+ pep->phy_intf);
+ if (err)
+ return err;
+
+ err = pxa168_get_settings(dev, &cmd);
+ if (err)
+ return err;
+
+ cmd.phy_address = pep->phy_addr;
+ cmd.speed = pep->phy_speed;
+ cmd.duplex = pep->phy_duplex;
+ cmd.advertising = PHY_BASIC_FEATURES;
+ cmd.autoneg = AUTONEG_ENABLE;
+
+ if (cmd.speed != 0)
+ cmd.autoneg = AUTONEG_DISABLE;
+
+ return pxa168_set_settings(dev, &cmd);
+}
+
static int pxa168_init_hw(struct pxa168_eth_private *pep)
{
int err = 0;
@@ -1133,6 +1149,10 @@ static int pxa168_eth_open(struct net_device *dev)
struct pxa168_eth_private *pep = netdev_priv(dev);
int err;
+ err = pxa168_init_phy(dev);
+ if (err)
+ return err;
+
err = request_irq(dev->irq, pxa168_eth_int_handler, 0, dev->name, dev);
if (err) {
dev_err(&dev->dev, "can't assign irq\n");
@@ -1231,10 +1251,6 @@ static int pxa168_rx_poll(struct napi_struct *napi, int budget)
struct net_device *dev = pep->dev;
int work_done = 0;
- if (unlikely(pep->work_todo & WORK_LINK)) {
- pep->work_todo &= ~(WORK_LINK);
- handle_link_event(pep);
- }
/*
* We call txq_reclaim every time since in NAPI interupts are disabled
* and due to this we miss the TX_DONE interrupt,which is not updated in
@@ -1357,77 +1373,6 @@ static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
return -EOPNOTSUPP;
}
-static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
-{
- struct mii_bus *bus = pep->smi_bus;
- struct phy_device *phydev;
- int start;
- int num;
- int i;
-
- if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
- /* Scan entire range */
- start = ethernet_phy_get(pep);
- num = 32;
- } else {
- /* Use phy addr specific to platform */
- start = phy_addr & 0x1f;
- num = 1;
- }
- phydev = NULL;
- for (i = 0; i < num; i++) {
- int addr = (start + i) & 0x1f;
- if (bus->phy_map[addr] == NULL)
- mdiobus_scan(bus, addr);
-
- if (phydev == NULL) {
- phydev = bus->phy_map[addr];
- if (phydev != NULL)
- ethernet_phy_set_addr(pep, addr);
- }
- }
-
- return phydev;
-}
-
-static void phy_init(struct pxa168_eth_private *pep)
-{
- struct phy_device *phy = pep->phy;
-
- phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII);
-
- if (pep->pd && pep->pd->speed != 0) {
- phy->autoneg = AUTONEG_DISABLE;
- phy->advertising = 0;
- phy->speed = pep->pd->speed;
- phy->duplex = pep->pd->duplex;
- } else {
- phy->autoneg = AUTONEG_ENABLE;
- phy->speed = 0;
- phy->duplex = 0;
- phy->supported &= PHY_BASIC_FEATURES;
- phy->advertising = phy->supported | ADVERTISED_Autoneg;
- }
-
- phy_start_aneg(phy);
-}
-
-static int ethernet_phy_setup(struct net_device *dev)
-{
- struct pxa168_eth_private *pep = netdev_priv(dev);
-
- if (pep->pd && pep->pd->init)
- pep->pd->init();
-
- pep->phy = phy_scan(pep, pep->phy_addr & 0x1f);
- if (pep->phy != NULL)
- phy_init(pep);
-
- update_hash_table_mac_address(pep, NULL, dev->dev_addr);
-
- return 0;
-}
-
static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct pxa168_eth_private *pep = netdev_priv(dev);
@@ -1552,13 +1497,23 @@ static int pxa168_eth_probe(struct platform_device *pdev)
pep->port_num = pep->pd->port_number;
pep->phy_addr = pep->pd->phy_addr;
+ pep->phy_speed = pep->pd->speed;
+ pep->phy_duplex = pep->pd->duplex;
+ pep->phy_intf = pep->pd->intf;
+
+ if (pep->pd->init)
+ pep->pd->init();
} else if (pdev->dev.of_node) {
of_property_read_u32(pdev->dev.of_node, "port-id",
&pep->port_num);
np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
- if (np)
- of_property_read_u32(np, "reg", &pep->phy_addr);
+ if (!np) {
+ dev_err(&pdev->dev, "missing phy-handle\n");
+ return -EINVAL;
+ }
+ of_property_read_u32(np, "reg", &pep->phy_addr);
+ pep->phy_intf = of_get_phy_mode(pdev->dev.of_node);
}
/* Hardware supports only 3 ports */
@@ -1588,9 +1543,6 @@ static int pxa168_eth_probe(struct platform_device *pdev)
goto err_free_mdio;
pxa168_init_hw(pep);
- err = ethernet_phy_setup(dev);
- if (err)
- goto err_mdiobus;
SET_NETDEV_DEV(dev, &pdev->dev);
err = register_netdev(dev);
if (err)
@@ -1621,13 +1573,13 @@ static int pxa168_eth_remove(struct platform_device *pdev)
pep->htpr, pep->htpr_dma);
pep->htpr = NULL;
}
+ if (pep->phy)
+ phy_disconnect(pep->phy);
if (pep->clk) {
clk_disable(pep->clk);
clk_put(pep->clk);
pep->clk = NULL;
}
- if (pep->phy != NULL)
- phy_detach(pep->phy);
iounmap(pep->base);
pep->base = NULL;
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index bd37e45c89c0..d2b2f2f795d5 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -118,6 +118,9 @@
#define MII_M1116R_CONTROL_REG_MAC 21
+#define MII_88E3016_PHY_SPEC_CTRL 0x10
+#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
+#define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030
MODULE_DESCRIPTION("Marvell PHY driver");
MODULE_AUTHOR("Andy Fleming");
@@ -434,6 +437,25 @@ static int m88e1116r_config_init(struct phy_device *phydev)
return 0;
}
+static int m88e3016_config_init(struct phy_device *phydev)
+{
+ int reg;
+
+ /* Enable Scrambler and Auto-Crossover */
+ reg = phy_read(phydev, MII_88E3016_PHY_SPEC_CTRL);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~MII_88E3016_DISABLE_SCRAMBLER;
+ reg |= MII_88E3016_AUTO_MDIX_CROSSOVER;
+
+ reg = phy_write(phydev, MII_88E3016_PHY_SPEC_CTRL, reg);
+ if (reg < 0)
+ return reg;
+
+ return 0;
+}
+
static int m88e1111_config_init(struct phy_device *phydev)
{
int err;
@@ -770,6 +792,12 @@ static int marvell_read_status(struct phy_device *phydev)
return 0;
}
+static int marvell_aneg_done(struct phy_device *phydev)
+{
+ int retval = phy_read(phydev, MII_M1011_PHY_STATUS);
+ return (retval < 0) ? retval : (retval & MII_M1011_PHY_STATUS_RESOLVED);
+}
+
static int m88e1121_did_interrupt(struct phy_device *phydev)
{
int imask;
@@ -1050,6 +1078,23 @@ static struct phy_driver marvell_drivers[] = {
.suspend = &genphy_suspend,
.driver = { .owner = THIS_MODULE },
},
+ {
+ .phy_id = MARVELL_PHY_ID_88E3016,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .name = "Marvell 88E3016",
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = &genphy_config_aneg,
+ .config_init = &m88e3016_config_init,
+ .aneg_done = &marvell_aneg_done,
+ .read_status = &marvell_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_config_intr,
+ .did_interrupt = &m88e1121_did_interrupt,
+ .resume = &genphy_resume,
+ .suspend = &genphy_suspend,
+ .driver = { .owner = THIS_MODULE },
+ },
};
static int __init marvell_init(void)
@@ -1079,6 +1124,7 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = {
{ MARVELL_PHY_ID_88E1318S, MARVELL_PHY_ID_MASK },
{ MARVELL_PHY_ID_88E1116R, MARVELL_PHY_ID_MASK },
{ MARVELL_PHY_ID_88E1510, MARVELL_PHY_ID_MASK },
+ { MARVELL_PHY_ID_88E3016, MARVELL_PHY_ID_MASK },
{ }
};
diff --git a/include/linux/marvell_phy.h b/include/linux/marvell_phy.h
index 8e9a029e093d..e6982ac3200d 100644
--- a/include/linux/marvell_phy.h
+++ b/include/linux/marvell_phy.h
@@ -16,6 +16,7 @@
#define MARVELL_PHY_ID_88E1318S 0x01410e90
#define MARVELL_PHY_ID_88E1116R 0x01410e40
#define MARVELL_PHY_ID_88E1510 0x01410dd0
+#define MARVELL_PHY_ID_88E3016 0x01410e60
/* struct phy_device dev_flags definitions */
#define MARVELL_PHY_M1145_FLAGS_RESISTANCE 0x00000001
diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
index 18d75e795606..37c381120bc8 100644
--- a/include/linux/pxa168_eth.h
+++ b/include/linux/pxa168_eth.h
@@ -13,6 +13,7 @@ struct pxa168_eth_platform_data {
*/
int speed; /* 0, SPEED_10, SPEED_100 */
int duplex; /* DUPLEX_HALF or DUPLEX_FULL */
+ phy_interface_t intf;
/*
* Override default RX/TX queue sizes if nonzero.