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

Merge branch 'ax88772-phylib'



Oleksij Rempel says:

====================
port asix ax88772 to the PHYlib

changes v2:
- add Reviewed-by: Andrew Lunn <andrew@lunn.ch> to some patches
- refactor asix_read_phy_addr() and add error handling for all callers
- refactor asix_mdio_bus_read()

Port ax88772 part of asix driver to the phylib to be able to use more
advanced external PHY attached to this controller.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ef91f798 2c9d6c2b
Loading
Loading
Loading
Loading
+73 −1
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@
#include <linux/mii.h>
#include <linux/phy.h>

#define PHY_ID_ASIX_AX88772A		0x003b1861
#define PHY_ID_ASIX_AX88772C		0x003b1881
#define PHY_ID_ASIX_AX88796B		0x003b1841

MODULE_DESCRIPTION("Asix PHY driver");
@@ -39,7 +41,75 @@ static int asix_soft_reset(struct phy_device *phydev)
	return genphy_soft_reset(phydev);
}

static struct phy_driver asix_driver[] = { {
/* AX88772A is not working properly with some old switches (NETGEAR EN 108TP):
 * after autoneg is done and the link status is reported as active, the MII_LPA
 * register is 0. This issue is not reproducible on AX88772C.
 */
static int asix_ax88772a_read_status(struct phy_device *phydev)
{
	int ret, val;

	ret = genphy_update_link(phydev);
	if (ret)
		return ret;

	if (!phydev->link)
		return 0;

	/* If MII_LPA is 0, phy_resolve_aneg_linkmode() will fail to resolve
	 * linkmode so use MII_BMCR as default values.
	 */
	val = phy_read(phydev, MII_BMCR);
	if (val < 0)
		return val;

	if (val & BMCR_SPEED100)
		phydev->speed = SPEED_100;
	else
		phydev->speed = SPEED_10;

	if (val & BMCR_FULLDPLX)
		phydev->duplex = DUPLEX_FULL;
	else
		phydev->duplex = DUPLEX_HALF;

	ret = genphy_read_lpa(phydev);
	if (ret < 0)
		return ret;

	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
		phy_resolve_aneg_linkmode(phydev);

	return 0;
}

static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
{
	/* Reset PHY, otherwise MII_LPA will provide outdated information.
	 * This issue is reproducible only with some link partner PHYs
	 */
	if (phydev->state == PHY_NOLINK && phydev->drv->soft_reset)
		phydev->drv->soft_reset(phydev);
}

static struct phy_driver asix_driver[] = {
{
	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A),
	.name		= "Asix Electronics AX88772A",
	.flags		= PHY_IS_INTERNAL,
	.read_status	= asix_ax88772a_read_status,
	.suspend	= genphy_suspend,
	.resume		= genphy_resume,
	.soft_reset	= asix_soft_reset,
	.link_change_notify	= asix_ax88772a_link_change_notify,
}, {
	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C),
	.name		= "Asix Electronics AX88772C",
	.flags		= PHY_IS_INTERNAL,
	.suspend	= genphy_suspend,
	.resume		= genphy_resume,
	.soft_reset	= asix_soft_reset,
}, {
	.phy_id		= PHY_ID_ASIX_AX88796B,
	.name		= "Asix Electronics AX88796B",
	.phy_id_mask	= 0xfffffff0,
@@ -50,6 +120,8 @@ static struct phy_driver asix_driver[] = { {
module_phy_driver(asix_driver);

static struct mdio_device_id __maybe_unused asix_tbl[] = {
	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) },
	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) },
	{ PHY_ID_ASIX_AX88796B, 0xfffffff0 },
	{ }
};
+3 −0
Original line number Diff line number Diff line
@@ -1136,6 +1136,9 @@ void phy_state_machine(struct work_struct *work)
	else if (do_suspend)
		phy_suspend(phydev);

	if (err == -ENODEV)
		return;

	if (err < 0)
		phy_error(phydev);

+2 −0
Original line number Diff line number Diff line
@@ -164,6 +164,8 @@ config USB_NET_AX8817X
	depends on USB_USBNET
	select CRC32
	select PHYLIB
	select AX88796B_PHY
	imply NET_SELFTESTS
	default y
	help
	  This option adds support for ASIX AX88xxx based USB 2.0
+11 −2
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include <linux/usb/usbnet.h>
#include <linux/slab.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <net/selftests.h>

#define DRIVER_VERSION "22-Dec-2011"
#define DRIVER_NAME "asix"
@@ -178,6 +180,10 @@ struct asix_common_private {
	u16 presvd_phy_advertise;
	u16 presvd_phy_bmcr;
	struct asix_rx_fixup_info rx_fixup_info;
	struct mii_bus *mdio;
	struct phy_device *phydev;
	u16 phy_addr;
	char phy_name[20];
};

extern const struct driver_info ax88172a_info;
@@ -205,8 +211,7 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
int asix_set_sw_mii(struct usbnet *dev, int in_pm);
int asix_set_hw_mii(struct usbnet *dev, int in_pm);

int asix_read_phy_addr(struct usbnet *dev, int internal);
int asix_get_phy_addr(struct usbnet *dev);
int asix_read_phy_addr(struct usbnet *dev, bool internal);

int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm);

@@ -215,6 +220,7 @@ int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm);

u16 asix_read_medium_status(struct usbnet *dev, int in_pm);
int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm);
void asix_adjust_link(struct net_device *netdev);

int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm);

@@ -223,6 +229,9 @@ void asix_set_multicast(struct net_device *net);
int asix_mdio_read(struct net_device *netdev, int phy_id, int loc);
void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);

int asix_mdio_bus_read(struct mii_bus *bus, int phy_id, int regnum);
int asix_mdio_bus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val);

int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc);
void asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc,
			  int val);
+79 −27
Original line number Diff line number Diff line
@@ -288,32 +288,33 @@ int asix_set_hw_mii(struct usbnet *dev, int in_pm)
	return ret;
}

int asix_read_phy_addr(struct usbnet *dev, int internal)
int asix_read_phy_addr(struct usbnet *dev, bool internal)
{
	int offset = (internal ? 1 : 0);
	int ret, offset;
	u8 buf[2];
	int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0);

	netdev_dbg(dev->net, "asix_get_phy_addr()\n");
	ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0);
	if (ret < 0)
		goto error;

	if (ret < 2) {
		netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret);
		goto out;
		ret = -EIO;
		goto error;
	}
	netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n",
		   *((__le16 *)buf));

	offset = (internal ? 1 : 0);
	ret = buf[offset];

out:
	netdev_dbg(dev->net, "%s PHY address 0x%x\n",
		   internal ? "internal" : "external", ret);

	return ret;
}

int asix_get_phy_addr(struct usbnet *dev)
{
	/* return the address of the internal phy */
	return asix_read_phy_addr(dev, 1);
}
error:
	netdev_err(dev->net, "Error reading PHY_ID register: %02x\n", ret);

	return ret;
}

int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm)
{
@@ -383,6 +384,27 @@ int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm)
	return ret;
}

/* set MAC link settings according to information from phylib */
void asix_adjust_link(struct net_device *netdev)
{
	struct phy_device *phydev = netdev->phydev;
	struct usbnet *dev = netdev_priv(netdev);
	u16 mode = 0;

	if (phydev->link) {
		mode = AX88772_MEDIUM_DEFAULT;

		if (phydev->duplex == DUPLEX_HALF)
			mode &= ~AX_MEDIUM_FD;

		if (phydev->speed != SPEED_100)
			mode &= ~AX_MEDIUM_PS;
	}

	asix_write_medium_mode(dev, mode, 0);
	phy_print_status(phydev);
}

int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm)
{
	int ret;
@@ -463,18 +485,23 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
		return ret;
	}

	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
				(__u16)loc, 2, &res, 0);
	asix_set_hw_mii(dev, 0);
	ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2,
			    &res, 0);
	if (ret < 0)
		goto out;

	ret = asix_set_hw_mii(dev, 0);
out:
	mutex_unlock(&dev->phy_mutex);

	netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
			phy_id, loc, le16_to_cpu(res));

	return le16_to_cpu(res);
	return ret < 0 ? ret : le16_to_cpu(res);
}

void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
static int __asix_mdio_write(struct net_device *netdev, int phy_id, int loc,
			     int val)
{
	struct usbnet *dev = netdev_priv(netdev);
	__le16 res = cpu_to_le16(val);
@@ -494,15 +521,40 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
		ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
				    0, 0, 1, &smsr, 0);
	} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
	if (ret == -ENODEV) {

	if (ret == -ENODEV)
		goto out;

	ret = asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2,
			     &res, 0);
	if (ret < 0)
		goto out;

	ret = asix_set_hw_mii(dev, 0);
out:
	mutex_unlock(&dev->phy_mutex);
		return;

	return ret < 0 ? ret : 0;
}

	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
		       (__u16)loc, 2, &res, 0);
	asix_set_hw_mii(dev, 0);
	mutex_unlock(&dev->phy_mutex);
void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
{
	__asix_mdio_write(netdev, phy_id, loc, val);
}

/* MDIO read and write wrappers for phylib */
int asix_mdio_bus_read(struct mii_bus *bus, int phy_id, int regnum)
{
	struct usbnet *priv = bus->priv;

	return asix_mdio_read(priv->net, phy_id, regnum);
}

int asix_mdio_bus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
{
	struct usbnet *priv = bus->priv;

	return __asix_mdio_write(priv->net, phy_id, regnum, val);
}

int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc)
Loading