blob: 3eaedbb12815eba2ddc5dbaf1e908eb7343f5893 [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Florian Fainelli246d7f72014-08-27 17:04:56 -07002/*
3 * Broadcom Starfighter 2 DSA switch driver
4 *
5 * Copyright (C) 2014, Broadcom Corporation
Florian Fainelli246d7f72014-08-27 17:04:56 -07006 */
7
8#include <linux/list.h>
9#include <linux/module.h>
10#include <linux/netdevice.h>
11#include <linux/interrupt.h>
12#include <linux/platform_device.h>
Florian Fainelli246d7f72014-08-27 17:04:56 -070013#include <linux/phy.h>
14#include <linux/phy_fixed.h>
Florian Fainellibc0cb652018-05-10 13:17:33 -070015#include <linux/phylink.h>
Florian Fainelli246d7f72014-08-27 17:04:56 -070016#include <linux/mii.h>
Florian Fainellie9ec5c32020-09-01 15:59:12 -070017#include <linux/clk.h>
Florian Fainelli246d7f72014-08-27 17:04:56 -070018#include <linux/of.h>
19#include <linux/of_irq.h>
20#include <linux/of_address.h>
Florian Fainelli8b7c94e2015-10-23 12:11:08 -070021#include <linux/of_net.h>
Florian Fainelli461cd1b02016-06-07 16:32:43 -070022#include <linux/of_mdio.h>
Florian Fainelli246d7f72014-08-27 17:04:56 -070023#include <net/dsa.h>
Florian Fainelli96e65d72014-09-18 17:31:25 -070024#include <linux/ethtool.h>
Florian Fainelli12f460f2015-02-24 13:15:34 -080025#include <linux/if_bridge.h>
Florian Fainelliaafc66f2015-06-10 18:08:01 -070026#include <linux/brcmphy.h>
Florian Fainelli680060d2015-10-23 11:38:07 -070027#include <linux/etherdevice.h>
Florian Fainellif4589952016-08-26 12:18:33 -070028#include <linux/platform_data/b53.h>
Florian Fainelli246d7f72014-08-27 17:04:56 -070029
30#include "bcm_sf2.h"
31#include "bcm_sf2_regs.h"
Florian Fainellif4589952016-08-26 12:18:33 -070032#include "b53/b53_priv.h"
33#include "b53/b53_regs.h"
Florian Fainelli246d7f72014-08-27 17:04:56 -070034
Florian Fainelli2ee3adc2020-09-01 15:59:13 -070035/* Return the number of active ports, not counting the IMP (CPU) port */
36static unsigned int bcm_sf2_num_active_ports(struct dsa_switch *ds)
37{
38 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
39 unsigned int port, count = 0;
40
41 for (port = 0; port < ARRAY_SIZE(priv->port_sts); port++) {
42 if (dsa_is_cpu_port(ds, port))
43 continue;
44 if (priv->port_sts[port].enabled)
45 count++;
46 }
47
48 return count;
49}
50
51static void bcm_sf2_recalc_clock(struct dsa_switch *ds)
52{
53 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
54 unsigned long new_rate;
55 unsigned int ports_active;
56 /* Frequenty in Mhz */
Colin Ian Kingd978d6d2020-10-20 17:50:29 +010057 static const unsigned long rate_table[] = {
Florian Fainelli2ee3adc2020-09-01 15:59:13 -070058 59220000,
59 60820000,
60 62500000,
61 62500000,
62 };
63
64 ports_active = bcm_sf2_num_active_ports(ds);
65 if (ports_active == 0 || !priv->clk_mdiv)
66 return;
67
68 /* If we overflow our table, just use the recommended operational
69 * frequency
70 */
71 if (ports_active > ARRAY_SIZE(rate_table))
72 new_rate = 90000000;
73 else
74 new_rate = rate_table[ports_active - 1];
75 clk_set_rate(priv->clk_mdiv, new_rate);
76}
77
Florian Fainelliebb2ac42017-01-20 12:36:31 -080078static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
79{
80 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainellic837fc82017-09-03 20:27:03 -070081 unsigned int i;
Florian Fainelliebb2ac42017-01-20 12:36:31 -080082 u32 reg, offset;
83
Florian Fainelliebb2ac42017-01-20 12:36:31 -080084 /* Enable the port memories */
85 reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
86 reg &= ~P_TXQ_PSM_VDD(port);
87 core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
88
Florian Fainelliebb2ac42017-01-20 12:36:31 -080089 /* Enable forwarding */
90 core_writel(priv, SW_FWDG_EN, CORE_SWMODE);
91
92 /* Enable IMP port in dumb mode */
93 reg = core_readl(priv, CORE_SWITCH_CTRL);
94 reg |= MII_DUMB_FWDG_EN;
95 core_writel(priv, reg, CORE_SWITCH_CTRL);
96
Florian Fainellic837fc82017-09-03 20:27:03 -070097 /* Configure Traffic Class to QoS mapping, allow each priority to map
98 * to a different queue number
99 */
100 reg = core_readl(priv, CORE_PORT_TC2_QOS_MAP_PORT(port));
101 for (i = 0; i < SF2_NUM_EGRESS_QUEUES; i++)
102 reg |= i << (PRT_TO_QID_SHIFT * i);
103 core_writel(priv, reg, CORE_PORT_TC2_QOS_MAP_PORT(port));
104
Florian Fainellib409a9e2017-09-19 10:46:48 -0700105 b53_brcm_hdr_setup(ds, port);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700106
Florian Fainelli5fc0f212019-10-31 15:54:05 -0700107 if (port == 8) {
Rafał Miłecki73b7a602021-01-06 22:32:02 +0100108 if (priv->type == BCM4908_DEVICE_ID ||
109 priv->type == BCM7445_DEVICE_ID)
Florian Fainelli5fc0f212019-10-31 15:54:05 -0700110 offset = CORE_STS_OVERRIDE_IMP;
111 else
112 offset = CORE_STS_OVERRIDE_IMP2;
113
114 /* Force link status for IMP port */
115 reg = core_readl(priv, offset);
Florian Fainellide34d702020-02-06 11:23:52 -0800116 reg |= (MII_SW_OR | LINK_STS);
Florian Fainelli98c5f7d2020-02-24 15:56:32 -0800117 reg &= ~GMII_SPEED_UP_2G;
Florian Fainelli5fc0f212019-10-31 15:54:05 -0700118 core_writel(priv, reg, offset);
119
120 /* Enable Broadcast, Multicast, Unicast forwarding to IMP port */
121 reg = core_readl(priv, CORE_IMP_CTL);
122 reg |= (RX_BCST_EN | RX_MCST_EN | RX_UCST_EN);
123 reg &= ~(RX_DIS | TX_DIS);
124 core_writel(priv, reg, CORE_IMP_CTL);
125 } else {
126 reg = core_readl(priv, CORE_G_PCTL_PORT(port));
127 reg &= ~(RX_DIS | TX_DIS);
128 core_writel(priv, reg, CORE_G_PCTL_PORT(port));
129 }
Florian Fainelli2ee3adc2020-09-01 15:59:13 -0700130
131 priv->port_sts[port].enabled = true;
Florian Fainelli246d7f72014-08-27 17:04:56 -0700132}
133
Florian Fainellib0836682015-02-05 11:40:41 -0800134static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable)
135{
Florian Fainellif4589952016-08-26 12:18:33 -0700136 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainellib0836682015-02-05 11:40:41 -0800137 u32 reg;
138
Florian Fainelli9af197a2015-02-05 11:40:42 -0800139 reg = reg_readl(priv, REG_SPHY_CNTRL);
140 if (enable) {
141 reg |= PHY_RESET;
Florian Fainelli4b52d012017-11-21 17:37:46 -0800142 reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS | IDDQ_GLOBAL_PWR | CK25_DIS);
Florian Fainelli9af197a2015-02-05 11:40:42 -0800143 reg_writel(priv, reg, REG_SPHY_CNTRL);
144 udelay(21);
145 reg = reg_readl(priv, REG_SPHY_CNTRL);
146 reg &= ~PHY_RESET;
147 } else {
148 reg |= EXT_PWR_DOWN | IDDQ_BIAS | PHY_RESET;
149 reg_writel(priv, reg, REG_SPHY_CNTRL);
150 mdelay(1);
151 reg |= CK25_DIS;
152 }
153 reg_writel(priv, reg, REG_SPHY_CNTRL);
Florian Fainellib0836682015-02-05 11:40:41 -0800154
Florian Fainelli9af197a2015-02-05 11:40:42 -0800155 /* Use PHY-driven LED signaling */
156 if (!enable) {
157 reg = reg_readl(priv, REG_LED_CNTRL(0));
158 reg |= SPDLNK_SRC_SEL;
159 reg_writel(priv, reg, REG_LED_CNTRL(0));
160 }
Florian Fainellib0836682015-02-05 11:40:41 -0800161}
162
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700163static inline void bcm_sf2_port_intr_enable(struct bcm_sf2_priv *priv,
164 int port)
165{
166 unsigned int off;
167
168 switch (port) {
169 case 7:
170 off = P7_IRQ_OFF;
171 break;
172 case 0:
173 /* Port 0 interrupts are located on the first bank */
174 intrl2_0_mask_clear(priv, P_IRQ_MASK(P0_IRQ_OFF));
175 return;
176 default:
177 off = P_IRQ_OFF(port);
178 break;
179 }
180
181 intrl2_1_mask_clear(priv, P_IRQ_MASK(off));
182}
183
184static inline void bcm_sf2_port_intr_disable(struct bcm_sf2_priv *priv,
185 int port)
186{
187 unsigned int off;
188
189 switch (port) {
190 case 7:
191 off = P7_IRQ_OFF;
192 break;
193 case 0:
194 /* Port 0 interrupts are located on the first bank */
195 intrl2_0_mask_set(priv, P_IRQ_MASK(P0_IRQ_OFF));
196 intrl2_0_writel(priv, P_IRQ_MASK(P0_IRQ_OFF), INTRL2_CPU_CLEAR);
197 return;
198 default:
199 off = P_IRQ_OFF(port);
200 break;
201 }
202
203 intrl2_1_mask_set(priv, P_IRQ_MASK(off));
204 intrl2_1_writel(priv, P_IRQ_MASK(off), INTRL2_CPU_CLEAR);
205}
206
Florian Fainellib6d045d2014-09-24 17:05:20 -0700207static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
208 struct phy_device *phy)
Florian Fainelli246d7f72014-08-27 17:04:56 -0700209{
Florian Fainellif4589952016-08-26 12:18:33 -0700210 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainellie1b91472017-01-30 09:48:41 -0800211 unsigned int i;
Florian Fainelli246d7f72014-08-27 17:04:56 -0700212 u32 reg;
213
Vivien Didelot74be4ba2019-08-19 16:00:49 -0400214 if (!dsa_is_user_port(ds, port))
215 return 0;
216
Florian Fainelli2ee3adc2020-09-01 15:59:13 -0700217 priv->port_sts[port].enabled = true;
218
219 bcm_sf2_recalc_clock(ds);
220
Florian Fainelli246d7f72014-08-27 17:04:56 -0700221 /* Clear the memory power down */
222 reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
223 reg &= ~P_TXQ_PSM_VDD(port);
224 core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
225
Florian Fainellic0e68202018-08-03 11:08:42 -0700226 /* Enable learning */
227 reg = core_readl(priv, CORE_DIS_LEARN);
228 reg &= ~BIT(port);
229 core_writel(priv, reg, CORE_DIS_LEARN);
230
Florian Fainelli64ff2ae2017-01-20 12:36:32 -0800231 /* Enable Broadcom tags for that port if requested */
Florian Fainelli8b6b2082020-03-30 14:38:50 -0700232 if (priv->brcm_tag_mask & BIT(port)) {
Florian Fainellib409a9e2017-09-19 10:46:48 -0700233 b53_brcm_hdr_setup(ds, port);
Florian Fainelli64ff2ae2017-01-20 12:36:32 -0800234
Florian Fainelli8b6b2082020-03-30 14:38:50 -0700235 /* Disable learning on ASP port */
236 if (port == 7) {
237 reg = core_readl(priv, CORE_DIS_LEARN);
238 reg |= BIT(port);
239 core_writel(priv, reg, CORE_DIS_LEARN);
240 }
241 }
242
Florian Fainellie1b91472017-01-30 09:48:41 -0800243 /* Configure Traffic Class to QoS mapping, allow each priority to map
244 * to a different queue number
245 */
246 reg = core_readl(priv, CORE_PORT_TC2_QOS_MAP_PORT(port));
Florian Fainelli181183772017-09-03 20:27:02 -0700247 for (i = 0; i < SF2_NUM_EGRESS_QUEUES; i++)
Florian Fainellie1b91472017-01-30 09:48:41 -0800248 reg |= i << (PRT_TO_QID_SHIFT * i);
249 core_writel(priv, reg, CORE_PORT_TC2_QOS_MAP_PORT(port));
250
Florian Fainelli9af197a2015-02-05 11:40:42 -0800251 /* Re-enable the GPHY and re-apply workarounds */
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700252 if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1) {
Florian Fainelli9af197a2015-02-05 11:40:42 -0800253 bcm_sf2_gphy_enable_set(ds, true);
254 if (phy) {
255 /* if phy_stop() has been called before, phy
256 * will be in halted state, and phy_start()
257 * will call resume.
258 *
259 * the resume path does not configure back
260 * autoneg settings, and since we hard reset
261 * the phy manually here, we need to reset the
262 * state machine also.
263 */
264 phy->state = PHY_READY;
265 phy_init_hw(phy);
266 }
267 }
268
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700269 /* Enable MoCA port interrupts to get notified */
270 if (port == priv->moca_port)
271 bcm_sf2_port_intr_enable(priv, port);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700272
Florian Fainelli32e47ff2017-10-11 10:57:51 -0700273 /* Set per-queue pause threshold to 32 */
274 core_writel(priv, 32, CORE_TXQ_THD_PAUSE_QN_PORT(port));
275
276 /* Set ACB threshold to 24 */
277 for (i = 0; i < SF2_NUM_EGRESS_QUEUES; i++) {
278 reg = acb_readl(priv, ACB_QUEUE_CFG(port *
279 SF2_NUM_EGRESS_QUEUES + i));
280 reg &= ~XOFF_THRESHOLD_MASK;
281 reg |= 24;
282 acb_writel(priv, reg, ACB_QUEUE_CFG(port *
283 SF2_NUM_EGRESS_QUEUES + i));
284 }
285
Florian Fainellif86ad772017-09-19 10:46:54 -0700286 return b53_enable_port(ds, port, phy);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700287}
288
Andrew Lunn75104db2019-02-24 20:44:43 +0100289static void bcm_sf2_port_disable(struct dsa_switch *ds, int port)
Florian Fainelli246d7f72014-08-27 17:04:56 -0700290{
Florian Fainellif4589952016-08-26 12:18:33 -0700291 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Colin Ian King5c17a072018-07-04 07:54:36 +0100292 u32 reg;
Florian Fainelli246d7f72014-08-27 17:04:56 -0700293
Florian Fainellic0e68202018-08-03 11:08:42 -0700294 /* Disable learning while in WoL mode */
295 if (priv->wol_ports_mask & (1 << port)) {
296 reg = core_readl(priv, CORE_DIS_LEARN);
297 reg |= BIT(port);
298 core_writel(priv, reg, CORE_DIS_LEARN);
Florian Fainelli96e65d72014-09-18 17:31:25 -0700299 return;
Florian Fainellic0e68202018-08-03 11:08:42 -0700300 }
Florian Fainelli96e65d72014-09-18 17:31:25 -0700301
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700302 if (port == priv->moca_port)
303 bcm_sf2_port_intr_disable(priv, port);
Florian Fainellib6d045d2014-09-24 17:05:20 -0700304
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700305 if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1)
Florian Fainelli9af197a2015-02-05 11:40:42 -0800306 bcm_sf2_gphy_enable_set(ds, false);
307
Andrew Lunn75104db2019-02-24 20:44:43 +0100308 b53_disable_port(ds, port);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700309
310 /* Power down the port memory */
311 reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
312 reg |= P_TXQ_PSM_VDD(port);
313 core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL);
Florian Fainelli2ee3adc2020-09-01 15:59:13 -0700314
315 priv->port_sts[port].enabled = false;
316
317 bcm_sf2_recalc_clock(ds);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700318}
319
Florian Fainelli450b05c2014-09-24 17:05:22 -0700320
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700321static int bcm_sf2_sw_indir_rw(struct bcm_sf2_priv *priv, int op, int addr,
322 int regnum, u16 val)
323{
324 int ret = 0;
325 u32 reg;
326
327 reg = reg_readl(priv, REG_SWITCH_CNTRL);
328 reg |= MDIO_MASTER_SEL;
329 reg_writel(priv, reg, REG_SWITCH_CNTRL);
330
331 /* Page << 8 | offset */
332 reg = 0x70;
333 reg <<= 2;
334 core_writel(priv, addr, reg);
335
336 /* Page << 8 | offset */
337 reg = 0x80 << 8 | regnum << 1;
338 reg <<= 2;
339
340 if (op)
341 ret = core_readl(priv, reg);
342 else
343 core_writel(priv, val, reg);
344
345 reg = reg_readl(priv, REG_SWITCH_CNTRL);
346 reg &= ~MDIO_MASTER_SEL;
347 reg_writel(priv, reg, REG_SWITCH_CNTRL);
348
349 return ret & 0xffff;
350}
351
352static int bcm_sf2_sw_mdio_read(struct mii_bus *bus, int addr, int regnum)
353{
354 struct bcm_sf2_priv *priv = bus->priv;
355
356 /* Intercept reads from Broadcom pseudo-PHY address, else, send
357 * them to our master MDIO bus controller
358 */
359 if (addr == BRCM_PSEUDO_PHY_ADDR && priv->indir_phy_mask & BIT(addr))
360 return bcm_sf2_sw_indir_rw(priv, 1, addr, regnum, 0);
361 else
Florian Fainelli2cfe8f822017-01-07 21:01:57 -0800362 return mdiobus_read_nested(priv->master_mii_bus, addr, regnum);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700363}
364
365static int bcm_sf2_sw_mdio_write(struct mii_bus *bus, int addr, int regnum,
366 u16 val)
367{
368 struct bcm_sf2_priv *priv = bus->priv;
369
370 /* Intercept writes to the Broadcom pseudo-PHY address, else,
371 * send them to our master MDIO bus controller
372 */
373 if (addr == BRCM_PSEUDO_PHY_ADDR && priv->indir_phy_mask & BIT(addr))
Kangjie Lue49505f2018-12-25 22:08:18 -0600374 return bcm_sf2_sw_indir_rw(priv, 0, addr, regnum, val);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700375 else
Kangjie Lue49505f2018-12-25 22:08:18 -0600376 return mdiobus_write_nested(priv->master_mii_bus, addr,
377 regnum, val);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700378}
379
Florian Fainelli246d7f72014-08-27 17:04:56 -0700380static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
381{
Florian Fainellibc0cb652018-05-10 13:17:33 -0700382 struct dsa_switch *ds = dev_id;
383 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700384
385 priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
386 ~priv->irq0_mask;
387 intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR);
388
389 return IRQ_HANDLED;
390}
391
392static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
393{
Florian Fainellibc0cb652018-05-10 13:17:33 -0700394 struct dsa_switch *ds = dev_id;
395 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelli246d7f72014-08-27 17:04:56 -0700396
397 priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
398 ~priv->irq1_mask;
399 intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
400
Florian Fainellibc0cb652018-05-10 13:17:33 -0700401 if (priv->irq1_stat & P_LINK_UP_IRQ(P7_IRQ_OFF)) {
402 priv->port_sts[7].link = true;
403 dsa_port_phylink_mac_change(ds, 7, true);
404 }
405 if (priv->irq1_stat & P_LINK_DOWN_IRQ(P7_IRQ_OFF)) {
406 priv->port_sts[7].link = false;
407 dsa_port_phylink_mac_change(ds, 7, false);
408 }
Florian Fainelli246d7f72014-08-27 17:04:56 -0700409
410 return IRQ_HANDLED;
411}
412
Florian Fainelli33f84612014-11-25 18:08:49 -0800413static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv)
414{
415 unsigned int timeout = 1000;
416 u32 reg;
Florian Fainellieee87e42019-11-04 13:51:39 -0800417 int ret;
418
419 /* The watchdog reset does not work on 7278, we need to hit the
420 * "external" reset line through the reset controller.
421 */
422 if (priv->type == BCM7278_DEVICE_ID && !IS_ERR(priv->rcdev)) {
423 ret = reset_control_assert(priv->rcdev);
424 if (ret)
425 return ret;
426
427 return reset_control_deassert(priv->rcdev);
428 }
Florian Fainelli33f84612014-11-25 18:08:49 -0800429
430 reg = core_readl(priv, CORE_WATCHDOG_CTRL);
431 reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET;
432 core_writel(priv, reg, CORE_WATCHDOG_CTRL);
433
434 do {
435 reg = core_readl(priv, CORE_WATCHDOG_CTRL);
436 if (!(reg & SOFTWARE_RESET))
437 break;
438
439 usleep_range(1000, 2000);
440 } while (timeout-- > 0);
441
442 if (timeout == 0)
443 return -ETIMEDOUT;
444
445 return 0;
446}
447
Florian Fainelli691c9a82015-01-20 16:42:00 -0800448static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv)
449{
Florian Fainellif01d5982016-08-25 15:23:41 -0700450 intrl2_0_mask_set(priv, 0xffffffff);
Florian Fainelli691c9a82015-01-20 16:42:00 -0800451 intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
Florian Fainellif01d5982016-08-25 15:23:41 -0700452 intrl2_1_mask_set(priv, 0xffffffff);
Florian Fainelli691c9a82015-01-20 16:42:00 -0800453 intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
Florian Fainelli691c9a82015-01-20 16:42:00 -0800454}
455
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700456static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,
457 struct device_node *dn)
458{
459 struct device_node *port;
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700460 unsigned int port_num;
Florian Fainelli8c280442020-09-22 13:03:55 -0700461 struct property *prop;
Andrew Lunn0c65b2b2019-11-04 02:40:33 +0100462 phy_interface_t mode;
463 int err;
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700464
465 priv->moca_port = -1;
466
467 for_each_available_child_of_node(dn, port) {
468 if (of_property_read_u32(port, "reg", &port_num))
469 continue;
470
471 /* Internal PHYs get assigned a specific 'phy-mode' property
472 * value: "internal" to help flag them before MDIO probing
473 * has completed, since they might be turned off at that
474 * time
475 */
Andrew Lunn0c65b2b2019-11-04 02:40:33 +0100476 err = of_get_phy_mode(port, &mode);
477 if (err)
Florian Fainellibedd00c2017-06-23 10:33:16 -0700478 continue;
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700479
Florian Fainellibedd00c2017-06-23 10:33:16 -0700480 if (mode == PHY_INTERFACE_MODE_INTERNAL)
481 priv->int_phy_mask |= 1 << port_num;
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700482
483 if (mode == PHY_INTERFACE_MODE_MOCA)
484 priv->moca_port = port_num;
Florian Fainelli64ff2ae2017-01-20 12:36:32 -0800485
486 if (of_property_read_bool(port, "brcm,use-bcm-hdr"))
487 priv->brcm_tag_mask |= 1 << port_num;
Florian Fainelli8c280442020-09-22 13:03:55 -0700488
489 /* Ensure that port 5 is not picked up as a DSA CPU port
490 * flavour but a regular port instead. We should be using
491 * devlink to be able to set the port flavour.
492 */
493 if (port_num == 5 && priv->type == BCM7278_DEVICE_ID) {
494 prop = of_find_property(port, "ethernet", NULL);
495 if (prop)
496 of_remove_property(port, prop);
497 }
Florian Fainelli8b7c94e2015-10-23 12:11:08 -0700498 }
499}
500
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700501static int bcm_sf2_mdio_register(struct dsa_switch *ds)
502{
Florian Fainellif4589952016-08-26 12:18:33 -0700503 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelli771089c2020-09-04 14:37:30 -0700504 struct device_node *dn, *child;
505 struct phy_device *phydev;
506 struct property *prop;
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700507 static int index;
Florian Fainelli771089c2020-09-04 14:37:30 -0700508 int err, reg;
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700509
510 /* Find our integrated MDIO bus node */
511 dn = of_find_compatible_node(NULL, NULL, "brcm,unimac-mdio");
512 priv->master_mii_bus = of_mdio_find_bus(dn);
Pan Biancf3c4662021-01-21 04:33:43 -0800513 if (!priv->master_mii_bus) {
514 of_node_put(dn);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700515 return -EPROBE_DEFER;
Pan Biancf3c4662021-01-21 04:33:43 -0800516 }
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700517
518 get_device(&priv->master_mii_bus->dev);
519 priv->master_mii_dn = dn;
520
521 priv->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
Pan Biancf3c4662021-01-21 04:33:43 -0800522 if (!priv->slave_mii_bus) {
523 of_node_put(dn);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700524 return -ENOMEM;
Pan Biancf3c4662021-01-21 04:33:43 -0800525 }
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700526
527 priv->slave_mii_bus->priv = priv;
528 priv->slave_mii_bus->name = "sf2 slave mii";
529 priv->slave_mii_bus->read = bcm_sf2_sw_mdio_read;
530 priv->slave_mii_bus->write = bcm_sf2_sw_mdio_write;
531 snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "sf2-%d",
532 index++);
533 priv->slave_mii_bus->dev.of_node = dn;
534
535 /* Include the pseudo-PHY address to divert reads towards our
536 * workaround. This is only required for 7445D0, since 7445E0
537 * disconnects the internal switch pseudo-PHY such that we can use the
538 * regular SWITCH_MDIO master controller instead.
539 *
540 * Here we flag the pseudo PHY as needing special treatment and would
541 * otherwise make all other PHY read/writes go to the master MDIO bus
542 * controller that comes with this switch backed by the "mdio-unimac"
543 * driver.
544 */
545 if (of_machine_is_compatible("brcm,bcm7445d0"))
Florian Fainelli0fa45ee2020-09-22 13:03:56 -0700546 priv->indir_phy_mask |= (1 << BRCM_PSEUDO_PHY_ADDR) | (1 << 0);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700547 else
548 priv->indir_phy_mask = 0;
549
550 ds->phys_mii_mask = priv->indir_phy_mask;
551 ds->slave_mii_bus = priv->slave_mii_bus;
552 priv->slave_mii_bus->parent = ds->dev->parent;
553 priv->slave_mii_bus->phy_mask = ~priv->indir_phy_mask;
554
Florian Fainelli771089c2020-09-04 14:37:30 -0700555 /* We need to make sure that of_phy_connect() will not work by
556 * removing the 'phandle' and 'linux,phandle' properties and
557 * unregister the existing PHY device that was already registered.
558 */
559 for_each_available_child_of_node(dn, child) {
560 if (of_property_read_u32(child, "reg", &reg) ||
561 reg >= PHY_MAX_ADDR)
562 continue;
563
564 if (!(priv->indir_phy_mask & BIT(reg)))
565 continue;
566
567 prop = of_find_property(child, "phandle", NULL);
568 if (prop)
569 of_remove_property(child, prop);
570
571 prop = of_find_property(child, "linux,phandle", NULL);
572 if (prop)
573 of_remove_property(child, prop);
574
575 phydev = of_phy_find_device(child);
576 if (phydev)
577 phy_device_remove(phydev);
578 }
579
Florian Fainelli536fab52020-04-04 14:35:17 -0700580 err = mdiobus_register(priv->slave_mii_bus);
Florian Fainelli00e798c2018-05-15 16:56:19 -0700581 if (err && dn)
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700582 of_node_put(dn);
583
584 return err;
585}
586
587static void bcm_sf2_mdio_unregister(struct bcm_sf2_priv *priv)
588{
589 mdiobus_unregister(priv->slave_mii_bus);
zhong jiang1ddc5d32018-09-16 21:22:31 +0800590 of_node_put(priv->master_mii_dn);
Florian Fainelli461cd1b02016-06-07 16:32:43 -0700591}
592
Florian Fainelliaa9aef72014-09-19 13:07:55 -0700593static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port)
594{
Florian Fainellif4589952016-08-26 12:18:33 -0700595 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelliaa9aef72014-09-19 13:07:55 -0700596
597 /* The BCM7xxx PHY driver expects to find the integrated PHY revision
598 * in bits 15:8 and the patch level in bits 7:0 which is exactly what
599 * the REG_PHY_REVISION register layout is.
600 */
601
602 return priv->hw_params.gphy_rev;
603}
604
Florian Fainellibc0cb652018-05-10 13:17:33 -0700605static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
606 unsigned long *supported,
607 struct phylink_link_state *state)
608{
Florian Fainelli738a2e42019-08-21 17:07:46 -0700609 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainellibc0cb652018-05-10 13:17:33 -0700610 __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
611
612 if (!phy_interface_mode_is_rgmii(state->interface) &&
613 state->interface != PHY_INTERFACE_MODE_MII &&
614 state->interface != PHY_INTERFACE_MODE_REVMII &&
615 state->interface != PHY_INTERFACE_MODE_GMII &&
616 state->interface != PHY_INTERFACE_MODE_INTERNAL &&
617 state->interface != PHY_INTERFACE_MODE_MOCA) {
618 bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
Florian Fainelli738a2e42019-08-21 17:07:46 -0700619 if (port != core_readl(priv, CORE_IMP0_PRT_ID))
620 dev_err(ds->dev,
621 "Unsupported interface: %d for port %d\n",
622 state->interface, port);
Florian Fainellibc0cb652018-05-10 13:17:33 -0700623 return;
624 }
625
626 /* Allow all the expected bits */
627 phylink_set(mask, Autoneg);
628 phylink_set_port_modes(mask);
629 phylink_set(mask, Pause);
630 phylink_set(mask, Asym_Pause);
631
632 /* With the exclusion of MII and Reverse MII, we support Gigabit,
633 * including Half duplex
634 */
635 if (state->interface != PHY_INTERFACE_MODE_MII &&
636 state->interface != PHY_INTERFACE_MODE_REVMII) {
637 phylink_set(mask, 1000baseT_Full);
638 phylink_set(mask, 1000baseT_Half);
639 }
640
641 phylink_set(mask, 10baseT_Half);
642 phylink_set(mask, 10baseT_Full);
643 phylink_set(mask, 100baseT_Half);
644 phylink_set(mask, 100baseT_Full);
645
646 bitmap_and(supported, supported, mask,
647 __ETHTOOL_LINK_MODE_MASK_NBITS);
648 bitmap_and(state->advertising, state->advertising, mask,
649 __ETHTOOL_LINK_MODE_MASK_NBITS);
650}
651
652static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
653 unsigned int mode,
654 const struct phylink_link_state *state)
655{
656 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
657 u32 id_mode_dis = 0, port_mode;
Russell King50cc20202020-06-30 11:28:13 +0100658 u32 reg;
Florian Fainellibc0cb652018-05-10 13:17:33 -0700659
Florian Fainelli738a2e42019-08-21 17:07:46 -0700660 if (port == core_readl(priv, CORE_IMP0_PRT_ID))
661 return;
662
Florian Fainellibc0cb652018-05-10 13:17:33 -0700663 switch (state->interface) {
664 case PHY_INTERFACE_MODE_RGMII:
665 id_mode_dis = 1;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500666 fallthrough;
Florian Fainellibc0cb652018-05-10 13:17:33 -0700667 case PHY_INTERFACE_MODE_RGMII_TXID:
668 port_mode = EXT_GPHY;
669 break;
670 case PHY_INTERFACE_MODE_MII:
671 port_mode = EXT_EPHY;
672 break;
673 case PHY_INTERFACE_MODE_REVMII:
674 port_mode = EXT_REVMII;
675 break;
676 default:
Russell King50cc20202020-06-30 11:28:13 +0100677 /* Nothing required for all other PHYs: internal and MoCA */
678 return;
Florian Fainellibc0cb652018-05-10 13:17:33 -0700679 }
680
681 /* Clear id_mode_dis bit, and the existing port mode, let
682 * RGMII_MODE_EN bet set by mac_link_{up,down}
683 */
684 reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
685 reg &= ~ID_MODE_DIS;
686 reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT);
Florian Fainellibc0cb652018-05-10 13:17:33 -0700687
688 reg |= port_mode;
689 if (id_mode_dis)
690 reg |= ID_MODE_DIS;
691
Florian Fainellibc0cb652018-05-10 13:17:33 -0700692 reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
Florian Fainellibc0cb652018-05-10 13:17:33 -0700693}
694
695static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
696 phy_interface_t interface, bool link)
697{
698 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
699 u32 reg;
700
701 if (!phy_interface_mode_is_rgmii(interface) &&
702 interface != PHY_INTERFACE_MODE_MII &&
703 interface != PHY_INTERFACE_MODE_REVMII)
704 return;
705
706 /* If the link is down, just disable the interface to conserve power */
707 reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
708 if (link)
709 reg |= RGMII_MODE_EN;
710 else
711 reg &= ~RGMII_MODE_EN;
712 reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
713}
714
715static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
716 unsigned int mode,
717 phy_interface_t interface)
718{
Russell King2d1f90f2020-06-30 11:28:08 +0100719 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
720 u32 reg, offset;
721
722 if (port != core_readl(priv, CORE_IMP0_PRT_ID)) {
Rafał Miłecki73b7a602021-01-06 22:32:02 +0100723 if (priv->type == BCM4908_DEVICE_ID ||
724 priv->type == BCM7445_DEVICE_ID)
Russell King2d1f90f2020-06-30 11:28:08 +0100725 offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
726 else
727 offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
728
729 reg = core_readl(priv, offset);
730 reg &= ~LINK_STS;
731 core_writel(priv, reg, offset);
732 }
733
Florian Fainellibc0cb652018-05-10 13:17:33 -0700734 bcm_sf2_sw_mac_link_set(ds, port, interface, false);
735}
736
737static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
738 unsigned int mode,
739 phy_interface_t interface,
Russell King5b502a72020-02-26 10:23:46 +0000740 struct phy_device *phydev,
741 int speed, int duplex,
742 bool tx_pause, bool rx_pause)
Florian Fainellibc0cb652018-05-10 13:17:33 -0700743{
744 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
745 struct ethtool_eee *p = &priv->dev->ports[port].eee;
Russell King2d1f90f2020-06-30 11:28:08 +0100746 u32 reg, offset;
Florian Fainellibc0cb652018-05-10 13:17:33 -0700747
748 bcm_sf2_sw_mac_link_set(ds, port, interface, true);
749
Russell King2d1f90f2020-06-30 11:28:08 +0100750 if (port != core_readl(priv, CORE_IMP0_PRT_ID)) {
Rafał Miłecki73b7a602021-01-06 22:32:02 +0100751 if (priv->type == BCM4908_DEVICE_ID ||
752 priv->type == BCM7445_DEVICE_ID)
Russell King2d1f90f2020-06-30 11:28:08 +0100753 offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
754 else
755 offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
756
Russell King981015a2020-06-30 11:28:18 +0100757 if (interface == PHY_INTERFACE_MODE_RGMII ||
758 interface == PHY_INTERFACE_MODE_RGMII_TXID ||
759 interface == PHY_INTERFACE_MODE_MII ||
760 interface == PHY_INTERFACE_MODE_REVMII) {
761 reg = reg_readl(priv, REG_RGMII_CNTRL_P(port));
762 reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
763
764 if (tx_pause)
765 reg |= TX_PAUSE_EN;
766 if (rx_pause)
767 reg |= RX_PAUSE_EN;
768
769 reg_writel(priv, reg, REG_RGMII_CNTRL_P(port));
770 }
771
Russell King50cc20202020-06-30 11:28:13 +0100772 reg = SW_OVERRIDE | LINK_STS;
773 switch (speed) {
774 case SPEED_1000:
775 reg |= SPDSTS_1000 << SPEED_SHIFT;
776 break;
777 case SPEED_100:
778 reg |= SPDSTS_100 << SPEED_SHIFT;
779 break;
780 }
781
782 if (duplex == DUPLEX_FULL)
783 reg |= DUPLX_MODE;
784
Russell King2d1f90f2020-06-30 11:28:08 +0100785 core_writel(priv, reg, offset);
786 }
787
Florian Fainellibc0cb652018-05-10 13:17:33 -0700788 if (mode == MLO_AN_PHY && phydev)
789 p->eee_enabled = b53_eee_init(ds, port, phydev);
790}
791
792static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port,
793 struct phylink_link_state *status)
794{
795 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
796
797 status->link = false;
798
799 /* MoCA port is special as we do not get link status from CORE_LNKSTS,
800 * which means that we need to force the link at the port override
801 * level to get the data to flow. We do use what the interrupt handler
802 * did determine before.
803 *
804 * For the other ports, we just force the link status, since this is
805 * a fixed PHY device.
806 */
807 if (port == priv->moca_port) {
808 status->link = priv->port_sts[port].link;
809 /* For MoCA interfaces, also force a link down notification
810 * since some version of the user-space daemon (mocad) use
811 * cmd->autoneg to force the link, which messes up the PHY
812 * state machine and make it go in PHY_FORCING state instead.
813 */
814 if (!status->link)
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400815 netif_carrier_off(dsa_to_port(ds, port)->slave);
Florian Fainellibc0cb652018-05-10 13:17:33 -0700816 status->duplex = DUPLEX_FULL;
817 } else {
818 status->link = true;
819 }
820}
821
Florian Fainelli32e47ff2017-10-11 10:57:51 -0700822static void bcm_sf2_enable_acb(struct dsa_switch *ds)
823{
824 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
825 u32 reg;
826
827 /* Enable ACB globally */
828 reg = acb_readl(priv, ACB_CONTROL);
829 reg |= (ACB_FLUSH_MASK << ACB_FLUSH_SHIFT);
830 acb_writel(priv, reg, ACB_CONTROL);
831 reg &= ~(ACB_FLUSH_MASK << ACB_FLUSH_SHIFT);
832 reg |= ACB_EN | ACB_ALGORITHM;
833 acb_writel(priv, reg, ACB_CONTROL);
834}
835
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700836static int bcm_sf2_sw_suspend(struct dsa_switch *ds)
837{
Florian Fainellif4589952016-08-26 12:18:33 -0700838 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700839 unsigned int port;
840
Florian Fainelli691c9a82015-01-20 16:42:00 -0800841 bcm_sf2_intr_disable(priv);
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700842
843 /* Disable all ports physically present including the IMP
844 * port, the other ones have already been disabled during
845 * bcm_sf2_sw_setup
846 */
Dan Carpenter8d6ea932019-02-13 11:23:04 +0300847 for (port = 0; port < ds->num_ports; port++) {
Vivien Didelot4a5b85f2017-10-26 11:22:55 -0400848 if (dsa_is_user_port(ds, port) || dsa_is_cpu_port(ds, port))
Andrew Lunn75104db2019-02-24 20:44:43 +0100849 bcm_sf2_port_disable(ds, port);
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700850 }
851
Florian Fainellie9ec5c32020-09-01 15:59:12 -0700852 if (!priv->wol_ports_mask)
853 clk_disable_unprepare(priv->clk);
854
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700855 return 0;
856}
857
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700858static int bcm_sf2_sw_resume(struct dsa_switch *ds)
859{
Florian Fainellif4589952016-08-26 12:18:33 -0700860 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700861 int ret;
862
Florian Fainellie9ec5c32020-09-01 15:59:12 -0700863 if (!priv->wol_ports_mask)
864 clk_prepare_enable(priv->clk);
865
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700866 ret = bcm_sf2_sw_rst(priv);
867 if (ret) {
868 pr_err("%s: failed to software reset switch\n", __func__);
869 return ret;
870 }
871
Florian Fainelli1c0130f2018-11-06 12:58:39 -0800872 ret = bcm_sf2_cfp_resume(ds);
873 if (ret)
874 return ret;
875
Florian Fainellib0836682015-02-05 11:40:41 -0800876 if (priv->hw_params.num_gphy == 1)
877 bcm_sf2_gphy_enable_set(ds, true);
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700878
Florian Fainelliabd01ba2018-10-09 16:48:58 -0700879 ds->ops->setup(ds);
Florian Fainelli32e47ff2017-10-11 10:57:51 -0700880
Florian Fainelli8cfa9492014-09-18 17:31:23 -0700881 return 0;
882}
883
Florian Fainelli96e65d72014-09-18 17:31:25 -0700884static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port,
885 struct ethtool_wolinfo *wol)
886{
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400887 struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master;
Florian Fainellif4589952016-08-26 12:18:33 -0700888 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainellic3152ec2019-02-15 12:16:52 -0800889 struct ethtool_wolinfo pwol = { };
Florian Fainelli96e65d72014-09-18 17:31:25 -0700890
891 /* Get the parent device WoL settings */
Florian Fainellic3152ec2019-02-15 12:16:52 -0800892 if (p->ethtool_ops->get_wol)
893 p->ethtool_ops->get_wol(p, &pwol);
Florian Fainelli96e65d72014-09-18 17:31:25 -0700894
895 /* Advertise the parent device supported settings */
896 wol->supported = pwol.supported;
897 memset(&wol->sopass, 0, sizeof(wol->sopass));
898
899 if (pwol.wolopts & WAKE_MAGICSECURE)
900 memcpy(&wol->sopass, pwol.sopass, sizeof(wol->sopass));
901
902 if (priv->wol_ports_mask & (1 << port))
903 wol->wolopts = pwol.wolopts;
904 else
905 wol->wolopts = 0;
906}
907
908static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port,
909 struct ethtool_wolinfo *wol)
910{
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400911 struct net_device *p = dsa_to_port(ds, port)->cpu_dp->master;
Florian Fainellif4589952016-08-26 12:18:33 -0700912 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Vivien Didelot68bb8ea2019-10-21 16:51:15 -0400913 s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
Florian Fainellic3152ec2019-02-15 12:16:52 -0800914 struct ethtool_wolinfo pwol = { };
Florian Fainelli96e65d72014-09-18 17:31:25 -0700915
Florian Fainellic3152ec2019-02-15 12:16:52 -0800916 if (p->ethtool_ops->get_wol)
917 p->ethtool_ops->get_wol(p, &pwol);
Florian Fainelli96e65d72014-09-18 17:31:25 -0700918 if (wol->wolopts & ~pwol.supported)
919 return -EINVAL;
920
921 if (wol->wolopts)
922 priv->wol_ports_mask |= (1 << port);
923 else
924 priv->wol_ports_mask &= ~(1 << port);
925
926 /* If we have at least one port enabled, make sure the CPU port
927 * is also enabled. If the CPU port is the last one enabled, we disable
928 * it since this configuration does not make sense.
929 */
930 if (priv->wol_ports_mask && priv->wol_ports_mask != (1 << cpu_port))
931 priv->wol_ports_mask |= (1 << cpu_port);
932 else
933 priv->wol_ports_mask &= ~(1 << cpu_port);
934
935 return p->ethtool_ops->set_wol(p, wol);
936}
937
Florian Fainelli7fbb1a92016-06-09 17:42:06 -0700938static int bcm_sf2_sw_setup(struct dsa_switch *ds)
939{
Florian Fainellif4589952016-08-26 12:18:33 -0700940 struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -0700941 unsigned int port;
Florian Fainellid9338022016-08-18 15:30:14 -0700942
Florian Fainelli21a27742017-09-28 11:19:06 -0700943 /* Enable all valid ports and disable those unused */
Florian Fainellid9338022016-08-18 15:30:14 -0700944 for (port = 0; port < priv->hw_params.num_ports; port++) {
Florian Fainelli21a27742017-09-28 11:19:06 -0700945 /* IMP port receives special treatment */
Vivien Didelot4a5b85f2017-10-26 11:22:55 -0400946 if (dsa_is_user_port(ds, port))
Florian Fainelli21a27742017-09-28 11:19:06 -0700947 bcm_sf2_port_setup(ds, port, NULL);
948 else if (dsa_is_cpu_port(ds, port))
Florian Fainellid9338022016-08-18 15:30:14 -0700949 bcm_sf2_imp_setup(ds, port);
Florian Fainelli21a27742017-09-28 11:19:06 -0700950 else
Andrew Lunn75104db2019-02-24 20:44:43 +0100951 bcm_sf2_port_disable(ds, port);
Florian Fainellid9338022016-08-18 15:30:14 -0700952 }
953
Florian Fainelli5c1a6ea2017-10-27 15:56:01 -0700954 b53_configure_vlan(ds);
Florian Fainelli32e47ff2017-10-11 10:57:51 -0700955 bcm_sf2_enable_acb(ds);
Florian Fainellid9338022016-08-18 15:30:14 -0700956
Florian Fainelli4f6a5ca2020-09-09 10:49:31 -0700957 return b53_setup_devlink_resources(ds);
958}
959
960static void bcm_sf2_sw_teardown(struct dsa_switch *ds)
961{
962 dsa_devlink_resources_unregister(ds);
Florian Fainellid9338022016-08-18 15:30:14 -0700963}
964
Florian Fainellif4589952016-08-26 12:18:33 -0700965/* The SWITCH_CORE register space is managed by b53 but operates on a page +
966 * register basis so we need to translate that into an address that the
967 * bus-glue understands.
968 */
969#define SF2_PAGE_REG_MKADDR(page, reg) ((page) << 10 | (reg) << 2)
970
971static int bcm_sf2_core_read8(struct b53_device *dev, u8 page, u8 reg,
972 u8 *val)
973{
974 struct bcm_sf2_priv *priv = dev->priv;
975
976 *val = core_readl(priv, SF2_PAGE_REG_MKADDR(page, reg));
977
978 return 0;
979}
980
981static int bcm_sf2_core_read16(struct b53_device *dev, u8 page, u8 reg,
982 u16 *val)
983{
984 struct bcm_sf2_priv *priv = dev->priv;
985
986 *val = core_readl(priv, SF2_PAGE_REG_MKADDR(page, reg));
987
988 return 0;
989}
990
991static int bcm_sf2_core_read32(struct b53_device *dev, u8 page, u8 reg,
992 u32 *val)
993{
994 struct bcm_sf2_priv *priv = dev->priv;
995
996 *val = core_readl(priv, SF2_PAGE_REG_MKADDR(page, reg));
997
998 return 0;
999}
1000
1001static int bcm_sf2_core_read64(struct b53_device *dev, u8 page, u8 reg,
1002 u64 *val)
1003{
1004 struct bcm_sf2_priv *priv = dev->priv;
1005
1006 *val = core_readq(priv, SF2_PAGE_REG_MKADDR(page, reg));
1007
1008 return 0;
1009}
1010
1011static int bcm_sf2_core_write8(struct b53_device *dev, u8 page, u8 reg,
1012 u8 value)
1013{
1014 struct bcm_sf2_priv *priv = dev->priv;
1015
1016 core_writel(priv, value, SF2_PAGE_REG_MKADDR(page, reg));
1017
1018 return 0;
1019}
1020
1021static int bcm_sf2_core_write16(struct b53_device *dev, u8 page, u8 reg,
1022 u16 value)
1023{
1024 struct bcm_sf2_priv *priv = dev->priv;
1025
1026 core_writel(priv, value, SF2_PAGE_REG_MKADDR(page, reg));
1027
1028 return 0;
1029}
1030
1031static int bcm_sf2_core_write32(struct b53_device *dev, u8 page, u8 reg,
1032 u32 value)
1033{
1034 struct bcm_sf2_priv *priv = dev->priv;
1035
1036 core_writel(priv, value, SF2_PAGE_REG_MKADDR(page, reg));
1037
1038 return 0;
1039}
1040
1041static int bcm_sf2_core_write64(struct b53_device *dev, u8 page, u8 reg,
1042 u64 value)
1043{
1044 struct bcm_sf2_priv *priv = dev->priv;
1045
1046 core_writeq(priv, value, SF2_PAGE_REG_MKADDR(page, reg));
1047
1048 return 0;
1049}
1050
Bhumika Goyal7e3108f2017-08-29 22:17:52 +05301051static const struct b53_io_ops bcm_sf2_io_ops = {
Florian Fainellif4589952016-08-26 12:18:33 -07001052 .read8 = bcm_sf2_core_read8,
1053 .read16 = bcm_sf2_core_read16,
1054 .read32 = bcm_sf2_core_read32,
1055 .read48 = bcm_sf2_core_read64,
1056 .read64 = bcm_sf2_core_read64,
1057 .write8 = bcm_sf2_core_write8,
1058 .write16 = bcm_sf2_core_write16,
1059 .write32 = bcm_sf2_core_write32,
1060 .write48 = bcm_sf2_core_write64,
1061 .write64 = bcm_sf2_core_write64,
1062};
1063
Florian Fainellibadd62c2019-02-06 12:45:58 -08001064static void bcm_sf2_sw_get_strings(struct dsa_switch *ds, int port,
1065 u32 stringset, uint8_t *data)
1066{
Florian Fainellif4ae9c02019-02-06 12:45:59 -08001067 int cnt = b53_get_sset_count(ds, port, stringset);
1068
Florian Fainellibadd62c2019-02-06 12:45:58 -08001069 b53_get_strings(ds, port, stringset, data);
Florian Fainellif4ae9c02019-02-06 12:45:59 -08001070 bcm_sf2_cfp_get_strings(ds, port, stringset,
1071 data + cnt * ETH_GSTRING_LEN);
Florian Fainellibadd62c2019-02-06 12:45:58 -08001072}
1073
1074static void bcm_sf2_sw_get_ethtool_stats(struct dsa_switch *ds, int port,
1075 uint64_t *data)
1076{
Florian Fainellif4ae9c02019-02-06 12:45:59 -08001077 int cnt = b53_get_sset_count(ds, port, ETH_SS_STATS);
1078
Florian Fainellibadd62c2019-02-06 12:45:58 -08001079 b53_get_ethtool_stats(ds, port, data);
Florian Fainellif4ae9c02019-02-06 12:45:59 -08001080 bcm_sf2_cfp_get_ethtool_stats(ds, port, data + cnt);
Florian Fainellibadd62c2019-02-06 12:45:58 -08001081}
1082
1083static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds, int port,
1084 int sset)
1085{
Florian Fainellif4ae9c02019-02-06 12:45:59 -08001086 int cnt = b53_get_sset_count(ds, port, sset);
1087
1088 if (cnt < 0)
1089 return cnt;
1090
1091 cnt += bcm_sf2_cfp_get_sset_count(ds, port, sset);
1092
1093 return cnt;
Florian Fainellibadd62c2019-02-06 12:45:58 -08001094}
1095
Florian Fainellia82f67a2017-01-08 14:52:08 -08001096static const struct dsa_switch_ops bcm_sf2_ops = {
Florian Fainelli9f668162017-11-30 09:55:35 -08001097 .get_tag_protocol = b53_get_tag_protocol,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001098 .setup = bcm_sf2_sw_setup,
Florian Fainelli4f6a5ca2020-09-09 10:49:31 -07001099 .teardown = bcm_sf2_sw_teardown,
Florian Fainellibadd62c2019-02-06 12:45:58 -08001100 .get_strings = bcm_sf2_sw_get_strings,
1101 .get_ethtool_stats = bcm_sf2_sw_get_ethtool_stats,
1102 .get_sset_count = bcm_sf2_sw_get_sset_count,
Florian Fainellic7d28c92018-04-25 12:12:53 -07001103 .get_ethtool_phy_stats = b53_get_ethtool_phy_stats,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001104 .get_phy_flags = bcm_sf2_sw_get_phy_flags,
Florian Fainellibc0cb652018-05-10 13:17:33 -07001105 .phylink_validate = bcm_sf2_sw_validate,
1106 .phylink_mac_config = bcm_sf2_sw_mac_config,
1107 .phylink_mac_link_down = bcm_sf2_sw_mac_link_down,
1108 .phylink_mac_link_up = bcm_sf2_sw_mac_link_up,
1109 .phylink_fixed_state = bcm_sf2_sw_fixed_state,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001110 .suspend = bcm_sf2_sw_suspend,
1111 .resume = bcm_sf2_sw_resume,
1112 .get_wol = bcm_sf2_sw_get_wol,
1113 .set_wol = bcm_sf2_sw_set_wol,
1114 .port_enable = bcm_sf2_port_setup,
1115 .port_disable = bcm_sf2_port_disable,
Florian Fainelli22256b02017-09-19 10:46:50 -07001116 .get_mac_eee = b53_get_mac_eee,
1117 .set_mac_eee = b53_set_mac_eee,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001118 .port_bridge_join = b53_br_join,
1119 .port_bridge_leave = b53_br_leave,
Florian Fainellie6dd86e2021-02-22 14:30:09 -08001120 .port_pre_bridge_flags = b53_br_flags_pre,
1121 .port_bridge_flags = b53_br_flags,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001122 .port_stp_state_set = b53_br_set_stp_state,
Florian Fainellie6dd86e2021-02-22 14:30:09 -08001123 .port_set_mrouter = b53_set_mrouter,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001124 .port_fast_age = b53_br_fast_age,
1125 .port_vlan_filtering = b53_vlan_filtering,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001126 .port_vlan_add = b53_vlan_add,
1127 .port_vlan_del = b53_vlan_del,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001128 .port_fdb_dump = b53_fdb_dump,
1129 .port_fdb_add = b53_fdb_add,
1130 .port_fdb_del = b53_fdb_del,
Florian Fainelli73181662017-01-30 09:48:43 -08001131 .get_rxnfc = bcm_sf2_get_rxnfc,
1132 .set_rxnfc = bcm_sf2_set_rxnfc,
Florian Fainelliec960de2017-01-30 12:41:43 -08001133 .port_mirror_add = b53_mirror_add,
1134 .port_mirror_del = b53_mirror_del,
Florian Fainelli29bb5e82019-10-24 12:45:08 -07001135 .port_mdb_add = b53_mdb_add,
1136 .port_mdb_del = b53_mdb_del,
Florian Fainelli73095cb2017-01-08 14:52:06 -08001137};
1138
Florian Fainellia78e86e2017-01-20 12:36:29 -08001139struct bcm_sf2_of_data {
1140 u32 type;
1141 const u16 *reg_offsets;
1142 unsigned int core_reg_align;
Florian Fainellidf191632017-08-30 12:39:33 -07001143 unsigned int num_cfp_rules;
Florian Fainellia78e86e2017-01-20 12:36:29 -08001144};
1145
Rafał Miłecki73b7a602021-01-06 22:32:02 +01001146static const u16 bcm_sf2_4908_reg_offsets[] = {
1147 [REG_SWITCH_CNTRL] = 0x00,
1148 [REG_SWITCH_STATUS] = 0x04,
1149 [REG_DIR_DATA_WRITE] = 0x08,
1150 [REG_DIR_DATA_READ] = 0x0c,
1151 [REG_SWITCH_REVISION] = 0x10,
1152 [REG_PHY_REVISION] = 0x14,
1153 [REG_SPHY_CNTRL] = 0x24,
1154 [REG_CROSSBAR] = 0xc8,
1155 [REG_RGMII_0_CNTRL] = 0xe0,
1156 [REG_RGMII_1_CNTRL] = 0xec,
1157 [REG_RGMII_2_CNTRL] = 0xf8,
1158 [REG_LED_0_CNTRL] = 0x40,
1159 [REG_LED_1_CNTRL] = 0x4c,
1160 [REG_LED_2_CNTRL] = 0x58,
1161};
1162
1163static const struct bcm_sf2_of_data bcm_sf2_4908_data = {
1164 .type = BCM4908_DEVICE_ID,
1165 .core_reg_align = 0,
1166 .reg_offsets = bcm_sf2_4908_reg_offsets,
1167 .num_cfp_rules = 0, /* FIXME */
1168};
1169
Florian Fainellia78e86e2017-01-20 12:36:29 -08001170/* Register offsets for the SWITCH_REG_* block */
1171static const u16 bcm_sf2_7445_reg_offsets[] = {
1172 [REG_SWITCH_CNTRL] = 0x00,
1173 [REG_SWITCH_STATUS] = 0x04,
1174 [REG_DIR_DATA_WRITE] = 0x08,
1175 [REG_DIR_DATA_READ] = 0x0C,
1176 [REG_SWITCH_REVISION] = 0x18,
1177 [REG_PHY_REVISION] = 0x1C,
1178 [REG_SPHY_CNTRL] = 0x2C,
1179 [REG_RGMII_0_CNTRL] = 0x34,
1180 [REG_RGMII_1_CNTRL] = 0x40,
1181 [REG_RGMII_2_CNTRL] = 0x4c,
1182 [REG_LED_0_CNTRL] = 0x90,
1183 [REG_LED_1_CNTRL] = 0x94,
1184 [REG_LED_2_CNTRL] = 0x98,
1185};
1186
1187static const struct bcm_sf2_of_data bcm_sf2_7445_data = {
1188 .type = BCM7445_DEVICE_ID,
1189 .core_reg_align = 0,
1190 .reg_offsets = bcm_sf2_7445_reg_offsets,
Florian Fainellidf191632017-08-30 12:39:33 -07001191 .num_cfp_rules = 256,
Florian Fainellia78e86e2017-01-20 12:36:29 -08001192};
1193
Florian Fainelli0fe99332017-01-20 12:36:30 -08001194static const u16 bcm_sf2_7278_reg_offsets[] = {
1195 [REG_SWITCH_CNTRL] = 0x00,
1196 [REG_SWITCH_STATUS] = 0x04,
1197 [REG_DIR_DATA_WRITE] = 0x08,
1198 [REG_DIR_DATA_READ] = 0x0c,
1199 [REG_SWITCH_REVISION] = 0x10,
1200 [REG_PHY_REVISION] = 0x14,
1201 [REG_SPHY_CNTRL] = 0x24,
1202 [REG_RGMII_0_CNTRL] = 0xe0,
1203 [REG_RGMII_1_CNTRL] = 0xec,
1204 [REG_RGMII_2_CNTRL] = 0xf8,
1205 [REG_LED_0_CNTRL] = 0x40,
1206 [REG_LED_1_CNTRL] = 0x4c,
1207 [REG_LED_2_CNTRL] = 0x58,
1208};
1209
1210static const struct bcm_sf2_of_data bcm_sf2_7278_data = {
1211 .type = BCM7278_DEVICE_ID,
1212 .core_reg_align = 1,
1213 .reg_offsets = bcm_sf2_7278_reg_offsets,
Florian Fainellidf191632017-08-30 12:39:33 -07001214 .num_cfp_rules = 128,
Florian Fainelli0fe99332017-01-20 12:36:30 -08001215};
1216
Florian Fainellia78e86e2017-01-20 12:36:29 -08001217static const struct of_device_id bcm_sf2_of_match[] = {
Rafał Miłecki73b7a602021-01-06 22:32:02 +01001218 { .compatible = "brcm,bcm4908-switch",
1219 .data = &bcm_sf2_4908_data
1220 },
Florian Fainellia78e86e2017-01-20 12:36:29 -08001221 { .compatible = "brcm,bcm7445-switch-v4.0",
1222 .data = &bcm_sf2_7445_data
1223 },
Florian Fainelli0fe99332017-01-20 12:36:30 -08001224 { .compatible = "brcm,bcm7278-switch-v4.0",
1225 .data = &bcm_sf2_7278_data
1226 },
Florian Fainelli3b07d782017-12-14 17:59:40 -08001227 { .compatible = "brcm,bcm7278-switch-v4.8",
1228 .data = &bcm_sf2_7278_data
1229 },
Florian Fainellia78e86e2017-01-20 12:36:29 -08001230 { /* sentinel */ },
1231};
1232MODULE_DEVICE_TABLE(of, bcm_sf2_of_match);
1233
Florian Fainellid9338022016-08-18 15:30:14 -07001234static int bcm_sf2_sw_probe(struct platform_device *pdev)
1235{
1236 const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
1237 struct device_node *dn = pdev->dev.of_node;
Florian Fainellia78e86e2017-01-20 12:36:29 -08001238 const struct of_device_id *of_id = NULL;
1239 const struct bcm_sf2_of_data *data;
Florian Fainellif4589952016-08-26 12:18:33 -07001240 struct b53_platform_data *pdata;
Florian Fainellia4c61b92017-01-07 21:01:56 -08001241 struct dsa_switch_ops *ops;
Florian Fainelliafa3b592020-04-05 13:00:30 -07001242 struct device_node *ports;
Florian Fainellid9338022016-08-18 15:30:14 -07001243 struct bcm_sf2_priv *priv;
Florian Fainellif4589952016-08-26 12:18:33 -07001244 struct b53_device *dev;
Florian Fainellid9338022016-08-18 15:30:14 -07001245 struct dsa_switch *ds;
1246 void __iomem **base;
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001247 unsigned int i;
1248 u32 reg, rev;
1249 int ret;
1250
Florian Fainellif4589952016-08-26 12:18:33 -07001251 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1252 if (!priv)
Florian Fainellid9338022016-08-18 15:30:14 -07001253 return -ENOMEM;
1254
Florian Fainellia4c61b92017-01-07 21:01:56 -08001255 ops = devm_kzalloc(&pdev->dev, sizeof(*ops), GFP_KERNEL);
1256 if (!ops)
1257 return -ENOMEM;
1258
Florian Fainellif4589952016-08-26 12:18:33 -07001259 dev = b53_switch_alloc(&pdev->dev, &bcm_sf2_io_ops, priv);
1260 if (!dev)
1261 return -ENOMEM;
Florian Fainellid9338022016-08-18 15:30:14 -07001262
Florian Fainellif4589952016-08-26 12:18:33 -07001263 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
1264 if (!pdata)
1265 return -ENOMEM;
1266
Florian Fainellia78e86e2017-01-20 12:36:29 -08001267 of_id = of_match_node(bcm_sf2_of_match, dn);
1268 if (!of_id || !of_id->data)
1269 return -EINVAL;
1270
1271 data = of_id->data;
1272
1273 /* Set SWITCH_REG register offsets and SWITCH_CORE align factor */
1274 priv->type = data->type;
1275 priv->reg_offsets = data->reg_offsets;
1276 priv->core_reg_align = data->core_reg_align;
Florian Fainellidf191632017-08-30 12:39:33 -07001277 priv->num_cfp_rules = data->num_cfp_rules;
Florian Fainellia78e86e2017-01-20 12:36:29 -08001278
Florian Fainellieee87e42019-11-04 13:51:39 -08001279 priv->rcdev = devm_reset_control_get_optional_exclusive(&pdev->dev,
1280 "switch");
1281 if (PTR_ERR(priv->rcdev) == -EPROBE_DEFER)
1282 return PTR_ERR(priv->rcdev);
1283
Florian Fainellif4589952016-08-26 12:18:33 -07001284 /* Auto-detection using standard registers will not work, so
1285 * provide an indication of what kind of device we are for
1286 * b53_common to work with
1287 */
Florian Fainellia78e86e2017-01-20 12:36:29 -08001288 pdata->chip_id = priv->type;
Florian Fainellif4589952016-08-26 12:18:33 -07001289 dev->pdata = pdata;
1290
1291 priv->dev = dev;
1292 ds = dev->ds;
Florian Fainelli73095cb2017-01-08 14:52:06 -08001293 ds->ops = &bcm_sf2_ops;
Florian Fainellif4589952016-08-26 12:18:33 -07001294
Florian Fainelli181183772017-09-03 20:27:02 -07001295 /* Advertise the 8 egress queues */
1296 ds->num_tx_queues = SF2_NUM_EGRESS_QUEUES;
1297
Florian Fainellif4589952016-08-26 12:18:33 -07001298 dev_set_drvdata(&pdev->dev, priv);
Florian Fainellid9338022016-08-18 15:30:14 -07001299
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001300 spin_lock_init(&priv->indir_lock);
Florian Fainelli73181662017-01-30 09:48:43 -08001301 mutex_init(&priv->cfp.lock);
Florian Fainelliae7a5af2018-11-06 12:58:37 -08001302 INIT_LIST_HEAD(&priv->cfp.rules_list);
Florian Fainelli73181662017-01-30 09:48:43 -08001303
1304 /* CFP rule #0 cannot be used for specific classifications, flag it as
1305 * permanently used
1306 */
1307 set_bit(0, priv->cfp.used);
Florian Fainelliba0696c2017-10-20 14:39:47 -07001308 set_bit(0, priv->cfp.unique);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001309
Florian Fainelli8dbe4c52020-06-17 20:42:44 -07001310 /* Balance of_node_put() done by of_find_node_by_name() */
1311 of_node_get(dn);
Florian Fainelliafa3b592020-04-05 13:00:30 -07001312 ports = of_find_node_by_name(dn, "ports");
1313 if (ports) {
1314 bcm_sf2_identify_ports(priv, ports);
1315 of_node_put(ports);
1316 }
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001317
1318 priv->irq0 = irq_of_parse_and_map(dn, 0);
1319 priv->irq1 = irq_of_parse_and_map(dn, 1);
1320
1321 base = &priv->core;
1322 for (i = 0; i < BCM_SF2_REGS_NUM; i++) {
YueHaibing42376782019-08-01 20:29:11 +08001323 *base = devm_platform_ioremap_resource(pdev, i);
Florian Fainelli4bd11672016-08-18 15:30:15 -07001324 if (IS_ERR(*base)) {
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001325 pr_err("unable to find register: %s\n", reg_names[i]);
Florian Fainelli4bd11672016-08-18 15:30:15 -07001326 return PTR_ERR(*base);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001327 }
1328 base++;
1329 }
1330
Florian Fainellie9ec5c32020-09-01 15:59:12 -07001331 priv->clk = devm_clk_get_optional(&pdev->dev, "sw_switch");
1332 if (IS_ERR(priv->clk))
1333 return PTR_ERR(priv->clk);
1334
1335 clk_prepare_enable(priv->clk);
1336
Florian Fainelli2ee3adc2020-09-01 15:59:13 -07001337 priv->clk_mdiv = devm_clk_get_optional(&pdev->dev, "sw_switch_mdiv");
1338 if (IS_ERR(priv->clk_mdiv)) {
1339 ret = PTR_ERR(priv->clk_mdiv);
1340 goto out_clk;
1341 }
1342
1343 clk_prepare_enable(priv->clk_mdiv);
1344
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001345 ret = bcm_sf2_sw_rst(priv);
1346 if (ret) {
1347 pr_err("unable to software reset switch: %d\n", ret);
Florian Fainelli2ee3adc2020-09-01 15:59:13 -07001348 goto out_clk_mdiv;
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001349 }
1350
Florian Fainellic04a17d2018-11-06 15:15:16 -08001351 bcm_sf2_gphy_enable_set(priv->dev->ds, true);
1352
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001353 ret = bcm_sf2_mdio_register(ds);
1354 if (ret) {
1355 pr_err("failed to register MDIO bus\n");
Florian Fainelli2ee3adc2020-09-01 15:59:13 -07001356 goto out_clk_mdiv;
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001357 }
1358
Florian Fainellic04a17d2018-11-06 15:15:16 -08001359 bcm_sf2_gphy_enable_set(priv->dev->ds, false);
1360
Florian Fainelli73181662017-01-30 09:48:43 -08001361 ret = bcm_sf2_cfp_rst(priv);
1362 if (ret) {
1363 pr_err("failed to reset CFP\n");
1364 goto out_mdio;
1365 }
1366
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001367 /* Disable all interrupts and request them */
1368 bcm_sf2_intr_disable(priv);
1369
Florian Fainelli4bd11672016-08-18 15:30:15 -07001370 ret = devm_request_irq(&pdev->dev, priv->irq0, bcm_sf2_switch_0_isr, 0,
Florian Fainellibc0cb652018-05-10 13:17:33 -07001371 "switch_0", ds);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001372 if (ret < 0) {
1373 pr_err("failed to request switch_0 IRQ\n");
Florian Fainellibb9c0fa2016-07-29 12:35:57 -07001374 goto out_mdio;
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001375 }
1376
Florian Fainelli4bd11672016-08-18 15:30:15 -07001377 ret = devm_request_irq(&pdev->dev, priv->irq1, bcm_sf2_switch_1_isr, 0,
Florian Fainellibc0cb652018-05-10 13:17:33 -07001378 "switch_1", ds);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001379 if (ret < 0) {
1380 pr_err("failed to request switch_1 IRQ\n");
Florian Fainelli4bd11672016-08-18 15:30:15 -07001381 goto out_mdio;
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001382 }
1383
1384 /* Reset the MIB counters */
1385 reg = core_readl(priv, CORE_GMNCFGCFG);
1386 reg |= RST_MIB_CNT;
1387 core_writel(priv, reg, CORE_GMNCFGCFG);
1388 reg &= ~RST_MIB_CNT;
1389 core_writel(priv, reg, CORE_GMNCFGCFG);
1390
1391 /* Get the maximum number of ports for this switch */
1392 priv->hw_params.num_ports = core_readl(priv, CORE_IMP0_PRT_ID) + 1;
1393 if (priv->hw_params.num_ports > DSA_MAX_PORTS)
1394 priv->hw_params.num_ports = DSA_MAX_PORTS;
1395
1396 /* Assume a single GPHY setup if we can't read that property */
1397 if (of_property_read_u32(dn, "brcm,num-gphy",
1398 &priv->hw_params.num_gphy))
1399 priv->hw_params.num_gphy = 1;
1400
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001401 rev = reg_readl(priv, REG_SWITCH_REVISION);
1402 priv->hw_params.top_rev = (rev >> SWITCH_TOP_REV_SHIFT) &
1403 SWITCH_TOP_REV_MASK;
1404 priv->hw_params.core_rev = (rev & SF2_REV_MASK);
1405
1406 rev = reg_readl(priv, REG_PHY_REVISION);
1407 priv->hw_params.gphy_rev = rev & PHY_REVISION_MASK;
1408
Florian Fainellif4589952016-08-26 12:18:33 -07001409 ret = b53_switch_register(dev);
Florian Fainellid9338022016-08-18 15:30:14 -07001410 if (ret)
Florian Fainelli4bd11672016-08-18 15:30:15 -07001411 goto out_mdio;
Florian Fainellid9338022016-08-18 15:30:14 -07001412
Florian Fainellifbb7bc42019-03-20 09:45:16 -07001413 dev_info(&pdev->dev,
1414 "Starfighter 2 top: %x.%02x, core: %x.%02x, IRQs: %d, %d\n",
1415 priv->hw_params.top_rev >> 8, priv->hw_params.top_rev & 0xff,
1416 priv->hw_params.core_rev >> 8, priv->hw_params.core_rev & 0xff,
1417 priv->irq0, priv->irq1);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001418
1419 return 0;
1420
Florian Fainellibb9c0fa2016-07-29 12:35:57 -07001421out_mdio:
1422 bcm_sf2_mdio_unregister(priv);
Florian Fainelli2ee3adc2020-09-01 15:59:13 -07001423out_clk_mdiv:
1424 clk_disable_unprepare(priv->clk_mdiv);
Florian Fainellie9ec5c32020-09-01 15:59:12 -07001425out_clk:
1426 clk_disable_unprepare(priv->clk);
Florian Fainelli7fbb1a92016-06-09 17:42:06 -07001427 return ret;
1428}
1429
Florian Fainellid9338022016-08-18 15:30:14 -07001430static int bcm_sf2_sw_remove(struct platform_device *pdev)
Florian Fainelli246d7f72014-08-27 17:04:56 -07001431{
Florian Fainellif4589952016-08-26 12:18:33 -07001432 struct bcm_sf2_priv *priv = platform_get_drvdata(pdev);
Florian Fainellid9338022016-08-18 15:30:14 -07001433
Florian Fainellid9338022016-08-18 15:30:14 -07001434 priv->wol_ports_mask = 0;
Florian Fainellie6840002019-11-02 20:17:39 -07001435 /* Disable interrupts */
1436 bcm_sf2_intr_disable(priv);
Florian Fainellif4589952016-08-26 12:18:33 -07001437 dsa_unregister_switch(priv->dev->ds);
Florian Fainelliae7a5af2018-11-06 12:58:37 -08001438 bcm_sf2_cfp_exit(priv->dev->ds);
Florian Fainellid9338022016-08-18 15:30:14 -07001439 bcm_sf2_mdio_unregister(priv);
Florian Fainelli2ee3adc2020-09-01 15:59:13 -07001440 clk_disable_unprepare(priv->clk_mdiv);
Florian Fainellie9ec5c32020-09-01 15:59:12 -07001441 clk_disable_unprepare(priv->clk);
Florian Fainellieee87e42019-11-04 13:51:39 -08001442 if (priv->type == BCM7278_DEVICE_ID && !IS_ERR(priv->rcdev))
1443 reset_control_assert(priv->rcdev);
Florian Fainelli246d7f72014-08-27 17:04:56 -07001444
1445 return 0;
1446}
Florian Fainelli246d7f72014-08-27 17:04:56 -07001447
Florian Fainelli2399d612016-10-20 09:32:19 -07001448static void bcm_sf2_sw_shutdown(struct platform_device *pdev)
1449{
1450 struct bcm_sf2_priv *priv = platform_get_drvdata(pdev);
1451
1452 /* For a kernel about to be kexec'd we want to keep the GPHY on for a
1453 * successful MDIO bus scan to occur. If we did turn off the GPHY
1454 * before (e.g: port_disable), this will also power it back on.
Florian Fainelli4a2947e2016-10-21 14:21:56 -07001455 *
1456 * Do not rely on kexec_in_progress, just power the PHY on.
Florian Fainelli2399d612016-10-20 09:32:19 -07001457 */
1458 if (priv->hw_params.num_gphy == 1)
Florian Fainelli4a2947e2016-10-21 14:21:56 -07001459 bcm_sf2_gphy_enable_set(priv->dev->ds, true);
Florian Fainelli2399d612016-10-20 09:32:19 -07001460}
1461
Florian Fainellid9338022016-08-18 15:30:14 -07001462#ifdef CONFIG_PM_SLEEP
1463static int bcm_sf2_suspend(struct device *dev)
Florian Fainelli246d7f72014-08-27 17:04:56 -07001464{
Wolfram Sang63382e02018-10-21 22:00:12 +02001465 struct bcm_sf2_priv *priv = dev_get_drvdata(dev);
Florian Fainellid9338022016-08-18 15:30:14 -07001466
Florian Fainellif4589952016-08-26 12:18:33 -07001467 return dsa_switch_suspend(priv->dev->ds);
Florian Fainelli246d7f72014-08-27 17:04:56 -07001468}
Florian Fainellid9338022016-08-18 15:30:14 -07001469
1470static int bcm_sf2_resume(struct device *dev)
1471{
Wolfram Sang63382e02018-10-21 22:00:12 +02001472 struct bcm_sf2_priv *priv = dev_get_drvdata(dev);
Florian Fainellid9338022016-08-18 15:30:14 -07001473
Florian Fainellif4589952016-08-26 12:18:33 -07001474 return dsa_switch_resume(priv->dev->ds);
Florian Fainellid9338022016-08-18 15:30:14 -07001475}
1476#endif /* CONFIG_PM_SLEEP */
1477
1478static SIMPLE_DEV_PM_OPS(bcm_sf2_pm_ops,
1479 bcm_sf2_suspend, bcm_sf2_resume);
1480
Florian Fainellid9338022016-08-18 15:30:14 -07001481
1482static struct platform_driver bcm_sf2_driver = {
1483 .probe = bcm_sf2_sw_probe,
1484 .remove = bcm_sf2_sw_remove,
Florian Fainelli2399d612016-10-20 09:32:19 -07001485 .shutdown = bcm_sf2_sw_shutdown,
Florian Fainellid9338022016-08-18 15:30:14 -07001486 .driver = {
1487 .name = "brcm-sf2",
1488 .of_match_table = bcm_sf2_of_match,
1489 .pm = &bcm_sf2_pm_ops,
1490 },
1491};
1492module_platform_driver(bcm_sf2_driver);
Florian Fainelli246d7f72014-08-27 17:04:56 -07001493
1494MODULE_AUTHOR("Broadcom Corporation");
1495MODULE_DESCRIPTION("Driver for Broadcom Starfighter 2 ethernet switch chip");
1496MODULE_LICENSE("GPL");
1497MODULE_ALIAS("platform:brcm-sf2");