124ae3961SKevin Smith /*
224ae3961SKevin Smith * (C) Copyright 2015
324ae3961SKevin Smith * Elecsys Corporation <www.elecsyscorp.com>
424ae3961SKevin Smith * Kevin Smith <kevin.smith@elecsyscorp.com>
524ae3961SKevin Smith *
624ae3961SKevin Smith * Original driver:
724ae3961SKevin Smith * (C) Copyright 2009
824ae3961SKevin Smith * Marvell Semiconductor <www.marvell.com>
924ae3961SKevin Smith * Prafulla Wadaskar <prafulla@marvell.com>
1024ae3961SKevin Smith *
1124ae3961SKevin Smith * SPDX-License-Identifier: GPL-2.0+
1224ae3961SKevin Smith */
1324ae3961SKevin Smith
1424ae3961SKevin Smith /*
1524ae3961SKevin Smith * PHY driver for mv88e61xx ethernet switches.
1624ae3961SKevin Smith *
1724ae3961SKevin Smith * This driver configures the mv88e61xx for basic use as a PHY. The switch
1824ae3961SKevin Smith * supports a VLAN configuration that determines how traffic will be routed
1924ae3961SKevin Smith * between the ports. This driver uses a simple configuration that routes
2024ae3961SKevin Smith * traffic from each PHY port only to the CPU port, and from the CPU port to
2124ae3961SKevin Smith * any PHY port.
2224ae3961SKevin Smith *
2324ae3961SKevin Smith * The configuration determines which PHY ports to activate using the
2424ae3961SKevin Smith * CONFIG_MV88E61XX_PHY_PORTS bitmask. Setting bit 0 will activate port 0, bit
2524ae3961SKevin Smith * 1 activates port 1, etc. Do not set the bit for the port the CPU is
2624ae3961SKevin Smith * connected to unless it is connected over a PHY interface (not MII).
2724ae3961SKevin Smith *
2824ae3961SKevin Smith * This driver was written for and tested on the mv88e6176 with an SGMII
2924ae3961SKevin Smith * connection. Other configurations should be supported, but some additions or
3024ae3961SKevin Smith * changes may be required.
3124ae3961SKevin Smith */
3224ae3961SKevin Smith
3324ae3961SKevin Smith #include <common.h>
3424ae3961SKevin Smith
3524ae3961SKevin Smith #include <bitfield.h>
3624ae3961SKevin Smith #include <errno.h>
3724ae3961SKevin Smith #include <malloc.h>
3824ae3961SKevin Smith #include <miiphy.h>
3924ae3961SKevin Smith #include <netdev.h>
4024ae3961SKevin Smith
4124ae3961SKevin Smith #define PHY_AUTONEGOTIATE_TIMEOUT 5000
4224ae3961SKevin Smith
4365d4d00aSChris Packham #define PORT_COUNT 11
4424ae3961SKevin Smith #define PORT_MASK ((1 << PORT_COUNT) - 1)
4524ae3961SKevin Smith
4624ae3961SKevin Smith /* Device addresses */
4724ae3961SKevin Smith #define DEVADDR_PHY(p) (p)
4824ae3961SKevin Smith #define DEVADDR_PORT(p) (0x10 + (p))
4924ae3961SKevin Smith #define DEVADDR_SERDES 0x0F
5024ae3961SKevin Smith #define DEVADDR_GLOBAL_1 0x1B
5124ae3961SKevin Smith #define DEVADDR_GLOBAL_2 0x1C
5224ae3961SKevin Smith
5324ae3961SKevin Smith /* SMI indirection registers for multichip addressing mode */
5424ae3961SKevin Smith #define SMI_CMD_REG 0x00
5524ae3961SKevin Smith #define SMI_DATA_REG 0x01
5624ae3961SKevin Smith
5724ae3961SKevin Smith /* Global registers */
5824ae3961SKevin Smith #define GLOBAL1_STATUS 0x00
5924ae3961SKevin Smith #define GLOBAL1_CTRL 0x04
6024ae3961SKevin Smith #define GLOBAL1_MON_CTRL 0x1A
6124ae3961SKevin Smith
6224ae3961SKevin Smith /* Global 2 registers */
6324ae3961SKevin Smith #define GLOBAL2_REG_PHY_CMD 0x18
6424ae3961SKevin Smith #define GLOBAL2_REG_PHY_DATA 0x19
6524ae3961SKevin Smith
6624ae3961SKevin Smith /* Port registers */
6724ae3961SKevin Smith #define PORT_REG_STATUS 0x00
6824ae3961SKevin Smith #define PORT_REG_PHYS_CTRL 0x01
6924ae3961SKevin Smith #define PORT_REG_SWITCH_ID 0x03
7024ae3961SKevin Smith #define PORT_REG_CTRL 0x04
7124ae3961SKevin Smith #define PORT_REG_VLAN_MAP 0x06
7224ae3961SKevin Smith #define PORT_REG_VLAN_ID 0x07
7324ae3961SKevin Smith
7424ae3961SKevin Smith /* Phy registers */
7524ae3961SKevin Smith #define PHY_REG_CTRL1 0x10
7624ae3961SKevin Smith #define PHY_REG_STATUS1 0x11
7724ae3961SKevin Smith #define PHY_REG_PAGE 0x16
7824ae3961SKevin Smith
7924ae3961SKevin Smith /* Serdes registers */
8024ae3961SKevin Smith #define SERDES_REG_CTRL_1 0x10
8124ae3961SKevin Smith
8224ae3961SKevin Smith /* Phy page numbers */
8324ae3961SKevin Smith #define PHY_PAGE_COPPER 0
8424ae3961SKevin Smith #define PHY_PAGE_SERDES 1
8524ae3961SKevin Smith
8624ae3961SKevin Smith /* Register fields */
8724ae3961SKevin Smith #define GLOBAL1_CTRL_SWRESET BIT(15)
8824ae3961SKevin Smith
8924ae3961SKevin Smith #define GLOBAL1_MON_CTRL_CPUDEST_SHIFT 4
9024ae3961SKevin Smith #define GLOBAL1_MON_CTRL_CPUDEST_WIDTH 4
9124ae3961SKevin Smith
9224ae3961SKevin Smith #define PORT_REG_STATUS_LINK BIT(11)
9324ae3961SKevin Smith #define PORT_REG_STATUS_DUPLEX BIT(10)
9424ae3961SKevin Smith
9524ae3961SKevin Smith #define PORT_REG_STATUS_SPEED_SHIFT 8
9624ae3961SKevin Smith #define PORT_REG_STATUS_SPEED_WIDTH 2
9724ae3961SKevin Smith #define PORT_REG_STATUS_SPEED_10 0
9824ae3961SKevin Smith #define PORT_REG_STATUS_SPEED_100 1
9924ae3961SKevin Smith #define PORT_REG_STATUS_SPEED_1000 2
10024ae3961SKevin Smith
10124ae3961SKevin Smith #define PORT_REG_STATUS_CMODE_MASK 0xF
10224ae3961SKevin Smith #define PORT_REG_STATUS_CMODE_100BASE_X 0x8
10324ae3961SKevin Smith #define PORT_REG_STATUS_CMODE_1000BASE_X 0x9
10424ae3961SKevin Smith #define PORT_REG_STATUS_CMODE_SGMII 0xa
10524ae3961SKevin Smith
106b755abecSChris Packham #define PORT_REG_PHYS_CTRL_PCS_AN_EN BIT(10)
107b755abecSChris Packham #define PORT_REG_PHYS_CTRL_PCS_AN_RST BIT(9)
108b755abecSChris Packham #define PORT_REG_PHYS_CTRL_FC_VALUE BIT(7)
109b755abecSChris Packham #define PORT_REG_PHYS_CTRL_FC_FORCE BIT(6)
11024ae3961SKevin Smith #define PORT_REG_PHYS_CTRL_LINK_VALUE BIT(5)
11124ae3961SKevin Smith #define PORT_REG_PHYS_CTRL_LINK_FORCE BIT(4)
112b755abecSChris Packham #define PORT_REG_PHYS_CTRL_DUPLEX_VALUE BIT(3)
113b755abecSChris Packham #define PORT_REG_PHYS_CTRL_DUPLEX_FORCE BIT(2)
114b755abecSChris Packham #define PORT_REG_PHYS_CTRL_SPD1000 BIT(1)
115b755abecSChris Packham #define PORT_REG_PHYS_CTRL_SPD_MASK (BIT(1) | BIT(0))
11624ae3961SKevin Smith
11724ae3961SKevin Smith #define PORT_REG_CTRL_PSTATE_SHIFT 0
11824ae3961SKevin Smith #define PORT_REG_CTRL_PSTATE_WIDTH 2
11924ae3961SKevin Smith
12024ae3961SKevin Smith #define PORT_REG_VLAN_ID_DEF_VID_SHIFT 0
12124ae3961SKevin Smith #define PORT_REG_VLAN_ID_DEF_VID_WIDTH 12
12224ae3961SKevin Smith
12324ae3961SKevin Smith #define PORT_REG_VLAN_MAP_TABLE_SHIFT 0
12424ae3961SKevin Smith #define PORT_REG_VLAN_MAP_TABLE_WIDTH 11
12524ae3961SKevin Smith
12624ae3961SKevin Smith #define SERDES_REG_CTRL_1_FORCE_LINK BIT(10)
12724ae3961SKevin Smith
12824ae3961SKevin Smith #define PHY_REG_CTRL1_ENERGY_DET_SHIFT 8
12924ae3961SKevin Smith #define PHY_REG_CTRL1_ENERGY_DET_WIDTH 2
13024ae3961SKevin Smith
13124ae3961SKevin Smith /* Field values */
13224ae3961SKevin Smith #define PORT_REG_CTRL_PSTATE_DISABLED 0
13324ae3961SKevin Smith #define PORT_REG_CTRL_PSTATE_FORWARD 3
13424ae3961SKevin Smith
13524ae3961SKevin Smith #define PHY_REG_CTRL1_ENERGY_DET_OFF 0
13624ae3961SKevin Smith #define PHY_REG_CTRL1_ENERGY_DET_SENSE_ONLY 2
13724ae3961SKevin Smith #define PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT 3
13824ae3961SKevin Smith
13924ae3961SKevin Smith /* PHY Status Register */
14024ae3961SKevin Smith #define PHY_REG_STATUS1_SPEED 0xc000
14124ae3961SKevin Smith #define PHY_REG_STATUS1_GBIT 0x8000
14224ae3961SKevin Smith #define PHY_REG_STATUS1_100 0x4000
14324ae3961SKevin Smith #define PHY_REG_STATUS1_DUPLEX 0x2000
14424ae3961SKevin Smith #define PHY_REG_STATUS1_SPDDONE 0x0800
14524ae3961SKevin Smith #define PHY_REG_STATUS1_LINK 0x0400
14624ae3961SKevin Smith #define PHY_REG_STATUS1_ENERGY 0x0010
14724ae3961SKevin Smith
14824ae3961SKevin Smith /*
14924ae3961SKevin Smith * Macros for building commands for indirect addressing modes. These are valid
15024ae3961SKevin Smith * for both the indirect multichip addressing mode and the PHY indirection
15124ae3961SKevin Smith * required for the writes to any PHY register.
15224ae3961SKevin Smith */
15324ae3961SKevin Smith #define SMI_BUSY BIT(15)
15424ae3961SKevin Smith #define SMI_CMD_CLAUSE_22 BIT(12)
15524ae3961SKevin Smith #define SMI_CMD_CLAUSE_22_OP_READ (2 << 10)
15624ae3961SKevin Smith #define SMI_CMD_CLAUSE_22_OP_WRITE (1 << 10)
15724ae3961SKevin Smith
15824ae3961SKevin Smith #define SMI_CMD_READ (SMI_BUSY | SMI_CMD_CLAUSE_22 | \
15924ae3961SKevin Smith SMI_CMD_CLAUSE_22_OP_READ)
16024ae3961SKevin Smith #define SMI_CMD_WRITE (SMI_BUSY | SMI_CMD_CLAUSE_22 | \
16124ae3961SKevin Smith SMI_CMD_CLAUSE_22_OP_WRITE)
16224ae3961SKevin Smith
16324ae3961SKevin Smith #define SMI_CMD_ADDR_SHIFT 5
16424ae3961SKevin Smith #define SMI_CMD_ADDR_WIDTH 5
16524ae3961SKevin Smith #define SMI_CMD_REG_SHIFT 0
16624ae3961SKevin Smith #define SMI_CMD_REG_WIDTH 5
16724ae3961SKevin Smith
16824ae3961SKevin Smith /* Check for required macros */
16924ae3961SKevin Smith #ifndef CONFIG_MV88E61XX_PHY_PORTS
17024ae3961SKevin Smith #error Define CONFIG_MV88E61XX_PHY_PORTS to indicate which physical ports \
17124ae3961SKevin Smith to activate
17224ae3961SKevin Smith #endif
17324ae3961SKevin Smith #ifndef CONFIG_MV88E61XX_CPU_PORT
17424ae3961SKevin Smith #error Define CONFIG_MV88E61XX_CPU_PORT to the port the CPU is attached to
17524ae3961SKevin Smith #endif
17624ae3961SKevin Smith
177b755abecSChris Packham /*
178b755abecSChris Packham * These are ports without PHYs that may be wired directly
179b755abecSChris Packham * to other serdes interfaces
180b755abecSChris Packham */
181b755abecSChris Packham #ifndef CONFIG_MV88E61XX_FIXED_PORTS
182b755abecSChris Packham #define CONFIG_MV88E61XX_FIXED_PORTS 0
183b755abecSChris Packham #endif
184b755abecSChris Packham
18524ae3961SKevin Smith /* ID register values for different switch models */
18665d4d00aSChris Packham #define PORT_SWITCH_ID_6096 0x0980
18765d4d00aSChris Packham #define PORT_SWITCH_ID_6097 0x0990
18824ae3961SKevin Smith #define PORT_SWITCH_ID_6172 0x1720
18924ae3961SKevin Smith #define PORT_SWITCH_ID_6176 0x1760
19024ae3961SKevin Smith #define PORT_SWITCH_ID_6240 0x2400
19124ae3961SKevin Smith #define PORT_SWITCH_ID_6352 0x3520
19224ae3961SKevin Smith
19324ae3961SKevin Smith struct mv88e61xx_phy_priv {
19424ae3961SKevin Smith struct mii_dev *mdio_bus;
19524ae3961SKevin Smith int smi_addr;
19624ae3961SKevin Smith int id;
19724ae3961SKevin Smith };
19824ae3961SKevin Smith
smi_cmd(int cmd,int addr,int reg)19924ae3961SKevin Smith static inline int smi_cmd(int cmd, int addr, int reg)
20024ae3961SKevin Smith {
20124ae3961SKevin Smith cmd = bitfield_replace(cmd, SMI_CMD_ADDR_SHIFT, SMI_CMD_ADDR_WIDTH,
20224ae3961SKevin Smith addr);
20324ae3961SKevin Smith cmd = bitfield_replace(cmd, SMI_CMD_REG_SHIFT, SMI_CMD_REG_WIDTH, reg);
20424ae3961SKevin Smith return cmd;
20524ae3961SKevin Smith }
20624ae3961SKevin Smith
smi_cmd_read(int addr,int reg)20724ae3961SKevin Smith static inline int smi_cmd_read(int addr, int reg)
20824ae3961SKevin Smith {
20924ae3961SKevin Smith return smi_cmd(SMI_CMD_READ, addr, reg);
21024ae3961SKevin Smith }
21124ae3961SKevin Smith
smi_cmd_write(int addr,int reg)21224ae3961SKevin Smith static inline int smi_cmd_write(int addr, int reg)
21324ae3961SKevin Smith {
21424ae3961SKevin Smith return smi_cmd(SMI_CMD_WRITE, addr, reg);
21524ae3961SKevin Smith }
21624ae3961SKevin Smith
mv88e61xx_hw_reset(struct phy_device * phydev)21724ae3961SKevin Smith __weak int mv88e61xx_hw_reset(struct phy_device *phydev)
21824ae3961SKevin Smith {
21924ae3961SKevin Smith return 0;
22024ae3961SKevin Smith }
22124ae3961SKevin Smith
22224ae3961SKevin Smith /* Wait for the current SMI indirect command to complete */
mv88e61xx_smi_wait(struct mii_dev * bus,int smi_addr)22324ae3961SKevin Smith static int mv88e61xx_smi_wait(struct mii_dev *bus, int smi_addr)
22424ae3961SKevin Smith {
22524ae3961SKevin Smith int val;
22624ae3961SKevin Smith u32 timeout = 100;
22724ae3961SKevin Smith
22824ae3961SKevin Smith do {
22924ae3961SKevin Smith val = bus->read(bus, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG);
23024ae3961SKevin Smith if (val >= 0 && (val & SMI_BUSY) == 0)
23124ae3961SKevin Smith return 0;
23224ae3961SKevin Smith
23324ae3961SKevin Smith mdelay(1);
23424ae3961SKevin Smith } while (--timeout);
23524ae3961SKevin Smith
23624ae3961SKevin Smith puts("SMI busy timeout\n");
23724ae3961SKevin Smith return -ETIMEDOUT;
23824ae3961SKevin Smith }
23924ae3961SKevin Smith
24024ae3961SKevin Smith /*
24124ae3961SKevin Smith * The mv88e61xx has three types of addresses: the smi bus address, the device
24224ae3961SKevin Smith * address, and the register address. The smi bus address distinguishes it on
24324ae3961SKevin Smith * the smi bus from other PHYs or switches. The device address determines
24424ae3961SKevin Smith * which on-chip register set you are reading/writing (the various PHYs, their
24524ae3961SKevin Smith * associated ports, or global configuration registers). The register address
24624ae3961SKevin Smith * is the offset of the register you are reading/writing.
24724ae3961SKevin Smith *
24824ae3961SKevin Smith * When the mv88e61xx is hardware configured to have address zero, it behaves in
24924ae3961SKevin Smith * single-chip addressing mode, where it responds to all SMI addresses, using
25024ae3961SKevin Smith * the smi address as its device address. This obviously only works when this
25124ae3961SKevin Smith * is the only chip on the SMI bus. This allows the driver to access device
25224ae3961SKevin Smith * registers without using indirection. When the chip is configured to a
25324ae3961SKevin Smith * non-zero address, it only responds to that SMI address and requires indirect
25424ae3961SKevin Smith * writes to access the different device addresses.
25524ae3961SKevin Smith */
mv88e61xx_reg_read(struct phy_device * phydev,int dev,int reg)25624ae3961SKevin Smith static int mv88e61xx_reg_read(struct phy_device *phydev, int dev, int reg)
25724ae3961SKevin Smith {
25824ae3961SKevin Smith struct mv88e61xx_phy_priv *priv = phydev->priv;
25924ae3961SKevin Smith struct mii_dev *mdio_bus = priv->mdio_bus;
26024ae3961SKevin Smith int smi_addr = priv->smi_addr;
26124ae3961SKevin Smith int res;
26224ae3961SKevin Smith
26324ae3961SKevin Smith /* In single-chip mode, the device can be addressed directly */
26424ae3961SKevin Smith if (smi_addr == 0)
26524ae3961SKevin Smith return mdio_bus->read(mdio_bus, dev, MDIO_DEVAD_NONE, reg);
26624ae3961SKevin Smith
26724ae3961SKevin Smith /* Wait for the bus to become free */
26824ae3961SKevin Smith res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
26924ae3961SKevin Smith if (res < 0)
27024ae3961SKevin Smith return res;
27124ae3961SKevin Smith
27224ae3961SKevin Smith /* Issue the read command */
27324ae3961SKevin Smith res = mdio_bus->write(mdio_bus, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG,
27424ae3961SKevin Smith smi_cmd_read(dev, reg));
27524ae3961SKevin Smith if (res < 0)
27624ae3961SKevin Smith return res;
27724ae3961SKevin Smith
27824ae3961SKevin Smith /* Wait for the read command to complete */
27924ae3961SKevin Smith res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
28024ae3961SKevin Smith if (res < 0)
28124ae3961SKevin Smith return res;
28224ae3961SKevin Smith
28324ae3961SKevin Smith /* Read the data */
28424ae3961SKevin Smith res = mdio_bus->read(mdio_bus, smi_addr, MDIO_DEVAD_NONE, SMI_DATA_REG);
28524ae3961SKevin Smith if (res < 0)
28624ae3961SKevin Smith return res;
28724ae3961SKevin Smith
28824ae3961SKevin Smith return bitfield_extract(res, 0, 16);
28924ae3961SKevin Smith }
29024ae3961SKevin Smith
29124ae3961SKevin Smith /* See the comment above mv88e61xx_reg_read */
mv88e61xx_reg_write(struct phy_device * phydev,int dev,int reg,u16 val)29224ae3961SKevin Smith static int mv88e61xx_reg_write(struct phy_device *phydev, int dev, int reg,
29324ae3961SKevin Smith u16 val)
29424ae3961SKevin Smith {
29524ae3961SKevin Smith struct mv88e61xx_phy_priv *priv = phydev->priv;
29624ae3961SKevin Smith struct mii_dev *mdio_bus = priv->mdio_bus;
29724ae3961SKevin Smith int smi_addr = priv->smi_addr;
29824ae3961SKevin Smith int res;
29924ae3961SKevin Smith
30024ae3961SKevin Smith /* In single-chip mode, the device can be addressed directly */
30124ae3961SKevin Smith if (smi_addr == 0) {
30224ae3961SKevin Smith return mdio_bus->write(mdio_bus, dev, MDIO_DEVAD_NONE, reg,
30324ae3961SKevin Smith val);
30424ae3961SKevin Smith }
30524ae3961SKevin Smith
30624ae3961SKevin Smith /* Wait for the bus to become free */
30724ae3961SKevin Smith res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
30824ae3961SKevin Smith if (res < 0)
30924ae3961SKevin Smith return res;
31024ae3961SKevin Smith
31124ae3961SKevin Smith /* Set the data to write */
31224ae3961SKevin Smith res = mdio_bus->write(mdio_bus, smi_addr, MDIO_DEVAD_NONE,
31324ae3961SKevin Smith SMI_DATA_REG, val);
31424ae3961SKevin Smith if (res < 0)
31524ae3961SKevin Smith return res;
31624ae3961SKevin Smith
31724ae3961SKevin Smith /* Issue the write command */
31824ae3961SKevin Smith res = mdio_bus->write(mdio_bus, smi_addr, MDIO_DEVAD_NONE, SMI_CMD_REG,
31924ae3961SKevin Smith smi_cmd_write(dev, reg));
32024ae3961SKevin Smith if (res < 0)
32124ae3961SKevin Smith return res;
32224ae3961SKevin Smith
32324ae3961SKevin Smith /* Wait for the write command to complete */
32424ae3961SKevin Smith res = mv88e61xx_smi_wait(mdio_bus, smi_addr);
32524ae3961SKevin Smith if (res < 0)
32624ae3961SKevin Smith return res;
32724ae3961SKevin Smith
32824ae3961SKevin Smith return 0;
32924ae3961SKevin Smith }
33024ae3961SKevin Smith
mv88e61xx_phy_wait(struct phy_device * phydev)33124ae3961SKevin Smith static int mv88e61xx_phy_wait(struct phy_device *phydev)
33224ae3961SKevin Smith {
33324ae3961SKevin Smith int val;
33424ae3961SKevin Smith u32 timeout = 100;
33524ae3961SKevin Smith
33624ae3961SKevin Smith do {
33724ae3961SKevin Smith val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_2,
33824ae3961SKevin Smith GLOBAL2_REG_PHY_CMD);
33924ae3961SKevin Smith if (val >= 0 && (val & SMI_BUSY) == 0)
34024ae3961SKevin Smith return 0;
34124ae3961SKevin Smith
34224ae3961SKevin Smith mdelay(1);
34324ae3961SKevin Smith } while (--timeout);
34424ae3961SKevin Smith
34524ae3961SKevin Smith return -ETIMEDOUT;
34624ae3961SKevin Smith }
34724ae3961SKevin Smith
mv88e61xx_phy_read_indirect(struct mii_dev * smi_wrapper,int dev,int devad,int reg)34824ae3961SKevin Smith static int mv88e61xx_phy_read_indirect(struct mii_dev *smi_wrapper, int dev,
34924ae3961SKevin Smith int devad, int reg)
35024ae3961SKevin Smith {
35124ae3961SKevin Smith struct phy_device *phydev;
35224ae3961SKevin Smith int res;
35324ae3961SKevin Smith
35424ae3961SKevin Smith phydev = (struct phy_device *)smi_wrapper->priv;
35524ae3961SKevin Smith
35624ae3961SKevin Smith /* Issue command to read */
35724ae3961SKevin Smith res = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_2,
35824ae3961SKevin Smith GLOBAL2_REG_PHY_CMD,
35924ae3961SKevin Smith smi_cmd_read(dev, reg));
36024ae3961SKevin Smith
36124ae3961SKevin Smith /* Wait for data to be read */
36224ae3961SKevin Smith res = mv88e61xx_phy_wait(phydev);
36324ae3961SKevin Smith if (res < 0)
36424ae3961SKevin Smith return res;
36524ae3961SKevin Smith
36624ae3961SKevin Smith /* Read retrieved data */
36724ae3961SKevin Smith return mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_2,
36824ae3961SKevin Smith GLOBAL2_REG_PHY_DATA);
36924ae3961SKevin Smith }
37024ae3961SKevin Smith
mv88e61xx_phy_write_indirect(struct mii_dev * smi_wrapper,int dev,int devad,int reg,u16 data)37124ae3961SKevin Smith static int mv88e61xx_phy_write_indirect(struct mii_dev *smi_wrapper, int dev,
37224ae3961SKevin Smith int devad, int reg, u16 data)
37324ae3961SKevin Smith {
37424ae3961SKevin Smith struct phy_device *phydev;
37524ae3961SKevin Smith int res;
37624ae3961SKevin Smith
37724ae3961SKevin Smith phydev = (struct phy_device *)smi_wrapper->priv;
37824ae3961SKevin Smith
37924ae3961SKevin Smith /* Set the data to write */
38024ae3961SKevin Smith res = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_2,
38124ae3961SKevin Smith GLOBAL2_REG_PHY_DATA, data);
38224ae3961SKevin Smith if (res < 0)
38324ae3961SKevin Smith return res;
38424ae3961SKevin Smith /* Issue the write command */
38524ae3961SKevin Smith res = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_2,
38624ae3961SKevin Smith GLOBAL2_REG_PHY_CMD,
38724ae3961SKevin Smith smi_cmd_write(dev, reg));
38824ae3961SKevin Smith if (res < 0)
38924ae3961SKevin Smith return res;
39024ae3961SKevin Smith
39124ae3961SKevin Smith /* Wait for command to complete */
39224ae3961SKevin Smith return mv88e61xx_phy_wait(phydev);
39324ae3961SKevin Smith }
39424ae3961SKevin Smith
39524ae3961SKevin Smith /* Wrapper function to make calls to phy_read_indirect simpler */
mv88e61xx_phy_read(struct phy_device * phydev,int phy,int reg)39624ae3961SKevin Smith static int mv88e61xx_phy_read(struct phy_device *phydev, int phy, int reg)
39724ae3961SKevin Smith {
39824ae3961SKevin Smith return mv88e61xx_phy_read_indirect(phydev->bus, DEVADDR_PHY(phy),
39924ae3961SKevin Smith MDIO_DEVAD_NONE, reg);
40024ae3961SKevin Smith }
40124ae3961SKevin Smith
40224ae3961SKevin Smith /* Wrapper function to make calls to phy_read_indirect simpler */
mv88e61xx_phy_write(struct phy_device * phydev,int phy,int reg,u16 val)40324ae3961SKevin Smith static int mv88e61xx_phy_write(struct phy_device *phydev, int phy,
40424ae3961SKevin Smith int reg, u16 val)
40524ae3961SKevin Smith {
40624ae3961SKevin Smith return mv88e61xx_phy_write_indirect(phydev->bus, DEVADDR_PHY(phy),
40724ae3961SKevin Smith MDIO_DEVAD_NONE, reg, val);
40824ae3961SKevin Smith }
40924ae3961SKevin Smith
mv88e61xx_port_read(struct phy_device * phydev,u8 port,u8 reg)41024ae3961SKevin Smith static int mv88e61xx_port_read(struct phy_device *phydev, u8 port, u8 reg)
41124ae3961SKevin Smith {
41224ae3961SKevin Smith return mv88e61xx_reg_read(phydev, DEVADDR_PORT(port), reg);
41324ae3961SKevin Smith }
41424ae3961SKevin Smith
mv88e61xx_port_write(struct phy_device * phydev,u8 port,u8 reg,u16 val)41524ae3961SKevin Smith static int mv88e61xx_port_write(struct phy_device *phydev, u8 port, u8 reg,
41624ae3961SKevin Smith u16 val)
41724ae3961SKevin Smith {
41824ae3961SKevin Smith return mv88e61xx_reg_write(phydev, DEVADDR_PORT(port), reg, val);
41924ae3961SKevin Smith }
42024ae3961SKevin Smith
mv88e61xx_set_page(struct phy_device * phydev,u8 phy,u8 page)42124ae3961SKevin Smith static int mv88e61xx_set_page(struct phy_device *phydev, u8 phy, u8 page)
42224ae3961SKevin Smith {
42324ae3961SKevin Smith return mv88e61xx_phy_write(phydev, phy, PHY_REG_PAGE, page);
42424ae3961SKevin Smith }
42524ae3961SKevin Smith
mv88e61xx_get_switch_id(struct phy_device * phydev)42624ae3961SKevin Smith static int mv88e61xx_get_switch_id(struct phy_device *phydev)
42724ae3961SKevin Smith {
42824ae3961SKevin Smith int res;
42924ae3961SKevin Smith
43024ae3961SKevin Smith res = mv88e61xx_port_read(phydev, 0, PORT_REG_SWITCH_ID);
43124ae3961SKevin Smith if (res < 0)
43224ae3961SKevin Smith return res;
43324ae3961SKevin Smith return res & 0xfff0;
43424ae3961SKevin Smith }
43524ae3961SKevin Smith
mv88e61xx_6352_family(struct phy_device * phydev)43624ae3961SKevin Smith static bool mv88e61xx_6352_family(struct phy_device *phydev)
43724ae3961SKevin Smith {
43824ae3961SKevin Smith struct mv88e61xx_phy_priv *priv = phydev->priv;
43924ae3961SKevin Smith
44024ae3961SKevin Smith switch (priv->id) {
44124ae3961SKevin Smith case PORT_SWITCH_ID_6172:
44224ae3961SKevin Smith case PORT_SWITCH_ID_6176:
44324ae3961SKevin Smith case PORT_SWITCH_ID_6240:
44424ae3961SKevin Smith case PORT_SWITCH_ID_6352:
44524ae3961SKevin Smith return true;
44624ae3961SKevin Smith }
44724ae3961SKevin Smith return false;
44824ae3961SKevin Smith }
44924ae3961SKevin Smith
mv88e61xx_get_cmode(struct phy_device * phydev,u8 port)45024ae3961SKevin Smith static int mv88e61xx_get_cmode(struct phy_device *phydev, u8 port)
45124ae3961SKevin Smith {
45224ae3961SKevin Smith int res;
45324ae3961SKevin Smith
45424ae3961SKevin Smith res = mv88e61xx_port_read(phydev, port, PORT_REG_STATUS);
45524ae3961SKevin Smith if (res < 0)
45624ae3961SKevin Smith return res;
45724ae3961SKevin Smith return res & PORT_REG_STATUS_CMODE_MASK;
45824ae3961SKevin Smith }
45924ae3961SKevin Smith
mv88e61xx_parse_status(struct phy_device * phydev)46024ae3961SKevin Smith static int mv88e61xx_parse_status(struct phy_device *phydev)
46124ae3961SKevin Smith {
46224ae3961SKevin Smith unsigned int speed;
46324ae3961SKevin Smith unsigned int mii_reg;
46424ae3961SKevin Smith
46524ae3961SKevin Smith mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, PHY_REG_STATUS1);
46624ae3961SKevin Smith
46724ae3961SKevin Smith if ((mii_reg & PHY_REG_STATUS1_LINK) &&
46824ae3961SKevin Smith !(mii_reg & PHY_REG_STATUS1_SPDDONE)) {
46924ae3961SKevin Smith int i = 0;
47024ae3961SKevin Smith
47124ae3961SKevin Smith puts("Waiting for PHY realtime link");
47224ae3961SKevin Smith while (!(mii_reg & PHY_REG_STATUS1_SPDDONE)) {
47324ae3961SKevin Smith /* Timeout reached ? */
47424ae3961SKevin Smith if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
47524ae3961SKevin Smith puts(" TIMEOUT !\n");
47624ae3961SKevin Smith phydev->link = 0;
47724ae3961SKevin Smith break;
47824ae3961SKevin Smith }
47924ae3961SKevin Smith
48024ae3961SKevin Smith if ((i++ % 1000) == 0)
48124ae3961SKevin Smith putc('.');
48224ae3961SKevin Smith udelay(1000);
48324ae3961SKevin Smith mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
48424ae3961SKevin Smith PHY_REG_STATUS1);
48524ae3961SKevin Smith }
48624ae3961SKevin Smith puts(" done\n");
48724ae3961SKevin Smith udelay(500000); /* another 500 ms (results in faster booting) */
48824ae3961SKevin Smith } else {
48924ae3961SKevin Smith if (mii_reg & PHY_REG_STATUS1_LINK)
49024ae3961SKevin Smith phydev->link = 1;
49124ae3961SKevin Smith else
49224ae3961SKevin Smith phydev->link = 0;
49324ae3961SKevin Smith }
49424ae3961SKevin Smith
49524ae3961SKevin Smith if (mii_reg & PHY_REG_STATUS1_DUPLEX)
49624ae3961SKevin Smith phydev->duplex = DUPLEX_FULL;
49724ae3961SKevin Smith else
49824ae3961SKevin Smith phydev->duplex = DUPLEX_HALF;
49924ae3961SKevin Smith
50024ae3961SKevin Smith speed = mii_reg & PHY_REG_STATUS1_SPEED;
50124ae3961SKevin Smith
50224ae3961SKevin Smith switch (speed) {
50324ae3961SKevin Smith case PHY_REG_STATUS1_GBIT:
50424ae3961SKevin Smith phydev->speed = SPEED_1000;
50524ae3961SKevin Smith break;
50624ae3961SKevin Smith case PHY_REG_STATUS1_100:
50724ae3961SKevin Smith phydev->speed = SPEED_100;
50824ae3961SKevin Smith break;
50924ae3961SKevin Smith default:
51024ae3961SKevin Smith phydev->speed = SPEED_10;
51124ae3961SKevin Smith break;
51224ae3961SKevin Smith }
51324ae3961SKevin Smith
51424ae3961SKevin Smith return 0;
51524ae3961SKevin Smith }
51624ae3961SKevin Smith
mv88e61xx_switch_reset(struct phy_device * phydev)51724ae3961SKevin Smith static int mv88e61xx_switch_reset(struct phy_device *phydev)
51824ae3961SKevin Smith {
51924ae3961SKevin Smith int time;
52024ae3961SKevin Smith int val;
52124ae3961SKevin Smith u8 port;
52224ae3961SKevin Smith
52324ae3961SKevin Smith /* Disable all ports */
52424ae3961SKevin Smith for (port = 0; port < PORT_COUNT; port++) {
52524ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_CTRL);
52624ae3961SKevin Smith if (val < 0)
52724ae3961SKevin Smith return val;
52824ae3961SKevin Smith val = bitfield_replace(val, PORT_REG_CTRL_PSTATE_SHIFT,
52924ae3961SKevin Smith PORT_REG_CTRL_PSTATE_WIDTH,
53024ae3961SKevin Smith PORT_REG_CTRL_PSTATE_DISABLED);
53124ae3961SKevin Smith val = mv88e61xx_port_write(phydev, port, PORT_REG_CTRL, val);
53224ae3961SKevin Smith if (val < 0)
53324ae3961SKevin Smith return val;
53424ae3961SKevin Smith }
53524ae3961SKevin Smith
53624ae3961SKevin Smith /* Wait 2 ms for queues to drain */
53724ae3961SKevin Smith udelay(2000);
53824ae3961SKevin Smith
53924ae3961SKevin Smith /* Reset switch */
54024ae3961SKevin Smith val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_1, GLOBAL1_CTRL);
54124ae3961SKevin Smith if (val < 0)
54224ae3961SKevin Smith return val;
54324ae3961SKevin Smith val |= GLOBAL1_CTRL_SWRESET;
54424ae3961SKevin Smith val = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_1,
54524ae3961SKevin Smith GLOBAL1_CTRL, val);
54624ae3961SKevin Smith if (val < 0)
54724ae3961SKevin Smith return val;
54824ae3961SKevin Smith
54924ae3961SKevin Smith /* Wait up to 1 second for switch reset complete */
55024ae3961SKevin Smith for (time = 1000; time; time--) {
55124ae3961SKevin Smith val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_1,
55224ae3961SKevin Smith GLOBAL1_CTRL);
55324ae3961SKevin Smith if (val >= 0 && ((val & GLOBAL1_CTRL_SWRESET) == 0))
55424ae3961SKevin Smith break;
55524ae3961SKevin Smith udelay(1000);
55624ae3961SKevin Smith }
55724ae3961SKevin Smith if (!time)
55824ae3961SKevin Smith return -ETIMEDOUT;
55924ae3961SKevin Smith
56024ae3961SKevin Smith return 0;
56124ae3961SKevin Smith }
56224ae3961SKevin Smith
mv88e61xx_serdes_init(struct phy_device * phydev)56324ae3961SKevin Smith static int mv88e61xx_serdes_init(struct phy_device *phydev)
56424ae3961SKevin Smith {
56524ae3961SKevin Smith int val;
56624ae3961SKevin Smith
56724ae3961SKevin Smith val = mv88e61xx_set_page(phydev, DEVADDR_SERDES, PHY_PAGE_SERDES);
56824ae3961SKevin Smith if (val < 0)
56924ae3961SKevin Smith return val;
57024ae3961SKevin Smith
57124ae3961SKevin Smith /* Power up serdes module */
57224ae3961SKevin Smith val = mv88e61xx_phy_read(phydev, DEVADDR_SERDES, MII_BMCR);
57324ae3961SKevin Smith if (val < 0)
57424ae3961SKevin Smith return val;
57524ae3961SKevin Smith val &= ~(BMCR_PDOWN);
57624ae3961SKevin Smith val = mv88e61xx_phy_write(phydev, DEVADDR_SERDES, MII_BMCR, val);
57724ae3961SKevin Smith if (val < 0)
57824ae3961SKevin Smith return val;
57924ae3961SKevin Smith
58024ae3961SKevin Smith return 0;
58124ae3961SKevin Smith }
58224ae3961SKevin Smith
mv88e61xx_port_enable(struct phy_device * phydev,u8 port)58324ae3961SKevin Smith static int mv88e61xx_port_enable(struct phy_device *phydev, u8 port)
58424ae3961SKevin Smith {
58524ae3961SKevin Smith int val;
58624ae3961SKevin Smith
58724ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_CTRL);
58824ae3961SKevin Smith if (val < 0)
58924ae3961SKevin Smith return val;
59024ae3961SKevin Smith val = bitfield_replace(val, PORT_REG_CTRL_PSTATE_SHIFT,
59124ae3961SKevin Smith PORT_REG_CTRL_PSTATE_WIDTH,
59224ae3961SKevin Smith PORT_REG_CTRL_PSTATE_FORWARD);
59324ae3961SKevin Smith val = mv88e61xx_port_write(phydev, port, PORT_REG_CTRL, val);
59424ae3961SKevin Smith if (val < 0)
59524ae3961SKevin Smith return val;
59624ae3961SKevin Smith
59724ae3961SKevin Smith return 0;
59824ae3961SKevin Smith }
59924ae3961SKevin Smith
mv88e61xx_port_set_vlan(struct phy_device * phydev,u8 port,u16 mask)60024ae3961SKevin Smith static int mv88e61xx_port_set_vlan(struct phy_device *phydev, u8 port,
60165d4d00aSChris Packham u16 mask)
60224ae3961SKevin Smith {
60324ae3961SKevin Smith int val;
60424ae3961SKevin Smith
60524ae3961SKevin Smith /* Set VID to port number plus one */
60624ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_VLAN_ID);
60724ae3961SKevin Smith if (val < 0)
60824ae3961SKevin Smith return val;
60924ae3961SKevin Smith val = bitfield_replace(val, PORT_REG_VLAN_ID_DEF_VID_SHIFT,
61024ae3961SKevin Smith PORT_REG_VLAN_ID_DEF_VID_WIDTH,
61124ae3961SKevin Smith port + 1);
61224ae3961SKevin Smith val = mv88e61xx_port_write(phydev, port, PORT_REG_VLAN_ID, val);
61324ae3961SKevin Smith if (val < 0)
61424ae3961SKevin Smith return val;
61524ae3961SKevin Smith
61624ae3961SKevin Smith /* Set VID mask */
61724ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_VLAN_MAP);
61824ae3961SKevin Smith if (val < 0)
61924ae3961SKevin Smith return val;
62024ae3961SKevin Smith val = bitfield_replace(val, PORT_REG_VLAN_MAP_TABLE_SHIFT,
62124ae3961SKevin Smith PORT_REG_VLAN_MAP_TABLE_WIDTH,
62224ae3961SKevin Smith mask);
62324ae3961SKevin Smith val = mv88e61xx_port_write(phydev, port, PORT_REG_VLAN_MAP, val);
62424ae3961SKevin Smith if (val < 0)
62524ae3961SKevin Smith return val;
62624ae3961SKevin Smith
62724ae3961SKevin Smith return 0;
62824ae3961SKevin Smith }
62924ae3961SKevin Smith
mv88e61xx_read_port_config(struct phy_device * phydev,u8 port)63024ae3961SKevin Smith static int mv88e61xx_read_port_config(struct phy_device *phydev, u8 port)
63124ae3961SKevin Smith {
63224ae3961SKevin Smith int res;
63324ae3961SKevin Smith int val;
63424ae3961SKevin Smith bool forced = false;
63524ae3961SKevin Smith
63624ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_STATUS);
63724ae3961SKevin Smith if (val < 0)
63824ae3961SKevin Smith return val;
63924ae3961SKevin Smith if (!(val & PORT_REG_STATUS_LINK)) {
64024ae3961SKevin Smith /* Temporarily force link to read port configuration */
64124ae3961SKevin Smith u32 timeout = 100;
64224ae3961SKevin Smith forced = true;
64324ae3961SKevin Smith
64424ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
64524ae3961SKevin Smith if (val < 0)
64624ae3961SKevin Smith return val;
64724ae3961SKevin Smith val |= (PORT_REG_PHYS_CTRL_LINK_FORCE |
64824ae3961SKevin Smith PORT_REG_PHYS_CTRL_LINK_VALUE);
64924ae3961SKevin Smith val = mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL,
65024ae3961SKevin Smith val);
65124ae3961SKevin Smith if (val < 0)
65224ae3961SKevin Smith return val;
65324ae3961SKevin Smith
65424ae3961SKevin Smith /* Wait for status register to reflect forced link */
65524ae3961SKevin Smith do {
65624ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port,
65724ae3961SKevin Smith PORT_REG_STATUS);
658*4201223dSTom Rini if (val < 0) {
659*4201223dSTom Rini res = -EIO;
66024ae3961SKevin Smith goto unforce;
661*4201223dSTom Rini }
66224ae3961SKevin Smith if (val & PORT_REG_STATUS_LINK)
66324ae3961SKevin Smith break;
66424ae3961SKevin Smith } while (--timeout);
66524ae3961SKevin Smith
66624ae3961SKevin Smith if (timeout == 0) {
66724ae3961SKevin Smith res = -ETIMEDOUT;
66824ae3961SKevin Smith goto unforce;
66924ae3961SKevin Smith }
67024ae3961SKevin Smith }
67124ae3961SKevin Smith
67224ae3961SKevin Smith if (val & PORT_REG_STATUS_DUPLEX)
67324ae3961SKevin Smith phydev->duplex = DUPLEX_FULL;
67424ae3961SKevin Smith else
67524ae3961SKevin Smith phydev->duplex = DUPLEX_HALF;
67624ae3961SKevin Smith
67724ae3961SKevin Smith val = bitfield_extract(val, PORT_REG_STATUS_SPEED_SHIFT,
67824ae3961SKevin Smith PORT_REG_STATUS_SPEED_WIDTH);
67924ae3961SKevin Smith switch (val) {
68024ae3961SKevin Smith case PORT_REG_STATUS_SPEED_1000:
68124ae3961SKevin Smith phydev->speed = SPEED_1000;
68224ae3961SKevin Smith break;
68324ae3961SKevin Smith case PORT_REG_STATUS_SPEED_100:
68424ae3961SKevin Smith phydev->speed = SPEED_100;
68524ae3961SKevin Smith break;
68624ae3961SKevin Smith default:
68724ae3961SKevin Smith phydev->speed = SPEED_10;
68824ae3961SKevin Smith break;
68924ae3961SKevin Smith }
69024ae3961SKevin Smith
69124ae3961SKevin Smith res = 0;
69224ae3961SKevin Smith
69324ae3961SKevin Smith unforce:
69424ae3961SKevin Smith if (forced) {
69524ae3961SKevin Smith val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
69624ae3961SKevin Smith if (val < 0)
69724ae3961SKevin Smith return val;
69824ae3961SKevin Smith val &= ~(PORT_REG_PHYS_CTRL_LINK_FORCE |
69924ae3961SKevin Smith PORT_REG_PHYS_CTRL_LINK_VALUE);
70024ae3961SKevin Smith val = mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL,
70124ae3961SKevin Smith val);
70224ae3961SKevin Smith if (val < 0)
70324ae3961SKevin Smith return val;
70424ae3961SKevin Smith }
70524ae3961SKevin Smith
70624ae3961SKevin Smith return res;
70724ae3961SKevin Smith }
70824ae3961SKevin Smith
mv88e61xx_set_cpu_port(struct phy_device * phydev)70924ae3961SKevin Smith static int mv88e61xx_set_cpu_port(struct phy_device *phydev)
71024ae3961SKevin Smith {
71124ae3961SKevin Smith int val;
71224ae3961SKevin Smith
71324ae3961SKevin Smith /* Set CPUDest */
71424ae3961SKevin Smith val = mv88e61xx_reg_read(phydev, DEVADDR_GLOBAL_1, GLOBAL1_MON_CTRL);
71524ae3961SKevin Smith if (val < 0)
71624ae3961SKevin Smith return val;
71724ae3961SKevin Smith val = bitfield_replace(val, GLOBAL1_MON_CTRL_CPUDEST_SHIFT,
71824ae3961SKevin Smith GLOBAL1_MON_CTRL_CPUDEST_WIDTH,
71924ae3961SKevin Smith CONFIG_MV88E61XX_CPU_PORT);
72024ae3961SKevin Smith val = mv88e61xx_reg_write(phydev, DEVADDR_GLOBAL_1,
72124ae3961SKevin Smith GLOBAL1_MON_CTRL, val);
72224ae3961SKevin Smith if (val < 0)
72324ae3961SKevin Smith return val;
72424ae3961SKevin Smith
72524ae3961SKevin Smith /* Allow CPU to route to any port */
72624ae3961SKevin Smith val = PORT_MASK & ~(1 << CONFIG_MV88E61XX_CPU_PORT);
72724ae3961SKevin Smith val = mv88e61xx_port_set_vlan(phydev, CONFIG_MV88E61XX_CPU_PORT, val);
72824ae3961SKevin Smith if (val < 0)
72924ae3961SKevin Smith return val;
73024ae3961SKevin Smith
73124ae3961SKevin Smith /* Enable CPU port */
73224ae3961SKevin Smith val = mv88e61xx_port_enable(phydev, CONFIG_MV88E61XX_CPU_PORT);
73324ae3961SKevin Smith if (val < 0)
73424ae3961SKevin Smith return val;
73524ae3961SKevin Smith
73624ae3961SKevin Smith val = mv88e61xx_read_port_config(phydev, CONFIG_MV88E61XX_CPU_PORT);
73724ae3961SKevin Smith if (val < 0)
73824ae3961SKevin Smith return val;
73924ae3961SKevin Smith
74024ae3961SKevin Smith /* If CPU is connected to serdes, initialize serdes */
74124ae3961SKevin Smith if (mv88e61xx_6352_family(phydev)) {
74224ae3961SKevin Smith val = mv88e61xx_get_cmode(phydev, CONFIG_MV88E61XX_CPU_PORT);
74324ae3961SKevin Smith if (val < 0)
74424ae3961SKevin Smith return val;
74524ae3961SKevin Smith if (val == PORT_REG_STATUS_CMODE_100BASE_X ||
74624ae3961SKevin Smith val == PORT_REG_STATUS_CMODE_1000BASE_X ||
74724ae3961SKevin Smith val == PORT_REG_STATUS_CMODE_SGMII) {
74824ae3961SKevin Smith val = mv88e61xx_serdes_init(phydev);
74924ae3961SKevin Smith if (val < 0)
75024ae3961SKevin Smith return val;
75124ae3961SKevin Smith }
75224ae3961SKevin Smith }
75324ae3961SKevin Smith
75424ae3961SKevin Smith return 0;
75524ae3961SKevin Smith }
75624ae3961SKevin Smith
mv88e61xx_switch_init(struct phy_device * phydev)75724ae3961SKevin Smith static int mv88e61xx_switch_init(struct phy_device *phydev)
75824ae3961SKevin Smith {
75924ae3961SKevin Smith static int init;
76024ae3961SKevin Smith int res;
76124ae3961SKevin Smith
76224ae3961SKevin Smith if (init)
76324ae3961SKevin Smith return 0;
76424ae3961SKevin Smith
76524ae3961SKevin Smith res = mv88e61xx_switch_reset(phydev);
76624ae3961SKevin Smith if (res < 0)
76724ae3961SKevin Smith return res;
76824ae3961SKevin Smith
76924ae3961SKevin Smith res = mv88e61xx_set_cpu_port(phydev);
77024ae3961SKevin Smith if (res < 0)
77124ae3961SKevin Smith return res;
77224ae3961SKevin Smith
77324ae3961SKevin Smith init = 1;
77424ae3961SKevin Smith
77524ae3961SKevin Smith return 0;
77624ae3961SKevin Smith }
77724ae3961SKevin Smith
mv88e61xx_phy_enable(struct phy_device * phydev,u8 phy)77824ae3961SKevin Smith static int mv88e61xx_phy_enable(struct phy_device *phydev, u8 phy)
77924ae3961SKevin Smith {
78024ae3961SKevin Smith int val;
78124ae3961SKevin Smith
78224ae3961SKevin Smith val = mv88e61xx_phy_read(phydev, phy, MII_BMCR);
78324ae3961SKevin Smith if (val < 0)
78424ae3961SKevin Smith return val;
78524ae3961SKevin Smith val &= ~(BMCR_PDOWN);
78624ae3961SKevin Smith val = mv88e61xx_phy_write(phydev, phy, MII_BMCR, val);
78724ae3961SKevin Smith if (val < 0)
78824ae3961SKevin Smith return val;
78924ae3961SKevin Smith
79024ae3961SKevin Smith return 0;
79124ae3961SKevin Smith }
79224ae3961SKevin Smith
mv88e61xx_phy_setup(struct phy_device * phydev,u8 phy)79324ae3961SKevin Smith static int mv88e61xx_phy_setup(struct phy_device *phydev, u8 phy)
79424ae3961SKevin Smith {
79524ae3961SKevin Smith int val;
79624ae3961SKevin Smith
79724ae3961SKevin Smith /*
79824ae3961SKevin Smith * Enable energy-detect sensing on PHY, used to determine when a PHY
79924ae3961SKevin Smith * port is physically connected
80024ae3961SKevin Smith */
80124ae3961SKevin Smith val = mv88e61xx_phy_read(phydev, phy, PHY_REG_CTRL1);
80224ae3961SKevin Smith if (val < 0)
80324ae3961SKevin Smith return val;
80424ae3961SKevin Smith val = bitfield_replace(val, PHY_REG_CTRL1_ENERGY_DET_SHIFT,
80524ae3961SKevin Smith PHY_REG_CTRL1_ENERGY_DET_WIDTH,
80624ae3961SKevin Smith PHY_REG_CTRL1_ENERGY_DET_SENSE_XMIT);
80724ae3961SKevin Smith val = mv88e61xx_phy_write(phydev, phy, PHY_REG_CTRL1, val);
80824ae3961SKevin Smith if (val < 0)
80924ae3961SKevin Smith return val;
81024ae3961SKevin Smith
81124ae3961SKevin Smith return 0;
81224ae3961SKevin Smith }
81324ae3961SKevin Smith
mv88e61xx_fixed_port_setup(struct phy_device * phydev,u8 port)814b755abecSChris Packham static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port)
815b755abecSChris Packham {
816b755abecSChris Packham int val;
817b755abecSChris Packham
818b755abecSChris Packham val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL);
819b755abecSChris Packham if (val < 0)
820b755abecSChris Packham return val;
821b755abecSChris Packham
822b755abecSChris Packham val &= ~(PORT_REG_PHYS_CTRL_SPD_MASK |
823b755abecSChris Packham PORT_REG_PHYS_CTRL_FC_VALUE);
824b755abecSChris Packham val |= PORT_REG_PHYS_CTRL_PCS_AN_EN |
825b755abecSChris Packham PORT_REG_PHYS_CTRL_PCS_AN_RST |
826b755abecSChris Packham PORT_REG_PHYS_CTRL_FC_FORCE |
827b755abecSChris Packham PORT_REG_PHYS_CTRL_DUPLEX_VALUE |
828b755abecSChris Packham PORT_REG_PHYS_CTRL_DUPLEX_FORCE |
829b755abecSChris Packham PORT_REG_PHYS_CTRL_SPD1000;
830b755abecSChris Packham
831b755abecSChris Packham return mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL,
832b755abecSChris Packham val);
833b755abecSChris Packham }
834b755abecSChris Packham
mv88e61xx_phy_config_port(struct phy_device * phydev,u8 phy)83524ae3961SKevin Smith static int mv88e61xx_phy_config_port(struct phy_device *phydev, u8 phy)
83624ae3961SKevin Smith {
83724ae3961SKevin Smith int val;
83824ae3961SKevin Smith
83924ae3961SKevin Smith val = mv88e61xx_port_enable(phydev, phy);
84024ae3961SKevin Smith if (val < 0)
84124ae3961SKevin Smith return val;
84224ae3961SKevin Smith
84324ae3961SKevin Smith val = mv88e61xx_port_set_vlan(phydev, phy,
84424ae3961SKevin Smith 1 << CONFIG_MV88E61XX_CPU_PORT);
84524ae3961SKevin Smith if (val < 0)
84624ae3961SKevin Smith return val;
84724ae3961SKevin Smith
84824ae3961SKevin Smith return 0;
84924ae3961SKevin Smith }
85024ae3961SKevin Smith
mv88e61xx_probe(struct phy_device * phydev)85124ae3961SKevin Smith static int mv88e61xx_probe(struct phy_device *phydev)
85224ae3961SKevin Smith {
85324ae3961SKevin Smith struct mii_dev *smi_wrapper;
85424ae3961SKevin Smith struct mv88e61xx_phy_priv *priv;
85524ae3961SKevin Smith int res;
85624ae3961SKevin Smith
85724ae3961SKevin Smith res = mv88e61xx_hw_reset(phydev);
85824ae3961SKevin Smith if (res < 0)
85924ae3961SKevin Smith return res;
86024ae3961SKevin Smith
86124ae3961SKevin Smith priv = malloc(sizeof(*priv));
86224ae3961SKevin Smith if (!priv)
86324ae3961SKevin Smith return -ENOMEM;
86424ae3961SKevin Smith
86524ae3961SKevin Smith memset(priv, 0, sizeof(*priv));
86624ae3961SKevin Smith
86724ae3961SKevin Smith /*
86824ae3961SKevin Smith * This device requires indirect reads/writes to the PHY registers
86924ae3961SKevin Smith * which the generic PHY code can't handle. Make a wrapper MII device
87024ae3961SKevin Smith * to handle reads/writes
87124ae3961SKevin Smith */
87224ae3961SKevin Smith smi_wrapper = mdio_alloc();
87324ae3961SKevin Smith if (!smi_wrapper) {
87424ae3961SKevin Smith free(priv);
87524ae3961SKevin Smith return -ENOMEM;
87624ae3961SKevin Smith }
87724ae3961SKevin Smith
87824ae3961SKevin Smith /*
87924ae3961SKevin Smith * Store the mdio bus in the private data, as we are going to replace
88024ae3961SKevin Smith * the bus with the wrapper bus
88124ae3961SKevin Smith */
88224ae3961SKevin Smith priv->mdio_bus = phydev->bus;
88324ae3961SKevin Smith
88424ae3961SKevin Smith /*
88524ae3961SKevin Smith * Store the smi bus address in private data. This lets us use the
88624ae3961SKevin Smith * phydev addr field for device address instead, as the genphy code
88724ae3961SKevin Smith * expects.
88824ae3961SKevin Smith */
88924ae3961SKevin Smith priv->smi_addr = phydev->addr;
89024ae3961SKevin Smith
89124ae3961SKevin Smith /*
89224ae3961SKevin Smith * Store the phy_device in the wrapper mii device. This lets us get it
89324ae3961SKevin Smith * back when genphy functions call phy_read/phy_write.
89424ae3961SKevin Smith */
89524ae3961SKevin Smith smi_wrapper->priv = phydev;
89624ae3961SKevin Smith strncpy(smi_wrapper->name, "indirect mii", sizeof(smi_wrapper->name));
89724ae3961SKevin Smith smi_wrapper->read = mv88e61xx_phy_read_indirect;
89824ae3961SKevin Smith smi_wrapper->write = mv88e61xx_phy_write_indirect;
89924ae3961SKevin Smith
90024ae3961SKevin Smith /* Replace the bus with the wrapper device */
90124ae3961SKevin Smith phydev->bus = smi_wrapper;
90224ae3961SKevin Smith
90324ae3961SKevin Smith phydev->priv = priv;
90424ae3961SKevin Smith
90524ae3961SKevin Smith priv->id = mv88e61xx_get_switch_id(phydev);
90624ae3961SKevin Smith
90724ae3961SKevin Smith return 0;
90824ae3961SKevin Smith }
90924ae3961SKevin Smith
mv88e61xx_phy_config(struct phy_device * phydev)91024ae3961SKevin Smith static int mv88e61xx_phy_config(struct phy_device *phydev)
91124ae3961SKevin Smith {
91224ae3961SKevin Smith int res;
91324ae3961SKevin Smith int i;
91424ae3961SKevin Smith int ret = -1;
91524ae3961SKevin Smith
91624ae3961SKevin Smith res = mv88e61xx_switch_init(phydev);
91724ae3961SKevin Smith if (res < 0)
91824ae3961SKevin Smith return res;
91924ae3961SKevin Smith
92024ae3961SKevin Smith for (i = 0; i < PORT_COUNT; i++) {
92124ae3961SKevin Smith if ((1 << i) & CONFIG_MV88E61XX_PHY_PORTS) {
92224ae3961SKevin Smith phydev->addr = i;
92324ae3961SKevin Smith
92424ae3961SKevin Smith res = mv88e61xx_phy_enable(phydev, i);
92524ae3961SKevin Smith if (res < 0) {
92624ae3961SKevin Smith printf("Error enabling PHY %i\n", i);
92724ae3961SKevin Smith continue;
92824ae3961SKevin Smith }
92924ae3961SKevin Smith res = mv88e61xx_phy_setup(phydev, i);
93024ae3961SKevin Smith if (res < 0) {
93124ae3961SKevin Smith printf("Error setting up PHY %i\n", i);
93224ae3961SKevin Smith continue;
93324ae3961SKevin Smith }
93424ae3961SKevin Smith res = mv88e61xx_phy_config_port(phydev, i);
93524ae3961SKevin Smith if (res < 0) {
93624ae3961SKevin Smith printf("Error configuring PHY %i\n", i);
93724ae3961SKevin Smith continue;
93824ae3961SKevin Smith }
93924ae3961SKevin Smith
94024ae3961SKevin Smith res = genphy_config_aneg(phydev);
94124ae3961SKevin Smith if (res < 0) {
94224ae3961SKevin Smith printf("Error setting PHY %i autoneg\n", i);
94324ae3961SKevin Smith continue;
94424ae3961SKevin Smith }
94524ae3961SKevin Smith res = phy_reset(phydev);
94624ae3961SKevin Smith if (res < 0) {
94724ae3961SKevin Smith printf("Error resetting PHY %i\n", i);
94824ae3961SKevin Smith continue;
94924ae3961SKevin Smith }
95024ae3961SKevin Smith
95124ae3961SKevin Smith /* Return success if any PHY succeeds */
95224ae3961SKevin Smith ret = 0;
953b755abecSChris Packham } else if ((1 << i) & CONFIG_MV88E61XX_FIXED_PORTS) {
954b755abecSChris Packham res = mv88e61xx_fixed_port_setup(phydev, i);
955b755abecSChris Packham if (res < 0) {
956b755abecSChris Packham printf("Error configuring port %i\n", i);
957b755abecSChris Packham continue;
958b755abecSChris Packham }
95924ae3961SKevin Smith }
96024ae3961SKevin Smith }
96124ae3961SKevin Smith
96224ae3961SKevin Smith return ret;
96324ae3961SKevin Smith }
96424ae3961SKevin Smith
mv88e61xx_phy_is_connected(struct phy_device * phydev)96524ae3961SKevin Smith static int mv88e61xx_phy_is_connected(struct phy_device *phydev)
96624ae3961SKevin Smith {
96724ae3961SKevin Smith int val;
96824ae3961SKevin Smith
96924ae3961SKevin Smith val = mv88e61xx_phy_read(phydev, phydev->addr, PHY_REG_STATUS1);
97024ae3961SKevin Smith if (val < 0)
97124ae3961SKevin Smith return 0;
97224ae3961SKevin Smith
97324ae3961SKevin Smith /*
97424ae3961SKevin Smith * After reset, the energy detect signal remains high for a few seconds
97524ae3961SKevin Smith * regardless of whether a cable is connected. This function will
97624ae3961SKevin Smith * return false positives during this time.
97724ae3961SKevin Smith */
97824ae3961SKevin Smith return (val & PHY_REG_STATUS1_ENERGY) == 0;
97924ae3961SKevin Smith }
98024ae3961SKevin Smith
mv88e61xx_phy_startup(struct phy_device * phydev)98124ae3961SKevin Smith static int mv88e61xx_phy_startup(struct phy_device *phydev)
98224ae3961SKevin Smith {
98324ae3961SKevin Smith int i;
98424ae3961SKevin Smith int link = 0;
98524ae3961SKevin Smith int res;
98624ae3961SKevin Smith int speed = phydev->speed;
98724ae3961SKevin Smith int duplex = phydev->duplex;
98824ae3961SKevin Smith
98924ae3961SKevin Smith for (i = 0; i < PORT_COUNT; i++) {
99024ae3961SKevin Smith if ((1 << i) & CONFIG_MV88E61XX_PHY_PORTS) {
99124ae3961SKevin Smith phydev->addr = i;
99224ae3961SKevin Smith if (!mv88e61xx_phy_is_connected(phydev))
99324ae3961SKevin Smith continue;
99424ae3961SKevin Smith res = genphy_update_link(phydev);
99524ae3961SKevin Smith if (res < 0)
99624ae3961SKevin Smith continue;
99724ae3961SKevin Smith res = mv88e61xx_parse_status(phydev);
99824ae3961SKevin Smith if (res < 0)
99924ae3961SKevin Smith continue;
100024ae3961SKevin Smith link = (link || phydev->link);
100124ae3961SKevin Smith }
100224ae3961SKevin Smith }
100324ae3961SKevin Smith phydev->link = link;
100424ae3961SKevin Smith
100524ae3961SKevin Smith /* Restore CPU interface speed and duplex after it was changed for
100624ae3961SKevin Smith * other ports */
100724ae3961SKevin Smith phydev->speed = speed;
100824ae3961SKevin Smith phydev->duplex = duplex;
100924ae3961SKevin Smith
101024ae3961SKevin Smith return 0;
101124ae3961SKevin Smith }
101224ae3961SKevin Smith
101324ae3961SKevin Smith static struct phy_driver mv88e61xx_driver = {
101424ae3961SKevin Smith .name = "Marvell MV88E61xx",
101524ae3961SKevin Smith .uid = 0x01410eb1,
101624ae3961SKevin Smith .mask = 0xfffffff0,
101724ae3961SKevin Smith .features = PHY_GBIT_FEATURES,
101824ae3961SKevin Smith .probe = mv88e61xx_probe,
101924ae3961SKevin Smith .config = mv88e61xx_phy_config,
102024ae3961SKevin Smith .startup = mv88e61xx_phy_startup,
102124ae3961SKevin Smith .shutdown = &genphy_shutdown,
102224ae3961SKevin Smith };
102324ae3961SKevin Smith
102465d4d00aSChris Packham static struct phy_driver mv88e609x_driver = {
102565d4d00aSChris Packham .name = "Marvell MV88E609x",
102665d4d00aSChris Packham .uid = 0x1410c89,
102765d4d00aSChris Packham .mask = 0xfffffff0,
102865d4d00aSChris Packham .features = PHY_GBIT_FEATURES,
102965d4d00aSChris Packham .probe = mv88e61xx_probe,
103065d4d00aSChris Packham .config = mv88e61xx_phy_config,
103165d4d00aSChris Packham .startup = mv88e61xx_phy_startup,
103265d4d00aSChris Packham .shutdown = &genphy_shutdown,
103365d4d00aSChris Packham };
103465d4d00aSChris Packham
phy_mv88e61xx_init(void)103524ae3961SKevin Smith int phy_mv88e61xx_init(void)
103624ae3961SKevin Smith {
103724ae3961SKevin Smith phy_register(&mv88e61xx_driver);
103865d4d00aSChris Packham phy_register(&mv88e609x_driver);
103924ae3961SKevin Smith
104024ae3961SKevin Smith return 0;
104124ae3961SKevin Smith }
104224ae3961SKevin Smith
104324ae3961SKevin Smith /*
104424ae3961SKevin Smith * Overload weak get_phy_id definition since we need non-standard functions
104524ae3961SKevin Smith * to read PHY registers
104624ae3961SKevin Smith */
get_phy_id(struct mii_dev * bus,int smi_addr,int devad,u32 * phy_id)104724ae3961SKevin Smith int get_phy_id(struct mii_dev *bus, int smi_addr, int devad, u32 *phy_id)
104824ae3961SKevin Smith {
104924ae3961SKevin Smith struct phy_device temp_phy;
105024ae3961SKevin Smith struct mv88e61xx_phy_priv temp_priv;
105124ae3961SKevin Smith struct mii_dev temp_mii;
105224ae3961SKevin Smith int val;
105324ae3961SKevin Smith
105424ae3961SKevin Smith /*
105524ae3961SKevin Smith * Buid temporary data structures that the chip reading code needs to
105624ae3961SKevin Smith * read the ID
105724ae3961SKevin Smith */
105824ae3961SKevin Smith temp_priv.mdio_bus = bus;
105924ae3961SKevin Smith temp_priv.smi_addr = smi_addr;
106024ae3961SKevin Smith temp_phy.priv = &temp_priv;
106124ae3961SKevin Smith temp_mii.priv = &temp_phy;
106224ae3961SKevin Smith
106324ae3961SKevin Smith val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID1);
106424ae3961SKevin Smith if (val < 0)
106524ae3961SKevin Smith return -EIO;
106624ae3961SKevin Smith
106724ae3961SKevin Smith *phy_id = val << 16;
106824ae3961SKevin Smith
106924ae3961SKevin Smith val = mv88e61xx_phy_read_indirect(&temp_mii, 0, devad, MII_PHYSID2);
107024ae3961SKevin Smith if (val < 0)
107124ae3961SKevin Smith return -EIO;
107224ae3961SKevin Smith
107324ae3961SKevin Smith *phy_id |= (val & 0xffff);
107424ae3961SKevin Smith
107524ae3961SKevin Smith return 0;
107624ae3961SKevin Smith }
1077