aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/bcm7xxx.c60
-rw-r--r--drivers/net/phy/dp83848.c88
-rw-r--r--drivers/net/phy/marvell.c52
-rw-r--r--drivers/net/phy/micrel.c9
-rw-r--r--drivers/net/phy/spi_ks8995.c304
5 files changed, 369 insertions, 144 deletions
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index db507e3bcab9..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,7 +247,7 @@ 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);
/* set shadow mode 2 */
@@ -317,6 +317,21 @@ static int bcm7xxx_suspend(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"),
@@ -324,43 +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_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,
-}, {
- .phy_id = PHY_ID_BCM7429,
- .phy_id_mask = 0xfffffff0,
- .name = "Broadcom BCM7429",
- .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,
-}, {
- .phy_id = PHY_ID_BCM7435,
- .phy_id_mask = 0xfffffff0,
- .name = "Broadcom BCM7435",
- .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,
-} };
+ 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, },
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 ab1d0fcaf1d9..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");
@@ -631,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;
@@ -1031,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,
@@ -1042,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,
@@ -1268,7 +1308,7 @@ static struct phy_driver marvell_drivers[] = {
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.probe = marvell_probe,
- .config_init = &marvell_config_init,
+ .config_init = &m88e1510_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 dc85f7095e51..4516c8a4fd82 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -623,18 +623,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/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);