162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
462306a36Sopenharmony_ci * Copyright (C) 2016 Hauke Mehrtens <hauke@hauke-m.de>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/mdio.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/phy.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/bitfield.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define XWAY_MDIO_MIICTRL		0x17	/* mii control */
1462306a36Sopenharmony_ci#define XWAY_MDIO_IMASK			0x19	/* interrupt mask */
1562306a36Sopenharmony_ci#define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */
1662306a36Sopenharmony_ci#define XWAY_MDIO_LED			0x1B	/* led control */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define XWAY_MDIO_MIICTRL_RXSKEW_MASK	GENMASK(14, 12)
1962306a36Sopenharmony_ci#define XWAY_MDIO_MIICTRL_TXSKEW_MASK	GENMASK(10, 8)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* bit 15:12 are reserved */
2262306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED3_EN		BIT(11)	/* Enable the integrated function of LED3 */
2362306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED2_EN		BIT(10)	/* Enable the integrated function of LED2 */
2462306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED1_EN		BIT(9)	/* Enable the integrated function of LED1 */
2562306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED0_EN		BIT(8)	/* Enable the integrated function of LED0 */
2662306a36Sopenharmony_ci/* bit 7:4 are reserved */
2762306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED3_DA		BIT(3)	/* Direct Access to LED3 */
2862306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED2_DA		BIT(2)	/* Direct Access to LED2 */
2962306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED1_DA		BIT(1)	/* Direct Access to LED1 */
3062306a36Sopenharmony_ci#define XWAY_MDIO_LED_LED0_DA		BIT(0)	/* Direct Access to LED0 */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define XWAY_MDIO_INIT_WOL		BIT(15)	/* Wake-On-LAN */
3362306a36Sopenharmony_ci#define XWAY_MDIO_INIT_MSRE		BIT(14)
3462306a36Sopenharmony_ci#define XWAY_MDIO_INIT_NPRX		BIT(13)
3562306a36Sopenharmony_ci#define XWAY_MDIO_INIT_NPTX		BIT(12)
3662306a36Sopenharmony_ci#define XWAY_MDIO_INIT_ANE		BIT(11)	/* Auto-Neg error */
3762306a36Sopenharmony_ci#define XWAY_MDIO_INIT_ANC		BIT(10)	/* Auto-Neg complete */
3862306a36Sopenharmony_ci#define XWAY_MDIO_INIT_ADSC		BIT(5)	/* Link auto-downspeed detect */
3962306a36Sopenharmony_ci#define XWAY_MDIO_INIT_MPIPC		BIT(4)
4062306a36Sopenharmony_ci#define XWAY_MDIO_INIT_MDIXC		BIT(3)
4162306a36Sopenharmony_ci#define XWAY_MDIO_INIT_DXMC		BIT(2)	/* Duplex mode change */
4262306a36Sopenharmony_ci#define XWAY_MDIO_INIT_LSPC		BIT(1)	/* Link speed change */
4362306a36Sopenharmony_ci#define XWAY_MDIO_INIT_LSTC		BIT(0)	/* Link state change */
4462306a36Sopenharmony_ci#define XWAY_MDIO_INIT_MASK		(XWAY_MDIO_INIT_LSTC | \
4562306a36Sopenharmony_ci					 XWAY_MDIO_INIT_ADSC)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define ADVERTISED_MPD			BIT(10)	/* Multi-port device */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* LED Configuration */
5062306a36Sopenharmony_ci#define XWAY_MMD_LEDCH			0x01E0
5162306a36Sopenharmony_ci/* Inverse of SCAN Function */
5262306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_NONE	0x0000
5362306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_LINK	0x0001
5462306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_PDOWN	0x0002
5562306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_EEE	0x0003
5662306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_ANEG	0x0004
5762306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_ABIST	0x0005
5862306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_CDIAG	0x0006
5962306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_NACS_TEST	0x0007
6062306a36Sopenharmony_ci/* Slow Blink Frequency */
6162306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SBF_F02HZ	0x0000
6262306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SBF_F04HZ	0x0010
6362306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SBF_F08HZ	0x0020
6462306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SBF_F16HZ	0x0030
6562306a36Sopenharmony_ci/* Fast Blink Frequency */
6662306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_FBF_F02HZ	0x0000
6762306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_FBF_F04HZ	0x0040
6862306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_FBF_F08HZ	0x0080
6962306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_FBF_F16HZ	0x00C0
7062306a36Sopenharmony_ci/* LED Configuration */
7162306a36Sopenharmony_ci#define XWAY_MMD_LEDCL			0x01E1
7262306a36Sopenharmony_ci/* Complex Blinking Configuration */
7362306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_NONE	0x0000
7462306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_LINK	0x0001
7562306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_PDOWN	0x0002
7662306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_EEE	0x0003
7762306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_ANEG	0x0004
7862306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_ABIST	0x0005
7962306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_CDIAG	0x0006
8062306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_CBLINK_TEST	0x0007
8162306a36Sopenharmony_ci/* Complex SCAN Configuration */
8262306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_NONE	0x0000
8362306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_LINK	0x0010
8462306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_PDOWN	0x0020
8562306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_EEE	0x0030
8662306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_ANEG	0x0040
8762306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_ABIST	0x0050
8862306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_CDIAG	0x0060
8962306a36Sopenharmony_ci#define  XWAY_MMD_LEDCH_SCAN_TEST	0x0070
9062306a36Sopenharmony_ci/* Configuration for LED Pin x */
9162306a36Sopenharmony_ci#define XWAY_MMD_LED0H			0x01E2
9262306a36Sopenharmony_ci/* Fast Blinking Configuration */
9362306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_MASK	0x000F
9462306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_NONE	0x0000
9562306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK10	0x0001
9662306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK100	0x0002
9762306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK10X	0x0003
9862306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK1000	0x0004
9962306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK10_0	0x0005
10062306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK100X	0x0006
10162306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_LINK10XX	0x0007
10262306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_PDOWN	0x0008
10362306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_EEE	0x0009
10462306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_ANEG	0x000A
10562306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_ABIST	0x000B
10662306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_BLINKF_CDIAG	0x000C
10762306a36Sopenharmony_ci/* Constant On Configuration */
10862306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_MASK	0x00F0
10962306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_NONE	0x0000
11062306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK10	0x0010
11162306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK100	0x0020
11262306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK10X	0x0030
11362306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK1000	0x0040
11462306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK10_0	0x0050
11562306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK100X	0x0060
11662306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_LINK10XX	0x0070
11762306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_PDOWN	0x0080
11862306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_EEE		0x0090
11962306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_ANEG	0x00A0
12062306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_ABIST	0x00B0
12162306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_CDIAG	0x00C0
12262306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_COPPER	0x00D0
12362306a36Sopenharmony_ci#define  XWAY_MMD_LEDxH_CON_FIBER	0x00E0
12462306a36Sopenharmony_ci/* Configuration for LED Pin x */
12562306a36Sopenharmony_ci#define XWAY_MMD_LED0L			0x01E3
12662306a36Sopenharmony_ci/* Pulsing Configuration */
12762306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_PULSE_MASK	0x000F
12862306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_PULSE_NONE	0x0000
12962306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_PULSE_TXACT	0x0001
13062306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_PULSE_RXACT	0x0002
13162306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_PULSE_COL	0x0004
13262306a36Sopenharmony_ci/* Slow Blinking Configuration */
13362306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_MASK	0x00F0
13462306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_NONE	0x0000
13562306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK10	0x0010
13662306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK100	0x0020
13762306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK10X	0x0030
13862306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK1000	0x0040
13962306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK10_0	0x0050
14062306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK100X	0x0060
14162306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_LINK10XX	0x0070
14262306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_PDOWN	0x0080
14362306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_EEE	0x0090
14462306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_ANEG	0x00A0
14562306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_ABIST	0x00B0
14662306a36Sopenharmony_ci#define  XWAY_MMD_LEDxL_BLINKS_CDIAG	0x00C0
14762306a36Sopenharmony_ci#define XWAY_MMD_LED1H			0x01E4
14862306a36Sopenharmony_ci#define XWAY_MMD_LED1L			0x01E5
14962306a36Sopenharmony_ci#define XWAY_MMD_LED2H			0x01E6
15062306a36Sopenharmony_ci#define XWAY_MMD_LED2L			0x01E7
15162306a36Sopenharmony_ci#define XWAY_MMD_LED3H			0x01E8
15262306a36Sopenharmony_ci#define XWAY_MMD_LED3L			0x01E9
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define PHY_ID_PHY11G_1_3		0x030260D1
15562306a36Sopenharmony_ci#define PHY_ID_PHY22F_1_3		0x030260E1
15662306a36Sopenharmony_ci#define PHY_ID_PHY11G_1_4		0xD565A400
15762306a36Sopenharmony_ci#define PHY_ID_PHY22F_1_4		0xD565A410
15862306a36Sopenharmony_ci#define PHY_ID_PHY11G_1_5		0xD565A401
15962306a36Sopenharmony_ci#define PHY_ID_PHY22F_1_5		0xD565A411
16062306a36Sopenharmony_ci#define PHY_ID_PHY11G_VR9_1_1		0xD565A408
16162306a36Sopenharmony_ci#define PHY_ID_PHY22F_VR9_1_1		0xD565A418
16262306a36Sopenharmony_ci#define PHY_ID_PHY11G_VR9_1_2		0xD565A409
16362306a36Sopenharmony_ci#define PHY_ID_PHY22F_VR9_1_2		0xD565A419
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500,
16662306a36Sopenharmony_ci					 3000, 3500};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int xway_gphy_rgmii_init(struct phy_device *phydev)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
17162306a36Sopenharmony_ci	unsigned int delay_size = ARRAY_SIZE(xway_internal_delay);
17262306a36Sopenharmony_ci	s32 int_delay;
17362306a36Sopenharmony_ci	int val = 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!phy_interface_is_rgmii(phydev))
17662306a36Sopenharmony_ci		return 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Existing behavior was to use default pin strapping delay in rgmii
17962306a36Sopenharmony_ci	 * mode, but rgmii should have meant no delay.  Warn existing users,
18062306a36Sopenharmony_ci	 * but do not change anything at the moment.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
18362306a36Sopenharmony_ci		u16 txskew, rxskew;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		val = phy_read(phydev, XWAY_MDIO_MIICTRL);
18662306a36Sopenharmony_ci		if (val < 0)
18762306a36Sopenharmony_ci			return val;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		txskew = FIELD_GET(XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
19062306a36Sopenharmony_ci		rxskew = FIELD_GET(XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		if (txskew > 0 || rxskew > 0)
19362306a36Sopenharmony_ci			phydev_warn(phydev,
19462306a36Sopenharmony_ci				    "PHY has delays (e.g. via pin strapping), but phy-mode = 'rgmii'\n"
19562306a36Sopenharmony_ci				    "Should be 'rgmii-id' to use internal delays txskew:%d ps rxskew:%d ps\n",
19662306a36Sopenharmony_ci				    xway_internal_delay[txskew],
19762306a36Sopenharmony_ci				    xway_internal_delay[rxskew]);
19862306a36Sopenharmony_ci		return 0;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
20262306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
20362306a36Sopenharmony_ci		int_delay = phy_get_internal_delay(phydev, dev,
20462306a36Sopenharmony_ci						   xway_internal_delay,
20562306a36Sopenharmony_ci						   delay_size, true);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		/* if rx-internal-delay-ps is missing, use default of 2.0 ns */
20862306a36Sopenharmony_ci		if (int_delay < 0)
20962306a36Sopenharmony_ci			int_delay = 4; /* 2000 ps */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, int_delay);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
21562306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
21662306a36Sopenharmony_ci		int_delay = phy_get_internal_delay(phydev, dev,
21762306a36Sopenharmony_ci						   xway_internal_delay,
21862306a36Sopenharmony_ci						   delay_size, false);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		/* if tx-internal-delay-ps is missing, use default of 2.0 ns */
22162306a36Sopenharmony_ci		if (int_delay < 0)
22262306a36Sopenharmony_ci			int_delay = 4; /* 2000 ps */
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, int_delay);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return phy_modify(phydev, XWAY_MDIO_MIICTRL,
22862306a36Sopenharmony_ci			  XWAY_MDIO_MIICTRL_RXSKEW_MASK |
22962306a36Sopenharmony_ci			  XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int xway_gphy_config_init(struct phy_device *phydev)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	int err;
23562306a36Sopenharmony_ci	u32 ledxh;
23662306a36Sopenharmony_ci	u32 ledxl;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Mask all interrupts */
23962306a36Sopenharmony_ci	err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
24062306a36Sopenharmony_ci	if (err)
24162306a36Sopenharmony_ci		return err;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* Clear all pending interrupts */
24462306a36Sopenharmony_ci	phy_read(phydev, XWAY_MDIO_ISTAT);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Ensure that integrated led function is enabled for all leds */
24762306a36Sopenharmony_ci	err = phy_write(phydev, XWAY_MDIO_LED,
24862306a36Sopenharmony_ci			XWAY_MDIO_LED_LED0_EN |
24962306a36Sopenharmony_ci			XWAY_MDIO_LED_LED1_EN |
25062306a36Sopenharmony_ci			XWAY_MDIO_LED_LED2_EN |
25162306a36Sopenharmony_ci			XWAY_MDIO_LED_LED3_EN);
25262306a36Sopenharmony_ci	if (err)
25362306a36Sopenharmony_ci		return err;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH,
25662306a36Sopenharmony_ci		      XWAY_MMD_LEDCH_NACS_NONE |
25762306a36Sopenharmony_ci		      XWAY_MMD_LEDCH_SBF_F02HZ |
25862306a36Sopenharmony_ci		      XWAY_MMD_LEDCH_FBF_F16HZ);
25962306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL,
26062306a36Sopenharmony_ci		      XWAY_MMD_LEDCH_CBLINK_NONE |
26162306a36Sopenharmony_ci		      XWAY_MMD_LEDCH_SCAN_NONE);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/**
26462306a36Sopenharmony_ci	 * In most cases only one LED is connected to this phy, so
26562306a36Sopenharmony_ci	 * configure them all to constant on and pulse mode. LED3 is
26662306a36Sopenharmony_ci	 * only available in some packages, leave it in its reset
26762306a36Sopenharmony_ci	 * configuration.
26862306a36Sopenharmony_ci	 */
26962306a36Sopenharmony_ci	ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX;
27062306a36Sopenharmony_ci	ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT |
27162306a36Sopenharmony_ci		XWAY_MMD_LEDxL_BLINKS_NONE;
27262306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh);
27362306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl);
27462306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh);
27562306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl);
27662306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
27762306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	err = xway_gphy_rgmii_init(phydev);
28062306a36Sopenharmony_ci	if (err)
28162306a36Sopenharmony_ci		return err;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int xway_gphy14_config_aneg(struct phy_device *phydev)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	int reg, err;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Advertise as multi-port device, see IEEE802.3-2002 40.5.1.1 */
29162306a36Sopenharmony_ci	/* This is a workaround for an errata in rev < 1.5 devices */
29262306a36Sopenharmony_ci	reg = phy_read(phydev, MII_CTRL1000);
29362306a36Sopenharmony_ci	reg |= ADVERTISED_MPD;
29462306a36Sopenharmony_ci	err = phy_write(phydev, MII_CTRL1000, reg);
29562306a36Sopenharmony_ci	if (err)
29662306a36Sopenharmony_ci		return err;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return genphy_config_aneg(phydev);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int xway_gphy_ack_interrupt(struct phy_device *phydev)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	int reg;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	reg = phy_read(phydev, XWAY_MDIO_ISTAT);
30662306a36Sopenharmony_ci	return (reg < 0) ? reg : 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int xway_gphy_config_intr(struct phy_device *phydev)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	u16 mask = 0;
31262306a36Sopenharmony_ci	int err;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
31562306a36Sopenharmony_ci		err = xway_gphy_ack_interrupt(phydev);
31662306a36Sopenharmony_ci		if (err)
31762306a36Sopenharmony_ci			return err;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		mask = XWAY_MDIO_INIT_MASK;
32062306a36Sopenharmony_ci		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
32162306a36Sopenharmony_ci	} else {
32262306a36Sopenharmony_ci		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
32362306a36Sopenharmony_ci		if (err)
32462306a36Sopenharmony_ci			return err;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		err = xway_gphy_ack_interrupt(phydev);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return err;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic irqreturn_t xway_gphy_handle_interrupt(struct phy_device *phydev)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	int irq_status;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	irq_status = phy_read(phydev, XWAY_MDIO_ISTAT);
33762306a36Sopenharmony_ci	if (irq_status < 0) {
33862306a36Sopenharmony_ci		phy_error(phydev);
33962306a36Sopenharmony_ci		return IRQ_NONE;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (!(irq_status & XWAY_MDIO_INIT_MASK))
34362306a36Sopenharmony_ci		return IRQ_NONE;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	phy_trigger_machine(phydev);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return IRQ_HANDLED;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic struct phy_driver xway_gphy[] = {
35162306a36Sopenharmony_ci	{
35262306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY11G_1_3,
35362306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
35462306a36Sopenharmony_ci		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
35562306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
35662306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
35762306a36Sopenharmony_ci		.config_aneg	= xway_gphy14_config_aneg,
35862306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
35962306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
36062306a36Sopenharmony_ci		.suspend	= genphy_suspend,
36162306a36Sopenharmony_ci		.resume		= genphy_resume,
36262306a36Sopenharmony_ci	}, {
36362306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY22F_1_3,
36462306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
36562306a36Sopenharmony_ci		.name		= "Intel XWAY PHY22F (PEF 7061) v1.3",
36662306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */
36762306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
36862306a36Sopenharmony_ci		.config_aneg	= xway_gphy14_config_aneg,
36962306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
37062306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
37162306a36Sopenharmony_ci		.suspend	= genphy_suspend,
37262306a36Sopenharmony_ci		.resume		= genphy_resume,
37362306a36Sopenharmony_ci	}, {
37462306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY11G_1_4,
37562306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
37662306a36Sopenharmony_ci		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
37762306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
37862306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
37962306a36Sopenharmony_ci		.config_aneg	= xway_gphy14_config_aneg,
38062306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
38162306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
38262306a36Sopenharmony_ci		.suspend	= genphy_suspend,
38362306a36Sopenharmony_ci		.resume		= genphy_resume,
38462306a36Sopenharmony_ci	}, {
38562306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY22F_1_4,
38662306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
38762306a36Sopenharmony_ci		.name		= "Intel XWAY PHY22F (PEF 7061) v1.4",
38862306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */
38962306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
39062306a36Sopenharmony_ci		.config_aneg	= xway_gphy14_config_aneg,
39162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
39262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
39362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
39462306a36Sopenharmony_ci		.resume		= genphy_resume,
39562306a36Sopenharmony_ci	}, {
39662306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY11G_1_5,
39762306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
39862306a36Sopenharmony_ci		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
39962306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
40062306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
40162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
40262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
40362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
40462306a36Sopenharmony_ci		.resume		= genphy_resume,
40562306a36Sopenharmony_ci	}, {
40662306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY22F_1_5,
40762306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
40862306a36Sopenharmony_ci		.name		= "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
40962306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */
41062306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
41162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
41262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
41362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
41462306a36Sopenharmony_ci		.resume		= genphy_resume,
41562306a36Sopenharmony_ci	}, {
41662306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY11G_VR9_1_1,
41762306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
41862306a36Sopenharmony_ci		.name		= "Intel XWAY PHY11G (xRX v1.1 integrated)",
41962306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
42062306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
42162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
42262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
42362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
42462306a36Sopenharmony_ci		.resume		= genphy_resume,
42562306a36Sopenharmony_ci	}, {
42662306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY22F_VR9_1_1,
42762306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
42862306a36Sopenharmony_ci		.name		= "Intel XWAY PHY22F (xRX v1.1 integrated)",
42962306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */
43062306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
43162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
43262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
43362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
43462306a36Sopenharmony_ci		.resume		= genphy_resume,
43562306a36Sopenharmony_ci	}, {
43662306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY11G_VR9_1_2,
43762306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
43862306a36Sopenharmony_ci		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
43962306a36Sopenharmony_ci		/* PHY_GBIT_FEATURES */
44062306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
44162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
44262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
44362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
44462306a36Sopenharmony_ci		.resume		= genphy_resume,
44562306a36Sopenharmony_ci	}, {
44662306a36Sopenharmony_ci		.phy_id		= PHY_ID_PHY22F_VR9_1_2,
44762306a36Sopenharmony_ci		.phy_id_mask	= 0xffffffff,
44862306a36Sopenharmony_ci		.name		= "Intel XWAY PHY22F (xRX v1.2 integrated)",
44962306a36Sopenharmony_ci		/* PHY_BASIC_FEATURES */
45062306a36Sopenharmony_ci		.config_init	= xway_gphy_config_init,
45162306a36Sopenharmony_ci		.handle_interrupt = xway_gphy_handle_interrupt,
45262306a36Sopenharmony_ci		.config_intr	= xway_gphy_config_intr,
45362306a36Sopenharmony_ci		.suspend	= genphy_suspend,
45462306a36Sopenharmony_ci		.resume		= genphy_resume,
45562306a36Sopenharmony_ci	},
45662306a36Sopenharmony_ci};
45762306a36Sopenharmony_cimodule_phy_driver(xway_gphy);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
46062306a36Sopenharmony_ci	{ PHY_ID_PHY11G_1_3, 0xffffffff },
46162306a36Sopenharmony_ci	{ PHY_ID_PHY22F_1_3, 0xffffffff },
46262306a36Sopenharmony_ci	{ PHY_ID_PHY11G_1_4, 0xffffffff },
46362306a36Sopenharmony_ci	{ PHY_ID_PHY22F_1_4, 0xffffffff },
46462306a36Sopenharmony_ci	{ PHY_ID_PHY11G_1_5, 0xffffffff },
46562306a36Sopenharmony_ci	{ PHY_ID_PHY22F_1_5, 0xffffffff },
46662306a36Sopenharmony_ci	{ PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
46762306a36Sopenharmony_ci	{ PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
46862306a36Sopenharmony_ci	{ PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
46962306a36Sopenharmony_ci	{ PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
47062306a36Sopenharmony_ci	{ }
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel XWAY PHY driver");
47562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
476