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