2016-10-07 15:33:24

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 00/10] Introduce Juniper I2CS FPGA driver

Add Juniper's I2CS FPGA driver. Those FPGAs
are present in Juniper's PTX series of routers.

The MFD driver provices i2c/gpio/leds/hwmon devices.

There are full device tree binding documents for the
master mfd driver and for all slave drivers.

This patchset is against mainline as of today: v4.8-9431-g3477d16
and is dependent on the "Juniper prerequisites" and
"Juniper infrastructure" patchsets sent earlier.

Avirup Banerjee (1):
hwmon: Add driver for Fan Tray on Juniper I2CS FGPA

Georgi Vlaev (8):
mfd: Add Juniper I2CS MFD driver
mfd: dt-bindings: Add bindings for the Juniper I2CS MFD
i2c/muxes: Juniper I2CS RE mux
i2c: i2c-mux-i2cs: Add device tree bindings
gpio: gpio-i2cs: Document bindings of I2CS FPGA GPIO block
leds: i2cs: Add I2CS FPGA leds driver
leds: Add binding for Juniper's I2CS FPGA
hwmon: i2cs-fan: Add hwmon dts binding documentation

Guenter Roeck (1):
gpio: i2cs: Juniper I2CS to GPIO pin mapping driver

.../devicetree/bindings/gpio/jnx,gpio-i2cs.txt | 43 ++
.../devicetree/bindings/hwmon/i2cs-fan.txt | 19 +
.../devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt | 27 ++
.../devicetree/bindings/leds/leds-i2cs.txt | 34 ++
Documentation/devicetree/bindings/mfd/jnx-i2cs.txt | 68 +++
drivers/gpio/Kconfig | 11 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-jnx-i2cs.c | 523 +++++++++++++++++++++
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/jnx-fan.c | 471 +++++++++++++++++++
drivers/i2c/muxes/Kconfig | 10 +
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-i2cs.c | 155 ++++++
drivers/leds/Kconfig | 9 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-jnx-i2cs.c | 219 +++++++++
drivers/mfd/Kconfig | 17 +
drivers/mfd/Makefile | 1 +
drivers/mfd/jnx-i2cs-core.c | 118 +++++
include/linux/mfd/jnx-i2cs-core.h | 96 ++++
include/linux/platform_data/jnx-i2cs-fan.h | 13 +
22 files changed, 1849 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-i2cs.txt
create mode 100644 Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
create mode 100644 Documentation/devicetree/bindings/leds/leds-i2cs.txt
create mode 100644 Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
create mode 100644 drivers/gpio/gpio-jnx-i2cs.c
create mode 100644 drivers/hwmon/jnx-fan.c
create mode 100644 drivers/i2c/muxes/i2c-mux-i2cs.c
create mode 100644 drivers/leds/leds-jnx-i2cs.c
create mode 100644 drivers/mfd/jnx-i2cs-core.c
create mode 100644 include/linux/mfd/jnx-i2cs-core.h
create mode 100644 include/linux/platform_data/jnx-i2cs-fan.h

--
1.9.1


2016-10-07 15:23:54

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 01/10] mfd: Add Juniper I2CS MFD driver

From: Georgi Vlaev <[email protected]>

Add Juniper's I2CS FPGA MFD driver.

This driver is bare-bones and only provides a method for the
instantiation of the following subdevices.

- I2C mux driver (i2c-mux-i2cs)
- LEDS driver (leds-jnx-i2cs)
- GPIO driver (gpio-jnx-i2cs)
- HWMON driver (i2cs-fan-hwmon)

Signed-off-by: Georgi Vlaev <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/mfd/Kconfig | 17 ++++++
drivers/mfd/Makefile | 1 +
drivers/mfd/jnx-i2cs-core.c | 118 ++++++++++++++++++++++++++++++++++++++
include/linux/mfd/jnx-i2cs-core.h | 96 +++++++++++++++++++++++++++++++
4 files changed, 232 insertions(+)
create mode 100644 drivers/mfd/jnx-i2cs-core.c
create mode 100644 include/linux/mfd/jnx-i2cs-core.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6107f7a..82493d5 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1400,6 +1400,23 @@ config MFD_JUNIPER_CBC
This driver can be built as a module. If built as a module it will be
called "cbc-core"

+config MFD_JUNIPER_I2CS
+ tristate "Juniper I2CS FPGA"
+ depends on JNX_PTX1K_RCB || PTXPMB_COMMON || JNX_SYSTEM
+ select MFD_CORE
+ select REGMAP_I2C
+ select GPIO_JNX_I2CS
+ select I2C_MUX_I2CS
+ select LEDS_JNX_I2CS
+ depends on I2C=y
+ default y
+ help
+ Select this to enable the I2CS FPGA multi-function kernel driver.
+ This FPGA is present on almost any Juniper Networks card.
+
+ This driver can be built as a module. If built as a module it will be
+ called "jnx_i2cs"
+
config MFD_TWL4030_AUDIO
bool "TI TWL4030 Audio"
depends on TWL4030_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 0ea6dc6..215d9cf 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -152,6 +152,7 @@ obj-$(CONFIG_MFD_JUNIPER_CPLD) += ptxpmb-cpld-core.o
obj-$(CONFIG_MFD_JUNIPER_SAM) += sam-core.o
obj-$(CONFIG_MFD_JUNIPER_EXT_CPLD) += ptxpmb-ext-cpld-core.o
obj-$(CONFIG_MFD_JUNIPER_CBC) += cbc-core.o
+obj-$(CONFIG_MFD_JUNIPER_I2CS) += jnx-i2cs-core.o
obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
# ab8500-core need to come after db8500-prcmu (which provides the channel)
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
diff --git a/drivers/mfd/jnx-i2cs-core.c b/drivers/mfd/jnx-i2cs-core.c
new file mode 100644
index 0000000..d279a73
--- /dev/null
+++ b/drivers/mfd/jnx-i2cs-core.c
@@ -0,0 +1,118 @@
+/*
+ * drivers/mfd/jnx-i2cs-core.c
+ *
+ * Copyright (c) 2014, Juniper Networks
+ * Author: Georgi Vlaev <[email protected]>
+ *
+ * The I2C Slave FPGA (I2CS) - found on almost any Juniper card.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/mfd/jnx-i2cs-core.h>
+
+static int jnx_i2cs_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct jnx_i2cs_platform_data *pdata = dev_get_platdata(dev);
+ struct device_node *child, *np = dev->of_node;
+ struct mfd_cell *cells;
+ int ncells, ret;
+
+ if (np) {
+ ncells = of_get_child_count(np);
+ if (ncells == 0) {
+ dev_err(dev, "No child specified for %s\n", np->name);
+ return -EINVAL;
+ }
+
+ cells = devm_kzalloc(dev, ncells * sizeof(*cells), GFP_KERNEL);
+ if (!cells)
+ return -ENOMEM;
+
+ ncells = 0;
+ for_each_available_child_of_node(dev->of_node, child) {
+ const char *s;
+
+ s = of_get_property(child, "compatible", NULL);
+ if (!s) {
+ dev_err(dev,
+ "Missing compatible property for %s\n",
+ child->name);
+ return -EINVAL;
+ }
+
+ cells[ncells].name = child->name;
+ cells[ncells].of_compatible = s;
+ ncells++;
+ }
+ if (ncells == 0) {
+ dev_err(dev, "No active child for %s\n", np->name);
+ return -EINVAL;
+ }
+ } else if (pdata) {
+ cells = pdata->cells;
+ ncells = pdata->ncells;
+ } else {
+ return -ENODEV;
+ }
+
+ ret = mfd_add_devices(dev, i2c_adapter_id(i2c->adapter),
+ cells, ncells, NULL, 0, NULL);
+
+ return ret;
+}
+
+static int jnx_i2cs_remove(struct i2c_client *i2c)
+{
+ mfd_remove_devices(&i2c->dev);
+
+ return 0;
+}
+
+static const struct of_device_id jnx_i2cs_of_match[] = {
+ { .compatible = "jnx,i2cs-rcb" },
+ { .compatible = "jnx,i2cs-fpc" },
+ { .compatible = "jnx,i2cs-sib" },
+ { .compatible = "jnx,i2cs-fan" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jnx_i2cs_of_match);
+
+static const struct i2c_device_id jnx_i2cs_id[] = {
+ { "jnx_i2cs_rcb", 0 },
+ { "jnx_i2cs_fpc", 0 },
+ { "jnx_i2cs_sib", 0 },
+ { "jnx_i2cs_fan", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, jnx_i2cs_id);
+
+static struct i2c_driver jnx_i2cs_driver = {
+ .driver = {
+ .name = "jnx_i2cs",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(jnx_i2cs_of_match),
+ },
+ .probe = jnx_i2cs_probe,
+ .remove = jnx_i2cs_remove,
+ .id_table = jnx_i2cs_id,
+};
+
+module_i2c_driver(jnx_i2cs_driver);
+
+MODULE_DESCRIPTION("Juniper I2CS MFD core driver");
+MODULE_AUTHOR("Georgi Vlaev <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/jnx-i2cs-core.h b/include/linux/mfd/jnx-i2cs-core.h
new file mode 100644
index 0000000..5e63aab
--- /dev/null
+++ b/include/linux/mfd/jnx-i2cs-core.h
@@ -0,0 +1,96 @@
+/*
+ * I2CS FPGA (jnx-i2cs-core) registers
+ *
+ * Copyright (C) 2014 Juniper Networks
+ * Author: Georgi Vlaev <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __JNX_I2CS_CORE_H__
+#define __JNX_I2CS_CORE_H__
+
+/* SIB I2CS registers */
+#define I2CS_SCRATCH 0x00
+#define I2CS_VERSION 0x01
+#define I2CS_MASTER_STATUS 0x02
+#define I2CS_MASTER_TIMEOUT 0x03
+#define I2CS_MASTER_FORCE 0x04
+#define I2CS_I2C0_STATUS 0x05
+#define I2CS_I2C1_STATUS 0x06
+#define I2CS_I2C_RESET 0x07
+#define I2CS_DATECODE 0x0f
+#define I2CS_INT_STATUS 0x10
+#define I2CS_INT_ENABLE 0x11
+#define I2CS_FRU_LED 0x12
+#define I2CS_MISC_IO 0x13
+#define I2CS_BUTTON_STATUS 0x14
+#define I2CS_BUTTON_ENABLE 0x15
+#define I2CS_GPIO_OE 0x16
+#define I2CS_GPIO_OUT 0x17
+#define I2CS_GPIO_IN 0x18
+#define I2CS_SERIAL_CONFIG0 0x19
+#define I2CS_SERIAL_CONFIG1 0x1a
+#define I2CS_MII_CONFIG 0x1b
+#define I2CS_MII_ADDR 0x1c
+#define I2CS_MII_DATA0 0x1d
+#define I2CS_MII_DATA1 0x1e
+#define I2CS_MII_DATA2 0x1f
+#define I2CS_PWR_CONTROL 0x20
+#define I2CS_PWR_UP_STATUS 0x21
+#define I2CS_PWR_DIS_STATUS 0x22
+#define I2CS_PWR_DIS_CAUSE 0x23
+#define I2CS_PWR_VFAIL_STATUS 0x24
+#define I2CS_PWR_VFAIL_CAUSE 0x25
+#define I2CS_PWR_TRIM_UP_A 0x26
+#define I2CS_PWR_TRIM_UP_B 0x27
+#define I2CS_PWR_TRIM_DN_A 0x28
+#define I2CS_PWR_TRIM_DN_B 0x29
+#define I2CS_PWR_VFAIL_STATUS_B 0x2a
+#define I2CS_PWR_VFAIL_CAUSE_B 0x2b
+#define I2CS_SCB 0x2c
+#define I2CS_RESET_I2CS_REG 0x2d
+#define I2CS_RESET_CONTROL 0x40
+#define I2CS_SIB_ID_PORT_STATUS 0x41
+#define I2CS_SIB_PCIE_GEN 0x42
+#define I2CS_TF_LTC 0x43
+#define I2CS_POWER_RST_HSWAP 0x44
+#define I2CS_SIB_EN 0x45
+#define I2CS_SIB_SPARE 0x46
+#define I2CS_TF_PCIE_INT_STAT 0x47
+#define I2CS_TF_PCIE_INT_EN 0x48
+#define I2CS_SIB_MISC_0 0x49
+#define I2CS_SIB_MISC_1 0x4a
+#define I2CS_LINK_STATUS_0 0x4b
+#define I2CS_LINK_STATUS_1 0x4c
+#define I2CS_LINK_STATUS_2 0x4d
+#define I2CS_LINK_STATUS_3 0x4e
+#define I2CS_LINK_STATUS_4 0x4f
+#define I2CS_LINK_STATUS_5 0x50
+#define I2CS_LINK_STATUS_6 0x51
+#define I2CS_LINK_STATUS_7 0x52
+#define I2CS_LINK_STATUS_8 0x53
+#define I2CS_LINK_STATUS_9 0x54
+#define I2CS_LINK_STATUS_10 0x55
+#define I2CS_LINK_STATUS_11 0x56
+#define I2CS_LINK_STATUS_12 0x57
+#define I2CS_LINK_STATUS_13 0x58
+#define I2CS_LINK_STATUS_14 0x59
+#define I2CS_LINK_STATUS_15 0x5a
+#define I2CS_LINK_STATUS_16 0x5b
+#define I2CS_LINK_STATUS_17 0x5c
+#define I2CS_PWR_VFAIL_IGNORE_SNG 0x5d
+#define I2CS_SPARE 0x5e
+#define I2CS_SPARE_OE 0x5f
+#define I2CS_MAX_REGISTER I2CS_SPARE_OE
+
+struct mfd_cell;
+
+struct jnx_i2cs_platform_data {
+ int ncells;
+ struct mfd_cell *cells;
+};
+
+#endif /*__JNX_I2CS_CORE_H__*/
--
1.9.1

2016-10-07 15:24:31

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 07/10] leds: i2cs: Add I2CS FPGA leds driver

From: Georgi Vlaev <[email protected]>

Add support for the FRU faceplate status LEDs (OK, FAIL,
ACTIVE, STANDBY) controlled by the Juniper I2CS FPGA. This
driver is a jnx-i2cs-core client.

Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/leds/Kconfig | 9 ++
drivers/leds/Makefile | 1 +
drivers/leds/leds-jnx-i2cs.c | 219 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 229 insertions(+)
create mode 100644 drivers/leds/leds-jnx-i2cs.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 7a628c6..45c6612 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -659,6 +659,15 @@ config LEDS_MLXCPLD
This option enabled support for the LEDs on the Mellanox
boards. Say Y to enabled these.

+config LEDS_JNX_I2CS
+ tristate "LED support for the Juniper Networks I2CS FPGA"
+ depends on LEDS_CLASS && I2C
+ select REGMAP_I2C
+ help
+ This option enables support for the FRU faceplate status
+ LEDs (OK, FAIL, ACTIVE, STANDBY) controlled by the Juniper
+ I2CS FPGA.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"

diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3965070..1ce2d0b 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
+obj-$(CONFIG_LEDS_JNX_I2CS) += leds-jnx-i2cs.o

# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-jnx-i2cs.c b/drivers/leds/leds-jnx-i2cs.c
new file mode 100644
index 0000000..c2d7274
--- /dev/null
+++ b/drivers/leds/leds-jnx-i2cs.c
@@ -0,0 +1,219 @@
+/*
+ * Juniper Networks I2CS FPGA LEDs driver
+ *
+ * Copyright (C) 2016 Juniper Networks
+ * Author: Georgi Vlaev <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/jnx-i2cs-core.h>
+
+#define FRU_LEDS 4 /* Total LEDs (active, fail, ok, standby) */
+#define HW_BLINK_LEDS 3 /* LEDs with hw blink cap (standby not supported) */
+
+/*
+ * I2CS fru_led [0x12]
+ *
+ * bit 6 | bit 5 | bit 4 |... | bit 0
+ * blink_ok|blink_fail|blink_act|led_standby|led_ok|led_fail|led_act
+ */
+
+/* TODO: Use the regmap from the parent MFD */
+static struct regmap_config i2cs_leds_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = I2CS_SPARE_OE,
+};
+
+struct i2cs_led {
+ struct led_classdev lc;
+ struct regmap *regmap;
+ struct work_struct work;
+ int blink;
+ int on;
+ int bit;
+};
+
+struct i2cs_led_data {
+ int num_leds;
+ struct i2cs_led *leds;
+};
+
+static void jnx_i2cs_leds_work(struct work_struct *work)
+{
+ struct i2cs_led *led = container_of(work, struct i2cs_led, work);
+
+ int mask = (BIT(led->bit) << 4) | BIT(led->bit);
+ int value = ((led->blink << led->bit) << 4) | (led->on << led->bit);
+
+ regmap_update_bits(led->regmap, I2CS_FRU_LED, mask, value & 0x7f);
+}
+
+static void jnx_i2cs_leds_brightness_set(struct led_classdev *lc,
+ enum led_brightness brightness)
+{
+ struct i2cs_led *led = container_of(lc, struct i2cs_led, lc);
+
+ led->on = (brightness != LED_OFF);
+ led->blink = 0; /* always turn off hw blink on brightness_set() */
+ schedule_work(&led->work);
+}
+
+static int jnx_i2cs_leds_blink_set(struct led_classdev *lc,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct i2cs_led *led = container_of(lc, struct i2cs_led, lc);
+
+ led->blink = (*delay_on > 0);
+ led->on = led->blink; /* 'on' bit should be set if blinking */
+ schedule_work(&led->work);
+
+ return 0;
+}
+
+static int jnx_i2cs_leds_init_one(struct device *dev, struct device_node *np,
+ struct i2cs_led_data *ild,
+ struct regmap *regmap, int num)
+{
+ struct i2cs_led *led;
+ const char *string;
+ bool hw_blink;
+ int ret;
+ u32 reg;
+
+ ret = of_property_read_u32(np, "reg", &reg);
+ if (ret || reg >= FRU_LEDS)
+ return -ENODEV;
+
+ led = &ild->leds[num];
+ led->bit = reg;
+ led->regmap = regmap;
+
+ if (!of_property_read_string(np, "label", &string))
+ led->lc.name = string;
+ else
+ led->lc.name = np->name;
+
+ if (!of_property_read_string(np, "linux,default-trigger", &string))
+ led->lc.default_trigger = string;
+
+ led->lc.brightness = LED_OFF;
+ led->lc.brightness_set = jnx_i2cs_leds_brightness_set;
+ if (led->bit <= HW_BLINK_LEDS) {
+ hw_blink = of_property_read_bool(np, "hw-blink");
+ if (hw_blink)
+ led->lc.blink_set = jnx_i2cs_leds_blink_set;
+ }
+
+ ret = devm_led_classdev_register(dev, &led->lc);
+ if (ret)
+ return ret;
+
+ INIT_WORK(&led->work, jnx_i2cs_leds_work);
+
+ return 0;
+}
+
+static int jnx_i2cs_leds_of_init(struct device *dev, struct i2cs_led_data *ild)
+{
+ struct device_node *child, *np = dev->of_node;
+ struct regmap *regmap;
+ struct i2c_client *client;
+ int ret, num_leds, i = 0;
+
+ if (!dev->parent)
+ return -ENODEV;
+
+ client = i2c_verify_client(dev->parent);
+ if (!client)
+ return -ENODEV;
+
+ regmap = devm_regmap_init_i2c(client, &i2cs_leds_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ num_leds = of_get_child_count(np);
+ if (!num_leds || num_leds > FRU_LEDS)
+ return -ENODEV;
+
+ ild->num_leds = num_leds;
+ ild->leds = devm_kzalloc(dev, sizeof(struct i2cs_led) * num_leds,
+ GFP_KERNEL);
+ if (!ild->leds)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ ret = jnx_i2cs_leds_init_one(dev, child, ild, regmap, i++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int jnx_i2cs_leds_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct i2cs_led_data *ild;
+ int ret;
+
+ ild = devm_kzalloc(dev, sizeof(*ild), GFP_KERNEL);
+ if (!ild)
+ return -ENOMEM;
+
+ ret = jnx_i2cs_leds_of_init(dev, ild);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, ild);
+
+ return 0;
+}
+
+static int jnx_i2cs_leds_remove(struct platform_device *pdev)
+{
+ struct i2cs_led_data *ild = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < ild->num_leds; i++) {
+ devm_led_classdev_unregister(&pdev->dev, &ild->leds[i].lc);
+ cancel_work_sync(&ild->leds[i].work);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id jnx_i2cs_leds_match[] = {
+ { .compatible = "jnx,leds-i2cs", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, jnx_i2cs_leds_match);
+
+static struct platform_driver jnx_i2cs_leds_driver = {
+ .driver = {
+ .name = "leds-i2cs",
+ .of_match_table = jnx_i2cs_leds_match,
+ },
+ .probe = jnx_i2cs_leds_probe,
+ .remove = jnx_i2cs_leds_remove,
+};
+
+module_platform_driver(jnx_i2cs_leds_driver);
+
+MODULE_DESCRIPTION("Juniper Networks I2CS leds driver");
+MODULE_AUTHOR("Georgi Vlaev <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.9.1

2016-10-07 15:24:49

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 08/10] leds: Add binding for Juniper's I2CS FPGA

From: Georgi Vlaev <[email protected]>

Document bindings for the I2CS FPGA leds.

Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
.../devicetree/bindings/leds/leds-i2cs.txt | 34 ++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/leds-i2cs.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-i2cs.txt b/Documentation/devicetree/bindings/leds/leds-i2cs.txt
new file mode 100644
index 0000000..100e584
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-i2cs.txt
@@ -0,0 +1,34 @@
+Juniper I2CS LED driver.
+
+This is present in Juniper platforms that use a I2CS Slave FPGA.
+
+Required properties:
+ - compatible: must be "jnx,leds-i2cs"
+ - #address-cells : must be 1.
+ - #size-cells : must be 0.
+
+Each led is represented as a sub-node of the jnx,leds-i2cs device.
+
+LED sub-node properties:
+- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
+- reg : number of LED
+- linux,default-trigger : (optional)
+ see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+
+leds_fpc0: leds-jnx-i2cs {
+ compatible = "jnx,leds-i2cs";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fpc0-fail {
+ reg = <1>;
+ linux,default-trigger = "fpc0-fail";
+ };
+
+ fpc0-ok {
+ reg = <2>;
+ linux,default-trigger = "fpc0-ok";
+ };
+};
--
1.9.1

2016-10-07 15:25:01

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 09/10] hwmon: Add driver for Fan Tray on Juniper I2CS FGPA

From: Avirup Banerjee <[email protected]>

Add a hwmon driver for Fan Trays using Juniper's I2CS FPGA.

Signed-off-by: Avirup Banerjee <[email protected]>
Signed-off-by: Georgi Vlaev <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/jnx-fan.c | 471 +++++++++++++++++++++++++++++
include/linux/platform_data/jnx-i2cs-fan.h | 13 +
4 files changed, 496 insertions(+)
create mode 100644 drivers/hwmon/jnx-fan.c
create mode 100644 include/linux/platform_data/jnx-i2cs-fan.h

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 45cef3d..b9348d2 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -663,6 +663,17 @@ config SENSORS_JC42
This driver can also be built as a module. If so, the module
will be called jc42.

+config SENSORS_JNX_FAN
+ tristate "Juniper Fan Tray driver"
+ depends on I2C && MFD_JUNIPER_I2CS
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the Juniper Networks
+ Fan Tray Driver.
+
+ This driver can also be built as a module. If so, the module
+ will be called jnx-fan.
+
config SENSORS_POWR1220
tristate "Lattice POWR1220 Power Monitoring"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index aecf4ba..eea631e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_INA3221) += ina3221.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
+obj-$(CONFIG_SENSORS_JNX_FAN) += jnx-fan.o
obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
diff --git a/drivers/hwmon/jnx-fan.c b/drivers/hwmon/jnx-fan.c
new file mode 100644
index 0000000..d04e3ce
--- /dev/null
+++ b/drivers/hwmon/jnx-fan.c
@@ -0,0 +1,471 @@
+/*
+ * hwmon: Driver for Juniper Fan Tray Controller
+ *
+ * Copyright (c) 2014 Juniper Networks. All rights reserved.
+ * Author: Avirup Banerjee <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/jnx-i2cs-fan.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRIVER_NAME "i2cs_fan_hwmon"
+
+/*
+ * Fan fpga register offsets
+ */
+
+#define I2CS_FAN_ADEC_VER 0x01
+#define I2CS_FAN_SELECT 0x40
+#define I2CS_FAN_SPEED_CTRL 0x41
+#define I2CS_FAN_MAX_TACH 0x42
+#define I2CS_FAN_MIN_TACH 0x43
+#define I2CS_FAN_TACH 0x44
+#define I2CS_FAN_INT_SRC 0x45
+#define I2CS_FAN_INT_MASK 0x46
+#define I2CS_FAN_INT_1_7 0x47
+#define I2CS_FAN_INT_8_14 0x48
+#define I2CS_FAN_SPARE_FAN_INT_15_22 0x49
+#define I2CS_FAN_MODE_AND_TEST 0x4A
+#define I2CS_FAN_HW_DEBUG_1 0x4B
+#define I2CS_FAN_HW_DEBUG_2 0x4C
+#define I2CS_FAN_SW_FAN_POWER_SPEED_FAIL 0x4D
+#define I2CS_FAN_ADEC_MASK_WAIT_SECOND 0x4F
+#define I2CS_FAN_RESET_WAIT_CONTROL 0x50
+#define I2CS_FAN_BOARD_STAT_1 0x51
+#define I2CS_FAN_BOARD_STAT_2 0x52
+#define I2CS_FAN_OK_THRESHOLD 0x53
+#define I2CS_FAN_SPARE 0x54
+#define I2CS_FAN_SPARE_OE 0x55
+
+#define FAN_TACH_FACTOR 120
+#define NUM_FANS_PER_TRAY 14
+
+struct jnx_fan_data {
+ struct regmap *regmap;
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ int fan_index;
+ int num_fans;
+ int factor;
+};
+
+static int jnx_fan_select(struct jnx_fan_data *data, int index)
+{
+ /* Return if fan has already been selected */
+ if (data->fan_index == index)
+ return 0;
+
+ data->fan_index = index;
+
+ return regmap_write(data->regmap, I2CS_FAN_SELECT, index);
+}
+
+static int jnx_fan_read_reg(struct jnx_fan_data *data, u8 reg, int index)
+{
+ unsigned int value;
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ ret = jnx_fan_select(data, index);
+ if (ret < 0)
+ goto done;
+
+ ret = regmap_read(data->regmap, reg, &value);
+ if (ret < 0)
+ goto done;
+ ret = value;
+
+done:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int jnx_fan_write_reg(struct jnx_fan_data *data, u8 reg,
+ unsigned int value, int index)
+{
+ int ret;
+
+ mutex_lock(&data->update_lock);
+ ret = jnx_fan_select(data, index);
+ if (ret < 0)
+ goto done;
+
+ ret = regmap_write(data->regmap, reg, value);
+
+done:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t jnx_fan_set_pwm(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct jnx_fan_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if (val > 255)
+ return -EINVAL;
+
+ ret = jnx_fan_write_reg(data, I2CS_FAN_SPEED_CTRL, val, attr->index);
+ return ret ? ret : count;
+}
+
+static ssize_t jnx_fan_show_pwm(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct jnx_fan_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = jnx_fan_read_reg(data, I2CS_FAN_SPEED_CTRL, attr->index);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t jnx_fan_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
+ struct jnx_fan_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = jnx_fan_read_reg(data, attr->nr, attr->index);
+ if (ret < 0)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ret * data->factor);
+}
+
+static ssize_t jnx_fan_set(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
+ struct jnx_fan_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ DIV_ROUND_CLOSEST(val, data->factor);
+ clamp_val(val, 0, 255);
+
+ ret = jnx_fan_write_reg(data, attr->nr, val, attr->index);
+ return ret ? ret : count;
+}
+
+static umode_t jnx_fan_is_visible(struct kobject *kobj, struct attribute *a,
+ int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct jnx_fan_data *data = dev_get_drvdata(dev);
+ unsigned int index = n % 14;
+
+ if (index < data->num_fans)
+ return a->mode;
+
+ return 0;
+}
+
+static struct regmap_config jnx_fan_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = I2CS_FAN_SPARE_OE,
+};
+
+/* Fan speed */
+static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 1);
+static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 2);
+static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 3);
+static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 4);
+static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 5);
+static SENSOR_DEVICE_ATTR_2(fan6_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 6);
+static SENSOR_DEVICE_ATTR_2(fan7_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 7);
+static SENSOR_DEVICE_ATTR_2(fan8_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 8);
+static SENSOR_DEVICE_ATTR_2(fan9_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 9);
+static SENSOR_DEVICE_ATTR_2(fan10_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 10);
+static SENSOR_DEVICE_ATTR_2(fan11_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 11);
+static SENSOR_DEVICE_ATTR_2(fan12_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 12);
+static SENSOR_DEVICE_ATTR_2(fan13_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 13);
+static SENSOR_DEVICE_ATTR_2(fan14_input, S_IRUGO, jnx_fan_show, NULL,
+ I2CS_FAN_TACH, 14);
+
+/* PWM values */
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 4);
+static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 5);
+static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 6);
+static SENSOR_DEVICE_ATTR(pwm7, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 7);
+static SENSOR_DEVICE_ATTR(pwm8, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 8);
+static SENSOR_DEVICE_ATTR(pwm9, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 9);
+static SENSOR_DEVICE_ATTR(pwm10, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 10);
+static SENSOR_DEVICE_ATTR(pwm11, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 11);
+static SENSOR_DEVICE_ATTR(pwm12, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 12);
+static SENSOR_DEVICE_ATTR(pwm13, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 13);
+static SENSOR_DEVICE_ATTR(pwm14, S_IWUSR | S_IRUGO, jnx_fan_show_pwm,
+ jnx_fan_set_pwm, 14);
+
+/* Fan Thresholds */
+
+/* Min */
+static SENSOR_DEVICE_ATTR_2(fan1_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 1);
+static SENSOR_DEVICE_ATTR_2(fan2_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 2);
+static SENSOR_DEVICE_ATTR_2(fan3_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 3);
+static SENSOR_DEVICE_ATTR_2(fan4_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 4);
+static SENSOR_DEVICE_ATTR_2(fan5_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 5);
+static SENSOR_DEVICE_ATTR_2(fan6_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 6);
+static SENSOR_DEVICE_ATTR_2(fan7_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 7);
+static SENSOR_DEVICE_ATTR_2(fan8_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 8);
+static SENSOR_DEVICE_ATTR_2(fan9_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 9);
+static SENSOR_DEVICE_ATTR_2(fan10_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 10);
+static SENSOR_DEVICE_ATTR_2(fan11_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 11);
+static SENSOR_DEVICE_ATTR_2(fan12_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 12);
+static SENSOR_DEVICE_ATTR_2(fan13_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 13);
+static SENSOR_DEVICE_ATTR_2(fan14_min, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MIN_TACH, 14);
+/* Max */
+static SENSOR_DEVICE_ATTR_2(fan1_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 1);
+static SENSOR_DEVICE_ATTR_2(fan2_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 2);
+static SENSOR_DEVICE_ATTR_2(fan3_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 3);
+static SENSOR_DEVICE_ATTR_2(fan4_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 4);
+static SENSOR_DEVICE_ATTR_2(fan5_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 5);
+static SENSOR_DEVICE_ATTR_2(fan6_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 6);
+static SENSOR_DEVICE_ATTR_2(fan7_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 7);
+static SENSOR_DEVICE_ATTR_2(fan8_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 8);
+static SENSOR_DEVICE_ATTR_2(fan9_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 9);
+static SENSOR_DEVICE_ATTR_2(fan10_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 10);
+static SENSOR_DEVICE_ATTR_2(fan11_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 11);
+static SENSOR_DEVICE_ATTR_2(fan12_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 12);
+static SENSOR_DEVICE_ATTR_2(fan13_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 13);
+static SENSOR_DEVICE_ATTR_2(fan14_max, S_IWUSR | S_IRUGO, jnx_fan_show,
+ jnx_fan_set, I2CS_FAN_MAX_TACH, 14);
+
+static struct attribute *jnx_fan_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan9_input.dev_attr.attr,
+ &sensor_dev_attr_fan10_input.dev_attr.attr,
+ &sensor_dev_attr_fan11_input.dev_attr.attr,
+ &sensor_dev_attr_fan12_input.dev_attr.attr,
+ &sensor_dev_attr_fan13_input.dev_attr.attr,
+ &sensor_dev_attr_fan14_input.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm7.dev_attr.attr,
+ &sensor_dev_attr_pwm8.dev_attr.attr,
+ &sensor_dev_attr_pwm9.dev_attr.attr,
+ &sensor_dev_attr_pwm10.dev_attr.attr,
+ &sensor_dev_attr_pwm11.dev_attr.attr,
+ &sensor_dev_attr_pwm12.dev_attr.attr,
+ &sensor_dev_attr_pwm13.dev_attr.attr,
+ &sensor_dev_attr_pwm14.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan8_min.dev_attr.attr,
+ &sensor_dev_attr_fan9_min.dev_attr.attr,
+ &sensor_dev_attr_fan10_min.dev_attr.attr,
+ &sensor_dev_attr_fan11_min.dev_attr.attr,
+ &sensor_dev_attr_fan12_min.dev_attr.attr,
+ &sensor_dev_attr_fan13_min.dev_attr.attr,
+ &sensor_dev_attr_fan14_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_max.dev_attr.attr,
+ &sensor_dev_attr_fan2_max.dev_attr.attr,
+ &sensor_dev_attr_fan3_max.dev_attr.attr,
+ &sensor_dev_attr_fan4_max.dev_attr.attr,
+ &sensor_dev_attr_fan5_max.dev_attr.attr,
+ &sensor_dev_attr_fan6_max.dev_attr.attr,
+ &sensor_dev_attr_fan7_max.dev_attr.attr,
+ &sensor_dev_attr_fan8_max.dev_attr.attr,
+ &sensor_dev_attr_fan9_max.dev_attr.attr,
+ &sensor_dev_attr_fan10_max.dev_attr.attr,
+ &sensor_dev_attr_fan11_max.dev_attr.attr,
+ &sensor_dev_attr_fan12_max.dev_attr.attr,
+ &sensor_dev_attr_fan13_max.dev_attr.attr,
+ &sensor_dev_attr_fan14_max.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group jnx_fan_group = {
+ .attrs = jnx_fan_attrs,
+ .is_visible = jnx_fan_is_visible,
+};
+__ATTRIBUTE_GROUPS(jnx_fan);
+
+static int jnx_fan_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct i2cs_fan_platform_data *pdata = dev_get_platdata(dev);
+ struct i2c_client *client;
+ struct jnx_fan_data *data;
+
+ if (!dev->parent)
+ return -ENODEV;
+
+ client = i2c_verify_client(dev->parent);
+ if (!client)
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct jnx_fan_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &jnx_fan_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ if (pdata) {
+ data->num_fans = pdata->num_fans;
+ data->factor = pdata->factor;
+ } else {
+ data->num_fans = NUM_FANS_PER_TRAY;
+ data->factor = FAN_TACH_FACTOR;
+ }
+
+ if (dev->of_node) {
+ of_property_read_u32(dev->of_node,
+ "num-fans", &data->num_fans);
+ of_property_read_u32(dev->of_node,
+ "tach-factor", &data->factor);
+ }
+
+ data->fan_index = -1;
+ mutex_init(&data->update_lock);
+
+ platform_set_drvdata(pdev, data);
+
+ data->hwmon_dev = hwmon_device_register_with_groups(dev->parent,
+ "i2cs_fan", data,
+ jnx_fan_groups);
+ return PTR_ERR_OR_ZERO(data->hwmon_dev);
+}
+
+static int jnx_fan_remove(struct platform_device *pdev)
+{
+ struct jnx_fan_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ return 0;
+}
+
+static const struct of_device_id jnx_fan_of_match[] = {
+ { .compatible = "jnx,i2cs-fan-hwmon", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, jnx_fan_of_match);
+
+static struct platform_driver jnx_fan_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(jnx_fan_of_match),
+ },
+ .probe = jnx_fan_probe,
+ .remove = jnx_fan_remove,
+};
+
+module_platform_driver(jnx_fan_driver);
+
+MODULE_AUTHOR("Avirup Banerjee <[email protected]>");
+MODULE_DESCRIPTION("JNPR FAN driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/include/linux/platform_data/jnx-i2cs-fan.h b/include/linux/platform_data/jnx-i2cs-fan.h
new file mode 100644
index 0000000..b3fc8c2
--- /dev/null
+++ b/include/linux/platform_data/jnx-i2cs-fan.h
@@ -0,0 +1,13 @@
+/*
+ * i2cs-fan.h
+ */
+
+#ifndef I2CS_FAN_H
+#define I2CS_FAN_H
+
+struct i2cs_fan_platform_data {
+ int num_fans; /* Number of fans in tray */
+ int factor; /* fan speed multiplication factor */
+};
+
+#endif /* I2CS_FAN_H */
--
1.9.1

2016-10-07 15:25:17

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 10/10] hwmon: i2cs-fan: Add hwmon dts binding documentation

From: Georgi Vlaev <[email protected]>

Adds the I2CS Fan Tray hwmon device tree node documentation.

Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
Documentation/devicetree/bindings/hwmon/i2cs-fan.txt | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/i2cs-fan.txt

diff --git a/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt b/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
new file mode 100644
index 0000000..4ef880c
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
@@ -0,0 +1,19 @@
+Hwmon driver for Juniper fan trays on I2CS Slave FPGA
+
+Required properties:
+
+- compatible: "i2cs-fan-hwmon"
+
+Optional properties:
+
+- num-fans: fans per tray (default 14)
+
+- tach-factor: TACH count scaling factor (default 120)
+
+Example:
+
+fan-hwmon {
+ compatible = "jnx,i2cs-fan-hwmon";
+ num-fans = <6>;
+ tach-factor = <33>;
+};
--
1.9.1

2016-10-07 15:25:29

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 05/10] gpio: i2cs: Juniper I2CS to GPIO pin mapping driver

From: Guenter Roeck <[email protected]>

This driver maps I2C slave register bits to GPIO pins. Registers
are supposed to be 8 bit wide. Interrupt support is optional.

The driver is implemented as client of the I2CS MFD driver.

Signed-off-by: Georgi Vlaev <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/gpio/Kconfig | 11 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-jnx-i2cs.c | 523 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 535 insertions(+)
create mode 100644 drivers/gpio/gpio-jnx-i2cs.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ef8f408..34840e9 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -746,6 +746,17 @@ config GPIO_ADNP
enough to represent all pins, but the driver will assume a
register layout for 64 pins (8 registers).

+config GPIO_JNX_I2CS
+ tristate "Juniper I2C slave GPIO driver"
+ depends on I2C
+ depends on MFD_JUNIPER_I2CS
+ help
+ This driver maps I2C slave register bits to GPIO pins.
+ Mapping is configured through devicetree data.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-jnx-i2cs.
+
config GPIO_MAX7300
tristate "Maxim MAX7300 GPIO expander"
select GPIO_MAX730X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 825c2636..06d5d51 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
+obj-$(CONFIG_GPIO_JNX_I2CS) += gpio-jnx-i2cs.o
obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
diff --git a/drivers/gpio/gpio-jnx-i2cs.c b/drivers/gpio/gpio-jnx-i2cs.c
new file mode 100644
index 0000000..3a87b6a
--- /dev/null
+++ b/drivers/gpio/gpio-jnx-i2cs.c
@@ -0,0 +1,523 @@
+/*
+ * I2C -> GPIO mapping driver
+ * Copyright (c) 2013 Juniper Networks
+ *
+ * Derived from gpio-adnp.c
+ * Copyright (C) 2011-2012 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+struct i2c_gpio_map {
+ u8 reg; /* register offset */
+ u8 direction; /* direction (in/out) for each bit */
+ u8 value; /* cached value */
+ u8 irq_enable;
+ u8 irq_level;
+ u8 irq_rise;
+ u8 irq_fall;
+ u8 irq_high;
+ u8 irq_low;
+};
+
+struct i2cs_gpio {
+ struct i2c_client *client; /* platform's device parent client */
+ struct device *dev; /* our device */
+ struct gpio_chip gpio;
+ int irq;
+
+ struct mutex i2c_lock;
+
+ struct irq_domain *domain;
+ struct mutex irq_lock;
+
+ struct delayed_work work;
+
+ int num_regs;
+ struct i2c_gpio_map *map;
+};
+
+static inline struct i2cs_gpio *to_i2cs_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct i2cs_gpio, gpio);
+}
+
+static int i2cs_gpio_read_byte_data(struct i2c_client *client, u8 reg)
+{
+ int val, retries;
+
+ /*
+ * i2c slave reads fail once in a while for no obvious reason.
+ * Retry on any error code.
+ */
+ for (retries = 0; retries < 10; retries++) {
+ val = i2c_smbus_read_byte_data(client, reg);
+ if (val >= 0)
+ break;
+ }
+ return val;
+}
+
+static int i2cs_gpio_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct i2cs_gpio *i2cs_gpio = to_i2cs_gpio(chip);
+ struct i2c_gpio_map *map = &i2cs_gpio->map[offset >> 3];
+ struct i2c_client *client = i2cs_gpio->client;
+ u8 pos = offset & 7;
+ u8 reg = map->reg;
+ int val;
+
+ val = i2cs_gpio_read_byte_data(client, reg);
+ if (val < 0)
+ return val;
+
+ map->value = val;
+
+ return !!(val & BIT(pos));
+}
+
+static void __i2cs_gpio_gpio_set(struct i2cs_gpio *i2cs_gpio,
+ unsigned int offset, int value)
+{
+ struct i2c_gpio_map *map = &i2cs_gpio->map[offset >> 3];
+ struct i2c_client *client = i2cs_gpio->client;
+ u8 pos = offset & 7;
+ u8 reg = map->reg;
+ int val;
+
+ val = i2cs_gpio_read_byte_data(client, reg);
+ if (val < 0)
+ return;
+
+ if (value)
+ val |= BIT(pos);
+ else
+ val &= ~BIT(pos);
+
+ map->value = val;
+ i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static void i2cs_gpio_gpio_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct i2cs_gpio *i2cs_gpio = to_i2cs_gpio(chip);
+
+ mutex_lock(&i2cs_gpio->i2c_lock);
+ __i2cs_gpio_gpio_set(i2cs_gpio, offset, value);
+ mutex_unlock(&i2cs_gpio->i2c_lock);
+}
+
+static int i2cs_gpio_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct i2cs_gpio *i2cs_gpio = to_i2cs_gpio(chip);
+ struct i2c_gpio_map *map = &i2cs_gpio->map[offset >> 3];
+ u8 pos = offset & 7;
+
+ /*
+ * Direction is determined by devicetree data and can not be
+ * overwritten.
+ */
+ return (map->direction & BIT(pos)) ? 0 : -EACCES;
+}
+
+static int i2cs_gpio_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct i2cs_gpio *i2cs_gpio = to_i2cs_gpio(chip);
+ struct i2c_gpio_map *map = &i2cs_gpio->map[offset >> 3];
+ u8 pos = offset & 7;
+
+ /*
+ * Direction is determined by devicetree data and can not be
+ * overwritten.
+ */
+ return (map->direction & BIT(pos)) ? -EACCES : 0;
+}
+
+static int i2cs_gpio_gpio_setup(struct i2cs_gpio *i2cs_gpio,
+ unsigned int num_gpios)
+{
+ struct gpio_chip *chip = &i2cs_gpio->gpio;
+ struct i2c_client *client = i2cs_gpio->client;
+ char *name;
+
+ name = devm_kzalloc(i2cs_gpio->dev, 64, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ scnprintf(name, 64, "%s-%d-%02x", dev_name(i2cs_gpio->dev),
+ i2c_adapter_id(client->adapter), client->addr);
+
+ chip->direction_input = i2cs_gpio_gpio_direction_input;
+ chip->direction_output = i2cs_gpio_gpio_direction_output;
+ chip->get = i2cs_gpio_gpio_get;
+ chip->set = i2cs_gpio_gpio_set;
+ chip->can_sleep = 1;
+
+ chip->base = -1;
+ chip->ngpio = num_gpios;
+ chip->label = name;
+ chip->parent = i2cs_gpio->dev;
+ chip->of_node = chip->parent->of_node;
+ chip->owner = THIS_MODULE;
+
+ return 0;
+}
+
+static void i2cs_gpio_irq_work(struct i2cs_gpio *i2cs_gpio)
+{
+ unsigned int i;
+
+ for (i = 0; i < i2cs_gpio->num_regs; i++) {
+ struct i2c_gpio_map *map = &i2cs_gpio->map[i];
+ unsigned int base = i << 3, bit;
+ unsigned long pending;
+ u8 changed, level;
+
+ /* Don't read from i2c bus if interrupts are disabled */
+ if (!map->irq_enable)
+ continue;
+
+ level = i2cs_gpio_read_byte_data(i2cs_gpio->client, map->reg);
+ if (level < 0)
+ continue;
+
+ /* determine if bit changed levels */
+ changed = level ^ map->value;
+
+ /* compute edge-triggered interrupts */
+ pending = changed & ((map->irq_fall & ~level) |
+ (map->irq_rise & level));
+
+ /* add in level-triggered interrupts */
+ pending |= (map->irq_high & level) |
+ (map->irq_low & ~level);
+
+ /* mask out disabled interrupts */
+ pending &= map->irq_enable;
+
+ for_each_set_bit(bit, &pending, 8) {
+ unsigned int virq;
+
+ virq = irq_find_mapping(i2cs_gpio->domain, base + bit);
+ handle_nested_irq(virq);
+ }
+ map->value = level;
+ }
+}
+
+static irqreturn_t i2cs_gpio_irq_handler(int irq, void *data)
+{
+ i2cs_gpio_irq_work(data);
+
+ return IRQ_HANDLED;
+}
+
+static void i2cs_gpio_worker(struct work_struct *work)
+{
+ struct i2cs_gpio *i2cs_gpio;
+
+ i2cs_gpio = container_of(work, struct i2cs_gpio, work.work);
+ i2cs_gpio_irq_work(i2cs_gpio);
+ schedule_delayed_work(&i2cs_gpio->work, msecs_to_jiffies(100));
+}
+
+static int i2cs_gpio_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct i2cs_gpio *i2cs_gpio = to_i2cs_gpio(chip);
+
+ return irq_create_mapping(i2cs_gpio->domain, offset);
+}
+
+static void i2cs_gpio_irq_mask(struct irq_data *data)
+{
+ struct i2cs_gpio *i2cs_gpio = irq_data_get_irq_chip_data(data);
+ struct i2c_gpio_map *map = &i2cs_gpio->map[data->hwirq >> 3];
+ unsigned int pos = data->hwirq & 7;
+
+ map->irq_enable &= ~BIT(pos);
+}
+
+static void i2cs_gpio_irq_unmask(struct irq_data *data)
+{
+ struct i2cs_gpio *i2cs_gpio = irq_data_get_irq_chip_data(data);
+ struct i2c_gpio_map *map = &i2cs_gpio->map[data->hwirq >> 3];
+ unsigned int pos = data->hwirq & 7;
+
+ map->irq_enable |= BIT(pos);
+}
+
+static int i2cs_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct i2cs_gpio *i2cs_gpio = irq_data_get_irq_chip_data(data);
+ struct i2c_gpio_map *map = &i2cs_gpio->map[data->hwirq >> 3];
+ unsigned int pos = data->hwirq & 7;
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ map->irq_rise |= BIT(pos);
+ else
+ map->irq_rise &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ map->irq_fall |= BIT(pos);
+ else
+ map->irq_fall &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ map->irq_high |= BIT(pos);
+ else
+ map->irq_high &= ~BIT(pos);
+
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ map->irq_low |= BIT(pos);
+ else
+ map->irq_low &= ~BIT(pos);
+
+ return 0;
+}
+
+static void i2cs_gpio_irq_bus_lock(struct irq_data *data)
+{
+ struct i2cs_gpio *i2cs_gpio = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&i2cs_gpio->irq_lock);
+}
+
+static void i2cs_gpio_irq_bus_unlock(struct irq_data *data)
+{
+ struct i2cs_gpio *i2cs_gpio = irq_data_get_irq_chip_data(data);
+
+ mutex_unlock(&i2cs_gpio->irq_lock);
+}
+
+static struct irq_chip i2cs_gpio_irq_chip = {
+ .name = "jnx-gpio-i2cs",
+ .irq_mask = i2cs_gpio_irq_mask,
+ .irq_unmask = i2cs_gpio_irq_unmask,
+ .irq_set_type = i2cs_gpio_irq_set_type,
+ .irq_bus_lock = i2cs_gpio_irq_bus_lock,
+ .irq_bus_sync_unlock = i2cs_gpio_irq_bus_unlock,
+};
+
+static int i2cs_gpio_irq_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, domain->host_data);
+ irq_set_chip(irq, &i2cs_gpio_irq_chip);
+ irq_set_nested_thread(irq, true);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops i2cs_gpio_irq_domain_ops = {
+ .map = i2cs_gpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int i2cs_gpio_irq_setup(struct i2cs_gpio *i2cs_gpio)
+{
+ struct i2c_client *client = i2cs_gpio->client;
+ struct gpio_chip *chip = &i2cs_gpio->gpio;
+ int i, val, err;
+
+ mutex_init(&i2cs_gpio->irq_lock);
+
+ /* Cache initial register values */
+ for (i = 0; i < i2cs_gpio->num_regs; i++) {
+ struct i2c_gpio_map *map = &i2cs_gpio->map[i];
+
+ val = i2cs_gpio_read_byte_data(client, map->reg);
+ if (val < 0) {
+ dev_err(i2cs_gpio->dev,
+ "Failed to read register 0x%x: %d\n",
+ map->reg, val);
+ return val;
+ }
+ map->value = val;
+ }
+
+ i2cs_gpio->domain = irq_domain_add_linear(chip->of_node, chip->ngpio,
+ &i2cs_gpio_irq_domain_ops,
+ i2cs_gpio);
+
+ INIT_DELAYED_WORK(&i2cs_gpio->work, i2cs_gpio_worker);
+
+ if (i2cs_gpio->irq) {
+ err = request_threaded_irq(i2cs_gpio->irq, NULL,
+ i2cs_gpio_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(chip->parent), i2cs_gpio);
+ if (err) {
+ dev_err(chip->parent, "can't request IRQ#%d: %d\n",
+ i2cs_gpio->irq, err);
+ goto error;
+ }
+ } else {
+ schedule_delayed_work(&i2cs_gpio->work, HZ / 10);
+ }
+
+ chip->to_irq = i2cs_gpio_gpio_to_irq;
+ return 0;
+
+error:
+ irq_domain_remove(i2cs_gpio->domain);
+ return err;
+}
+
+static void i2cs_gpio_irq_teardown(struct i2cs_gpio *i2cs_gpio)
+{
+ unsigned int irq, i;
+
+ if (i2cs_gpio->irq)
+ free_irq(i2cs_gpio->irq, i2cs_gpio);
+ else
+ cancel_delayed_work_sync(&i2cs_gpio->work);
+
+ for (i = 0; i < i2cs_gpio->gpio.ngpio; i++) {
+ irq = irq_find_mapping(i2cs_gpio->domain, i);
+ if (irq > 0)
+ irq_dispose_mapping(irq);
+ }
+
+ irq_domain_remove(i2cs_gpio->domain);
+}
+
+static int i2cs_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct i2c_client *client;
+ struct i2cs_gpio *i2cs_gpio;
+ struct property *prop;
+ int num_regs;
+ int i, err;
+
+ if (!dev->parent)
+ return -ENODEV;
+
+ client = i2c_verify_client(dev->parent);
+ if (!client)
+ return -ENODEV;
+
+ i2cs_gpio = devm_kzalloc(dev, sizeof(*i2cs_gpio), GFP_KERNEL);
+ if (!i2cs_gpio)
+ return -ENOMEM;
+
+ prop = of_find_property(np, "i2c-gpio-map", &num_regs);
+ if (!prop)
+ return -EINVAL;
+ num_regs /= sizeof(u32);
+ if (!num_regs || (num_regs & 1))
+ return -EINVAL;
+ num_regs /= 2;
+
+ /*
+ * If irq_of_parse_and_map() fails (returns 0), assume that
+ * no interrupts are configured and that we need to poll instead.
+ * We don't support deferred probes for this driver.
+ */
+ i2cs_gpio->irq = irq_of_parse_and_map(np, 0);
+ i2cs_gpio->dev = dev;
+ i2cs_gpio->num_regs = num_regs;
+ i2cs_gpio->map = devm_kzalloc(dev,
+ num_regs * sizeof(struct i2c_gpio_map),
+ GFP_KERNEL);
+ if (!i2cs_gpio->map)
+ return -ENOMEM;
+
+ for (i = 0; i < num_regs; i++) {
+ struct i2c_gpio_map *map = &i2cs_gpio->map[i];
+ u32 val;
+
+ err = of_property_read_u32_index(np, "i2c-gpio-map", i * 2,
+ &val);
+ if (err)
+ return err;
+ if (val > 0xff)
+ return -EINVAL;
+ map->reg = val;
+
+ err = of_property_read_u32_index(np, "i2c-gpio-map", i * 2 + 1,
+ &val);
+ if (err)
+ return err;
+ if (val > 0xff)
+ return -EINVAL;
+ map->direction = val;
+ }
+
+ mutex_init(&i2cs_gpio->i2c_lock);
+ i2cs_gpio->client = client;
+
+ err = i2cs_gpio_gpio_setup(i2cs_gpio, num_regs << 3);
+ if (err < 0)
+ return err;
+
+ if (of_find_property(np, "interrupt-controller", NULL)) {
+ err = i2cs_gpio_irq_setup(i2cs_gpio);
+ if (err < 0)
+ return err;
+ }
+
+ err = gpiochip_add(&i2cs_gpio->gpio);
+ if (err < 0)
+ goto teardown;
+
+ platform_set_drvdata(pdev, i2cs_gpio);
+ return 0;
+
+teardown:
+ if (of_find_property(np, "interrupt-controller", NULL))
+ i2cs_gpio_irq_teardown(i2cs_gpio);
+
+ return err;
+}
+
+static int i2cs_gpio_remove(struct platform_device *pdev)
+{
+ struct i2cs_gpio *i2cs_gpio = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+
+ gpiochip_remove(&i2cs_gpio->gpio);
+
+ if (of_find_property(np, "interrupt-controller", NULL))
+ i2cs_gpio_irq_teardown(i2cs_gpio);
+
+ return 0;
+}
+
+static const struct of_device_id i2cs_gpio_of_match[] = {
+ { .compatible = "jnx,gpio-i2cs", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, i2cs_gpio_of_match);
+
+static struct platform_driver i2cs_gpio_driver = {
+ .driver = {
+ .name = "gpio-jnx-i2cs",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(i2cs_gpio_of_match),
+ },
+ .probe = i2cs_gpio_probe,
+ .remove = i2cs_gpio_remove,
+};
+module_platform_driver(i2cs_gpio_driver);
+
+MODULE_DESCRIPTION("Juniper Networks I2C to GPIO mapping driver");
+MODULE_AUTHOR("Guenter Roeck <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.9.1

2016-10-07 15:24:37

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 06/10] gpio: gpio-i2cs: Document bindings of I2CS FPGA GPIO block

From: Georgi Vlaev <[email protected]>

Add device tree bindings document for the GPIO driver of
Juniper's I2CS FPGA.

Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
.../devicetree/bindings/gpio/jnx,gpio-i2cs.txt | 43 ++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-i2cs.txt

diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-i2cs.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-i2cs.txt
new file mode 100644
index 0000000..835b7fb4
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-i2cs.txt
@@ -0,0 +1,43 @@
+Juniper I2CS FPGA GPIO presence block
+
+This is virtual gpio driver, that maps each bit of the I2CS FPGA to
+a gpio. It's used as a compatibility replacement for FRUs that use
+I2CS FPGA to report presence, control and report power status in
+the Juniper's driver infra that uses gpios. Compatible with any I2CS.
+
+Required properties:
+
+Required properties:
+- compatible: "jnx,gpio-i2cs"
+
+- reg:
+ Address on the I2C bus of the I2CS FPGA
+
+- gpio-controller: Marks the device node as a gpio controller.
+
+- interrupt-controller: Marks the device node as an interrupt
+ controller.
+
+- i2c-gpio-map: Map of "I2CS register" and "direction". The registers
+ are 8 bit wide, each bit of the register is mapped to either
+ input or output depending on the bits of the "direction". If
+ the bit in the direction is 1, then that bit from the I2CS
+ register is mapped to gpio input, otherwise to gpio output.
+
+- #gpio-cells : Should be two. The first cell is the pin number and
+ the second cell is used to specify the gpio polarity:
+ 0 = active high
+ 1 = active low
+
+Optional properties:
+
+Example:
+
+cb0_slave: i2c-slave@54 {
+ compatible = "jnx,gpio-i2cs";
+ reg = <0x54>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ i2c-gpio-map = <0x21 0xff>; /* power status (bit 6) */
+};
--
1.9.1

2016-10-07 15:33:36

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 03/10] i2c/muxes: Juniper I2CS RE mux

From: Georgi Vlaev <[email protected]>

Add support for Juniper I2C Slave RE multiplexer driver.

This I2C multiplexer driver allows the RE to access some of
the FPC I2C buses. It's compatible only with the FPC variant of the
I2CS.

Signed-off-by: Georgi Vlaev <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/i2c/muxes/Kconfig | 10 +++
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-i2cs.c | 155 +++++++++++++++++++++++++++++++++++++++
3 files changed, 166 insertions(+)
create mode 100644 drivers/i2c/muxes/i2c-mux-i2cs.c

diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index f45a9cb..c95380d 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -30,6 +30,16 @@ config I2C_MUX_GPIO
This driver can also be built as a module. If so, the module
will be called i2c-mux-gpio.

+config I2C_MUX_I2CS
+ tristate "Juniper I2C Slave MFD client RE multiplexer"
+ depends on MFD_JUNIPER_I2CS
+ help
+ Select this to enable the Juniper I2C Slave RE multiplexer driver
+ on the relevant Juniper platforms.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-i2cs.
+
config I2C_MUX_PCA9541
tristate "NXP PCA9541 I2C Master Selector"
help
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 78d8cba..45b4287 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o

obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
+obj-$(CONFIG_I2C_MUX_I2CS) += i2c-mux-i2cs.o
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
diff --git a/drivers/i2c/muxes/i2c-mux-i2cs.c b/drivers/i2c/muxes/i2c-mux-i2cs.c
new file mode 100644
index 0000000..c498a44
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-i2cs.c
@@ -0,0 +1,155 @@
+/*
+ * Juniper Networks I2CS RE mux driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/jnx-i2cs-core.h>
+
+#define MISC_IO_RE_EN 0x01
+
+/*
+ * Read/write to mux register.
+ * Don't use i2c_transfer()/i2c_smbus_xfer()
+ * for this as they will try to lock adapter a second time
+ */
+static int i2cs_mux_read_byte(struct i2c_client *client,
+ u8 offset, u8 *val)
+{
+ struct i2c_msg msg[2];
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &offset;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = val;
+
+ return client->adapter->algo->master_xfer(client->adapter, msg, 2);
+}
+
+static int i2cs_mux_write_byte(struct i2c_client *client, u8 offset, u8 val)
+{
+ struct i2c_msg msg;
+ char buf[2];
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 2;
+ buf[0] = offset;
+ buf[1] = val;
+ msg.buf = buf;
+
+ return client->adapter->algo->master_xfer(client->adapter, &msg, 1);
+}
+
+static int i2cs_mux_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ int ret;
+ u8 val = 0;
+ struct i2c_client *client = i2c_mux_priv(muxc);
+
+ ret = i2cs_mux_read_byte(client, I2CS_MISC_IO, &val);
+ if (ret < 0)
+ return ret;
+
+ val |= MISC_IO_RE_EN;
+ ret = i2cs_mux_write_byte(client, I2CS_MISC_IO, val);
+
+ return ret;
+}
+
+static int i2cs_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ int ret;
+ u8 val = 0;
+ struct i2c_client *client = i2c_mux_priv(muxc);
+
+ ret = i2cs_mux_read_byte(client, I2CS_MISC_IO, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= ~MISC_IO_RE_EN;
+ ret = i2cs_mux_write_byte(client, I2CS_MISC_IO, val);
+
+ return 0;
+}
+
+static int i2cs_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct i2c_client *client;
+ struct i2c_mux_core *muxc;
+ int ret;
+
+ if (!dev->parent)
+ return -ENODEV;
+
+ client = i2c_verify_client(dev->parent);
+ if (!client)
+ return -ENODEV;
+
+ muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
+ i2cs_mux_select, i2cs_mux_deselect);
+ if (!muxc)
+ return -ENOMEM;
+ muxc->priv = client;
+
+ ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, muxc);
+
+ return 0;
+}
+
+static int i2cs_mux_remove(struct platform_device *pdev)
+{
+ i2c_mux_del_adapters(platform_get_drvdata(pdev));
+
+ return 0;
+}
+
+static const struct of_device_id i2cs_mux_of_match[] = {
+ { .compatible = "jnx,i2c-mux-i2cs", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2cs_mux_of_match);
+
+static struct platform_driver i2cs_mux_driver = {
+ .driver = {
+ .name = "i2c-mux-i2cs",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(i2cs_mux_of_match),
+ },
+ .probe = i2cs_mux_probe,
+ .remove = i2cs_mux_remove,
+};
+module_platform_driver(i2cs_mux_driver);
+
+MODULE_DESCRIPTION("Juniper Networks I2CS RE Mux driver");
+MODULE_AUTHOR("Georgi Vlaev <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c-mux-i2cs");
--
1.9.1

2016-10-07 15:33:47

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 02/10] mfd: dt-bindings: Add bindings for the Juniper I2CS MFD

From: Georgi Vlaev <[email protected]>

Add device tree bindings for the Juniper I2CS MFD driver.

Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
Documentation/devicetree/bindings/mfd/jnx-i2cs.txt | 68 ++++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/jnx-i2cs.txt

diff --git a/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt b/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
new file mode 100644
index 0000000..0ec103b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
@@ -0,0 +1,68 @@
+Device-Tree bindings for Juniper Networks I2CS FPGA MFD
+
+Required properties:
+- compatible - Must be one of:
+ "jnx,i2cs-rcb" (Routing Engine or Control Board FRUs)
+ "jnx,i2cs-fpc" (Flexible Port Concentrator FRUs)
+ "jnx,i2cs-sib" (Switching Interface Board FRUs)
+ "jnx,i2cs-fan" (Fan Tray FRUs)
+
+Optional properties:
+
+Depending on the FRU, the I2CS MFD has varied group of sub-devices:
+
+Device Description
+------ -----------
+jnx,i2cs-gpio : Virtual gpio mapping driver
+jnx,i2cs-fan-hwmon : hwmon driver for fan trays
+jnx,i2c-mux-i2cs : I2C Mux driver for FPC FRUs
+jnx,leds-i2cs : Led driver
+
+All these optional nodes are described in their respective binding
+documents.
+
+Example node:
+
+i2cs@54 {
+ compatible = "jnx,i2cs-fpc";
+ reg = <0x54>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+
+ fpc2_mux {
+ compatible = "jnx,i2c-mux-i2cs";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fpc2i2c0: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+ };
+
+ fpc2_gpiomap: gpio-jnx-i2cs {
+ compatible = "jnx,gpio-i2cs";
+ reg = <0x54>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+
+ /*
+ * Map bits [0-3] of reg 0x21 as gpio inputs, bits [4-7]
+ * as gpio outputs
+ */
+ i2c-gpio-map = <0x21 0x0f>;
+ };
+
+ leds-jnx-i2cs {
+ compatible = "jnx,leds-i2cs";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sib8-active {
+ reg = <0>;
+ linux,default-trigger = "sib8-active";
+ };
+ };
+};
--
1.9.1

2016-10-07 15:33:57

by Pantelis Antoniou

[permalink] [raw]
Subject: [PATCH 04/10] i2c: i2c-mux-i2cs: Add device tree bindings

From: Georgi Vlaev <[email protected]>

Add binding document for the i2c mux driver of Juniper's I2CS FPGA.

Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
.../devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt

diff --git a/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
new file mode 100644
index 0000000..03d917f
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
@@ -0,0 +1,27 @@
+* Juniper I2C Mux on I2CS
+
+ I2C mux driver for switching the RE access to the FPC i2c bus.
+ Compatible with the FPC variant of the I2CS.
+
+Required properties:
+
+ - compatible: "jnx,i2cs-mux-i2cs".
+
+The following required properties are defined externally:
+
+ - Standard I2C mux properties. See i2c-mux.txt in this directory.
+ - I2C child bus nodes. See i2c-mux.txt in this directory.
+
+Example:
+
+fpc0_mux {
+ compatible = "jnx,i2c-mux-i2cs";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fpc0i2c0: i2c@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ };
+};
--
1.9.1

2016-10-10 09:41:51

by Jacek Anaszewski

[permalink] [raw]
Subject: Re: [PATCH 07/10] leds: i2cs: Add I2CS FPGA leds driver

Hi Pantelis,

Thanks for the patch. Please find my comments in the code below.

On 10/07/2016 05:21 PM, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Add support for the FRU faceplate status LEDs (OK, FAIL,
> ACTIVE, STANDBY) controlled by the Juniper I2CS FPGA. This
> driver is a jnx-i2cs-core client.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> drivers/leds/Kconfig | 9 ++
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-jnx-i2cs.c | 219 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 229 insertions(+)
> create mode 100644 drivers/leds/leds-jnx-i2cs.c
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 7a628c6..45c6612 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -659,6 +659,15 @@ config LEDS_MLXCPLD
> This option enabled support for the LEDs on the Mellanox
> boards. Say Y to enabled these.
>
> +config LEDS_JNX_I2CS
> + tristate "LED support for the Juniper Networks I2CS FPGA"
> + depends on LEDS_CLASS && I2C
> + select REGMAP_I2C
> + help
> + This option enables support for the FRU faceplate status
> + LEDs (OK, FAIL, ACTIVE, STANDBY) controlled by the Juniper
> + I2CS FPGA.
> +
> comment "LED Triggers"
> source "drivers/leds/trigger/Kconfig"
>
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 3965070..1ce2d0b 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -71,6 +71,7 @@ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
> obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
> obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
> obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
> +obj-$(CONFIG_LEDS_JNX_I2CS) += leds-jnx-i2cs.o
>
> # LED SPI Drivers
> obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
> diff --git a/drivers/leds/leds-jnx-i2cs.c b/drivers/leds/leds-jnx-i2cs.c
> new file mode 100644
> index 0000000..c2d7274
> --- /dev/null
> +++ b/drivers/leds/leds-jnx-i2cs.c
> @@ -0,0 +1,219 @@
> +/*
> + * Juniper Networks I2CS FPGA LEDs driver
> + *
> + * Copyright (C) 2016 Juniper Networks
> + * Author: Georgi Vlaev <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/of.h>
> +#include <linux/delay.h>
> +#include <linux/leds.h>
> +#include <linux/i2c.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/jnx-i2cs-core.h>

Please arrange include directives in alphabetical order.

> +#define FRU_LEDS 4 /* Total LEDs (active, fail, ok, standby) */
> +#define HW_BLINK_LEDS 3 /* LEDs with hw blink cap (standby not supported) */
> +
> +/*
> + * I2CS fru_led [0x12]
> + *
> + * bit 6 | bit 5 | bit 4 |... | bit 0
> + * blink_ok|blink_fail|blink_act|led_standby|led_ok|led_fail|led_act
> + */
> +
> +/* TODO: Use the regmap from the parent MFD */

Isn't it a good moment to address that?

> +static struct regmap_config i2cs_leds_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = I2CS_SPARE_OE,
> +};
> +
> +struct i2cs_led {
> + struct led_classdev lc;

Most drivers use led_cdev or cdev name for it.
It is more informative and improves readability.

> + struct regmap *regmap;
> + struct work_struct work;
> + int blink;
> + int on;
> + int bit;
> +};
> +
> +struct i2cs_led_data {
> + int num_leds;
> + struct i2cs_led *leds;
> +};
> +
> +static void jnx_i2cs_leds_work(struct work_struct *work)
> +{
> + struct i2cs_led *led = container_of(work, struct i2cs_led, work);
> +
> + int mask = (BIT(led->bit) << 4) | BIT(led->bit);
> + int value = ((led->blink << led->bit) << 4) | (led->on << led->bit);
> +
> + regmap_update_bits(led->regmap, I2CS_FRU_LED, mask, value & 0x7f);
> +}

If you used brightness_set_blocking op instead of brightness_set,
then you could get rid of the in-driver work queue.

> +
> +static void jnx_i2cs_leds_brightness_set(struct led_classdev *lc,
> + enum led_brightness brightness)
> +{
> + struct i2cs_led *led = container_of(lc, struct i2cs_led, lc);
> +
> + led->on = (brightness != LED_OFF);
> + led->blink = 0; /* always turn off hw blink on brightness_set() */

Some time ago we changed brightness setting semantics when blinking
is on. Now setting any brightness > 0 shouldn't disable blinking.
Following commit provides the details:

7cfe749fad51 ("leds: core: Fix brightness setting upon hardware blinking
enabled")

It seems that the hardware supports only one brightness level.
In such a case I'd return immediately if the LED is on and
brightness to be set is 1.

> + schedule_work(&led->work);
> +}
> +
> +static int jnx_i2cs_leds_blink_set(struct led_classdev *lc,
> + unsigned long *delay_on,
> + unsigned long *delay_off)
> +{
> + struct i2cs_led *led = container_of(lc, struct i2cs_led, lc);
> +
> + led->blink = (*delay_on > 0);

blink_set op should fail if hardware doesn't support delay_on
or delay_off periods. I lets the LED core to apply software blink
fallback.

> + led->on = led->blink; /* 'on' bit should be set if blinking */
> + schedule_work(&led->work);
> +
> + return 0;
> +}
> +
> +static int jnx_i2cs_leds_init_one(struct device *dev, struct device_node *np,
> + struct i2cs_led_data *ild,
> + struct regmap *regmap, int num)
> +{
> + struct i2cs_led *led;
> + const char *string;
> + bool hw_blink;
> + int ret;
> + u32 reg;
> +
> + ret = of_property_read_u32(np, "reg", &reg);
> + if (ret || reg >= FRU_LEDS)
> + return -ENODEV;
> +
> + led = &ild->leds[num];
> + led->bit = reg;
> + led->regmap = regmap;
> +
> + if (!of_property_read_string(np, "label", &string))
> + led->lc.name = string;
> + else
> + led->lc.name = np->name;
> +
> + if (!of_property_read_string(np, "linux,default-trigger", &string))
> + led->lc.default_trigger = string;
> +
> + led->lc.brightness = LED_OFF;
> + led->lc.brightness_set = jnx_i2cs_leds_brightness_set;

You need also:

led->lc.max_brightness = 1;

> + if (led->bit <= HW_BLINK_LEDS) {
> + hw_blink = of_property_read_bool(np, "hw-blink");
> + if (hw_blink)
> + led->lc.blink_set = jnx_i2cs_leds_blink_set;
> + }
> +
> + ret = devm_led_classdev_register(dev, &led->lc);
> + if (ret)
> + return ret;
> +
> + INIT_WORK(&led->work, jnx_i2cs_leds_work);
> +
> + return 0;
> +}
> +
> +static int jnx_i2cs_leds_of_init(struct device *dev, struct i2cs_led_data *ild)
> +{
> + struct device_node *child, *np = dev->of_node;
> + struct regmap *regmap;
> + struct i2c_client *client;
> + int ret, num_leds, i = 0;
> +
> + if (!dev->parent)
> + return -ENODEV;
> +
> + client = i2c_verify_client(dev->parent);
> + if (!client)
> + return -ENODEV;
> +
> + regmap = devm_regmap_init_i2c(client, &i2cs_leds_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(dev, "Failed to allocate register map\n");
> + return PTR_ERR(regmap);
> + }
> +
> + num_leds = of_get_child_count(np);
> + if (!num_leds || num_leds > FRU_LEDS)
> + return -ENODEV;
> +
> + ild->num_leds = num_leds;
> + ild->leds = devm_kzalloc(dev, sizeof(struct i2cs_led) * num_leds,
> + GFP_KERNEL);
> + if (!ild->leds)
> + return -ENOMEM;
> +
> + for_each_child_of_node(np, child) {
> + ret = jnx_i2cs_leds_init_one(dev, child, ild, regmap, i++);
> + if (ret)
> + return ret;

of_node_put(child) should be called on error to avoid memory leak.

> + }
> +
> + return 0;
> +}
> +
> +static int jnx_i2cs_leds_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct i2cs_led_data *ild;
> + int ret;
> +
> + ild = devm_kzalloc(dev, sizeof(*ild), GFP_KERNEL);
> + if (!ild)
> + return -ENOMEM;
> +
> + ret = jnx_i2cs_leds_of_init(dev, ild);
> + if (ret < 0)
> + return ret;
> +
> + platform_set_drvdata(pdev, ild);
> +
> + return 0;
> +}
> +
> +static int jnx_i2cs_leds_remove(struct platform_device *pdev)
> +{
> + struct i2cs_led_data *ild = platform_get_drvdata(pdev);
> + int i;
> +
> + for (i = 0; i < ild->num_leds; i++) {
> + devm_led_classdev_unregister(&pdev->dev, &ild->leds[i].lc);

This is redundant.

> + cancel_work_sync(&ild->leds[i].work);
> + }
> +
> + return 0;
> +}

This function will be redundant after removing work queue.

> +static const struct of_device_id jnx_i2cs_leds_match[] = {
> + { .compatible = "jnx,leds-i2cs", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, jnx_i2cs_leds_match);
> +
> +static struct platform_driver jnx_i2cs_leds_driver = {
> + .driver = {
> + .name = "leds-i2cs",
> + .of_match_table = jnx_i2cs_leds_match,
> + },
> + .probe = jnx_i2cs_leds_probe,
> + .remove = jnx_i2cs_leds_remove,
> +};
> +
> +module_platform_driver(jnx_i2cs_leds_driver);
> +
> +MODULE_DESCRIPTION("Juniper Networks I2CS leds driver");
> +MODULE_AUTHOR("Georgi Vlaev <[email protected]>");
> +MODULE_LICENSE("GPL");
>


--
Best regards,
Jacek Anaszewski

2016-10-10 09:41:59

by Jacek Anaszewski

[permalink] [raw]
Subject: Re: [PATCH 08/10] leds: Add binding for Juniper's I2CS FPGA

Hi Pantelis,

On 10/07/2016 05:21 PM, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Document bindings for the I2CS FPGA leds.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> .../devicetree/bindings/leds/leds-i2cs.txt | 34 ++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/leds/leds-i2cs.txt
>
> diff --git a/Documentation/devicetree/bindings/leds/leds-i2cs.txt b/Documentation/devicetree/bindings/leds/leds-i2cs.txt
> new file mode 100644
> index 0000000..100e584
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/leds-i2cs.txt
> @@ -0,0 +1,34 @@
> +Juniper I2CS LED driver.
> +
> +This is present in Juniper platforms that use a I2CS Slave FPGA.
> +
> +Required properties:
> + - compatible: must be "jnx,leds-i2cs"

s/compatible:/compatible :/

Also treat the description as a regular sentence, i.e. begin it
with a capital letter and put a dot at the end.

> + - #address-cells : must be 1.
> + - #size-cells : must be 0.

s/must/Must/

> +Each led is represented as a sub-node of the jnx,leds-i2cs device.
> +
> +LED sub-node properties:
> +- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
> +- reg : number of LED
> +- linux,default-trigger : (optional)
> + see Documentation/devicetree/bindings/leds/common.txt

Driver uses also "hw-blink" property. Please document it here, but
add also a prefix:

jnx,hw-blink

Also "jnx" entry should be added to
Documentation/devicetree/bindings/vendor-prefixes.txt.

> +Example:
> +
> +leds_fpc0: leds-jnx-i2cs {
> + compatible = "jnx,leds-i2cs";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + fpc0-fail {
> + reg = <1>;

It would be good to provide also an example of "label"
property to suggest LED class device name according to
the LED class device naming convention.
See Documentation/leds/leds-class.txt for details.

> + linux,default-trigger = "fpc0-fail";
> + };
> +
> + fpc0-ok {
> + reg = <2>;
> + linux,default-trigger = "fpc0-ok";
> + };
> +};
>


--
Best regards,
Jacek Anaszewski

2016-10-10 18:02:33

by Peter Rosin

[permalink] [raw]
Subject: Re: [PATCH 03/10] i2c/muxes: Juniper I2CS RE mux

On 2016-10-07 17:21, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Add support for Juniper I2C Slave RE multiplexer driver.
>
> This I2C multiplexer driver allows the RE to access some of
> the FPC I2C buses. It's compatible only with the FPC variant of the
> I2CS.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> Signed-off-by: Guenter Roeck <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> drivers/i2c/muxes/Kconfig | 10 +++
> drivers/i2c/muxes/Makefile | 1 +
> drivers/i2c/muxes/i2c-mux-i2cs.c | 155 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 166 insertions(+)
> create mode 100644 drivers/i2c/muxes/i2c-mux-i2cs.c
>
> diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> index f45a9cb..c95380d 100644
> --- a/drivers/i2c/muxes/Kconfig
> +++ b/drivers/i2c/muxes/Kconfig
> @@ -30,6 +30,16 @@ config I2C_MUX_GPIO
> This driver can also be built as a module. If so, the module
> will be called i2c-mux-gpio.
>
> +config I2C_MUX_I2CS
> + tristate "Juniper I2C Slave MFD client RE multiplexer"
> + depends on MFD_JUNIPER_I2CS
> + help
> + Select this to enable the Juniper I2C Slave RE multiplexer driver
> + on the relevant Juniper platforms.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-mux-i2cs.
> +
> config I2C_MUX_PCA9541
> tristate "NXP PCA9541 I2C Master Selector"
> help
> diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
> index 78d8cba..45b4287 100644
> --- a/drivers/i2c/muxes/Makefile
> +++ b/drivers/i2c/muxes/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
> obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
>
> obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
> +obj-$(CONFIG_I2C_MUX_I2CS) += i2c-mux-i2cs.o
> obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
> obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
> obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
> diff --git a/drivers/i2c/muxes/i2c-mux-i2cs.c b/drivers/i2c/muxes/i2c-mux-i2cs.c

Please name the file i2c-mux-jnx-i2cs.c. Or something else a bit more
specific.

> new file mode 100644
> index 0000000..c498a44
> --- /dev/null
> +++ b/drivers/i2c/muxes/i2c-mux-i2cs.c
> @@ -0,0 +1,155 @@
> +/*
> + * Juniper Networks I2CS RE mux driver
> + *
> + * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/init.h>

init?

> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/regmap.h>

regmap?

> +#include <linux/platform_device.h>
> +#include <linux/mfd/jnx-i2cs-core.h>
> +
> +#define MISC_IO_RE_EN 0x01
> +
> +/*
> + * Read/write to mux register.
> + * Don't use i2c_transfer()/i2c_smbus_xfer()
> + * for this as they will try to lock adapter a second time
> + */

If this is the only concern, you should consider making this
gate mux-locked instead of parent-locked. Then you can avoid
all these unlocked games. But there are caveats, so you better
read Documentation/i2c/i2c-topology for the details before
you change.

> +static int i2cs_mux_read_byte(struct i2c_client *client,
> + u8 offset, u8 *val)
> +{
> + struct i2c_msg msg[2];
> +
> + msg[0].addr = client->addr;
> + msg[0].flags = 0;
> + msg[0].len = 1;
> + msg[0].buf = &offset;
> +
> + msg[1].addr = client->addr;
> + msg[1].flags = I2C_M_RD;
> + msg[1].len = 1;
> + msg[1].buf = val;
> +
> + return client->adapter->algo->master_xfer(client->adapter, msg, 2);

If you still want to be parent-locked, use __i2c_transfer. In
either case, consider adding code that handles the case of no
algo->master_xfer (some i2c adapters only support
algo->smbus_xfer).

> +}
> +
> +static int i2cs_mux_write_byte(struct i2c_client *client, u8 offset, u8 val)
> +{
> + struct i2c_msg msg;
> + char buf[2];
> +
> + msg.addr = client->addr;
> + msg.flags = 0;
> + msg.len = 2;
> + buf[0] = offset;
> + buf[1] = val;
> + msg.buf = buf;
> +
> + return client->adapter->algo->master_xfer(client->adapter, &msg, 1);
> +}
> +
> +static int i2cs_mux_select(struct i2c_mux_core *muxc, u32 chan)
> +{
> + int ret;
> + u8 val = 0;
> + struct i2c_client *client = i2c_mux_priv(muxc);
> +
> + ret = i2cs_mux_read_byte(client, I2CS_MISC_IO, &val);
> + if (ret < 0)
> + return ret;
> +
> + val |= MISC_IO_RE_EN;
> + ret = i2cs_mux_write_byte(client, I2CS_MISC_IO, val);
> +

To me, it sounds as if I2CS_MISC_IO is a register with more than
one bit and that you are open to nasty races here. You probably
need to interact with the mfd parent and arrange some locking.

> + return ret;
> +}
> +
> +static int i2cs_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
> +{
> + int ret;
> + u8 val = 0;
> + struct i2c_client *client = i2c_mux_priv(muxc);
> +
> + ret = i2cs_mux_read_byte(client, I2CS_MISC_IO, &val);
> + if (ret < 0)
> + return ret;
> +
> + val &= ~MISC_IO_RE_EN;
> + ret = i2cs_mux_write_byte(client, I2CS_MISC_IO, val);
> +
> + return 0;
> +}
> +
> +static int i2cs_mux_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct i2c_client *client;
> + struct i2c_mux_core *muxc;
> + int ret;
> +
> + if (!dev->parent)
> + return -ENODEV;
> +
> + client = i2c_verify_client(dev->parent);
> + if (!client)
> + return -ENODEV;
> +
> + muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
> + i2cs_mux_select, i2cs_mux_deselect);

You only have one child adapter, making this a gate. Please use
the I2C_MUX_GATE flag which should be available in 4.9. This also
affects the preferred devicetree, see comment on that patch.

> + if (!muxc)
> + return -ENOMEM;
> + muxc->priv = client;
> +
> + ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, muxc);
> +
> + return 0;
> +}
> +
> +static int i2cs_mux_remove(struct platform_device *pdev)
> +{
> + i2c_mux_del_adapters(platform_get_drvdata(pdev));
> +
> + return 0;
> +}
> +
> +static const struct of_device_id i2cs_mux_of_match[] = {
> + { .compatible = "jnx,i2c-mux-i2cs", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, i2cs_mux_of_match);
> +
> +static struct platform_driver i2cs_mux_driver = {
> + .driver = {
> + .name = "i2c-mux-i2cs",
> + .owner = THIS_MODULE,

Drop this line.

Cheers,
Peter

> + .of_match_table = of_match_ptr(i2cs_mux_of_match),
> + },
> + .probe = i2cs_mux_probe,
> + .remove = i2cs_mux_remove,
> +};
> +module_platform_driver(i2cs_mux_driver);
> +
> +MODULE_DESCRIPTION("Juniper Networks I2CS RE Mux driver");
> +MODULE_AUTHOR("Georgi Vlaev <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:i2c-mux-i2cs");
>

2016-10-10 20:26:01

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 04/10] i2c: i2c-mux-i2cs: Add device tree bindings

On Fri, Oct 07, 2016 at 06:21:03PM +0300, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Add binding document for the i2c mux driver of Juniper's I2CS FPGA.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> .../devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt | 27 ++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
>
> diff --git a/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> new file mode 100644
> index 0000000..03d917f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> @@ -0,0 +1,27 @@
> +* Juniper I2C Mux on I2CS
> +
> + I2C mux driver for switching the RE access to the FPC i2c bus.

What is RE?

> + Compatible with the FPC variant of the I2CS.
> +
> +Required properties:
> +
> + - compatible: "jnx,i2cs-mux-i2cs".
> +
> +The following required properties are defined externally:
> +
> + - Standard I2C mux properties. See i2c-mux.txt in this directory.
> + - I2C child bus nodes. See i2c-mux.txt in this directory.
> +
> +Example:
> +
> +fpc0_mux {
> + compatible = "jnx,i2c-mux-i2cs";
> + #address-cells = <1>;
> + #size-cells = <0>;

Needs some address?

> +
> + fpc0i2c0: i2c@0 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <0>;
> + };
> +};
> --
> 1.9.1
>

2016-10-10 20:29:13

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 10/10] hwmon: i2cs-fan: Add hwmon dts binding documentation

On Fri, Oct 07, 2016 at 06:21:09PM +0300, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Adds the I2CS Fan Tray hwmon device tree node documentation.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> Documentation/devicetree/bindings/hwmon/i2cs-fan.txt | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
>
> diff --git a/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt b/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
> new file mode 100644
> index 0000000..4ef880c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
> @@ -0,0 +1,19 @@
> +Hwmon driver for Juniper fan trays on I2CS Slave FPGA

This is a h/w description, not a driver.

> +
> +Required properties:
> +
> +- compatible: "i2cs-fan-hwmon"

I doubt any fan control h/w has "hwmon" in the name.

> +
> +Optional properties:
> +
> +- num-fans: fans per tray (default 14)
> +
> +- tach-factor: TACH count scaling factor (default 120)

vendor prefixes needed.

> +
> +Example:
> +
> +fan-hwmon {
> + compatible = "jnx,i2cs-fan-hwmon";
> + num-fans = <6>;
> + tach-factor = <33>;
> +};
> --
> 1.9.1
>

2016-10-10 20:38:26

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 02/10] mfd: dt-bindings: Add bindings for the Juniper I2CS MFD

On Fri, Oct 07, 2016 at 06:21:01PM +0300, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Add device tree bindings for the Juniper I2CS MFD driver.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> Documentation/devicetree/bindings/mfd/jnx-i2cs.txt | 68 ++++++++++++++++++++++
> 1 file changed, 68 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt b/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
> new file mode 100644
> index 0000000..0ec103b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
> @@ -0,0 +1,68 @@
> +Device-Tree bindings for Juniper Networks I2CS FPGA MFD
> +
> +Required properties:
> +- compatible - Must be one of:
> + "jnx,i2cs-rcb" (Routing Engine or Control Board FRUs)
> + "jnx,i2cs-fpc" (Flexible Port Concentrator FRUs)
> + "jnx,i2cs-sib" (Switching Interface Board FRUs)
> + "jnx,i2cs-fan" (Fan Tray FRUs)
> +
> +Optional properties:
> +
> +Depending on the FRU, the I2CS MFD has varied group of sub-devices:
> +
> +Device Description
> +------ -----------
> +jnx,i2cs-gpio : Virtual gpio mapping driver
> +jnx,i2cs-fan-hwmon : hwmon driver for fan trays
> +jnx,i2c-mux-i2cs : I2C Mux driver for FPC FRUs
> +jnx,leds-i2cs : Led driver
> +
> +All these optional nodes are described in their respective binding
> +documents.
> +
> +Example node:
> +
> +i2cs@54 {
> + compatible = "jnx,i2cs-fpc";
> + reg = <0x54>;
> + #address-cells = <0>;
> + #size-cells = <0>;
> +
> + fpc2_mux {

Generic node names please.

mux {

> + compatible = "jnx,i2c-mux-i2cs";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + fpc2i2c0: i2c@0 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <0>;
> + };
> + };
> +
> + fpc2_gpiomap: gpio-jnx-i2cs {

gpio@54 {

> + compatible = "jnx,gpio-i2cs";
> + reg = <0x54>;
> + #gpio-cells = <2>;
> + gpio-controller;
> + interrupt-controller;
> +
> + /*
> + * Map bits [0-3] of reg 0x21 as gpio inputs, bits [4-7]
> + * as gpio outputs
> + */
> + i2c-gpio-map = <0x21 0x0f>;
> + };
> +
> + leds-jnx-i2cs {

leds {

> + compatible = "jnx,leds-i2cs";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + sib8-active {
> + reg = <0>;
> + linux,default-trigger = "sib8-active";
> + };
> + };
> +};
> --
> 1.9.1
>

2016-10-11 03:25:15

by Peter Rosin

[permalink] [raw]
Subject: Re: [PATCH 04/10] i2c: i2c-mux-i2cs: Add device tree bindings

On 2016-10-07 17:21, Pantelis Antoniou wrote:
> From: Georgi Vlaev <[email protected]>
>
> Add binding document for the i2c mux driver of Juniper's I2CS FPGA.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> .../devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt | 27 ++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
>
> diff --git a/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> new file mode 100644
> index 0000000..03d917f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
> @@ -0,0 +1,27 @@
> +* Juniper I2C Mux on I2CS
> +
> + I2C mux driver for switching the RE access to the FPC i2c bus.
> + Compatible with the FPC variant of the I2CS.
> +
> +Required properties:
> +
> + - compatible: "jnx,i2cs-mux-i2cs".

jnx,i2c-mux-i2cs

> +
> +The following required properties are defined externally:
> +
> + - Standard I2C mux properties. See i2c-mux.txt in this directory.

To accommodate changes pending for 4.9, change "mux" to "gate" in
this line...

> + - I2C child bus nodes. See i2c-mux.txt in this directory.

This line is wrong since the child nodes themselves are optional in
i2c-gate.txt (and i2c-mux.txt). I guess you can just drop it since
the child nodes are mentioned in i2c-gate.txt (and i2c-mux.txt).

> +
> +Example:
> +
> +fpc0_mux {
> + compatible = "jnx,i2c-mux-i2cs";
> + #address-cells = <1>;
> + #size-cells = <0>;

...drop these two...

> +
> + fpc0i2c0: i2c@0 {

...change i2c@0 to i2c-gate...

> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <0>;

...and drop reg.

Cheers,
Peter

> + };
> +};
>

2016-10-17 19:10:36

by Pantelis Antoniou

[permalink] [raw]
Subject: Re: [PATCH 02/10] mfd: dt-bindings: Add bindings for the Juniper I2CS MFD

Hi Rob,

> On Oct 10, 2016, at 23:23 , Rob Herring <[email protected]> wrote:
>
> On Fri, Oct 07, 2016 at 06:21:01PM +0300, Pantelis Antoniou wrote:
>> From: Georgi Vlaev <[email protected]>
>>
>> Add device tree bindings for the Juniper I2CS MFD driver.
>>
>> Signed-off-by: Georgi Vlaev <[email protected]>
>> [Ported from Juniper kernel]
>> Signed-off-by: Pantelis Antoniou <[email protected]>
>> ---
>> Documentation/devicetree/bindings/mfd/jnx-i2cs.txt | 68 ++++++++++++++++++++++
>> 1 file changed, 68 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt b/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
>> new file mode 100644
>> index 0000000..0ec103b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/jnx-i2cs.txt
>> @@ -0,0 +1,68 @@
>> +Device-Tree bindings for Juniper Networks I2CS FPGA MFD
>> +
>> +Required properties:
>> +- compatible - Must be one of:
>> + "jnx,i2cs-rcb" (Routing Engine or Control Board FRUs)
>> + "jnx,i2cs-fpc" (Flexible Port Concentrator FRUs)
>> + "jnx,i2cs-sib" (Switching Interface Board FRUs)
>> + "jnx,i2cs-fan" (Fan Tray FRUs)
>> +
>> +Optional properties:
>> +
>> +Depending on the FRU, the I2CS MFD has varied group of sub-devices:
>> +
>> +Device Description
>> +------ -----------
>> +jnx,i2cs-gpio : Virtual gpio mapping driver
>> +jnx,i2cs-fan-hwmon : hwmon driver for fan trays
>> +jnx,i2c-mux-i2cs : I2C Mux driver for FPC FRUs
>> +jnx,leds-i2cs : Led driver
>> +
>> +All these optional nodes are described in their respective binding
>> +documents.
>> +
>> +Example node:
>> +
>> +i2cs@54 {
>> + compatible = "jnx,i2cs-fpc";
>> + reg = <0x54>;
>> + #address-cells = <0>;
>> + #size-cells = <0>;
>> +
>> + fpc2_mux {
>
> Generic node names please.
>

OK.

> mux {
>
>> + compatible = "jnx,i2c-mux-i2cs";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + fpc2i2c0: i2c@0 {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + reg = <0>;
>> + };
>> + };
>> +
>> + fpc2_gpiomap: gpio-jnx-i2cs {
>
> gpio@54 {
>
OK

>> + compatible = "jnx,gpio-i2cs";
>> + reg = <0x54>;
>> + #gpio-cells = <2>;
>> + gpio-controller;
>> + interrupt-controller;
>> +
>> + /*
>> + * Map bits [0-3] of reg 0x21 as gpio inputs, bits [4-7]
>> + * as gpio outputs
>> + */
>> + i2c-gpio-map = <0x21 0x0f>;
>> + };
>> +
>> + leds-jnx-i2cs {
>
> leds {
>

OK.

>> + compatible = "jnx,leds-i2cs";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + sib8-active {
>> + reg = <0>;
>> + linux,default-trigger = "sib8-active";
>> + };
>> + };
>> +};
>> --
>> 1.9.1


2016-10-17 19:11:32

by Pantelis Antoniou

[permalink] [raw]
Subject: Re: [PATCH 04/10] i2c: i2c-mux-i2cs: Add device tree bindings

Hi Rob,

> On Oct 10, 2016, at 18:48 , Peter Rosin <[email protected]> wrote:
>
> On 2016-10-07 17:21, Pantelis Antoniou wrote:
>> From: Georgi Vlaev <[email protected]>
>>
>> Add binding document for the i2c mux driver of Juniper's I2CS FPGA.
>>
>> Signed-off-by: Georgi Vlaev <[email protected]>
>> [Ported from Juniper kernel]
>> Signed-off-by: Pantelis Antoniou <[email protected]>
>> ---
>> .../devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt | 27 ++++++++++++++++++++++
>> 1 file changed, 27 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
>> new file mode 100644
>> index 0000000..03d917f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-i2cs.txt
>> @@ -0,0 +1,27 @@
>> +* Juniper I2C Mux on I2CS
>> +
>> + I2C mux driver for switching the RE access to the FPC i2c bus.
>> + Compatible with the FPC variant of the I2CS.
>> +
>> +Required properties:
>> +
>> + - compatible: "jnx,i2cs-mux-i2cs".
>
> jnx,i2c-mux-i2cs
>

OK.

>> +
>> +The following required properties are defined externally:
>> +
>> + - Standard I2C mux properties. See i2c-mux.txt in this directory.
>
> To accommodate changes pending for 4.9, change "mux" to "gate" in
> this line?
>

OK, interesting.


>> + - I2C child bus nodes. See i2c-mux.txt in this directory.
>
> This line is wrong since the child nodes themselves are optional in
> i2c-gate.txt (and i2c-mux.txt). I guess you can just drop it since
> the child nodes are mentioned in i2c-gate.txt (and i2c-mux.txt).
>

OK then.

>> +
>> +Example:
>> +
>> +fpc0_mux {
>> + compatible = "jnx,i2c-mux-i2cs";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>
> ...drop these two...
>
>> +
>> + fpc0i2c0: i2c@0 {
>
> ...change i2c@0 to i2c-gate...
>
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + reg = <0>;
>
> ...and drop reg.
>
> Cheers,
> Peter
>

OK on all.

>> + };
>> +};


2016-10-17 19:13:02

by Pantelis Antoniou

[permalink] [raw]
Subject: Re: [PATCH 10/10] hwmon: i2cs-fan: Add hwmon dts binding documentation


> On Oct 10, 2016, at 23:29 , Rob Herring <[email protected]> wrote:
>
> On Fri, Oct 07, 2016 at 06:21:09PM +0300, Pantelis Antoniou wrote:
>> From: Georgi Vlaev <[email protected]>
>>
>> Adds the I2CS Fan Tray hwmon device tree node documentation.
>>
>> Signed-off-by: Georgi Vlaev <[email protected]>
>> [Ported from Juniper kernel]
>> Signed-off-by: Pantelis Antoniou <[email protected]>
>> ---
>> Documentation/devicetree/bindings/hwmon/i2cs-fan.txt | 19 +++++++++++++++++++
>> 1 file changed, 19 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
>>
>> diff --git a/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt b/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
>> new file mode 100644
>> index 0000000..4ef880c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/hwmon/i2cs-fan.txt
>> @@ -0,0 +1,19 @@
>> +Hwmon driver for Juniper fan trays on I2CS Slave FPGA
>
> This is a h/w description, not a driver.
>
>> +
>> +Required properties:
>> +
>> +- compatible: "i2cs-fan-hwmon"
>
> I doubt any fan control h/w has "hwmon" in the name.
>
>> +
>> +Optional properties:
>> +
>> +- num-fans: fans per tray (default 14)
>> +
>> +- tach-factor: TACH count scaling factor (default 120)
>
> vendor prefixes needed.
>
>> +
>> +Example:
>> +
>> +fan-hwmon {
>> + compatible = "jnx,i2cs-fan-hwmon";
>> + num-fans = <6>;
>> + tach-factor = <33>;
>> +};
>> --
>> 1.9.1

OK on all three.

2016-10-21 08:42:03

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 05/10] gpio: i2cs: Juniper I2CS to GPIO pin mapping driver

On Fri, Oct 7, 2016 at 5:21 PM, Pantelis Antoniou
<[email protected]> wrote:

> From: Guenter Roeck <[email protected]>
>
> This driver maps I2C slave register bits to GPIO pins. Registers
> are supposed to be 8 bit wide. Interrupt support is optional.
>
> The driver is implemented as client of the I2CS MFD driver.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> Signed-off-by: Guenter Roeck <[email protected]>
> Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>

This driver again has the same review comments as the two first
ones, GPIOLIB_IRQCHIP etc. Revise accordingly and repost
and I will look at it again.

(Interestingly this one uses BIT() properly.)

Yours,
Linus Walleij

2016-10-21 08:59:36

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 06/10] gpio: gpio-i2cs: Document bindings of I2CS FPGA GPIO block

On Fri, Oct 7, 2016 at 5:21 PM, Pantelis Antoniou
<[email protected]> wrote:

> +This is virtual gpio driver, that maps each bit of the I2CS FPGA to
> +a gpio. It's used as a compatibility replacement for FRUs that use
> +I2CS FPGA to report presence, control and report power status in
> +the Juniper's driver infra that uses gpios. Compatible with any I2CS.

What does "virtual" mean?

I regularly NACK patches that try to shoehorn stuff into
GPIO not because they are actually GPIO hardware drivers or
general purpose at all, but because it is convenient. Don't do this.

> +- i2c-gpio-map: Map of "I2CS register" and "direction". The registers
> + are 8 bit wide, each bit of the register is mapped to either
> + input or output depending on the bits of the "direction". If
> + the bit in the direction is 1, then that bit from the I2CS
> + register is mapped to gpio input, otherwise to gpio output.

Does this mean that the hardware is synthesized so that some lines
are hardwired as input and some hardwired as output?

That again does not sound like "general purpose" at all.

I think this happens in other hardware and should be a generic
binding like

gpio-lines-input-only = <0>, <4>;
gpio-lines-output-only = <7>, <8>;

And no bitmasking, use the local offset, parse and construct the
mask.

Yours,
Linus Walleij