sonic-buildimage/platform/mellanox/non-upstream-patches/patches/0153-mlxsw-i2c-Add-support-for-system-events-handling.patch

199 lines
6.1 KiB
Diff
Raw Normal View History

From c5235b3c4a8ab2b758140d75a7422117e917478c Mon Sep 17 00:00:00 2001
From: Vadim Pasternak <vadimp@nvidia.com>
Date: Sun, 19 Dec 2021 09:12:58 +0000
Subject: [PATCH] mlxsw: i2c: Add support for system events handling
Extend i2c bus driver with interrupt handler to support system specific
hotplug events, related to line card state change.
Provide system IRQ line for interrupt handler. Line Id could be
provided through the platform data if available, or could be set to the
default value.
Handler is supposed to be set by "mlxsw" driver through bus driver init()
call.
Signed-off-by: Vadim Pasternak <vadimp@nvidia.com>
---
drivers/net/ethernet/mellanox/mlxsw/i2c.c | 110 ++++++++++++++++++++++
1 file changed, 110 insertions(+)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/i2c.c b/drivers/net/ethernet/mellanox/mlxsw/i2c.c
index b75416561..e5883b4e8 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/i2c.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.c
@@ -9,6 +9,7 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/platform_data/mlxreg.h>
#include <linux/slab.h>
#include "cmd.h"
@@ -51,6 +52,12 @@
#define MLXSW_I2C_TIMEOUT_MSECS 5000
#define MLXSW_I2C_MAX_DATA_SIZE 256
+#define MLXSW_I2C_WORK_ARMED 1
+#define MLXSW_I2C_WORK_CLOSED GENMASK(31, 0)
+#define MLXSW_I2C_WORK_DELAY (usecs_to_jiffies(100))
+#define MLXSW_I2C_DEFAULT_IRQ 17
+#define MLXSW_I2C_VIRT_SLAVE 0x37
+
/**
* struct mlxsw_i2c - device private data:
* @cmd: command attributes;
@@ -64,6 +71,12 @@
* @bus_info: bus info block;
* @block_size: maximum block size allowed to pass to under layer;
* @status: status to indicate chip reset or in-service update;
+ * @pdata: device platform data;
+ * @dwork_irq: interrupts delayed work queue;
+ * @lock - lock for interrupts sync;
+ * @sys_event_handler: system events handler callback;
+ * @irq: IRQ line number;
+ * @irq_unhandled_count: number of unhandled interrupts;
*/
struct mlxsw_i2c {
struct {
@@ -78,6 +91,12 @@ struct mlxsw_i2c {
struct mlxsw_bus_info bus_info;
u16 block_size;
u8 status;
+ struct mlxreg_core_hotplug_platform_data *pdata;
+ struct delayed_work dwork_irq;
+ spinlock_t lock; /* sync with interrupt */
+ void (*sys_event_handler)(struct mlxsw_core *mlxsw_core);
+ int irq;
+ atomic_t irq_unhandled_count;
};
#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \
@@ -538,6 +557,7 @@ mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
int err;
mlxsw_i2c->core = mlxsw_core;
+ mlxsw_i2c->sys_event_handler = sys_event_handler;
mbox = mlxsw_cmd_mbox_alloc();
if (!mbox)
@@ -568,6 +588,87 @@ static void mlxsw_i2c_fini(void *bus_priv)
mlxsw_i2c->core = NULL;
}
+static void mlxsw_i2c_work_handler(struct work_struct *work)
+{
+ struct mlxsw_i2c *mlxsw_i2c;
+ unsigned long flags;
+
+ mlxsw_i2c = container_of(work, struct mlxsw_i2c, dwork_irq.work);
+
+ if (atomic_read(&mlxsw_i2c->irq_unhandled_count)) {
+ if (atomic_dec_and_test(&mlxsw_i2c->irq_unhandled_count))
+ return;
+ }
+
+ mlxsw_i2c->sys_event_handler(mlxsw_i2c->core);
+
+ spin_lock_irqsave(&mlxsw_i2c->lock, flags);
+
+ /* It is possible, that some signals have been inserted, while
+ * interrupts has been masked. In this case such signals could be missed.
+ * In order to handle these signals delayed work is canceled and work task
+ * re-scheduled for immediate execution. It allows to handle missed
+ * signals, if any. In other case work handler just validates that no new
+ * signals have been received during masking.
+ */
+ cancel_delayed_work(&mlxsw_i2c->dwork_irq);
+ schedule_delayed_work(&mlxsw_i2c->dwork_irq, MLXSW_I2C_WORK_DELAY);
+
+ spin_unlock_irqrestore(&mlxsw_i2c->lock, flags);
+
+ if (!atomic_read(&mlxsw_i2c->irq_unhandled_count))
+ atomic_set(&mlxsw_i2c->irq_unhandled_count, MLXSW_I2C_WORK_ARMED);
+}
+
+static irqreturn_t mlxsw_i2c_irq_handler(int irq, void *dev)
+{
+ struct mlxsw_i2c *mlxsw_i2c = (struct mlxsw_i2c *)dev;
+
+ /* Schedule work task for immediate execution.*/
+ schedule_delayed_work(&mlxsw_i2c->dwork_irq, 0);
+
+ return IRQ_NONE;
+}
+
+static int mlxsw_i2c_event_handler_register(struct mlxsw_i2c *mlxsw_i2c)
+{
+ int err;
+
+ /* Initialize interrupt handler if system hotplug driver is reachable
+ * and platform data is available.
+ */
+ if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG))
+ return 0;
+
+ if (mlxsw_i2c->pdata && mlxsw_i2c->pdata->irq)
+ mlxsw_i2c->irq = mlxsw_i2c->pdata->irq;
+
+ if (!mlxsw_i2c->irq)
+ return 0;
+
+ err = request_irq(mlxsw_i2c->irq, mlxsw_i2c_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED, "mlxsw-i2c",
+ mlxsw_i2c);
+ if (err) {
+ dev_err(mlxsw_i2c->bus_info.dev, "Failed to request irq: %d\n",
+ err);
+ return err;
+ }
+
+ spin_lock_init(&mlxsw_i2c->lock);
+ INIT_DELAYED_WORK(&mlxsw_i2c->dwork_irq, mlxsw_i2c_work_handler);
+
+ return 0;
+}
+
+static void mlxsw_i2c_event_handler_unregister(struct mlxsw_i2c *mlxsw_i2c)
+{
+ if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG) || !mlxsw_i2c->irq)
+ return;
+ cancel_delayed_work_sync(&mlxsw_i2c->dwork_irq);
+ free_irq(mlxsw_i2c->irq, mlxsw_i2c);
+}
+
static const struct mlxsw_bus mlxsw_i2c_bus = {
.kind = "i2c",
.init = mlxsw_i2c_init,
@@ -662,6 +763,7 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
mlxsw_i2c->bus_info.dev = &client->dev;
mlxsw_i2c->bus_info.low_frequency = true;
mlxsw_i2c->dev = &client->dev;
+ mlxsw_i2c->pdata = client->dev.platform_data;
err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info,
&mlxsw_i2c_bus, mlxsw_i2c, false,
@@ -671,6 +773,12 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
return err;
}
+ if (client->addr == MLXSW_I2C_VIRT_SLAVE)
+ mlxsw_i2c->irq = MLXSW_I2C_DEFAULT_IRQ;
+ err = mlxsw_i2c_event_handler_register(mlxsw_i2c);
+ if (err)
+ return err;
+
return 0;
errout:
@@ -684,6 +792,8 @@ static int mlxsw_i2c_remove(struct i2c_client *client)
{
struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
+ atomic_set(&mlxsw_i2c->irq_unhandled_count, MLXSW_I2C_WORK_CLOSED);
+ mlxsw_i2c_event_handler_unregister(mlxsw_i2c);
mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false);
mutex_destroy(&mlxsw_i2c->cmd.lock);
--
2.30.2