Commit 35aed4ac authored by David S. Miller's avatar David S. Miller

Merge branch 'mvpp2-comphy'

Antoine Tenart says:

====================
net: mvpp2: comphy configuration

This series, following up the one one the GoP/MAC configuration, aims at
stopping to depend on the firmware/bootloader configuration when using
the PPv2 engine. With this series the PPv2 driver does not need to rely
on a previous configuration, and dynamic reconfiguration while the
kernel is running can be done (i.e. switch one port from SGMII to 10G,
or the opposite). A port can now be configured in a different mode than
what's done in the firmware/bootloader as well.

The series first contain patches in the generic PHY framework to support
what is called the comphy (common PHYs), which is an h/w block providing
PHYs that can be configured in various modes ranging from SGMII, 10G
to SATA and others. As of now only the SGMII and 10G modes are
supported by the comphy driver.

Then patches are modifying the PPv2 driver to first add the comphy
initialization sequence (i.e. calls to the generic PHY framework) and to
then take advantage of this to allow dynamic reconfiguration (i.e.
configuring the mode of a port given what's connected, between sgmii and
10G). Note the use of the comphy in the PPv2 driver is kept optional
(i.e. if not described in dt the driver still as before an relies on the
firmware/bootloader configuration).

Finally there are dt/defconfig patches to describe and take advantage of
this.

This was tested on a range of devices: 8040-db, 8040-mcbin and 7040-db.

@Dave: the dt patches should go through the mvebu tree (patches 9-13).

Thanks!
Antoine

Since v3:
  - Now use of_phy_simple_xlate() to retrieve the phy.
  - Added an owner in the phy_ops structure.
  - Now allow the module to be selected with COMPILE_TEST.
  - Removed unused parameter in the comphy set_mode functions.
  - Added Kishon Acked-by in patch 1.

Since v2:
  - Kept the link mode enforcement.
  - Removed the netif_running() check.
  - Reworded the "dynamic reconfiguration of the PHY mode" commit log.
  - Added one patch not to force the GMAC autoneg parameters when using
    the XLG MAC.

Since v1:
  - Updated the mode settings variable name in the comphy driver to
    have 'cp110' in it.
  - Documented the PHY cell argument in the dt documentation.
  - New patch adding comphy phandles for the 7040-db board.
  - Checked if the carrier_on/off functions were needed. They are.
  - s/PHY/generic PHY/ in commit log of patch 1.
  - Rebased on the latest net-next/master.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d36b82bc 89273bc0
mvebu comphy driver
-------------------
A comphy controller can be found on Marvell Armada 7k/8k on the CP110. It
provides a number of shared PHYs used by various interfaces (network, sata,
usb, PCIe...).
Required properties:
- compatible: should be "marvell,comphy-cp110"
- reg: should contain the comphy register location and length.
- marvell,system-controller: should contain a phandle to the
system controller node.
- #address-cells: should be 1.
- #size-cells: should be 0.
A sub-node is required for each comphy lane provided by the comphy.
Required properties (child nodes):
- reg: comphy lane number.
- #phy-cells : from the generic phy bindings, must be 1. Defines the
input port to use for a given comphy lane.
Example:
cpm_comphy: phy@120000 {
compatible = "marvell,comphy-cp110";
reg = <0x120000 0x6000>;
marvell,system-controller = <&cpm_syscon0>;
#address-cells = <1>;
#size-cells = <0>;
cpm_comphy0: phy@0 {
reg = <0>;
#phy-cells = <1>;
};
cpm_comphy1: phy@1 {
reg = <1>;
#phy-cells = <1>;
};
};
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/ktime.h> #include <linux/ktime.h>
...@@ -861,6 +862,7 @@ struct mvpp2_port { ...@@ -861,6 +862,7 @@ struct mvpp2_port {
phy_interface_t phy_interface; phy_interface_t phy_interface;
struct device_node *phy_node; struct device_node *phy_node;
struct phy *comphy;
unsigned int link; unsigned int link;
unsigned int duplex; unsigned int duplex;
unsigned int speed; unsigned int speed;
...@@ -4420,6 +4422,32 @@ static int mvpp22_gop_init(struct mvpp2_port *port) ...@@ -4420,6 +4422,32 @@ static int mvpp22_gop_init(struct mvpp2_port *port)
return -EINVAL; return -EINVAL;
} }
static int mvpp22_comphy_init(struct mvpp2_port *port)
{
enum phy_mode mode;
int ret;
if (!port->comphy)
return 0;
switch (port->phy_interface) {
case PHY_INTERFACE_MODE_SGMII:
mode = PHY_MODE_SGMII;
break;
case PHY_INTERFACE_MODE_10GKR:
mode = PHY_MODE_10GKR;
break;
default:
return -EINVAL;
}
ret = phy_set_mode(port->comphy, mode);
if (ret)
return ret;
return phy_power_on(port->comphy);
}
static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port)
{ {
u32 val; u32 val;
...@@ -5707,63 +5735,108 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id) ...@@ -5707,63 +5735,108 @@ static irqreturn_t mvpp2_isr(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port,
struct phy_device *phydev)
{
u32 val;
if (port->phy_interface != PHY_INTERFACE_MODE_RGMII &&
port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID &&
port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID &&
port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID &&
port->phy_interface != PHY_INTERFACE_MODE_SGMII)
return;
val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX |
MVPP2_GMAC_AN_SPEED_EN |
MVPP2_GMAC_AN_DUPLEX_EN);
if (phydev->duplex)
val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
if (phydev->speed == SPEED_1000)
val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
else if (phydev->speed == SPEED_100)
val |= MVPP2_GMAC_CONFIG_MII_SPEED;
writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
}
/* Adjust link */ /* Adjust link */
static void mvpp2_link_event(struct net_device *dev) static void mvpp2_link_event(struct net_device *dev)
{ {
struct mvpp2_port *port = netdev_priv(dev); struct mvpp2_port *port = netdev_priv(dev);
struct phy_device *phydev = dev->phydev; struct phy_device *phydev = dev->phydev;
int status_change = 0; bool link_reconfigured = false;
u32 val; u32 val;
if (phydev->link) { if (phydev->link) {
if (port->phy_interface != phydev->interface && port->comphy) {
/* disable current port for reconfiguration */
mvpp2_interrupts_disable(port);
netif_carrier_off(port->dev);
mvpp2_port_disable(port);
phy_power_off(port->comphy);
/* comphy reconfiguration */
port->phy_interface = phydev->interface;
mvpp22_comphy_init(port);
/* gop/mac reconfiguration */
mvpp22_gop_init(port);
mvpp2_port_mii_set(port);
link_reconfigured = true;
}
if ((port->speed != phydev->speed) || if ((port->speed != phydev->speed) ||
(port->duplex != phydev->duplex)) { (port->duplex != phydev->duplex)) {
u32 val; mvpp2_gmac_set_autoneg(port, phydev);
val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED |
MVPP2_GMAC_CONFIG_GMII_SPEED |
MVPP2_GMAC_CONFIG_FULL_DUPLEX |
MVPP2_GMAC_AN_SPEED_EN |
MVPP2_GMAC_AN_DUPLEX_EN);
if (phydev->duplex)
val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
if (phydev->speed == SPEED_1000)
val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
else if (phydev->speed == SPEED_100)
val |= MVPP2_GMAC_CONFIG_MII_SPEED;
writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
port->duplex = phydev->duplex; port->duplex = phydev->duplex;
port->speed = phydev->speed; port->speed = phydev->speed;
} }
} }
if (phydev->link != port->link) { if (phydev->link != port->link || link_reconfigured) {
if (!phydev->link) {
port->duplex = -1;
port->speed = 0;
}
port->link = phydev->link; port->link = phydev->link;
status_change = 1;
}
if (status_change) {
if (phydev->link) { if (phydev->link) {
val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); if (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
val |= (MVPP2_GMAC_FORCE_LINK_PASS | port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
MVPP2_GMAC_FORCE_LINK_DOWN); port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
val |= (MVPP2_GMAC_FORCE_LINK_PASS |
MVPP2_GMAC_FORCE_LINK_DOWN);
writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
}
mvpp2_interrupts_enable(port);
mvpp2_port_enable(port);
mvpp2_egress_enable(port); mvpp2_egress_enable(port);
mvpp2_ingress_enable(port); mvpp2_ingress_enable(port);
netif_carrier_on(dev);
netif_tx_wake_all_queues(dev);
} else { } else {
port->duplex = -1;
port->speed = 0;
netif_tx_stop_all_queues(dev);
netif_carrier_off(dev);
mvpp2_ingress_disable(port); mvpp2_ingress_disable(port);
mvpp2_egress_disable(port); mvpp2_egress_disable(port);
mvpp2_port_disable(port);
mvpp2_interrupts_disable(port);
} }
phy_print_status(phydev); phy_print_status(phydev);
} }
} }
...@@ -6404,8 +6477,10 @@ static void mvpp2_start_dev(struct mvpp2_port *port) ...@@ -6404,8 +6477,10 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
/* Enable interrupts on all CPUs */ /* Enable interrupts on all CPUs */
mvpp2_interrupts_enable(port); mvpp2_interrupts_enable(port);
if (port->priv->hw_version == MVPP22) if (port->priv->hw_version == MVPP22) {
mvpp22_comphy_init(port);
mvpp22_gop_init(port); mvpp22_gop_init(port);
}
mvpp2_port_mii_set(port); mvpp2_port_mii_set(port);
mvpp2_port_enable(port); mvpp2_port_enable(port);
...@@ -6436,6 +6511,7 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) ...@@ -6436,6 +6511,7 @@ static void mvpp2_stop_dev(struct mvpp2_port *port)
mvpp2_egress_disable(port); mvpp2_egress_disable(port);
mvpp2_port_disable(port); mvpp2_port_disable(port);
phy_stop(ndev->phydev); phy_stop(ndev->phydev);
phy_power_off(port->comphy);
} }
static int mvpp2_check_ringparam_valid(struct net_device *dev, static int mvpp2_check_ringparam_valid(struct net_device *dev,
...@@ -7242,6 +7318,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, ...@@ -7242,6 +7318,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
struct mvpp2 *priv) struct mvpp2 *priv)
{ {
struct device_node *phy_node; struct device_node *phy_node;
struct phy *comphy;
struct mvpp2_port *port; struct mvpp2_port *port;
struct mvpp2_port_pcpu *port_pcpu; struct mvpp2_port_pcpu *port_pcpu;
struct net_device *dev; struct net_device *dev;
...@@ -7285,6 +7362,15 @@ static int mvpp2_port_probe(struct platform_device *pdev, ...@@ -7285,6 +7362,15 @@ static int mvpp2_port_probe(struct platform_device *pdev,
goto err_free_netdev; goto err_free_netdev;
} }
comphy = devm_of_phy_get(&pdev->dev, port_node, NULL);
if (IS_ERR(comphy)) {
if (PTR_ERR(comphy) == -EPROBE_DEFER) {
err = -EPROBE_DEFER;
goto err_free_netdev;
}
comphy = NULL;
}
if (of_property_read_u32(port_node, "port-id", &id)) { if (of_property_read_u32(port_node, "port-id", &id)) {
err = -EINVAL; err = -EINVAL;
dev_err(&pdev->dev, "missing port-id value\n"); dev_err(&pdev->dev, "missing port-id value\n");
...@@ -7318,6 +7404,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, ...@@ -7318,6 +7404,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
port->phy_node = phy_node; port->phy_node = phy_node;
port->phy_interface = phy_mode; port->phy_interface = phy_mode;
port->comphy = comphy;
if (priv->hw_version == MVPP21) { if (priv->hw_version == MVPP21) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + id); res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + id);
......
...@@ -21,6 +21,17 @@ config PHY_BERLIN_USB ...@@ -21,6 +21,17 @@ config PHY_BERLIN_USB
help help
Enable this to support the USB PHY on Marvell Berlin SoCs. Enable this to support the USB PHY on Marvell Berlin SoCs.
config PHY_MVEBU_CP110_COMPHY
tristate "Marvell CP110 comphy driver"
depends on ARCH_MVEBU || COMPILE_TEST
depends on OF
select GENERIC_PHY
help
This driver allows to control the comphy, an hardware block providing
shared serdes PHYs on Marvell Armada 7k/8k (in the CP110). Its serdes
lanes can be used by various controllers (Ethernet, sata, usb,
PCIe...).
config PHY_MVEBU_SATA config PHY_MVEBU_SATA
def_bool y def_bool y
depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
......
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o
This diff is collapsed.
...@@ -27,6 +27,8 @@ enum phy_mode { ...@@ -27,6 +27,8 @@ enum phy_mode {
PHY_MODE_USB_HOST, PHY_MODE_USB_HOST,
PHY_MODE_USB_DEVICE, PHY_MODE_USB_DEVICE,
PHY_MODE_USB_OTG, PHY_MODE_USB_OTG,
PHY_MODE_SGMII,
PHY_MODE_10GKR,
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment