Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de2fbe7a authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'sfp-infra'



Russell King says:

====================
Initial SFP support patches

Please review and merge this initial patch set, which is part of a
larger set previously posted adding SFP support to phy and mvneta.

This initial set are focused on cleaning up and reorganising the
fixed-phy code to allow the core software-phy code to be re-used.

These are based on net-next.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5db15872 bf7afb29
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -12,6 +12,9 @@ menuconfig PHYLIB

if PHYLIB

config SWPHY
	bool

comment "MII PHY device drivers"

config AQUANTIA_PHY
@@ -159,6 +162,7 @@ config MICROCHIP_PHY
config FIXED_PHY
	tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
	depends on PHYLIB
	select SWPHY
	---help---
	  Adds the platform "fixed" MDIO Bus to cover the boards that use
	  PHYs that are not connected to the real MDIO bus.
+2 −1
Original line number Diff line number Diff line
# Makefile for Linux PHY drivers

libphy-objs			:= phy.o phy_device.o mdio_bus.o mdio_device.o
libphy-y			:= phy.o phy_device.o mdio_bus.o mdio_device.o
libphy-$(CONFIG_SWPHY)		+= swphy.o

obj-$(CONFIG_PHYLIB)		+= libphy.o
obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia.o
+28 −125
Original line number Diff line number Diff line
@@ -23,8 +23,9 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/seqlock.h>

#define MII_REGS_NUM 29
#include "swphy.h"

struct fixed_mdio_bus {
	struct mii_bus *mii_bus;
@@ -33,8 +34,8 @@ struct fixed_mdio_bus {

struct fixed_phy {
	int addr;
	u16 regs[MII_REGS_NUM];
	struct phy_device *phydev;
	seqcount_t seqcount;
	struct fixed_phy_status status;
	int (*link_update)(struct net_device *, struct fixed_phy_status *);
	struct list_head node;
@@ -46,103 +47,10 @@ static struct fixed_mdio_bus platform_fmb = {
	.phys = LIST_HEAD_INIT(platform_fmb.phys),
};

static int fixed_phy_update_regs(struct fixed_phy *fp)
static void fixed_phy_update(struct fixed_phy *fp)
{
	u16 bmsr = BMSR_ANEGCAPABLE;
	u16 bmcr = 0;
	u16 lpagb = 0;
	u16 lpa = 0;

	if (gpio_is_valid(fp->link_gpio))
		fp->status.link = !!gpio_get_value_cansleep(fp->link_gpio);

	if (fp->status.duplex) {
		switch (fp->status.speed) {
		case 1000:
			bmsr |= BMSR_ESTATEN;
			break;
		case 100:
			bmsr |= BMSR_100FULL;
			break;
		case 10:
			bmsr |= BMSR_10FULL;
			break;
		default:
			break;
		}
	} else {
		switch (fp->status.speed) {
		case 1000:
			bmsr |= BMSR_ESTATEN;
			break;
		case 100:
			bmsr |= BMSR_100HALF;
			break;
		case 10:
			bmsr |= BMSR_10HALF;
			break;
		default:
			break;
		}
	}

	if (fp->status.link) {
		bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;

		if (fp->status.duplex) {
			bmcr |= BMCR_FULLDPLX;

			switch (fp->status.speed) {
			case 1000:
				bmcr |= BMCR_SPEED1000;
				lpagb |= LPA_1000FULL;
				break;
			case 100:
				bmcr |= BMCR_SPEED100;
				lpa |= LPA_100FULL;
				break;
			case 10:
				lpa |= LPA_10FULL;
				break;
			default:
				pr_warn("fixed phy: unknown speed\n");
				return -EINVAL;
			}
		} else {
			switch (fp->status.speed) {
			case 1000:
				bmcr |= BMCR_SPEED1000;
				lpagb |= LPA_1000HALF;
				break;
			case 100:
				bmcr |= BMCR_SPEED100;
				lpa |= LPA_100HALF;
				break;
			case 10:
				lpa |= LPA_10HALF;
				break;
			default:
				pr_warn("fixed phy: unknown speed\n");
			return -EINVAL;
			}
		}

		if (fp->status.pause)
			lpa |= LPA_PAUSE_CAP;

		if (fp->status.asym_pause)
			lpa |= LPA_PAUSE_ASYM;
	}

	fp->regs[MII_PHYSID1] = 0;
	fp->regs[MII_PHYSID2] = 0;

	fp->regs[MII_BMSR] = bmsr;
	fp->regs[MII_BMCR] = bmcr;
	fp->regs[MII_LPA] = lpa;
	fp->regs[MII_STAT1000] = lpagb;

	return 0;
}

static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
@@ -150,29 +58,23 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
	struct fixed_mdio_bus *fmb = bus->priv;
	struct fixed_phy *fp;

	if (reg_num >= MII_REGS_NUM)
		return -1;

	/* We do not support emulating Clause 45 over Clause 22 register reads
	 * return an error instead of bogus data.
	 */
	switch (reg_num) {
	case MII_MMD_CTRL:
	case MII_MMD_DATA:
		return -1;
	default:
		break;
	}

	list_for_each_entry(fp, &fmb->phys, node) {
		if (fp->addr == phy_addr) {
			struct fixed_phy_status state;
			int s;

			do {
				s = read_seqcount_begin(&fp->seqcount);
				/* Issue callback if user registered it. */
				if (fp->link_update) {
					fp->link_update(fp->phydev->attached_dev,
							&fp->status);
				fixed_phy_update_regs(fp);
					fixed_phy_update(fp);
				}
			return fp->regs[reg_num];
				state = fp->status;
			} while (read_seqcount_retry(&fp->seqcount, s));

			return swphy_read_reg(reg_num, &state);
		}
	}

@@ -224,6 +126,7 @@ int fixed_phy_update_state(struct phy_device *phydev,

	list_for_each_entry(fp, &fmb->phys, node) {
		if (fp->addr == phydev->mdio.addr) {
			write_seqcount_begin(&fp->seqcount);
#define _UPD(x) if (changed->x) \
	fp->status.x = status->x
			_UPD(link);
@@ -232,7 +135,8 @@ int fixed_phy_update_state(struct phy_device *phydev,
			_UPD(pause);
			_UPD(asym_pause);
#undef _UPD
			fixed_phy_update_regs(fp);
			fixed_phy_update(fp);
			write_seqcount_end(&fp->seqcount);
			return 0;
		}
	}
@@ -249,11 +153,15 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
	struct fixed_mdio_bus *fmb = &platform_fmb;
	struct fixed_phy *fp;

	ret = swphy_validate_state(status);
	if (ret < 0)
		return ret;

	fp = kzalloc(sizeof(*fp), GFP_KERNEL);
	if (!fp)
		return -ENOMEM;

	memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
	seqcount_init(&fp->seqcount);

	if (irq != PHY_POLL)
		fmb->mii_bus->irq[phy_addr] = irq;
@@ -269,17 +177,12 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
			goto err_regs;
	}

	ret = fixed_phy_update_regs(fp);
	if (ret)
		goto err_gpio;
	fixed_phy_update(fp);

	list_add_tail(&fp->node, &fmb->phys);

	return 0;

err_gpio:
	if (gpio_is_valid(fp->link_gpio))
		gpio_free(fp->link_gpio);
err_regs:
	kfree(fp);
	return ret;
+179 −0
Original line number Diff line number Diff line
/*
 * Software PHY emulation
 *
 * Code taken from fixed_phy.c by Russell King <rmk+kernel@arm.linux.org.uk>
 *
 * Author: Vitaly Bordug <vbordug@ru.mvista.com>
 *         Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * Copyright (c) 2006-2007 MontaVista Software, Inc.
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */
#include <linux/export.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>

#include "swphy.h"

#define MII_REGS_NUM 29

struct swmii_regs {
	u16 bmcr;
	u16 bmsr;
	u16 lpa;
	u16 lpagb;
};

enum {
	SWMII_SPEED_10 = 0,
	SWMII_SPEED_100,
	SWMII_SPEED_1000,
	SWMII_DUPLEX_HALF = 0,
	SWMII_DUPLEX_FULL,
};

/*
 * These two tables get bitwise-anded together to produce the final result.
 * This means the speed table must contain both duplex settings, and the
 * duplex table must contain all speed settings.
 */
static const struct swmii_regs speed[] = {
	[SWMII_SPEED_10] = {
		.bmcr  = BMCR_FULLDPLX,
		.lpa   = LPA_10FULL | LPA_10HALF,
	},
	[SWMII_SPEED_100] = {
		.bmcr  = BMCR_FULLDPLX | BMCR_SPEED100,
		.bmsr  = BMSR_100FULL | BMSR_100HALF,
		.lpa   = LPA_100FULL | LPA_100HALF,
	},
	[SWMII_SPEED_1000] = {
		.bmcr  = BMCR_FULLDPLX | BMCR_SPEED1000,
		.bmsr  = BMSR_ESTATEN,
		.lpagb = LPA_1000FULL | LPA_1000HALF,
	},
};

static const struct swmii_regs duplex[] = {
	[SWMII_DUPLEX_HALF] = {
		.bmcr  = ~BMCR_FULLDPLX,
		.bmsr  = BMSR_ESTATEN | BMSR_100HALF,
		.lpa   = LPA_10HALF | LPA_100HALF,
		.lpagb = LPA_1000HALF,
	},
	[SWMII_DUPLEX_FULL] = {
		.bmcr  = ~0,
		.bmsr  = BMSR_ESTATEN | BMSR_100FULL,
		.lpa   = LPA_10FULL | LPA_100FULL,
		.lpagb = LPA_1000FULL,
	},
};

static int swphy_decode_speed(int speed)
{
	switch (speed) {
	case 1000:
		return SWMII_SPEED_1000;
	case 100:
		return SWMII_SPEED_100;
	case 10:
		return SWMII_SPEED_10;
	default:
		return -EINVAL;
	}
}

/**
 * swphy_validate_state - validate the software phy status
 * @state: software phy status
 *
 * This checks that we can represent the state stored in @state can be
 * represented in the emulated MII registers.  Returns 0 if it can,
 * otherwise returns -EINVAL.
 */
int swphy_validate_state(const struct fixed_phy_status *state)
{
	int err;

	if (state->link) {
		err = swphy_decode_speed(state->speed);
		if (err < 0) {
			pr_warn("swphy: unknown speed\n");
			return -EINVAL;
		}
	}
	return 0;
}
EXPORT_SYMBOL_GPL(swphy_validate_state);

/**
 * swphy_read_reg - return a MII register from the fixed phy state
 * @reg: MII register
 * @state: fixed phy status
 *
 * Return the MII @reg register generated from the fixed phy state @state.
 */
int swphy_read_reg(int reg, const struct fixed_phy_status *state)
{
	int speed_index, duplex_index;
	u16 bmsr = BMSR_ANEGCAPABLE;
	u16 bmcr = 0;
	u16 lpagb = 0;
	u16 lpa = 0;

	if (reg > MII_REGS_NUM)
		return -1;

	speed_index = swphy_decode_speed(state->speed);
	if (WARN_ON(speed_index < 0))
		return 0;

	duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;

	bmsr |= speed[speed_index].bmsr & duplex[duplex_index].bmsr;

	if (state->link) {
		bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;

		bmcr  |= speed[speed_index].bmcr  & duplex[duplex_index].bmcr;
		lpa   |= speed[speed_index].lpa   & duplex[duplex_index].lpa;
		lpagb |= speed[speed_index].lpagb & duplex[duplex_index].lpagb;

		if (state->pause)
			lpa |= LPA_PAUSE_CAP;

		if (state->asym_pause)
			lpa |= LPA_PAUSE_ASYM;
	}

	switch (reg) {
	case MII_BMCR:
		return bmcr;
	case MII_BMSR:
		return bmsr;
	case MII_PHYSID1:
	case MII_PHYSID2:
		return 0;
	case MII_LPA:
		return lpa;
	case MII_STAT1000:
		return lpagb;

	/*
	 * We do not support emulating Clause 45 over Clause 22 register
	 * reads.  Return an error instead of bogus data.
	 */
	case MII_MMD_CTRL:
	case MII_MMD_DATA:
		return -1;

	default:
		return 0xffff;
	}
}
EXPORT_SYMBOL_GPL(swphy_read_reg);
+9 −0
Original line number Diff line number Diff line
#ifndef SWPHY_H
#define SWPHY_H

struct fixed_phy_status;

int swphy_validate_state(const struct fixed_phy_status *state);
int swphy_read_reg(int reg, const struct fixed_phy_status *state);

#endif