1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Marvell 88E6185 family SERDES PCS support
5 * Copyright (c) 2008 Marvell Semiconductor
7 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
9 #include <linux/phylink.h>
15 struct mv88e6185_pcs
{
16 struct phylink_pcs phylink_pcs
;
20 struct mv88e6xxx_chip
*chip
;
24 static struct mv88e6185_pcs
*pcs_to_mv88e6185_pcs(struct phylink_pcs
*pcs
)
26 return container_of(pcs
, struct mv88e6185_pcs
, phylink_pcs
);
29 static irqreturn_t
mv88e6185_pcs_handle_irq(int irq
, void *dev_id
)
31 struct mv88e6185_pcs
*mpcs
= dev_id
;
32 struct mv88e6xxx_chip
*chip
;
33 irqreturn_t ret
= IRQ_NONE
;
42 mv88e6xxx_reg_lock(chip
);
43 err
= mv88e6xxx_port_read(chip
, port
, MV88E6XXX_PORT_STS
, &status
);
44 mv88e6xxx_reg_unlock(chip
);
47 link_up
= !!(status
& MV88E6XXX_PORT_STS_LINK
);
49 phylink_pcs_change(&mpcs
->phylink_pcs
, link_up
);
57 static void mv88e6185_pcs_get_state(struct phylink_pcs
*pcs
,
58 struct phylink_link_state
*state
)
60 struct mv88e6185_pcs
*mpcs
= pcs_to_mv88e6185_pcs(pcs
);
61 struct mv88e6xxx_chip
*chip
= mpcs
->chip
;
62 int port
= mpcs
->port
;
66 mv88e6xxx_reg_lock(chip
);
67 err
= mv88e6xxx_port_read(chip
, port
, MV88E6XXX_PORT_STS
, &status
);
68 mv88e6xxx_reg_unlock(chip
);
73 state
->link
= !!(status
& MV88E6XXX_PORT_STS_LINK
);
75 state
->duplex
= status
& MV88E6XXX_PORT_STS_DUPLEX
?
76 DUPLEX_FULL
: DUPLEX_HALF
;
78 switch (status
& MV88E6XXX_PORT_STS_SPEED_MASK
) {
79 case MV88E6XXX_PORT_STS_SPEED_1000
:
80 state
->speed
= SPEED_1000
;
83 case MV88E6XXX_PORT_STS_SPEED_100
:
84 state
->speed
= SPEED_100
;
87 case MV88E6XXX_PORT_STS_SPEED_10
:
88 state
->speed
= SPEED_10
;
98 static int mv88e6185_pcs_config(struct phylink_pcs
*pcs
, unsigned int neg_mode
,
99 phy_interface_t interface
,
100 const unsigned long *advertising
,
101 bool permit_pause_to_mac
)
106 static void mv88e6185_pcs_an_restart(struct phylink_pcs
*pcs
)
110 static const struct phylink_pcs_ops mv88e6185_phylink_pcs_ops
= {
111 .pcs_get_state
= mv88e6185_pcs_get_state
,
112 .pcs_config
= mv88e6185_pcs_config
,
113 .pcs_an_restart
= mv88e6185_pcs_an_restart
,
116 static int mv88e6185_pcs_init(struct mv88e6xxx_chip
*chip
, int port
)
118 struct mv88e6185_pcs
*mpcs
;
123 /* There are no configurable serdes lanes on this switch chip, so
124 * we use the static cmode configuration to determine whether we
127 if (chip
->ports
[port
].cmode
!= MV88E6185_PORT_STS_CMODE_SERDES
&&
128 chip
->ports
[port
].cmode
!= MV88E6185_PORT_STS_CMODE_1000BASE_X
)
133 mpcs
= kzalloc(sizeof(*mpcs
), GFP_KERNEL
);
139 mpcs
->phylink_pcs
.ops
= &mv88e6185_phylink_pcs_ops
;
140 mpcs
->phylink_pcs
.neg_mode
= true;
142 irq
= mv88e6xxx_serdes_irq_mapping(chip
, port
);
144 snprintf(mpcs
->name
, sizeof(mpcs
->name
),
145 "mv88e6xxx-%s-serdes-%d", dev_name(dev
), port
);
147 err
= request_threaded_irq(irq
, NULL
, mv88e6185_pcs_handle_irq
,
148 IRQF_ONESHOT
, mpcs
->name
, mpcs
);
156 mpcs
->phylink_pcs
.poll
= true;
159 chip
->ports
[port
].pcs_private
= &mpcs
->phylink_pcs
;
164 static void mv88e6185_pcs_teardown(struct mv88e6xxx_chip
*chip
, int port
)
166 struct mv88e6185_pcs
*mpcs
;
168 mpcs
= chip
->ports
[port
].pcs_private
;
173 free_irq(mpcs
->irq
, mpcs
);
177 chip
->ports
[port
].pcs_private
= NULL
;
180 static struct phylink_pcs
*mv88e6185_pcs_select(struct mv88e6xxx_chip
*chip
,
182 phy_interface_t interface
)
184 return chip
->ports
[port
].pcs_private
;
187 const struct mv88e6xxx_pcs_ops mv88e6185_pcs_ops
= {
188 .pcs_init
= mv88e6185_pcs_init
,
189 .pcs_teardown
= mv88e6185_pcs_teardown
,
190 .pcs_select
= mv88e6185_pcs_select
,