1 // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
3 * Northstar Plus switch SerDes/SGMII PHY main logic
5 * Copyright (C) 2018 Florian Fainelli <f.fainelli@gmail.com>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/delay.h>
11 #include <linux/kernel.h>
12 #include <linux/phy.h>
13 #include <linux/phylink.h>
17 #include "b53_serdes.h"
20 static void b53_serdes_write_blk(struct b53_device
*dev
, u8 offset
, u16 block
,
23 b53_write16(dev
, B53_SERDES_PAGE
, B53_SERDES_BLKADDR
, block
);
24 b53_write16(dev
, B53_SERDES_PAGE
, offset
, value
);
27 static u16
b53_serdes_read_blk(struct b53_device
*dev
, u8 offset
, u16 block
)
31 b53_write16(dev
, B53_SERDES_PAGE
, B53_SERDES_BLKADDR
, block
);
32 b53_read16(dev
, B53_SERDES_PAGE
, offset
, &value
);
37 static void b53_serdes_set_lane(struct b53_device
*dev
, u8 lane
)
39 if (dev
->serdes_lane
== lane
)
44 b53_serdes_write_blk(dev
, B53_SERDES_LANE
,
45 SERDES_XGXSBLK0_BLOCKADDRESS
, lane
);
46 dev
->serdes_lane
= lane
;
49 static void b53_serdes_write(struct b53_device
*dev
, u8 lane
,
50 u8 offset
, u16 block
, u16 value
)
52 b53_serdes_set_lane(dev
, lane
);
53 b53_serdes_write_blk(dev
, offset
, block
, value
);
56 static u16
b53_serdes_read(struct b53_device
*dev
, u8 lane
,
59 b53_serdes_set_lane(dev
, lane
);
60 return b53_serdes_read_blk(dev
, offset
, block
);
63 void b53_serdes_config(struct b53_device
*dev
, int port
, unsigned int mode
,
64 const struct phylink_link_state
*state
)
66 u8 lane
= b53_serdes_map_lane(dev
, port
);
69 if (lane
== B53_INVALID_LANE
)
72 reg
= b53_serdes_read(dev
, lane
, B53_SERDES_DIGITAL_CONTROL(1),
74 if (state
->interface
== PHY_INTERFACE_MODE_1000BASEX
)
75 reg
|= FIBER_MODE_1000X
;
77 reg
&= ~FIBER_MODE_1000X
;
78 b53_serdes_write(dev
, lane
, B53_SERDES_DIGITAL_CONTROL(1),
79 SERDES_DIGITAL_BLK
, reg
);
81 EXPORT_SYMBOL(b53_serdes_config
);
83 void b53_serdes_an_restart(struct b53_device
*dev
, int port
)
85 u8 lane
= b53_serdes_map_lane(dev
, port
);
88 if (lane
== B53_INVALID_LANE
)
91 reg
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
93 reg
|= BMCR_ANRESTART
;
94 b53_serdes_write(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
97 EXPORT_SYMBOL(b53_serdes_an_restart
);
99 int b53_serdes_link_state(struct b53_device
*dev
, int port
,
100 struct phylink_link_state
*state
)
102 u8 lane
= b53_serdes_map_lane(dev
, port
);
105 if (lane
== B53_INVALID_LANE
)
108 dig
= b53_serdes_read(dev
, lane
, B53_SERDES_DIGITAL_STATUS
,
110 bmsr
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_BMSR
),
113 switch ((dig
>> SPEED_STATUS_SHIFT
) & SPEED_STATUS_MASK
) {
114 case SPEED_STATUS_10
:
115 state
->speed
= SPEED_10
;
117 case SPEED_STATUS_100
:
118 state
->speed
= SPEED_100
;
120 case SPEED_STATUS_1000
:
121 state
->speed
= SPEED_1000
;
124 case SPEED_STATUS_2500
:
125 state
->speed
= SPEED_2500
;
129 state
->duplex
= dig
& DUPLEX_STATUS
? DUPLEX_FULL
: DUPLEX_HALF
;
130 state
->an_complete
= !!(bmsr
& BMSR_ANEGCOMPLETE
);
131 state
->link
= !!(dig
& LINK_STATUS
);
132 if (dig
& PAUSE_RESOLUTION_RX_SIDE
)
133 state
->pause
|= MLO_PAUSE_RX
;
134 if (dig
& PAUSE_RESOLUTION_TX_SIDE
)
135 state
->pause
|= MLO_PAUSE_TX
;
139 EXPORT_SYMBOL(b53_serdes_link_state
);
141 void b53_serdes_link_set(struct b53_device
*dev
, int port
, unsigned int mode
,
142 phy_interface_t interface
, bool link_up
)
144 u8 lane
= b53_serdes_map_lane(dev
, port
);
147 if (lane
== B53_INVALID_LANE
)
150 reg
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
156 b53_serdes_write(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
157 SERDES_MII_BLK
, reg
);
159 EXPORT_SYMBOL(b53_serdes_link_set
);
161 void b53_serdes_phylink_validate(struct b53_device
*dev
, int port
,
162 unsigned long *supported
,
163 struct phylink_link_state
*state
)
165 u8 lane
= b53_serdes_map_lane(dev
, port
);
167 if (lane
== B53_INVALID_LANE
)
172 phylink_set(supported
, 2500baseX_Full
);
175 phylink_set(supported
, 1000baseX_Full
);
181 EXPORT_SYMBOL(b53_serdes_phylink_validate
);
183 int b53_serdes_init(struct b53_device
*dev
, int port
)
185 u8 lane
= b53_serdes_map_lane(dev
, port
);
188 if (lane
== B53_INVALID_LANE
)
191 id0
= b53_serdes_read(dev
, lane
, B53_SERDES_ID0
, SERDES_ID0
);
192 msb
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_PHYSID1
),
194 lsb
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_PHYSID2
),
196 if (id0
== 0 || id0
== 0xffff) {
197 dev_err(dev
->dev
, "SerDes not initialized, check settings\n");
202 "SerDes lane %d, model: %d, rev %c%d (OUI: 0x%08x)\n",
203 lane
, id0
& SERDES_ID0_MODEL_MASK
,
204 (id0
>> SERDES_ID0_REV_LETTER_SHIFT
) + 0x41,
205 (id0
>> SERDES_ID0_REV_NUM_SHIFT
) & SERDES_ID0_REV_NUM_MASK
,
206 (u32
)msb
<< 16 | lsb
);
210 EXPORT_SYMBOL(b53_serdes_init
);
212 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
213 MODULE_DESCRIPTION("B53 Switch SerDes driver");
214 MODULE_LICENSE("Dual BSD/GPL");