xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/phy.c (revision c8dbaa22)
1 /*
2  * Marvell 88e6xxx Ethernet switch PHY and PPU support
3  *
4  * Copyright (c) 2008 Marvell Semiconductor
5  *
6  * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13 
14 #include <linux/mdio.h>
15 #include <linux/module.h>
16 #include <net/dsa.h>
17 
18 #include "chip.h"
19 #include "phy.h"
20 
21 int mv88e6165_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
22 		       int addr, int reg, u16 *val)
23 {
24 	return mv88e6xxx_read(chip, addr, reg, val);
25 }
26 
27 int mv88e6165_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
28 			int addr, int reg, u16 val)
29 {
30 	return mv88e6xxx_write(chip, addr, reg, val);
31 }
32 
33 int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, int reg, u16 *val)
34 {
35 	int addr = phy; /* PHY devices addresses start at 0x0 */
36 	struct mii_bus *bus;
37 
38 	bus = mv88e6xxx_default_mdio_bus(chip);
39 	if (!bus)
40 		return -EOPNOTSUPP;
41 
42 	if (!chip->info->ops->phy_read)
43 		return -EOPNOTSUPP;
44 
45 	return chip->info->ops->phy_read(chip, bus, addr, reg, val);
46 }
47 
48 int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy, int reg, u16 val)
49 {
50 	int addr = phy; /* PHY devices addresses start at 0x0 */
51 	struct mii_bus *bus;
52 
53 	bus = mv88e6xxx_default_mdio_bus(chip);
54 	if (!bus)
55 		return -EOPNOTSUPP;
56 
57 	if (!chip->info->ops->phy_write)
58 		return -EOPNOTSUPP;
59 
60 	return chip->info->ops->phy_write(chip, bus, addr, reg, val);
61 }
62 
63 static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
64 {
65 	return mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
66 }
67 
68 static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
69 {
70 	int err;
71 
72 	/* Restore PHY page Copper 0x0 for access via the registered
73 	 * MDIO bus
74 	 */
75 	err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE,
76 				  MV88E6XXX_PHY_PAGE_COPPER);
77 	if (unlikely(err)) {
78 		dev_err(chip->dev,
79 			"failed to restore PHY %d page Copper (%d)\n",
80 			phy, err);
81 	}
82 }
83 
84 int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
85 			    u8 page, int reg, u16 *val)
86 {
87 	int err;
88 
89 	/* There is no paging for registers 22 */
90 	if (reg == MV88E6XXX_PHY_PAGE)
91 		return -EINVAL;
92 
93 	err = mv88e6xxx_phy_page_get(chip, phy, page);
94 	if (!err) {
95 		err = mv88e6xxx_phy_read(chip, phy, reg, val);
96 		mv88e6xxx_phy_page_put(chip, phy);
97 	}
98 
99 	return err;
100 }
101 
102 int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
103 			     u8 page, int reg, u16 val)
104 {
105 	int err;
106 
107 	/* There is no paging for registers 22 */
108 	if (reg == MV88E6XXX_PHY_PAGE)
109 		return -EINVAL;
110 
111 	err = mv88e6xxx_phy_page_get(chip, phy, page);
112 	if (!err) {
113 		err = mv88e6xxx_phy_write(chip, phy, MV88E6XXX_PHY_PAGE, page);
114 		mv88e6xxx_phy_page_put(chip, phy);
115 	}
116 
117 	return err;
118 }
119 
120 static int mv88e6xxx_phy_ppu_disable(struct mv88e6xxx_chip *chip)
121 {
122 	if (!chip->info->ops->ppu_disable)
123 		return 0;
124 
125 	return chip->info->ops->ppu_disable(chip);
126 }
127 
128 static int mv88e6xxx_phy_ppu_enable(struct mv88e6xxx_chip *chip)
129 {
130 	if (!chip->info->ops->ppu_enable)
131 		return 0;
132 
133 	return chip->info->ops->ppu_enable(chip);
134 }
135 
136 static void mv88e6xxx_phy_ppu_reenable_work(struct work_struct *ugly)
137 {
138 	struct mv88e6xxx_chip *chip;
139 
140 	chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
141 
142 	mutex_lock(&chip->reg_lock);
143 
144 	if (mutex_trylock(&chip->ppu_mutex)) {
145 		if (mv88e6xxx_phy_ppu_enable(chip) == 0)
146 			chip->ppu_disabled = 0;
147 		mutex_unlock(&chip->ppu_mutex);
148 	}
149 
150 	mutex_unlock(&chip->reg_lock);
151 }
152 
153 static void mv88e6xxx_phy_ppu_reenable_timer(unsigned long _ps)
154 {
155 	struct mv88e6xxx_chip *chip = (void *)_ps;
156 
157 	schedule_work(&chip->ppu_work);
158 }
159 
160 static int mv88e6xxx_phy_ppu_access_get(struct mv88e6xxx_chip *chip)
161 {
162 	int ret;
163 
164 	mutex_lock(&chip->ppu_mutex);
165 
166 	/* If the PHY polling unit is enabled, disable it so that
167 	 * we can access the PHY registers.  If it was already
168 	 * disabled, cancel the timer that is going to re-enable
169 	 * it.
170 	 */
171 	if (!chip->ppu_disabled) {
172 		ret = mv88e6xxx_phy_ppu_disable(chip);
173 		if (ret < 0) {
174 			mutex_unlock(&chip->ppu_mutex);
175 			return ret;
176 		}
177 		chip->ppu_disabled = 1;
178 	} else {
179 		del_timer(&chip->ppu_timer);
180 		ret = 0;
181 	}
182 
183 	return ret;
184 }
185 
186 static void mv88e6xxx_phy_ppu_access_put(struct mv88e6xxx_chip *chip)
187 {
188 	/* Schedule a timer to re-enable the PHY polling unit. */
189 	mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
190 	mutex_unlock(&chip->ppu_mutex);
191 }
192 
193 static void mv88e6xxx_phy_ppu_state_init(struct mv88e6xxx_chip *chip)
194 {
195 	mutex_init(&chip->ppu_mutex);
196 	INIT_WORK(&chip->ppu_work, mv88e6xxx_phy_ppu_reenable_work);
197 	setup_timer(&chip->ppu_timer, mv88e6xxx_phy_ppu_reenable_timer,
198 		    (unsigned long)chip);
199 }
200 
201 static void mv88e6xxx_phy_ppu_state_destroy(struct mv88e6xxx_chip *chip)
202 {
203 	del_timer_sync(&chip->ppu_timer);
204 }
205 
206 int mv88e6185_phy_ppu_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
207 			   int addr, int reg, u16 *val)
208 {
209 	int err;
210 
211 	err = mv88e6xxx_phy_ppu_access_get(chip);
212 	if (!err) {
213 		err = mv88e6xxx_read(chip, addr, reg, val);
214 		mv88e6xxx_phy_ppu_access_put(chip);
215 	}
216 
217 	return err;
218 }
219 
220 int mv88e6185_phy_ppu_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus,
221 			    int addr, int reg, u16 val)
222 {
223 	int err;
224 
225 	err = mv88e6xxx_phy_ppu_access_get(chip);
226 	if (!err) {
227 		err = mv88e6xxx_write(chip, addr, reg, val);
228 		mv88e6xxx_phy_ppu_access_put(chip);
229 	}
230 
231 	return err;
232 }
233 
234 void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
235 {
236 	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
237 		mv88e6xxx_phy_ppu_state_init(chip);
238 }
239 
240 void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
241 {
242 	if (chip->info->ops->ppu_enable && chip->info->ops->ppu_disable)
243 		mv88e6xxx_phy_ppu_state_destroy(chip);
244 }
245 
246 int mv88e6xxx_phy_setup(struct mv88e6xxx_chip *chip)
247 {
248 	return mv88e6xxx_phy_ppu_enable(chip);
249 }
250