1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2022 Schneider-Electric
5 * Clément Léger <clement.leger@bootlin.com>
9 #include <linux/etherdevice.h>
10 #include <linux/if_bridge.h>
11 #include <linux/if_ether.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
15 #include <linux/of_mdio.h>
18 #include "rzn1_a5psw.h"
22 const char name[ETH_GSTRING_LEN];
25 #define STAT_DESC(_offset) { \
26 .offset = A5PSW_##_offset, \
27 .name = __stringify(_offset), \
30 static const struct a5psw_stats a5psw_stats[] = {
31 STAT_DESC(aFramesTransmittedOK),
32 STAT_DESC(aFramesReceivedOK),
33 STAT_DESC(aFrameCheckSequenceErrors),
34 STAT_DESC(aAlignmentErrors),
35 STAT_DESC(aOctetsTransmittedOK),
36 STAT_DESC(aOctetsReceivedOK),
37 STAT_DESC(aTxPAUSEMACCtrlFrames),
38 STAT_DESC(aRxPAUSEMACCtrlFrames),
39 STAT_DESC(ifInErrors),
40 STAT_DESC(ifOutErrors),
41 STAT_DESC(ifInUcastPkts),
42 STAT_DESC(ifInMulticastPkts),
43 STAT_DESC(ifInBroadcastPkts),
44 STAT_DESC(ifOutDiscards),
45 STAT_DESC(ifOutUcastPkts),
46 STAT_DESC(ifOutMulticastPkts),
47 STAT_DESC(ifOutBroadcastPkts),
48 STAT_DESC(etherStatsDropEvents),
49 STAT_DESC(etherStatsOctets),
50 STAT_DESC(etherStatsPkts),
51 STAT_DESC(etherStatsUndersizePkts),
52 STAT_DESC(etherStatsOversizePkts),
53 STAT_DESC(etherStatsPkts64Octets),
54 STAT_DESC(etherStatsPkts65to127Octets),
55 STAT_DESC(etherStatsPkts128to255Octets),
56 STAT_DESC(etherStatsPkts256to511Octets),
57 STAT_DESC(etherStatsPkts1024to1518Octets),
58 STAT_DESC(etherStatsPkts1519toXOctets),
59 STAT_DESC(etherStatsJabbers),
60 STAT_DESC(etherStatsFragments),
61 STAT_DESC(VLANReceived),
62 STAT_DESC(VLANTransmitted),
64 STAT_DESC(aMultipleCollisions),
65 STAT_DESC(aSingleCollisions),
66 STAT_DESC(aLateCollisions),
67 STAT_DESC(aExcessiveCollisions),
68 STAT_DESC(aCarrierSenseErrors),
71 static void a5psw_reg_writel(struct a5psw *a5psw, int offset, u32 value)
73 writel(value, a5psw->base + offset);
76 static u32 a5psw_reg_readl(struct a5psw *a5psw, int offset)
78 return readl(a5psw->base + offset);
81 static void a5psw_reg_rmw(struct a5psw *a5psw, int offset, u32 mask, u32 val)
85 spin_lock(&a5psw->reg_lock);
87 reg = a5psw_reg_readl(a5psw, offset);
90 a5psw_reg_writel(a5psw, offset, reg);
92 spin_unlock(&a5psw->reg_lock);
95 static enum dsa_tag_protocol a5psw_get_tag_protocol(struct dsa_switch *ds,
97 enum dsa_tag_protocol mp)
99 return DSA_TAG_PROTO_RZN1_A5PSW;
102 static void a5psw_port_pattern_set(struct a5psw *a5psw, int port, int pattern,
108 rx_match |= A5PSW_RXMATCH_CONFIG_PATTERN(pattern);
110 a5psw_reg_rmw(a5psw, A5PSW_RXMATCH_CONFIG(port),
111 A5PSW_RXMATCH_CONFIG_PATTERN(pattern), rx_match);
114 static void a5psw_port_mgmtfwd_set(struct a5psw *a5psw, int port, bool enable)
116 /* Enable "management forward" pattern matching, this will forward
117 * packets from this port only towards the management port and thus
120 a5psw_port_pattern_set(a5psw, port, A5PSW_PATTERN_MGMTFWD, enable);
123 static void a5psw_port_enable_set(struct a5psw *a5psw, int port, bool enable)
128 port_ena |= A5PSW_PORT_ENA_TX_RX(port);
130 a5psw_reg_rmw(a5psw, A5PSW_PORT_ENA, A5PSW_PORT_ENA_TX_RX(port),
134 static int a5psw_lk_execute_ctrl(struct a5psw *a5psw, u32 *ctrl)
138 a5psw_reg_writel(a5psw, A5PSW_LK_ADDR_CTRL, *ctrl);
140 ret = readl_poll_timeout(a5psw->base + A5PSW_LK_ADDR_CTRL, *ctrl,
141 !(*ctrl & A5PSW_LK_ADDR_CTRL_BUSY),
142 A5PSW_LK_BUSY_USEC_POLL, A5PSW_CTRL_TIMEOUT);
144 dev_err(a5psw->dev, "LK_CTRL timeout waiting for BUSY bit\n");
149 static void a5psw_port_fdb_flush(struct a5psw *a5psw, int port)
151 u32 ctrl = A5PSW_LK_ADDR_CTRL_DELETE_PORT | BIT(port);
153 mutex_lock(&a5psw->lk_lock);
154 a5psw_lk_execute_ctrl(a5psw, &ctrl);
155 mutex_unlock(&a5psw->lk_lock);
158 static void a5psw_port_authorize_set(struct a5psw *a5psw, int port,
161 u32 reg = a5psw_reg_readl(a5psw, A5PSW_AUTH_PORT(port));
164 reg |= A5PSW_AUTH_PORT_AUTHORIZED;
166 reg &= ~A5PSW_AUTH_PORT_AUTHORIZED;
168 a5psw_reg_writel(a5psw, A5PSW_AUTH_PORT(port), reg);
171 static void a5psw_port_disable(struct dsa_switch *ds, int port)
173 struct a5psw *a5psw = ds->priv;
175 a5psw_port_authorize_set(a5psw, port, false);
176 a5psw_port_enable_set(a5psw, port, false);
179 static int a5psw_port_enable(struct dsa_switch *ds, int port,
180 struct phy_device *phy)
182 struct a5psw *a5psw = ds->priv;
184 a5psw_port_authorize_set(a5psw, port, true);
185 a5psw_port_enable_set(a5psw, port, true);
190 static int a5psw_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
192 struct a5psw *a5psw = ds->priv;
194 new_mtu += ETH_HLEN + A5PSW_EXTRA_MTU_LEN + ETH_FCS_LEN;
195 a5psw_reg_writel(a5psw, A5PSW_FRM_LENGTH(port), new_mtu);
200 static int a5psw_port_max_mtu(struct dsa_switch *ds, int port)
202 return A5PSW_MAX_MTU;
205 static void a5psw_phylink_get_caps(struct dsa_switch *ds, int port,
206 struct phylink_config *config)
208 unsigned long *intf = config->supported_interfaces;
210 config->mac_capabilities = MAC_1000FD;
212 if (dsa_is_cpu_port(ds, port)) {
213 /* GMII is used internally and GMAC2 is connected to the switch
214 * using 1000Mbps Full-Duplex mode only (cf ethernet manual)
216 __set_bit(PHY_INTERFACE_MODE_GMII, intf);
218 config->mac_capabilities |= MAC_100 | MAC_10;
219 phy_interface_set_rgmii(intf);
220 __set_bit(PHY_INTERFACE_MODE_RMII, intf);
221 __set_bit(PHY_INTERFACE_MODE_MII, intf);
225 static struct phylink_pcs *
226 a5psw_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
227 phy_interface_t interface)
229 struct dsa_port *dp = dsa_to_port(ds, port);
230 struct a5psw *a5psw = ds->priv;
232 if (!dsa_port_is_cpu(dp) && a5psw->pcs[port])
233 return a5psw->pcs[port];
238 static void a5psw_phylink_mac_link_down(struct dsa_switch *ds, int port,
240 phy_interface_t interface)
242 struct a5psw *a5psw = ds->priv;
245 cmd_cfg = a5psw_reg_readl(a5psw, A5PSW_CMD_CFG(port));
246 cmd_cfg &= ~(A5PSW_CMD_CFG_RX_ENA | A5PSW_CMD_CFG_TX_ENA);
247 a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg);
250 static void a5psw_phylink_mac_link_up(struct dsa_switch *ds, int port,
252 phy_interface_t interface,
253 struct phy_device *phydev, int speed,
254 int duplex, bool tx_pause, bool rx_pause)
256 u32 cmd_cfg = A5PSW_CMD_CFG_RX_ENA | A5PSW_CMD_CFG_TX_ENA |
257 A5PSW_CMD_CFG_TX_CRC_APPEND;
258 struct a5psw *a5psw = ds->priv;
260 if (speed == SPEED_1000)
261 cmd_cfg |= A5PSW_CMD_CFG_ETH_SPEED;
263 if (duplex == DUPLEX_HALF)
264 cmd_cfg |= A5PSW_CMD_CFG_HD_ENA;
266 cmd_cfg |= A5PSW_CMD_CFG_CNTL_FRM_ENA;
269 cmd_cfg &= ~A5PSW_CMD_CFG_PAUSE_IGNORE;
271 a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg);
274 static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
276 struct a5psw *a5psw = ds->priv;
281 rate = clk_get_rate(a5psw->clk);
282 max = div64_ul(((u64)A5PSW_LK_AGETIME_MASK * A5PSW_TABLE_ENTRIES * 1024),
287 tmp = div_u64(rate, MSEC_PER_SEC);
288 agetime = div_u64(msecs * tmp, 1024 * A5PSW_TABLE_ENTRIES);
290 a5psw_reg_writel(a5psw, A5PSW_LK_AGETIME, agetime);
295 static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,
298 u8 offsets[] = {A5PSW_UCAST_DEF_MASK, A5PSW_BCAST_DEF_MASK,
299 A5PSW_MCAST_DEF_MASK};
303 a5psw->bridged_ports |= BIT(port);
305 a5psw->bridged_ports &= ~BIT(port);
307 for (i = 0; i < ARRAY_SIZE(offsets); i++)
308 a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports);
311 static int a5psw_port_bridge_join(struct dsa_switch *ds, int port,
312 struct dsa_bridge bridge,
313 bool *tx_fwd_offload,
314 struct netlink_ext_ack *extack)
316 struct a5psw *a5psw = ds->priv;
318 /* We only support 1 bridge device */
319 if (a5psw->br_dev && bridge.dev != a5psw->br_dev) {
320 NL_SET_ERR_MSG_MOD(extack,
321 "Forwarding offload supported for a single bridge");
325 a5psw->br_dev = bridge.dev;
326 a5psw_flooding_set_resolution(a5psw, port, true);
327 a5psw_port_mgmtfwd_set(a5psw, port, false);
332 static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,
333 struct dsa_bridge bridge)
335 struct a5psw *a5psw = ds->priv;
337 a5psw_flooding_set_resolution(a5psw, port, false);
338 a5psw_port_mgmtfwd_set(a5psw, port, true);
340 /* No more ports bridged */
341 if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT))
342 a5psw->br_dev = NULL;
345 static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
347 u32 mask = A5PSW_INPUT_LEARN_DIS(port) | A5PSW_INPUT_LEARN_BLOCK(port);
348 struct a5psw *a5psw = ds->priv;
352 case BR_STATE_DISABLED:
353 case BR_STATE_BLOCKING:
354 reg |= A5PSW_INPUT_LEARN_DIS(port);
355 reg |= A5PSW_INPUT_LEARN_BLOCK(port);
357 case BR_STATE_LISTENING:
358 reg |= A5PSW_INPUT_LEARN_DIS(port);
360 case BR_STATE_LEARNING:
361 reg |= A5PSW_INPUT_LEARN_BLOCK(port);
363 case BR_STATE_FORWARDING:
368 a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
371 static void a5psw_port_fast_age(struct dsa_switch *ds, int port)
373 struct a5psw *a5psw = ds->priv;
375 a5psw_port_fdb_flush(a5psw, port);
378 static int a5psw_lk_execute_lookup(struct a5psw *a5psw, union lk_data *lk_data,
384 a5psw_reg_writel(a5psw, A5PSW_LK_DATA_LO, lk_data->lo);
385 a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data->hi);
387 ctrl = A5PSW_LK_ADDR_CTRL_LOOKUP;
388 ret = a5psw_lk_execute_ctrl(a5psw, &ctrl);
392 *entry = ctrl & A5PSW_LK_ADDR_CTRL_ADDRESS;
397 static int a5psw_port_fdb_add(struct dsa_switch *ds, int port,
398 const unsigned char *addr, u16 vid,
401 struct a5psw *a5psw = ds->priv;
402 union lk_data lk_data = {0};
403 bool inc_learncount = false;
408 ether_addr_copy(lk_data.entry.mac, addr);
409 lk_data.entry.port_mask = BIT(port);
411 mutex_lock(&a5psw->lk_lock);
413 /* Set the value to be written in the lookup table */
414 ret = a5psw_lk_execute_lookup(a5psw, &lk_data, &entry);
418 lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI);
419 if (!lk_data.entry.valid) {
420 inc_learncount = true;
421 /* port_mask set to 0x1f when entry is not valid, clear it */
422 lk_data.entry.port_mask = 0;
423 lk_data.entry.prio = 0;
426 lk_data.entry.port_mask |= BIT(port);
427 lk_data.entry.is_static = 1;
428 lk_data.entry.valid = 1;
430 a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data.hi);
432 reg = A5PSW_LK_ADDR_CTRL_WRITE | entry;
433 ret = a5psw_lk_execute_ctrl(a5psw, ®);
437 if (inc_learncount) {
438 reg = A5PSW_LK_LEARNCOUNT_MODE_INC;
439 a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg);
443 mutex_unlock(&a5psw->lk_lock);
448 static int a5psw_port_fdb_del(struct dsa_switch *ds, int port,
449 const unsigned char *addr, u16 vid,
452 struct a5psw *a5psw = ds->priv;
453 union lk_data lk_data = {0};
459 ether_addr_copy(lk_data.entry.mac, addr);
461 mutex_lock(&a5psw->lk_lock);
463 ret = a5psw_lk_execute_lookup(a5psw, &lk_data, &entry);
467 lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI);
469 /* Our hardware does not associate any VID to the FDB entries so this
470 * means that if two entries were added for the same mac but for
471 * different VID, then, on the deletion of the first one, we would also
472 * delete the second one. Since there is unfortunately nothing we can do
473 * about that, do not return an error...
475 if (!lk_data.entry.valid)
478 lk_data.entry.port_mask &= ~BIT(port);
479 /* If there is no more port in the mask, clear the entry */
480 if (lk_data.entry.port_mask == 0)
483 a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data.hi);
487 reg |= A5PSW_LK_ADDR_CTRL_CLEAR;
489 reg |= A5PSW_LK_ADDR_CTRL_WRITE;
491 ret = a5psw_lk_execute_ctrl(a5psw, ®);
495 /* Decrement LEARNCOUNT */
497 reg = A5PSW_LK_LEARNCOUNT_MODE_DEC;
498 a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg);
502 mutex_unlock(&a5psw->lk_lock);
507 static int a5psw_port_fdb_dump(struct dsa_switch *ds, int port,
508 dsa_fdb_dump_cb_t *cb, void *data)
510 struct a5psw *a5psw = ds->priv;
511 union lk_data lk_data;
515 mutex_lock(&a5psw->lk_lock);
517 for (i = 0; i < A5PSW_TABLE_ENTRIES; i++) {
518 reg = A5PSW_LK_ADDR_CTRL_READ | A5PSW_LK_ADDR_CTRL_WAIT | i;
520 ret = a5psw_lk_execute_ctrl(a5psw, ®);
524 lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI);
525 /* If entry is not valid or does not contain the port, skip */
526 if (!lk_data.entry.valid ||
527 !(lk_data.entry.port_mask & BIT(port)))
530 lk_data.lo = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_LO);
532 ret = cb(lk_data.entry.mac, 0, lk_data.entry.is_static, data);
538 mutex_unlock(&a5psw->lk_lock);
543 static u64 a5psw_read_stat(struct a5psw *a5psw, u32 offset, int port)
547 reg_lo = a5psw_reg_readl(a5psw, offset + A5PSW_PORT_OFFSET(port));
548 /* A5PSW_STATS_HIWORD is latched on stat read */
549 reg_hi = a5psw_reg_readl(a5psw, A5PSW_STATS_HIWORD);
551 return ((u64)reg_hi << 32) | reg_lo;
554 static void a5psw_get_strings(struct dsa_switch *ds, int port, u32 stringset,
559 if (stringset != ETH_SS_STATS)
562 for (u = 0; u < ARRAY_SIZE(a5psw_stats); u++) {
563 memcpy(data + u * ETH_GSTRING_LEN, a5psw_stats[u].name,
568 static void a5psw_get_ethtool_stats(struct dsa_switch *ds, int port,
571 struct a5psw *a5psw = ds->priv;
574 for (u = 0; u < ARRAY_SIZE(a5psw_stats); u++)
575 data[u] = a5psw_read_stat(a5psw, a5psw_stats[u].offset, port);
578 static int a5psw_get_sset_count(struct dsa_switch *ds, int port, int sset)
580 if (sset != ETH_SS_STATS)
583 return ARRAY_SIZE(a5psw_stats);
586 static void a5psw_get_eth_mac_stats(struct dsa_switch *ds, int port,
587 struct ethtool_eth_mac_stats *mac_stats)
589 struct a5psw *a5psw = ds->priv;
591 #define RD(name) a5psw_read_stat(a5psw, A5PSW_##name, port)
592 mac_stats->FramesTransmittedOK = RD(aFramesTransmittedOK);
593 mac_stats->SingleCollisionFrames = RD(aSingleCollisions);
594 mac_stats->MultipleCollisionFrames = RD(aMultipleCollisions);
595 mac_stats->FramesReceivedOK = RD(aFramesReceivedOK);
596 mac_stats->FrameCheckSequenceErrors = RD(aFrameCheckSequenceErrors);
597 mac_stats->AlignmentErrors = RD(aAlignmentErrors);
598 mac_stats->OctetsTransmittedOK = RD(aOctetsTransmittedOK);
599 mac_stats->FramesWithDeferredXmissions = RD(aDeferred);
600 mac_stats->LateCollisions = RD(aLateCollisions);
601 mac_stats->FramesAbortedDueToXSColls = RD(aExcessiveCollisions);
602 mac_stats->FramesLostDueToIntMACXmitError = RD(ifOutErrors);
603 mac_stats->CarrierSenseErrors = RD(aCarrierSenseErrors);
604 mac_stats->OctetsReceivedOK = RD(aOctetsReceivedOK);
605 mac_stats->FramesLostDueToIntMACRcvError = RD(ifInErrors);
606 mac_stats->MulticastFramesXmittedOK = RD(ifOutMulticastPkts);
607 mac_stats->BroadcastFramesXmittedOK = RD(ifOutBroadcastPkts);
608 mac_stats->FramesWithExcessiveDeferral = RD(aDeferred);
609 mac_stats->MulticastFramesReceivedOK = RD(ifInMulticastPkts);
610 mac_stats->BroadcastFramesReceivedOK = RD(ifInBroadcastPkts);
614 static const struct ethtool_rmon_hist_range a5psw_rmon_ranges[] = {
621 { 1519, A5PSW_MAX_MTU },
625 static void a5psw_get_rmon_stats(struct dsa_switch *ds, int port,
626 struct ethtool_rmon_stats *rmon_stats,
627 const struct ethtool_rmon_hist_range **ranges)
629 struct a5psw *a5psw = ds->priv;
631 #define RD(name) a5psw_read_stat(a5psw, A5PSW_##name, port)
632 rmon_stats->undersize_pkts = RD(etherStatsUndersizePkts);
633 rmon_stats->oversize_pkts = RD(etherStatsOversizePkts);
634 rmon_stats->fragments = RD(etherStatsFragments);
635 rmon_stats->jabbers = RD(etherStatsJabbers);
636 rmon_stats->hist[0] = RD(etherStatsPkts64Octets);
637 rmon_stats->hist[1] = RD(etherStatsPkts65to127Octets);
638 rmon_stats->hist[2] = RD(etherStatsPkts128to255Octets);
639 rmon_stats->hist[3] = RD(etherStatsPkts256to511Octets);
640 rmon_stats->hist[4] = RD(etherStatsPkts512to1023Octets);
641 rmon_stats->hist[5] = RD(etherStatsPkts1024to1518Octets);
642 rmon_stats->hist[6] = RD(etherStatsPkts1519toXOctets);
645 *ranges = a5psw_rmon_ranges;
648 static void a5psw_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
649 struct ethtool_eth_ctrl_stats *ctrl_stats)
651 struct a5psw *a5psw = ds->priv;
654 stat = a5psw_read_stat(a5psw, A5PSW_aTxPAUSEMACCtrlFrames, port);
655 ctrl_stats->MACControlFramesTransmitted = stat;
656 stat = a5psw_read_stat(a5psw, A5PSW_aRxPAUSEMACCtrlFrames, port);
657 ctrl_stats->MACControlFramesReceived = stat;
660 static int a5psw_setup(struct dsa_switch *ds)
662 struct a5psw *a5psw = ds->priv;
667 /* Validate that there is only 1 CPU port with index A5PSW_CPU_PORT */
668 dsa_switch_for_each_cpu_port(dp, ds) {
669 if (dp->index != A5PSW_CPU_PORT) {
670 dev_err(a5psw->dev, "Invalid CPU port\n");
675 /* Configure management port */
676 reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_DISCARD;
677 a5psw_reg_writel(a5psw, A5PSW_MGMT_CFG, reg);
679 /* Set pattern 0 to forward all frame to mgmt port */
680 a5psw_reg_writel(a5psw, A5PSW_PATTERN_CTRL(A5PSW_PATTERN_MGMTFWD),
681 A5PSW_PATTERN_CTRL_MGMTFWD);
683 /* Enable port tagging */
684 reg = FIELD_PREP(A5PSW_MGMT_TAG_CFG_TAGFIELD, ETH_P_DSA_A5PSW);
685 reg |= A5PSW_MGMT_TAG_CFG_ENABLE | A5PSW_MGMT_TAG_CFG_ALL_FRAMES;
686 a5psw_reg_writel(a5psw, A5PSW_MGMT_TAG_CFG, reg);
688 /* Enable normal switch operation */
689 reg = A5PSW_LK_ADDR_CTRL_BLOCKING | A5PSW_LK_ADDR_CTRL_LEARNING |
690 A5PSW_LK_ADDR_CTRL_AGEING | A5PSW_LK_ADDR_CTRL_ALLOW_MIGR |
691 A5PSW_LK_ADDR_CTRL_CLEAR_TABLE;
692 a5psw_reg_writel(a5psw, A5PSW_LK_CTRL, reg);
694 ret = readl_poll_timeout(a5psw->base + A5PSW_LK_CTRL, reg,
695 !(reg & A5PSW_LK_ADDR_CTRL_CLEAR_TABLE),
696 A5PSW_LK_BUSY_USEC_POLL, A5PSW_CTRL_TIMEOUT);
698 dev_err(a5psw->dev, "Failed to clear lookup table\n");
702 /* Reset learn count to 0 */
703 reg = A5PSW_LK_LEARNCOUNT_MODE_SET;
704 a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg);
706 /* Clear VLAN resource table */
707 reg = A5PSW_VLAN_RES_WR_PORTMASK | A5PSW_VLAN_RES_WR_TAGMASK;
708 for (vlan = 0; vlan < A5PSW_VLAN_COUNT; vlan++)
709 a5psw_reg_writel(a5psw, A5PSW_VLAN_RES(vlan), reg);
711 /* Reset all ports */
712 dsa_switch_for_each_port(dp, ds) {
716 a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port),
717 A5PSW_CMD_CFG_SW_RESET);
719 /* Enable only CPU port */
720 a5psw_port_enable_set(a5psw, port, dsa_port_is_cpu(dp));
722 if (dsa_port_is_unused(dp))
725 /* Enable egress flooding for CPU port */
726 if (dsa_port_is_cpu(dp))
727 a5psw_flooding_set_resolution(a5psw, port, true);
729 /* Enable management forward only for user ports */
730 if (dsa_port_is_user(dp))
731 a5psw_port_mgmtfwd_set(a5psw, port, true);
737 static const struct dsa_switch_ops a5psw_switch_ops = {
738 .get_tag_protocol = a5psw_get_tag_protocol,
739 .setup = a5psw_setup,
740 .port_disable = a5psw_port_disable,
741 .port_enable = a5psw_port_enable,
742 .phylink_get_caps = a5psw_phylink_get_caps,
743 .phylink_mac_select_pcs = a5psw_phylink_mac_select_pcs,
744 .phylink_mac_link_down = a5psw_phylink_mac_link_down,
745 .phylink_mac_link_up = a5psw_phylink_mac_link_up,
746 .port_change_mtu = a5psw_port_change_mtu,
747 .port_max_mtu = a5psw_port_max_mtu,
748 .get_sset_count = a5psw_get_sset_count,
749 .get_strings = a5psw_get_strings,
750 .get_ethtool_stats = a5psw_get_ethtool_stats,
751 .get_eth_mac_stats = a5psw_get_eth_mac_stats,
752 .get_eth_ctrl_stats = a5psw_get_eth_ctrl_stats,
753 .get_rmon_stats = a5psw_get_rmon_stats,
754 .set_ageing_time = a5psw_set_ageing_time,
755 .port_bridge_join = a5psw_port_bridge_join,
756 .port_bridge_leave = a5psw_port_bridge_leave,
757 .port_stp_state_set = a5psw_port_stp_state_set,
758 .port_fast_age = a5psw_port_fast_age,
759 .port_fdb_add = a5psw_port_fdb_add,
760 .port_fdb_del = a5psw_port_fdb_del,
761 .port_fdb_dump = a5psw_port_fdb_dump,
764 static int a5psw_mdio_wait_busy(struct a5psw *a5psw)
769 err = readl_poll_timeout(a5psw->base + A5PSW_MDIO_CFG_STATUS, status,
770 !(status & A5PSW_MDIO_CFG_STATUS_BUSY), 10,
771 1000 * USEC_PER_MSEC);
773 dev_err(a5psw->dev, "MDIO command timeout\n");
778 static int a5psw_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
780 struct a5psw *a5psw = bus->priv;
784 cmd = A5PSW_MDIO_COMMAND_READ;
785 cmd |= FIELD_PREP(A5PSW_MDIO_COMMAND_REG_ADDR, phy_reg);
786 cmd |= FIELD_PREP(A5PSW_MDIO_COMMAND_PHY_ADDR, phy_id);
788 a5psw_reg_writel(a5psw, A5PSW_MDIO_COMMAND, cmd);
790 ret = a5psw_mdio_wait_busy(a5psw);
794 ret = a5psw_reg_readl(a5psw, A5PSW_MDIO_DATA) & A5PSW_MDIO_DATA_MASK;
796 status = a5psw_reg_readl(a5psw, A5PSW_MDIO_CFG_STATUS);
797 if (status & A5PSW_MDIO_CFG_STATUS_READERR)
803 static int a5psw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg,
806 struct a5psw *a5psw = bus->priv;
809 cmd = FIELD_PREP(A5PSW_MDIO_COMMAND_REG_ADDR, phy_reg);
810 cmd |= FIELD_PREP(A5PSW_MDIO_COMMAND_PHY_ADDR, phy_id);
812 a5psw_reg_writel(a5psw, A5PSW_MDIO_COMMAND, cmd);
813 a5psw_reg_writel(a5psw, A5PSW_MDIO_DATA, phy_data);
815 return a5psw_mdio_wait_busy(a5psw);
818 static int a5psw_mdio_config(struct a5psw *a5psw, u32 mdio_freq)
824 rate = clk_get_rate(a5psw->hclk);
825 div = ((rate / mdio_freq) / 2);
826 if (div > FIELD_MAX(A5PSW_MDIO_CFG_STATUS_CLKDIV) ||
827 div < A5PSW_MDIO_CLK_DIV_MIN) {
828 dev_err(a5psw->dev, "MDIO clock div %ld out of range\n", div);
832 cfgstatus = FIELD_PREP(A5PSW_MDIO_CFG_STATUS_CLKDIV, div);
834 a5psw_reg_writel(a5psw, A5PSW_MDIO_CFG_STATUS, cfgstatus);
839 static int a5psw_probe_mdio(struct a5psw *a5psw, struct device_node *node)
841 struct device *dev = a5psw->dev;
846 if (of_property_read_u32(node, "clock-frequency", &mdio_freq))
847 mdio_freq = A5PSW_MDIO_DEF_FREQ;
849 ret = a5psw_mdio_config(a5psw, mdio_freq);
853 bus = devm_mdiobus_alloc(dev);
857 bus->name = "a5psw_mdio";
858 bus->read = a5psw_mdio_read;
859 bus->write = a5psw_mdio_write;
862 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
864 a5psw->mii_bus = bus;
866 return devm_of_mdiobus_register(dev, bus, node);
869 static void a5psw_pcs_free(struct a5psw *a5psw)
873 for (i = 0; i < ARRAY_SIZE(a5psw->pcs); i++) {
875 miic_destroy(a5psw->pcs[i]);
879 static int a5psw_pcs_get(struct a5psw *a5psw)
881 struct device_node *ports, *port, *pcs_node;
882 struct phylink_pcs *pcs;
886 ports = of_get_child_by_name(a5psw->dev->of_node, "ethernet-ports");
890 for_each_available_child_of_node(ports, port) {
891 pcs_node = of_parse_phandle(port, "pcs-handle", 0);
895 if (of_property_read_u32(port, "reg", ®)) {
900 if (reg >= ARRAY_SIZE(a5psw->pcs)) {
905 pcs = miic_create(a5psw->dev, pcs_node);
907 dev_err(a5psw->dev, "Failed to create PCS for port %d\n",
913 a5psw->pcs[reg] = pcs;
914 of_node_put(pcs_node);
921 of_node_put(pcs_node);
924 a5psw_pcs_free(a5psw);
929 static int a5psw_probe(struct platform_device *pdev)
931 struct device *dev = &pdev->dev;
932 struct device_node *mdio;
933 struct dsa_switch *ds;
937 a5psw = devm_kzalloc(dev, sizeof(*a5psw), GFP_KERNEL);
942 mutex_init(&a5psw->lk_lock);
943 spin_lock_init(&a5psw->reg_lock);
944 a5psw->base = devm_platform_ioremap_resource(pdev, 0);
945 if (IS_ERR(a5psw->base))
946 return PTR_ERR(a5psw->base);
948 ret = a5psw_pcs_get(a5psw);
952 a5psw->hclk = devm_clk_get(dev, "hclk");
953 if (IS_ERR(a5psw->hclk)) {
954 dev_err(dev, "failed get hclk clock\n");
955 ret = PTR_ERR(a5psw->hclk);
959 a5psw->clk = devm_clk_get(dev, "clk");
960 if (IS_ERR(a5psw->clk)) {
961 dev_err(dev, "failed get clk_switch clock\n");
962 ret = PTR_ERR(a5psw->clk);
966 ret = clk_prepare_enable(a5psw->clk);
970 ret = clk_prepare_enable(a5psw->hclk);
974 mdio = of_get_child_by_name(dev->of_node, "mdio");
975 if (of_device_is_available(mdio)) {
976 ret = a5psw_probe_mdio(a5psw, mdio);
979 dev_err(dev, "Failed to register MDIO: %d\n", ret);
988 ds->num_ports = A5PSW_PORTS_NUM;
989 ds->ops = &a5psw_switch_ops;
992 ret = dsa_register_switch(ds);
994 dev_err(dev, "Failed to register DSA switch: %d\n", ret);
1001 clk_disable_unprepare(a5psw->hclk);
1003 clk_disable_unprepare(a5psw->clk);
1005 a5psw_pcs_free(a5psw);
1010 static int a5psw_remove(struct platform_device *pdev)
1012 struct a5psw *a5psw = platform_get_drvdata(pdev);
1017 dsa_unregister_switch(&a5psw->ds);
1018 a5psw_pcs_free(a5psw);
1019 clk_disable_unprepare(a5psw->hclk);
1020 clk_disable_unprepare(a5psw->clk);
1025 static void a5psw_shutdown(struct platform_device *pdev)
1027 struct a5psw *a5psw = platform_get_drvdata(pdev);
1032 dsa_switch_shutdown(&a5psw->ds);
1034 platform_set_drvdata(pdev, NULL);
1037 static const struct of_device_id a5psw_of_mtable[] = {
1038 { .compatible = "renesas,rzn1-a5psw", },
1041 MODULE_DEVICE_TABLE(of, a5psw_of_mtable);
1043 static struct platform_driver a5psw_driver = {
1045 .name = "rzn1_a5psw",
1046 .of_match_table = of_match_ptr(a5psw_of_mtable),
1048 .probe = a5psw_probe,
1049 .remove = a5psw_remove,
1050 .shutdown = a5psw_shutdown,
1052 module_platform_driver(a5psw_driver);
1054 MODULE_LICENSE("GPL");
1055 MODULE_DESCRIPTION("Renesas RZ/N1 Advanced 5-port Switch driver");
1056 MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");