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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-06-27 10:41:02 -04:00
Родитель 5db15872c5 bf7afb29d5
Коммит de2fbe7ae3
5 изменённых файлов: 222 добавлений и 126 удалений

Просмотреть файл

@ -12,6 +12,9 @@ menuconfig PHYLIB
if PHYLIB if PHYLIB
config SWPHY
bool
comment "MII PHY device drivers" comment "MII PHY device drivers"
config AQUANTIA_PHY config AQUANTIA_PHY
@ -159,6 +162,7 @@ config MICROCHIP_PHY
config FIXED_PHY config FIXED_PHY
tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
depends on PHYLIB depends on PHYLIB
select SWPHY
---help--- ---help---
Adds the platform "fixed" MDIO Bus to cover the boards that use Adds the platform "fixed" MDIO Bus to cover the boards that use
PHYs that are not connected to the real MDIO bus. PHYs that are not connected to the real MDIO bus.

Просмотреть файл

@ -1,6 +1,7 @@
# Makefile for Linux PHY drivers # 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_PHYLIB) += libphy.o
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o

Просмотреть файл

@ -23,8 +23,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/seqlock.h>
#define MII_REGS_NUM 29 #include "swphy.h"
struct fixed_mdio_bus { struct fixed_mdio_bus {
struct mii_bus *mii_bus; struct mii_bus *mii_bus;
@ -33,8 +34,8 @@ struct fixed_mdio_bus {
struct fixed_phy { struct fixed_phy {
int addr; int addr;
u16 regs[MII_REGS_NUM];
struct phy_device *phydev; struct phy_device *phydev;
seqcount_t seqcount;
struct fixed_phy_status status; struct fixed_phy_status status;
int (*link_update)(struct net_device *, struct fixed_phy_status *); int (*link_update)(struct net_device *, struct fixed_phy_status *);
struct list_head node; struct list_head node;
@ -46,103 +47,10 @@ static struct fixed_mdio_bus platform_fmb = {
.phys = LIST_HEAD_INIT(platform_fmb.phys), .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)) if (gpio_is_valid(fp->link_gpio))
fp->status.link = !!gpio_get_value_cansleep(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) 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_mdio_bus *fmb = bus->priv;
struct fixed_phy *fp; 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) { list_for_each_entry(fp, &fmb->phys, node) {
if (fp->addr == phy_addr) { if (fp->addr == phy_addr) {
/* Issue callback if user registered it. */ struct fixed_phy_status state;
if (fp->link_update) { int s;
fp->link_update(fp->phydev->attached_dev,
&fp->status); do {
fixed_phy_update_regs(fp); s = read_seqcount_begin(&fp->seqcount);
} /* Issue callback if user registered it. */
return fp->regs[reg_num]; if (fp->link_update) {
fp->link_update(fp->phydev->attached_dev,
&fp->status);
fixed_phy_update(fp);
}
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) { list_for_each_entry(fp, &fmb->phys, node) {
if (fp->addr == phydev->mdio.addr) { if (fp->addr == phydev->mdio.addr) {
write_seqcount_begin(&fp->seqcount);
#define _UPD(x) if (changed->x) \ #define _UPD(x) if (changed->x) \
fp->status.x = status->x fp->status.x = status->x
_UPD(link); _UPD(link);
@ -232,7 +135,8 @@ int fixed_phy_update_state(struct phy_device *phydev,
_UPD(pause); _UPD(pause);
_UPD(asym_pause); _UPD(asym_pause);
#undef _UPD #undef _UPD
fixed_phy_update_regs(fp); fixed_phy_update(fp);
write_seqcount_end(&fp->seqcount);
return 0; 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_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp; struct fixed_phy *fp;
ret = swphy_validate_state(status);
if (ret < 0)
return ret;
fp = kzalloc(sizeof(*fp), GFP_KERNEL); fp = kzalloc(sizeof(*fp), GFP_KERNEL);
if (!fp) if (!fp)
return -ENOMEM; return -ENOMEM;
memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); seqcount_init(&fp->seqcount);
if (irq != PHY_POLL) if (irq != PHY_POLL)
fmb->mii_bus->irq[phy_addr] = irq; fmb->mii_bus->irq[phy_addr] = irq;
@ -269,17 +177,12 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
goto err_regs; goto err_regs;
} }
ret = fixed_phy_update_regs(fp); fixed_phy_update(fp);
if (ret)
goto err_gpio;
list_add_tail(&fp->node, &fmb->phys); list_add_tail(&fp->node, &fmb->phys);
return 0; return 0;
err_gpio:
if (gpio_is_valid(fp->link_gpio))
gpio_free(fp->link_gpio);
err_regs: err_regs:
kfree(fp); kfree(fp);
return ret; return ret;

179
drivers/net/phy/swphy.c Normal file
Просмотреть файл

@ -0,0 +1,179 @@
/*
* 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
drivers/net/phy/swphy.h Normal file
Просмотреть файл

@ -0,0 +1,9 @@
#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