summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-11-21 14:09:01 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-21 14:09:01 -0500
commitd123cee2751836d82b170a293e86e7c7dfdd01e0 (patch)
tree8279d529db29473e66bb4e8e1fa90b20fd02faa8
parent2e95b2a8244bfd8993f55c993f1b7d33a89124e0 (diff)
parentee0dc2fbfc915b349a42ee808242321ffda6abef (diff)
downloadop-kernel-dev-d123cee2751836d82b170a293e86e7c7dfdd01e0.zip
op-kernel-dev-d123cee2751836d82b170a293e86e7c7dfdd01e0.tar.gz
Merge branch 'phy_device_type'
Johan Hovold says: ==================== net: phy: add device-type abstraction This series adds device and device-type abstractions to the micrel driver, and enables support for RMII-reference clock selection for KSZ8081 and KSZ8091 devices. While adding support for more features for the Micrel PHYs mentioned above, it became apparent that the configuration space is much too large and that adding type-specific callbacks will simply not scale. Instead I added a driver_data field to struct phy_device, which can be used to store static device type data that can be parsed and acted on in generic driver callbacks. This allows a lot of duplicated code to be removed, and should make it much easier to add new features or deal with device-type quirks in the future. The series has been tested on a dual KSZ8081 setup. Further testing on other Micrel PHYs would be much appreciated. The recent commit a95a18afe4c8 ("phy/micrel: KSZ8031RNL RMII clock reconfiguration bug") currently prevents KSZ8031 PHYs from using the generic config-init. Bruno, who is the author of that patch, has agreed to test this series and some follow-up diagnostic patches to determine how best to incorporate these devices as well. I intend to send a follow-up patch that removes the custom 8031 config-init and documents this quirk, but the current series can be applied meanwhile. These patches are against net-next which contains some already merged prerequisite patches to the driver. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/devicetree/bindings/net/micrel.txt37
-rw-r--r--drivers/net/phy/micrel.c262
-rw-r--r--include/linux/micrel_phy.h1
-rw-r--r--include/linux/phy.h2
4 files changed, 184 insertions, 118 deletions
diff --git a/Documentation/devicetree/bindings/net/micrel.txt b/Documentation/devicetree/bindings/net/micrel.txt
index 30062fa..87496a8 100644
--- a/Documentation/devicetree/bindings/net/micrel.txt
+++ b/Documentation/devicetree/bindings/net/micrel.txt
@@ -6,21 +6,32 @@ Optional properties:
- micrel,led-mode : LED mode value to set for PHYs with configurable LEDs.
- Configure the LED mode with single value. The list of PHYs and
- the bits that are currently supported:
+ Configure the LED mode with single value. The list of PHYs and the
+ bits that are currently supported:
- KSZ8001: register 0x1e, bits 15..14
- KSZ8041: register 0x1e, bits 15..14
- KSZ8021: register 0x1f, bits 5..4
- KSZ8031: register 0x1f, bits 5..4
- KSZ8051: register 0x1f, bits 5..4
- KSZ8081: register 0x1f, bits 5..4
- KSZ8091: register 0x1f, bits 5..4
+ KSZ8001: register 0x1e, bits 15..14
+ KSZ8041: register 0x1e, bits 15..14
+ KSZ8021: register 0x1f, bits 5..4
+ KSZ8031: register 0x1f, bits 5..4
+ KSZ8051: register 0x1f, bits 5..4
+ KSZ8081: register 0x1f, bits 5..4
+ KSZ8091: register 0x1f, bits 5..4
- See the respective PHY datasheet for the mode values.
+ See the respective PHY datasheet for the mode values.
+
+ - micrel,rmii-reference-clock-select-25-mhz: RMII Reference Clock Select
+ bit selects 25 MHz mode
+
+ Setting the RMII Reference Clock Select bit enables 25 MHz rather
+ than 50 MHz clock mode.
+
+ Note that this option in only needed for certain PHY revisions with a
+ non-standard, inverted function of this configuration bit.
+ Specifically, a clock reference ("rmii-ref" below) is always needed to
+ actually select a mode.
- clocks, clock-names: contains clocks according to the common clock bindings.
- supported clocks:
- - KSZ8021, KSZ8031: "rmii-ref": The RMII refence input clock. Used
- to determine the XI input clock.
+ supported clocks:
+ - KSZ8021, KSZ8031, KSZ8081, KSZ8091: "rmii-ref": The RMII reference
+ input clock. Used to determine the XI input clock.
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 30e894d..c530de1 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -6,6 +6,7 @@
* Author: David J. Choi
*
* Copyright (c) 2010-2013 Micrel, Inc.
+ * Copyright (c) 2014 Johan Hovold <johan@kernel.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -55,9 +56,7 @@
#define MII_KSZPHY_CTRL MII_KSZPHY_CTRL_2
/* bitmap of PHY register to set interrupt mode */
#define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9)
-#define KSZ9021_CTRL_INT_ACTIVE_HIGH BIT(14)
-#define KS8737_CTRL_INT_ACTIVE_HIGH BIT(14)
-#define KSZ8051_RMII_50MHZ_CLK BIT(7)
+#define KSZPHY_RMII_REF_CLK_SEL BIT(7)
/* Write/read to/from extended registers */
#define MII_KSZPHY_EXTREG 0x0b
@@ -73,20 +72,46 @@
#define PS_TO_REG 200
-static int ksz_config_flags(struct phy_device *phydev)
-{
- int regval;
+struct kszphy_type {
+ u32 led_mode_reg;
+ u16 interrupt_level_mask;
+ bool has_broadcast_disable;
+ bool has_rmii_ref_clk_sel;
+};
- if (phydev->dev_flags & (MICREL_PHY_50MHZ_CLK | MICREL_PHY_25MHZ_CLK)) {
- regval = phy_read(phydev, MII_KSZPHY_CTRL);
- if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK)
- regval |= KSZ8051_RMII_50MHZ_CLK;
- else
- regval &= ~KSZ8051_RMII_50MHZ_CLK;
- return phy_write(phydev, MII_KSZPHY_CTRL, regval);
- }
- return 0;
-}
+struct kszphy_priv {
+ const struct kszphy_type *type;
+ int led_mode;
+ bool rmii_ref_clk_sel;
+ bool rmii_ref_clk_sel_val;
+};
+
+static const struct kszphy_type ksz8021_type = {
+ .led_mode_reg = MII_KSZPHY_CTRL_2,
+ .has_rmii_ref_clk_sel = true,
+};
+
+static const struct kszphy_type ksz8041_type = {
+ .led_mode_reg = MII_KSZPHY_CTRL_1,
+};
+
+static const struct kszphy_type ksz8051_type = {
+ .led_mode_reg = MII_KSZPHY_CTRL_2,
+};
+
+static const struct kszphy_type ksz8081_type = {
+ .led_mode_reg = MII_KSZPHY_CTRL_2,
+ .has_broadcast_disable = true,
+ .has_rmii_ref_clk_sel = true,
+};
+
+static const struct kszphy_type ks8737_type = {
+ .interrupt_level_mask = BIT(14),
+};
+
+static const struct kszphy_type ksz9021_type = {
+ .interrupt_level_mask = BIT(14),
+};
static int kszphy_extended_write(struct phy_device *phydev,
u32 regnum, u16 val)
@@ -112,74 +137,52 @@ static int kszphy_ack_interrupt(struct phy_device *phydev)
return (rc < 0) ? rc : 0;
}
-static int kszphy_set_interrupt(struct phy_device *phydev)
+static int kszphy_config_intr(struct phy_device *phydev)
{
+ const struct kszphy_type *type = phydev->drv->driver_data;
int temp;
- temp = (PHY_INTERRUPT_ENABLED == phydev->interrupts) ?
- KSZPHY_INTCS_ALL : 0;
- return phy_write(phydev, MII_KSZPHY_INTCS, temp);
-}
+ u16 mask;
-static int kszphy_config_intr(struct phy_device *phydev)
-{
- int temp, rc;
+ if (type && type->interrupt_level_mask)
+ mask = type->interrupt_level_mask;
+ else
+ mask = KSZPHY_CTRL_INT_ACTIVE_HIGH;
/* set the interrupt pin active low */
temp = phy_read(phydev, MII_KSZPHY_CTRL);
if (temp < 0)
return temp;
- temp &= ~KSZPHY_CTRL_INT_ACTIVE_HIGH;
+ temp &= ~mask;
phy_write(phydev, MII_KSZPHY_CTRL, temp);
- rc = kszphy_set_interrupt(phydev);
- return rc < 0 ? rc : 0;
-}
-static int ksz9021_config_intr(struct phy_device *phydev)
-{
- int temp, rc;
+ /* enable / disable interrupts */
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ temp = KSZPHY_INTCS_ALL;
+ else
+ temp = 0;
- /* set the interrupt pin active low */
- temp = phy_read(phydev, MII_KSZPHY_CTRL);
- if (temp < 0)
- return temp;
- temp &= ~KSZ9021_CTRL_INT_ACTIVE_HIGH;
- phy_write(phydev, MII_KSZPHY_CTRL, temp);
- rc = kszphy_set_interrupt(phydev);
- return rc < 0 ? rc : 0;
+ return phy_write(phydev, MII_KSZPHY_INTCS, temp);
}
-static int ks8737_config_intr(struct phy_device *phydev)
+static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val)
{
- int temp, rc;
+ int ctrl;
- /* set the interrupt pin active low */
- temp = phy_read(phydev, MII_KSZPHY_CTRL);
- if (temp < 0)
- return temp;
- temp &= ~KS8737_CTRL_INT_ACTIVE_HIGH;
- phy_write(phydev, MII_KSZPHY_CTRL, temp);
- rc = kszphy_set_interrupt(phydev);
- return rc < 0 ? rc : 0;
+ ctrl = phy_read(phydev, MII_KSZPHY_CTRL);
+ if (ctrl < 0)
+ return ctrl;
+
+ if (val)
+ ctrl |= KSZPHY_RMII_REF_CLK_SEL;
+ else
+ ctrl &= ~KSZPHY_RMII_REF_CLK_SEL;
+
+ return phy_write(phydev, MII_KSZPHY_CTRL, ctrl);
}
-static int kszphy_setup_led(struct phy_device *phydev, u32 reg)
+static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
{
-
- struct device *dev = &phydev->dev;
- struct device_node *of_node = dev->of_node;
int rc, temp, shift;
- u32 val;
-
- if (!of_node && dev->parent->of_node)
- of_node = dev->parent->of_node;
-
- if (of_property_read_u32(of_node, "micrel,led-mode", &val))
- return 0;
-
- if (val > 3) {
- dev_err(&phydev->dev, "invalid led mode: 0x%02x\n", val);
- return -EINVAL;
- }
switch (reg) {
case MII_KSZPHY_CTRL_1:
@@ -229,45 +232,43 @@ out:
static int kszphy_config_init(struct phy_device *phydev)
{
- return 0;
-}
+ struct kszphy_priv *priv = phydev->priv;
+ const struct kszphy_type *type;
+ int ret;
-static int kszphy_config_init_led8041(struct phy_device *phydev)
-{
- return kszphy_setup_led(phydev, MII_KSZPHY_CTRL_1);
-}
+ if (!priv)
+ return 0;
-static int ksz8021_config_init(struct phy_device *phydev)
-{
- int rc;
+ type = priv->type;
- kszphy_setup_led(phydev, MII_KSZPHY_CTRL_2);
+ if (type->has_broadcast_disable)
+ kszphy_broadcast_disable(phydev);
- rc = ksz_config_flags(phydev);
- if (rc < 0)
- return rc;
+ if (priv->rmii_ref_clk_sel) {
+ ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val);
+ if (ret) {
+ dev_err(&phydev->dev, "failed to set rmii reference clock\n");
+ return ret;
+ }
+ }
- rc = kszphy_broadcast_disable(phydev);
+ if (priv->led_mode >= 0)
+ kszphy_setup_led(phydev, type->led_mode_reg, priv->led_mode);
- return rc < 0 ? rc : 0;
+ return 0;
}
-static int ks8051_config_init(struct phy_device *phydev)
+static int ksz8021_config_init(struct phy_device *phydev)
{
int rc;
- kszphy_setup_led(phydev, MII_KSZPHY_CTRL_2);
-
- rc = ksz_config_flags(phydev);
- return rc < 0 ? rc : 0;
-}
+ rc = kszphy_config_init(phydev);
+ if (rc)
+ return rc;
-static int ksz8081_config_init(struct phy_device *phydev)
-{
- kszphy_broadcast_disable(phydev);
- kszphy_setup_led(phydev, MII_KSZPHY_CTRL_2);
+ rc = kszphy_broadcast_disable(phydev);
- return 0;
+ return rc < 0 ? rc : 0;
}
static int ksz9021_load_values_from_of(struct phy_device *phydev,
@@ -499,24 +500,62 @@ ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
{
}
-static int ksz8021_probe(struct phy_device *phydev)
+static int kszphy_probe(struct phy_device *phydev)
{
+ const struct kszphy_type *type = phydev->drv->driver_data;
+ struct device_node *np = phydev->dev.of_node;
+ struct kszphy_priv *priv;
struct clk *clk;
+ int ret;
+
+ priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ priv->type = type;
+
+ if (type->led_mode_reg) {
+ ret = of_property_read_u32(np, "micrel,led-mode",
+ &priv->led_mode);
+ if (ret)
+ priv->led_mode = -1;
+
+ if (priv->led_mode > 3) {
+ dev_err(&phydev->dev, "invalid led mode: 0x%02x\n",
+ priv->led_mode);
+ priv->led_mode = -1;
+ }
+ } else {
+ priv->led_mode = -1;
+ }
clk = devm_clk_get(&phydev->dev, "rmii-ref");
if (!IS_ERR(clk)) {
unsigned long rate = clk_get_rate(clk);
+ bool rmii_ref_clk_sel_25_mhz;
+
+ priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
+ rmii_ref_clk_sel_25_mhz = of_property_read_bool(np,
+ "micrel,rmii-reference-clock-select-25-mhz");
if (rate > 24500000 && rate < 25500000) {
- phydev->dev_flags |= MICREL_PHY_25MHZ_CLK;
+ priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz;
} else if (rate > 49500000 && rate < 50500000) {
- phydev->dev_flags |= MICREL_PHY_50MHZ_CLK;
+ priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
} else {
dev_err(&phydev->dev, "Clock rate out of range: %ld\n", rate);
return -EINVAL;
}
}
+ /* Support legacy board-file configuration */
+ if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
+ priv->rmii_ref_clk_sel = true;
+ priv->rmii_ref_clk_sel_val = true;
+ }
+
return 0;
}
@@ -527,11 +566,12 @@ static struct phy_driver ksphy_driver[] = {
.name = "Micrel KS8737",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .driver_data = &ks8737_type,
.config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
- .config_intr = ks8737_config_intr,
+ .config_intr = kszphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
.driver = { .owner = THIS_MODULE,},
@@ -542,7 +582,8 @@ static struct phy_driver ksphy_driver[] = {
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .probe = ksz8021_probe,
+ .driver_data = &ksz8021_type,
+ .probe = kszphy_probe,
.config_init = ksz8021_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -558,7 +599,8 @@ static struct phy_driver ksphy_driver[] = {
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .probe = ksz8021_probe,
+ .driver_data = &ksz8021_type,
+ .probe = kszphy_probe,
.config_init = ksz8021_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
@@ -574,7 +616,9 @@ static struct phy_driver ksphy_driver[] = {
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = kszphy_config_init_led8041,
+ .driver_data = &ksz8041_type,
+ .probe = kszphy_probe,
+ .config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -589,7 +633,9 @@ static struct phy_driver ksphy_driver[] = {
.features = PHY_BASIC_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = kszphy_config_init_led8041,
+ .driver_data = &ksz8041_type,
+ .probe = kszphy_probe,
+ .config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -604,7 +650,9 @@ static struct phy_driver ksphy_driver[] = {
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = ks8051_config_init,
+ .driver_data = &ksz8051_type,
+ .probe = kszphy_probe,
+ .config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -618,7 +666,9 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = 0x00ffffff,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = kszphy_config_init_led8041,
+ .driver_data = &ksz8041_type,
+ .probe = kszphy_probe,
+ .config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -632,7 +682,9 @@ static struct phy_driver ksphy_driver[] = {
.phy_id_mask = 0x00fffff0,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
- .config_init = ksz8081_config_init,
+ .driver_data = &ksz8081_type,
+ .probe = kszphy_probe,
+ .config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
@@ -660,11 +712,12 @@ static struct phy_driver ksphy_driver[] = {
.name = "Micrel KSZ9021 Gigabit PHY",
.features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .driver_data = &ksz9021_type,
.config_init = ksz9021_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
- .config_intr = ksz9021_config_intr,
+ .config_intr = kszphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
.read_mmd_indirect = ksz9021_rd_mmd_phyreg,
@@ -676,11 +729,12 @@ static struct phy_driver ksphy_driver[] = {
.name = "Micrel KSZ9031 Gigabit PHY",
.features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .driver_data = &ksz9021_type,
.config_init = ksz9031_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
- .config_intr = ksz9021_config_intr,
+ .config_intr = kszphy_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
.driver = { .owner = THIS_MODULE, },
diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h
index 53d33de..2e5b194 100644
--- a/include/linux/micrel_phy.h
+++ b/include/linux/micrel_phy.h
@@ -37,7 +37,6 @@
/* struct phy_device dev_flags definitions */
#define MICREL_PHY_50MHZ_CLK 0x00000001
-#define MICREL_PHY_25MHZ_CLK 0x00000002
#define MICREL_KSZ9021_EXTREG_CTRL 0xB
#define MICREL_KSZ9021_EXTREG_DATA_WRITE 0xC
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 07794e7..22af8f8 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -433,6 +433,7 @@ struct phy_device {
* by this PHY
* flags: A bitfield defining certain other features this PHY
* supports (like interrupts)
+ * driver_data: static driver data
*
* The drivers must implement config_aneg and read_status. All
* other functions are optional. Note that none of these
@@ -448,6 +449,7 @@ struct phy_driver {
unsigned int phy_id_mask;
u32 features;
u32 flags;
+ const void *driver_data;
/*
* Called to issue a PHY software reset
OpenPOWER on IntegriCloud