xref: /openbmc/linux/drivers/net/phy/marvell.c (revision ac8c635a)
100db8189SAndy Fleming /*
200db8189SAndy Fleming  * drivers/net/phy/marvell.c
300db8189SAndy Fleming  *
400db8189SAndy Fleming  * Driver for Marvell PHYs
500db8189SAndy Fleming  *
600db8189SAndy Fleming  * Author: Andy Fleming
700db8189SAndy Fleming  *
800db8189SAndy Fleming  * Copyright (c) 2004 Freescale Semiconductor, Inc.
900db8189SAndy Fleming  *
1000db8189SAndy Fleming  * This program is free software; you can redistribute  it and/or modify it
1100db8189SAndy Fleming  * under  the terms of  the GNU General  Public License as published by the
1200db8189SAndy Fleming  * Free Software Foundation;  either version 2 of the  License, or (at your
1300db8189SAndy Fleming  * option) any later version.
1400db8189SAndy Fleming  *
1500db8189SAndy Fleming  */
1600db8189SAndy Fleming #include <linux/kernel.h>
1700db8189SAndy Fleming #include <linux/string.h>
1800db8189SAndy Fleming #include <linux/errno.h>
1900db8189SAndy Fleming #include <linux/unistd.h>
2000db8189SAndy Fleming #include <linux/slab.h>
2100db8189SAndy Fleming #include <linux/interrupt.h>
2200db8189SAndy Fleming #include <linux/init.h>
2300db8189SAndy Fleming #include <linux/delay.h>
2400db8189SAndy Fleming #include <linux/netdevice.h>
2500db8189SAndy Fleming #include <linux/etherdevice.h>
2600db8189SAndy Fleming #include <linux/skbuff.h>
2700db8189SAndy Fleming #include <linux/spinlock.h>
2800db8189SAndy Fleming #include <linux/mm.h>
2900db8189SAndy Fleming #include <linux/module.h>
3000db8189SAndy Fleming #include <linux/mii.h>
3100db8189SAndy Fleming #include <linux/ethtool.h>
3200db8189SAndy Fleming #include <linux/phy.h>
3300db8189SAndy Fleming 
3400db8189SAndy Fleming #include <asm/io.h>
3500db8189SAndy Fleming #include <asm/irq.h>
3600db8189SAndy Fleming #include <asm/uaccess.h>
3700db8189SAndy Fleming 
3800db8189SAndy Fleming #define MII_M1011_IEVENT		0x13
3900db8189SAndy Fleming #define MII_M1011_IEVENT_CLEAR		0x0000
4000db8189SAndy Fleming 
4100db8189SAndy Fleming #define MII_M1011_IMASK			0x12
4200db8189SAndy Fleming #define MII_M1011_IMASK_INIT		0x6400
4300db8189SAndy Fleming #define MII_M1011_IMASK_CLEAR		0x0000
4400db8189SAndy Fleming 
4576884679SAndy Fleming #define MII_M1011_PHY_SCR		0x10
4676884679SAndy Fleming #define MII_M1011_PHY_SCR_AUTO_CROSS	0x0060
4776884679SAndy Fleming 
4876884679SAndy Fleming #define MII_M1145_PHY_EXT_CR		0x14
4976884679SAndy Fleming #define MII_M1145_RGMII_RX_DELAY	0x0080
5076884679SAndy Fleming #define MII_M1145_RGMII_TX_DELAY	0x0002
5176884679SAndy Fleming 
5276884679SAndy Fleming #define M1145_DEV_FLAGS_RESISTANCE	0x00000001
5376884679SAndy Fleming 
5476884679SAndy Fleming #define MII_M1111_PHY_LED_CONTROL	0x18
5576884679SAndy Fleming #define MII_M1111_PHY_LED_DIRECT	0x4100
5676884679SAndy Fleming #define MII_M1111_PHY_LED_COMBINE	0x411c
57895ee682SKim Phillips #define MII_M1111_PHY_EXT_CR		0x14
58895ee682SKim Phillips #define MII_M1111_RX_DELAY		0x80
59895ee682SKim Phillips #define MII_M1111_TX_DELAY		0x2
60895ee682SKim Phillips #define MII_M1111_PHY_EXT_SR		0x1b
61895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_MASK	0xf
62895ee682SKim Phillips #define MII_M1111_HWCFG_MODE_RGMII	0xb
634117b5beSKapil Juneja #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK	0x4
6476884679SAndy Fleming 
6500db8189SAndy Fleming MODULE_DESCRIPTION("Marvell PHY driver");
6600db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
6700db8189SAndy Fleming MODULE_LICENSE("GPL");
6800db8189SAndy Fleming 
6900db8189SAndy Fleming static int marvell_ack_interrupt(struct phy_device *phydev)
7000db8189SAndy Fleming {
7100db8189SAndy Fleming 	int err;
7200db8189SAndy Fleming 
7300db8189SAndy Fleming 	/* Clear the interrupts by reading the reg */
7400db8189SAndy Fleming 	err = phy_read(phydev, MII_M1011_IEVENT);
7500db8189SAndy Fleming 
7600db8189SAndy Fleming 	if (err < 0)
7700db8189SAndy Fleming 		return err;
7800db8189SAndy Fleming 
7900db8189SAndy Fleming 	return 0;
8000db8189SAndy Fleming }
8100db8189SAndy Fleming 
8200db8189SAndy Fleming static int marvell_config_intr(struct phy_device *phydev)
8300db8189SAndy Fleming {
8400db8189SAndy Fleming 	int err;
8500db8189SAndy Fleming 
8600db8189SAndy Fleming 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
8700db8189SAndy Fleming 		err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
8800db8189SAndy Fleming 	else
8900db8189SAndy Fleming 		err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
9000db8189SAndy Fleming 
9100db8189SAndy Fleming 	return err;
9200db8189SAndy Fleming }
9300db8189SAndy Fleming 
9400db8189SAndy Fleming static int marvell_config_aneg(struct phy_device *phydev)
9500db8189SAndy Fleming {
9600db8189SAndy Fleming 	int err;
9700db8189SAndy Fleming 
9800db8189SAndy Fleming 	/* The Marvell PHY has an errata which requires
9900db8189SAndy Fleming 	 * that certain registers get written in order
10000db8189SAndy Fleming 	 * to restart autonegotiation */
10100db8189SAndy Fleming 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
10200db8189SAndy Fleming 
10300db8189SAndy Fleming 	if (err < 0)
10400db8189SAndy Fleming 		return err;
10500db8189SAndy Fleming 
10600db8189SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x1f);
10700db8189SAndy Fleming 	if (err < 0)
10800db8189SAndy Fleming 		return err;
10900db8189SAndy Fleming 
11000db8189SAndy Fleming 	err = phy_write(phydev, 0x1e, 0x200c);
11100db8189SAndy Fleming 	if (err < 0)
11200db8189SAndy Fleming 		return err;
11300db8189SAndy Fleming 
11400db8189SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x5);
11500db8189SAndy Fleming 	if (err < 0)
11600db8189SAndy Fleming 		return err;
11700db8189SAndy Fleming 
11800db8189SAndy Fleming 	err = phy_write(phydev, 0x1e, 0);
11900db8189SAndy Fleming 	if (err < 0)
12000db8189SAndy Fleming 		return err;
12100db8189SAndy Fleming 
12200db8189SAndy Fleming 	err = phy_write(phydev, 0x1e, 0x100);
12300db8189SAndy Fleming 	if (err < 0)
12400db8189SAndy Fleming 		return err;
12500db8189SAndy Fleming 
12676884679SAndy Fleming 	err = phy_write(phydev, MII_M1011_PHY_SCR,
12776884679SAndy Fleming 			MII_M1011_PHY_SCR_AUTO_CROSS);
12876884679SAndy Fleming 	if (err < 0)
12976884679SAndy Fleming 		return err;
13076884679SAndy Fleming 
13176884679SAndy Fleming 	err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
13276884679SAndy Fleming 			MII_M1111_PHY_LED_DIRECT);
13376884679SAndy Fleming 	if (err < 0)
13476884679SAndy Fleming 		return err;
13500db8189SAndy Fleming 
13600db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
13700db8189SAndy Fleming 
13800db8189SAndy Fleming 	return err;
13900db8189SAndy Fleming }
14000db8189SAndy Fleming 
141895ee682SKim Phillips static int m88e1111_config_init(struct phy_device *phydev)
142895ee682SKim Phillips {
143895ee682SKim Phillips 	int err;
144895ee682SKim Phillips 
145895ee682SKim Phillips 	if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
146895ee682SKim Phillips 	    (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)) {
147895ee682SKim Phillips 		int temp;
148895ee682SKim Phillips 
149895ee682SKim Phillips 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
150895ee682SKim Phillips 			temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
151895ee682SKim Phillips 			if (temp < 0)
152895ee682SKim Phillips 				return temp;
153895ee682SKim Phillips 
154895ee682SKim Phillips 			temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
155895ee682SKim Phillips 
156895ee682SKim Phillips 			err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
157895ee682SKim Phillips 			if (err < 0)
158895ee682SKim Phillips 				return err;
159895ee682SKim Phillips 		}
160895ee682SKim Phillips 
161895ee682SKim Phillips 		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
162895ee682SKim Phillips 		if (temp < 0)
163895ee682SKim Phillips 			return temp;
164895ee682SKim Phillips 
165895ee682SKim Phillips 		temp &= ~(MII_M1111_HWCFG_MODE_MASK);
166895ee682SKim Phillips 		temp |= MII_M1111_HWCFG_MODE_RGMII;
167895ee682SKim Phillips 
168895ee682SKim Phillips 		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
169895ee682SKim Phillips 		if (err < 0)
170895ee682SKim Phillips 			return err;
171895ee682SKim Phillips 	}
172895ee682SKim Phillips 
1734117b5beSKapil Juneja 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
1744117b5beSKapil Juneja 		int temp;
1754117b5beSKapil Juneja 
1764117b5beSKapil Juneja 		temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
1774117b5beSKapil Juneja 		if (temp < 0)
1784117b5beSKapil Juneja 			return temp;
1794117b5beSKapil Juneja 
1804117b5beSKapil Juneja 		temp &= ~(MII_M1111_HWCFG_MODE_MASK);
1814117b5beSKapil Juneja 		temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
1824117b5beSKapil Juneja 
1834117b5beSKapil Juneja 		err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
1844117b5beSKapil Juneja 		if (err < 0)
1854117b5beSKapil Juneja 			return err;
1864117b5beSKapil Juneja 	}
1874117b5beSKapil Juneja 
188895ee682SKim Phillips 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
189895ee682SKim Phillips 	if (err < 0)
190895ee682SKim Phillips 		return err;
191895ee682SKim Phillips 
192895ee682SKim Phillips 	return 0;
193895ee682SKim Phillips }
194895ee682SKim Phillips 
19576884679SAndy Fleming static int m88e1145_config_init(struct phy_device *phydev)
19676884679SAndy Fleming {
19776884679SAndy Fleming 	int err;
19876884679SAndy Fleming 
19976884679SAndy Fleming 	/* Take care of errata E0 & E1 */
20076884679SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x001b);
20176884679SAndy Fleming 	if (err < 0)
20276884679SAndy Fleming 		return err;
20376884679SAndy Fleming 
20476884679SAndy Fleming 	err = phy_write(phydev, 0x1e, 0x418f);
20576884679SAndy Fleming 	if (err < 0)
20676884679SAndy Fleming 		return err;
20776884679SAndy Fleming 
20876884679SAndy Fleming 	err = phy_write(phydev, 0x1d, 0x0016);
20976884679SAndy Fleming 	if (err < 0)
21076884679SAndy Fleming 		return err;
21176884679SAndy Fleming 
21276884679SAndy Fleming 	err = phy_write(phydev, 0x1e, 0xa2da);
21376884679SAndy Fleming 	if (err < 0)
21476884679SAndy Fleming 		return err;
21576884679SAndy Fleming 
216895ee682SKim Phillips 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
21776884679SAndy Fleming 		int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR);
21876884679SAndy Fleming 		if (temp < 0)
21976884679SAndy Fleming 			return temp;
22076884679SAndy Fleming 
22176884679SAndy Fleming 		temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY);
22276884679SAndy Fleming 
22376884679SAndy Fleming 		err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp);
22476884679SAndy Fleming 		if (err < 0)
22576884679SAndy Fleming 			return err;
22676884679SAndy Fleming 
22776884679SAndy Fleming 		if (phydev->dev_flags & M1145_DEV_FLAGS_RESISTANCE) {
22876884679SAndy Fleming 			err = phy_write(phydev, 0x1d, 0x0012);
22976884679SAndy Fleming 			if (err < 0)
23076884679SAndy Fleming 				return err;
23176884679SAndy Fleming 
23276884679SAndy Fleming 			temp = phy_read(phydev, 0x1e);
23376884679SAndy Fleming 			if (temp < 0)
23476884679SAndy Fleming 				return temp;
23576884679SAndy Fleming 
23676884679SAndy Fleming 			temp &= 0xf03f;
23776884679SAndy Fleming 			temp |= 2 << 9;	/* 36 ohm */
23876884679SAndy Fleming 			temp |= 2 << 6;	/* 39 ohm */
23976884679SAndy Fleming 
24076884679SAndy Fleming 			err = phy_write(phydev, 0x1e, temp);
24176884679SAndy Fleming 			if (err < 0)
24276884679SAndy Fleming 				return err;
24376884679SAndy Fleming 
24476884679SAndy Fleming 			err = phy_write(phydev, 0x1d, 0x3);
24576884679SAndy Fleming 			if (err < 0)
24676884679SAndy Fleming 				return err;
24776884679SAndy Fleming 
24876884679SAndy Fleming 			err = phy_write(phydev, 0x1e, 0x8000);
24976884679SAndy Fleming 			if (err < 0)
25076884679SAndy Fleming 				return err;
25176884679SAndy Fleming 		}
25276884679SAndy Fleming 	}
25376884679SAndy Fleming 
25476884679SAndy Fleming 	return 0;
25576884679SAndy Fleming }
25600db8189SAndy Fleming 
257e5479239SOlof Johansson static struct phy_driver marvell_drivers[] = {
258e5479239SOlof Johansson 	{
25976884679SAndy Fleming 		.phy_id = 0x01410c60,
26076884679SAndy Fleming 		.phy_id_mask = 0xfffffff0,
26100db8189SAndy Fleming 		.name = "Marvell 88E1101",
26200db8189SAndy Fleming 		.features = PHY_GBIT_FEATURES,
26300db8189SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
26400db8189SAndy Fleming 		.config_aneg = &marvell_config_aneg,
26500db8189SAndy Fleming 		.read_status = &genphy_read_status,
26600db8189SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
26700db8189SAndy Fleming 		.config_intr = &marvell_config_intr,
268ac8c635aSOlof Johansson 		.driver = { .owner = THIS_MODULE },
269e5479239SOlof Johansson 	},
270e5479239SOlof Johansson 	{
27185cfb534SOlof Johansson 		.phy_id = 0x01410c90,
27285cfb534SOlof Johansson 		.phy_id_mask = 0xfffffff0,
27385cfb534SOlof Johansson 		.name = "Marvell 88E1112",
27485cfb534SOlof Johansson 		.features = PHY_GBIT_FEATURES,
27585cfb534SOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
27685cfb534SOlof Johansson 		.config_init = &m88e1111_config_init,
27785cfb534SOlof Johansson 		.config_aneg = &marvell_config_aneg,
27885cfb534SOlof Johansson 		.read_status = &genphy_read_status,
27985cfb534SOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
28085cfb534SOlof Johansson 		.config_intr = &marvell_config_intr,
281ac8c635aSOlof Johansson 		.driver = { .owner = THIS_MODULE },
28285cfb534SOlof Johansson 	},
28385cfb534SOlof Johansson 	{
28476884679SAndy Fleming 		.phy_id = 0x01410cc0,
28576884679SAndy Fleming 		.phy_id_mask = 0xfffffff0,
28676884679SAndy Fleming 		.name = "Marvell 88E1111",
28776884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
28876884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
289e5479239SOlof Johansson 		.config_init = &m88e1111_config_init,
29076884679SAndy Fleming 		.config_aneg = &marvell_config_aneg,
29176884679SAndy Fleming 		.read_status = &genphy_read_status,
29276884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
29376884679SAndy Fleming 		.config_intr = &marvell_config_intr,
294ac8c635aSOlof Johansson 		.driver = { .owner = THIS_MODULE },
295e5479239SOlof Johansson 	},
296e5479239SOlof Johansson 	{
29776884679SAndy Fleming 		.phy_id = 0x01410cd0,
29876884679SAndy Fleming 		.phy_id_mask = 0xfffffff0,
29976884679SAndy Fleming 		.name = "Marvell 88E1145",
30076884679SAndy Fleming 		.features = PHY_GBIT_FEATURES,
30176884679SAndy Fleming 		.flags = PHY_HAS_INTERRUPT,
30276884679SAndy Fleming 		.config_init = &m88e1145_config_init,
30376884679SAndy Fleming 		.config_aneg = &marvell_config_aneg,
30476884679SAndy Fleming 		.read_status = &genphy_read_status,
30576884679SAndy Fleming 		.ack_interrupt = &marvell_ack_interrupt,
30676884679SAndy Fleming 		.config_intr = &marvell_config_intr,
307ac8c635aSOlof Johansson 		.driver = { .owner = THIS_MODULE },
308ac8c635aSOlof Johansson 	},
309ac8c635aSOlof Johansson 	{
310ac8c635aSOlof Johansson 		.phy_id = 0x01410e30,
311ac8c635aSOlof Johansson 		.phy_id_mask = 0xfffffff0,
312ac8c635aSOlof Johansson 		.name = "Marvell 88E1240",
313ac8c635aSOlof Johansson 		.features = PHY_GBIT_FEATURES,
314ac8c635aSOlof Johansson 		.flags = PHY_HAS_INTERRUPT,
315ac8c635aSOlof Johansson 		.config_init = &m88e1111_config_init,
316ac8c635aSOlof Johansson 		.config_aneg = &marvell_config_aneg,
317ac8c635aSOlof Johansson 		.read_status = &genphy_read_status,
318ac8c635aSOlof Johansson 		.ack_interrupt = &marvell_ack_interrupt,
319ac8c635aSOlof Johansson 		.config_intr = &marvell_config_intr,
320ac8c635aSOlof Johansson 		.driver = { .owner = THIS_MODULE },
321ac8c635aSOlof Johansson 	},
32276884679SAndy Fleming };
32376884679SAndy Fleming 
32400db8189SAndy Fleming static int __init marvell_init(void)
32500db8189SAndy Fleming {
32676884679SAndy Fleming 	int ret;
327e5479239SOlof Johansson 	int i;
32876884679SAndy Fleming 
329e5479239SOlof Johansson 	for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) {
330e5479239SOlof Johansson 		ret = phy_driver_register(&marvell_drivers[i]);
331e5479239SOlof Johansson 
332e5479239SOlof Johansson 		if (ret) {
333e5479239SOlof Johansson 			while (i-- > 0)
334e5479239SOlof Johansson 				phy_driver_unregister(&marvell_drivers[i]);
33576884679SAndy Fleming 			return ret;
336e5479239SOlof Johansson 		}
337e5479239SOlof Johansson 	}
33876884679SAndy Fleming 
33976884679SAndy Fleming 	return 0;
34000db8189SAndy Fleming }
34100db8189SAndy Fleming 
34200db8189SAndy Fleming static void __exit marvell_exit(void)
34300db8189SAndy Fleming {
344e5479239SOlof Johansson 	int i;
345e5479239SOlof Johansson 
346e5479239SOlof Johansson 	for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++)
347e5479239SOlof Johansson 		phy_driver_unregister(&marvell_drivers[i]);
34800db8189SAndy Fleming }
34900db8189SAndy Fleming 
35000db8189SAndy Fleming module_init(marvell_init);
35100db8189SAndy Fleming module_exit(marvell_exit);
352