Commit | Line | Data |
---|---|---|
00db8189 AF |
1 | /* |
2 | * drivers/net/phy/davicom.c | |
3 | * | |
4 | * Driver for Davicom PHYs | |
5 | * | |
6 | * Author: Andy Fleming | |
7 | * | |
8 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | */ | |
00db8189 | 16 | #include <linux/kernel.h> |
00db8189 AF |
17 | #include <linux/string.h> |
18 | #include <linux/errno.h> | |
19 | #include <linux/unistd.h> | |
00db8189 AF |
20 | #include <linux/interrupt.h> |
21 | #include <linux/init.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/netdevice.h> | |
24 | #include <linux/etherdevice.h> | |
25 | #include <linux/skbuff.h> | |
26 | #include <linux/spinlock.h> | |
27 | #include <linux/mm.h> | |
28 | #include <linux/module.h> | |
00db8189 AF |
29 | #include <linux/mii.h> |
30 | #include <linux/ethtool.h> | |
31 | #include <linux/phy.h> | |
32 | ||
33 | #include <asm/io.h> | |
34 | #include <asm/irq.h> | |
35 | #include <asm/uaccess.h> | |
36 | ||
37 | #define MII_DM9161_SCR 0x10 | |
38 | #define MII_DM9161_SCR_INIT 0x0610 | |
8b7c1664 | 39 | #define MII_DM9161_SCR_RMII 0x0100 |
00db8189 AF |
40 | |
41 | /* DM9161 Interrupt Register */ | |
42 | #define MII_DM9161_INTR 0x15 | |
43 | #define MII_DM9161_INTR_PEND 0x8000 | |
44 | #define MII_DM9161_INTR_DPLX_MASK 0x0800 | |
45 | #define MII_DM9161_INTR_SPD_MASK 0x0400 | |
46 | #define MII_DM9161_INTR_LINK_MASK 0x0200 | |
47 | #define MII_DM9161_INTR_MASK 0x0100 | |
48 | #define MII_DM9161_INTR_DPLX_CHANGE 0x0010 | |
49 | #define MII_DM9161_INTR_SPD_CHANGE 0x0008 | |
50 | #define MII_DM9161_INTR_LINK_CHANGE 0x0004 | |
51 | #define MII_DM9161_INTR_INIT 0x0000 | |
52 | #define MII_DM9161_INTR_STOP \ | |
53 | (MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \ | |
54 | | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK) | |
55 | ||
56 | /* DM9161 10BT Configuration/Status */ | |
57 | #define MII_DM9161_10BTCSR 0x12 | |
58 | #define MII_DM9161_10BTCSR_INIT 0x7800 | |
59 | ||
60 | MODULE_DESCRIPTION("Davicom PHY driver"); | |
61 | MODULE_AUTHOR("Andy Fleming"); | |
62 | MODULE_LICENSE("GPL"); | |
63 | ||
64 | ||
65 | #define DM9161_DELAY 1 | |
66 | static int dm9161_config_intr(struct phy_device *phydev) | |
67 | { | |
68 | int temp; | |
69 | ||
70 | temp = phy_read(phydev, MII_DM9161_INTR); | |
71 | ||
72 | if (temp < 0) | |
73 | return temp; | |
74 | ||
a60e7e1e | 75 | if (PHY_INTERRUPT_ENABLED == phydev->interrupts) |
00db8189 AF |
76 | temp &= ~(MII_DM9161_INTR_STOP); |
77 | else | |
78 | temp |= MII_DM9161_INTR_STOP; | |
79 | ||
80 | temp = phy_write(phydev, MII_DM9161_INTR, temp); | |
81 | ||
82 | return temp; | |
83 | } | |
84 | ||
85 | static int dm9161_config_aneg(struct phy_device *phydev) | |
86 | { | |
87 | int err; | |
88 | ||
89 | /* Isolate the PHY */ | |
90 | err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE); | |
91 | ||
92 | if (err < 0) | |
93 | return err; | |
94 | ||
95 | /* Configure the new settings */ | |
96 | err = genphy_config_aneg(phydev); | |
97 | ||
98 | if (err < 0) | |
99 | return err; | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | static int dm9161_config_init(struct phy_device *phydev) | |
105 | { | |
8b7c1664 | 106 | int err, temp; |
00db8189 AF |
107 | |
108 | /* Isolate the PHY */ | |
109 | err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE); | |
110 | ||
111 | if (err < 0) | |
112 | return err; | |
113 | ||
8b7c1664 R |
114 | switch (phydev->interface) { |
115 | case PHY_INTERFACE_MODE_MII: | |
116 | temp = MII_DM9161_SCR_INIT; | |
117 | break; | |
118 | case PHY_INTERFACE_MODE_RMII: | |
119 | temp = MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII; | |
120 | break; | |
121 | default: | |
122 | return -EINVAL; | |
123 | } | |
00db8189 | 124 | |
8b7c1664 R |
125 | /* Do not bypass the scrambler/descrambler */ |
126 | err = phy_write(phydev, MII_DM9161_SCR, temp); | |
00db8189 AF |
127 | if (err < 0) |
128 | return err; | |
129 | ||
130 | /* Clear 10BTCSR to default */ | |
131 | err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT); | |
132 | ||
133 | if (err < 0) | |
134 | return err; | |
135 | ||
136 | /* Reconnect the PHY, and enable Autonegotiation */ | |
8bc47ec6 | 137 | return phy_write(phydev, MII_BMCR, BMCR_ANENABLE); |
00db8189 AF |
138 | } |
139 | ||
140 | static int dm9161_ack_interrupt(struct phy_device *phydev) | |
141 | { | |
142 | int err = phy_read(phydev, MII_DM9161_INTR); | |
143 | ||
144 | return (err < 0) ? err : 0; | |
145 | } | |
146 | ||
d5bf9071 CH |
147 | static struct phy_driver dm91xx_driver[] = { |
148 | { | |
00db8189 AF |
149 | .phy_id = 0x0181b880, |
150 | .name = "Davicom DM9161E", | |
151 | .phy_id_mask = 0x0ffffff0, | |
152 | .features = PHY_BASIC_FEATURES, | |
63f71dd0 | 153 | .flags = PHY_HAS_INTERRUPT, |
00db8189 AF |
154 | .config_init = dm9161_config_init, |
155 | .config_aneg = dm9161_config_aneg, | |
156 | .read_status = genphy_read_status, | |
63f71dd0 JE |
157 | .ack_interrupt = dm9161_ack_interrupt, |
158 | .config_intr = dm9161_config_intr, | |
12414db1 | 159 | .driver = { .owner = THIS_MODULE,}, |
d5bf9071 | 160 | }, { |
12414db1 KP |
161 | .phy_id = 0x0181b8a0, |
162 | .name = "Davicom DM9161A", | |
163 | .phy_id_mask = 0x0ffffff0, | |
164 | .features = PHY_BASIC_FEATURES, | |
63f71dd0 | 165 | .flags = PHY_HAS_INTERRUPT, |
12414db1 KP |
166 | .config_init = dm9161_config_init, |
167 | .config_aneg = dm9161_config_aneg, | |
168 | .read_status = genphy_read_status, | |
63f71dd0 JE |
169 | .ack_interrupt = dm9161_ack_interrupt, |
170 | .config_intr = dm9161_config_intr, | |
12414db1 | 171 | .driver = { .owner = THIS_MODULE,}, |
d5bf9071 | 172 | }, { |
00db8189 AF |
173 | .phy_id = 0x00181b80, |
174 | .name = "Davicom DM9131", | |
175 | .phy_id_mask = 0x0ffffff0, | |
176 | .features = PHY_BASIC_FEATURES, | |
177 | .flags = PHY_HAS_INTERRUPT, | |
178 | .config_aneg = genphy_config_aneg, | |
179 | .read_status = genphy_read_status, | |
180 | .ack_interrupt = dm9161_ack_interrupt, | |
181 | .config_intr = dm9161_config_intr, | |
12414db1 | 182 | .driver = { .owner = THIS_MODULE,}, |
d5bf9071 | 183 | } }; |
00db8189 AF |
184 | |
185 | static int __init davicom_init(void) | |
186 | { | |
d5bf9071 CH |
187 | return phy_drivers_register(dm91xx_driver, |
188 | ARRAY_SIZE(dm91xx_driver)); | |
00db8189 AF |
189 | } |
190 | ||
191 | static void __exit davicom_exit(void) | |
192 | { | |
d5bf9071 CH |
193 | phy_drivers_unregister(dm91xx_driver, |
194 | ARRAY_SIZE(dm91xx_driver)); | |
00db8189 AF |
195 | } |
196 | ||
197 | module_init(davicom_init); | |
198 | module_exit(davicom_exit); | |
4e4f10f6 | 199 | |
cf93c945 | 200 | static struct mdio_device_id __maybe_unused davicom_tbl[] = { |
4e4f10f6 DW |
201 | { 0x0181b880, 0x0ffffff0 }, |
202 | { 0x0181b8a0, 0x0ffffff0 }, | |
203 | { 0x00181b80, 0x0ffffff0 }, | |
204 | { } | |
205 | }; | |
206 | ||
207 | MODULE_DEVICE_TABLE(mdio, davicom_tbl); |