1*59da9885SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
20e01491dSFlorian Fainelli /*
30e01491dSFlorian Fainelli * Northstar Plus switch SerDes/SGMII PHY main logic
40e01491dSFlorian Fainelli *
50e01491dSFlorian Fainelli * Copyright (C) 2018 Florian Fainelli <f.fainelli@gmail.com>
60e01491dSFlorian Fainelli */
70e01491dSFlorian Fainelli
80e01491dSFlorian Fainelli #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
90e01491dSFlorian Fainelli
100e01491dSFlorian Fainelli #include <linux/delay.h>
110e01491dSFlorian Fainelli #include <linux/kernel.h>
120e01491dSFlorian Fainelli #include <linux/phy.h>
130e01491dSFlorian Fainelli #include <linux/phylink.h>
140e01491dSFlorian Fainelli #include <net/dsa.h>
150e01491dSFlorian Fainelli
160e01491dSFlorian Fainelli #include "b53_priv.h"
170e01491dSFlorian Fainelli #include "b53_serdes.h"
180e01491dSFlorian Fainelli #include "b53_regs.h"
190e01491dSFlorian Fainelli
pcs_to_b53_pcs(struct phylink_pcs * pcs)2079396934SRussell King (Oracle) static inline struct b53_pcs *pcs_to_b53_pcs(struct phylink_pcs *pcs)
2179396934SRussell King (Oracle) {
2279396934SRussell King (Oracle) return container_of(pcs, struct b53_pcs, pcs);
2379396934SRussell King (Oracle) }
2479396934SRussell King (Oracle)
b53_serdes_write_blk(struct b53_device * dev,u8 offset,u16 block,u16 value)250e01491dSFlorian Fainelli static void b53_serdes_write_blk(struct b53_device *dev, u8 offset, u16 block,
260e01491dSFlorian Fainelli u16 value)
270e01491dSFlorian Fainelli {
280e01491dSFlorian Fainelli b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
290e01491dSFlorian Fainelli b53_write16(dev, B53_SERDES_PAGE, offset, value);
300e01491dSFlorian Fainelli }
310e01491dSFlorian Fainelli
b53_serdes_read_blk(struct b53_device * dev,u8 offset,u16 block)320e01491dSFlorian Fainelli static u16 b53_serdes_read_blk(struct b53_device *dev, u8 offset, u16 block)
330e01491dSFlorian Fainelli {
340e01491dSFlorian Fainelli u16 value;
350e01491dSFlorian Fainelli
360e01491dSFlorian Fainelli b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
370e01491dSFlorian Fainelli b53_read16(dev, B53_SERDES_PAGE, offset, &value);
380e01491dSFlorian Fainelli
390e01491dSFlorian Fainelli return value;
400e01491dSFlorian Fainelli }
410e01491dSFlorian Fainelli
b53_serdes_set_lane(struct b53_device * dev,u8 lane)420e01491dSFlorian Fainelli static void b53_serdes_set_lane(struct b53_device *dev, u8 lane)
430e01491dSFlorian Fainelli {
440e01491dSFlorian Fainelli if (dev->serdes_lane == lane)
450e01491dSFlorian Fainelli return;
460e01491dSFlorian Fainelli
470e01491dSFlorian Fainelli WARN_ON(lane > 1);
480e01491dSFlorian Fainelli
490e01491dSFlorian Fainelli b53_serdes_write_blk(dev, B53_SERDES_LANE,
500e01491dSFlorian Fainelli SERDES_XGXSBLK0_BLOCKADDRESS, lane);
510e01491dSFlorian Fainelli dev->serdes_lane = lane;
520e01491dSFlorian Fainelli }
530e01491dSFlorian Fainelli
b53_serdes_write(struct b53_device * dev,u8 lane,u8 offset,u16 block,u16 value)540e01491dSFlorian Fainelli static void b53_serdes_write(struct b53_device *dev, u8 lane,
550e01491dSFlorian Fainelli u8 offset, u16 block, u16 value)
560e01491dSFlorian Fainelli {
570e01491dSFlorian Fainelli b53_serdes_set_lane(dev, lane);
580e01491dSFlorian Fainelli b53_serdes_write_blk(dev, offset, block, value);
590e01491dSFlorian Fainelli }
600e01491dSFlorian Fainelli
b53_serdes_read(struct b53_device * dev,u8 lane,u8 offset,u16 block)610e01491dSFlorian Fainelli static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
620e01491dSFlorian Fainelli u8 offset, u16 block)
630e01491dSFlorian Fainelli {
640e01491dSFlorian Fainelli b53_serdes_set_lane(dev, lane);
650e01491dSFlorian Fainelli return b53_serdes_read_blk(dev, offset, block);
660e01491dSFlorian Fainelli }
670e01491dSFlorian Fainelli
b53_serdes_config(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)68772c476dSRussell King (Oracle) static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int neg_mode,
6979396934SRussell King (Oracle) phy_interface_t interface,
7079396934SRussell King (Oracle) const unsigned long *advertising,
7179396934SRussell King (Oracle) bool permit_pause_to_mac)
720e01491dSFlorian Fainelli {
7379396934SRussell King (Oracle) struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
7479396934SRussell King (Oracle) u8 lane = pcs_to_b53_pcs(pcs)->lane;
750e01491dSFlorian Fainelli u16 reg;
760e01491dSFlorian Fainelli
770e01491dSFlorian Fainelli reg = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
780e01491dSFlorian Fainelli SERDES_DIGITAL_BLK);
7979396934SRussell King (Oracle) if (interface == PHY_INTERFACE_MODE_1000BASEX)
800e01491dSFlorian Fainelli reg |= FIBER_MODE_1000X;
810e01491dSFlorian Fainelli else
820e01491dSFlorian Fainelli reg &= ~FIBER_MODE_1000X;
830e01491dSFlorian Fainelli b53_serdes_write(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
840e01491dSFlorian Fainelli SERDES_DIGITAL_BLK, reg);
8579396934SRussell King (Oracle)
8679396934SRussell King (Oracle) return 0;
870e01491dSFlorian Fainelli }
880e01491dSFlorian Fainelli
b53_serdes_an_restart(struct phylink_pcs * pcs)8979396934SRussell King (Oracle) static void b53_serdes_an_restart(struct phylink_pcs *pcs)
900e01491dSFlorian Fainelli {
9179396934SRussell King (Oracle) struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
9279396934SRussell King (Oracle) u8 lane = pcs_to_b53_pcs(pcs)->lane;
930e01491dSFlorian Fainelli u16 reg;
940e01491dSFlorian Fainelli
950e01491dSFlorian Fainelli reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
960e01491dSFlorian Fainelli SERDES_MII_BLK);
970e01491dSFlorian Fainelli reg |= BMCR_ANRESTART;
980e01491dSFlorian Fainelli b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
990e01491dSFlorian Fainelli SERDES_MII_BLK, reg);
1000e01491dSFlorian Fainelli }
1010e01491dSFlorian Fainelli
b53_serdes_get_state(struct phylink_pcs * pcs,struct phylink_link_state * state)10279396934SRussell King (Oracle) static void b53_serdes_get_state(struct phylink_pcs *pcs,
1030e01491dSFlorian Fainelli struct phylink_link_state *state)
1040e01491dSFlorian Fainelli {
10579396934SRussell King (Oracle) struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
10679396934SRussell King (Oracle) u8 lane = pcs_to_b53_pcs(pcs)->lane;
107e24cf6b3SFlorian Fainelli u16 dig, bmsr;
1080e01491dSFlorian Fainelli
1090e01491dSFlorian Fainelli dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
1100e01491dSFlorian Fainelli SERDES_DIGITAL_BLK);
1110e01491dSFlorian Fainelli bmsr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMSR),
1120e01491dSFlorian Fainelli SERDES_MII_BLK);
1130e01491dSFlorian Fainelli
1140e01491dSFlorian Fainelli switch ((dig >> SPEED_STATUS_SHIFT) & SPEED_STATUS_MASK) {
1150e01491dSFlorian Fainelli case SPEED_STATUS_10:
1160e01491dSFlorian Fainelli state->speed = SPEED_10;
1170e01491dSFlorian Fainelli break;
1180e01491dSFlorian Fainelli case SPEED_STATUS_100:
1190e01491dSFlorian Fainelli state->speed = SPEED_100;
1200e01491dSFlorian Fainelli break;
1210e01491dSFlorian Fainelli case SPEED_STATUS_1000:
1220e01491dSFlorian Fainelli state->speed = SPEED_1000;
1230e01491dSFlorian Fainelli break;
1240e01491dSFlorian Fainelli default:
1250e01491dSFlorian Fainelli case SPEED_STATUS_2500:
1260e01491dSFlorian Fainelli state->speed = SPEED_2500;
1270e01491dSFlorian Fainelli break;
1280e01491dSFlorian Fainelli }
1290e01491dSFlorian Fainelli
1300e01491dSFlorian Fainelli state->duplex = dig & DUPLEX_STATUS ? DUPLEX_FULL : DUPLEX_HALF;
1310e01491dSFlorian Fainelli state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
1320e01491dSFlorian Fainelli state->link = !!(dig & LINK_STATUS);
1330e01491dSFlorian Fainelli if (dig & PAUSE_RESOLUTION_RX_SIDE)
1340e01491dSFlorian Fainelli state->pause |= MLO_PAUSE_RX;
1350e01491dSFlorian Fainelli if (dig & PAUSE_RESOLUTION_TX_SIDE)
1360e01491dSFlorian Fainelli state->pause |= MLO_PAUSE_TX;
1370e01491dSFlorian Fainelli }
1380e01491dSFlorian Fainelli
b53_serdes_link_set(struct b53_device * dev,int port,unsigned int mode,phy_interface_t interface,bool link_up)1390e01491dSFlorian Fainelli void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode,
1400e01491dSFlorian Fainelli phy_interface_t interface, bool link_up)
1410e01491dSFlorian Fainelli {
1420e01491dSFlorian Fainelli u8 lane = b53_serdes_map_lane(dev, port);
1430e01491dSFlorian Fainelli u16 reg;
1440e01491dSFlorian Fainelli
1450e01491dSFlorian Fainelli if (lane == B53_INVALID_LANE)
1460e01491dSFlorian Fainelli return;
1470e01491dSFlorian Fainelli
1480e01491dSFlorian Fainelli reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
1490e01491dSFlorian Fainelli SERDES_MII_BLK);
1500e01491dSFlorian Fainelli if (link_up)
1510e01491dSFlorian Fainelli reg &= ~BMCR_PDOWN;
1520e01491dSFlorian Fainelli else
1530e01491dSFlorian Fainelli reg |= BMCR_PDOWN;
1540e01491dSFlorian Fainelli b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
1550e01491dSFlorian Fainelli SERDES_MII_BLK, reg);
1560e01491dSFlorian Fainelli }
1570e01491dSFlorian Fainelli EXPORT_SYMBOL(b53_serdes_link_set);
1580e01491dSFlorian Fainelli
15979396934SRussell King (Oracle) static const struct phylink_pcs_ops b53_pcs_ops = {
16079396934SRussell King (Oracle) .pcs_get_state = b53_serdes_get_state,
16179396934SRussell King (Oracle) .pcs_config = b53_serdes_config,
16279396934SRussell King (Oracle) .pcs_an_restart = b53_serdes_an_restart,
16379396934SRussell King (Oracle) };
16479396934SRussell King (Oracle)
b53_serdes_phylink_get_caps(struct b53_device * dev,int port,struct phylink_config * config)165dda1c257SRussell King (Oracle) void b53_serdes_phylink_get_caps(struct b53_device *dev, int port,
166dda1c257SRussell King (Oracle) struct phylink_config *config)
167dda1c257SRussell King (Oracle) {
168dda1c257SRussell King (Oracle) u8 lane = b53_serdes_map_lane(dev, port);
169dda1c257SRussell King (Oracle)
170dda1c257SRussell King (Oracle) if (lane == B53_INVALID_LANE)
171dda1c257SRussell King (Oracle) return;
172dda1c257SRussell King (Oracle)
173dda1c257SRussell King (Oracle) switch (lane) {
174dda1c257SRussell King (Oracle) case 0:
175dda1c257SRussell King (Oracle) /* It appears lane 0 supports 2500base-X and 1000base-X */
176dda1c257SRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_2500BASEX,
177dda1c257SRussell King (Oracle) config->supported_interfaces);
178dda1c257SRussell King (Oracle) config->mac_capabilities |= MAC_2500FD;
179dda1c257SRussell King (Oracle) fallthrough;
180dda1c257SRussell King (Oracle) case 1:
181dda1c257SRussell King (Oracle) /* It appears lane 1 only supports 1000base-X and SGMII */
182dda1c257SRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_1000BASEX,
183dda1c257SRussell King (Oracle) config->supported_interfaces);
184dda1c257SRussell King (Oracle) __set_bit(PHY_INTERFACE_MODE_SGMII,
185dda1c257SRussell King (Oracle) config->supported_interfaces);
186dda1c257SRussell King (Oracle) config->mac_capabilities |= MAC_1000FD;
187dda1c257SRussell King (Oracle) break;
188dda1c257SRussell King (Oracle) default:
189dda1c257SRussell King (Oracle) break;
190dda1c257SRussell King (Oracle) }
191dda1c257SRussell King (Oracle) }
192dda1c257SRussell King (Oracle) EXPORT_SYMBOL(b53_serdes_phylink_get_caps);
193dda1c257SRussell King (Oracle)
b53_serdes_phylink_mac_select_pcs(struct b53_device * dev,int port,phy_interface_t interface)19479396934SRussell King (Oracle) struct phylink_pcs *b53_serdes_phylink_mac_select_pcs(struct b53_device *dev,
19579396934SRussell King (Oracle) int port,
19679396934SRussell King (Oracle) phy_interface_t interface)
19779396934SRussell King (Oracle) {
19879396934SRussell King (Oracle) u8 lane = b53_serdes_map_lane(dev, port);
19979396934SRussell King (Oracle)
20079396934SRussell King (Oracle) if (lane == B53_INVALID_LANE || lane >= B53_N_PCS ||
20179396934SRussell King (Oracle) !dev->pcs[lane].dev)
20279396934SRussell King (Oracle) return NULL;
20379396934SRussell King (Oracle)
20479396934SRussell King (Oracle) if (!phy_interface_mode_is_8023z(interface) &&
20579396934SRussell King (Oracle) interface != PHY_INTERFACE_MODE_SGMII)
20679396934SRussell King (Oracle) return NULL;
20779396934SRussell King (Oracle)
20879396934SRussell King (Oracle) return &dev->pcs[lane].pcs;
20979396934SRussell King (Oracle) }
21079396934SRussell King (Oracle) EXPORT_SYMBOL(b53_serdes_phylink_mac_select_pcs);
21179396934SRussell King (Oracle)
b53_serdes_init(struct b53_device * dev,int port)2120e01491dSFlorian Fainelli int b53_serdes_init(struct b53_device *dev, int port)
2130e01491dSFlorian Fainelli {
2140e01491dSFlorian Fainelli u8 lane = b53_serdes_map_lane(dev, port);
21579396934SRussell King (Oracle) struct b53_pcs *pcs;
2160e01491dSFlorian Fainelli u16 id0, msb, lsb;
2170e01491dSFlorian Fainelli
2180e01491dSFlorian Fainelli if (lane == B53_INVALID_LANE)
2190e01491dSFlorian Fainelli return -EINVAL;
2200e01491dSFlorian Fainelli
2210e01491dSFlorian Fainelli id0 = b53_serdes_read(dev, lane, B53_SERDES_ID0, SERDES_ID0);
2220e01491dSFlorian Fainelli msb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID1),
2230e01491dSFlorian Fainelli SERDES_MII_BLK);
2240e01491dSFlorian Fainelli lsb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID2),
2250e01491dSFlorian Fainelli SERDES_MII_BLK);
2260e01491dSFlorian Fainelli if (id0 == 0 || id0 == 0xffff) {
2270e01491dSFlorian Fainelli dev_err(dev->dev, "SerDes not initialized, check settings\n");
2280e01491dSFlorian Fainelli return -ENODEV;
2290e01491dSFlorian Fainelli }
2300e01491dSFlorian Fainelli
2310e01491dSFlorian Fainelli dev_info(dev->dev,
2320e01491dSFlorian Fainelli "SerDes lane %d, model: %d, rev %c%d (OUI: 0x%08x)\n",
2330e01491dSFlorian Fainelli lane, id0 & SERDES_ID0_MODEL_MASK,
2340e01491dSFlorian Fainelli (id0 >> SERDES_ID0_REV_LETTER_SHIFT) + 0x41,
2350e01491dSFlorian Fainelli (id0 >> SERDES_ID0_REV_NUM_SHIFT) & SERDES_ID0_REV_NUM_MASK,
2360e01491dSFlorian Fainelli (u32)msb << 16 | lsb);
2370e01491dSFlorian Fainelli
23879396934SRussell King (Oracle) pcs = &dev->pcs[lane];
23979396934SRussell King (Oracle) pcs->dev = dev;
24079396934SRussell King (Oracle) pcs->lane = lane;
24179396934SRussell King (Oracle) pcs->pcs.ops = &b53_pcs_ops;
242772c476dSRussell King (Oracle) pcs->pcs.neg_mode = true;
24379396934SRussell King (Oracle)
2440e01491dSFlorian Fainelli return 0;
2450e01491dSFlorian Fainelli }
2460e01491dSFlorian Fainelli EXPORT_SYMBOL(b53_serdes_init);
2470e01491dSFlorian Fainelli
2480e01491dSFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
2490e01491dSFlorian Fainelli MODULE_DESCRIPTION("B53 Switch SerDes driver");
2500e01491dSFlorian Fainelli MODULE_LICENSE("Dual BSD/GPL");
251