mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
Merge branch 'dt_fixed_phy'
Thomas Petazzoni says: ==================== Add DT support for fixed PHYs Here is a fourth version of the patch set that adds a Device Tree binding and the related code to support fixed PHYs. I'm hoping to get this merged in 3.16. Changes since v3: * Rebased on top of v3.15-rc5 * In patch "net: phy: decouple PHY id and PHY address in fixed PHY driver", changed the PHY ID of fixed PHYs from 0xdeadbeef to 0x0, as suggested by Grant Likely. * Fixed the !CONFIG_PHY_FIXED case in patch "net: phy: extend fixed driver with fixed_phy_register()". Noticed by Florian Fainelli. * Added Acked-by from Grant Likely and Florian Fainelli on patch "net: phy: extend fixed driver with fixed_phy_register()". * Reworked the new fixed-link DT binding to be just a sub-node of the Ethernet MAC node, and not a node referenced by the 'phy' property. This was requested by Grant Likely. * Reworked the code implementing the new DT binding to also make it accept the old, single property based, DT binding. * Added a patch that actually uses the new fixed link DT binding for the Armada XP Matrix board. Changes since v2: * Rebased on top of v3.14-rc1, and re-tested on hardware. * Removed the RFC tag, since there seems to be some real interest in this feature, and the code has gone through several iterations already. * The error handling in fixed_phy_register() has been fixed. Changes since v1: * Instead of using a 'fixed-link' property inside the Ethernet device DT node, with a fairly cryptic succession of integer values, we now use a PHY subnode under the Ethernet device DT node, with explicit properties to configure the duplex, speed, pause and other PHY properties. * The PHY address is automatically allocated by the kernel and no longer visible in the Device Tree binding. * The PHY device is created directly when the network driver calls of_phy_connect_fixed_link(), and associated to the PHY DT node, which allows the existing of_phy_connect() function to work, without the need to use the deprecated of_phy_connect_fixed_link(). Posts of previous versions: RFCv1: http://www.spinics.net/lists/netdev/msg243253.html RFCv2: http://lists.infradead.org/pipermail/linux-arm-kernel/2013-September/196919.html PATCHv3: http://www.spinics.net/lists/netdev/msg273117.html ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
ad2ebb3d4d
30
Documentation/devicetree/bindings/net/fixed-link.txt
Normal file
30
Documentation/devicetree/bindings/net/fixed-link.txt
Normal file
@ -0,0 +1,30 @@
|
||||
Fixed link Device Tree binding
|
||||
------------------------------
|
||||
|
||||
Some Ethernet MACs have a "fixed link", and are not connected to a
|
||||
normal MDIO-managed PHY device. For those situations, a Device Tree
|
||||
binding allows to describe a "fixed link".
|
||||
|
||||
Such a fixed link situation is described by creating a 'fixed-link'
|
||||
sub-node of the Ethernet MAC device node, with the following
|
||||
properties:
|
||||
|
||||
* 'speed' (integer, mandatory), to indicate the link speed. Accepted
|
||||
values are 10, 100 and 1000
|
||||
* 'full-duplex' (boolean, optional), to indicate that full duplex is
|
||||
used. When absent, half duplex is assumed.
|
||||
* 'pause' (boolean, optional), to indicate that pause should be
|
||||
enabled.
|
||||
* 'asym-pause' (boolean, optional), to indicate that asym_pause should
|
||||
be enabled.
|
||||
|
||||
Example:
|
||||
|
||||
ethernet@0 {
|
||||
...
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
...
|
||||
};
|
@ -61,6 +61,10 @@
|
||||
ethernet@30000 {
|
||||
status = "okay";
|
||||
phy-mode = "sgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
pcie-controller {
|
||||
|
@ -2797,9 +2797,22 @@ static int mvneta_probe(struct platform_device *pdev)
|
||||
|
||||
phy_node = of_parse_phandle(dn, "phy", 0);
|
||||
if (!phy_node) {
|
||||
dev_err(&pdev->dev, "no associated PHY\n");
|
||||
err = -ENODEV;
|
||||
goto err_free_irq;
|
||||
if (!of_phy_is_fixed_link(dn)) {
|
||||
dev_err(&pdev->dev, "no PHY specified\n");
|
||||
err = -ENODEV;
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
err = of_phy_register_fixed_link(dn);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "cannot register fixed PHY\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
/* In the case of a fixed PHY, the DT node associated
|
||||
* to the PHY is the Ethernet MAC DT node.
|
||||
*/
|
||||
phy_node = dn;
|
||||
}
|
||||
|
||||
phy_mode = of_get_phy_mode(dn);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/phy_fixed.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define MII_REGS_NUM 29
|
||||
|
||||
@ -31,7 +32,7 @@ struct fixed_mdio_bus {
|
||||
};
|
||||
|
||||
struct fixed_phy {
|
||||
int id;
|
||||
int addr;
|
||||
u16 regs[MII_REGS_NUM];
|
||||
struct phy_device *phydev;
|
||||
struct fixed_phy_status status;
|
||||
@ -104,8 +105,8 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
|
||||
if (fp->status.asym_pause)
|
||||
lpa |= LPA_PAUSE_ASYM;
|
||||
|
||||
fp->regs[MII_PHYSID1] = fp->id >> 16;
|
||||
fp->regs[MII_PHYSID2] = fp->id;
|
||||
fp->regs[MII_PHYSID1] = 0;
|
||||
fp->regs[MII_PHYSID2] = 0;
|
||||
|
||||
fp->regs[MII_BMSR] = bmsr;
|
||||
fp->regs[MII_BMCR] = bmcr;
|
||||
@ -115,7 +116,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
|
||||
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;
|
||||
@ -124,7 +125,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
|
||||
return -1;
|
||||
|
||||
list_for_each_entry(fp, &fmb->phys, node) {
|
||||
if (fp->id == phy_id) {
|
||||
if (fp->addr == phy_addr) {
|
||||
/* Issue callback if user registered it. */
|
||||
if (fp->link_update) {
|
||||
fp->link_update(fp->phydev->attached_dev,
|
||||
@ -138,7 +139,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
|
||||
static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
|
||||
u16 val)
|
||||
{
|
||||
return 0;
|
||||
@ -160,7 +161,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(fp, &fmb->phys, node) {
|
||||
if (fp->id == phydev->phy_id) {
|
||||
if (fp->addr == phydev->addr) {
|
||||
fp->link_update = link_update;
|
||||
fp->phydev = phydev;
|
||||
return 0;
|
||||
@ -171,7 +172,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
|
||||
|
||||
int fixed_phy_add(unsigned int irq, int phy_id,
|
||||
int fixed_phy_add(unsigned int irq, int phy_addr,
|
||||
struct fixed_phy_status *status)
|
||||
{
|
||||
int ret;
|
||||
@ -184,9 +185,9 @@ int fixed_phy_add(unsigned int irq, int phy_id,
|
||||
|
||||
memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM);
|
||||
|
||||
fmb->irqs[phy_id] = irq;
|
||||
fmb->irqs[phy_addr] = irq;
|
||||
|
||||
fp->id = phy_id;
|
||||
fp->addr = phy_addr;
|
||||
fp->status = *status;
|
||||
|
||||
ret = fixed_phy_update_regs(fp);
|
||||
@ -203,6 +204,66 @@ err_regs:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fixed_phy_add);
|
||||
|
||||
void fixed_phy_del(int phy_addr)
|
||||
{
|
||||
struct fixed_mdio_bus *fmb = &platform_fmb;
|
||||
struct fixed_phy *fp, *tmp;
|
||||
|
||||
list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
|
||||
if (fp->addr == phy_addr) {
|
||||
list_del(&fp->node);
|
||||
kfree(fp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fixed_phy_del);
|
||||
|
||||
static int phy_fixed_addr;
|
||||
static DEFINE_SPINLOCK(phy_fixed_addr_lock);
|
||||
|
||||
int fixed_phy_register(unsigned int irq,
|
||||
struct fixed_phy_status *status,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct fixed_mdio_bus *fmb = &platform_fmb;
|
||||
struct phy_device *phy;
|
||||
int phy_addr;
|
||||
int ret;
|
||||
|
||||
/* Get the next available PHY address, up to PHY_MAX_ADDR */
|
||||
spin_lock(&phy_fixed_addr_lock);
|
||||
if (phy_fixed_addr == PHY_MAX_ADDR) {
|
||||
spin_unlock(&phy_fixed_addr_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
phy_addr = phy_fixed_addr++;
|
||||
spin_unlock(&phy_fixed_addr_lock);
|
||||
|
||||
ret = fixed_phy_add(PHY_POLL, phy_addr, status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
phy = get_phy_device(fmb->mii_bus, phy_addr, false);
|
||||
if (!phy || IS_ERR(phy)) {
|
||||
fixed_phy_del(phy_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
of_node_get(np);
|
||||
phy->dev.of_node = np;
|
||||
|
||||
ret = phy_device_register(phy);
|
||||
if (ret) {
|
||||
phy_device_free(phy);
|
||||
of_node_put(np);
|
||||
fixed_phy_del(phy_addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init fixed_mdio_bus_init(void)
|
||||
{
|
||||
struct fixed_mdio_bus *fmb = &platform_fmb;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy_fixed.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_mdio.h>
|
||||
@ -301,3 +302,69 @@ struct phy_device *of_phy_attach(struct net_device *dev,
|
||||
return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
|
||||
}
|
||||
EXPORT_SYMBOL(of_phy_attach);
|
||||
|
||||
#if defined(CONFIG_FIXED_PHY)
|
||||
/*
|
||||
* of_phy_is_fixed_link() and of_phy_register_fixed_link() must
|
||||
* support two DT bindings:
|
||||
* - the old DT binding, where 'fixed-link' was a property with 5
|
||||
* cells encoding various informations about the fixed PHY
|
||||
* - the new DT binding, where 'fixed-link' is a sub-node of the
|
||||
* Ethernet device.
|
||||
*/
|
||||
bool of_phy_is_fixed_link(struct device_node *np)
|
||||
{
|
||||
struct device_node *dn;
|
||||
int len;
|
||||
|
||||
/* New binding */
|
||||
dn = of_get_child_by_name(np, "fixed-link");
|
||||
if (dn) {
|
||||
of_node_put(dn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Old binding */
|
||||
if (of_get_property(np, "fixed-link", &len) &&
|
||||
len == (5 * sizeof(__be32)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(of_phy_is_fixed_link);
|
||||
|
||||
int of_phy_register_fixed_link(struct device_node *np)
|
||||
{
|
||||
struct fixed_phy_status status = {};
|
||||
struct device_node *fixed_link_node;
|
||||
const __be32 *fixed_link_prop;
|
||||
int len;
|
||||
|
||||
/* New binding */
|
||||
fixed_link_node = of_get_child_by_name(np, "fixed-link");
|
||||
if (fixed_link_node) {
|
||||
status.link = 1;
|
||||
status.duplex = of_property_read_bool(np, "full-duplex");
|
||||
if (of_property_read_u32(fixed_link_node, "speed", &status.speed))
|
||||
return -EINVAL;
|
||||
status.pause = of_property_read_bool(np, "pause");
|
||||
status.asym_pause = of_property_read_bool(np, "asym-pause");
|
||||
of_node_put(fixed_link_node);
|
||||
return fixed_phy_register(PHY_POLL, &status, np);
|
||||
}
|
||||
|
||||
/* Old binding */
|
||||
fixed_link_prop = of_get_property(np, "fixed-link", &len);
|
||||
if (fixed_link_prop && len == (5 * sizeof(__be32))) {
|
||||
status.link = 1;
|
||||
status.duplex = be32_to_cpu(fixed_link_prop[1]);
|
||||
status.speed = be32_to_cpu(fixed_link_prop[2]);
|
||||
status.pause = be32_to_cpu(fixed_link_prop[3]);
|
||||
status.asym_pause = be32_to_cpu(fixed_link_prop[4]);
|
||||
return fixed_phy_register(PHY_POLL, &status, np);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(of_phy_register_fixed_link);
|
||||
#endif
|
||||
|
@ -72,4 +72,19 @@ static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np)
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY)
|
||||
extern int of_phy_register_fixed_link(struct device_node *np);
|
||||
extern bool of_phy_is_fixed_link(struct device_node *np);
|
||||
#else
|
||||
static inline int of_phy_register_fixed_link(struct device_node *np)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline bool of_phy_is_fixed_link(struct device_node *np)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __LINUX_OF_MDIO_H */
|
||||
|
@ -9,15 +9,26 @@ struct fixed_phy_status {
|
||||
int asym_pause;
|
||||
};
|
||||
|
||||
struct device_node;
|
||||
|
||||
#ifdef CONFIG_FIXED_PHY
|
||||
extern int fixed_phy_add(unsigned int irq, int phy_id,
|
||||
struct fixed_phy_status *status);
|
||||
extern int fixed_phy_register(unsigned int irq,
|
||||
struct fixed_phy_status *status,
|
||||
struct device_node *np);
|
||||
#else
|
||||
static inline int fixed_phy_add(unsigned int irq, int phy_id,
|
||||
struct fixed_phy_status *status)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int fixed_phy_register(unsigned int irq,
|
||||
struct fixed_phy_status *status,
|
||||
struct device_node *np)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif /* CONFIG_FIXED_PHY */
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user