blob: 9b2525a5d9587f89074edd7281c54b65e9d2949e [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 Didelotf22ab642016-07-18 20:45:31 -0400219/* Indirect write to single pointer-data register with an Update bit */
220static int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg,
221 u16 update)
222{
223 u16 val;
224 int i, err;
225
226 /* Wait until the previous operation is completed */
227 for (i = 0; i < 16; ++i) {
228 err = mv88e6xxx_read(chip, addr, reg, &val);
229 if (err)
230 return err;
231
232 if (!(val & BIT(15)))
233 break;
234 }
235
236 if (i == 16)
237 return -ETIMEDOUT;
238
239 /* Set the Update bit to trigger a write operation */
240 val = BIT(15) | update;
241
242 return mv88e6xxx_write(chip, addr, reg, val);
243}
244
Vivien Didelotfad09c72016-06-21 12:28:20 -0400245static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000246{
Vivien Didelot914b32f2016-06-20 13:14:11 -0400247 u16 val;
248 int err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000249
Vivien Didelotfad09c72016-06-21 12:28:20 -0400250 err = mv88e6xxx_read(chip, addr, reg, &val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400251 if (err)
252 return err;
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400253
Vivien Didelot914b32f2016-06-20 13:14:11 -0400254 return val;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000255}
256
Vivien Didelotfad09c72016-06-21 12:28:20 -0400257static int mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700258{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700259 int ret;
260
Vivien Didelotfad09c72016-06-21 12:28:20 -0400261 mutex_lock(&chip->reg_lock);
262 ret = _mv88e6xxx_reg_read(chip, addr, reg);
263 mutex_unlock(&chip->reg_lock);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700264
265 return ret;
266}
267
Vivien Didelotfad09c72016-06-21 12:28:20 -0400268static int _mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
Andrew Lunn158bc062016-04-28 21:24:06 -0400269 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000270{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400271 return mv88e6xxx_write(chip, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700272}
273
Vivien Didelotfad09c72016-06-21 12:28:20 -0400274static int mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
Vivien Didelot57d32312016-06-20 13:13:58 -0400275 int reg, u16 val)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700276{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700277 int ret;
278
Vivien Didelotfad09c72016-06-21 12:28:20 -0400279 mutex_lock(&chip->reg_lock);
280 ret = _mv88e6xxx_reg_write(chip, addr, reg, val);
281 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000282
283 return ret;
284}
285
Vivien Didelot1d13a062016-05-09 13:22:43 -0400286static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000287{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400288 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200289 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000290
Vivien Didelotfad09c72016-06-21 12:28:20 -0400291 err = mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200292 (addr[0] << 8) | addr[1]);
293 if (err)
294 return err;
295
Vivien Didelotfad09c72016-06-21 12:28:20 -0400296 err = mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200297 (addr[2] << 8) | addr[3]);
298 if (err)
299 return err;
300
Vivien Didelotfad09c72016-06-21 12:28:20 -0400301 return mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200302 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000303}
304
Vivien Didelot1d13a062016-05-09 13:22:43 -0400305static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000306{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400307 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000308 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200309 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000310
311 for (i = 0; i < 6; i++) {
312 int j;
313
Barry Grussling3675c8d2013-01-08 16:05:53 +0000314 /* Write the MAC address byte. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400315 ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200316 GLOBAL2_SWITCH_MAC_BUSY |
317 (i << 8) | addr[i]);
318 if (ret)
319 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000320
Barry Grussling3675c8d2013-01-08 16:05:53 +0000321 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000322 for (j = 0; j < 16; j++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400323 ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200324 GLOBAL2_SWITCH_MAC);
325 if (ret < 0)
326 return ret;
327
Andrew Lunncca8b132015-04-02 04:06:39 +0200328 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000329 break;
330 }
331 if (j == 16)
332 return -ETIMEDOUT;
333 }
334
335 return 0;
336}
337
Vivien Didelot57d32312016-06-20 13:13:58 -0400338static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
Vivien Didelot1d13a062016-05-09 13:22:43 -0400339{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400340 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot1d13a062016-05-09 13:22:43 -0400341
Vivien Didelotfad09c72016-06-21 12:28:20 -0400342 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SWITCH_MAC))
Vivien Didelot1d13a062016-05-09 13:22:43 -0400343 return mv88e6xxx_set_addr_indirect(ds, addr);
344 else
345 return mv88e6xxx_set_addr_direct(ds, addr);
346}
347
Vivien Didelotfad09c72016-06-21 12:28:20 -0400348static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +0200349 int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000350{
351 if (addr >= 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400352 return _mv88e6xxx_reg_read(chip, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000353 return 0xffff;
354}
355
Vivien Didelotfad09c72016-06-21 12:28:20 -0400356static int mv88e6xxx_mdio_write_direct(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +0200357 int addr, int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000358{
359 if (addr >= 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400360 return _mv88e6xxx_reg_write(chip, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000361 return 0;
362}
363
Vivien Didelotfad09c72016-06-21 12:28:20 -0400364static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000365{
366 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000367 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000368
Vivien Didelotfad09c72016-06-21 12:28:20 -0400369 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200370 if (ret < 0)
371 return ret;
372
Vivien Didelotfad09c72016-06-21 12:28:20 -0400373 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400374 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200375 if (ret)
376 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000377
Barry Grussling19b2f972013-01-08 16:05:54 +0000378 timeout = jiffies + 1 * HZ;
379 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400380 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200381 if (ret < 0)
382 return ret;
383
Barry Grussling19b2f972013-01-08 16:05:54 +0000384 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200385 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
386 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000387 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000388 }
389
390 return -ETIMEDOUT;
391}
392
Vivien Didelotfad09c72016-06-21 12:28:20 -0400393static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000394{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200395 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000396 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000397
Vivien Didelotfad09c72016-06-21 12:28:20 -0400398 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200399 if (ret < 0)
400 return ret;
401
Vivien Didelotfad09c72016-06-21 12:28:20 -0400402 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot762eb672016-06-04 21:16:54 +0200403 ret | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200404 if (err)
405 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000406
Barry Grussling19b2f972013-01-08 16:05:54 +0000407 timeout = jiffies + 1 * HZ;
408 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400409 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200410 if (ret < 0)
411 return ret;
412
Barry Grussling19b2f972013-01-08 16:05:54 +0000413 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200414 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
415 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000416 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000417 }
418
419 return -ETIMEDOUT;
420}
421
422static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
423{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400424 struct mv88e6xxx_chip *chip;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000425
Vivien Didelotfad09c72016-06-21 12:28:20 -0400426 chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200427
Vivien Didelotfad09c72016-06-21 12:28:20 -0400428 mutex_lock(&chip->reg_lock);
Vivien Didelot762eb672016-06-04 21:16:54 +0200429
Vivien Didelotfad09c72016-06-21 12:28:20 -0400430 if (mutex_trylock(&chip->ppu_mutex)) {
431 if (mv88e6xxx_ppu_enable(chip) == 0)
432 chip->ppu_disabled = 0;
433 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000434 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200435
Vivien Didelotfad09c72016-06-21 12:28:20 -0400436 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000437}
438
439static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
440{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400441 struct mv88e6xxx_chip *chip = (void *)_ps;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000442
Vivien Didelotfad09c72016-06-21 12:28:20 -0400443 schedule_work(&chip->ppu_work);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000444}
445
Vivien Didelotfad09c72016-06-21 12:28:20 -0400446static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000447{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000448 int ret;
449
Vivien Didelotfad09c72016-06-21 12:28:20 -0400450 mutex_lock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000451
Barry Grussling3675c8d2013-01-08 16:05:53 +0000452 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000453 * we can access the PHY registers. If it was already
454 * disabled, cancel the timer that is going to re-enable
455 * it.
456 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400457 if (!chip->ppu_disabled) {
458 ret = mv88e6xxx_ppu_disable(chip);
Barry Grussling85686582013-01-08 16:05:56 +0000459 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400460 mutex_unlock(&chip->ppu_mutex);
Barry Grussling85686582013-01-08 16:05:56 +0000461 return ret;
462 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400463 chip->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000464 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400465 del_timer(&chip->ppu_timer);
Barry Grussling85686582013-01-08 16:05:56 +0000466 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000467 }
468
469 return ret;
470}
471
Vivien Didelotfad09c72016-06-21 12:28:20 -0400472static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000473{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000474 /* Schedule a timer to re-enable the PHY polling unit. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400475 mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
476 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000477}
478
Vivien Didelotfad09c72016-06-21 12:28:20 -0400479static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000480{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400481 mutex_init(&chip->ppu_mutex);
482 INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
483 init_timer(&chip->ppu_timer);
484 chip->ppu_timer.data = (unsigned long)chip;
485 chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000486}
487
Vivien Didelotfad09c72016-06-21 12:28:20 -0400488static int mv88e6xxx_mdio_read_ppu(struct mv88e6xxx_chip *chip, int addr,
Andrew Lunn03a4a542016-06-04 21:17:05 +0200489 int regnum)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000490{
491 int ret;
492
Vivien Didelotfad09c72016-06-21 12:28:20 -0400493 ret = mv88e6xxx_ppu_access_get(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000494 if (ret >= 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400495 ret = _mv88e6xxx_reg_read(chip, addr, regnum);
496 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000497 }
498
499 return ret;
500}
501
Vivien Didelotfad09c72016-06-21 12:28:20 -0400502static int mv88e6xxx_mdio_write_ppu(struct mv88e6xxx_chip *chip, int addr,
Andrew Lunn03a4a542016-06-04 21:17:05 +0200503 int regnum, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000504{
505 int ret;
506
Vivien Didelotfad09c72016-06-21 12:28:20 -0400507 ret = mv88e6xxx_ppu_access_get(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000508 if (ret >= 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400509 ret = _mv88e6xxx_reg_write(chip, addr, regnum, val);
510 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000511 }
512
513 return ret;
514}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000515
Vivien Didelotfad09c72016-06-21 12:28:20 -0400516static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200517{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400518 return chip->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200519}
520
Vivien Didelotfad09c72016-06-21 12:28:20 -0400521static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200522{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400523 return chip->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200524}
525
Vivien Didelotfad09c72016-06-21 12:28:20 -0400526static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200527{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400528 return chip->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200529}
530
Vivien Didelotfad09c72016-06-21 12:28:20 -0400531static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200532{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400533 return chip->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200534}
535
Vivien Didelotfad09c72016-06-21 12:28:20 -0400536static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200537{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400538 return chip->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200539}
540
Vivien Didelotfad09c72016-06-21 12:28:20 -0400541static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700542{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400543 return chip->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700544}
545
Vivien Didelotfad09c72016-06-21 12:28:20 -0400546static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200547{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400548 return chip->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200549}
550
Vivien Didelotfad09c72016-06-21 12:28:20 -0400551static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200552{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400553 return chip->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200554}
555
Vivien Didelotfad09c72016-06-21 12:28:20 -0400556static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400557{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400558 return chip->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400559}
560
Vivien Didelotfad09c72016-06-21 12:28:20 -0400561static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_chip *chip)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400562{
563 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400564 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
565 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400566 return true;
567
568 return false;
569}
570
Andrew Lunndea87022015-08-31 15:56:47 +0200571/* We expect the switch to perform auto negotiation if there is a real
572 * phy. However, in the case of a fixed link phy, we force the port
573 * settings from the fixed link settings.
574 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400575static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
576 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200577{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400578 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200579 u32 reg;
580 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200581
582 if (!phy_is_pseudo_fixed_link(phydev))
583 return;
584
Vivien Didelotfad09c72016-06-21 12:28:20 -0400585 mutex_lock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200586
Vivien Didelotfad09c72016-06-21 12:28:20 -0400587 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200588 if (ret < 0)
589 goto out;
590
591 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
592 PORT_PCS_CTRL_FORCE_LINK |
593 PORT_PCS_CTRL_DUPLEX_FULL |
594 PORT_PCS_CTRL_FORCE_DUPLEX |
595 PORT_PCS_CTRL_UNFORCED);
596
597 reg |= PORT_PCS_CTRL_FORCE_LINK;
598 if (phydev->link)
Vivien Didelot57d32312016-06-20 13:13:58 -0400599 reg |= PORT_PCS_CTRL_LINK_UP;
Andrew Lunndea87022015-08-31 15:56:47 +0200600
Vivien Didelotfad09c72016-06-21 12:28:20 -0400601 if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200602 goto out;
603
604 switch (phydev->speed) {
605 case SPEED_1000:
606 reg |= PORT_PCS_CTRL_1000;
607 break;
608 case SPEED_100:
609 reg |= PORT_PCS_CTRL_100;
610 break;
611 case SPEED_10:
612 reg |= PORT_PCS_CTRL_10;
613 break;
614 default:
615 pr_info("Unknown speed");
616 goto out;
617 }
618
619 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
620 if (phydev->duplex == DUPLEX_FULL)
621 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
622
Vivien Didelotfad09c72016-06-21 12:28:20 -0400623 if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
624 (port >= chip->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200625 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
626 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
627 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
628 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
629 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
630 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
631 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
632 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400633 _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200634
635out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400636 mutex_unlock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200637}
638
Vivien Didelotfad09c72016-06-21 12:28:20 -0400639static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000640{
641 int ret;
642 int i;
643
644 for (i = 0; i < 10; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400645 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200646 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000647 return 0;
648 }
649
650 return -ETIMEDOUT;
651}
652
Vivien Didelotfad09c72016-06-21 12:28:20 -0400653static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000654{
655 int ret;
656
Vivien Didelotfad09c72016-06-21 12:28:20 -0400657 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200658 port = (port + 1) << 5;
659
Barry Grussling3675c8d2013-01-08 16:05:53 +0000660 /* Snapshot the hardware statistics counters for this port. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400661 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200662 GLOBAL_STATS_OP_CAPTURE_PORT |
663 GLOBAL_STATS_OP_HIST_RX_TX | port);
664 if (ret < 0)
665 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000666
Barry Grussling3675c8d2013-01-08 16:05:53 +0000667 /* Wait for the snapshotting to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400668 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000669 if (ret < 0)
670 return ret;
671
672 return 0;
673}
674
Vivien Didelotfad09c72016-06-21 12:28:20 -0400675static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400676 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000677{
678 u32 _val;
679 int ret;
680
681 *val = 0;
682
Vivien Didelotfad09c72016-06-21 12:28:20 -0400683 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200684 GLOBAL_STATS_OP_READ_CAPTURED |
685 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000686 if (ret < 0)
687 return;
688
Vivien Didelotfad09c72016-06-21 12:28:20 -0400689 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000690 if (ret < 0)
691 return;
692
Vivien Didelotfad09c72016-06-21 12:28:20 -0400693 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000694 if (ret < 0)
695 return;
696
697 _val = ret << 16;
698
Vivien Didelotfad09c72016-06-21 12:28:20 -0400699 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000700 if (ret < 0)
701 return;
702
703 *val = _val | ret;
704}
705
Andrew Lunne413e7e2015-04-02 04:06:38 +0200706static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100707 { "in_good_octets", 8, 0x00, BANK0, },
708 { "in_bad_octets", 4, 0x02, BANK0, },
709 { "in_unicast", 4, 0x04, BANK0, },
710 { "in_broadcasts", 4, 0x06, BANK0, },
711 { "in_multicasts", 4, 0x07, BANK0, },
712 { "in_pause", 4, 0x16, BANK0, },
713 { "in_undersize", 4, 0x18, BANK0, },
714 { "in_fragments", 4, 0x19, BANK0, },
715 { "in_oversize", 4, 0x1a, BANK0, },
716 { "in_jabber", 4, 0x1b, BANK0, },
717 { "in_rx_error", 4, 0x1c, BANK0, },
718 { "in_fcs_error", 4, 0x1d, BANK0, },
719 { "out_octets", 8, 0x0e, BANK0, },
720 { "out_unicast", 4, 0x10, BANK0, },
721 { "out_broadcasts", 4, 0x13, BANK0, },
722 { "out_multicasts", 4, 0x12, BANK0, },
723 { "out_pause", 4, 0x15, BANK0, },
724 { "excessive", 4, 0x11, BANK0, },
725 { "collisions", 4, 0x1e, BANK0, },
726 { "deferred", 4, 0x05, BANK0, },
727 { "single", 4, 0x14, BANK0, },
728 { "multiple", 4, 0x17, BANK0, },
729 { "out_fcs_error", 4, 0x03, BANK0, },
730 { "late", 4, 0x1f, BANK0, },
731 { "hist_64bytes", 4, 0x08, BANK0, },
732 { "hist_65_127bytes", 4, 0x09, BANK0, },
733 { "hist_128_255bytes", 4, 0x0a, BANK0, },
734 { "hist_256_511bytes", 4, 0x0b, BANK0, },
735 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
736 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
737 { "sw_in_discards", 4, 0x10, PORT, },
738 { "sw_in_filtered", 2, 0x12, PORT, },
739 { "sw_out_filtered", 2, 0x13, PORT, },
740 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
741 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
742 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
743 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
744 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
745 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
746 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
747 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
748 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
749 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
750 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
751 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
752 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
753 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
754 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
755 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
756 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
757 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
758 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
759 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
760 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
761 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
762 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
763 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
764 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
765 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200766};
767
Vivien Didelotfad09c72016-06-21 12:28:20 -0400768static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100769 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200770{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100771 switch (stat->type) {
772 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200773 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100774 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400775 return mv88e6xxx_6320_family(chip);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100776 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400777 return mv88e6xxx_6095_family(chip) ||
778 mv88e6xxx_6185_family(chip) ||
779 mv88e6xxx_6097_family(chip) ||
780 mv88e6xxx_6165_family(chip) ||
781 mv88e6xxx_6351_family(chip) ||
782 mv88e6xxx_6352_family(chip);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200783 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100784 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000785}
786
Vivien Didelotfad09c72016-06-21 12:28:20 -0400787static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100788 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200789 int port)
790{
Andrew Lunn80c46272015-06-20 18:42:30 +0200791 u32 low;
792 u32 high = 0;
793 int ret;
794 u64 value;
795
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100796 switch (s->type) {
797 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400798 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200799 if (ret < 0)
800 return UINT64_MAX;
801
802 low = ret;
803 if (s->sizeof_stat == 4) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400804 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100805 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200806 if (ret < 0)
807 return UINT64_MAX;
808 high = ret;
809 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100810 break;
811 case BANK0:
812 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400813 _mv88e6xxx_stats_read(chip, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200814 if (s->sizeof_stat == 8)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400815 _mv88e6xxx_stats_read(chip, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200816 }
817 value = (((u64)high) << 16) | low;
818 return value;
819}
820
Vivien Didelotf81ec902016-05-09 13:22:58 -0400821static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
822 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100823{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400824 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100825 struct mv88e6xxx_hw_stat *stat;
826 int i, j;
827
828 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
829 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400830 if (mv88e6xxx_has_stat(chip, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100831 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
832 ETH_GSTRING_LEN);
833 j++;
834 }
835 }
836}
837
Vivien Didelotf81ec902016-05-09 13:22:58 -0400838static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100839{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400840 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100841 struct mv88e6xxx_hw_stat *stat;
842 int i, j;
843
844 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
845 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400846 if (mv88e6xxx_has_stat(chip, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100847 j++;
848 }
849 return j;
850}
851
Vivien Didelotf81ec902016-05-09 13:22:58 -0400852static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
853 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000854{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400855 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100856 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000857 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100858 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000859
Vivien Didelotfad09c72016-06-21 12:28:20 -0400860 mutex_lock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000861
Vivien Didelotfad09c72016-06-21 12:28:20 -0400862 ret = _mv88e6xxx_stats_snapshot(chip, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000863 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400864 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000865 return;
866 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100867 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
868 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400869 if (mv88e6xxx_has_stat(chip, stat)) {
870 data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100871 j++;
872 }
873 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000874
Vivien Didelotfad09c72016-06-21 12:28:20 -0400875 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000876}
Ben Hutchings98e67302011-11-25 14:36:19 +0000877
Vivien Didelotf81ec902016-05-09 13:22:58 -0400878static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700879{
880 return 32 * sizeof(u16);
881}
882
Vivien Didelotf81ec902016-05-09 13:22:58 -0400883static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
884 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700885{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400886 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700887 u16 *p = _p;
888 int i;
889
890 regs->version = 0;
891
892 memset(p, 0xff, 32 * sizeof(u16));
893
Vivien Didelotfad09c72016-06-21 12:28:20 -0400894 mutex_lock(&chip->reg_lock);
Vivien Didelot23062512016-05-09 13:22:45 -0400895
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700896 for (i = 0; i < 32; i++) {
897 int ret;
898
Vivien Didelotfad09c72016-06-21 12:28:20 -0400899 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700900 if (ret >= 0)
901 p[i] = ret;
902 }
Vivien Didelot23062512016-05-09 13:22:45 -0400903
Vivien Didelotfad09c72016-06-21 12:28:20 -0400904 mutex_unlock(&chip->reg_lock);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700905}
906
Vivien Didelotfad09c72016-06-21 12:28:20 -0400907static int _mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int reg, int offset,
Andrew Lunn3898c142015-05-06 01:09:53 +0200908 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700909{
910 unsigned long timeout = jiffies + HZ / 10;
911
912 while (time_before(jiffies, timeout)) {
913 int ret;
914
Vivien Didelotfad09c72016-06-21 12:28:20 -0400915 ret = _mv88e6xxx_reg_read(chip, reg, offset);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700916 if (ret < 0)
917 return ret;
918 if (!(ret & mask))
919 return 0;
920
921 usleep_range(1000, 2000);
922 }
923 return -ETIMEDOUT;
924}
925
Vivien Didelotfad09c72016-06-21 12:28:20 -0400926static int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int reg,
Andrew Lunn158bc062016-04-28 21:24:06 -0400927 int offset, u16 mask)
Andrew Lunn3898c142015-05-06 01:09:53 +0200928{
Andrew Lunn3898c142015-05-06 01:09:53 +0200929 int ret;
930
Vivien Didelotfad09c72016-06-21 12:28:20 -0400931 mutex_lock(&chip->reg_lock);
932 ret = _mv88e6xxx_wait(chip, reg, offset, mask);
933 mutex_unlock(&chip->reg_lock);
Andrew Lunn3898c142015-05-06 01:09:53 +0200934
935 return ret;
936}
937
Vivien Didelotfad09c72016-06-21 12:28:20 -0400938static int mv88e6xxx_mdio_wait(struct mv88e6xxx_chip *chip)
Andrew Lunn3898c142015-05-06 01:09:53 +0200939{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400940 return _mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200941 GLOBAL2_SMI_OP_BUSY);
942}
943
Vivien Didelotd24645b2016-05-09 13:22:41 -0400944static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200945{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400946 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -0400947
Vivien Didelotfad09c72016-06-21 12:28:20 -0400948 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200949 GLOBAL2_EEPROM_OP_LOAD);
950}
951
Vivien Didelotd24645b2016-05-09 13:22:41 -0400952static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200953{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400954 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -0400955
Vivien Didelotfad09c72016-06-21 12:28:20 -0400956 return mv88e6xxx_wait(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200957 GLOBAL2_EEPROM_OP_BUSY);
958}
959
Vivien Didelotd24645b2016-05-09 13:22:41 -0400960static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
961{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400962 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotd24645b2016-05-09 13:22:41 -0400963 int ret;
964
Vivien Didelotfad09c72016-06-21 12:28:20 -0400965 mutex_lock(&chip->eeprom_mutex);
Vivien Didelotd24645b2016-05-09 13:22:41 -0400966
Vivien Didelotfad09c72016-06-21 12:28:20 -0400967 ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Vivien Didelotd24645b2016-05-09 13:22:41 -0400968 GLOBAL2_EEPROM_OP_READ |
969 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
970 if (ret < 0)
971 goto error;
972
973 ret = mv88e6xxx_eeprom_busy_wait(ds);
974 if (ret < 0)
975 goto error;
976
Vivien Didelotfad09c72016-06-21 12:28:20 -0400977 ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
Vivien Didelotd24645b2016-05-09 13:22:41 -0400978error:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400979 mutex_unlock(&chip->eeprom_mutex);
Vivien Didelotd24645b2016-05-09 13:22:41 -0400980 return ret;
981}
982
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200983static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
984{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400985 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200986
Vivien Didelotfad09c72016-06-21 12:28:20 -0400987 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
988 return chip->eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200989
990 return 0;
991}
992
Vivien Didelotf81ec902016-05-09 13:22:58 -0400993static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
994 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400995{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400996 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotd24645b2016-05-09 13:22:41 -0400997 int offset;
998 int len;
999 int ret;
1000
Vivien Didelotfad09c72016-06-21 12:28:20 -04001001 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
Vivien Didelotd24645b2016-05-09 13:22:41 -04001002 return -EOPNOTSUPP;
1003
1004 offset = eeprom->offset;
1005 len = eeprom->len;
1006 eeprom->len = 0;
1007
1008 eeprom->magic = 0xc3ec4951;
1009
1010 ret = mv88e6xxx_eeprom_load_wait(ds);
1011 if (ret < 0)
1012 return ret;
1013
1014 if (offset & 1) {
1015 int word;
1016
1017 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1018 if (word < 0)
1019 return word;
1020
1021 *data++ = (word >> 8) & 0xff;
1022
1023 offset++;
1024 len--;
1025 eeprom->len++;
1026 }
1027
1028 while (len >= 2) {
1029 int word;
1030
1031 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1032 if (word < 0)
1033 return word;
1034
1035 *data++ = word & 0xff;
1036 *data++ = (word >> 8) & 0xff;
1037
1038 offset += 2;
1039 len -= 2;
1040 eeprom->len += 2;
1041 }
1042
1043 if (len) {
1044 int word;
1045
1046 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1047 if (word < 0)
1048 return word;
1049
1050 *data++ = word & 0xff;
1051
1052 offset++;
1053 len--;
1054 eeprom->len++;
1055 }
1056
1057 return 0;
1058}
1059
1060static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
1061{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001062 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001063 int ret;
1064
Vivien Didelotfad09c72016-06-21 12:28:20 -04001065 ret = mv88e6xxx_reg_read(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001066 if (ret < 0)
1067 return ret;
1068
1069 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
1070 return -EROFS;
1071
1072 return 0;
1073}
1074
1075static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
1076 u16 data)
1077{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001078 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001079 int ret;
1080
Vivien Didelotfad09c72016-06-21 12:28:20 -04001081 mutex_lock(&chip->eeprom_mutex);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001082
Vivien Didelotfad09c72016-06-21 12:28:20 -04001083 ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001084 if (ret < 0)
1085 goto error;
1086
Vivien Didelotfad09c72016-06-21 12:28:20 -04001087 ret = mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Vivien Didelotd24645b2016-05-09 13:22:41 -04001088 GLOBAL2_EEPROM_OP_WRITE |
1089 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
1090 if (ret < 0)
1091 goto error;
1092
1093 ret = mv88e6xxx_eeprom_busy_wait(ds);
1094error:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001095 mutex_unlock(&chip->eeprom_mutex);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001096 return ret;
1097}
1098
Vivien Didelotf81ec902016-05-09 13:22:58 -04001099static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
1100 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -04001101{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001102 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotd24645b2016-05-09 13:22:41 -04001103 int offset;
1104 int ret;
1105 int len;
1106
Vivien Didelotfad09c72016-06-21 12:28:20 -04001107 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
Vivien Didelotd24645b2016-05-09 13:22:41 -04001108 return -EOPNOTSUPP;
1109
1110 if (eeprom->magic != 0xc3ec4951)
1111 return -EINVAL;
1112
1113 ret = mv88e6xxx_eeprom_is_readonly(ds);
1114 if (ret)
1115 return ret;
1116
1117 offset = eeprom->offset;
1118 len = eeprom->len;
1119 eeprom->len = 0;
1120
1121 ret = mv88e6xxx_eeprom_load_wait(ds);
1122 if (ret < 0)
1123 return ret;
1124
1125 if (offset & 1) {
1126 int word;
1127
1128 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1129 if (word < 0)
1130 return word;
1131
1132 word = (*data++ << 8) | (word & 0xff);
1133
1134 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1135 if (ret < 0)
1136 return ret;
1137
1138 offset++;
1139 len--;
1140 eeprom->len++;
1141 }
1142
1143 while (len >= 2) {
1144 int word;
1145
1146 word = *data++;
1147 word |= *data++ << 8;
1148
1149 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1150 if (ret < 0)
1151 return ret;
1152
1153 offset += 2;
1154 len -= 2;
1155 eeprom->len += 2;
1156 }
1157
1158 if (len) {
1159 int word;
1160
1161 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1162 if (word < 0)
1163 return word;
1164
1165 word = (word & 0xff00) | *data++;
1166
1167 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1168 if (ret < 0)
1169 return ret;
1170
1171 offset++;
1172 len--;
1173 eeprom->len++;
1174 }
1175
1176 return 0;
1177}
1178
Vivien Didelotfad09c72016-06-21 12:28:20 -04001179static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001180{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001181 return _mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001182 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001183}
1184
Vivien Didelotfad09c72016-06-21 12:28:20 -04001185static int mv88e6xxx_mdio_read_indirect(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001186 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001187{
1188 int ret;
1189
Vivien Didelotfad09c72016-06-21 12:28:20 -04001190 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001191 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1192 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001193 if (ret < 0)
1194 return ret;
1195
Vivien Didelotfad09c72016-06-21 12:28:20 -04001196 ret = mv88e6xxx_mdio_wait(chip);
Andrew Lunn3898c142015-05-06 01:09:53 +02001197 if (ret < 0)
1198 return ret;
1199
Vivien Didelotfad09c72016-06-21 12:28:20 -04001200 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL2, GLOBAL2_SMI_DATA);
Andrew Lunn158bc062016-04-28 21:24:06 -04001201
1202 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001203}
1204
Vivien Didelotfad09c72016-06-21 12:28:20 -04001205static int mv88e6xxx_mdio_write_indirect(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001206 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001207{
Andrew Lunn3898c142015-05-06 01:09:53 +02001208 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001209
Vivien Didelotfad09c72016-06-21 12:28:20 -04001210 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001211 if (ret < 0)
1212 return ret;
1213
Vivien Didelotfad09c72016-06-21 12:28:20 -04001214 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001215 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1216 regnum);
1217
Vivien Didelotfad09c72016-06-21 12:28:20 -04001218 return mv88e6xxx_mdio_wait(chip);
Andrew Lunnf3044682015-02-14 19:17:50 +01001219}
1220
Vivien Didelotf81ec902016-05-09 13:22:58 -04001221static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1222 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001223{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001224 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001225 int reg;
1226
Vivien Didelotfad09c72016-06-21 12:28:20 -04001227 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001228 return -EOPNOTSUPP;
1229
Vivien Didelotfad09c72016-06-21 12:28:20 -04001230 mutex_lock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001231
Vivien Didelotfad09c72016-06-21 12:28:20 -04001232 reg = mv88e6xxx_mdio_read_indirect(chip, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001233 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001234 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001235
1236 e->eee_enabled = !!(reg & 0x0200);
1237 e->tx_lpi_enabled = !!(reg & 0x0100);
1238
Vivien Didelotfad09c72016-06-21 12:28:20 -04001239 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001240 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001241 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001242
Andrew Lunncca8b132015-04-02 04:06:39 +02001243 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001244 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001245
Andrew Lunn2f40c692015-04-02 04:06:37 +02001246out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001247 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001248 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001249}
1250
Vivien Didelotf81ec902016-05-09 13:22:58 -04001251static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1252 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001253{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001254 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001255 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001256 int ret;
1257
Vivien Didelotfad09c72016-06-21 12:28:20 -04001258 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001259 return -EOPNOTSUPP;
1260
Vivien Didelotfad09c72016-06-21 12:28:20 -04001261 mutex_lock(&chip->reg_lock);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001262
Vivien Didelotfad09c72016-06-21 12:28:20 -04001263 ret = mv88e6xxx_mdio_read_indirect(chip, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001264 if (ret < 0)
1265 goto out;
1266
1267 reg = ret & ~0x0300;
1268 if (e->eee_enabled)
1269 reg |= 0x0200;
1270 if (e->tx_lpi_enabled)
1271 reg |= 0x0100;
1272
Vivien Didelotfad09c72016-06-21 12:28:20 -04001273 ret = mv88e6xxx_mdio_write_indirect(chip, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001274out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001275 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001276
1277 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001278}
1279
Vivien Didelotfad09c72016-06-21 12:28:20 -04001280static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001281{
1282 int ret;
1283
Vivien Didelotfad09c72016-06-21 12:28:20 -04001284 if (mv88e6xxx_has_fid_reg(chip)) {
1285 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_FID,
1286 fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001287 if (ret < 0)
1288 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001289 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001290 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001291 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001292 if (ret < 0)
1293 return ret;
1294
Vivien Didelotfad09c72016-06-21 12:28:20 -04001295 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001296 (ret & 0xfff) |
1297 ((fid << 8) & 0xf000));
1298 if (ret < 0)
1299 return ret;
1300
1301 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1302 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001303 }
1304
Vivien Didelotfad09c72016-06-21 12:28:20 -04001305 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001306 if (ret < 0)
1307 return ret;
1308
Vivien Didelotfad09c72016-06-21 12:28:20 -04001309 return _mv88e6xxx_atu_wait(chip);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001310}
1311
Vivien Didelotfad09c72016-06-21 12:28:20 -04001312static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot37705b72015-09-04 14:34:11 -04001313 struct mv88e6xxx_atu_entry *entry)
1314{
1315 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1316
1317 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1318 unsigned int mask, shift;
1319
1320 if (entry->trunk) {
1321 data |= GLOBAL_ATU_DATA_TRUNK;
1322 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1323 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1324 } else {
1325 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1326 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1327 }
1328
1329 data |= (entry->portv_trunkid << shift) & mask;
1330 }
1331
Vivien Didelotfad09c72016-06-21 12:28:20 -04001332 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001333}
1334
Vivien Didelotfad09c72016-06-21 12:28:20 -04001335static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001336 struct mv88e6xxx_atu_entry *entry,
1337 bool static_too)
1338{
1339 int op;
1340 int err;
1341
Vivien Didelotfad09c72016-06-21 12:28:20 -04001342 err = _mv88e6xxx_atu_wait(chip);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001343 if (err)
1344 return err;
1345
Vivien Didelotfad09c72016-06-21 12:28:20 -04001346 err = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001347 if (err)
1348 return err;
1349
1350 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001351 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1352 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1353 } else {
1354 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1355 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1356 }
1357
Vivien Didelotfad09c72016-06-21 12:28:20 -04001358 return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001359}
1360
Vivien Didelotfad09c72016-06-21 12:28:20 -04001361static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001362 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001363{
1364 struct mv88e6xxx_atu_entry entry = {
1365 .fid = fid,
1366 .state = 0, /* EntryState bits must be 0 */
1367 };
1368
Vivien Didelotfad09c72016-06-21 12:28:20 -04001369 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001370}
1371
Vivien Didelotfad09c72016-06-21 12:28:20 -04001372static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001373 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001374{
1375 struct mv88e6xxx_atu_entry entry = {
1376 .trunk = false,
1377 .fid = fid,
1378 };
1379
1380 /* EntryState bits must be 0xF */
1381 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1382
1383 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1384 entry.portv_trunkid = (to_port & 0x0f) << 4;
1385 entry.portv_trunkid |= from_port & 0x0f;
1386
Vivien Didelotfad09c72016-06-21 12:28:20 -04001387 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001388}
1389
Vivien Didelotfad09c72016-06-21 12:28:20 -04001390static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001391 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001392{
1393 /* Destination port 0xF means remove the entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001394 return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001395}
1396
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001397static const char * const mv88e6xxx_port_state_names[] = {
1398 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1399 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1400 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1401 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1402};
1403
Vivien Didelotfad09c72016-06-21 12:28:20 -04001404static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001405 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001406{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001407 struct dsa_switch *ds = chip->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001408 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001409 u8 oldstate;
1410
Vivien Didelotfad09c72016-06-21 12:28:20 -04001411 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001412 if (reg < 0)
1413 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001414
Andrew Lunncca8b132015-04-02 04:06:39 +02001415 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001416
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001417 if (oldstate != state) {
1418 /* Flush forwarding database if we're moving a port
1419 * from Learning or Forwarding state to Disabled or
1420 * Blocking or Listening state.
1421 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001422 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
Vivien Didelot57d32312016-06-20 13:13:58 -04001423 oldstate == PORT_CONTROL_STATE_FORWARDING) &&
1424 (state == PORT_CONTROL_STATE_DISABLED ||
1425 state == PORT_CONTROL_STATE_BLOCKING)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001426 ret = _mv88e6xxx_atu_remove(chip, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001427 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001428 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001429 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001430
Andrew Lunncca8b132015-04-02 04:06:39 +02001431 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001432 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001433 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001434 if (ret)
1435 return ret;
1436
Andrew Lunnc8b09802016-06-04 21:16:57 +02001437 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001438 mv88e6xxx_port_state_names[state],
1439 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001440 }
1441
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001442 return ret;
1443}
1444
Vivien Didelotfad09c72016-06-21 12:28:20 -04001445static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001446{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001447 struct net_device *bridge = chip->ports[port].bridge_dev;
1448 const u16 mask = (1 << chip->info->num_ports) - 1;
1449 struct dsa_switch *ds = chip->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001450 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001451 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001452 int i;
1453
1454 /* allow CPU port or DSA link(s) to send frames to every port */
1455 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1456 output_ports = mask;
1457 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001458 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001459 /* allow sending frames to every group member */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001460 if (bridge && chip->ports[i].bridge_dev == bridge)
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001461 output_ports |= BIT(i);
1462
1463 /* allow sending frames to CPU port and DSA link(s) */
1464 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1465 output_ports |= BIT(i);
1466 }
1467 }
1468
1469 /* prevent frames from going back out of the port they came in on */
1470 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001471
Vivien Didelotfad09c72016-06-21 12:28:20 -04001472 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001473 if (reg < 0)
1474 return reg;
1475
1476 reg &= ~mask;
1477 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001478
Vivien Didelotfad09c72016-06-21 12:28:20 -04001479 return _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001480}
1481
Vivien Didelotf81ec902016-05-09 13:22:58 -04001482static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1483 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001484{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001485 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001486 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001487 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001488
1489 switch (state) {
1490 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001491 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001492 break;
1493 case BR_STATE_BLOCKING:
1494 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001495 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001496 break;
1497 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001498 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001499 break;
1500 case BR_STATE_FORWARDING:
1501 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001502 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001503 break;
1504 }
1505
Vivien Didelotfad09c72016-06-21 12:28:20 -04001506 mutex_lock(&chip->reg_lock);
1507 err = _mv88e6xxx_port_state(chip, port, stp_state);
1508 mutex_unlock(&chip->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001509
1510 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001511 netdev_err(ds->ports[port].netdev,
1512 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001513 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001514}
1515
Vivien Didelotfad09c72016-06-21 12:28:20 -04001516static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001517 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001518{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001519 struct dsa_switch *ds = chip->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001520 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001521 int ret;
1522
Vivien Didelotfad09c72016-06-21 12:28:20 -04001523 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001524 if (ret < 0)
1525 return ret;
1526
Vivien Didelot5da96032016-03-07 18:24:39 -05001527 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1528
1529 if (new) {
1530 ret &= ~PORT_DEFAULT_VLAN_MASK;
1531 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1532
Vivien Didelotfad09c72016-06-21 12:28:20 -04001533 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001534 PORT_DEFAULT_VLAN, ret);
1535 if (ret < 0)
1536 return ret;
1537
Andrew Lunnc8b09802016-06-04 21:16:57 +02001538 netdev_dbg(ds->ports[port].netdev,
1539 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001540 }
1541
1542 if (old)
1543 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001544
1545 return 0;
1546}
1547
Vivien Didelotfad09c72016-06-21 12:28:20 -04001548static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001549 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001550{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001551 return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001552}
1553
Vivien Didelotfad09c72016-06-21 12:28:20 -04001554static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001555 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001556{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001557 return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001558}
1559
Vivien Didelotfad09c72016-06-21 12:28:20 -04001560static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001561{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001562 return _mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001563 GLOBAL_VTU_OP_BUSY);
1564}
1565
Vivien Didelotfad09c72016-06-21 12:28:20 -04001566static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001567{
1568 int ret;
1569
Vivien Didelotfad09c72016-06-21 12:28:20 -04001570 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001571 if (ret < 0)
1572 return ret;
1573
Vivien Didelotfad09c72016-06-21 12:28:20 -04001574 return _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001575}
1576
Vivien Didelotfad09c72016-06-21 12:28:20 -04001577static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001578{
1579 int ret;
1580
Vivien Didelotfad09c72016-06-21 12:28:20 -04001581 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001582 if (ret < 0)
1583 return ret;
1584
Vivien Didelotfad09c72016-06-21 12:28:20 -04001585 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001586}
1587
Vivien Didelotfad09c72016-06-21 12:28:20 -04001588static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001589 struct mv88e6xxx_vtu_stu_entry *entry,
1590 unsigned int nibble_offset)
1591{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001592 u16 regs[3];
1593 int i;
1594 int ret;
1595
1596 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001597 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001598 GLOBAL_VTU_DATA_0_3 + i);
1599 if (ret < 0)
1600 return ret;
1601
1602 regs[i] = ret;
1603 }
1604
Vivien Didelotfad09c72016-06-21 12:28:20 -04001605 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001606 unsigned int shift = (i % 4) * 4 + nibble_offset;
1607 u16 reg = regs[i / 4];
1608
1609 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1610 }
1611
1612 return 0;
1613}
1614
Vivien Didelotfad09c72016-06-21 12:28:20 -04001615static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001616 struct mv88e6xxx_vtu_stu_entry *entry)
1617{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001618 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001619}
1620
Vivien Didelotfad09c72016-06-21 12:28:20 -04001621static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001622 struct mv88e6xxx_vtu_stu_entry *entry)
1623{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001624 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001625}
1626
Vivien Didelotfad09c72016-06-21 12:28:20 -04001627static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001628 struct mv88e6xxx_vtu_stu_entry *entry,
1629 unsigned int nibble_offset)
1630{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001631 u16 regs[3] = { 0 };
1632 int i;
1633 int ret;
1634
Vivien Didelotfad09c72016-06-21 12:28:20 -04001635 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001636 unsigned int shift = (i % 4) * 4 + nibble_offset;
1637 u8 data = entry->data[i];
1638
1639 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1640 }
1641
1642 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001643 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001644 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1645 if (ret < 0)
1646 return ret;
1647 }
1648
1649 return 0;
1650}
1651
Vivien Didelotfad09c72016-06-21 12:28:20 -04001652static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001653 struct mv88e6xxx_vtu_stu_entry *entry)
1654{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001655 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001656}
1657
Vivien Didelotfad09c72016-06-21 12:28:20 -04001658static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001659 struct mv88e6xxx_vtu_stu_entry *entry)
1660{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001661 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001662}
1663
Vivien Didelotfad09c72016-06-21 12:28:20 -04001664static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001665{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001666 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001667 vid & GLOBAL_VTU_VID_MASK);
1668}
1669
Vivien Didelotfad09c72016-06-21 12:28:20 -04001670static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001671 struct mv88e6xxx_vtu_stu_entry *entry)
1672{
1673 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1674 int ret;
1675
Vivien Didelotfad09c72016-06-21 12:28:20 -04001676 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001677 if (ret < 0)
1678 return ret;
1679
Vivien Didelotfad09c72016-06-21 12:28:20 -04001680 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001681 if (ret < 0)
1682 return ret;
1683
Vivien Didelotfad09c72016-06-21 12:28:20 -04001684 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001685 if (ret < 0)
1686 return ret;
1687
1688 next.vid = ret & GLOBAL_VTU_VID_MASK;
1689 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1690
1691 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001692 ret = mv88e6xxx_vtu_data_read(chip, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001693 if (ret < 0)
1694 return ret;
1695
Vivien Didelotfad09c72016-06-21 12:28:20 -04001696 if (mv88e6xxx_has_fid_reg(chip)) {
1697 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001698 GLOBAL_VTU_FID);
1699 if (ret < 0)
1700 return ret;
1701
1702 next.fid = ret & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001703 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001704 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1705 * VTU DBNum[3:0] are located in VTU Operation 3:0
1706 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001707 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001708 GLOBAL_VTU_OP);
1709 if (ret < 0)
1710 return ret;
1711
1712 next.fid = (ret & 0xf00) >> 4;
1713 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001714 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001715
Vivien Didelotfad09c72016-06-21 12:28:20 -04001716 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1717 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001718 GLOBAL_VTU_SID);
1719 if (ret < 0)
1720 return ret;
1721
1722 next.sid = ret & GLOBAL_VTU_SID_MASK;
1723 }
1724 }
1725
1726 *entry = next;
1727 return 0;
1728}
1729
Vivien Didelotf81ec902016-05-09 13:22:58 -04001730static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1731 struct switchdev_obj_port_vlan *vlan,
1732 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001733{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001734 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001735 struct mv88e6xxx_vtu_stu_entry next;
1736 u16 pvid;
1737 int err;
1738
Vivien Didelotfad09c72016-06-21 12:28:20 -04001739 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001740 return -EOPNOTSUPP;
1741
Vivien Didelotfad09c72016-06-21 12:28:20 -04001742 mutex_lock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001743
Vivien Didelotfad09c72016-06-21 12:28:20 -04001744 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001745 if (err)
1746 goto unlock;
1747
Vivien Didelotfad09c72016-06-21 12:28:20 -04001748 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001749 if (err)
1750 goto unlock;
1751
1752 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001753 err = _mv88e6xxx_vtu_getnext(chip, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001754 if (err)
1755 break;
1756
1757 if (!next.valid)
1758 break;
1759
1760 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1761 continue;
1762
1763 /* reinit and dump this VLAN obj */
Vivien Didelot57d32312016-06-20 13:13:58 -04001764 vlan->vid_begin = next.vid;
1765 vlan->vid_end = next.vid;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001766 vlan->flags = 0;
1767
1768 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1769 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1770
1771 if (next.vid == pvid)
1772 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1773
1774 err = cb(&vlan->obj);
1775 if (err)
1776 break;
1777 } while (next.vid < GLOBAL_VTU_VID_MASK);
1778
1779unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001780 mutex_unlock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001781
1782 return err;
1783}
1784
Vivien Didelotfad09c72016-06-21 12:28:20 -04001785static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001786 struct mv88e6xxx_vtu_stu_entry *entry)
1787{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001788 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001789 u16 reg = 0;
1790 int ret;
1791
Vivien Didelotfad09c72016-06-21 12:28:20 -04001792 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001793 if (ret < 0)
1794 return ret;
1795
1796 if (!entry->valid)
1797 goto loadpurge;
1798
1799 /* Write port member tags */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001800 ret = mv88e6xxx_vtu_data_write(chip, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001801 if (ret < 0)
1802 return ret;
1803
Vivien Didelotfad09c72016-06-21 12:28:20 -04001804 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001805 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001806 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
1807 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001808 if (ret < 0)
1809 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001810 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001811
Vivien Didelotfad09c72016-06-21 12:28:20 -04001812 if (mv88e6xxx_has_fid_reg(chip)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001813 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001814 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_FID,
1815 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001816 if (ret < 0)
1817 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001818 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001819 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1820 * VTU DBNum[3:0] are located in VTU Operation 3:0
1821 */
1822 op |= (entry->fid & 0xf0) << 8;
1823 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001824 }
1825
1826 reg = GLOBAL_VTU_VID_VALID;
1827loadpurge:
1828 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001829 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001830 if (ret < 0)
1831 return ret;
1832
Vivien Didelotfad09c72016-06-21 12:28:20 -04001833 return _mv88e6xxx_vtu_cmd(chip, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001834}
1835
Vivien Didelotfad09c72016-06-21 12:28:20 -04001836static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001837 struct mv88e6xxx_vtu_stu_entry *entry)
1838{
1839 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1840 int ret;
1841
Vivien Didelotfad09c72016-06-21 12:28:20 -04001842 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001843 if (ret < 0)
1844 return ret;
1845
Vivien Didelotfad09c72016-06-21 12:28:20 -04001846 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001847 sid & GLOBAL_VTU_SID_MASK);
1848 if (ret < 0)
1849 return ret;
1850
Vivien Didelotfad09c72016-06-21 12:28:20 -04001851 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001852 if (ret < 0)
1853 return ret;
1854
Vivien Didelotfad09c72016-06-21 12:28:20 -04001855 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001856 if (ret < 0)
1857 return ret;
1858
1859 next.sid = ret & GLOBAL_VTU_SID_MASK;
1860
Vivien Didelotfad09c72016-06-21 12:28:20 -04001861 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001862 if (ret < 0)
1863 return ret;
1864
1865 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1866
1867 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001868 ret = mv88e6xxx_stu_data_read(chip, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001869 if (ret < 0)
1870 return ret;
1871 }
1872
1873 *entry = next;
1874 return 0;
1875}
1876
Vivien Didelotfad09c72016-06-21 12:28:20 -04001877static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001878 struct mv88e6xxx_vtu_stu_entry *entry)
1879{
1880 u16 reg = 0;
1881 int ret;
1882
Vivien Didelotfad09c72016-06-21 12:28:20 -04001883 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001884 if (ret < 0)
1885 return ret;
1886
1887 if (!entry->valid)
1888 goto loadpurge;
1889
1890 /* Write port states */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001891 ret = mv88e6xxx_stu_data_write(chip, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001892 if (ret < 0)
1893 return ret;
1894
1895 reg = GLOBAL_VTU_VID_VALID;
1896loadpurge:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001897 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001898 if (ret < 0)
1899 return ret;
1900
1901 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001902 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001903 if (ret < 0)
1904 return ret;
1905
Vivien Didelotfad09c72016-06-21 12:28:20 -04001906 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001907}
1908
Vivien Didelotfad09c72016-06-21 12:28:20 -04001909static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001910 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001911{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001912 struct dsa_switch *ds = chip->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001913 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001914 u16 fid;
1915 int ret;
1916
Vivien Didelotfad09c72016-06-21 12:28:20 -04001917 if (mv88e6xxx_num_databases(chip) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001918 upper_mask = 0xff;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001919 else if (mv88e6xxx_num_databases(chip) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001920 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001921 else
1922 return -EOPNOTSUPP;
1923
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001924 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001925 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001926 if (ret < 0)
1927 return ret;
1928
1929 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1930
1931 if (new) {
1932 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1933 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1934
Vivien Didelotfad09c72016-06-21 12:28:20 -04001935 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001936 ret);
1937 if (ret < 0)
1938 return ret;
1939 }
1940
1941 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001942 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001943 if (ret < 0)
1944 return ret;
1945
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001946 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001947
1948 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001949 ret &= ~upper_mask;
1950 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001951
Vivien Didelotfad09c72016-06-21 12:28:20 -04001952 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001953 ret);
1954 if (ret < 0)
1955 return ret;
1956
Andrew Lunnc8b09802016-06-04 21:16:57 +02001957 netdev_dbg(ds->ports[port].netdev,
1958 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001959 }
1960
1961 if (old)
1962 *old = fid;
1963
1964 return 0;
1965}
1966
Vivien Didelotfad09c72016-06-21 12:28:20 -04001967static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001968 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001969{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001970 return _mv88e6xxx_port_fid(chip, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001971}
1972
Vivien Didelotfad09c72016-06-21 12:28:20 -04001973static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001974 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001975{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001976 return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001977}
1978
Vivien Didelotfad09c72016-06-21 12:28:20 -04001979static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001980{
1981 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1982 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001983 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001984
1985 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1986
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001987 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001988 for (i = 0; i < chip->info->num_ports; ++i) {
1989 err = _mv88e6xxx_port_fid_get(chip, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001990 if (err)
1991 return err;
1992
1993 set_bit(*fid, fid_bitmap);
1994 }
1995
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001996 /* Set every FID bit used by the VLAN entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001997 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001998 if (err)
1999 return err;
2000
2001 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002002 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002003 if (err)
2004 return err;
2005
2006 if (!vlan.valid)
2007 break;
2008
2009 set_bit(vlan.fid, fid_bitmap);
2010 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2011
2012 /* The reset value 0x000 is used to indicate that multiple address
2013 * databases are not needed. Return the next positive available.
2014 */
2015 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002016 if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002017 return -ENOSPC;
2018
2019 /* Clear the database */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002020 return _mv88e6xxx_atu_flush(chip, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002021}
2022
Vivien Didelotfad09c72016-06-21 12:28:20 -04002023static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002024 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002025{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002026 struct dsa_switch *ds = chip->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002027 struct mv88e6xxx_vtu_stu_entry vlan = {
2028 .valid = true,
2029 .vid = vid,
2030 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002031 int i, err;
2032
Vivien Didelotfad09c72016-06-21 12:28:20 -04002033 err = _mv88e6xxx_fid_new(chip, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002034 if (err)
2035 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002036
Vivien Didelot3d131f02015-11-03 10:52:52 -05002037 /* exclude all ports except the CPU and DSA ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002038 for (i = 0; i < chip->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05002039 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
2040 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
2041 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002042
Vivien Didelotfad09c72016-06-21 12:28:20 -04002043 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
2044 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002045 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002046
2047 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
2048 * implemented, only one STU entry is needed to cover all VTU
2049 * entries. Thus, validate the SID 0.
2050 */
2051 vlan.sid = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002052 err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002053 if (err)
2054 return err;
2055
2056 if (vstp.sid != vlan.sid || !vstp.valid) {
2057 memset(&vstp, 0, sizeof(vstp));
2058 vstp.valid = true;
2059 vstp.sid = vlan.sid;
2060
Vivien Didelotfad09c72016-06-21 12:28:20 -04002061 err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002062 if (err)
2063 return err;
2064 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002065 }
2066
2067 *entry = vlan;
2068 return 0;
2069}
2070
Vivien Didelotfad09c72016-06-21 12:28:20 -04002071static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002072 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
2073{
2074 int err;
2075
2076 if (!vid)
2077 return -EINVAL;
2078
Vivien Didelotfad09c72016-06-21 12:28:20 -04002079 err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002080 if (err)
2081 return err;
2082
Vivien Didelotfad09c72016-06-21 12:28:20 -04002083 err = _mv88e6xxx_vtu_getnext(chip, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002084 if (err)
2085 return err;
2086
2087 if (entry->vid != vid || !entry->valid) {
2088 if (!creat)
2089 return -EOPNOTSUPP;
2090 /* -ENOENT would've been more appropriate, but switchdev expects
2091 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
2092 */
2093
Vivien Didelotfad09c72016-06-21 12:28:20 -04002094 err = _mv88e6xxx_vtu_new(chip, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002095 }
2096
2097 return err;
2098}
2099
Vivien Didelotda9c3592016-02-12 12:09:40 -05002100static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
2101 u16 vid_begin, u16 vid_end)
2102{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002103 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002104 struct mv88e6xxx_vtu_stu_entry vlan;
2105 int i, err;
2106
2107 if (!vid_begin)
2108 return -EOPNOTSUPP;
2109
Vivien Didelotfad09c72016-06-21 12:28:20 -04002110 mutex_lock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002111
Vivien Didelotfad09c72016-06-21 12:28:20 -04002112 err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002113 if (err)
2114 goto unlock;
2115
2116 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002117 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002118 if (err)
2119 goto unlock;
2120
2121 if (!vlan.valid)
2122 break;
2123
2124 if (vlan.vid > vid_end)
2125 break;
2126
Vivien Didelotfad09c72016-06-21 12:28:20 -04002127 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05002128 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
2129 continue;
2130
2131 if (vlan.data[i] ==
2132 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2133 continue;
2134
Vivien Didelotfad09c72016-06-21 12:28:20 -04002135 if (chip->ports[i].bridge_dev ==
2136 chip->ports[port].bridge_dev)
Vivien Didelotda9c3592016-02-12 12:09:40 -05002137 break; /* same bridge, check next VLAN */
2138
Andrew Lunnc8b09802016-06-04 21:16:57 +02002139 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05002140 "hardware VLAN %d already used by %s\n",
2141 vlan.vid,
Vivien Didelotfad09c72016-06-21 12:28:20 -04002142 netdev_name(chip->ports[i].bridge_dev));
Vivien Didelotda9c3592016-02-12 12:09:40 -05002143 err = -EOPNOTSUPP;
2144 goto unlock;
2145 }
2146 } while (vlan.vid < vid_end);
2147
2148unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002149 mutex_unlock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002150
2151 return err;
2152}
2153
Vivien Didelot214cdb92016-02-26 13:16:08 -05002154static const char * const mv88e6xxx_port_8021q_mode_names[] = {
2155 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
2156 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
2157 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
2158 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
2159};
2160
Vivien Didelotf81ec902016-05-09 13:22:58 -04002161static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
2162 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002163{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002164 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002165 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
2166 PORT_CONTROL_2_8021Q_DISABLED;
2167 int ret;
2168
Vivien Didelotfad09c72016-06-21 12:28:20 -04002169 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002170 return -EOPNOTSUPP;
2171
Vivien Didelotfad09c72016-06-21 12:28:20 -04002172 mutex_lock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002173
Vivien Didelotfad09c72016-06-21 12:28:20 -04002174 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002175 if (ret < 0)
2176 goto unlock;
2177
2178 old = ret & PORT_CONTROL_2_8021Q_MASK;
2179
Vivien Didelot5220ef12016-03-07 18:24:52 -05002180 if (new != old) {
2181 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2182 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002183
Vivien Didelotfad09c72016-06-21 12:28:20 -04002184 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002185 ret);
2186 if (ret < 0)
2187 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002188
Andrew Lunnc8b09802016-06-04 21:16:57 +02002189 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05002190 mv88e6xxx_port_8021q_mode_names[new],
2191 mv88e6xxx_port_8021q_mode_names[old]);
2192 }
2193
2194 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002195unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002196 mutex_unlock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002197
2198 return ret;
2199}
2200
Vivien Didelot57d32312016-06-20 13:13:58 -04002201static int
2202mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2203 const struct switchdev_obj_port_vlan *vlan,
2204 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002205{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002206 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002207 int err;
2208
Vivien Didelotfad09c72016-06-21 12:28:20 -04002209 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002210 return -EOPNOTSUPP;
2211
Vivien Didelotda9c3592016-02-12 12:09:40 -05002212 /* If the requested port doesn't belong to the same bridge as the VLAN
2213 * members, do not support it (yet) and fallback to software VLAN.
2214 */
2215 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2216 vlan->vid_end);
2217 if (err)
2218 return err;
2219
Vivien Didelot76e398a2015-11-01 12:33:55 -05002220 /* We don't need any dynamic resource from the kernel (yet),
2221 * so skip the prepare phase.
2222 */
2223 return 0;
2224}
2225
Vivien Didelotfad09c72016-06-21 12:28:20 -04002226static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04002227 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002228{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002229 struct mv88e6xxx_vtu_stu_entry vlan;
2230 int err;
2231
Vivien Didelotfad09c72016-06-21 12:28:20 -04002232 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002233 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002234 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002235
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002236 vlan.data[port] = untagged ?
2237 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2238 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2239
Vivien Didelotfad09c72016-06-21 12:28:20 -04002240 return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002241}
2242
Vivien Didelotf81ec902016-05-09 13:22:58 -04002243static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2244 const struct switchdev_obj_port_vlan *vlan,
2245 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002246{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002247 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002248 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2249 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2250 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002251
Vivien Didelotfad09c72016-06-21 12:28:20 -04002252 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002253 return;
2254
Vivien Didelotfad09c72016-06-21 12:28:20 -04002255 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002256
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002257 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002258 if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002259 netdev_err(ds->ports[port].netdev,
2260 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002261 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002262
Vivien Didelotfad09c72016-06-21 12:28:20 -04002263 if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002264 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002265 vlan->vid_end);
2266
Vivien Didelotfad09c72016-06-21 12:28:20 -04002267 mutex_unlock(&chip->reg_lock);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002268}
2269
Vivien Didelotfad09c72016-06-21 12:28:20 -04002270static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002271 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002272{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002273 struct dsa_switch *ds = chip->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002274 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002275 int i, err;
2276
Vivien Didelotfad09c72016-06-21 12:28:20 -04002277 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002278 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002279 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002280
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002281 /* Tell switchdev if this VLAN is handled in software */
2282 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002283 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002284
2285 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2286
2287 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002288 vlan.valid = false;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002289 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002290 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002291 continue;
2292
2293 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002294 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002295 break;
2296 }
2297 }
2298
Vivien Didelotfad09c72016-06-21 12:28:20 -04002299 err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002300 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002301 return err;
2302
Vivien Didelotfad09c72016-06-21 12:28:20 -04002303 return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002304}
2305
Vivien Didelotf81ec902016-05-09 13:22:58 -04002306static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2307 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002308{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002309 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002310 u16 pvid, vid;
2311 int err = 0;
2312
Vivien Didelotfad09c72016-06-21 12:28:20 -04002313 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002314 return -EOPNOTSUPP;
2315
Vivien Didelotfad09c72016-06-21 12:28:20 -04002316 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002317
Vivien Didelotfad09c72016-06-21 12:28:20 -04002318 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002319 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002320 goto unlock;
2321
Vivien Didelot76e398a2015-11-01 12:33:55 -05002322 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002323 err = _mv88e6xxx_port_vlan_del(chip, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002324 if (err)
2325 goto unlock;
2326
2327 if (vid == pvid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002328 err = _mv88e6xxx_port_pvid_set(chip, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002329 if (err)
2330 goto unlock;
2331 }
2332 }
2333
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002334unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002335 mutex_unlock(&chip->reg_lock);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002336
2337 return err;
2338}
2339
Vivien Didelotfad09c72016-06-21 12:28:20 -04002340static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002341 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002342{
2343 int i, ret;
2344
2345 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002346 ret = _mv88e6xxx_reg_write(
Vivien Didelotfad09c72016-06-21 12:28:20 -04002347 chip, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002348 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002349 if (ret < 0)
2350 return ret;
2351 }
2352
2353 return 0;
2354}
2355
Vivien Didelotfad09c72016-06-21 12:28:20 -04002356static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002357 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002358{
2359 int i, ret;
2360
2361 for (i = 0; i < 3; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002362 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002363 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002364 if (ret < 0)
2365 return ret;
2366 addr[i * 2] = ret >> 8;
2367 addr[i * 2 + 1] = ret & 0xff;
2368 }
2369
2370 return 0;
2371}
2372
Vivien Didelotfad09c72016-06-21 12:28:20 -04002373static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002374 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002375{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002376 int ret;
2377
Vivien Didelotfad09c72016-06-21 12:28:20 -04002378 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002379 if (ret < 0)
2380 return ret;
2381
Vivien Didelotfad09c72016-06-21 12:28:20 -04002382 ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002383 if (ret < 0)
2384 return ret;
2385
Vivien Didelotfad09c72016-06-21 12:28:20 -04002386 ret = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002387 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002388 return ret;
2389
Vivien Didelotfad09c72016-06-21 12:28:20 -04002390 return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002391}
David S. Millercdf09692015-08-11 12:00:37 -07002392
Vivien Didelotfad09c72016-06-21 12:28:20 -04002393static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_chip *chip, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002394 const unsigned char *addr, u16 vid,
2395 u8 state)
2396{
2397 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002398 struct mv88e6xxx_vtu_stu_entry vlan;
2399 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002400
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002401 /* Null VLAN ID corresponds to the port private database */
2402 if (vid == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002403 err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002404 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002405 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002406 if (err)
2407 return err;
2408
2409 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002410 entry.state = state;
2411 ether_addr_copy(entry.mac, addr);
2412 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2413 entry.trunk = false;
2414 entry.portv_trunkid = BIT(port);
2415 }
2416
Vivien Didelotfad09c72016-06-21 12:28:20 -04002417 return _mv88e6xxx_atu_load(chip, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002418}
2419
Vivien Didelotf81ec902016-05-09 13:22:58 -04002420static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2421 const struct switchdev_obj_port_fdb *fdb,
2422 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002423{
2424 /* We don't need any dynamic resource from the kernel (yet),
2425 * so skip the prepare phase.
2426 */
2427 return 0;
2428}
2429
Vivien Didelotf81ec902016-05-09 13:22:58 -04002430static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2431 const struct switchdev_obj_port_fdb *fdb,
2432 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002433{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002434 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002435 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2436 GLOBAL_ATU_DATA_STATE_UC_STATIC;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002437 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002438
Vivien Didelotfad09c72016-06-21 12:28:20 -04002439 mutex_lock(&chip->reg_lock);
2440 if (_mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid, state))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002441 netdev_err(ds->ports[port].netdev,
2442 "failed to load MAC address\n");
Vivien Didelotfad09c72016-06-21 12:28:20 -04002443 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002444}
2445
Vivien Didelotf81ec902016-05-09 13:22:58 -04002446static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2447 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002448{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002449 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
David S. Millercdf09692015-08-11 12:00:37 -07002450 int ret;
2451
Vivien Didelotfad09c72016-06-21 12:28:20 -04002452 mutex_lock(&chip->reg_lock);
2453 ret = _mv88e6xxx_port_fdb_load(chip, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002454 GLOBAL_ATU_DATA_STATE_UNUSED);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002455 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002456
2457 return ret;
2458}
2459
Vivien Didelotfad09c72016-06-21 12:28:20 -04002460static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002461 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002462{
Vivien Didelot1d194042015-08-10 09:09:51 -04002463 struct mv88e6xxx_atu_entry next = { 0 };
2464 int ret;
2465
2466 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002467
Vivien Didelotfad09c72016-06-21 12:28:20 -04002468 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002469 if (ret < 0)
2470 return ret;
2471
Vivien Didelotfad09c72016-06-21 12:28:20 -04002472 ret = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002473 if (ret < 0)
2474 return ret;
2475
Vivien Didelotfad09c72016-06-21 12:28:20 -04002476 ret = _mv88e6xxx_atu_mac_read(chip, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002477 if (ret < 0)
2478 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002479
Vivien Didelotfad09c72016-06-21 12:28:20 -04002480 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002481 if (ret < 0)
2482 return ret;
2483
2484 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2485 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2486 unsigned int mask, shift;
2487
2488 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2489 next.trunk = true;
2490 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2491 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2492 } else {
2493 next.trunk = false;
2494 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2495 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2496 }
2497
2498 next.portv_trunkid = (ret & mask) >> shift;
2499 }
2500
2501 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002502 return 0;
2503}
2504
Vivien Didelotfad09c72016-06-21 12:28:20 -04002505static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002506 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002507 struct switchdev_obj_port_fdb *fdb,
2508 int (*cb)(struct switchdev_obj *obj))
2509{
2510 struct mv88e6xxx_atu_entry addr = {
2511 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2512 };
2513 int err;
2514
Vivien Didelotfad09c72016-06-21 12:28:20 -04002515 err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002516 if (err)
2517 return err;
2518
2519 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002520 err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002521 if (err)
2522 break;
2523
2524 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2525 break;
2526
2527 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2528 bool is_static = addr.state ==
2529 (is_multicast_ether_addr(addr.mac) ?
2530 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2531 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2532
2533 fdb->vid = vid;
2534 ether_addr_copy(fdb->addr, addr.mac);
2535 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2536
2537 err = cb(&fdb->obj);
2538 if (err)
2539 break;
2540 }
2541 } while (!is_broadcast_ether_addr(addr.mac));
2542
2543 return err;
2544}
2545
Vivien Didelotf81ec902016-05-09 13:22:58 -04002546static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2547 struct switchdev_obj_port_fdb *fdb,
2548 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002549{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002550 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002551 struct mv88e6xxx_vtu_stu_entry vlan = {
2552 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2553 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002554 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002555 int err;
2556
Vivien Didelotfad09c72016-06-21 12:28:20 -04002557 mutex_lock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002558
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002559 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002560 err = _mv88e6xxx_port_fid_get(chip, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002561 if (err)
2562 goto unlock;
2563
Vivien Didelotfad09c72016-06-21 12:28:20 -04002564 err = _mv88e6xxx_port_fdb_dump_one(chip, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002565 if (err)
2566 goto unlock;
2567
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002568 /* Dump VLANs' Filtering Information Databases */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002569 err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002570 if (err)
2571 goto unlock;
2572
2573 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002574 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002575 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002576 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002577
2578 if (!vlan.valid)
2579 break;
2580
Vivien Didelotfad09c72016-06-21 12:28:20 -04002581 err = _mv88e6xxx_port_fdb_dump_one(chip, vlan.fid, vlan.vid,
2582 port, fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002583 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002584 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002585 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2586
2587unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002588 mutex_unlock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002589
2590 return err;
2591}
2592
Vivien Didelotf81ec902016-05-09 13:22:58 -04002593static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2594 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002595{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002596 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002597 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002598
Vivien Didelotfad09c72016-06-21 12:28:20 -04002599 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002600
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002601 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002602 chip->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002603
Vivien Didelotfad09c72016-06-21 12:28:20 -04002604 for (i = 0; i < chip->info->num_ports; ++i) {
2605 if (chip->ports[i].bridge_dev == bridge) {
2606 err = _mv88e6xxx_port_based_vlan_map(chip, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002607 if (err)
2608 break;
2609 }
2610 }
2611
Vivien Didelotfad09c72016-06-21 12:28:20 -04002612 mutex_unlock(&chip->reg_lock);
Vivien Didelota6692752016-02-12 12:09:39 -05002613
Vivien Didelot466dfa02016-02-26 13:16:05 -05002614 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002615}
2616
Vivien Didelotf81ec902016-05-09 13:22:58 -04002617static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002618{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002619 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
2620 struct net_device *bridge = chip->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002621 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002622
Vivien Didelotfad09c72016-06-21 12:28:20 -04002623 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002624
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002625 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002626 chip->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002627
Vivien Didelotfad09c72016-06-21 12:28:20 -04002628 for (i = 0; i < chip->info->num_ports; ++i)
2629 if (i == port || chip->ports[i].bridge_dev == bridge)
2630 if (_mv88e6xxx_port_based_vlan_map(chip, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002631 netdev_warn(ds->ports[i].netdev,
2632 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002633
Vivien Didelotfad09c72016-06-21 12:28:20 -04002634 mutex_unlock(&chip->reg_lock);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002635}
2636
Vivien Didelotfad09c72016-06-21 12:28:20 -04002637static int _mv88e6xxx_mdio_page_write(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002638 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002639{
2640 int ret;
2641
Vivien Didelotfad09c72016-06-21 12:28:20 -04002642 ret = mv88e6xxx_mdio_write_indirect(chip, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002643 if (ret < 0)
2644 goto restore_page_0;
2645
Vivien Didelotfad09c72016-06-21 12:28:20 -04002646 ret = mv88e6xxx_mdio_write_indirect(chip, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002647restore_page_0:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002648 mv88e6xxx_mdio_write_indirect(chip, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002649
2650 return ret;
2651}
2652
Vivien Didelotfad09c72016-06-21 12:28:20 -04002653static int _mv88e6xxx_mdio_page_read(struct mv88e6xxx_chip *chip,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002654 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002655{
2656 int ret;
2657
Vivien Didelotfad09c72016-06-21 12:28:20 -04002658 ret = mv88e6xxx_mdio_write_indirect(chip, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002659 if (ret < 0)
2660 goto restore_page_0;
2661
Vivien Didelotfad09c72016-06-21 12:28:20 -04002662 ret = mv88e6xxx_mdio_read_indirect(chip, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002663restore_page_0:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002664 mv88e6xxx_mdio_write_indirect(chip, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002665
2666 return ret;
2667}
2668
Vivien Didelotfad09c72016-06-21 12:28:20 -04002669static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
Vivien Didelot552238b2016-05-09 13:22:49 -04002670{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002671 bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
Vivien Didelot552238b2016-05-09 13:22:49 -04002672 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002673 struct gpio_desc *gpiod = chip->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002674 unsigned long timeout;
2675 int ret;
2676 int i;
2677
2678 /* Set all ports to the disabled state. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002679 for (i = 0; i < chip->info->num_ports; i++) {
2680 ret = _mv88e6xxx_reg_read(chip, REG_PORT(i), PORT_CONTROL);
Vivien Didelot552238b2016-05-09 13:22:49 -04002681 if (ret < 0)
2682 return ret;
2683
Vivien Didelotfad09c72016-06-21 12:28:20 -04002684 ret = _mv88e6xxx_reg_write(chip, REG_PORT(i), PORT_CONTROL,
Vivien Didelot552238b2016-05-09 13:22:49 -04002685 ret & 0xfffc);
2686 if (ret)
2687 return ret;
2688 }
2689
2690 /* Wait for transmit queues to drain. */
2691 usleep_range(2000, 4000);
2692
2693 /* If there is a gpio connected to the reset pin, toggle it */
2694 if (gpiod) {
2695 gpiod_set_value_cansleep(gpiod, 1);
2696 usleep_range(10000, 20000);
2697 gpiod_set_value_cansleep(gpiod, 0);
2698 usleep_range(10000, 20000);
2699 }
2700
2701 /* Reset the switch. Keep the PPU active if requested. The PPU
2702 * needs to be active to support indirect phy register access
2703 * through global registers 0x18 and 0x19.
2704 */
2705 if (ppu_active)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002706 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc000);
Vivien Didelot552238b2016-05-09 13:22:49 -04002707 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002708 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc400);
Vivien Didelot552238b2016-05-09 13:22:49 -04002709 if (ret)
2710 return ret;
2711
2712 /* Wait up to one second for reset to complete. */
2713 timeout = jiffies + 1 * HZ;
2714 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002715 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, 0x00);
Vivien Didelot552238b2016-05-09 13:22:49 -04002716 if (ret < 0)
2717 return ret;
2718
2719 if ((ret & is_reset) == is_reset)
2720 break;
2721 usleep_range(1000, 2000);
2722 }
2723 if (time_after(jiffies, timeout))
2724 ret = -ETIMEDOUT;
2725 else
2726 ret = 0;
2727
2728 return ret;
2729}
2730
Vivien Didelotfad09c72016-06-21 12:28:20 -04002731static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_chip *chip)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002732{
2733 int ret;
2734
Vivien Didelotfad09c72016-06-21 12:28:20 -04002735 ret = _mv88e6xxx_mdio_page_read(chip, REG_FIBER_SERDES,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002736 PAGE_FIBER_SERDES, MII_BMCR);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002737 if (ret < 0)
2738 return ret;
2739
2740 if (ret & BMCR_PDOWN) {
2741 ret &= ~BMCR_PDOWN;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002742 ret = _mv88e6xxx_mdio_page_write(chip, REG_FIBER_SERDES,
Andrew Lunn03a4a542016-06-04 21:17:05 +02002743 PAGE_FIBER_SERDES, MII_BMCR,
2744 ret);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002745 }
2746
2747 return ret;
2748}
2749
Vivien Didelotfad09c72016-06-21 12:28:20 -04002750static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002751{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002752 struct dsa_switch *ds = chip->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002753 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002754 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002755
Vivien Didelotfad09c72016-06-21 12:28:20 -04002756 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2757 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2758 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2759 mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002760 /* MAC Forcing register: don't force link, speed,
2761 * duplex or flow control state to any particular
2762 * values on physical ports, but force the CPU port
2763 * and all DSA ports to their maximum bandwidth and
2764 * full duplex.
2765 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002766 reg = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002767 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002768 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002769 reg |= PORT_PCS_CTRL_FORCE_LINK |
2770 PORT_PCS_CTRL_LINK_UP |
2771 PORT_PCS_CTRL_DUPLEX_FULL |
2772 PORT_PCS_CTRL_FORCE_DUPLEX;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002773 if (mv88e6xxx_6065_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002774 reg |= PORT_PCS_CTRL_100;
2775 else
2776 reg |= PORT_PCS_CTRL_1000;
2777 } else {
2778 reg |= PORT_PCS_CTRL_UNFORCED;
2779 }
2780
Vivien Didelotfad09c72016-06-21 12:28:20 -04002781 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002782 PORT_PCS_CTRL, reg);
2783 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002784 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002785 }
2786
2787 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2788 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2789 * tunneling, determine priority by looking at 802.1p and IP
2790 * priority fields (IP prio has precedence), and set STP state
2791 * to Forwarding.
2792 *
2793 * If this is the CPU link, use DSA or EDSA tagging depending
2794 * on which tagging mode was configured.
2795 *
2796 * If this is a link to another switch, use DSA tagging mode.
2797 *
2798 * If this is the upstream port for this switch, enable
2799 * forwarding of unknown unicasts and multicasts.
2800 */
2801 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002802 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2803 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2804 mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2805 mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002806 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2807 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2808 PORT_CONTROL_STATE_FORWARDING;
2809 if (dsa_is_cpu_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002810 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002811 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002812 if (mv88e6xxx_6352_family(chip) ||
2813 mv88e6xxx_6351_family(chip) ||
2814 mv88e6xxx_6165_family(chip) ||
2815 mv88e6xxx_6097_family(chip) ||
2816 mv88e6xxx_6320_family(chip)) {
Andrew Lunn5377b802016-06-04 21:17:02 +02002817 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2818 PORT_CONTROL_FORWARD_UNKNOWN |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002819 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002820 }
2821
Vivien Didelotfad09c72016-06-21 12:28:20 -04002822 if (mv88e6xxx_6352_family(chip) ||
2823 mv88e6xxx_6351_family(chip) ||
2824 mv88e6xxx_6165_family(chip) ||
2825 mv88e6xxx_6097_family(chip) ||
2826 mv88e6xxx_6095_family(chip) ||
2827 mv88e6xxx_6065_family(chip) ||
2828 mv88e6xxx_6185_family(chip) ||
2829 mv88e6xxx_6320_family(chip)) {
Vivien Didelot57d32312016-06-20 13:13:58 -04002830 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002831 }
2832 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002833 if (dsa_is_dsa_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002834 if (mv88e6xxx_6095_family(chip) ||
2835 mv88e6xxx_6185_family(chip))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002836 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002837 if (mv88e6xxx_6352_family(chip) ||
2838 mv88e6xxx_6351_family(chip) ||
2839 mv88e6xxx_6165_family(chip) ||
2840 mv88e6xxx_6097_family(chip) ||
2841 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002842 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002843 }
2844
Andrew Lunn54d792f2015-05-06 01:09:47 +02002845 if (port == dsa_upstream_port(ds))
2846 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2847 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2848 }
2849 if (reg) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002850 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002851 PORT_CONTROL, reg);
2852 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002853 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002854 }
2855
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002856 /* If this port is connected to a SerDes, make sure the SerDes is not
2857 * powered down.
2858 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002859 if (mv88e6xxx_6352_family(chip)) {
2860 ret = _mv88e6xxx_reg_read(chip, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002861 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002862 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002863 ret &= PORT_STATUS_CMODE_MASK;
2864 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2865 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2866 (ret == PORT_STATUS_CMODE_SGMII)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002867 ret = mv88e6xxx_power_on_serdes(chip);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002868 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002869 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002870 }
2871 }
2872
Vivien Didelot8efdda42015-08-13 12:52:23 -04002873 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002874 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002875 * untagged frames on this port, do a destination address lookup on all
2876 * received packets as usual, disable ARP mirroring and don't send a
2877 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002878 */
2879 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002880 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2881 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2882 mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2883 mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002884 reg = PORT_CONTROL_2_MAP_DA;
2885
Vivien Didelotfad09c72016-06-21 12:28:20 -04002886 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2887 mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002888 reg |= PORT_CONTROL_2_JUMBO_10240;
2889
Vivien Didelotfad09c72016-06-21 12:28:20 -04002890 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002891 /* Set the upstream port this port should use */
2892 reg |= dsa_upstream_port(ds);
2893 /* enable forwarding of unknown multicast addresses to
2894 * the upstream port
2895 */
2896 if (port == dsa_upstream_port(ds))
2897 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2898 }
2899
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002900 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002901
Andrew Lunn54d792f2015-05-06 01:09:47 +02002902 if (reg) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002903 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002904 PORT_CONTROL_2, reg);
2905 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002906 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002907 }
2908
2909 /* Port Association Vector: when learning source addresses
2910 * of packets, add the address to the address database using
2911 * a port bitmap that has only the bit for this port set and
2912 * the other bits clear.
2913 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002914 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002915 /* Disable learning for CPU port */
2916 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002917 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002918
Vivien Didelotfad09c72016-06-21 12:28:20 -04002919 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_ASSOC_VECTOR,
2920 reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002921 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002922 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002923
2924 /* Egress rate control 2: disable egress rate control. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002925 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002926 0x0000);
2927 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002928 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002929
Vivien Didelotfad09c72016-06-21 12:28:20 -04002930 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2931 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2932 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002933 /* Do not limit the period of time that this port can
2934 * be paused for by the remote end or the period of
2935 * time that this port can pause the remote end.
2936 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002937 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002938 PORT_PAUSE_CTRL, 0x0000);
2939 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002940 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002941
2942 /* Port ATU control: disable limiting the number of
2943 * address database entries that this port is allowed
2944 * to use.
2945 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002946 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002947 PORT_ATU_CONTROL, 0x0000);
2948 /* Priority Override: disable DA, SA and VTU priority
2949 * override.
2950 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002951 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002952 PORT_PRI_OVERRIDE, 0x0000);
2953 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002954 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002955
2956 /* Port Ethertype: use the Ethertype DSA Ethertype
2957 * value.
2958 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002959 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002960 PORT_ETH_TYPE, ETH_P_EDSA);
2961 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002962 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002963 /* Tag Remap: use an identity 802.1p prio -> switch
2964 * prio mapping.
2965 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002966 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002967 PORT_TAG_REGMAP_0123, 0x3210);
2968 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002969 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002970
2971 /* Tag Remap 2: use an identity 802.1p prio -> switch
2972 * prio mapping.
2973 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002974 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002975 PORT_TAG_REGMAP_4567, 0x7654);
2976 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002977 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002978 }
2979
Vivien Didelotfad09c72016-06-21 12:28:20 -04002980 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2981 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2982 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2983 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002984 /* Rate Control: disable ingress rate limiting. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002985 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002986 PORT_RATE_CONTROL, 0x0001);
2987 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002988 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002989 }
2990
Guenter Roeck366f0a02015-03-26 18:36:30 -07002991 /* Port Control 1: disable trunking, disable sending
2992 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002993 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002994 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_CONTROL_1,
2995 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002996 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002997 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002998
Vivien Didelot207afda2016-04-14 14:42:09 -04002999 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05003000 * database, and allow bidirectional communication between the
3001 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07003002 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003003 ret = _mv88e6xxx_port_fid_set(chip, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05003004 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003005 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05003006
Vivien Didelotfad09c72016-06-21 12:28:20 -04003007 ret = _mv88e6xxx_port_based_vlan_map(chip, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07003008 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003009 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07003010
3011 /* Default VLAN ID and priority: don't set a default VLAN
3012 * ID, and set the default packet priority to zero.
3013 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003014 ret = _mv88e6xxx_reg_write(chip, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e62015-04-20 17:43:26 -04003015 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003016 if (ret)
3017 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07003018
Andrew Lunndbde9e62015-05-06 01:09:48 +02003019 return 0;
3020}
3021
Vivien Didelot97299342016-07-18 20:45:30 -04003022static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
Vivien Didelot08a01262016-05-09 13:22:50 -04003023{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003024 struct dsa_switch *ds = chip->ds;
Vivien Didelotb0745e872016-05-09 13:22:53 -04003025 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04003026 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04003027 int err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003028
Vivien Didelot119477b2016-05-09 13:22:51 -04003029 /* Enable the PHY Polling Unit if present, don't discard any packets,
3030 * and mask all interrupt sources.
3031 */
3032 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003033 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
3034 mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
Vivien Didelot119477b2016-05-09 13:22:51 -04003035 reg |= GLOBAL_CONTROL_PPU_ENABLE;
3036
Vivien Didelotfad09c72016-06-21 12:28:20 -04003037 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL, reg);
Vivien Didelot119477b2016-05-09 13:22:51 -04003038 if (err)
3039 return err;
3040
Vivien Didelotb0745e872016-05-09 13:22:53 -04003041 /* Configure the upstream port, and configure it as the port to which
3042 * ingress and egress and ARP monitor frames are to be sent.
3043 */
3044 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
3045 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
3046 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003047 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MONITOR_CONTROL,
3048 reg);
Vivien Didelotb0745e872016-05-09 13:22:53 -04003049 if (err)
3050 return err;
3051
Vivien Didelot50484ff2016-05-09 13:22:54 -04003052 /* Disable remote management, and set the switch's DSA device number. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003053 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL_2,
Vivien Didelot50484ff2016-05-09 13:22:54 -04003054 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
3055 (ds->index & 0x1f));
3056 if (err)
3057 return err;
3058
Vivien Didelot08a01262016-05-09 13:22:50 -04003059 /* Set the default address aging time to 5 minutes, and
3060 * enable address learn messages to be sent to all message
3061 * ports.
3062 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003063 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot08a01262016-05-09 13:22:50 -04003064 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
3065 if (err)
3066 return err;
3067
Vivien Didelot97299342016-07-18 20:45:30 -04003068 /* Clear all the VTU and STU entries */
3069 err = _mv88e6xxx_vtu_stu_flush(chip);
3070 if (err < 0)
3071 return err;
3072
3073 /* Clear all ATU entries */
3074 err = _mv88e6xxx_atu_flush(chip, 0, true);
3075 if (err)
3076 return err;
3077
Vivien Didelot08a01262016-05-09 13:22:50 -04003078 /* Configure the IP ToS mapping registers. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003079 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04003080 if (err)
3081 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003082 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04003083 if (err)
3084 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003085 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04003086 if (err)
3087 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003088 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04003089 if (err)
3090 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003091 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04003092 if (err)
3093 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003094 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04003095 if (err)
3096 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003097 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04003098 if (err)
3099 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003100 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04003101 if (err)
3102 return err;
3103
3104 /* Configure the IEEE 802.1p priority mapping register. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003105 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Vivien Didelot08a01262016-05-09 13:22:50 -04003106 if (err)
3107 return err;
3108
Vivien Didelot97299342016-07-18 20:45:30 -04003109 /* Clear the statistics counters for all ports */
3110 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
3111 GLOBAL_STATS_OP_FLUSH_ALL);
3112 if (err)
3113 return err;
3114
3115 /* Wait for the flush to complete. */
3116 err = _mv88e6xxx_stats_wait(chip);
3117 if (err)
3118 return err;
3119
3120 return 0;
3121}
3122
Vivien Didelotf22ab642016-07-18 20:45:31 -04003123static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
3124 int target, int port)
3125{
3126 u16 val = (target << 8) | (port & 0xf);
3127
3128 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, val);
3129}
3130
3131static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
3132{
3133 int target, port;
3134 int err;
3135
3136 /* Initialize the routing port to the 32 possible target devices */
3137 for (target = 0; target < 32; ++target) {
3138 port = 0xf;
3139
3140 if (target < DSA_MAX_SWITCHES) {
3141 port = chip->ds->rtable[target];
3142 if (port == DSA_RTABLE_NONE)
3143 port = 0xf;
3144 }
3145
3146 err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
3147 if (err)
3148 break;
3149 }
3150
3151 return err;
3152}
3153
Vivien Didelot97299342016-07-18 20:45:30 -04003154static int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
3155{
Vivien Didelot97299342016-07-18 20:45:30 -04003156 int err;
3157 int i;
3158
Vivien Didelot08a01262016-05-09 13:22:50 -04003159 /* Send all frames with destination addresses matching
3160 * 01:80:c2:00:00:0x to the CPU port.
3161 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003162 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
3163 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04003164 if (err)
3165 return err;
3166
3167 /* Ignore removed tag data on doubly tagged packets, disable
3168 * flow control messages, force flow control priority to the
3169 * highest, and send all special multicast frames to the CPU
3170 * port at the highest priority.
3171 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003172 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
Vivien Didelot08a01262016-05-09 13:22:50 -04003173 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3174 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3175 if (err)
3176 return err;
3177
3178 /* Program the DSA routing table. */
Vivien Didelotf22ab642016-07-18 20:45:31 -04003179 err = mv88e6xxx_g2_set_device_mapping(chip);
3180 if (err)
3181 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003182
3183 /* Clear all trunk masks. */
3184 for (i = 0; i < 8; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003185 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
3186 GLOBAL2_TRUNK_MASK,
Vivien Didelot08a01262016-05-09 13:22:50 -04003187 0x8000 |
3188 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
Vivien Didelotfad09c72016-06-21 12:28:20 -04003189 ((1 << chip->info->num_ports) - 1));
Vivien Didelot08a01262016-05-09 13:22:50 -04003190 if (err)
3191 return err;
3192 }
3193
3194 /* Clear all trunk mappings. */
3195 for (i = 0; i < 16; i++) {
3196 err = _mv88e6xxx_reg_write(
Vivien Didelotfad09c72016-06-21 12:28:20 -04003197 chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003198 GLOBAL2_TRUNK_MAPPING,
3199 GLOBAL2_TRUNK_MAPPING_UPDATE |
3200 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
3201 if (err)
3202 return err;
3203 }
3204
Vivien Didelotfad09c72016-06-21 12:28:20 -04003205 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
3206 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
3207 mv88e6xxx_6320_family(chip)) {
Vivien Didelot08a01262016-05-09 13:22:50 -04003208 /* Send all frames with destination addresses matching
3209 * 01:80:c2:00:00:2x to the CPU port.
3210 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003211 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003212 GLOBAL2_MGMT_EN_2X, 0xffff);
3213 if (err)
3214 return err;
3215
3216 /* Initialise cross-chip port VLAN table to reset
3217 * defaults.
3218 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003219 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003220 GLOBAL2_PVT_ADDR, 0x9000);
3221 if (err)
3222 return err;
3223
3224 /* Clear the priority override table. */
3225 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003226 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003227 GLOBAL2_PRIO_OVERRIDE,
3228 0x8000 | (i << 8));
3229 if (err)
3230 return err;
3231 }
3232 }
3233
Vivien Didelotfad09c72016-06-21 12:28:20 -04003234 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
3235 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
3236 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
3237 mv88e6xxx_6320_family(chip)) {
Vivien Didelot08a01262016-05-09 13:22:50 -04003238 /* Disable ingress rate limiting by resetting all
3239 * ingress rate limit registers to their initial
3240 * state.
3241 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003242 for (i = 0; i < chip->info->num_ports; i++) {
3243 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003244 GLOBAL2_INGRESS_OP,
3245 0x9000 | (i << 8));
3246 if (err)
3247 return err;
3248 }
3249 }
3250
Vivien Didelot97299342016-07-18 20:45:30 -04003251 return 0;
Vivien Didelot08a01262016-05-09 13:22:50 -04003252}
3253
Vivien Didelotf81ec902016-05-09 13:22:58 -04003254static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003255{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003256 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003257 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003258 int i;
3259
Vivien Didelotfad09c72016-06-21 12:28:20 -04003260 chip->ds = ds;
3261 ds->slave_mii_bus = chip->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003262
Vivien Didelotfad09c72016-06-21 12:28:20 -04003263 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
3264 mutex_init(&chip->eeprom_mutex);
Vivien Didelotd24645b2016-05-09 13:22:41 -04003265
Vivien Didelotfad09c72016-06-21 12:28:20 -04003266 mutex_lock(&chip->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04003267
Vivien Didelotfad09c72016-06-21 12:28:20 -04003268 err = mv88e6xxx_switch_reset(chip);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003269 if (err)
3270 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003271
Vivien Didelot97299342016-07-18 20:45:30 -04003272 /* Setup Switch Port Registers */
3273 for (i = 0; i < chip->info->num_ports; i++) {
3274 err = mv88e6xxx_setup_port(chip, i);
3275 if (err)
3276 goto unlock;
3277 }
3278
3279 /* Setup Switch Global 1 Registers */
3280 err = mv88e6xxx_g1_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003281 if (err)
3282 goto unlock;
3283
Vivien Didelot97299342016-07-18 20:45:30 -04003284 /* Setup Switch Global 2 Registers */
3285 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
3286 err = mv88e6xxx_g2_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003287 if (err)
3288 goto unlock;
3289 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003290
Vivien Didelot6b17e862015-08-13 12:52:18 -04003291unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003292 mutex_unlock(&chip->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02003293
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003294 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003295}
3296
Vivien Didelot57d32312016-06-20 13:13:58 -04003297static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
3298 int reg)
Andrew Lunn491435852015-04-02 04:06:35 +02003299{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003300 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn491435852015-04-02 04:06:35 +02003301 int ret;
3302
Vivien Didelotfad09c72016-06-21 12:28:20 -04003303 mutex_lock(&chip->reg_lock);
3304 ret = _mv88e6xxx_mdio_page_read(chip, port, page, reg);
3305 mutex_unlock(&chip->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003306
Andrew Lunn491435852015-04-02 04:06:35 +02003307 return ret;
3308}
3309
Vivien Didelot57d32312016-06-20 13:13:58 -04003310static int mv88e6xxx_mdio_page_write(struct dsa_switch *ds, int port, int page,
3311 int reg, int val)
Andrew Lunn491435852015-04-02 04:06:35 +02003312{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003313 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn491435852015-04-02 04:06:35 +02003314 int ret;
3315
Vivien Didelotfad09c72016-06-21 12:28:20 -04003316 mutex_lock(&chip->reg_lock);
3317 ret = _mv88e6xxx_mdio_page_write(chip, port, page, reg, val);
3318 mutex_unlock(&chip->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003319
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003320 return ret;
3321}
3322
Vivien Didelotfad09c72016-06-21 12:28:20 -04003323static int mv88e6xxx_port_to_mdio_addr(struct mv88e6xxx_chip *chip, int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003324{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003325 if (port >= 0 && port < chip->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003326 return port;
3327 return -EINVAL;
3328}
3329
Andrew Lunnb516d452016-06-04 21:17:06 +02003330static int mv88e6xxx_mdio_read(struct mii_bus *bus, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003331{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003332 struct mv88e6xxx_chip *chip = bus->priv;
3333 int addr = mv88e6xxx_port_to_mdio_addr(chip, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003334 int ret;
3335
3336 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003337 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003338
Vivien Didelotfad09c72016-06-21 12:28:20 -04003339 mutex_lock(&chip->reg_lock);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003340
Vivien Didelotfad09c72016-06-21 12:28:20 -04003341 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3342 ret = mv88e6xxx_mdio_read_ppu(chip, addr, regnum);
3343 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SMI_PHY))
3344 ret = mv88e6xxx_mdio_read_indirect(chip, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003345 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04003346 ret = mv88e6xxx_mdio_read_direct(chip, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003347
Vivien Didelotfad09c72016-06-21 12:28:20 -04003348 mutex_unlock(&chip->reg_lock);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003349 return ret;
3350}
3351
Andrew Lunnb516d452016-06-04 21:17:06 +02003352static int mv88e6xxx_mdio_write(struct mii_bus *bus, int port, int regnum,
Andrew Lunn03a4a542016-06-04 21:17:05 +02003353 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003354{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003355 struct mv88e6xxx_chip *chip = bus->priv;
3356 int addr = mv88e6xxx_port_to_mdio_addr(chip, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003357 int ret;
3358
3359 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003360 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003361
Vivien Didelotfad09c72016-06-21 12:28:20 -04003362 mutex_lock(&chip->reg_lock);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003363
Vivien Didelotfad09c72016-06-21 12:28:20 -04003364 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3365 ret = mv88e6xxx_mdio_write_ppu(chip, addr, regnum, val);
3366 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SMI_PHY))
3367 ret = mv88e6xxx_mdio_write_indirect(chip, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003368 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04003369 ret = mv88e6xxx_mdio_write_direct(chip, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003370
Vivien Didelotfad09c72016-06-21 12:28:20 -04003371 mutex_unlock(&chip->reg_lock);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003372 return ret;
3373}
3374
Vivien Didelotfad09c72016-06-21 12:28:20 -04003375static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
Andrew Lunnb516d452016-06-04 21:17:06 +02003376 struct device_node *np)
3377{
3378 static int index;
3379 struct mii_bus *bus;
3380 int err;
3381
Vivien Didelotfad09c72016-06-21 12:28:20 -04003382 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3383 mv88e6xxx_ppu_state_init(chip);
Andrew Lunnb516d452016-06-04 21:17:06 +02003384
3385 if (np)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003386 chip->mdio_np = of_get_child_by_name(np, "mdio");
Andrew Lunnb516d452016-06-04 21:17:06 +02003387
Vivien Didelotfad09c72016-06-21 12:28:20 -04003388 bus = devm_mdiobus_alloc(chip->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003389 if (!bus)
3390 return -ENOMEM;
3391
Vivien Didelotfad09c72016-06-21 12:28:20 -04003392 bus->priv = (void *)chip;
Andrew Lunnb516d452016-06-04 21:17:06 +02003393 if (np) {
3394 bus->name = np->full_name;
3395 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3396 } else {
3397 bus->name = "mv88e6xxx SMI";
3398 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3399 }
3400
3401 bus->read = mv88e6xxx_mdio_read;
3402 bus->write = mv88e6xxx_mdio_write;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003403 bus->parent = chip->dev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003404
Vivien Didelotfad09c72016-06-21 12:28:20 -04003405 if (chip->mdio_np)
3406 err = of_mdiobus_register(bus, chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003407 else
3408 err = mdiobus_register(bus);
3409 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003410 dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
Andrew Lunnb516d452016-06-04 21:17:06 +02003411 goto out;
3412 }
Vivien Didelotfad09c72016-06-21 12:28:20 -04003413 chip->mdio_bus = bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003414
3415 return 0;
3416
3417out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003418 if (chip->mdio_np)
3419 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003420
3421 return err;
3422}
3423
Vivien Didelotfad09c72016-06-21 12:28:20 -04003424static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
Andrew Lunnb516d452016-06-04 21:17:06 +02003425
3426{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003427 struct mii_bus *bus = chip->mdio_bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003428
3429 mdiobus_unregister(bus);
3430
Vivien Didelotfad09c72016-06-21 12:28:20 -04003431 if (chip->mdio_np)
3432 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003433}
3434
Guenter Roeckc22995c2015-07-25 09:42:28 -07003435#ifdef CONFIG_NET_DSA_HWMON
3436
3437static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3438{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003439 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003440 int ret;
3441 int val;
3442
3443 *temp = 0;
3444
Vivien Didelotfad09c72016-06-21 12:28:20 -04003445 mutex_lock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003446
Vivien Didelotfad09c72016-06-21 12:28:20 -04003447 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003448 if (ret < 0)
3449 goto error;
3450
3451 /* Enable temperature sensor */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003452 ret = mv88e6xxx_mdio_read_direct(chip, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003453 if (ret < 0)
3454 goto error;
3455
Vivien Didelotfad09c72016-06-21 12:28:20 -04003456 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003457 if (ret < 0)
3458 goto error;
3459
3460 /* Wait for temperature to stabilize */
3461 usleep_range(10000, 12000);
3462
Vivien Didelotfad09c72016-06-21 12:28:20 -04003463 val = mv88e6xxx_mdio_read_direct(chip, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003464 if (val < 0) {
3465 ret = val;
3466 goto error;
3467 }
3468
3469 /* Disable temperature sensor */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003470 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003471 if (ret < 0)
3472 goto error;
3473
3474 *temp = ((val & 0x1f) - 5) * 5;
3475
3476error:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003477 mv88e6xxx_mdio_write_direct(chip, 0x0, 0x16, 0x0);
3478 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003479 return ret;
3480}
3481
3482static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3483{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003484 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3485 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003486 int ret;
3487
3488 *temp = 0;
3489
Andrew Lunn03a4a542016-06-04 21:17:05 +02003490 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 27);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003491 if (ret < 0)
3492 return ret;
3493
3494 *temp = (ret & 0xff) - 25;
3495
3496 return 0;
3497}
3498
Vivien Didelotf81ec902016-05-09 13:22:58 -04003499static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003500{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003501 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003502
Vivien Didelotfad09c72016-06-21 12:28:20 -04003503 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
Vivien Didelot6594f612016-05-09 13:22:42 -04003504 return -EOPNOTSUPP;
3505
Vivien Didelotfad09c72016-06-21 12:28:20 -04003506 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003507 return mv88e63xx_get_temp(ds, temp);
3508
3509 return mv88e61xx_get_temp(ds, temp);
3510}
3511
Vivien Didelotf81ec902016-05-09 13:22:58 -04003512static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003513{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003514 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3515 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003516 int ret;
3517
Vivien Didelotfad09c72016-06-21 12:28:20 -04003518 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003519 return -EOPNOTSUPP;
3520
3521 *temp = 0;
3522
Andrew Lunn03a4a542016-06-04 21:17:05 +02003523 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003524 if (ret < 0)
3525 return ret;
3526
3527 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3528
3529 return 0;
3530}
3531
Vivien Didelotf81ec902016-05-09 13:22:58 -04003532static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003533{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003534 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3535 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003536 int ret;
3537
Vivien Didelotfad09c72016-06-21 12:28:20 -04003538 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003539 return -EOPNOTSUPP;
3540
Andrew Lunn03a4a542016-06-04 21:17:05 +02003541 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003542 if (ret < 0)
3543 return ret;
3544 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003545 return mv88e6xxx_mdio_page_write(ds, phy, 6, 26,
3546 (ret & 0xe0ff) | (temp << 8));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003547}
3548
Vivien Didelotf81ec902016-05-09 13:22:58 -04003549static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003550{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003551 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3552 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003553 int ret;
3554
Vivien Didelotfad09c72016-06-21 12:28:20 -04003555 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003556 return -EOPNOTSUPP;
3557
3558 *alarm = false;
3559
Andrew Lunn03a4a542016-06-04 21:17:05 +02003560 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003561 if (ret < 0)
3562 return ret;
3563
3564 *alarm = !!(ret & 0x40);
3565
3566 return 0;
3567}
3568#endif /* CONFIG_NET_DSA_HWMON */
3569
Vivien Didelotf81ec902016-05-09 13:22:58 -04003570static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3571 [MV88E6085] = {
3572 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3573 .family = MV88E6XXX_FAMILY_6097,
3574 .name = "Marvell 88E6085",
3575 .num_databases = 4096,
3576 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003577 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003578 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3579 },
3580
3581 [MV88E6095] = {
3582 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3583 .family = MV88E6XXX_FAMILY_6095,
3584 .name = "Marvell 88E6095/88E6095F",
3585 .num_databases = 256,
3586 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003587 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003588 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3589 },
3590
3591 [MV88E6123] = {
3592 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3593 .family = MV88E6XXX_FAMILY_6165,
3594 .name = "Marvell 88E6123",
3595 .num_databases = 4096,
3596 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003597 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003598 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3599 },
3600
3601 [MV88E6131] = {
3602 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3603 .family = MV88E6XXX_FAMILY_6185,
3604 .name = "Marvell 88E6131",
3605 .num_databases = 256,
3606 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003607 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003608 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3609 },
3610
3611 [MV88E6161] = {
3612 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3613 .family = MV88E6XXX_FAMILY_6165,
3614 .name = "Marvell 88E6161",
3615 .num_databases = 4096,
3616 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003617 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003618 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3619 },
3620
3621 [MV88E6165] = {
3622 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3623 .family = MV88E6XXX_FAMILY_6165,
3624 .name = "Marvell 88E6165",
3625 .num_databases = 4096,
3626 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003627 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003628 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3629 },
3630
3631 [MV88E6171] = {
3632 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3633 .family = MV88E6XXX_FAMILY_6351,
3634 .name = "Marvell 88E6171",
3635 .num_databases = 4096,
3636 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003637 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003638 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3639 },
3640
3641 [MV88E6172] = {
3642 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3643 .family = MV88E6XXX_FAMILY_6352,
3644 .name = "Marvell 88E6172",
3645 .num_databases = 4096,
3646 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003647 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003648 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3649 },
3650
3651 [MV88E6175] = {
3652 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3653 .family = MV88E6XXX_FAMILY_6351,
3654 .name = "Marvell 88E6175",
3655 .num_databases = 4096,
3656 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003657 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003658 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3659 },
3660
3661 [MV88E6176] = {
3662 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3663 .family = MV88E6XXX_FAMILY_6352,
3664 .name = "Marvell 88E6176",
3665 .num_databases = 4096,
3666 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003667 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003668 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3669 },
3670
3671 [MV88E6185] = {
3672 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3673 .family = MV88E6XXX_FAMILY_6185,
3674 .name = "Marvell 88E6185",
3675 .num_databases = 256,
3676 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003677 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003678 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3679 },
3680
3681 [MV88E6240] = {
3682 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3683 .family = MV88E6XXX_FAMILY_6352,
3684 .name = "Marvell 88E6240",
3685 .num_databases = 4096,
3686 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003687 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003688 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3689 },
3690
3691 [MV88E6320] = {
3692 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3693 .family = MV88E6XXX_FAMILY_6320,
3694 .name = "Marvell 88E6320",
3695 .num_databases = 4096,
3696 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003697 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003698 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3699 },
3700
3701 [MV88E6321] = {
3702 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3703 .family = MV88E6XXX_FAMILY_6320,
3704 .name = "Marvell 88E6321",
3705 .num_databases = 4096,
3706 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003707 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003708 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3709 },
3710
3711 [MV88E6350] = {
3712 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3713 .family = MV88E6XXX_FAMILY_6351,
3714 .name = "Marvell 88E6350",
3715 .num_databases = 4096,
3716 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003717 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003718 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3719 },
3720
3721 [MV88E6351] = {
3722 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3723 .family = MV88E6XXX_FAMILY_6351,
3724 .name = "Marvell 88E6351",
3725 .num_databases = 4096,
3726 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003727 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003728 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3729 },
3730
3731 [MV88E6352] = {
3732 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3733 .family = MV88E6XXX_FAMILY_6352,
3734 .name = "Marvell 88E6352",
3735 .num_databases = 4096,
3736 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003737 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003738 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3739 },
3740};
3741
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003742static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003743{
Vivien Didelota439c062016-04-17 13:23:58 -04003744 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003745
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003746 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3747 if (mv88e6xxx_table[i].prod_num == prod_num)
3748 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003749
Vivien Didelotb9b37712015-10-30 19:39:48 -04003750 return NULL;
3751}
3752
Vivien Didelotfad09c72016-06-21 12:28:20 -04003753static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003754{
3755 const struct mv88e6xxx_info *info;
3756 int id, prod_num, rev;
3757
Vivien Didelotfad09c72016-06-21 12:28:20 -04003758 id = mv88e6xxx_reg_read(chip, chip->info->port_base_addr,
3759 PORT_SWITCH_ID);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003760 if (id < 0)
3761 return id;
3762
3763 prod_num = (id & 0xfff0) >> 4;
3764 rev = id & 0x000f;
3765
3766 info = mv88e6xxx_lookup_info(prod_num);
3767 if (!info)
3768 return -ENODEV;
3769
Vivien Didelotcaac8542016-06-20 13:14:09 -04003770 /* Update the compatible info with the probed one */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003771 chip->info = info;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003772
Vivien Didelotfad09c72016-06-21 12:28:20 -04003773 dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3774 chip->info->prod_num, chip->info->name, rev);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003775
3776 return 0;
3777}
3778
Vivien Didelotfad09c72016-06-21 12:28:20 -04003779static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
Vivien Didelot469d7292016-06-20 13:14:06 -04003780{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003781 struct mv88e6xxx_chip *chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003782
Vivien Didelotfad09c72016-06-21 12:28:20 -04003783 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3784 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003785 return NULL;
3786
Vivien Didelotfad09c72016-06-21 12:28:20 -04003787 chip->dev = dev;
Vivien Didelot469d7292016-06-20 13:14:06 -04003788
Vivien Didelotfad09c72016-06-21 12:28:20 -04003789 mutex_init(&chip->reg_lock);
Vivien Didelot469d7292016-06-20 13:14:06 -04003790
Vivien Didelotfad09c72016-06-21 12:28:20 -04003791 return chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003792}
3793
Vivien Didelotfad09c72016-06-21 12:28:20 -04003794static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003795 struct mii_bus *bus, int sw_addr)
3796{
3797 /* ADDR[0] pin is unavailable externally and considered zero */
3798 if (sw_addr & 0x1)
3799 return -EINVAL;
3800
Vivien Didelot914b32f2016-06-20 13:14:11 -04003801 if (sw_addr == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003802 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
3803 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_MULTI_CHIP))
3804 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
Vivien Didelot914b32f2016-06-20 13:14:11 -04003805 else
3806 return -EINVAL;
3807
Vivien Didelotfad09c72016-06-21 12:28:20 -04003808 chip->bus = bus;
3809 chip->sw_addr = sw_addr;
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003810
3811 return 0;
3812}
3813
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003814static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3815 struct device *host_dev, int sw_addr,
3816 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003817{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003818 struct mv88e6xxx_chip *chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003819 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003820 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003821
Vivien Didelota439c062016-04-17 13:23:58 -04003822 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003823 if (!bus)
3824 return NULL;
3825
Vivien Didelotfad09c72016-06-21 12:28:20 -04003826 chip = mv88e6xxx_alloc_chip(dsa_dev);
3827 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003828 return NULL;
3829
Vivien Didelotcaac8542016-06-20 13:14:09 -04003830 /* Legacy SMI probing will only support chips similar to 88E6085 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003831 chip->info = &mv88e6xxx_table[MV88E6085];
Vivien Didelotcaac8542016-06-20 13:14:09 -04003832
Vivien Didelotfad09c72016-06-21 12:28:20 -04003833 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003834 if (err)
3835 goto free;
3836
Vivien Didelotfad09c72016-06-21 12:28:20 -04003837 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003838 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003839 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003840
Vivien Didelotfad09c72016-06-21 12:28:20 -04003841 err = mv88e6xxx_mdio_register(chip, NULL);
Andrew Lunnb516d452016-06-04 21:17:06 +02003842 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003843 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003844
Vivien Didelotfad09c72016-06-21 12:28:20 -04003845 *priv = chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003846
Vivien Didelotfad09c72016-06-21 12:28:20 -04003847 return chip->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003848free:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003849 devm_kfree(dsa_dev, chip);
Vivien Didelot469d7292016-06-20 13:14:06 -04003850
3851 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003852}
3853
Vivien Didelot57d32312016-06-20 13:13:58 -04003854static struct dsa_switch_driver mv88e6xxx_switch_driver = {
Vivien Didelotf81ec902016-05-09 13:22:58 -04003855 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003856 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003857 .setup = mv88e6xxx_setup,
3858 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003859 .adjust_link = mv88e6xxx_adjust_link,
3860 .get_strings = mv88e6xxx_get_strings,
3861 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3862 .get_sset_count = mv88e6xxx_get_sset_count,
3863 .set_eee = mv88e6xxx_set_eee,
3864 .get_eee = mv88e6xxx_get_eee,
3865#ifdef CONFIG_NET_DSA_HWMON
3866 .get_temp = mv88e6xxx_get_temp,
3867 .get_temp_limit = mv88e6xxx_get_temp_limit,
3868 .set_temp_limit = mv88e6xxx_set_temp_limit,
3869 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3870#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003871 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003872 .get_eeprom = mv88e6xxx_get_eeprom,
3873 .set_eeprom = mv88e6xxx_set_eeprom,
3874 .get_regs_len = mv88e6xxx_get_regs_len,
3875 .get_regs = mv88e6xxx_get_regs,
3876 .port_bridge_join = mv88e6xxx_port_bridge_join,
3877 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3878 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3879 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3880 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3881 .port_vlan_add = mv88e6xxx_port_vlan_add,
3882 .port_vlan_del = mv88e6xxx_port_vlan_del,
3883 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3884 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3885 .port_fdb_add = mv88e6xxx_port_fdb_add,
3886 .port_fdb_del = mv88e6xxx_port_fdb_del,
3887 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3888};
3889
Vivien Didelotfad09c72016-06-21 12:28:20 -04003890static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003891 struct device_node *np)
3892{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003893 struct device *dev = chip->dev;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003894 struct dsa_switch *ds;
3895
3896 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3897 if (!ds)
3898 return -ENOMEM;
3899
3900 ds->dev = dev;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003901 ds->priv = chip;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003902 ds->drv = &mv88e6xxx_switch_driver;
3903
3904 dev_set_drvdata(dev, ds);
3905
3906 return dsa_register_switch(ds, np);
3907}
3908
Vivien Didelotfad09c72016-06-21 12:28:20 -04003909static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003910{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003911 dsa_unregister_switch(chip->ds);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003912}
3913
Vivien Didelot57d32312016-06-20 13:13:58 -04003914static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003915{
3916 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003917 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003918 const struct mv88e6xxx_info *compat_info;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003919 struct mv88e6xxx_chip *chip;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003920 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003921 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003922
Vivien Didelotcaac8542016-06-20 13:14:09 -04003923 compat_info = of_device_get_match_data(dev);
3924 if (!compat_info)
3925 return -EINVAL;
3926
Vivien Didelotfad09c72016-06-21 12:28:20 -04003927 chip = mv88e6xxx_alloc_chip(dev);
3928 if (!chip)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003929 return -ENOMEM;
3930
Vivien Didelotfad09c72016-06-21 12:28:20 -04003931 chip->info = compat_info;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003932
Vivien Didelotfad09c72016-06-21 12:28:20 -04003933 err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003934 if (err)
3935 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003936
Vivien Didelotfad09c72016-06-21 12:28:20 -04003937 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003938 if (err)
3939 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003940
Vivien Didelotfad09c72016-06-21 12:28:20 -04003941 chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
3942 if (IS_ERR(chip->reset))
3943 return PTR_ERR(chip->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02003944
Vivien Didelotfad09c72016-06-21 12:28:20 -04003945 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM) &&
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003946 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003947 chip->eeprom_len = eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003948
Vivien Didelotfad09c72016-06-21 12:28:20 -04003949 err = mv88e6xxx_mdio_register(chip, np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003950 if (err)
3951 return err;
3952
Vivien Didelotfad09c72016-06-21 12:28:20 -04003953 err = mv88e6xxx_register_switch(chip, np);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003954 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003955 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003956 return err;
3957 }
3958
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003959 return 0;
3960}
3961
3962static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3963{
3964 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003965 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003966
Vivien Didelotfad09c72016-06-21 12:28:20 -04003967 mv88e6xxx_unregister_switch(chip);
3968 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003969}
3970
3971static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04003972 {
3973 .compatible = "marvell,mv88e6085",
3974 .data = &mv88e6xxx_table[MV88E6085],
3975 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003976 { /* sentinel */ },
3977};
3978
3979MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3980
3981static struct mdio_driver mv88e6xxx_driver = {
3982 .probe = mv88e6xxx_probe,
3983 .remove = mv88e6xxx_remove,
3984 .mdiodrv.driver = {
3985 .name = "mv88e6085",
3986 .of_match_table = mv88e6xxx_of_match,
3987 },
3988};
3989
Ben Hutchings98e67302011-11-25 14:36:19 +00003990static int __init mv88e6xxx_init(void)
3991{
Vivien Didelotf81ec902016-05-09 13:22:58 -04003992 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003993 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003994}
3995module_init(mv88e6xxx_init);
3996
3997static void __exit mv88e6xxx_cleanup(void)
3998{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003999 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04004000 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004001}
4002module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00004003
4004MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4005MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4006MODULE_LICENSE("GPL");