1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * drivers/net/phy/qsemi.c |
4 | * |
5 | * Driver for Quality Semiconductor 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 | /* ------------------------------------------------------------------------- */ |
33 | /* The Quality Semiconductor QS6612 is used on the RPX CLLF */ |
34 | |
35 | /* register definitions */ |
36 | |
37 | #define MII_QS6612_MCR 17 /* Mode Control Register */ |
38 | #define MII_QS6612_FTR 27 /* Factory Test Register */ |
39 | #define MII_QS6612_MCO 28 /* Misc. Control Register */ |
40 | #define MII_QS6612_ISR 29 /* Interrupt Source Register */ |
41 | #define MII_QS6612_IMR 30 /* Interrupt Mask Register */ |
42 | #define MII_QS6612_IMR_INIT 0x003a |
43 | #define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */ |
44 | |
45 | #define QS6612_PCR_AN_COMPLETE 0x1000 |
46 | #define QS6612_PCR_RLBEN 0x0200 |
47 | #define QS6612_PCR_DCREN 0x0100 |
48 | #define QS6612_PCR_4B5BEN 0x0040 |
49 | #define QS6612_PCR_TX_ISOLATE 0x0020 |
50 | #define QS6612_PCR_MLT3_DIS 0x0002 |
51 | #define QS6612_PCR_SCRM_DESCRM 0x0001 |
52 | |
53 | MODULE_DESCRIPTION("Quality Semiconductor PHY driver" ); |
54 | MODULE_AUTHOR("Andy Fleming" ); |
55 | MODULE_LICENSE("GPL" ); |
56 | |
57 | /* Returns 0, unless there's a write error */ |
58 | static int qs6612_config_init(struct phy_device *phydev) |
59 | { |
60 | /* The PHY powers up isolated on the RPX, |
61 | * so send a command to allow operation. |
62 | * XXX - My docs indicate this should be 0x0940 |
63 | * ...or something. The current value sets three |
64 | * reserved bits, bit 11, which specifies it should be |
65 | * set to one, bit 10, which specifies it should be set |
66 | * to 0, and bit 7, which doesn't specify. However, my |
67 | * docs are preliminary, and I will leave it like this |
68 | * until someone more knowledgable corrects me or it. |
69 | * -- Andy Fleming |
70 | */ |
71 | return phy_write(phydev, MII_QS6612_PCR, val: 0x0dc0); |
72 | } |
73 | |
74 | static int qs6612_ack_interrupt(struct phy_device *phydev) |
75 | { |
76 | int err; |
77 | |
78 | /* The Interrupt Source register is not self-clearing, bits 4 and 5 are |
79 | * cleared when MII_BMSR is read and bits 1 and 3 are cleared when |
80 | * MII_EXPANSION is read |
81 | */ |
82 | err = phy_read(phydev, MII_QS6612_ISR); |
83 | |
84 | if (err < 0) |
85 | return err; |
86 | |
87 | err = phy_read(phydev, MII_BMSR); |
88 | |
89 | if (err < 0) |
90 | return err; |
91 | |
92 | err = phy_read(phydev, MII_EXPANSION); |
93 | |
94 | if (err < 0) |
95 | return err; |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static int qs6612_config_intr(struct phy_device *phydev) |
101 | { |
102 | int err; |
103 | |
104 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
105 | /* clear any interrupts before enabling them */ |
106 | err = qs6612_ack_interrupt(phydev); |
107 | if (err) |
108 | return err; |
109 | |
110 | err = phy_write(phydev, MII_QS6612_IMR, |
111 | MII_QS6612_IMR_INIT); |
112 | } else { |
113 | err = phy_write(phydev, MII_QS6612_IMR, val: 0); |
114 | if (err) |
115 | return err; |
116 | |
117 | /* clear any leftover interrupts */ |
118 | err = qs6612_ack_interrupt(phydev); |
119 | } |
120 | |
121 | return err; |
122 | |
123 | } |
124 | |
125 | static irqreturn_t qs6612_handle_interrupt(struct phy_device *phydev) |
126 | { |
127 | int irq_status; |
128 | |
129 | irq_status = phy_read(phydev, MII_QS6612_ISR); |
130 | if (irq_status < 0) { |
131 | phy_error(phydev); |
132 | return IRQ_NONE; |
133 | } |
134 | |
135 | if (!(irq_status & MII_QS6612_IMR_INIT)) |
136 | return IRQ_NONE; |
137 | |
138 | /* the interrupt source register is not self-clearing */ |
139 | qs6612_ack_interrupt(phydev); |
140 | |
141 | phy_trigger_machine(phydev); |
142 | |
143 | return IRQ_HANDLED; |
144 | } |
145 | |
146 | static struct phy_driver qs6612_driver[] = { { |
147 | .phy_id = 0x00181440, |
148 | .name = "QS6612" , |
149 | .phy_id_mask = 0xfffffff0, |
150 | /* PHY_BASIC_FEATURES */ |
151 | .config_init = qs6612_config_init, |
152 | .config_intr = qs6612_config_intr, |
153 | .handle_interrupt = qs6612_handle_interrupt, |
154 | } }; |
155 | |
156 | module_phy_driver(qs6612_driver); |
157 | |
158 | static struct mdio_device_id __maybe_unused qs6612_tbl[] = { |
159 | { 0x00181440, 0xfffffff0 }, |
160 | { } |
161 | }; |
162 | |
163 | MODULE_DEVICE_TABLE(mdio, qs6612_tbl); |
164 | |