diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/phy/at803x.c | 110 | ||||
-rw-r--r-- | drivers/net/phy/bcm7xxx.c | 97 | ||||
-rw-r--r-- | drivers/net/phy/dp83640.c | 17 | ||||
-rw-r--r-- | drivers/net/phy/dp83848.c | 88 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 65 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 9 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 46 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 7 | ||||
-rw-r--r-- | drivers/net/phy/smsc.c | 54 | ||||
-rw-r--r-- | drivers/net/phy/spi_ks8995.c | 304 |
11 files changed, 564 insertions, 234 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 60994a83a0d6..f0a77020037a 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -186,6 +186,7 @@ config MDIO_GPIO config MDIO_OCTEON tristate "Support for MDIO buses on Octeon and ThunderX SOCs" depends on 64BIT + depends on HAS_IOMEM help This module provides a driver for the Octeon and ThunderX MDIO diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 8a8f6fb2880d..2174ec937b4d 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -20,10 +20,21 @@ #include <linux/gpio/consumer.h> #define AT803X_INTR_ENABLE 0x12 +#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) +#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) +#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) +#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) +#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) +#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) +#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) +#define AT803X_INTR_ENABLE_WOL BIT(0) + #define AT803X_INTR_STATUS 0x13 + #define AT803X_SMART_SPEED 0x14 #define AT803X_LED_CONTROL 0x18 -#define AT803X_WOL_ENABLE 0x01 + #define AT803X_DEVICE_ADDR 0x03 #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B @@ -31,13 +42,15 @@ #define AT803X_MMD_ACCESS_CONTROL 0x0D #define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E #define AT803X_FUNC_DATA 0x4003 -#define AT803X_INER 0x0012 -#define AT803X_INER_INIT 0xec00 -#define AT803X_INSR 0x0013 + #define AT803X_DEBUG_ADDR 0x1D #define AT803X_DEBUG_DATA 0x1E -#define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 -#define AT803X_DEBUG_RGMII_TX_CLK_DLY BIT(8) + +#define AT803X_DEBUG_REG_0 0x00 +#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) + +#define AT803X_DEBUG_REG_5 0x05 +#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) #define ATH8030_PHY_ID 0x004dd076 #define ATH8031_PHY_ID 0x004dd074 @@ -61,6 +74,46 @@ struct at803x_context { u16 led_control; }; +static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) +{ + int ret; + + ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); + if (ret < 0) + return ret; + + return phy_read(phydev, AT803X_DEBUG_DATA); +} + +static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set) +{ + u16 val; + int ret; + + ret = at803x_debug_reg_read(phydev, reg); + if (ret < 0) + return ret; + + val = ret & 0xffff; + val &= ~clear; + val |= set; + + return phy_write(phydev, AT803X_DEBUG_DATA, val); +} + +static inline int at803x_enable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, + AT803X_DEBUG_RX_CLK_DLY_EN); +} + +static inline int at803x_enable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, + AT803X_DEBUG_TX_CLK_DLY_EN); +} + /* save relevant PHY registers to private copy */ static void at803x_context_save(struct phy_device *phydev, struct at803x_context *context) @@ -119,14 +172,14 @@ static int at803x_set_wol(struct phy_device *phydev, } value = phy_read(phydev, AT803X_INTR_ENABLE); - value |= AT803X_WOL_ENABLE; + value |= AT803X_INTR_ENABLE_WOL; ret = phy_write(phydev, AT803X_INTR_ENABLE, value); if (ret) return ret; value = phy_read(phydev, AT803X_INTR_STATUS); } else { value = phy_read(phydev, AT803X_INTR_ENABLE); - value &= (~AT803X_WOL_ENABLE); + value &= (~AT803X_INTR_ENABLE_WOL); ret = phy_write(phydev, AT803X_INTR_ENABLE, value); if (ret) return ret; @@ -145,7 +198,7 @@ static void at803x_get_wol(struct phy_device *phydev, wol->wolopts = 0; value = phy_read(phydev, AT803X_INTR_ENABLE); - if (value & AT803X_WOL_ENABLE) + if (value & AT803X_INTR_ENABLE_WOL) wol->wolopts |= WAKE_MAGIC; } @@ -157,7 +210,7 @@ static int at803x_suspend(struct phy_device *phydev) mutex_lock(&phydev->lock); value = phy_read(phydev, AT803X_INTR_ENABLE); - wol_enabled = value & AT803X_WOL_ENABLE; + wol_enabled = value & AT803X_INTR_ENABLE_WOL; value = phy_read(phydev, MII_BMCR); @@ -217,14 +270,17 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - ret = phy_write(phydev, AT803X_DEBUG_ADDR, - AT803X_DEBUG_SYSTEM_MODE_CTRL); - if (ret) + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { + ret = at803x_enable_rx_delay(phydev); + if (ret < 0) return ret; - ret = phy_write(phydev, AT803X_DEBUG_DATA, - AT803X_DEBUG_RGMII_TX_CLK_DLY); - if (ret) + } + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { + ret = at803x_enable_tx_delay(phydev); + if (ret < 0) return ret; } @@ -235,7 +291,7 @@ static int at803x_ack_interrupt(struct phy_device *phydev) { int err; - err = phy_read(phydev, AT803X_INSR); + err = phy_read(phydev, AT803X_INTR_STATUS); return (err < 0) ? err : 0; } @@ -245,13 +301,19 @@ static int at803x_config_intr(struct phy_device *phydev) int err; int value; - value = phy_read(phydev, AT803X_INER); + value = phy_read(phydev, AT803X_INTR_ENABLE); + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + value |= AT803X_INTR_ENABLE_AUTONEG_ERR; + value |= AT803X_INTR_ENABLE_SPEED_CHANGED; + value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; + value |= AT803X_INTR_ENABLE_LINK_FAIL; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS; - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - err = phy_write(phydev, AT803X_INER, - value | AT803X_INER_INIT); + err = phy_write(phydev, AT803X_INTR_ENABLE, value); + } else - err = phy_write(phydev, AT803X_INER, 0); + err = phy_write(phydev, AT803X_INTR_ENABLE, 0); return err; } @@ -322,7 +384,7 @@ static struct phy_driver at803x_driver[] = { .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, - .features = PHY_GBIT_FEATURES, + .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index bf241a3ec5e5..b881a7b1e4f6 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -24,7 +24,7 @@ #define MII_BCM7XXX_100TX_FALSE_CAR 0x13 #define MII_BCM7XXX_100TX_DISC 0x14 #define MII_BCM7XXX_AUX_MODE 0x1d -#define MII_BCM7XX_64CLK_MDIO BIT(12) +#define MII_BCM7XXX_64CLK_MDIO BIT(12) #define MII_BCM7XXX_TEST 0x1f #define MII_BCM7XXX_SHD_MODE_2 BIT(2) @@ -247,13 +247,9 @@ static int bcm7xxx_config_init(struct phy_device *phydev) int ret; /* Enable 64 clock MDIO */ - phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO); + phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO); phy_read(phydev, MII_BCM7XXX_AUX_MODE); - /* Workaround only required for 100Mbits/sec capable PHYs */ - if (phydev->supported & PHY_GBIT_FEATURES) - return 0; - /* set shadow mode 2 */ ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); @@ -270,7 +266,7 @@ static int bcm7xxx_config_init(struct phy_device *phydev) phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); /* reset shadow mode 2 */ - ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0); + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2); if (ret < 0) return ret; @@ -307,11 +303,6 @@ static int bcm7xxx_suspend(struct phy_device *phydev) return 0; } -static int bcm7xxx_dummy_config_init(struct phy_device *phydev) -{ - return 0; -} - #define BCM7XXX_28NM_GPHY(_oui, _name) \ { \ .phy_id = (_oui), \ @@ -326,6 +317,21 @@ static int bcm7xxx_dummy_config_init(struct phy_device *phydev) .resume = bcm7xxx_28nm_resume, \ } +#define BCM7XXX_40NM_EPHY(_oui, _name) \ +{ \ + .phy_id = (_oui), \ + .phy_id_mask = 0xfffffff0, \ + .name = _name, \ + .features = PHY_BASIC_FEATURES | \ + SUPPORTED_Pause | SUPPORTED_Asym_Pause, \ + .flags = PHY_IS_INTERNAL, \ + .config_init = bcm7xxx_config_init, \ + .config_aneg = genphy_config_aneg, \ + .read_status = genphy_read_status, \ + .suspend = bcm7xxx_suspend, \ + .resume = bcm7xxx_config_init, \ +} + static struct phy_driver bcm7xxx_driver[] = { BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), @@ -333,67 +339,10 @@ static struct phy_driver bcm7xxx_driver[] = { BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), -{ - .phy_id = PHY_ID_BCM7425, - .phy_id_mask = 0xfffffff0, - .name = "Broadcom BCM7425", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_IS_INTERNAL, - .config_init = bcm7xxx_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .suspend = bcm7xxx_suspend, - .resume = bcm7xxx_config_init, -}, { - .phy_id = PHY_ID_BCM7429, - .phy_id_mask = 0xfffffff0, - .name = "Broadcom BCM7429", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_IS_INTERNAL, - .config_init = bcm7xxx_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .suspend = bcm7xxx_suspend, - .resume = bcm7xxx_config_init, -}, { - .phy_id = PHY_ID_BCM7435, - .phy_id_mask = 0xfffffff0, - .name = "Broadcom BCM7435", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_IS_INTERNAL, - .config_init = bcm7xxx_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .suspend = bcm7xxx_suspend, - .resume = bcm7xxx_config_init, -}, { - .phy_id = PHY_BCM_OUI_4, - .phy_id_mask = 0xffff0000, - .name = "Broadcom BCM7XXX 40nm", - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_IS_INTERNAL, - .config_init = bcm7xxx_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .suspend = bcm7xxx_suspend, - .resume = bcm7xxx_config_init, -}, { - .phy_id = PHY_BCM_OUI_5, - .phy_id_mask = 0xffffff00, - .name = "Broadcom BCM7XXX 65nm", - .features = PHY_BASIC_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_IS_INTERNAL, - .config_init = bcm7xxx_dummy_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .suspend = bcm7xxx_suspend, - .resume = bcm7xxx_config_init, -} }; + BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"), + BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"), + BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"), +}; static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { { PHY_ID_BCM7250, 0xfffffff0, }, @@ -404,8 +353,6 @@ static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { { PHY_ID_BCM7439, 0xfffffff0, }, { PHY_ID_BCM7435, 0xfffffff0, }, { PHY_ID_BCM7445, 0xfffffff0, }, - { PHY_BCM_OUI_4, 0xffff0000 }, - { PHY_BCM_OUI_5, 0xffffff00 }, { } }; diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 180f69952779..7a240fce3a7e 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -846,6 +846,11 @@ static void decode_rxts(struct dp83640_private *dp83640, struct skb_shared_hwtstamps *shhwtstamps = NULL; struct sk_buff *skb; unsigned long flags; + u8 overflow; + + overflow = (phy_rxts->ns_hi >> 14) & 0x3; + if (overflow) + pr_debug("rx timestamp queue overflow, count %d\n", overflow); spin_lock_irqsave(&dp83640->rx_lock, flags); @@ -888,6 +893,7 @@ static void decode_txts(struct dp83640_private *dp83640, struct skb_shared_hwtstamps shhwtstamps; struct sk_buff *skb; u64 ns; + u8 overflow; /* We must already have the skb that triggered this. */ @@ -897,6 +903,17 @@ static void decode_txts(struct dp83640_private *dp83640, pr_debug("have timestamp but tx_queue empty\n"); return; } + + overflow = (phy_txts->ns_hi >> 14) & 0x3; + if (overflow) { + pr_debug("tx timestamp queue overflow, count %d\n", overflow); + while (skb) { + skb_complete_tx_timestamp(skb, NULL); + skb = skb_dequeue(&dp83640->tx_queue); + } + return; + } + ns = phy2txts(phy_txts); memset(&shhwtstamps, 0, sizeof(shhwtstamps)); shhwtstamps.hwtstamp = ns_to_ktime(ns); diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c index 5e14e629c597..03d54c4adc88 100644 --- a/drivers/net/phy/dp83848.c +++ b/drivers/net/phy/dp83848.c @@ -1,7 +1,7 @@ /* * Driver for the Texas Instruments DP83848 PHY * - * Copyright (C) 2015 Texas Instruments Inc. + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * * 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 @@ -16,11 +16,13 @@ #include <linux/module.h> #include <linux/phy.h> -#define DP83848_PHY_ID 0x20005c90 +#define TI_DP83848C_PHY_ID 0x20005ca0 +#define NS_DP83848C_PHY_ID 0x20005c90 +#define TLK10X_PHY_ID 0x2000a210 /* Registers */ -#define DP83848_MICR 0x11 -#define DP83848_MISR 0x12 +#define DP83848_MICR 0x11 /* MII Interrupt Control Register */ +#define DP83848_MISR 0x12 /* MII Interrupt Status Register */ /* MICR Register Fields */ #define DP83848_MICR_INT_OE BIT(0) /* Interrupt Output Enable */ @@ -36,6 +38,12 @@ #define DP83848_MISR_ED_INT_EN BIT(6) /* Energy detect */ #define DP83848_MISR_LQM_INT_EN BIT(7) /* Link Quality Monitor */ +#define DP83848_INT_EN_MASK \ + (DP83848_MISR_ANC_INT_EN | \ + DP83848_MISR_DUP_INT_EN | \ + DP83848_MISR_SPD_INT_EN | \ + DP83848_MISR_LINK_INT_EN) + static int dp83848_ack_interrupt(struct phy_device *phydev) { int err = phy_read(phydev, DP83848_MISR); @@ -45,50 +53,58 @@ static int dp83848_ack_interrupt(struct phy_device *phydev) static int dp83848_config_intr(struct phy_device *phydev) { - int err; + int control, ret; + + control = phy_read(phydev, DP83848_MICR); + if (control < 0) + return control; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { - err = phy_write(phydev, DP83848_MICR, - DP83848_MICR_INT_OE | - DP83848_MICR_INTEN); - if (err < 0) - return err; - - return phy_write(phydev, DP83848_MISR, - DP83848_MISR_ANC_INT_EN | - DP83848_MISR_DUP_INT_EN | - DP83848_MISR_SPD_INT_EN | - DP83848_MISR_LINK_INT_EN); + control |= DP83848_MICR_INT_OE; + control |= DP83848_MICR_INTEN; + + ret = phy_write(phydev, DP83848_MISR, DP83848_INT_EN_MASK); + if (ret < 0) + return ret; + } else { + control &= ~DP83848_MICR_INTEN; } - return phy_write(phydev, DP83848_MICR, 0x0); + return phy_write(phydev, DP83848_MICR, control); } static struct mdio_device_id __maybe_unused dp83848_tbl[] = { - { DP83848_PHY_ID, 0xfffffff0 }, + { TI_DP83848C_PHY_ID, 0xfffffff0 }, + { NS_DP83848C_PHY_ID, 0xfffffff0 }, + { TLK10X_PHY_ID, 0xfffffff0 }, { } }; MODULE_DEVICE_TABLE(mdio, dp83848_tbl); +#define DP83848_PHY_DRIVER(_id, _name) \ + { \ + .phy_id = _id, \ + .phy_id_mask = 0xfffffff0, \ + .name = _name, \ + .features = PHY_BASIC_FEATURES, \ + .flags = PHY_HAS_INTERRUPT, \ + \ + .soft_reset = genphy_soft_reset, \ + .config_init = genphy_config_init, \ + .suspend = genphy_suspend, \ + .resume = genphy_resume, \ + .config_aneg = genphy_config_aneg, \ + .read_status = genphy_read_status, \ + \ + /* IRQ related */ \ + .ack_interrupt = dp83848_ack_interrupt, \ + .config_intr = dp83848_config_intr, \ + } + static struct phy_driver dp83848_driver[] = { - { - .phy_id = DP83848_PHY_ID, - .phy_id_mask = 0xfffffff0, - .name = "TI DP83848", - .features = PHY_BASIC_FEATURES, - .flags = PHY_HAS_INTERRUPT, - - .soft_reset = genphy_soft_reset, - .config_init = genphy_config_init, - .suspend = genphy_suspend, - .resume = genphy_resume, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - - /* IRQ related */ - .ack_interrupt = dp83848_ack_interrupt, - .config_intr = dp83848_config_intr, - }, + DP83848_PHY_DRIVER(TI_DP83848C_PHY_ID, "TI DP83848C 10/100 Mbps PHY"), + DP83848_PHY_DRIVER(NS_DP83848C_PHY_ID, "NS DP83848C 10/100 Mbps PHY"), + DP83848_PHY_DRIVER(TLK10X_PHY_ID, "TI TLK10X 10/100 Mbps PHY"), }; module_phy_driver(dp83848_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index e3eb96443c97..280e8795b463 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -133,6 +133,11 @@ #define MII_88E3016_DISABLE_SCRAMBLER 0x0200 #define MII_88E3016_AUTO_MDIX_CROSSOVER 0x0030 +#define MII_88E1510_GEN_CTRL_REG_1 0x14 +#define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7 +#define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */ +#define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ + MODULE_DESCRIPTION("Marvell PHY driver"); MODULE_AUTHOR("Andy Fleming"); MODULE_LICENSE("GPL"); @@ -446,6 +451,12 @@ static int m88e1510_config_aneg(struct phy_device *phydev) if (err < 0) return err; + return 0; +} + +static int marvell_config_init(struct phy_device *phydev) +{ + /* Set registers from marvell,reg-init DT property */ return marvell_of_reg_init(phydev); } @@ -495,7 +506,7 @@ static int m88e1116r_config_init(struct phy_device *phydev) mdelay(500); - return 0; + return marvell_config_init(phydev); } static int m88e3016_config_init(struct phy_device *phydev) @@ -514,7 +525,7 @@ static int m88e3016_config_init(struct phy_device *phydev) if (reg < 0) return reg; - return 0; + return marvell_config_init(phydev); } static int m88e1111_config_init(struct phy_device *phydev) @@ -625,6 +636,41 @@ static int m88e1111_config_init(struct phy_device *phydev) return phy_write(phydev, MII_BMCR, BMCR_RESET); } +static int m88e1510_config_init(struct phy_device *phydev) +{ + int err; + int temp; + + /* SGMII-to-Copper mode initialization */ + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + /* Select page 18 */ + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 18); + if (err < 0) + return err; + + /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */ + temp = phy_read(phydev, MII_88E1510_GEN_CTRL_REG_1); + temp &= ~MII_88E1510_GEN_CTRL_REG_1_MODE_MASK; + temp |= MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII; + err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp); + if (err < 0) + return err; + + /* PHY reset is necessary after changing MODE[2:0] */ + temp |= MII_88E1510_GEN_CTRL_REG_1_RESET; + err = phy_write(phydev, MII_88E1510_GEN_CTRL_REG_1, temp); + if (err < 0) + return err; + + /* Reset page selection */ + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0); + if (err < 0) + return err; + } + + return marvell_config_init(phydev); +} + static int m88e1118_config_aneg(struct phy_device *phydev) { int err; @@ -1025,8 +1071,8 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i) { struct marvell_hw_stat stat = marvell_hw_stats[i]; struct marvell_priv *priv = phydev->priv; - int err, oldpage; - u64 val; + int err, oldpage, val; + u64 ret; oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); err = phy_write(phydev, MII_MARVELL_PHY_PAGE, @@ -1036,16 +1082,16 @@ static u64 marvell_get_stat(struct phy_device *phydev, int i) val = phy_read(phydev, stat.reg); if (val < 0) { - val = UINT64_MAX; + ret = UINT64_MAX; } else { val = val & ((1 << stat.bits) - 1); priv->stats[i] += val; - val = priv->stats[i]; + ret = priv->stats[i]; } phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); - return val; + return ret; } static void marvell_get_stats(struct phy_device *phydev, @@ -1078,6 +1124,7 @@ static struct phy_driver marvell_drivers[] = { .features = PHY_GBIT_FEATURES, .probe = marvell_probe, .flags = PHY_HAS_INTERRUPT, + .config_init = &marvell_config_init, .config_aneg = &marvell_config_aneg, .read_status = &genphy_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1149,6 +1196,7 @@ static struct phy_driver marvell_drivers[] = { .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .probe = marvell_probe, + .config_init = &marvell_config_init, .config_aneg = &m88e1121_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1167,6 +1215,7 @@ static struct phy_driver marvell_drivers[] = { .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .probe = marvell_probe, + .config_init = &marvell_config_init, .config_aneg = &m88e1318_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1259,6 +1308,7 @@ static struct phy_driver marvell_drivers[] = { .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .probe = marvell_probe, + .config_init = &m88e1510_config_init, .config_aneg = &m88e1510_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1277,6 +1327,7 @@ static struct phy_driver marvell_drivers[] = { .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .probe = marvell_probe, + .config_init = &marvell_config_init, .config_aneg = &m88e1510_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 03833dbfca67..48219c83fb00 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -612,18 +612,19 @@ static u64 kszphy_get_stat(struct phy_device *phydev, int i) { struct kszphy_hw_stat stat = kszphy_hw_stats[i]; struct kszphy_priv *priv = phydev->priv; - u64 val; + int val; + u64 ret; val = phy_read(phydev, stat.reg); if (val < 0) { - val = UINT64_MAX; + ret = UINT64_MAX; } else { val = val & ((1 << stat.bits) - 1); priv->stats[i] += val; - val = priv->stats[i]; + ret = priv->stats[i]; } - return val; + return ret; } static void kszphy_get_stats(struct phy_device *phydev, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 8763bb20988a..5590b9c182c9 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -692,25 +692,29 @@ void phy_change(struct work_struct *work) struct phy_device *phydev = container_of(work, struct phy_device, phy_queue); - if (phydev->drv->did_interrupt && - !phydev->drv->did_interrupt(phydev)) - goto ignore; + if (phy_interrupt_is_valid(phydev)) { + if (phydev->drv->did_interrupt && + !phydev->drv->did_interrupt(phydev)) + goto ignore; - if (phy_disable_interrupts(phydev)) - goto phy_err; + if (phy_disable_interrupts(phydev)) + goto phy_err; + } mutex_lock(&phydev->lock); if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state)) phydev->state = PHY_CHANGELINK; mutex_unlock(&phydev->lock); - atomic_dec(&phydev->irq_disable); - enable_irq(phydev->irq); + if (phy_interrupt_is_valid(phydev)) { + atomic_dec(&phydev->irq_disable); + enable_irq(phydev->irq); - /* Reenable interrupts */ - if (PHY_HALTED != phydev->state && - phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED)) - goto irq_enable_err; + /* Reenable interrupts */ + if (PHY_HALTED != phydev->state && + phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED)) + goto irq_enable_err; + } /* reschedule state queue work to run as soon as possible */ cancel_delayed_work_sync(&phydev->state_queue); @@ -905,10 +909,10 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); break; case PHY_RUNNING: - /* Only register a CHANGE if we are polling or ignoring - * interrupts and link changed since latest checking. + /* Only register a CHANGE if we are polling and link changed + * since latest checking. */ - if (!phy_interrupt_is_valid(phydev)) { + if (phydev->irq == PHY_POLL) { old_link = phydev->link; err = phy_read_status(phydev); if (err) @@ -1000,15 +1004,21 @@ void phy_state_machine(struct work_struct *work) phy_state_to_str(old_state), phy_state_to_str(phydev->state)); - queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, - PHY_STATE_TIME * HZ); + /* Only re-schedule a PHY state machine change if we are polling the + * PHY, if PHY_IGNORE_INTERRUPT is set, then we will be moving + * between states from phy_mac_interrupt() + */ + if (phydev->irq == PHY_POLL) + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, + PHY_STATE_TIME * HZ); } void phy_mac_interrupt(struct phy_device *phydev, int new_link) { - cancel_work_sync(&phydev->phy_queue); phydev->link = new_link; - schedule_work(&phydev->phy_queue); + + /* Trigger a state machine change */ + queue_work(system_power_efficient_wq, &phydev->phy_queue); } EXPORT_SYMBOL(phy_mac_interrupt); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 903737adfc01..e551f3a89cfd 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -901,6 +901,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, phydev->state = PHY_READY; + /* Initial carrier state is off as the phy is about to be + * (re)initialized. + */ + netif_carrier_off(phydev->attached_dev); + /* Do initial configuration here, now that * we have certain key parameters * (dev_flags and interface) @@ -1405,7 +1410,7 @@ int genphy_config_init(struct phy_device *phydev) features = (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | - SUPPORTED_BNC); + SUPPORTED_BNC | SUPPORTED_Pause | SUPPORTED_Asym_Pause); /* Do we support autonegotiation? */ val = phy_read(phydev, MII_BMSR); diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index e485f2653c82..2e21e9366f76 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -24,6 +24,10 @@ #include <linux/netdevice.h> #include <linux/smscphy.h> +struct smsc_phy_priv { + bool energy_enable; +}; + static int smsc_phy_config_intr(struct phy_device *phydev) { int rc = phy_write (phydev, MII_LAN83C185_IM, @@ -43,19 +47,14 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev) static int smsc_phy_config_init(struct phy_device *phydev) { - int __maybe_unused len; - struct device *dev __maybe_unused = &phydev->mdio.dev; - struct device_node *of_node __maybe_unused = dev->of_node; + struct smsc_phy_priv *priv = phydev->priv; + int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); - int enable_energy = 1; if (rc < 0) return rc; - if (of_find_property(of_node, "smsc,disable-energy-detect", &len)) - enable_energy = 0; - - if (enable_energy) { + if (priv->energy_enable) { /* Enable energy detect mode for this SMSC Transceivers */ rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc | MII_LAN83C185_EDPWRDOWN); @@ -110,10 +109,13 @@ static int lan911x_config_init(struct phy_device *phydev) */ static int lan87xx_read_status(struct phy_device *phydev) { + struct smsc_phy_priv *priv = phydev->priv; + int err = genphy_read_status(phydev); - int i; - if (!phydev->link) { + if (!phydev->link && priv->energy_enable) { + int i; + /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) @@ -149,6 +151,26 @@ static int lan87xx_read_status(struct phy_device *phydev) return err; } +static int smsc_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct device_node *of_node = dev->of_node; + struct smsc_phy_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->energy_enable = true; + + if (of_property_read_bool(of_node, "smsc,disable-energy-detect")) + priv->energy_enable = false; + + phydev->priv = priv; + + return 0; +} + static struct phy_driver smsc_phy_driver[] = { { .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ @@ -159,6 +181,8 @@ static struct phy_driver smsc_phy_driver[] = { | SUPPORTED_Asym_Pause), .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + .probe = smsc_phy_probe, + /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -180,6 +204,8 @@ static struct phy_driver smsc_phy_driver[] = { | SUPPORTED_Asym_Pause), .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + .probe = smsc_phy_probe, + /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -201,6 +227,8 @@ static struct phy_driver smsc_phy_driver[] = { | SUPPORTED_Asym_Pause), .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + .probe = smsc_phy_probe, + /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = lan87xx_read_status, @@ -222,6 +250,8 @@ static struct phy_driver smsc_phy_driver[] = { | SUPPORTED_Asym_Pause), .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + .probe = smsc_phy_probe, + /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -242,6 +272,8 @@ static struct phy_driver smsc_phy_driver[] = { | SUPPORTED_Asym_Pause), .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + .probe = smsc_phy_probe, + /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = lan87xx_read_status, @@ -263,6 +295,8 @@ static struct phy_driver smsc_phy_driver[] = { | SUPPORTED_Asym_Pause), .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + .probe = smsc_phy_probe, + /* basic functions */ .config_aneg = genphy_config_aneg, .read_status = lan87xx_read_status, diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index c72c42206850..b5d50d458728 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -18,6 +18,9 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/spi/spi.h> @@ -74,6 +77,7 @@ #define KS8995_REGS_SIZE 0x80 #define KSZ8864_REGS_SIZE 0x100 +#define KSZ8795_REGS_SIZE 0x100 #define ID1_CHIPID_M 0xf #define ID1_CHIPID_S 4 @@ -82,15 +86,63 @@ #define ID1_START_SW 1 /* start the switch */ #define FAMILY_KS8995 0x95 +#define FAMILY_KSZ8795 0x87 #define CHIPID_M 0 +#define KS8995_CHIP_ID 0x00 +#define KSZ8864_CHIP_ID 0x01 +#define KSZ8795_CHIP_ID 0x09 #define KS8995_CMD_WRITE 0x02U #define KS8995_CMD_READ 0x03U #define KS8995_RESET_DELAY 10 /* usec */ +enum ks8995_chip_variant { + ks8995, + ksz8864, + ksz8795, + max_variant +}; + +struct ks8995_chip_params { + char *name; + int family_id; + int chip_id; + int regs_size; + int addr_width; + int addr_shift; +}; + +static const struct ks8995_chip_params ks8995_chip[] = { + [ks8995] = { + .name = "KS8995MA", + .family_id = FAMILY_KS8995, + .chip_id = KS8995_CHIP_ID, + .regs_size = KS8995_REGS_SIZE, + .addr_width = 8, + .addr_shift = 0, + }, + [ksz8864] = { + .name = "KSZ8864RMN", + .family_id = FAMILY_KS8995, + .chip_id = KSZ8864_CHIP_ID, + .regs_size = KSZ8864_REGS_SIZE, + .addr_width = 8, + .addr_shift = 0, + }, + [ksz8795] = { + .name = "KSZ8795CLX", + .family_id = FAMILY_KSZ8795, + .chip_id = KSZ8795_CHIP_ID, + .regs_size = KSZ8795_REGS_SIZE, + .addr_width = 12, + .addr_shift = 1, + }, +}; + struct ks8995_pdata { - /* not yet implemented */ + int reset_gpio; + enum of_gpio_flags reset_gpio_flags; }; struct ks8995_switch { @@ -98,7 +150,17 @@ struct ks8995_switch { struct mutex lock; struct ks8995_pdata *pdata; struct bin_attribute regs_attr; + const struct ks8995_chip_params *chip; + int revision_id; +}; + +static const struct spi_device_id ks8995_id[] = { + {"ks8995", ks8995}, + {"ksz8864", ksz8864}, + {"ksz8795", ksz8795}, + { } }; +MODULE_DEVICE_TABLE(spi, ks8995_id); static inline u8 get_chip_id(u8 val) { @@ -110,20 +172,44 @@ static inline u8 get_chip_rev(u8 val) return (val >> ID1_REVISION_S) & ID1_REVISION_M; } +/* create_spi_cmd - create a chip specific SPI command header + * @ks: pointer to switch instance + * @cmd: SPI command for switch + * @address: register address for command + * + * Different chip families use different bit pattern to address the switches + * registers: + * + * KS8995: 8bit command + 8bit address + * KSZ8795: 3bit command + 12bit address + 1bit TR (?) + */ +static inline __be16 create_spi_cmd(struct ks8995_switch *ks, int cmd, + unsigned address) +{ + u16 result = cmd; + + /* make room for address (incl. address shift) */ + result <<= ks->chip->addr_width + ks->chip->addr_shift; + /* add address */ + result |= address << ks->chip->addr_shift; + /* SPI protocol needs big endian */ + return cpu_to_be16(result); +} /* ------------------------------------------------------------------------ */ static int ks8995_read(struct ks8995_switch *ks, char *buf, unsigned offset, size_t count) { - u8 cmd[2]; + __be16 cmd; struct spi_transfer t[2]; struct spi_message m; int err; + cmd = create_spi_cmd(ks, KS8995_CMD_READ, offset); spi_message_init(&m); memset(&t, 0, sizeof(t)); - t[0].tx_buf = cmd; + t[0].tx_buf = &cmd; t[0].len = sizeof(cmd); spi_message_add_tail(&t[0], &m); @@ -131,9 +217,6 @@ static int ks8995_read(struct ks8995_switch *ks, char *buf, t[1].len = count; spi_message_add_tail(&t[1], &m); - cmd[0] = KS8995_CMD_READ; - cmd[1] = offset; - mutex_lock(&ks->lock); err = spi_sync(ks->spi, &m); mutex_unlock(&ks->lock); @@ -141,20 +224,20 @@ static int ks8995_read(struct ks8995_switch *ks, char *buf, return err ? err : count; } - static int ks8995_write(struct ks8995_switch *ks, char *buf, unsigned offset, size_t count) { - u8 cmd[2]; + __be16 cmd; struct spi_transfer t[2]; struct spi_message m; int err; + cmd = create_spi_cmd(ks, KS8995_CMD_WRITE, offset); spi_message_init(&m); memset(&t, 0, sizeof(t)); - t[0].tx_buf = cmd; + t[0].tx_buf = &cmd; t[0].len = sizeof(cmd); spi_message_add_tail(&t[0], &m); @@ -162,9 +245,6 @@ static int ks8995_write(struct ks8995_switch *ks, char *buf, t[1].len = count; spi_message_add_tail(&t[1], &m); - cmd[0] = KS8995_CMD_WRITE; - cmd[1] = offset; - mutex_lock(&ks->lock); err = spi_sync(ks->spi, &m); mutex_unlock(&ks->lock); @@ -233,6 +313,107 @@ static ssize_t ks8995_registers_write(struct file *filp, struct kobject *kobj, return ks8995_write(ks8995, buf, off, count); } +/* ks8995_get_revision - get chip revision + * @ks: pointer to switch instance + * + * Verify chip family and id and get chip revision. + */ +static int ks8995_get_revision(struct ks8995_switch *ks) +{ + int err; + u8 id0, id1, ksz8864_id; + + /* read family id */ + err = ks8995_read_reg(ks, KS8995_REG_ID0, &id0); + if (err) { + err = -EIO; + goto err_out; + } + + /* verify family id */ + if (id0 != ks->chip->family_id) { + dev_err(&ks->spi->dev, "chip family id mismatch: expected 0x%02x but 0x%02x read\n", + ks->chip->family_id, id0); + err = -ENODEV; + goto err_out; + } + + switch (ks->chip->family_id) { + case FAMILY_KS8995: + /* try reading chip id at CHIP ID1 */ + err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); + if (err) { + err = -EIO; + goto err_out; + } + + /* verify chip id */ + if ((get_chip_id(id1) == CHIPID_M) && + (get_chip_id(id1) == ks->chip->chip_id)) { + /* KS8995MA */ + ks->revision_id = get_chip_rev(id1); + } else if (get_chip_id(id1) != CHIPID_M) { + /* KSZ8864RMN */ + err = ks8995_read_reg(ks, KS8995_REG_ID1, &ksz8864_id); + if (err) { + err = -EIO; + goto err_out; + } + + if ((ksz8864_id & 0x80) && + (ks->chip->chip_id == KSZ8864_CHIP_ID)) { + ks->revision_id = get_chip_rev(id1); + } + + } else { + dev_err(&ks->spi->dev, "unsupported chip id for KS8995 family: 0x%02x\n", + id1); + err = -ENODEV; + } + break; + case FAMILY_KSZ8795: + /* try reading chip id at CHIP ID1 */ + err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); + if (err) { + err = -EIO; + goto err_out; + } + + if (get_chip_id(id1) == ks->chip->chip_id) { + ks->revision_id = get_chip_rev(id1); + } else { + dev_err(&ks->spi->dev, "unsupported chip id for KSZ8795 family: 0x%02x\n", + id1); + err = -ENODEV; + } + break; + default: + dev_err(&ks->spi->dev, "unsupported family id: 0x%02x\n", id0); + err = -ENODEV; + break; + } +err_out: + return err; +} + +/* ks8995_parse_dt - setup platform data from devicetree + * @ks: pointer to switch instance + * + * Parses supported DT properties and sets up platform data + * accordingly. + */ +static void ks8995_parse_dt(struct ks8995_switch *ks) +{ + struct device_node *np = ks->spi->dev.of_node; + struct ks8995_pdata *pdata = ks->pdata; + + if (!np) + return; + + pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, + &pdata->reset_gpio_flags); +} + static const struct bin_attribute ks8995_registers_attr = { .attr = { .name = "registers", @@ -244,24 +425,58 @@ static const struct bin_attribute ks8995_registers_attr = { }; /* ------------------------------------------------------------------------ */ - static int ks8995_probe(struct spi_device *spi) { - struct ks8995_switch *ks; - struct ks8995_pdata *pdata; - u8 ids[2]; - int err; + struct ks8995_switch *ks; + int err; + int variant = spi_get_device_id(spi)->driver_data; - /* Chip description */ - pdata = spi->dev.platform_data; + if (variant >= max_variant) { + dev_err(&spi->dev, "bad chip variant %d\n", variant); + return -ENODEV; + } ks = devm_kzalloc(&spi->dev, sizeof(*ks), GFP_KERNEL); if (!ks) return -ENOMEM; mutex_init(&ks->lock); - ks->pdata = pdata; ks->spi = spi_dev_get(spi); + ks->chip = &ks8995_chip[variant]; + + if (ks->spi->dev.of_node) { + ks->pdata = devm_kzalloc(&spi->dev, sizeof(*ks->pdata), + GFP_KERNEL); + if (!ks->pdata) + return -ENOMEM; + + ks->pdata->reset_gpio = -1; + + ks8995_parse_dt(ks); + } + + if (!ks->pdata) + ks->pdata = spi->dev.platform_data; + + /* de-assert switch reset */ + if (ks->pdata && gpio_is_valid(ks->pdata->reset_gpio)) { + unsigned long flags; + + flags = (ks->pdata->reset_gpio_flags == OF_GPIO_ACTIVE_LOW ? + GPIOF_ACTIVE_LOW : 0); + + err = devm_gpio_request_one(&spi->dev, + ks->pdata->reset_gpio, + flags, "switch-reset"); + if (err) { + dev_err(&spi->dev, + "failed to get reset-gpios: %d\n", err); + return -EIO; + } + + gpiod_set_value(gpio_to_desc(ks->pdata->reset_gpio), 0); + } + spi_set_drvdata(spi, ks); spi->mode = SPI_MODE_0; @@ -272,39 +487,12 @@ static int ks8995_probe(struct spi_device *spi) return err; } - err = ks8995_read(ks, ids, KS8995_REG_ID0, sizeof(ids)); - if (err < 0) { - dev_err(&spi->dev, "unable to read id registers, err=%d\n", - err); + err = ks8995_get_revision(ks); + if (err) return err; - } - - switch (ids[0]) { - case FAMILY_KS8995: - break; - default: - dev_err(&spi->dev, "unknown family id:%02x\n", ids[0]); - return -ENODEV; - } + ks->regs_attr.size = ks->chip->regs_size; memcpy(&ks->regs_attr, &ks8995_registers_attr, sizeof(ks->regs_attr)); - if (get_chip_id(ids[1]) != CHIPID_M) { - u8 val; - - /* Check if this is a KSZ8864RMN */ - err = ks8995_read(ks, &val, KSZ8864_REG_ID1, sizeof(val)); - if (err < 0) { - dev_err(&spi->dev, - "unable to read chip id register, err=%d\n", - err); - return err; - } - if ((val & 0x80) == 0) { - dev_err(&spi->dev, "unknown chip:%02x,0\n", ids[1]); - return err; - } - ks->regs_attr.size = KSZ8864_REGS_SIZE; - } err = ks8995_reset(ks); if (err) @@ -317,14 +505,8 @@ static int ks8995_probe(struct spi_device *spi) return err; } - if (get_chip_id(ids[1]) == CHIPID_M) { - dev_info(&spi->dev, - "KS8995 device found, Chip ID:%x, Revision:%x\n", - get_chip_id(ids[1]), get_chip_rev(ids[1])); - } else { - dev_info(&spi->dev, "KSZ8864 device found, Revision:%x\n", - get_chip_rev(ids[1])); - } + dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n", + ks->chip->name, ks->chip->chip_id, ks->revision_id); return 0; } @@ -335,17 +517,21 @@ static int ks8995_remove(struct spi_device *spi) sysfs_remove_bin_file(&spi->dev.kobj, &ks->regs_attr); + /* assert reset */ + if (ks->pdata && gpio_is_valid(ks->pdata->reset_gpio)) + gpiod_set_value(gpio_to_desc(ks->pdata->reset_gpio), 1); + return 0; } /* ------------------------------------------------------------------------ */ - static struct spi_driver ks8995_driver = { .driver = { .name = "spi-ks8995", }, .probe = ks8995_probe, .remove = ks8995_remove, + .id_table = ks8995_id, }; module_spi_driver(ks8995_driver); |