1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * drivers/net/phy/cicada.c |
4 | * |
5 | * Driver for Cicada 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 <linux/io.h> |
29 | #include <asm/irq.h> |
30 | #include <linux/uaccess.h> |
31 | |
32 | /* Cicada Extended Control Register 1 */ |
33 | #define MII_CIS8201_EXT_CON1 0x17 |
34 | #define MII_CIS8201_EXTCON1_INIT 0x0000 |
35 | |
36 | /* Cicada Interrupt Mask Register */ |
37 | #define MII_CIS8201_IMASK 0x19 |
38 | #define MII_CIS8201_IMASK_IEN 0x8000 |
39 | #define MII_CIS8201_IMASK_SPEED 0x4000 |
40 | #define MII_CIS8201_IMASK_LINK 0x2000 |
41 | #define MII_CIS8201_IMASK_DUPLEX 0x1000 |
42 | #define MII_CIS8201_IMASK_MASK 0xf000 |
43 | |
44 | /* Cicada Interrupt Status Register */ |
45 | #define MII_CIS8201_ISTAT 0x1a |
46 | #define MII_CIS8201_ISTAT_STATUS 0x8000 |
47 | #define MII_CIS8201_ISTAT_SPEED 0x4000 |
48 | #define MII_CIS8201_ISTAT_LINK 0x2000 |
49 | #define MII_CIS8201_ISTAT_DUPLEX 0x1000 |
50 | |
51 | /* Cicada Auxiliary Control/Status Register */ |
52 | #define MII_CIS8201_AUX_CONSTAT 0x1c |
53 | #define MII_CIS8201_AUXCONSTAT_INIT 0x0004 |
54 | #define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020 |
55 | #define MII_CIS8201_AUXCONSTAT_SPEED 0x0018 |
56 | #define MII_CIS8201_AUXCONSTAT_GBIT 0x0010 |
57 | #define MII_CIS8201_AUXCONSTAT_100 0x0008 |
58 | |
59 | MODULE_DESCRIPTION("Cicadia PHY driver" ); |
60 | MODULE_AUTHOR("Andy Fleming" ); |
61 | MODULE_LICENSE("GPL" ); |
62 | |
63 | static int cis820x_config_init(struct phy_device *phydev) |
64 | { |
65 | int err; |
66 | |
67 | err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT, |
68 | MII_CIS8201_AUXCONSTAT_INIT); |
69 | |
70 | if (err < 0) |
71 | return err; |
72 | |
73 | err = phy_write(phydev, MII_CIS8201_EXT_CON1, |
74 | MII_CIS8201_EXTCON1_INIT); |
75 | |
76 | return err; |
77 | } |
78 | |
79 | static int cis820x_ack_interrupt(struct phy_device *phydev) |
80 | { |
81 | int err = phy_read(phydev, MII_CIS8201_ISTAT); |
82 | |
83 | return (err < 0) ? err : 0; |
84 | } |
85 | |
86 | static int cis820x_config_intr(struct phy_device *phydev) |
87 | { |
88 | int err; |
89 | |
90 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
91 | err = cis820x_ack_interrupt(phydev); |
92 | if (err) |
93 | return err; |
94 | |
95 | err = phy_write(phydev, MII_CIS8201_IMASK, |
96 | MII_CIS8201_IMASK_MASK); |
97 | } else { |
98 | err = phy_write(phydev, MII_CIS8201_IMASK, val: 0); |
99 | if (err) |
100 | return err; |
101 | |
102 | err = cis820x_ack_interrupt(phydev); |
103 | } |
104 | |
105 | return err; |
106 | } |
107 | |
108 | static irqreturn_t cis820x_handle_interrupt(struct phy_device *phydev) |
109 | { |
110 | int irq_status; |
111 | |
112 | irq_status = phy_read(phydev, MII_CIS8201_ISTAT); |
113 | if (irq_status < 0) { |
114 | phy_error(phydev); |
115 | return IRQ_NONE; |
116 | } |
117 | |
118 | if (!(irq_status & MII_CIS8201_IMASK_MASK)) |
119 | return IRQ_NONE; |
120 | |
121 | phy_trigger_machine(phydev); |
122 | |
123 | return IRQ_HANDLED; |
124 | } |
125 | |
126 | /* Cicada 8201, a.k.a Vitesse VSC8201 */ |
127 | static struct phy_driver cis820x_driver[] = { |
128 | { |
129 | .phy_id = 0x000fc410, |
130 | .name = "Cicada Cis8201" , |
131 | .phy_id_mask = 0x000ffff0, |
132 | /* PHY_GBIT_FEATURES */ |
133 | .config_init = &cis820x_config_init, |
134 | .config_intr = &cis820x_config_intr, |
135 | .handle_interrupt = &cis820x_handle_interrupt, |
136 | }, { |
137 | .phy_id = 0x000fc440, |
138 | .name = "Cicada Cis8204" , |
139 | .phy_id_mask = 0x000fffc0, |
140 | /* PHY_GBIT_FEATURES */ |
141 | .config_init = &cis820x_config_init, |
142 | .config_intr = &cis820x_config_intr, |
143 | .handle_interrupt = &cis820x_handle_interrupt, |
144 | } }; |
145 | |
146 | module_phy_driver(cis820x_driver); |
147 | |
148 | static struct mdio_device_id __maybe_unused cicada_tbl[] = { |
149 | { 0x000fc410, 0x000ffff0 }, |
150 | { 0x000fc440, 0x000fffc0 }, |
151 | { } |
152 | }; |
153 | |
154 | MODULE_DEVICE_TABLE(mdio, cicada_tbl); |
155 | |