xref: /openbmc/linux/drivers/net/phy/davicom.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
200db8189SAndy Fleming /*
300db8189SAndy Fleming  * drivers/net/phy/davicom.c
400db8189SAndy Fleming  *
500db8189SAndy Fleming  * Driver for Davicom PHYs
600db8189SAndy Fleming  *
700db8189SAndy Fleming  * Author: Andy Fleming
800db8189SAndy Fleming  *
900db8189SAndy Fleming  * Copyright (c) 2004 Freescale Semiconductor, Inc.
1000db8189SAndy Fleming  */
1100db8189SAndy Fleming #include <linux/kernel.h>
1200db8189SAndy Fleming #include <linux/string.h>
1300db8189SAndy Fleming #include <linux/errno.h>
1400db8189SAndy Fleming #include <linux/unistd.h>
1500db8189SAndy Fleming #include <linux/interrupt.h>
1600db8189SAndy Fleming #include <linux/init.h>
1700db8189SAndy Fleming #include <linux/delay.h>
1800db8189SAndy Fleming #include <linux/netdevice.h>
1900db8189SAndy Fleming #include <linux/etherdevice.h>
2000db8189SAndy Fleming #include <linux/skbuff.h>
2100db8189SAndy Fleming #include <linux/spinlock.h>
2200db8189SAndy Fleming #include <linux/mm.h>
2300db8189SAndy Fleming #include <linux/module.h>
2400db8189SAndy Fleming #include <linux/mii.h>
2500db8189SAndy Fleming #include <linux/ethtool.h>
2600db8189SAndy Fleming #include <linux/phy.h>
2700db8189SAndy Fleming 
2800db8189SAndy Fleming #include <asm/io.h>
2900db8189SAndy Fleming #include <asm/irq.h>
307c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
3100db8189SAndy Fleming 
3200db8189SAndy Fleming #define MII_DM9161_SCR		0x10
3300db8189SAndy Fleming #define MII_DM9161_SCR_INIT	0x0610
348b7c1664Sfrederic Rodo #define MII_DM9161_SCR_RMII	0x0100
3500db8189SAndy Fleming 
3600db8189SAndy Fleming /* DM9161 Interrupt Register */
3700db8189SAndy Fleming #define MII_DM9161_INTR	0x15
3800db8189SAndy Fleming #define MII_DM9161_INTR_PEND		0x8000
3900db8189SAndy Fleming #define MII_DM9161_INTR_DPLX_MASK	0x0800
4000db8189SAndy Fleming #define MII_DM9161_INTR_SPD_MASK	0x0400
4100db8189SAndy Fleming #define MII_DM9161_INTR_LINK_MASK	0x0200
4200db8189SAndy Fleming #define MII_DM9161_INTR_MASK		0x0100
4300db8189SAndy Fleming #define MII_DM9161_INTR_DPLX_CHANGE	0x0010
4400db8189SAndy Fleming #define MII_DM9161_INTR_SPD_CHANGE	0x0008
4500db8189SAndy Fleming #define MII_DM9161_INTR_LINK_CHANGE	0x0004
4600db8189SAndy Fleming #define MII_DM9161_INTR_INIT		0x0000
4700db8189SAndy Fleming #define MII_DM9161_INTR_STOP	\
48*3bdee6a8SWenpeng Liang 	(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK |	\
49*3bdee6a8SWenpeng Liang 	 MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
50e954631cSIoana Ciornei #define MII_DM9161_INTR_CHANGE	\
51e954631cSIoana Ciornei 	(MII_DM9161_INTR_DPLX_CHANGE | \
52e954631cSIoana Ciornei 	 MII_DM9161_INTR_SPD_CHANGE | \
53e954631cSIoana Ciornei 	 MII_DM9161_INTR_LINK_CHANGE)
5400db8189SAndy Fleming 
5500db8189SAndy Fleming /* DM9161 10BT Configuration/Status */
5600db8189SAndy Fleming #define MII_DM9161_10BTCSR	0x12
5700db8189SAndy Fleming #define MII_DM9161_10BTCSR_INIT	0x7800
5800db8189SAndy Fleming 
5900db8189SAndy Fleming MODULE_DESCRIPTION("Davicom PHY driver");
6000db8189SAndy Fleming MODULE_AUTHOR("Andy Fleming");
6100db8189SAndy Fleming MODULE_LICENSE("GPL");
6200db8189SAndy Fleming 
6300db8189SAndy Fleming 
dm9161_ack_interrupt(struct phy_device * phydev)640d65cc18SIoana Ciornei static int dm9161_ack_interrupt(struct phy_device *phydev)
650d65cc18SIoana Ciornei {
660d65cc18SIoana Ciornei 	int err = phy_read(phydev, MII_DM9161_INTR);
670d65cc18SIoana Ciornei 
680d65cc18SIoana Ciornei 	return (err < 0) ? err : 0;
690d65cc18SIoana Ciornei }
700d65cc18SIoana Ciornei 
7100db8189SAndy Fleming #define DM9161_DELAY 1
dm9161_config_intr(struct phy_device * phydev)7200db8189SAndy Fleming static int dm9161_config_intr(struct phy_device *phydev)
7300db8189SAndy Fleming {
740d65cc18SIoana Ciornei 	int temp, err;
7500db8189SAndy Fleming 
7600db8189SAndy Fleming 	temp = phy_read(phydev, MII_DM9161_INTR);
7700db8189SAndy Fleming 
7800db8189SAndy Fleming 	if (temp < 0)
7900db8189SAndy Fleming 		return temp;
8000db8189SAndy Fleming 
810d65cc18SIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
820d65cc18SIoana Ciornei 		err = dm9161_ack_interrupt(phydev);
830d65cc18SIoana Ciornei 		if (err)
840d65cc18SIoana Ciornei 			return err;
850d65cc18SIoana Ciornei 
8600db8189SAndy Fleming 		temp &= ~(MII_DM9161_INTR_STOP);
870d65cc18SIoana Ciornei 		err = phy_write(phydev, MII_DM9161_INTR, temp);
880d65cc18SIoana Ciornei 	} else {
8900db8189SAndy Fleming 		temp |= MII_DM9161_INTR_STOP;
900d65cc18SIoana Ciornei 		err = phy_write(phydev, MII_DM9161_INTR, temp);
910d65cc18SIoana Ciornei 		if (err)
920d65cc18SIoana Ciornei 			return err;
9300db8189SAndy Fleming 
940d65cc18SIoana Ciornei 		err = dm9161_ack_interrupt(phydev);
950d65cc18SIoana Ciornei 	}
9600db8189SAndy Fleming 
970d65cc18SIoana Ciornei 	return err;
9800db8189SAndy Fleming }
9900db8189SAndy Fleming 
dm9161_handle_interrupt(struct phy_device * phydev)100e954631cSIoana Ciornei static irqreturn_t dm9161_handle_interrupt(struct phy_device *phydev)
101e954631cSIoana Ciornei {
102e954631cSIoana Ciornei 	int irq_status;
103e954631cSIoana Ciornei 
104e954631cSIoana Ciornei 	irq_status = phy_read(phydev, MII_DM9161_INTR);
105e954631cSIoana Ciornei 	if (irq_status < 0) {
106e954631cSIoana Ciornei 		phy_error(phydev);
107e954631cSIoana Ciornei 		return IRQ_NONE;
108e954631cSIoana Ciornei 	}
109e954631cSIoana Ciornei 
110e954631cSIoana Ciornei 	if (!(irq_status & MII_DM9161_INTR_CHANGE))
111e954631cSIoana Ciornei 		return IRQ_NONE;
112e954631cSIoana Ciornei 
113e954631cSIoana Ciornei 	phy_trigger_machine(phydev);
114e954631cSIoana Ciornei 
115e954631cSIoana Ciornei 	return IRQ_HANDLED;
116e954631cSIoana Ciornei }
117e954631cSIoana Ciornei 
dm9161_config_aneg(struct phy_device * phydev)11800db8189SAndy Fleming static int dm9161_config_aneg(struct phy_device *phydev)
11900db8189SAndy Fleming {
12000db8189SAndy Fleming 	int err;
12100db8189SAndy Fleming 
12200db8189SAndy Fleming 	/* Isolate the PHY */
12300db8189SAndy Fleming 	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
12400db8189SAndy Fleming 
12500db8189SAndy Fleming 	if (err < 0)
12600db8189SAndy Fleming 		return err;
12700db8189SAndy Fleming 
12800db8189SAndy Fleming 	/* Configure the new settings */
12900db8189SAndy Fleming 	err = genphy_config_aneg(phydev);
13000db8189SAndy Fleming 
13100db8189SAndy Fleming 	if (err < 0)
13200db8189SAndy Fleming 		return err;
13300db8189SAndy Fleming 
13400db8189SAndy Fleming 	return 0;
13500db8189SAndy Fleming }
13600db8189SAndy Fleming 
dm9161_config_init(struct phy_device * phydev)13700db8189SAndy Fleming static int dm9161_config_init(struct phy_device *phydev)
13800db8189SAndy Fleming {
1398b7c1664Sfrederic Rodo 	int err, temp;
14000db8189SAndy Fleming 
14100db8189SAndy Fleming 	/* Isolate the PHY */
14200db8189SAndy Fleming 	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
14300db8189SAndy Fleming 
14400db8189SAndy Fleming 	if (err < 0)
14500db8189SAndy Fleming 		return err;
14600db8189SAndy Fleming 
1478b7c1664Sfrederic Rodo 	switch (phydev->interface) {
1488b7c1664Sfrederic Rodo 	case PHY_INTERFACE_MODE_MII:
1498b7c1664Sfrederic Rodo 		temp = MII_DM9161_SCR_INIT;
1508b7c1664Sfrederic Rodo 		break;
1518b7c1664Sfrederic Rodo 	case PHY_INTERFACE_MODE_RMII:
1528b7c1664Sfrederic Rodo 		temp =  MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII;
1538b7c1664Sfrederic Rodo 		break;
1548b7c1664Sfrederic Rodo 	default:
1558b7c1664Sfrederic Rodo 		return -EINVAL;
1568b7c1664Sfrederic Rodo 	}
15700db8189SAndy Fleming 
1588b7c1664Sfrederic Rodo 	/* Do not bypass the scrambler/descrambler */
1598b7c1664Sfrederic Rodo 	err = phy_write(phydev, MII_DM9161_SCR, temp);
16000db8189SAndy Fleming 	if (err < 0)
16100db8189SAndy Fleming 		return err;
16200db8189SAndy Fleming 
16300db8189SAndy Fleming 	/* Clear 10BTCSR to default */
16400db8189SAndy Fleming 	err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
16500db8189SAndy Fleming 
16600db8189SAndy Fleming 	if (err < 0)
16700db8189SAndy Fleming 		return err;
16800db8189SAndy Fleming 
16900db8189SAndy Fleming 	/* Reconnect the PHY, and enable Autonegotiation */
1708bc47ec6SSrinivas Kandagatla 	return phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
17100db8189SAndy Fleming }
17200db8189SAndy Fleming 
173d5bf9071SChristian Hohnstaedt static struct phy_driver dm91xx_driver[] = {
174d5bf9071SChristian Hohnstaedt {
17500db8189SAndy Fleming 	.phy_id		= 0x0181b880,
17600db8189SAndy Fleming 	.name		= "Davicom DM9161E",
17700db8189SAndy Fleming 	.phy_id_mask	= 0x0ffffff0,
178dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
17900db8189SAndy Fleming 	.config_init	= dm9161_config_init,
18000db8189SAndy Fleming 	.config_aneg	= dm9161_config_aneg,
18163f71dd0SJoachim Eastwood 	.config_intr	= dm9161_config_intr,
182e954631cSIoana Ciornei 	.handle_interrupt = dm9161_handle_interrupt,
183d5bf9071SChristian Hohnstaedt }, {
1841059261eSGustavo Zacarias 	.phy_id		= 0x0181b8b0,
1851059261eSGustavo Zacarias 	.name		= "Davicom DM9161B/C",
1861059261eSGustavo Zacarias 	.phy_id_mask	= 0x0ffffff0,
187dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1881059261eSGustavo Zacarias 	.config_init	= dm9161_config_init,
1891059261eSGustavo Zacarias 	.config_aneg	= dm9161_config_aneg,
1901059261eSGustavo Zacarias 	.config_intr	= dm9161_config_intr,
191e954631cSIoana Ciornei 	.handle_interrupt = dm9161_handle_interrupt,
1921059261eSGustavo Zacarias }, {
19312414db1SKim Phillips 	.phy_id		= 0x0181b8a0,
19412414db1SKim Phillips 	.name		= "Davicom DM9161A",
19512414db1SKim Phillips 	.phy_id_mask	= 0x0ffffff0,
196dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
19712414db1SKim Phillips 	.config_init	= dm9161_config_init,
19812414db1SKim Phillips 	.config_aneg	= dm9161_config_aneg,
19963f71dd0SJoachim Eastwood 	.config_intr	= dm9161_config_intr,
200e954631cSIoana Ciornei 	.handle_interrupt = dm9161_handle_interrupt,
201d5bf9071SChristian Hohnstaedt }, {
20200db8189SAndy Fleming 	.phy_id		= 0x00181b80,
20300db8189SAndy Fleming 	.name		= "Davicom DM9131",
20400db8189SAndy Fleming 	.phy_id_mask	= 0x0ffffff0,
205dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
20600db8189SAndy Fleming 	.config_intr	= dm9161_config_intr,
207e954631cSIoana Ciornei 	.handle_interrupt = dm9161_handle_interrupt,
208d5bf9071SChristian Hohnstaedt } };
20900db8189SAndy Fleming 
21050fd7150SJohan Hovold module_phy_driver(dm91xx_driver);
2114e4f10f6SDavid Woodhouse 
212cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused davicom_tbl[] = {
2134e4f10f6SDavid Woodhouse 	{ 0x0181b880, 0x0ffffff0 },
2141059261eSGustavo Zacarias 	{ 0x0181b8b0, 0x0ffffff0 },
2154e4f10f6SDavid Woodhouse 	{ 0x0181b8a0, 0x0ffffff0 },
2164e4f10f6SDavid Woodhouse 	{ 0x00181b80, 0x0ffffff0 },
2174e4f10f6SDavid Woodhouse 	{ }
2184e4f10f6SDavid Woodhouse };
2194e4f10f6SDavid Woodhouse 
2204e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, davicom_tbl);
221