Merge "msm: phy: Add WOL functions in micrel driver"
This commit is contained in:
commit
c3d86ab59c
@ -28,6 +28,8 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/ptp_clock.h>
|
||||
#include <linux/ptp_classify.h>
|
||||
@ -252,6 +254,21 @@
|
||||
#define PS_TO_REG 200
|
||||
#define FIFO_SIZE 8
|
||||
|
||||
/*Register 2.10. 15:14 PME Output Select*/
|
||||
#define MII_KSZPHY_OMSO_PME_N2 BIT(10)
|
||||
/*Register 2.10. BITS 6, 1 and 0 to detect the type of WOL */
|
||||
#define MII_KSZPHY_WOL_MAGIC_PKT BIT(6)
|
||||
#define MII_KSZPHY_WOL_LINK_DOWN BIT(1)
|
||||
#define MII_KSZPHY_WOL_LINK_UP BIT(0)
|
||||
/* Register 2.10.15:14 PME Output Select */
|
||||
#define MII_KSZPHY_WOL_CTRL_PME_N2 BIT(15)
|
||||
#define MII_KSZPHY_WOL_CTRL_INT_N BIT(14)
|
||||
|
||||
/* MMD Address 2h, Register 2h Operation Mode Strap Override*/
|
||||
#define MII_KSZPHY_OMSO_REG 0x2
|
||||
/* MMD Address 2h, Register 10h Wake-On-LAN Control */
|
||||
#define MII_KSZPHY_WOL_CTRL_REG 0x10
|
||||
|
||||
struct kszphy_hw_stat {
|
||||
const char *string;
|
||||
u8 reg;
|
||||
@ -887,6 +904,70 @@ static int ksz9021_config_init(struct phy_device *phydev)
|
||||
#define MII_KSZ9031RN_EDPD 0x23
|
||||
#define MII_KSZ9031RN_EDPD_ENABLE BIT(0)
|
||||
|
||||
static int ksz9031_ack_interrupt(struct phy_device *phydev)
|
||||
{
|
||||
/* bit[7..0] int status, which is a read and clear register. */
|
||||
int rc;
|
||||
u32 reg_value;
|
||||
|
||||
rc = phy_read(phydev, MII_KSZPHY_INTCS);
|
||||
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG);
|
||||
if (reg_value & MII_KSZPHY_OMSO_PME_N2) {
|
||||
/* PME output is cleared by disabling the PME trigger src */
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG);
|
||||
reg_value &= ~MII_KSZPHY_WOL_MAGIC_PKT;
|
||||
reg_value &= ~MII_KSZPHY_WOL_LINK_UP;
|
||||
reg_value &= ~MII_KSZPHY_WOL_LINK_DOWN;
|
||||
phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value);
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG);
|
||||
reg_value |= MII_KSZPHY_WOL_MAGIC_PKT;
|
||||
reg_value |= MII_KSZPHY_WOL_LINK_UP;
|
||||
reg_value |= MII_KSZPHY_WOL_LINK_DOWN;
|
||||
phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value);
|
||||
}
|
||||
|
||||
return (rc < 0) ? rc : 0;
|
||||
}
|
||||
|
||||
static int ksz9031_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
const struct kszphy_type *type = phydev->drv->driver_data;
|
||||
int temp, err;
|
||||
u16 mask;
|
||||
|
||||
if (type && type->interrupt_level_mask)
|
||||
mask = type->interrupt_level_mask;
|
||||
else
|
||||
mask = KSZPHY_CTRL_INT_ACTIVE_HIGH;
|
||||
|
||||
/* set the interrupt pin active low */
|
||||
temp = phy_read(phydev, MII_KSZPHY_CTRL);
|
||||
if (temp < 0)
|
||||
return temp;
|
||||
temp &= ~mask;
|
||||
phy_write(phydev, MII_KSZPHY_CTRL, temp);
|
||||
|
||||
/* enable / disable interrupts */
|
||||
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
|
||||
err = ksz9031_ack_interrupt(phydev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
temp = KSZPHY_INTCS_ALL;
|
||||
err = phy_write(phydev, MII_KSZPHY_INTCS, temp);
|
||||
} else {
|
||||
temp = 0;
|
||||
err = phy_write(phydev, MII_KSZPHY_INTCS, temp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ksz9031_ack_interrupt(phydev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ksz9031_of_load_skew_values(struct phy_device *phydev,
|
||||
const struct device_node *of_node,
|
||||
u16 reg, size_t field_sz,
|
||||
@ -1366,7 +1447,8 @@ static int ksz9031_read_status(struct phy_device *phydev)
|
||||
if ((regval & 0xFF) == 0xFF) {
|
||||
phy_init_hw(phydev);
|
||||
phydev->link = 0;
|
||||
if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev))
|
||||
if (phydev->drv->config_intr &&
|
||||
(phydev->irq == PHY_MAC_INTERRUPT || phy_interrupt_is_valid(phydev)))
|
||||
phydev->drv->config_intr(phydev);
|
||||
return genphy_config_aneg(phydev);
|
||||
}
|
||||
@ -1814,6 +1896,95 @@ static int kszphy_probe(struct phy_device *phydev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ksz9031_set_wol_settings(struct phy_device *phydev)
|
||||
{
|
||||
u32 reg_value;
|
||||
|
||||
/* Enable both PHY and PME_N2 interrupts */
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG);
|
||||
reg_value |= MII_KSZPHY_WOL_CTRL_PME_N2;
|
||||
reg_value &= ~MII_KSZPHY_WOL_CTRL_INT_N;
|
||||
reg_value |= MII_KSZPHY_WOL_MAGIC_PKT;
|
||||
reg_value |= MII_KSZPHY_WOL_LINK_UP;
|
||||
reg_value |= MII_KSZPHY_WOL_LINK_DOWN;
|
||||
phy_write_mmd(phydev, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value);
|
||||
}
|
||||
|
||||
static int ksz9031_set_wol(struct phy_device *phydev,
|
||||
struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct net_device *ndev = phydev->attached_dev;
|
||||
const u8 *mac;
|
||||
int ret = 0;
|
||||
u32 reg_value;
|
||||
|
||||
if (!ndev)
|
||||
return -ENODEV;
|
||||
|
||||
if (wol->wolopts & WAKE_MAGIC) {
|
||||
mac = (const u8 *)ndev->dev_addr;
|
||||
|
||||
if (!is_valid_ether_addr(mac))
|
||||
return -EINVAL;
|
||||
|
||||
phy_write_mmd(phydev, 0x2, 0x11, mac[5] | (mac[4] << 8));
|
||||
phy_write_mmd(phydev, 0x2, 0x12, mac[3] | (mac[2] << 8));
|
||||
phy_write_mmd(phydev, 0x2, 0x13, mac[1] | (mac[0] << 8));
|
||||
|
||||
/* Enable WOL interrupt for magic pkt, link up and down */
|
||||
ksz9031_set_wol_settings(phydev);
|
||||
|
||||
/* Enable PME_N2 output */
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG);
|
||||
reg_value |= MII_KSZPHY_OMSO_PME_N2;
|
||||
phy_write_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG, reg_value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ksz9031_get_wol(struct phy_device *phydev,
|
||||
struct ethtool_wolinfo *wol)
|
||||
{
|
||||
u32 reg_value;
|
||||
|
||||
wol->supported = WAKE_MAGIC;
|
||||
wol->wolopts = 0;
|
||||
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG);
|
||||
if (reg_value & MII_KSZPHY_OMSO_PME_N2)
|
||||
wol->wolopts |= WAKE_MAGIC;
|
||||
}
|
||||
|
||||
static int ksz9031_suspend(struct phy_device *phydev)
|
||||
{
|
||||
int value;
|
||||
int wol_enabled;
|
||||
u32 reg_value;
|
||||
|
||||
reg_value = phy_read_mmd(phydev, 0x2, MII_KSZPHY_OMSO_REG);
|
||||
wol_enabled = reg_value & MII_KSZPHY_OMSO_PME_N2;
|
||||
|
||||
value = phy_read(phydev, MII_BMCR);
|
||||
if (wol_enabled)
|
||||
value |= BMCR_ISOLATE;
|
||||
else
|
||||
value |= BMCR_PDOWN;
|
||||
|
||||
phy_write(phydev, MII_BMCR, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz9031_resume(struct phy_device *phydev)
|
||||
{
|
||||
int value;
|
||||
|
||||
value = phy_read(phydev, MII_BMCR);
|
||||
value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
|
||||
phy_write(phydev, MII_BMCR, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lan8814_cable_test_start(struct phy_device *phydev)
|
||||
{
|
||||
/* If autoneg is enabled, we won't be able to test cross pair
|
||||
@ -3250,13 +3421,15 @@ static struct phy_driver ksphy_driver[] = {
|
||||
.config_init = ksz9031_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.read_status = ksz9031_read_status,
|
||||
.config_intr = kszphy_config_intr,
|
||||
.config_intr = ksz9031_config_intr,
|
||||
.handle_interrupt = kszphy_handle_interrupt,
|
||||
.get_sset_count = kszphy_get_sset_count,
|
||||
.get_strings = kszphy_get_strings,
|
||||
.get_stats = kszphy_get_stats,
|
||||
.suspend = kszphy_suspend,
|
||||
.resume = kszphy_resume,
|
||||
.set_wol = ksz9031_set_wol,
|
||||
.get_wol = ksz9031_get_wol,
|
||||
.suspend = ksz9031_suspend,
|
||||
.resume = ksz9031_resume,
|
||||
.cable_test_start = ksz9x31_cable_test_start,
|
||||
.cable_test_get_status = ksz9x31_cable_test_get_status,
|
||||
}, {
|
||||
|
Loading…
Reference in New Issue
Block a user