xref: /openbmc/linux/drivers/net/phy/davicom.c (revision 7ce05074)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * drivers/net/phy/davicom.c
4  *
5  * Driver for Davicom PHYs
6  *
7  * Author: Andy Fleming
8  *
9  * Copyright (c) 2004 Freescale Semiconductor, Inc.
10  */
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13 #include <linux/errno.h>
14 #include <linux/unistd.h>
15 #include <linux/interrupt.h>
16 #include <linux/init.h>
17 #include <linux/delay.h>
18 #include <linux/netdevice.h>
19 #include <linux/etherdevice.h>
20 #include <linux/skbuff.h>
21 #include <linux/spinlock.h>
22 #include <linux/mm.h>
23 #include <linux/module.h>
24 #include <linux/mii.h>
25 #include <linux/ethtool.h>
26 #include <linux/phy.h>
27 
28 #include <asm/io.h>
29 #include <asm/irq.h>
30 #include <linux/uaccess.h>
31 
32 #define MII_DM9161_SCR		0x10
33 #define MII_DM9161_SCR_INIT	0x0610
34 #define MII_DM9161_SCR_RMII	0x0100
35 
36 /* DM9161 Interrupt Register */
37 #define MII_DM9161_INTR	0x15
38 #define MII_DM9161_INTR_PEND		0x8000
39 #define MII_DM9161_INTR_DPLX_MASK	0x0800
40 #define MII_DM9161_INTR_SPD_MASK	0x0400
41 #define MII_DM9161_INTR_LINK_MASK	0x0200
42 #define MII_DM9161_INTR_MASK		0x0100
43 #define MII_DM9161_INTR_DPLX_CHANGE	0x0010
44 #define MII_DM9161_INTR_SPD_CHANGE	0x0008
45 #define MII_DM9161_INTR_LINK_CHANGE	0x0004
46 #define MII_DM9161_INTR_INIT		0x0000
47 #define MII_DM9161_INTR_STOP	\
48 	(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK |	\
49 	 MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
50 #define MII_DM9161_INTR_CHANGE	\
51 	(MII_DM9161_INTR_DPLX_CHANGE | \
52 	 MII_DM9161_INTR_SPD_CHANGE | \
53 	 MII_DM9161_INTR_LINK_CHANGE)
54 
55 /* DM9161 10BT Configuration/Status */
56 #define MII_DM9161_10BTCSR	0x12
57 #define MII_DM9161_10BTCSR_INIT	0x7800
58 
59 MODULE_DESCRIPTION("Davicom PHY driver");
60 MODULE_AUTHOR("Andy Fleming");
61 MODULE_LICENSE("GPL");
62 
63 
64 static int dm9161_ack_interrupt(struct phy_device *phydev)
65 {
66 	int err = phy_read(phydev, MII_DM9161_INTR);
67 
68 	return (err < 0) ? err : 0;
69 }
70 
71 #define DM9161_DELAY 1
72 static int dm9161_config_intr(struct phy_device *phydev)
73 {
74 	int temp, err;
75 
76 	temp = phy_read(phydev, MII_DM9161_INTR);
77 
78 	if (temp < 0)
79 		return temp;
80 
81 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
82 		err = dm9161_ack_interrupt(phydev);
83 		if (err)
84 			return err;
85 
86 		temp &= ~(MII_DM9161_INTR_STOP);
87 		err = phy_write(phydev, MII_DM9161_INTR, temp);
88 	} else {
89 		temp |= MII_DM9161_INTR_STOP;
90 		err = phy_write(phydev, MII_DM9161_INTR, temp);
91 		if (err)
92 			return err;
93 
94 		err = dm9161_ack_interrupt(phydev);
95 	}
96 
97 	return err;
98 }
99 
100 static irqreturn_t dm9161_handle_interrupt(struct phy_device *phydev)
101 {
102 	int irq_status;
103 
104 	irq_status = phy_read(phydev, MII_DM9161_INTR);
105 	if (irq_status < 0) {
106 		phy_error(phydev);
107 		return IRQ_NONE;
108 	}
109 
110 	if (!(irq_status & MII_DM9161_INTR_CHANGE))
111 		return IRQ_NONE;
112 
113 	phy_trigger_machine(phydev);
114 
115 	return IRQ_HANDLED;
116 }
117 
118 static int dm9161_config_aneg(struct phy_device *phydev)
119 {
120 	int err;
121 
122 	/* Isolate the PHY */
123 	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
124 
125 	if (err < 0)
126 		return err;
127 
128 	/* Configure the new settings */
129 	err = genphy_config_aneg(phydev);
130 
131 	if (err < 0)
132 		return err;
133 
134 	return 0;
135 }
136 
137 static int dm9161_config_init(struct phy_device *phydev)
138 {
139 	int err, temp;
140 
141 	/* Isolate the PHY */
142 	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
143 
144 	if (err < 0)
145 		return err;
146 
147 	switch (phydev->interface) {
148 	case PHY_INTERFACE_MODE_MII:
149 		temp = MII_DM9161_SCR_INIT;
150 		break;
151 	case PHY_INTERFACE_MODE_RMII:
152 		temp =  MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII;
153 		break;
154 	default:
155 		return -EINVAL;
156 	}
157 
158 	/* Do not bypass the scrambler/descrambler */
159 	err = phy_write(phydev, MII_DM9161_SCR, temp);
160 	if (err < 0)
161 		return err;
162 
163 	/* Clear 10BTCSR to default */
164 	err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
165 
166 	if (err < 0)
167 		return err;
168 
169 	/* Reconnect the PHY, and enable Autonegotiation */
170 	return phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
171 }
172 
173 static struct phy_driver dm91xx_driver[] = {
174 {
175 	.phy_id		= 0x0181b880,
176 	.name		= "Davicom DM9161E",
177 	.phy_id_mask	= 0x0ffffff0,
178 	/* PHY_BASIC_FEATURES */
179 	.config_init	= dm9161_config_init,
180 	.config_aneg	= dm9161_config_aneg,
181 	.config_intr	= dm9161_config_intr,
182 	.handle_interrupt = dm9161_handle_interrupt,
183 }, {
184 	.phy_id		= 0x0181b8b0,
185 	.name		= "Davicom DM9161B/C",
186 	.phy_id_mask	= 0x0ffffff0,
187 	/* PHY_BASIC_FEATURES */
188 	.config_init	= dm9161_config_init,
189 	.config_aneg	= dm9161_config_aneg,
190 	.config_intr	= dm9161_config_intr,
191 	.handle_interrupt = dm9161_handle_interrupt,
192 }, {
193 	.phy_id		= 0x0181b8a0,
194 	.name		= "Davicom DM9161A",
195 	.phy_id_mask	= 0x0ffffff0,
196 	/* PHY_BASIC_FEATURES */
197 	.config_init	= dm9161_config_init,
198 	.config_aneg	= dm9161_config_aneg,
199 	.config_intr	= dm9161_config_intr,
200 	.handle_interrupt = dm9161_handle_interrupt,
201 }, {
202 	.phy_id		= 0x00181b80,
203 	.name		= "Davicom DM9131",
204 	.phy_id_mask	= 0x0ffffff0,
205 	/* PHY_BASIC_FEATURES */
206 	.config_intr	= dm9161_config_intr,
207 	.handle_interrupt = dm9161_handle_interrupt,
208 } };
209 
210 module_phy_driver(dm91xx_driver);
211 
212 static struct mdio_device_id __maybe_unused davicom_tbl[] = {
213 	{ 0x0181b880, 0x0ffffff0 },
214 	{ 0x0181b8b0, 0x0ffffff0 },
215 	{ 0x0181b8a0, 0x0ffffff0 },
216 	{ 0x00181b80, 0x0ffffff0 },
217 	{ }
218 };
219 
220 MODULE_DEVICE_TABLE(mdio, davicom_tbl);
221