blob: 25bd3fa744d931b544d7e41e9758e008bb3bc288 [file] [log] [blame]
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001/*
Vivien Didelot0d3cd4b2016-06-21 12:28:19 -04002 * Marvell 88e6xxx Ethernet switch single-chip support
3 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00004 * Copyright (c) 2008 Marvell Semiconductor
5 *
Vivien Didelotb8fee952015-08-13 12:52:19 -04006 * Copyright (c) 2015 CMC Electronics, Inc.
7 * Added support for VLAN Table Unit operations
8 *
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02009 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
10 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
Barry Grussling19b2f972013-01-08 16:05:54 +000017#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070018#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020019#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070020#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000021#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000022#include <linux/list.h>
Andrew Lunn14c7b3c2016-05-10 23:27:21 +020023#include <linux/mdio.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000024#include <linux/module.h>
Vivien Didelotcaac8542016-06-20 13:14:09 -040025#include <linux/of_device.h>
Andrew Lunnb516d452016-06-04 21:17:06 +020026#include <linux/of_mdio.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000027#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010028#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000029#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000030#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040031#include <net/switchdev.h>
Vivien Didelotec561272016-09-02 14:45:33 -040032
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000033#include "mv88e6xxx.h"
Vivien Didelotec561272016-09-02 14:45:33 -040034#include "global2.h"
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000035
Vivien Didelotfad09c72016-06-21 12:28:20 -040036static void assert_reg_lock(struct mv88e6xxx_chip *chip)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040037{
Vivien Didelotfad09c72016-06-21 12:28:20 -040038 if (unlikely(!mutex_is_locked(&chip->reg_lock))) {
39 dev_err(chip->dev, "Switch registers lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040040 dump_stack();
41 }
42}
43
Vivien Didelot914b32f2016-06-20 13:14:11 -040044/* The switch ADDR[4:1] configuration pins define the chip SMI device address
45 * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
46 *
47 * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
48 * is the only device connected to the SMI master. In this mode it responds to
49 * all 32 possible SMI addresses, and thus maps directly the internal devices.
50 *
51 * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
52 * multiple devices to share the SMI interface. In this mode it responds to only
53 * 2 registers, used to indirectly access the internal SMI devices.
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000054 */
Vivien Didelot914b32f2016-06-20 13:14:11 -040055
Vivien Didelotfad09c72016-06-21 12:28:20 -040056static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040057 int addr, int reg, u16 *val)
58{
Vivien Didelotfad09c72016-06-21 12:28:20 -040059 if (!chip->smi_ops)
Vivien Didelot914b32f2016-06-20 13:14:11 -040060 return -EOPNOTSUPP;
61
Vivien Didelotfad09c72016-06-21 12:28:20 -040062 return chip->smi_ops->read(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040063}
64
Vivien Didelotfad09c72016-06-21 12:28:20 -040065static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040066 int addr, int reg, u16 val)
67{
Vivien Didelotfad09c72016-06-21 12:28:20 -040068 if (!chip->smi_ops)
Vivien Didelot914b32f2016-06-20 13:14:11 -040069 return -EOPNOTSUPP;
70
Vivien Didelotfad09c72016-06-21 12:28:20 -040071 return chip->smi_ops->write(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040072}
73
Vivien Didelotfad09c72016-06-21 12:28:20 -040074static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040075 int addr, int reg, u16 *val)
76{
77 int ret;
78
Vivien Didelotfad09c72016-06-21 12:28:20 -040079 ret = mdiobus_read_nested(chip->bus, addr, reg);
Vivien Didelot914b32f2016-06-20 13:14:11 -040080 if (ret < 0)
81 return ret;
82
83 *val = ret & 0xffff;
84
85 return 0;
86}
87
Vivien Didelotfad09c72016-06-21 12:28:20 -040088static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040089 int addr, int reg, u16 val)
90{
91 int ret;
92
Vivien Didelotfad09c72016-06-21 12:28:20 -040093 ret = mdiobus_write_nested(chip->bus, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040094 if (ret < 0)
95 return ret;
96
97 return 0;
98}
99
100static const struct mv88e6xxx_ops mv88e6xxx_smi_single_chip_ops = {
101 .read = mv88e6xxx_smi_single_chip_read,
102 .write = mv88e6xxx_smi_single_chip_write,
103};
104
Vivien Didelotfad09c72016-06-21 12:28:20 -0400105static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000106{
107 int ret;
108 int i;
109
110 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400111 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000112 if (ret < 0)
113 return ret;
114
Andrew Lunncca8b132015-04-02 04:06:39 +0200115 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000116 return 0;
117 }
118
119 return -ETIMEDOUT;
120}
121
Vivien Didelotfad09c72016-06-21 12:28:20 -0400122static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400123 int addr, int reg, u16 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000124{
125 int ret;
126
Barry Grussling3675c8d2013-01-08 16:05:53 +0000127 /* Wait for the bus to become free. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400128 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000129 if (ret < 0)
130 return ret;
131
Barry Grussling3675c8d2013-01-08 16:05:53 +0000132 /* Transmit the read command. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400133 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
Neil Armstrong6e899e62015-10-22 10:37:53 +0200134 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000135 if (ret < 0)
136 return ret;
137
Barry Grussling3675c8d2013-01-08 16:05:53 +0000138 /* Wait for the read command to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400139 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000140 if (ret < 0)
141 return ret;
142
Barry Grussling3675c8d2013-01-08 16:05:53 +0000143 /* Read the data. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400144 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000145 if (ret < 0)
146 return ret;
147
Vivien Didelot914b32f2016-06-20 13:14:11 -0400148 *val = ret & 0xffff;
149
150 return 0;
151}
152
Vivien Didelotfad09c72016-06-21 12:28:20 -0400153static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400154 int addr, int reg, u16 val)
155{
156 int ret;
157
158 /* Wait for the bus to become free. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400159 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400160 if (ret < 0)
161 return ret;
162
163 /* Transmit the data to write. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400164 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400165 if (ret < 0)
166 return ret;
167
168 /* Transmit the write command. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400169 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400170 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
171 if (ret < 0)
172 return ret;
173
174 /* Wait for the write command to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400175 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400176 if (ret < 0)
177 return ret;
178
179 return 0;
180}
181
182static const struct mv88e6xxx_ops mv88e6xxx_smi_multi_chip_ops = {
183 .read = mv88e6xxx_smi_multi_chip_read,
184 .write = mv88e6xxx_smi_multi_chip_write,
185};
186
Vivien Didelotec561272016-09-02 14:45:33 -0400187int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
Vivien Didelot914b32f2016-06-20 13:14:11 -0400188{
189 int err;
190
Vivien Didelotfad09c72016-06-21 12:28:20 -0400191 assert_reg_lock(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400192
Vivien Didelotfad09c72016-06-21 12:28:20 -0400193 err = mv88e6xxx_smi_read(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400194 if (err)
195 return err;
196
Vivien Didelotfad09c72016-06-21 12:28:20 -0400197 dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelot914b32f2016-06-20 13:14:11 -0400198 addr, reg, *val);
199
200 return 0;
201}
202
Vivien Didelotec561272016-09-02 14:45:33 -0400203int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
Vivien Didelot914b32f2016-06-20 13:14:11 -0400204{
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
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200219int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
220 u16 *val)
221{
222 int addr = chip->info->port_base_addr + port;
223
224 return mv88e6xxx_read(chip, addr, reg, val);
225}
226
227int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
228 u16 val)
229{
230 int addr = chip->info->port_base_addr + port;
231
232 return mv88e6xxx_write(chip, addr, reg, val);
233}
234
Vivien Didelote57e5e72016-08-15 17:19:00 -0400235static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
236 int reg, u16 *val)
237{
238 int addr = phy; /* PHY devices addresses start at 0x0 */
239
240 if (!chip->phy_ops)
241 return -EOPNOTSUPP;
242
243 return chip->phy_ops->read(chip, addr, reg, val);
244}
245
246static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
247 int reg, u16 val)
248{
249 int addr = phy; /* PHY devices addresses start at 0x0 */
250
251 if (!chip->phy_ops)
252 return -EOPNOTSUPP;
253
254 return chip->phy_ops->write(chip, addr, reg, val);
255}
256
Vivien Didelot09cb7df2016-08-15 17:19:01 -0400257static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
258{
259 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE))
260 return -EOPNOTSUPP;
261
262 return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
263}
264
265static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
266{
267 int err;
268
269 /* Restore PHY page Copper 0x0 for access via the registered MDIO bus */
270 err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER);
271 if (unlikely(err)) {
272 dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n",
273 phy, err);
274 }
275}
276
277static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
278 u8 page, int reg, u16 *val)
279{
280 int err;
281
282 /* There is no paging for registers 22 */
283 if (reg == PHY_PAGE)
284 return -EINVAL;
285
286 err = mv88e6xxx_phy_page_get(chip, phy, page);
287 if (!err) {
288 err = mv88e6xxx_phy_read(chip, phy, reg, val);
289 mv88e6xxx_phy_page_put(chip, phy);
290 }
291
292 return err;
293}
294
295static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
296 u8 page, int reg, u16 val)
297{
298 int err;
299
300 /* There is no paging for registers 22 */
301 if (reg == PHY_PAGE)
302 return -EINVAL;
303
304 err = mv88e6xxx_phy_page_get(chip, phy, page);
305 if (!err) {
306 err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
307 mv88e6xxx_phy_page_put(chip, phy);
308 }
309
310 return err;
311}
312
313static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
314{
315 return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
316 reg, val);
317}
318
319static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
320{
321 return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
322 reg, val);
323}
324
Vivien Didelotec561272016-09-02 14:45:33 -0400325int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
Vivien Didelot2d79af62016-08-15 17:18:57 -0400326{
Andrew Lunn6441e6692016-08-19 00:01:55 +0200327 int i;
Vivien Didelot2d79af62016-08-15 17:18:57 -0400328
Andrew Lunn6441e6692016-08-19 00:01:55 +0200329 for (i = 0; i < 16; i++) {
Vivien Didelot2d79af62016-08-15 17:18:57 -0400330 u16 val;
331 int err;
332
333 err = mv88e6xxx_read(chip, addr, reg, &val);
334 if (err)
335 return err;
336
337 if (!(val & mask))
338 return 0;
339
340 usleep_range(1000, 2000);
341 }
342
Andrew Lunn30853552016-08-19 00:01:57 +0200343 dev_err(chip->dev, "Timeout while waiting for switch\n");
Vivien Didelot2d79af62016-08-15 17:18:57 -0400344 return -ETIMEDOUT;
345}
346
Vivien Didelotf22ab642016-07-18 20:45:31 -0400347/* Indirect write to single pointer-data register with an Update bit */
Vivien Didelotec561272016-09-02 14:45:33 -0400348int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
Vivien Didelotf22ab642016-07-18 20:45:31 -0400349{
350 u16 val;
Andrew Lunn0f02b4f2016-08-19 00:01:56 +0200351 int err;
Vivien Didelotf22ab642016-07-18 20:45:31 -0400352
353 /* Wait until the previous operation is completed */
Andrew Lunn0f02b4f2016-08-19 00:01:56 +0200354 err = mv88e6xxx_wait(chip, addr, reg, BIT(15));
355 if (err)
356 return err;
Vivien Didelotf22ab642016-07-18 20:45:31 -0400357
358 /* Set the Update bit to trigger a write operation */
359 val = BIT(15) | update;
360
361 return mv88e6xxx_write(chip, addr, reg, val);
362}
363
Vivien Didelotfad09c72016-06-21 12:28:20 -0400364static int _mv88e6xxx_reg_read(struct mv88e6xxx_chip *chip, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000365{
Vivien Didelot914b32f2016-06-20 13:14:11 -0400366 u16 val;
367 int err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000368
Vivien Didelotfad09c72016-06-21 12:28:20 -0400369 err = mv88e6xxx_read(chip, addr, reg, &val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400370 if (err)
371 return err;
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400372
Vivien Didelot914b32f2016-06-20 13:14:11 -0400373 return val;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000374}
375
Vivien Didelotfad09c72016-06-21 12:28:20 -0400376static int _mv88e6xxx_reg_write(struct mv88e6xxx_chip *chip, int addr,
Andrew Lunn158bc062016-04-28 21:24:06 -0400377 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000378{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400379 return mv88e6xxx_write(chip, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700380}
381
Vivien Didelotfad09c72016-06-21 12:28:20 -0400382static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000383{
384 int ret;
Andrew Lunn6441e6692016-08-19 00:01:55 +0200385 int i;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000386
Vivien Didelotfad09c72016-06-21 12:28:20 -0400387 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200388 if (ret < 0)
389 return ret;
390
Vivien Didelotfad09c72016-06-21 12:28:20 -0400391 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400392 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200393 if (ret)
394 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000395
Andrew Lunn6441e6692016-08-19 00:01:55 +0200396 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400397 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200398 if (ret < 0)
399 return ret;
400
Barry Grussling19b2f972013-01-08 16:05:54 +0000401 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200402 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
403 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000404 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000405 }
406
407 return -ETIMEDOUT;
408}
409
Vivien Didelotfad09c72016-06-21 12:28:20 -0400410static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000411{
Andrew Lunn6441e6692016-08-19 00:01:55 +0200412 int ret, err, i;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000413
Vivien Didelotfad09c72016-06-21 12:28:20 -0400414 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200415 if (ret < 0)
416 return ret;
417
Vivien Didelotfad09c72016-06-21 12:28:20 -0400418 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL,
Vivien Didelot762eb672016-06-04 21:16:54 +0200419 ret | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200420 if (err)
421 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000422
Andrew Lunn6441e6692016-08-19 00:01:55 +0200423 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400424 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200425 if (ret < 0)
426 return ret;
427
Barry Grussling19b2f972013-01-08 16:05:54 +0000428 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200429 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
430 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000431 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000432 }
433
434 return -ETIMEDOUT;
435}
436
437static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
438{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400439 struct mv88e6xxx_chip *chip;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000440
Vivien Didelotfad09c72016-06-21 12:28:20 -0400441 chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200442
Vivien Didelotfad09c72016-06-21 12:28:20 -0400443 mutex_lock(&chip->reg_lock);
Vivien Didelot762eb672016-06-04 21:16:54 +0200444
Vivien Didelotfad09c72016-06-21 12:28:20 -0400445 if (mutex_trylock(&chip->ppu_mutex)) {
446 if (mv88e6xxx_ppu_enable(chip) == 0)
447 chip->ppu_disabled = 0;
448 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000449 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200450
Vivien Didelotfad09c72016-06-21 12:28:20 -0400451 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000452}
453
454static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
455{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400456 struct mv88e6xxx_chip *chip = (void *)_ps;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000457
Vivien Didelotfad09c72016-06-21 12:28:20 -0400458 schedule_work(&chip->ppu_work);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000459}
460
Vivien Didelotfad09c72016-06-21 12:28:20 -0400461static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000462{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000463 int ret;
464
Vivien Didelotfad09c72016-06-21 12:28:20 -0400465 mutex_lock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000466
Barry Grussling3675c8d2013-01-08 16:05:53 +0000467 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000468 * we can access the PHY registers. If it was already
469 * disabled, cancel the timer that is going to re-enable
470 * it.
471 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400472 if (!chip->ppu_disabled) {
473 ret = mv88e6xxx_ppu_disable(chip);
Barry Grussling85686582013-01-08 16:05:56 +0000474 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400475 mutex_unlock(&chip->ppu_mutex);
Barry Grussling85686582013-01-08 16:05:56 +0000476 return ret;
477 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400478 chip->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000479 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400480 del_timer(&chip->ppu_timer);
Barry Grussling85686582013-01-08 16:05:56 +0000481 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000482 }
483
484 return ret;
485}
486
Vivien Didelotfad09c72016-06-21 12:28:20 -0400487static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000488{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000489 /* Schedule a timer to re-enable the PHY polling unit. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400490 mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
491 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000492}
493
Vivien Didelotfad09c72016-06-21 12:28:20 -0400494static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000495{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400496 mutex_init(&chip->ppu_mutex);
497 INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
498 init_timer(&chip->ppu_timer);
499 chip->ppu_timer.data = (unsigned long)chip;
500 chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000501}
502
Andrew Lunn930188c2016-08-22 16:01:03 +0200503static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip)
504{
505 del_timer_sync(&chip->ppu_timer);
506}
507
Vivien Didelote57e5e72016-08-15 17:19:00 -0400508static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
509 int reg, u16 *val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000510{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400511 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000512
Vivien Didelote57e5e72016-08-15 17:19:00 -0400513 err = mv88e6xxx_ppu_access_get(chip);
514 if (!err) {
515 err = mv88e6xxx_read(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400516 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000517 }
518
Vivien Didelote57e5e72016-08-15 17:19:00 -0400519 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000520}
521
Vivien Didelote57e5e72016-08-15 17:19:00 -0400522static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
523 int reg, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000524{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400525 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000526
Vivien Didelote57e5e72016-08-15 17:19:00 -0400527 err = mv88e6xxx_ppu_access_get(chip);
528 if (!err) {
529 err = mv88e6xxx_write(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400530 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000531 }
532
Vivien Didelote57e5e72016-08-15 17:19:00 -0400533 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000534}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000535
Vivien Didelote57e5e72016-08-15 17:19:00 -0400536static const struct mv88e6xxx_ops mv88e6xxx_phy_ppu_ops = {
537 .read = mv88e6xxx_phy_ppu_read,
538 .write = mv88e6xxx_phy_ppu_write,
539};
540
Vivien Didelotfad09c72016-06-21 12:28:20 -0400541static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200542{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400543 return chip->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200544}
545
Vivien Didelotfad09c72016-06-21 12:28:20 -0400546static bool mv88e6xxx_6095_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_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200549}
550
Vivien Didelotfad09c72016-06-21 12:28:20 -0400551static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200552{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400553 return chip->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200554}
555
Vivien Didelotfad09c72016-06-21 12:28:20 -0400556static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200557{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400558 return chip->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200559}
560
Vivien Didelotfad09c72016-06-21 12:28:20 -0400561static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200562{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400563 return chip->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200564}
565
Vivien Didelotfad09c72016-06-21 12:28:20 -0400566static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700567{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400568 return chip->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700569}
570
Vivien Didelotfad09c72016-06-21 12:28:20 -0400571static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200572{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400573 return chip->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200574}
575
Vivien Didelotfad09c72016-06-21 12:28:20 -0400576static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200577{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400578 return chip->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200579}
580
Vivien Didelotfad09c72016-06-21 12:28:20 -0400581static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400582{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400583 return chip->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400584}
585
Vivien Didelotfad09c72016-06-21 12:28:20 -0400586static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_chip *chip)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400587{
588 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400589 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
590 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400591 return true;
592
593 return false;
594}
595
Andrew Lunndea87022015-08-31 15:56:47 +0200596/* We expect the switch to perform auto negotiation if there is a real
597 * phy. However, in the case of a fixed link phy, we force the port
598 * settings from the fixed link settings.
599 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400600static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
601 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200602{
Vivien Didelot04bed142016-08-31 18:06:13 -0400603 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200604 u16 reg;
605 int err;
Andrew Lunndea87022015-08-31 15:56:47 +0200606
607 if (!phy_is_pseudo_fixed_link(phydev))
608 return;
609
Vivien Didelotfad09c72016-06-21 12:28:20 -0400610 mutex_lock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200611
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200612 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
613 if (err)
Andrew Lunndea87022015-08-31 15:56:47 +0200614 goto out;
615
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200616 reg &= ~(PORT_PCS_CTRL_LINK_UP |
617 PORT_PCS_CTRL_FORCE_LINK |
618 PORT_PCS_CTRL_DUPLEX_FULL |
619 PORT_PCS_CTRL_FORCE_DUPLEX |
620 PORT_PCS_CTRL_UNFORCED);
Andrew Lunndea87022015-08-31 15:56:47 +0200621
622 reg |= PORT_PCS_CTRL_FORCE_LINK;
623 if (phydev->link)
Vivien Didelot57d32312016-06-20 13:13:58 -0400624 reg |= PORT_PCS_CTRL_LINK_UP;
Andrew Lunndea87022015-08-31 15:56:47 +0200625
Vivien Didelotfad09c72016-06-21 12:28:20 -0400626 if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200627 goto out;
628
629 switch (phydev->speed) {
630 case SPEED_1000:
631 reg |= PORT_PCS_CTRL_1000;
632 break;
633 case SPEED_100:
634 reg |= PORT_PCS_CTRL_100;
635 break;
636 case SPEED_10:
637 reg |= PORT_PCS_CTRL_10;
638 break;
639 default:
640 pr_info("Unknown speed");
641 goto out;
642 }
643
644 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
645 if (phydev->duplex == DUPLEX_FULL)
646 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
647
Vivien Didelotfad09c72016-06-21 12:28:20 -0400648 if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
649 (port >= chip->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200650 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
651 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
652 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
653 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
654 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
655 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
656 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
657 }
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200658 mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200659
660out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400661 mutex_unlock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200662}
663
Vivien Didelotfad09c72016-06-21 12:28:20 -0400664static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000665{
666 int ret;
667 int i;
668
669 for (i = 0; i < 10; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400670 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200671 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000672 return 0;
673 }
674
675 return -ETIMEDOUT;
676}
677
Vivien Didelotfad09c72016-06-21 12:28:20 -0400678static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000679{
680 int ret;
681
Vivien Didelotfad09c72016-06-21 12:28:20 -0400682 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200683 port = (port + 1) << 5;
684
Barry Grussling3675c8d2013-01-08 16:05:53 +0000685 /* Snapshot the hardware statistics counters for this port. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400686 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200687 GLOBAL_STATS_OP_CAPTURE_PORT |
688 GLOBAL_STATS_OP_HIST_RX_TX | port);
689 if (ret < 0)
690 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000691
Barry Grussling3675c8d2013-01-08 16:05:53 +0000692 /* Wait for the snapshotting to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400693 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000694 if (ret < 0)
695 return ret;
696
697 return 0;
698}
699
Vivien Didelotfad09c72016-06-21 12:28:20 -0400700static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400701 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000702{
703 u32 _val;
704 int ret;
705
706 *val = 0;
707
Vivien Didelotfad09c72016-06-21 12:28:20 -0400708 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200709 GLOBAL_STATS_OP_READ_CAPTURED |
710 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000711 if (ret < 0)
712 return;
713
Vivien Didelotfad09c72016-06-21 12:28:20 -0400714 ret = _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000715 if (ret < 0)
716 return;
717
Vivien Didelotfad09c72016-06-21 12:28:20 -0400718 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000719 if (ret < 0)
720 return;
721
722 _val = ret << 16;
723
Vivien Didelotfad09c72016-06-21 12:28:20 -0400724 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000725 if (ret < 0)
726 return;
727
728 *val = _val | ret;
729}
730
Andrew Lunne413e7e2015-04-02 04:06:38 +0200731static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100732 { "in_good_octets", 8, 0x00, BANK0, },
733 { "in_bad_octets", 4, 0x02, BANK0, },
734 { "in_unicast", 4, 0x04, BANK0, },
735 { "in_broadcasts", 4, 0x06, BANK0, },
736 { "in_multicasts", 4, 0x07, BANK0, },
737 { "in_pause", 4, 0x16, BANK0, },
738 { "in_undersize", 4, 0x18, BANK0, },
739 { "in_fragments", 4, 0x19, BANK0, },
740 { "in_oversize", 4, 0x1a, BANK0, },
741 { "in_jabber", 4, 0x1b, BANK0, },
742 { "in_rx_error", 4, 0x1c, BANK0, },
743 { "in_fcs_error", 4, 0x1d, BANK0, },
744 { "out_octets", 8, 0x0e, BANK0, },
745 { "out_unicast", 4, 0x10, BANK0, },
746 { "out_broadcasts", 4, 0x13, BANK0, },
747 { "out_multicasts", 4, 0x12, BANK0, },
748 { "out_pause", 4, 0x15, BANK0, },
749 { "excessive", 4, 0x11, BANK0, },
750 { "collisions", 4, 0x1e, BANK0, },
751 { "deferred", 4, 0x05, BANK0, },
752 { "single", 4, 0x14, BANK0, },
753 { "multiple", 4, 0x17, BANK0, },
754 { "out_fcs_error", 4, 0x03, BANK0, },
755 { "late", 4, 0x1f, BANK0, },
756 { "hist_64bytes", 4, 0x08, BANK0, },
757 { "hist_65_127bytes", 4, 0x09, BANK0, },
758 { "hist_128_255bytes", 4, 0x0a, BANK0, },
759 { "hist_256_511bytes", 4, 0x0b, BANK0, },
760 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
761 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
762 { "sw_in_discards", 4, 0x10, PORT, },
763 { "sw_in_filtered", 2, 0x12, PORT, },
764 { "sw_out_filtered", 2, 0x13, PORT, },
765 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
766 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
767 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
768 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
769 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
770 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
771 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
772 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
773 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
774 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
775 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
776 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
777 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
778 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
779 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
780 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
781 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
782 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
783 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
784 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
785 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
786 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
787 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
788 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
789 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
790 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200791};
792
Vivien Didelotfad09c72016-06-21 12:28:20 -0400793static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100794 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200795{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100796 switch (stat->type) {
797 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200798 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100799 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400800 return mv88e6xxx_6320_family(chip);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100801 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400802 return mv88e6xxx_6095_family(chip) ||
803 mv88e6xxx_6185_family(chip) ||
804 mv88e6xxx_6097_family(chip) ||
805 mv88e6xxx_6165_family(chip) ||
806 mv88e6xxx_6351_family(chip) ||
807 mv88e6xxx_6352_family(chip);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200808 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100809 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000810}
811
Vivien Didelotfad09c72016-06-21 12:28:20 -0400812static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100813 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200814 int port)
815{
Andrew Lunn80c46272015-06-20 18:42:30 +0200816 u32 low;
817 u32 high = 0;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200818 int err;
819 u16 reg;
Andrew Lunn80c46272015-06-20 18:42:30 +0200820 u64 value;
821
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100822 switch (s->type) {
823 case PORT:
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200824 err = mv88e6xxx_port_read(chip, port, s->reg, &reg);
825 if (err)
Andrew Lunn80c46272015-06-20 18:42:30 +0200826 return UINT64_MAX;
827
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200828 low = reg;
Andrew Lunn80c46272015-06-20 18:42:30 +0200829 if (s->sizeof_stat == 4) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200830 err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
831 if (err)
Andrew Lunn80c46272015-06-20 18:42:30 +0200832 return UINT64_MAX;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200833 high = reg;
Andrew Lunn80c46272015-06-20 18:42:30 +0200834 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100835 break;
836 case BANK0:
837 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400838 _mv88e6xxx_stats_read(chip, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200839 if (s->sizeof_stat == 8)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400840 _mv88e6xxx_stats_read(chip, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200841 }
842 value = (((u64)high) << 16) | low;
843 return value;
844}
845
Vivien Didelotf81ec902016-05-09 13:22:58 -0400846static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
847 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100848{
Vivien Didelot04bed142016-08-31 18:06:13 -0400849 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100850 struct mv88e6xxx_hw_stat *stat;
851 int i, j;
852
853 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
854 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400855 if (mv88e6xxx_has_stat(chip, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100856 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
857 ETH_GSTRING_LEN);
858 j++;
859 }
860 }
861}
862
Vivien Didelotf81ec902016-05-09 13:22:58 -0400863static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100864{
Vivien Didelot04bed142016-08-31 18:06:13 -0400865 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100866 struct mv88e6xxx_hw_stat *stat;
867 int i, j;
868
869 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
870 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400871 if (mv88e6xxx_has_stat(chip, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100872 j++;
873 }
874 return j;
875}
876
Vivien Didelotf81ec902016-05-09 13:22:58 -0400877static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
878 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000879{
Vivien Didelot04bed142016-08-31 18:06:13 -0400880 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100881 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000882 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100883 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000884
Vivien Didelotfad09c72016-06-21 12:28:20 -0400885 mutex_lock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000886
Vivien Didelotfad09c72016-06-21 12:28:20 -0400887 ret = _mv88e6xxx_stats_snapshot(chip, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000888 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400889 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000890 return;
891 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100892 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
893 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400894 if (mv88e6xxx_has_stat(chip, stat)) {
895 data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100896 j++;
897 }
898 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000899
Vivien Didelotfad09c72016-06-21 12:28:20 -0400900 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000901}
Ben Hutchings98e67302011-11-25 14:36:19 +0000902
Vivien Didelotf81ec902016-05-09 13:22:58 -0400903static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700904{
905 return 32 * sizeof(u16);
906}
907
Vivien Didelotf81ec902016-05-09 13:22:58 -0400908static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
909 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700910{
Vivien Didelot04bed142016-08-31 18:06:13 -0400911 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200912 int err;
913 u16 reg;
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700914 u16 *p = _p;
915 int i;
916
917 regs->version = 0;
918
919 memset(p, 0xff, 32 * sizeof(u16));
920
Vivien Didelotfad09c72016-06-21 12:28:20 -0400921 mutex_lock(&chip->reg_lock);
Vivien Didelot23062512016-05-09 13:22:45 -0400922
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700923 for (i = 0; i < 32; i++) {
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700924
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200925 err = mv88e6xxx_port_read(chip, port, i, &reg);
926 if (!err)
927 p[i] = reg;
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700928 }
Vivien Didelot23062512016-05-09 13:22:45 -0400929
Vivien Didelotfad09c72016-06-21 12:28:20 -0400930 mutex_unlock(&chip->reg_lock);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700931}
932
Vivien Didelotfad09c72016-06-21 12:28:20 -0400933static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700934{
Vivien Didelot2d79af62016-08-15 17:18:57 -0400935 return mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_ATU_OP,
936 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700937}
938
Vivien Didelotf81ec902016-05-09 13:22:58 -0400939static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
940 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -0800941{
Vivien Didelot04bed142016-08-31 18:06:13 -0400942 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot9c938292016-08-15 17:19:02 -0400943 u16 reg;
944 int err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800945
Vivien Didelotfad09c72016-06-21 12:28:20 -0400946 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -0400947 return -EOPNOTSUPP;
948
Vivien Didelotfad09c72016-06-21 12:28:20 -0400949 mutex_lock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200950
Vivien Didelot9c938292016-08-15 17:19:02 -0400951 err = mv88e6xxx_phy_read(chip, port, 16, &reg);
952 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200953 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800954
955 e->eee_enabled = !!(reg & 0x0200);
956 e->tx_lpi_enabled = !!(reg & 0x0100);
957
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200958 err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
Vivien Didelot9c938292016-08-15 17:19:02 -0400959 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200960 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800961
Andrew Lunncca8b132015-04-02 04:06:39 +0200962 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200963out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400964 mutex_unlock(&chip->reg_lock);
Vivien Didelot9c938292016-08-15 17:19:02 -0400965
966 return err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800967}
968
Vivien Didelotf81ec902016-05-09 13:22:58 -0400969static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
970 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -0800971{
Vivien Didelot04bed142016-08-31 18:06:13 -0400972 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot9c938292016-08-15 17:19:02 -0400973 u16 reg;
974 int err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800975
Vivien Didelotfad09c72016-06-21 12:28:20 -0400976 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -0400977 return -EOPNOTSUPP;
978
Vivien Didelotfad09c72016-06-21 12:28:20 -0400979 mutex_lock(&chip->reg_lock);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800980
Vivien Didelot9c938292016-08-15 17:19:02 -0400981 err = mv88e6xxx_phy_read(chip, port, 16, &reg);
982 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200983 goto out;
984
Vivien Didelot9c938292016-08-15 17:19:02 -0400985 reg &= ~0x0300;
Andrew Lunn2f40c692015-04-02 04:06:37 +0200986 if (e->eee_enabled)
987 reg |= 0x0200;
988 if (e->tx_lpi_enabled)
989 reg |= 0x0100;
990
Vivien Didelot9c938292016-08-15 17:19:02 -0400991 err = mv88e6xxx_phy_write(chip, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200992out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400993 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200994
Vivien Didelot9c938292016-08-15 17:19:02 -0400995 return err;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800996}
997
Vivien Didelotfad09c72016-06-21 12:28:20 -0400998static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700999{
1000 int ret;
1001
Vivien Didelotfad09c72016-06-21 12:28:20 -04001002 if (mv88e6xxx_has_fid_reg(chip)) {
1003 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_FID,
1004 fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001005 if (ret < 0)
1006 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001007 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001008 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001009 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001010 if (ret < 0)
1011 return ret;
1012
Vivien Didelotfad09c72016-06-21 12:28:20 -04001013 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001014 (ret & 0xfff) |
1015 ((fid << 8) & 0xf000));
1016 if (ret < 0)
1017 return ret;
1018
1019 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1020 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001021 }
1022
Vivien Didelotfad09c72016-06-21 12:28:20 -04001023 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001024 if (ret < 0)
1025 return ret;
1026
Vivien Didelotfad09c72016-06-21 12:28:20 -04001027 return _mv88e6xxx_atu_wait(chip);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001028}
1029
Vivien Didelotfad09c72016-06-21 12:28:20 -04001030static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot37705b72015-09-04 14:34:11 -04001031 struct mv88e6xxx_atu_entry *entry)
1032{
1033 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1034
1035 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1036 unsigned int mask, shift;
1037
1038 if (entry->trunk) {
1039 data |= GLOBAL_ATU_DATA_TRUNK;
1040 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1041 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1042 } else {
1043 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1044 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1045 }
1046
1047 data |= (entry->portv_trunkid << shift) & mask;
1048 }
1049
Vivien Didelotfad09c72016-06-21 12:28:20 -04001050 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001051}
1052
Vivien Didelotfad09c72016-06-21 12:28:20 -04001053static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001054 struct mv88e6xxx_atu_entry *entry,
1055 bool static_too)
1056{
1057 int op;
1058 int err;
1059
Vivien Didelotfad09c72016-06-21 12:28:20 -04001060 err = _mv88e6xxx_atu_wait(chip);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001061 if (err)
1062 return err;
1063
Vivien Didelotfad09c72016-06-21 12:28:20 -04001064 err = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001065 if (err)
1066 return err;
1067
1068 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001069 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1070 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1071 } else {
1072 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1073 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1074 }
1075
Vivien Didelotfad09c72016-06-21 12:28:20 -04001076 return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001077}
1078
Vivien Didelotfad09c72016-06-21 12:28:20 -04001079static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001080 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001081{
1082 struct mv88e6xxx_atu_entry entry = {
1083 .fid = fid,
1084 .state = 0, /* EntryState bits must be 0 */
1085 };
1086
Vivien Didelotfad09c72016-06-21 12:28:20 -04001087 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001088}
1089
Vivien Didelotfad09c72016-06-21 12:28:20 -04001090static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001091 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001092{
1093 struct mv88e6xxx_atu_entry entry = {
1094 .trunk = false,
1095 .fid = fid,
1096 };
1097
1098 /* EntryState bits must be 0xF */
1099 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1100
1101 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1102 entry.portv_trunkid = (to_port & 0x0f) << 4;
1103 entry.portv_trunkid |= from_port & 0x0f;
1104
Vivien Didelotfad09c72016-06-21 12:28:20 -04001105 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001106}
1107
Vivien Didelotfad09c72016-06-21 12:28:20 -04001108static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001109 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001110{
1111 /* Destination port 0xF means remove the entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001112 return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001113}
1114
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001115static const char * const mv88e6xxx_port_state_names[] = {
1116 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1117 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1118 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1119 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1120};
1121
Vivien Didelotfad09c72016-06-21 12:28:20 -04001122static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001123 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001124{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001125 struct dsa_switch *ds = chip->ds;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001126 u16 reg;
1127 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001128 u8 oldstate;
1129
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001130 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
1131 if (err)
1132 return err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001133
Andrew Lunncca8b132015-04-02 04:06:39 +02001134 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001135
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001136 if (oldstate != state) {
1137 /* Flush forwarding database if we're moving a port
1138 * from Learning or Forwarding state to Disabled or
1139 * Blocking or Listening state.
1140 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001141 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
Vivien Didelot57d32312016-06-20 13:13:58 -04001142 oldstate == PORT_CONTROL_STATE_FORWARDING) &&
1143 (state == PORT_CONTROL_STATE_DISABLED ||
1144 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001145 err = _mv88e6xxx_atu_remove(chip, 0, port, false);
1146 if (err)
1147 return err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001148 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001149
Andrew Lunncca8b132015-04-02 04:06:39 +02001150 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001151 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
1152 if (err)
1153 return err;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001154
Andrew Lunnc8b09802016-06-04 21:16:57 +02001155 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001156 mv88e6xxx_port_state_names[state],
1157 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001158 }
1159
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001160 return err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001161}
1162
Vivien Didelotfad09c72016-06-21 12:28:20 -04001163static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001164{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001165 struct net_device *bridge = chip->ports[port].bridge_dev;
1166 const u16 mask = (1 << chip->info->num_ports) - 1;
1167 struct dsa_switch *ds = chip->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001168 u16 output_ports = 0;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001169 u16 reg;
1170 int err;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001171 int i;
1172
1173 /* allow CPU port or DSA link(s) to send frames to every port */
1174 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1175 output_ports = mask;
1176 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001177 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001178 /* allow sending frames to every group member */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001179 if (bridge && chip->ports[i].bridge_dev == bridge)
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001180 output_ports |= BIT(i);
1181
1182 /* allow sending frames to CPU port and DSA link(s) */
1183 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1184 output_ports |= BIT(i);
1185 }
1186 }
1187
1188 /* prevent frames from going back out of the port they came in on */
1189 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001190
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001191 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
1192 if (err)
1193 return err;
Vivien Didelotede80982015-10-11 18:08:35 -04001194
1195 reg &= ~mask;
1196 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001197
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001198 return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001199}
1200
Vivien Didelotf81ec902016-05-09 13:22:58 -04001201static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1202 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001203{
Vivien Didelot04bed142016-08-31 18:06:13 -04001204 struct mv88e6xxx_chip *chip = ds->priv;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001205 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001206 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001207
1208 switch (state) {
1209 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001210 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001211 break;
1212 case BR_STATE_BLOCKING:
1213 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001214 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001215 break;
1216 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001217 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001218 break;
1219 case BR_STATE_FORWARDING:
1220 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001221 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001222 break;
1223 }
1224
Vivien Didelotfad09c72016-06-21 12:28:20 -04001225 mutex_lock(&chip->reg_lock);
1226 err = _mv88e6xxx_port_state(chip, port, stp_state);
1227 mutex_unlock(&chip->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001228
1229 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001230 netdev_err(ds->ports[port].netdev,
1231 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001232 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001233}
1234
Vivien Didelotfad09c72016-06-21 12:28:20 -04001235static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001236 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001237{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001238 struct dsa_switch *ds = chip->ds;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001239 u16 pvid, reg;
1240 int err;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001241
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001242 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
1243 if (err)
1244 return err;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001245
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001246 pvid = reg & PORT_DEFAULT_VLAN_MASK;
Vivien Didelot5da96032016-03-07 18:24:39 -05001247
1248 if (new) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001249 reg &= ~PORT_DEFAULT_VLAN_MASK;
1250 reg |= *new & PORT_DEFAULT_VLAN_MASK;
Vivien Didelot5da96032016-03-07 18:24:39 -05001251
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001252 err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
1253 if (err)
1254 return err;
Vivien Didelot5da96032016-03-07 18:24:39 -05001255
Andrew Lunnc8b09802016-06-04 21:16:57 +02001256 netdev_dbg(ds->ports[port].netdev,
1257 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001258 }
1259
1260 if (old)
1261 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001262
1263 return 0;
1264}
1265
Vivien Didelotfad09c72016-06-21 12:28:20 -04001266static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001267 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001268{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001269 return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001270}
1271
Vivien Didelotfad09c72016-06-21 12:28:20 -04001272static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001273 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001274{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001275 return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001276}
1277
Vivien Didelotfad09c72016-06-21 12:28:20 -04001278static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001279{
Vivien Didelot2d79af62016-08-15 17:18:57 -04001280 return mv88e6xxx_wait(chip, REG_GLOBAL, GLOBAL_VTU_OP,
1281 GLOBAL_VTU_OP_BUSY);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001282}
1283
Vivien Didelotfad09c72016-06-21 12:28:20 -04001284static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001285{
1286 int ret;
1287
Vivien Didelotfad09c72016-06-21 12:28:20 -04001288 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001289 if (ret < 0)
1290 return ret;
1291
Vivien Didelotfad09c72016-06-21 12:28:20 -04001292 return _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001293}
1294
Vivien Didelotfad09c72016-06-21 12:28:20 -04001295static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001296{
1297 int ret;
1298
Vivien Didelotfad09c72016-06-21 12:28:20 -04001299 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001300 if (ret < 0)
1301 return ret;
1302
Vivien Didelotfad09c72016-06-21 12:28:20 -04001303 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001304}
1305
Vivien Didelotfad09c72016-06-21 12:28:20 -04001306static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001307 struct mv88e6xxx_vtu_stu_entry *entry,
1308 unsigned int nibble_offset)
1309{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001310 u16 regs[3];
1311 int i;
1312 int ret;
1313
1314 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001315 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001316 GLOBAL_VTU_DATA_0_3 + i);
1317 if (ret < 0)
1318 return ret;
1319
1320 regs[i] = ret;
1321 }
1322
Vivien Didelotfad09c72016-06-21 12:28:20 -04001323 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001324 unsigned int shift = (i % 4) * 4 + nibble_offset;
1325 u16 reg = regs[i / 4];
1326
1327 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1328 }
1329
1330 return 0;
1331}
1332
Vivien Didelotfad09c72016-06-21 12:28:20 -04001333static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001334 struct mv88e6xxx_vtu_stu_entry *entry)
1335{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001336 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001337}
1338
Vivien Didelotfad09c72016-06-21 12:28:20 -04001339static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001340 struct mv88e6xxx_vtu_stu_entry *entry)
1341{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001342 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001343}
1344
Vivien Didelotfad09c72016-06-21 12:28:20 -04001345static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001346 struct mv88e6xxx_vtu_stu_entry *entry,
1347 unsigned int nibble_offset)
1348{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001349 u16 regs[3] = { 0 };
1350 int i;
1351 int ret;
1352
Vivien Didelotfad09c72016-06-21 12:28:20 -04001353 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001354 unsigned int shift = (i % 4) * 4 + nibble_offset;
1355 u8 data = entry->data[i];
1356
1357 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1358 }
1359
1360 for (i = 0; i < 3; ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001361 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001362 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1363 if (ret < 0)
1364 return ret;
1365 }
1366
1367 return 0;
1368}
1369
Vivien Didelotfad09c72016-06-21 12:28:20 -04001370static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001371 struct mv88e6xxx_vtu_stu_entry *entry)
1372{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001373 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001374}
1375
Vivien Didelotfad09c72016-06-21 12:28:20 -04001376static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001377 struct mv88e6xxx_vtu_stu_entry *entry)
1378{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001379 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001380}
1381
Vivien Didelotfad09c72016-06-21 12:28:20 -04001382static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001383{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001384 return _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001385 vid & GLOBAL_VTU_VID_MASK);
1386}
1387
Vivien Didelotfad09c72016-06-21 12:28:20 -04001388static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001389 struct mv88e6xxx_vtu_stu_entry *entry)
1390{
1391 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1392 int ret;
1393
Vivien Didelotfad09c72016-06-21 12:28:20 -04001394 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001395 if (ret < 0)
1396 return ret;
1397
Vivien Didelotfad09c72016-06-21 12:28:20 -04001398 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001399 if (ret < 0)
1400 return ret;
1401
Vivien Didelotfad09c72016-06-21 12:28:20 -04001402 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001403 if (ret < 0)
1404 return ret;
1405
1406 next.vid = ret & GLOBAL_VTU_VID_MASK;
1407 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1408
1409 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001410 ret = mv88e6xxx_vtu_data_read(chip, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001411 if (ret < 0)
1412 return ret;
1413
Vivien Didelotfad09c72016-06-21 12:28:20 -04001414 if (mv88e6xxx_has_fid_reg(chip)) {
1415 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001416 GLOBAL_VTU_FID);
1417 if (ret < 0)
1418 return ret;
1419
1420 next.fid = ret & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001421 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001422 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1423 * VTU DBNum[3:0] are located in VTU Operation 3:0
1424 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001425 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001426 GLOBAL_VTU_OP);
1427 if (ret < 0)
1428 return ret;
1429
1430 next.fid = (ret & 0xf00) >> 4;
1431 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001432 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001433
Vivien Didelotfad09c72016-06-21 12:28:20 -04001434 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1435 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001436 GLOBAL_VTU_SID);
1437 if (ret < 0)
1438 return ret;
1439
1440 next.sid = ret & GLOBAL_VTU_SID_MASK;
1441 }
1442 }
1443
1444 *entry = next;
1445 return 0;
1446}
1447
Vivien Didelotf81ec902016-05-09 13:22:58 -04001448static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1449 struct switchdev_obj_port_vlan *vlan,
1450 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001451{
Vivien Didelot04bed142016-08-31 18:06:13 -04001452 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001453 struct mv88e6xxx_vtu_stu_entry next;
1454 u16 pvid;
1455 int err;
1456
Vivien Didelotfad09c72016-06-21 12:28:20 -04001457 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001458 return -EOPNOTSUPP;
1459
Vivien Didelotfad09c72016-06-21 12:28:20 -04001460 mutex_lock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001461
Vivien Didelotfad09c72016-06-21 12:28:20 -04001462 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001463 if (err)
1464 goto unlock;
1465
Vivien Didelotfad09c72016-06-21 12:28:20 -04001466 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001467 if (err)
1468 goto unlock;
1469
1470 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001471 err = _mv88e6xxx_vtu_getnext(chip, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001472 if (err)
1473 break;
1474
1475 if (!next.valid)
1476 break;
1477
1478 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1479 continue;
1480
1481 /* reinit and dump this VLAN obj */
Vivien Didelot57d32312016-06-20 13:13:58 -04001482 vlan->vid_begin = next.vid;
1483 vlan->vid_end = next.vid;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001484 vlan->flags = 0;
1485
1486 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1487 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1488
1489 if (next.vid == pvid)
1490 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1491
1492 err = cb(&vlan->obj);
1493 if (err)
1494 break;
1495 } while (next.vid < GLOBAL_VTU_VID_MASK);
1496
1497unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001498 mutex_unlock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001499
1500 return err;
1501}
1502
Vivien Didelotfad09c72016-06-21 12:28:20 -04001503static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001504 struct mv88e6xxx_vtu_stu_entry *entry)
1505{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001506 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001507 u16 reg = 0;
1508 int ret;
1509
Vivien Didelotfad09c72016-06-21 12:28:20 -04001510 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001511 if (ret < 0)
1512 return ret;
1513
1514 if (!entry->valid)
1515 goto loadpurge;
1516
1517 /* Write port member tags */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001518 ret = mv88e6xxx_vtu_data_write(chip, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001519 if (ret < 0)
1520 return ret;
1521
Vivien Didelotfad09c72016-06-21 12:28:20 -04001522 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001523 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001524 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
1525 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001526 if (ret < 0)
1527 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001528 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001529
Vivien Didelotfad09c72016-06-21 12:28:20 -04001530 if (mv88e6xxx_has_fid_reg(chip)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001531 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001532 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_FID,
1533 reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001534 if (ret < 0)
1535 return ret;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001536 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001537 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1538 * VTU DBNum[3:0] are located in VTU Operation 3:0
1539 */
1540 op |= (entry->fid & 0xf0) << 8;
1541 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001542 }
1543
1544 reg = GLOBAL_VTU_VID_VALID;
1545loadpurge:
1546 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001547 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001548 if (ret < 0)
1549 return ret;
1550
Vivien Didelotfad09c72016-06-21 12:28:20 -04001551 return _mv88e6xxx_vtu_cmd(chip, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001552}
1553
Vivien Didelotfad09c72016-06-21 12:28:20 -04001554static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001555 struct mv88e6xxx_vtu_stu_entry *entry)
1556{
1557 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1558 int ret;
1559
Vivien Didelotfad09c72016-06-21 12:28:20 -04001560 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001561 if (ret < 0)
1562 return ret;
1563
Vivien Didelotfad09c72016-06-21 12:28:20 -04001564 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001565 sid & GLOBAL_VTU_SID_MASK);
1566 if (ret < 0)
1567 return ret;
1568
Vivien Didelotfad09c72016-06-21 12:28:20 -04001569 ret = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001570 if (ret < 0)
1571 return ret;
1572
Vivien Didelotfad09c72016-06-21 12:28:20 -04001573 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001574 if (ret < 0)
1575 return ret;
1576
1577 next.sid = ret & GLOBAL_VTU_SID_MASK;
1578
Vivien Didelotfad09c72016-06-21 12:28:20 -04001579 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001580 if (ret < 0)
1581 return ret;
1582
1583 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1584
1585 if (next.valid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001586 ret = mv88e6xxx_stu_data_read(chip, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001587 if (ret < 0)
1588 return ret;
1589 }
1590
1591 *entry = next;
1592 return 0;
1593}
1594
Vivien Didelotfad09c72016-06-21 12:28:20 -04001595static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001596 struct mv88e6xxx_vtu_stu_entry *entry)
1597{
1598 u16 reg = 0;
1599 int ret;
1600
Vivien Didelotfad09c72016-06-21 12:28:20 -04001601 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001602 if (ret < 0)
1603 return ret;
1604
1605 if (!entry->valid)
1606 goto loadpurge;
1607
1608 /* Write port states */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001609 ret = mv88e6xxx_stu_data_write(chip, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001610 if (ret < 0)
1611 return ret;
1612
1613 reg = GLOBAL_VTU_VID_VALID;
1614loadpurge:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001615 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001616 if (ret < 0)
1617 return ret;
1618
1619 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001620 ret = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001621 if (ret < 0)
1622 return ret;
1623
Vivien Didelotfad09c72016-06-21 12:28:20 -04001624 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001625}
1626
Vivien Didelotfad09c72016-06-21 12:28:20 -04001627static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001628 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001629{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001630 struct dsa_switch *ds = chip->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001631 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001632 u16 fid;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001633 u16 reg;
1634 int err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001635
Vivien Didelotfad09c72016-06-21 12:28:20 -04001636 if (mv88e6xxx_num_databases(chip) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001637 upper_mask = 0xff;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001638 else if (mv88e6xxx_num_databases(chip) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001639 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001640 else
1641 return -EOPNOTSUPP;
1642
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001643 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001644 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
1645 if (err)
1646 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001647
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001648 fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001649
1650 if (new) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001651 reg &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1652 reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001653
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001654 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
1655 if (err)
1656 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001657 }
1658
1659 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001660 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
1661 if (err)
1662 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001663
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001664 fid |= (reg & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001665
1666 if (new) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001667 reg &= ~upper_mask;
1668 reg |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001669
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001670 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
1671 if (err)
1672 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001673
Andrew Lunnc8b09802016-06-04 21:16:57 +02001674 netdev_dbg(ds->ports[port].netdev,
1675 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001676 }
1677
1678 if (old)
1679 *old = fid;
1680
1681 return 0;
1682}
1683
Vivien Didelotfad09c72016-06-21 12:28:20 -04001684static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001685 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001686{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001687 return _mv88e6xxx_port_fid(chip, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001688}
1689
Vivien Didelotfad09c72016-06-21 12:28:20 -04001690static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001691 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001692{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001693 return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001694}
1695
Vivien Didelotfad09c72016-06-21 12:28:20 -04001696static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001697{
1698 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1699 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001700 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001701
1702 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1703
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001704 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001705 for (i = 0; i < chip->info->num_ports; ++i) {
1706 err = _mv88e6xxx_port_fid_get(chip, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001707 if (err)
1708 return err;
1709
1710 set_bit(*fid, fid_bitmap);
1711 }
1712
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001713 /* Set every FID bit used by the VLAN entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001714 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001715 if (err)
1716 return err;
1717
1718 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001719 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001720 if (err)
1721 return err;
1722
1723 if (!vlan.valid)
1724 break;
1725
1726 set_bit(vlan.fid, fid_bitmap);
1727 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1728
1729 /* The reset value 0x000 is used to indicate that multiple address
1730 * databases are not needed. Return the next positive available.
1731 */
1732 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotfad09c72016-06-21 12:28:20 -04001733 if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001734 return -ENOSPC;
1735
1736 /* Clear the database */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001737 return _mv88e6xxx_atu_flush(chip, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001738}
1739
Vivien Didelotfad09c72016-06-21 12:28:20 -04001740static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001741 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001742{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001743 struct dsa_switch *ds = chip->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001744 struct mv88e6xxx_vtu_stu_entry vlan = {
1745 .valid = true,
1746 .vid = vid,
1747 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001748 int i, err;
1749
Vivien Didelotfad09c72016-06-21 12:28:20 -04001750 err = _mv88e6xxx_fid_new(chip, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001751 if (err)
1752 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001753
Vivien Didelot3d131f02015-11-03 10:52:52 -05001754 /* exclude all ports except the CPU and DSA ports */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001755 for (i = 0; i < chip->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001756 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1757 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1758 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001759
Vivien Didelotfad09c72016-06-21 12:28:20 -04001760 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
1761 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001762 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001763
1764 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1765 * implemented, only one STU entry is needed to cover all VTU
1766 * entries. Thus, validate the SID 0.
1767 */
1768 vlan.sid = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001769 err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001770 if (err)
1771 return err;
1772
1773 if (vstp.sid != vlan.sid || !vstp.valid) {
1774 memset(&vstp, 0, sizeof(vstp));
1775 vstp.valid = true;
1776 vstp.sid = vlan.sid;
1777
Vivien Didelotfad09c72016-06-21 12:28:20 -04001778 err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001779 if (err)
1780 return err;
1781 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001782 }
1783
1784 *entry = vlan;
1785 return 0;
1786}
1787
Vivien Didelotfad09c72016-06-21 12:28:20 -04001788static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001789 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1790{
1791 int err;
1792
1793 if (!vid)
1794 return -EINVAL;
1795
Vivien Didelotfad09c72016-06-21 12:28:20 -04001796 err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001797 if (err)
1798 return err;
1799
Vivien Didelotfad09c72016-06-21 12:28:20 -04001800 err = _mv88e6xxx_vtu_getnext(chip, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001801 if (err)
1802 return err;
1803
1804 if (entry->vid != vid || !entry->valid) {
1805 if (!creat)
1806 return -EOPNOTSUPP;
1807 /* -ENOENT would've been more appropriate, but switchdev expects
1808 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1809 */
1810
Vivien Didelotfad09c72016-06-21 12:28:20 -04001811 err = _mv88e6xxx_vtu_new(chip, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001812 }
1813
1814 return err;
1815}
1816
Vivien Didelotda9c3592016-02-12 12:09:40 -05001817static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1818 u16 vid_begin, u16 vid_end)
1819{
Vivien Didelot04bed142016-08-31 18:06:13 -04001820 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotda9c3592016-02-12 12:09:40 -05001821 struct mv88e6xxx_vtu_stu_entry vlan;
1822 int i, err;
1823
1824 if (!vid_begin)
1825 return -EOPNOTSUPP;
1826
Vivien Didelotfad09c72016-06-21 12:28:20 -04001827 mutex_lock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001828
Vivien Didelotfad09c72016-06-21 12:28:20 -04001829 err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001830 if (err)
1831 goto unlock;
1832
1833 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001834 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001835 if (err)
1836 goto unlock;
1837
1838 if (!vlan.valid)
1839 break;
1840
1841 if (vlan.vid > vid_end)
1842 break;
1843
Vivien Didelotfad09c72016-06-21 12:28:20 -04001844 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001845 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1846 continue;
1847
1848 if (vlan.data[i] ==
1849 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1850 continue;
1851
Vivien Didelotfad09c72016-06-21 12:28:20 -04001852 if (chip->ports[i].bridge_dev ==
1853 chip->ports[port].bridge_dev)
Vivien Didelotda9c3592016-02-12 12:09:40 -05001854 break; /* same bridge, check next VLAN */
1855
Andrew Lunnc8b09802016-06-04 21:16:57 +02001856 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05001857 "hardware VLAN %d already used by %s\n",
1858 vlan.vid,
Vivien Didelotfad09c72016-06-21 12:28:20 -04001859 netdev_name(chip->ports[i].bridge_dev));
Vivien Didelotda9c3592016-02-12 12:09:40 -05001860 err = -EOPNOTSUPP;
1861 goto unlock;
1862 }
1863 } while (vlan.vid < vid_end);
1864
1865unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001866 mutex_unlock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001867
1868 return err;
1869}
1870
Vivien Didelot214cdb92016-02-26 13:16:08 -05001871static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1872 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1873 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1874 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1875 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1876};
1877
Vivien Didelotf81ec902016-05-09 13:22:58 -04001878static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1879 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05001880{
Vivien Didelot04bed142016-08-31 18:06:13 -04001881 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001882 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1883 PORT_CONTROL_2_8021Q_DISABLED;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001884 u16 reg;
1885 int err;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001886
Vivien Didelotfad09c72016-06-21 12:28:20 -04001887 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001888 return -EOPNOTSUPP;
1889
Vivien Didelotfad09c72016-06-21 12:28:20 -04001890 mutex_lock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001891
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001892 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
1893 if (err)
Vivien Didelot214cdb92016-02-26 13:16:08 -05001894 goto unlock;
1895
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001896 old = reg & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001897
Vivien Didelot5220ef12016-03-07 18:24:52 -05001898 if (new != old) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001899 reg &= ~PORT_CONTROL_2_8021Q_MASK;
1900 reg |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001901
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001902 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
1903 if (err)
Vivien Didelot5220ef12016-03-07 18:24:52 -05001904 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001905
Andrew Lunnc8b09802016-06-04 21:16:57 +02001906 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05001907 mv88e6xxx_port_8021q_mode_names[new],
1908 mv88e6xxx_port_8021q_mode_names[old]);
1909 }
1910
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001911 err = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001912unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001913 mutex_unlock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001914
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001915 return err;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001916}
1917
Vivien Didelot57d32312016-06-20 13:13:58 -04001918static int
1919mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1920 const struct switchdev_obj_port_vlan *vlan,
1921 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001922{
Vivien Didelot04bed142016-08-31 18:06:13 -04001923 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotda9c3592016-02-12 12:09:40 -05001924 int err;
1925
Vivien Didelotfad09c72016-06-21 12:28:20 -04001926 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001927 return -EOPNOTSUPP;
1928
Vivien Didelotda9c3592016-02-12 12:09:40 -05001929 /* If the requested port doesn't belong to the same bridge as the VLAN
1930 * members, do not support it (yet) and fallback to software VLAN.
1931 */
1932 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1933 vlan->vid_end);
1934 if (err)
1935 return err;
1936
Vivien Didelot76e398a2015-11-01 12:33:55 -05001937 /* We don't need any dynamic resource from the kernel (yet),
1938 * so skip the prepare phase.
1939 */
1940 return 0;
1941}
1942
Vivien Didelotfad09c72016-06-21 12:28:20 -04001943static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001944 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001945{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001946 struct mv88e6xxx_vtu_stu_entry vlan;
1947 int err;
1948
Vivien Didelotfad09c72016-06-21 12:28:20 -04001949 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001950 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001951 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001952
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001953 vlan.data[port] = untagged ?
1954 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1955 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1956
Vivien Didelotfad09c72016-06-21 12:28:20 -04001957 return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001958}
1959
Vivien Didelotf81ec902016-05-09 13:22:58 -04001960static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1961 const struct switchdev_obj_port_vlan *vlan,
1962 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001963{
Vivien Didelot04bed142016-08-31 18:06:13 -04001964 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001965 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1966 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1967 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001968
Vivien Didelotfad09c72016-06-21 12:28:20 -04001969 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001970 return;
1971
Vivien Didelotfad09c72016-06-21 12:28:20 -04001972 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001973
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001974 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Vivien Didelotfad09c72016-06-21 12:28:20 -04001975 if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02001976 netdev_err(ds->ports[port].netdev,
1977 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001978 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05001979
Vivien Didelotfad09c72016-06-21 12:28:20 -04001980 if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02001981 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001982 vlan->vid_end);
1983
Vivien Didelotfad09c72016-06-21 12:28:20 -04001984 mutex_unlock(&chip->reg_lock);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001985}
1986
Vivien Didelotfad09c72016-06-21 12:28:20 -04001987static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001988 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001989{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001990 struct dsa_switch *ds = chip->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001991 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001992 int i, err;
1993
Vivien Didelotfad09c72016-06-21 12:28:20 -04001994 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001995 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001996 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001997
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001998 /* Tell switchdev if this VLAN is handled in software */
1999 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002000 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002001
2002 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2003
2004 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002005 vlan.valid = false;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002006 for (i = 0; i < chip->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002007 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002008 continue;
2009
2010 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002011 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002012 break;
2013 }
2014 }
2015
Vivien Didelotfad09c72016-06-21 12:28:20 -04002016 err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002017 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002018 return err;
2019
Vivien Didelotfad09c72016-06-21 12:28:20 -04002020 return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002021}
2022
Vivien Didelotf81ec902016-05-09 13:22:58 -04002023static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2024 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002025{
Vivien Didelot04bed142016-08-31 18:06:13 -04002026 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002027 u16 pvid, vid;
2028 int err = 0;
2029
Vivien Didelotfad09c72016-06-21 12:28:20 -04002030 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002031 return -EOPNOTSUPP;
2032
Vivien Didelotfad09c72016-06-21 12:28:20 -04002033 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002034
Vivien Didelotfad09c72016-06-21 12:28:20 -04002035 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002036 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002037 goto unlock;
2038
Vivien Didelot76e398a2015-11-01 12:33:55 -05002039 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002040 err = _mv88e6xxx_port_vlan_del(chip, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002041 if (err)
2042 goto unlock;
2043
2044 if (vid == pvid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002045 err = _mv88e6xxx_port_pvid_set(chip, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002046 if (err)
2047 goto unlock;
2048 }
2049 }
2050
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002051unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002052 mutex_unlock(&chip->reg_lock);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002053
2054 return err;
2055}
2056
Vivien Didelotfad09c72016-06-21 12:28:20 -04002057static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002058 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002059{
2060 int i, ret;
2061
2062 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002063 ret = _mv88e6xxx_reg_write(
Vivien Didelotfad09c72016-06-21 12:28:20 -04002064 chip, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002065 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002066 if (ret < 0)
2067 return ret;
2068 }
2069
2070 return 0;
2071}
2072
Vivien Didelotfad09c72016-06-21 12:28:20 -04002073static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002074 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002075{
2076 int i, ret;
2077
2078 for (i = 0; i < 3; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002079 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002080 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002081 if (ret < 0)
2082 return ret;
2083 addr[i * 2] = ret >> 8;
2084 addr[i * 2 + 1] = ret & 0xff;
2085 }
2086
2087 return 0;
2088}
2089
Vivien Didelotfad09c72016-06-21 12:28:20 -04002090static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002091 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002092{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002093 int ret;
2094
Vivien Didelotfad09c72016-06-21 12:28:20 -04002095 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002096 if (ret < 0)
2097 return ret;
2098
Vivien Didelotfad09c72016-06-21 12:28:20 -04002099 ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002100 if (ret < 0)
2101 return ret;
2102
Vivien Didelotfad09c72016-06-21 12:28:20 -04002103 ret = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002104 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002105 return ret;
2106
Vivien Didelotfad09c72016-06-21 12:28:20 -04002107 return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002108}
David S. Millercdf09692015-08-11 12:00:37 -07002109
Vivien Didelot88472932016-09-19 19:56:11 -04002110static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
2111 struct mv88e6xxx_atu_entry *entry);
2112
2113static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid,
2114 const u8 *addr, struct mv88e6xxx_atu_entry *entry)
2115{
2116 struct mv88e6xxx_atu_entry next;
2117 int err;
2118
2119 eth_broadcast_addr(next.mac);
2120
2121 err = _mv88e6xxx_atu_mac_write(chip, next.mac);
2122 if (err)
2123 return err;
2124
2125 do {
2126 err = _mv88e6xxx_atu_getnext(chip, fid, &next);
2127 if (err)
2128 return err;
2129
2130 if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2131 break;
2132
2133 if (ether_addr_equal(next.mac, addr)) {
2134 *entry = next;
2135 return 0;
2136 }
2137 } while (!is_broadcast_ether_addr(next.mac));
2138
2139 memset(entry, 0, sizeof(*entry));
2140 entry->fid = fid;
2141 ether_addr_copy(entry->mac, addr);
2142
2143 return 0;
2144}
2145
Vivien Didelot83dabd12016-08-31 11:50:04 -04002146static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
2147 const unsigned char *addr, u16 vid,
2148 u8 state)
Vivien Didelotfd231c82015-08-10 09:09:50 -04002149{
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002150 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot88472932016-09-19 19:56:11 -04002151 struct mv88e6xxx_atu_entry entry;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002152 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002153
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002154 /* Null VLAN ID corresponds to the port private database */
2155 if (vid == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002156 err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002157 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002158 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002159 if (err)
2160 return err;
2161
Vivien Didelot88472932016-09-19 19:56:11 -04002162 err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry);
2163 if (err)
2164 return err;
2165
2166 /* Purge the ATU entry only if no port is using it anymore */
2167 if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
2168 entry.portv_trunkid &= ~BIT(port);
2169 if (!entry.portv_trunkid)
2170 entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
2171 } else {
2172 entry.portv_trunkid |= BIT(port);
2173 entry.state = state;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002174 }
2175
Vivien Didelotfad09c72016-06-21 12:28:20 -04002176 return _mv88e6xxx_atu_load(chip, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002177}
2178
Vivien Didelotf81ec902016-05-09 13:22:58 -04002179static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2180 const struct switchdev_obj_port_fdb *fdb,
2181 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002182{
2183 /* We don't need any dynamic resource from the kernel (yet),
2184 * so skip the prepare phase.
2185 */
2186 return 0;
2187}
2188
Vivien Didelotf81ec902016-05-09 13:22:58 -04002189static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2190 const struct switchdev_obj_port_fdb *fdb,
2191 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002192{
Vivien Didelot04bed142016-08-31 18:06:13 -04002193 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot6630e232015-08-06 01:44:07 -04002194
Vivien Didelotfad09c72016-06-21 12:28:20 -04002195 mutex_lock(&chip->reg_lock);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002196 if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
2197 GLOBAL_ATU_DATA_STATE_UC_STATIC))
2198 netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
Vivien Didelotfad09c72016-06-21 12:28:20 -04002199 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002200}
2201
Vivien Didelotf81ec902016-05-09 13:22:58 -04002202static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2203 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002204{
Vivien Didelot04bed142016-08-31 18:06:13 -04002205 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot83dabd12016-08-31 11:50:04 -04002206 int err;
David S. Millercdf09692015-08-11 12:00:37 -07002207
Vivien Didelotfad09c72016-06-21 12:28:20 -04002208 mutex_lock(&chip->reg_lock);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002209 err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
2210 GLOBAL_ATU_DATA_STATE_UNUSED);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002211 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002212
Vivien Didelot83dabd12016-08-31 11:50:04 -04002213 return err;
David S. Millercdf09692015-08-11 12:00:37 -07002214}
2215
Vivien Didelotfad09c72016-06-21 12:28:20 -04002216static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002217 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002218{
Vivien Didelot1d194042015-08-10 09:09:51 -04002219 struct mv88e6xxx_atu_entry next = { 0 };
2220 int ret;
2221
2222 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002223
Vivien Didelotfad09c72016-06-21 12:28:20 -04002224 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002225 if (ret < 0)
2226 return ret;
2227
Vivien Didelotfad09c72016-06-21 12:28:20 -04002228 ret = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002229 if (ret < 0)
2230 return ret;
2231
Vivien Didelotfad09c72016-06-21 12:28:20 -04002232 ret = _mv88e6xxx_atu_mac_read(chip, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002233 if (ret < 0)
2234 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002235
Vivien Didelotfad09c72016-06-21 12:28:20 -04002236 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002237 if (ret < 0)
2238 return ret;
2239
2240 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2241 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2242 unsigned int mask, shift;
2243
2244 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2245 next.trunk = true;
2246 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2247 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2248 } else {
2249 next.trunk = false;
2250 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2251 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2252 }
2253
2254 next.portv_trunkid = (ret & mask) >> shift;
2255 }
2256
2257 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002258 return 0;
2259}
2260
Vivien Didelot83dabd12016-08-31 11:50:04 -04002261static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
2262 u16 fid, u16 vid, int port,
2263 struct switchdev_obj *obj,
2264 int (*cb)(struct switchdev_obj *obj))
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002265{
2266 struct mv88e6xxx_atu_entry addr = {
2267 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2268 };
2269 int err;
2270
Vivien Didelotfad09c72016-06-21 12:28:20 -04002271 err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002272 if (err)
2273 return err;
2274
2275 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002276 err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002277 if (err)
Vivien Didelot83dabd12016-08-31 11:50:04 -04002278 return err;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002279
2280 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2281 break;
2282
Vivien Didelot83dabd12016-08-31 11:50:04 -04002283 if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
2284 continue;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002285
Vivien Didelot83dabd12016-08-31 11:50:04 -04002286 if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
2287 struct switchdev_obj_port_fdb *fdb;
2288
2289 if (!is_unicast_ether_addr(addr.mac))
2290 continue;
2291
2292 fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002293 fdb->vid = vid;
2294 ether_addr_copy(fdb->addr, addr.mac);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002295 if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
2296 fdb->ndm_state = NUD_NOARP;
2297 else
2298 fdb->ndm_state = NUD_REACHABLE;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04002299 } else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
2300 struct switchdev_obj_port_mdb *mdb;
2301
2302 if (!is_multicast_ether_addr(addr.mac))
2303 continue;
2304
2305 mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
2306 mdb->vid = vid;
2307 ether_addr_copy(mdb->addr, addr.mac);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002308 } else {
2309 return -EOPNOTSUPP;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002310 }
Vivien Didelot83dabd12016-08-31 11:50:04 -04002311
2312 err = cb(obj);
2313 if (err)
2314 return err;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002315 } while (!is_broadcast_ether_addr(addr.mac));
2316
2317 return err;
2318}
2319
Vivien Didelot83dabd12016-08-31 11:50:04 -04002320static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
2321 struct switchdev_obj *obj,
2322 int (*cb)(struct switchdev_obj *obj))
2323{
2324 struct mv88e6xxx_vtu_stu_entry vlan = {
2325 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2326 };
2327 u16 fid;
2328 int err;
2329
2330 /* Dump port's default Filtering Information Database (VLAN ID 0) */
2331 err = _mv88e6xxx_port_fid_get(chip, port, &fid);
2332 if (err)
2333 return err;
2334
2335 err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
2336 if (err)
2337 return err;
2338
2339 /* Dump VLANs' Filtering Information Databases */
2340 err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
2341 if (err)
2342 return err;
2343
2344 do {
2345 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
2346 if (err)
2347 return err;
2348
2349 if (!vlan.valid)
2350 break;
2351
2352 err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
2353 obj, cb);
2354 if (err)
2355 return err;
2356 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2357
2358 return err;
2359}
2360
Vivien Didelotf81ec902016-05-09 13:22:58 -04002361static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2362 struct switchdev_obj_port_fdb *fdb,
2363 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002364{
Vivien Didelot04bed142016-08-31 18:06:13 -04002365 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002366 int err;
2367
Vivien Didelotfad09c72016-06-21 12:28:20 -04002368 mutex_lock(&chip->reg_lock);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002369 err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002370 mutex_unlock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002371
2372 return err;
2373}
2374
Vivien Didelotf81ec902016-05-09 13:22:58 -04002375static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2376 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002377{
Vivien Didelot04bed142016-08-31 18:06:13 -04002378 struct mv88e6xxx_chip *chip = ds->priv;
Colin Ian King1d9619d2016-04-25 23:11:22 +01002379 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002380
Vivien Didelotfad09c72016-06-21 12:28:20 -04002381 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002382
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002383 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002384 chip->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002385
Vivien Didelotfad09c72016-06-21 12:28:20 -04002386 for (i = 0; i < chip->info->num_ports; ++i) {
2387 if (chip->ports[i].bridge_dev == bridge) {
2388 err = _mv88e6xxx_port_based_vlan_map(chip, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002389 if (err)
2390 break;
2391 }
2392 }
2393
Vivien Didelotfad09c72016-06-21 12:28:20 -04002394 mutex_unlock(&chip->reg_lock);
Vivien Didelota6692752016-02-12 12:09:39 -05002395
Vivien Didelot466dfa02016-02-26 13:16:05 -05002396 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002397}
2398
Vivien Didelotf81ec902016-05-09 13:22:58 -04002399static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002400{
Vivien Didelot04bed142016-08-31 18:06:13 -04002401 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002402 struct net_device *bridge = chip->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002403 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002404
Vivien Didelotfad09c72016-06-21 12:28:20 -04002405 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002406
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002407 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002408 chip->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002409
Vivien Didelotfad09c72016-06-21 12:28:20 -04002410 for (i = 0; i < chip->info->num_ports; ++i)
2411 if (i == port || chip->ports[i].bridge_dev == bridge)
2412 if (_mv88e6xxx_port_based_vlan_map(chip, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002413 netdev_warn(ds->ports[i].netdev,
2414 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002415
Vivien Didelotfad09c72016-06-21 12:28:20 -04002416 mutex_unlock(&chip->reg_lock);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002417}
2418
Vivien Didelotfad09c72016-06-21 12:28:20 -04002419static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
Vivien Didelot552238b2016-05-09 13:22:49 -04002420{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002421 bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
Vivien Didelot552238b2016-05-09 13:22:49 -04002422 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002423 struct gpio_desc *gpiod = chip->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002424 unsigned long timeout;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002425 int err, ret;
2426 u16 reg;
Vivien Didelot552238b2016-05-09 13:22:49 -04002427 int i;
2428
2429 /* Set all ports to the disabled state. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002430 for (i = 0; i < chip->info->num_ports; i++) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002431 err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, &reg);
2432 if (err)
2433 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002434
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002435 err = mv88e6xxx_port_write(chip, i, PORT_CONTROL,
2436 reg & 0xfffc);
2437 if (err)
2438 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002439 }
2440
2441 /* Wait for transmit queues to drain. */
2442 usleep_range(2000, 4000);
2443
2444 /* If there is a gpio connected to the reset pin, toggle it */
2445 if (gpiod) {
2446 gpiod_set_value_cansleep(gpiod, 1);
2447 usleep_range(10000, 20000);
2448 gpiod_set_value_cansleep(gpiod, 0);
2449 usleep_range(10000, 20000);
2450 }
2451
2452 /* Reset the switch. Keep the PPU active if requested. The PPU
2453 * needs to be active to support indirect phy register access
2454 * through global registers 0x18 and 0x19.
2455 */
2456 if (ppu_active)
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002457 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc000);
Vivien Didelot552238b2016-05-09 13:22:49 -04002458 else
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002459 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, 0x04, 0xc400);
2460 if (err)
2461 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002462
2463 /* Wait up to one second for reset to complete. */
2464 timeout = jiffies + 1 * HZ;
2465 while (time_before(jiffies, timeout)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002466 ret = _mv88e6xxx_reg_read(chip, REG_GLOBAL, 0x00);
Vivien Didelot552238b2016-05-09 13:22:49 -04002467 if (ret < 0)
2468 return ret;
2469
2470 if ((ret & is_reset) == is_reset)
2471 break;
2472 usleep_range(1000, 2000);
2473 }
2474 if (time_after(jiffies, timeout))
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002475 err = -ETIMEDOUT;
Vivien Didelot552238b2016-05-09 13:22:49 -04002476 else
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002477 err = 0;
Vivien Didelot552238b2016-05-09 13:22:49 -04002478
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002479 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002480}
2481
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002482static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002483{
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002484 u16 val;
2485 int err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002486
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002487 /* Clear Power Down bit */
2488 err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
2489 if (err)
2490 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002491
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002492 if (val & BMCR_PDOWN) {
2493 val &= ~BMCR_PDOWN;
2494 err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002495 }
2496
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002497 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002498}
2499
Vivien Didelotfad09c72016-06-21 12:28:20 -04002500static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002501{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002502 struct dsa_switch *ds = chip->ds;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002503 int err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002504 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002505
Vivien Didelotfad09c72016-06-21 12:28:20 -04002506 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2507 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2508 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2509 mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002510 /* MAC Forcing register: don't force link, speed,
2511 * duplex or flow control state to any particular
2512 * values on physical ports, but force the CPU port
2513 * and all DSA ports to their maximum bandwidth and
2514 * full duplex.
2515 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002516 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002517 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002518 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002519 reg |= PORT_PCS_CTRL_FORCE_LINK |
2520 PORT_PCS_CTRL_LINK_UP |
2521 PORT_PCS_CTRL_DUPLEX_FULL |
2522 PORT_PCS_CTRL_FORCE_DUPLEX;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002523 if (mv88e6xxx_6065_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002524 reg |= PORT_PCS_CTRL_100;
2525 else
2526 reg |= PORT_PCS_CTRL_1000;
2527 } else {
2528 reg |= PORT_PCS_CTRL_UNFORCED;
2529 }
2530
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002531 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
2532 if (err)
2533 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002534 }
2535
2536 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2537 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2538 * tunneling, determine priority by looking at 802.1p and IP
2539 * priority fields (IP prio has precedence), and set STP state
2540 * to Forwarding.
2541 *
2542 * If this is the CPU link, use DSA or EDSA tagging depending
2543 * on which tagging mode was configured.
2544 *
2545 * If this is a link to another switch, use DSA tagging mode.
2546 *
2547 * If this is the upstream port for this switch, enable
2548 * forwarding of unknown unicasts and multicasts.
2549 */
2550 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002551 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2552 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2553 mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2554 mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002555 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2556 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2557 PORT_CONTROL_STATE_FORWARDING;
2558 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002559 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
Andrew Lunn5377b802016-06-04 21:17:02 +02002560 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002561 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002562 else
2563 reg |= PORT_CONTROL_DSA_TAG;
Jamie Lentinf027e0c2016-08-22 16:01:04 +02002564 reg |= PORT_CONTROL_EGRESS_ADD_TAG |
2565 PORT_CONTROL_FORWARD_UNKNOWN;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002566 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002567 if (dsa_is_dsa_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002568 if (mv88e6xxx_6095_family(chip) ||
2569 mv88e6xxx_6185_family(chip))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002570 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002571 if (mv88e6xxx_6352_family(chip) ||
2572 mv88e6xxx_6351_family(chip) ||
2573 mv88e6xxx_6165_family(chip) ||
2574 mv88e6xxx_6097_family(chip) ||
2575 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002576 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002577 }
2578
Andrew Lunn54d792f2015-05-06 01:09:47 +02002579 if (port == dsa_upstream_port(ds))
2580 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2581 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2582 }
2583 if (reg) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002584 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
2585 if (err)
2586 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002587 }
2588
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002589 /* If this port is connected to a SerDes, make sure the SerDes is not
2590 * powered down.
2591 */
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002592 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002593 err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
2594 if (err)
2595 return err;
2596 reg &= PORT_STATUS_CMODE_MASK;
2597 if ((reg == PORT_STATUS_CMODE_100BASE_X) ||
2598 (reg == PORT_STATUS_CMODE_1000BASE_X) ||
2599 (reg == PORT_STATUS_CMODE_SGMII)) {
2600 err = mv88e6xxx_serdes_power_on(chip);
2601 if (err < 0)
2602 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002603 }
2604 }
2605
Vivien Didelot8efdda42015-08-13 12:52:23 -04002606 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002607 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002608 * untagged frames on this port, do a destination address lookup on all
2609 * received packets as usual, disable ARP mirroring and don't send a
2610 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002611 */
2612 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002613 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2614 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2615 mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2616 mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002617 reg = PORT_CONTROL_2_MAP_DA;
2618
Vivien Didelotfad09c72016-06-21 12:28:20 -04002619 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2620 mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002621 reg |= PORT_CONTROL_2_JUMBO_10240;
2622
Vivien Didelotfad09c72016-06-21 12:28:20 -04002623 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002624 /* Set the upstream port this port should use */
2625 reg |= dsa_upstream_port(ds);
2626 /* enable forwarding of unknown multicast addresses to
2627 * the upstream port
2628 */
2629 if (port == dsa_upstream_port(ds))
2630 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2631 }
2632
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002633 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002634
Andrew Lunn54d792f2015-05-06 01:09:47 +02002635 if (reg) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002636 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
2637 if (err)
2638 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002639 }
2640
2641 /* Port Association Vector: when learning source addresses
2642 * of packets, add the address to the address database using
2643 * a port bitmap that has only the bit for this port set and
2644 * the other bits clear.
2645 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002646 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002647 /* Disable learning for CPU port */
2648 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002649 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002650
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002651 err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
2652 if (err)
2653 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002654
2655 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002656 err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
2657 if (err)
2658 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002659
Vivien Didelotfad09c72016-06-21 12:28:20 -04002660 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2661 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2662 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002663 /* Do not limit the period of time that this port can
2664 * be paused for by the remote end or the period of
2665 * time that this port can pause the remote end.
2666 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002667 err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
2668 if (err)
2669 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002670
2671 /* Port ATU control: disable limiting the number of
2672 * address database entries that this port is allowed
2673 * to use.
2674 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002675 err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL,
2676 0x0000);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002677 /* Priority Override: disable DA, SA and VTU priority
2678 * override.
2679 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002680 err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE,
2681 0x0000);
2682 if (err)
2683 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002684
2685 /* Port Ethertype: use the Ethertype DSA Ethertype
2686 * value.
2687 */
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002688 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002689 err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE,
2690 ETH_P_EDSA);
2691 if (err)
2692 return err;
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002693 }
2694
Andrew Lunn54d792f2015-05-06 01:09:47 +02002695 /* Tag Remap: use an identity 802.1p prio -> switch
2696 * prio mapping.
2697 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002698 err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123,
2699 0x3210);
2700 if (err)
2701 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002702
2703 /* Tag Remap 2: use an identity 802.1p prio -> switch
2704 * prio mapping.
2705 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002706 err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567,
2707 0x7654);
2708 if (err)
2709 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002710 }
2711
Jamie Lentin1bc261f2016-08-22 22:47:08 +01002712 /* Rate Control: disable ingress rate limiting. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002713 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2714 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
Vivien Didelotfad09c72016-06-21 12:28:20 -04002715 mv88e6xxx_6320_family(chip)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002716 err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
2717 0x0001);
2718 if (err)
2719 return err;
Jamie Lentin1bc261f2016-08-22 22:47:08 +01002720 } else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002721 err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
2722 0x0000);
2723 if (err)
2724 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002725 }
2726
Guenter Roeck366f0a02015-03-26 18:36:30 -07002727 /* Port Control 1: disable trunking, disable sending
2728 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002729 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002730 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000);
2731 if (err)
2732 return err;
Guenter Roeckd827e882015-03-26 18:36:29 -07002733
Vivien Didelot207afda2016-04-14 14:42:09 -04002734 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002735 * database, and allow bidirectional communication between the
2736 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002737 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002738 err = _mv88e6xxx_port_fid_set(chip, port, 0);
2739 if (err)
2740 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002741
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002742 err = _mv88e6xxx_port_based_vlan_map(chip, port);
2743 if (err)
2744 return err;
Guenter Roeckd827e882015-03-26 18:36:29 -07002745
2746 /* Default VLAN ID and priority: don't set a default VLAN
2747 * ID, and set the default packet priority to zero.
2748 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002749 return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
Andrew Lunndbde9e62015-05-06 01:09:48 +02002750}
2751
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002752static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
2753{
2754 int err;
2755
2756 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_01,
2757 (addr[0] << 8) | addr[1]);
2758 if (err)
2759 return err;
2760
2761 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_23,
2762 (addr[2] << 8) | addr[3]);
2763 if (err)
2764 return err;
2765
2766 return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_MAC_45,
2767 (addr[4] << 8) | addr[5]);
2768}
2769
Vivien Didelotacddbd22016-07-18 20:45:39 -04002770static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
2771 unsigned int msecs)
2772{
2773 const unsigned int coeff = chip->info->age_time_coeff;
2774 const unsigned int min = 0x01 * coeff;
2775 const unsigned int max = 0xff * coeff;
2776 u8 age_time;
2777 u16 val;
2778 int err;
2779
2780 if (msecs < min || msecs > max)
2781 return -ERANGE;
2782
2783 /* Round to nearest multiple of coeff */
2784 age_time = (msecs + coeff / 2) / coeff;
2785
2786 err = mv88e6xxx_read(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, &val);
2787 if (err)
2788 return err;
2789
2790 /* AgeTime is 11:4 bits */
2791 val &= ~0xff0;
2792 val |= age_time << 4;
2793
2794 return mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL, val);
2795}
2796
Vivien Didelot2cfcd962016-07-18 20:45:40 -04002797static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
2798 unsigned int ageing_time)
2799{
Vivien Didelot04bed142016-08-31 18:06:13 -04002800 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot2cfcd962016-07-18 20:45:40 -04002801 int err;
2802
2803 mutex_lock(&chip->reg_lock);
2804 err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
2805 mutex_unlock(&chip->reg_lock);
2806
2807 return err;
2808}
2809
Vivien Didelot97299342016-07-18 20:45:30 -04002810static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
Vivien Didelot08a01262016-05-09 13:22:50 -04002811{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002812 struct dsa_switch *ds = chip->ds;
Vivien Didelotb0745e872016-05-09 13:22:53 -04002813 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002814 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002815 int err;
Vivien Didelot08a01262016-05-09 13:22:50 -04002816
Vivien Didelot119477b2016-05-09 13:22:51 -04002817 /* Enable the PHY Polling Unit if present, don't discard any packets,
2818 * and mask all interrupt sources.
2819 */
2820 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002821 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
2822 mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
Vivien Didelot119477b2016-05-09 13:22:51 -04002823 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2824
Vivien Didelotfad09c72016-06-21 12:28:20 -04002825 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL, reg);
Vivien Didelot119477b2016-05-09 13:22:51 -04002826 if (err)
2827 return err;
2828
Vivien Didelotb0745e872016-05-09 13:22:53 -04002829 /* Configure the upstream port, and configure it as the port to which
2830 * ingress and egress and ARP monitor frames are to be sent.
2831 */
2832 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2833 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2834 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002835 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_MONITOR_CONTROL,
2836 reg);
Vivien Didelotb0745e872016-05-09 13:22:53 -04002837 if (err)
2838 return err;
2839
Vivien Didelot50484ff2016-05-09 13:22:54 -04002840 /* Disable remote management, and set the switch's DSA device number. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002841 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_CONTROL_2,
Vivien Didelot50484ff2016-05-09 13:22:54 -04002842 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2843 (ds->index & 0x1f));
2844 if (err)
2845 return err;
2846
Vivien Didelotacddbd22016-07-18 20:45:39 -04002847 /* Clear all the VTU and STU entries */
2848 err = _mv88e6xxx_vtu_stu_flush(chip);
2849 if (err < 0)
2850 return err;
2851
Vivien Didelot08a01262016-05-09 13:22:50 -04002852 /* Set the default address aging time to 5 minutes, and
2853 * enable address learn messages to be sent to all message
2854 * ports.
2855 */
Vivien Didelotacddbd22016-07-18 20:45:39 -04002856 err = mv88e6xxx_write(chip, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2857 GLOBAL_ATU_CONTROL_LEARN2ALL);
Vivien Didelot08a01262016-05-09 13:22:50 -04002858 if (err)
2859 return err;
2860
Vivien Didelotacddbd22016-07-18 20:45:39 -04002861 err = mv88e6xxx_g1_set_age_time(chip, 300000);
2862 if (err)
Vivien Didelot97299342016-07-18 20:45:30 -04002863 return err;
2864
2865 /* Clear all ATU entries */
2866 err = _mv88e6xxx_atu_flush(chip, 0, true);
2867 if (err)
2868 return err;
2869
Vivien Didelot08a01262016-05-09 13:22:50 -04002870 /* Configure the IP ToS mapping registers. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002871 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002872 if (err)
2873 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002874 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002875 if (err)
2876 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002877 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002878 if (err)
2879 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002880 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002881 if (err)
2882 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002883 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04002884 if (err)
2885 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002886 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04002887 if (err)
2888 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002889 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04002890 if (err)
2891 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002892 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04002893 if (err)
2894 return err;
2895
2896 /* Configure the IEEE 802.1p priority mapping register. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002897 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Vivien Didelot08a01262016-05-09 13:22:50 -04002898 if (err)
2899 return err;
2900
Vivien Didelot97299342016-07-18 20:45:30 -04002901 /* Clear the statistics counters for all ports */
2902 err = _mv88e6xxx_reg_write(chip, REG_GLOBAL, GLOBAL_STATS_OP,
2903 GLOBAL_STATS_OP_FLUSH_ALL);
2904 if (err)
2905 return err;
2906
2907 /* Wait for the flush to complete. */
2908 err = _mv88e6xxx_stats_wait(chip);
2909 if (err)
2910 return err;
2911
2912 return 0;
2913}
2914
Vivien Didelotf81ec902016-05-09 13:22:58 -04002915static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002916{
Vivien Didelot04bed142016-08-31 18:06:13 -04002917 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot552238b2016-05-09 13:22:49 -04002918 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002919 int i;
2920
Vivien Didelotfad09c72016-06-21 12:28:20 -04002921 chip->ds = ds;
2922 ds->slave_mii_bus = chip->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04002923
Vivien Didelotfad09c72016-06-21 12:28:20 -04002924 mutex_lock(&chip->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04002925
Vivien Didelotfad09c72016-06-21 12:28:20 -04002926 err = mv88e6xxx_switch_reset(chip);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002927 if (err)
2928 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002929
Vivien Didelot97299342016-07-18 20:45:30 -04002930 /* Setup Switch Port Registers */
2931 for (i = 0; i < chip->info->num_ports; i++) {
2932 err = mv88e6xxx_setup_port(chip, i);
2933 if (err)
2934 goto unlock;
2935 }
2936
2937 /* Setup Switch Global 1 Registers */
2938 err = mv88e6xxx_g1_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002939 if (err)
2940 goto unlock;
2941
Vivien Didelot97299342016-07-18 20:45:30 -04002942 /* Setup Switch Global 2 Registers */
2943 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
2944 err = mv88e6xxx_g2_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002945 if (err)
2946 goto unlock;
2947 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002948
Vivien Didelot6b17e862015-08-13 12:52:18 -04002949unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002950 mutex_unlock(&chip->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02002951
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002952 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002953}
2954
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002955static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
2956{
Vivien Didelot04bed142016-08-31 18:06:13 -04002957 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002958 int err;
2959
2960 mutex_lock(&chip->reg_lock);
2961
2962 /* Has an indirect Switch MAC/WoL/WoF register in Global 2? */
2963 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_SWITCH_MAC))
2964 err = mv88e6xxx_g2_set_switch_mac(chip, addr);
2965 else
2966 err = mv88e6xxx_g1_set_switch_mac(chip, addr);
2967
2968 mutex_unlock(&chip->reg_lock);
2969
2970 return err;
2971}
2972
Vivien Didelote57e5e72016-08-15 17:19:00 -04002973static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002974{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002975 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04002976 u16 val;
2977 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002978
Vivien Didelote57e5e72016-08-15 17:19:00 -04002979 if (phy >= chip->info->num_ports)
Andrew Lunn158bc062016-04-28 21:24:06 -04002980 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002981
Vivien Didelotfad09c72016-06-21 12:28:20 -04002982 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04002983 err = mv88e6xxx_phy_read(chip, phy, reg, &val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002984 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04002985
2986 return err ? err : val;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002987}
2988
Vivien Didelote57e5e72016-08-15 17:19:00 -04002989static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002990{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002991 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04002992 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002993
Vivien Didelote57e5e72016-08-15 17:19:00 -04002994 if (phy >= chip->info->num_ports)
Andrew Lunn158bc062016-04-28 21:24:06 -04002995 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002996
Vivien Didelotfad09c72016-06-21 12:28:20 -04002997 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04002998 err = mv88e6xxx_phy_write(chip, phy, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002999 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003000
3001 return err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003002}
3003
Vivien Didelotfad09c72016-06-21 12:28:20 -04003004static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
Andrew Lunnb516d452016-06-04 21:17:06 +02003005 struct device_node *np)
3006{
3007 static int index;
3008 struct mii_bus *bus;
3009 int err;
3010
Andrew Lunnb516d452016-06-04 21:17:06 +02003011 if (np)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003012 chip->mdio_np = of_get_child_by_name(np, "mdio");
Andrew Lunnb516d452016-06-04 21:17:06 +02003013
Vivien Didelotfad09c72016-06-21 12:28:20 -04003014 bus = devm_mdiobus_alloc(chip->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003015 if (!bus)
3016 return -ENOMEM;
3017
Vivien Didelotfad09c72016-06-21 12:28:20 -04003018 bus->priv = (void *)chip;
Andrew Lunnb516d452016-06-04 21:17:06 +02003019 if (np) {
3020 bus->name = np->full_name;
3021 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3022 } else {
3023 bus->name = "mv88e6xxx SMI";
3024 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3025 }
3026
3027 bus->read = mv88e6xxx_mdio_read;
3028 bus->write = mv88e6xxx_mdio_write;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003029 bus->parent = chip->dev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003030
Vivien Didelotfad09c72016-06-21 12:28:20 -04003031 if (chip->mdio_np)
3032 err = of_mdiobus_register(bus, chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003033 else
3034 err = mdiobus_register(bus);
3035 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003036 dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
Andrew Lunnb516d452016-06-04 21:17:06 +02003037 goto out;
3038 }
Vivien Didelotfad09c72016-06-21 12:28:20 -04003039 chip->mdio_bus = bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003040
3041 return 0;
3042
3043out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003044 if (chip->mdio_np)
3045 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003046
3047 return err;
3048}
3049
Vivien Didelotfad09c72016-06-21 12:28:20 -04003050static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
Andrew Lunnb516d452016-06-04 21:17:06 +02003051
3052{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003053 struct mii_bus *bus = chip->mdio_bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003054
3055 mdiobus_unregister(bus);
3056
Vivien Didelotfad09c72016-06-21 12:28:20 -04003057 if (chip->mdio_np)
3058 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003059}
3060
Guenter Roeckc22995c2015-07-25 09:42:28 -07003061#ifdef CONFIG_NET_DSA_HWMON
3062
3063static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3064{
Vivien Didelot04bed142016-08-31 18:06:13 -04003065 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot9c938292016-08-15 17:19:02 -04003066 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003067 int ret;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003068
3069 *temp = 0;
3070
Vivien Didelotfad09c72016-06-21 12:28:20 -04003071 mutex_lock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003072
Vivien Didelot9c938292016-08-15 17:19:02 -04003073 ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003074 if (ret < 0)
3075 goto error;
3076
3077 /* Enable temperature sensor */
Vivien Didelot9c938292016-08-15 17:19:02 -04003078 ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003079 if (ret < 0)
3080 goto error;
3081
Vivien Didelot9c938292016-08-15 17:19:02 -04003082 ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003083 if (ret < 0)
3084 goto error;
3085
3086 /* Wait for temperature to stabilize */
3087 usleep_range(10000, 12000);
3088
Vivien Didelot9c938292016-08-15 17:19:02 -04003089 ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
3090 if (ret < 0)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003091 goto error;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003092
3093 /* Disable temperature sensor */
Vivien Didelot9c938292016-08-15 17:19:02 -04003094 ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003095 if (ret < 0)
3096 goto error;
3097
3098 *temp = ((val & 0x1f) - 5) * 5;
3099
3100error:
Vivien Didelot9c938292016-08-15 17:19:02 -04003101 mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003102 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003103 return ret;
3104}
3105
3106static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3107{
Vivien Didelot04bed142016-08-31 18:06:13 -04003108 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003109 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003110 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003111 int ret;
3112
3113 *temp = 0;
3114
Vivien Didelot9c938292016-08-15 17:19:02 -04003115 mutex_lock(&chip->reg_lock);
3116 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
3117 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003118 if (ret < 0)
3119 return ret;
3120
Vivien Didelot9c938292016-08-15 17:19:02 -04003121 *temp = (val & 0xff) - 25;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003122
3123 return 0;
3124}
3125
Vivien Didelotf81ec902016-05-09 13:22:58 -04003126static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003127{
Vivien Didelot04bed142016-08-31 18:06:13 -04003128 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn158bc062016-04-28 21:24:06 -04003129
Vivien Didelotfad09c72016-06-21 12:28:20 -04003130 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
Vivien Didelot6594f612016-05-09 13:22:42 -04003131 return -EOPNOTSUPP;
3132
Vivien Didelotfad09c72016-06-21 12:28:20 -04003133 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003134 return mv88e63xx_get_temp(ds, temp);
3135
3136 return mv88e61xx_get_temp(ds, temp);
3137}
3138
Vivien Didelotf81ec902016-05-09 13:22:58 -04003139static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003140{
Vivien Didelot04bed142016-08-31 18:06:13 -04003141 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003142 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003143 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003144 int ret;
3145
Vivien Didelotfad09c72016-06-21 12:28:20 -04003146 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003147 return -EOPNOTSUPP;
3148
3149 *temp = 0;
3150
Vivien Didelot9c938292016-08-15 17:19:02 -04003151 mutex_lock(&chip->reg_lock);
3152 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3153 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003154 if (ret < 0)
3155 return ret;
3156
Vivien Didelot9c938292016-08-15 17:19:02 -04003157 *temp = (((val >> 8) & 0x1f) * 5) - 25;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003158
3159 return 0;
3160}
3161
Vivien Didelotf81ec902016-05-09 13:22:58 -04003162static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003163{
Vivien Didelot04bed142016-08-31 18:06:13 -04003164 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003165 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003166 u16 val;
3167 int err;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003168
Vivien Didelotfad09c72016-06-21 12:28:20 -04003169 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003170 return -EOPNOTSUPP;
3171
Vivien Didelot9c938292016-08-15 17:19:02 -04003172 mutex_lock(&chip->reg_lock);
3173 err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3174 if (err)
3175 goto unlock;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003176 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Vivien Didelot9c938292016-08-15 17:19:02 -04003177 err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
3178 (val & 0xe0ff) | (temp << 8));
3179unlock:
3180 mutex_unlock(&chip->reg_lock);
3181
3182 return err;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003183}
3184
Vivien Didelotf81ec902016-05-09 13:22:58 -04003185static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003186{
Vivien Didelot04bed142016-08-31 18:06:13 -04003187 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003188 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003189 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003190 int ret;
3191
Vivien Didelotfad09c72016-06-21 12:28:20 -04003192 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003193 return -EOPNOTSUPP;
3194
3195 *alarm = false;
3196
Vivien Didelot9c938292016-08-15 17:19:02 -04003197 mutex_lock(&chip->reg_lock);
3198 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3199 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003200 if (ret < 0)
3201 return ret;
3202
Vivien Didelot9c938292016-08-15 17:19:02 -04003203 *alarm = !!(val & 0x40);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003204
3205 return 0;
3206}
3207#endif /* CONFIG_NET_DSA_HWMON */
3208
Vivien Didelot855b1932016-07-20 18:18:35 -04003209static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
3210{
Vivien Didelot04bed142016-08-31 18:06:13 -04003211 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot855b1932016-07-20 18:18:35 -04003212
3213 return chip->eeprom_len;
3214}
3215
Vivien Didelot855b1932016-07-20 18:18:35 -04003216static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
3217 struct ethtool_eeprom *eeprom, u8 *data)
3218{
Vivien Didelot04bed142016-08-31 18:06:13 -04003219 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot855b1932016-07-20 18:18:35 -04003220 int err;
3221
3222 mutex_lock(&chip->reg_lock);
3223
3224 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
Vivien Didelotec561272016-09-02 14:45:33 -04003225 err = mv88e6xxx_g2_get_eeprom16(chip, eeprom, data);
Vivien Didelot855b1932016-07-20 18:18:35 -04003226 else
3227 err = -EOPNOTSUPP;
3228
3229 mutex_unlock(&chip->reg_lock);
3230
3231 if (err)
3232 return err;
3233
3234 eeprom->magic = 0xc3ec4951;
3235
3236 return 0;
3237}
3238
Vivien Didelot855b1932016-07-20 18:18:35 -04003239static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
3240 struct ethtool_eeprom *eeprom, u8 *data)
3241{
Vivien Didelot04bed142016-08-31 18:06:13 -04003242 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot855b1932016-07-20 18:18:35 -04003243 int err;
3244
3245 if (eeprom->magic != 0xc3ec4951)
3246 return -EINVAL;
3247
3248 mutex_lock(&chip->reg_lock);
3249
3250 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16))
Vivien Didelotec561272016-09-02 14:45:33 -04003251 err = mv88e6xxx_g2_set_eeprom16(chip, eeprom, data);
Vivien Didelot855b1932016-07-20 18:18:35 -04003252 else
3253 err = -EOPNOTSUPP;
3254
3255 mutex_unlock(&chip->reg_lock);
3256
3257 return err;
3258}
3259
Vivien Didelotf81ec902016-05-09 13:22:58 -04003260static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3261 [MV88E6085] = {
3262 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3263 .family = MV88E6XXX_FAMILY_6097,
3264 .name = "Marvell 88E6085",
3265 .num_databases = 4096,
3266 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003267 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003268 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003269 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3270 },
3271
3272 [MV88E6095] = {
3273 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3274 .family = MV88E6XXX_FAMILY_6095,
3275 .name = "Marvell 88E6095/88E6095F",
3276 .num_databases = 256,
3277 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003278 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003279 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003280 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3281 },
3282
3283 [MV88E6123] = {
3284 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3285 .family = MV88E6XXX_FAMILY_6165,
3286 .name = "Marvell 88E6123",
3287 .num_databases = 4096,
3288 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003289 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003290 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003291 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3292 },
3293
3294 [MV88E6131] = {
3295 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3296 .family = MV88E6XXX_FAMILY_6185,
3297 .name = "Marvell 88E6131",
3298 .num_databases = 256,
3299 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003300 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003301 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003302 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3303 },
3304
3305 [MV88E6161] = {
3306 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3307 .family = MV88E6XXX_FAMILY_6165,
3308 .name = "Marvell 88E6161",
3309 .num_databases = 4096,
3310 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003311 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003312 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003313 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3314 },
3315
3316 [MV88E6165] = {
3317 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3318 .family = MV88E6XXX_FAMILY_6165,
3319 .name = "Marvell 88E6165",
3320 .num_databases = 4096,
3321 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003322 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003323 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003324 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3325 },
3326
3327 [MV88E6171] = {
3328 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3329 .family = MV88E6XXX_FAMILY_6351,
3330 .name = "Marvell 88E6171",
3331 .num_databases = 4096,
3332 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003333 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003334 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003335 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3336 },
3337
3338 [MV88E6172] = {
3339 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3340 .family = MV88E6XXX_FAMILY_6352,
3341 .name = "Marvell 88E6172",
3342 .num_databases = 4096,
3343 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003344 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003345 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003346 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3347 },
3348
3349 [MV88E6175] = {
3350 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3351 .family = MV88E6XXX_FAMILY_6351,
3352 .name = "Marvell 88E6175",
3353 .num_databases = 4096,
3354 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003355 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003356 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003357 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3358 },
3359
3360 [MV88E6176] = {
3361 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3362 .family = MV88E6XXX_FAMILY_6352,
3363 .name = "Marvell 88E6176",
3364 .num_databases = 4096,
3365 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003366 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003367 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003368 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3369 },
3370
3371 [MV88E6185] = {
3372 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3373 .family = MV88E6XXX_FAMILY_6185,
3374 .name = "Marvell 88E6185",
3375 .num_databases = 256,
3376 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003377 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003378 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003379 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3380 },
3381
3382 [MV88E6240] = {
3383 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3384 .family = MV88E6XXX_FAMILY_6352,
3385 .name = "Marvell 88E6240",
3386 .num_databases = 4096,
3387 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003388 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003389 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003390 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3391 },
3392
3393 [MV88E6320] = {
3394 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3395 .family = MV88E6XXX_FAMILY_6320,
3396 .name = "Marvell 88E6320",
3397 .num_databases = 4096,
3398 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003399 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003400 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003401 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3402 },
3403
3404 [MV88E6321] = {
3405 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3406 .family = MV88E6XXX_FAMILY_6320,
3407 .name = "Marvell 88E6321",
3408 .num_databases = 4096,
3409 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003410 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003411 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003412 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3413 },
3414
3415 [MV88E6350] = {
3416 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3417 .family = MV88E6XXX_FAMILY_6351,
3418 .name = "Marvell 88E6350",
3419 .num_databases = 4096,
3420 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003421 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003422 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003423 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3424 },
3425
3426 [MV88E6351] = {
3427 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3428 .family = MV88E6XXX_FAMILY_6351,
3429 .name = "Marvell 88E6351",
3430 .num_databases = 4096,
3431 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003432 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003433 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003434 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3435 },
3436
3437 [MV88E6352] = {
3438 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3439 .family = MV88E6XXX_FAMILY_6352,
3440 .name = "Marvell 88E6352",
3441 .num_databases = 4096,
3442 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003443 .port_base_addr = 0x10,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003444 .age_time_coeff = 15000,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003445 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3446 },
3447};
3448
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003449static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003450{
Vivien Didelota439c062016-04-17 13:23:58 -04003451 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003452
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003453 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3454 if (mv88e6xxx_table[i].prod_num == prod_num)
3455 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003456
Vivien Didelotb9b37712015-10-30 19:39:48 -04003457 return NULL;
3458}
3459
Vivien Didelotfad09c72016-06-21 12:28:20 -04003460static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003461{
3462 const struct mv88e6xxx_info *info;
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003463 unsigned int prod_num, rev;
3464 u16 id;
3465 int err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003466
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003467 mutex_lock(&chip->reg_lock);
3468 err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
3469 mutex_unlock(&chip->reg_lock);
3470 if (err)
3471 return err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003472
3473 prod_num = (id & 0xfff0) >> 4;
3474 rev = id & 0x000f;
3475
3476 info = mv88e6xxx_lookup_info(prod_num);
3477 if (!info)
3478 return -ENODEV;
3479
Vivien Didelotcaac8542016-06-20 13:14:09 -04003480 /* Update the compatible info with the probed one */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003481 chip->info = info;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003482
Vivien Didelotca070c12016-09-02 14:45:34 -04003483 err = mv88e6xxx_g2_require(chip);
3484 if (err)
3485 return err;
3486
Vivien Didelotfad09c72016-06-21 12:28:20 -04003487 dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3488 chip->info->prod_num, chip->info->name, rev);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003489
3490 return 0;
3491}
3492
Vivien Didelotfad09c72016-06-21 12:28:20 -04003493static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
Vivien Didelot469d7292016-06-20 13:14:06 -04003494{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003495 struct mv88e6xxx_chip *chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003496
Vivien Didelotfad09c72016-06-21 12:28:20 -04003497 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3498 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003499 return NULL;
3500
Vivien Didelotfad09c72016-06-21 12:28:20 -04003501 chip->dev = dev;
Vivien Didelot469d7292016-06-20 13:14:06 -04003502
Vivien Didelotfad09c72016-06-21 12:28:20 -04003503 mutex_init(&chip->reg_lock);
Vivien Didelot469d7292016-06-20 13:14:06 -04003504
Vivien Didelotfad09c72016-06-21 12:28:20 -04003505 return chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003506}
3507
Vivien Didelotec561272016-09-02 14:45:33 -04003508static const struct mv88e6xxx_ops mv88e6xxx_g2_smi_phy_ops = {
3509 .read = mv88e6xxx_g2_smi_phy_read,
3510 .write = mv88e6xxx_g2_smi_phy_write,
3511};
3512
Vivien Didelote57e5e72016-08-15 17:19:00 -04003513static const struct mv88e6xxx_ops mv88e6xxx_phy_ops = {
3514 .read = mv88e6xxx_read,
3515 .write = mv88e6xxx_write,
3516};
3517
3518static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
3519{
3520 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SMI_PHY)) {
3521 chip->phy_ops = &mv88e6xxx_g2_smi_phy_ops;
3522 } else if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) {
3523 chip->phy_ops = &mv88e6xxx_phy_ppu_ops;
3524 mv88e6xxx_ppu_state_init(chip);
3525 } else {
3526 chip->phy_ops = &mv88e6xxx_phy_ops;
3527 }
3528}
3529
Andrew Lunn930188c2016-08-22 16:01:03 +02003530static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
3531{
3532 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU)) {
3533 mv88e6xxx_ppu_state_destroy(chip);
3534 }
3535}
3536
Vivien Didelotfad09c72016-06-21 12:28:20 -04003537static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003538 struct mii_bus *bus, int sw_addr)
3539{
3540 /* ADDR[0] pin is unavailable externally and considered zero */
3541 if (sw_addr & 0x1)
3542 return -EINVAL;
3543
Vivien Didelot914b32f2016-06-20 13:14:11 -04003544 if (sw_addr == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003545 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
Vivien Didelota0ffff22016-08-15 17:18:58 -04003546 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003547 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
Vivien Didelot914b32f2016-06-20 13:14:11 -04003548 else
3549 return -EINVAL;
3550
Vivien Didelotfad09c72016-06-21 12:28:20 -04003551 chip->bus = bus;
3552 chip->sw_addr = sw_addr;
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003553
3554 return 0;
3555}
3556
Andrew Lunn7b314362016-08-22 16:01:01 +02003557static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
3558{
Vivien Didelot04bed142016-08-31 18:06:13 -04003559 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02003560
3561 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
3562 return DSA_TAG_PROTO_EDSA;
3563
3564 return DSA_TAG_PROTO_DSA;
Andrew Lunn7b314362016-08-22 16:01:01 +02003565}
3566
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003567static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3568 struct device *host_dev, int sw_addr,
3569 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003570{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003571 struct mv88e6xxx_chip *chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003572 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003573 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003574
Vivien Didelota439c062016-04-17 13:23:58 -04003575 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003576 if (!bus)
3577 return NULL;
3578
Vivien Didelotfad09c72016-06-21 12:28:20 -04003579 chip = mv88e6xxx_alloc_chip(dsa_dev);
3580 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003581 return NULL;
3582
Vivien Didelotcaac8542016-06-20 13:14:09 -04003583 /* Legacy SMI probing will only support chips similar to 88E6085 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003584 chip->info = &mv88e6xxx_table[MV88E6085];
Vivien Didelotcaac8542016-06-20 13:14:09 -04003585
Vivien Didelotfad09c72016-06-21 12:28:20 -04003586 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003587 if (err)
3588 goto free;
3589
Vivien Didelotfad09c72016-06-21 12:28:20 -04003590 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003591 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003592 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003593
Vivien Didelote57e5e72016-08-15 17:19:00 -04003594 mv88e6xxx_phy_init(chip);
3595
Vivien Didelotfad09c72016-06-21 12:28:20 -04003596 err = mv88e6xxx_mdio_register(chip, NULL);
Andrew Lunnb516d452016-06-04 21:17:06 +02003597 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003598 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003599
Vivien Didelotfad09c72016-06-21 12:28:20 -04003600 *priv = chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003601
Vivien Didelotfad09c72016-06-21 12:28:20 -04003602 return chip->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003603free:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003604 devm_kfree(dsa_dev, chip);
Vivien Didelot469d7292016-06-20 13:14:06 -04003605
3606 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003607}
3608
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003609static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
3610 const struct switchdev_obj_port_mdb *mdb,
3611 struct switchdev_trans *trans)
3612{
3613 /* We don't need any dynamic resource from the kernel (yet),
3614 * so skip the prepare phase.
3615 */
3616
3617 return 0;
3618}
3619
3620static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
3621 const struct switchdev_obj_port_mdb *mdb,
3622 struct switchdev_trans *trans)
3623{
Vivien Didelot04bed142016-08-31 18:06:13 -04003624 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003625
3626 mutex_lock(&chip->reg_lock);
3627 if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
3628 GLOBAL_ATU_DATA_STATE_MC_STATIC))
3629 netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
3630 mutex_unlock(&chip->reg_lock);
3631}
3632
3633static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
3634 const struct switchdev_obj_port_mdb *mdb)
3635{
Vivien Didelot04bed142016-08-31 18:06:13 -04003636 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003637 int err;
3638
3639 mutex_lock(&chip->reg_lock);
3640 err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
3641 GLOBAL_ATU_DATA_STATE_UNUSED);
3642 mutex_unlock(&chip->reg_lock);
3643
3644 return err;
3645}
3646
3647static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
3648 struct switchdev_obj_port_mdb *mdb,
3649 int (*cb)(struct switchdev_obj *obj))
3650{
Vivien Didelot04bed142016-08-31 18:06:13 -04003651 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003652 int err;
3653
3654 mutex_lock(&chip->reg_lock);
3655 err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
3656 mutex_unlock(&chip->reg_lock);
3657
3658 return err;
3659}
3660
Vivien Didelot9d490b42016-08-23 12:38:56 -04003661static struct dsa_switch_ops mv88e6xxx_switch_ops = {
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003662 .probe = mv88e6xxx_drv_probe,
Andrew Lunn7b314362016-08-22 16:01:01 +02003663 .get_tag_protocol = mv88e6xxx_get_tag_protocol,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003664 .setup = mv88e6xxx_setup,
3665 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003666 .adjust_link = mv88e6xxx_adjust_link,
3667 .get_strings = mv88e6xxx_get_strings,
3668 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3669 .get_sset_count = mv88e6xxx_get_sset_count,
3670 .set_eee = mv88e6xxx_set_eee,
3671 .get_eee = mv88e6xxx_get_eee,
3672#ifdef CONFIG_NET_DSA_HWMON
3673 .get_temp = mv88e6xxx_get_temp,
3674 .get_temp_limit = mv88e6xxx_get_temp_limit,
3675 .set_temp_limit = mv88e6xxx_set_temp_limit,
3676 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3677#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003678 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003679 .get_eeprom = mv88e6xxx_get_eeprom,
3680 .set_eeprom = mv88e6xxx_set_eeprom,
3681 .get_regs_len = mv88e6xxx_get_regs_len,
3682 .get_regs = mv88e6xxx_get_regs,
Vivien Didelot2cfcd962016-07-18 20:45:40 -04003683 .set_ageing_time = mv88e6xxx_set_ageing_time,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003684 .port_bridge_join = mv88e6xxx_port_bridge_join,
3685 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3686 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3687 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3688 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3689 .port_vlan_add = mv88e6xxx_port_vlan_add,
3690 .port_vlan_del = mv88e6xxx_port_vlan_del,
3691 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3692 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3693 .port_fdb_add = mv88e6xxx_port_fdb_add,
3694 .port_fdb_del = mv88e6xxx_port_fdb_del,
3695 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003696 .port_mdb_prepare = mv88e6xxx_port_mdb_prepare,
3697 .port_mdb_add = mv88e6xxx_port_mdb_add,
3698 .port_mdb_del = mv88e6xxx_port_mdb_del,
3699 .port_mdb_dump = mv88e6xxx_port_mdb_dump,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003700};
3701
Vivien Didelotfad09c72016-06-21 12:28:20 -04003702static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003703 struct device_node *np)
3704{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003705 struct device *dev = chip->dev;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003706 struct dsa_switch *ds;
3707
3708 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3709 if (!ds)
3710 return -ENOMEM;
3711
3712 ds->dev = dev;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003713 ds->priv = chip;
Vivien Didelot9d490b42016-08-23 12:38:56 -04003714 ds->ops = &mv88e6xxx_switch_ops;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003715
3716 dev_set_drvdata(dev, ds);
3717
3718 return dsa_register_switch(ds, np);
3719}
3720
Vivien Didelotfad09c72016-06-21 12:28:20 -04003721static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003722{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003723 dsa_unregister_switch(chip->ds);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003724}
3725
Vivien Didelot57d32312016-06-20 13:13:58 -04003726static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003727{
3728 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003729 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003730 const struct mv88e6xxx_info *compat_info;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003731 struct mv88e6xxx_chip *chip;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003732 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003733 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003734
Vivien Didelotcaac8542016-06-20 13:14:09 -04003735 compat_info = of_device_get_match_data(dev);
3736 if (!compat_info)
3737 return -EINVAL;
3738
Vivien Didelotfad09c72016-06-21 12:28:20 -04003739 chip = mv88e6xxx_alloc_chip(dev);
3740 if (!chip)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003741 return -ENOMEM;
3742
Vivien Didelotfad09c72016-06-21 12:28:20 -04003743 chip->info = compat_info;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003744
Vivien Didelotfad09c72016-06-21 12:28:20 -04003745 err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003746 if (err)
3747 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003748
Vivien Didelotfad09c72016-06-21 12:28:20 -04003749 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003750 if (err)
3751 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003752
Vivien Didelote57e5e72016-08-15 17:19:00 -04003753 mv88e6xxx_phy_init(chip);
3754
Vivien Didelotfad09c72016-06-21 12:28:20 -04003755 chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
3756 if (IS_ERR(chip->reset))
3757 return PTR_ERR(chip->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02003758
Vivien Didelot855b1932016-07-20 18:18:35 -04003759 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_EEPROM16) &&
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003760 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003761 chip->eeprom_len = eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003762
Vivien Didelotfad09c72016-06-21 12:28:20 -04003763 err = mv88e6xxx_mdio_register(chip, np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003764 if (err)
3765 return err;
3766
Vivien Didelotfad09c72016-06-21 12:28:20 -04003767 err = mv88e6xxx_register_switch(chip, np);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003768 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003769 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003770 return err;
3771 }
3772
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003773 return 0;
3774}
3775
3776static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3777{
3778 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
Vivien Didelot04bed142016-08-31 18:06:13 -04003779 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003780
Andrew Lunn930188c2016-08-22 16:01:03 +02003781 mv88e6xxx_phy_destroy(chip);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003782 mv88e6xxx_unregister_switch(chip);
3783 mv88e6xxx_mdio_unregister(chip);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003784}
3785
3786static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04003787 {
3788 .compatible = "marvell,mv88e6085",
3789 .data = &mv88e6xxx_table[MV88E6085],
3790 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003791 { /* sentinel */ },
3792};
3793
3794MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3795
3796static struct mdio_driver mv88e6xxx_driver = {
3797 .probe = mv88e6xxx_probe,
3798 .remove = mv88e6xxx_remove,
3799 .mdiodrv.driver = {
3800 .name = "mv88e6085",
3801 .of_match_table = mv88e6xxx_of_match,
3802 },
3803};
3804
Ben Hutchings98e67302011-11-25 14:36:19 +00003805static int __init mv88e6xxx_init(void)
3806{
Vivien Didelot9d490b42016-08-23 12:38:56 -04003807 register_switch_driver(&mv88e6xxx_switch_ops);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003808 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003809}
3810module_init(mv88e6xxx_init);
3811
3812static void __exit mv88e6xxx_cleanup(void)
3813{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003814 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelot9d490b42016-08-23 12:38:56 -04003815 unregister_switch_driver(&mv88e6xxx_switch_ops);
Ben Hutchings98e67302011-11-25 14:36:19 +00003816}
3817module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003818
3819MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3820MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3821MODULE_LICENSE("GPL");