[PATCH 16/16] net: phy: mv88e6xxx: Add support for MAC ports

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Andrey Smirnov <andrew.smirnov@xxxxxxxxx>
---
 drivers/net/phy/mv88e6xxx/chip.c | 146 ++++++-
 drivers/net/phy/mv88e6xxx/chip.h |  72 ++++
 drivers/net/phy/mv88e6xxx/port.c | 646 +++++++++++++++++++++++++++++++
 drivers/net/phy/mv88e6xxx/port.h |  38 ++
 4 files changed, 898 insertions(+), 4 deletions(-)

diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c
index 162acf1cc..5a8947f8b 100644
--- a/drivers/net/phy/mv88e6xxx/chip.c
+++ b/drivers/net/phy/mv88e6xxx/chip.c
@@ -64,12 +64,20 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
 	/* MV88E6XXX_FAMILY_6097 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6123_ops = {
 	/* MV88E6XXX_FAMILY_6165 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6131_ops = {
@@ -85,12 +93,20 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6161_ops = {
 	/* MV88E6XXX_FAMILY_6165 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6165_ops = {
@@ -104,6 +120,11 @@ static const struct mv88e6xxx_ops mv88e6171_ops = {
 	/* MV88E6XXX_FAMILY_6351 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6172_ops = {
@@ -112,12 +133,22 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6352_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6175_ops = {
 	/* MV88E6XXX_FAMILY_6351 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6176_ops = {
@@ -126,6 +157,11 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6352_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -141,6 +177,11 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6190x_ops = {
@@ -149,6 +190,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390x_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6191_ops = {
@@ -157,6 +203,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -165,6 +216,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6352_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6290_ops = {
@@ -173,6 +229,11 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390_port_set_speed,
+	.port_set_cmode = mv88e6390x_port_set_cmode,
 };
 
 static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -181,6 +242,10 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -189,6 +254,10 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6341_ops = {
@@ -197,18 +266,33 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6350_ops = {
 	/* MV88E6XXX_FAMILY_6351 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6351_ops = {
 	/* MV88E6XXX_FAMILY_6351 */
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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,
+	.port_set_speed = mv88e6185_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -217,6 +301,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
 	.phy_write = mv88e6xxx_g2_smi_phy_write,
 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
+	.port_set_link = mv88e6xxx_port_set_link,
+	.port_set_duplex = mv88e6xxx_port_set_duplex,
+	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+	.port_set_speed = mv88e6352_port_set_speed,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6390_ops = {
@@ -225,6 +314,12 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390_port_set_speed,
+	.port_set_cmode = mv88e6390x_port_set_cmode,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_ops mv88e6390x_ops = {
@@ -233,6 +328,12 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
 	.phy_read = mv88e6xxx_g2_smi_phy_read,
 	.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 = mv88e6390_port_set_rgmii_delay,
+	.port_set_speed = mv88e6390x_port_set_speed,
+	.port_set_cmode = mv88e6390x_port_set_cmode,
+	.port_link_state = mv88e6352_port_link_state,
 };
 
 static const struct mv88e6xxx_info mv88e6xxx_table[] = {
@@ -241,6 +342,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6097,
 		.name = "Marvell 88E6085",
 		.num_ports = 10,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6085_ops,
@@ -251,6 +353,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6095,
 		.name = "Marvell 88E6095/88E6095F",
 		.num_ports = 11,
+		.num_internal_phys = 0,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6095_ops,
@@ -261,6 +364,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6097,
 		.name = "Marvell 88E6097/88E6097F",
 		.num_ports = 11,
+		.num_internal_phys = 8,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6097_ops,
@@ -271,6 +375,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6165,
 		.name = "Marvell 88E6123",
 		.num_ports = 3,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6123_ops,
@@ -281,6 +386,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6185,
 		.name = "Marvell 88E6131",
 		.num_ports = 8,
+		.num_internal_phys = 0,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6131_ops,
@@ -291,6 +397,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6341,
 		.name = "Marvell 88E6341",
 		.num_ports = 6,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6141_ops,
@@ -301,6 +408,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6165,
 		.name = "Marvell 88E6161",
 		.num_ports = 6,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6161_ops,
@@ -311,6 +419,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6165,
 		.name = "Marvell 88E6165",
 		.num_ports = 6,
+		.num_internal_phys = 0,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6165_ops,
@@ -321,6 +430,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6351,
 		.name = "Marvell 88E6171",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6171_ops,
@@ -331,6 +441,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6352,
 		.name = "Marvell 88E6172",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6172_ops,
@@ -341,6 +452,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6351,
 		.name = "Marvell 88E6175",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6175_ops,
@@ -351,6 +463,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6352,
 		.name = "Marvell 88E6176",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6176_ops,
@@ -361,6 +474,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6185,
 		.name = "Marvell 88E6185",
 		.num_ports = 10,
+		.num_internal_phys = 0,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6185_ops,
@@ -371,6 +485,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6390,
 		.name = "Marvell 88E6190",
 		.num_ports = 11,	/* 10 + Z80 */
+		.num_internal_phys = 11,
 		.port_base_addr = 0x0,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6190_ops,
@@ -381,6 +496,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6390,
 		.name = "Marvell 88E6190X",
 		.num_ports = 11,	/* 10 + Z80 */
+		.num_internal_phys = 11,
 		.port_base_addr = 0x0,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6190x_ops,
@@ -391,6 +507,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6390,
 		.name = "Marvell 88E6191",
 		.num_ports = 11,	/* 10 + Z80 */
+		.num_internal_phys = 11,
 		.port_base_addr = 0x0,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6191_ops,
@@ -401,6 +518,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6352,
 		.name = "Marvell 88E6240",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6240_ops,
@@ -411,6 +529,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6390,
 		.name = "Marvell 88E6290",
 		.num_ports = 11,	/* 10 + Z80 */
+		.num_internal_phys = 11,
 		.port_base_addr = 0x0,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6290_ops,
@@ -421,6 +540,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6320,
 		.name = "Marvell 88E6320",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6320_ops,
@@ -431,6 +551,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6320,
 		.name = "Marvell 88E6321",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6321_ops,
@@ -441,6 +562,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6341,
 		.name = "Marvell 88E6341",
 		.num_ports = 6,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6341_ops,
@@ -451,6 +573,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6351,
 		.name = "Marvell 88E6350",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6350_ops,
@@ -461,6 +584,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6351,
 		.name = "Marvell 88E6351",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6351_ops,
@@ -471,6 +595,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6352,
 		.name = "Marvell 88E6352",
 		.num_ports = 7,
+		.num_internal_phys = 5,
 		.port_base_addr = 0x10,
 		.ops = &mv88e6352_ops,
 		.global2_addr = 0x1c,
@@ -482,6 +607,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6390,
 		.name = "Marvell 88E6390",
 		.num_ports = 11,	/* 10 + Z80 */
+		.num_internal_phys = 11,
 		.port_base_addr = 0x0,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6390_ops,
@@ -492,6 +618,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
 		.family = MV88E6XXX_FAMILY_6390,
 		.name = "Marvell 88E6390X",
 		.num_ports = 11,	/* 10 + Z80 */
+		.num_internal_phys = 11,
 		.port_base_addr = 0x0,
 		.global2_addr = 0x1c,
 		.ops = &mv88e6390x_ops,
@@ -692,8 +819,8 @@ static int mv88e6xxx_probe(struct device_d *dev)
 	struct device_node *mdio_node;
 	struct mv88e6xxx_chip *chip;
 	enum of_gpio_flags of_flags;
-	int err;
 	u32 reg, eeprom_len = 0;
+	int err;
 
 	err = of_property_read_u32(np, "reg", &reg);
 	if (err) {
@@ -766,13 +893,16 @@ static int mv88e6xxx_probe(struct device_d *dev)
 	 * correspond to any PHYs, so we mask them to pervent from
 	 * being exposed as phy devices
 	 */
-	chip->parent_miibus->phy_mask |= GENMASK(0x1f, 0x10);
+	/* FIXME: This should probably use port_base_addr */
+
+	chip->parent_miibus->phy_mask |= GENMASK(0x1f,
+						 chip->info->port_base_addr);
 	/*
 	 * Address 0x0f on internal bus is dedicated to SERDES
 	 * registers and won't be very useful against standard PHY
 	 * driver
 	 */
-	chip->miibus.phy_mask |= GENMASK(0x1f, 0x0f);
+	chip->miibus.phy_mask |= GENMASK(0x1f, chip->info->num_ports);
 
 	chip->miibus.read = mv88e6xxx_mdio_read;
 	chip->miibus.write = mv88e6xxx_mdio_write;
@@ -784,7 +914,15 @@ static int mv88e6xxx_probe(struct device_d *dev)
 	if (mdio_node)
 		chip->miibus.dev.device_node = mdio_node;
 
-	return mdiobus_register(&chip->miibus);
+	err = m88e6xxx_port_probe(chip);
+	if (err)
+		return err;
+
+	err = mdiobus_register(&chip->miibus);
+	if (err)
+		return err;
+
+	return 0;
 }
 
 static const struct of_device_id mv88e6xxx_of_match[] = {
diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h
index 021df2664..bc527efeb 100644
--- a/drivers/net/phy/mv88e6xxx/chip.h
+++ b/drivers/net/phy/mv88e6xxx/chip.h
@@ -26,23 +26,33 @@ enum mv88e6xxx_family {
 
 struct mv88e6xxx_ops;
 
+#define DSA_MAX_PORTS		12
+
 struct mv88e6xxx_info {
 	enum mv88e6xxx_family family;
 	u16 prod_num;
 	const char *name;
 	unsigned int num_ports;
+	unsigned int num_internal_phys;
 	unsigned int port_base_addr;
 	unsigned int global2_addr;	
 
 	const struct mv88e6xxx_ops *ops;
 };
 
+struct mv88e6xxx_port {
+	u8 cmode;
+};
+
 struct mv88e6xxx_chip {
 	const struct mv88e6xxx_info *info;
 	struct mii_bus *parent_miibus;
 	struct mii_bus miibus;
 	struct device_d *dev;
 	int reset;
+
+	/* Array of port structures. */
+	struct mv88e6xxx_port ports[DSA_MAX_PORTS];
 };
 
 struct ethtool_eeprom {
@@ -50,6 +60,16 @@ struct ethtool_eeprom {
 	__u32	len;
 };
 
+struct phylink_link_state {
+	phy_interface_t interface;
+	int speed;
+	int duplex;
+	int pause;
+	unsigned int link:1;
+	unsigned int an_enabled:1;
+	unsigned int an_complete:1;
+};
+
 struct mv88e6xxx_ops {
 	int (*phy_read)(struct mv88e6xxx_chip *chip,
 			struct mii_bus *bus,
@@ -62,6 +82,58 @@ struct mv88e6xxx_ops {
 			  struct ethtool_eeprom *eeprom, u8 *data);
 	int (*set_eeprom)(struct mv88e6xxx_chip *chip,
 			  struct ethtool_eeprom *eeprom, u8 *data);
+
+	/* 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
+
+	/* Port's MAC link state
+	 * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down,
+	 * or LINK_UNFORCED for normal link detection.
+	 */
+	int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
+
+#define DUPLEX_UNFORCED		-2
+
+	/* Port's MAC duplex mode
+	 *
+	 * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex,
+	 * or DUPLEX_UNFORCED for normal duplex detection.
+	 */
+	int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
+
+#define PAUSE_ON		1
+#define PAUSE_OFF		0
+
+	/* Enable/disable sending Pause */
+	int (*port_set_pause)(struct mv88e6xxx_chip *chip, int port,
+			      int pause);
+
+#define SPEED_MAX		INT_MAX
+#define SPEED_UNFORCED		-2
+
+	/* Port's MAC speed (in Mbps)
+	 *
+	 * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.
+	 * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value.
+	 */
+	int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
+
+	/* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc.
+	 * Some chips allow this to be configured on specific ports.
+	 */
+	int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port,
+			      phy_interface_t mode);
+
+	/* Return the port link state, as required by phylink */
+	int (*port_link_state)(struct mv88e6xxx_chip *chip, int port,
+			       struct phylink_link_state *state);
 };
 
 int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c
index 59afdbd97..c15b25cc2 100644
--- a/drivers/net/phy/mv88e6xxx/port.c
+++ b/drivers/net/phy/mv88e6xxx/port.c
@@ -1,6 +1,8 @@
 #include <common.h>
 #include <init.h>
 
+#include <linux/marvell_phy.h>
+
 #include "port.h"
 
 int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
@@ -18,3 +20,647 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
 
 	return mv88e6xxx_write(chip, addr, reg, val);
 }
+
+/* Offset 0x00: MAC (or PCS or Physical) Status Register
+ *
+ * For most devices, this is read only. However the 6185 has the MyPause
+ * bit read/write.
+ */
+int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
+			     int pause)
+{
+	u16 reg;
+	int err;
+
+	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
+	if (err)
+		return err;
+
+	if (pause)
+		reg |= MV88E6XXX_PORT_STS_MY_PAUSE;
+	else
+		reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE;
+
+	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
+}
+
+/* Offset 0x01: MAC (or PCS or Physical) Control Register
+ *
+ * Link, Duplex and Flow Control have one force bit, one value bit.
+ *
+ * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value.
+ * Alternative values require the 200BASE (or AltSpeed) bit 12 set.
+ * Newer chips need a ForcedSpd bit 13 set to consider the value.
+ */
+
+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, MV88E6XXX_PORT_MAC_CTL, &reg);
+	if (err)
+		return err;
+
+	reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
+		 MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK);
+
+	switch (mode) {
+	case PHY_INTERFACE_MODE_RGMII_RXID:
+		reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK;
+		break;
+	case PHY_INTERFACE_MODE_RGMII_TXID:
+		reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
+		break;
+	case PHY_INTERFACE_MODE_RGMII_ID:
+		reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
+			MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
+		break;
+	case PHY_INTERFACE_MODE_RGMII:
+		break;
+	default:
+		return 0;
+	}
+
+	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+	if (err)
+		return err;
+
+	dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port,
+		reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no",
+		reg & MV88E6XXX_PORT_MAC_CTL_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;
+	int err;
+
+	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
+	if (err)
+		return err;
+
+	reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
+		 MV88E6XXX_PORT_MAC_CTL_LINK_UP);
+
+	switch (link) {
+	case LINK_FORCED_DOWN:
+		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK;
+		break;
+	case LINK_FORCED_UP:
+		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
+			MV88E6XXX_PORT_MAC_CTL_LINK_UP;
+		break;
+	case LINK_UNFORCED:
+		/* normal link detection */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+	if (err)
+		return err;
+
+	dev_dbg(chip->dev, "p%d: %s link %s\n", port,
+		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce",
+		reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down");
+
+	return 0;
+}
+
+int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
+{
+	u16 reg;
+	int err;
+
+	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
+	if (err)
+		return err;
+
+	reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
+		 MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);
+
+	switch (dup) {
+	case DUPLEX_HALF:
+		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
+		break;
+	case DUPLEX_FULL:
+		reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
+			MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
+		break;
+	case DUPLEX_UNFORCED:
+		/* normal duplex detection */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+	if (err)
+		return err;
+
+	dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
+		reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
+		reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
+
+	return 0;
+}
+
+static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
+				    int speed, bool alt_bit, bool force_bit)
+{
+	u16 reg, ctrl;
+	int err;
+
+	switch (speed) {
+	case 10:
+		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
+		break;
+	case 100:
+		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
+		break;
+	case 200:
+		if (alt_bit)
+			ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
+				MV88E6390_PORT_MAC_CTL_ALTSPEED;
+		else
+			ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200;
+		break;
+	case 1000:
+		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
+		break;
+	case 2500:
+		ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
+			MV88E6390_PORT_MAC_CTL_ALTSPEED;
+		break;
+	case 10000:
+		/* all bits set, fall through... */
+	case SPEED_UNFORCED:
+		ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
+	if (err)
+		return err;
+
+	reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK;
+	if (alt_bit)
+		reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;
+	if (force_bit) {
+		reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
+		if (speed != SPEED_UNFORCED)
+			ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
+	}
+	reg |= ctrl;
+
+	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+	if (err)
+		return err;
+
+	if (speed)
+		dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
+	else
+		dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
+
+	return 0;
+}
+
+/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
+int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+	if (speed == SPEED_MAX)
+		speed = 200;
+
+	if (speed > 200)
+		return -EOPNOTSUPP;
+
+	/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
+	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
+}
+
+/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
+int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+	if (speed == SPEED_MAX)
+		speed = 1000;
+
+	if (speed == 200 || speed > 1000)
+		return -EOPNOTSUPP;
+
+	return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
+}
+
+/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
+int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+	if (speed == SPEED_MAX)
+		speed = 1000;
+
+	if (speed > 1000)
+		return -EOPNOTSUPP;
+
+	if (speed == 200 && port < 5)
+		return -EOPNOTSUPP;
+
+	return mv88e6xxx_port_set_speed(chip, port, speed, true, false);
+}
+
+/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
+int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+	if (speed == SPEED_MAX)
+		speed = port < 9 ? 1000 : 2500;
+
+	if (speed > 2500)
+		return -EOPNOTSUPP;
+
+	if (speed == 200 && port != 0)
+		return -EOPNOTSUPP;
+
+	if (speed == 2500 && port < 9)
+		return -EOPNOTSUPP;
+
+	return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
+}
+
+/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
+int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
+{
+	if (speed == SPEED_MAX)
+		speed = port < 9 ? 1000 : 10000;
+
+	if (speed == 200 && port != 0)
+		return -EOPNOTSUPP;
+
+	if (speed >= 2500 && port < 9)
+		return -EOPNOTSUPP;
+
+	return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
+}
+
+int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+			      phy_interface_t mode)
+{
+	/* int lane; */
+	u16 cmode;
+	/* u16 reg; */
+	/* int err; */
+
+	if (mode == PHY_INTERFACE_MODE_NA)
+		return 0;
+
+	if (port != 9 && port != 10)
+		return -EOPNOTSUPP;
+
+	switch (mode) {
+	case PHY_INTERFACE_MODE_1000BASEX:
+		cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X;
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		cmode = MV88E6XXX_PORT_STS_CMODE_SGMII;
+		break;
+	case PHY_INTERFACE_MODE_2500BASEX:
+		cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX;
+		break;
+	case PHY_INTERFACE_MODE_XGMII:
+	case PHY_INTERFACE_MODE_XAUI:
+		cmode = MV88E6XXX_PORT_STS_CMODE_XAUI;
+		break;
+	case PHY_INTERFACE_MODE_RXAUI:
+		cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
+		break;
+	default:
+		cmode = 0;
+	}
+#if 0
+	lane = mv88e6390x_serdes_get_lane(chip, port);
+	if (lane < 0)
+		return lane;
+
+	if (chip->ports[port].serdes_irq) {
+		err = mv88e6390_serdes_irq_disable(chip, port, lane);
+		if (err)
+			return err;
+	}
+
+	err = mv88e6390_serdes_power(chip, port, false);
+	if (err)
+		return err;
+
+	if (cmode) {
+		err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
+		if (err)
+			return err;
+
+		reg &= ~MV88E6XXX_PORT_STS_CMODE_MASK;
+		reg |= cmode;
+
+		err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
+		if (err)
+			return err;
+
+		err = mv88e6390_serdes_power(chip, port, true);
+		if (err)
+			return err;
+
+		if (chip->ports[port].serdes_irq) {
+			err = mv88e6390_serdes_irq_enable(chip, port, lane);
+			if (err)
+				return err;
+		}
+	}
+#endif
+	chip->ports[port].cmode = cmode;
+
+	return 0;
+}
+
+int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
+			      struct phylink_link_state *state)
+{
+	int err;
+	u16 reg;
+
+	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
+	if (err)
+		return err;
+
+	switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) {
+	case MV88E6XXX_PORT_STS_SPEED_10:
+		state->speed = SPEED_10;
+		break;
+	case MV88E6XXX_PORT_STS_SPEED_100:
+		state->speed = SPEED_100;
+		break;
+	case MV88E6XXX_PORT_STS_SPEED_1000:
+		state->speed = SPEED_1000;
+		break;
+	case MV88E6XXX_PORT_STS_SPEED_10000:
+		if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) ==
+		    MV88E6XXX_PORT_STS_CMODE_2500BASEX)
+			state->speed = SPEED_2500;
+		else
+			state->speed = SPEED_10000;
+		break;
+	}
+
+	state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ?
+			DUPLEX_FULL : DUPLEX_HALF;
+	state->link = !!(reg & MV88E6XXX_PORT_STS_LINK);
+	state->an_enabled = 1;
+	state->an_complete = state->link;
+
+	return 0;
+}
+
+int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
+			      struct phylink_link_state *state)
+{
+	if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
+		u8 cmode = chip->ports[port].cmode;
+
+		/* When a port is in "Cross-chip serdes" mode, it uses
+		 * 1000Base-X full duplex mode, but there is no automatic
+		 * link detection. Use the sync OK status for link (as it
+		 * would do for 1000Base-X mode.)
+		 */
+		if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) {
+			u16 mac;
+			int err;
+
+			err = mv88e6xxx_port_read(chip, port,
+						  MV88E6XXX_PORT_MAC_CTL, &mac);
+			if (err)
+				return err;
+
+			state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK);
+			state->an_enabled = 1;
+			state->an_complete =
+				!!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE);
+			state->duplex =
+				state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN;
+			state->speed =
+				state->link ? SPEED_1000 : SPEED_UNKNOWN;
+
+			return 0;
+		}
+	}
+
+	return mv88e6352_port_link_state(chip, port, state);
+}
+
+
+static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
+				    int link, int speed, int duplex, int pause,
+				    phy_interface_t mode)
+{
+	int err;
+
+	if (!chip->info->ops->port_set_link)
+		return 0;
+
+	/* Port's MAC control must not be changed unless the link is down */
+	err = chip->info->ops->port_set_link(chip, port, 0);
+	if (err)
+		return err;
+
+	if (chip->info->ops->port_set_speed) {
+		err = chip->info->ops->port_set_speed(chip, port, speed);
+		if (err && err != -EOPNOTSUPP)
+			goto restore_link;
+	}
+
+	if (chip->info->ops->port_set_pause) {
+		err = chip->info->ops->port_set_pause(chip, port, pause);
+		if (err)
+			goto restore_link;
+
+	}
+
+	if (chip->info->ops->port_set_duplex) {
+		err = chip->info->ops->port_set_duplex(chip, port, duplex);
+		if (err && err != -EOPNOTSUPP)
+			goto restore_link;
+	}
+
+	if (chip->info->ops->port_set_rgmii_delay) {
+		err = chip->info->ops->port_set_rgmii_delay(chip, port, mode);
+		if (err && err != -EOPNOTSUPP)
+			goto restore_link;
+	}
+
+	if (chip->info->ops->port_set_cmode) {
+		err = chip->info->ops->port_set_cmode(chip, port, mode);
+		if (err && err != -EOPNOTSUPP)
+			goto restore_link;
+	}
+
+	err = 0;
+restore_link:
+	if (chip->info->ops->port_set_link(chip, port, link))
+		dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port);
+
+	return err;
+}
+
+static int mv88e6xxx_port_config_init(struct phy_device *phydev)
+{
+	struct mv88e6xxx_chip *chip = phydev->dev.priv;
+	int port = phydev->addr - chip->info->port_base_addr;
+	int err;
+
+	err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed,
+				       phydev->duplex, phydev->pause,
+				       phydev->interface);
+
+	if (err && err != -EOPNOTSUPP)
+		dev_err(&phydev->dev, "p%d: failed to configure MAC\n", port);
+
+	return err;
+}
+
+static int mv88e6xxx_port_config_aneg(struct phy_device *phydev)
+{
+	return 0;
+}
+
+static int mv88e6xxx_port_read_status(struct phy_device *phydev)
+{
+	struct mv88e6xxx_chip *chip = phydev->dev.priv;
+	int port = phydev->addr - chip->info->port_base_addr;
+	struct phylink_link_state state;
+	int err;
+
+	err = mv88e6352_port_link_state(chip, port, &state);
+	if (err)
+		return err;
+
+	phydev->link = state.link;
+	phydev->duplex = state.duplex;
+	phydev->speed = state.speed;
+
+	phydev->pause = phydev->asym_pause = 0;
+
+ 	return 0;
+}
+
+/*
+ * Fake switch PHY_ID used to match this driver against, devices
+ * create in m88e6xxx_port_probe.
+ */
+#define MV88E6XXX_SWITCH_PORT_PHY_ID (0x01410000 | \
+				      MV88E6XXX_PORT_SWITCH_ID_PROD_6085)
+
+static struct phy_driver mv88e6xxx_port_driver = {
+	.phy_id         = MV88E6XXX_SWITCH_PORT_PHY_ID,
+	.phy_id_mask    = MARVELL_PHY_ID_MASK,
+	.drv.name	= "Marvel 88E6xxx Port",
+	.features	= PHY_GBIT_FEATURES & ~SUPPORTED_Autoneg,
+	.config_init	= mv88e6xxx_port_config_init,
+	.config_aneg	= mv88e6xxx_port_config_aneg,
+	.read_status	= mv88e6xxx_port_read_status,
+};
+
+static int __init mv88e6xxx_port_driver_register(void)
+{
+	return phy_driver_register(&mv88e6xxx_port_driver);
+}
+fs_initcall(mv88e6xxx_port_driver_register);
+
+int m88e6xxx_port_probe(struct mv88e6xxx_chip *chip)
+{
+	struct device_d *dev = chip->dev;
+	struct device_node *np = dev->device_node;
+	struct device_node *port_node, *switch_node;
+	struct device_node *port_nodes[DSA_MAX_PORTS] = { NULL };
+	int err, i;
+
+	switch_node = of_find_node_by_name(np, "ports");
+	if (!switch_node)
+		return -EINVAL;
+
+ 	for_each_available_child_of_node(switch_node, port_node) {
+		u32 nr;
+
+ 		err = of_property_read_u32(port_node, "reg", &nr);
+		if (err) {
+			dev_err(dev,
+				"Error: Failed to find reg for child %s\n",
+				port_node->full_name);
+			continue;
+		}
+
+		port_nodes[nr] = port_node;
+	}
+
+	/*
+	 * Walk through all of the ports and unmask those that are
+	 * connected to a PHY via child MDIO bus, so the can be picked
+	 * up via regular PHY discover.
+	 *
+	 * While at it, also create PHY objects for ports that are
+	 * not, so they can be correctly configured
+	 */
+	for (i = 0; i < chip->info->num_ports; i++) {
+		struct phy_device *phydev;
+		u16 status;
+
+		err = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_STS,
+					  &status);
+		if (err)
+			return err;
+
+		if (status & MV88E6XXX_PORT_STS_PHY_DETECT) {
+			/*
+			 * True PHYs will be automaticall handled by
+			 * generic PHY driver, so we ignore those.
+			 */
+			/* chip->parent_miibus->phy_mask &= ~BIT(i); */
+			continue;
+		}
+
+		/*
+		 * In order to expose MAC-only ports on the switch, so
+		 * they can be properly configured to match other
+		 * end's settings, we create pseudo PHY devices that
+		 * will match against our special PHY driver
+		 */
+ 		phydev = phy_device_create(chip->parent_miibus,
+					   chip->info->port_base_addr + i,
+					   MV88E6XXX_SWITCH_PORT_PHY_ID);
+		phydev->dev.device_node = port_nodes[i];
+		phydev->dev.priv = chip;
+		phydev->duplex = DUPLEX_UNFORCED;
+
+		err = phy_register_device(phydev);
+		if (err)
+			dev_err(dev, "Error: Failed to register a PHY\n");
+	}
+
+	return 0;
+}
\ No newline at end of file
diff --git a/drivers/net/phy/mv88e6xxx/port.h b/drivers/net/phy/mv88e6xxx/port.h
index 84c76c42c..35fdc908d 100644
--- a/drivers/net/phy/mv88e6xxx/port.h
+++ b/drivers/net/phy/mv88e6xxx/port.h
@@ -15,6 +15,7 @@
 #define MV88E6XXX_PORT_STS_SPEED_10		0x0000
 #define MV88E6XXX_PORT_STS_SPEED_100		0x0100
 #define MV88E6XXX_PORT_STS_SPEED_1000		0x0200
+#define MV88E6XXX_PORT_STS_SPEED_10000		0x0300
 #define MV88E6352_PORT_STS_EEE			0x0040
 #define MV88E6165_PORT_STS_AM_DIS		0x0040
 #define MV88E6185_PORT_STS_MGMII		0x0040
@@ -27,14 +28,28 @@
 #define MV88E6XXX_PORT_STS_CMODE_2500BASEX	0x000b
 #define MV88E6XXX_PORT_STS_CMODE_XAUI		0x000c
 #define MV88E6XXX_PORT_STS_CMODE_RXAUI		0x000d
+#define MV88E6185_PORT_STS_CDUPLEX		0x0008
+#define MV88E6185_PORT_STS_CMODE_MASK		0x0007
+#define MV88E6185_PORT_STS_CMODE_GMII_FD	0x0000
+#define MV88E6185_PORT_STS_CMODE_MII_100_FD_PS	0x0001
+#define MV88E6185_PORT_STS_CMODE_MII_100	0x0002
+#define MV88E6185_PORT_STS_CMODE_MII_10		0x0003
+#define MV88E6185_PORT_STS_CMODE_SERDES		0x0004
+#define MV88E6185_PORT_STS_CMODE_1000BASE_X	0x0005
+#define MV88E6185_PORT_STS_CMODE_PHY		0x0006
+#define MV88E6185_PORT_STS_CMODE_DISABLED	0x0007
 
 /* Offset 0x01: MAC (or PCS or Physical) Control Register */
 #define MV88E6XXX_PORT_MAC_CTL				0x01
 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK	0x8000
 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK	0x4000
+#define MV88E6185_PORT_MAC_CTL_SYNC_OK			0x4000
 #define MV88E6390_PORT_MAC_CTL_FORCE_SPEED		0x2000
 #define MV88E6390_PORT_MAC_CTL_ALTSPEED			0x1000
 #define MV88E6352_PORT_MAC_CTL_200BASE			0x1000
+#define MV88E6185_PORT_MAC_CTL_AN_EN			0x0400
+#define MV88E6185_PORT_MAC_CTL_AN_RESTART		0x0200
+#define MV88E6185_PORT_MAC_CTL_AN_DONE			0x0100
 #define MV88E6XXX_PORT_MAC_CTL_FC			0x0080
 #define MV88E6XXX_PORT_MAC_CTL_FORCE_FC			0x0040
 #define MV88E6XXX_PORT_MAC_CTL_LINK_UP			0x0020
@@ -85,5 +100,28 @@ 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 mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
+			     int pause);
+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);
+int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
+int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
+			      phy_interface_t mode);
+int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
+			      struct phylink_link_state *state);
+int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
+			      struct phylink_link_state *state);
+
+/* Barebox specific */
+int m88e6xxx_port_probe(struct mv88e6xxx_chip *chip);
+
 
 #endif /* _MV88E6XXX_PORT_H */
\ No newline at end of file
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox



[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux