sonic-buildimage/platform/mellanox/non-upstream-patches/patches/0153-mlxsw-i2c-Add-support-for-system-events-handling.patch
Vivek 0df155b014
Made non-upstream patch design order aware (#14434)
- Why I did it

Currently, non upstream patches are applied only after upstream patches.

Depends on sonic-net/sonic-linux-kernel#313. Can be merged in any order, preferably together

- What I did it

Non upstream Patches that reside in the sonic repo will not be saved in a tar file bur rather in a folder pointed out by EXTERNAL_KERNEL_PATCH_LOC. This is to make changes to the non upstream patches easily traceable.
The build variable name is also updated to INCLUDE_EXTERNAL_PATCHES
Files/folders expected under EXTERNAL_KERNEL_PATCH_LOC
EXTERNAL_KERNEL_PATCH_LOC/
       ├──── patches/
             ├── 0001-xxxxx.patch
             ├── 0001-yyyyyyyy.patch
             ├── .............
       ├──── series.patch
series.patch should contain a diff that is applied on the sonic-linux-kernel/patch/series file. The diff should include all the non-upstream patches.
How to verify it

Build the Kernel and verified if all the patches are applied properly

Signed-off-by: Vivek Reddy Karri <vkarri@nvidia.com>
2023-04-10 19:48:27 +03:00

199 lines
6.1 KiB
Diff

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