diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 17 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/broadcom.c | 6 | ||||
-rw-r--r-- | drivers/net/phy/dp83822.c | 344 | ||||
-rw-r--r-- | drivers/net/phy/dp83848.c | 3 | ||||
-rw-r--r-- | drivers/net/phy/phy_led_triggers.c | 63 | ||||
-rw-r--r-- | drivers/net/phy/phylink.c | 16 | ||||
-rw-r--r-- | drivers/net/phy/realtek.c | 56 | ||||
-rw-r--r-- | drivers/net/phy/sfp-bus.c | 11 | ||||
-rw-r--r-- | drivers/net/phy/sfp.c | 29 | ||||
-rw-r--r-- | drivers/net/phy/uPD60620.c | 109 |
12 files changed, 603 insertions, 55 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index cd931cf9dcc2..bdfbabb86ee0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -191,11 +191,14 @@ config LED_TRIGGER_PHY Adds support for a set of LED trigger events per-PHY. Link state change will trigger the events, for consumption by an LED class driver. There are triggers for each link speed currently - supported by the phy, and are of the form: + supported by the PHY and also a one common "link" trigger as a + logical-or of all the link speed ones. + All these triggers are named according to the following pattern: <mii bus id>:<phy>:<speed> Where speed is in the form: - <Speed in megabits>Mbps or <Speed in gigabits>Gbps + <Speed in megabits>Mbps OR <Speed in gigabits>Gbps OR link + for any speed known to the PHY. comment "MII PHY device drivers" @@ -277,6 +280,11 @@ config DAVICOM_PHY ---help--- Currently supports dm9161e and dm9131 +config DP83822_PHY + tristate "Texas Instruments DP83822 PHY" + ---help--- + Supports the DP83822 PHY. + config DP83848_PHY tristate "Texas Instruments DP83848 PHY" ---help--- @@ -366,6 +374,11 @@ config REALTEK_PHY ---help--- Supports the Realtek 821x PHY. +config RENESAS_PHY + tristate "Driver for Renesas PHYs" + ---help--- + Supports the Renesas PHYs uPD60620 and uPD60620A. + config ROCKCHIP_PHY tristate "Driver for Rockchip Ethernet PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 00f097e18c68..01acbcb2c798 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_CICADA_PHY) += cicada.o obj-$(CONFIG_CORTINA_PHY) += cortina.o obj-$(CONFIG_DAVICOM_PHY) += davicom.o obj-$(CONFIG_DP83640_PHY) += dp83640.o +obj-$(CONFIG_DP83822_PHY) += dp83822.o obj-$(CONFIG_DP83848_PHY) += dp83848.o obj-$(CONFIG_DP83867_PHY) += dp83867.o obj-$(CONFIG_FIXED_PHY) += fixed_phy.o @@ -73,6 +74,7 @@ obj-$(CONFIG_MICROSEMI_PHY) += mscc.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o +obj-$(CONFIG_RENESAS_PHY) += uPD60620.o obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_STE10XP) += ste10Xp.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index c1e52b9dc58d..5f93e6add563 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -167,7 +167,7 @@ static int at803x_set_wol(struct phy_device *phydev, mac = (const u8 *) ndev->dev_addr; if (!is_valid_ether_addr(mac)) - return -EFAULT; + return -EINVAL; for (i = 0; i < 3; i++) { phy_write(phydev, AT803X_MMD_ACCESS_CONTROL, diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 1e9ad30a35c8..d7ed69deabfb 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -43,6 +43,12 @@ static int bcm54210e_config_init(struct phy_device *phydev) val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); + if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) { + val = phy_read(phydev, MII_CTRL1000); + val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; + phy_write(phydev, MII_CTRL1000, val); + } + return 0; } diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c new file mode 100644 index 000000000000..14335d14e9e4 --- /dev/null +++ b/drivers/net/phy/dp83822.c @@ -0,0 +1,344 @@ +/* + * Driver for the Texas Instruments DP83822 PHY + * + * Copyright (C) 2017 Texas Instruments 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/ethtool.h> +#include <linux/etherdevice.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy.h> +#include <linux/netdevice.h> + +#define DP83822_PHY_ID 0x2000a240 +#define DP83822_DEVADDR 0x1f + +#define MII_DP83822_PHYSCR 0x11 +#define MII_DP83822_MISR1 0x12 +#define MII_DP83822_MISR2 0x13 +#define MII_DP83822_RESET_CTRL 0x1f + +#define DP83822_HW_RESET BIT(15) +#define DP83822_SW_RESET BIT(14) + +/* PHYSCR Register Fields */ +#define DP83822_PHYSCR_INT_OE BIT(0) /* Interrupt Output Enable */ +#define DP83822_PHYSCR_INTEN BIT(1) /* Interrupt Enable */ + +/* MISR1 bits */ +#define DP83822_RX_ERR_HF_INT_EN BIT(0) +#define DP83822_FALSE_CARRIER_HF_INT_EN BIT(1) +#define DP83822_ANEG_COMPLETE_INT_EN BIT(2) +#define DP83822_DUP_MODE_CHANGE_INT_EN BIT(3) +#define DP83822_SPEED_CHANGED_INT_EN BIT(4) +#define DP83822_LINK_STAT_INT_EN BIT(5) +#define DP83822_ENERGY_DET_INT_EN BIT(6) +#define DP83822_LINK_QUAL_INT_EN BIT(7) + +/* MISR2 bits */ +#define DP83822_JABBER_DET_INT_EN BIT(0) +#define DP83822_WOL_PKT_INT_EN BIT(1) +#define DP83822_SLEEP_MODE_INT_EN BIT(2) +#define DP83822_MDI_XOVER_INT_EN BIT(3) +#define DP83822_LB_FIFO_INT_EN BIT(4) +#define DP83822_PAGE_RX_INT_EN BIT(5) +#define DP83822_ANEG_ERR_INT_EN BIT(6) +#define DP83822_EEE_ERROR_CHANGE_INT_EN BIT(7) + +/* INT_STAT1 bits */ +#define DP83822_WOL_INT_EN BIT(4) +#define DP83822_WOL_INT_STAT BIT(12) + +#define MII_DP83822_RXSOP1 0x04a5 +#define MII_DP83822_RXSOP2 0x04a6 +#define MII_DP83822_RXSOP3 0x04a7 + +/* WoL Registers */ +#define MII_DP83822_WOL_CFG 0x04a0 +#define MII_DP83822_WOL_STAT 0x04a1 +#define MII_DP83822_WOL_DA1 0x04a2 +#define MII_DP83822_WOL_DA2 0x04a3 +#define MII_DP83822_WOL_DA3 0x04a4 + +/* WoL bits */ +#define DP83822_WOL_MAGIC_EN BIT(0) +#define DP83822_WOL_SECURE_ON BIT(5) +#define DP83822_WOL_EN BIT(7) +#define DP83822_WOL_INDICATION_SEL BIT(8) +#define DP83822_WOL_CLR_INDICATION BIT(11) + +static int dp83822_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, MII_DP83822_MISR1); + if (err < 0) + return err; + + err = phy_read(phydev, MII_DP83822_MISR2); + if (err < 0) + return err; + + return 0; +} + +static int dp83822_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + struct net_device *ndev = phydev->attached_dev; + u16 value; + const u8 *mac; + + if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { + mac = (const u8 *)ndev->dev_addr; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; + + /* MAC addresses start with byte 5, but stored in mac[0]. + * 822 PHYs store bytes 4|5, 2|3, 0|1 + */ + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1, + (mac[1] << 8) | mac[0]); + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2, + (mac[3] << 8) | mac[2]); + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3, + (mac[5] << 8) | mac[4]); + + value = phy_read_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_WOL_CFG); + if (wol->wolopts & WAKE_MAGIC) + value |= DP83822_WOL_MAGIC_EN; + else + value &= ~DP83822_WOL_MAGIC_EN; + + if (wol->wolopts & WAKE_MAGICSECURE) { + phy_write_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RXSOP1, + (wol->sopass[1] << 8) | wol->sopass[0]); + phy_write_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RXSOP2, + (wol->sopass[3] << 8) | wol->sopass[2]); + phy_write_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RXSOP3, + (wol->sopass[5] << 8) | wol->sopass[4]); + value |= DP83822_WOL_SECURE_ON; + } else { + value &= ~DP83822_WOL_SECURE_ON; + } + + value |= (DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL | + DP83822_WOL_CLR_INDICATION); + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, + value); + } else { + value = phy_read_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_WOL_CFG); + value &= ~DP83822_WOL_EN; + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, + value); + } + + return 0; +} + +static void dp83822_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int value; + u16 sopass_val; + + wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); + wol->wolopts = 0; + + value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); + + if (value & DP83822_WOL_MAGIC_EN) + wol->wolopts |= WAKE_MAGIC; + + if (value & DP83822_WOL_SECURE_ON) { + sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RXSOP1); + wol->sopass[0] = (sopass_val & 0xff); + wol->sopass[1] = (sopass_val >> 8); + + sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RXSOP2); + wol->sopass[2] = (sopass_val & 0xff); + wol->sopass[3] = (sopass_val >> 8); + + sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR, + MII_DP83822_RXSOP3); + wol->sopass[4] = (sopass_val & 0xff); + wol->sopass[5] = (sopass_val >> 8); + + wol->wolopts |= WAKE_MAGICSECURE; + } + + /* WoL is not enabled so set wolopts to 0 */ + if (!(value & DP83822_WOL_EN)) + wol->wolopts = 0; +} + +static int dp83822_config_intr(struct phy_device *phydev) +{ + int misr_status; + int physcr_status; + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + misr_status = phy_read(phydev, MII_DP83822_MISR1); + if (misr_status < 0) + return misr_status; + + misr_status |= (DP83822_RX_ERR_HF_INT_EN | + DP83822_FALSE_CARRIER_HF_INT_EN | + DP83822_ANEG_COMPLETE_INT_EN | + DP83822_DUP_MODE_CHANGE_INT_EN | + DP83822_SPEED_CHANGED_INT_EN | + DP83822_LINK_STAT_INT_EN | + DP83822_ENERGY_DET_INT_EN | + DP83822_LINK_QUAL_INT_EN); + + err = phy_write(phydev, MII_DP83822_MISR1, misr_status); + if (err < 0) + return err; + + misr_status = phy_read(phydev, MII_DP83822_MISR2); + if (misr_status < 0) + return misr_status; + + misr_status |= (DP83822_JABBER_DET_INT_EN | + DP83822_WOL_PKT_INT_EN | + DP83822_SLEEP_MODE_INT_EN | + DP83822_MDI_XOVER_INT_EN | + DP83822_LB_FIFO_INT_EN | + DP83822_PAGE_RX_INT_EN | + DP83822_ANEG_ERR_INT_EN | + DP83822_EEE_ERROR_CHANGE_INT_EN); + + err = phy_write(phydev, MII_DP83822_MISR2, misr_status); + if (err < 0) + return err; + + physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); + if (physcr_status < 0) + return physcr_status; + + physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN; + + } else { + err = phy_write(phydev, MII_DP83822_MISR1, 0); + if (err < 0) + return err; + + err = phy_write(phydev, MII_DP83822_MISR1, 0); + if (err < 0) + return err; + + physcr_status = phy_read(phydev, MII_DP83822_PHYSCR); + if (physcr_status < 0) + return physcr_status; + + physcr_status &= ~DP83822_PHYSCR_INTEN; + } + + return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status); +} + +static int dp83822_config_init(struct phy_device *phydev) +{ + int err; + int value; + + err = genphy_config_init(phydev); + if (err < 0) + return err; + + value = DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON | DP83822_WOL_EN; + + return phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, + value); +} + +static int dp83822_phy_reset(struct phy_device *phydev) +{ + int err; + + err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET); + if (err < 0) + return err; + + dp83822_config_init(phydev); + + return 0; +} + +static int dp83822_suspend(struct phy_device *phydev) +{ + int value; + + value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); + + if (!(value & DP83822_WOL_EN)) + genphy_suspend(phydev); + + return 0; +} + +static int dp83822_resume(struct phy_device *phydev) +{ + int value; + + genphy_resume(phydev); + + value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); + + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value | + DP83822_WOL_CLR_INDICATION); + + return 0; +} + +static struct phy_driver dp83822_driver[] = { + { + .phy_id = DP83822_PHY_ID, + .phy_id_mask = 0xfffffff0, + .name = "TI DP83822", + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = dp83822_config_init, + .soft_reset = dp83822_phy_reset, + .get_wol = dp83822_get_wol, + .set_wol = dp83822_set_wol, + .ack_interrupt = dp83822_ack_interrupt, + .config_intr = dp83822_config_intr, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .suspend = dp83822_suspend, + .resume = dp83822_resume, + }, +}; +module_phy_driver(dp83822_driver); + +static struct mdio_device_id __maybe_unused dp83822_tbl[] = { + { DP83822_PHY_ID, 0xfffffff0 }, + { }, +}; +MODULE_DEVICE_TABLE(mdio, dp83822_tbl); + +MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c index 3de4fe4dda77..3966d43c5146 100644 --- a/drivers/net/phy/dp83848.c +++ b/drivers/net/phy/dp83848.c @@ -20,7 +20,6 @@ #define TI_DP83620_PHY_ID 0x20005ce0 #define NS_DP83848C_PHY_ID 0x20005c90 #define TLK10X_PHY_ID 0x2000a210 -#define TI_DP83822_PHY_ID 0x2000a240 /* Registers */ #define DP83848_MICR 0x11 /* MII Interrupt Control Register */ @@ -80,7 +79,6 @@ static struct mdio_device_id __maybe_unused dp83848_tbl[] = { { NS_DP83848C_PHY_ID, 0xfffffff0 }, { TI_DP83620_PHY_ID, 0xfffffff0 }, { TLK10X_PHY_ID, 0xfffffff0 }, - { TI_DP83822_PHY_ID, 0xfffffff0 }, { } }; MODULE_DEVICE_TABLE(mdio, dp83848_tbl); @@ -110,7 +108,6 @@ static struct phy_driver dp83848_driver[] = { DP83848_PHY_DRIVER(NS_DP83848C_PHY_ID, "NS DP83848C 10/100 Mbps PHY"), DP83848_PHY_DRIVER(TI_DP83620_PHY_ID, "TI DP83620 10/100 Mbps PHY"), DP83848_PHY_DRIVER(TLK10X_PHY_ID, "TI TLK10X 10/100 Mbps PHY"), - DP83848_PHY_DRIVER(TI_DP83822_PHY_ID, "TI DP83822 10/100 Mbps PHY"), }; module_phy_driver(dp83848_driver); diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c index 94ca42e630bb..39ecad25b201 100644 --- a/drivers/net/phy/phy_led_triggers.c +++ b/drivers/net/phy/phy_led_triggers.c @@ -27,12 +27,21 @@ static struct phy_led_trigger *phy_speed_to_led_trigger(struct phy_device *phy, return NULL; } +static void phy_led_trigger_no_link(struct phy_device *phy) +{ + if (phy->last_triggered) { + led_trigger_event(&phy->last_triggered->trigger, LED_OFF); + led_trigger_event(&phy->led_link_trigger->trigger, LED_OFF); + phy->last_triggered = NULL; + } +} + void phy_led_trigger_change_speed(struct phy_device *phy) { struct phy_led_trigger *plt; if (!phy->link) - goto out_change_speed; + return phy_led_trigger_no_link(phy); if (phy->speed == 0) return; @@ -42,25 +51,28 @@ void phy_led_trigger_change_speed(struct phy_device *phy) netdev_alert(phy->attached_dev, "No phy led trigger registered for speed(%d)\n", phy->speed); - goto out_change_speed; + return phy_led_trigger_no_link(phy); } if (plt != phy->last_triggered) { + if (!phy->last_triggered) + led_trigger_event(&phy->led_link_trigger->trigger, + LED_FULL); + led_trigger_event(&phy->last_triggered->trigger, LED_OFF); led_trigger_event(&plt->trigger, LED_FULL); phy->last_triggered = plt; } - return; - -out_change_speed: - if (phy->last_triggered) { - led_trigger_event(&phy->last_triggered->trigger, - LED_OFF); - phy->last_triggered = NULL; - } } EXPORT_SYMBOL_GPL(phy_led_trigger_change_speed); +static void phy_led_trigger_format_name(struct phy_device *phy, char *buf, + size_t size, char *suffix) +{ + snprintf(buf, size, PHY_ID_FMT ":%s", + phy->mdio.bus->id, phy->mdio.addr, suffix); +} + static int phy_led_trigger_register(struct phy_device *phy, struct phy_led_trigger *plt, unsigned int speed) @@ -77,8 +89,8 @@ static int phy_led_trigger_register(struct phy_device *phy, snprintf(name_suffix, sizeof(name_suffix), "%dGbps", DIV_ROUND_CLOSEST(speed, 1000)); - snprintf(plt->name, sizeof(plt->name), PHY_ID_FMT ":%s", - phy->mdio.bus->id, phy->mdio.addr, name_suffix); + phy_led_trigger_format_name(phy, plt->name, sizeof(plt->name), + name_suffix); plt->trigger.name = plt->name; return led_trigger_register(&plt->trigger); @@ -99,13 +111,30 @@ int phy_led_triggers_register(struct phy_device *phy) if (!phy->phy_num_led_triggers) return 0; + phy->led_link_trigger = devm_kzalloc(&phy->mdio.dev, + sizeof(*phy->led_link_trigger), + GFP_KERNEL); + if (!phy->led_link_trigger) { + err = -ENOMEM; + goto out_clear; + } + + phy_led_trigger_format_name(phy, phy->led_link_trigger->name, + sizeof(phy->led_link_trigger->name), + "link"); + phy->led_link_trigger->trigger.name = phy->led_link_trigger->name; + + err = led_trigger_register(&phy->led_link_trigger->trigger); + if (err) + goto out_free_link; + phy->phy_led_triggers = devm_kzalloc(&phy->mdio.dev, sizeof(struct phy_led_trigger) * phy->phy_num_led_triggers, GFP_KERNEL); if (!phy->phy_led_triggers) { err = -ENOMEM; - goto out_clear; + goto out_unreg_link; } for (i = 0; i < phy->phy_num_led_triggers; i++) { @@ -123,6 +152,11 @@ out_unreg: while (i--) phy_led_trigger_unregister(&phy->phy_led_triggers[i]); devm_kfree(&phy->mdio.dev, phy->phy_led_triggers); +out_unreg_link: + phy_led_trigger_unregister(phy->led_link_trigger); +out_free_link: + devm_kfree(&phy->mdio.dev, phy->led_link_trigger); + phy->led_link_trigger = NULL; out_clear: phy->phy_num_led_triggers = 0; return err; @@ -135,5 +169,8 @@ void phy_led_triggers_unregister(struct phy_device *phy) for (i = 0; i < phy->phy_num_led_triggers; i++) phy_led_trigger_unregister(&phy->phy_led_triggers[i]); + + if (phy->led_link_trigger) + phy_led_trigger_unregister(phy->led_link_trigger); } EXPORT_SYMBOL_GPL(phy_led_triggers_unregister); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index bcb4755bcd95..e3bbc70372d3 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -357,7 +357,7 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat * 1 1 0 1 TX */ static void phylink_resolve_flow(struct phylink *pl, - struct phylink_link_state *state) + struct phylink_link_state *state) { int new_pause = 0; @@ -506,7 +506,8 @@ static int phylink_register_sfp(struct phylink *pl, struct device_node *np) } struct phylink *phylink_create(struct net_device *ndev, struct device_node *np, - phy_interface_t iface, const struct phylink_mac_ops *ops) + phy_interface_t iface, + const struct phylink_mac_ops *ops) { struct phylink *pl; int ret; @@ -566,7 +567,8 @@ void phylink_destroy(struct phylink *pl) } EXPORT_SYMBOL_GPL(phylink_destroy); -void phylink_phy_change(struct phy_device *phydev, bool up, bool do_carrier) +static void phylink_phy_change(struct phy_device *phydev, bool up, + bool do_carrier) { struct phylink *pl = phydev->phylink; @@ -585,7 +587,7 @@ void phylink_phy_change(struct phy_device *phydev, bool up, bool do_carrier) phylink_run_resolve(pl); netdev_dbg(pl->netdev, "phy link %s %s/%s/%s\n", up ? "up" : "down", - phy_modes(phydev->interface), + phy_modes(phydev->interface), phy_speed_to_str(phydev->speed), phy_duplex_to_str(phydev->duplex)); } @@ -823,7 +825,7 @@ static void phylink_get_ksettings(const struct phylink_link_state *state, } int phylink_ethtool_ksettings_get(struct phylink *pl, - struct ethtool_link_ksettings *kset) + struct ethtool_link_ksettings *kset) { struct phylink_link_state link_state; @@ -870,7 +872,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); int phylink_ethtool_ksettings_set(struct phylink *pl, - const struct ethtool_link_ksettings *kset) + const struct ethtool_link_ksettings *kset) { struct ethtool_link_ksettings our_kset; struct phylink_link_state config; @@ -1337,8 +1339,6 @@ int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) } EXPORT_SYMBOL_GPL(phylink_mii_ioctl); - - static int phylink_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id) { diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 9cbe645e3d89..eda0a6e86918 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -22,17 +22,29 @@ #define RTL821x_INER 0x12 #define RTL821x_INER_INIT 0x6400 #define RTL821x_INSR 0x13 +#define RTL821x_PAGE_SELECT 0x1f #define RTL8211E_INER_LINK_STATUS 0x400 #define RTL8211F_INER_LINK_STATUS 0x0010 #define RTL8211F_INSR 0x1d -#define RTL8211F_PAGE_SELECT 0x1f #define RTL8211F_TX_DELAY 0x100 +#define RTL8201F_ISR 0x1e +#define RTL8201F_IER 0x13 + MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); MODULE_LICENSE("GPL"); +static int rtl8201_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, RTL8201F_ISR); + + return (err < 0) ? err : 0; +} + static int rtl821x_ack_interrupt(struct phy_device *phydev) { int err; @@ -46,14 +58,33 @@ static int rtl8211f_ack_interrupt(struct phy_device *phydev) { int err; - phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43); + phy_write(phydev, RTL821x_PAGE_SELECT, 0xa43); err = phy_read(phydev, RTL8211F_INSR); /* restore to default page 0 */ - phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); + phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); return (err < 0) ? err : 0; } +static int rtl8201_config_intr(struct phy_device *phydev) +{ + int err; + + /* switch to page 7 */ + phy_write(phydev, RTL821x_PAGE_SELECT, 0x7); + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + err = phy_write(phydev, RTL8201F_IER, + BIT(13) | BIT(12) | BIT(11)); + else + err = phy_write(phydev, RTL8201F_IER, 0); + + /* restore to default page 0 */ + phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); + + return err; +} + static int rtl8211b_config_intr(struct phy_device *phydev) { int err; @@ -84,11 +115,13 @@ static int rtl8211f_config_intr(struct phy_device *phydev) { int err; + phy_write(phydev, RTL821x_PAGE_SELECT, 0xa42); if (phydev->interrupts == PHY_INTERRUPT_ENABLED) err = phy_write(phydev, RTL821x_INER, RTL8211F_INER_LINK_STATUS); else err = phy_write(phydev, RTL821x_INER, 0); + phy_write(phydev, RTL821x_PAGE_SELECT, 0); return err; } @@ -102,7 +135,7 @@ static int rtl8211f_config_init(struct phy_device *phydev) if (ret < 0) return ret; - phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08); + phy_write(phydev, RTL821x_PAGE_SELECT, 0xd08); reg = phy_read(phydev, 0x11); /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ @@ -114,7 +147,7 @@ static int rtl8211f_config_init(struct phy_device *phydev) phy_write(phydev, 0x11, reg); /* restore to default page 0 */ - phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); + phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); return 0; } @@ -129,6 +162,18 @@ static struct phy_driver realtek_drvs[] = { .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, }, { + .phy_id = 0x001cc816, + .name = "RTL8201F 10/100Mbps Ethernet", + .phy_id_mask = 0x001fffff, + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &rtl8201_ack_interrupt, + .config_intr = &rtl8201_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { .phy_id = 0x001cc912, .name = "RTL8211B Gigabit Ethernet", .phy_id_mask = 0x001fffff, @@ -181,6 +226,7 @@ static struct phy_driver realtek_drvs[] = { module_phy_driver(realtek_drvs); static struct mdio_device_id __maybe_unused realtek_tbl[] = { + { 0x001cc816, 0x001fffff }, { 0x001cc912, 0x001fffff }, { 0x001cc914, 0x001fffff }, { 0x001cc915, 0x001fffff }, diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 5cb5384697ea..8a1b1f4c1b7c 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -26,7 +26,6 @@ struct sfp_bus { bool started; }; - int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned long *support) { @@ -208,7 +207,6 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } EXPORT_SYMBOL_GPL(sfp_parse_support); - static LIST_HEAD(sfp_buses); static DEFINE_MUTEX(sfp_mutex); @@ -295,7 +293,6 @@ static void sfp_unregister_bus(struct sfp_bus *bus) bus->registered = false; } - int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo) { if (!bus->registered) @@ -305,7 +302,7 @@ int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo) EXPORT_SYMBOL_GPL(sfp_get_module_info); int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, - u8 *data) + u8 *data) { if (!bus->registered) return -ENOIOCTLCMD; @@ -330,8 +327,8 @@ void sfp_upstream_stop(struct sfp_bus *bus) EXPORT_SYMBOL_GPL(sfp_upstream_stop); struct sfp_bus *sfp_register_upstream(struct device_node *np, - struct net_device *ndev, void *upstream, - const struct sfp_upstream_ops *ops) + struct net_device *ndev, void *upstream, + const struct sfp_upstream_ops *ops) { struct sfp_bus *bus = sfp_bus_get(np); int ret = 0; @@ -368,7 +365,6 @@ void sfp_unregister_upstream(struct sfp_bus *bus) } EXPORT_SYMBOL_GPL(sfp_unregister_upstream); - /* Socket driver entry points */ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) { @@ -395,7 +391,6 @@ void sfp_remove_phy(struct sfp_bus *bus) } EXPORT_SYMBOL_GPL(sfp_remove_phy); - void sfp_link_up(struct sfp_bus *bus) { const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index baee371bf767..e381811e5f11 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1,5 +1,5 @@ #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/jiffies.h> @@ -88,15 +88,12 @@ static const enum gpiod_flags gpio_flags[] = { #define T_PROBE_INIT msecs_to_jiffies(300) #define T_PROBE_RETRY msecs_to_jiffies(100) -/* - * SFP modules appear to always have their PHY configured for bus address +/* SFP modules appear to always have their PHY configured for bus address * 0x56 (which with mdio-i2c, translates to a PHY address of 22). */ #define SFP_PHY_ADDR 22 -/* - * Give this long for the PHY to reset. - */ +/* Give this long for the PHY to reset. */ #define T_PHY_RESET_MS 50 static DEFINE_MUTEX(sfp_mutex); @@ -150,10 +147,10 @@ static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state) /* If the module is present, drive the signals */ if (sfp->gpio[GPIO_TX_DISABLE]) gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE], - state & SFP_F_TX_DISABLE); + state & SFP_F_TX_DISABLE); if (state & SFP_F_RATE_SELECT) gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT], - state & SFP_F_RATE_SELECT); + state & SFP_F_RATE_SELECT); } else { /* Otherwise, let them float to the pull-ups */ if (sfp->gpio[GPIO_TX_DISABLE]) @@ -164,7 +161,7 @@ static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state) } static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr, - void *buf, size_t len) + void *buf, size_t len) { struct i2c_msg msgs[2]; int ret; @@ -186,7 +183,7 @@ static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr, } static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf, - size_t len) + size_t len) { return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len); } @@ -220,7 +217,6 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) return 0; } - /* Interface */ static unsigned int sfp_get_state(struct sfp *sfp) { @@ -295,7 +291,8 @@ static void sfp_sm_next(struct sfp *sfp, unsigned int state, sfp_sm_set_timer(sfp, timeout); } -static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, unsigned int timeout) +static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, + unsigned int timeout) { sfp->sm_mod_state = state; sfp_sm_set_timer(sfp, timeout); @@ -370,7 +367,8 @@ static void sfp_sm_link_check_los(struct sfp *sfp) static void sfp_sm_fault(struct sfp *sfp, bool warn) { if (sfp->sm_retries && !--sfp->sm_retries) { - dev_err(sfp->dev, "module persistently indicates fault, disabling\n"); + dev_err(sfp->dev, + "module persistently indicates fault, disabling\n"); sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0); } else { if (warn) @@ -461,7 +459,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp) memcpy(date, sfp->id.ext.datecode, 8); date[8] = '\0'; - dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date); + dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", + vendor, part, rev, sn, date); /* We only support SFP modules, not the legacy GBIC modules. */ if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP || @@ -651,7 +650,7 @@ static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo) } static int sfp_module_eeprom(struct sfp *sfp, struct ethtool_eeprom *ee, - u8 *data) + u8 *data) { unsigned int first, last, len; int ret; diff --git a/drivers/net/phy/uPD60620.c b/drivers/net/phy/uPD60620.c new file mode 100644 index 000000000000..96b33475ea5e --- /dev/null +++ b/drivers/net/phy/uPD60620.c @@ -0,0 +1,109 @@ +/* + * Driver for the Renesas PHY uPD60620. + * + * Copyright (C) 2015 Softing Industrial Automation GmbH + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/phy.h> + +#define UPD60620_PHY_ID 0xb8242824 + +/* Extended Registers and values */ +/* PHY Special Control/Status */ +#define PHY_PHYSCR 0x1F /* PHY.31 */ +#define PHY_PHYSCR_10MB 0x0004 /* PHY speed = 10mb */ +#define PHY_PHYSCR_100MB 0x0008 /* PHY speed = 100mb */ +#define PHY_PHYSCR_DUPLEX 0x0010 /* PHY Duplex */ + +/* PHY Special Modes */ +#define PHY_SPM 0x12 /* PHY.18 */ + +/* Init PHY */ + +static int upd60620_config_init(struct phy_device *phydev) +{ + /* Enable support for passive HUBs (could be a strap option) */ + /* PHYMODE: All speeds, HD in parallel detect */ + return phy_write(phydev, PHY_SPM, 0x0180 | phydev->mdio.addr); +} + +/* Get PHY status from common registers */ + +static int upd60620_read_status(struct phy_device *phydev) +{ + int phy_state; + + /* Read negotiated state */ + phy_state = phy_read(phydev, MII_BMSR); + if (phy_state < 0) + return phy_state; + + phydev->link = 0; + phydev->lp_advertising = 0; + phydev->pause = 0; + phydev->asym_pause = 0; + + if (phy_state & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) { + phy_state = phy_read(phydev, PHY_PHYSCR); + if (phy_state < 0) + return phy_state; + + if (phy_state & (PHY_PHYSCR_10MB | PHY_PHYSCR_100MB)) { + phydev->link = 1; + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + + if (phy_state & PHY_PHYSCR_100MB) + phydev->speed = SPEED_100; + if (phy_state & PHY_PHYSCR_DUPLEX) + phydev->duplex = DUPLEX_FULL; + + phy_state = phy_read(phydev, MII_LPA); + if (phy_state < 0) + return phy_state; + + phydev->lp_advertising + = mii_lpa_to_ethtool_lpa_t(phy_state); + + if (phydev->duplex == DUPLEX_FULL) { + if (phy_state & LPA_PAUSE_CAP) + phydev->pause = 1; + if (phy_state & LPA_PAUSE_ASYM) + phydev->asym_pause = 1; + } + } + } + return 0; +} + +MODULE_DESCRIPTION("Renesas uPD60620 PHY driver"); +MODULE_AUTHOR("Bernd Edlinger <bernd.edlinger@hotmail.de>"); +MODULE_LICENSE("GPL"); + +static struct phy_driver upd60620_driver[1] = { { + .phy_id = UPD60620_PHY_ID, + .phy_id_mask = 0xfffffffe, + .name = "Renesas uPD60620", + .features = PHY_BASIC_FEATURES, + .flags = 0, + .config_init = upd60620_config_init, + .config_aneg = genphy_config_aneg, + .read_status = upd60620_read_status, +} }; + +module_phy_driver(upd60620_driver); + +static struct mdio_device_id __maybe_unused upd60620_tbl[] = { + { UPD60620_PHY_ID, 0xfffffffe }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, upd60620_tbl); |