blob: a230fcba5b641368c67cfc82e5992498d5343652 [file] [log] [blame]
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001/*
Vivien Didelot0d3cd4b2016-06-21 12:28:19 -04002 * Marvell 88e6xxx Ethernet switch single-chip support
3 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00004 * Copyright (c) 2008 Marvell Semiconductor
5 *
Vivien Didelotb8fee952015-08-13 12:52:19 -04006 * Copyright (c) 2015 CMC Electronics, Inc.
7 * Added support for VLAN Table Unit operations
8 *
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02009 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
10 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
Barry Grussling19b2f972013-01-08 16:05:54 +000017#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070018#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020019#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070020#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000021#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000022#include <linux/list.h>
Andrew Lunn14c7b3c2016-05-10 23:27:21 +020023#include <linux/mdio.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000024#include <linux/module.h>
Vivien Didelotcaac8542016-06-20 13:14:09 -040025#include <linux/of_device.h>
Andrew Lunnb516d452016-06-04 21:17:06 +020026#include <linux/of_mdio.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000027#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010028#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000029#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000030#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040031#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000032#include "mv88e6xxx.h"
33
Vivien Didelotfad09c72016-06-21 12:28:20 -040034static void assert_reg_lock(struct mv88e6xxx_chip *chip)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040035{
Vivien Didelotfad09c72016-06-21 12:28:20 -040036 if (unlikely(!mutex_is_locked(&chip->reg_lock))) {
37 dev_err(chip->dev, "Switch registers lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040038 dump_stack();
39 }
40}
41
Vivien Didelot914b32f2016-06-20 13:14:11 -040042/* The switch ADDR[4:1] configuration pins define the chip SMI device address
43 * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
44 *
45 * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
46 * is the only device connected to the SMI master. In this mode it responds to
47 * all 32 possible SMI addresses, and thus maps directly the internal devices.
48 *
49 * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
50 * multiple devices to share the SMI interface. In this mode it responds to only
51 * 2 registers, used to indirectly access the internal SMI devices.
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000052 */
Vivien Didelot914b32f2016-06-20 13:14:11 -040053
Vivien Didelotfad09c72016-06-21 12:28:20 -040054static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040055 int addr, int reg, u16 *val)
56{
Vivien Didelotfad09c72016-06-21 12:28:20 -040057 if (!chip->smi_ops)
Vivien Didelot914b32f2016-06-20 13:14:11 -040058 return -EOPNOTSUPP;
59
Vivien Didelotfad09c72016-06-21 12:28:20 -040060 return chip->smi_ops->read(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040061}
62
Vivien Didelotfad09c72016-06-21 12:28:20 -040063static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040064 int addr, int reg, u16 val)
65{
Vivien Didelotfad09c72016-06-21 12:28:20 -040066 if (!chip->smi_ops)
Vivien Didelot914b32f2016-06-20 13:14:11 -040067 return -EOPNOTSUPP;
68
Vivien Didelotfad09c72016-06-21 12:28:20 -040069 return chip->smi_ops->write(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040070}
71
Vivien Didelotfad09c72016-06-21 12:28:20 -040072static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040073 int addr, int reg, u16 *val)
74{
75 int ret;
76
Vivien Didelotfad09c72016-06-21 12:28:20 -040077 ret = mdiobus_read_nested(chip->bus, addr, reg);
Vivien Didelot914b32f2016-06-20 13:14:11 -040078 if (ret < 0)
79 return ret;
80
81 *val = ret & 0xffff;
82
83 return 0;
84}
85
Vivien Didelotfad09c72016-06-21 12:28:20 -040086static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040087 int addr, int reg, u16 val)
88{
89 int ret;
90
Vivien Didelotfad09c72016-06-21 12:28:20 -040091 ret = mdiobus_write_nested(chip->bus, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040092 if (ret < 0)
93 return ret;
94
95 return 0;
96}
97
98static const struct mv88e6xxx_ops mv88e6xxx_smi_single_chip_ops = {
99 .read = mv88e6xxx_smi_single_chip_read,
100 .write = mv88e6xxx_smi_single_chip_write,
101};
102
Vivien Didelotfad09c72016-06-21 12:28:20 -0400103static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000104{
105 int ret;
106 int i;
107
108 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400109 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000110 if (ret < 0)
111 return ret;
112
Andrew Lunncca8b132015-04-02 04:06:39 +0200113 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000114 return 0;
115 }
116
117 return -ETIMEDOUT;
118}
119
Vivien Didelotfad09c72016-06-21 12:28:20 -0400120static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400121 int addr, int reg, u16 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000122{
123 int ret;
124
Barry Grussling3675c8d2013-01-08 16:05:53 +0000125 /* Wait for the bus to become free. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400126 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000127 if (ret < 0)
128 return ret;
129
Barry Grussling3675c8d2013-01-08 16:05:53 +0000130 /* Transmit the read command. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400131 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
Neil Armstrong6e899e62015-10-22 10:37:53 +0200132 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000133 if (ret < 0)
134 return ret;
135
Barry Grussling3675c8d2013-01-08 16:05:53 +0000136 /* Wait for the read command to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400137 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000138 if (ret < 0)
139 return ret;
140
Barry Grussling3675c8d2013-01-08 16:05:53 +0000141 /* Read the data. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400142 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000143 if (ret < 0)
144 return ret;
145
Vivien Didelot914b32f2016-06-20 13:14:11 -0400146 *val = ret & 0xffff;
147
148 return 0;
149}
150
Vivien Didelotfad09c72016-06-21 12:28:20 -0400151static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400152 int addr, int reg, u16 val)
153{
154 int ret;
155
156 /* Wait for the bus to become free. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400157 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400158 if (ret < 0)
159 return ret;
160
161 /* Transmit the data to write. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400162 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400163 if (ret < 0)
164 return ret;
165
166 /* Transmit the write command. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400167 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400168 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
169 if (ret < 0)
170 return ret;
171
172 /* Wait for the write command to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400173 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400174 if (ret < 0)
175 return ret;
176
177 return 0;
178}
179
180static const struct mv88e6xxx_ops mv88e6xxx_smi_multi_chip_ops = {
181 .read = mv88e6xxx_smi_multi_chip_read,
182 .write = mv88e6xxx_smi_multi_chip_write,
183};
184
Vivien Didelotfad09c72016-06-21 12:28:20 -0400185static int mv88e6xxx_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400186 int addr, int reg, u16 *val)
187{
188 int err;
189
Vivien Didelotfad09c72016-06-21 12:28:20 -0400190 assert_reg_lock(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400191
Vivien Didelotfad09c72016-06-21 12:28:20 -0400192 err = mv88e6xxx_smi_read(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400193 if (err)
194 return err;
195
Vivien Didelotfad09c72016-06-21 12:28:20 -0400196 dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelot914b32f2016-06-20 13:14:11 -0400197 addr, reg, *val);
198
199 return 0;
200}
201
Vivien Didelotfad09c72016-06-21 12:28:20 -0400202static int mv88e6xxx_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400203 int addr, int reg, u16 val)
204{
205 int err;
206
Vivien Didelotfad09c72016-06-21 12:28:20 -0400207 assert_reg_lock(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400208
Vivien Didelotfad09c72016-06-21 12:28:20 -0400209 err = mv88e6xxx_smi_write(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400210 if (err)
211 return err;
212
Vivien Didelotfad09c72016-06-21 12:28:20 -0400213 dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelot914b32f2016-06-20 13:14:11 -0400214 addr, reg, val);
215
216 return 0;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000217}
218
Vivien Didelote57e5e72016-08-15 17:19:00 -0400219static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
220 int reg, u16 *val)
221{
222 int addr = phy; /* PHY devices addresses start at 0x0 */
223
224 if (!chip->phy_ops)
225 return -EOPNOTSUPP;
226
227 return chip->phy_ops->read(chip, addr, reg, val);
228}
229
230static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
231 int reg, u16 val)
232{
233 int addr = phy; /* PHY devices addresses start at 0x0 */
234
235 if (!chip->phy_ops)
236 return -EOPNOTSUPP;
237
238 return chip->phy_ops->write(chip, addr, reg, val);
239}
240
Vivien Didelot09cb7df2016-08-15 17:19:01 -0400241static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
242{
243 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE))
244 return -EOPNOTSUPP;
245
246 return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
247}
248
249static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
250{
251 int err;
252
253 /* Restore PHY page Copper 0x0 for access via the registered MDIO bus */
254 err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER);
255 if (unlikely(err)) {
256 dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n",
257 phy, err);
258 }
259}
260
261static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
262 u8 page, int reg, u16 *val)
263{
264 int err;
265
266 /* There is no paging for registers 22 */
267 if (reg == PHY_PAGE)
268 return -EINVAL;
269
270 err = mv88e6xxx_phy_page_get(chip, phy, page);
271 if (!err) {
272 err = mv88e6xxx_phy_read(chip, phy, reg, val);
273 mv88e6xxx_phy_page_put(chip, phy);
274 }
275
276 return err;
277}
278
279static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
280 u8 page, int reg, u16 val)
281{
282 int err;
283
284 /* There is no paging for registers 22 */
285 if (reg == PHY_PAGE)
286 return -EINVAL;
287
288 err = mv88e6xxx_phy_page_get(chip, phy, page);
289 if (!err) {
290 err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
291 mv88e6xxx_phy_page_put(chip, phy);
292 }
293
294 return err;
295}
296
297static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
298{
299 return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
300 reg, val);
301}
302
303static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
304{
305 return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
306 reg, val);
307}
308
Vivien Didelot2d79af62016-08-15 17:18:57 -0400309static int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg,
310 u16 mask)
311{
312 unsigned long timeout = jiffies + HZ / 10;
313
314 while (time_before(jiffies, timeout)) {
315 u16 val;
316 int err;
317
318 err = mv88e6xxx_read(chip, addr, reg, &val);
319 if (err)
320 return err;
321
322 if (!(val & mask))
323 return 0;
324
325 usleep_range(1000, 2000);
326 }
327
328 return -ETIMEDOUT;
329}
330
Vivien Didelotf22ab642016-07-18 20:45:31 -0400331/* Indirect write to single pointer-data register with an Update bit */
332static int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
333 u16 update)
334{
335 u16 val;
336 int i, err;
337
338 /* Wait until the previous operation is completed */
339 for (i = 0; i < 16; ++i) {
340 err = mv88e6xxx_read(chip, addr, reg, &val);
341 if (err)
342 return err;
343
344 if (!(val & BIT(15)))
345 break;
346 }
347
348 if (i == 16)
349 return -ETIMEDOUT;
350
351 /* Set the Update bit to trigger a write operation */
352 val = BIT(15) | update;
353
354 return mv88e6xxx_write(chip, addr, reg, val);
355}
356
Vivien Didelotfad09c72016-06-21 12:28:20 -0400357static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000358{
Vivien Didelot914b32f2016-06-20 13:14:11 -0400359 u16 val;
360 int err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000361
Vivien Didelotfad09c72016-06-21 12:28:20 -0400362 err = mv88e6xxx_read(chip, addr, reg, &val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400363 if (err)
364 return err;
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400365
Vivien Didelot914b32f2016-06-20 13:14:11 -0400366 return val;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000367}
368
Vivien Didelotfad09c72016-06-21 12:28:20 -0400369static int _mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
Andrew Lunn158bc062016-04-28 21:24:06 -0400370 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000371{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400372 return mv88e6xxx_write(chip, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700373}
374
Vivien Didelotfad09c72016-06-21 12:28:20 -0400375static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000376{
377 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000378 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000379
Vivien Didelotfad09c72016-06-21 12:28:20 -0400380 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200381 if (ret < 0)
382 return ret;
383
Vivien Didelotfad09c72016-06-21 12:28:20 -0400384 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400385 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200386 if (ret)
387 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000388
Barry Grussling19b2f972013-01-08 16:05:54 +0000389 timeout = jiffies + 1 * HZ;
390 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400391 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200392 if (ret < 0)
393 return ret;
394
Barry Grussling19b2f972013-01-08 16:05:54 +0000395 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200396 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
397 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000398 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000399 }
400
401 return -ETIMEDOUT;
402}
403
Vivien Didelotfad09c72016-06-21 12:28:20 -0400404static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000405{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200406 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000407 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000408
Vivien Didelotfad09c72016-06-21 12:28:20 -0400409 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200410 if (ret < 0)
411 return ret;
412
Vivien Didelotfad09c72016-06-21 12:28:20 -0400413 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot762eb672016-06-04 21:16:54 +0200414 ret | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200415 if (err)
416 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000417
Barry Grussling19b2f972013-01-08 16:05:54 +0000418 timeout = jiffies + 1 * HZ;
419 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400420 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200421 if (ret < 0)
422 return ret;
423
Barry Grussling19b2f972013-01-08 16:05:54 +0000424 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200425 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
426 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000427 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000428 }
429
430 return -ETIMEDOUT;
431}
432
433static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
434{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400435 struct mv88e6xxx_chip *chip;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000436
Vivien Didelotfad09c72016-06-21 12:28:20 -0400437 chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200438
Vivien Didelotfad09c72016-06-21 12:28:20 -0400439 mutex_lock(&chip->reg_lock);
Vivien Didelot762eb672016-06-04 21:16:54 +0200440
Vivien Didelotfad09c72016-06-21 12:28:20 -0400441 if (mutex_trylock(&chip->ppu_mutex)) {
442 if (mv88e6xxx_ppu_enable(chip) == 0)
443 chip->ppu_disabled = 0;
444 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000445 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200446
Vivien Didelotfad09c72016-06-21 12:28:20 -0400447 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000448}
449
450static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
451{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400452 struct mv88e6xxx_chip *chip = (void *)_ps;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000453
Vivien Didelotfad09c72016-06-21 12:28:20 -0400454 schedule_work(&chip->ppu_work);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000455}
456
Vivien Didelotfad09c72016-06-21 12:28:20 -0400457static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000458{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000459 int ret;
460
Vivien Didelotfad09c72016-06-21 12:28:20 -0400461 mutex_lock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000462
Barry Grussling3675c8d2013-01-08 16:05:53 +0000463 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000464 * we can access the PHY registers. If it was already
465 * disabled, cancel the timer that is going to re-enable
466 * it.
467 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400468 if (!chip->ppu_disabled) {
469 ret = mv88e6xxx_ppu_disable(chip);
Barry Grussling85686582013-01-08 16:05:56 +0000470 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400471 mutex_unlock(&chip->ppu_mutex);
Barry Grussling85686582013-01-08 16:05:56 +0000472 return ret;
473 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400474 chip->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000475 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400476 del_timer(&chip->ppu_timer);
Barry Grussling85686582013-01-08 16:05:56 +0000477 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000478 }
479
480 return ret;
481}
482
Vivien Didelotfad09c72016-06-21 12:28:20 -0400483static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000484{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000485 /* Schedule a timer to re-enable the PHY polling unit. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400486 mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
487 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000488}
489
Vivien Didelotfad09c72016-06-21 12:28:20 -0400490static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000491{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400492 mutex_init(&chip->ppu_mutex);
493 INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
494 init_timer(&chip->ppu_timer);
495 chip->ppu_timer.data = (unsigned long)chip;
496 chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000497}
498
Vivien Didelote57e5e72016-08-15 17:19:00 -0400499static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
500 int reg, u16 *val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000501{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400502 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000503
Vivien Didelote57e5e72016-08-15 17:19:00 -0400504 err = mv88e6xxx_ppu_access_get(chip);
505 if (!err) {
506 err = mv88e6xxx_read(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400507 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000508 }
509
Vivien Didelote57e5e72016-08-15 17:19:00 -0400510 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000511}
512
Vivien Didelote57e5e72016-08-15 17:19:00 -0400513static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
514 int reg, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000515{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400516 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000517
Vivien Didelote57e5e72016-08-15 17:19:00 -0400518 err = mv88e6xxx_ppu_access_get(chip);
519 if (!err) {
520 err = mv88e6xxx_write(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400521 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000522 }
523
Vivien Didelote57e5e72016-08-15 17:19:00 -0400524 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000525}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000526
Vivien Didelote57e5e72016-08-15 17:19:00 -0400527static const struct mv88e6xxx_ops mv88e6xxx_phy_ppu_ops = {
528 .read = mv88e6xxx_phy_ppu_read,
529 .write = mv88e6xxx_phy_ppu_write,
530};
531
Vivien Didelotfad09c72016-06-21 12:28:20 -0400532static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200533{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400534 return chip->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200535}
536
Vivien Didelotfad09c72016-06-21 12:28:20 -0400537static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200538{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400539 return chip->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200540}
541
Vivien Didelotfad09c72016-06-21 12:28:20 -0400542static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200543{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400544 return chip->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200545}
546
Vivien Didelotfad09c72016-06-21 12:28:20 -0400547static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200548{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400549 return chip->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200550}
551
Vivien Didelotfad09c72016-06-21 12:28:20 -0400552static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200553{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400554 return chip->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200555}
556
Vivien Didelotfad09c72016-06-21 12:28:20 -0400557static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700558{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400559 return chip->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700560}
561
Vivien Didelotfad09c72016-06-21 12:28:20 -0400562static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200563{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400564 return chip->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200565}
566
Vivien Didelotfad09c72016-06-21 12:28:20 -0400567static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200568{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400569 return chip->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200570}
571
Vivien Didelotfad09c72016-06-21 12:28:20 -0400572static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400573{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400574 return chip->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400575}
576
Vivien Didelotfad09c72016-06-21 12:28:20 -0400577static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_chip *chip)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400578{
579 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400580 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
581 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400582 return true;
583
584 return false;
585}
586
Andrew Lunndea87022015-08-31 15:56:47 +0200587/* We expect the switch to perform auto negotiation if there is a real
588 * phy. However, in the case of a fixed link phy, we force the port
589 * settings from the fixed link settings.
590 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400591static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
592 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200593{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400594 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200595 u32 reg;
596 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200597
598 if (!phy_is_pseudo_fixed_link(phydev))
599 return;
600
Vivien Didelotfad09c72016-06-21 12:28:20 -0400601 mutex_lock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200602
Vivien Didelotfad09c72016-06-21 12:28:20 -0400603 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200604 if (ret < 0)
605 goto out;
606
607 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
608 PORT_PCS_CTRL_FORCE_LINK |
609 PORT_PCS_CTRL_DUPLEX_FULL |
610 PORT_PCS_CTRL_FORCE_DUPLEX |
611 PORT_PCS_CTRL_UNFORCED);
612
613 reg |= PORT_PCS_CTRL_FORCE_LINK;
614 if (phydev->link)
Vivien Didelot57d32312016-06-20 13:13:58 -0400615 reg |= PORT_PCS_CTRL_LINK_UP;
Andrew Lunndea87022015-08-31 15:56:47 +0200616
Vivien Didelotfad09c72016-06-21 12:28:20 -0400617 if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200618 goto out;
619
620 switch (phydev->speed) {
621 case SPEED_1000:
622 reg |= PORT_PCS_CTRL_1000;
623 break;
624 case SPEED_100:
625 reg |= PORT_PCS_CTRL_100;
626 break;
627 case SPEED_10:
628 reg |= PORT_PCS_CTRL_10;
629 break;
630 default:
631 pr_info("Unknown speed");
632 goto out;
633 }
634
635 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
636 if (phydev->duplex == DUPLEX_FULL)
637 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
638
Vivien Didelotfad09c72016-06-21 12:28:20 -0400639 if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
640 (port >= chip->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200641 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
642 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
643 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
644 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
645 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
646 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
647 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
648 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400649 _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200650
651out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400652 mutex_unlock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200653}
654
Vivien Didelotfad09c72016-06-21 12:28:20 -0400655static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000656{
657 int ret;
658 int i;
659
660 for (i = 0; i < 10; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400661 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200662 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000663 return 0;
664 }
665
666 return -ETIMEDOUT;
667}
668
Vivien Didelotfad09c72016-06-21 12:28:20 -0400669static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000670{
671 int ret;
672
Vivien Didelotfad09c72016-06-21 12:28:20 -0400673 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200674 port = (port + 1) << 5;
675
Barry Grussling3675c8d2013-01-08 16:05:53 +0000676 /* Snapshot the hardware statistics counters for this port. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400677 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200678 GLOBAL_STATS_OP_CAPTURE_PORT |
679 GLOBAL_STATS_OP_HIST_RX_TX | port);
680 if (ret < 0)
681 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000682
Barry Grussling3675c8d2013-01-08 16:05:53 +0000683 /* Wait for the snapshotting to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400684 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000685 if (ret < 0)
686 return ret;
687
688 return 0;
689}
690
Vivien Didelotfad09c72016-06-21 12:28:20 -0400691static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400692 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000693{
694 u32 _val;
695 int ret;
696
697 *val = 0;
698
Vivien Didelotfad09c72016-06-21 12:28:20 -0400699 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200700 GLOBAL_STATS_OP_READ_CAPTURED |
701 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000702 if (ret < 0)
703 return;
704
Vivien Didelotfad09c72016-06-21 12:28:20 -0400705 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000706 if (ret < 0)
707 return;
708
Vivien Didelotfad09c72016-06-21 12:28:20 -0400709 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000710 if (ret < 0)
711 return;
712
713 _val = ret << 16;
714
Vivien Didelotfad09c72016-06-21 12:28:20 -0400715 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000716 if (ret < 0)
717 return;
718
719 *val = _val | ret;
720}
721
Andrew Lunne413e7e2015-04-02 04:06:38 +0200722static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100723 { "in_good_octets", 8, 0x00, BANK0, },
724 { "in_bad_octets", 4, 0x02, BANK0, },
725 { "in_unicast", 4, 0x04, BANK0, },
726 { "in_broadcasts", 4, 0x06, BANK0, },
727 { "in_multicasts", 4, 0x07, BANK0, },
728 { "in_pause", 4, 0x16, BANK0, },
729 { "in_undersize", 4, 0x18, BANK0, },
730 { "in_fragments", 4, 0x19, BANK0, },
731 { "in_oversize", 4, 0x1a, BANK0, },
732 { "in_jabber", 4, 0x1b, BANK0, },
733 { "in_rx_error", 4, 0x1c, BANK0, },
734 { "in_fcs_error", 4, 0x1d, BANK0, },
735 { "out_octets", 8, 0x0e, BANK0, },
736 { "out_unicast", 4, 0x10, BANK0, },
737 { "out_broadcasts", 4, 0x13, BANK0, },
738 { "out_multicasts", 4, 0x12, BANK0, },
739 { "out_pause", 4, 0x15, BANK0, },
740 { "excessive", 4, 0x11, BANK0, },
741 { "collisions", 4, 0x1e, BANK0, },
742 { "deferred", 4, 0x05, BANK0, },
743 { "single", 4, 0x14, BANK0, },
744 { "multiple", 4, 0x17, BANK0, },
745 { "out_fcs_error", 4, 0x03, BANK0, },
746 { "late", 4, 0x1f, BANK0, },
747 { "hist_64bytes", 4, 0x08, BANK0, },
748 { "hist_65_127bytes", 4, 0x09, BANK0, },
749 { "hist_128_255bytes", 4, 0x0a, BANK0, },
750 { "hist_256_511bytes", 4, 0x0b, BANK0, },
751 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
752 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
753 { "sw_in_discards", 4, 0x10, PORT, },
754 { "sw_in_filtered", 2, 0x12, PORT, },
755 { "sw_out_filtered", 2, 0x13, PORT, },
756 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
757 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
758 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
759 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
760 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
761 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
762 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
763 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
764 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
765 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
766 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
767 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
768 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
769 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
770 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
771 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
772 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
773 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
774 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
775 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
776 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
777 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
778 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
779 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
780 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
781 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200782};
783
Vivien Didelotfad09c72016-06-21 12:28:20 -0400784static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100785 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200786{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100787 switch (stat->type) {
788 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200789 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100790 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400791 return mv88e6xxx_6320_family(chip);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100792 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400793 return mv88e6xxx_6095_family(chip) ||
794 mv88e6xxx_6185_family(chip) ||
795 mv88e6xxx_6097_family(chip) ||
796 mv88e6xxx_6165_family(chip) ||
797 mv88e6xxx_6351_family(chip) ||
798 mv88e6xxx_6352_family(chip);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200799 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100800 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000801}
802
Vivien Didelotfad09c72016-06-21 12:28:20 -0400803static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100804 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200805 int port)
806{
Andrew Lunn80c46272015-06-20 18:42:30 +0200807 u32 low;
808 u32 high = 0;
809 int ret;
810 u64 value;
811
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100812 switch (s->type) {
813 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400814 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200815 if (ret < 0)
816 return UINT64_MAX;
817
818 low = ret;
819 if (s->sizeof_stat == 4) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400820 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100821 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200822 if (ret < 0)
823 return UINT64_MAX;
824 high = ret;
825 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100826 break;
827 case BANK0:
828 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400829 _mv88e6xxx_stats_read(chip, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200830 if (s->sizeof_stat == 8)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400831 _mv88e6xxx_stats_read(chip, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200832 }
833 value = (((u64)high) << 16) | low;
834 return value;
835}
836
Vivien Didelotf81ec902016-05-09 13:22:58 -0400837static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
838 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100839{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400840 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100841 struct mv88e6xxx_hw_stat *stat;
842 int i, j;
843
844 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
845 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400846 if (mv88e6xxx_has_stat(chip, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100847 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
848 ETH_GSTRING_LEN);
849 j++;
850 }
851 }
852}
853
Vivien Didelotf81ec902016-05-09 13:22:58 -0400854static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100855{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400856 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100857 struct mv88e6xxx_hw_stat *stat;
858 int i, j;
859
860 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
861 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400862 if (mv88e6xxx_has_stat(chip, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100863 j++;
864 }
865 return j;
866}
867
Vivien Didelotf81ec902016-05-09 13:22:58 -0400868static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
869 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000870{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400871 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100872 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000873 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100874 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000875
Vivien Didelotfad09c72016-06-21 12:28:20 -0400876 mutex_lock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000877
Vivien Didelotfad09c72016-06-21 12:28:20 -0400878 ret = _mv88e6xxx_stats_snapshot(chip, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000879 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400880 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000881 return;
882 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100883 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
884 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400885 if (mv88e6xxx_has_stat(chip, stat)) {
886 data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100887 j++;
888 }
889 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000890
Vivien Didelotfad09c72016-06-21 12:28:20 -0400891 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000892}
Ben Hutchings98e67302011-11-25 14:36:19 +0000893
Vivien Didelotf81ec902016-05-09 13:22:58 -0400894static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700895{
896 return 32 * sizeof(u16);
897}
898
Vivien Didelotf81ec902016-05-09 13:22:58 -0400899static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
900 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700901{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400902 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700903 u16 *p = _p;
904 int i;
905
906 regs->version = 0;
907
908 memset(p, 0xff, 32 * sizeof(u16));
909
Vivien Didelotfad09c72016-06-21 12:28:20 -0400910 mutex_lock(&chip->reg_lock);
Vivien Didelot23062512016-05-09 13:22:45 -0400911
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700912 for (i = 0; i < 32; i++) {
913 int ret;
914
Vivien Didelotfad09c72016-06-21 12:28:20 -0400915 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700916 if (ret >= 0)
917 p[i] = ret;
918 }
Vivien Didelot23062512016-05-09 13:22:45 -0400919
Vivien Didelotfad09c72016-06-21 12:28:20 -0400920 mutex_unlock(&chip->reg_lock);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700921}
922
Vivien Didelotfad09c72016-06-21 12:28:20 -0400923static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700924{
Vivien Didelot2d79af62016-08-15 17:18:57 -0400925 return mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_ATU_OP,
926 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700927}
928
Vivien Didelotf81ec902016-05-09 13:22:58 -0400929static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
930 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -0800931{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400932 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot9c938292016-08-15 17:19:02 -0400933 u16 reg;
934 int err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800935
Vivien Didelotfad09c72016-06-21 12:28:20 -0400936 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -0400937 return -EOPNOTSUPP;
938
Vivien Didelotfad09c72016-06-21 12:28:20 -0400939 mutex_lock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200940
Vivien Didelot9c938292016-08-15 17:19:02 -0400941 err = mv88e6xxx_phy_read(chip, port, 16, &reg);
942 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200943 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800944
945 e->eee_enabled = !!(reg & 0x0200);
946 e->tx_lpi_enabled = !!(reg & 0x0100);
947
Vivien Didelot9c938292016-08-15 17:19:02 -0400948 err = mv88e6xxx_read(chip, REG_PORT(port), PORT_STATUS, &reg);
949 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200950 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800951
Andrew Lunncca8b132015-04-02 04:06:39 +0200952 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200953out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400954 mutex_unlock(&chip->reg_lock);
Vivien Didelot9c938292016-08-15 17:19:02 -0400955
956 return err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800957}
958
Vivien Didelotf81ec902016-05-09 13:22:58 -0400959static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
960 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -0800961{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400962 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot9c938292016-08-15 17:19:02 -0400963 u16 reg;
964 int err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800965
Vivien Didelotfad09c72016-06-21 12:28:20 -0400966 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -0400967 return -EOPNOTSUPP;
968
Vivien Didelotfad09c72016-06-21 12:28:20 -0400969 mutex_lock(&chip->reg_lock);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800970
Vivien Didelot9c938292016-08-15 17:19:02 -0400971 err = mv88e6xxx_phy_read(chip, port, 16, &reg);
972 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200973 goto out;
974
Vivien Didelot9c938292016-08-15 17:19:02 -0400975 reg &= ~0x0300;
Andrew Lunn2f40c692015-04-02 04:06:37 +0200976 if (e->eee_enabled)
977 reg |= 0x0200;
978 if (e->tx_lpi_enabled)
979 reg |= 0x0100;
980
Vivien Didelot9c938292016-08-15 17:19:02 -0400981 err = mv88e6xxx_phy_write(chip, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200982out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400983 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200984
Vivien Didelot9c938292016-08-15 17:19:02 -0400985 return err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800986}
987
Vivien Didelotfad09c72016-06-21 12:28:20 -0400988static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700989{
990 int ret;
991
Vivien Didelotfad09c72016-06-21 12:28:20 -0400992 if (mv88e6xxx_has_fid_reg(chip)) {
993 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_FID,
994 fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400995 if (ret < 0)
996 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -0400997 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -0400998 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400999 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001000 if (ret < 0)
1001 return ret;
1002
Vivien Didelotfad09c72016-06-21 12:28:20 -04001003 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001004 (ret & 0xfff) |
1005 ((fid << 8) & 0xf000));
1006 if (ret < 0)
1007 return ret;
1008
1009 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1010 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001011 }
1012
Vivien Didelotfad09c72016-06-21 12:28:20 -04001013 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001014 if (ret < 0)
1015 return ret;
1016
Vivien Didelotfad09c72016-06-21 12:28:20 -04001017 return _mv88e6xxx_atu_wait(chip);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001018}
1019
Vivien Didelotfad09c72016-06-21 12:28:20 -04001020static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot37705b72015-09-04 14:34:11 -04001021 struct mv88e6xxx_atu_entry *entry)
1022{
1023 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1024
1025 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1026 unsigned int mask, shift;
1027
1028 if (entry->trunk) {
1029 data |= GLOBAL_ATU_DATA_TRUNK;
1030 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1031 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1032 } else {
1033 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1034 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1035 }
1036
1037 data |= (entry->portv_trunkid << shift) & mask;
1038 }
1039
Vivien Didelotfad09c72016-06-21 12:28:20 -04001040 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001041}
1042
Vivien Didelotfad09c72016-06-21 12:28:20 -04001043static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001044 struct mv88e6xxx_atu_entry *entry,
1045 bool static_too)
1046{
1047 int op;
1048 int err;
1049
Vivien Didelotfad09c72016-06-21 12:28:20 -04001050 err = _mv88e6xxx_atu_wait(chip);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001051 if (err)
1052 return err;
1053
Vivien Didelotfad09c72016-06-21 12:28:20 -04001054 err = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001055 if (err)
1056 return err;
1057
1058 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001059 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1060 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1061 } else {
1062 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1063 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1064 }
1065
Vivien Didelotfad09c72016-06-21 12:28:20 -04001066 return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001067}
1068
Vivien Didelotfad09c72016-06-21 12:28:20 -04001069static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001070 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001071{
1072 struct mv88e6xxx_atu_entry entry = {
1073 .fid = fid,
1074 .state = 0, /* EntryState bits must be 0 */
1075 };
1076
Vivien Didelotfad09c72016-06-21 12:28:20 -04001077 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001078}
1079
Vivien Didelotfad09c72016-06-21 12:28:20 -04001080static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001081 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001082{
1083 struct mv88e6xxx_atu_entry entry = {
1084 .trunk = false,
1085 .fid = fid,
1086 };
1087
1088 /* EntryState bits must be 0xF */
1089 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1090
1091 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1092 entry.portv_trunkid = (to_port & 0x0f) << 4;
1093 entry.portv_trunkid |= from_port & 0x0f;
1094
Vivien Didelotfad09c72016-06-21 12:28:20 -04001095 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001096}
1097
Vivien Didelotfad09c72016-06-21 12:28:20 -04001098static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001099 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001100{
1101 /* Destination port 0xF means remove the entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001102 return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001103}
1104
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001105static const char * const mv88e6xxx_port_state_names[] = {
1106 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1107 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1108 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1109 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1110};
1111
Vivien Didelotfad09c72016-06-21 12:28:20 -04001112static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001113 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001114{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001115 struct dsa_switch *ds = chip->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001116 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001117 u8 oldstate;
1118
Vivien Didelotfad09c72016-06-21 12:28:20 -04001119 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001120 if (reg < 0)
1121 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001122
Andrew Lunncca8b132015-04-02 04:06:39 +02001123 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001124
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001125 if (oldstate != state) {
1126 /* Flush forwarding database if we're moving a port
1127 * from Learning or Forwarding state to Disabled or
1128 * Blocking or Listening state.
1129 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001130 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
Vivien Didelot57d32312016-06-20 13:13:58 -04001131 oldstate == PORT_CONTROL_STATE_FORWARDING) &&
1132 (state == PORT_CONTROL_STATE_DISABLED ||
1133 state == PORT_CONTROL_STATE_BLOCKING)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001134 ret = _mv88e6xxx_atu_remove(chip, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001135 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001136 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001137 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001138
Andrew Lunncca8b132015-04-02 04:06:39 +02001139 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001140 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001141 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001142 if (ret)
1143 return ret;
1144
Andrew Lunnc8b09802016-06-04 21:16:57 +02001145 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001146 mv88e6xxx_port_state_names[state],
1147 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001148 }
1149
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001150 return ret;
1151}
1152
Vivien Didelotfad09c72016-06-21 12:28:20 -04001153static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001154{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001155 struct net_device *bridge = chip->ports[port].bridge_dev;
1156 const u16 mask = (1 << chip->info->num_ports) - 1;
1157 struct dsa_switch *ds = chip->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001158 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001159 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001160 int i;
1161
1162 /* allow CPU port or DSA link(s) to send frames to every port */
1163 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1164 output_ports = mask;
1165 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001166 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001167 /* allow sending frames to every group member */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001168 if (bridge && chip->ports[i].bridge_dev == bridge)
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001169 output_ports |= BIT(i);
1170
1171 /* allow sending frames to CPU port and DSA link(s) */
1172 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1173 output_ports |= BIT(i);
1174 }
1175 }
1176
1177 /* prevent frames from going back out of the port they came in on */
1178 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001179
Vivien Didelotfad09c72016-06-21 12:28:20 -04001180 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001181 if (reg < 0)
1182 return reg;
1183
1184 reg &= ~mask;
1185 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001186
Vivien Didelotfad09c72016-06-21 12:28:20 -04001187 return _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001188}
1189
Vivien Didelotf81ec902016-05-09 13:22:58 -04001190static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1191 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001192{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001193 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001194 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001195 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001196
1197 switch (state) {
1198 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001199 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001200 break;
1201 case BR_STATE_BLOCKING:
1202 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001203 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001204 break;
1205 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001206 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001207 break;
1208 case BR_STATE_FORWARDING:
1209 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001210 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001211 break;
1212 }
1213
Vivien Didelotfad09c72016-06-21 12:28:20 -04001214 mutex_lock(&chip->reg_lock);
1215 err = _mv88e6xxx_port_state(chip, port, stp_state);
1216 mutex_unlock(&chip->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001217
1218 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001219 netdev_err(ds->ports[port].netdev,
1220 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001221 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001222}
1223
Vivien Didelotfad09c72016-06-21 12:28:20 -04001224static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001225 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001226{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001227 struct dsa_switch *ds = chip->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001228 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001229 int ret;
1230
Vivien Didelotfad09c72016-06-21 12:28:20 -04001231 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001232 if (ret < 0)
1233 return ret;
1234
Vivien Didelot5da96032016-03-07 18:24:39 -05001235 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1236
1237 if (new) {
1238 ret &= ~PORT_DEFAULT_VLAN_MASK;
1239 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1240
Vivien Didelotfad09c72016-06-21 12:28:20 -04001241 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001242 PORT_DEFAULT_VLAN, ret);
1243 if (ret < 0)
1244 return ret;
1245
Andrew Lunnc8b09802016-06-04 21:16:57 +02001246 netdev_dbg(ds->ports[port].netdev,
1247 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001248 }
1249
1250 if (old)
1251 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001252
1253 return 0;
1254}
1255
Vivien Didelotfad09c72016-06-21 12:28:20 -04001256static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001257 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001258{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001259 return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001260}
1261
Vivien Didelotfad09c72016-06-21 12:28:20 -04001262static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001263 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001264{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001265 return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001266}
1267
Vivien Didelotfad09c72016-06-21 12:28:20 -04001268static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001269{
Vivien Didelot2d79af62016-08-15 17:18:57 -04001270 return mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_VTU_OP,
1271 GLOBAL_VTU_OP_BUSY);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001272}
1273
Vivien Didelotfad09c72016-06-21 12:28:20 -04001274static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001275{
1276 int ret;
1277
Vivien Didelotfad09c72016-06-21 12:28:20 -04001278 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001279 if (ret < 0)
1280 return ret;
1281
Vivien Didelotfad09c72016-06-21 12:28:20 -04001282 return _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001283}
1284
Vivien Didelotfad09c72016-06-21 12:28:20 -04001285static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001286{
1287 int ret;
1288
Vivien Didelotfad09c72016-06-21 12:28:20 -04001289 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001290 if (ret < 0)
1291 return ret;
1292
Vivien Didelotfad09c72016-06-21 12:28:20 -04001293 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001294}
1295
Vivien Didelotfad09c72016-06-21 12:28:20 -04001296static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001297 struct mv88e6xxx_vtu_stu_entry *entry,
1298 unsigned int nibble_offset)
1299{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001300 u16 regs[3];
1301 int i;
1302 int ret;
1303
1304 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001305 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001306 GLOBAL_VTU_DATA_0_3 + i);
1307 if (ret < 0)
1308 return ret;
1309
1310 regs[i] = ret;
1311 }
1312
Vivien Didelotfad09c72016-06-21 12:28:20 -04001313 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001314 unsigned int shift = (i % 4) * 4 + nibble_offset;
1315 u16 reg = regs[i / 4];
1316
1317 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1318 }
1319
1320 return 0;
1321}
1322
Vivien Didelotfad09c72016-06-21 12:28:20 -04001323static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001324 struct mv88e6xxx_vtu_stu_entry *entry)
1325{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001326 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001327}
1328
Vivien Didelotfad09c72016-06-21 12:28:20 -04001329static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001330 struct mv88e6xxx_vtu_stu_entry *entry)
1331{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001332 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001333}
1334
Vivien Didelotfad09c72016-06-21 12:28:20 -04001335static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001336 struct mv88e6xxx_vtu_stu_entry *entry,
1337 unsigned int nibble_offset)
1338{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001339 u16 regs[3] = { 0 };
1340 int i;
1341 int ret;
1342
Vivien Didelotfad09c72016-06-21 12:28:20 -04001343 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001344 unsigned int shift = (i % 4) * 4 + nibble_offset;
1345 u8 data = entry->data[i];
1346
1347 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1348 }
1349
1350 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001351 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001352 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1353 if (ret < 0)
1354 return ret;
1355 }
1356
1357 return 0;
1358}
1359
Vivien Didelotfad09c72016-06-21 12:28:20 -04001360static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001361 struct mv88e6xxx_vtu_stu_entry *entry)
1362{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001363 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001364}
1365
Vivien Didelotfad09c72016-06-21 12:28:20 -04001366static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001367 struct mv88e6xxx_vtu_stu_entry *entry)
1368{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001369 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001370}
1371
Vivien Didelotfad09c72016-06-21 12:28:20 -04001372static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001373{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001374 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001375 vid & GLOBAL_VTU_VID_MASK);
1376}
1377
Vivien Didelotfad09c72016-06-21 12:28:20 -04001378static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001379 struct mv88e6xxx_vtu_stu_entry *entry)
1380{
1381 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1382 int ret;
1383
Vivien Didelotfad09c72016-06-21 12:28:20 -04001384 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001385 if (ret < 0)
1386 return ret;
1387
Vivien Didelotfad09c72016-06-21 12:28:20 -04001388 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001389 if (ret < 0)
1390 return ret;
1391
Vivien Didelotfad09c72016-06-21 12:28:20 -04001392 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001393 if (ret < 0)
1394 return ret;
1395
1396 next.vid = ret & GLOBAL_VTU_VID_MASK;
1397 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1398
1399 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001400 ret = mv88e6xxx_vtu_data_read(chip, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001401 if (ret < 0)
1402 return ret;
1403
Vivien Didelotfad09c72016-06-21 12:28:20 -04001404 if (mv88e6xxx_has_fid_reg(chip)) {
1405 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001406 GLOBAL_VTU_FID);
1407 if (ret < 0)
1408 return ret;
1409
1410 next.fid = ret & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001411 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001412 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1413 * VTU DBNum[3:0] are located in VTU Operation 3:0
1414 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001415 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001416 GLOBAL_VTU_OP);
1417 if (ret < 0)
1418 return ret;
1419
1420 next.fid = (ret & 0xf00) >> 4;
1421 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001422 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001423
Vivien Didelotfad09c72016-06-21 12:28:20 -04001424 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1425 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001426 GLOBAL_VTU_SID);
1427 if (ret < 0)
1428 return ret;
1429
1430 next.sid = ret & GLOBAL_VTU_SID_MASK;
1431 }
1432 }
1433
1434 *entry = next;
1435 return 0;
1436}
1437
Vivien Didelotf81ec902016-05-09 13:22:58 -04001438static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1439 struct switchdev_obj_port_vlan *vlan,
1440 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001441{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001442 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001443 struct mv88e6xxx_vtu_stu_entry next;
1444 u16 pvid;
1445 int err;
1446
Vivien Didelotfad09c72016-06-21 12:28:20 -04001447 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001448 return -EOPNOTSUPP;
1449
Vivien Didelotfad09c72016-06-21 12:28:20 -04001450 mutex_lock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001451
Vivien Didelotfad09c72016-06-21 12:28:20 -04001452 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001453 if (err)
1454 goto unlock;
1455
Vivien Didelotfad09c72016-06-21 12:28:20 -04001456 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001457 if (err)
1458 goto unlock;
1459
1460 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001461 err = _mv88e6xxx_vtu_getnext(chip, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001462 if (err)
1463 break;
1464
1465 if (!next.valid)
1466 break;
1467
1468 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1469 continue;
1470
1471 /* reinit and dump this VLAN obj */
Vivien Didelot57d32312016-06-20 13:13:58 -04001472 vlan->vid_begin = next.vid;
1473 vlan->vid_end = next.vid;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001474 vlan->flags = 0;
1475
1476 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1477 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1478
1479 if (next.vid == pvid)
1480 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1481
1482 err = cb(&vlan->obj);
1483 if (err)
1484 break;
1485 } while (next.vid < GLOBAL_VTU_VID_MASK);
1486
1487unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001488 mutex_unlock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001489
1490 return err;
1491}
1492
Vivien Didelotfad09c72016-06-21 12:28:20 -04001493static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001494 struct mv88e6xxx_vtu_stu_entry *entry)
1495{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001496 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001497 u16 reg = 0;
1498 int ret;
1499
Vivien Didelotfad09c72016-06-21 12:28:20 -04001500 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001501 if (ret < 0)
1502 return ret;
1503
1504 if (!entry->valid)
1505 goto loadpurge;
1506
1507 /* Write port member tags */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001508 ret = mv88e6xxx_vtu_data_write(chip, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001509 if (ret < 0)
1510 return ret;
1511
Vivien Didelotfad09c72016-06-21 12:28:20 -04001512 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001513 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001514 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
1515 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001516 if (ret < 0)
1517 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001518 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001519
Vivien Didelotfad09c72016-06-21 12:28:20 -04001520 if (mv88e6xxx_has_fid_reg(chip)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001521 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001522 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_FID,
1523 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001524 if (ret < 0)
1525 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001526 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001527 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1528 * VTU DBNum[3:0] are located in VTU Operation 3:0
1529 */
1530 op |= (entry->fid & 0xf0) << 8;
1531 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001532 }
1533
1534 reg = GLOBAL_VTU_VID_VALID;
1535loadpurge:
1536 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001537 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001538 if (ret < 0)
1539 return ret;
1540
Vivien Didelotfad09c72016-06-21 12:28:20 -04001541 return _mv88e6xxx_vtu_cmd(chip, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001542}
1543
Vivien Didelotfad09c72016-06-21 12:28:20 -04001544static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001545 struct mv88e6xxx_vtu_stu_entry *entry)
1546{
1547 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1548 int ret;
1549
Vivien Didelotfad09c72016-06-21 12:28:20 -04001550 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001551 if (ret < 0)
1552 return ret;
1553
Vivien Didelotfad09c72016-06-21 12:28:20 -04001554 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001555 sid & GLOBAL_VTU_SID_MASK);
1556 if (ret < 0)
1557 return ret;
1558
Vivien Didelotfad09c72016-06-21 12:28:20 -04001559 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001560 if (ret < 0)
1561 return ret;
1562
Vivien Didelotfad09c72016-06-21 12:28:20 -04001563 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001564 if (ret < 0)
1565 return ret;
1566
1567 next.sid = ret & GLOBAL_VTU_SID_MASK;
1568
Vivien Didelotfad09c72016-06-21 12:28:20 -04001569 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001570 if (ret < 0)
1571 return ret;
1572
1573 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1574
1575 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001576 ret = mv88e6xxx_stu_data_read(chip, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001577 if (ret < 0)
1578 return ret;
1579 }
1580
1581 *entry = next;
1582 return 0;
1583}
1584
Vivien Didelotfad09c72016-06-21 12:28:20 -04001585static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001586 struct mv88e6xxx_vtu_stu_entry *entry)
1587{
1588 u16 reg = 0;
1589 int ret;
1590
Vivien Didelotfad09c72016-06-21 12:28:20 -04001591 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001592 if (ret < 0)
1593 return ret;
1594
1595 if (!entry->valid)
1596 goto loadpurge;
1597
1598 /* Write port states */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001599 ret = mv88e6xxx_stu_data_write(chip, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001600 if (ret < 0)
1601 return ret;
1602
1603 reg = GLOBAL_VTU_VID_VALID;
1604loadpurge:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001605 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001606 if (ret < 0)
1607 return ret;
1608
1609 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001610 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001611 if (ret < 0)
1612 return ret;
1613
Vivien Didelotfad09c72016-06-21 12:28:20 -04001614 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001615}
1616
Vivien Didelotfad09c72016-06-21 12:28:20 -04001617static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001618 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001619{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001620 struct dsa_switch *ds = chip->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001621 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001622 u16 fid;
1623 int ret;
1624
Vivien Didelotfad09c72016-06-21 12:28:20 -04001625 if (mv88e6xxx_num_databases(chip) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001626 upper_mask = 0xff;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001627 else if (mv88e6xxx_num_databases(chip) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001628 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001629 else
1630 return -EOPNOTSUPP;
1631
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001632 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001633 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001634 if (ret < 0)
1635 return ret;
1636
1637 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1638
1639 if (new) {
1640 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1641 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1642
Vivien Didelotfad09c72016-06-21 12:28:20 -04001643 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001644 ret);
1645 if (ret < 0)
1646 return ret;
1647 }
1648
1649 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001650 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001651 if (ret < 0)
1652 return ret;
1653
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001654 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001655
1656 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001657 ret &= ~upper_mask;
1658 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001659
Vivien Didelotfad09c72016-06-21 12:28:20 -04001660 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001661 ret);
1662 if (ret < 0)
1663 return ret;
1664
Andrew Lunnc8b09802016-06-04 21:16:57 +02001665 netdev_dbg(ds->ports[port].netdev,
1666 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001667 }
1668
1669 if (old)
1670 *old = fid;
1671
1672 return 0;
1673}
1674
Vivien Didelotfad09c72016-06-21 12:28:20 -04001675static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001676 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001677{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001678 return _mv88e6xxx_port_fid(chip, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001679}
1680
Vivien Didelotfad09c72016-06-21 12:28:20 -04001681static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001682 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001683{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001684 return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001685}
1686
Vivien Didelotfad09c72016-06-21 12:28:20 -04001687static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001688{
1689 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1690 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001691 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001692
1693 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1694
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001695 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001696 for (i = 0; i < chip->info->num_ports; ++i) {
1697 err = _mv88e6xxx_port_fid_get(chip, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001698 if (err)
1699 return err;
1700
1701 set_bit(*fid, fid_bitmap);
1702 }
1703
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001704 /* Set every FID bit used by the VLAN entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001705 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001706 if (err)
1707 return err;
1708
1709 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001710 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001711 if (err)
1712 return err;
1713
1714 if (!vlan.valid)
1715 break;
1716
1717 set_bit(vlan.fid, fid_bitmap);
1718 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1719
1720 /* The reset value 0x000 is used to indicate that multiple address
1721 * databases are not needed. Return the next positive available.
1722 */
1723 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotfad09c72016-06-21 12:28:20 -04001724 if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001725 return -ENOSPC;
1726
1727 /* Clear the database */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001728 return _mv88e6xxx_atu_flush(chip, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001729}
1730
Vivien Didelotfad09c72016-06-21 12:28:20 -04001731static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001732 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001733{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001734 struct dsa_switch *ds = chip->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001735 struct mv88e6xxx_vtu_stu_entry vlan = {
1736 .valid = true,
1737 .vid = vid,
1738 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001739 int i, err;
1740
Vivien Didelotfad09c72016-06-21 12:28:20 -04001741 err = _mv88e6xxx_fid_new(chip, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001742 if (err)
1743 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001744
Vivien Didelot3d131f02015-11-03 10:52:52 -05001745 /* exclude all ports except the CPU and DSA ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001746 for (i = 0; i < chip->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001747 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1748 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1749 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001750
Vivien Didelotfad09c72016-06-21 12:28:20 -04001751 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
1752 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001753 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001754
1755 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1756 * implemented, only one STU entry is needed to cover all VTU
1757 * entries. Thus, validate the SID 0.
1758 */
1759 vlan.sid = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001760 err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001761 if (err)
1762 return err;
1763
1764 if (vstp.sid != vlan.sid || !vstp.valid) {
1765 memset(&vstp, 0, sizeof(vstp));
1766 vstp.valid = true;
1767 vstp.sid = vlan.sid;
1768
Vivien Didelotfad09c72016-06-21 12:28:20 -04001769 err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001770 if (err)
1771 return err;
1772 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001773 }
1774
1775 *entry = vlan;
1776 return 0;
1777}
1778
Vivien Didelotfad09c72016-06-21 12:28:20 -04001779static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001780 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1781{
1782 int err;
1783
1784 if (!vid)
1785 return -EINVAL;
1786
Vivien Didelotfad09c72016-06-21 12:28:20 -04001787 err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001788 if (err)
1789 return err;
1790
Vivien Didelotfad09c72016-06-21 12:28:20 -04001791 err = _mv88e6xxx_vtu_getnext(chip, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001792 if (err)
1793 return err;
1794
1795 if (entry->vid != vid || !entry->valid) {
1796 if (!creat)
1797 return -EOPNOTSUPP;
1798 /* -ENOENT would've been more appropriate, but switchdev expects
1799 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1800 */
1801
Vivien Didelotfad09c72016-06-21 12:28:20 -04001802 err = _mv88e6xxx_vtu_new(chip, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001803 }
1804
1805 return err;
1806}
1807
Vivien Didelotda9c3592016-02-12 12:09:40 -05001808static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1809 u16 vid_begin, u16 vid_end)
1810{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001811 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001812 struct mv88e6xxx_vtu_stu_entry vlan;
1813 int i, err;
1814
1815 if (!vid_begin)
1816 return -EOPNOTSUPP;
1817
Vivien Didelotfad09c72016-06-21 12:28:20 -04001818 mutex_lock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001819
Vivien Didelotfad09c72016-06-21 12:28:20 -04001820 err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001821 if (err)
1822 goto unlock;
1823
1824 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001825 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001826 if (err)
1827 goto unlock;
1828
1829 if (!vlan.valid)
1830 break;
1831
1832 if (vlan.vid > vid_end)
1833 break;
1834
Vivien Didelotfad09c72016-06-21 12:28:20 -04001835 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001836 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1837 continue;
1838
1839 if (vlan.data[i] ==
1840 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1841 continue;
1842
Vivien Didelotfad09c72016-06-21 12:28:20 -04001843 if (chip->ports[i].bridge_dev ==
1844 chip->ports[port].bridge_dev)
Vivien Didelotda9c3592016-02-12 12:09:40 -05001845 break; /* same bridge, check next VLAN */
1846
Andrew Lunnc8b09802016-06-04 21:16:57 +02001847 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05001848 "hardware VLAN %d already used by %s\n",
1849 vlan.vid,
Vivien Didelotfad09c72016-06-21 12:28:20 -04001850 netdev_name(chip->ports[i].bridge_dev));
Vivien Didelotda9c3592016-02-12 12:09:40 -05001851 err = -EOPNOTSUPP;
1852 goto unlock;
1853 }
1854 } while (vlan.vid < vid_end);
1855
1856unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001857 mutex_unlock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001858
1859 return err;
1860}
1861
Vivien Didelot214cdb92016-02-26 13:16:08 -05001862static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1863 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1864 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1865 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1866 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1867};
1868
Vivien Didelotf81ec902016-05-09 13:22:58 -04001869static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1870 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05001871{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001872 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001873 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1874 PORT_CONTROL_2_8021Q_DISABLED;
1875 int ret;
1876
Vivien Didelotfad09c72016-06-21 12:28:20 -04001877 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001878 return -EOPNOTSUPP;
1879
Vivien Didelotfad09c72016-06-21 12:28:20 -04001880 mutex_lock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001881
Vivien Didelotfad09c72016-06-21 12:28:20 -04001882 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001883 if (ret < 0)
1884 goto unlock;
1885
1886 old = ret & PORT_CONTROL_2_8021Q_MASK;
1887
Vivien Didelot5220ef12016-03-07 18:24:52 -05001888 if (new != old) {
1889 ret &= ~PORT_CONTROL_2_8021Q_MASK;
1890 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001891
Vivien Didelotfad09c72016-06-21 12:28:20 -04001892 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05001893 ret);
1894 if (ret < 0)
1895 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001896
Andrew Lunnc8b09802016-06-04 21:16:57 +02001897 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05001898 mv88e6xxx_port_8021q_mode_names[new],
1899 mv88e6xxx_port_8021q_mode_names[old]);
1900 }
1901
1902 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001903unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001904 mutex_unlock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001905
1906 return ret;
1907}
1908
Vivien Didelot57d32312016-06-20 13:13:58 -04001909static int
1910mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1911 const struct switchdev_obj_port_vlan *vlan,
1912 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001913{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001914 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001915 int err;
1916
Vivien Didelotfad09c72016-06-21 12:28:20 -04001917 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001918 return -EOPNOTSUPP;
1919
Vivien Didelotda9c3592016-02-12 12:09:40 -05001920 /* If the requested port doesn't belong to the same bridge as the VLAN
1921 * members, do not support it (yet) and fallback to software VLAN.
1922 */
1923 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1924 vlan->vid_end);
1925 if (err)
1926 return err;
1927
Vivien Didelot76e398a2015-11-01 12:33:55 -05001928 /* We don't need any dynamic resource from the kernel (yet),
1929 * so skip the prepare phase.
1930 */
1931 return 0;
1932}
1933
Vivien Didelotfad09c72016-06-21 12:28:20 -04001934static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001935 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001936{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001937 struct mv88e6xxx_vtu_stu_entry vlan;
1938 int err;
1939
Vivien Didelotfad09c72016-06-21 12:28:20 -04001940 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001941 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001942 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001943
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001944 vlan.data[port] = untagged ?
1945 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1946 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1947
Vivien Didelotfad09c72016-06-21 12:28:20 -04001948 return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001949}
1950
Vivien Didelotf81ec902016-05-09 13:22:58 -04001951static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1952 const struct switchdev_obj_port_vlan *vlan,
1953 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001954{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001955 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001956 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1957 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1958 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001959
Vivien Didelotfad09c72016-06-21 12:28:20 -04001960 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001961 return;
1962
Vivien Didelotfad09c72016-06-21 12:28:20 -04001963 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001964
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001965 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Vivien Didelotfad09c72016-06-21 12:28:20 -04001966 if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02001967 netdev_err(ds->ports[port].netdev,
1968 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001969 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05001970
Vivien Didelotfad09c72016-06-21 12:28:20 -04001971 if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02001972 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001973 vlan->vid_end);
1974
Vivien Didelotfad09c72016-06-21 12:28:20 -04001975 mutex_unlock(&chip->reg_lock);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001976}
1977
Vivien Didelotfad09c72016-06-21 12:28:20 -04001978static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001979 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001980{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001981 struct dsa_switch *ds = chip->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001982 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001983 int i, err;
1984
Vivien Didelotfad09c72016-06-21 12:28:20 -04001985 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001986 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001987 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001988
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001989 /* Tell switchdev if this VLAN is handled in software */
1990 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05001991 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001992
1993 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1994
1995 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001996 vlan.valid = false;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001997 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05001998 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001999 continue;
2000
2001 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002002 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002003 break;
2004 }
2005 }
2006
Vivien Didelotfad09c72016-06-21 12:28:20 -04002007 err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002008 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002009 return err;
2010
Vivien Didelotfad09c72016-06-21 12:28:20 -04002011 return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002012}
2013
Vivien Didelotf81ec902016-05-09 13:22:58 -04002014static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2015 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002016{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002017 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002018 u16 pvid, vid;
2019 int err = 0;
2020
Vivien Didelotfad09c72016-06-21 12:28:20 -04002021 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002022 return -EOPNOTSUPP;
2023
Vivien Didelotfad09c72016-06-21 12:28:20 -04002024 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002025
Vivien Didelotfad09c72016-06-21 12:28:20 -04002026 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002027 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002028 goto unlock;
2029
Vivien Didelot76e398a2015-11-01 12:33:55 -05002030 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002031 err = _mv88e6xxx_port_vlan_del(chip, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002032 if (err)
2033 goto unlock;
2034
2035 if (vid == pvid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002036 err = _mv88e6xxx_port_pvid_set(chip, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002037 if (err)
2038 goto unlock;
2039 }
2040 }
2041
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002042unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002043 mutex_unlock(&chip->reg_lock);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002044
2045 return err;
2046}
2047
Vivien Didelotfad09c72016-06-21 12:28:20 -04002048static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002049 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002050{
2051 int i, ret;
2052
2053 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002054 ret = _mv88e6xxx_reg_write(
Vivien Didelotfad09c72016-06-21 12:28:20 -04002055 chip, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002056 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002057 if (ret < 0)
2058 return ret;
2059 }
2060
2061 return 0;
2062}
2063
Vivien Didelotfad09c72016-06-21 12:28:20 -04002064static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002065 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002066{
2067 int i, ret;
2068
2069 for (i = 0; i < 3; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002070 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002071 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002072 if (ret < 0)
2073 return ret;
2074 addr[i * 2] = ret >> 8;
2075 addr[i * 2 + 1] = ret & 0xff;
2076 }
2077
2078 return 0;
2079}
2080
Vivien Didelotfad09c72016-06-21 12:28:20 -04002081static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002082 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002083{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002084 int ret;
2085
Vivien Didelotfad09c72016-06-21 12:28:20 -04002086 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002087 if (ret < 0)
2088 return ret;
2089
Vivien Didelotfad09c72016-06-21 12:28:20 -04002090 ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002091 if (ret < 0)
2092 return ret;
2093
Vivien Didelotfad09c72016-06-21 12:28:20 -04002094 ret = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002095 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002096 return ret;
2097
Vivien Didelotfad09c72016-06-21 12:28:20 -04002098 return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002099}
David S. Millercdf09692015-08-11 12:00:37 -07002100
Vivien Didelotfad09c72016-06-21 12:28:20 -04002101static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_chip *chip, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002102 const unsigned char *addr, u16 vid,
2103 u8 state)
2104{
2105 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002106 struct mv88e6xxx_vtu_stu_entry vlan;
2107 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002108
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002109 /* Null VLAN ID corresponds to the port private database */
2110 if (vid == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002111 err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002112 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002113 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002114 if (err)
2115 return err;
2116
2117 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002118 entry.state = state;
2119 ether_addr_copy(entry.mac, addr);
2120 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2121 entry.trunk = false;
2122 entry.portv_trunkid = BIT(port);
2123 }
2124
Vivien Didelotfad09c72016-06-21 12:28:20 -04002125 return _mv88e6xxx_atu_load(chip, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002126}
2127
Vivien Didelotf81ec902016-05-09 13:22:58 -04002128static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2129 const struct switchdev_obj_port_fdb *fdb,
2130 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002131{
2132 /* We don't need any dynamic resource from the kernel (yet),
2133 * so skip the prepare phase.
2134 */
2135 return 0;
2136}
2137
Vivien Didelotf81ec902016-05-09 13:22:58 -04002138static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2139 const struct switchdev_obj_port_fdb *fdb,
2140 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002141{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002142 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002143 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2144 GLOBAL_ATU_DATA_STATE_UC_STATIC;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002145 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002146
Vivien Didelotfad09c72016-06-21 12:28:20 -04002147 mutex_lock(&chip->reg_lock);
2148 if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002149 netdev_err(ds->ports[port].netdev,
2150 "failed to load MAC address\n");
Vivien Didelotfad09c72016-06-21 12:28:20 -04002151 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002152}
2153
Vivien Didelotf81ec902016-05-09 13:22:58 -04002154static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2155 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002156{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002157 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
David S. Millercdf09692015-08-11 12:00:37 -07002158 int ret;
2159
Vivien Didelotfad09c72016-06-21 12:28:20 -04002160 mutex_lock(&chip->reg_lock);
2161 ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002162 GLOBAL_ATU_DATA_STATE_UNUSED);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002163 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002164
2165 return ret;
2166}
2167
Vivien Didelotfad09c72016-06-21 12:28:20 -04002168static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002169 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002170{
Vivien Didelot1d194042015-08-10 09:09:51 -04002171 struct mv88e6xxx_atu_entry next = { 0 };
2172 int ret;
2173
2174 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002175
Vivien Didelotfad09c72016-06-21 12:28:20 -04002176 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002177 if (ret < 0)
2178 return ret;
2179
Vivien Didelotfad09c72016-06-21 12:28:20 -04002180 ret = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002181 if (ret < 0)
2182 return ret;
2183
Vivien Didelotfad09c72016-06-21 12:28:20 -04002184 ret = _mv88e6xxx_atu_mac_read(chip, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002185 if (ret < 0)
2186 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002187
Vivien Didelotfad09c72016-06-21 12:28:20 -04002188 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002189 if (ret < 0)
2190 return ret;
2191
2192 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2193 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2194 unsigned int mask, shift;
2195
2196 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2197 next.trunk = true;
2198 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2199 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2200 } else {
2201 next.trunk = false;
2202 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2203 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2204 }
2205
2206 next.portv_trunkid = (ret & mask) >> shift;
2207 }
2208
2209 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002210 return 0;
2211}
2212
Vivien Didelotfad09c72016-06-21 12:28:20 -04002213static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002214 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002215 struct switchdev_obj_port_fdb *fdb,
2216 int (*cb)(struct switchdev_obj *obj))
2217{
2218 struct mv88e6xxx_atu_entry addr = {
2219 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2220 };
2221 int err;
2222
Vivien Didelotfad09c72016-06-21 12:28:20 -04002223 err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002224 if (err)
2225 return err;
2226
2227 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002228 err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002229 if (err)
2230 break;
2231
2232 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2233 break;
2234
2235 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2236 bool is_static = addr.state ==
2237 (is_multicast_ether_addr(addr.mac) ?
2238 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2239 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2240
2241 fdb->vid = vid;
2242 ether_addr_copy(fdb->addr, addr.mac);
2243 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2244
2245 err = cb(&fdb->obj);
2246 if (err)
2247 break;
2248 }
2249 } while (!is_broadcast_ether_addr(addr.mac));
2250
2251 return err;
2252}
2253
Vivien Didelotf81ec902016-05-09 13:22:58 -04002254static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2255 struct switchdev_obj_port_fdb *fdb,
2256 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002257{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002258 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002259 struct mv88e6xxx_vtu_stu_entry vlan = {
2260 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2261 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002262 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002263 int err;
2264
Vivien Didelotfad09c72016-06-21 12:28:20 -04002265 mutex_lock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002266
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002267 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002268 err = _mv88e6xxx_port_fid_get(chip, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002269 if (err)
2270 goto unlock;
2271
Vivien Didelotfad09c72016-06-21 12:28:20 -04002272 err = _mv88e6xxx_port_fdb_dump_one(chip, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002273 if (err)
2274 goto unlock;
2275
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002276 /* Dump VLANs' Filtering Information Databases */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002277 err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002278 if (err)
2279 goto unlock;
2280
2281 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002282 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002283 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002284 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002285
2286 if (!vlan.valid)
2287 break;
2288
Vivien Didelotfad09c72016-06-21 12:28:20 -04002289 err = _mv88e6xxx_port_fdb_dump_one(chip, vlan.fid, vlan.vid,
2290 port, fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002291 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002292 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002293 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2294
2295unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002296 mutex_unlock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002297
2298 return err;
2299}
2300
Vivien Didelotf81ec902016-05-09 13:22:58 -04002301static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2302 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002303{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002304 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002305 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002306
Vivien Didelotfad09c72016-06-21 12:28:20 -04002307 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002308
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002309 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002310 chip->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002311
Vivien Didelotfad09c72016-06-21 12:28:20 -04002312 for (i = 0; i < chip->info->num_ports; ++i) {
2313 if (chip->ports[i].bridge_dev == bridge) {
2314 err = _mv88e6xxx_port_based_vlan_map(chip, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002315 if (err)
2316 break;
2317 }
2318 }
2319
Vivien Didelotfad09c72016-06-21 12:28:20 -04002320 mutex_unlock(&chip->reg_lock);
Vivien Didelota6692752016-02-12 12:09:39 -05002321
Vivien Didelot466dfa02016-02-26 13:16:05 -05002322 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002323}
2324
Vivien Didelotf81ec902016-05-09 13:22:58 -04002325static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002326{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002327 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
2328 struct net_device *bridge = chip->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002329 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002330
Vivien Didelotfad09c72016-06-21 12:28:20 -04002331 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002332
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002333 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002334 chip->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002335
Vivien Didelotfad09c72016-06-21 12:28:20 -04002336 for (i = 0; i < chip->info->num_ports; ++i)
2337 if (i == port || chip->ports[i].bridge_dev == bridge)
2338 if (_mv88e6xxx_port_based_vlan_map(chip, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002339 netdev_warn(ds->ports[i].netdev,
2340 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002341
Vivien Didelotfad09c72016-06-21 12:28:20 -04002342 mutex_unlock(&chip->reg_lock);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002343}
2344
Vivien Didelotfad09c72016-06-21 12:28:20 -04002345static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
Vivien Didelot552238b2016-05-09 13:22:49 -04002346{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002347 bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
Vivien Didelot552238b2016-05-09 13:22:49 -04002348 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002349 struct gpio_desc *gpiod = chip->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002350 unsigned long timeout;
2351 int ret;
2352 int i;
2353
2354 /* Set all ports to the disabled state. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002355 for (i = 0; i < chip->info->num_ports; i++) {
2356 ret = _mv88e6xxx_reg_read(chip, REG_PORT(i), PORT_CONTROL);
Vivien Didelot552238b2016-05-09 13:22:49 -04002357 if (ret < 0)
2358 return ret;
2359
Vivien Didelotfad09c72016-06-21 12:28:20 -04002360 ret = _mv88e6xxx_reg_write(chip, REG_PORT(i), PORT_CONTROL,
Vivien Didelot552238b2016-05-09 13:22:49 -04002361 ret & 0xfffc);
2362 if (ret)
2363 return ret;
2364 }
2365
2366 /* Wait for transmit queues to drain. */
2367 usleep_range(2000, 4000);
2368
2369 /* If there is a gpio connected to the reset pin, toggle it */
2370 if (gpiod) {
2371 gpiod_set_value_cansleep(gpiod, 1);
2372 usleep_range(10000, 20000);
2373 gpiod_set_value_cansleep(gpiod, 0);
2374 usleep_range(10000, 20000);
2375 }
2376
2377 /* Reset the switch. Keep the PPU active if requested. The PPU
2378 * needs to be active to support indirect phy register access
2379 * through global registers 0x18 and 0x19.
2380 */
2381 if (ppu_active)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002382 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc000);
Vivien Didelot552238b2016-05-09 13:22:49 -04002383 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002384 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc400);
Vivien Didelot552238b2016-05-09 13:22:49 -04002385 if (ret)
2386 return ret;
2387
2388 /* Wait up to one second for reset to complete. */
2389 timeout = jiffies + 1 * HZ;
2390 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002391 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, 0x00);
Vivien Didelot552238b2016-05-09 13:22:49 -04002392 if (ret < 0)
2393 return ret;
2394
2395 if ((ret & is_reset) == is_reset)
2396 break;
2397 usleep_range(1000, 2000);
2398 }
2399 if (time_after(jiffies, timeout))
2400 ret = -ETIMEDOUT;
2401 else
2402 ret = 0;
2403
2404 return ret;
2405}
2406
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002407static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002408{
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002409 u16 val;
2410 int err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002411
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002412 /* Clear Power Down bit */
2413 err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
2414 if (err)
2415 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002416
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002417 if (val & BMCR_PDOWN) {
2418 val &= ~BMCR_PDOWN;
2419 err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002420 }
2421
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002422 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002423}
2424
Vivien Didelot8f6345b2016-07-20 18:18:36 -04002425static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port,
2426 int reg, u16 *val)
2427{
2428 int addr = chip->info->port_base_addr + port;
2429
2430 if (port >= chip->info->num_ports)
2431 return -EINVAL;
2432
2433 return mv88e6xxx_read(chip, addr, reg, val);
2434}
2435
Vivien Didelotfad09c72016-06-21 12:28:20 -04002436static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002437{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002438 struct dsa_switch *ds = chip->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002439 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002440 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002441
Vivien Didelotfad09c72016-06-21 12:28:20 -04002442 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2443 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2444 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2445 mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002446 /* MAC Forcing register: don't force link, speed,
2447 * duplex or flow control state to any particular
2448 * values on physical ports, but force the CPU port
2449 * and all DSA ports to their maximum bandwidth and
2450 * full duplex.
2451 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002452 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002453 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002454 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002455 reg |= PORT_PCS_CTRL_FORCE_LINK |
2456 PORT_PCS_CTRL_LINK_UP |
2457 PORT_PCS_CTRL_DUPLEX_FULL |
2458 PORT_PCS_CTRL_FORCE_DUPLEX;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002459 if (mv88e6xxx_6065_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002460 reg |= PORT_PCS_CTRL_100;
2461 else
2462 reg |= PORT_PCS_CTRL_1000;
2463 } else {
2464 reg |= PORT_PCS_CTRL_UNFORCED;
2465 }
2466
Vivien Didelotfad09c72016-06-21 12:28:20 -04002467 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002468 PORT_PCS_CTRL, reg);
2469 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002470 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002471 }
2472
2473 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2474 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2475 * tunneling, determine priority by looking at 802.1p and IP
2476 * priority fields (IP prio has precedence), and set STP state
2477 * to Forwarding.
2478 *
2479 * If this is the CPU link, use DSA or EDSA tagging depending
2480 * on which tagging mode was configured.
2481 *
2482 * If this is a link to another switch, use DSA tagging mode.
2483 *
2484 * If this is the upstream port for this switch, enable
2485 * forwarding of unknown unicasts and multicasts.
2486 */
2487 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002488 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2489 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2490 mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2491 mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002492 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2493 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2494 PORT_CONTROL_STATE_FORWARDING;
2495 if (dsa_is_cpu_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002496 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002497 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002498 if (mv88e6xxx_6352_family(chip) ||
2499 mv88e6xxx_6351_family(chip) ||
2500 mv88e6xxx_6165_family(chip) ||
2501 mv88e6xxx_6097_family(chip) ||
2502 mv88e6xxx_6320_family(chip)) {
Andrew Lunn5377b802016-06-04 21:17:02 +02002503 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2504 PORT_CONTROL_FORWARD_UNKNOWN |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002505 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002506 }
2507
Vivien Didelotfad09c72016-06-21 12:28:20 -04002508 if (mv88e6xxx_6352_family(chip) ||
2509 mv88e6xxx_6351_family(chip) ||
2510 mv88e6xxx_6165_family(chip) ||
2511 mv88e6xxx_6097_family(chip) ||
2512 mv88e6xxx_6095_family(chip) ||
2513 mv88e6xxx_6065_family(chip) ||
2514 mv88e6xxx_6185_family(chip) ||
2515 mv88e6xxx_6320_family(chip)) {
Vivien Didelot57d32312016-06-20 13:13:58 -04002516 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002517 }
2518 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002519 if (dsa_is_dsa_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002520 if (mv88e6xxx_6095_family(chip) ||
2521 mv88e6xxx_6185_family(chip))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002522 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002523 if (mv88e6xxx_6352_family(chip) ||
2524 mv88e6xxx_6351_family(chip) ||
2525 mv88e6xxx_6165_family(chip) ||
2526 mv88e6xxx_6097_family(chip) ||
2527 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002528 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002529 }
2530
Andrew Lunn54d792f2015-05-06 01:09:47 +02002531 if (port == dsa_upstream_port(ds))
2532 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2533 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2534 }
2535 if (reg) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002536 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002537 PORT_CONTROL, reg);
2538 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002539 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002540 }
2541
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002542 /* If this port is connected to a SerDes, make sure the SerDes is not
2543 * powered down.
2544 */
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002545 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002546 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002547 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002548 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002549 ret &= PORT_STATUS_CMODE_MASK;
2550 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2551 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2552 (ret == PORT_STATUS_CMODE_SGMII)) {
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002553 ret = mv88e6xxx_serdes_power_on(chip);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002554 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002555 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002556 }
2557 }
2558
Vivien Didelot8efdda42015-08-13 12:52:23 -04002559 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002560 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002561 * untagged frames on this port, do a destination address lookup on all
2562 * received packets as usual, disable ARP mirroring and don't send a
2563 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002564 */
2565 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002566 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2567 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2568 mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2569 mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002570 reg = PORT_CONTROL_2_MAP_DA;
2571
Vivien Didelotfad09c72016-06-21 12:28:20 -04002572 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2573 mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002574 reg |= PORT_CONTROL_2_JUMBO_10240;
2575
Vivien Didelotfad09c72016-06-21 12:28:20 -04002576 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002577 /* Set the upstream port this port should use */
2578 reg |= dsa_upstream_port(ds);
2579 /* enable forwarding of unknown multicast addresses to
2580 * the upstream port
2581 */
2582 if (port == dsa_upstream_port(ds))
2583 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2584 }
2585
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002586 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002587
Andrew Lunn54d792f2015-05-06 01:09:47 +02002588 if (reg) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002589 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002590 PORT_CONTROL_2, reg);
2591 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002592 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002593 }
2594
2595 /* Port Association Vector: when learning source addresses
2596 * of packets, add the address to the address database using
2597 * a port bitmap that has only the bit for this port set and
2598 * the other bits clear.
2599 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002600 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002601 /* Disable learning for CPU port */
2602 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002603 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002604
Vivien Didelotfad09c72016-06-21 12:28:20 -04002605 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_ASSOC_VECTOR,
2606 reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002607 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002608 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002609
2610 /* Egress rate control 2: disable egress rate control. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002611 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002612 0x0000);
2613 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002614 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002615
Vivien Didelotfad09c72016-06-21 12:28:20 -04002616 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2617 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2618 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002619 /* Do not limit the period of time that this port can
2620 * be paused for by the remote end or the period of
2621 * time that this port can pause the remote end.
2622 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002623 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002624 PORT_PAUSE_CTRL, 0x0000);
2625 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002626 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002627
2628 /* Port ATU control: disable limiting the number of
2629 * address database entries that this port is allowed
2630 * to use.
2631 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002632 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002633 PORT_ATU_CONTROL, 0x0000);
2634 /* Priority Override: disable DA, SA and VTU priority
2635 * override.
2636 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002637 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002638 PORT_PRI_OVERRIDE, 0x0000);
2639 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002640 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002641
2642 /* Port Ethertype: use the Ethertype DSA Ethertype
2643 * value.
2644 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002645 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002646 PORT_ETH_TYPE, ETH_P_EDSA);
2647 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002648 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002649 /* Tag Remap: use an identity 802.1p prio -> switch
2650 * prio mapping.
2651 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002652 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002653 PORT_TAG_REGMAP_0123, 0x3210);
2654 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002655 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002656
2657 /* Tag Remap 2: use an identity 802.1p prio -> switch
2658 * prio mapping.
2659 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002660 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002661 PORT_TAG_REGMAP_4567, 0x7654);
2662 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002663 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002664 }
2665
Vivien Didelotfad09c72016-06-21 12:28:20 -04002666 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2667 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2668 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2669 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002670 /* Rate Control: disable ingress rate limiting. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002671 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002672 PORT_RATE_CONTROL, 0x0001);
2673 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002674 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002675 }
2676
Guenter Roeck366f0a02015-03-26 18:36:30 -07002677 /* Port Control 1: disable trunking, disable sending
2678 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002679 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002680 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_1,
2681 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002682 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002683 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002684
Vivien Didelot207afda2016-04-14 14:42:09 -04002685 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002686 * database, and allow bidirectional communication between the
2687 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002688 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002689 ret = _mv88e6xxx_port_fid_set(chip, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002690 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002691 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002692
Vivien Didelotfad09c72016-06-21 12:28:20 -04002693 ret = _mv88e6xxx_port_based_vlan_map(chip, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002694 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002695 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002696
2697 /* Default VLAN ID and priority: don't set a default VLAN
2698 * ID, and set the default packet priority to zero.
2699 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002700 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002701 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002702 if (ret)
2703 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002704
Andrew Lunndbde9e62015-05-06 01:09:48 +02002705 return 0;
2706}
2707
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002708static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
2709{
2710 int err;
2711
2712 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
2713 (addr[0] << 8) | addr[1]);
2714 if (err)
2715 return err;
2716
2717 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
2718 (addr[2] << 8) | addr[3]);
2719 if (err)
2720 return err;
2721
2722 return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
2723 (addr[4] << 8) | addr[5]);
2724}
2725
Vivien Didelotacddbd22016-07-18 20:45:39 -04002726static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
2727 unsigned int msecs)
2728{
2729 const unsigned int coeff = chip->info->age_time_coeff;
2730 const unsigned int min = 0x01 * coeff;
2731 const unsigned int max = 0xff * coeff;
2732 u8 age_time;
2733 u16 val;
2734 int err;
2735
2736 if (msecs < min || msecs > max)
2737 return -ERANGE;
2738
2739 /* Round to nearest multiple of coeff */
2740 age_time = (msecs + coeff / 2) / coeff;
2741
2742 err = mv88e6xxx_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, &val);
2743 if (err)
2744 return err;
2745
2746 /* AgeTime is 11:4 bits */
2747 val &= ~0xff0;
2748 val |= age_time << 4;
2749
2750 return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, val);
2751}
2752
Vivien Didelot2cfcd962016-07-18 20:45:40 -04002753static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
2754 unsigned int ageing_time)
2755{
2756 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
2757 int err;
2758
2759 mutex_lock(&chip->reg_lock);
2760 err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
2761 mutex_unlock(&chip->reg_lock);
2762
2763 return err;
2764}
2765
Vivien Didelot97299342016-07-18 20:45:30 -04002766static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
Vivien Didelot08a01262016-05-09 13:22:50 -04002767{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002768 struct dsa_switch *ds = chip->ds;
Vivien Didelotb0745e872016-05-09 13:22:53 -04002769 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002770 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002771 int err;
Vivien Didelot08a01262016-05-09 13:22:50 -04002772
Vivien Didelot119477b2016-05-09 13:22:51 -04002773 /* Enable the PHY Polling Unit if present, don't discard any packets,
2774 * and mask all interrupt sources.
2775 */
2776 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002777 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
2778 mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
Vivien Didelot119477b2016-05-09 13:22:51 -04002779 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2780
Vivien Didelotfad09c72016-06-21 12:28:20 -04002781 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL, reg);
Vivien Didelot119477b2016-05-09 13:22:51 -04002782 if (err)
2783 return err;
2784
Vivien Didelotb0745e872016-05-09 13:22:53 -04002785 /* Configure the upstream port, and configure it as the port to which
2786 * ingress and egress and ARP monitor frames are to be sent.
2787 */
2788 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2789 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2790 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002791 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MONITOR_CONTROL,
2792 reg);
Vivien Didelotb0745e872016-05-09 13:22:53 -04002793 if (err)
2794 return err;
2795
Vivien Didelot50484ff2016-05-09 13:22:54 -04002796 /* Disable remote management, and set the switch's DSA device number. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002797 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL_2,
Vivien Didelot50484ff2016-05-09 13:22:54 -04002798 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2799 (ds->index & 0x1f));
2800 if (err)
2801 return err;
2802
Vivien Didelotacddbd22016-07-18 20:45:39 -04002803 /* Clear all the VTU and STU entries */
2804 err = _mv88e6xxx_vtu_stu_flush(chip);
2805 if (err < 0)
2806 return err;
2807
Vivien Didelot08a01262016-05-09 13:22:50 -04002808 /* Set the default address aging time to 5 minutes, and
2809 * enable address learn messages to be sent to all message
2810 * ports.
2811 */
Vivien Didelotacddbd22016-07-18 20:45:39 -04002812 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2813 GLOBAL_ATU_CONTROL_LEARN2ALL);
Vivien Didelot08a01262016-05-09 13:22:50 -04002814 if (err)
2815 return err;
2816
Vivien Didelotacddbd22016-07-18 20:45:39 -04002817 err = mv88e6xxx_g1_set_age_time(chip, 300000);
2818 if (err)
Vivien Didelot97299342016-07-18 20:45:30 -04002819 return err;
2820
2821 /* Clear all ATU entries */
2822 err = _mv88e6xxx_atu_flush(chip, 0, true);
2823 if (err)
2824 return err;
2825
Vivien Didelot08a01262016-05-09 13:22:50 -04002826 /* Configure the IP ToS mapping registers. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002827 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002828 if (err)
2829 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002830 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002831 if (err)
2832 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002833 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002834 if (err)
2835 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002836 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002837 if (err)
2838 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002839 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04002840 if (err)
2841 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002842 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04002843 if (err)
2844 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002845 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04002846 if (err)
2847 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002848 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04002849 if (err)
2850 return err;
2851
2852 /* Configure the IEEE 802.1p priority mapping register. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002853 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Vivien Didelot08a01262016-05-09 13:22:50 -04002854 if (err)
2855 return err;
2856
Vivien Didelot97299342016-07-18 20:45:30 -04002857 /* Clear the statistics counters for all ports */
2858 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
2859 GLOBAL_STATS_OP_FLUSH_ALL);
2860 if (err)
2861 return err;
2862
2863 /* Wait for the flush to complete. */
2864 err = _mv88e6xxx_stats_wait(chip);
2865 if (err)
2866 return err;
2867
2868 return 0;
2869}
2870
Vivien Didelotf22ab642016-07-18 20:45:31 -04002871static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
2872 int target, int port)
2873{
2874 u16 val = (target << 8) | (port & 0xf);
2875
2876 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, val);
2877}
2878
2879static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
2880{
2881 int target, port;
2882 int err;
2883
2884 /* Initialize the routing port to the 32 possible target devices */
2885 for (target = 0; target < 32; ++target) {
2886 port = 0xf;
2887
2888 if (target < DSA_MAX_SWITCHES) {
2889 port = chip->ds->rtable[target];
2890 if (port == DSA_RTABLE_NONE)
2891 port = 0xf;
2892 }
2893
2894 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
2895 if (err)
2896 break;
2897 }
2898
2899 return err;
2900}
2901
Vivien Didelot51540412016-07-18 20:45:32 -04002902static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
2903 bool hask, u16 mask)
2904{
2905 const u16 port_mask = BIT(chip->info->num_ports) - 1;
2906 u16 val = (num << 12) | (mask & port_mask);
2907
2908 if (hask)
2909 val |= GLOBAL2_TRUNK_MASK_HASK;
2910
2911 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, val);
2912}
2913
2914static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
2915 u16 map)
2916{
2917 const u16 port_mask = BIT(chip->info->num_ports) - 1;
2918 u16 val = (id << 11) | (map & port_mask);
2919
2920 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, val);
2921}
2922
2923static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
2924{
2925 const u16 port_mask = BIT(chip->info->num_ports) - 1;
2926 int i, err;
2927
2928 /* Clear all eight possible Trunk Mask vectors */
2929 for (i = 0; i < 8; ++i) {
2930 err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
2931 if (err)
2932 return err;
2933 }
2934
2935 /* Clear all sixteen possible Trunk ID routing vectors */
2936 for (i = 0; i < 16; ++i) {
2937 err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
2938 if (err)
2939 return err;
2940 }
2941
2942 return 0;
2943}
2944
Vivien Didelot8ec61c72016-07-18 20:45:37 -04002945static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
2946{
2947 int port, err;
2948
2949 /* Init all Ingress Rate Limit resources of all ports */
2950 for (port = 0; port < chip->info->num_ports; ++port) {
2951 /* XXX newer chips (like 88E6390) have different 2-bit ops */
2952 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
2953 GLOBAL2_IRL_CMD_OP_INIT_ALL |
2954 (port << 8));
2955 if (err)
2956 break;
2957
2958 /* Wait for the operation to complete */
Vivien Didelot2d79af62016-08-15 17:18:57 -04002959 err = mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
2960 GLOBAL2_IRL_CMD_BUSY);
Vivien Didelot8ec61c72016-07-18 20:45:37 -04002961 if (err)
2962 break;
2963 }
2964
2965 return err;
2966}
2967
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002968/* Indirect write to the Switch MAC/WoL/WoF register */
2969static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
2970 unsigned int pointer, u8 data)
2971{
2972 u16 val = (pointer << 8) | data;
2973
2974 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC, val);
2975}
2976
2977static int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
2978{
2979 int i, err;
2980
2981 for (i = 0; i < 6; i++) {
2982 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
2983 if (err)
2984 break;
2985 }
2986
2987 return err;
2988}
2989
Vivien Didelot9bda8892016-07-18 20:45:36 -04002990static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
2991 u8 data)
2992{
2993 u16 val = (pointer << 8) | (data & 0x7);
2994
2995 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, val);
2996}
2997
2998static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
2999{
3000 int i, err;
3001
3002 /* Clear all sixteen possible Priority Override entries */
3003 for (i = 0; i < 16; i++) {
3004 err = mv88e6xxx_g2_pot_write(chip, i, 0);
3005 if (err)
3006 break;
3007 }
3008
3009 return err;
3010}
3011
Vivien Didelot855b1932016-07-20 18:18:35 -04003012static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
3013{
Vivien Didelot2d79af62016-08-15 17:18:57 -04003014 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD,
3015 GLOBAL2_EEPROM_CMD_BUSY |
3016 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelot855b1932016-07-20 18:18:35 -04003017}
3018
3019static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
3020{
3021 int err;
3022
3023 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, cmd);
3024 if (err)
3025 return err;
3026
3027 return mv88e6xxx_g2_eeprom_wait(chip);
3028}
3029
3030static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
3031 u8 addr, u16 *data)
3032{
3033 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
3034 int err;
3035
3036 err = mv88e6xxx_g2_eeprom_wait(chip);
3037 if (err)
3038 return err;
3039
3040 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
3041 if (err)
3042 return err;
3043
3044 return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
3045}
3046
3047static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
3048 u8 addr, u16 data)
3049{
3050 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
3051 int err;
3052
3053 err = mv88e6xxx_g2_eeprom_wait(chip);
3054 if (err)
3055 return err;
3056
3057 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
3058 if (err)
3059 return err;
3060
3061 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
3062}
3063
Vivien Didelot57c67cf2016-08-15 17:18:59 -04003064static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
3065{
3066 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_CMD,
3067 GLOBAL2_SMI_PHY_CMD_BUSY);
3068}
3069
3070static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
3071{
3072 int err;
3073
3074 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_CMD, cmd);
3075 if (err)
3076 return err;
3077
3078 return mv88e6xxx_g2_smi_phy_wait(chip);
3079}
3080
3081static int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int addr,
3082 int reg, u16 *val)
3083{
3084 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
3085 int err;
3086
3087 err = mv88e6xxx_g2_smi_phy_wait(chip);
3088 if (err)
3089 return err;
3090
3091 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
3092 if (err)
3093 return err;
3094
3095 return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_DATA, val);
3096}
3097
3098static int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr,
3099 int reg, u16 val)
3100{
3101 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
3102 int err;
3103
3104 err = mv88e6xxx_g2_smi_phy_wait(chip);
3105 if (err)
3106 return err;
3107
3108 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_DATA, val);
3109 if (err)
3110 return err;
3111
3112 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
3113}
3114
Vivien Didelote57e5e72016-08-15 17:19:00 -04003115static const struct mv88e6xxx_ops mv88e6xxx_g2_smi_phy_ops = {
3116 .read = mv88e6xxx_g2_smi_phy_read,
3117 .write = mv88e6xxx_g2_smi_phy_write,
3118};
3119
Vivien Didelot97299342016-07-18 20:45:30 -04003120static int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
3121{
Vivien Didelot47395ed2016-07-18 20:45:33 -04003122 u16 reg;
Vivien Didelot97299342016-07-18 20:45:30 -04003123 int err;
Vivien Didelot97299342016-07-18 20:45:30 -04003124
Vivien Didelot47395ed2016-07-18 20:45:33 -04003125 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
3126 /* Consider the frames with reserved multicast destination
3127 * addresses matching 01:80:c2:00:00:2x as MGMT.
3128 */
3129 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_2X,
3130 0xffff);
3131 if (err)
3132 return err;
3133 }
3134
3135 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) {
3136 /* Consider the frames with reserved multicast destination
3137 * addresses matching 01:80:c2:00:00:0x as MGMT.
3138 */
3139 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
3140 0xffff);
3141 if (err)
3142 return err;
3143 }
Vivien Didelot08a01262016-05-09 13:22:50 -04003144
3145 /* Ignore removed tag data on doubly tagged packets, disable
3146 * flow control messages, force flow control priority to the
3147 * highest, and send all special multicast frames to the CPU
3148 * port at the highest priority.
3149 */
Vivien Didelot47395ed2016-07-18 20:45:33 -04003150 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
3151 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
3152 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
3153 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
3154 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, reg);
Vivien Didelot08a01262016-05-09 13:22:50 -04003155 if (err)
3156 return err;
3157
3158 /* Program the DSA routing table. */
Vivien Didelotf22ab642016-07-18 20:45:31 -04003159 err = mv88e6xxx_g2_set_device_mapping(chip);
3160 if (err)
3161 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003162
Vivien Didelot51540412016-07-18 20:45:32 -04003163 /* Clear all trunk masks and mapping. */
3164 err = mv88e6xxx_g2_clear_trunk(chip);
3165 if (err)
3166 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003167
Vivien Didelot8ec61c72016-07-18 20:45:37 -04003168 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
3169 /* Disable ingress rate limiting by resetting all per port
3170 * ingress rate limit resources to their initial state.
3171 */
3172 err = mv88e6xxx_g2_clear_irl(chip);
3173 if (err)
3174 return err;
3175 }
3176
Vivien Didelot63ed8802016-07-18 20:45:35 -04003177 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
3178 /* Initialize Cross-chip Port VLAN Table to reset defaults */
3179 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_PVT_ADDR,
3180 GLOBAL2_PVT_ADDR_OP_INIT_ONES);
3181 if (err)
3182 return err;
3183 }
3184
Vivien Didelot9bda8892016-07-18 20:45:36 -04003185 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
Vivien Didelot08a01262016-05-09 13:22:50 -04003186 /* Clear the priority override table. */
Vivien Didelot9bda8892016-07-18 20:45:36 -04003187 err = mv88e6xxx_g2_clear_pot(chip);
3188 if (err)
3189 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003190 }
3191
Vivien Didelot97299342016-07-18 20:45:30 -04003192 return 0;
Vivien Didelot08a01262016-05-09 13:22:50 -04003193}
3194
Vivien Didelotf81ec902016-05-09 13:22:58 -04003195static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003196{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003197 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003198 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003199 int i;
3200
Vivien Didelotfad09c72016-06-21 12:28:20 -04003201 chip->ds = ds;
3202 ds->slave_mii_bus = chip->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003203
Vivien Didelotfad09c72016-06-21 12:28:20 -04003204 mutex_lock(&chip->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04003205
Vivien Didelotfad09c72016-06-21 12:28:20 -04003206 err = mv88e6xxx_switch_reset(chip);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003207 if (err)
3208 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003209
Vivien Didelot97299342016-07-18 20:45:30 -04003210 /* Setup Switch Port Registers */
3211 for (i = 0; i < chip->info->num_ports; i++) {
3212 err = mv88e6xxx_setup_port(chip, i);
3213 if (err)
3214 goto unlock;
3215 }
3216
3217 /* Setup Switch Global 1 Registers */
3218 err = mv88e6xxx_g1_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003219 if (err)
3220 goto unlock;
3221
Vivien Didelot97299342016-07-18 20:45:30 -04003222 /* Setup Switch Global 2 Registers */
3223 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
3224 err = mv88e6xxx_g2_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003225 if (err)
3226 goto unlock;
3227 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003228
Vivien Didelot6b17e862015-08-13 12:52:18 -04003229unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003230 mutex_unlock(&chip->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02003231
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003232 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003233}
3234
Vivien Didelot3b4caa12016-07-18 20:45:34 -04003235static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
3236{
3237 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3238 int err;
3239
3240 mutex_lock(&chip->reg_lock);
3241
3242 /* Has an indirect Switch MAC/WoL/WoF register in Global 2? */
3243 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_SWITCH_MAC))
3244 err = mv88e6xxx_g2_set_switch_mac(chip, addr);
3245 else
3246 err = mv88e6xxx_g1_set_switch_mac(chip, addr);
3247
3248 mutex_unlock(&chip->reg_lock);
3249
3250 return err;
3251}
3252
Vivien Didelote57e5e72016-08-15 17:19:00 -04003253static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003254{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003255 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04003256 u16 val;
3257 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003258
Vivien Didelote57e5e72016-08-15 17:19:00 -04003259 if (phy >= chip->info->num_ports)
Andrew Lunn158bc062016-04-28 21:24:06 -04003260 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003261
Vivien Didelotfad09c72016-06-21 12:28:20 -04003262 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003263 err = mv88e6xxx_phy_read(chip, phy, reg, &val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003264 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003265
3266 return err ? err : val;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003267}
3268
Vivien Didelote57e5e72016-08-15 17:19:00 -04003269static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003270{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003271 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04003272 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003273
Vivien Didelote57e5e72016-08-15 17:19:00 -04003274 if (phy >= chip->info->num_ports)
Andrew Lunn158bc062016-04-28 21:24:06 -04003275 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003276
Vivien Didelotfad09c72016-06-21 12:28:20 -04003277 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003278 err = mv88e6xxx_phy_write(chip, phy, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003279 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003280
3281 return err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003282}
3283
Vivien Didelotfad09c72016-06-21 12:28:20 -04003284static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
Andrew Lunnb516d452016-06-04 21:17:06 +02003285 struct device_node *np)
3286{
3287 static int index;
3288 struct mii_bus *bus;
3289 int err;
3290
Andrew Lunnb516d452016-06-04 21:17:06 +02003291 if (np)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003292 chip->mdio_np = of_get_child_by_name(np, "mdio");
Andrew Lunnb516d452016-06-04 21:17:06 +02003293
Vivien Didelotfad09c72016-06-21 12:28:20 -04003294 bus = devm_mdiobus_alloc(chip->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003295 if (!bus)
3296 return -ENOMEM;
3297
Vivien Didelotfad09c72016-06-21 12:28:20 -04003298 bus->priv = (void *)chip;
Andrew Lunnb516d452016-06-04 21:17:06 +02003299 if (np) {
3300 bus->name = np->full_name;
3301 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3302 } else {
3303 bus->name = "mv88e6xxx SMI";
3304 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3305 }
3306
3307 bus->read = mv88e6xxx_mdio_read;
3308 bus->write = mv88e6xxx_mdio_write;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003309 bus->parent = chip->dev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003310
Vivien Didelotfad09c72016-06-21 12:28:20 -04003311 if (chip->mdio_np)
3312 err = of_mdiobus_register(bus, chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003313 else
3314 err = mdiobus_register(bus);
3315 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003316 dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
Andrew Lunnb516d452016-06-04 21:17:06 +02003317 goto out;
3318 }
Vivien Didelotfad09c72016-06-21 12:28:20 -04003319 chip->mdio_bus = bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003320
3321 return 0;
3322
3323out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003324 if (chip->mdio_np)
3325 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003326
3327 return err;
3328}
3329
Vivien Didelotfad09c72016-06-21 12:28:20 -04003330static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
Andrew Lunnb516d452016-06-04 21:17:06 +02003331
3332{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003333 struct mii_bus *bus = chip->mdio_bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003334
3335 mdiobus_unregister(bus);
3336
Vivien Didelotfad09c72016-06-21 12:28:20 -04003337 if (chip->mdio_np)
3338 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003339}
3340
Guenter Roeckc22995c2015-07-25 09:42:28 -07003341#ifdef CONFIG_NET_DSA_HWMON
3342
3343static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3344{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003345 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot9c938292016-08-15 17:19:02 -04003346 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003347 int ret;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003348
3349 *temp = 0;
3350
Vivien Didelotfad09c72016-06-21 12:28:20 -04003351 mutex_lock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003352
Vivien Didelot9c938292016-08-15 17:19:02 -04003353 ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003354 if (ret < 0)
3355 goto error;
3356
3357 /* Enable temperature sensor */
Vivien Didelot9c938292016-08-15 17:19:02 -04003358 ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003359 if (ret < 0)
3360 goto error;
3361
Vivien Didelot9c938292016-08-15 17:19:02 -04003362 ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003363 if (ret < 0)
3364 goto error;
3365
3366 /* Wait for temperature to stabilize */
3367 usleep_range(10000, 12000);
3368
Vivien Didelot9c938292016-08-15 17:19:02 -04003369 ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
3370 if (ret < 0)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003371 goto error;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003372
3373 /* Disable temperature sensor */
Vivien Didelot9c938292016-08-15 17:19:02 -04003374 ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003375 if (ret < 0)
3376 goto error;
3377
3378 *temp = ((val & 0x1f) - 5) * 5;
3379
3380error:
Vivien Didelot9c938292016-08-15 17:19:02 -04003381 mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003382 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003383 return ret;
3384}
3385
3386static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3387{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003388 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3389 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003390 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003391 int ret;
3392
3393 *temp = 0;
3394
Vivien Didelot9c938292016-08-15 17:19:02 -04003395 mutex_lock(&chip->reg_lock);
3396 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
3397 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003398 if (ret < 0)
3399 return ret;
3400
Vivien Didelot9c938292016-08-15 17:19:02 -04003401 *temp = (val & 0xff) - 25;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003402
3403 return 0;
3404}
3405
Vivien Didelotf81ec902016-05-09 13:22:58 -04003406static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003407{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003408 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003409
Vivien Didelotfad09c72016-06-21 12:28:20 -04003410 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
Vivien Didelot6594f612016-05-09 13:22:42 -04003411 return -EOPNOTSUPP;
3412
Vivien Didelotfad09c72016-06-21 12:28:20 -04003413 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003414 return mv88e63xx_get_temp(ds, temp);
3415
3416 return mv88e61xx_get_temp(ds, temp);
3417}
3418
Vivien Didelotf81ec902016-05-09 13:22:58 -04003419static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003420{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003421 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3422 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003423 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003424 int ret;
3425
Vivien Didelotfad09c72016-06-21 12:28:20 -04003426 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003427 return -EOPNOTSUPP;
3428
3429 *temp = 0;
3430
Vivien Didelot9c938292016-08-15 17:19:02 -04003431 mutex_lock(&chip->reg_lock);
3432 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3433 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003434 if (ret < 0)
3435 return ret;
3436
Vivien Didelot9c938292016-08-15 17:19:02 -04003437 *temp = (((val >> 8) & 0x1f) * 5) - 25;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003438
3439 return 0;
3440}
3441
Vivien Didelotf81ec902016-05-09 13:22:58 -04003442static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003443{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003444 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3445 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003446 u16 val;
3447 int err;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003448
Vivien Didelotfad09c72016-06-21 12:28:20 -04003449 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003450 return -EOPNOTSUPP;
3451
Vivien Didelot9c938292016-08-15 17:19:02 -04003452 mutex_lock(&chip->reg_lock);
3453 err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3454 if (err)
3455 goto unlock;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003456 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Vivien Didelot9c938292016-08-15 17:19:02 -04003457 err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
3458 (val & 0xe0ff) | (temp << 8));
3459unlock:
3460 mutex_unlock(&chip->reg_lock);
3461
3462 return err;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003463}
3464
Vivien Didelotf81ec902016-05-09 13:22:58 -04003465static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003466{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003467 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3468 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003469 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003470 int ret;
3471
Vivien Didelotfad09c72016-06-21 12:28:20 -04003472 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003473 return -EOPNOTSUPP;
3474
3475 *alarm = false;
3476
Vivien Didelot9c938292016-08-15 17:19:02 -04003477 mutex_lock(&chip->reg_lock);
3478 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3479 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003480 if (ret < 0)
3481 return ret;
3482
Vivien Didelot9c938292016-08-15 17:19:02 -04003483 *alarm = !!(val & 0x40);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003484
3485 return 0;
3486}
3487#endif /* CONFIG_NET_DSA_HWMON */
3488
Vivien Didelot855b1932016-07-20 18:18:35 -04003489static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
3490{
3491 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3492
3493 return chip->eeprom_len;
3494}
3495
3496static int mv88e6xxx_get_eeprom16(struct mv88e6xxx_chip *chip,
3497 struct ethtool_eeprom *eeprom, u8 *data)
3498{
3499 unsigned int offset = eeprom->offset;
3500 unsigned int len = eeprom->len;
3501 u16 val;
3502 int err;
3503
3504 eeprom->len = 0;
3505
3506 if (offset & 1) {
3507 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3508 if (err)
3509 return err;
3510
3511 *data++ = (val >> 8) & 0xff;
3512
3513 offset++;
3514 len--;
3515 eeprom->len++;
3516 }
3517
3518 while (len >= 2) {
3519 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3520 if (err)
3521 return err;
3522
3523 *data++ = val & 0xff;
3524 *data++ = (val >> 8) & 0xff;
3525
3526 offset += 2;
3527 len -= 2;
3528 eeprom->len += 2;
3529 }
3530
3531 if (len) {
3532 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3533 if (err)
3534 return err;
3535
3536 *data++ = val & 0xff;
3537
3538 offset++;
3539 len--;
3540 eeprom->len++;
3541 }
3542
3543 return 0;
3544}
3545
3546static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
3547 struct ethtool_eeprom *eeprom, u8 *data)
3548{
3549 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3550 int err;
3551
3552 mutex_lock(&chip->reg_lock);
3553
3554 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
3555 err = mv88e6xxx_get_eeprom16(chip, eeprom, data);
3556 else
3557 err = -EOPNOTSUPP;
3558
3559 mutex_unlock(&chip->reg_lock);
3560
3561 if (err)
3562 return err;
3563
3564 eeprom->magic = 0xc3ec4951;
3565
3566 return 0;
3567}
3568
3569static int mv88e6xxx_set_eeprom16(struct mv88e6xxx_chip *chip,
3570 struct ethtool_eeprom *eeprom, u8 *data)
3571{
3572 unsigned int offset = eeprom->offset;
3573 unsigned int len = eeprom->len;
3574 u16 val;
3575 int err;
3576
3577 /* Ensure the RO WriteEn bit is set */
3578 err = mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, &val);
3579 if (err)
3580 return err;
3581
3582 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
3583 return -EROFS;
3584
3585 eeprom->len = 0;
3586
3587 if (offset & 1) {
3588 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3589 if (err)
3590 return err;
3591
3592 val = (*data++ << 8) | (val & 0xff);
3593
3594 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
3595 if (err)
3596 return err;
3597
3598 offset++;
3599 len--;
3600 eeprom->len++;
3601 }
3602
3603 while (len >= 2) {
3604 val = *data++;
3605 val |= *data++ << 8;
3606
3607 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
3608 if (err)
3609 return err;
3610
3611 offset += 2;
3612 len -= 2;
3613 eeprom->len += 2;
3614 }
3615
3616 if (len) {
3617 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3618 if (err)
3619 return err;
3620
3621 val = (val & 0xff00) | *data++;
3622
3623 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
3624 if (err)
3625 return err;
3626
3627 offset++;
3628 len--;
3629 eeprom->len++;
3630 }
3631
3632 return 0;
3633}
3634
3635static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
3636 struct ethtool_eeprom *eeprom, u8 *data)
3637{
3638 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3639 int err;
3640
3641 if (eeprom->magic != 0xc3ec4951)
3642 return -EINVAL;
3643
3644 mutex_lock(&chip->reg_lock);
3645
3646 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
3647 err = mv88e6xxx_set_eeprom16(chip, eeprom, data);
3648 else
3649 err = -EOPNOTSUPP;
3650
3651 mutex_unlock(&chip->reg_lock);
3652
3653 return err;
3654}
3655
Vivien Didelotf81ec902016-05-09 13:22:58 -04003656static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3657 [MV88E6085] = {
3658 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3659 .family = MV88E6XXX_FAMILY_6097,
3660 .name = "Marvell 88E6085",
3661 .num_databases = 4096,
3662 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003663 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003664 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003665 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3666 },
3667
3668 [MV88E6095] = {
3669 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3670 .family = MV88E6XXX_FAMILY_6095,
3671 .name = "Marvell 88E6095/88E6095F",
3672 .num_databases = 256,
3673 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003674 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003675 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003676 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3677 },
3678
3679 [MV88E6123] = {
3680 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3681 .family = MV88E6XXX_FAMILY_6165,
3682 .name = "Marvell 88E6123",
3683 .num_databases = 4096,
3684 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003685 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003686 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003687 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3688 },
3689
3690 [MV88E6131] = {
3691 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3692 .family = MV88E6XXX_FAMILY_6185,
3693 .name = "Marvell 88E6131",
3694 .num_databases = 256,
3695 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003696 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003697 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003698 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3699 },
3700
3701 [MV88E6161] = {
3702 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3703 .family = MV88E6XXX_FAMILY_6165,
3704 .name = "Marvell 88E6161",
3705 .num_databases = 4096,
3706 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003707 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003708 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003709 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3710 },
3711
3712 [MV88E6165] = {
3713 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3714 .family = MV88E6XXX_FAMILY_6165,
3715 .name = "Marvell 88E6165",
3716 .num_databases = 4096,
3717 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003718 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003719 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003720 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3721 },
3722
3723 [MV88E6171] = {
3724 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3725 .family = MV88E6XXX_FAMILY_6351,
3726 .name = "Marvell 88E6171",
3727 .num_databases = 4096,
3728 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003729 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003730 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003731 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3732 },
3733
3734 [MV88E6172] = {
3735 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3736 .family = MV88E6XXX_FAMILY_6352,
3737 .name = "Marvell 88E6172",
3738 .num_databases = 4096,
3739 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003740 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003741 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003742 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3743 },
3744
3745 [MV88E6175] = {
3746 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3747 .family = MV88E6XXX_FAMILY_6351,
3748 .name = "Marvell 88E6175",
3749 .num_databases = 4096,
3750 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003751 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003752 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003753 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3754 },
3755
3756 [MV88E6176] = {
3757 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3758 .family = MV88E6XXX_FAMILY_6352,
3759 .name = "Marvell 88E6176",
3760 .num_databases = 4096,
3761 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003762 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003763 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003764 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3765 },
3766
3767 [MV88E6185] = {
3768 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3769 .family = MV88E6XXX_FAMILY_6185,
3770 .name = "Marvell 88E6185",
3771 .num_databases = 256,
3772 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003773 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003774 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003775 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3776 },
3777
3778 [MV88E6240] = {
3779 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3780 .family = MV88E6XXX_FAMILY_6352,
3781 .name = "Marvell 88E6240",
3782 .num_databases = 4096,
3783 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003784 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003785 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003786 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3787 },
3788
3789 [MV88E6320] = {
3790 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3791 .family = MV88E6XXX_FAMILY_6320,
3792 .name = "Marvell 88E6320",
3793 .num_databases = 4096,
3794 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003795 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003796 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003797 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3798 },
3799
3800 [MV88E6321] = {
3801 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3802 .family = MV88E6XXX_FAMILY_6320,
3803 .name = "Marvell 88E6321",
3804 .num_databases = 4096,
3805 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003806 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003807 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003808 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3809 },
3810
3811 [MV88E6350] = {
3812 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3813 .family = MV88E6XXX_FAMILY_6351,
3814 .name = "Marvell 88E6350",
3815 .num_databases = 4096,
3816 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003817 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003818 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003819 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3820 },
3821
3822 [MV88E6351] = {
3823 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3824 .family = MV88E6XXX_FAMILY_6351,
3825 .name = "Marvell 88E6351",
3826 .num_databases = 4096,
3827 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003828 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003829 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003830 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3831 },
3832
3833 [MV88E6352] = {
3834 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3835 .family = MV88E6XXX_FAMILY_6352,
3836 .name = "Marvell 88E6352",
3837 .num_databases = 4096,
3838 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003839 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003840 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003841 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3842 },
3843};
3844
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003845static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003846{
Vivien Didelota439c062016-04-17 13:23:58 -04003847 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003848
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003849 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3850 if (mv88e6xxx_table[i].prod_num == prod_num)
3851 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003852
Vivien Didelotb9b37712015-10-30 19:39:48 -04003853 return NULL;
3854}
3855
Vivien Didelotfad09c72016-06-21 12:28:20 -04003856static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003857{
3858 const struct mv88e6xxx_info *info;
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003859 unsigned int prod_num, rev;
3860 u16 id;
3861 int err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003862
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003863 mutex_lock(&chip->reg_lock);
3864 err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
3865 mutex_unlock(&chip->reg_lock);
3866 if (err)
3867 return err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003868
3869 prod_num = (id & 0xfff0) >> 4;
3870 rev = id & 0x000f;
3871
3872 info = mv88e6xxx_lookup_info(prod_num);
3873 if (!info)
3874 return -ENODEV;
3875
Vivien Didelotcaac8542016-06-20 13:14:09 -04003876 /* Update the compatible info with the probed one */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003877 chip->info = info;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003878
Vivien Didelotfad09c72016-06-21 12:28:20 -04003879 dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3880 chip->info->prod_num, chip->info->name, rev);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003881
3882 return 0;
3883}
3884
Vivien Didelotfad09c72016-06-21 12:28:20 -04003885static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
Vivien Didelot469d7292016-06-20 13:14:06 -04003886{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003887 struct mv88e6xxx_chip *chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003888
Vivien Didelotfad09c72016-06-21 12:28:20 -04003889 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3890 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003891 return NULL;
3892
Vivien Didelotfad09c72016-06-21 12:28:20 -04003893 chip->dev = dev;
Vivien Didelot469d7292016-06-20 13:14:06 -04003894
Vivien Didelotfad09c72016-06-21 12:28:20 -04003895 mutex_init(&chip->reg_lock);
Vivien Didelot469d7292016-06-20 13:14:06 -04003896
Vivien Didelotfad09c72016-06-21 12:28:20 -04003897 return chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003898}
3899
Vivien Didelote57e5e72016-08-15 17:19:00 -04003900static const struct mv88e6xxx_ops mv88e6xxx_phy_ops = {
3901 .read = mv88e6xxx_read,
3902 .write = mv88e6xxx_write,
3903};
3904
3905static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
3906{
3907 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SMI_PHY)) {
3908 chip->phy_ops = &mv88e6xxx_g2_smi_phy_ops;
3909 } else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) {
3910 chip->phy_ops = &mv88e6xxx_phy_ppu_ops;
3911 mv88e6xxx_ppu_state_init(chip);
3912 } else {
3913 chip->phy_ops = &mv88e6xxx_phy_ops;
3914 }
3915}
3916
Vivien Didelotfad09c72016-06-21 12:28:20 -04003917static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003918 struct mii_bus *bus, int sw_addr)
3919{
3920 /* ADDR[0] pin is unavailable externally and considered zero */
3921 if (sw_addr & 0x1)
3922 return -EINVAL;
3923
Vivien Didelot914b32f2016-06-20 13:14:11 -04003924 if (sw_addr == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003925 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
Vivien Didelota0ffff22016-08-15 17:18:58 -04003926 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003927 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
Vivien Didelot914b32f2016-06-20 13:14:11 -04003928 else
3929 return -EINVAL;
3930
Vivien Didelotfad09c72016-06-21 12:28:20 -04003931 chip->bus = bus;
3932 chip->sw_addr = sw_addr;
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003933
3934 return 0;
3935}
3936
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003937static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3938 struct device *host_dev, int sw_addr,
3939 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003940{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003941 struct mv88e6xxx_chip *chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003942 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003943 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003944
Vivien Didelota439c062016-04-17 13:23:58 -04003945 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003946 if (!bus)
3947 return NULL;
3948
Vivien Didelotfad09c72016-06-21 12:28:20 -04003949 chip = mv88e6xxx_alloc_chip(dsa_dev);
3950 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003951 return NULL;
3952
Vivien Didelotcaac8542016-06-20 13:14:09 -04003953 /* Legacy SMI probing will only support chips similar to 88E6085 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003954 chip->info = &mv88e6xxx_table[MV88E6085];
Vivien Didelotcaac8542016-06-20 13:14:09 -04003955
Vivien Didelotfad09c72016-06-21 12:28:20 -04003956 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003957 if (err)
3958 goto free;
3959
Vivien Didelotfad09c72016-06-21 12:28:20 -04003960 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003961 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003962 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003963
Vivien Didelote57e5e72016-08-15 17:19:00 -04003964 mv88e6xxx_phy_init(chip);
3965
Vivien Didelotfad09c72016-06-21 12:28:20 -04003966 err = mv88e6xxx_mdio_register(chip, NULL);
Andrew Lunnb516d452016-06-04 21:17:06 +02003967 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003968 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003969
Vivien Didelotfad09c72016-06-21 12:28:20 -04003970 *priv = chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003971
Vivien Didelotfad09c72016-06-21 12:28:20 -04003972 return chip->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003973free:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003974 devm_kfree(dsa_dev, chip);
Vivien Didelot469d7292016-06-20 13:14:06 -04003975
3976 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003977}
3978
Vivien Didelot57d32312016-06-20 13:13:58 -04003979static struct dsa_switch_driver mv88e6xxx_switch_driver = {
Vivien Didelotf81ec902016-05-09 13:22:58 -04003980 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003981 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003982 .setup = mv88e6xxx_setup,
3983 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003984 .adjust_link = mv88e6xxx_adjust_link,
3985 .get_strings = mv88e6xxx_get_strings,
3986 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3987 .get_sset_count = mv88e6xxx_get_sset_count,
3988 .set_eee = mv88e6xxx_set_eee,
3989 .get_eee = mv88e6xxx_get_eee,
3990#ifdef CONFIG_NET_DSA_HWMON
3991 .get_temp = mv88e6xxx_get_temp,
3992 .get_temp_limit = mv88e6xxx_get_temp_limit,
3993 .set_temp_limit = mv88e6xxx_set_temp_limit,
3994 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3995#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003996 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003997 .get_eeprom = mv88e6xxx_get_eeprom,
3998 .set_eeprom = mv88e6xxx_set_eeprom,
3999 .get_regs_len = mv88e6xxx_get_regs_len,
4000 .get_regs = mv88e6xxx_get_regs,
Vivien Didelot2cfcd962016-07-18 20:45:40 -04004001 .set_ageing_time = mv88e6xxx_set_ageing_time,
Vivien Didelotf81ec902016-05-09 13:22:58 -04004002 .port_bridge_join = mv88e6xxx_port_bridge_join,
4003 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
4004 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
4005 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
4006 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
4007 .port_vlan_add = mv88e6xxx_port_vlan_add,
4008 .port_vlan_del = mv88e6xxx_port_vlan_del,
4009 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
4010 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
4011 .port_fdb_add = mv88e6xxx_port_fdb_add,
4012 .port_fdb_del = mv88e6xxx_port_fdb_del,
4013 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
4014};
4015
Vivien Didelotfad09c72016-06-21 12:28:20 -04004016static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004017 struct device_node *np)
4018{
Vivien Didelotfad09c72016-06-21 12:28:20 -04004019 struct device *dev = chip->dev;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004020 struct dsa_switch *ds;
4021
4022 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
4023 if (!ds)
4024 return -ENOMEM;
4025
4026 ds->dev = dev;
Vivien Didelotfad09c72016-06-21 12:28:20 -04004027 ds->priv = chip;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004028 ds->drv = &mv88e6xxx_switch_driver;
4029
4030 dev_set_drvdata(dev, ds);
4031
4032 return dsa_register_switch(ds, np);
4033}
4034
Vivien Didelotfad09c72016-06-21 12:28:20 -04004035static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004036{
Vivien Didelotfad09c72016-06-21 12:28:20 -04004037 dsa_unregister_switch(chip->ds);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004038}
4039
Vivien Didelot57d32312016-06-20 13:13:58 -04004040static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004041{
4042 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004043 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04004044 const struct mv88e6xxx_info *compat_info;
Vivien Didelotfad09c72016-06-21 12:28:20 -04004045 struct mv88e6xxx_chip *chip;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004046 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02004047 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004048
Vivien Didelotcaac8542016-06-20 13:14:09 -04004049 compat_info = of_device_get_match_data(dev);
4050 if (!compat_info)
4051 return -EINVAL;
4052
Vivien Didelotfad09c72016-06-21 12:28:20 -04004053 chip = mv88e6xxx_alloc_chip(dev);
4054 if (!chip)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004055 return -ENOMEM;
4056
Vivien Didelotfad09c72016-06-21 12:28:20 -04004057 chip->info = compat_info;
Vivien Didelotcaac8542016-06-20 13:14:09 -04004058
Vivien Didelotfad09c72016-06-21 12:28:20 -04004059 err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04004060 if (err)
4061 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004062
Vivien Didelotfad09c72016-06-21 12:28:20 -04004063 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04004064 if (err)
4065 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004066
Vivien Didelote57e5e72016-08-15 17:19:00 -04004067 mv88e6xxx_phy_init(chip);
4068
Vivien Didelotfad09c72016-06-21 12:28:20 -04004069 chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
4070 if (IS_ERR(chip->reset))
4071 return PTR_ERR(chip->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02004072
Vivien Didelot855b1932016-07-20 18:18:35 -04004073 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16) &&
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004074 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
Vivien Didelotfad09c72016-06-21 12:28:20 -04004075 chip->eeprom_len = eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004076
Vivien Didelotfad09c72016-06-21 12:28:20 -04004077 err = mv88e6xxx_mdio_register(chip, np);
Andrew Lunnb516d452016-06-04 21:17:06 +02004078 if (err)
4079 return err;
4080
Vivien Didelotfad09c72016-06-21 12:28:20 -04004081 err = mv88e6xxx_register_switch(chip, np);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02004082 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04004083 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02004084 return err;
4085 }
4086
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004087 return 0;
4088}
4089
4090static void mv88e6xxx_remove(struct mdio_device *mdiodev)
4091{
4092 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
Vivien Didelotfad09c72016-06-21 12:28:20 -04004093 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004094
Vivien Didelotfad09c72016-06-21 12:28:20 -04004095 mv88e6xxx_unregister_switch(chip);
4096 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004097}
4098
4099static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04004100 {
4101 .compatible = "marvell,mv88e6085",
4102 .data = &mv88e6xxx_table[MV88E6085],
4103 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004104 { /* sentinel */ },
4105};
4106
4107MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
4108
4109static struct mdio_driver mv88e6xxx_driver = {
4110 .probe = mv88e6xxx_probe,
4111 .remove = mv88e6xxx_remove,
4112 .mdiodrv.driver = {
4113 .name = "mv88e6085",
4114 .of_match_table = mv88e6xxx_of_match,
4115 },
4116};
4117
Ben Hutchings98e67302011-11-25 14:36:19 +00004118static int __init mv88e6xxx_init(void)
4119{
Vivien Didelotf81ec902016-05-09 13:22:58 -04004120 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004121 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004122}
4123module_init(mv88e6xxx_init);
4124
4125static void __exit mv88e6xxx_cleanup(void)
4126{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004127 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04004128 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004129}
4130module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00004131
4132MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4133MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4134MODULE_LICENSE("GPL");