blob: d18e5c8ad12fa266fa08a14143c7a809137312ed [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 Didelot51540412016-07-18 20:45:32 -04003154static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
3155 bool hask, u16 mask)
3156{
3157 const u16 port_mask = BIT(chip->info->num_ports) - 1;
3158 u16 val = (num << 12) | (mask & port_mask);
3159
3160 if (hask)
3161 val |= GLOBAL2_TRUNK_MASK_HASK;
3162
3163 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, val);
3164}
3165
3166static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
3167 u16 map)
3168{
3169 const u16 port_mask = BIT(chip->info->num_ports) - 1;
3170 u16 val = (id << 11) | (map & port_mask);
3171
3172 return mv88e6xxx_update(chip, REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, val);
3173}
3174
3175static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
3176{
3177 const u16 port_mask = BIT(chip->info->num_ports) - 1;
3178 int i, err;
3179
3180 /* Clear all eight possible Trunk Mask vectors */
3181 for (i = 0; i < 8; ++i) {
3182 err = mv88e6xxx_g2_trunk_mask_write(chip, i, false, port_mask);
3183 if (err)
3184 return err;
3185 }
3186
3187 /* Clear all sixteen possible Trunk ID routing vectors */
3188 for (i = 0; i < 16; ++i) {
3189 err = mv88e6xxx_g2_trunk_mapping_write(chip, i, 0);
3190 if (err)
3191 return err;
3192 }
3193
3194 return 0;
3195}
3196
Vivien Didelot97299342016-07-18 20:45:30 -04003197static int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip)
3198{
Vivien Didelot97299342016-07-18 20:45:30 -04003199 int err;
3200 int i;
3201
Vivien Didelot08a01262016-05-09 13:22:50 -04003202 /* Send all frames with destination addresses matching
3203 * 01:80:c2:00:00:0x to the CPU port.
3204 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003205 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X,
3206 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04003207 if (err)
3208 return err;
3209
3210 /* Ignore removed tag data on doubly tagged packets, disable
3211 * flow control messages, force flow control priority to the
3212 * highest, and send all special multicast frames to the CPU
3213 * port at the highest priority.
3214 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003215 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
Vivien Didelot08a01262016-05-09 13:22:50 -04003216 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3217 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3218 if (err)
3219 return err;
3220
3221 /* Program the DSA routing table. */
Vivien Didelotf22ab642016-07-18 20:45:31 -04003222 err = mv88e6xxx_g2_set_device_mapping(chip);
3223 if (err)
3224 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003225
Vivien Didelot51540412016-07-18 20:45:32 -04003226 /* Clear all trunk masks and mapping. */
3227 err = mv88e6xxx_g2_clear_trunk(chip);
3228 if (err)
3229 return err;
Vivien Didelot08a01262016-05-09 13:22:50 -04003230
Vivien Didelotfad09c72016-06-21 12:28:20 -04003231 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
3232 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
3233 mv88e6xxx_6320_family(chip)) {
Vivien Didelot08a01262016-05-09 13:22:50 -04003234 /* Send all frames with destination addresses matching
3235 * 01:80:c2:00:00:2x to the CPU port.
3236 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003237 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003238 GLOBAL2_MGMT_EN_2X, 0xffff);
3239 if (err)
3240 return err;
3241
3242 /* Initialise cross-chip port VLAN table to reset
3243 * defaults.
3244 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003245 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003246 GLOBAL2_PVT_ADDR, 0x9000);
3247 if (err)
3248 return err;
3249
3250 /* Clear the priority override table. */
3251 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003252 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003253 GLOBAL2_PRIO_OVERRIDE,
3254 0x8000 | (i << 8));
3255 if (err)
3256 return err;
3257 }
3258 }
3259
Vivien Didelotfad09c72016-06-21 12:28:20 -04003260 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
3261 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
3262 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
3263 mv88e6xxx_6320_family(chip)) {
Vivien Didelot08a01262016-05-09 13:22:50 -04003264 /* Disable ingress rate limiting by resetting all
3265 * ingress rate limit registers to their initial
3266 * state.
3267 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003268 for (i = 0; i < chip->info->num_ports; i++) {
3269 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL2,
Vivien Didelot08a01262016-05-09 13:22:50 -04003270 GLOBAL2_INGRESS_OP,
3271 0x9000 | (i << 8));
3272 if (err)
3273 return err;
3274 }
3275 }
3276
Vivien Didelot97299342016-07-18 20:45:30 -04003277 return 0;
Vivien Didelot08a01262016-05-09 13:22:50 -04003278}
3279
Vivien Didelotf81ec902016-05-09 13:22:58 -04003280static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003281{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003282 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003283 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003284 int i;
3285
Vivien Didelotfad09c72016-06-21 12:28:20 -04003286 chip->ds = ds;
3287 ds->slave_mii_bus = chip->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003288
Vivien Didelotfad09c72016-06-21 12:28:20 -04003289 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM))
3290 mutex_init(&chip->eeprom_mutex);
Vivien Didelotd24645b2016-05-09 13:22:41 -04003291
Vivien Didelotfad09c72016-06-21 12:28:20 -04003292 mutex_lock(&chip->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04003293
Vivien Didelotfad09c72016-06-21 12:28:20 -04003294 err = mv88e6xxx_switch_reset(chip);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003295 if (err)
3296 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003297
Vivien Didelot97299342016-07-18 20:45:30 -04003298 /* Setup Switch Port Registers */
3299 for (i = 0; i < chip->info->num_ports; i++) {
3300 err = mv88e6xxx_setup_port(chip, i);
3301 if (err)
3302 goto unlock;
3303 }
3304
3305 /* Setup Switch Global 1 Registers */
3306 err = mv88e6xxx_g1_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003307 if (err)
3308 goto unlock;
3309
Vivien Didelot97299342016-07-18 20:45:30 -04003310 /* Setup Switch Global 2 Registers */
3311 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
3312 err = mv88e6xxx_g2_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003313 if (err)
3314 goto unlock;
3315 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003316
Vivien Didelot6b17e862015-08-13 12:52:18 -04003317unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003318 mutex_unlock(&chip->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02003319
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003320 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003321}
3322
Vivien Didelot57d32312016-06-20 13:13:58 -04003323static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
3324 int reg)
Andrew Lunn491435852015-04-02 04:06:35 +02003325{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003326 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn491435852015-04-02 04:06:35 +02003327 int ret;
3328
Vivien Didelotfad09c72016-06-21 12:28:20 -04003329 mutex_lock(&chip->reg_lock);
3330 ret = _mv88e6xxx_mdio_page_read(chip, port, page, reg);
3331 mutex_unlock(&chip->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003332
Andrew Lunn491435852015-04-02 04:06:35 +02003333 return ret;
3334}
3335
Vivien Didelot57d32312016-06-20 13:13:58 -04003336static int mv88e6xxx_mdio_page_write(struct dsa_switch *ds, int port, int page,
3337 int reg, int val)
Andrew Lunn491435852015-04-02 04:06:35 +02003338{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003339 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn491435852015-04-02 04:06:35 +02003340 int ret;
3341
Vivien Didelotfad09c72016-06-21 12:28:20 -04003342 mutex_lock(&chip->reg_lock);
3343 ret = _mv88e6xxx_mdio_page_write(chip, port, page, reg, val);
3344 mutex_unlock(&chip->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003345
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003346 return ret;
3347}
3348
Vivien Didelotfad09c72016-06-21 12:28:20 -04003349static int mv88e6xxx_port_to_mdio_addr(struct mv88e6xxx_chip *chip, int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003350{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003351 if (port >= 0 && port < chip->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003352 return port;
3353 return -EINVAL;
3354}
3355
Andrew Lunnb516d452016-06-04 21:17:06 +02003356static int mv88e6xxx_mdio_read(struct mii_bus *bus, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003357{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003358 struct mv88e6xxx_chip *chip = bus->priv;
3359 int addr = mv88e6xxx_port_to_mdio_addr(chip, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003360 int ret;
3361
3362 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003363 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003364
Vivien Didelotfad09c72016-06-21 12:28:20 -04003365 mutex_lock(&chip->reg_lock);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003366
Vivien Didelotfad09c72016-06-21 12:28:20 -04003367 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3368 ret = mv88e6xxx_mdio_read_ppu(chip, addr, regnum);
3369 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SMI_PHY))
3370 ret = mv88e6xxx_mdio_read_indirect(chip, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003371 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04003372 ret = mv88e6xxx_mdio_read_direct(chip, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003373
Vivien Didelotfad09c72016-06-21 12:28:20 -04003374 mutex_unlock(&chip->reg_lock);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003375 return ret;
3376}
3377
Andrew Lunnb516d452016-06-04 21:17:06 +02003378static int mv88e6xxx_mdio_write(struct mii_bus *bus, int port, int regnum,
Andrew Lunn03a4a542016-06-04 21:17:05 +02003379 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003380{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003381 struct mv88e6xxx_chip *chip = bus->priv;
3382 int addr = mv88e6xxx_port_to_mdio_addr(chip, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003383 int ret;
3384
3385 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003386 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003387
Vivien Didelotfad09c72016-06-21 12:28:20 -04003388 mutex_lock(&chip->reg_lock);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003389
Vivien Didelotfad09c72016-06-21 12:28:20 -04003390 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3391 ret = mv88e6xxx_mdio_write_ppu(chip, addr, regnum, val);
3392 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_SMI_PHY))
3393 ret = mv88e6xxx_mdio_write_indirect(chip, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003394 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04003395 ret = mv88e6xxx_mdio_write_direct(chip, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003396
Vivien Didelotfad09c72016-06-21 12:28:20 -04003397 mutex_unlock(&chip->reg_lock);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003398 return ret;
3399}
3400
Vivien Didelotfad09c72016-06-21 12:28:20 -04003401static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
Andrew Lunnb516d452016-06-04 21:17:06 +02003402 struct device_node *np)
3403{
3404 static int index;
3405 struct mii_bus *bus;
3406 int err;
3407
Vivien Didelotfad09c72016-06-21 12:28:20 -04003408 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3409 mv88e6xxx_ppu_state_init(chip);
Andrew Lunnb516d452016-06-04 21:17:06 +02003410
3411 if (np)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003412 chip->mdio_np = of_get_child_by_name(np, "mdio");
Andrew Lunnb516d452016-06-04 21:17:06 +02003413
Vivien Didelotfad09c72016-06-21 12:28:20 -04003414 bus = devm_mdiobus_alloc(chip->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003415 if (!bus)
3416 return -ENOMEM;
3417
Vivien Didelotfad09c72016-06-21 12:28:20 -04003418 bus->priv = (void *)chip;
Andrew Lunnb516d452016-06-04 21:17:06 +02003419 if (np) {
3420 bus->name = np->full_name;
3421 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3422 } else {
3423 bus->name = "mv88e6xxx SMI";
3424 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3425 }
3426
3427 bus->read = mv88e6xxx_mdio_read;
3428 bus->write = mv88e6xxx_mdio_write;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003429 bus->parent = chip->dev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003430
Vivien Didelotfad09c72016-06-21 12:28:20 -04003431 if (chip->mdio_np)
3432 err = of_mdiobus_register(bus, chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003433 else
3434 err = mdiobus_register(bus);
3435 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003436 dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
Andrew Lunnb516d452016-06-04 21:17:06 +02003437 goto out;
3438 }
Vivien Didelotfad09c72016-06-21 12:28:20 -04003439 chip->mdio_bus = bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003440
3441 return 0;
3442
3443out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003444 if (chip->mdio_np)
3445 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003446
3447 return err;
3448}
3449
Vivien Didelotfad09c72016-06-21 12:28:20 -04003450static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
Andrew Lunnb516d452016-06-04 21:17:06 +02003451
3452{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003453 struct mii_bus *bus = chip->mdio_bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003454
3455 mdiobus_unregister(bus);
3456
Vivien Didelotfad09c72016-06-21 12:28:20 -04003457 if (chip->mdio_np)
3458 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003459}
3460
Guenter Roeckc22995c2015-07-25 09:42:28 -07003461#ifdef CONFIG_NET_DSA_HWMON
3462
3463static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3464{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003465 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003466 int ret;
3467 int val;
3468
3469 *temp = 0;
3470
Vivien Didelotfad09c72016-06-21 12:28:20 -04003471 mutex_lock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003472
Vivien Didelotfad09c72016-06-21 12:28:20 -04003473 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003474 if (ret < 0)
3475 goto error;
3476
3477 /* Enable temperature sensor */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003478 ret = mv88e6xxx_mdio_read_direct(chip, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003479 if (ret < 0)
3480 goto error;
3481
Vivien Didelotfad09c72016-06-21 12:28:20 -04003482 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003483 if (ret < 0)
3484 goto error;
3485
3486 /* Wait for temperature to stabilize */
3487 usleep_range(10000, 12000);
3488
Vivien Didelotfad09c72016-06-21 12:28:20 -04003489 val = mv88e6xxx_mdio_read_direct(chip, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003490 if (val < 0) {
3491 ret = val;
3492 goto error;
3493 }
3494
3495 /* Disable temperature sensor */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003496 ret = mv88e6xxx_mdio_write_direct(chip, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003497 if (ret < 0)
3498 goto error;
3499
3500 *temp = ((val & 0x1f) - 5) * 5;
3501
3502error:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003503 mv88e6xxx_mdio_write_direct(chip, 0x0, 0x16, 0x0);
3504 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003505 return ret;
3506}
3507
3508static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3509{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003510 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3511 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003512 int ret;
3513
3514 *temp = 0;
3515
Andrew Lunn03a4a542016-06-04 21:17:05 +02003516 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 27);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003517 if (ret < 0)
3518 return ret;
3519
3520 *temp = (ret & 0xff) - 25;
3521
3522 return 0;
3523}
3524
Vivien Didelotf81ec902016-05-09 13:22:58 -04003525static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003526{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003527 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003528
Vivien Didelotfad09c72016-06-21 12:28:20 -04003529 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
Vivien Didelot6594f612016-05-09 13:22:42 -04003530 return -EOPNOTSUPP;
3531
Vivien Didelotfad09c72016-06-21 12:28:20 -04003532 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003533 return mv88e63xx_get_temp(ds, temp);
3534
3535 return mv88e61xx_get_temp(ds, temp);
3536}
3537
Vivien Didelotf81ec902016-05-09 13:22:58 -04003538static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003539{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003540 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3541 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003542 int ret;
3543
Vivien Didelotfad09c72016-06-21 12:28:20 -04003544 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003545 return -EOPNOTSUPP;
3546
3547 *temp = 0;
3548
Andrew Lunn03a4a542016-06-04 21:17:05 +02003549 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003550 if (ret < 0)
3551 return ret;
3552
3553 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3554
3555 return 0;
3556}
3557
Vivien Didelotf81ec902016-05-09 13:22:58 -04003558static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003559{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003560 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3561 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003562 int ret;
3563
Vivien Didelotfad09c72016-06-21 12:28:20 -04003564 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003565 return -EOPNOTSUPP;
3566
Andrew Lunn03a4a542016-06-04 21:17:05 +02003567 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003568 if (ret < 0)
3569 return ret;
3570 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003571 return mv88e6xxx_mdio_page_write(ds, phy, 6, 26,
3572 (ret & 0xe0ff) | (temp << 8));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003573}
3574
Vivien Didelotf81ec902016-05-09 13:22:58 -04003575static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003576{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003577 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
3578 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003579 int ret;
3580
Vivien Didelotfad09c72016-06-21 12:28:20 -04003581 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003582 return -EOPNOTSUPP;
3583
3584 *alarm = false;
3585
Andrew Lunn03a4a542016-06-04 21:17:05 +02003586 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003587 if (ret < 0)
3588 return ret;
3589
3590 *alarm = !!(ret & 0x40);
3591
3592 return 0;
3593}
3594#endif /* CONFIG_NET_DSA_HWMON */
3595
Vivien Didelotf81ec902016-05-09 13:22:58 -04003596static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3597 [MV88E6085] = {
3598 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3599 .family = MV88E6XXX_FAMILY_6097,
3600 .name = "Marvell 88E6085",
3601 .num_databases = 4096,
3602 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003603 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003604 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3605 },
3606
3607 [MV88E6095] = {
3608 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3609 .family = MV88E6XXX_FAMILY_6095,
3610 .name = "Marvell 88E6095/88E6095F",
3611 .num_databases = 256,
3612 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003613 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003614 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3615 },
3616
3617 [MV88E6123] = {
3618 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3619 .family = MV88E6XXX_FAMILY_6165,
3620 .name = "Marvell 88E6123",
3621 .num_databases = 4096,
3622 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003623 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003624 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3625 },
3626
3627 [MV88E6131] = {
3628 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3629 .family = MV88E6XXX_FAMILY_6185,
3630 .name = "Marvell 88E6131",
3631 .num_databases = 256,
3632 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003633 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003634 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3635 },
3636
3637 [MV88E6161] = {
3638 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3639 .family = MV88E6XXX_FAMILY_6165,
3640 .name = "Marvell 88E6161",
3641 .num_databases = 4096,
3642 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003643 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003644 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3645 },
3646
3647 [MV88E6165] = {
3648 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3649 .family = MV88E6XXX_FAMILY_6165,
3650 .name = "Marvell 88E6165",
3651 .num_databases = 4096,
3652 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003653 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003654 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3655 },
3656
3657 [MV88E6171] = {
3658 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3659 .family = MV88E6XXX_FAMILY_6351,
3660 .name = "Marvell 88E6171",
3661 .num_databases = 4096,
3662 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003663 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003664 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3665 },
3666
3667 [MV88E6172] = {
3668 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3669 .family = MV88E6XXX_FAMILY_6352,
3670 .name = "Marvell 88E6172",
3671 .num_databases = 4096,
3672 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003673 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003674 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3675 },
3676
3677 [MV88E6175] = {
3678 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3679 .family = MV88E6XXX_FAMILY_6351,
3680 .name = "Marvell 88E6175",
3681 .num_databases = 4096,
3682 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003683 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003684 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3685 },
3686
3687 [MV88E6176] = {
3688 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3689 .family = MV88E6XXX_FAMILY_6352,
3690 .name = "Marvell 88E6176",
3691 .num_databases = 4096,
3692 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003693 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003694 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3695 },
3696
3697 [MV88E6185] = {
3698 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3699 .family = MV88E6XXX_FAMILY_6185,
3700 .name = "Marvell 88E6185",
3701 .num_databases = 256,
3702 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003703 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003704 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3705 },
3706
3707 [MV88E6240] = {
3708 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3709 .family = MV88E6XXX_FAMILY_6352,
3710 .name = "Marvell 88E6240",
3711 .num_databases = 4096,
3712 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003713 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003714 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3715 },
3716
3717 [MV88E6320] = {
3718 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3719 .family = MV88E6XXX_FAMILY_6320,
3720 .name = "Marvell 88E6320",
3721 .num_databases = 4096,
3722 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003723 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003724 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3725 },
3726
3727 [MV88E6321] = {
3728 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3729 .family = MV88E6XXX_FAMILY_6320,
3730 .name = "Marvell 88E6321",
3731 .num_databases = 4096,
3732 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003733 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003734 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3735 },
3736
3737 [MV88E6350] = {
3738 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3739 .family = MV88E6XXX_FAMILY_6351,
3740 .name = "Marvell 88E6350",
3741 .num_databases = 4096,
3742 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003743 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003744 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3745 },
3746
3747 [MV88E6351] = {
3748 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3749 .family = MV88E6XXX_FAMILY_6351,
3750 .name = "Marvell 88E6351",
3751 .num_databases = 4096,
3752 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003753 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003754 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3755 },
3756
3757 [MV88E6352] = {
3758 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3759 .family = MV88E6XXX_FAMILY_6352,
3760 .name = "Marvell 88E6352",
3761 .num_databases = 4096,
3762 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003763 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003764 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3765 },
3766};
3767
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003768static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003769{
Vivien Didelota439c062016-04-17 13:23:58 -04003770 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003771
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003772 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3773 if (mv88e6xxx_table[i].prod_num == prod_num)
3774 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003775
Vivien Didelotb9b37712015-10-30 19:39:48 -04003776 return NULL;
3777}
3778
Vivien Didelotfad09c72016-06-21 12:28:20 -04003779static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003780{
3781 const struct mv88e6xxx_info *info;
3782 int id, prod_num, rev;
3783
Vivien Didelotfad09c72016-06-21 12:28:20 -04003784 id = mv88e6xxx_reg_read(chip, chip->info->port_base_addr,
3785 PORT_SWITCH_ID);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003786 if (id < 0)
3787 return id;
3788
3789 prod_num = (id & 0xfff0) >> 4;
3790 rev = id & 0x000f;
3791
3792 info = mv88e6xxx_lookup_info(prod_num);
3793 if (!info)
3794 return -ENODEV;
3795
Vivien Didelotcaac8542016-06-20 13:14:09 -04003796 /* Update the compatible info with the probed one */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003797 chip->info = info;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003798
Vivien Didelotfad09c72016-06-21 12:28:20 -04003799 dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3800 chip->info->prod_num, chip->info->name, rev);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003801
3802 return 0;
3803}
3804
Vivien Didelotfad09c72016-06-21 12:28:20 -04003805static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
Vivien Didelot469d7292016-06-20 13:14:06 -04003806{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003807 struct mv88e6xxx_chip *chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003808
Vivien Didelotfad09c72016-06-21 12:28:20 -04003809 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3810 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003811 return NULL;
3812
Vivien Didelotfad09c72016-06-21 12:28:20 -04003813 chip->dev = dev;
Vivien Didelot469d7292016-06-20 13:14:06 -04003814
Vivien Didelotfad09c72016-06-21 12:28:20 -04003815 mutex_init(&chip->reg_lock);
Vivien Didelot469d7292016-06-20 13:14:06 -04003816
Vivien Didelotfad09c72016-06-21 12:28:20 -04003817 return chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003818}
3819
Vivien Didelotfad09c72016-06-21 12:28:20 -04003820static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003821 struct mii_bus *bus, int sw_addr)
3822{
3823 /* ADDR[0] pin is unavailable externally and considered zero */
3824 if (sw_addr & 0x1)
3825 return -EINVAL;
3826
Vivien Didelot914b32f2016-06-20 13:14:11 -04003827 if (sw_addr == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003828 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
3829 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_MULTI_CHIP))
3830 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
Vivien Didelot914b32f2016-06-20 13:14:11 -04003831 else
3832 return -EINVAL;
3833
Vivien Didelotfad09c72016-06-21 12:28:20 -04003834 chip->bus = bus;
3835 chip->sw_addr = sw_addr;
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003836
3837 return 0;
3838}
3839
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003840static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3841 struct device *host_dev, int sw_addr,
3842 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003843{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003844 struct mv88e6xxx_chip *chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003845 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003846 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003847
Vivien Didelota439c062016-04-17 13:23:58 -04003848 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003849 if (!bus)
3850 return NULL;
3851
Vivien Didelotfad09c72016-06-21 12:28:20 -04003852 chip = mv88e6xxx_alloc_chip(dsa_dev);
3853 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003854 return NULL;
3855
Vivien Didelotcaac8542016-06-20 13:14:09 -04003856 /* Legacy SMI probing will only support chips similar to 88E6085 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003857 chip->info = &mv88e6xxx_table[MV88E6085];
Vivien Didelotcaac8542016-06-20 13:14:09 -04003858
Vivien Didelotfad09c72016-06-21 12:28:20 -04003859 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003860 if (err)
3861 goto free;
3862
Vivien Didelotfad09c72016-06-21 12:28:20 -04003863 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003864 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003865 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003866
Vivien Didelotfad09c72016-06-21 12:28:20 -04003867 err = mv88e6xxx_mdio_register(chip, NULL);
Andrew Lunnb516d452016-06-04 21:17:06 +02003868 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003869 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003870
Vivien Didelotfad09c72016-06-21 12:28:20 -04003871 *priv = chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003872
Vivien Didelotfad09c72016-06-21 12:28:20 -04003873 return chip->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003874free:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003875 devm_kfree(dsa_dev, chip);
Vivien Didelot469d7292016-06-20 13:14:06 -04003876
3877 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003878}
3879
Vivien Didelot57d32312016-06-20 13:13:58 -04003880static struct dsa_switch_driver mv88e6xxx_switch_driver = {
Vivien Didelotf81ec902016-05-09 13:22:58 -04003881 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003882 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003883 .setup = mv88e6xxx_setup,
3884 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003885 .adjust_link = mv88e6xxx_adjust_link,
3886 .get_strings = mv88e6xxx_get_strings,
3887 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3888 .get_sset_count = mv88e6xxx_get_sset_count,
3889 .set_eee = mv88e6xxx_set_eee,
3890 .get_eee = mv88e6xxx_get_eee,
3891#ifdef CONFIG_NET_DSA_HWMON
3892 .get_temp = mv88e6xxx_get_temp,
3893 .get_temp_limit = mv88e6xxx_get_temp_limit,
3894 .set_temp_limit = mv88e6xxx_set_temp_limit,
3895 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3896#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003897 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003898 .get_eeprom = mv88e6xxx_get_eeprom,
3899 .set_eeprom = mv88e6xxx_set_eeprom,
3900 .get_regs_len = mv88e6xxx_get_regs_len,
3901 .get_regs = mv88e6xxx_get_regs,
3902 .port_bridge_join = mv88e6xxx_port_bridge_join,
3903 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3904 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3905 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3906 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3907 .port_vlan_add = mv88e6xxx_port_vlan_add,
3908 .port_vlan_del = mv88e6xxx_port_vlan_del,
3909 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3910 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3911 .port_fdb_add = mv88e6xxx_port_fdb_add,
3912 .port_fdb_del = mv88e6xxx_port_fdb_del,
3913 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3914};
3915
Vivien Didelotfad09c72016-06-21 12:28:20 -04003916static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003917 struct device_node *np)
3918{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003919 struct device *dev = chip->dev;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003920 struct dsa_switch *ds;
3921
3922 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3923 if (!ds)
3924 return -ENOMEM;
3925
3926 ds->dev = dev;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003927 ds->priv = chip;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003928 ds->drv = &mv88e6xxx_switch_driver;
3929
3930 dev_set_drvdata(dev, ds);
3931
3932 return dsa_register_switch(ds, np);
3933}
3934
Vivien Didelotfad09c72016-06-21 12:28:20 -04003935static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003936{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003937 dsa_unregister_switch(chip->ds);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003938}
3939
Vivien Didelot57d32312016-06-20 13:13:58 -04003940static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003941{
3942 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003943 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003944 const struct mv88e6xxx_info *compat_info;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003945 struct mv88e6xxx_chip *chip;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003946 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003947 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003948
Vivien Didelotcaac8542016-06-20 13:14:09 -04003949 compat_info = of_device_get_match_data(dev);
3950 if (!compat_info)
3951 return -EINVAL;
3952
Vivien Didelotfad09c72016-06-21 12:28:20 -04003953 chip = mv88e6xxx_alloc_chip(dev);
3954 if (!chip)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003955 return -ENOMEM;
3956
Vivien Didelotfad09c72016-06-21 12:28:20 -04003957 chip->info = compat_info;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003958
Vivien Didelotfad09c72016-06-21 12:28:20 -04003959 err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003960 if (err)
3961 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003962
Vivien Didelotfad09c72016-06-21 12:28:20 -04003963 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003964 if (err)
3965 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003966
Vivien Didelotfad09c72016-06-21 12:28:20 -04003967 chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
3968 if (IS_ERR(chip->reset))
3969 return PTR_ERR(chip->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02003970
Vivien Didelotfad09c72016-06-21 12:28:20 -04003971 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEPROM) &&
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003972 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003973 chip->eeprom_len = eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003974
Vivien Didelotfad09c72016-06-21 12:28:20 -04003975 err = mv88e6xxx_mdio_register(chip, np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003976 if (err)
3977 return err;
3978
Vivien Didelotfad09c72016-06-21 12:28:20 -04003979 err = mv88e6xxx_register_switch(chip, np);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003980 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003981 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003982 return err;
3983 }
3984
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003985 return 0;
3986}
3987
3988static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3989{
3990 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003991 struct mv88e6xxx_chip *chip = ds_to_priv(ds);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003992
Vivien Didelotfad09c72016-06-21 12:28:20 -04003993 mv88e6xxx_unregister_switch(chip);
3994 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003995}
3996
3997static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04003998 {
3999 .compatible = "marvell,mv88e6085",
4000 .data = &mv88e6xxx_table[MV88E6085],
4001 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004002 { /* sentinel */ },
4003};
4004
4005MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
4006
4007static struct mdio_driver mv88e6xxx_driver = {
4008 .probe = mv88e6xxx_probe,
4009 .remove = mv88e6xxx_remove,
4010 .mdiodrv.driver = {
4011 .name = "mv88e6085",
4012 .of_match_table = mv88e6xxx_of_match,
4013 },
4014};
4015
Ben Hutchings98e67302011-11-25 14:36:19 +00004016static int __init mv88e6xxx_init(void)
4017{
Vivien Didelotf81ec902016-05-09 13:22:58 -04004018 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004019 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004020}
4021module_init(mv88e6xxx_init);
4022
4023static void __exit mv88e6xxx_cleanup(void)
4024{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004025 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04004026 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004027}
4028module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00004029
4030MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4031MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4032MODULE_LICENSE("GPL");