Add extra cycle for hotplug work queue to handle the case when a signal is
It adds missed logic for signal acknowledge, by adding an extra run for
received, but no specific signal assertion is detected. Such case
theoretically can happen for example in case several units are removed or
inserted at the same time. In this situation acknowledge for some signal
can be missed at signal top aggreagation status level. The extra run will
allow to handler to acknowledge the missed signal.
The interrupt handling flow performs the next steps:
(1)
Enter mlxreg_hotplug_work_handler due to signal assertion.
Aggregation status register is changed for example from 0xff to 0xfd
(event signal group related to bit 1).
(2)
Mask aggregation interrupts, read aggregation status register and save it
(0xfd) in aggr_cache, then traverse down to handle signal from groups
related to the changed bit.
(3)
Read and mask group related signal.
Acknowledge and unmask group related signal (acknowledge should clear
aggregation status register from 0xfd back to 0xff).
(4)
Re-schedule work queue for the immediate execution.
(5)
Enter mlxreg_hotplug_work_handler due to re-scheduling.
Aggregation status is changed from previous 0xfd to 0xff.
Go over steps (2) - (5) and in case no new signal assertion
is detected - unmask aggregation interrupts.
The possible race could happen in case new signal from the same group is
asserted after step (3) and prior step (5). In such case aggregation
status will change back from 0xff to 0xfd and the value read from the
aggregation status register will be the same as a value saved in
aggr_cache. As a result the handler will not traverse down and signal
will stay unhandled.
Example of faulty flow:
The signal routing flow is as following (f.e. for of FANi removing):
- FAN status and event registers related bit is changed;
-- intermediate aggregation status register is changed;
--- top aggregation status register is changed;
---- interrupt routed to CPU and interrupt handler is invoked.
When interrupt handler is invoked it follows the next simple logic (f.e
FAN3 is removed):
(a1) mask top aggregation interrupt mask register;
(a2) read top aggregation interrupt status register and test to which
underling group belongs a signal (FANs in this case and is changed
from 0xff to 0xfb and 0xfb is saved as a last status value);
(b1) mask FANs interrupt mask register;
(b2) read FANs status register and test which FAN has been changed
FAN3 in this example);
(c1) perform relevant action;
<--------------- (FAN2 is removed at this point)
(b3) clear FANs interrupt event register to acknowledge FAN3 signal;
(b4) unmask FANs interrupt mask register
(a3) unmask top aggregation interrupt mask register;
An interrupt handler is invoked, since FAN2 interrupt is not acknowledge.
It should set top aggregation interrupt status register bit 6 (0xfb).
In step (a2)
(a2) read top aggregation interrupt and comparing it with saved value
does not show change (same 0xfb) and after (a2) execution jumps to
(a3) and signal leaved unhandled
The fix will enforce handler to traverse down in case the signal is
received, but signal assertion is not detected.
Fixes: 07b89c2b2a5e ("platform/x86: Introduce support for Mellanox hotplug driver")
Signed-off-by: Vadim Pasternak <[email protected]>
---
V1->v2:
Comments pointed out by Darren:
- Modify commit message;
- Remove redundant check of aggr_asserted;
- Change bug fix reference from 1f976f6978bf, which just relocated driver
to 07b89c2b2a5e, which was initial driver commit.
---
drivers/platform/mellanox/mlxreg-hotplug.c | 48 +++++++++++++++++++-----------
1 file changed, 31 insertions(+), 17 deletions(-)
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
index b56953a..922913b 100644
--- a/drivers/platform/mellanox/mlxreg-hotplug.c
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c
@@ -55,6 +55,7 @@
#define MLXREG_HOTPLUG_RST_CNTR 3
#define MLXREG_HOTPLUG_ATTRS_MAX 24
+#define MLXREG_HOTPLUG_NOT_ASSERT 3
/**
* struct mlxreg_hotplug_priv_data - platform private data:
@@ -74,6 +75,7 @@
* @mask: top aggregation interrupt common mask;
* @aggr_cache: last value of aggregation register status;
* @after_probe: flag indication probing completion;
+ * @not_asserted: number of entries in workqueue with no signal assertion;
*/
struct mlxreg_hotplug_priv_data {
int irq;
@@ -93,6 +95,7 @@ struct mlxreg_hotplug_priv_data {
u32 mask;
u32 aggr_cache;
bool after_probe;
+ u8 not_asserted;
};
static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
@@ -410,6 +413,18 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work)
aggr_asserted = priv->aggr_cache ^ regval;
priv->aggr_cache = regval;
+ /*
+ * Handler is invoked, but no assertion is detected at top aggregation
+ * status level. Set aggr_asserted to mask value to allow handler extra
+ * run over all relevant signals to recover any missed signal.
+ */
+ if (priv->not_asserted == MLXREG_HOTPLUG_NOT_ASSERT) {
+ priv->not_asserted = 0;
+ aggr_asserted = pdata->mask;
+ }
+ if (!aggr_asserted)
+ goto unmask_event;
+
/* Handle topology and health configuration changes. */
for (i = 0; i < pdata->counter; i++, item++) {
if (aggr_asserted & item->aggr_mask) {
@@ -420,27 +435,26 @@ static void mlxreg_hotplug_work_handler(struct work_struct *work)
}
}
- if (aggr_asserted) {
- spin_lock_irqsave(&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
- /*
- * It is possible, that some signals have been inserted, while
- * interrupt has been masked by mlxreg_hotplug_work_handler.
- * In this case such signals will 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(&priv->dwork_irq);
- schedule_delayed_work(&priv->dwork_irq, 0);
+ /*
+ * It is possible, that some signals have been inserted, while
+ * interrupt has been masked by mlxreg_hotplug_work_handler. In this
+ * case such signals will 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(&priv->dwork_irq);
+ schedule_delayed_work(&priv->dwork_irq, 0);
- spin_unlock_irqrestore(&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
- return;
- }
+ return;
+unmask_event:
+ priv->not_asserted++;
/* Unmask aggregation event (no need acknowledge). */
ret = regmap_write(priv->regmap, pdata->cell +
MLXREG_HOTPLUG_AGGR_MASK_OFF, pdata->mask);
--
2.1.4
Add LED platform driver activation from mlx-platform. This LED driver uses
the same regmap infrastructure as others Mellanox platform drivers, so LED
specific registers description is added.
System LED configuration depends on system type. To support all the
relevant types per system type LED descriptions are defined for passing
to LED platform driver.
Signed-off-by: Vadim Pasternak <[email protected]>
v1->v2:
Fixes added by Vadim:
- Remove inline functions from mlxreg.h file;
---
drivers/platform/x86/mlx-platform.c | 259 +++++++++++++++++++++++++++++++++++-
1 file changed, 258 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 912f844..a0fd9aa 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -47,6 +47,11 @@
/* LPC bus IO offsets */
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000
#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500
+#define MLXPLAT_CPLD_LPC_REG_LED1_OFFSET 0x20
+#define MLXPLAT_CPLD_LPC_REG_LED2_OFFSET 0x21
+#define MLXPLAT_CPLD_LPC_REG_LED3_OFFSET 0x22
+#define MLXPLAT_CPLD_LPC_REG_LED4_OFFSET 0x23
+#define MLXPLAT_CPLD_LPC_REG_LED5_OFFSET 0x24
#define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a
#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40
@@ -84,6 +89,8 @@
#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0)
+#define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4)
+#define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0)
/* Default I2C parent bus number */
#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1
@@ -114,11 +121,13 @@
* @pdev_i2c - i2c controller platform device
* @pdev_mux - array of mux platform devices
* @pdev_hotplug - hotplug platform devices
+ * @pdev_led - led platform devices
*/
struct mlxplat_priv {
struct platform_device *pdev_i2c;
struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
struct platform_device *pdev_hotplug;
+ struct platform_device *pdev_led;
};
/* Regions for LPC I2C controller and LPC base register space */
@@ -592,9 +601,227 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = {
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
+/* Platform led default data */
+static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = {
+ {
+ .label = "status:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "status:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
+ },
+ {
+ .label = "psu:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "psu:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_default_led_data = {
+ .data = mlxplat_mlxcpld_default_led_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_data),
+};
+
+/* Platform led MSN21xx system family data */
+static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = {
+ {
+ .label = "status:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "status:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
+ },
+ {
+ .label = "fan:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "psu1:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "psu1:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "psu2:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "psu2:red",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "uid:blue",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_msn21xx_led_data = {
+ .data = mlxplat_mlxcpld_msn21xx_led_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn21xx_led_data),
+};
+
+/* Platform led for default data for 200GbE systems */
+static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = {
+ {
+ .label = "status:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "status:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK
+ },
+ {
+ .label = "psu:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "psu:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan1:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan2:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan3:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan4:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan5:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan5:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK,
+ },
+ {
+ .label = "fan6:green",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+ {
+ .label = "fan6:orange",
+ .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET,
+ .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_default_ng_led_data = {
+ .data = mlxplat_mlxcpld_default_ng_led_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_led_data),
+};
+
+
static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
@@ -611,6 +838,11 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
@@ -632,6 +864,11 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
@@ -692,6 +929,7 @@ static struct resource mlxplat_mlxcpld_resources[] = {
static struct platform_device *mlxplat_dev;
static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
+static struct mlxreg_core_platform_data *mlxplat_led;
static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
{
@@ -705,6 +943,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_default_led_data;
return 1;
};
@@ -721,6 +960,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_msn21xx_led_data;
return 1;
};
@@ -737,6 +977,7 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_default_led_data;
return 1;
};
@@ -753,6 +994,7 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_default_ng_led_data;
return 1;
};
@@ -769,6 +1011,7 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
+ mlxplat_led = &mlxplat_msn21xx_led_data;
return 1;
};
@@ -990,14 +1233,27 @@ static int __init mlxplat_init(void)
goto fail_platform_mux_register;
}
+ /* Add LED driver. */
+ mlxplat_led->regmap = mlxplat_hotplug->regmap;
+ priv->pdev_led = platform_device_register_resndata(
+ &mlxplat_dev->dev, "leds-mlxreg",
+ PLATFORM_DEVID_NONE, NULL, 0,
+ mlxplat_led, sizeof(*mlxplat_led));
+ if (IS_ERR(priv->pdev_led)) {
+ err = PTR_ERR(priv->pdev_led);
+ goto fail_platform_hotplug_register;
+ }
+
/* Sync registers with hardware. */
regcache_mark_dirty(mlxplat_hotplug->regmap);
err = regcache_sync(mlxplat_hotplug->regmap);
if (err)
- goto fail_platform_hotplug_register;
+ goto fail_platform_led_register;
return 0;
+fail_platform_led_register:
+ platform_device_unregister(priv->pdev_led);
fail_platform_hotplug_register:
platform_device_unregister(priv->pdev_hotplug);
fail_platform_mux_register:
@@ -1016,6 +1272,7 @@ static void __exit mlxplat_exit(void)
struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
int i;
+ platform_device_unregister(priv->pdev_led);
platform_device_unregister(priv->pdev_hotplug);
for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--)
--
2.1.4
Introduce new Mellanox platform driver to allow access to Mellanox
programmable device register space trough sysfs interface.
The driver purpose is to provide sysfs interface for user space for the
registers essential for system control and monitoring.
The sets of registers for sysfs access are supposed to be defined per
system type bases and include the registers related to system resets
operation, system reset causes monitoring and some kinds of mux selection.
Signed-off-by: Vadim Pasternak <[email protected]>
---
v1->v2:
Changed added by Vadim:
- Change ---help--- to help in Kconfig, according to new requirements;
---
drivers/platform/mellanox/Kconfig | 11 ++
drivers/platform/mellanox/Makefile | 1 +
drivers/platform/mellanox/mlxreg-io.c | 221 ++++++++++++++++++++++++++++++++++
3 files changed, 233 insertions(+)
create mode 100644 drivers/platform/mellanox/mlxreg-io.c
diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index 591bccd..ddfae9fc 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -23,4 +23,15 @@ config MLXREG_HOTPLUG
This driver handles hot-plug events for the power suppliers, power
cables and fans on the wide range Mellanox IB and Ethernet systems.
+config MLXREG_IO
+ tristate "Mellanox platform register access driver support"
+ depends on REGMAP
+ depends on HWMON
+ help
+ This driver allows access to Mellanox programmable device register
+ space trough sysfs interface. The sets of registers for sysfs access
+ are defined per system type bases and includes the registers related
+ to system resets operation, system reset causes monitoring and some
+ kinds of mux selection.
+
endif # MELLANOX_PLATFORM
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index 7c8385e..57074d9c 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -4,3 +4,4 @@
# Mellanox Platform-Specific Drivers
#
obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o
+obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c
new file mode 100644
index 0000000..f573b65
--- /dev/null
+++ b/drivers/platform/mellanox/mlxreg-io.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Mellanox register access driver
+ *
+ * Copyright (C) 2018 Mellanox Technologies
+ * Copyright (C) 2018 Vadim Pasternak <[email protected]>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* Attribute parameters. */
+#define MLXREG_IO_ATT_SIZE 10
+#define MLXREG_IO_ATT_NUM 48
+
+/**
+ * struct mlxreg_io_priv_data - driver's private data:
+ *
+ * @pdev: platform device;
+ * @pdata: platform data;
+ * @hwmon: hwmon device;
+ * @mlxreg_io_attr: sysfs attributes array;
+ * @mlxreg_io_dev_attr: sysfs sensor device attribute array;
+ * @group: sysfs attribute group;
+ * @groups: list of sysfs attribute group for hwmon registration;
+ */
+struct mlxreg_io_priv_data {
+ struct platform_device *pdev;
+ struct mlxreg_core_platform_data *pdata;
+ struct device *hwmon;
+ struct attribute *mlxreg_io_attr[MLXREG_IO_ATT_NUM + 1];
+ struct sensor_device_attribute mlxreg_io_dev_attr[MLXREG_IO_ATT_NUM];
+ struct attribute_group group;
+ const struct attribute_group *groups[2];
+};
+
+static ssize_t
+mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct mlxreg_core_data *data = priv->pdata->data + index;
+ u32 regval = 0;
+ int ret;
+
+ ret = regmap_read(priv->pdata->regmap, data->reg, ®val);
+ if (ret)
+ goto access_error;
+
+ if (!data->bit)
+ regval = !!(regval & ~data->mask);
+
+ return sprintf(buf, "%u\n", regval);
+
+access_error:
+ return ret;
+}
+
+static ssize_t
+mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct mlxreg_core_data *data = priv->pdata->data + index;
+ u32 val, regval;
+ int ret;
+
+ ret = kstrtou32(buf, MLXREG_IO_ATT_SIZE, &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->pdata->regmap, data->reg, ®val);
+ if (ret)
+ goto access_error;
+
+ regval &= data->mask;
+
+ val = !!val;
+ if (val)
+ regval |= ~data->mask;
+ else
+ regval &= data->mask;
+
+ ret = regmap_write(priv->pdata->regmap, data->reg, regval);
+ if (ret)
+ goto access_error;
+
+ return len;
+
+access_error:
+ dev_err(&priv->pdev->dev, "Bus access error\n");
+ return ret;
+}
+
+static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv)
+{
+ int i;
+
+ priv->group.attrs = devm_kzalloc(&priv->pdev->dev,
+ priv->pdata->counter *
+ sizeof(struct attribute *),
+ GFP_KERNEL);
+ if (!priv->group.attrs)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->pdata->counter; i++) {
+ priv->mlxreg_io_attr[i] =
+ &priv->mlxreg_io_dev_attr[i].dev_attr.attr;
+
+ /* Set attribute name as a label. */
+ priv->mlxreg_io_attr[i]->name =
+ devm_kasprintf(&priv->pdev->dev, GFP_KERNEL,
+ priv->pdata->data[i].label);
+
+ if (!priv->mlxreg_io_attr[i]->name) {
+ dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n",
+ i + 1);
+ return -ENOMEM;
+ }
+
+ priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode =
+ priv->pdata->data[i].mode;
+ switch (priv->pdata->data[i].mode) {
+ case 0200:
+ priv->mlxreg_io_dev_attr[i].dev_attr.store =
+ mlxreg_io_attr_store;
+ break;
+
+ case 0444:
+ priv->mlxreg_io_dev_attr[i].dev_attr.show =
+ mlxreg_io_attr_show;
+ break;
+
+ case 0644:
+ priv->mlxreg_io_dev_attr[i].dev_attr.show =
+ mlxreg_io_attr_show;
+ priv->mlxreg_io_dev_attr[i].dev_attr.store =
+ mlxreg_io_attr_store;
+ break;
+
+ default:
+ dev_err(&priv->pdev->dev, "Bad access mode %u for attribute %s.\n",
+ priv->pdata->data[i].mode,
+ priv->mlxreg_io_attr[i]->name);
+ return -EINVAL;
+ }
+
+ priv->mlxreg_io_dev_attr[i].dev_attr.attr.name =
+ priv->mlxreg_io_attr[i]->name;
+ priv->mlxreg_io_dev_attr[i].index = i;
+ sysfs_attr_init(&priv->mlxreg_io_dev_attr[i].dev_attr.attr);
+ }
+
+ priv->group.attrs = priv->mlxreg_io_attr;
+ priv->groups[0] = &priv->group;
+ priv->groups[1] = NULL;
+
+ return 0;
+}
+
+static int mlxreg_io_probe(struct platform_device *pdev)
+{
+ struct mlxreg_io_priv_data *priv;
+ int err;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->pdata = dev_get_platdata(&pdev->dev);
+ if (!priv->pdata) {
+ dev_err(&pdev->dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+
+ priv->pdev = pdev;
+
+ err = mlxreg_io_attr_init(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev, "Failed to allocate attributes: %d\n",
+ err);
+ return err;
+ }
+
+ priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
+ "mlxreg_io",
+ priv,
+ priv->groups);
+ if (IS_ERR(priv->hwmon)) {
+ dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
+ PTR_ERR(priv->hwmon));
+ return PTR_ERR(priv->hwmon);
+ }
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ return 0;
+}
+
+static struct platform_driver mlxreg_io_driver = {
+ .driver = {
+ .name = "mlxreg-io",
+ },
+ .probe = mlxreg_io_probe,
+};
+
+module_platform_driver(mlxreg_io_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <[email protected]>");
+MODULE_DESCRIPTION("Mellanox regmap I/O access driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mlxreg-io");
--
2.1.4
Add new ODM system types, matched according to DMI_BOARD_NAME. The
supported ODM Ids are: VMOD0001, VMOD0002, VMOD0003, VMOD0004, VMOD0005.
Patch does not introduce new systems, but allows to ODM companies to set
DMI_BOARD_VENDOR and DMI_PRODUCT_NAME on their own. It assumes that ODM
company can't change DMI_BOARD_NAME.
Signed-off-by: Vadim Pasternak <[email protected]>
---
v1->v2:
Comments pointed out by Darren:
- Modify commit message;
---
drivers/platform/x86/mlx-platform.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 7a0bd24..912f844 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -844,6 +844,36 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "SN34"),
},
},
+ {
+ .callback = mlxplat_dmi_default_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn21xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn274x_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_msn201x_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
+ },
+ },
+ {
+ .callback = mlxplat_dmi_qmb7xx_matched,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
+ },
+ },
{ }
};
--
2.1.4
Add mlxreg-io platform driver activation. Access driver uses the same
regmap infrastructure as others Mellanox platform drivers.
Specific registers description for default platform data configuration are
added to mlx-platform. There are the registers for resets control, reset
causes monitoring, programmable devices version reading and mux select
control. This platform data is passed to mlxreg-io driver. Also some
default values for the register are set at initialization time through
the regmap infrastructure, which are necessary for moving write protection
from the general purpose registers, which are used by mlxreg-io for
write access.
Signed-off-by: Vadim Pasternak <[email protected]>
---
drivers/platform/x86/mlx-platform.c | 159 +++++++++++++++++++++++++++++++++++-
1 file changed, 157 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index a0fd9aa..87ddd3a 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -47,11 +47,18 @@
/* LPC bus IO offsets */
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADRR 0x2000
#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500
+#define MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET 0x00
+#define MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET 0x01
+#define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET 0x1d
#define MLXPLAT_CPLD_LPC_REG_LED1_OFFSET 0x20
#define MLXPLAT_CPLD_LPC_REG_LED2_OFFSET 0x21
#define MLXPLAT_CPLD_LPC_REG_LED3_OFFSET 0x22
#define MLXPLAT_CPLD_LPC_REG_LED4_OFFSET 0x23
#define MLXPLAT_CPLD_LPC_REG_LED5_OFFSET 0x24
+#define MLXPLAT_CPLD_LPC_REG_GP1_OFFSET 0x30
+#define MLXPLAT_CPLD_LPC_REG_WP1_OFFSET 0x31
+#define MLXPLAT_CPLD_LPC_REG_GP2_OFFSET 0x32
+#define MLXPLAT_CPLD_LPC_REG_WP2_OFFSET 0x33
#define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a
#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40
@@ -122,12 +129,14 @@
* @pdev_mux - array of mux platform devices
* @pdev_hotplug - hotplug platform devices
* @pdev_led - led platform devices
+ * @pdev_io_regs - register access platform devices
*/
struct mlxplat_priv {
struct platform_device *pdev_i2c;
struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS];
struct platform_device *pdev_hotplug;
struct platform_device *pdev_led;
+ struct platform_device *pdev_io_regs;
};
/* Regions for LPC I2C controller and LPC base register space */
@@ -813,6 +822,98 @@ static struct mlxreg_core_platform_data mlxplat_default_ng_led_data = {
.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_led_data),
};
+/* Platform register access default */
+static struct mlxreg_core_data mlxplat_mlxcpld_default_regs_io_data[] = {
+ {
+ .label = "cpld1_version",
+ .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET,
+ .bit = GENMASK(7, 0),
+ .mode = 0444,
+ },
+ {
+ .label = "cpld2_version",
+ .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET,
+ .bit = GENMASK(7, 0),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_long_pb",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(0),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_short_pb",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(1),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_aux_pwr_or_ref",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(2),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_main_pwr_fail",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(3),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_sw_reset",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(4),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_fw_reset",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(5),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_hotswap_or_wd",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(6),
+ .mode = 0444,
+ },
+ {
+ .label = "cause_asic_thermal",
+ .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(7),
+ .mode = 0444,
+ },
+ {
+ .label = "psu1_on",
+ .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(0),
+ .mode = 0200,
+ },
+ {
+ .label = "psu2_on",
+ .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(1),
+ .mode = 0200,
+ },
+ {
+ .label = "pwr_cycle",
+ .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(2),
+ .mode = 0200,
+ },
+ {
+ .label = "select_iio",
+ .reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET,
+ .mask = GENMASK(7, 0) & ~BIT(6),
+ .mode = 0644,
+ },
+};
+
+static struct mlxreg_core_platform_data mlxplat_default_regs_io_data = {
+ .data = mlxplat_mlxcpld_default_regs_io_data,
+ .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_regs_io_data),
+};
static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
{
@@ -822,6 +923,10 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_WP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
@@ -838,11 +943,18 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_WP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
@@ -864,11 +976,16 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET:
case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
@@ -887,6 +1004,11 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
+static const struct reg_default mlxplat_mlxcpld_regmap_default[] = {
+ { MLXPLAT_CPLD_LPC_REG_WP1_OFFSET, 0x00 },
+ { MLXPLAT_CPLD_LPC_REG_WP2_OFFSET, 0x00 },
+};
+
struct mlxplat_mlxcpld_regmap_context {
void __iomem *base;
};
@@ -919,6 +1041,8 @@ static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
.writeable_reg = mlxplat_mlxcpld_writeable_reg,
.readable_reg = mlxplat_mlxcpld_readable_reg,
.volatile_reg = mlxplat_mlxcpld_volatile_reg,
+ .reg_defaults = mlxplat_mlxcpld_regmap_default,
+ .num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_default),
.reg_read = mlxplat_mlxcpld_reg_read,
.reg_write = mlxplat_mlxcpld_reg_write,
};
@@ -930,6 +1054,7 @@ static struct resource mlxplat_mlxcpld_resources[] = {
static struct platform_device *mlxplat_dev;
static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
static struct mlxreg_core_platform_data *mlxplat_led;
+static struct mlxreg_core_platform_data *mlxplat_regs_io;
static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
{
@@ -944,6 +1069,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
mlxplat_led = &mlxplat_default_led_data;
+ mlxplat_regs_io = &mlxplat_default_regs_io_data;
return 1;
};
@@ -978,6 +1104,7 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
mlxplat_led = &mlxplat_default_led_data;
+ mlxplat_regs_io = &mlxplat_default_regs_io_data;
return 1;
};
@@ -1163,7 +1290,7 @@ static int mlxplat_mlxcpld_verify_bus_topology(int *nr)
static int __init mlxplat_init(void)
{
struct mlxplat_priv *priv;
- int i, nr, err;
+ int i, j, nr, err;
if (!dmi_check_system(mlxplat_dmi_table))
return -ENODEV;
@@ -1233,6 +1360,15 @@ static int __init mlxplat_init(void)
goto fail_platform_mux_register;
}
+ /* Set default registers. */
+ for (j = 0; j < mlxplat_mlxcpld_regmap_config.num_reg_defaults; j++) {
+ err = regmap_write(mlxplat_hotplug->regmap,
+ mlxplat_mlxcpld_regmap_default[j].reg,
+ mlxplat_mlxcpld_regmap_default[j].def);
+ if (err)
+ goto fail_platform_mux_register;
+ }
+
/* Add LED driver. */
mlxplat_led->regmap = mlxplat_hotplug->regmap;
priv->pdev_led = platform_device_register_resndata(
@@ -1244,14 +1380,31 @@ static int __init mlxplat_init(void)
goto fail_platform_hotplug_register;
}
+ /* Add registers io access driver. */
+ if (mlxplat_regs_io) {
+ mlxplat_regs_io->regmap = mlxplat_hotplug->regmap;
+ priv->pdev_io_regs = platform_device_register_resndata(
+ &mlxplat_dev->dev, "mlxreg-io",
+ PLATFORM_DEVID_NONE, NULL, 0,
+ mlxplat_regs_io,
+ sizeof(*mlxplat_regs_io));
+ if (IS_ERR(priv->pdev_io_regs)) {
+ err = PTR_ERR(priv->pdev_io_regs);
+ goto fail_platform_led_register;
+ }
+ }
+
/* Sync registers with hardware. */
regcache_mark_dirty(mlxplat_hotplug->regmap);
err = regcache_sync(mlxplat_hotplug->regmap);
if (err)
- goto fail_platform_led_register;
+ goto fail_platform_io_regs_register;
return 0;
+fail_platform_io_regs_register:
+ if (mlxplat_regs_io)
+ platform_device_unregister(priv->pdev_io_regs);
fail_platform_led_register:
platform_device_unregister(priv->pdev_led);
fail_platform_hotplug_register:
@@ -1272,6 +1425,8 @@ static void __exit mlxplat_exit(void)
struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev);
int i;
+ if (priv->pdev_io_regs)
+ platform_device_unregister(priv->pdev_io_regs);
platform_device_unregister(priv->pdev_led);
platform_device_unregister(priv->pdev_hotplug);
--
2.1.4
On Mon, May 07, 2018 at 06:48:53AM +0000, Vadim Pasternak wrote:
> Add LED platform driver activation from mlx-platform. This LED driver uses
> the same regmap infrastructure as others Mellanox platform drivers, so LED
> specific registers description is added.
>
> System LED configuration depends on system type. To support all the
> relevant types per system type LED descriptions are defined for passing
> to LED platform driver.
>
> Signed-off-by: Vadim Pasternak <[email protected]>
> v1->v2:
> Fixes added by Vadim:
> - Remove inline functions from mlxreg.h file;
Please remember to put the patch changelog below the --- line below.
> ---
Here
Queued up for testing, thanks.
--
Darren Hart
VMware Open Source Technology Center
On Mon, May 07, 2018 at 06:48:51AM +0000, Vadim Pasternak wrote:
Hi Vadim,
> Add extra cycle for hotplug work queue to handle the case when a signal is
> It adds missed logic for signal acknowledge, by adding an extra run for
> received, but no specific signal assertion is detected. Such case
> theoretically can happen for example in case several units are removed or
> inserted at the same time. In this situation acknowledge for some signal
> can be missed at signal top aggreagation status level. The extra run will
> allow to handler to acknowledge the missed signal.
>
...
>
> Fixes: 07b89c2b2a5e ("platform/x86: Introduce support for Mellanox hotplug driver")
This sha doesn't exist in mainline, I believe you are referring to:
$ git l | grep "Introduce support for Mellanox hotplug"
304887041d95 platform/x86: Introduce support for Mellanox hotplug driver
While this was introduced in 4.10. There have been many changes since then, with
significant changes in the 4.17 cycle. I have not added the stable tag, please
let me know if you think this should be pushed to stable, and which of the
intervening patches you'd consider dependencies.
I've fixed the sha in the commit message, and queued for testing.
Thanks,
--
Darren Hart
VMware Open Source Technology Center
On Mon, May 07, 2018 at 06:48:54AM +0000, Vadim Pasternak wrote:
> Introduce new Mellanox platform driver to allow access to Mellanox
> programmable device register space trough sysfs interface.
> The driver purpose is to provide sysfs interface for user space for the
> registers essential for system control and monitoring.
> The sets of registers for sysfs access are supposed to be defined per
> system type bases and include the registers related to system resets
> operation, system reset causes monitoring and some kinds of mux selection.
>
> Signed-off-by: Vadim Pasternak <[email protected]>
> ---
One question on the attr init which I'm not familiar with... Andy, Greg - can
you offer your opinion below...
> +static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv)
> +{
> + int i;
> +
> + priv->group.attrs = devm_kzalloc(&priv->pdev->dev,
> + priv->pdata->counter *
> + sizeof(struct attribute *),
> + GFP_KERNEL);
> + if (!priv->group.attrs)
> + return -ENOMEM;
> +
> + for (i = 0; i < priv->pdata->counter; i++) {
> + priv->mlxreg_io_attr[i] =
> + &priv->mlxreg_io_dev_attr[i].dev_attr.attr;
> +
> + /* Set attribute name as a label. */
> + priv->mlxreg_io_attr[i]->name =
> + devm_kasprintf(&priv->pdev->dev, GFP_KERNEL,
> + priv->pdata->data[i].label);
> +
> + if (!priv->mlxreg_io_attr[i]->name) {
> + dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n",
> + i + 1);
> + return -ENOMEM;
> + }
> +
> + priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode =
> + priv->pdata->data[i].mode;
> + switch (priv->pdata->data[i].mode) {
This seemed a bit odd to me. Do we need to do this conditional assignment within
the kernel, or can these just be assigned, and the mode will guard against the
user being able to call store on a read only attr?
> + case 0200:
> + priv->mlxreg_io_dev_attr[i].dev_attr.store =
> + mlxreg_io_attr_store;
> + break;
> +
> + case 0444:
> + priv->mlxreg_io_dev_attr[i].dev_attr.show =
> + mlxreg_io_attr_show;
> + break;
> +
> + case 0644:
> + priv->mlxreg_io_dev_attr[i].dev_attr.show =
> + mlxreg_io_attr_show;
> + priv->mlxreg_io_dev_attr[i].dev_attr.store =
> + mlxreg_io_attr_store;
> + break;
If this is necessary, we can simplify this by checking for the read mask and the
write mask and setting each once - rather than duplicating this for r, w, and
rw. As it is a 0400 would not assign the show function, even though it is
readable by somebody.
--
Darren Hart
VMware Open Source Technology Center
> -----Original Message-----
> From: Darren Hart [mailto:[email protected]]
> Sent: Friday, May 25, 2018 2:52 AM
> To: Vadim Pasternak <[email protected]>
> Cc: [email protected]; [email protected]; linux-
> [email protected]; [email protected]; [email protected];
> Michael Shych <[email protected]>; [email protected]
> Subject: Re: [PATCH v2 3/7] platform/mellanox: mlxreg-hotplug: add extra cycle
> for hotplug work queue
>
> On Mon, May 07, 2018 at 06:48:51AM +0000, Vadim Pasternak wrote:
>
> Hi Vadim,
>
> > Add extra cycle for hotplug work queue to handle the case when a
> > signal is It adds missed logic for signal acknowledge, by adding an
> > extra run for received, but no specific signal assertion is detected.
> > Such case theoretically can happen for example in case several units
> > are removed or inserted at the same time. In this situation
> > acknowledge for some signal can be missed at signal top aggreagation
> > status level. The extra run will allow to handler to acknowledge the missed
> signal.
> >
>
> ...
>
> >
> > Fixes: 07b89c2b2a5e ("platform/x86: Introduce support for Mellanox
> > hotplug driver")
>
> This sha doesn't exist in mainline, I believe you are referring to:
>
> $ git l | grep "Introduce support for Mellanox hotplug"
> 304887041d95 platform/x86: Introduce support for Mellanox hotplug driver
>
> While this was introduced in 4.10. There have been many changes since then,
> with significant changes in the 4.17 cycle. I have not added the stable tag, please
> let me know if you think this should be pushed to stable, and which of the
> intervening patches you'd consider dependencies.
Hi Darren,
Thank you for review.
I think it's worth pushing to stable.
It doesn't depend one previous patches from the patchset, but possibly
this one, which just fixes few comments in mlxreg-hotplug:
platform/mellanox: mlxreg-hotplug: Document fixes for hotplug private data
(commit 321089a4da2f6b20bb8af96354f76d260a4ca2c6 in testing branch).
Thanks,
Vadim.
>
> I've fixed the sha in the commit message, and queued for testing.
>
> Thanks,
>
> --
> Darren Hart
> VMware Open Source Technology Center
> -----Original Message-----
> From: Darren Hart [mailto:[email protected]]
> Sent: Friday, May 25, 2018 3:31 AM
> To: Vadim Pasternak <[email protected]>
> Cc: [email protected]; [email protected]; linux-
> [email protected]; [email protected]; [email protected];
> Michael Shych <[email protected]>; [email protected]
> Subject: Re: [PATCH v2 6/7] platform/mellanox: Introduce support for Mellanox
> register access driver
>
> On Mon, May 07, 2018 at 06:48:54AM +0000, Vadim Pasternak wrote:
> > Introduce new Mellanox platform driver to allow access to Mellanox
> > programmable device register space trough sysfs interface.
> > The driver purpose is to provide sysfs interface for user space for
> > the registers essential for system control and monitoring.
> > The sets of registers for sysfs access are supposed to be defined per
> > system type bases and include the registers related to system resets
> > operation, system reset causes monitoring and some kinds of mux selection.
> >
> > Signed-off-by: Vadim Pasternak <[email protected]>
> > ---
>
> One question on the attr init which I'm not familiar with... Andy, Greg - can you
> offer your opinion below...
>
> > +static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv) {
> > + int i;
> > +
> > + priv->group.attrs = devm_kzalloc(&priv->pdev->dev,
> > + priv->pdata->counter *
> > + sizeof(struct attribute *),
> > + GFP_KERNEL);
> > + if (!priv->group.attrs)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < priv->pdata->counter; i++) {
> > + priv->mlxreg_io_attr[i] =
> > + &priv->mlxreg_io_dev_attr[i].dev_attr.attr;
> > +
> > + /* Set attribute name as a label. */
> > + priv->mlxreg_io_attr[i]->name =
> > + devm_kasprintf(&priv->pdev->dev,
> GFP_KERNEL,
> > + priv->pdata->data[i].label);
> > +
> > + if (!priv->mlxreg_io_attr[i]->name) {
> > + dev_err(&priv->pdev->dev, "Memory allocation failed
> for sysfs attribute %d.\n",
> > + i + 1);
> > + return -ENOMEM;
> > + }
> > +
> > + priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode =
> > + priv->pdata->data[i].mode;
> > + switch (priv->pdata->data[i].mode) {
>
> This seemed a bit odd to me. Do we need to do this conditional assignment
> within the kernel, or can these just be assigned, and the mode will guard against
> the user being able to call store on a read only attr?
>
> > + case 0200:
> > + priv->mlxreg_io_dev_attr[i].dev_attr.store =
> > + mlxreg_io_attr_store;
> > + break;
> > +
> > + case 0444:
> > + priv->mlxreg_io_dev_attr[i].dev_attr.show =
> > + mlxreg_io_attr_show;
> > + break;
> > +
> > + case 0644:
> > + priv->mlxreg_io_dev_attr[i].dev_attr.show =
> > + mlxreg_io_attr_show;
> > + priv->mlxreg_io_dev_attr[i].dev_attr.store =
> > + mlxreg_io_attr_store;
> > + break;
>
> If this is necessary, we can simplify this by checking for the read mask and the
> write mask and setting each once - rather than duplicating this for r, w, and rw.
> As it is a 0400 would not assign the show function, even though it is readable by
> somebody.
Maybe I really can add something like
static struct device_attribute mlxreg_io_devattr_rw = {
.show = mlxreg_io_attr_show,
.store = mlxreg_io_attr_store,
};
And replace this whole switch statement just with:
memcpy(&priv->mlxreg_io_dev_attr[i].dev_attr,
&mlxreg_io_devattr_rw, sizeof(struct device_attribute));
>
> --
> Darren Hart
> VMware Open Source Technology Center
On Sat, May 26, 2018 at 11:15:35AM +0000, Vadim Pasternak wrote:
>
>
> > -----Original Message-----
> > From: Darren Hart [mailto:[email protected]]
> > Sent: Friday, May 25, 2018 3:31 AM
> > To: Vadim Pasternak <[email protected]>
> > Cc: [email protected]; [email protected]; linux-
> > [email protected]; [email protected]; [email protected];
> > Michael Shych <[email protected]>; [email protected]
> > Subject: Re: [PATCH v2 6/7] platform/mellanox: Introduce support for Mellanox
> > register access driver
> >
> > On Mon, May 07, 2018 at 06:48:54AM +0000, Vadim Pasternak wrote:
> > > Introduce new Mellanox platform driver to allow access to Mellanox
> > > programmable device register space trough sysfs interface.
> > > The driver purpose is to provide sysfs interface for user space for
> > > the registers essential for system control and monitoring.
> > > The sets of registers for sysfs access are supposed to be defined per
> > > system type bases and include the registers related to system resets
> > > operation, system reset causes monitoring and some kinds of mux selection.
> > >
> > > Signed-off-by: Vadim Pasternak <[email protected]>
> > > ---
> >
> > One question on the attr init which I'm not familiar with... Andy, Greg - can you
> > offer your opinion below...
> >
...
> > > + priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode =
> > > + priv->pdata->data[i].mode;
> > > + switch (priv->pdata->data[i].mode) {
> >
> > This seemed a bit odd to me. Do we need to do this conditional assignment
> > within the kernel, or can these just be assigned, and the mode will guard against
> > the user being able to call store on a read only attr?
> >
> > > + case 0200:
> > > + priv->mlxreg_io_dev_attr[i].dev_attr.store =
> > > + mlxreg_io_attr_store;
> > > + break;
> > > +
> > > + case 0444:
> > > + priv->mlxreg_io_dev_attr[i].dev_attr.show =
> > > + mlxreg_io_attr_show;
> > > + break;
> > > +
> > > + case 0644:
> > > + priv->mlxreg_io_dev_attr[i].dev_attr.show =
> > > + mlxreg_io_attr_show;
> > > + priv->mlxreg_io_dev_attr[i].dev_attr.store =
> > > + mlxreg_io_attr_store;
> > > + break;
> >
> > If this is necessary, we can simplify this by checking for the read mask and the
> > write mask and setting each once - rather than duplicating this for r, w, and rw.
> > As it is a 0400 would not assign the show function, even though it is readable by
> > somebody.
>
> Maybe I really can add something like
> static struct device_attribute mlxreg_io_devattr_rw = {
> .show = mlxreg_io_attr_show,
> .store = mlxreg_io_attr_store,
> };
>
> And replace this whole switch statement just with:
> memcpy(&priv->mlxreg_io_dev_attr[i].dev_attr,
> &mlxreg_io_devattr_rw, sizeof(struct device_attribute));
This is certainly preferable if it doesn't present any functional problems.
Seems to me it must be doable because the OS has to deny write for Group and
Other and allow for User with 644, similarly for read is other perm conditions.
--
Darren Hart
VMware Open Source Technology Center