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 inline struct b53_pcs
*pcs_to_b53_pcs(struct phylink_pcs
*pcs
)
22 return container_of(pcs
, struct b53_pcs
, pcs
);
25 static void b53_serdes_write_blk(struct b53_device
*dev
, u8 offset
, u16 block
,
28 b53_write16(dev
, B53_SERDES_PAGE
, B53_SERDES_BLKADDR
, block
);
29 b53_write16(dev
, B53_SERDES_PAGE
, offset
, value
);
32 static u16
b53_serdes_read_blk(struct b53_device
*dev
, u8 offset
, u16 block
)
36 b53_write16(dev
, B53_SERDES_PAGE
, B53_SERDES_BLKADDR
, block
);
37 b53_read16(dev
, B53_SERDES_PAGE
, offset
, &value
);
42 static void b53_serdes_set_lane(struct b53_device
*dev
, u8 lane
)
44 if (dev
->serdes_lane
== lane
)
49 b53_serdes_write_blk(dev
, B53_SERDES_LANE
,
50 SERDES_XGXSBLK0_BLOCKADDRESS
, lane
);
51 dev
->serdes_lane
= lane
;
54 static void b53_serdes_write(struct b53_device
*dev
, u8 lane
,
55 u8 offset
, u16 block
, u16 value
)
57 b53_serdes_set_lane(dev
, lane
);
58 b53_serdes_write_blk(dev
, offset
, block
, value
);
61 static u16
b53_serdes_read(struct b53_device
*dev
, u8 lane
,
64 b53_serdes_set_lane(dev
, lane
);
65 return b53_serdes_read_blk(dev
, offset
, block
);
68 static int b53_serdes_config(struct phylink_pcs
*pcs
, unsigned int neg_mode
,
69 phy_interface_t interface
,
70 const unsigned long *advertising
,
71 bool permit_pause_to_mac
)
73 struct b53_device
*dev
= pcs_to_b53_pcs(pcs
)->dev
;
74 u8 lane
= pcs_to_b53_pcs(pcs
)->lane
;
77 reg
= b53_serdes_read(dev
, lane
, B53_SERDES_DIGITAL_CONTROL(1),
79 if (interface
== PHY_INTERFACE_MODE_1000BASEX
)
80 reg
|= FIBER_MODE_1000X
;
82 reg
&= ~FIBER_MODE_1000X
;
83 b53_serdes_write(dev
, lane
, B53_SERDES_DIGITAL_CONTROL(1),
84 SERDES_DIGITAL_BLK
, reg
);
89 static void b53_serdes_an_restart(struct phylink_pcs
*pcs
)
91 struct b53_device
*dev
= pcs_to_b53_pcs(pcs
)->dev
;
92 u8 lane
= pcs_to_b53_pcs(pcs
)->lane
;
95 reg
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
97 reg
|= BMCR_ANRESTART
;
98 b53_serdes_write(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
102 static void b53_serdes_get_state(struct phylink_pcs
*pcs
,
103 struct phylink_link_state
*state
)
105 struct b53_device
*dev
= pcs_to_b53_pcs(pcs
)->dev
;
106 u8 lane
= pcs_to_b53_pcs(pcs
)->lane
;
109 dig
= b53_serdes_read(dev
, lane
, B53_SERDES_DIGITAL_STATUS
,
111 bmsr
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_BMSR
),
114 switch ((dig
>> SPEED_STATUS_SHIFT
) & SPEED_STATUS_MASK
) {
115 case SPEED_STATUS_10
:
116 state
->speed
= SPEED_10
;
118 case SPEED_STATUS_100
:
119 state
->speed
= SPEED_100
;
121 case SPEED_STATUS_1000
:
122 state
->speed
= SPEED_1000
;
125 case SPEED_STATUS_2500
:
126 state
->speed
= SPEED_2500
;
130 state
->duplex
= dig
& DUPLEX_STATUS
? DUPLEX_FULL
: DUPLEX_HALF
;
131 state
->an_complete
= !!(bmsr
& BMSR_ANEGCOMPLETE
);
132 state
->link
= !!(dig
& LINK_STATUS
);
133 if (dig
& PAUSE_RESOLUTION_RX_SIDE
)
134 state
->pause
|= MLO_PAUSE_RX
;
135 if (dig
& PAUSE_RESOLUTION_TX_SIDE
)
136 state
->pause
|= MLO_PAUSE_TX
;
139 void b53_serdes_link_set(struct b53_device
*dev
, int port
, unsigned int mode
,
140 phy_interface_t interface
, bool link_up
)
142 u8 lane
= b53_serdes_map_lane(dev
, port
);
145 if (lane
== B53_INVALID_LANE
)
148 reg
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
154 b53_serdes_write(dev
, lane
, B53_SERDES_MII_REG(MII_BMCR
),
155 SERDES_MII_BLK
, reg
);
157 EXPORT_SYMBOL(b53_serdes_link_set
);
159 static const struct phylink_pcs_ops b53_pcs_ops
= {
160 .pcs_get_state
= b53_serdes_get_state
,
161 .pcs_config
= b53_serdes_config
,
162 .pcs_an_restart
= b53_serdes_an_restart
,
165 void b53_serdes_phylink_get_caps(struct b53_device
*dev
, int port
,
166 struct phylink_config
*config
)
168 u8 lane
= b53_serdes_map_lane(dev
, port
);
170 if (lane
== B53_INVALID_LANE
)
175 /* It appears lane 0 supports 2500base-X and 1000base-X */
176 __set_bit(PHY_INTERFACE_MODE_2500BASEX
,
177 config
->supported_interfaces
);
178 config
->mac_capabilities
|= MAC_2500FD
;
181 /* It appears lane 1 only supports 1000base-X and SGMII */
182 __set_bit(PHY_INTERFACE_MODE_1000BASEX
,
183 config
->supported_interfaces
);
184 __set_bit(PHY_INTERFACE_MODE_SGMII
,
185 config
->supported_interfaces
);
186 config
->mac_capabilities
|= MAC_1000FD
;
192 EXPORT_SYMBOL(b53_serdes_phylink_get_caps
);
194 struct phylink_pcs
*b53_serdes_phylink_mac_select_pcs(struct b53_device
*dev
,
196 phy_interface_t interface
)
198 u8 lane
= b53_serdes_map_lane(dev
, port
);
200 if (lane
== B53_INVALID_LANE
|| lane
>= B53_N_PCS
||
204 if (!phy_interface_mode_is_8023z(interface
) &&
205 interface
!= PHY_INTERFACE_MODE_SGMII
)
208 return &dev
->pcs
[lane
].pcs
;
210 EXPORT_SYMBOL(b53_serdes_phylink_mac_select_pcs
);
212 int b53_serdes_init(struct b53_device
*dev
, int port
)
214 u8 lane
= b53_serdes_map_lane(dev
, port
);
218 if (lane
== B53_INVALID_LANE
)
221 id0
= b53_serdes_read(dev
, lane
, B53_SERDES_ID0
, SERDES_ID0
);
222 msb
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_PHYSID1
),
224 lsb
= b53_serdes_read(dev
, lane
, B53_SERDES_MII_REG(MII_PHYSID2
),
226 if (id0
== 0 || id0
== 0xffff) {
227 dev_err(dev
->dev
, "SerDes not initialized, check settings\n");
232 "SerDes lane %d, model: %d, rev %c%d (OUI: 0x%08x)\n",
233 lane
, id0
& SERDES_ID0_MODEL_MASK
,
234 (id0
>> SERDES_ID0_REV_LETTER_SHIFT
) + 0x41,
235 (id0
>> SERDES_ID0_REV_NUM_SHIFT
) & SERDES_ID0_REV_NUM_MASK
,
236 (u32
)msb
<< 16 | lsb
);
238 pcs
= &dev
->pcs
[lane
];
241 pcs
->pcs
.ops
= &b53_pcs_ops
;
242 pcs
->pcs
.neg_mode
= true;
246 EXPORT_SYMBOL(b53_serdes_init
);
248 MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
249 MODULE_DESCRIPTION("B53 Switch SerDes driver");
250 MODULE_LICENSE("Dual BSD/GPL");