* Initial Mvebu RFC https://github.com/armbian/build/issues/1426 Signed-off-by: Igor Pecovnik <igor.pecovnik@gmail.com> * mvebu: add missing patches Signed-off-by: Igor Pecovnik <igor.pecovnik@gmail.com> * mvebu: change making u-boot targets to standard way, adjust patches and config Signed-off-by: Igor Pecovnik <igor.pecovnik@gmail.com> * helios4: set default branch to use U-Boot 2018.11 Switch over to U-Boot 2018.11 that has been used for some time in next branch. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: helios4: Enable DEV branch Signed-off-by: Aditya Prayoga <aditya@kobol.io> * u-boot: Add RTC support on Clearfog and Helios4 Added DM driver for mvebu RTC and enable it on Clearfog and Helios4 configuration. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * add boot-marvell.cmd backward compatibility The patches added missing variable that used on boot-marvell.cmd and also adjust the some memory addresses to prevent crash due to usage of fdt_high and initrd_high. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * helios4: Added SPI NOR flash target Build bootable SPI NOR flash image. Change the boot order to USB -> SATA -> MMC Signed-off-by: Aditya Prayoga <aditya@kobol.io> * Restore SPI support on U-Boot 2019.04 * mvebu: kernel: Added Wake-On-GPIO and WoL support The patch set was missing during transition. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * [#1429] SolidRun's ARMADA A388 SOM U-Boot ODT Update Old versions of U-Boot did not configure correctly the ODT on data signals of DDR RAM on SolidRun's ARMADA A388 SOMs. According to SolidRun Knowledge Base, the changes already pushed to mainline U-Boot. But then it was overwritten when Marvell DDR Training Tool updated [URL] https://developer.solid-run.com/knowledge-base/armada-38x-som-u-boot-odt-update/ Signed-off-by: Aditya Prayoga <aditya@kobol.io> * [#1429] mvebu: u-boot: Add revision id for Armada 38x B0 Added patch for SolidRun U-Boot v2018.01 and for Helios4 U-Boot v2018.11 Signed-off-by: Aditya Prayoga <aditya@kobol.io> * clearfog: Added SPI NOR flash target Build bootable SPI NOR flash image. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: clearfog: DEV branch use mainline U-Boot Also move clearfog base patch into its own board folder. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: enable U-Boot uart target Normal MMC image can be used for uart boot using following command: ./tools/kwboot -b u-boot-spl.kwb /dev/ttyUSBX But on Helios4, the SPL failed to continue the booting process if ECC is enabled, so disable it. Since the usage of uart boot is more for rescue/debug, disable autoboot. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: NEXT branch use mainline U-Boot Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: helios4: fix fancontrol related bug during buster testing - On kernel 4.19, cpu thermal sensor changed the name from armada_thermal into f10e4078.thermal. Added this new name to udev rules - Since DEFAULT branch now use kernel 4.14, update fancontrol configuration - Load lm75 kernel module - On kernel 4.19, cpu temp reading about 20 degree C lower, update fancontrol configuration. [URL] https://forum.armbian.com/topic/10214-clearfogpro-possible-change-in-temperature-reporting-between-414next-and-419dev Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: helios4: Override vendor provided fancontrol unit systemd emit following message on dmesg systemd[1]: /lib/systemd/system/fancontrol.service:9: PIDFile= references path below legacy directory /var/run/, updating /var/run/fancontrol.pid \xe2\x86\x92 /run/fancontrol.pid; please update the unit file accordingly. Override and change the value in the unit file to remove the message. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: DEV branch use its own u-boot patch folder The patches are copied over from u-boot-mvebu-next Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: u-boot: Make clearfog model distinction more obvious While at it, also change SerDes LANE4 into USB 3.0 on Clearfog Base. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * lib: Use apt-get install instead of dpkg on install_deb_chroot() dpkg -i does not install dependencies required by the package. This is needed if the BSP package requires other package that is not installed during debootstrap. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * config: mvebu: helios4: Move various tweak to family_tweak_bsp() Various tweak in family_tweaks_s() only applied to SD card image. Move it to family_tweaks_bsp() so it will also included on the BSP package and applied to existing user. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * config: mvebu: helios4: Add /etc/modules to BSP On kernel 4.19, user need to modify the /etc/modules to add lm75 kernel module. Pack the file into BSP so user no longer needed to modify it. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: kernel: Make zbud as built-in module To remove the following error: [ 1.705485] zswap: default zpool zbud not available [ 1.705488] zswap: pool creation failed Signed-off-by: Aditya Prayoga <aditya@kobol.io> * bootscripts: mvebu: Add default value for spi_workaround Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: kernel: Backport armada_thermal changes to 4.14 (#1452) On kernel 4.19, armada_thermal driver has been fixed to address Marvell's Errata #132698 (The changes first appear on LK 4.16). The result is temperature reading is around 20 degree Celsius lower. Currently armbian-motd apply -20C tweak for both LK 4.14 and LK 4.19 which is incorrect. Instead of adding some logic on what condition to apply the tweak, it is better to remove the tweak and patch the kernel instead. Revert commitb3dd4e9("[ mvebu ] Put back Armada temperature tweak in motd") which is part of #1421 solution. [URL] https://forum.armbian.com/topic/10214-clearfogpro-possible-change-in-temperature-reporting-between-414next-and-419dev/ Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: helios4: unified fancontrol config Since LK 4.14 on DEFAULT branch already patched and the temp reading is same as LK 4.19 on NEXT branch, it is no longer needed to separate fancontrol configuration file. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * u-boot: helios4: Remove rev id patch The patch is already applied in helios4 repo, no need to have it in armbian. This revert helios4 part of commit7411c55Signed-off-by: Aditya Prayoga <aditya@kobol.io> * u-boot: clearfog: enable PCIe support and PCIe reset Signed-off-by: Aditya Prayoga <aditya@kobol.io> * u-boot: clearfog: add boot-marvell.cmd backward compatibility The patches added missing variable that used on boot-marvell.cmd and also adjust the some memory addresses to prevent crash due to usage of fdt_high and initrd_high. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu: helios4: tweak regarding temperature setting Make fan speed similar compared to pre-patched armada-thermal. Target PWM value around 70 during idle. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * config: sources: clearfog to use u-boot 2018.01 for NEXT branch This changes also affect Helios4. Moved the shared U-Boot source setting back to Helios4 for NEXT branch. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * config: boards: build Stretch image for Clearfog and Helios4 Also remove DEV from Helios4 CLI_TARGET Signed-off-by: Aditya Prayoga <aditya@kobol.io> * kernel: mvebu-next: Disable access to SPI Flash User need to set spi_workaround=yes to enable SPI Flash access and lost access to internal SATA. Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu-next: adjust kernel config * mvebu-dev: bump to 5.2 and adjust kernel configuraion. Tested for building. * Adjust kernel config, add AUFS Signed-off-by: Igor Pecovnik <igor.pecovnik@gmail.com> * mvebu-next: Adjust kernel config, add debug GPIO Signed-off-by: Aditya Prayoga <aditya@kobol.io> * mvebu-dev: separate Clearfog Base U-boot configuration file and patch Signed-off-by: Aditya Prayoga <aditya@kobol.io>
980 lines
29 KiB
Diff
980 lines
29 KiB
Diff
From 36f29e6cf8071fed3854d9825217ed2a3c83b990 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@arm.linux.org.uk>
|
|
Date: Wed, 16 Sep 2015 21:27:10 +0100
|
|
Subject: net: mvneta: convert to phylink
|
|
|
|
Convert mvneta to use phylink, which models the MAC to PHY link in
|
|
a generic, reusable form.
|
|
|
|
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
|
|
|
|
- remove unused sync status
|
|
---
|
|
drivers/net/ethernet/marvell/Kconfig | 2 +-
|
|
drivers/net/ethernet/marvell/mvneta.c | 594 ++++++++++++++++++++--------------
|
|
2 files changed, 349 insertions(+), 247 deletions(-)
|
|
|
|
--- a/drivers/net/ethernet/marvell/Kconfig
|
|
+++ b/drivers/net/ethernet/marvell/Kconfig
|
|
@@ -60,7 +60,7 @@ config MVNETA
|
|
depends on ARCH_MVEBU || COMPILE_TEST
|
|
depends on HAS_DMA
|
|
select MVMDIO
|
|
- select FIXED_PHY
|
|
+ select PHYLINK
|
|
---help---
|
|
This driver supports the network interface units in the
|
|
Marvell ARMADA XP, ARMADA 370, ARMADA 38x and
|
|
--- a/drivers/net/ethernet/marvell/mvneta.c
|
|
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
|
@@ -28,7 +28,7 @@
|
|
#include <linux/of_mdio.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/phy.h>
|
|
-#include <linux/phy_fixed.h>
|
|
+#include <linux/phylink.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/hwbm.h>
|
|
@@ -189,6 +189,7 @@
|
|
#define MVNETA_GMAC_CTRL_0 0x2c00
|
|
#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2
|
|
#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc
|
|
+#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1)
|
|
#define MVNETA_GMAC0_PORT_ENABLE BIT(0)
|
|
#define MVNETA_GMAC_CTRL_2 0x2c08
|
|
#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0)
|
|
@@ -204,13 +205,19 @@
|
|
#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5)
|
|
#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6)
|
|
#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7)
|
|
+#define MVNETA_GMAC_AN_COMPLETE BIT(11)
|
|
+#define MVNETA_GMAC_SYNC_OK BIT(14)
|
|
#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c
|
|
#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0)
|
|
#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1)
|
|
#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2)
|
|
+#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3)
|
|
+#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4)
|
|
#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5)
|
|
#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6)
|
|
#define MVNETA_GMAC_AN_SPEED_EN BIT(7)
|
|
+#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8)
|
|
+#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9)
|
|
#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11)
|
|
#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12)
|
|
#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13)
|
|
@@ -237,6 +244,12 @@
|
|
#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
|
|
#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
|
|
|
|
+#define MVNETA_LPI_CTRL_0 0x2cc0
|
|
+#define MVNETA_LPI_CTRL_1 0x2cc4
|
|
+#define MVNETA_LPI_REQUEST_ENABLE BIT(0)
|
|
+#define MVNETA_LPI_CTRL_2 0x2cc8
|
|
+#define MVNETA_LPI_STATUS 0x2ccc
|
|
+
|
|
#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
|
|
|
|
/* Descriptor ring Macros */
|
|
@@ -313,6 +326,11 @@
|
|
#define MVNETA_RX_GET_BM_POOL_ID(rxd) \
|
|
(((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)
|
|
|
|
+enum {
|
|
+ ETHTOOL_STAT_EEE_WAKEUP,
|
|
+ ETHTOOL_MAX_STATS,
|
|
+};
|
|
+
|
|
struct mvneta_statistic {
|
|
unsigned short offset;
|
|
unsigned short type;
|
|
@@ -321,6 +339,7 @@ struct mvneta_statistic {
|
|
|
|
#define T_REG_32 32
|
|
#define T_REG_64 64
|
|
+#define T_SW 1
|
|
|
|
static const struct mvneta_statistic mvneta_statistics[] = {
|
|
{ 0x3000, T_REG_64, "good_octets_received", },
|
|
@@ -355,6 +374,7 @@ static const struct mvneta_statistic mvn
|
|
{ 0x304c, T_REG_32, "broadcast_frames_sent", },
|
|
{ 0x3054, T_REG_32, "fc_sent", },
|
|
{ 0x300c, T_REG_32, "internal_mac_transmit_err", },
|
|
+ { ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", },
|
|
};
|
|
|
|
struct mvneta_pcpu_stats {
|
|
@@ -407,20 +427,19 @@ struct mvneta_port {
|
|
u16 tx_ring_size;
|
|
u16 rx_ring_size;
|
|
|
|
- struct mii_bus *mii_bus;
|
|
- phy_interface_t phy_interface;
|
|
- struct device_node *phy_node;
|
|
- unsigned int link;
|
|
- unsigned int duplex;
|
|
- unsigned int speed;
|
|
+ struct device_node *dn;
|
|
unsigned int tx_csum_limit;
|
|
- unsigned int use_inband_status:1;
|
|
+ struct phylink *phylink;
|
|
|
|
struct mvneta_bm *bm_priv;
|
|
struct mvneta_bm_pool *pool_long;
|
|
struct mvneta_bm_pool *pool_short;
|
|
int bm_win_id;
|
|
|
|
+ bool eee_enabled;
|
|
+ bool eee_active;
|
|
+ bool tx_lpi_enabled;
|
|
+
|
|
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
|
|
|
|
u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
|
|
@@ -1214,10 +1233,6 @@ static void mvneta_port_disable(struct m
|
|
val &= ~MVNETA_GMAC0_PORT_ENABLE;
|
|
mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
|
|
|
|
- pp->link = 0;
|
|
- pp->duplex = -1;
|
|
- pp->speed = 0;
|
|
-
|
|
udelay(200);
|
|
}
|
|
|
|
@@ -1277,44 +1292,6 @@ static void mvneta_set_other_mcast_table
|
|
mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
|
|
}
|
|
|
|
-static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
|
|
-{
|
|
- u32 val;
|
|
-
|
|
- if (enable) {
|
|
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
|
|
- MVNETA_GMAC_FORCE_LINK_DOWN |
|
|
- MVNETA_GMAC_AN_FLOW_CTRL_EN);
|
|
- val |= MVNETA_GMAC_INBAND_AN_ENABLE |
|
|
- MVNETA_GMAC_AN_SPEED_EN |
|
|
- MVNETA_GMAC_AN_DUPLEX_EN;
|
|
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
|
-
|
|
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
|
- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
|
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
|
-
|
|
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
|
- val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
|
|
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
|
- } else {
|
|
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
|
|
- MVNETA_GMAC_AN_SPEED_EN |
|
|
- MVNETA_GMAC_AN_DUPLEX_EN);
|
|
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
|
-
|
|
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
|
- val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
|
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
|
-
|
|
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
|
- val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
|
|
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
|
- }
|
|
-}
|
|
-
|
|
static void mvneta_percpu_unmask_interrupt(void *arg)
|
|
{
|
|
struct mvneta_port *pp = arg;
|
|
@@ -1467,7 +1444,6 @@ static void mvneta_defaults_set(struct m
|
|
val &= ~MVNETA_PHY_POLLING_ENABLE;
|
|
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
|
|
|
|
- mvneta_set_autoneg(pp, pp->use_inband_status);
|
|
mvneta_set_ucast_table(pp, -1);
|
|
mvneta_set_special_mcast_table(pp, -1);
|
|
mvneta_set_other_mcast_table(pp, -1);
|
|
@@ -2692,26 +2668,11 @@ static irqreturn_t mvneta_percpu_isr(int
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
-static int mvneta_fixed_link_update(struct mvneta_port *pp,
|
|
- struct phy_device *phy)
|
|
+static void mvneta_link_change(struct mvneta_port *pp)
|
|
{
|
|
- struct fixed_phy_status status;
|
|
- struct fixed_phy_status changed = {};
|
|
u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
|
|
|
|
- status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
|
|
- if (gmac_stat & MVNETA_GMAC_SPEED_1000)
|
|
- status.speed = SPEED_1000;
|
|
- else if (gmac_stat & MVNETA_GMAC_SPEED_100)
|
|
- status.speed = SPEED_100;
|
|
- else
|
|
- status.speed = SPEED_10;
|
|
- status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
|
|
- changed.link = 1;
|
|
- changed.speed = 1;
|
|
- changed.duplex = 1;
|
|
- fixed_phy_update_state(phy, &status, &changed);
|
|
- return 0;
|
|
+ phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
|
|
}
|
|
|
|
/* NAPI handler
|
|
@@ -2727,7 +2688,6 @@ static int mvneta_poll(struct napi_struc
|
|
u32 cause_rx_tx;
|
|
int rx_queue;
|
|
struct mvneta_port *pp = netdev_priv(napi->dev);
|
|
- struct net_device *ndev = pp->dev;
|
|
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
|
|
|
|
if (!netif_running(pp->dev)) {
|
|
@@ -2741,12 +2701,11 @@ static int mvneta_poll(struct napi_struc
|
|
u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE);
|
|
|
|
mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
|
|
- if (pp->use_inband_status && (cause_misc &
|
|
- (MVNETA_CAUSE_PHY_STATUS_CHANGE |
|
|
- MVNETA_CAUSE_LINK_CHANGE |
|
|
- MVNETA_CAUSE_PSC_SYNC_CHANGE))) {
|
|
- mvneta_fixed_link_update(pp, ndev->phydev);
|
|
- }
|
|
+
|
|
+ if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE |
|
|
+ MVNETA_CAUSE_LINK_CHANGE |
|
|
+ MVNETA_CAUSE_PSC_SYNC_CHANGE))
|
|
+ mvneta_link_change(pp);
|
|
}
|
|
|
|
/* Release Tx descriptors */
|
|
@@ -3060,7 +3019,6 @@ static int mvneta_setup_txqs(struct mvne
|
|
static void mvneta_start_dev(struct mvneta_port *pp)
|
|
{
|
|
int cpu;
|
|
- struct net_device *ndev = pp->dev;
|
|
|
|
mvneta_max_rx_size_set(pp, pp->pkt_size);
|
|
mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
|
|
@@ -3088,16 +3046,15 @@ static void mvneta_start_dev(struct mvne
|
|
MVNETA_CAUSE_LINK_CHANGE |
|
|
MVNETA_CAUSE_PSC_SYNC_CHANGE);
|
|
|
|
- phy_start(ndev->phydev);
|
|
+ phylink_start(pp->phylink);
|
|
netif_tx_start_all_queues(pp->dev);
|
|
}
|
|
|
|
static void mvneta_stop_dev(struct mvneta_port *pp)
|
|
{
|
|
unsigned int cpu;
|
|
- struct net_device *ndev = pp->dev;
|
|
|
|
- phy_stop(ndev->phydev);
|
|
+ phylink_stop(pp->phylink);
|
|
|
|
if (!pp->neta_armada3700) {
|
|
for_each_online_cpu(cpu) {
|
|
@@ -3251,103 +3208,232 @@ static int mvneta_set_mac_addr(struct ne
|
|
return 0;
|
|
}
|
|
|
|
-static void mvneta_adjust_link(struct net_device *ndev)
|
|
+static void mvneta_validate(struct net_device *ndev, unsigned long *supported,
|
|
+ struct phylink_link_state *state)
|
|
+{
|
|
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
|
+
|
|
+ /* Allow all the expected bits */
|
|
+ phylink_set(mask, Autoneg);
|
|
+ phylink_set_port_modes(mask);
|
|
+
|
|
+ /* Asymmetric pause is unsupported */
|
|
+ phylink_set(mask, Pause);
|
|
+ /* Half-duplex at speeds higher than 100Mbit is unsupported */
|
|
+ phylink_set(mask, 1000baseT_Full);
|
|
+ phylink_set(mask, 1000baseX_Full);
|
|
+
|
|
+ if (state->interface != PHY_INTERFACE_MODE_1000BASEX) {
|
|
+ /* 10M and 100M are only supported in non-802.3z mode */
|
|
+ phylink_set(mask, 10baseT_Half);
|
|
+ phylink_set(mask, 10baseT_Full);
|
|
+ phylink_set(mask, 100baseT_Half);
|
|
+ phylink_set(mask, 100baseT_Full);
|
|
+ }
|
|
+
|
|
+ bitmap_and(supported, supported, mask,
|
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
+ bitmap_and(state->advertising, state->advertising, mask,
|
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
+}
|
|
+
|
|
+static int mvneta_mac_link_state(struct net_device *ndev,
|
|
+ struct phylink_link_state *state)
|
|
{
|
|
struct mvneta_port *pp = netdev_priv(ndev);
|
|
- struct phy_device *phydev = ndev->phydev;
|
|
- int status_change = 0;
|
|
+ u32 gmac_stat;
|
|
|
|
- if (phydev->link) {
|
|
- if ((pp->speed != phydev->speed) ||
|
|
- (pp->duplex != phydev->duplex)) {
|
|
- u32 val;
|
|
-
|
|
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
|
|
- MVNETA_GMAC_CONFIG_GMII_SPEED |
|
|
- MVNETA_GMAC_CONFIG_FULL_DUPLEX);
|
|
-
|
|
- if (phydev->duplex)
|
|
- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
|
|
-
|
|
- if (phydev->speed == SPEED_1000)
|
|
- val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
|
|
- else if (phydev->speed == SPEED_100)
|
|
- val |= MVNETA_GMAC_CONFIG_MII_SPEED;
|
|
+ gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
|
|
|
|
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
|
+ if (gmac_stat & MVNETA_GMAC_SPEED_1000)
|
|
+ state->speed = SPEED_1000;
|
|
+ else if (gmac_stat & MVNETA_GMAC_SPEED_100)
|
|
+ state->speed = SPEED_100;
|
|
+ else
|
|
+ state->speed = SPEED_10;
|
|
|
|
- pp->duplex = phydev->duplex;
|
|
- pp->speed = phydev->speed;
|
|
- }
|
|
+ state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE);
|
|
+ state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
|
|
+ state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
|
|
+
|
|
+ state->pause = 0;
|
|
+ if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE)
|
|
+ state->pause |= MLO_PAUSE_RX;
|
|
+ if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE)
|
|
+ state->pause |= MLO_PAUSE_TX;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void mvneta_mac_an_restart(struct net_device *ndev)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(ndev);
|
|
+ u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
+
|
|
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
|
|
+ gmac_an | MVNETA_GMAC_INBAND_RESTART_AN);
|
|
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
|
|
+ gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN);
|
|
+}
|
|
+
|
|
+static void mvneta_mac_config(struct net_device *ndev, unsigned int mode,
|
|
+ const struct phylink_link_state *state)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(ndev);
|
|
+ u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
|
|
+ u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
|
+ u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
|
+ u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
+
|
|
+ new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X;
|
|
+ new_ctrl2 = gmac_ctrl2 & ~MVNETA_GMAC2_INBAND_AN_ENABLE;
|
|
+ new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
|
+ new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE |
|
|
+ MVNETA_GMAC_INBAND_RESTART_AN |
|
|
+ MVNETA_GMAC_CONFIG_MII_SPEED |
|
|
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
|
|
+ MVNETA_GMAC_AN_SPEED_EN |
|
|
+ MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL |
|
|
+ MVNETA_GMAC_CONFIG_FLOW_CTRL |
|
|
+ MVNETA_GMAC_AN_FLOW_CTRL_EN |
|
|
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX |
|
|
+ MVNETA_GMAC_AN_DUPLEX_EN);
|
|
+
|
|
+ if (phylink_test(state->advertising, Pause))
|
|
+ new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
|
|
+ if (state->pause & MLO_PAUSE_TXRX_MASK)
|
|
+ new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
|
|
+
|
|
+ if (!phylink_autoneg_inband(mode)) {
|
|
+ /* Phy or fixed speed */
|
|
+ if (state->duplex)
|
|
+ new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
|
|
+
|
|
+ if (state->speed == SPEED_1000)
|
|
+ new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED;
|
|
+ else if (state->speed == SPEED_100)
|
|
+ new_an |= MVNETA_GMAC_CONFIG_MII_SPEED;
|
|
+ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
|
|
+ /* SGMII mode receives the state from the PHY */
|
|
+ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
|
|
+ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
|
+ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
|
|
+ MVNETA_GMAC_FORCE_LINK_PASS)) |
|
|
+ MVNETA_GMAC_INBAND_AN_ENABLE |
|
|
+ MVNETA_GMAC_AN_SPEED_EN |
|
|
+ MVNETA_GMAC_AN_DUPLEX_EN;
|
|
+ } else {
|
|
+ /* 802.3z negotiation - only 1000base-X */
|
|
+ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
|
|
+ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
|
+ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
|
|
+ MVNETA_GMAC_FORCE_LINK_PASS)) |
|
|
+ MVNETA_GMAC_INBAND_AN_ENABLE |
|
|
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
|
|
+ /* The MAC only supports FD mode */
|
|
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX;
|
|
+
|
|
+ if (state->pause & MLO_PAUSE_AN && state->an_enabled)
|
|
+ new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
|
|
+ }
|
|
+
|
|
+ /* Armada 370 documentation says we can only change the port mode
|
|
+ * and in-band enable when the link is down, so force it down
|
|
+ * while making these changes. We also do this for GMAC_CTRL2 */
|
|
+ if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X ||
|
|
+ (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE ||
|
|
+ (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) {
|
|
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
|
|
+ (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) |
|
|
+ MVNETA_GMAC_FORCE_LINK_DOWN);
|
|
+ }
|
|
+
|
|
+ if (new_ctrl0 != gmac_ctrl0)
|
|
+ mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0);
|
|
+ if (new_ctrl2 != gmac_ctrl2)
|
|
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2);
|
|
+ if (new_clk != gmac_clk)
|
|
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk);
|
|
+ if (new_an != gmac_an)
|
|
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
|
|
+}
|
|
+
|
|
+static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
|
|
+{
|
|
+ u32 lpi_ctl1;
|
|
+
|
|
+ lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
|
|
+ if (enable)
|
|
+ lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
|
|
+ else
|
|
+ lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
|
|
+ mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
|
|
+}
|
|
+
|
|
+static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(ndev);
|
|
+ u32 val;
|
|
+
|
|
+ mvneta_port_down(pp);
|
|
+
|
|
+ if (!phylink_autoneg_inband(mode)) {
|
|
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
+ val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
|
|
+ val |= MVNETA_GMAC_FORCE_LINK_DOWN;
|
|
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
|
}
|
|
|
|
- if (phydev->link != pp->link) {
|
|
- if (!phydev->link) {
|
|
- pp->duplex = -1;
|
|
- pp->speed = 0;
|
|
- }
|
|
+ pp->eee_active = false;
|
|
+ mvneta_set_eee(pp, false);
|
|
+}
|
|
+
|
|
+static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
|
|
+ struct phy_device *phy)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(ndev);
|
|
+ u32 val;
|
|
|
|
- pp->link = phydev->link;
|
|
- status_change = 1;
|
|
+ if (!phylink_autoneg_inband(mode)) {
|
|
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
+ val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
|
|
+ val |= MVNETA_GMAC_FORCE_LINK_PASS;
|
|
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
|
}
|
|
|
|
- if (status_change) {
|
|
- if (phydev->link) {
|
|
- if (!pp->use_inband_status) {
|
|
- u32 val = mvreg_read(pp,
|
|
- MVNETA_GMAC_AUTONEG_CONFIG);
|
|
- val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
|
|
- val |= MVNETA_GMAC_FORCE_LINK_PASS;
|
|
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
|
|
- val);
|
|
- }
|
|
- mvneta_port_up(pp);
|
|
- } else {
|
|
- if (!pp->use_inband_status) {
|
|
- u32 val = mvreg_read(pp,
|
|
- MVNETA_GMAC_AUTONEG_CONFIG);
|
|
- val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
|
|
- val |= MVNETA_GMAC_FORCE_LINK_DOWN;
|
|
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
|
|
- val);
|
|
- }
|
|
- mvneta_port_down(pp);
|
|
- }
|
|
- phy_print_status(phydev);
|
|
+ mvneta_port_up(pp);
|
|
+
|
|
+ if (phy && pp->eee_enabled) {
|
|
+ pp->eee_active = phy_init_eee(phy, 0) >= 0;
|
|
+ mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
|
|
}
|
|
}
|
|
|
|
+static const struct phylink_mac_ops mvneta_phylink_ops = {
|
|
+ .validate = mvneta_validate,
|
|
+ .mac_link_state = mvneta_mac_link_state,
|
|
+ .mac_an_restart = mvneta_mac_an_restart,
|
|
+ .mac_config = mvneta_mac_config,
|
|
+ .mac_link_down = mvneta_mac_link_down,
|
|
+ .mac_link_up = mvneta_mac_link_up,
|
|
+};
|
|
+
|
|
static int mvneta_mdio_probe(struct mvneta_port *pp)
|
|
{
|
|
- struct phy_device *phy_dev;
|
|
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
|
|
+ int err = phylink_of_phy_connect(pp->phylink, pp->dn);
|
|
+ if (err)
|
|
+ netdev_err(pp->dev, "could not attach PHY\n");
|
|
|
|
- phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0,
|
|
- pp->phy_interface);
|
|
- if (!phy_dev) {
|
|
- netdev_err(pp->dev, "could not find the PHY\n");
|
|
- return -ENODEV;
|
|
- }
|
|
-
|
|
- phy_ethtool_get_wol(phy_dev, &wol);
|
|
+ phylink_ethtool_get_wol(pp->phylink, &wol);
|
|
device_set_wakeup_capable(&pp->dev->dev, !!wol.supported);
|
|
|
|
- phy_dev->supported &= PHY_GBIT_FEATURES;
|
|
- phy_dev->advertising = phy_dev->supported;
|
|
-
|
|
- pp->link = 0;
|
|
- pp->duplex = 0;
|
|
- pp->speed = 0;
|
|
-
|
|
- return 0;
|
|
+ return err;
|
|
}
|
|
|
|
static void mvneta_mdio_remove(struct mvneta_port *pp)
|
|
{
|
|
- struct net_device *ndev = pp->dev;
|
|
-
|
|
- phy_disconnect(ndev->phydev);
|
|
+ phylink_disconnect_phy(pp->phylink);
|
|
}
|
|
|
|
/* Electing a CPU must be done in an atomic way: it should be done
|
|
@@ -3626,10 +3712,9 @@ static int mvneta_stop(struct net_device
|
|
|
|
static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
{
|
|
- if (!dev->phydev)
|
|
- return -ENOTSUPP;
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
|
|
- return phy_mii_ioctl(dev->phydev, ifr, cmd);
|
|
+ return phylink_mii_ioctl(pp->phylink, ifr, cmd);
|
|
}
|
|
|
|
/* Ethtool methods */
|
|
@@ -3640,44 +3725,25 @@ mvneta_ethtool_set_link_ksettings(struct
|
|
const struct ethtool_link_ksettings *cmd)
|
|
{
|
|
struct mvneta_port *pp = netdev_priv(ndev);
|
|
- struct phy_device *phydev = ndev->phydev;
|
|
|
|
- if (!phydev)
|
|
- return -ENODEV;
|
|
-
|
|
- if ((cmd->base.autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
|
|
- u32 val;
|
|
-
|
|
- mvneta_set_autoneg(pp, cmd->base.autoneg == AUTONEG_ENABLE);
|
|
-
|
|
- if (cmd->base.autoneg == AUTONEG_DISABLE) {
|
|
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
|
- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
|
|
- MVNETA_GMAC_CONFIG_GMII_SPEED |
|
|
- MVNETA_GMAC_CONFIG_FULL_DUPLEX);
|
|
-
|
|
- if (phydev->duplex)
|
|
- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
|
|
+ return phylink_ethtool_ksettings_set(pp->phylink, cmd);
|
|
+}
|
|
|
|
- if (phydev->speed == SPEED_1000)
|
|
- val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
|
|
- else if (phydev->speed == SPEED_100)
|
|
- val |= MVNETA_GMAC_CONFIG_MII_SPEED;
|
|
+/* Get link ksettings for ethtools */
|
|
+static int
|
|
+mvneta_ethtool_get_link_ksettings(struct net_device *ndev,
|
|
+ struct ethtool_link_ksettings *cmd)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(ndev);
|
|
|
|
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
|
- }
|
|
+ return phylink_ethtool_ksettings_get(pp->phylink, cmd);
|
|
+}
|
|
|
|
- pp->use_inband_status = (cmd->base.autoneg == AUTONEG_ENABLE);
|
|
- netdev_info(pp->dev, "autoneg status set to %i\n",
|
|
- pp->use_inband_status);
|
|
-
|
|
- if (netif_running(ndev)) {
|
|
- mvneta_port_down(pp);
|
|
- mvneta_port_up(pp);
|
|
- }
|
|
- }
|
|
+static int mvneta_ethtool_nway_reset(struct net_device *dev)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
|
|
- return phy_ethtool_ksettings_set(ndev->phydev, cmd);
|
|
+ return phylink_ethtool_nway_reset(pp->phylink);
|
|
}
|
|
|
|
/* Set interrupt coalescing for ethtools */
|
|
@@ -3769,6 +3835,22 @@ static int mvneta_ethtool_set_ringparam(
|
|
return 0;
|
|
}
|
|
|
|
+static void mvneta_ethtool_get_pauseparam(struct net_device *dev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
+
|
|
+ phylink_ethtool_get_pauseparam(pp->phylink, pause);
|
|
+}
|
|
+
|
|
+static int mvneta_ethtool_set_pauseparam(struct net_device *dev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
+
|
|
+ return phylink_ethtool_set_pauseparam(pp->phylink, pause);
|
|
+}
|
|
+
|
|
static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset,
|
|
u8 *data)
|
|
{
|
|
@@ -3785,26 +3867,35 @@ static void mvneta_ethtool_update_stats(
|
|
{
|
|
const struct mvneta_statistic *s;
|
|
void __iomem *base = pp->base;
|
|
- u32 high, low, val;
|
|
- u64 val64;
|
|
+ u32 high, low;
|
|
+ u64 val;
|
|
int i;
|
|
|
|
for (i = 0, s = mvneta_statistics;
|
|
s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
|
|
s++, i++) {
|
|
+ val = 0;
|
|
+
|
|
switch (s->type) {
|
|
case T_REG_32:
|
|
val = readl_relaxed(base + s->offset);
|
|
- pp->ethtool_stats[i] += val;
|
|
break;
|
|
case T_REG_64:
|
|
/* Docs say to read low 32-bit then high */
|
|
low = readl_relaxed(base + s->offset);
|
|
high = readl_relaxed(base + s->offset + 4);
|
|
- val64 = (u64)high << 32 | low;
|
|
- pp->ethtool_stats[i] += val64;
|
|
+ val = (u64)high << 32 | low;
|
|
+ break;
|
|
+ case T_SW:
|
|
+ switch (s->offset) {
|
|
+ case ETHTOOL_STAT_EEE_WAKEUP:
|
|
+ val = phylink_get_eee_err(pp->phylink);
|
|
+ break;
|
|
+ }
|
|
break;
|
|
}
|
|
+
|
|
+ pp->ethtool_stats[i] += val;
|
|
}
|
|
}
|
|
|
|
@@ -3939,28 +4030,65 @@ static int mvneta_ethtool_get_rxfh(struc
|
|
static void mvneta_ethtool_get_wol(struct net_device *dev,
|
|
struct ethtool_wolinfo *wol)
|
|
{
|
|
- wol->supported = 0;
|
|
- wol->wolopts = 0;
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
|
|
- if (dev->phydev)
|
|
- phy_ethtool_get_wol(dev->phydev, wol);
|
|
+ phylink_ethtool_get_wol(pp->phylink, wol);
|
|
}
|
|
|
|
static int mvneta_ethtool_set_wol(struct net_device *dev,
|
|
struct ethtool_wolinfo *wol)
|
|
{
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
int ret;
|
|
|
|
- if (!dev->phydev)
|
|
- return -EOPNOTSUPP;
|
|
-
|
|
- ret = phy_ethtool_set_wol(dev->phydev, wol);
|
|
+ ret = phylink_ethtool_set_wol(pp->phylink, wol);
|
|
if (!ret)
|
|
device_set_wakeup_enable(&dev->dev, !!wol->wolopts);
|
|
|
|
return ret;
|
|
}
|
|
|
|
+static int mvneta_ethtool_get_eee(struct net_device *dev,
|
|
+ struct ethtool_eee *eee)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
+ u32 lpi_ctl0;
|
|
+
|
|
+ lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
|
|
+
|
|
+ eee->eee_enabled = pp->eee_enabled;
|
|
+ eee->eee_active = pp->eee_active;
|
|
+ eee->tx_lpi_enabled = pp->tx_lpi_enabled;
|
|
+ eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
|
|
+
|
|
+ return phylink_ethtool_get_eee(pp->phylink, eee);
|
|
+}
|
|
+
|
|
+static int mvneta_ethtool_set_eee(struct net_device *dev,
|
|
+ struct ethtool_eee *eee)
|
|
+{
|
|
+ struct mvneta_port *pp = netdev_priv(dev);
|
|
+ u32 lpi_ctl0;
|
|
+
|
|
+ /* The Armada 37x documents do not give limits for this other than
|
|
+ * it being an 8-bit register. */
|
|
+ if (eee->tx_lpi_enabled &&
|
|
+ (eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255))
|
|
+ return -EINVAL;
|
|
+
|
|
+ lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
|
|
+ lpi_ctl0 &= ~(0xff << 8);
|
|
+ lpi_ctl0 |= eee->tx_lpi_timer << 8;
|
|
+ mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
|
|
+
|
|
+ pp->eee_enabled = eee->eee_enabled;
|
|
+ pp->tx_lpi_enabled = eee->tx_lpi_enabled;
|
|
+
|
|
+ mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
|
|
+
|
|
+ return phylink_ethtool_set_eee(pp->phylink, eee);
|
|
+}
|
|
+
|
|
static u16 mvneta_select_queue(struct net_device *dev, struct sk_buff *skb,
|
|
void *accel_priv,
|
|
select_queue_fallback_t fallback)
|
|
@@ -3984,13 +4112,15 @@ static const struct net_device_ops mvnet
|
|
};
|
|
|
|
static const struct ethtool_ops mvneta_eth_tool_ops = {
|
|
- .nway_reset = phy_ethtool_nway_reset,
|
|
+ .nway_reset = mvneta_ethtool_nway_reset,
|
|
.get_link = ethtool_op_get_link,
|
|
.set_coalesce = mvneta_ethtool_set_coalesce,
|
|
.get_coalesce = mvneta_ethtool_get_coalesce,
|
|
.get_drvinfo = mvneta_ethtool_get_drvinfo,
|
|
.get_ringparam = mvneta_ethtool_get_ringparam,
|
|
.set_ringparam = mvneta_ethtool_set_ringparam,
|
|
+ .get_pauseparam = mvneta_ethtool_get_pauseparam,
|
|
+ .set_pauseparam = mvneta_ethtool_set_pauseparam,
|
|
.get_strings = mvneta_ethtool_get_strings,
|
|
.get_ethtool_stats = mvneta_ethtool_get_stats,
|
|
.get_sset_count = mvneta_ethtool_get_sset_count,
|
|
@@ -3998,10 +4128,12 @@ static const struct ethtool_ops mvneta_e
|
|
.get_rxnfc = mvneta_ethtool_get_rxnfc,
|
|
.get_rxfh = mvneta_ethtool_get_rxfh,
|
|
.set_rxfh = mvneta_ethtool_set_rxfh,
|
|
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
|
|
+ .get_link_ksettings = mvneta_ethtool_get_link_ksettings,
|
|
.set_link_ksettings = mvneta_ethtool_set_link_ksettings,
|
|
.get_wol = mvneta_ethtool_get_wol,
|
|
.set_wol = mvneta_ethtool_set_wol,
|
|
+ .get_eee = mvneta_ethtool_get_eee,
|
|
+ .set_eee = mvneta_ethtool_set_eee,
|
|
};
|
|
|
|
/* Initialize hw */
|
|
@@ -4146,14 +4278,13 @@ static int mvneta_probe(struct platform_
|
|
{
|
|
struct resource *res;
|
|
struct device_node *dn = pdev->dev.of_node;
|
|
- struct device_node *phy_node;
|
|
struct device_node *bm_node;
|
|
struct mvneta_port *pp;
|
|
struct net_device *dev;
|
|
+ struct phylink *phylink;
|
|
const char *dt_mac_addr;
|
|
char hw_mac_addr[ETH_ALEN];
|
|
const char *mac_from;
|
|
- const char *managed;
|
|
int tx_csum_limit;
|
|
int phy_mode;
|
|
int err;
|
|
@@ -4169,31 +4300,11 @@ static int mvneta_probe(struct platform_
|
|
goto err_free_netdev;
|
|
}
|
|
|
|
- phy_node = of_parse_phandle(dn, "phy", 0);
|
|
- if (!phy_node) {
|
|
- 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 = of_node_get(dn);
|
|
- }
|
|
-
|
|
phy_mode = of_get_phy_mode(dn);
|
|
if (phy_mode < 0) {
|
|
dev_err(&pdev->dev, "incorrect phy-mode\n");
|
|
err = -EINVAL;
|
|
- goto err_put_phy_node;
|
|
+ goto err_free_irq;
|
|
}
|
|
|
|
dev->tx_queue_len = MVNETA_MAX_TXD;
|
|
@@ -4204,12 +4315,7 @@ static int mvneta_probe(struct platform_
|
|
|
|
pp = netdev_priv(dev);
|
|
spin_lock_init(&pp->lock);
|
|
- pp->phy_node = phy_node;
|
|
- pp->phy_interface = phy_mode;
|
|
-
|
|
- err = of_property_read_string(dn, "managed", &managed);
|
|
- pp->use_inband_status = (err == 0 &&
|
|
- strcmp(managed, "in-band-status") == 0);
|
|
+ pp->dn = dn;
|
|
|
|
pp->rxq_def = rxq_def;
|
|
|
|
@@ -4231,7 +4337,7 @@ static int mvneta_probe(struct platform_
|
|
pp->clk = devm_clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(pp->clk)) {
|
|
err = PTR_ERR(pp->clk);
|
|
- goto err_put_phy_node;
|
|
+ goto err_free_irq;
|
|
}
|
|
|
|
clk_prepare_enable(pp->clk);
|
|
@@ -4357,6 +4463,14 @@ static int mvneta_probe(struct platform_
|
|
/* 9676 == 9700 - 20 and rounding to 8 */
|
|
dev->max_mtu = 9676;
|
|
|
|
+ phylink = phylink_create(dev, dn, phy_mode, &mvneta_phylink_ops);
|
|
+ if (IS_ERR(phylink)) {
|
|
+ err = PTR_ERR(phylink);
|
|
+ goto err_free_stats;
|
|
+ }
|
|
+
|
|
+ pp->phylink = phylink;
|
|
+
|
|
err = register_netdev(dev);
|
|
if (err < 0) {
|
|
dev_err(&pdev->dev, "failed to register\n");
|
|
@@ -4368,14 +4482,6 @@ static int mvneta_probe(struct platform_
|
|
|
|
platform_set_drvdata(pdev, pp->dev);
|
|
|
|
- if (pp->use_inband_status) {
|
|
- struct phy_device *phy = of_phy_find_device(dn);
|
|
-
|
|
- mvneta_fixed_link_update(pp, phy);
|
|
-
|
|
- put_device(&phy->mdio.dev);
|
|
- }
|
|
-
|
|
return 0;
|
|
|
|
err_netdev:
|
|
@@ -4386,16 +4492,14 @@ err_netdev:
|
|
1 << pp->id);
|
|
}
|
|
err_free_stats:
|
|
+ if (pp->phylink)
|
|
+ phylink_destroy(pp->phylink);
|
|
free_percpu(pp->stats);
|
|
err_free_ports:
|
|
free_percpu(pp->ports);
|
|
err_clk:
|
|
clk_disable_unprepare(pp->clk_bus);
|
|
clk_disable_unprepare(pp->clk);
|
|
-err_put_phy_node:
|
|
- of_node_put(phy_node);
|
|
- if (of_phy_is_fixed_link(dn))
|
|
- of_phy_deregister_fixed_link(dn);
|
|
err_free_irq:
|
|
irq_dispose_mapping(dev->irq);
|
|
err_free_netdev:
|
|
@@ -4407,7 +4511,6 @@ err_free_netdev:
|
|
static int mvneta_remove(struct platform_device *pdev)
|
|
{
|
|
struct net_device *dev = platform_get_drvdata(pdev);
|
|
- struct device_node *dn = pdev->dev.of_node;
|
|
struct mvneta_port *pp = netdev_priv(dev);
|
|
|
|
unregister_netdev(dev);
|
|
@@ -4415,10 +4518,8 @@ static int mvneta_remove(struct platform
|
|
clk_disable_unprepare(pp->clk);
|
|
free_percpu(pp->ports);
|
|
free_percpu(pp->stats);
|
|
- if (of_phy_is_fixed_link(dn))
|
|
- of_phy_deregister_fixed_link(dn);
|
|
irq_dispose_mapping(dev->irq);
|
|
- of_node_put(pp->phy_node);
|
|
+ phylink_destroy(pp->phylink);
|
|
free_netdev(dev);
|
|
|
|
if (pp->bm_priv) {
|
|
@@ -4470,9 +4571,6 @@ static int mvneta_resume(struct device *
|
|
return err;
|
|
}
|
|
|
|
- if (pp->use_inband_status)
|
|
- mvneta_fixed_link_update(pp, dev->phydev);
|
|
-
|
|
netif_device_attach(dev);
|
|
if (netif_running(dev)) {
|
|
mvneta_open(dev);
|