mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 06:55:13 +08:00
Merge branch 'IP101GR-devicetree-based-configuration-of-SEL_INTR32'
Martin Blumenstingl says:
====================
IP101GR: devicetree based configuration of SEL_INTR32
The IP101GR is a 32-pin QFN package variant of the IP101G/IP101GA
Ethernet PHY. Due to it's limited amount of pins the RXER (receive
error) and INTR32 (interrupt) functions share pin 21.
The goal of this series is:
- some small cleanups in patches 3, 4 and 5
- allowing the kernel to detect IRQ floods on boards where the IP101GR
is configured in RXER mode but the RXER line is configured on the
host SoC as interrupt line (patch 6)
- configuration of the SEL_INTR32 register so we can use the interrupt
function on boards where the RXER/INTR32 pin (pin 21) is routed to
one of the host SoC's interrupt inputs (patches 1, 2, 7)
A use-case where this is needed is the Endless Mini (EC-100). I have
tested my changes on that board. This also confirms that Heiner
Kallweit's recent icplus.c PHY driver changes are working (at least on
my setup).
This series is based on net-next commit 7c460cf9cd
("net: aquantia:
fix spelling mistake "specfield" -> "specified"")
Changes since v1 at [0]:
- collected Andrew's Reviewed-by's (thank you!)
- updated description of patch #2 to explain why two properties were
added instead of adding an "this is a IP101GR" property
- validate that there's no conflicting configuration in patch #7
- rebased on top of latest net-next
[0] https://patchwork.ozlabs.org/cover/999371/
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
05b0e1d698
19
Documentation/devicetree/bindings/net/icplus-ip101ag.txt
Normal file
19
Documentation/devicetree/bindings/net/icplus-ip101ag.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
IC Plus Corp. IP101A / IP101G Ethernet PHYs
|
||||||
|
|
||||||
|
There are different models of the IP101G Ethernet PHY:
|
||||||
|
- IP101GR (32-pin QFN package)
|
||||||
|
- IP101G (die only, no package)
|
||||||
|
- IP101GA (48-pin LQFP package)
|
||||||
|
|
||||||
|
There are different models of the IP101A Ethernet PHY (which is the
|
||||||
|
predecessor of the IP101G):
|
||||||
|
- IP101A (48-pin LQFP package)
|
||||||
|
- IP101AH (48-pin LQFP package)
|
||||||
|
|
||||||
|
Optional properties for the IP101GR (32-pin QFN package):
|
||||||
|
|
||||||
|
- icplus,select-rx-error:
|
||||||
|
pin 21 ("RXER/INTR_32") will output the receive error status.
|
||||||
|
interrupts are not routed outside the PHY in this mode.
|
||||||
|
- icplus,select-interrupt:
|
||||||
|
pin 21 ("RXER/INTR_32") will output the interrupt signal.
|
@ -170,6 +170,7 @@ holtek Holtek Semiconductor, Inc.
|
|||||||
hwacom HwaCom Systems Inc.
|
hwacom HwaCom Systems Inc.
|
||||||
i2se I2SE GmbH
|
i2se I2SE GmbH
|
||||||
ibm International Business Machines (IBM)
|
ibm International Business Machines (IBM)
|
||||||
|
icplus IC Plus Corp.
|
||||||
idt Integrated Device Technologies, Inc.
|
idt Integrated Device Technologies, Inc.
|
||||||
ifi Ingenieurburo Fur Ic-Technologie (I/F/I)
|
ifi Ingenieurburo Fur Ic-Technologie (I/F/I)
|
||||||
ilitek ILI Technology Corporation (ILITEK)
|
ilitek ILI Technology Corporation (ILITEK)
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <linux/mii.h>
|
#include <linux/mii.h>
|
||||||
#include <linux/ethtool.h>
|
#include <linux/ethtool.h>
|
||||||
#include <linux/phy.h>
|
#include <linux/phy.h>
|
||||||
|
#include <linux/property.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
@ -36,14 +37,34 @@ MODULE_LICENSE("GPL");
|
|||||||
|
|
||||||
/* IP101A/G - IP1001 */
|
/* IP101A/G - IP1001 */
|
||||||
#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */
|
#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */
|
||||||
#define IP1001_RXPHASE_SEL (1<<0) /* Add delay on RX_CLK */
|
#define IP1001_RXPHASE_SEL BIT(0) /* Add delay on RX_CLK */
|
||||||
#define IP1001_TXPHASE_SEL (1<<1) /* Add delay on TX_CLK */
|
#define IP1001_TXPHASE_SEL BIT(1) /* Add delay on TX_CLK */
|
||||||
#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */
|
#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */
|
||||||
#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */
|
#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */
|
||||||
#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */
|
#define IP101A_G_APS_ON BIT(1) /* IP101A/G APS Mode bit */
|
||||||
#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */
|
#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */
|
||||||
#define IP101A_G_IRQ_PIN_USED BIT(15) /* INTR pin used */
|
#define IP101A_G_IRQ_PIN_USED BIT(15) /* INTR pin used */
|
||||||
#define IP101A_G_NO_IRQ BIT(11) /* IRQ's inactive */
|
#define IP101A_G_IRQ_ALL_MASK BIT(11) /* IRQ's inactive */
|
||||||
|
#define IP101A_G_IRQ_SPEED_CHANGE BIT(2)
|
||||||
|
#define IP101A_G_IRQ_DUPLEX_CHANGE BIT(1)
|
||||||
|
#define IP101A_G_IRQ_LINK_CHANGE BIT(0)
|
||||||
|
|
||||||
|
#define IP101G_DIGITAL_IO_SPEC_CTRL 0x1d
|
||||||
|
#define IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32 BIT(2)
|
||||||
|
|
||||||
|
/* The 32-pin IP101GR package can re-configure the mode of the RXER/INTR_32 pin
|
||||||
|
* (pin number 21). The hardware default is RXER (receive error) mode. But it
|
||||||
|
* can be configured to interrupt mode manually.
|
||||||
|
*/
|
||||||
|
enum ip101gr_sel_intr32 {
|
||||||
|
IP101GR_SEL_INTR32_KEEP,
|
||||||
|
IP101GR_SEL_INTR32_INTR,
|
||||||
|
IP101GR_SEL_INTR32_RXER,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ip101a_g_phy_priv {
|
||||||
|
enum ip101gr_sel_intr32 sel_intr32;
|
||||||
|
};
|
||||||
|
|
||||||
static int ip175c_config_init(struct phy_device *phydev)
|
static int ip175c_config_init(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
@ -162,21 +183,6 @@ static int ip1001_config_init(struct phy_device *phydev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ip101a_g_config_init(struct phy_device *phydev)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
|
|
||||||
c = ip1xx_reset(phydev);
|
|
||||||
if (c < 0)
|
|
||||||
return c;
|
|
||||||
|
|
||||||
/* Enable Auto Power Saving mode */
|
|
||||||
c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
|
|
||||||
c |= IP101A_G_APS_ON;
|
|
||||||
|
|
||||||
return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ip175c_read_status(struct phy_device *phydev)
|
static int ip175c_read_status(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
if (phydev->mdio.addr == 4) /* WAN port */
|
if (phydev->mdio.addr == 4) /* WAN port */
|
||||||
@ -196,6 +202,81 @@ static int ip175c_config_aneg(struct phy_device *phydev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ip101a_g_probe(struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
struct device *dev = &phydev->mdio.dev;
|
||||||
|
struct ip101a_g_phy_priv *priv;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Both functions (RX error and interrupt status) are sharing the same
|
||||||
|
* pin on the 32-pin IP101GR, so this is an exclusive choice.
|
||||||
|
*/
|
||||||
|
if (device_property_read_bool(dev, "icplus,select-rx-error") &&
|
||||||
|
device_property_read_bool(dev, "icplus,select-interrupt")) {
|
||||||
|
dev_err(dev,
|
||||||
|
"RXER and INTR mode cannot be selected together\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_property_read_bool(dev, "icplus,select-rx-error"))
|
||||||
|
priv->sel_intr32 = IP101GR_SEL_INTR32_RXER;
|
||||||
|
else if (device_property_read_bool(dev, "icplus,select-interrupt"))
|
||||||
|
priv->sel_intr32 = IP101GR_SEL_INTR32_INTR;
|
||||||
|
else
|
||||||
|
priv->sel_intr32 = IP101GR_SEL_INTR32_KEEP;
|
||||||
|
|
||||||
|
phydev->priv = priv;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ip101a_g_config_init(struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
struct ip101a_g_phy_priv *priv = phydev->priv;
|
||||||
|
int err, c;
|
||||||
|
|
||||||
|
c = ip1xx_reset(phydev);
|
||||||
|
if (c < 0)
|
||||||
|
return c;
|
||||||
|
|
||||||
|
/* configure the RXER/INTR_32 pin of the 32-pin IP101GR if needed: */
|
||||||
|
switch (priv->sel_intr32) {
|
||||||
|
case IP101GR_SEL_INTR32_RXER:
|
||||||
|
err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL,
|
||||||
|
IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32, 0);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP101GR_SEL_INTR32_INTR:
|
||||||
|
err = phy_modify(phydev, IP101G_DIGITAL_IO_SPEC_CTRL,
|
||||||
|
IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32,
|
||||||
|
IP101G_DIGITAL_IO_SPEC_CTRL_SEL_INTR32);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Don't touch IP101G_DIGITAL_IO_SPEC_CTRL because it's not
|
||||||
|
* documented on IP101A and it's not clear whether this would
|
||||||
|
* cause problems.
|
||||||
|
* For the 32-pin IP101GR we simply keep the SEL_INTR32
|
||||||
|
* configuration as set by the bootloader when not configured
|
||||||
|
* to one of the special functions.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable Auto Power Saving mode */
|
||||||
|
c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS);
|
||||||
|
c |= IP101A_G_APS_ON;
|
||||||
|
|
||||||
|
return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c);
|
||||||
|
}
|
||||||
|
|
||||||
static int ip101a_g_config_intr(struct phy_device *phydev)
|
static int ip101a_g_config_intr(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
u16 val;
|
u16 val;
|
||||||
@ -204,11 +285,23 @@ static int ip101a_g_config_intr(struct phy_device *phydev)
|
|||||||
/* INTR pin used: Speed/link/duplex will cause an interrupt */
|
/* INTR pin used: Speed/link/duplex will cause an interrupt */
|
||||||
val = IP101A_G_IRQ_PIN_USED;
|
val = IP101A_G_IRQ_PIN_USED;
|
||||||
else
|
else
|
||||||
val = IP101A_G_NO_IRQ;
|
val = IP101A_G_IRQ_ALL_MASK;
|
||||||
|
|
||||||
return phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val);
|
return phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ip101a_g_did_interrupt(struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
int val = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS);
|
||||||
|
|
||||||
|
if (val < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return val & (IP101A_G_IRQ_SPEED_CHANGE |
|
||||||
|
IP101A_G_IRQ_DUPLEX_CHANGE |
|
||||||
|
IP101A_G_IRQ_LINK_CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
static int ip101a_g_ack_interrupt(struct phy_device *phydev)
|
static int ip101a_g_ack_interrupt(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS);
|
int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS);
|
||||||
@ -242,7 +335,9 @@ static struct phy_driver icplus_driver[] = {
|
|||||||
.name = "ICPlus IP101A/G",
|
.name = "ICPlus IP101A/G",
|
||||||
.phy_id_mask = 0x0ffffff0,
|
.phy_id_mask = 0x0ffffff0,
|
||||||
.features = PHY_BASIC_FEATURES,
|
.features = PHY_BASIC_FEATURES,
|
||||||
|
.probe = ip101a_g_probe,
|
||||||
.config_intr = ip101a_g_config_intr,
|
.config_intr = ip101a_g_config_intr,
|
||||||
|
.did_interrupt = ip101a_g_did_interrupt,
|
||||||
.ack_interrupt = ip101a_g_ack_interrupt,
|
.ack_interrupt = ip101a_g_ack_interrupt,
|
||||||
.config_init = &ip101a_g_config_init,
|
.config_init = &ip101a_g_config_init,
|
||||||
.suspend = genphy_suspend,
|
.suspend = genphy_suspend,
|
||||||
|
Loading…
Reference in New Issue
Block a user