net: dsa: mv88e6xxx: add port's RGMII delay setter
authorVivien Didelot <vivien.didelot@savoirfairelinux.com>
Fri, 4 Nov 2016 02:23:34 +0000 (03:23 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 4 Nov 2016 18:40:00 +0000 (14:40 -0400)
Some chips such as 88E6352 and 88E6390 can be programmed to add delays
to RXCLK for IND inputs or to GTXCLK for OUTD outputs when port is in
RGMII mode.

Add a port function to program such delays according to the provided PHY
interface mode.

Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
drivers/net/dsa/mv88e6xxx/port.c
drivers/net/dsa/mv88e6xxx/port.h

index 49a69354b5b3f2080601bc0decf37b00c2275d04..bb93d0aefb81353c1a1b373bbd0c1e992f0e4f6d 100644 (file)
@@ -3220,6 +3220,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
        .phy_write = mv88e6xxx_g2_smi_phy_write,
        .port_set_link = mv88e6xxx_port_set_link,
        .port_set_duplex = mv88e6xxx_port_set_duplex,
+       .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 };
 
 static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -3238,6 +3239,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
        .phy_write = mv88e6xxx_g2_smi_phy_write,
        .port_set_link = mv88e6xxx_port_set_link,
        .port_set_duplex = mv88e6xxx_port_set_duplex,
+       .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 };
 
 static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -3256,6 +3258,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
        .phy_write = mv88e6xxx_g2_smi_phy_write,
        .port_set_link = mv88e6xxx_port_set_link,
        .port_set_duplex = mv88e6xxx_port_set_duplex,
+       .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 };
 
 static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3302,6 +3305,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
        .phy_write = mv88e6xxx_g2_smi_phy_write,
        .port_set_link = mv88e6xxx_port_set_link,
        .port_set_duplex = mv88e6xxx_port_set_duplex,
+       .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
 };
 
 static const struct mv88e6xxx_info mv88e6xxx_table[] = {
index ab48eb996667c1f0af6c8f9deb782beb3c25a8bb..c7527c0c77ccb9f21e309402d17d41e0f996f006 100644 (file)
@@ -728,6 +728,12 @@ struct mv88e6xxx_ops {
        int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
                         u16 val);
 
+       /* RGMII Receive/Transmit Timing Control
+        * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise.
+        */
+       int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port,
+                                   phy_interface_t mode);
+
 #define LINK_FORCED_DOWN       0
 #define LINK_FORCED_UP         1
 #define LINK_UNFORCED          -2
index 17b54441b0e904655d276ca11a78f2d9fdbe719a..838068d2b581830c507fa08bc86085d1383d0a1e 100644 (file)
@@ -35,6 +35,64 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
  * Link, Duplex and Flow Control have one force bit, one value bit.
  */
 
+static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+                                         phy_interface_t mode)
+{
+       u16 reg;
+       int err;
+
+       err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
+       if (err)
+               return err;
+
+       reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
+                PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
+
+       switch (mode) {
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+               reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
+               break;
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
+               break;
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
+                       PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
+               break;
+       default:
+               /* no delay */
+               break;
+       }
+
+       err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
+       if (err)
+               return err;
+
+       netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n",
+                  reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no",
+                  reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no");
+
+       return 0;
+}
+
+int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+                                  phy_interface_t mode)
+{
+       if (port < 5)
+               return -EOPNOTSUPP;
+
+       return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
+}
+
+int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+                                  phy_interface_t mode)
+{
+       if (port != 0)
+               return -EOPNOTSUPP;
+
+       return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
+}
+
 int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
 {
        u16 reg;
index f73e90efa61589b4012cc08fd3b15e22920ee5e2..3472b792ab5945d3764c4ca36e4fd918fac8d38d 100644 (file)
@@ -21,6 +21,11 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
 int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
                         u16 val);
 
+int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+                                  phy_interface_t mode);
+int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
+                                  phy_interface_t mode);
+
 int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);
 
 int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);