192 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Marvell 88E6185 family SERDES PCS support4 *5 * Copyright (c) 2008 Marvell Semiconductor6 *7 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>8 */9#include <linux/phylink.h>10 11#include "global2.h"12#include "port.h"13#include "serdes.h"14 15struct mv88e6185_pcs {16 struct phylink_pcs phylink_pcs;17 unsigned int irq;18 char name[64];19 20 struct mv88e6xxx_chip *chip;21 int port;22};23 24static struct mv88e6185_pcs *pcs_to_mv88e6185_pcs(struct phylink_pcs *pcs)25{26 return container_of(pcs, struct mv88e6185_pcs, phylink_pcs);27}28 29static irqreturn_t mv88e6185_pcs_handle_irq(int irq, void *dev_id)30{31 struct mv88e6185_pcs *mpcs = dev_id;32 struct mv88e6xxx_chip *chip;33 irqreturn_t ret = IRQ_NONE;34 bool link_up;35 u16 status;36 int port;37 int err;38 39 chip = mpcs->chip;40 port = mpcs->port;41 42 mv88e6xxx_reg_lock(chip);43 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);44 mv88e6xxx_reg_unlock(chip);45 46 if (!err) {47 link_up = !!(status & MV88E6XXX_PORT_STS_LINK);48 49 phylink_pcs_change(&mpcs->phylink_pcs, link_up);50 51 ret = IRQ_HANDLED;52 }53 54 return ret;55}56 57static void mv88e6185_pcs_get_state(struct phylink_pcs *pcs,58 struct phylink_link_state *state)59{60 struct mv88e6185_pcs *mpcs = pcs_to_mv88e6185_pcs(pcs);61 struct mv88e6xxx_chip *chip = mpcs->chip;62 int port = mpcs->port;63 u16 status;64 int err;65 66 mv88e6xxx_reg_lock(chip);67 err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);68 mv88e6xxx_reg_unlock(chip);69 70 if (err)71 status = 0;72 73 state->link = !!(status & MV88E6XXX_PORT_STS_LINK);74 if (state->link) {75 state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ?76 DUPLEX_FULL : DUPLEX_HALF;77 78 switch (status & MV88E6XXX_PORT_STS_SPEED_MASK) {79 case MV88E6XXX_PORT_STS_SPEED_1000:80 state->speed = SPEED_1000;81 break;82 83 case MV88E6XXX_PORT_STS_SPEED_100:84 state->speed = SPEED_100;85 break;86 87 case MV88E6XXX_PORT_STS_SPEED_10:88 state->speed = SPEED_10;89 break;90 91 default:92 state->link = false;93 break;94 }95 }96}97 98static 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)102{103 return 0;104}105 106static void mv88e6185_pcs_an_restart(struct phylink_pcs *pcs)107{108}109 110static 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,114};115 116static int mv88e6185_pcs_init(struct mv88e6xxx_chip *chip, int port)117{118 struct mv88e6185_pcs *mpcs;119 struct device *dev;120 unsigned int irq;121 int err;122 123 /* There are no configurable serdes lanes on this switch chip, so124 * we use the static cmode configuration to determine whether we125 * have a PCS or not.126 */127 if (chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_SERDES &&128 chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_1000BASE_X)129 return 0;130 131 dev = chip->dev;132 133 mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);134 if (!mpcs)135 return -ENOMEM;136 137 mpcs->chip = chip;138 mpcs->port = port;139 mpcs->phylink_pcs.ops = &mv88e6185_phylink_pcs_ops;140 mpcs->phylink_pcs.neg_mode = true;141 142 irq = mv88e6xxx_serdes_irq_mapping(chip, port);143 if (irq) {144 snprintf(mpcs->name, sizeof(mpcs->name),145 "mv88e6xxx-%s-serdes-%d", dev_name(dev), port);146 147 err = request_threaded_irq(irq, NULL, mv88e6185_pcs_handle_irq,148 IRQF_ONESHOT, mpcs->name, mpcs);149 if (err) {150 kfree(mpcs);151 return err;152 }153 154 mpcs->irq = irq;155 } else {156 mpcs->phylink_pcs.poll = true;157 }158 159 chip->ports[port].pcs_private = &mpcs->phylink_pcs;160 161 return 0;162}163 164static void mv88e6185_pcs_teardown(struct mv88e6xxx_chip *chip, int port)165{166 struct mv88e6185_pcs *mpcs;167 168 mpcs = chip->ports[port].pcs_private;169 if (!mpcs)170 return;171 172 if (mpcs->irq)173 free_irq(mpcs->irq, mpcs);174 175 kfree(mpcs);176 177 chip->ports[port].pcs_private = NULL;178}179 180static struct phylink_pcs *mv88e6185_pcs_select(struct mv88e6xxx_chip *chip,181 int port,182 phy_interface_t interface)183{184 return chip->ports[port].pcs_private;185}186 187const 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,191};192