brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · 5a27d04 Raw
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