xref: /openbmc/linux/drivers/net/phy/micrel.c (revision f873f112553b520e86be3992a38b19e3747b31af)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2d0507009SDavid J. Choi /*
3d0507009SDavid J. Choi  * drivers/net/phy/micrel.c
4d0507009SDavid J. Choi  *
5d0507009SDavid J. Choi  * Driver for Micrel PHYs
6d0507009SDavid J. Choi  *
7d0507009SDavid J. Choi  * Author: David J. Choi
8d0507009SDavid J. Choi  *
97ab59dc1SDavid J. Choi  * Copyright (c) 2010-2013 Micrel, Inc.
10ee0dc2fbSJohan Hovold  * Copyright (c) 2014 Johan Hovold <johan@kernel.org>
11d0507009SDavid J. Choi  *
127ab59dc1SDavid J. Choi  * Support : Micrel Phys:
13bff5b4b3SYuiko Oshino  *		Giga phys: ksz9021, ksz9031, ksz9131
147ab59dc1SDavid J. Choi  *		100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041
157ab59dc1SDavid J. Choi  *			   ksz8021, ksz8031, ksz8051,
167ab59dc1SDavid J. Choi  *			   ksz8081, ksz8091,
177ab59dc1SDavid J. Choi  *			   ksz8061,
187ab59dc1SDavid J. Choi  *		Switch : ksz8873, ksz886x
19fc3973a1SWoojung Huh  *			 ksz9477
20d0507009SDavid J. Choi  */
21d0507009SDavid J. Choi 
22bcf3440cSOleksij Rempel #include <linux/bitfield.h>
23d0507009SDavid J. Choi #include <linux/kernel.h>
24d0507009SDavid J. Choi #include <linux/module.h>
25d0507009SDavid J. Choi #include <linux/phy.h>
26d606ef3fSBaruch Siach #include <linux/micrel_phy.h>
27954c3967SSean Cross #include <linux/of.h>
281fadee0cSSascha Hauer #include <linux/clk.h>
296110dff7SOleksij Rempel #include <linux/delay.h>
30d0507009SDavid J. Choi 
31212ea99aSMarek Vasut /* Operation Mode Strap Override */
32212ea99aSMarek Vasut #define MII_KSZPHY_OMSO				0x16
337a1d8390SAntoine Tenart #define KSZPHY_OMSO_FACTORY_TEST		BIT(15)
3400aee095SJohan Hovold #define KSZPHY_OMSO_B_CAST_OFF			BIT(9)
352b0ba96cSSylvain Rochet #define KSZPHY_OMSO_NAND_TREE_ON		BIT(5)
3600aee095SJohan Hovold #define KSZPHY_OMSO_RMII_OVERRIDE		BIT(1)
3700aee095SJohan Hovold #define KSZPHY_OMSO_MII_OVERRIDE		BIT(0)
38212ea99aSMarek Vasut 
3951f932c4SChoi, David /* general Interrupt control/status reg in vendor specific block. */
4051f932c4SChoi, David #define MII_KSZPHY_INTCS			0x1B
4100aee095SJohan Hovold #define KSZPHY_INTCS_JABBER			BIT(15)
4200aee095SJohan Hovold #define KSZPHY_INTCS_RECEIVE_ERR		BIT(14)
4300aee095SJohan Hovold #define KSZPHY_INTCS_PAGE_RECEIVE		BIT(13)
4400aee095SJohan Hovold #define KSZPHY_INTCS_PARELLEL			BIT(12)
4500aee095SJohan Hovold #define KSZPHY_INTCS_LINK_PARTNER_ACK		BIT(11)
4600aee095SJohan Hovold #define KSZPHY_INTCS_LINK_DOWN			BIT(10)
4700aee095SJohan Hovold #define KSZPHY_INTCS_REMOTE_FAULT		BIT(9)
4800aee095SJohan Hovold #define KSZPHY_INTCS_LINK_UP			BIT(8)
4951f932c4SChoi, David #define KSZPHY_INTCS_ALL			(KSZPHY_INTCS_LINK_UP |\
5051f932c4SChoi, David 						KSZPHY_INTCS_LINK_DOWN)
5159ca4e58SIoana Ciornei #define KSZPHY_INTCS_LINK_DOWN_STATUS		BIT(2)
5259ca4e58SIoana Ciornei #define KSZPHY_INTCS_LINK_UP_STATUS		BIT(0)
5359ca4e58SIoana Ciornei #define KSZPHY_INTCS_STATUS			(KSZPHY_INTCS_LINK_DOWN_STATUS |\
5459ca4e58SIoana Ciornei 						 KSZPHY_INTCS_LINK_UP_STATUS)
5551f932c4SChoi, David 
565a16778eSJohan Hovold /* PHY Control 1 */
575a16778eSJohan Hovold #define MII_KSZPHY_CTRL_1			0x1e
58*f873f112SOleksij Rempel #define KSZ8081_CTRL1_MDIX_STAT			BIT(4)
595a16778eSJohan Hovold 
605a16778eSJohan Hovold /* PHY Control 2 / PHY Control (if no PHY Control 1) */
615a16778eSJohan Hovold #define MII_KSZPHY_CTRL_2			0x1f
625a16778eSJohan Hovold #define MII_KSZPHY_CTRL				MII_KSZPHY_CTRL_2
6351f932c4SChoi, David /* bitmap of PHY register to set interrupt mode */
64*f873f112SOleksij Rempel #define KSZ8081_CTRL2_HP_MDIX			BIT(15)
65*f873f112SOleksij Rempel #define KSZ8081_CTRL2_MDI_MDI_X_SELECT		BIT(14)
66*f873f112SOleksij Rempel #define KSZ8081_CTRL2_DISABLE_AUTO_MDIX		BIT(13)
67*f873f112SOleksij Rempel #define KSZ8081_CTRL2_FORCE_LINK		BIT(11)
68*f873f112SOleksij Rempel #define KSZ8081_CTRL2_POWER_SAVING		BIT(10)
6900aee095SJohan Hovold #define KSZPHY_CTRL_INT_ACTIVE_HIGH		BIT(9)
7063f44b2bSJohan Hovold #define KSZPHY_RMII_REF_CLK_SEL			BIT(7)
7151f932c4SChoi, David 
72954c3967SSean Cross /* Write/read to/from extended registers */
73954c3967SSean Cross #define MII_KSZPHY_EXTREG			0x0b
74954c3967SSean Cross #define KSZPHY_EXTREG_WRITE			0x8000
75954c3967SSean Cross 
76954c3967SSean Cross #define MII_KSZPHY_EXTREG_WRITE			0x0c
77954c3967SSean Cross #define MII_KSZPHY_EXTREG_READ			0x0d
78954c3967SSean Cross 
79954c3967SSean Cross /* Extended registers */
80954c3967SSean Cross #define MII_KSZPHY_CLK_CONTROL_PAD_SKEW		0x104
81954c3967SSean Cross #define MII_KSZPHY_RX_DATA_PAD_SKEW		0x105
82954c3967SSean Cross #define MII_KSZPHY_TX_DATA_PAD_SKEW		0x106
83954c3967SSean Cross 
84954c3967SSean Cross #define PS_TO_REG				200
85954c3967SSean Cross 
862b2427d0SAndrew Lunn struct kszphy_hw_stat {
872b2427d0SAndrew Lunn 	const char *string;
882b2427d0SAndrew Lunn 	u8 reg;
892b2427d0SAndrew Lunn 	u8 bits;
902b2427d0SAndrew Lunn };
912b2427d0SAndrew Lunn 
922b2427d0SAndrew Lunn static struct kszphy_hw_stat kszphy_hw_stats[] = {
932b2427d0SAndrew Lunn 	{ "phy_receive_errors", 21, 16},
942b2427d0SAndrew Lunn 	{ "phy_idle_errors", 10, 8 },
952b2427d0SAndrew Lunn };
962b2427d0SAndrew Lunn 
97e6a423a8SJohan Hovold struct kszphy_type {
98e6a423a8SJohan Hovold 	u32 led_mode_reg;
99c6f9575cSJohan Hovold 	u16 interrupt_level_mask;
1000f95903eSJohan Hovold 	bool has_broadcast_disable;
1012b0ba96cSSylvain Rochet 	bool has_nand_tree_disable;
10263f44b2bSJohan Hovold 	bool has_rmii_ref_clk_sel;
103e6a423a8SJohan Hovold };
104e6a423a8SJohan Hovold 
105e6a423a8SJohan Hovold struct kszphy_priv {
106e6a423a8SJohan Hovold 	const struct kszphy_type *type;
107e7a792e9SJohan Hovold 	int led_mode;
10863f44b2bSJohan Hovold 	bool rmii_ref_clk_sel;
10963f44b2bSJohan Hovold 	bool rmii_ref_clk_sel_val;
1102b2427d0SAndrew Lunn 	u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
111e6a423a8SJohan Hovold };
112e6a423a8SJohan Hovold 
113e6a423a8SJohan Hovold static const struct kszphy_type ksz8021_type = {
114e6a423a8SJohan Hovold 	.led_mode_reg		= MII_KSZPHY_CTRL_2,
115d0e1df9cSJohan Hovold 	.has_broadcast_disable	= true,
1162b0ba96cSSylvain Rochet 	.has_nand_tree_disable	= true,
11763f44b2bSJohan Hovold 	.has_rmii_ref_clk_sel	= true,
118e6a423a8SJohan Hovold };
119e6a423a8SJohan Hovold 
120e6a423a8SJohan Hovold static const struct kszphy_type ksz8041_type = {
121e6a423a8SJohan Hovold 	.led_mode_reg		= MII_KSZPHY_CTRL_1,
122e6a423a8SJohan Hovold };
123e6a423a8SJohan Hovold 
124e6a423a8SJohan Hovold static const struct kszphy_type ksz8051_type = {
125e6a423a8SJohan Hovold 	.led_mode_reg		= MII_KSZPHY_CTRL_2,
1262b0ba96cSSylvain Rochet 	.has_nand_tree_disable	= true,
127e6a423a8SJohan Hovold };
128e6a423a8SJohan Hovold 
129e6a423a8SJohan Hovold static const struct kszphy_type ksz8081_type = {
130e6a423a8SJohan Hovold 	.led_mode_reg		= MII_KSZPHY_CTRL_2,
1310f95903eSJohan Hovold 	.has_broadcast_disable	= true,
1322b0ba96cSSylvain Rochet 	.has_nand_tree_disable	= true,
13386dc1342SJohan Hovold 	.has_rmii_ref_clk_sel	= true,
134e6a423a8SJohan Hovold };
135e6a423a8SJohan Hovold 
136c6f9575cSJohan Hovold static const struct kszphy_type ks8737_type = {
137c6f9575cSJohan Hovold 	.interrupt_level_mask	= BIT(14),
138c6f9575cSJohan Hovold };
139c6f9575cSJohan Hovold 
140c6f9575cSJohan Hovold static const struct kszphy_type ksz9021_type = {
141c6f9575cSJohan Hovold 	.interrupt_level_mask	= BIT(14),
142c6f9575cSJohan Hovold };
143c6f9575cSJohan Hovold 
144954c3967SSean Cross static int kszphy_extended_write(struct phy_device *phydev,
145954c3967SSean Cross 				u32 regnum, u16 val)
146954c3967SSean Cross {
147954c3967SSean Cross 	phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum);
148954c3967SSean Cross 	return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val);
149954c3967SSean Cross }
150954c3967SSean Cross 
151954c3967SSean Cross static int kszphy_extended_read(struct phy_device *phydev,
152954c3967SSean Cross 				u32 regnum)
153954c3967SSean Cross {
154954c3967SSean Cross 	phy_write(phydev, MII_KSZPHY_EXTREG, regnum);
155954c3967SSean Cross 	return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
156954c3967SSean Cross }
157954c3967SSean Cross 
15851f932c4SChoi, David static int kszphy_ack_interrupt(struct phy_device *phydev)
15951f932c4SChoi, David {
16051f932c4SChoi, David 	/* bit[7..0] int status, which is a read and clear register. */
16151f932c4SChoi, David 	int rc;
16251f932c4SChoi, David 
16351f932c4SChoi, David 	rc = phy_read(phydev, MII_KSZPHY_INTCS);
16451f932c4SChoi, David 
16551f932c4SChoi, David 	return (rc < 0) ? rc : 0;
16651f932c4SChoi, David }
16751f932c4SChoi, David 
16851f932c4SChoi, David static int kszphy_config_intr(struct phy_device *phydev)
16951f932c4SChoi, David {
170c6f9575cSJohan Hovold 	const struct kszphy_type *type = phydev->drv->driver_data;
171c0c99d0cSIoana Ciornei 	int temp, err;
172c6f9575cSJohan Hovold 	u16 mask;
173c6f9575cSJohan Hovold 
174c6f9575cSJohan Hovold 	if (type && type->interrupt_level_mask)
175c6f9575cSJohan Hovold 		mask = type->interrupt_level_mask;
176c6f9575cSJohan Hovold 	else
177c6f9575cSJohan Hovold 		mask = KSZPHY_CTRL_INT_ACTIVE_HIGH;
17851f932c4SChoi, David 
17951f932c4SChoi, David 	/* set the interrupt pin active low */
18051f932c4SChoi, David 	temp = phy_read(phydev, MII_KSZPHY_CTRL);
1815bb8fc0dSJohan Hovold 	if (temp < 0)
1825bb8fc0dSJohan Hovold 		return temp;
183c6f9575cSJohan Hovold 	temp &= ~mask;
18451f932c4SChoi, David 	phy_write(phydev, MII_KSZPHY_CTRL, temp);
18551f932c4SChoi, David 
186c6f9575cSJohan Hovold 	/* enable / disable interrupts */
187c0c99d0cSIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
188c0c99d0cSIoana Ciornei 		err = kszphy_ack_interrupt(phydev);
189c0c99d0cSIoana Ciornei 		if (err)
190c0c99d0cSIoana Ciornei 			return err;
19151f932c4SChoi, David 
192c0c99d0cSIoana Ciornei 		temp = KSZPHY_INTCS_ALL;
193c0c99d0cSIoana Ciornei 		err = phy_write(phydev, MII_KSZPHY_INTCS, temp);
194c0c99d0cSIoana Ciornei 	} else {
195c0c99d0cSIoana Ciornei 		temp = 0;
196c0c99d0cSIoana Ciornei 		err = phy_write(phydev, MII_KSZPHY_INTCS, temp);
197c0c99d0cSIoana Ciornei 		if (err)
198c0c99d0cSIoana Ciornei 			return err;
199c0c99d0cSIoana Ciornei 
200c0c99d0cSIoana Ciornei 		err = kszphy_ack_interrupt(phydev);
201c0c99d0cSIoana Ciornei 	}
202c0c99d0cSIoana Ciornei 
203c0c99d0cSIoana Ciornei 	return err;
20451f932c4SChoi, David }
205d0507009SDavid J. Choi 
20659ca4e58SIoana Ciornei static irqreturn_t kszphy_handle_interrupt(struct phy_device *phydev)
20759ca4e58SIoana Ciornei {
20859ca4e58SIoana Ciornei 	int irq_status;
20959ca4e58SIoana Ciornei 
21059ca4e58SIoana Ciornei 	irq_status = phy_read(phydev, MII_KSZPHY_INTCS);
21159ca4e58SIoana Ciornei 	if (irq_status < 0) {
21259ca4e58SIoana Ciornei 		phy_error(phydev);
21359ca4e58SIoana Ciornei 		return IRQ_NONE;
21459ca4e58SIoana Ciornei 	}
21559ca4e58SIoana Ciornei 
216fff4c746SOleksij Rempel 	if (!(irq_status & KSZPHY_INTCS_STATUS))
21759ca4e58SIoana Ciornei 		return IRQ_NONE;
21859ca4e58SIoana Ciornei 
21959ca4e58SIoana Ciornei 	phy_trigger_machine(phydev);
22059ca4e58SIoana Ciornei 
22159ca4e58SIoana Ciornei 	return IRQ_HANDLED;
22259ca4e58SIoana Ciornei }
22359ca4e58SIoana Ciornei 
22463f44b2bSJohan Hovold static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val)
22563f44b2bSJohan Hovold {
22663f44b2bSJohan Hovold 	int ctrl;
22763f44b2bSJohan Hovold 
22863f44b2bSJohan Hovold 	ctrl = phy_read(phydev, MII_KSZPHY_CTRL);
22963f44b2bSJohan Hovold 	if (ctrl < 0)
23063f44b2bSJohan Hovold 		return ctrl;
23163f44b2bSJohan Hovold 
23263f44b2bSJohan Hovold 	if (val)
23363f44b2bSJohan Hovold 		ctrl |= KSZPHY_RMII_REF_CLK_SEL;
23463f44b2bSJohan Hovold 	else
23563f44b2bSJohan Hovold 		ctrl &= ~KSZPHY_RMII_REF_CLK_SEL;
23663f44b2bSJohan Hovold 
23763f44b2bSJohan Hovold 	return phy_write(phydev, MII_KSZPHY_CTRL, ctrl);
23863f44b2bSJohan Hovold }
23963f44b2bSJohan Hovold 
240e7a792e9SJohan Hovold static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
24120d8435aSBen Dooks {
2425a16778eSJohan Hovold 	int rc, temp, shift;
2438620546cSJohan Hovold 
2445a16778eSJohan Hovold 	switch (reg) {
2455a16778eSJohan Hovold 	case MII_KSZPHY_CTRL_1:
2465a16778eSJohan Hovold 		shift = 14;
2475a16778eSJohan Hovold 		break;
2485a16778eSJohan Hovold 	case MII_KSZPHY_CTRL_2:
2495a16778eSJohan Hovold 		shift = 4;
2505a16778eSJohan Hovold 		break;
2515a16778eSJohan Hovold 	default:
2525a16778eSJohan Hovold 		return -EINVAL;
2535a16778eSJohan Hovold 	}
2545a16778eSJohan Hovold 
25520d8435aSBen Dooks 	temp = phy_read(phydev, reg);
256b7035860SJohan Hovold 	if (temp < 0) {
257b7035860SJohan Hovold 		rc = temp;
258b7035860SJohan Hovold 		goto out;
259b7035860SJohan Hovold 	}
26020d8435aSBen Dooks 
26128bdc499SSergei Shtylyov 	temp &= ~(3 << shift);
26220d8435aSBen Dooks 	temp |= val << shift;
26320d8435aSBen Dooks 	rc = phy_write(phydev, reg, temp);
264b7035860SJohan Hovold out:
265b7035860SJohan Hovold 	if (rc < 0)
26672ba48beSAndrew Lunn 		phydev_err(phydev, "failed to set led mode\n");
26720d8435aSBen Dooks 
268b7035860SJohan Hovold 	return rc;
26920d8435aSBen Dooks }
27020d8435aSBen Dooks 
271bde15129SJohan Hovold /* Disable PHY address 0 as the broadcast address, so that it can be used as a
272bde15129SJohan Hovold  * unique (non-broadcast) address on a shared bus.
273bde15129SJohan Hovold  */
274bde15129SJohan Hovold static int kszphy_broadcast_disable(struct phy_device *phydev)
275bde15129SJohan Hovold {
276bde15129SJohan Hovold 	int ret;
277bde15129SJohan Hovold 
278bde15129SJohan Hovold 	ret = phy_read(phydev, MII_KSZPHY_OMSO);
279bde15129SJohan Hovold 	if (ret < 0)
280bde15129SJohan Hovold 		goto out;
281bde15129SJohan Hovold 
282bde15129SJohan Hovold 	ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF);
283bde15129SJohan Hovold out:
284bde15129SJohan Hovold 	if (ret)
28572ba48beSAndrew Lunn 		phydev_err(phydev, "failed to disable broadcast address\n");
286bde15129SJohan Hovold 
287bde15129SJohan Hovold 	return ret;
288bde15129SJohan Hovold }
289bde15129SJohan Hovold 
2902b0ba96cSSylvain Rochet static int kszphy_nand_tree_disable(struct phy_device *phydev)
2912b0ba96cSSylvain Rochet {
2922b0ba96cSSylvain Rochet 	int ret;
2932b0ba96cSSylvain Rochet 
2942b0ba96cSSylvain Rochet 	ret = phy_read(phydev, MII_KSZPHY_OMSO);
2952b0ba96cSSylvain Rochet 	if (ret < 0)
2962b0ba96cSSylvain Rochet 		goto out;
2972b0ba96cSSylvain Rochet 
2982b0ba96cSSylvain Rochet 	if (!(ret & KSZPHY_OMSO_NAND_TREE_ON))
2992b0ba96cSSylvain Rochet 		return 0;
3002b0ba96cSSylvain Rochet 
3012b0ba96cSSylvain Rochet 	ret = phy_write(phydev, MII_KSZPHY_OMSO,
3022b0ba96cSSylvain Rochet 			ret & ~KSZPHY_OMSO_NAND_TREE_ON);
3032b0ba96cSSylvain Rochet out:
3042b0ba96cSSylvain Rochet 	if (ret)
30572ba48beSAndrew Lunn 		phydev_err(phydev, "failed to disable NAND tree mode\n");
3062b0ba96cSSylvain Rochet 
3072b0ba96cSSylvain Rochet 	return ret;
3082b0ba96cSSylvain Rochet }
3092b0ba96cSSylvain Rochet 
31079e498a9SLeonard Crestez /* Some config bits need to be set again on resume, handle them here. */
31179e498a9SLeonard Crestez static int kszphy_config_reset(struct phy_device *phydev)
31279e498a9SLeonard Crestez {
31379e498a9SLeonard Crestez 	struct kszphy_priv *priv = phydev->priv;
31479e498a9SLeonard Crestez 	int ret;
31579e498a9SLeonard Crestez 
31679e498a9SLeonard Crestez 	if (priv->rmii_ref_clk_sel) {
31779e498a9SLeonard Crestez 		ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val);
31879e498a9SLeonard Crestez 		if (ret) {
31979e498a9SLeonard Crestez 			phydev_err(phydev,
32079e498a9SLeonard Crestez 				   "failed to set rmii reference clock\n");
32179e498a9SLeonard Crestez 			return ret;
32279e498a9SLeonard Crestez 		}
32379e498a9SLeonard Crestez 	}
32479e498a9SLeonard Crestez 
32579e498a9SLeonard Crestez 	if (priv->led_mode >= 0)
32679e498a9SLeonard Crestez 		kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode);
32779e498a9SLeonard Crestez 
32879e498a9SLeonard Crestez 	return 0;
32979e498a9SLeonard Crestez }
33079e498a9SLeonard Crestez 
331d0507009SDavid J. Choi static int kszphy_config_init(struct phy_device *phydev)
332d0507009SDavid J. Choi {
333e6a423a8SJohan Hovold 	struct kszphy_priv *priv = phydev->priv;
334e6a423a8SJohan Hovold 	const struct kszphy_type *type;
335d0507009SDavid J. Choi 
336e6a423a8SJohan Hovold 	if (!priv)
337e6a423a8SJohan Hovold 		return 0;
338e6a423a8SJohan Hovold 
339e6a423a8SJohan Hovold 	type = priv->type;
340e6a423a8SJohan Hovold 
3410f95903eSJohan Hovold 	if (type->has_broadcast_disable)
3420f95903eSJohan Hovold 		kszphy_broadcast_disable(phydev);
3430f95903eSJohan Hovold 
3442b0ba96cSSylvain Rochet 	if (type->has_nand_tree_disable)
3452b0ba96cSSylvain Rochet 		kszphy_nand_tree_disable(phydev);
3462b0ba96cSSylvain Rochet 
34779e498a9SLeonard Crestez 	return kszphy_config_reset(phydev);
34820d8435aSBen Dooks }
34920d8435aSBen Dooks 
3504217a64eSMichael Walle static int ksz8041_fiber_mode(struct phy_device *phydev)
3514217a64eSMichael Walle {
3524217a64eSMichael Walle 	struct device_node *of_node = phydev->mdio.dev.of_node;
3534217a64eSMichael Walle 
3544217a64eSMichael Walle 	return of_property_read_bool(of_node, "micrel,fiber-mode");
3554217a64eSMichael Walle }
3564217a64eSMichael Walle 
35777501a79SPhilipp Zabel static int ksz8041_config_init(struct phy_device *phydev)
35877501a79SPhilipp Zabel {
3593c1bcc86SAndrew Lunn 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
3603c1bcc86SAndrew Lunn 
36177501a79SPhilipp Zabel 	/* Limit supported and advertised modes in fiber mode */
3624217a64eSMichael Walle 	if (ksz8041_fiber_mode(phydev)) {
36377501a79SPhilipp Zabel 		phydev->dev_flags |= MICREL_PHY_FXEN;
3643c1bcc86SAndrew Lunn 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
3653c1bcc86SAndrew Lunn 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
3663c1bcc86SAndrew Lunn 
3673c1bcc86SAndrew Lunn 		linkmode_and(phydev->supported, phydev->supported, mask);
3683c1bcc86SAndrew Lunn 		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
3693c1bcc86SAndrew Lunn 				 phydev->supported);
3703c1bcc86SAndrew Lunn 		linkmode_and(phydev->advertising, phydev->advertising, mask);
3713c1bcc86SAndrew Lunn 		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
3723c1bcc86SAndrew Lunn 				 phydev->advertising);
37377501a79SPhilipp Zabel 		phydev->autoneg = AUTONEG_DISABLE;
37477501a79SPhilipp Zabel 	}
37577501a79SPhilipp Zabel 
37677501a79SPhilipp Zabel 	return kszphy_config_init(phydev);
37777501a79SPhilipp Zabel }
37877501a79SPhilipp Zabel 
37977501a79SPhilipp Zabel static int ksz8041_config_aneg(struct phy_device *phydev)
38077501a79SPhilipp Zabel {
38177501a79SPhilipp Zabel 	/* Skip auto-negotiation in fiber mode */
38277501a79SPhilipp Zabel 	if (phydev->dev_flags & MICREL_PHY_FXEN) {
38377501a79SPhilipp Zabel 		phydev->speed = SPEED_100;
38477501a79SPhilipp Zabel 		return 0;
38577501a79SPhilipp Zabel 	}
38677501a79SPhilipp Zabel 
38777501a79SPhilipp Zabel 	return genphy_config_aneg(phydev);
38877501a79SPhilipp Zabel }
38977501a79SPhilipp Zabel 
3908b95599cSMarek Vasut static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev,
3918b95599cSMarek Vasut 					    const u32 ksz_phy_id)
3928b95599cSMarek Vasut {
3938b95599cSMarek Vasut 	int ret;
3948b95599cSMarek Vasut 
3958b95599cSMarek Vasut 	if ((phydev->phy_id & MICREL_PHY_ID_MASK) != ksz_phy_id)
3968b95599cSMarek Vasut 		return 0;
3978b95599cSMarek Vasut 
3988b95599cSMarek Vasut 	ret = phy_read(phydev, MII_BMSR);
3998b95599cSMarek Vasut 	if (ret < 0)
4008b95599cSMarek Vasut 		return ret;
4018b95599cSMarek Vasut 
4028b95599cSMarek Vasut 	/* KSZ8051 PHY and KSZ8794/KSZ8795/KSZ8765 switch share the same
4038b95599cSMarek Vasut 	 * exact PHY ID. However, they can be told apart by the extended
4048b95599cSMarek Vasut 	 * capability registers presence. The KSZ8051 PHY has them while
4058b95599cSMarek Vasut 	 * the switch does not.
4068b95599cSMarek Vasut 	 */
4078b95599cSMarek Vasut 	ret &= BMSR_ERCAP;
4088b95599cSMarek Vasut 	if (ksz_phy_id == PHY_ID_KSZ8051)
4098b95599cSMarek Vasut 		return ret;
4108b95599cSMarek Vasut 	else
4118b95599cSMarek Vasut 		return !ret;
4128b95599cSMarek Vasut }
4138b95599cSMarek Vasut 
4148b95599cSMarek Vasut static int ksz8051_match_phy_device(struct phy_device *phydev)
4158b95599cSMarek Vasut {
4168b95599cSMarek Vasut 	return ksz8051_ksz8795_match_phy_device(phydev, PHY_ID_KSZ8051);
4178b95599cSMarek Vasut }
4188b95599cSMarek Vasut 
4197a1d8390SAntoine Tenart static int ksz8081_config_init(struct phy_device *phydev)
4207a1d8390SAntoine Tenart {
4217a1d8390SAntoine Tenart 	/* KSZPHY_OMSO_FACTORY_TEST is set at de-assertion of the reset line
4227a1d8390SAntoine Tenart 	 * based on the RXER (KSZ8081RNA/RND) or TXC (KSZ8081MNX/RNB) pin. If a
4237a1d8390SAntoine Tenart 	 * pull-down is missing, the factory test mode should be cleared by
4247a1d8390SAntoine Tenart 	 * manually writing a 0.
4257a1d8390SAntoine Tenart 	 */
4267a1d8390SAntoine Tenart 	phy_clear_bits(phydev, MII_KSZPHY_OMSO, KSZPHY_OMSO_FACTORY_TEST);
4277a1d8390SAntoine Tenart 
4287a1d8390SAntoine Tenart 	return kszphy_config_init(phydev);
4297a1d8390SAntoine Tenart }
4307a1d8390SAntoine Tenart 
431*f873f112SOleksij Rempel static int ksz8081_config_mdix(struct phy_device *phydev, u8 ctrl)
432*f873f112SOleksij Rempel {
433*f873f112SOleksij Rempel 	u16 val;
434*f873f112SOleksij Rempel 
435*f873f112SOleksij Rempel 	switch (ctrl) {
436*f873f112SOleksij Rempel 	case ETH_TP_MDI:
437*f873f112SOleksij Rempel 		val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX;
438*f873f112SOleksij Rempel 		break;
439*f873f112SOleksij Rempel 	case ETH_TP_MDI_X:
440*f873f112SOleksij Rempel 		val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX |
441*f873f112SOleksij Rempel 			KSZ8081_CTRL2_MDI_MDI_X_SELECT;
442*f873f112SOleksij Rempel 		break;
443*f873f112SOleksij Rempel 	case ETH_TP_MDI_AUTO:
444*f873f112SOleksij Rempel 		val = 0;
445*f873f112SOleksij Rempel 		break;
446*f873f112SOleksij Rempel 	default:
447*f873f112SOleksij Rempel 		return 0;
448*f873f112SOleksij Rempel 	}
449*f873f112SOleksij Rempel 
450*f873f112SOleksij Rempel 	return phy_modify(phydev, MII_KSZPHY_CTRL_2,
451*f873f112SOleksij Rempel 			  KSZ8081_CTRL2_HP_MDIX |
452*f873f112SOleksij Rempel 			  KSZ8081_CTRL2_MDI_MDI_X_SELECT |
453*f873f112SOleksij Rempel 			  KSZ8081_CTRL2_DISABLE_AUTO_MDIX,
454*f873f112SOleksij Rempel 			  KSZ8081_CTRL2_HP_MDIX | val);
455*f873f112SOleksij Rempel }
456*f873f112SOleksij Rempel 
457*f873f112SOleksij Rempel static int ksz8081_config_aneg(struct phy_device *phydev)
458*f873f112SOleksij Rempel {
459*f873f112SOleksij Rempel 	int ret;
460*f873f112SOleksij Rempel 
461*f873f112SOleksij Rempel 	ret = genphy_config_aneg(phydev);
462*f873f112SOleksij Rempel 	if (ret)
463*f873f112SOleksij Rempel 		return ret;
464*f873f112SOleksij Rempel 
465*f873f112SOleksij Rempel 	/* The MDI-X configuration is automatically changed by the PHY after
466*f873f112SOleksij Rempel 	 * switching from autoneg off to on. So, take MDI-X configuration under
467*f873f112SOleksij Rempel 	 * own control and set it after autoneg configuration was done.
468*f873f112SOleksij Rempel 	 */
469*f873f112SOleksij Rempel 	return ksz8081_config_mdix(phydev, phydev->mdix_ctrl);
470*f873f112SOleksij Rempel }
471*f873f112SOleksij Rempel 
472*f873f112SOleksij Rempel static int ksz8081_mdix_update(struct phy_device *phydev)
473*f873f112SOleksij Rempel {
474*f873f112SOleksij Rempel 	int ret;
475*f873f112SOleksij Rempel 
476*f873f112SOleksij Rempel 	ret = phy_read(phydev, MII_KSZPHY_CTRL_2);
477*f873f112SOleksij Rempel 	if (ret < 0)
478*f873f112SOleksij Rempel 		return ret;
479*f873f112SOleksij Rempel 
480*f873f112SOleksij Rempel 	if (ret & KSZ8081_CTRL2_DISABLE_AUTO_MDIX) {
481*f873f112SOleksij Rempel 		if (ret & KSZ8081_CTRL2_MDI_MDI_X_SELECT)
482*f873f112SOleksij Rempel 			phydev->mdix_ctrl = ETH_TP_MDI_X;
483*f873f112SOleksij Rempel 		else
484*f873f112SOleksij Rempel 			phydev->mdix_ctrl = ETH_TP_MDI;
485*f873f112SOleksij Rempel 	} else {
486*f873f112SOleksij Rempel 		phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
487*f873f112SOleksij Rempel 	}
488*f873f112SOleksij Rempel 
489*f873f112SOleksij Rempel 	ret = phy_read(phydev, MII_KSZPHY_CTRL_1);
490*f873f112SOleksij Rempel 	if (ret < 0)
491*f873f112SOleksij Rempel 		return ret;
492*f873f112SOleksij Rempel 
493*f873f112SOleksij Rempel 	if (ret & KSZ8081_CTRL1_MDIX_STAT)
494*f873f112SOleksij Rempel 		phydev->mdix = ETH_TP_MDI;
495*f873f112SOleksij Rempel 	else
496*f873f112SOleksij Rempel 		phydev->mdix = ETH_TP_MDI_X;
497*f873f112SOleksij Rempel 
498*f873f112SOleksij Rempel 	return 0;
499*f873f112SOleksij Rempel }
500*f873f112SOleksij Rempel 
501*f873f112SOleksij Rempel static int ksz8081_read_status(struct phy_device *phydev)
502*f873f112SOleksij Rempel {
503*f873f112SOleksij Rempel 	int ret;
504*f873f112SOleksij Rempel 
505*f873f112SOleksij Rempel 	ret = ksz8081_mdix_update(phydev);
506*f873f112SOleksij Rempel 	if (ret < 0)
507*f873f112SOleksij Rempel 		return ret;
508*f873f112SOleksij Rempel 
509*f873f112SOleksij Rempel 	return genphy_read_status(phydev);
510*f873f112SOleksij Rempel }
511*f873f112SOleksij Rempel 
512232ba3a5SRajasingh Thavamani static int ksz8061_config_init(struct phy_device *phydev)
513232ba3a5SRajasingh Thavamani {
514232ba3a5SRajasingh Thavamani 	int ret;
515232ba3a5SRajasingh Thavamani 
516232ba3a5SRajasingh Thavamani 	ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A);
517232ba3a5SRajasingh Thavamani 	if (ret)
518232ba3a5SRajasingh Thavamani 		return ret;
519232ba3a5SRajasingh Thavamani 
520232ba3a5SRajasingh Thavamani 	return kszphy_config_init(phydev);
521232ba3a5SRajasingh Thavamani }
522232ba3a5SRajasingh Thavamani 
5238b95599cSMarek Vasut static int ksz8795_match_phy_device(struct phy_device *phydev)
5248b95599cSMarek Vasut {
5251d951ba3SMarek Vasut 	return ksz8051_ksz8795_match_phy_device(phydev, PHY_ID_KSZ87XX);
5268b95599cSMarek Vasut }
5278b95599cSMarek Vasut 
528954c3967SSean Cross static int ksz9021_load_values_from_of(struct phy_device *phydev,
5293c9a9f7fSJaeden Amero 				       const struct device_node *of_node,
5303c9a9f7fSJaeden Amero 				       u16 reg,
5313c9a9f7fSJaeden Amero 				       const char *field1, const char *field2,
5323c9a9f7fSJaeden Amero 				       const char *field3, const char *field4)
533954c3967SSean Cross {
534954c3967SSean Cross 	int val1 = -1;
535954c3967SSean Cross 	int val2 = -2;
536954c3967SSean Cross 	int val3 = -3;
537954c3967SSean Cross 	int val4 = -4;
538954c3967SSean Cross 	int newval;
539954c3967SSean Cross 	int matches = 0;
540954c3967SSean Cross 
541954c3967SSean Cross 	if (!of_property_read_u32(of_node, field1, &val1))
542954c3967SSean Cross 		matches++;
543954c3967SSean Cross 
544954c3967SSean Cross 	if (!of_property_read_u32(of_node, field2, &val2))
545954c3967SSean Cross 		matches++;
546954c3967SSean Cross 
547954c3967SSean Cross 	if (!of_property_read_u32(of_node, field3, &val3))
548954c3967SSean Cross 		matches++;
549954c3967SSean Cross 
550954c3967SSean Cross 	if (!of_property_read_u32(of_node, field4, &val4))
551954c3967SSean Cross 		matches++;
552954c3967SSean Cross 
553954c3967SSean Cross 	if (!matches)
554954c3967SSean Cross 		return 0;
555954c3967SSean Cross 
556954c3967SSean Cross 	if (matches < 4)
557954c3967SSean Cross 		newval = kszphy_extended_read(phydev, reg);
558954c3967SSean Cross 	else
559954c3967SSean Cross 		newval = 0;
560954c3967SSean Cross 
561954c3967SSean Cross 	if (val1 != -1)
562954c3967SSean Cross 		newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0);
563954c3967SSean Cross 
5646a119745SHubert Chaumette 	if (val2 != -2)
565954c3967SSean Cross 		newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4);
566954c3967SSean Cross 
5676a119745SHubert Chaumette 	if (val3 != -3)
568954c3967SSean Cross 		newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8);
569954c3967SSean Cross 
5706a119745SHubert Chaumette 	if (val4 != -4)
571954c3967SSean Cross 		newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12);
572954c3967SSean Cross 
573954c3967SSean Cross 	return kszphy_extended_write(phydev, reg, newval);
574954c3967SSean Cross }
575954c3967SSean Cross 
576954c3967SSean Cross static int ksz9021_config_init(struct phy_device *phydev)
577954c3967SSean Cross {
578ce4f8afdSColin Ian King 	const struct device_node *of_node;
579651df218SAndrew Lunn 	const struct device *dev_walker;
580954c3967SSean Cross 
581651df218SAndrew Lunn 	/* The Micrel driver has a deprecated option to place phy OF
582651df218SAndrew Lunn 	 * properties in the MAC node. Walk up the tree of devices to
583651df218SAndrew Lunn 	 * find a device with an OF node.
584651df218SAndrew Lunn 	 */
585e5a03bfdSAndrew Lunn 	dev_walker = &phydev->mdio.dev;
586651df218SAndrew Lunn 	do {
587651df218SAndrew Lunn 		of_node = dev_walker->of_node;
588651df218SAndrew Lunn 		dev_walker = dev_walker->parent;
589651df218SAndrew Lunn 
590651df218SAndrew Lunn 	} while (!of_node && dev_walker);
591954c3967SSean Cross 
592954c3967SSean Cross 	if (of_node) {
593954c3967SSean Cross 		ksz9021_load_values_from_of(phydev, of_node,
594954c3967SSean Cross 				    MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
595954c3967SSean Cross 				    "txen-skew-ps", "txc-skew-ps",
596954c3967SSean Cross 				    "rxdv-skew-ps", "rxc-skew-ps");
597954c3967SSean Cross 		ksz9021_load_values_from_of(phydev, of_node,
598954c3967SSean Cross 				    MII_KSZPHY_RX_DATA_PAD_SKEW,
599954c3967SSean Cross 				    "rxd0-skew-ps", "rxd1-skew-ps",
600954c3967SSean Cross 				    "rxd2-skew-ps", "rxd3-skew-ps");
601954c3967SSean Cross 		ksz9021_load_values_from_of(phydev, of_node,
602954c3967SSean Cross 				    MII_KSZPHY_TX_DATA_PAD_SKEW,
603954c3967SSean Cross 				    "txd0-skew-ps", "txd1-skew-ps",
604954c3967SSean Cross 				    "txd2-skew-ps", "txd3-skew-ps");
605954c3967SSean Cross 	}
606954c3967SSean Cross 	return 0;
607954c3967SSean Cross }
608954c3967SSean Cross 
6096e4b8273SHubert Chaumette #define KSZ9031_PS_TO_REG		60
6106e4b8273SHubert Chaumette 
6116e4b8273SHubert Chaumette /* Extended registers */
6126270e1aeSJaeden Amero /* MMD Address 0x0 */
6136270e1aeSJaeden Amero #define MII_KSZ9031RN_FLP_BURST_TX_LO	3
6146270e1aeSJaeden Amero #define MII_KSZ9031RN_FLP_BURST_TX_HI	4
6156270e1aeSJaeden Amero 
616ae6c97bbSJaeden Amero /* MMD Address 0x2 */
6176e4b8273SHubert Chaumette #define MII_KSZ9031RN_CONTROL_PAD_SKEW	4
618bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RX_CTL_M		GENMASK(7, 4)
619bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TX_CTL_M		GENMASK(3, 0)
620bcf3440cSOleksij Rempel 
6216e4b8273SHubert Chaumette #define MII_KSZ9031RN_RX_DATA_PAD_SKEW	5
622bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD3		GENMASK(15, 12)
623bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD2		GENMASK(11, 8)
624bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD1		GENMASK(7, 4)
625bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RXD0		GENMASK(3, 0)
626bcf3440cSOleksij Rempel 
6276e4b8273SHubert Chaumette #define MII_KSZ9031RN_TX_DATA_PAD_SKEW	6
628bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD3		GENMASK(15, 12)
629bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD2		GENMASK(11, 8)
630bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD1		GENMASK(7, 4)
631bcf3440cSOleksij Rempel #define MII_KSZ9031RN_TXD0		GENMASK(3, 0)
632bcf3440cSOleksij Rempel 
6336e4b8273SHubert Chaumette #define MII_KSZ9031RN_CLK_PAD_SKEW	8
634bcf3440cSOleksij Rempel #define MII_KSZ9031RN_GTX_CLK		GENMASK(9, 5)
635bcf3440cSOleksij Rempel #define MII_KSZ9031RN_RX_CLK		GENMASK(4, 0)
636bcf3440cSOleksij Rempel 
637bcf3440cSOleksij Rempel /* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To
638bcf3440cSOleksij Rempel  * provide different RGMII options we need to configure delay offset
639bcf3440cSOleksij Rempel  * for each pad relative to build in delay.
640bcf3440cSOleksij Rempel  */
641bcf3440cSOleksij Rempel /* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of
642bcf3440cSOleksij Rempel  * 1.80ns
643bcf3440cSOleksij Rempel  */
644bcf3440cSOleksij Rempel #define RX_ID				0x7
645bcf3440cSOleksij Rempel #define RX_CLK_ID			0x19
646bcf3440cSOleksij Rempel 
647bcf3440cSOleksij Rempel /* set rx to +0.30ns and rx_clk to -0.90ns to compensate the
648bcf3440cSOleksij Rempel  * internal 1.2ns delay.
649bcf3440cSOleksij Rempel  */
650bcf3440cSOleksij Rempel #define RX_ND				0xc
651bcf3440cSOleksij Rempel #define RX_CLK_ND			0x0
652bcf3440cSOleksij Rempel 
653bcf3440cSOleksij Rempel /* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */
654bcf3440cSOleksij Rempel #define TX_ID				0x0
655bcf3440cSOleksij Rempel #define TX_CLK_ID			0x1f
656bcf3440cSOleksij Rempel 
657bcf3440cSOleksij Rempel /* set tx and tx_clk to "No delay adjustment" to keep 0ns
658bcf3440cSOleksij Rempel  * dealy
659bcf3440cSOleksij Rempel  */
660bcf3440cSOleksij Rempel #define TX_ND				0x7
661bcf3440cSOleksij Rempel #define TX_CLK_ND			0xf
6626e4b8273SHubert Chaumette 
663af70c1f9SMike Looijmans /* MMD Address 0x1C */
664af70c1f9SMike Looijmans #define MII_KSZ9031RN_EDPD		0x23
665af70c1f9SMike Looijmans #define MII_KSZ9031RN_EDPD_ENABLE	BIT(0)
666af70c1f9SMike Looijmans 
6676e4b8273SHubert Chaumette static int ksz9031_of_load_skew_values(struct phy_device *phydev,
6683c9a9f7fSJaeden Amero 				       const struct device_node *of_node,
6696e4b8273SHubert Chaumette 				       u16 reg, size_t field_sz,
670bcf3440cSOleksij Rempel 				       const char *field[], u8 numfields,
671bcf3440cSOleksij Rempel 				       bool *update)
6726e4b8273SHubert Chaumette {
6736e4b8273SHubert Chaumette 	int val[4] = {-1, -2, -3, -4};
6746e4b8273SHubert Chaumette 	int matches = 0;
6756e4b8273SHubert Chaumette 	u16 mask;
6766e4b8273SHubert Chaumette 	u16 maxval;
6776e4b8273SHubert Chaumette 	u16 newval;
6786e4b8273SHubert Chaumette 	int i;
6796e4b8273SHubert Chaumette 
6806e4b8273SHubert Chaumette 	for (i = 0; i < numfields; i++)
6816e4b8273SHubert Chaumette 		if (!of_property_read_u32(of_node, field[i], val + i))
6826e4b8273SHubert Chaumette 			matches++;
6836e4b8273SHubert Chaumette 
6846e4b8273SHubert Chaumette 	if (!matches)
6856e4b8273SHubert Chaumette 		return 0;
6866e4b8273SHubert Chaumette 
687bcf3440cSOleksij Rempel 	*update |= true;
688bcf3440cSOleksij Rempel 
6896e4b8273SHubert Chaumette 	if (matches < numfields)
6909b420effSHeiner Kallweit 		newval = phy_read_mmd(phydev, 2, reg);
6916e4b8273SHubert Chaumette 	else
6926e4b8273SHubert Chaumette 		newval = 0;
6936e4b8273SHubert Chaumette 
6946e4b8273SHubert Chaumette 	maxval = (field_sz == 4) ? 0xf : 0x1f;
6956e4b8273SHubert Chaumette 	for (i = 0; i < numfields; i++)
6966e4b8273SHubert Chaumette 		if (val[i] != -(i + 1)) {
6976e4b8273SHubert Chaumette 			mask = 0xffff;
6986e4b8273SHubert Chaumette 			mask ^= maxval << (field_sz * i);
6996e4b8273SHubert Chaumette 			newval = (newval & mask) |
7006e4b8273SHubert Chaumette 				(((val[i] / KSZ9031_PS_TO_REG) & maxval)
7016e4b8273SHubert Chaumette 					<< (field_sz * i));
7026e4b8273SHubert Chaumette 		}
7036e4b8273SHubert Chaumette 
7049b420effSHeiner Kallweit 	return phy_write_mmd(phydev, 2, reg, newval);
7056e4b8273SHubert Chaumette }
7066e4b8273SHubert Chaumette 
707a0da456bSMax Uvarov /* Center KSZ9031RNX FLP timing at 16ms. */
7086270e1aeSJaeden Amero static int ksz9031_center_flp_timing(struct phy_device *phydev)
7096270e1aeSJaeden Amero {
7106270e1aeSJaeden Amero 	int result;
7116270e1aeSJaeden Amero 
7129b420effSHeiner Kallweit 	result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI,
7139b420effSHeiner Kallweit 			       0x0006);
714a0da456bSMax Uvarov 	if (result)
715a0da456bSMax Uvarov 		return result;
716a0da456bSMax Uvarov 
7179b420effSHeiner Kallweit 	result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO,
7189b420effSHeiner Kallweit 			       0x1A80);
7196270e1aeSJaeden Amero 	if (result)
7206270e1aeSJaeden Amero 		return result;
7216270e1aeSJaeden Amero 
7226270e1aeSJaeden Amero 	return genphy_restart_aneg(phydev);
7236270e1aeSJaeden Amero }
7246270e1aeSJaeden Amero 
725af70c1f9SMike Looijmans /* Enable energy-detect power-down mode */
726af70c1f9SMike Looijmans static int ksz9031_enable_edpd(struct phy_device *phydev)
727af70c1f9SMike Looijmans {
728af70c1f9SMike Looijmans 	int reg;
729af70c1f9SMike Looijmans 
7309b420effSHeiner Kallweit 	reg = phy_read_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD);
731af70c1f9SMike Looijmans 	if (reg < 0)
732af70c1f9SMike Looijmans 		return reg;
7339b420effSHeiner Kallweit 	return phy_write_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD,
734af70c1f9SMike Looijmans 			     reg | MII_KSZ9031RN_EDPD_ENABLE);
735af70c1f9SMike Looijmans }
736af70c1f9SMike Looijmans 
737bcf3440cSOleksij Rempel static int ksz9031_config_rgmii_delay(struct phy_device *phydev)
738bcf3440cSOleksij Rempel {
739bcf3440cSOleksij Rempel 	u16 rx, tx, rx_clk, tx_clk;
740bcf3440cSOleksij Rempel 	int ret;
741bcf3440cSOleksij Rempel 
742bcf3440cSOleksij Rempel 	switch (phydev->interface) {
743bcf3440cSOleksij Rempel 	case PHY_INTERFACE_MODE_RGMII:
744bcf3440cSOleksij Rempel 		tx = TX_ND;
745bcf3440cSOleksij Rempel 		tx_clk = TX_CLK_ND;
746bcf3440cSOleksij Rempel 		rx = RX_ND;
747bcf3440cSOleksij Rempel 		rx_clk = RX_CLK_ND;
748bcf3440cSOleksij Rempel 		break;
749bcf3440cSOleksij Rempel 	case PHY_INTERFACE_MODE_RGMII_ID:
750bcf3440cSOleksij Rempel 		tx = TX_ID;
751bcf3440cSOleksij Rempel 		tx_clk = TX_CLK_ID;
752bcf3440cSOleksij Rempel 		rx = RX_ID;
753bcf3440cSOleksij Rempel 		rx_clk = RX_CLK_ID;
754bcf3440cSOleksij Rempel 		break;
755bcf3440cSOleksij Rempel 	case PHY_INTERFACE_MODE_RGMII_RXID:
756bcf3440cSOleksij Rempel 		tx = TX_ND;
757bcf3440cSOleksij Rempel 		tx_clk = TX_CLK_ND;
758bcf3440cSOleksij Rempel 		rx = RX_ID;
759bcf3440cSOleksij Rempel 		rx_clk = RX_CLK_ID;
760bcf3440cSOleksij Rempel 		break;
761bcf3440cSOleksij Rempel 	case PHY_INTERFACE_MODE_RGMII_TXID:
762bcf3440cSOleksij Rempel 		tx = TX_ID;
763bcf3440cSOleksij Rempel 		tx_clk = TX_CLK_ID;
764bcf3440cSOleksij Rempel 		rx = RX_ND;
765bcf3440cSOleksij Rempel 		rx_clk = RX_CLK_ND;
766bcf3440cSOleksij Rempel 		break;
767bcf3440cSOleksij Rempel 	default:
768bcf3440cSOleksij Rempel 		return 0;
769bcf3440cSOleksij Rempel 	}
770bcf3440cSOleksij Rempel 
771bcf3440cSOleksij Rempel 	ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_CONTROL_PAD_SKEW,
772bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) |
773bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx));
774bcf3440cSOleksij Rempel 	if (ret < 0)
775bcf3440cSOleksij Rempel 		return ret;
776bcf3440cSOleksij Rempel 
777bcf3440cSOleksij Rempel 	ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_RX_DATA_PAD_SKEW,
778bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_RXD3, rx) |
779bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_RXD2, rx) |
780bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_RXD1, rx) |
781bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_RXD0, rx));
782bcf3440cSOleksij Rempel 	if (ret < 0)
783bcf3440cSOleksij Rempel 		return ret;
784bcf3440cSOleksij Rempel 
785bcf3440cSOleksij Rempel 	ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_TX_DATA_PAD_SKEW,
786bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_TXD3, tx) |
787bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_TXD2, tx) |
788bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_TXD1, tx) |
789bcf3440cSOleksij Rempel 			    FIELD_PREP(MII_KSZ9031RN_TXD0, tx));
790bcf3440cSOleksij Rempel 	if (ret < 0)
791bcf3440cSOleksij Rempel 		return ret;
792bcf3440cSOleksij Rempel 
793bcf3440cSOleksij Rempel 	return phy_write_mmd(phydev, 2, MII_KSZ9031RN_CLK_PAD_SKEW,
794bcf3440cSOleksij Rempel 			     FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) |
795bcf3440cSOleksij Rempel 			     FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk));
796bcf3440cSOleksij Rempel }
797bcf3440cSOleksij Rempel 
7986e4b8273SHubert Chaumette static int ksz9031_config_init(struct phy_device *phydev)
7996e4b8273SHubert Chaumette {
800ce4f8afdSColin Ian King 	const struct device_node *of_node;
8013c9a9f7fSJaeden Amero 	static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
8023c9a9f7fSJaeden Amero 	static const char *rx_data_skews[4] = {
8036e4b8273SHubert Chaumette 		"rxd0-skew-ps", "rxd1-skew-ps",
8046e4b8273SHubert Chaumette 		"rxd2-skew-ps", "rxd3-skew-ps"
8056e4b8273SHubert Chaumette 	};
8063c9a9f7fSJaeden Amero 	static const char *tx_data_skews[4] = {
8076e4b8273SHubert Chaumette 		"txd0-skew-ps", "txd1-skew-ps",
8086e4b8273SHubert Chaumette 		"txd2-skew-ps", "txd3-skew-ps"
8096e4b8273SHubert Chaumette 	};
8103c9a9f7fSJaeden Amero 	static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
811b4c19f71SRoosen Henri 	const struct device *dev_walker;
812af70c1f9SMike Looijmans 	int result;
813af70c1f9SMike Looijmans 
814af70c1f9SMike Looijmans 	result = ksz9031_enable_edpd(phydev);
815af70c1f9SMike Looijmans 	if (result < 0)
816af70c1f9SMike Looijmans 		return result;
8176e4b8273SHubert Chaumette 
818b4c19f71SRoosen Henri 	/* The Micrel driver has a deprecated option to place phy OF
819b4c19f71SRoosen Henri 	 * properties in the MAC node. Walk up the tree of devices to
820b4c19f71SRoosen Henri 	 * find a device with an OF node.
821b4c19f71SRoosen Henri 	 */
8229d367eddSDavid S. Miller 	dev_walker = &phydev->mdio.dev;
823b4c19f71SRoosen Henri 	do {
824b4c19f71SRoosen Henri 		of_node = dev_walker->of_node;
825b4c19f71SRoosen Henri 		dev_walker = dev_walker->parent;
826b4c19f71SRoosen Henri 	} while (!of_node && dev_walker);
8276e4b8273SHubert Chaumette 
8286e4b8273SHubert Chaumette 	if (of_node) {
829bcf3440cSOleksij Rempel 		bool update = false;
830bcf3440cSOleksij Rempel 
831bcf3440cSOleksij Rempel 		if (phy_interface_is_rgmii(phydev)) {
832bcf3440cSOleksij Rempel 			result = ksz9031_config_rgmii_delay(phydev);
833bcf3440cSOleksij Rempel 			if (result < 0)
834bcf3440cSOleksij Rempel 				return result;
835bcf3440cSOleksij Rempel 		}
836bcf3440cSOleksij Rempel 
8376e4b8273SHubert Chaumette 		ksz9031_of_load_skew_values(phydev, of_node,
8386e4b8273SHubert Chaumette 				MII_KSZ9031RN_CLK_PAD_SKEW, 5,
839bcf3440cSOleksij Rempel 				clk_skews, 2, &update);
8406e4b8273SHubert Chaumette 
8416e4b8273SHubert Chaumette 		ksz9031_of_load_skew_values(phydev, of_node,
8426e4b8273SHubert Chaumette 				MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
843bcf3440cSOleksij Rempel 				control_skews, 2, &update);
8446e4b8273SHubert Chaumette 
8456e4b8273SHubert Chaumette 		ksz9031_of_load_skew_values(phydev, of_node,
8466e4b8273SHubert Chaumette 				MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
847bcf3440cSOleksij Rempel 				rx_data_skews, 4, &update);
8486e4b8273SHubert Chaumette 
8496e4b8273SHubert Chaumette 		ksz9031_of_load_skew_values(phydev, of_node,
8506e4b8273SHubert Chaumette 				MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
851bcf3440cSOleksij Rempel 				tx_data_skews, 4, &update);
852bcf3440cSOleksij Rempel 
853bcf3440cSOleksij Rempel 		if (update && phydev->interface != PHY_INTERFACE_MODE_RGMII)
854bcf3440cSOleksij Rempel 			phydev_warn(phydev,
855bcf3440cSOleksij Rempel 				    "*-skew-ps values should be used only with phy-mode = \"rgmii\"\n");
856e1b505a6SMarkus Niebel 
857e1b505a6SMarkus Niebel 		/* Silicon Errata Sheet (DS80000691D or DS80000692D):
858e1b505a6SMarkus Niebel 		 * When the device links in the 1000BASE-T slave mode only,
859e1b505a6SMarkus Niebel 		 * the optional 125MHz reference output clock (CLK125_NDO)
860e1b505a6SMarkus Niebel 		 * has wide duty cycle variation.
861e1b505a6SMarkus Niebel 		 *
862e1b505a6SMarkus Niebel 		 * The optional CLK125_NDO clock does not meet the RGMII
863e1b505a6SMarkus Niebel 		 * 45/55 percent (min/max) duty cycle requirement and therefore
864e1b505a6SMarkus Niebel 		 * cannot be used directly by the MAC side for clocking
865e1b505a6SMarkus Niebel 		 * applications that have setup/hold time requirements on
866e1b505a6SMarkus Niebel 		 * rising and falling clock edges.
867e1b505a6SMarkus Niebel 		 *
868e1b505a6SMarkus Niebel 		 * Workaround:
869e1b505a6SMarkus Niebel 		 * Force the phy to be the master to receive a stable clock
870e1b505a6SMarkus Niebel 		 * which meets the duty cycle requirement.
871e1b505a6SMarkus Niebel 		 */
872e1b505a6SMarkus Niebel 		if (of_property_read_bool(of_node, "micrel,force-master")) {
873e1b505a6SMarkus Niebel 			result = phy_read(phydev, MII_CTRL1000);
874e1b505a6SMarkus Niebel 			if (result < 0)
875e1b505a6SMarkus Niebel 				goto err_force_master;
876e1b505a6SMarkus Niebel 
877e1b505a6SMarkus Niebel 			/* enable master mode, config & prefer master */
878e1b505a6SMarkus Niebel 			result |= CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER;
879e1b505a6SMarkus Niebel 			result = phy_write(phydev, MII_CTRL1000, result);
880e1b505a6SMarkus Niebel 			if (result < 0)
881e1b505a6SMarkus Niebel 				goto err_force_master;
882e1b505a6SMarkus Niebel 		}
8836e4b8273SHubert Chaumette 	}
8846270e1aeSJaeden Amero 
8856270e1aeSJaeden Amero 	return ksz9031_center_flp_timing(phydev);
886e1b505a6SMarkus Niebel 
887e1b505a6SMarkus Niebel err_force_master:
888e1b505a6SMarkus Niebel 	phydev_err(phydev, "failed to force the phy to master mode\n");
889e1b505a6SMarkus Niebel 	return result;
8906e4b8273SHubert Chaumette }
8916e4b8273SHubert Chaumette 
892bff5b4b3SYuiko Oshino #define KSZ9131_SKEW_5BIT_MAX	2400
893bff5b4b3SYuiko Oshino #define KSZ9131_SKEW_4BIT_MAX	800
894bff5b4b3SYuiko Oshino #define KSZ9131_OFFSET		700
895bff5b4b3SYuiko Oshino #define KSZ9131_STEP		100
896bff5b4b3SYuiko Oshino 
897bff5b4b3SYuiko Oshino static int ksz9131_of_load_skew_values(struct phy_device *phydev,
898bff5b4b3SYuiko Oshino 				       struct device_node *of_node,
899bff5b4b3SYuiko Oshino 				       u16 reg, size_t field_sz,
900bff5b4b3SYuiko Oshino 				       char *field[], u8 numfields)
901bff5b4b3SYuiko Oshino {
902bff5b4b3SYuiko Oshino 	int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET),
903bff5b4b3SYuiko Oshino 		      -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)};
904bff5b4b3SYuiko Oshino 	int skewval, skewmax = 0;
905bff5b4b3SYuiko Oshino 	int matches = 0;
906bff5b4b3SYuiko Oshino 	u16 maxval;
907bff5b4b3SYuiko Oshino 	u16 newval;
908bff5b4b3SYuiko Oshino 	u16 mask;
909bff5b4b3SYuiko Oshino 	int i;
910bff5b4b3SYuiko Oshino 
911bff5b4b3SYuiko Oshino 	/* psec properties in dts should mean x pico seconds */
912bff5b4b3SYuiko Oshino 	if (field_sz == 5)
913bff5b4b3SYuiko Oshino 		skewmax = KSZ9131_SKEW_5BIT_MAX;
914bff5b4b3SYuiko Oshino 	else
915bff5b4b3SYuiko Oshino 		skewmax = KSZ9131_SKEW_4BIT_MAX;
916bff5b4b3SYuiko Oshino 
917bff5b4b3SYuiko Oshino 	for (i = 0; i < numfields; i++)
918bff5b4b3SYuiko Oshino 		if (!of_property_read_s32(of_node, field[i], &skewval)) {
919bff5b4b3SYuiko Oshino 			if (skewval < -KSZ9131_OFFSET)
920bff5b4b3SYuiko Oshino 				skewval = -KSZ9131_OFFSET;
921bff5b4b3SYuiko Oshino 			else if (skewval > skewmax)
922bff5b4b3SYuiko Oshino 				skewval = skewmax;
923bff5b4b3SYuiko Oshino 
924bff5b4b3SYuiko Oshino 			val[i] = skewval + KSZ9131_OFFSET;
925bff5b4b3SYuiko Oshino 			matches++;
926bff5b4b3SYuiko Oshino 		}
927bff5b4b3SYuiko Oshino 
928bff5b4b3SYuiko Oshino 	if (!matches)
929bff5b4b3SYuiko Oshino 		return 0;
930bff5b4b3SYuiko Oshino 
931bff5b4b3SYuiko Oshino 	if (matches < numfields)
9329b420effSHeiner Kallweit 		newval = phy_read_mmd(phydev, 2, reg);
933bff5b4b3SYuiko Oshino 	else
934bff5b4b3SYuiko Oshino 		newval = 0;
935bff5b4b3SYuiko Oshino 
936bff5b4b3SYuiko Oshino 	maxval = (field_sz == 4) ? 0xf : 0x1f;
937bff5b4b3SYuiko Oshino 	for (i = 0; i < numfields; i++)
938bff5b4b3SYuiko Oshino 		if (val[i] != -(i + 1 + KSZ9131_OFFSET)) {
939bff5b4b3SYuiko Oshino 			mask = 0xffff;
940bff5b4b3SYuiko Oshino 			mask ^= maxval << (field_sz * i);
941bff5b4b3SYuiko Oshino 			newval = (newval & mask) |
942bff5b4b3SYuiko Oshino 				(((val[i] / KSZ9131_STEP) & maxval)
943bff5b4b3SYuiko Oshino 					<< (field_sz * i));
944bff5b4b3SYuiko Oshino 		}
945bff5b4b3SYuiko Oshino 
9469b420effSHeiner Kallweit 	return phy_write_mmd(phydev, 2, reg, newval);
947bff5b4b3SYuiko Oshino }
948bff5b4b3SYuiko Oshino 
949bd734a74SPhilippe Schenker #define KSZ9131RN_MMD_COMMON_CTRL_REG	2
950bd734a74SPhilippe Schenker #define KSZ9131RN_RXC_DLL_CTRL		76
951bd734a74SPhilippe Schenker #define KSZ9131RN_TXC_DLL_CTRL		77
952bd734a74SPhilippe Schenker #define KSZ9131RN_DLL_CTRL_BYPASS	BIT_MASK(12)
953bd734a74SPhilippe Schenker #define KSZ9131RN_DLL_ENABLE_DELAY	0
954bd734a74SPhilippe Schenker #define KSZ9131RN_DLL_DISABLE_DELAY	BIT(12)
955bd734a74SPhilippe Schenker 
956bd734a74SPhilippe Schenker static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
957bd734a74SPhilippe Schenker {
958bd734a74SPhilippe Schenker 	u16 rxcdll_val, txcdll_val;
959bd734a74SPhilippe Schenker 	int ret;
960bd734a74SPhilippe Schenker 
961bd734a74SPhilippe Schenker 	switch (phydev->interface) {
962bd734a74SPhilippe Schenker 	case PHY_INTERFACE_MODE_RGMII:
963bd734a74SPhilippe Schenker 		rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
964bd734a74SPhilippe Schenker 		txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
965bd734a74SPhilippe Schenker 		break;
966bd734a74SPhilippe Schenker 	case PHY_INTERFACE_MODE_RGMII_ID:
967bd734a74SPhilippe Schenker 		rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
968bd734a74SPhilippe Schenker 		txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
969bd734a74SPhilippe Schenker 		break;
970bd734a74SPhilippe Schenker 	case PHY_INTERFACE_MODE_RGMII_RXID:
971bd734a74SPhilippe Schenker 		rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
972bd734a74SPhilippe Schenker 		txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
973bd734a74SPhilippe Schenker 		break;
974bd734a74SPhilippe Schenker 	case PHY_INTERFACE_MODE_RGMII_TXID:
975bd734a74SPhilippe Schenker 		rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY;
976bd734a74SPhilippe Schenker 		txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
977bd734a74SPhilippe Schenker 		break;
978bd734a74SPhilippe Schenker 	default:
979bd734a74SPhilippe Schenker 		return 0;
980bd734a74SPhilippe Schenker 	}
981bd734a74SPhilippe Schenker 
982bd734a74SPhilippe Schenker 	ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
983bd734a74SPhilippe Schenker 			     KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
984bd734a74SPhilippe Schenker 			     rxcdll_val);
985bd734a74SPhilippe Schenker 	if (ret < 0)
986bd734a74SPhilippe Schenker 		return ret;
987bd734a74SPhilippe Schenker 
988bd734a74SPhilippe Schenker 	return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
989bd734a74SPhilippe Schenker 			      KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS,
990bd734a74SPhilippe Schenker 			      txcdll_val);
991bd734a74SPhilippe Schenker }
992bd734a74SPhilippe Schenker 
993bff5b4b3SYuiko Oshino static int ksz9131_config_init(struct phy_device *phydev)
994bff5b4b3SYuiko Oshino {
995ce4f8afdSColin Ian King 	struct device_node *of_node;
996bff5b4b3SYuiko Oshino 	char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"};
997bff5b4b3SYuiko Oshino 	char *rx_data_skews[4] = {
998bff5b4b3SYuiko Oshino 		"rxd0-skew-psec", "rxd1-skew-psec",
999bff5b4b3SYuiko Oshino 		"rxd2-skew-psec", "rxd3-skew-psec"
1000bff5b4b3SYuiko Oshino 	};
1001bff5b4b3SYuiko Oshino 	char *tx_data_skews[4] = {
1002bff5b4b3SYuiko Oshino 		"txd0-skew-psec", "txd1-skew-psec",
1003bff5b4b3SYuiko Oshino 		"txd2-skew-psec", "txd3-skew-psec"
1004bff5b4b3SYuiko Oshino 	};
1005bff5b4b3SYuiko Oshino 	char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"};
1006bff5b4b3SYuiko Oshino 	const struct device *dev_walker;
1007bff5b4b3SYuiko Oshino 	int ret;
1008bff5b4b3SYuiko Oshino 
1009bff5b4b3SYuiko Oshino 	dev_walker = &phydev->mdio.dev;
1010bff5b4b3SYuiko Oshino 	do {
1011bff5b4b3SYuiko Oshino 		of_node = dev_walker->of_node;
1012bff5b4b3SYuiko Oshino 		dev_walker = dev_walker->parent;
1013bff5b4b3SYuiko Oshino 	} while (!of_node && dev_walker);
1014bff5b4b3SYuiko Oshino 
1015bff5b4b3SYuiko Oshino 	if (!of_node)
1016bff5b4b3SYuiko Oshino 		return 0;
1017bff5b4b3SYuiko Oshino 
1018bd734a74SPhilippe Schenker 	if (phy_interface_is_rgmii(phydev)) {
1019bd734a74SPhilippe Schenker 		ret = ksz9131_config_rgmii_delay(phydev);
1020bd734a74SPhilippe Schenker 		if (ret < 0)
1021bd734a74SPhilippe Schenker 			return ret;
1022bd734a74SPhilippe Schenker 	}
1023bd734a74SPhilippe Schenker 
1024bff5b4b3SYuiko Oshino 	ret = ksz9131_of_load_skew_values(phydev, of_node,
1025bff5b4b3SYuiko Oshino 					  MII_KSZ9031RN_CLK_PAD_SKEW, 5,
1026bff5b4b3SYuiko Oshino 					  clk_skews, 2);
1027bff5b4b3SYuiko Oshino 	if (ret < 0)
1028bff5b4b3SYuiko Oshino 		return ret;
1029bff5b4b3SYuiko Oshino 
1030bff5b4b3SYuiko Oshino 	ret = ksz9131_of_load_skew_values(phydev, of_node,
1031bff5b4b3SYuiko Oshino 					  MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
1032bff5b4b3SYuiko Oshino 					  control_skews, 2);
1033bff5b4b3SYuiko Oshino 	if (ret < 0)
1034bff5b4b3SYuiko Oshino 		return ret;
1035bff5b4b3SYuiko Oshino 
1036bff5b4b3SYuiko Oshino 	ret = ksz9131_of_load_skew_values(phydev, of_node,
1037bff5b4b3SYuiko Oshino 					  MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
1038bff5b4b3SYuiko Oshino 					  rx_data_skews, 4);
1039bff5b4b3SYuiko Oshino 	if (ret < 0)
1040bff5b4b3SYuiko Oshino 		return ret;
1041bff5b4b3SYuiko Oshino 
1042bff5b4b3SYuiko Oshino 	ret = ksz9131_of_load_skew_values(phydev, of_node,
1043bff5b4b3SYuiko Oshino 					  MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
1044bff5b4b3SYuiko Oshino 					  tx_data_skews, 4);
1045bff5b4b3SYuiko Oshino 	if (ret < 0)
1046bff5b4b3SYuiko Oshino 		return ret;
1047bff5b4b3SYuiko Oshino 
1048bff5b4b3SYuiko Oshino 	return 0;
1049bff5b4b3SYuiko Oshino }
1050bff5b4b3SYuiko Oshino 
105193272e07SJean-Christophe PLAGNIOL-VILLARD #define KSZ8873MLL_GLOBAL_CONTROL_4	0x06
105200aee095SJohan Hovold #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX	BIT(6)
105300aee095SJohan Hovold #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED	BIT(4)
105432d73b14SJingoo Han static int ksz8873mll_read_status(struct phy_device *phydev)
105593272e07SJean-Christophe PLAGNIOL-VILLARD {
105693272e07SJean-Christophe PLAGNIOL-VILLARD 	int regval;
105793272e07SJean-Christophe PLAGNIOL-VILLARD 
105893272e07SJean-Christophe PLAGNIOL-VILLARD 	/* dummy read */
105993272e07SJean-Christophe PLAGNIOL-VILLARD 	regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
106093272e07SJean-Christophe PLAGNIOL-VILLARD 
106193272e07SJean-Christophe PLAGNIOL-VILLARD 	regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
106293272e07SJean-Christophe PLAGNIOL-VILLARD 
106393272e07SJean-Christophe PLAGNIOL-VILLARD 	if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
106493272e07SJean-Christophe PLAGNIOL-VILLARD 		phydev->duplex = DUPLEX_HALF;
106593272e07SJean-Christophe PLAGNIOL-VILLARD 	else
106693272e07SJean-Christophe PLAGNIOL-VILLARD 		phydev->duplex = DUPLEX_FULL;
106793272e07SJean-Christophe PLAGNIOL-VILLARD 
106893272e07SJean-Christophe PLAGNIOL-VILLARD 	if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
106993272e07SJean-Christophe PLAGNIOL-VILLARD 		phydev->speed = SPEED_10;
107093272e07SJean-Christophe PLAGNIOL-VILLARD 	else
107193272e07SJean-Christophe PLAGNIOL-VILLARD 		phydev->speed = SPEED_100;
107293272e07SJean-Christophe PLAGNIOL-VILLARD 
107393272e07SJean-Christophe PLAGNIOL-VILLARD 	phydev->link = 1;
107493272e07SJean-Christophe PLAGNIOL-VILLARD 	phydev->pause = phydev->asym_pause = 0;
107593272e07SJean-Christophe PLAGNIOL-VILLARD 
107693272e07SJean-Christophe PLAGNIOL-VILLARD 	return 0;
107793272e07SJean-Christophe PLAGNIOL-VILLARD }
107893272e07SJean-Christophe PLAGNIOL-VILLARD 
10793aed3e2aSAntoine Tenart static int ksz9031_get_features(struct phy_device *phydev)
10803aed3e2aSAntoine Tenart {
10813aed3e2aSAntoine Tenart 	int ret;
10823aed3e2aSAntoine Tenart 
10833aed3e2aSAntoine Tenart 	ret = genphy_read_abilities(phydev);
10843aed3e2aSAntoine Tenart 	if (ret < 0)
10853aed3e2aSAntoine Tenart 		return ret;
10863aed3e2aSAntoine Tenart 
10873aed3e2aSAntoine Tenart 	/* Silicon Errata Sheet (DS80000691D or DS80000692D):
10883aed3e2aSAntoine Tenart 	 * Whenever the device's Asymmetric Pause capability is set to 1,
10893aed3e2aSAntoine Tenart 	 * link-up may fail after a link-up to link-down transition.
10903aed3e2aSAntoine Tenart 	 *
1091407d8098SHans Andersson 	 * The Errata Sheet is for ksz9031, but ksz9021 has the same issue
1092407d8098SHans Andersson 	 *
10933aed3e2aSAntoine Tenart 	 * Workaround:
10943aed3e2aSAntoine Tenart 	 * Do not enable the Asymmetric Pause capability bit.
10953aed3e2aSAntoine Tenart 	 */
10963aed3e2aSAntoine Tenart 	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
10973aed3e2aSAntoine Tenart 
10983aed3e2aSAntoine Tenart 	/* We force setting the Pause capability as the core will force the
10993aed3e2aSAntoine Tenart 	 * Asymmetric Pause capability to 1 otherwise.
11003aed3e2aSAntoine Tenart 	 */
11013aed3e2aSAntoine Tenart 	linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
11023aed3e2aSAntoine Tenart 
11033aed3e2aSAntoine Tenart 	return 0;
11043aed3e2aSAntoine Tenart }
11053aed3e2aSAntoine Tenart 
1106d2fd719bSNathan Sullivan static int ksz9031_read_status(struct phy_device *phydev)
1107d2fd719bSNathan Sullivan {
1108d2fd719bSNathan Sullivan 	int err;
1109d2fd719bSNathan Sullivan 	int regval;
1110d2fd719bSNathan Sullivan 
1111d2fd719bSNathan Sullivan 	err = genphy_read_status(phydev);
1112d2fd719bSNathan Sullivan 	if (err)
1113d2fd719bSNathan Sullivan 		return err;
1114d2fd719bSNathan Sullivan 
1115d2fd719bSNathan Sullivan 	/* Make sure the PHY is not broken. Read idle error count,
1116d2fd719bSNathan Sullivan 	 * and reset the PHY if it is maxed out.
1117d2fd719bSNathan Sullivan 	 */
1118d2fd719bSNathan Sullivan 	regval = phy_read(phydev, MII_STAT1000);
1119d2fd719bSNathan Sullivan 	if ((regval & 0xFF) == 0xFF) {
1120d2fd719bSNathan Sullivan 		phy_init_hw(phydev);
1121d2fd719bSNathan Sullivan 		phydev->link = 0;
1122b866203dSZach Brown 		if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev))
1123b866203dSZach Brown 			phydev->drv->config_intr(phydev);
1124c1a8d0a3SGrygorii Strashko 		return genphy_config_aneg(phydev);
1125d2fd719bSNathan Sullivan 	}
1126d2fd719bSNathan Sullivan 
1127d2fd719bSNathan Sullivan 	return 0;
1128d2fd719bSNathan Sullivan }
1129d2fd719bSNathan Sullivan 
113093272e07SJean-Christophe PLAGNIOL-VILLARD static int ksz8873mll_config_aneg(struct phy_device *phydev)
113193272e07SJean-Christophe PLAGNIOL-VILLARD {
113293272e07SJean-Christophe PLAGNIOL-VILLARD 	return 0;
113393272e07SJean-Christophe PLAGNIOL-VILLARD }
113493272e07SJean-Christophe PLAGNIOL-VILLARD 
113552939393SOleksij Rempel static int ksz886x_config_mdix(struct phy_device *phydev, u8 ctrl)
113652939393SOleksij Rempel {
113752939393SOleksij Rempel 	u16 val;
113852939393SOleksij Rempel 
113952939393SOleksij Rempel 	switch (ctrl) {
114052939393SOleksij Rempel 	case ETH_TP_MDI:
114152939393SOleksij Rempel 		val = KSZ886X_BMCR_DISABLE_AUTO_MDIX;
114252939393SOleksij Rempel 		break;
114352939393SOleksij Rempel 	case ETH_TP_MDI_X:
114452939393SOleksij Rempel 		/* Note: The naming of the bit KSZ886X_BMCR_FORCE_MDI is bit
114552939393SOleksij Rempel 		 * counter intuitive, the "-X" in "1 = Force MDI" in the data
114652939393SOleksij Rempel 		 * sheet seems to be missing:
114752939393SOleksij Rempel 		 * 1 = Force MDI (sic!) (transmit on RX+/RX- pins)
114852939393SOleksij Rempel 		 * 0 = Normal operation (transmit on TX+/TX- pins)
114952939393SOleksij Rempel 		 */
115052939393SOleksij Rempel 		val = KSZ886X_BMCR_DISABLE_AUTO_MDIX | KSZ886X_BMCR_FORCE_MDI;
115152939393SOleksij Rempel 		break;
115252939393SOleksij Rempel 	case ETH_TP_MDI_AUTO:
115352939393SOleksij Rempel 		val = 0;
115452939393SOleksij Rempel 		break;
115552939393SOleksij Rempel 	default:
115652939393SOleksij Rempel 		return 0;
115752939393SOleksij Rempel 	}
115852939393SOleksij Rempel 
115952939393SOleksij Rempel 	return phy_modify(phydev, MII_BMCR,
116052939393SOleksij Rempel 			  KSZ886X_BMCR_HP_MDIX | KSZ886X_BMCR_FORCE_MDI |
116152939393SOleksij Rempel 			  KSZ886X_BMCR_DISABLE_AUTO_MDIX,
116252939393SOleksij Rempel 			  KSZ886X_BMCR_HP_MDIX | val);
116352939393SOleksij Rempel }
116452939393SOleksij Rempel 
116552939393SOleksij Rempel static int ksz886x_config_aneg(struct phy_device *phydev)
116652939393SOleksij Rempel {
116752939393SOleksij Rempel 	int ret;
116852939393SOleksij Rempel 
116952939393SOleksij Rempel 	ret = genphy_config_aneg(phydev);
117052939393SOleksij Rempel 	if (ret)
117152939393SOleksij Rempel 		return ret;
117252939393SOleksij Rempel 
117352939393SOleksij Rempel 	/* The MDI-X configuration is automatically changed by the PHY after
117452939393SOleksij Rempel 	 * switching from autoneg off to on. So, take MDI-X configuration under
117552939393SOleksij Rempel 	 * own control and set it after autoneg configuration was done.
117652939393SOleksij Rempel 	 */
117752939393SOleksij Rempel 	return ksz886x_config_mdix(phydev, phydev->mdix_ctrl);
117852939393SOleksij Rempel }
117952939393SOleksij Rempel 
118052939393SOleksij Rempel static int ksz886x_mdix_update(struct phy_device *phydev)
118152939393SOleksij Rempel {
118252939393SOleksij Rempel 	int ret;
118352939393SOleksij Rempel 
118452939393SOleksij Rempel 	ret = phy_read(phydev, MII_BMCR);
118552939393SOleksij Rempel 	if (ret < 0)
118652939393SOleksij Rempel 		return ret;
118752939393SOleksij Rempel 
118852939393SOleksij Rempel 	if (ret & KSZ886X_BMCR_DISABLE_AUTO_MDIX) {
118952939393SOleksij Rempel 		if (ret & KSZ886X_BMCR_FORCE_MDI)
119052939393SOleksij Rempel 			phydev->mdix_ctrl = ETH_TP_MDI_X;
119152939393SOleksij Rempel 		else
119252939393SOleksij Rempel 			phydev->mdix_ctrl = ETH_TP_MDI;
119352939393SOleksij Rempel 	} else {
119452939393SOleksij Rempel 		phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
119552939393SOleksij Rempel 	}
119652939393SOleksij Rempel 
119752939393SOleksij Rempel 	ret = phy_read(phydev, MII_KSZPHY_CTRL);
119852939393SOleksij Rempel 	if (ret < 0)
119952939393SOleksij Rempel 		return ret;
120052939393SOleksij Rempel 
120152939393SOleksij Rempel 	/* Same reverse logic as KSZ886X_BMCR_FORCE_MDI */
120252939393SOleksij Rempel 	if (ret & KSZ886X_CTRL_MDIX_STAT)
120352939393SOleksij Rempel 		phydev->mdix = ETH_TP_MDI_X;
120452939393SOleksij Rempel 	else
120552939393SOleksij Rempel 		phydev->mdix = ETH_TP_MDI;
120652939393SOleksij Rempel 
120752939393SOleksij Rempel 	return 0;
120852939393SOleksij Rempel }
120952939393SOleksij Rempel 
121052939393SOleksij Rempel static int ksz886x_read_status(struct phy_device *phydev)
121152939393SOleksij Rempel {
121252939393SOleksij Rempel 	int ret;
121352939393SOleksij Rempel 
121452939393SOleksij Rempel 	ret = ksz886x_mdix_update(phydev);
121552939393SOleksij Rempel 	if (ret < 0)
121652939393SOleksij Rempel 		return ret;
121752939393SOleksij Rempel 
121852939393SOleksij Rempel 	return genphy_read_status(phydev);
121952939393SOleksij Rempel }
122052939393SOleksij Rempel 
12212b2427d0SAndrew Lunn static int kszphy_get_sset_count(struct phy_device *phydev)
12222b2427d0SAndrew Lunn {
12232b2427d0SAndrew Lunn 	return ARRAY_SIZE(kszphy_hw_stats);
12242b2427d0SAndrew Lunn }
12252b2427d0SAndrew Lunn 
12262b2427d0SAndrew Lunn static void kszphy_get_strings(struct phy_device *phydev, u8 *data)
12272b2427d0SAndrew Lunn {
12282b2427d0SAndrew Lunn 	int i;
12292b2427d0SAndrew Lunn 
12302b2427d0SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) {
123155f53567SFlorian Fainelli 		strlcpy(data + i * ETH_GSTRING_LEN,
12322b2427d0SAndrew Lunn 			kszphy_hw_stats[i].string, ETH_GSTRING_LEN);
12332b2427d0SAndrew Lunn 	}
12342b2427d0SAndrew Lunn }
12352b2427d0SAndrew Lunn 
12362b2427d0SAndrew Lunn static u64 kszphy_get_stat(struct phy_device *phydev, int i)
12372b2427d0SAndrew Lunn {
12382b2427d0SAndrew Lunn 	struct kszphy_hw_stat stat = kszphy_hw_stats[i];
12392b2427d0SAndrew Lunn 	struct kszphy_priv *priv = phydev->priv;
1240321b4d4bSAndrew Lunn 	int val;
1241321b4d4bSAndrew Lunn 	u64 ret;
12422b2427d0SAndrew Lunn 
12432b2427d0SAndrew Lunn 	val = phy_read(phydev, stat.reg);
12442b2427d0SAndrew Lunn 	if (val < 0) {
12456c3442f5SJisheng Zhang 		ret = U64_MAX;
12462b2427d0SAndrew Lunn 	} else {
12472b2427d0SAndrew Lunn 		val = val & ((1 << stat.bits) - 1);
12482b2427d0SAndrew Lunn 		priv->stats[i] += val;
1249321b4d4bSAndrew Lunn 		ret = priv->stats[i];
12502b2427d0SAndrew Lunn 	}
12512b2427d0SAndrew Lunn 
1252321b4d4bSAndrew Lunn 	return ret;
12532b2427d0SAndrew Lunn }
12542b2427d0SAndrew Lunn 
12552b2427d0SAndrew Lunn static void kszphy_get_stats(struct phy_device *phydev,
12562b2427d0SAndrew Lunn 			     struct ethtool_stats *stats, u64 *data)
12572b2427d0SAndrew Lunn {
12582b2427d0SAndrew Lunn 	int i;
12592b2427d0SAndrew Lunn 
12602b2427d0SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++)
12612b2427d0SAndrew Lunn 		data[i] = kszphy_get_stat(phydev, i);
12622b2427d0SAndrew Lunn }
12632b2427d0SAndrew Lunn 
1264836384d2SWenyou Yang static int kszphy_suspend(struct phy_device *phydev)
1265836384d2SWenyou Yang {
1266836384d2SWenyou Yang 	/* Disable PHY Interrupts */
1267836384d2SWenyou Yang 	if (phy_interrupt_is_valid(phydev)) {
1268836384d2SWenyou Yang 		phydev->interrupts = PHY_INTERRUPT_DISABLED;
1269836384d2SWenyou Yang 		if (phydev->drv->config_intr)
1270836384d2SWenyou Yang 			phydev->drv->config_intr(phydev);
1271836384d2SWenyou Yang 	}
1272836384d2SWenyou Yang 
1273836384d2SWenyou Yang 	return genphy_suspend(phydev);
1274836384d2SWenyou Yang }
1275836384d2SWenyou Yang 
1276f5aba91dSAlexandre Belloni static int kszphy_resume(struct phy_device *phydev)
1277f5aba91dSAlexandre Belloni {
127879e498a9SLeonard Crestez 	int ret;
127979e498a9SLeonard Crestez 
1280836384d2SWenyou Yang 	genphy_resume(phydev);
1281f5aba91dSAlexandre Belloni 
12826110dff7SOleksij Rempel 	/* After switching from power-down to normal mode, an internal global
12836110dff7SOleksij Rempel 	 * reset is automatically generated. Wait a minimum of 1 ms before
12846110dff7SOleksij Rempel 	 * read/write access to the PHY registers.
12856110dff7SOleksij Rempel 	 */
12866110dff7SOleksij Rempel 	usleep_range(1000, 2000);
12876110dff7SOleksij Rempel 
128879e498a9SLeonard Crestez 	ret = kszphy_config_reset(phydev);
128979e498a9SLeonard Crestez 	if (ret)
129079e498a9SLeonard Crestez 		return ret;
129179e498a9SLeonard Crestez 
1292836384d2SWenyou Yang 	/* Enable PHY Interrupts */
1293836384d2SWenyou Yang 	if (phy_interrupt_is_valid(phydev)) {
1294836384d2SWenyou Yang 		phydev->interrupts = PHY_INTERRUPT_ENABLED;
1295836384d2SWenyou Yang 		if (phydev->drv->config_intr)
1296836384d2SWenyou Yang 			phydev->drv->config_intr(phydev);
1297836384d2SWenyou Yang 	}
1298f5aba91dSAlexandre Belloni 
1299f5aba91dSAlexandre Belloni 	return 0;
1300f5aba91dSAlexandre Belloni }
1301f5aba91dSAlexandre Belloni 
1302e6a423a8SJohan Hovold static int kszphy_probe(struct phy_device *phydev)
1303e6a423a8SJohan Hovold {
1304e6a423a8SJohan Hovold 	const struct kszphy_type *type = phydev->drv->driver_data;
1305e5a03bfdSAndrew Lunn 	const struct device_node *np = phydev->mdio.dev.of_node;
1306e6a423a8SJohan Hovold 	struct kszphy_priv *priv;
130763f44b2bSJohan Hovold 	struct clk *clk;
1308e7a792e9SJohan Hovold 	int ret;
1309e6a423a8SJohan Hovold 
1310e5a03bfdSAndrew Lunn 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
1311e6a423a8SJohan Hovold 	if (!priv)
1312e6a423a8SJohan Hovold 		return -ENOMEM;
1313e6a423a8SJohan Hovold 
1314e6a423a8SJohan Hovold 	phydev->priv = priv;
1315e6a423a8SJohan Hovold 
1316e6a423a8SJohan Hovold 	priv->type = type;
1317e6a423a8SJohan Hovold 
1318e7a792e9SJohan Hovold 	if (type->led_mode_reg) {
1319e7a792e9SJohan Hovold 		ret = of_property_read_u32(np, "micrel,led-mode",
1320e7a792e9SJohan Hovold 				&priv->led_mode);
1321e7a792e9SJohan Hovold 		if (ret)
1322e7a792e9SJohan Hovold 			priv->led_mode = -1;
1323e7a792e9SJohan Hovold 
1324e7a792e9SJohan Hovold 		if (priv->led_mode > 3) {
132572ba48beSAndrew Lunn 			phydev_err(phydev, "invalid led mode: 0x%02x\n",
1326e7a792e9SJohan Hovold 				   priv->led_mode);
1327e7a792e9SJohan Hovold 			priv->led_mode = -1;
1328e7a792e9SJohan Hovold 		}
1329e7a792e9SJohan Hovold 	} else {
1330e7a792e9SJohan Hovold 		priv->led_mode = -1;
1331e7a792e9SJohan Hovold 	}
1332e7a792e9SJohan Hovold 
1333e5a03bfdSAndrew Lunn 	clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref");
1334bced8701SNiklas Cassel 	/* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
1335bced8701SNiklas Cassel 	if (!IS_ERR_OR_NULL(clk)) {
13361fadee0cSSascha Hauer 		unsigned long rate = clk_get_rate(clk);
133786dc1342SJohan Hovold 		bool rmii_ref_clk_sel_25_mhz;
13381fadee0cSSascha Hauer 
133963f44b2bSJohan Hovold 		priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
134086dc1342SJohan Hovold 		rmii_ref_clk_sel_25_mhz = of_property_read_bool(np,
134186dc1342SJohan Hovold 				"micrel,rmii-reference-clock-select-25-mhz");
134263f44b2bSJohan Hovold 
13431fadee0cSSascha Hauer 		if (rate > 24500000 && rate < 25500000) {
134486dc1342SJohan Hovold 			priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz;
13451fadee0cSSascha Hauer 		} else if (rate > 49500000 && rate < 50500000) {
134686dc1342SJohan Hovold 			priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
13471fadee0cSSascha Hauer 		} else {
134872ba48beSAndrew Lunn 			phydev_err(phydev, "Clock rate out of range: %ld\n",
134972ba48beSAndrew Lunn 				   rate);
13501fadee0cSSascha Hauer 			return -EINVAL;
13511fadee0cSSascha Hauer 		}
13521fadee0cSSascha Hauer 	}
13531fadee0cSSascha Hauer 
13544217a64eSMichael Walle 	if (ksz8041_fiber_mode(phydev))
13554217a64eSMichael Walle 		phydev->port = PORT_FIBRE;
13564217a64eSMichael Walle 
135763f44b2bSJohan Hovold 	/* Support legacy board-file configuration */
135863f44b2bSJohan Hovold 	if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
135963f44b2bSJohan Hovold 		priv->rmii_ref_clk_sel = true;
136063f44b2bSJohan Hovold 		priv->rmii_ref_clk_sel_val = true;
136163f44b2bSJohan Hovold 	}
136263f44b2bSJohan Hovold 
136363f44b2bSJohan Hovold 	return 0;
13641fadee0cSSascha Hauer }
13651fadee0cSSascha Hauer 
1366d5bf9071SChristian Hohnstaedt static struct phy_driver ksphy_driver[] = {
1367d5bf9071SChristian Hohnstaedt {
136851f932c4SChoi, David 	.phy_id		= PHY_ID_KS8737,
1369f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
137051f932c4SChoi, David 	.name		= "Micrel KS8737",
1371dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1372c6f9575cSJohan Hovold 	.driver_data	= &ks8737_type,
1373d0507009SDavid J. Choi 	.config_init	= kszphy_config_init,
1374c6f9575cSJohan Hovold 	.config_intr	= kszphy_config_intr,
137559ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
13761a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
13771a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1378d5bf9071SChristian Hohnstaedt }, {
1379212ea99aSMarek Vasut 	.phy_id		= PHY_ID_KSZ8021,
1380212ea99aSMarek Vasut 	.phy_id_mask	= 0x00ffffff,
13817ab59dc1SDavid J. Choi 	.name		= "Micrel KSZ8021 or KSZ8031",
1382dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1383e6a423a8SJohan Hovold 	.driver_data	= &ksz8021_type,
138463f44b2bSJohan Hovold 	.probe		= kszphy_probe,
1385d0e1df9cSJohan Hovold 	.config_init	= kszphy_config_init,
1386212ea99aSMarek Vasut 	.config_intr	= kszphy_config_intr,
138759ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
13882b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
13892b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
13902b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
13911a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
13921a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1393212ea99aSMarek Vasut }, {
1394b818d1a7SHector Palacios 	.phy_id		= PHY_ID_KSZ8031,
1395b818d1a7SHector Palacios 	.phy_id_mask	= 0x00ffffff,
1396b818d1a7SHector Palacios 	.name		= "Micrel KSZ8031",
1397dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1398e6a423a8SJohan Hovold 	.driver_data	= &ksz8021_type,
139963f44b2bSJohan Hovold 	.probe		= kszphy_probe,
1400d0e1df9cSJohan Hovold 	.config_init	= kszphy_config_init,
1401b818d1a7SHector Palacios 	.config_intr	= kszphy_config_intr,
140259ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14032b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
14042b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
14052b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
14061a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
14071a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1408b818d1a7SHector Palacios }, {
1409510d573fSMarek Vasut 	.phy_id		= PHY_ID_KSZ8041,
1410f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
1411510d573fSMarek Vasut 	.name		= "Micrel KSZ8041",
1412dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1413e6a423a8SJohan Hovold 	.driver_data	= &ksz8041_type,
1414e6a423a8SJohan Hovold 	.probe		= kszphy_probe,
141577501a79SPhilipp Zabel 	.config_init	= ksz8041_config_init,
141677501a79SPhilipp Zabel 	.config_aneg	= ksz8041_config_aneg,
141751f932c4SChoi, David 	.config_intr	= kszphy_config_intr,
141859ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14192b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
14202b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
14212b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
14221a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
14231a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1424d5bf9071SChristian Hohnstaedt }, {
14254bd7b512SSergei Shtylyov 	.phy_id		= PHY_ID_KSZ8041RNLI,
1426f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
14274bd7b512SSergei Shtylyov 	.name		= "Micrel KSZ8041RNLI",
1428dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1429e6a423a8SJohan Hovold 	.driver_data	= &ksz8041_type,
1430e6a423a8SJohan Hovold 	.probe		= kszphy_probe,
1431e6a423a8SJohan Hovold 	.config_init	= kszphy_config_init,
14324bd7b512SSergei Shtylyov 	.config_intr	= kszphy_config_intr,
143359ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14342b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
14352b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
14362b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
14374bd7b512SSergei Shtylyov 	.suspend	= genphy_suspend,
14384bd7b512SSergei Shtylyov 	.resume		= genphy_resume,
14394bd7b512SSergei Shtylyov }, {
1440510d573fSMarek Vasut 	.name		= "Micrel KSZ8051",
1441dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1442e6a423a8SJohan Hovold 	.driver_data	= &ksz8051_type,
1443e6a423a8SJohan Hovold 	.probe		= kszphy_probe,
144463f44b2bSJohan Hovold 	.config_init	= kszphy_config_init,
144551f932c4SChoi, David 	.config_intr	= kszphy_config_intr,
144659ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14472b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
14482b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
14492b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
14508b95599cSMarek Vasut 	.match_phy_device = ksz8051_match_phy_device,
14511a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
14521a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1453d5bf9071SChristian Hohnstaedt }, {
1454510d573fSMarek Vasut 	.phy_id		= PHY_ID_KSZ8001,
1455510d573fSMarek Vasut 	.name		= "Micrel KSZ8001 or KS8721",
1456ecd5a323SAlexander Stein 	.phy_id_mask	= 0x00fffffc,
1457dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1458e6a423a8SJohan Hovold 	.driver_data	= &ksz8041_type,
1459e6a423a8SJohan Hovold 	.probe		= kszphy_probe,
1460e6a423a8SJohan Hovold 	.config_init	= kszphy_config_init,
146151f932c4SChoi, David 	.config_intr	= kszphy_config_intr,
146259ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14632b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
14642b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
14652b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
14661a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
14671a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1468d5bf9071SChristian Hohnstaedt }, {
14697ab59dc1SDavid J. Choi 	.phy_id		= PHY_ID_KSZ8081,
14707ab59dc1SDavid J. Choi 	.name		= "Micrel KSZ8081 or KSZ8091",
1471f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
1472dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1473e6a423a8SJohan Hovold 	.driver_data	= &ksz8081_type,
1474e6a423a8SJohan Hovold 	.probe		= kszphy_probe,
14757a1d8390SAntoine Tenart 	.config_init	= ksz8081_config_init,
1476764d31caSChristian Melki 	.soft_reset	= genphy_soft_reset,
1477*f873f112SOleksij Rempel 	.config_aneg	= ksz8081_config_aneg,
1478*f873f112SOleksij Rempel 	.read_status	= ksz8081_read_status,
14797ab59dc1SDavid J. Choi 	.config_intr	= kszphy_config_intr,
148059ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14812b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
14822b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
14832b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
1484836384d2SWenyou Yang 	.suspend	= kszphy_suspend,
1485f5aba91dSAlexandre Belloni 	.resume		= kszphy_resume,
14867ab59dc1SDavid J. Choi }, {
14877ab59dc1SDavid J. Choi 	.phy_id		= PHY_ID_KSZ8061,
14887ab59dc1SDavid J. Choi 	.name		= "Micrel KSZ8061",
1489f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
1490dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1491232ba3a5SRajasingh Thavamani 	.config_init	= ksz8061_config_init,
14927ab59dc1SDavid J. Choi 	.config_intr	= kszphy_config_intr,
149359ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
14941a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
14951a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
14967ab59dc1SDavid J. Choi }, {
1497d0507009SDavid J. Choi 	.phy_id		= PHY_ID_KSZ9021,
149848d7d0adSJason Wang 	.phy_id_mask	= 0x000ffffe,
1499d0507009SDavid J. Choi 	.name		= "Micrel KSZ9021 Gigabit PHY",
1500dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
1501c6f9575cSJohan Hovold 	.driver_data	= &ksz9021_type,
1502bfe72442SGrygorii Strashko 	.probe		= kszphy_probe,
1503407d8098SHans Andersson 	.get_features	= ksz9031_get_features,
1504954c3967SSean Cross 	.config_init	= ksz9021_config_init,
1505c6f9575cSJohan Hovold 	.config_intr	= kszphy_config_intr,
150659ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
15072b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
15082b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
15092b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
15101a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
15111a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
1512c846a2b7SKevin Hao 	.read_mmd	= genphy_read_mmd_unsupported,
1513c846a2b7SKevin Hao 	.write_mmd	= genphy_write_mmd_unsupported,
151493272e07SJean-Christophe PLAGNIOL-VILLARD }, {
15157ab59dc1SDavid J. Choi 	.phy_id		= PHY_ID_KSZ9031,
1516f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
15177ab59dc1SDavid J. Choi 	.name		= "Micrel KSZ9031 Gigabit PHY",
1518c6f9575cSJohan Hovold 	.driver_data	= &ksz9021_type,
1519bfe72442SGrygorii Strashko 	.probe		= kszphy_probe,
15203aed3e2aSAntoine Tenart 	.get_features	= ksz9031_get_features,
15216e4b8273SHubert Chaumette 	.config_init	= ksz9031_config_init,
15221d16073aSHeiner Kallweit 	.soft_reset	= genphy_soft_reset,
1523d2fd719bSNathan Sullivan 	.read_status	= ksz9031_read_status,
1524c6f9575cSJohan Hovold 	.config_intr	= kszphy_config_intr,
152559ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
15262b2427d0SAndrew Lunn 	.get_sset_count = kszphy_get_sset_count,
15272b2427d0SAndrew Lunn 	.get_strings	= kszphy_get_strings,
15282b2427d0SAndrew Lunn 	.get_stats	= kszphy_get_stats,
15291a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
1530f64f1482SXander Huff 	.resume		= kszphy_resume,
15317ab59dc1SDavid J. Choi }, {
15321623ad8eSDivya Koppera 	.phy_id		= PHY_ID_LAN8814,
15331623ad8eSDivya Koppera 	.phy_id_mask	= MICREL_PHY_ID_MASK,
15341623ad8eSDivya Koppera 	.name		= "Microchip INDY Gigabit Quad PHY",
15351623ad8eSDivya Koppera 	.driver_data	= &ksz9021_type,
15361623ad8eSDivya Koppera 	.probe		= kszphy_probe,
15371623ad8eSDivya Koppera 	.soft_reset	= genphy_soft_reset,
15381623ad8eSDivya Koppera 	.read_status	= ksz9031_read_status,
15391623ad8eSDivya Koppera 	.get_sset_count	= kszphy_get_sset_count,
15401623ad8eSDivya Koppera 	.get_strings	= kszphy_get_strings,
15411623ad8eSDivya Koppera 	.get_stats	= kszphy_get_stats,
15421623ad8eSDivya Koppera 	.suspend	= genphy_suspend,
15431623ad8eSDivya Koppera 	.resume		= kszphy_resume,
15441623ad8eSDivya Koppera }, {
1545bff5b4b3SYuiko Oshino 	.phy_id		= PHY_ID_KSZ9131,
1546bff5b4b3SYuiko Oshino 	.phy_id_mask	= MICREL_PHY_ID_MASK,
1547bff5b4b3SYuiko Oshino 	.name		= "Microchip KSZ9131 Gigabit PHY",
1548dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
1549bff5b4b3SYuiko Oshino 	.driver_data	= &ksz9021_type,
1550bff5b4b3SYuiko Oshino 	.probe		= kszphy_probe,
1551bff5b4b3SYuiko Oshino 	.config_init	= ksz9131_config_init,
1552bff5b4b3SYuiko Oshino 	.config_intr	= kszphy_config_intr,
155359ca4e58SIoana Ciornei 	.handle_interrupt = kszphy_handle_interrupt,
1554bff5b4b3SYuiko Oshino 	.get_sset_count = kszphy_get_sset_count,
1555bff5b4b3SYuiko Oshino 	.get_strings	= kszphy_get_strings,
1556bff5b4b3SYuiko Oshino 	.get_stats	= kszphy_get_stats,
1557bff5b4b3SYuiko Oshino 	.suspend	= genphy_suspend,
1558bff5b4b3SYuiko Oshino 	.resume		= kszphy_resume,
1559bff5b4b3SYuiko Oshino }, {
156093272e07SJean-Christophe PLAGNIOL-VILLARD 	.phy_id		= PHY_ID_KSZ8873MLL,
1561f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
156293272e07SJean-Christophe PLAGNIOL-VILLARD 	.name		= "Micrel KSZ8873MLL Switch",
1563dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
156493272e07SJean-Christophe PLAGNIOL-VILLARD 	.config_init	= kszphy_config_init,
156593272e07SJean-Christophe PLAGNIOL-VILLARD 	.config_aneg	= ksz8873mll_config_aneg,
156693272e07SJean-Christophe PLAGNIOL-VILLARD 	.read_status	= ksz8873mll_read_status,
15671a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
15681a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
15697ab59dc1SDavid J. Choi }, {
15707ab59dc1SDavid J. Choi 	.phy_id		= PHY_ID_KSZ886X,
1571f893a99eSFabio Estevam 	.phy_id_mask	= MICREL_PHY_ID_MASK,
1572ab36a3a2SMarek Vasut 	.name		= "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch",
1573dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
15747ab59dc1SDavid J. Choi 	.config_init	= kszphy_config_init,
157552939393SOleksij Rempel 	.config_aneg	= ksz886x_config_aneg,
157652939393SOleksij Rempel 	.read_status	= ksz886x_read_status,
15771a5465f5SPatrice Vilchez 	.suspend	= genphy_suspend,
15781a5465f5SPatrice Vilchez 	.resume		= genphy_resume,
15799d162ed6SSean Nyekjaer }, {
15801d951ba3SMarek Vasut 	.name		= "Micrel KSZ87XX Switch",
1581dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
15829d162ed6SSean Nyekjaer 	.config_init	= kszphy_config_init,
15839d162ed6SSean Nyekjaer 	.config_aneg	= ksz8873mll_config_aneg,
15849d162ed6SSean Nyekjaer 	.read_status	= ksz8873mll_read_status,
15858b95599cSMarek Vasut 	.match_phy_device = ksz8795_match_phy_device,
15869d162ed6SSean Nyekjaer 	.suspend	= genphy_suspend,
15879d162ed6SSean Nyekjaer 	.resume		= genphy_resume,
1588fc3973a1SWoojung Huh }, {
1589fc3973a1SWoojung Huh 	.phy_id		= PHY_ID_KSZ9477,
1590fc3973a1SWoojung Huh 	.phy_id_mask	= MICREL_PHY_ID_MASK,
1591fc3973a1SWoojung Huh 	.name		= "Microchip KSZ9477",
1592dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
1593fc3973a1SWoojung Huh 	.config_init	= kszphy_config_init,
1594fc3973a1SWoojung Huh 	.suspend	= genphy_suspend,
1595fc3973a1SWoojung Huh 	.resume		= genphy_resume,
1596d5bf9071SChristian Hohnstaedt } };
1597d0507009SDavid J. Choi 
159850fd7150SJohan Hovold module_phy_driver(ksphy_driver);
1599d0507009SDavid J. Choi 
1600d0507009SDavid J. Choi MODULE_DESCRIPTION("Micrel PHY driver");
1601d0507009SDavid J. Choi MODULE_AUTHOR("David J. Choi");
1602d0507009SDavid J. Choi MODULE_LICENSE("GPL");
160352a60ed2SDavid S. Miller 
1604cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused micrel_tbl[] = {
160548d7d0adSJason Wang 	{ PHY_ID_KSZ9021, 0x000ffffe },
1606f893a99eSFabio Estevam 	{ PHY_ID_KSZ9031, MICREL_PHY_ID_MASK },
1607bff5b4b3SYuiko Oshino 	{ PHY_ID_KSZ9131, MICREL_PHY_ID_MASK },
1608ecd5a323SAlexander Stein 	{ PHY_ID_KSZ8001, 0x00fffffc },
1609f893a99eSFabio Estevam 	{ PHY_ID_KS8737, MICREL_PHY_ID_MASK },
1610212ea99aSMarek Vasut 	{ PHY_ID_KSZ8021, 0x00ffffff },
1611b818d1a7SHector Palacios 	{ PHY_ID_KSZ8031, 0x00ffffff },
1612f893a99eSFabio Estevam 	{ PHY_ID_KSZ8041, MICREL_PHY_ID_MASK },
1613f893a99eSFabio Estevam 	{ PHY_ID_KSZ8051, MICREL_PHY_ID_MASK },
1614f893a99eSFabio Estevam 	{ PHY_ID_KSZ8061, MICREL_PHY_ID_MASK },
1615f893a99eSFabio Estevam 	{ PHY_ID_KSZ8081, MICREL_PHY_ID_MASK },
1616f893a99eSFabio Estevam 	{ PHY_ID_KSZ8873MLL, MICREL_PHY_ID_MASK },
1617f893a99eSFabio Estevam 	{ PHY_ID_KSZ886X, MICREL_PHY_ID_MASK },
16181623ad8eSDivya Koppera 	{ PHY_ID_LAN8814, MICREL_PHY_ID_MASK },
161952a60ed2SDavid S. Miller 	{ }
162052a60ed2SDavid S. Miller };
162152a60ed2SDavid S. Miller 
162252a60ed2SDavid S. Miller MODULE_DEVICE_TABLE(mdio, micrel_tbl);
1623