@@ -41,6 +41,8 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c,
if (i2c->dev.platform_data)
dev->pdata = i2c->dev.platform_data;
+ dev->irq = i2c->irq;
+
ret = ksz9477_switch_register(dev);
/* Main DSA driver may not be started yet. */
@@ -7,7 +7,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/interrupt.h>
#include <linux/iopoll.h>
+#include <linux/irq.h>
#include <linux/platform_data/microchip-ksz.h>
#include <linux/phy.h>
#include <linux/if_bridge.h>
@@ -1345,19 +1347,12 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds)
static int ksz9477_setup(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
- int ret = 0;
dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
dev->num_vlans, GFP_KERNEL);
if (!dev->vlan_cache)
return -ENOMEM;
- ret = ksz9477_reset_switch(dev);
- if (ret) {
- dev_err(ds->dev, "failed to reset switch\n");
- return ret;
- }
-
/* Required for port partitioning. */
ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY,
true);
@@ -1535,12 +1530,84 @@ static const struct ksz_chip_data ksz9477_switch_chips[] = {
},
};
+static irqreturn_t ksz9477_switch_irq_thread(int irq, void *dev_id)
+{
+ struct ksz_device *dev = dev_id;
+ u32 data;
+ int port;
+ int ret;
+ irqreturn_t result = IRQ_NONE;
+
+ /* Read global port interrupt status register */
+ ret = ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data);
+ if (ret)
+ return result;
+
+ for (port = 0; port < dev->port_cnt; port++) {
+ if (data & BIT(port)) {
+ u8 data8;
+
+ /* Read port interrupt status register */
+ ret = ksz_read8(dev, PORT_CTRL_ADDR(port, REG_PORT_INT_STATUS),
+ &data8);
+ if (ret)
+ return result;
+
+ /* ToDo: Add specific handling of port interrupts */
+ }
+ }
+
+ return result;
+}
+
+static int ksz9477_enable_port_interrupts(struct ksz_device *dev)
+{
+ u32 data;
+ int ret;
+
+ ret = ksz_read32(dev, REG_SW_PORT_INT_MASK__4, &data);
+ if (ret)
+ return ret;
+
+ /* Enable port interrupts (0 means enabled) */
+ data &= ~((1 << dev->port_cnt) - 1);
+ ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ksz9477_disable_port_interrupts(struct ksz_device *dev)
+{
+ u32 data;
+ int ret;
+
+ ret = ksz_read32(dev, REG_SW_PORT_INT_MASK__4, &data);
+ if (ret)
+ return ret;
+
+ /* Disable port interrupts (1 means disabled) */
+ data |= ((1 << dev->port_cnt) - 1);
+ ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int ksz9477_switch_init(struct ksz_device *dev)
{
- int i;
+ int i, ret;
dev->ds->ops = &ksz9477_switch_ops;
+ ret = ksz9477_reset_switch(dev);
+ if (ret) {
+ dev_err(dev->dev, "failed to reset switch\n");
+ return ret;
+ }
+
for (i = 0; i < ARRAY_SIZE(ksz9477_switch_chips); i++) {
const struct ksz_chip_data *chip = &ksz9477_switch_chips[i];
@@ -1584,12 +1651,32 @@ static int ksz9477_switch_init(struct ksz_device *dev)
/* set the real number of ports */
dev->ds->num_ports = dev->port_cnt;
+ if (dev->irq > 0) {
+ unsigned long irqflags = irqd_get_trigger_type(irq_get_irq_data(dev->irq));
+
+ irqflags |= IRQF_ONESHOT;
+ ret = devm_request_threaded_irq(dev->dev, dev->irq, NULL,
+ ksz9477_switch_irq_thread,
+ irqflags,
+ dev_name(dev->dev),
+ dev);
+ if (ret) {
+ dev_err(dev->dev, "failed to request IRQ.\n");
+ return ret;
+ }
+
+ ret = ksz9477_enable_port_interrupts(dev);
+ if (ret)
+ return ret;
+ }
return 0;
}
static void ksz9477_switch_exit(struct ksz_device *dev)
{
+ if (dev->irq > 0)
+ ksz9477_disable_port_interrupts(dev);
ksz9477_reset_switch(dev);
}
@@ -48,6 +48,8 @@ static int ksz9477_spi_probe(struct spi_device *spi)
if (spi->dev.platform_data)
dev->pdata = spi->dev.platform_data;
+ dev->irq = spi->irq;
+
ret = ksz9477_switch_register(dev);
/* Main DSA driver may not be started yet. */
@@ -55,6 +55,7 @@ struct ksz_device {
struct device *dev;
struct regmap *regmap[3];
+ int irq;
void *priv;
Interrupts are required for TX time stamping. Probably they could also be used for PHY connection status. This patch only adds the basic infrastructure for interrupts, no interrupts are actually enabled nor handled. ksz9477_reset_switch() must be called before requesting the IRQ (in ksz9477_init() instead of ksz9477_setup()). Signed-off-by: Christian Eggers <ceggers@arri.de> --- drivers/net/dsa/microchip/ksz9477_i2c.c | 2 + drivers/net/dsa/microchip/ksz9477_main.c | 103 +++++++++++++++++++++-- drivers/net/dsa/microchip/ksz9477_spi.c | 2 + include/linux/dsa/ksz_common.h | 1 + 4 files changed, 100 insertions(+), 8 deletions(-)