blob: 3b0bc88a3feb28a97c506d324623b04123f855b3 [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 Lunnc8c1b392015-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 Didelot2d79af62016-08-15 17:18:57 -0400241static int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg,
242 u16 mask)
243{
244 unsigned long timeout = jiffies + HZ / 10;
245
246 while (time_before(jiffies, timeout)) {
247 u16 val;
248 int err;
249
250 err = mv88e6xxx_read(chip, addr, reg, &val);
251 if (err)
252 return err;
253
254 if (!(val & mask))
255 return 0;
256
257 usleep_range(1000, 2000);
258 }
259
260 return -ETIMEDOUT;
261}
262
Vivien Didelotf22ab642016-07-18 20:45:31 -0400263/* Indirect write to single pointer-data register with an Update bit */
264static int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
265 u16 update)
266{
267 u16 val;
268 int i, err;
269
270 /* Wait until the previous operation is completed */
271 for (i = 0; i < 16; ++i) {
272 err = mv88e6xxx_read(chip, addr, reg, &val);
273 if (err)
274 return err;
275
276 if (!(val & BIT(15)))
277 break;
278 }
279
280 if (i == 16)
281 return -ETIMEDOUT;
282
283 /* Set the Update bit to trigger a write operation */
284 val = BIT(15) | update;
285
286 return mv88e6xxx_write(chip, addr, reg, val);
287}
288
Vivien Didelotfad09c72016-06-21 12:28:20 -0400289static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000290{
Vivien Didelot914b32f2016-06-20 13:14:11 -0400291 u16 val;
292 int err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000293
Vivien Didelotfad09c72016-06-21 12:28:20 -0400294 err = mv88e6xxx_read(chip, addr, reg, &val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400295 if (err)
296 return err;
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400297
Vivien Didelot914b32f2016-06-20 13:14:11 -0400298 return val;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000299}
300
Vivien Didelotfad09c72016-06-21 12:28:20 -0400301static int _mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
Andrew Lunn158bc062016-04-28 21:24:06 -0400302 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000303{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400304 return mv88e6xxx_write(chip, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700305}
306
Vivien Didelotfad09c72016-06-21 12:28:20 -0400307static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +0200308 int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000309{
310 if (addr >= 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400311 return _mv88e6xxx_reg_read(chip, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000312 return 0xffff;
313}
314
Vivien Didelotfad09c72016-06-21 12:28:20 -0400315static int mv88e6xxx_mdio_write_direct(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +0200316 int addr, int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000317{
318 if (addr >= 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400319 return _mv88e6xxx_reg_write(chip, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000320 return 0;
321}
322
Vivien Didelotfad09c72016-06-21 12:28:20 -0400323static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000324{
325 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000326 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000327
Vivien Didelotfad09c72016-06-21 12:28:20 -0400328 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200329 if (ret < 0)
330 return ret;
331
Vivien Didelotfad09c72016-06-21 12:28:20 -0400332 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400333 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200334 if (ret)
335 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000336
Barry Grussling19b2f972013-01-08 16:05:54 +0000337 timeout = jiffies + 1 * HZ;
338 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400339 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200340 if (ret < 0)
341 return ret;
342
Barry Grussling19b2f972013-01-08 16:05:54 +0000343 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200344 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
345 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000346 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000347 }
348
349 return -ETIMEDOUT;
350}
351
Vivien Didelotfad09c72016-06-21 12:28:20 -0400352static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000353{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200354 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000355 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000356
Vivien Didelotfad09c72016-06-21 12:28:20 -0400357 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200358 if (ret < 0)
359 return ret;
360
Vivien Didelotfad09c72016-06-21 12:28:20 -0400361 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot762eb672016-06-04 21:16:54 +0200362 ret | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200363 if (err)
364 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000365
Barry Grussling19b2f972013-01-08 16:05:54 +0000366 timeout = jiffies + 1 * HZ;
367 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400368 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200369 if (ret < 0)
370 return ret;
371
Barry Grussling19b2f972013-01-08 16:05:54 +0000372 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200373 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
374 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000375 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000376 }
377
378 return -ETIMEDOUT;
379}
380
381static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
382{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400383 struct mv88e6xxx_chip *chip;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000384
Vivien Didelotfad09c72016-06-21 12:28:20 -0400385 chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200386
Vivien Didelotfad09c72016-06-21 12:28:20 -0400387 mutex_lock(&chip->reg_lock);
Vivien Didelot762eb672016-06-04 21:16:54 +0200388
Vivien Didelotfad09c72016-06-21 12:28:20 -0400389 if (mutex_trylock(&chip->ppu_mutex)) {
390 if (mv88e6xxx_ppu_enable(chip) == 0)
391 chip->ppu_disabled = 0;
392 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000393 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200394
Vivien Didelotfad09c72016-06-21 12:28:20 -0400395 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000396}
397
398static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
399{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400400 struct mv88e6xxx_chip *chip = (void *)_ps;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000401
Vivien Didelotfad09c72016-06-21 12:28:20 -0400402 schedule_work(&chip->ppu_work);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000403}
404
Vivien Didelotfad09c72016-06-21 12:28:20 -0400405static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000406{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000407 int ret;
408
Vivien Didelotfad09c72016-06-21 12:28:20 -0400409 mutex_lock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000410
Barry Grussling3675c8d2013-01-08 16:05:53 +0000411 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000412 * we can access the PHY registers. If it was already
413 * disabled, cancel the timer that is going to re-enable
414 * it.
415 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400416 if (!chip->ppu_disabled) {
417 ret = mv88e6xxx_ppu_disable(chip);
Barry Grussling85686582013-01-08 16:05:56 +0000418 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400419 mutex_unlock(&chip->ppu_mutex);
Barry Grussling85686582013-01-08 16:05:56 +0000420 return ret;
421 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400422 chip->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000423 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400424 del_timer(&chip->ppu_timer);
Barry Grussling85686582013-01-08 16:05:56 +0000425 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000426 }
427
428 return ret;
429}
430
Vivien Didelotfad09c72016-06-21 12:28:20 -0400431static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000432{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000433 /* Schedule a timer to re-enable the PHY polling unit. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400434 mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
435 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000436}
437
Vivien Didelotfad09c72016-06-21 12:28:20 -0400438static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000439{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400440 mutex_init(&chip->ppu_mutex);
441 INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
442 init_timer(&chip->ppu_timer);
443 chip->ppu_timer.data = (unsigned long)chip;
444 chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000445}
446
Vivien Didelote57e5e72016-08-15 17:19:00 -0400447static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
448 int reg, u16 *val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000449{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400450 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000451
Vivien Didelote57e5e72016-08-15 17:19:00 -0400452 err = mv88e6xxx_ppu_access_get(chip);
453 if (!err) {
454 err = mv88e6xxx_read(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400455 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000456 }
457
Vivien Didelote57e5e72016-08-15 17:19:00 -0400458 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000459}
460
Vivien Didelote57e5e72016-08-15 17:19:00 -0400461static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
462 int reg, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000463{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400464 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000465
Vivien Didelote57e5e72016-08-15 17:19:00 -0400466 err = mv88e6xxx_ppu_access_get(chip);
467 if (!err) {
468 err = mv88e6xxx_write(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400469 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000470 }
471
Vivien Didelote57e5e72016-08-15 17:19:00 -0400472 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000473}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000474
Vivien Didelote57e5e72016-08-15 17:19:00 -0400475static const struct mv88e6xxx_ops mv88e6xxx_phy_ppu_ops = {
476 .read = mv88e6xxx_phy_ppu_read,
477 .write = mv88e6xxx_phy_ppu_write,
478};
479
Vivien Didelotfad09c72016-06-21 12:28:20 -0400480static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200481{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400482 return chip->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200483}
484
Vivien Didelotfad09c72016-06-21 12:28:20 -0400485static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200486{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400487 return chip->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200488}
489
Vivien Didelotfad09c72016-06-21 12:28:20 -0400490static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200491{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400492 return chip->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200493}
494
Vivien Didelotfad09c72016-06-21 12:28:20 -0400495static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200496{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400497 return chip->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200498}
499
Vivien Didelotfad09c72016-06-21 12:28:20 -0400500static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200501{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400502 return chip->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200503}
504
Vivien Didelotfad09c72016-06-21 12:28:20 -0400505static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700506{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400507 return chip->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700508}
509
Vivien Didelotfad09c72016-06-21 12:28:20 -0400510static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200511{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400512 return chip->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200513}
514
Vivien Didelotfad09c72016-06-21 12:28:20 -0400515static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200516{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400517 return chip->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200518}
519
Vivien Didelotfad09c72016-06-21 12:28:20 -0400520static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400521{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400522 return chip->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400523}
524
Vivien Didelotfad09c72016-06-21 12:28:20 -0400525static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_chip *chip)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400526{
527 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400528 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
529 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400530 return true;
531
532 return false;
533}
534
Andrew Lunndea87022015-08-31 15:56:47 +0200535/* We expect the switch to perform auto negotiation if there is a real
536 * phy. However, in the case of a fixed link phy, we force the port
537 * settings from the fixed link settings.
538 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400539static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
540 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200541{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400542 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200543 u32 reg;
544 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200545
546 if (!phy_is_pseudo_fixed_link(phydev))
547 return;
548
Vivien Didelotfad09c72016-06-21 12:28:20 -0400549 mutex_lock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200550
Vivien Didelotfad09c72016-06-21 12:28:20 -0400551 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200552 if (ret < 0)
553 goto out;
554
555 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
556 PORT_PCS_CTRL_FORCE_LINK |
557 PORT_PCS_CTRL_DUPLEX_FULL |
558 PORT_PCS_CTRL_FORCE_DUPLEX |
559 PORT_PCS_CTRL_UNFORCED);
560
561 reg |= PORT_PCS_CTRL_FORCE_LINK;
562 if (phydev->link)
Vivien Didelot57d32312016-06-20 13:13:58 -0400563 reg |= PORT_PCS_CTRL_LINK_UP;
Andrew Lunndea87022015-08-31 15:56:47 +0200564
Vivien Didelotfad09c72016-06-21 12:28:20 -0400565 if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200566 goto out;
567
568 switch (phydev->speed) {
569 case SPEED_1000:
570 reg |= PORT_PCS_CTRL_1000;
571 break;
572 case SPEED_100:
573 reg |= PORT_PCS_CTRL_100;
574 break;
575 case SPEED_10:
576 reg |= PORT_PCS_CTRL_10;
577 break;
578 default:
579 pr_info("Unknown speed");
580 goto out;
581 }
582
583 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
584 if (phydev->duplex == DUPLEX_FULL)
585 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
586
Vivien Didelotfad09c72016-06-21 12:28:20 -0400587 if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
588 (port >= chip->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200589 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
590 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
591 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
592 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
593 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
594 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
595 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
596 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400597 _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200598
599out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400600 mutex_unlock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200601}
602
Vivien Didelotfad09c72016-06-21 12:28:20 -0400603static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000604{
605 int ret;
606 int i;
607
608 for (i = 0; i < 10; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400609 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200610 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000611 return 0;
612 }
613
614 return -ETIMEDOUT;
615}
616
Vivien Didelotfad09c72016-06-21 12:28:20 -0400617static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000618{
619 int ret;
620
Vivien Didelotfad09c72016-06-21 12:28:20 -0400621 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200622 port = (port + 1) << 5;
623
Barry Grussling3675c8d2013-01-08 16:05:53 +0000624 /* Snapshot the hardware statistics counters for this port. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400625 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200626 GLOBAL_STATS_OP_CAPTURE_PORT |
627 GLOBAL_STATS_OP_HIST_RX_TX | port);
628 if (ret < 0)
629 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000630
Barry Grussling3675c8d2013-01-08 16:05:53 +0000631 /* Wait for the snapshotting to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400632 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000633 if (ret < 0)
634 return ret;
635
636 return 0;
637}
638
Vivien Didelotfad09c72016-06-21 12:28:20 -0400639static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400640 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000641{
642 u32 _val;
643 int ret;
644
645 *val = 0;
646
Vivien Didelotfad09c72016-06-21 12:28:20 -0400647 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200648 GLOBAL_STATS_OP_READ_CAPTURED |
649 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000650 if (ret < 0)
651 return;
652
Vivien Didelotfad09c72016-06-21 12:28:20 -0400653 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000654 if (ret < 0)
655 return;
656
Vivien Didelotfad09c72016-06-21 12:28:20 -0400657 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000658 if (ret < 0)
659 return;
660
661 _val = ret << 16;
662
Vivien Didelotfad09c72016-06-21 12:28:20 -0400663 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000664 if (ret < 0)
665 return;
666
667 *val = _val | ret;
668}
669
Andrew Lunne413e7e2015-04-02 04:06:38 +0200670static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100671 { "in_good_octets", 8, 0x00, BANK0, },
672 { "in_bad_octets", 4, 0x02, BANK0, },
673 { "in_unicast", 4, 0x04, BANK0, },
674 { "in_broadcasts", 4, 0x06, BANK0, },
675 { "in_multicasts", 4, 0x07, BANK0, },
676 { "in_pause", 4, 0x16, BANK0, },
677 { "in_undersize", 4, 0x18, BANK0, },
678 { "in_fragments", 4, 0x19, BANK0, },
679 { "in_oversize", 4, 0x1a, BANK0, },
680 { "in_jabber", 4, 0x1b, BANK0, },
681 { "in_rx_error", 4, 0x1c, BANK0, },
682 { "in_fcs_error", 4, 0x1d, BANK0, },
683 { "out_octets", 8, 0x0e, BANK0, },
684 { "out_unicast", 4, 0x10, BANK0, },
685 { "out_broadcasts", 4, 0x13, BANK0, },
686 { "out_multicasts", 4, 0x12, BANK0, },
687 { "out_pause", 4, 0x15, BANK0, },
688 { "excessive", 4, 0x11, BANK0, },
689 { "collisions", 4, 0x1e, BANK0, },
690 { "deferred", 4, 0x05, BANK0, },
691 { "single", 4, 0x14, BANK0, },
692 { "multiple", 4, 0x17, BANK0, },
693 { "out_fcs_error", 4, 0x03, BANK0, },
694 { "late", 4, 0x1f, BANK0, },
695 { "hist_64bytes", 4, 0x08, BANK0, },
696 { "hist_65_127bytes", 4, 0x09, BANK0, },
697 { "hist_128_255bytes", 4, 0x0a, BANK0, },
698 { "hist_256_511bytes", 4, 0x0b, BANK0, },
699 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
700 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
701 { "sw_in_discards", 4, 0x10, PORT, },
702 { "sw_in_filtered", 2, 0x12, PORT, },
703 { "sw_out_filtered", 2, 0x13, PORT, },
704 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
705 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
706 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
707 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
708 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
709 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
710 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
711 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
712 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
713 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
714 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
715 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
716 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
717 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
718 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
719 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
720 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
721 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
722 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
723 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
724 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
725 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
726 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
727 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
728 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
729 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200730};
731
Vivien Didelotfad09c72016-06-21 12:28:20 -0400732static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100733 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200734{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100735 switch (stat->type) {
736 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200737 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100738 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400739 return mv88e6xxx_6320_family(chip);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100740 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400741 return mv88e6xxx_6095_family(chip) ||
742 mv88e6xxx_6185_family(chip) ||
743 mv88e6xxx_6097_family(chip) ||
744 mv88e6xxx_6165_family(chip) ||
745 mv88e6xxx_6351_family(chip) ||
746 mv88e6xxx_6352_family(chip);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200747 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100748 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000749}
750
Vivien Didelotfad09c72016-06-21 12:28:20 -0400751static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100752 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200753 int port)
754{
Andrew Lunn80c46272015-06-20 18:42:30 +0200755 u32 low;
756 u32 high = 0;
757 int ret;
758 u64 value;
759
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100760 switch (s->type) {
761 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400762 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200763 if (ret < 0)
764 return UINT64_MAX;
765
766 low = ret;
767 if (s->sizeof_stat == 4) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400768 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100769 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200770 if (ret < 0)
771 return UINT64_MAX;
772 high = ret;
773 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100774 break;
775 case BANK0:
776 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400777 _mv88e6xxx_stats_read(chip, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200778 if (s->sizeof_stat == 8)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400779 _mv88e6xxx_stats_read(chip, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200780 }
781 value = (((u64)high) << 16) | low;
782 return value;
783}
784
Vivien Didelotf81ec902016-05-09 13:22:58 -0400785static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
786 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100787{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400788 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100789 struct mv88e6xxx_hw_stat *stat;
790 int i, j;
791
792 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
793 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400794 if (mv88e6xxx_has_stat(chip, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100795 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
796 ETH_GSTRING_LEN);
797 j++;
798 }
799 }
800}
801
Vivien Didelotf81ec902016-05-09 13:22:58 -0400802static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100803{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400804 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100805 struct mv88e6xxx_hw_stat *stat;
806 int i, j;
807
808 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
809 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400810 if (mv88e6xxx_has_stat(chip, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100811 j++;
812 }
813 return j;
814}
815
Vivien Didelotf81ec902016-05-09 13:22:58 -0400816static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
817 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000818{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400819 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100820 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000821 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100822 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000823
Vivien Didelotfad09c72016-06-21 12:28:20 -0400824 mutex_lock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000825
Vivien Didelotfad09c72016-06-21 12:28:20 -0400826 ret = _mv88e6xxx_stats_snapshot(chip, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000827 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400828 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000829 return;
830 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100831 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
832 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400833 if (mv88e6xxx_has_stat(chip, stat)) {
834 data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100835 j++;
836 }
837 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000838
Vivien Didelotfad09c72016-06-21 12:28:20 -0400839 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000840}
Ben Hutchings98e67302011-11-25 14:36:19 +0000841
Vivien Didelotf81ec902016-05-09 13:22:58 -0400842static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700843{
844 return 32 * sizeof(u16);
845}
846
Vivien Didelotf81ec902016-05-09 13:22:58 -0400847static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
848 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700849{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400850 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700851 u16 *p = _p;
852 int i;
853
854 regs->version = 0;
855
856 memset(p, 0xff, 32 * sizeof(u16));
857
Vivien Didelotfad09c72016-06-21 12:28:20 -0400858 mutex_lock(&chip->reg_lock);
Vivien Didelot23062512016-05-09 13:22:45 -0400859
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700860 for (i = 0; i < 32; i++) {
861 int ret;
862
Vivien Didelotfad09c72016-06-21 12:28:20 -0400863 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700864 if (ret >= 0)
865 p[i] = ret;
866 }
Vivien Didelot23062512016-05-09 13:22:45 -0400867
Vivien Didelotfad09c72016-06-21 12:28:20 -0400868 mutex_unlock(&chip->reg_lock);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700869}
870
Vivien Didelotfad09c72016-06-21 12:28:20 -0400871static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700872{
Vivien Didelot2d79af62016-08-15 17:18:57 -0400873 return mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_ATU_OP,
874 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700875}
876
Vivien Didelot57c67cf2016-08-15 17:18:59 -0400877static int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int addr,
878 int reg, u16 *val);
879static int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr,
880 int reg, u16 val);
881
Vivien Didelotfad09c72016-06-21 12:28:20 -0400882static int mv88e6xxx_mdio_read_indirect(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400883 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +0100884{
Vivien Didelot57c67cf2016-08-15 17:18:59 -0400885 u16 val;
886 int err;
Andrew Lunnf3044682015-02-14 19:17:50 +0100887
Vivien Didelot57c67cf2016-08-15 17:18:59 -0400888 err = mv88e6xxx_g2_smi_phy_read(chip, addr, regnum, &val);
889 if (err)
890 return err;
Andrew Lunnf3044682015-02-14 19:17:50 +0100891
Vivien Didelot57c67cf2016-08-15 17:18:59 -0400892 return val;
Andrew Lunnf3044682015-02-14 19:17:50 +0100893}
894
Vivien Didelotfad09c72016-06-21 12:28:20 -0400895static int mv88e6xxx_mdio_write_indirect(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400896 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +0100897{
Vivien Didelot57c67cf2016-08-15 17:18:59 -0400898 return mv88e6xxx_g2_smi_phy_write(chip, addr, regnum, val);
Andrew Lunnf3044682015-02-14 19:17:50 +0100899}
900
Vivien Didelotf81ec902016-05-09 13:22:58 -0400901static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
902 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -0800903{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400904 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800905 int reg;
906
Vivien Didelotfad09c72016-06-21 12:28:20 -0400907 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -0400908 return -EOPNOTSUPP;
909
Vivien Didelotfad09c72016-06-21 12:28:20 -0400910 mutex_lock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200911
Vivien Didelotfad09c72016-06-21 12:28:20 -0400912 reg = mv88e6xxx_mdio_read_indirect(chip, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800913 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200914 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800915
916 e->eee_enabled = !!(reg & 0x0200);
917 e->tx_lpi_enabled = !!(reg & 0x0100);
918
Vivien Didelotfad09c72016-06-21 12:28:20 -0400919 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800920 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200921 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800922
Andrew Lunncca8b132015-04-02 04:06:39 +0200923 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200924 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800925
Andrew Lunn2f40c692015-04-02 04:06:37 +0200926out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400927 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200928 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800929}
930
Vivien Didelotf81ec902016-05-09 13:22:58 -0400931static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
932 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -0800933{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400934 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200935 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800936 int ret;
937
Vivien Didelotfad09c72016-06-21 12:28:20 -0400938 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -0400939 return -EOPNOTSUPP;
940
Vivien Didelotfad09c72016-06-21 12:28:20 -0400941 mutex_lock(&chip->reg_lock);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800942
Vivien Didelotfad09c72016-06-21 12:28:20 -0400943 ret = mv88e6xxx_mdio_read_indirect(chip, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200944 if (ret < 0)
945 goto out;
946
947 reg = ret & ~0x0300;
948 if (e->eee_enabled)
949 reg |= 0x0200;
950 if (e->tx_lpi_enabled)
951 reg |= 0x0100;
952
Vivien Didelotfad09c72016-06-21 12:28:20 -0400953 ret = mv88e6xxx_mdio_write_indirect(chip, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200954out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400955 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200956
957 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800958}
959
Vivien Didelotfad09c72016-06-21 12:28:20 -0400960static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700961{
962 int ret;
963
Vivien Didelotfad09c72016-06-21 12:28:20 -0400964 if (mv88e6xxx_has_fid_reg(chip)) {
965 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_FID,
966 fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400967 if (ret < 0)
968 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -0400969 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -0400970 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400971 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -0400972 if (ret < 0)
973 return ret;
974
Vivien Didelotfad09c72016-06-21 12:28:20 -0400975 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -0400976 (ret & 0xfff) |
977 ((fid << 8) & 0xf000));
978 if (ret < 0)
979 return ret;
980
981 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
982 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400983 }
984
Vivien Didelotfad09c72016-06-21 12:28:20 -0400985 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700986 if (ret < 0)
987 return ret;
988
Vivien Didelotfad09c72016-06-21 12:28:20 -0400989 return _mv88e6xxx_atu_wait(chip);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700990}
991
Vivien Didelotfad09c72016-06-21 12:28:20 -0400992static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot37705b72015-09-04 14:34:11 -0400993 struct mv88e6xxx_atu_entry *entry)
994{
995 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
996
997 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
998 unsigned int mask, shift;
999
1000 if (entry->trunk) {
1001 data |= GLOBAL_ATU_DATA_TRUNK;
1002 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1003 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1004 } else {
1005 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1006 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1007 }
1008
1009 data |= (entry->portv_trunkid << shift) & mask;
1010 }
1011
Vivien Didelotfad09c72016-06-21 12:28:20 -04001012 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001013}
1014
Vivien Didelotfad09c72016-06-21 12:28:20 -04001015static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001016 struct mv88e6xxx_atu_entry *entry,
1017 bool static_too)
1018{
1019 int op;
1020 int err;
1021
Vivien Didelotfad09c72016-06-21 12:28:20 -04001022 err = _mv88e6xxx_atu_wait(chip);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001023 if (err)
1024 return err;
1025
Vivien Didelotfad09c72016-06-21 12:28:20 -04001026 err = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001027 if (err)
1028 return err;
1029
1030 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001031 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1032 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1033 } else {
1034 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1035 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1036 }
1037
Vivien Didelotfad09c72016-06-21 12:28:20 -04001038 return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001039}
1040
Vivien Didelotfad09c72016-06-21 12:28:20 -04001041static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001042 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001043{
1044 struct mv88e6xxx_atu_entry entry = {
1045 .fid = fid,
1046 .state = 0, /* EntryState bits must be 0 */
1047 };
1048
Vivien Didelotfad09c72016-06-21 12:28:20 -04001049 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001050}
1051
Vivien Didelotfad09c72016-06-21 12:28:20 -04001052static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001053 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001054{
1055 struct mv88e6xxx_atu_entry entry = {
1056 .trunk = false,
1057 .fid = fid,
1058 };
1059
1060 /* EntryState bits must be 0xF */
1061 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1062
1063 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1064 entry.portv_trunkid = (to_port & 0x0f) << 4;
1065 entry.portv_trunkid |= from_port & 0x0f;
1066
Vivien Didelotfad09c72016-06-21 12:28:20 -04001067 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001068}
1069
Vivien Didelotfad09c72016-06-21 12:28:20 -04001070static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001071 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001072{
1073 /* Destination port 0xF means remove the entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001074 return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001075}
1076
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001077static const char * const mv88e6xxx_port_state_names[] = {
1078 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1079 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1080 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1081 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1082};
1083
Vivien Didelotfad09c72016-06-21 12:28:20 -04001084static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001085 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001086{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001087 struct dsa_switch *ds = chip->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001088 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001089 u8 oldstate;
1090
Vivien Didelotfad09c72016-06-21 12:28:20 -04001091 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001092 if (reg < 0)
1093 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001094
Andrew Lunncca8b132015-04-02 04:06:39 +02001095 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001096
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001097 if (oldstate != state) {
1098 /* Flush forwarding database if we're moving a port
1099 * from Learning or Forwarding state to Disabled or
1100 * Blocking or Listening state.
1101 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001102 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
Vivien Didelot57d32312016-06-20 13:13:58 -04001103 oldstate == PORT_CONTROL_STATE_FORWARDING) &&
1104 (state == PORT_CONTROL_STATE_DISABLED ||
1105 state == PORT_CONTROL_STATE_BLOCKING)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001106 ret = _mv88e6xxx_atu_remove(chip, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001107 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001108 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001109 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001110
Andrew Lunncca8b132015-04-02 04:06:39 +02001111 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001112 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001113 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001114 if (ret)
1115 return ret;
1116
Andrew Lunnc8b09802016-06-04 21:16:57 +02001117 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001118 mv88e6xxx_port_state_names[state],
1119 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001120 }
1121
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001122 return ret;
1123}
1124
Vivien Didelotfad09c72016-06-21 12:28:20 -04001125static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001126{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001127 struct net_device *bridge = chip->ports[port].bridge_dev;
1128 const u16 mask = (1 << chip->info->num_ports) - 1;
1129 struct dsa_switch *ds = chip->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001130 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001131 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001132 int i;
1133
1134 /* allow CPU port or DSA link(s) to send frames to every port */
1135 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1136 output_ports = mask;
1137 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001138 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001139 /* allow sending frames to every group member */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001140 if (bridge && chip->ports[i].bridge_dev == bridge)
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001141 output_ports |= BIT(i);
1142
1143 /* allow sending frames to CPU port and DSA link(s) */
1144 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1145 output_ports |= BIT(i);
1146 }
1147 }
1148
1149 /* prevent frames from going back out of the port they came in on */
1150 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001151
Vivien Didelotfad09c72016-06-21 12:28:20 -04001152 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001153 if (reg < 0)
1154 return reg;
1155
1156 reg &= ~mask;
1157 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001158
Vivien Didelotfad09c72016-06-21 12:28:20 -04001159 return _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001160}
1161
Vivien Didelotf81ec902016-05-09 13:22:58 -04001162static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1163 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001164{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001165 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001166 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001167 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001168
1169 switch (state) {
1170 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001171 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001172 break;
1173 case BR_STATE_BLOCKING:
1174 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001175 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001176 break;
1177 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001178 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001179 break;
1180 case BR_STATE_FORWARDING:
1181 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001182 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001183 break;
1184 }
1185
Vivien Didelotfad09c72016-06-21 12:28:20 -04001186 mutex_lock(&chip->reg_lock);
1187 err = _mv88e6xxx_port_state(chip, port, stp_state);
1188 mutex_unlock(&chip->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001189
1190 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001191 netdev_err(ds->ports[port].netdev,
1192 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001193 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001194}
1195
Vivien Didelotfad09c72016-06-21 12:28:20 -04001196static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001197 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001198{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001199 struct dsa_switch *ds = chip->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001200 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001201 int ret;
1202
Vivien Didelotfad09c72016-06-21 12:28:20 -04001203 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001204 if (ret < 0)
1205 return ret;
1206
Vivien Didelot5da96032016-03-07 18:24:39 -05001207 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1208
1209 if (new) {
1210 ret &= ~PORT_DEFAULT_VLAN_MASK;
1211 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1212
Vivien Didelotfad09c72016-06-21 12:28:20 -04001213 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001214 PORT_DEFAULT_VLAN, ret);
1215 if (ret < 0)
1216 return ret;
1217
Andrew Lunnc8b09802016-06-04 21:16:57 +02001218 netdev_dbg(ds->ports[port].netdev,
1219 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001220 }
1221
1222 if (old)
1223 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001224
1225 return 0;
1226}
1227
Vivien Didelotfad09c72016-06-21 12:28:20 -04001228static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001229 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001230{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001231 return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001232}
1233
Vivien Didelotfad09c72016-06-21 12:28:20 -04001234static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001235 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001236{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001237 return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001238}
1239
Vivien Didelotfad09c72016-06-21 12:28:20 -04001240static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001241{
Vivien Didelot2d79af62016-08-15 17:18:57 -04001242 return mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_VTU_OP,
1243 GLOBAL_VTU_OP_BUSY);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001244}
1245
Vivien Didelotfad09c72016-06-21 12:28:20 -04001246static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001247{
1248 int ret;
1249
Vivien Didelotfad09c72016-06-21 12:28:20 -04001250 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001251 if (ret < 0)
1252 return ret;
1253
Vivien Didelotfad09c72016-06-21 12:28:20 -04001254 return _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001255}
1256
Vivien Didelotfad09c72016-06-21 12:28:20 -04001257static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001258{
1259 int ret;
1260
Vivien Didelotfad09c72016-06-21 12:28:20 -04001261 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001262 if (ret < 0)
1263 return ret;
1264
Vivien Didelotfad09c72016-06-21 12:28:20 -04001265 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001266}
1267
Vivien Didelotfad09c72016-06-21 12:28:20 -04001268static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001269 struct mv88e6xxx_vtu_stu_entry *entry,
1270 unsigned int nibble_offset)
1271{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001272 u16 regs[3];
1273 int i;
1274 int ret;
1275
1276 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001277 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001278 GLOBAL_VTU_DATA_0_3 + i);
1279 if (ret < 0)
1280 return ret;
1281
1282 regs[i] = ret;
1283 }
1284
Vivien Didelotfad09c72016-06-21 12:28:20 -04001285 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001286 unsigned int shift = (i % 4) * 4 + nibble_offset;
1287 u16 reg = regs[i / 4];
1288
1289 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1290 }
1291
1292 return 0;
1293}
1294
Vivien Didelotfad09c72016-06-21 12:28:20 -04001295static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001296 struct mv88e6xxx_vtu_stu_entry *entry)
1297{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001298 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001299}
1300
Vivien Didelotfad09c72016-06-21 12:28:20 -04001301static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001302 struct mv88e6xxx_vtu_stu_entry *entry)
1303{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001304 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001305}
1306
Vivien Didelotfad09c72016-06-21 12:28:20 -04001307static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001308 struct mv88e6xxx_vtu_stu_entry *entry,
1309 unsigned int nibble_offset)
1310{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001311 u16 regs[3] = { 0 };
1312 int i;
1313 int ret;
1314
Vivien Didelotfad09c72016-06-21 12:28:20 -04001315 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001316 unsigned int shift = (i % 4) * 4 + nibble_offset;
1317 u8 data = entry->data[i];
1318
1319 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1320 }
1321
1322 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001323 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001324 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1325 if (ret < 0)
1326 return ret;
1327 }
1328
1329 return 0;
1330}
1331
Vivien Didelotfad09c72016-06-21 12:28:20 -04001332static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001333 struct mv88e6xxx_vtu_stu_entry *entry)
1334{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001335 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001336}
1337
Vivien Didelotfad09c72016-06-21 12:28:20 -04001338static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001339 struct mv88e6xxx_vtu_stu_entry *entry)
1340{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001341 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001342}
1343
Vivien Didelotfad09c72016-06-21 12:28:20 -04001344static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001345{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001346 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001347 vid & GLOBAL_VTU_VID_MASK);
1348}
1349
Vivien Didelotfad09c72016-06-21 12:28:20 -04001350static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001351 struct mv88e6xxx_vtu_stu_entry *entry)
1352{
1353 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1354 int ret;
1355
Vivien Didelotfad09c72016-06-21 12:28:20 -04001356 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001357 if (ret < 0)
1358 return ret;
1359
Vivien Didelotfad09c72016-06-21 12:28:20 -04001360 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001361 if (ret < 0)
1362 return ret;
1363
Vivien Didelotfad09c72016-06-21 12:28:20 -04001364 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001365 if (ret < 0)
1366 return ret;
1367
1368 next.vid = ret & GLOBAL_VTU_VID_MASK;
1369 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1370
1371 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001372 ret = mv88e6xxx_vtu_data_read(chip, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001373 if (ret < 0)
1374 return ret;
1375
Vivien Didelotfad09c72016-06-21 12:28:20 -04001376 if (mv88e6xxx_has_fid_reg(chip)) {
1377 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001378 GLOBAL_VTU_FID);
1379 if (ret < 0)
1380 return ret;
1381
1382 next.fid = ret & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001383 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001384 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1385 * VTU DBNum[3:0] are located in VTU Operation 3:0
1386 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001387 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001388 GLOBAL_VTU_OP);
1389 if (ret < 0)
1390 return ret;
1391
1392 next.fid = (ret & 0xf00) >> 4;
1393 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001394 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001395
Vivien Didelotfad09c72016-06-21 12:28:20 -04001396 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1397 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001398 GLOBAL_VTU_SID);
1399 if (ret < 0)
1400 return ret;
1401
1402 next.sid = ret & GLOBAL_VTU_SID_MASK;
1403 }
1404 }
1405
1406 *entry = next;
1407 return 0;
1408}
1409
Vivien Didelotf81ec902016-05-09 13:22:58 -04001410static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1411 struct switchdev_obj_port_vlan *vlan,
1412 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001413{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001414 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001415 struct mv88e6xxx_vtu_stu_entry next;
1416 u16 pvid;
1417 int err;
1418
Vivien Didelotfad09c72016-06-21 12:28:20 -04001419 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001420 return -EOPNOTSUPP;
1421
Vivien Didelotfad09c72016-06-21 12:28:20 -04001422 mutex_lock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001423
Vivien Didelotfad09c72016-06-21 12:28:20 -04001424 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001425 if (err)
1426 goto unlock;
1427
Vivien Didelotfad09c72016-06-21 12:28:20 -04001428 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001429 if (err)
1430 goto unlock;
1431
1432 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001433 err = _mv88e6xxx_vtu_getnext(chip, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001434 if (err)
1435 break;
1436
1437 if (!next.valid)
1438 break;
1439
1440 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1441 continue;
1442
1443 /* reinit and dump this VLAN obj */
Vivien Didelot57d32312016-06-20 13:13:58 -04001444 vlan->vid_begin = next.vid;
1445 vlan->vid_end = next.vid;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001446 vlan->flags = 0;
1447
1448 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1449 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1450
1451 if (next.vid == pvid)
1452 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1453
1454 err = cb(&vlan->obj);
1455 if (err)
1456 break;
1457 } while (next.vid < GLOBAL_VTU_VID_MASK);
1458
1459unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001460 mutex_unlock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001461
1462 return err;
1463}
1464
Vivien Didelotfad09c72016-06-21 12:28:20 -04001465static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001466 struct mv88e6xxx_vtu_stu_entry *entry)
1467{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001468 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001469 u16 reg = 0;
1470 int ret;
1471
Vivien Didelotfad09c72016-06-21 12:28:20 -04001472 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001473 if (ret < 0)
1474 return ret;
1475
1476 if (!entry->valid)
1477 goto loadpurge;
1478
1479 /* Write port member tags */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001480 ret = mv88e6xxx_vtu_data_write(chip, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001481 if (ret < 0)
1482 return ret;
1483
Vivien Didelotfad09c72016-06-21 12:28:20 -04001484 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001485 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001486 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
1487 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001488 if (ret < 0)
1489 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001490 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001491
Vivien Didelotfad09c72016-06-21 12:28:20 -04001492 if (mv88e6xxx_has_fid_reg(chip)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001493 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001494 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_FID,
1495 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001496 if (ret < 0)
1497 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001498 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001499 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1500 * VTU DBNum[3:0] are located in VTU Operation 3:0
1501 */
1502 op |= (entry->fid & 0xf0) << 8;
1503 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001504 }
1505
1506 reg = GLOBAL_VTU_VID_VALID;
1507loadpurge:
1508 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001509 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001510 if (ret < 0)
1511 return ret;
1512
Vivien Didelotfad09c72016-06-21 12:28:20 -04001513 return _mv88e6xxx_vtu_cmd(chip, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001514}
1515
Vivien Didelotfad09c72016-06-21 12:28:20 -04001516static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001517 struct mv88e6xxx_vtu_stu_entry *entry)
1518{
1519 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1520 int ret;
1521
Vivien Didelotfad09c72016-06-21 12:28:20 -04001522 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001523 if (ret < 0)
1524 return ret;
1525
Vivien Didelotfad09c72016-06-21 12:28:20 -04001526 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001527 sid & GLOBAL_VTU_SID_MASK);
1528 if (ret < 0)
1529 return ret;
1530
Vivien Didelotfad09c72016-06-21 12:28:20 -04001531 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001532 if (ret < 0)
1533 return ret;
1534
Vivien Didelotfad09c72016-06-21 12:28:20 -04001535 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001536 if (ret < 0)
1537 return ret;
1538
1539 next.sid = ret & GLOBAL_VTU_SID_MASK;
1540
Vivien Didelotfad09c72016-06-21 12:28:20 -04001541 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001542 if (ret < 0)
1543 return ret;
1544
1545 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1546
1547 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001548 ret = mv88e6xxx_stu_data_read(chip, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001549 if (ret < 0)
1550 return ret;
1551 }
1552
1553 *entry = next;
1554 return 0;
1555}
1556
Vivien Didelotfad09c72016-06-21 12:28:20 -04001557static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001558 struct mv88e6xxx_vtu_stu_entry *entry)
1559{
1560 u16 reg = 0;
1561 int ret;
1562
Vivien Didelotfad09c72016-06-21 12:28:20 -04001563 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001564 if (ret < 0)
1565 return ret;
1566
1567 if (!entry->valid)
1568 goto loadpurge;
1569
1570 /* Write port states */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001571 ret = mv88e6xxx_stu_data_write(chip, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001572 if (ret < 0)
1573 return ret;
1574
1575 reg = GLOBAL_VTU_VID_VALID;
1576loadpurge:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001577 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001578 if (ret < 0)
1579 return ret;
1580
1581 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001582 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001583 if (ret < 0)
1584 return ret;
1585
Vivien Didelotfad09c72016-06-21 12:28:20 -04001586 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001587}
1588
Vivien Didelotfad09c72016-06-21 12:28:20 -04001589static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001590 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001591{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001592 struct dsa_switch *ds = chip->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001593 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001594 u16 fid;
1595 int ret;
1596
Vivien Didelotfad09c72016-06-21 12:28:20 -04001597 if (mv88e6xxx_num_databases(chip) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001598 upper_mask = 0xff;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001599 else if (mv88e6xxx_num_databases(chip) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001600 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001601 else
1602 return -EOPNOTSUPP;
1603
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001604 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001605 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001606 if (ret < 0)
1607 return ret;
1608
1609 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1610
1611 if (new) {
1612 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1613 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1614
Vivien Didelotfad09c72016-06-21 12:28:20 -04001615 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001616 ret);
1617 if (ret < 0)
1618 return ret;
1619 }
1620
1621 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001622 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001623 if (ret < 0)
1624 return ret;
1625
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001626 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001627
1628 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001629 ret &= ~upper_mask;
1630 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001631
Vivien Didelotfad09c72016-06-21 12:28:20 -04001632 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001633 ret);
1634 if (ret < 0)
1635 return ret;
1636
Andrew Lunnc8b09802016-06-04 21:16:57 +02001637 netdev_dbg(ds->ports[port].netdev,
1638 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001639 }
1640
1641 if (old)
1642 *old = fid;
1643
1644 return 0;
1645}
1646
Vivien Didelotfad09c72016-06-21 12:28:20 -04001647static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001648 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001649{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001650 return _mv88e6xxx_port_fid(chip, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001651}
1652
Vivien Didelotfad09c72016-06-21 12:28:20 -04001653static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001654 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001655{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001656 return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001657}
1658
Vivien Didelotfad09c72016-06-21 12:28:20 -04001659static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001660{
1661 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1662 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001663 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001664
1665 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1666
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001667 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001668 for (i = 0; i < chip->info->num_ports; ++i) {
1669 err = _mv88e6xxx_port_fid_get(chip, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001670 if (err)
1671 return err;
1672
1673 set_bit(*fid, fid_bitmap);
1674 }
1675
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001676 /* Set every FID bit used by the VLAN entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001677 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001678 if (err)
1679 return err;
1680
1681 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001682 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001683 if (err)
1684 return err;
1685
1686 if (!vlan.valid)
1687 break;
1688
1689 set_bit(vlan.fid, fid_bitmap);
1690 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1691
1692 /* The reset value 0x000 is used to indicate that multiple address
1693 * databases are not needed. Return the next positive available.
1694 */
1695 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotfad09c72016-06-21 12:28:20 -04001696 if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001697 return -ENOSPC;
1698
1699 /* Clear the database */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001700 return _mv88e6xxx_atu_flush(chip, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001701}
1702
Vivien Didelotfad09c72016-06-21 12:28:20 -04001703static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001704 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001705{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001706 struct dsa_switch *ds = chip->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001707 struct mv88e6xxx_vtu_stu_entry vlan = {
1708 .valid = true,
1709 .vid = vid,
1710 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001711 int i, err;
1712
Vivien Didelotfad09c72016-06-21 12:28:20 -04001713 err = _mv88e6xxx_fid_new(chip, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001714 if (err)
1715 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001716
Vivien Didelot3d131f02015-11-03 10:52:52 -05001717 /* exclude all ports except the CPU and DSA ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001718 for (i = 0; i < chip->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001719 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1720 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1721 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001722
Vivien Didelotfad09c72016-06-21 12:28:20 -04001723 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
1724 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001725 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001726
1727 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1728 * implemented, only one STU entry is needed to cover all VTU
1729 * entries. Thus, validate the SID 0.
1730 */
1731 vlan.sid = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001732 err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001733 if (err)
1734 return err;
1735
1736 if (vstp.sid != vlan.sid || !vstp.valid) {
1737 memset(&vstp, 0, sizeof(vstp));
1738 vstp.valid = true;
1739 vstp.sid = vlan.sid;
1740
Vivien Didelotfad09c72016-06-21 12:28:20 -04001741 err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001742 if (err)
1743 return err;
1744 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001745 }
1746
1747 *entry = vlan;
1748 return 0;
1749}
1750
Vivien Didelotfad09c72016-06-21 12:28:20 -04001751static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001752 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1753{
1754 int err;
1755
1756 if (!vid)
1757 return -EINVAL;
1758
Vivien Didelotfad09c72016-06-21 12:28:20 -04001759 err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001760 if (err)
1761 return err;
1762
Vivien Didelotfad09c72016-06-21 12:28:20 -04001763 err = _mv88e6xxx_vtu_getnext(chip, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001764 if (err)
1765 return err;
1766
1767 if (entry->vid != vid || !entry->valid) {
1768 if (!creat)
1769 return -EOPNOTSUPP;
1770 /* -ENOENT would've been more appropriate, but switchdev expects
1771 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1772 */
1773
Vivien Didelotfad09c72016-06-21 12:28:20 -04001774 err = _mv88e6xxx_vtu_new(chip, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001775 }
1776
1777 return err;
1778}
1779
Vivien Didelotda9c3592016-02-12 12:09:40 -05001780static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1781 u16 vid_begin, u16 vid_end)
1782{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001783 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001784 struct mv88e6xxx_vtu_stu_entry vlan;
1785 int i, err;
1786
1787 if (!vid_begin)
1788 return -EOPNOTSUPP;
1789
Vivien Didelotfad09c72016-06-21 12:28:20 -04001790 mutex_lock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001791
Vivien Didelotfad09c72016-06-21 12:28:20 -04001792 err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001793 if (err)
1794 goto unlock;
1795
1796 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001797 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001798 if (err)
1799 goto unlock;
1800
1801 if (!vlan.valid)
1802 break;
1803
1804 if (vlan.vid > vid_end)
1805 break;
1806
Vivien Didelotfad09c72016-06-21 12:28:20 -04001807 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001808 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1809 continue;
1810
1811 if (vlan.data[i] ==
1812 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1813 continue;
1814
Vivien Didelotfad09c72016-06-21 12:28:20 -04001815 if (chip->ports[i].bridge_dev ==
1816 chip->ports[port].bridge_dev)
Vivien Didelotda9c3592016-02-12 12:09:40 -05001817 break; /* same bridge, check next VLAN */
1818
Andrew Lunnc8b09802016-06-04 21:16:57 +02001819 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05001820 "hardware VLAN %d already used by %s\n",
1821 vlan.vid,
Vivien Didelotfad09c72016-06-21 12:28:20 -04001822 netdev_name(chip->ports[i].bridge_dev));
Vivien Didelotda9c3592016-02-12 12:09:40 -05001823 err = -EOPNOTSUPP;
1824 goto unlock;
1825 }
1826 } while (vlan.vid < vid_end);
1827
1828unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001829 mutex_unlock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001830
1831 return err;
1832}
1833
Vivien Didelot214cdb92016-02-26 13:16:08 -05001834static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1835 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1836 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1837 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1838 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1839};
1840
Vivien Didelotf81ec902016-05-09 13:22:58 -04001841static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1842 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05001843{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001844 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001845 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1846 PORT_CONTROL_2_8021Q_DISABLED;
1847 int ret;
1848
Vivien Didelotfad09c72016-06-21 12:28:20 -04001849 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001850 return -EOPNOTSUPP;
1851
Vivien Didelotfad09c72016-06-21 12:28:20 -04001852 mutex_lock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001853
Vivien Didelotfad09c72016-06-21 12:28:20 -04001854 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001855 if (ret < 0)
1856 goto unlock;
1857
1858 old = ret & PORT_CONTROL_2_8021Q_MASK;
1859
Vivien Didelot5220ef12016-03-07 18:24:52 -05001860 if (new != old) {
1861 ret &= ~PORT_CONTROL_2_8021Q_MASK;
1862 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001863
Vivien Didelotfad09c72016-06-21 12:28:20 -04001864 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05001865 ret);
1866 if (ret < 0)
1867 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001868
Andrew Lunnc8b09802016-06-04 21:16:57 +02001869 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05001870 mv88e6xxx_port_8021q_mode_names[new],
1871 mv88e6xxx_port_8021q_mode_names[old]);
1872 }
1873
1874 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001875unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001876 mutex_unlock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001877
1878 return ret;
1879}
1880
Vivien Didelot57d32312016-06-20 13:13:58 -04001881static int
1882mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1883 const struct switchdev_obj_port_vlan *vlan,
1884 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001885{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001886 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001887 int err;
1888
Vivien Didelotfad09c72016-06-21 12:28:20 -04001889 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001890 return -EOPNOTSUPP;
1891
Vivien Didelotda9c3592016-02-12 12:09:40 -05001892 /* If the requested port doesn't belong to the same bridge as the VLAN
1893 * members, do not support it (yet) and fallback to software VLAN.
1894 */
1895 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1896 vlan->vid_end);
1897 if (err)
1898 return err;
1899
Vivien Didelot76e398a2015-11-01 12:33:55 -05001900 /* We don't need any dynamic resource from the kernel (yet),
1901 * so skip the prepare phase.
1902 */
1903 return 0;
1904}
1905
Vivien Didelotfad09c72016-06-21 12:28:20 -04001906static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001907 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001908{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001909 struct mv88e6xxx_vtu_stu_entry vlan;
1910 int err;
1911
Vivien Didelotfad09c72016-06-21 12:28:20 -04001912 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001913 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001914 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001915
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001916 vlan.data[port] = untagged ?
1917 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1918 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1919
Vivien Didelotfad09c72016-06-21 12:28:20 -04001920 return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001921}
1922
Vivien Didelotf81ec902016-05-09 13:22:58 -04001923static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1924 const struct switchdev_obj_port_vlan *vlan,
1925 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001926{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001927 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001928 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1929 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1930 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001931
Vivien Didelotfad09c72016-06-21 12:28:20 -04001932 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001933 return;
1934
Vivien Didelotfad09c72016-06-21 12:28:20 -04001935 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001936
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001937 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Vivien Didelotfad09c72016-06-21 12:28:20 -04001938 if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02001939 netdev_err(ds->ports[port].netdev,
1940 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001941 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05001942
Vivien Didelotfad09c72016-06-21 12:28:20 -04001943 if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02001944 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001945 vlan->vid_end);
1946
Vivien Didelotfad09c72016-06-21 12:28:20 -04001947 mutex_unlock(&chip->reg_lock);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001948}
1949
Vivien Didelotfad09c72016-06-21 12:28:20 -04001950static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001951 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001952{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001953 struct dsa_switch *ds = chip->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001954 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001955 int i, err;
1956
Vivien Didelotfad09c72016-06-21 12:28:20 -04001957 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001958 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001959 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001960
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001961 /* Tell switchdev if this VLAN is handled in software */
1962 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05001963 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001964
1965 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1966
1967 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001968 vlan.valid = false;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001969 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05001970 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001971 continue;
1972
1973 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001974 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001975 break;
1976 }
1977 }
1978
Vivien Didelotfad09c72016-06-21 12:28:20 -04001979 err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001980 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001981 return err;
1982
Vivien Didelotfad09c72016-06-21 12:28:20 -04001983 return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001984}
1985
Vivien Didelotf81ec902016-05-09 13:22:58 -04001986static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1987 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001988{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001989 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001990 u16 pvid, vid;
1991 int err = 0;
1992
Vivien Didelotfad09c72016-06-21 12:28:20 -04001993 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001994 return -EOPNOTSUPP;
1995
Vivien Didelotfad09c72016-06-21 12:28:20 -04001996 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001997
Vivien Didelotfad09c72016-06-21 12:28:20 -04001998 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001999 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002000 goto unlock;
2001
Vivien Didelot76e398a2015-11-01 12:33:55 -05002002 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002003 err = _mv88e6xxx_port_vlan_del(chip, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002004 if (err)
2005 goto unlock;
2006
2007 if (vid == pvid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002008 err = _mv88e6xxx_port_pvid_set(chip, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002009 if (err)
2010 goto unlock;
2011 }
2012 }
2013
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002014unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002015 mutex_unlock(&chip->reg_lock);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002016
2017 return err;
2018}
2019
Vivien Didelotfad09c72016-06-21 12:28:20 -04002020static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002021 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002022{
2023 int i, ret;
2024
2025 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002026 ret = _mv88e6xxx_reg_write(
Vivien Didelotfad09c72016-06-21 12:28:20 -04002027 chip, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002028 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002029 if (ret < 0)
2030 return ret;
2031 }
2032
2033 return 0;
2034}
2035
Vivien Didelotfad09c72016-06-21 12:28:20 -04002036static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002037 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002038{
2039 int i, ret;
2040
2041 for (i = 0; i < 3; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002042 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002043 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002044 if (ret < 0)
2045 return ret;
2046 addr[i * 2] = ret >> 8;
2047 addr[i * 2 + 1] = ret & 0xff;
2048 }
2049
2050 return 0;
2051}
2052
Vivien Didelotfad09c72016-06-21 12:28:20 -04002053static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002054 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002055{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002056 int ret;
2057
Vivien Didelotfad09c72016-06-21 12:28:20 -04002058 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002059 if (ret < 0)
2060 return ret;
2061
Vivien Didelotfad09c72016-06-21 12:28:20 -04002062 ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002063 if (ret < 0)
2064 return ret;
2065
Vivien Didelotfad09c72016-06-21 12:28:20 -04002066 ret = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002067 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002068 return ret;
2069
Vivien Didelotfad09c72016-06-21 12:28:20 -04002070 return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002071}
David S. Millercdf09692015-08-11 12:00:37 -07002072
Vivien Didelotfad09c72016-06-21 12:28:20 -04002073static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_chip *chip, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002074 const unsigned char *addr, u16 vid,
2075 u8 state)
2076{
2077 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002078 struct mv88e6xxx_vtu_stu_entry vlan;
2079 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002080
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002081 /* Null VLAN ID corresponds to the port private database */
2082 if (vid == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002083 err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002084 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002085 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002086 if (err)
2087 return err;
2088
2089 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002090 entry.state = state;
2091 ether_addr_copy(entry.mac, addr);
2092 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2093 entry.trunk = false;
2094 entry.portv_trunkid = BIT(port);
2095 }
2096
Vivien Didelotfad09c72016-06-21 12:28:20 -04002097 return _mv88e6xxx_atu_load(chip, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002098}
2099
Vivien Didelotf81ec902016-05-09 13:22:58 -04002100static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2101 const struct switchdev_obj_port_fdb *fdb,
2102 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002103{
2104 /* We don't need any dynamic resource from the kernel (yet),
2105 * so skip the prepare phase.
2106 */
2107 return 0;
2108}
2109
Vivien Didelotf81ec902016-05-09 13:22:58 -04002110static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2111 const struct switchdev_obj_port_fdb *fdb,
2112 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002113{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002114 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002115 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2116 GLOBAL_ATU_DATA_STATE_UC_STATIC;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002117 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002118
Vivien Didelotfad09c72016-06-21 12:28:20 -04002119 mutex_lock(&chip->reg_lock);
2120 if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002121 netdev_err(ds->ports[port].netdev,
2122 "failed to load MAC address\n");
Vivien Didelotfad09c72016-06-21 12:28:20 -04002123 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002124}
2125
Vivien Didelotf81ec902016-05-09 13:22:58 -04002126static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2127 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002128{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002129 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
David S. Millercdf09692015-08-11 12:00:37 -07002130 int ret;
2131
Vivien Didelotfad09c72016-06-21 12:28:20 -04002132 mutex_lock(&chip->reg_lock);
2133 ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002134 GLOBAL_ATU_DATA_STATE_UNUSED);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002135 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002136
2137 return ret;
2138}
2139
Vivien Didelotfad09c72016-06-21 12:28:20 -04002140static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002141 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002142{
Vivien Didelot1d194042015-08-10 09:09:51 -04002143 struct mv88e6xxx_atu_entry next = { 0 };
2144 int ret;
2145
2146 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002147
Vivien Didelotfad09c72016-06-21 12:28:20 -04002148 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002149 if (ret < 0)
2150 return ret;
2151
Vivien Didelotfad09c72016-06-21 12:28:20 -04002152 ret = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002153 if (ret < 0)
2154 return ret;
2155
Vivien Didelotfad09c72016-06-21 12:28:20 -04002156 ret = _mv88e6xxx_atu_mac_read(chip, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002157 if (ret < 0)
2158 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002159
Vivien Didelotfad09c72016-06-21 12:28:20 -04002160 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002161 if (ret < 0)
2162 return ret;
2163
2164 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2165 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2166 unsigned int mask, shift;
2167
2168 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2169 next.trunk = true;
2170 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2171 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2172 } else {
2173 next.trunk = false;
2174 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2175 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2176 }
2177
2178 next.portv_trunkid = (ret & mask) >> shift;
2179 }
2180
2181 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002182 return 0;
2183}
2184
Vivien Didelotfad09c72016-06-21 12:28:20 -04002185static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002186 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002187 struct switchdev_obj_port_fdb *fdb,
2188 int (*cb)(struct switchdev_obj *obj))
2189{
2190 struct mv88e6xxx_atu_entry addr = {
2191 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2192 };
2193 int err;
2194
Vivien Didelotfad09c72016-06-21 12:28:20 -04002195 err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002196 if (err)
2197 return err;
2198
2199 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002200 err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002201 if (err)
2202 break;
2203
2204 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2205 break;
2206
2207 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2208 bool is_static = addr.state ==
2209 (is_multicast_ether_addr(addr.mac) ?
2210 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2211 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2212
2213 fdb->vid = vid;
2214 ether_addr_copy(fdb->addr, addr.mac);
2215 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2216
2217 err = cb(&fdb->obj);
2218 if (err)
2219 break;
2220 }
2221 } while (!is_broadcast_ether_addr(addr.mac));
2222
2223 return err;
2224}
2225
Vivien Didelotf81ec902016-05-09 13:22:58 -04002226static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2227 struct switchdev_obj_port_fdb *fdb,
2228 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002229{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002230 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002231 struct mv88e6xxx_vtu_stu_entry vlan = {
2232 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2233 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002234 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002235 int err;
2236
Vivien Didelotfad09c72016-06-21 12:28:20 -04002237 mutex_lock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002238
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002239 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002240 err = _mv88e6xxx_port_fid_get(chip, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002241 if (err)
2242 goto unlock;
2243
Vivien Didelotfad09c72016-06-21 12:28:20 -04002244 err = _mv88e6xxx_port_fdb_dump_one(chip, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002245 if (err)
2246 goto unlock;
2247
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002248 /* Dump VLANs' Filtering Information Databases */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002249 err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002250 if (err)
2251 goto unlock;
2252
2253 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002254 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002255 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002256 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002257
2258 if (!vlan.valid)
2259 break;
2260
Vivien Didelotfad09c72016-06-21 12:28:20 -04002261 err = _mv88e6xxx_port_fdb_dump_one(chip, vlan.fid, vlan.vid,
2262 port, fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002263 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002264 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002265 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2266
2267unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002268 mutex_unlock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002269
2270 return err;
2271}
2272
Vivien Didelotf81ec902016-05-09 13:22:58 -04002273static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2274 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002275{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002276 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002277 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002278
Vivien Didelotfad09c72016-06-21 12:28:20 -04002279 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002280
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002281 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002282 chip->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002283
Vivien Didelotfad09c72016-06-21 12:28:20 -04002284 for (i = 0; i < chip->info->num_ports; ++i) {
2285 if (chip->ports[i].bridge_dev == bridge) {
2286 err = _mv88e6xxx_port_based_vlan_map(chip, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002287 if (err)
2288 break;
2289 }
2290 }
2291
Vivien Didelotfad09c72016-06-21 12:28:20 -04002292 mutex_unlock(&chip->reg_lock);
Vivien Didelota6692752016-02-12 12:09:39 -05002293
Vivien Didelot466dfa02016-02-26 13:16:05 -05002294 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002295}
2296
Vivien Didelotf81ec902016-05-09 13:22:58 -04002297static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002298{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002299 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
2300 struct net_device *bridge = chip->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002301 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002302
Vivien Didelotfad09c72016-06-21 12:28:20 -04002303 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002304
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002305 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002306 chip->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002307
Vivien Didelotfad09c72016-06-21 12:28:20 -04002308 for (i = 0; i < chip->info->num_ports; ++i)
2309 if (i == port || chip->ports[i].bridge_dev == bridge)
2310 if (_mv88e6xxx_port_based_vlan_map(chip, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002311 netdev_warn(ds->ports[i].netdev,
2312 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002313
Vivien Didelotfad09c72016-06-21 12:28:20 -04002314 mutex_unlock(&chip->reg_lock);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002315}
2316
Vivien Didelotfad09c72016-06-21 12:28:20 -04002317static int _mv88e6xxx_mdio_page_write(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002318 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002319{
2320 int ret;
2321
Vivien Didelotfad09c72016-06-21 12:28:20 -04002322 ret = mv88e6xxx_mdio_write_indirect(chip, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002323 if (ret < 0)
2324 goto restore_page_0;
2325
Vivien Didelotfad09c72016-06-21 12:28:20 -04002326 ret = mv88e6xxx_mdio_write_indirect(chip, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002327restore_page_0:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002328 mv88e6xxx_mdio_write_indirect(chip, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002329
2330 return ret;
2331}
2332
Vivien Didelotfad09c72016-06-21 12:28:20 -04002333static int _mv88e6xxx_mdio_page_read(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002334 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002335{
2336 int ret;
2337
Vivien Didelotfad09c72016-06-21 12:28:20 -04002338 ret = mv88e6xxx_mdio_write_indirect(chip, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002339 if (ret < 0)
2340 goto restore_page_0;
2341
Vivien Didelotfad09c72016-06-21 12:28:20 -04002342 ret = mv88e6xxx_mdio_read_indirect(chip, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002343restore_page_0:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002344 mv88e6xxx_mdio_write_indirect(chip, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002345
2346 return ret;
2347}
2348
Vivien Didelotfad09c72016-06-21 12:28:20 -04002349static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
Vivien Didelot552238b2016-05-09 13:22:49 -04002350{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002351 bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
Vivien Didelot552238b2016-05-09 13:22:49 -04002352 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002353 struct gpio_desc *gpiod = chip->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002354 unsigned long timeout;
2355 int ret;
2356 int i;
2357
2358 /* Set all ports to the disabled state. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002359 for (i = 0; i < chip->info->num_ports; i++) {
2360 ret = _mv88e6xxx_reg_read(chip, REG_PORT(i), PORT_CONTROL);
Vivien Didelot552238b2016-05-09 13:22:49 -04002361 if (ret < 0)
2362 return ret;
2363
Vivien Didelotfad09c72016-06-21 12:28:20 -04002364 ret = _mv88e6xxx_reg_write(chip, REG_PORT(i), PORT_CONTROL,
Vivien Didelot552238b2016-05-09 13:22:49 -04002365 ret & 0xfffc);
2366 if (ret)
2367 return ret;
2368 }
2369
2370 /* Wait for transmit queues to drain. */
2371 usleep_range(2000, 4000);
2372
2373 /* If there is a gpio connected to the reset pin, toggle it */
2374 if (gpiod) {
2375 gpiod_set_value_cansleep(gpiod, 1);
2376 usleep_range(10000, 20000);
2377 gpiod_set_value_cansleep(gpiod, 0);
2378 usleep_range(10000, 20000);
2379 }
2380
2381 /* Reset the switch. Keep the PPU active if requested. The PPU
2382 * needs to be active to support indirect phy register access
2383 * through global registers 0x18 and 0x19.
2384 */
2385 if (ppu_active)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002386 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc000);
Vivien Didelot552238b2016-05-09 13:22:49 -04002387 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002388 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc400);
Vivien Didelot552238b2016-05-09 13:22:49 -04002389 if (ret)
2390 return ret;
2391
2392 /* Wait up to one second for reset to complete. */
2393 timeout = jiffies + 1 * HZ;
2394 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002395 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, 0x00);
Vivien Didelot552238b2016-05-09 13:22:49 -04002396 if (ret < 0)
2397 return ret;
2398
2399 if ((ret & is_reset) == is_reset)
2400 break;
2401 usleep_range(1000, 2000);
2402 }
2403 if (time_after(jiffies, timeout))
2404 ret = -ETIMEDOUT;
2405 else
2406 ret = 0;
2407
2408 return ret;
2409}
2410
Vivien Didelotfad09c72016-06-21 12:28:20 -04002411static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_chip *chip)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002412{
2413 int ret;
2414
Vivien Didelotfad09c72016-06-21 12:28:20 -04002415 ret = _mv88e6xxx_mdio_page_read(chip, REG_FIBER_SERDES,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002416 PAGE_FIBER_SERDES, MII_BMCR);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002417 if (ret < 0)
2418 return ret;
2419
2420 if (ret & BMCR_PDOWN) {
2421 ret &= ~BMCR_PDOWN;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002422 ret = _mv88e6xxx_mdio_page_write(chip, REG_FIBER_SERDES,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002423 PAGE_FIBER_SERDES, MII_BMCR,
2424 ret);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002425 }
2426
2427 return ret;
2428}
2429
Vivien Didelot8f6345b2016-07-20 18:18:36 -04002430static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port,
2431 int reg, u16 *val)
2432{
2433 int addr = chip->info->port_base_addr + port;
2434
2435 if (port >= chip->info->num_ports)
2436 return -EINVAL;
2437
2438 return mv88e6xxx_read(chip, addr, reg, val);
2439}
2440
Vivien Didelotfad09c72016-06-21 12:28:20 -04002441static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002442{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002443 struct dsa_switch *ds = chip->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002444 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002445 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002446
Vivien Didelotfad09c72016-06-21 12:28:20 -04002447 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2448 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2449 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2450 mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002451 /* MAC Forcing register: don't force link, speed,
2452 * duplex or flow control state to any particular
2453 * values on physical ports, but force the CPU port
2454 * and all DSA ports to their maximum bandwidth and
2455 * full duplex.
2456 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002457 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002458 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002459 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002460 reg |= PORT_PCS_CTRL_FORCE_LINK |
2461 PORT_PCS_CTRL_LINK_UP |
2462 PORT_PCS_CTRL_DUPLEX_FULL |
2463 PORT_PCS_CTRL_FORCE_DUPLEX;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002464 if (mv88e6xxx_6065_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002465 reg |= PORT_PCS_CTRL_100;
2466 else
2467 reg |= PORT_PCS_CTRL_1000;
2468 } else {
2469 reg |= PORT_PCS_CTRL_UNFORCED;
2470 }
2471
Vivien Didelotfad09c72016-06-21 12:28:20 -04002472 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002473 PORT_PCS_CTRL, reg);
2474 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002475 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002476 }
2477
2478 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2479 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2480 * tunneling, determine priority by looking at 802.1p and IP
2481 * priority fields (IP prio has precedence), and set STP state
2482 * to Forwarding.
2483 *
2484 * If this is the CPU link, use DSA or EDSA tagging depending
2485 * on which tagging mode was configured.
2486 *
2487 * If this is a link to another switch, use DSA tagging mode.
2488 *
2489 * If this is the upstream port for this switch, enable
2490 * forwarding of unknown unicasts and multicasts.
2491 */
2492 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002493 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2494 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2495 mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2496 mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002497 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2498 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2499 PORT_CONTROL_STATE_FORWARDING;
2500 if (dsa_is_cpu_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002501 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002502 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002503 if (mv88e6xxx_6352_family(chip) ||
2504 mv88e6xxx_6351_family(chip) ||
2505 mv88e6xxx_6165_family(chip) ||
2506 mv88e6xxx_6097_family(chip) ||
2507 mv88e6xxx_6320_family(chip)) {
Andrew Lunn5377b802016-06-04 21:17:02 +02002508 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2509 PORT_CONTROL_FORWARD_UNKNOWN |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002510 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002511 }
2512
Vivien Didelotfad09c72016-06-21 12:28:20 -04002513 if (mv88e6xxx_6352_family(chip) ||
2514 mv88e6xxx_6351_family(chip) ||
2515 mv88e6xxx_6165_family(chip) ||
2516 mv88e6xxx_6097_family(chip) ||
2517 mv88e6xxx_6095_family(chip) ||
2518 mv88e6xxx_6065_family(chip) ||
2519 mv88e6xxx_6185_family(chip) ||
2520 mv88e6xxx_6320_family(chip)) {
Vivien Didelot57d32312016-06-20 13:13:58 -04002521 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002522 }
2523 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002524 if (dsa_is_dsa_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002525 if (mv88e6xxx_6095_family(chip) ||
2526 mv88e6xxx_6185_family(chip))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002527 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002528 if (mv88e6xxx_6352_family(chip) ||
2529 mv88e6xxx_6351_family(chip) ||
2530 mv88e6xxx_6165_family(chip) ||
2531 mv88e6xxx_6097_family(chip) ||
2532 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002533 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002534 }
2535
Andrew Lunn54d792f2015-05-06 01:09:47 +02002536 if (port == dsa_upstream_port(ds))
2537 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2538 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2539 }
2540 if (reg) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002541 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002542 PORT_CONTROL, reg);
2543 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002544 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002545 }
2546
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002547 /* If this port is connected to a SerDes, make sure the SerDes is not
2548 * powered down.
2549 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002550 if (mv88e6xxx_6352_family(chip)) {
2551 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002552 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002553 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002554 ret &= PORT_STATUS_CMODE_MASK;
2555 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2556 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2557 (ret == PORT_STATUS_CMODE_SGMII)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002558 ret = mv88e6xxx_power_on_serdes(chip);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002559 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002560 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002561 }
2562 }
2563
Vivien Didelot8efdda42015-08-13 12:52:23 -04002564 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002565 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002566 * untagged frames on this port, do a destination address lookup on all
2567 * received packets as usual, disable ARP mirroring and don't send a
2568 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002569 */
2570 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002571 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2572 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2573 mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2574 mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002575 reg = PORT_CONTROL_2_MAP_DA;
2576
Vivien Didelotfad09c72016-06-21 12:28:20 -04002577 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2578 mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002579 reg |= PORT_CONTROL_2_JUMBO_10240;
2580
Vivien Didelotfad09c72016-06-21 12:28:20 -04002581 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002582 /* Set the upstream port this port should use */
2583 reg |= dsa_upstream_port(ds);
2584 /* enable forwarding of unknown multicast addresses to
2585 * the upstream port
2586 */
2587 if (port == dsa_upstream_port(ds))
2588 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2589 }
2590
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002591 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002592
Andrew Lunn54d792f2015-05-06 01:09:47 +02002593 if (reg) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002594 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002595 PORT_CONTROL_2, reg);
2596 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002597 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002598 }
2599
2600 /* Port Association Vector: when learning source addresses
2601 * of packets, add the address to the address database using
2602 * a port bitmap that has only the bit for this port set and
2603 * the other bits clear.
2604 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002605 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002606 /* Disable learning for CPU port */
2607 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002608 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002609
Vivien Didelotfad09c72016-06-21 12:28:20 -04002610 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_ASSOC_VECTOR,
2611 reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002612 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002613 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002614
2615 /* Egress rate control 2: disable egress rate control. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002616 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002617 0x0000);
2618 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002619 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002620
Vivien Didelotfad09c72016-06-21 12:28:20 -04002621 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2622 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2623 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002624 /* Do not limit the period of time that this port can
2625 * be paused for by the remote end or the period of
2626 * time that this port can pause the remote end.
2627 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002628 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002629 PORT_PAUSE_CTRL, 0x0000);
2630 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002631 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002632
2633 /* Port ATU control: disable limiting the number of
2634 * address database entries that this port is allowed
2635 * to use.
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_ATU_CONTROL, 0x0000);
2639 /* Priority Override: disable DA, SA and VTU priority
2640 * override.
2641 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002642 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002643 PORT_PRI_OVERRIDE, 0x0000);
2644 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002645 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002646
2647 /* Port Ethertype: use the Ethertype DSA Ethertype
2648 * value.
2649 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002650 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002651 PORT_ETH_TYPE, ETH_P_EDSA);
2652 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002653 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002654 /* Tag Remap: use an identity 802.1p prio -> switch
2655 * prio mapping.
2656 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002657 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002658 PORT_TAG_REGMAP_0123, 0x3210);
2659 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002660 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002661
2662 /* Tag Remap 2: use an identity 802.1p prio -> switch
2663 * prio mapping.
2664 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002665 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002666 PORT_TAG_REGMAP_4567, 0x7654);
2667 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002668 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002669 }
2670
Vivien Didelotfad09c72016-06-21 12:28:20 -04002671 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2672 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2673 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2674 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002675 /* Rate Control: disable ingress rate limiting. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002676 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002677 PORT_RATE_CONTROL, 0x0001);
2678 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002679 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002680 }
2681
Guenter Roeck366f0a02015-03-26 18:36:30 -07002682 /* Port Control 1: disable trunking, disable sending
2683 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002684 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002685 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_1,
2686 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002687 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002688 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002689
Vivien Didelot207afda2016-04-14 14:42:09 -04002690 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002691 * database, and allow bidirectional communication between the
2692 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002693 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002694 ret = _mv88e6xxx_port_fid_set(chip, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002695 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002696 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002697
Vivien Didelotfad09c72016-06-21 12:28:20 -04002698 ret = _mv88e6xxx_port_based_vlan_map(chip, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002699 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002700 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002701
2702 /* Default VLAN ID and priority: don't set a default VLAN
2703 * ID, and set the default packet priority to zero.
2704 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002705 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e62015-04-20 17:43:26 -04002706 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002707 if (ret)
2708 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002709
Andrew Lunndbde9e62015-05-06 01:09:48 +02002710 return 0;
2711}
2712
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002713static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
2714{
2715 int err;
2716
2717 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
2718 (addr[0] << 8) | addr[1]);
2719 if (err)
2720 return err;
2721
2722 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
2723 (addr[2] << 8) | addr[3]);
2724 if (err)
2725 return err;
2726
2727 return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
2728 (addr[4] << 8) | addr[5]);
2729}
2730
Vivien Didelotacddbd22016-07-18 20:45:39 -04002731static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
2732 unsigned int msecs)
2733{
2734 const unsigned int coeff = chip->info->age_time_coeff;
2735 const unsigned int min = 0x01 * coeff;
2736 const unsigned int max = 0xff * coeff;
2737 u8 age_time;
2738 u16 val;
2739 int err;
2740
2741 if (msecs < min || msecs > max)
2742 return -ERANGE;
2743
2744 /* Round to nearest multiple of coeff */
2745 age_time = (msecs + coeff / 2) / coeff;
2746
2747 err = mv88e6xxx_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, &val);
2748 if (err)
2749 return err;
2750
2751 /* AgeTime is 11:4 bits */
2752 val &= ~0xff0;
2753 val |= age_time << 4;
2754
2755 return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, val);
2756}
2757
Vivien Didelot2cfcd962016-07-18 20:45:40 -04002758static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
2759 unsigned int ageing_time)
2760{
2761 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
2762 int err;
2763
2764 mutex_lock(&chip->reg_lock);
2765 err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
2766 mutex_unlock(&chip->reg_lock);
2767
2768 return err;
2769}
2770
Vivien Didelot97299342016-07-18 20:45:30 -04002771static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
Vivien Didelot08a01262016-05-09 13:22:50 -04002772{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002773 struct dsa_switch *ds = chip->ds;
Vivien Didelotb0745e872016-05-09 13:22:53 -04002774 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002775 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002776 int err;
Vivien Didelot08a01262016-05-09 13:22:50 -04002777
Vivien Didelot119477b2016-05-09 13:22:51 -04002778 /* Enable the PHY Polling Unit if present, don't discard any packets,
2779 * and mask all interrupt sources.
2780 */
2781 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002782 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
2783 mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
Vivien Didelot119477b2016-05-09 13:22:51 -04002784 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2785
Vivien Didelotfad09c72016-06-21 12:28:20 -04002786 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL, reg);
Vivien Didelot119477b2016-05-09 13:22:51 -04002787 if (err)
2788 return err;
2789
Vivien Didelotb0745e872016-05-09 13:22:53 -04002790 /* Configure the upstream port, and configure it as the port to which
2791 * ingress and egress and ARP monitor frames are to be sent.
2792 */
2793 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2794 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2795 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002796 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MONITOR_CONTROL,
2797 reg);
Vivien Didelotb0745e872016-05-09 13:22:53 -04002798 if (err)
2799 return err;
2800
Vivien Didelot50484ff2016-05-09 13:22:54 -04002801 /* Disable remote management, and set the switch's DSA device number. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002802 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL_2,
Vivien Didelot50484ff2016-05-09 13:22:54 -04002803 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2804 (ds->index & 0x1f));
2805 if (err)
2806 return err;
2807
Vivien Didelotacddbd22016-07-18 20:45:39 -04002808 /* Clear all the VTU and STU entries */
2809 err = _mv88e6xxx_vtu_stu_flush(chip);
2810 if (err < 0)
2811 return err;
2812
Vivien Didelot08a01262016-05-09 13:22:50 -04002813 /* Set the default address aging time to 5 minutes, and
2814 * enable address learn messages to be sent to all message
2815 * ports.
2816 */
Vivien Didelotacddbd22016-07-18 20:45:39 -04002817 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2818 GLOBAL_ATU_CONTROL_LEARN2ALL);
Vivien Didelot08a01262016-05-09 13:22:50 -04002819 if (err)
2820 return err;
2821
Vivien Didelotacddbd22016-07-18 20:45:39 -04002822 err = mv88e6xxx_g1_set_age_time(chip, 300000);
2823 if (err)
Vivien Didelot97299342016-07-18 20:45:30 -04002824 return err;
2825
2826 /* Clear all ATU entries */
2827 err = _mv88e6xxx_atu_flush(chip, 0, true);
2828 if (err)
2829 return err;
2830
Vivien Didelot08a01262016-05-09 13:22:50 -04002831 /* Configure the IP ToS mapping registers. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002832 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002833 if (err)
2834 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002835 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002836 if (err)
2837 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002838 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002839 if (err)
2840 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002841 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002842 if (err)
2843 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002844 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04002845 if (err)
2846 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002847 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04002848 if (err)
2849 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002850 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04002851 if (err)
2852 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002853 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04002854 if (err)
2855 return err;
2856
2857 /* Configure the IEEE 802.1p priority mapping register. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002858 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Vivien Didelot08a01262016-05-09 13:22:50 -04002859 if (err)
2860 return err;
2861
Vivien Didelot97299342016-07-18 20:45:30 -04002862 /* Clear the statistics counters for all ports */
2863 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
2864 GLOBAL_STATS_OP_FLUSH_ALL);
2865 if (err)
2866 return err;
2867
2868 /* Wait for the flush to complete. */
2869 err = _mv88e6xxx_stats_wait(chip);
2870 if (err)
2871 return err;
2872
2873 return 0;
2874}
2875
Vivien Didelotf22ab642016-07-18 20:45:31 -04002876static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
2877 int target, int port)
2878{
2879 u16 val = (target << 8) | (port & 0xf);
2880
2881 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, val);
2882}
2883
2884static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
2885{
2886 int target, port;
2887 int err;
2888
2889 /* Initialize the routing port to the 32 possible target devices */
2890 for (target = 0; target < 32; ++target) {
2891 port = 0xf;
2892
2893 if (target < DSA_MAX_SWITCHES) {
2894 port = chip->ds->rtable[target];
2895 if (port == DSA_RTABLE_NONE)
2896 port = 0xf;
2897 }
2898
2899 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
2900 if (err)
2901 break;
2902 }
2903
2904 return err;
2905}
2906
Vivien Didelot51540412016-07-18 20:45:32 -04002907static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
2908 bool hask, u16 mask)
2909{
2910 const u16 port_mask = BIT(chip->info->num_ports) - 1;
2911 u16 val = (num << 12) | (mask & port_mask);
2912
2913 if (hask)
2914 val |= GLOBAL2_TRUNK_MASK_HASK;
2915
2916 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, val);
2917}
2918
2919static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
2920 u16 map)
2921{
2922 const u16 port_mask = BIT(chip->info->num_ports) - 1;
2923 u16 val = (id << 11) | (map & port_mask);
2924
2925 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, val);
2926}
2927
2928static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
2929{
2930 const u16 port_mask = BIT(chip->info->num_ports) - 1;
2931 int i, err;
2932
2933 /* Clear all eight possible Trunk Mask vectors */
2934 for (i = 0; i < 8; ++i) {
2935 err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
2936 if (err)
2937 return err;
2938 }
2939
2940 /* Clear all sixteen possible Trunk ID routing vectors */
2941 for (i = 0; i < 16; ++i) {
2942 err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
2943 if (err)
2944 return err;
2945 }
2946
2947 return 0;
2948}
2949
Vivien Didelot8ec61c72016-07-18 20:45:37 -04002950static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip)
2951{
2952 int port, err;
2953
2954 /* Init all Ingress Rate Limit resources of all ports */
2955 for (port = 0; port < chip->info->num_ports; ++port) {
2956 /* XXX newer chips (like 88E6390) have different 2-bit ops */
2957 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
2958 GLOBAL2_IRL_CMD_OP_INIT_ALL |
2959 (port << 8));
2960 if (err)
2961 break;
2962
2963 /* Wait for the operation to complete */
Vivien Didelot2d79af62016-08-15 17:18:57 -04002964 err = mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_IRL_CMD,
2965 GLOBAL2_IRL_CMD_BUSY);
Vivien Didelot8ec61c72016-07-18 20:45:37 -04002966 if (err)
2967 break;
2968 }
2969
2970 return err;
2971}
2972
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002973/* Indirect write to the Switch MAC/WoL/WoF register */
2974static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip,
2975 unsigned int pointer, u8 data)
2976{
2977 u16 val = (pointer << 8) | data;
2978
2979 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC, val);
2980}
2981
2982static int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
2983{
2984 int i, err;
2985
2986 for (i = 0; i < 6; i++) {
2987 err = mv88e6xxx_g2_switch_mac_write(chip, i, addr[i]);
2988 if (err)
2989 break;
2990 }
2991
2992 return err;
2993}
2994
Vivien Didelot9bda8892016-07-18 20:45:36 -04002995static int mv88e6xxx_g2_pot_write(struct mv88e6xxx_chip *chip, int pointer,
2996 u8 data)
2997{
2998 u16 val = (pointer << 8) | (data & 0x7);
2999
3000 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, val);
3001}
3002
3003static int mv88e6xxx_g2_clear_pot(struct mv88e6xxx_chip *chip)
3004{
3005 int i, err;
3006
3007 /* Clear all sixteen possible Priority Override entries */
3008 for (i = 0; i < 16; i++) {
3009 err = mv88e6xxx_g2_pot_write(chip, i, 0);
3010 if (err)
3011 break;
3012 }
3013
3014 return err;
3015}
3016
Vivien Didelot855b1932016-07-20 18:18:35 -04003017static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip)
3018{
Vivien Didelot2d79af62016-08-15 17:18:57 -04003019 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD,
3020 GLOBAL2_EEPROM_CMD_BUSY |
3021 GLOBAL2_EEPROM_CMD_RUNNING);
Vivien Didelot855b1932016-07-20 18:18:35 -04003022}
3023
3024static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
3025{
3026 int err;
3027
3028 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, cmd);
3029 if (err)
3030 return err;
3031
3032 return mv88e6xxx_g2_eeprom_wait(chip);
3033}
3034
3035static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip,
3036 u8 addr, u16 *data)
3037{
3038 u16 cmd = GLOBAL2_EEPROM_CMD_OP_READ | addr;
3039 int err;
3040
3041 err = mv88e6xxx_g2_eeprom_wait(chip);
3042 if (err)
3043 return err;
3044
3045 err = mv88e6xxx_g2_eeprom_cmd(chip, cmd);
3046 if (err)
3047 return err;
3048
3049 return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
3050}
3051
3052static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip,
3053 u8 addr, u16 data)
3054{
3055 u16 cmd = GLOBAL2_EEPROM_CMD_OP_WRITE | addr;
3056 int err;
3057
3058 err = mv88e6xxx_g2_eeprom_wait(chip);
3059 if (err)
3060 return err;
3061
3062 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
3063 if (err)
3064 return err;
3065
3066 return mv88e6xxx_g2_eeprom_cmd(chip, cmd);
3067}
3068
Vivien Didelot57c67cf2016-08-15 17:18:59 -04003069static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip)
3070{
3071 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_CMD,
3072 GLOBAL2_SMI_PHY_CMD_BUSY);
3073}
3074
3075static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd)
3076{
3077 int err;
3078
3079 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_CMD, cmd);
3080 if (err)
3081 return err;
3082
3083 return mv88e6xxx_g2_smi_phy_wait(chip);
3084}
3085
3086static int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, int addr,
3087 int reg, u16 *val)
3088{
3089 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg;
3090 int err;
3091
3092 err = mv88e6xxx_g2_smi_phy_wait(chip);
3093 if (err)
3094 return err;
3095
3096 err = mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
3097 if (err)
3098 return err;
3099
3100 return mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_DATA, val);
3101}
3102
3103static int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, int addr,
3104 int reg, u16 val)
3105{
3106 u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg;
3107 int err;
3108
3109 err = mv88e6xxx_g2_smi_phy_wait(chip);
3110 if (err)
3111 return err;
3112
3113 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SMI_PHY_DATA, val);
3114 if (err)
3115 return err;
3116
3117 return mv88e6xxx_g2_smi_phy_cmd(chip, cmd);
3118}
3119
Vivien Didelote57e5e72016-08-15 17:19:00 -04003120static const struct mv88e6xxx_ops mv88e6xxx_g2_smi_phy_ops = {
3121 .read = mv88e6xxx_g2_smi_phy_read,
3122 .write = mv88e6xxx_g2_smi_phy_write,
3123};
3124
Vivien Didelot97299342016-07-18 20:45:30 -04003125static int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
3126{
Vivien Didelot47395ed2016-07-18 20:45:33 -04003127 u16 reg;
Vivien Didelot97299342016-07-18 20:45:30 -04003128 int err;
Vivien Didelot97299342016-07-18 20:45:30 -04003129
Vivien Didelot47395ed2016-07-18 20:45:33 -04003130 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
3131 /* Consider the frames with reserved multicast destination
3132 * addresses matching 01:80:c2:00:00:2x as MGMT.
3133 */
3134 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_2X,
3135 0xffff);
3136 if (err)
3137 return err;
3138 }
3139
3140 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X)) {
3141 /* Consider the frames with reserved multicast destination
3142 * addresses matching 01:80:c2:00:00:0x as MGMT.
3143 */
3144 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
3145 0xffff);
3146 if (err)
3147 return err;
3148 }
Vivien Didelot08a01262016-05-09 13:22:50 -04003149
3150 /* Ignore removed tag data on doubly tagged packets, disable
3151 * flow control messages, force flow control priority to the
3152 * highest, and send all special multicast frames to the CPU
3153 * port at the highest priority.
3154 */
Vivien Didelot47395ed2016-07-18 20:45:33 -04003155 reg = GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI | (0x7 << 4);
3156 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X) ||
3157 mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X))
3158 reg |= GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x7;
3159 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, reg);
Vivien Didelot08a01262016-05-09 13:22:50 -04003160 if (err)
3161 return err;
3162
3163 /* Program the DSA routing table. */
Vivien Didelotf22ab642016-07-18 20:45:31 -04003164 err = mv88e6xxx_g2_set_device_mapping(chip);
3165 if (err)
3166 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003167
Vivien Didelot51540412016-07-18 20:45:32 -04003168 /* Clear all trunk masks and mapping. */
3169 err = mv88e6xxx_g2_clear_trunk(chip);
3170 if (err)
3171 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003172
Vivien Didelot8ec61c72016-07-18 20:45:37 -04003173 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_IRL)) {
3174 /* Disable ingress rate limiting by resetting all per port
3175 * ingress rate limit resources to their initial state.
3176 */
3177 err = mv88e6xxx_g2_clear_irl(chip);
3178 if (err)
3179 return err;
3180 }
3181
Vivien Didelot63ed8802016-07-18 20:45:35 -04003182 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) {
3183 /* Initialize Cross-chip Port VLAN Table to reset defaults */
3184 err = mv88e6xxx_write(chip, REG_GLOBAL2, GLOBAL2_PVT_ADDR,
3185 GLOBAL2_PVT_ADDR_OP_INIT_ONES);
3186 if (err)
3187 return err;
3188 }
3189
Vivien Didelot9bda8892016-07-18 20:45:36 -04003190 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) {
Vivien Didelot08a01262016-05-09 13:22:50 -04003191 /* Clear the priority override table. */
Vivien Didelot9bda8892016-07-18 20:45:36 -04003192 err = mv88e6xxx_g2_clear_pot(chip);
3193 if (err)
3194 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003195 }
3196
Vivien Didelot97299342016-07-18 20:45:30 -04003197 return 0;
Vivien Didelot08a01262016-05-09 13:22:50 -04003198}
3199
Vivien Didelotf81ec902016-05-09 13:22:58 -04003200static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003201{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003202 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003203 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003204 int i;
3205
Vivien Didelotfad09c72016-06-21 12:28:20 -04003206 chip->ds = ds;
3207 ds->slave_mii_bus = chip->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003208
Vivien Didelotfad09c72016-06-21 12:28:20 -04003209 mutex_lock(&chip->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04003210
Vivien Didelotfad09c72016-06-21 12:28:20 -04003211 err = mv88e6xxx_switch_reset(chip);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003212 if (err)
3213 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003214
Vivien Didelot97299342016-07-18 20:45:30 -04003215 /* Setup Switch Port Registers */
3216 for (i = 0; i < chip->info->num_ports; i++) {
3217 err = mv88e6xxx_setup_port(chip, i);
3218 if (err)
3219 goto unlock;
3220 }
3221
3222 /* Setup Switch Global 1 Registers */
3223 err = mv88e6xxx_g1_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003224 if (err)
3225 goto unlock;
3226
Vivien Didelot97299342016-07-18 20:45:30 -04003227 /* Setup Switch Global 2 Registers */
3228 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
3229 err = mv88e6xxx_g2_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003230 if (err)
3231 goto unlock;
3232 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003233
Vivien Didelot6b17e862015-08-13 12:52:18 -04003234unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003235 mutex_unlock(&chip->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02003236
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003237 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003238}
3239
Vivien Didelot3b4caa12016-07-18 20:45:34 -04003240static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
3241{
3242 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3243 int err;
3244
3245 mutex_lock(&chip->reg_lock);
3246
3247 /* Has an indirect Switch MAC/WoL/WoF register in Global 2? */
3248 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_SWITCH_MAC))
3249 err = mv88e6xxx_g2_set_switch_mac(chip, addr);
3250 else
3251 err = mv88e6xxx_g1_set_switch_mac(chip, addr);
3252
3253 mutex_unlock(&chip->reg_lock);
3254
3255 return err;
3256}
3257
Vivien Didelot57d32312016-06-20 13:13:58 -04003258static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
3259 int reg)
Andrew Lunn491435852015-04-02 04:06:35 +02003260{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003261 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn491435852015-04-02 04:06:35 +02003262 int ret;
3263
Vivien Didelotfad09c72016-06-21 12:28:20 -04003264 mutex_lock(&chip->reg_lock);
3265 ret = _mv88e6xxx_mdio_page_read(chip, port, page, reg);
3266 mutex_unlock(&chip->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003267
Andrew Lunn491435852015-04-02 04:06:35 +02003268 return ret;
3269}
3270
Vivien Didelot57d32312016-06-20 13:13:58 -04003271static int mv88e6xxx_mdio_page_write(struct dsa_switch *ds, int port, int page,
3272 int reg, int val)
Andrew Lunn491435852015-04-02 04:06:35 +02003273{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003274 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn491435852015-04-02 04:06:35 +02003275 int ret;
3276
Vivien Didelotfad09c72016-06-21 12:28:20 -04003277 mutex_lock(&chip->reg_lock);
3278 ret = _mv88e6xxx_mdio_page_write(chip, port, page, reg, val);
3279 mutex_unlock(&chip->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003280
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003281 return ret;
3282}
3283
Vivien Didelote57e5e72016-08-15 17:19:00 -04003284static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003285{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003286 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04003287 u16 val;
3288 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003289
Vivien Didelote57e5e72016-08-15 17:19:00 -04003290 if (phy >= chip->info->num_ports)
Andrew Lunn158bc062016-04-28 21:24:06 -04003291 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003292
Vivien Didelotfad09c72016-06-21 12:28:20 -04003293 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003294 err = mv88e6xxx_phy_read(chip, phy, reg, &val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003295 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003296
3297 return err ? err : val;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003298}
3299
Vivien Didelote57e5e72016-08-15 17:19:00 -04003300static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003301{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003302 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04003303 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003304
Vivien Didelote57e5e72016-08-15 17:19:00 -04003305 if (phy >= chip->info->num_ports)
Andrew Lunn158bc062016-04-28 21:24:06 -04003306 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003307
Vivien Didelotfad09c72016-06-21 12:28:20 -04003308 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003309 err = mv88e6xxx_phy_write(chip, phy, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003310 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003311
3312 return err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003313}
3314
Vivien Didelotfad09c72016-06-21 12:28:20 -04003315static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
Andrew Lunnb516d452016-06-04 21:17:06 +02003316 struct device_node *np)
3317{
3318 static int index;
3319 struct mii_bus *bus;
3320 int err;
3321
Andrew Lunnb516d452016-06-04 21:17:06 +02003322 if (np)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003323 chip->mdio_np = of_get_child_by_name(np, "mdio");
Andrew Lunnb516d452016-06-04 21:17:06 +02003324
Vivien Didelotfad09c72016-06-21 12:28:20 -04003325 bus = devm_mdiobus_alloc(chip->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003326 if (!bus)
3327 return -ENOMEM;
3328
Vivien Didelotfad09c72016-06-21 12:28:20 -04003329 bus->priv = (void *)chip;
Andrew Lunnb516d452016-06-04 21:17:06 +02003330 if (np) {
3331 bus->name = np->full_name;
3332 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3333 } else {
3334 bus->name = "mv88e6xxx SMI";
3335 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3336 }
3337
3338 bus->read = mv88e6xxx_mdio_read;
3339 bus->write = mv88e6xxx_mdio_write;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003340 bus->parent = chip->dev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003341
Vivien Didelotfad09c72016-06-21 12:28:20 -04003342 if (chip->mdio_np)
3343 err = of_mdiobus_register(bus, chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003344 else
3345 err = mdiobus_register(bus);
3346 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003347 dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
Andrew Lunnb516d452016-06-04 21:17:06 +02003348 goto out;
3349 }
Vivien Didelotfad09c72016-06-21 12:28:20 -04003350 chip->mdio_bus = bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003351
3352 return 0;
3353
3354out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003355 if (chip->mdio_np)
3356 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003357
3358 return err;
3359}
3360
Vivien Didelotfad09c72016-06-21 12:28:20 -04003361static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
Andrew Lunnb516d452016-06-04 21:17:06 +02003362
3363{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003364 struct mii_bus *bus = chip->mdio_bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003365
3366 mdiobus_unregister(bus);
3367
Vivien Didelotfad09c72016-06-21 12:28:20 -04003368 if (chip->mdio_np)
3369 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003370}
3371
Guenter Roeckc22995c2015-07-25 09:42:28 -07003372#ifdef CONFIG_NET_DSA_HWMON
3373
3374static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3375{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003376 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003377 int ret;
3378 int val;
3379
3380 *temp = 0;
3381
Vivien Didelotfad09c72016-06-21 12:28:20 -04003382 mutex_lock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003383
Vivien Didelotfad09c72016-06-21 12:28:20 -04003384 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003385 if (ret < 0)
3386 goto error;
3387
3388 /* Enable temperature sensor */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003389 ret = mv88e6xxx_mdio_read_direct(chip, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003390 if (ret < 0)
3391 goto error;
3392
Vivien Didelotfad09c72016-06-21 12:28:20 -04003393 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003394 if (ret < 0)
3395 goto error;
3396
3397 /* Wait for temperature to stabilize */
3398 usleep_range(10000, 12000);
3399
Vivien Didelotfad09c72016-06-21 12:28:20 -04003400 val = mv88e6xxx_mdio_read_direct(chip, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003401 if (val < 0) {
3402 ret = val;
3403 goto error;
3404 }
3405
3406 /* Disable temperature sensor */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003407 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003408 if (ret < 0)
3409 goto error;
3410
3411 *temp = ((val & 0x1f) - 5) * 5;
3412
3413error:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003414 mv88e6xxx_mdio_write_direct(chip, 0x0, 0x16, 0x0);
3415 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003416 return ret;
3417}
3418
3419static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3420{
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;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003423 int ret;
3424
3425 *temp = 0;
3426
Andrew Lunn03a4a542016-06-04 21:17:05 +02003427 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 27);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003428 if (ret < 0)
3429 return ret;
3430
3431 *temp = (ret & 0xff) - 25;
3432
3433 return 0;
3434}
3435
Vivien Didelotf81ec902016-05-09 13:22:58 -04003436static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003437{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003438 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003439
Vivien Didelotfad09c72016-06-21 12:28:20 -04003440 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
Vivien Didelot6594f612016-05-09 13:22:42 -04003441 return -EOPNOTSUPP;
3442
Vivien Didelotfad09c72016-06-21 12:28:20 -04003443 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003444 return mv88e63xx_get_temp(ds, temp);
3445
3446 return mv88e61xx_get_temp(ds, temp);
3447}
3448
Vivien Didelotf81ec902016-05-09 13:22:58 -04003449static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003450{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003451 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3452 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003453 int ret;
3454
Vivien Didelotfad09c72016-06-21 12:28:20 -04003455 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003456 return -EOPNOTSUPP;
3457
3458 *temp = 0;
3459
Andrew Lunn03a4a542016-06-04 21:17:05 +02003460 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003461 if (ret < 0)
3462 return ret;
3463
3464 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3465
3466 return 0;
3467}
3468
Vivien Didelotf81ec902016-05-09 13:22:58 -04003469static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003470{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003471 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3472 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003473 int ret;
3474
Vivien Didelotfad09c72016-06-21 12:28:20 -04003475 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003476 return -EOPNOTSUPP;
3477
Andrew Lunn03a4a542016-06-04 21:17:05 +02003478 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003479 if (ret < 0)
3480 return ret;
3481 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003482 return mv88e6xxx_mdio_page_write(ds, phy, 6, 26,
3483 (ret & 0xe0ff) | (temp << 8));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003484}
3485
Vivien Didelotf81ec902016-05-09 13:22:58 -04003486static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003487{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003488 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3489 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003490 int ret;
3491
Vivien Didelotfad09c72016-06-21 12:28:20 -04003492 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003493 return -EOPNOTSUPP;
3494
3495 *alarm = false;
3496
Andrew Lunn03a4a542016-06-04 21:17:05 +02003497 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003498 if (ret < 0)
3499 return ret;
3500
3501 *alarm = !!(ret & 0x40);
3502
3503 return 0;
3504}
3505#endif /* CONFIG_NET_DSA_HWMON */
3506
Vivien Didelot855b1932016-07-20 18:18:35 -04003507static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
3508{
3509 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3510
3511 return chip->eeprom_len;
3512}
3513
3514static int mv88e6xxx_get_eeprom16(struct mv88e6xxx_chip *chip,
3515 struct ethtool_eeprom *eeprom, u8 *data)
3516{
3517 unsigned int offset = eeprom->offset;
3518 unsigned int len = eeprom->len;
3519 u16 val;
3520 int err;
3521
3522 eeprom->len = 0;
3523
3524 if (offset & 1) {
3525 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3526 if (err)
3527 return err;
3528
3529 *data++ = (val >> 8) & 0xff;
3530
3531 offset++;
3532 len--;
3533 eeprom->len++;
3534 }
3535
3536 while (len >= 2) {
3537 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3538 if (err)
3539 return err;
3540
3541 *data++ = val & 0xff;
3542 *data++ = (val >> 8) & 0xff;
3543
3544 offset += 2;
3545 len -= 2;
3546 eeprom->len += 2;
3547 }
3548
3549 if (len) {
3550 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3551 if (err)
3552 return err;
3553
3554 *data++ = val & 0xff;
3555
3556 offset++;
3557 len--;
3558 eeprom->len++;
3559 }
3560
3561 return 0;
3562}
3563
3564static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
3565 struct ethtool_eeprom *eeprom, u8 *data)
3566{
3567 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3568 int err;
3569
3570 mutex_lock(&chip->reg_lock);
3571
3572 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
3573 err = mv88e6xxx_get_eeprom16(chip, eeprom, data);
3574 else
3575 err = -EOPNOTSUPP;
3576
3577 mutex_unlock(&chip->reg_lock);
3578
3579 if (err)
3580 return err;
3581
3582 eeprom->magic = 0xc3ec4951;
3583
3584 return 0;
3585}
3586
3587static int mv88e6xxx_set_eeprom16(struct mv88e6xxx_chip *chip,
3588 struct ethtool_eeprom *eeprom, u8 *data)
3589{
3590 unsigned int offset = eeprom->offset;
3591 unsigned int len = eeprom->len;
3592 u16 val;
3593 int err;
3594
3595 /* Ensure the RO WriteEn bit is set */
3596 err = mv88e6xxx_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_CMD, &val);
3597 if (err)
3598 return err;
3599
3600 if (!(val & GLOBAL2_EEPROM_CMD_WRITE_EN))
3601 return -EROFS;
3602
3603 eeprom->len = 0;
3604
3605 if (offset & 1) {
3606 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3607 if (err)
3608 return err;
3609
3610 val = (*data++ << 8) | (val & 0xff);
3611
3612 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
3613 if (err)
3614 return err;
3615
3616 offset++;
3617 len--;
3618 eeprom->len++;
3619 }
3620
3621 while (len >= 2) {
3622 val = *data++;
3623 val |= *data++ << 8;
3624
3625 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
3626 if (err)
3627 return err;
3628
3629 offset += 2;
3630 len -= 2;
3631 eeprom->len += 2;
3632 }
3633
3634 if (len) {
3635 err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val);
3636 if (err)
3637 return err;
3638
3639 val = (val & 0xff00) | *data++;
3640
3641 err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val);
3642 if (err)
3643 return err;
3644
3645 offset++;
3646 len--;
3647 eeprom->len++;
3648 }
3649
3650 return 0;
3651}
3652
3653static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
3654 struct ethtool_eeprom *eeprom, u8 *data)
3655{
3656 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3657 int err;
3658
3659 if (eeprom->magic != 0xc3ec4951)
3660 return -EINVAL;
3661
3662 mutex_lock(&chip->reg_lock);
3663
3664 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
3665 err = mv88e6xxx_set_eeprom16(chip, eeprom, data);
3666 else
3667 err = -EOPNOTSUPP;
3668
3669 mutex_unlock(&chip->reg_lock);
3670
3671 return err;
3672}
3673
Vivien Didelotf81ec902016-05-09 13:22:58 -04003674static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3675 [MV88E6085] = {
3676 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3677 .family = MV88E6XXX_FAMILY_6097,
3678 .name = "Marvell 88E6085",
3679 .num_databases = 4096,
3680 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003681 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003682 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003683 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3684 },
3685
3686 [MV88E6095] = {
3687 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3688 .family = MV88E6XXX_FAMILY_6095,
3689 .name = "Marvell 88E6095/88E6095F",
3690 .num_databases = 256,
3691 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003692 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003693 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003694 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3695 },
3696
3697 [MV88E6123] = {
3698 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3699 .family = MV88E6XXX_FAMILY_6165,
3700 .name = "Marvell 88E6123",
3701 .num_databases = 4096,
3702 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003703 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003704 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003705 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3706 },
3707
3708 [MV88E6131] = {
3709 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3710 .family = MV88E6XXX_FAMILY_6185,
3711 .name = "Marvell 88E6131",
3712 .num_databases = 256,
3713 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003714 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003715 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003716 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3717 },
3718
3719 [MV88E6161] = {
3720 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3721 .family = MV88E6XXX_FAMILY_6165,
3722 .name = "Marvell 88E6161",
3723 .num_databases = 4096,
3724 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003725 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003726 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003727 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3728 },
3729
3730 [MV88E6165] = {
3731 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3732 .family = MV88E6XXX_FAMILY_6165,
3733 .name = "Marvell 88E6165",
3734 .num_databases = 4096,
3735 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003736 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003737 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003738 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3739 },
3740
3741 [MV88E6171] = {
3742 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3743 .family = MV88E6XXX_FAMILY_6351,
3744 .name = "Marvell 88E6171",
3745 .num_databases = 4096,
3746 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003747 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003748 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003749 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3750 },
3751
3752 [MV88E6172] = {
3753 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3754 .family = MV88E6XXX_FAMILY_6352,
3755 .name = "Marvell 88E6172",
3756 .num_databases = 4096,
3757 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003758 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003759 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003760 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3761 },
3762
3763 [MV88E6175] = {
3764 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3765 .family = MV88E6XXX_FAMILY_6351,
3766 .name = "Marvell 88E6175",
3767 .num_databases = 4096,
3768 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003769 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003770 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003771 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3772 },
3773
3774 [MV88E6176] = {
3775 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3776 .family = MV88E6XXX_FAMILY_6352,
3777 .name = "Marvell 88E6176",
3778 .num_databases = 4096,
3779 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003780 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003781 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003782 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3783 },
3784
3785 [MV88E6185] = {
3786 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3787 .family = MV88E6XXX_FAMILY_6185,
3788 .name = "Marvell 88E6185",
3789 .num_databases = 256,
3790 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003791 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003792 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003793 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3794 },
3795
3796 [MV88E6240] = {
3797 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3798 .family = MV88E6XXX_FAMILY_6352,
3799 .name = "Marvell 88E6240",
3800 .num_databases = 4096,
3801 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003802 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003803 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003804 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3805 },
3806
3807 [MV88E6320] = {
3808 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3809 .family = MV88E6XXX_FAMILY_6320,
3810 .name = "Marvell 88E6320",
3811 .num_databases = 4096,
3812 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003813 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003814 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003815 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3816 },
3817
3818 [MV88E6321] = {
3819 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3820 .family = MV88E6XXX_FAMILY_6320,
3821 .name = "Marvell 88E6321",
3822 .num_databases = 4096,
3823 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003824 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003825 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003826 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3827 },
3828
3829 [MV88E6350] = {
3830 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3831 .family = MV88E6XXX_FAMILY_6351,
3832 .name = "Marvell 88E6350",
3833 .num_databases = 4096,
3834 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003835 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003836 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003837 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3838 },
3839
3840 [MV88E6351] = {
3841 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3842 .family = MV88E6XXX_FAMILY_6351,
3843 .name = "Marvell 88E6351",
3844 .num_databases = 4096,
3845 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003846 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003847 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003848 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3849 },
3850
3851 [MV88E6352] = {
3852 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3853 .family = MV88E6XXX_FAMILY_6352,
3854 .name = "Marvell 88E6352",
3855 .num_databases = 4096,
3856 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003857 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003858 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003859 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3860 },
3861};
3862
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003863static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003864{
Vivien Didelota439c062016-04-17 13:23:58 -04003865 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003866
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003867 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3868 if (mv88e6xxx_table[i].prod_num == prod_num)
3869 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003870
Vivien Didelotb9b37712015-10-30 19:39:48 -04003871 return NULL;
3872}
3873
Vivien Didelotfad09c72016-06-21 12:28:20 -04003874static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003875{
3876 const struct mv88e6xxx_info *info;
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003877 unsigned int prod_num, rev;
3878 u16 id;
3879 int err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003880
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003881 mutex_lock(&chip->reg_lock);
3882 err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
3883 mutex_unlock(&chip->reg_lock);
3884 if (err)
3885 return err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003886
3887 prod_num = (id & 0xfff0) >> 4;
3888 rev = id & 0x000f;
3889
3890 info = mv88e6xxx_lookup_info(prod_num);
3891 if (!info)
3892 return -ENODEV;
3893
Vivien Didelotcaac8542016-06-20 13:14:09 -04003894 /* Update the compatible info with the probed one */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003895 chip->info = info;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003896
Vivien Didelotfad09c72016-06-21 12:28:20 -04003897 dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3898 chip->info->prod_num, chip->info->name, rev);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003899
3900 return 0;
3901}
3902
Vivien Didelotfad09c72016-06-21 12:28:20 -04003903static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
Vivien Didelot469d7292016-06-20 13:14:06 -04003904{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003905 struct mv88e6xxx_chip *chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003906
Vivien Didelotfad09c72016-06-21 12:28:20 -04003907 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3908 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003909 return NULL;
3910
Vivien Didelotfad09c72016-06-21 12:28:20 -04003911 chip->dev = dev;
Vivien Didelot469d7292016-06-20 13:14:06 -04003912
Vivien Didelotfad09c72016-06-21 12:28:20 -04003913 mutex_init(&chip->reg_lock);
Vivien Didelot469d7292016-06-20 13:14:06 -04003914
Vivien Didelotfad09c72016-06-21 12:28:20 -04003915 return chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003916}
3917
Vivien Didelote57e5e72016-08-15 17:19:00 -04003918static const struct mv88e6xxx_ops mv88e6xxx_phy_ops = {
3919 .read = mv88e6xxx_read,
3920 .write = mv88e6xxx_write,
3921};
3922
3923static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
3924{
3925 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SMI_PHY)) {
3926 chip->phy_ops = &mv88e6xxx_g2_smi_phy_ops;
3927 } else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) {
3928 chip->phy_ops = &mv88e6xxx_phy_ppu_ops;
3929 mv88e6xxx_ppu_state_init(chip);
3930 } else {
3931 chip->phy_ops = &mv88e6xxx_phy_ops;
3932 }
3933}
3934
Vivien Didelotfad09c72016-06-21 12:28:20 -04003935static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003936 struct mii_bus *bus, int sw_addr)
3937{
3938 /* ADDR[0] pin is unavailable externally and considered zero */
3939 if (sw_addr & 0x1)
3940 return -EINVAL;
3941
Vivien Didelot914b32f2016-06-20 13:14:11 -04003942 if (sw_addr == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003943 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
Vivien Didelota0ffff22016-08-15 17:18:58 -04003944 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003945 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
Vivien Didelot914b32f2016-06-20 13:14:11 -04003946 else
3947 return -EINVAL;
3948
Vivien Didelotfad09c72016-06-21 12:28:20 -04003949 chip->bus = bus;
3950 chip->sw_addr = sw_addr;
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003951
3952 return 0;
3953}
3954
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003955static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3956 struct device *host_dev, int sw_addr,
3957 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003958{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003959 struct mv88e6xxx_chip *chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003960 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003961 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003962
Vivien Didelota439c062016-04-17 13:23:58 -04003963 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003964 if (!bus)
3965 return NULL;
3966
Vivien Didelotfad09c72016-06-21 12:28:20 -04003967 chip = mv88e6xxx_alloc_chip(dsa_dev);
3968 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003969 return NULL;
3970
Vivien Didelotcaac8542016-06-20 13:14:09 -04003971 /* Legacy SMI probing will only support chips similar to 88E6085 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003972 chip->info = &mv88e6xxx_table[MV88E6085];
Vivien Didelotcaac8542016-06-20 13:14:09 -04003973
Vivien Didelotfad09c72016-06-21 12:28:20 -04003974 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003975 if (err)
3976 goto free;
3977
Vivien Didelotfad09c72016-06-21 12:28:20 -04003978 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003979 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003980 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003981
Vivien Didelote57e5e72016-08-15 17:19:00 -04003982 mv88e6xxx_phy_init(chip);
3983
Vivien Didelotfad09c72016-06-21 12:28:20 -04003984 err = mv88e6xxx_mdio_register(chip, NULL);
Andrew Lunnb516d452016-06-04 21:17:06 +02003985 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003986 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003987
Vivien Didelotfad09c72016-06-21 12:28:20 -04003988 *priv = chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003989
Vivien Didelotfad09c72016-06-21 12:28:20 -04003990 return chip->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003991free:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003992 devm_kfree(dsa_dev, chip);
Vivien Didelot469d7292016-06-20 13:14:06 -04003993
3994 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003995}
3996
Vivien Didelot57d32312016-06-20 13:13:58 -04003997static struct dsa_switch_driver mv88e6xxx_switch_driver = {
Vivien Didelotf81ec902016-05-09 13:22:58 -04003998 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003999 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04004000 .setup = mv88e6xxx_setup,
4001 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04004002 .adjust_link = mv88e6xxx_adjust_link,
4003 .get_strings = mv88e6xxx_get_strings,
4004 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
4005 .get_sset_count = mv88e6xxx_get_sset_count,
4006 .set_eee = mv88e6xxx_set_eee,
4007 .get_eee = mv88e6xxx_get_eee,
4008#ifdef CONFIG_NET_DSA_HWMON
4009 .get_temp = mv88e6xxx_get_temp,
4010 .get_temp_limit = mv88e6xxx_get_temp_limit,
4011 .set_temp_limit = mv88e6xxx_set_temp_limit,
4012 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
4013#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004014 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04004015 .get_eeprom = mv88e6xxx_get_eeprom,
4016 .set_eeprom = mv88e6xxx_set_eeprom,
4017 .get_regs_len = mv88e6xxx_get_regs_len,
4018 .get_regs = mv88e6xxx_get_regs,
Vivien Didelot2cfcd962016-07-18 20:45:40 -04004019 .set_ageing_time = mv88e6xxx_set_ageing_time,
Vivien Didelotf81ec902016-05-09 13:22:58 -04004020 .port_bridge_join = mv88e6xxx_port_bridge_join,
4021 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
4022 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
4023 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
4024 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
4025 .port_vlan_add = mv88e6xxx_port_vlan_add,
4026 .port_vlan_del = mv88e6xxx_port_vlan_del,
4027 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
4028 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
4029 .port_fdb_add = mv88e6xxx_port_fdb_add,
4030 .port_fdb_del = mv88e6xxx_port_fdb_del,
4031 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
4032};
4033
Vivien Didelotfad09c72016-06-21 12:28:20 -04004034static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004035 struct device_node *np)
4036{
Vivien Didelotfad09c72016-06-21 12:28:20 -04004037 struct device *dev = chip->dev;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004038 struct dsa_switch *ds;
4039
4040 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
4041 if (!ds)
4042 return -ENOMEM;
4043
4044 ds->dev = dev;
Vivien Didelotfad09c72016-06-21 12:28:20 -04004045 ds->priv = chip;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004046 ds->drv = &mv88e6xxx_switch_driver;
4047
4048 dev_set_drvdata(dev, ds);
4049
4050 return dsa_register_switch(ds, np);
4051}
4052
Vivien Didelotfad09c72016-06-21 12:28:20 -04004053static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004054{
Vivien Didelotfad09c72016-06-21 12:28:20 -04004055 dsa_unregister_switch(chip->ds);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04004056}
4057
Vivien Didelot57d32312016-06-20 13:13:58 -04004058static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004059{
4060 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004061 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04004062 const struct mv88e6xxx_info *compat_info;
Vivien Didelotfad09c72016-06-21 12:28:20 -04004063 struct mv88e6xxx_chip *chip;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004064 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02004065 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004066
Vivien Didelotcaac8542016-06-20 13:14:09 -04004067 compat_info = of_device_get_match_data(dev);
4068 if (!compat_info)
4069 return -EINVAL;
4070
Vivien Didelotfad09c72016-06-21 12:28:20 -04004071 chip = mv88e6xxx_alloc_chip(dev);
4072 if (!chip)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004073 return -ENOMEM;
4074
Vivien Didelotfad09c72016-06-21 12:28:20 -04004075 chip->info = compat_info;
Vivien Didelotcaac8542016-06-20 13:14:09 -04004076
Vivien Didelotfad09c72016-06-21 12:28:20 -04004077 err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04004078 if (err)
4079 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004080
Vivien Didelotfad09c72016-06-21 12:28:20 -04004081 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04004082 if (err)
4083 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004084
Vivien Didelote57e5e72016-08-15 17:19:00 -04004085 mv88e6xxx_phy_init(chip);
4086
Vivien Didelotfad09c72016-06-21 12:28:20 -04004087 chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
4088 if (IS_ERR(chip->reset))
4089 return PTR_ERR(chip->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02004090
Vivien Didelot855b1932016-07-20 18:18:35 -04004091 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16) &&
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004092 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
Vivien Didelotfad09c72016-06-21 12:28:20 -04004093 chip->eeprom_len = eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004094
Vivien Didelotfad09c72016-06-21 12:28:20 -04004095 err = mv88e6xxx_mdio_register(chip, np);
Andrew Lunnb516d452016-06-04 21:17:06 +02004096 if (err)
4097 return err;
4098
Vivien Didelotfad09c72016-06-21 12:28:20 -04004099 err = mv88e6xxx_register_switch(chip, np);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02004100 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04004101 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02004102 return err;
4103 }
4104
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004105 return 0;
4106}
4107
4108static void mv88e6xxx_remove(struct mdio_device *mdiodev)
4109{
4110 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
Vivien Didelotfad09c72016-06-21 12:28:20 -04004111 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004112
Vivien Didelotfad09c72016-06-21 12:28:20 -04004113 mv88e6xxx_unregister_switch(chip);
4114 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004115}
4116
4117static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04004118 {
4119 .compatible = "marvell,mv88e6085",
4120 .data = &mv88e6xxx_table[MV88E6085],
4121 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004122 { /* sentinel */ },
4123};
4124
4125MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
4126
4127static struct mdio_driver mv88e6xxx_driver = {
4128 .probe = mv88e6xxx_probe,
4129 .remove = mv88e6xxx_remove,
4130 .mdiodrv.driver = {
4131 .name = "mv88e6085",
4132 .of_match_table = mv88e6xxx_of_match,
4133 },
4134};
4135
Ben Hutchings98e67302011-11-25 14:36:19 +00004136static int __init mv88e6xxx_init(void)
4137{
Vivien Didelotf81ec902016-05-09 13:22:58 -04004138 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004139 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004140}
4141module_init(mv88e6xxx_init);
4142
4143static void __exit mv88e6xxx_cleanup(void)
4144{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004145 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04004146 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004147}
4148module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00004149
4150MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4151MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4152MODULE_LICENSE("GPL");