* Re: [PULL REQUEST] i2c for 4.9
From: Linus Torvalds @ 2016-10-07 21:17 UTC (permalink / raw)
To: Wolfram Sang; +Cc: linux-i2c, Linux Kernel Mailing List
In-Reply-To: <20161007093226.GA2058@katana>
On Fri, Oct 7, 2016 at 2:32 AM, Wolfram Sang <wsa@the-dreams.de> wrote:
>
> here is the 4.9 pull request from I2C including:
Would you mind double-checking my merge.
It looked very trivial, but it would be good to have somebody else
give that gpio-pca953x.c conflict a look to verify that I didn't screw
up the 'id' variable split.
Linus
^ permalink raw reply
* Re: [PATCH 09/10] net: phy: Add MDIO driver for Juniper's SAM FPGA
From: Andrew Lunn @ 2016-10-07 21:13 UTC (permalink / raw)
To: Pantelis Antoniou, David Miller
Cc: Lee Jones, Linus Walleij, Alexandre Courbot, Rob Herring,
Mark Rutland, Frank Rowand, Wolfram Sang, David Woodhouse,
Brian Norris, Florian Fainelli, Wim Van Sebroeck, Peter Rosin,
Debjit Ghosh, Georgi Vlaev, Guenter Roeck, Maryam Seraj,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-mtd,
linux-watchdog
In-Reply-To: <1475853518-22264-10-git-send-email-pantelis.antoniou@konsulko.com>
On Fri, Oct 07, 2016 at 06:18:37PM +0300, Pantelis Antoniou wrote:
> From: Georgi Vlaev <gvlaev@juniper.net>
>
> Add driver for the MDIO IP block present in Juniper's
> SAM FPGA.
>
> This driver supports only Clause 45 of the 802.3 spec.
>
> Note that due to the fact that there are no drivers for
> Broadcom/Avago retimers on 10/40Ge path that are controlled
> from the MDIO interface there is a method to have direct
> access to registers via a debugfs interface.
This seems to be the wrong solution. Why not write those drivers?
Controlling stuff from user space is generally frowned up. So i expect
DaveM will NACK this patch. Please remove all the debugfs stuff.
> +static int mdio_sam_stat_wait(struct mii_bus *bus, u32 wait_mask)
> +{
> + struct mdio_sam_data *data = bus->priv;
> + unsigned long timeout;
> + u32 stat;
> +
> + timeout = jiffies + msecs_to_jiffies(MDIO_RDY_TMO);
> + do {
> + stat = ioread32(data->base + MDIO_STATUS);
> + if (stat & wait_mask)
> + return 0;
> +
> + usleep_range(50, 100);
> + } while (time_before(jiffies, timeout));
> +
> + return -EBUSY;
I've recently had to fix a loop like this in another
driver. usleep_range(50, 100) can sleep for a lot longer. If it sleeps
for MDIO_RDY_TMO you exit out with -EBUSY after a single iteration,
which is not what you want. It is better to make a fixed number of
iterations rather than a timeout.
> +}
> +
> +static int mdio_sam_read(struct mii_bus *bus, int phy_id, int regnum)
> +{
> + struct mdio_sam_data *data = bus->priv;
> + u32 command, res;
> + int ret;
> +
> + /* mdiobus_read holds the bus->mdio_lock mutex */
> +
> + if (!(regnum & MII_ADDR_C45))
> + return -ENXIO;
> +
> + ret = mdio_sam_stat_wait(bus, STAT_REG_RDY);
> + if (ret < 0)
> + return ret;
> +
> + command = regnum & 0x1fffff; /* regnum = (dev_id << 16) | reg */
> + command |= ((phy_id & 0x1f) << 21);
> +
> + iowrite32(command, data->base + MDIO_CMD1);
> + ioread32(data->base + MDIO_CMD1);
> + iowrite32(CMD2_READ | CMD2_ENABLE, data->base + MDIO_CMD2);
> + ioread32(data->base + MDIO_CMD2);
Why do you need to read the values back? Hardware bug?
> + iowrite32(TBL_CMD_REG_GO, data->base + MDIO_TBL_CMD);
> + ioread32(data->base + MDIO_TBL_CMD);
Although not wrong, most drivers use writel().
> +
> + usleep_range(50, 100);
> +
> + ret = mdio_sam_stat_wait(bus, (STAT_REG_DONE | STAT_REG_ERR));
Do you really need a wait before calling mdio_sam_stat_wait()? Isn't
that what it is supposed to do, wait...
> + if (ret < 0)
> + return ret;
> +
> + res = ioread32(data->base + MDIO_RESULT);
> +
> + if (res & RES_ERROR || !(res & RES_SUCCESS))
> + return -EIO;
> +
> + return (res & 0xffff);
> +}
> +
> +static int mdio_sam_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
> +{
> + struct mdio_sam_data *data = bus->priv;
> + u32 command;
> + int ret;
> +
> + /* mdiobus_write holds the bus->mdio_lock mutex */
> +
> + if (!(regnum & MII_ADDR_C45))
> + return -ENXIO;
> +
> + ret = mdio_sam_stat_wait(bus, STAT_REG_RDY);
> + if (ret < 0)
> + return ret;
> +
> + command = regnum & 0x1fffff; /* regnum = (dev_id << 16) | reg */
> + command |= ((phy_id & 0x1f) << 21);
> +
> + iowrite32(command, data->base + MDIO_CMD1);
> + ioread32(data->base + MDIO_CMD1);
> + iowrite32(CMD2_ENABLE | val, data->base + MDIO_CMD2);
> + ioread32(data->base + MDIO_CMD2);
> + iowrite32(TBL_CMD_REG_GO, data->base + MDIO_TBL_CMD);
> + ioread32(data->base + MDIO_TBL_CMD);
> +
> + usleep_range(50, 100);
> +
> + ret = mdio_sam_stat_wait(bus, (STAT_REG_DONE | STAT_REG_ERR));
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int mdio_sam_reset(struct mii_bus *bus)
> +{
> + struct mdio_sam_data *data = bus->priv;
> +
> + iowrite32(TBL_CMD_SOFT_RESET, data->base + MDIO_TBL_CMD);
> + ioread32(data->base + MDIO_TBL_CMD);
> + mdelay(10);
> + iowrite32(0, data->base + MDIO_TBL_CMD);
> + ioread32(data->base + MDIO_TBL_CMD);
> +
> + /* zero tables */
> + memset_io(data->base + MDIO_CMD1, 0, 0x1000);
> + memset_io(data->base + MDIO_PRI_CMD1, 0, 0x1000);
What tables?
> +
> + return 0;
> +}
> +
> +static int mdio_sam_of_register_bus(struct platform_device *pdev,
> + struct device_node *np, void __iomem *base)
> +{
> + struct mii_bus *bus;
> + struct mdio_sam_data *data;
> + u32 reg;
> + int ret;
> +
> + bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*data));
> + if (!bus)
> + return -ENOMEM;
> +
> + /* bus offset */
> + ret = of_property_read_u32(np, "reg", ®);
> + if (ret)
> + return -ENODEV;
> +
> + data = bus->priv;
> + data->base = base + reg;
> +
> + bus->parent = &pdev->dev;
> + bus->name = "mdio-sam";
> + bus->read = mdio_sam_read;
> + bus->write = mdio_sam_write;
> + bus->reset = mdio_sam_reset;
> + snprintf(bus->id, MII_BUS_ID_SIZE, "mdiosam-%x-%x", pdev->id, reg);
> +
> + ret = of_mdiobus_register(bus, np);
> + if (ret < 0)
> + return ret;
> +#ifdef CONFIG_DEBUG_FS
> + ret = mdio_sam_debugfs_init(bus);
> + if (ret < 0)
> + goto err_unregister;
> +#endif
> + ret = device_create_bin_file(&bus->dev, &bin_attr_raw_io);
> + if (ret)
> + goto err_debugfs;
> +
> + return 0;
> +
> +err_debugfs:
> +#ifdef CONFIG_DEBUG_FS
> + mdio_sam_debugfs_remove(bus);
> +#endif
> +err_unregister:
> + mdiobus_unregister(bus);
> +
> + return ret;
> +}
> +
> +static int mdio_sam_of_unregister_bus(struct device_node *np)
> +{
> + struct mii_bus *bus;
> +
> + bus = of_mdio_find_bus(np);
> + if (bus) {
> + device_remove_bin_file(&bus->dev, &bin_attr_raw_io);
> +#ifdef CONFIG_DEBUG_FS
> + mdio_sam_debugfs_remove(bus);
> +#endif
> + mdiobus_unregister(bus);
> + }
> + return 0;
> +}
> +
> +static int mdio_sam_probe(struct platform_device *pdev)
> +{
> + struct device_node *np;
> + struct resource *res;
> + void __iomem *base;
> + int ret;
> +
> + if (!pdev->dev.of_node)
> + return -ENODEV;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_nocache(&pdev->dev, res->start,
> + resource_size(res));
Why nocache?
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + for_each_available_child_of_node(pdev->dev.of_node, np) {
> + ret = mdio_sam_of_register_bus(pdev, np, base);
> + if (ret)
> + goto err;
> + }
This is odd. There does not seem to be any shared resources. So you
should really have one MDIO bus as one device in the device tree.
Andrew
> +
> + return 0;
> +err:
> + /* roll back everything */
> + for_each_available_child_of_node(pdev->dev.of_node, np)
> + mdio_sam_of_unregister_bus(np);
> +
> + return ret;
> +}
> +
> +static int mdio_sam_remove(struct platform_device *pdev)
> +{
> + struct device_node *np;
> +
> + for_each_available_child_of_node(pdev->dev.of_node, np)
> + mdio_sam_of_unregister_bus(np);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id mdio_sam_of_match[] = {
> + { .compatible = "jnx,mdio-sam" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, mdio_sam_of_match);
> +
> +static struct platform_driver mdio_sam_driver = {
> + .probe = mdio_sam_probe,
> + .remove = mdio_sam_remove,
> + .driver = {
> + .name = "mdio-sam",
> + .owner = THIS_MODULE,
> + .of_match_table = mdio_sam_of_match,
> + },
> +};
> +
> +module_platform_driver(mdio_sam_driver);
> +
> +MODULE_ALIAS("platform:mdio-sam");
> +MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Juniper Networks SAM MDIO bus driver");
> --
> 1.9.1
>
^ permalink raw reply
* Re: [PULL REQUEST] i2c for 4.9
From: Wolfram Sang @ 2016-10-07 20:37 UTC (permalink / raw)
To: Bartosz Golaszewski; +Cc: linux-i2c, LKML
In-Reply-To: <CAMpxmJWmy7hCL5zueVfWoz_OL3zh=rob10hY1qyswVtd=Esd=g@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 264 bytes --]
> I see you didn't pick up the follow-up patch ("gpio: pca953x: add a
> comment explaining the need for a lockdep subclass"). Linus acked it
This was planned for the following pull request. Didn't want to change
what was successfully in linux-next for a while.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCHv2] hwmon: Add tc654 driver
From: Guenter Roeck @ 2016-10-07 18:29 UTC (permalink / raw)
To: Chris Packham
Cc: linux-hwmon, iwamoto, Joshua.Scott, Kevin Tsai, Wolfram Sang,
Rob Herring, Mark Rutland, Jean Delvare, Jonathan Corbet,
linux-i2c, devicetree, linux-kernel, linux-doc
In-Reply-To: <20161007013845.10949-1-chris.packham@alliedtelesis.co.nz>
On Fri, Oct 07, 2016 at 02:38:44PM +1300, Chris Packham wrote:
> Add support for the tc654 and tc655 fan controllers from Microchip.
>
> http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
>
> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
> ---
>
> Changes in v2:
> - Add Documentation/hwmon/tc654
> - Incorporate most of the review comments from Guenter. Additional error
> handling is added. Unused/unnecessary code is removed. I decided not
> to go down the regmap path yet. I may circle back to it when I look at
> using regmap in the adm9240 driver.
>
> .../devicetree/bindings/i2c/trivial-devices.txt | 2 +
> Documentation/hwmon/tc654 | 26 ++
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/tc654.c | 513 +++++++++++++++++++++
> 5 files changed, 553 insertions(+)
> create mode 100644 Documentation/hwmon/tc654
> create mode 100644 drivers/hwmon/tc654.c
>
> diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> index 1416c6a0d2cd..833fb9f133d3 100644
> --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> @@ -122,6 +122,8 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
> microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
> microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
> microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
> +microchip,tc654 PWM Fan Speed Controller With Fan Fault Detection
> +microchip,tc655 PWM Fan Speed Controller With Fan Fault Detection
> national,lm63 Temperature sensor with integrated fan control
> national,lm75 I2C TEMP SENSOR
> national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
> diff --git a/Documentation/hwmon/tc654 b/Documentation/hwmon/tc654
> new file mode 100644
> index 000000000000..93796c5c7e79
> --- /dev/null
> +++ b/Documentation/hwmon/tc654
> @@ -0,0 +1,26 @@
> +Kernel driver tc654
> +===================
> +
> +Supported chips:
> + * Microship TC654 and TC655
> + Prefix: 'tc654'
> + Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
> +
> +Authors:
> + Chris Packham <chris.packham@alliedtelesis.co.nz>
> + Masahiko Iwamoto <iwamoto@allied-telesis.co.jp>
> +
> +Description
> +-----------
> +This driver implements support for the Microchip TC654 and TC655.
> +
> +The TC654 used the 2-wire interface compatible with the SMBUS 2.0
uses
> +specification. The TC654 has two (2) inputs for measuring fan RPM and
> +one (1) PWM output which can be used for fan control.
> +
> +Configuration Notes
> +-------------------
> +Ordinarily the pwm1_mode ABI is used for controlling the pwm output
> +mode. However, for this chip the output is always pwm, and the
> +pwm1_mode determines if the pwm output is controlled via the pwm1 value
> +or via the Vin analog input.
Please describe the supported values here.
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 45cef3d2c75c..8681bc65cde5 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -907,6 +907,17 @@ config SENSORS_MCP3021
> This driver can also be built as a module. If so, the module
> will be called mcp3021.
>
> +config SENSORS_TC654
> + tristate "Microchip TC654/TC655 and compatibles"
> + depends on I2C
> + help
> + If you say yes here you get support for TC654 and TC655.
> + The TC654 and TC655 are PWM mode fan speed controllers with
> + FanSense technology for use with brushless DC fans.
> +
> + This driver can also be built as a module. If so, the module
> + will be called tc654.
> +
> config SENSORS_MENF21BMC_HWMON
> tristate "MEN 14F021P00 BMC Hardware Monitoring"
> depends on MFD_MENF21BMC
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index aecf4ba17460..c651f0f1d047 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
> obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
> obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
> +obj-$(CONFIG_SENSORS_TC654) += tc654.o
> obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
> obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
> obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
> diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c
> new file mode 100644
> index 000000000000..cba31cbd3383
> --- /dev/null
> +++ b/drivers/hwmon/tc654.c
> @@ -0,0 +1,513 @@
> +/*
> + * tc654.c - Linux kernel modules for fan speed controller
> + *
> + * Copyright (C) 2016 Allied Telesis Labs NZ
> + *
> + * 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/bitops.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/jiffies.h>
> +#include <linux/util_macros.h>
Please order include files alphabetically.
> +
> +enum tc654_regs {
> + TC654_REG_RPM1 = 0x00, /* RPM Output 1 */
> + TC654_REG_RPM2 = 0x01, /* RPM Output 2 */
> + TC654_REG_FAN_FAULT1 = 0x02, /* Fan Fault 1 Threshold */
> + TC654_REG_FAN_FAULT2 = 0x03, /* Fan Fault 2 Threshold */
> + TC654_REG_CONFIG = 0x04, /* Configuration */
> + TC654_REG_STATUS = 0x05, /* Status */
> + TC654_REG_DUTY_CYCLE = 0x06, /* Fan Speed Duty Cycle */
> + TC654_REG_MFR_ID = 0x07, /* Manufacturer Identification */
> + TC654_REG_VER_ID = 0x08, /* Version Identification */
> +};
> +
> +/* Macros to easily index the registers */
> +#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
> +#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
> +
> +/* Config register bits */
> +#define TC654_REG_CONFIG_RES 0x40 /* Resolution Selection */
> +#define TC654_REG_CONFIG_DUTYC 0x20 /* Duty Cycle Control Method */
> +#define TC654_REG_CONFIG_SDM 0x01 /* Shutdown Mode */
> +
> +/* Status register bits */
> +#define TC654_REG_STATUS_F2F 0x02 /* Fan 2 Fault */
> +#define TC654_REG_STATUS_F1F 0x01 /* Fan 1 Fault */
> +
Didn't notice earlier ... those are bits, so it would be better to use
the BIT() macro.
> +/* RPM resolution for RPM Output registers */
> +#define TC654_HIGH_RPM_RESOLUTION 25 /* 25 RPM resolution */
> +#define TC654_LOW_RPM_RESOLUTION 50 /* 50 RPM resolution */
> +
> +/* Convert to the fan fault RPM threshold from register value */
> +#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50) /* 50 RPM resolution */
> +
> +/* Convert to register value from the fan fault RPM threshold */
> +#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
> +
> +/* Register data is read (and cached) at most once per second. */
> +#define TC654_UPDATE_INTERVAL HZ
> +
> +struct tc654_data {
> + struct i2c_client *client;
> + struct device *hwmon_dev;
No longer needed. Just keep the variable local in the probe function.
> +
> + /* update mutex */
> + struct mutex update_lock;
> +
> + /* tc654 register cache */
> + bool valid;
> + unsigned long last_updated; /* in jiffies */
> +
> + u8 rpm_output[2]; /* The fan RPM data for fans 1 and 2 is then
> + * written to registers RPM1 and RPM2
> + */
> + u8 fan_fault[2]; /* The Fan Fault Threshold Registers are used to
> + * set the fan fault threshold levels for fan 1
> + * and fan 2
> + */
> + u8 config; /* The Configuration Register is an 8-bit read/
> + * writable multi-function control register
> + * 7: Fan Fault Clear
> + * 1 = Clear Fan Fault
> + * 0 = Normal Operation (default)
> + * 6: Resolution Selection for RPM Output Registers
> + * RPM Output Registers (RPM1 and RPM2) will be
> + * set for
> + * 1 = 25 RPM (9-bit) resolution
> + * 0 = 50 RPM (8-bit) resolution (default)
> + * 5: Duty Cycle Control Method
> + * The V OUT duty cycle will be controlled via
> + * 1 = the SMBus interface.
> + * 0 = via the V IN analog input pin. (default)
> + * 4,3: Fan 2 Pulses Per Rotation
> + * 00 = 1
> + * 01 = 2 (default)
> + * 10 = 4
> + * 11 = 8
> + * 2,1: Fan 1 Pulses Per Rotation
> + * 00 = 1
> + * 01 = 2 (default)
> + * 10 = 4
> + * 11 = 8
> + * 0: Shutdown Mode
> + * 1 = Shutdown mode.
> + * 0 = Normal operation. (default)
> + */
> + u8 status; /* The Status register provides all the information
> + * about what is going on within the TC654/TC655
> + * devices.
> + * 7,6: Unimplemented, Read as '0'
> + * 5: Over-Temperature Fault Condition
> + * 1 = Over-Temperature condition has occurred
> + * 0 = Normal operation. V IN is less than 2.6V
> + * 4: RPM2 Counter Overflow
> + * 1 = Fault condition
> + * 0 = Normal operation
> + * 3: RPM1 Counter Overflow
> + * 1 = Fault condition
> + * 0 = Normal operation
> + * 2: V IN Input Status
> + * 1 = V IN is open
> + * 0 = Normal operation. voltage present at V IN
> + * 1: Fan 2 Fault
> + * 1 = Fault condition
> + * 0 = Normal operation
> + * 0: Fan 1 Fault
> + * 1 = Fault condition
> + * 0 = Normal operation
> + */
> + u8 duty_cycle; /* The DUTY_CYCLE register is a 4-bit read/
> + * writable register used to control the duty
> + * cycle of the V OUT output.
> + */
> +};
> +
> +/* helper to grab and cache data, at most one time per second */
> +static struct tc654_data *tc654_update_client(struct device *dev)
> +{
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + int ret = 0;
> +
> + mutex_lock(&data->update_lock);
> + if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
> + likely(data->valid))
> + goto out;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
> + if (ret < 0)
> + goto out;
> + data->rpm_output[0] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
> + if (ret < 0)
> + goto out;
> + data->rpm_output[1] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
> + if (ret < 0)
> + goto out;
> + data->fan_fault[0] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
> + if (ret < 0)
> + goto out;
> + data->fan_fault[1] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
> + if (ret < 0)
> + goto out;
> + data->config = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
> + if (ret < 0)
> + goto out;
> + data->status = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
> + if (ret < 0)
> + goto out;
> + data->duty_cycle = ret;
Maybe make it
data->duty_cycle = ret & 0x0f;
While that should not be necessary, it doesn't hurt, and the datasheet isn't
entirely clear if the upper bits are guaranteed to be 0.
> +
> + data->last_updated = jiffies;
> + data->valid = true;
> +out:
> + mutex_unlock(&data->update_lock);
> +
> + if (ret < 0) /* upon error, encode it in return value */
> + data = ERR_PTR(ret);
> +
> + return data;
> +}
> +
> +/*
> + * sysfs attributes
> + */
> +
> +static ssize_t show_fan(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> + int val;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + if (data->config & TC654_REG_CONFIG_RES)
> + val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
> + else
> + val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
> +
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + return sprintf(buf, "%d\n",
> + TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
> +}
> +
> +static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> +
> + clamp_val(val, 0, 12750);
val = clamp_val(val, 0, 12750);
> +
> + mutex_lock(&data->update_lock);
> +
> + data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
> + data->fan_fault[nr]);
Hmmm ... the reason for asking you to align continuation lines with '('
is that it makes the code more uniform and helps me review it. I understand
that people sometimes don't like it, but please keep in mind that it helps
with the code review.
> +
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> + int val;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + if (nr == 0)
> + val = !!(data->status & TC654_REG_STATUS_F1F);
> + else
> + val = !!(data->status & TC654_REG_STATUS_F2F);
> +
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
> +
> +static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> + u8 val;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + u8 config;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> +
> + switch (val) {
> + case 1:
> + config = 0;
> + break;
> + case 2:
> + config = 1;
> + break;
> + case 4:
> + config = 2;
> + break;
> + case 8:
> + config = 3;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + mutex_lock(&data->update_lock);
> +
> + data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
> + data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static ssize_t show_pwm_mode(struct device *dev,
> + struct device_attribute *da, char *buf)
> +{
> + struct tc654_data *data = tc654_update_client(dev);
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + return sprintf(buf, "%d\n", data->config & TC654_REG_CONFIG_DUTYC);
Should be
!!(data->config & TC654_REG_CONFIG_DUTYC)
otherwise it displays 0 or 32.
> +}
> +
> +static ssize_t set_pwm_mode(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + mutex_lock(&data->update_lock);
> +
> + if (val)
> + data->config |= TC654_REG_CONFIG_DUTYC;
> + else
> + data->config &= ~TC654_REG_CONFIG_DUTYC;
> +
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static const int tc654_pwm_map[16] = { 76, 88, 100, 112, 124, 141, 147, 171,
> + 183, 195, 207, 219, 231, 243, 255 };
> +
This lists 15 entries for an array of size 16, leaving the last entry
at 0. Is there an entry missing ?
Also, 141 yields 55.29%, which doesn't match the datasheet.
I ended up spending some time to match the numbers:
map % datasheet
76 29.8 30.0
88 34.5 34.67
100 39.21 39.33
112 43.92 44.0
124 48.62 48.67
141 55.29 53.33 off (136 would be 53.33%)
147 57.64 58.0 148 would be 58.03%
?? ?? 62.67 missing (160 would be 62.67%)
171 67.05 67.33 172 would be 67.45%
183 71.76 72.0 184 would be 72.15%
195 76.47 76.67
207 81.17 81.33
219 85.88 86.0
231 90.58 90.67
243 95.29 95.33
255 100 100
> +static ssize_t show_pwm(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + struct tc654_data *data = tc654_update_client(dev);
> + int pwm;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + if (data->config & TC654_REG_CONFIG_SDM)
> + pwm = 0;
> + else
> + pwm = tc654_pwm_map[data->duty_cycle];
> +
> + return sprintf(buf, "%d\n", pwm);
> +}
> +
> +static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> + if (val > 255)
> + return -EINVAL;
> +
> + if (val == 0)
> + data->config |= TC654_REG_CONFIG_SDM;
> + else
> + data->config &= ~TC654_REG_CONFIG_SDM;
> +
> + data->duty_cycle = find_closest(val, tc654_pwm_map,
> + ARRAY_SIZE(tc654_pwm_map));
> +
> + mutex_lock(&data->update_lock);
> +
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> + if (ret < 0)
> + goto out;
> +
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
> + data->duty_cycle);
> + if (ret < 0)
> + goto out;
> +
> +out:
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
> + set_fan_min, 0);
> +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
> + set_fan_min, 1);
> +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
> + set_fan_pulses, 0);
> +static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
> + set_fan_pulses, 1);
> +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
> + show_pwm_mode, set_pwm_mode, 0);
> +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm,
> + set_pwm, 0);
> +
> +/* Driver data */
> +static struct attribute *tc654_attrs[] = {
> + &sensor_dev_attr_fan1_input.dev_attr.attr,
> + &sensor_dev_attr_fan2_input.dev_attr.attr,
> + &sensor_dev_attr_fan1_min.dev_attr.attr,
> + &sensor_dev_attr_fan2_min.dev_attr.attr,
> + &sensor_dev_attr_fan1_alarm.dev_attr.attr,
> + &sensor_dev_attr_fan2_alarm.dev_attr.attr,
> + &sensor_dev_attr_fan1_pulses.dev_attr.attr,
> + &sensor_dev_attr_fan2_pulses.dev_attr.attr,
> + &sensor_dev_attr_pwm1_mode.dev_attr.attr,
> + &sensor_dev_attr_pwm1.dev_attr.attr,
> + NULL
> +};
> +
> +ATTRIBUTE_GROUPS(tc654);
> +
> +/*
> + * device probe and removal
> + */
> +
> +static int tc654_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &client->dev;
> + struct tc654_data *data;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> + return -ENODEV;
> +
> + data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->client = client;
> + i2c_set_clientdata(client, data);
No longer needed.
> + mutex_init(&data->update_lock);
> +
> + data->hwmon_dev =
> + devm_hwmon_device_register_with_groups(dev, client->name, data,
> + tc654_groups);
> + if (IS_ERR(data->hwmon_dev))
> + return PTR_ERR(data->hwmon_dev);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id tc654_id[] = {
> + {"tc654", 0},
> + {"tc655", 0},
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, tc654_id);
> +
> +static struct i2c_driver tc654_driver = {
> + .driver = {
> + .name = "tc654",
> + .owner = THIS_MODULE,
Not needed (see Julia's patch)
> + },
> + .probe = tc654_probe,
> + .id_table = tc654_id,
> +};
> +
> +module_i2c_driver(tc654_driver);
> +
> +MODULE_AUTHOR("Allied Telesis Labs");
> +MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
> +MODULE_LICENSE("GPL");
> --
> 2.10.0.479.g7c56b16
>
^ permalink raw reply
* [PATCH 00/10] Introduce Juniper I2CS FPGA driver
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
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
^ permalink raw reply
* [PATCH 09/10] net: phy: Add MDIO driver for Juniper's SAM FPGA
From: Pantelis Antoniou @ 2016-10-07 15:18 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, David Woodhouse, Brian Norris,
Florian Fainelli, Wim Van Sebroeck, Peter Rosin, Debjit Ghosh,
Georgi Vlaev, Guenter Roeck, Maryam Seraj, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-mtd
In-Reply-To: <1475853518-22264-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add driver for the MDIO IP block present in Juniper's
SAM FPGA.
This driver supports only Clause 45 of the 802.3 spec.
Note that due to the fact that there are no drivers for
Broadcom/Avago retimers on 10/40Ge path that are controlled
from the MDIO interface there is a method to have direct
access to registers via a debugfs interface.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/net/phy/Kconfig | 8 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/mdio-sam.c | 564 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 573 insertions(+)
create mode 100644 drivers/net/phy/mdio-sam.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 5078a0d..7d7f265 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -122,6 +122,14 @@ config MDIO_OCTEON
buses. It is required by the Octeon and ThunderX ethernet device
drivers on some systems.
+config MDIO_SAM
+ tristate "Juniper Networks SAM FPGA MDIO controller"
+ depends on MFD_JUNIPER_SAM
+ help
+ This module provides a driver for the Juniper Network SAM FPGA MDIO
+ buses. This hardware can be found in the Gladiator PIC SAM FPGA. This
+ driver is client of the sam-core MFD driver.
+
config MDIO_SUN4I
tristate "Allwinner sun4i MDIO interface support"
depends on ARCH_SUNXI
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index e58667d..c7631cf 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
+obj-$(CONFIG_MDIO_SAM) += mdio-sam.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
diff --git a/drivers/net/phy/mdio-sam.c b/drivers/net/phy/mdio-sam.c
new file mode 100644
index 0000000..73cefa1
--- /dev/null
+++ b/drivers/net/phy/mdio-sam.c
@@ -0,0 +1,564 @@
+/*
+ * Juniper Networks SAM FPGA MDIO driver.
+ *
+ * Copyright (c) 2015, Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ *
+ * The MDIO bus driver supports GPQAM, GPCAM, GPQ28 FPGAs found
+ * on Juniper's 10/40/100GE Gladiator PIC cards. Only Clause 45
+ * access is currently available natively.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
+
+#define MDIO_CMD1 0x0000 /* Command Table 1 */
+#define MDIO_CMD2 0x0800 /* Command Table 2 */
+#define MDIO_RESULT 0x1000 /* Result Table (RO) */
+#define MDIO_PRI_CMD1 0x1800 /* Priority Command Table 1 */
+#define MDIO_PRI_CMD2 0x2000 /* Priority Command Table 1 */
+#define MDIO_PRI_RESULT 0x2800 /* Priority Result Table (RO) */
+#define MDIO_TBL_CMD 0x3000 /* Table Command Register (WO) */
+#define MDIO_STATUS 0x3008 /* Master Status (RO) */
+#define MDIO_STATUS_INT 0x3010 /* Master Status Interrupt Mask (W1C) */
+
+/* MDIO_TBL_CMD */
+#define TBL_CMD_REG_ABORT BIT(31) /* Regular Table ABORT */
+#define TBL_CMD_REG_GO BIT(30) /* Regular Table GO */
+#define TBL_CMD_PRI_ABORT BIT(29) /* Priority Table Abort */
+#define TBL_CMD_PRI_GO BIT(28) /* Priority Table GO */
+#define TBL_CMD_SOFT_RESET BIT(27) /* Soft Reset */
+
+/* MDIO_STATUS */
+#define STAT_REG_RDY BIT(31) /* READY for Programming Regular Table */
+#define STAT_REG_DONE BIT(30) /* DONE SUCCESSFULLY WITH REGULAR TABLE */
+#define STAT_PRI_RDY BIT(29) /* READY for Programming Priority Table */
+#define STAT_PRI_DONE BIT(28) /* DONE SUCCESSFULLY WITH PRIORITY TABLE */
+#define STAT_REG_ERR BIT(27) /* DONE WITH ERRORS for Regular Table */
+#define STAT_PRI_ERR BIT(26) /* DONE WITH ERRORS for Priority Table */
+#define STAT_REG_PROG_ERR BIT(25) /* Programming Err for Regular Table */
+#define STAT_PRI_PROG_ERR BIT(24) /* Programming Err for Priority Table */
+
+/* MDIO_CMD2, MDIO_PRI_CMD2 */
+#define CMD2_ENABLE BIT(17)
+#define CMD2_READ BIT(16)
+
+/* MDIO_RESULT, MDIO_PRI_RESULT */
+#define RES_SUCCESS BIT(17)
+#define RES_ERROR BIT(16)
+
+#define MDIO_RDY_TMO 30 /* in msec */
+
+struct mdio_sam_data {
+ void __iomem *base;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+ /* Clause 45 addressing
+ * addr[5]: PHYAD
+ * reg[21]: DEVAD 5 bits + REG 16 bits
+ * value[16]
+ */
+ u8 addr; /* phyad */
+ u32 reg; /* devad + reg = (devad & 0x1f) << 16 | reg */
+ u16 value; /* value */
+#endif
+};
+
+/* raw_io binary attribute: read/write any device on the bus */
+static ssize_t
+mdio_sam_sysfs_write_raw_io(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t size)
+{
+ struct mii_bus *bus = to_mii_bus(container_of(kobj,
+ struct device, kobj));
+ int ret;
+ u32 reg = offset & 0x1fffff;
+ u8 phy = (offset >> 21) & 0x1f;
+
+ if (size != 2)
+ return -EINVAL;
+
+ ret = mdiobus_write(bus, phy, reg | MII_ADDR_C45, *(u16 *)buf);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static ssize_t
+mdio_sam_sysfs_read_raw_io(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t size)
+{
+ struct mii_bus *bus = to_mii_bus(container_of(kobj,
+ struct device, kobj));
+ int ret;
+ u32 reg = offset & 0x1fffff;
+ u8 phy = (offset >> 21) & 0x1f;
+
+ if (size != 2)
+ return -EINVAL;
+
+ ret = mdiobus_read(bus, phy, reg | MII_ADDR_C45);
+ if (ret < 0)
+ return ret;
+
+ *(u16 *)buf = (u16)ret;
+
+ return size;
+}
+
+static struct bin_attribute bin_attr_raw_io = {
+ .attr = {.name = "raw_io", .mode = (S_IRUGO | S_IWUSR)},
+ .size = 0x4000000,
+ .read = mdio_sam_sysfs_read_raw_io,
+ .write = mdio_sam_sysfs_write_raw_io,
+};
+
+#ifdef CONFIG_DEBUG_FS
+/* debugfs: set/get register offset */
+static int mdio_sam_debugfs_addr_print(struct seq_file *s, void *p)
+{
+ struct mdio_sam_data *data = (struct mdio_sam_data *)s->private;
+
+ seq_printf(s, "0x%02x\n", data->addr);
+
+ return 0;
+}
+
+static int mdio_sam_debugfs_addr_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mdio_sam_debugfs_addr_print, inode->i_private);
+}
+
+static ssize_t
+mdio_sam_debugfs_addr_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mdio_sam_data *data =
+ ((struct seq_file *)(file->private_data))->private;
+ unsigned long addr;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &addr);
+ if (err)
+ return err;
+
+ if (addr > 0x1f)
+ return -EINVAL;
+
+ data->addr = (u8)(addr);
+
+ return count;
+}
+
+static const struct file_operations mdio_sam_debugfs_addr_fops = {
+ .open = mdio_sam_debugfs_addr_open,
+ .write = mdio_sam_debugfs_addr_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/* debugfs: set/get register offset */
+static int mdio_sam_debugfs_reg_print(struct seq_file *s, void *p)
+{
+ struct mdio_sam_data *data = (struct mdio_sam_data *)s->private;
+
+ seq_printf(s, "0x%06X\n", data->reg);
+
+ return 0;
+}
+
+static int mdio_sam_debugfs_reg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mdio_sam_debugfs_reg_print, inode->i_private);
+}
+
+static ssize_t
+mdio_sam_debugfs_reg_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mdio_sam_data *data =
+ ((struct seq_file *)(file->private_data))->private;
+ unsigned long reg;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, ®);
+ if (err)
+ return err;
+
+ if (reg > 0x1fffff)
+ return -EINVAL;
+
+ data->reg = reg;
+
+ return count;
+}
+
+static const struct file_operations mdio_sam_debugfs_reg_fops = {
+ .open = mdio_sam_debugfs_reg_open,
+ .write = mdio_sam_debugfs_reg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/* debugfs: set/get register value */
+static int mdio_sam_debugfs_val_print(struct seq_file *s, void *p)
+{
+ struct mii_bus *bus = (struct mii_bus *)s->private;
+ struct mdio_sam_data *data = bus->priv;
+ int ret;
+
+ ret = mdiobus_read(bus, data->addr, data->reg | MII_ADDR_C45);
+ if (ret < 0)
+ return ret;
+
+ seq_printf(s, "0x%04X\n", ret);
+ return 0;
+}
+
+static int mdio_sam_debugfs_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mdio_sam_debugfs_val_print, inode->i_private);
+}
+
+static ssize_t
+mdio_sam_debugfs_val_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mii_bus *bus =
+ ((struct seq_file *)(file->private_data))->private;
+ struct mdio_sam_data *data = bus->priv;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 0, &value);
+ if (ret)
+ return ret;
+
+ ret = mdiobus_write(bus, data->addr, data->reg | MII_ADDR_C45, value);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations mdio_sam_debugfs_val_fops = {
+ .open = mdio_sam_debugfs_val_open,
+ .write = mdio_sam_debugfs_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int mdio_sam_debugfs_init(struct mii_bus *bus)
+{
+ struct dentry *file;
+ struct mdio_sam_data *data = bus->priv;
+
+ data->dir = debugfs_create_dir(bus->id, NULL);
+ if (!data->dir)
+ return -ENOMEM;
+
+/* phy */
+ file = debugfs_create_file("addr", (S_IRUGO | S_IWUSR),
+ data->dir, data,
+ &mdio_sam_debugfs_addr_fops);
+ if (!file)
+ goto err;
+
+/* reg */
+ file = debugfs_create_file("reg", (S_IRUGO | S_IWUSR),
+ data->dir, data,
+ &mdio_sam_debugfs_reg_fops);
+ if (!file)
+ goto err;
+
+/* value */
+ file = debugfs_create_file("value", (S_IRUGO | S_IWUSR),
+ data->dir, bus,
+ &mdio_sam_debugfs_val_fops);
+ if (!file)
+ goto err;
+
+ return 0;
+err:
+ debugfs_remove_recursive(data->dir);
+ dev_err(&bus->dev, "failed to create debugfs entries.\n");
+
+ return -ENOMEM;
+}
+
+static void mdio_sam_debugfs_remove(struct mii_bus *bus)
+{
+ struct mdio_sam_data *data = bus->priv;
+
+ debugfs_remove_recursive(data->dir);
+}
+#endif
+
+static int mdio_sam_stat_wait(struct mii_bus *bus, u32 wait_mask)
+{
+ struct mdio_sam_data *data = bus->priv;
+ unsigned long timeout;
+ u32 stat;
+
+ timeout = jiffies + msecs_to_jiffies(MDIO_RDY_TMO);
+ do {
+ stat = ioread32(data->base + MDIO_STATUS);
+ if (stat & wait_mask)
+ return 0;
+
+ usleep_range(50, 100);
+ } while (time_before(jiffies, timeout));
+
+ return -EBUSY;
+}
+
+static int mdio_sam_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct mdio_sam_data *data = bus->priv;
+ u32 command, res;
+ int ret;
+
+ /* mdiobus_read holds the bus->mdio_lock mutex */
+
+ if (!(regnum & MII_ADDR_C45))
+ return -ENXIO;
+
+ ret = mdio_sam_stat_wait(bus, STAT_REG_RDY);
+ if (ret < 0)
+ return ret;
+
+ command = regnum & 0x1fffff; /* regnum = (dev_id << 16) | reg */
+ command |= ((phy_id & 0x1f) << 21);
+
+ iowrite32(command, data->base + MDIO_CMD1);
+ ioread32(data->base + MDIO_CMD1);
+ iowrite32(CMD2_READ | CMD2_ENABLE, data->base + MDIO_CMD2);
+ ioread32(data->base + MDIO_CMD2);
+ iowrite32(TBL_CMD_REG_GO, data->base + MDIO_TBL_CMD);
+ ioread32(data->base + MDIO_TBL_CMD);
+
+ usleep_range(50, 100);
+
+ ret = mdio_sam_stat_wait(bus, (STAT_REG_DONE | STAT_REG_ERR));
+ if (ret < 0)
+ return ret;
+
+ res = ioread32(data->base + MDIO_RESULT);
+
+ if (res & RES_ERROR || !(res & RES_SUCCESS))
+ return -EIO;
+
+ return (res & 0xffff);
+}
+
+static int mdio_sam_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
+{
+ struct mdio_sam_data *data = bus->priv;
+ u32 command;
+ int ret;
+
+ /* mdiobus_write holds the bus->mdio_lock mutex */
+
+ if (!(regnum & MII_ADDR_C45))
+ return -ENXIO;
+
+ ret = mdio_sam_stat_wait(bus, STAT_REG_RDY);
+ if (ret < 0)
+ return ret;
+
+ command = regnum & 0x1fffff; /* regnum = (dev_id << 16) | reg */
+ command |= ((phy_id & 0x1f) << 21);
+
+ iowrite32(command, data->base + MDIO_CMD1);
+ ioread32(data->base + MDIO_CMD1);
+ iowrite32(CMD2_ENABLE | val, data->base + MDIO_CMD2);
+ ioread32(data->base + MDIO_CMD2);
+ iowrite32(TBL_CMD_REG_GO, data->base + MDIO_TBL_CMD);
+ ioread32(data->base + MDIO_TBL_CMD);
+
+ usleep_range(50, 100);
+
+ ret = mdio_sam_stat_wait(bus, (STAT_REG_DONE | STAT_REG_ERR));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mdio_sam_reset(struct mii_bus *bus)
+{
+ struct mdio_sam_data *data = bus->priv;
+
+ iowrite32(TBL_CMD_SOFT_RESET, data->base + MDIO_TBL_CMD);
+ ioread32(data->base + MDIO_TBL_CMD);
+ mdelay(10);
+ iowrite32(0, data->base + MDIO_TBL_CMD);
+ ioread32(data->base + MDIO_TBL_CMD);
+
+ /* zero tables */
+ memset_io(data->base + MDIO_CMD1, 0, 0x1000);
+ memset_io(data->base + MDIO_PRI_CMD1, 0, 0x1000);
+
+ return 0;
+}
+
+static int mdio_sam_of_register_bus(struct platform_device *pdev,
+ struct device_node *np, void __iomem *base)
+{
+ struct mii_bus *bus;
+ struct mdio_sam_data *data;
+ u32 reg;
+ int ret;
+
+ bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*data));
+ if (!bus)
+ return -ENOMEM;
+
+ /* bus offset */
+ ret = of_property_read_u32(np, "reg", ®);
+ if (ret)
+ return -ENODEV;
+
+ data = bus->priv;
+ data->base = base + reg;
+
+ bus->parent = &pdev->dev;
+ bus->name = "mdio-sam";
+ bus->read = mdio_sam_read;
+ bus->write = mdio_sam_write;
+ bus->reset = mdio_sam_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "mdiosam-%x-%x", pdev->id, reg);
+
+ ret = of_mdiobus_register(bus, np);
+ if (ret < 0)
+ return ret;
+#ifdef CONFIG_DEBUG_FS
+ ret = mdio_sam_debugfs_init(bus);
+ if (ret < 0)
+ goto err_unregister;
+#endif
+ ret = device_create_bin_file(&bus->dev, &bin_attr_raw_io);
+ if (ret)
+ goto err_debugfs;
+
+ return 0;
+
+err_debugfs:
+#ifdef CONFIG_DEBUG_FS
+ mdio_sam_debugfs_remove(bus);
+#endif
+err_unregister:
+ mdiobus_unregister(bus);
+
+ return ret;
+}
+
+static int mdio_sam_of_unregister_bus(struct device_node *np)
+{
+ struct mii_bus *bus;
+
+ bus = of_mdio_find_bus(np);
+ if (bus) {
+ device_remove_bin_file(&bus->dev, &bin_attr_raw_io);
+#ifdef CONFIG_DEBUG_FS
+ mdio_sam_debugfs_remove(bus);
+#endif
+ mdiobus_unregister(bus);
+ }
+ return 0;
+}
+
+static int mdio_sam_probe(struct platform_device *pdev)
+{
+ struct device_node *np;
+ struct resource *res;
+ void __iomem *base;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ for_each_available_child_of_node(pdev->dev.of_node, np) {
+ ret = mdio_sam_of_register_bus(pdev, np, base);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ /* roll back everything */
+ for_each_available_child_of_node(pdev->dev.of_node, np)
+ mdio_sam_of_unregister_bus(np);
+
+ return ret;
+}
+
+static int mdio_sam_remove(struct platform_device *pdev)
+{
+ struct device_node *np;
+
+ for_each_available_child_of_node(pdev->dev.of_node, np)
+ mdio_sam_of_unregister_bus(np);
+
+ return 0;
+}
+
+static const struct of_device_id mdio_sam_of_match[] = {
+ { .compatible = "jnx,mdio-sam" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mdio_sam_of_match);
+
+static struct platform_driver mdio_sam_driver = {
+ .probe = mdio_sam_probe,
+ .remove = mdio_sam_remove,
+ .driver = {
+ .name = "mdio-sam",
+ .owner = THIS_MODULE,
+ .of_match_table = mdio_sam_of_match,
+ },
+};
+
+module_platform_driver(mdio_sam_driver);
+
+MODULE_ALIAS("platform:mdio-sam");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Juniper Networks SAM MDIO bus driver");
--
1.9.1
^ permalink raw reply related
* [PATCH 10/10] hwmon: i2cs-fan: Add hwmon dts binding documentation
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Adds the I2CS Fan Tray hwmon device tree node documentation.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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
^ permalink raw reply related
* [PATCH 09/10] hwmon: Add driver for Fan Tray on Juniper I2CS FGPA
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Avirup Banerjee <abanerjee@juniper.net>
Add a hwmon driver for Fan Trays using Juniper's I2CS FPGA.
Signed-off-by: Avirup Banerjee <abanerjee@juniper.net>
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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 <abanerjee@juniper.net>
+ *
+ * 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 <abanerjee@juniper.net>");
+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
^ permalink raw reply related
* [PATCH 08/10] leds: Add binding for Juniper's I2CS FPGA
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-leds-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
From: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Document bindings for the I2CS FPGA leds.
Signed-off-by: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
---
.../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
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 07/10] leds: i2cs: Add I2CS FPGA leds driver
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
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 <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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 <gvlaev@juniper.net>
+ *
+ * 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", ®);
+ 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 <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH 06/10] gpio: gpio-i2cs: Document bindings of I2CS FPGA GPIO block
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add device tree bindings document for the GPIO driver of
Juniper's I2CS FPGA.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../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
^ permalink raw reply related
* [PATCH 05/10] gpio: i2cs: Juniper I2CS to GPIO pin mapping driver
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Guenter Roeck <groeck@juniper.net>
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 <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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 <groeck@juniper.net>");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related
* [PATCH 04/10] i2c: i2c-mux-i2cs: Add device tree bindings
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add binding document for the i2c mux driver of Juniper's I2CS FPGA.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../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
^ permalink raw reply related
* [PATCH 03/10] i2c/muxes: Juniper I2CS RE mux
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-leds-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
From: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
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 <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
---
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 <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c-mux-i2cs");
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 04/10] i2c: i2c-sam: Add device tree bindings
From: Pantelis Antoniou @ 2016-10-07 15:18 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, David Woodhouse, Brian Norris,
Florian Fainelli, Wim Van Sebroeck, Peter Rosin, Debjit Ghosh,
Georgi Vlaev, Guenter Roeck, Maryam Seraj, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-mtd
In-Reply-To: <1475853518-22264-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add binding document for the i2c driver of SAM FPGA.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../devicetree/bindings/i2c/i2c-sam-mux.txt | 20 ++++++++++
Documentation/devicetree/bindings/i2c/i2c-sam.txt | 44 ++++++++++++++++++++++
2 files changed, 64 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-sam-mux.txt
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-sam.txt
diff --git a/Documentation/devicetree/bindings/i2c/i2c-sam-mux.txt b/Documentation/devicetree/bindings/i2c/i2c-sam-mux.txt
new file mode 100644
index 0000000..10ddffa
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-sam-mux.txt
@@ -0,0 +1,20 @@
+Juniper's SAM FPGA I2C accelerator mux
+
+The SAM FPGA I2C mux is present only on Juniper SAM FPGA PTX series
+of routers.
+
+The definition of the i2c sam bus is located in the i2c-sam.txt document.
+
+Required properties:
+- compatible: should be "jnx,i2c-sam-mux".
+- reg: master number and mux number.
+
+Optional properties:
+- speed: If present must be either 100000 or 400000. No other values supported.
+
+Examples:
+
+pe1i2c: i2c-sam-mux@1,0 {
+ compatible = "jnx,i2c-sam-mux";
+ reg = <1 0>;
+};
diff --git a/Documentation/devicetree/bindings/i2c/i2c-sam.txt b/Documentation/devicetree/bindings/i2c/i2c-sam.txt
new file mode 100644
index 0000000..4830b48
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-sam.txt
@@ -0,0 +1,44 @@
+Juniper's SAM FPGA I2C accelerator
+
+The SAM FPGA accelerator is used to connect the large number of
+I2C muxes that are present on Juniper PTX series of routers.
+While it's an i2c bus, no other devices are located besides
+i2c-sam-mux devices.
+
+The definition of the i2c sam mux is located in the i2c-sam-mux.txt document.
+
+Required properties:
+- compatible: should be "jnx,i2c-sam".
+- #address-cells: should be 2.
+- #size-cells: should be 0.
+- mux-channels: number of mux channels present
+
+Optional properties:
+- reg: offset and length of the register set for the device are optional since
+ typically the register range is provided by the parent SAM MFD device.
+- master-offset: Offset of where the master register memory starts.
+ Default value is 0x8000.
+- reverse-fill: Fill the start entries of transactions in reverse order
+- priority-tables: Use the pre-programmed priority tables in the FPGA
+- i2c-options: list of options to be written to the option field in the
+ FPGA controlling things like SCL push-pull drives, hold-times, etc.
+- bus-range: start of bus master range and number of masters.
+
+Examples:
+
+i2c-sam {
+ compatible = "jnx,i2c-sam";
+ mux-channels = <2>;
+ #size-cells = <0>;
+ #address-cells = <2>;
+
+ /* PE0 */ pe0i2c: i2c-sam-mux@0,0 {
+ compatible = "jnx,i2c-sam-mux";
+ reg = <0 0>;
+ };
+
+ /* PE1 */ pe1i2c: i2c-sam-mux@1,0 {
+ compatible = "jnx,i2c-sam-mux";
+ reg = <1 0>;
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 02/10] mfd: dt-bindings: Add bindings for the Juniper I2CS MFD
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-leds,
linux-hwmon
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add device tree bindings for the Juniper I2CS MFD driver.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
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
^ permalink raw reply related
* [PATCH 01/10] mfd: Add Juniper I2CS MFD driver
From: Pantelis Antoniou @ 2016-10-07 15:21 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, Richard Purdie, Jacek Anaszewski,
Jean Delvare, Peter Rosin, Avirup Banerjee, Georgi Vlaev,
Guenter Roeck, JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-leds-u79uwXL29TY76Z2rM5mHXA,
linux-hwmon-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
From: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
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 <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
---
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 <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
+ *
+ * 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 <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>");
+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 <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
+ *
+ * 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
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 04/10] watchdog: ptxpmb-wdt: Add ptxpmb-wdt device tree bindings
From: Pantelis Antoniou @ 2016-10-07 15:17 UTC (permalink / raw)
To: Lee Jones
Cc: Rob Herring, Linus Walleij, Alexandre Courbot, Mark Rutland,
Frank Rowand, Wolfram Sang, David Woodhouse, Brian Norris,
Wim Van Sebroeck, Guenter Roeck, Peter Rosin, Debjit Ghosh,
Georgi Vlaev, Guenter Roeck, JawaharBalaji Thirumalaisamy,
Rajat Jain, Pantelis Antoniou, devicetree, linux-kernel,
linux-gpio, linux-i2c
In-Reply-To: <1475853451-22121-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add binding document for the watchdog driver of PTXPMB CPLD.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../devicetree/bindings/watchdog/jnx-ptxpmb-wdt.txt | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/jnx-ptxpmb-wdt.txt
diff --git a/Documentation/devicetree/bindings/watchdog/jnx-ptxpmb-wdt.txt b/Documentation/devicetree/bindings/watchdog/jnx-ptxpmb-wdt.txt
new file mode 100644
index 0000000..34b64f6
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/jnx-ptxpmb-wdt.txt
@@ -0,0 +1,17 @@
+Juniper's PTXPMB FPGA watchdog driver
+
+Required properties:
+
+- compatible: Should be "jnx,ptxpmb-wdt"
+
+Optional properties:
+
+- reg : Specifies base physical address and size of the registers. It is
+ optional since the MFD parent driver supplies it, but can be overridden.
+
+Example:
+
+wdt {
+ compatible = "jnx,ptxpmb-wdt";
+ /* no properties defined */
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 4/4] gpio: ptxpmb-ext-cpld: Document bindings of PTXPMB extended CPLD
From: Pantelis Antoniou @ 2016-10-07 15:19 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Pantelis Antoniou, devicetree,
linux-kernel, linux-gpio, linux-i2c, linux-mtd, linux-watchdog,
netdev
In-Reply-To: <1475853574-22339-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add device tree bindings document for the GPIO driver of
Juniper's PTXPMB extended CPLD.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt | 36 ++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt
diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt
new file mode 100644
index 0000000..87f01b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt
@@ -0,0 +1,36 @@
+Juniper PTXPMB extended CPLD GPIO block
+
+Required properties:
+
+- compatible:
+ Must be "jnx,gpio-ptxpmb-ext-cpld"
+
+- #gpio-cells:
+ Should be <2>. The first cell is the pin number (within the controller's
+ pin space), and the second is used for the following flags:
+ bit[0]: direction (0 = out, 1 = in)
+ bit[1]: init high
+ bit[2]: active low
+
+- gpio-controller:
+ Specifies that the node is a GPIO controller.
+
+- interrupt-controller:
+ Specifies that the node is an interrupt controller.
+
+Optional properties:
+
+- reg:
+ Address and length of the register set for the device. Usually supplied
+ by the parent MFD device.
+
+
+Example:
+
+gpio_ext_cpld: cpld-ext-gpio {
+ compatible = "jnx,gpio-ptxpmb-ext-cpld";
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 3/4] gpio: ptxpmb-ext-cpld: Add driver for Juniper's PTXPMB extended CPLD
From: Pantelis Antoniou @ 2016-10-07 15:19 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853574-22339-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
From: Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
This IP block is present in the PTXPMB extended CPLD present on
Junipers PTX series of routers and provides SIB connector status pins
as GPIO pins for use with other drivers.
Signed-off-by: Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
---
drivers/gpio/Kconfig | 11 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ptxpmb-ext-cpld.c | 430 ++++++++++++++++++++++++++++++++++++
3 files changed, 442 insertions(+)
create mode 100644 drivers/gpio/gpio-ptxpmb-ext-cpld.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index c25dbe9..281029b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -371,6 +371,17 @@ config GPIO_PTXPMB_CPLD
This driver can also be built as a module. If so, the module
will be called gpio-ptxpmb-cpld.
+config GPIO_PTXPMB_EXT_CPLD
+ tristate "PTXPMB Extended CPLD GPIO"
+ depends on MFD_JUNIPER_EXT_CPLD
+ default y if MFD_JUNIPER_EXT_CPLD
+ help
+ This driver exports various bits on the Juniper Control Board
+ Extended CPLD as GPIO pins to userspace.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-ptxpmb-ext-cpld.
+
config GPIO_PXA
bool "PXA GPIO support"
depends on ARCH_PXA || ARCH_MMP
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 6691d8c..ec890c7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PTXPMB_CPLD) += gpio-ptxpmb-cpld.o
+obj-$(CONFIG_GPIO_PTXPMB_EXT_CPLD) += gpio-ptxpmb-ext-cpld.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
diff --git a/drivers/gpio/gpio-ptxpmb-ext-cpld.c b/drivers/gpio/gpio-ptxpmb-ext-cpld.c
new file mode 100644
index 0000000..0152f0b
--- /dev/null
+++ b/drivers/gpio/gpio-ptxpmb-ext-cpld.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2012 Juniper networks
+ *
+ * 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; version 2 of the License.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+
+#include <linux/mfd/ptxpmb_ext_cpld.h>
+
+#define EXT_CPLD_NGPIO 32 /* 0..15: SIB presence bits */
+ /* 16..31: SIB interrupt status */
+
+/**
+ * struct ext_cpld_gpio - GPIO private data structure.
+ * @base: PCI base address of Memory mapped I/O register.
+ * @dev: Pointer to device structure.
+ * @gpio: Data for GPIO infrastructure.
+ */
+struct ext_cpld_gpio {
+ void __iomem *base;
+ struct device *dev;
+ struct gpio_chip gpio;
+ struct mutex irq_lock;
+ struct mutex work_lock;
+ struct irq_domain *domain;
+ int irq;
+ u8 irq_type[EXT_CPLD_NGPIO];
+ u16 sib_presence_cache;
+ u16 sib_presence_irq_enabled;
+ u16 sib_irq_status_cache;
+ u16 sib_irq_enabled;
+ struct delayed_work work;
+};
+
+static int ext_cpld_gpio_get(struct gpio_chip *gpio, unsigned int nr)
+{
+ struct ext_cpld_gpio *chip = container_of(gpio,
+ struct ext_cpld_gpio, gpio);
+ struct pmb_boot_cpld_ext *cpld = chip->base;
+ u16 *addr = nr < 16 ? &cpld->sib_presence : &cpld->sib_irq_status;
+ u16 val;
+
+ val = ioread16(addr);
+ if (nr < 16)
+ chip->sib_presence_cache = val;
+ else
+ chip->sib_irq_status_cache = val;
+
+ return !!(val & (1 << (nr & 15)));
+}
+
+static int ext_cpld_gpio_direction_input(struct gpio_chip *gpio,
+ unsigned int nr)
+{
+ /* all pins are input pins */
+ return 0;
+}
+
+static int ext_cpld_gpio_to_irq(struct gpio_chip *gpio, unsigned int offset)
+{
+ struct ext_cpld_gpio *chip = container_of(gpio,
+ struct ext_cpld_gpio, gpio);
+
+ return irq_create_mapping(chip->domain, offset);
+}
+
+static void ext_cpld_irq_mask(struct irq_data *data)
+{
+ struct ext_cpld_gpio *chip = irq_data_get_irq_chip_data(data);
+ struct pmb_boot_cpld_ext *cpld = chip->base;
+ u16 *addr = data->hwirq < 16 ?
+ &cpld->sib_presence_irq_en : &cpld->sib_irq_en;
+ u16 mask = 1 << (data->hwirq & 0x0f);
+
+ if (chip->irq)
+ iowrite16(ioread16(addr) & ~mask, addr);
+
+ if (data->hwirq < 16)
+ chip->sib_presence_irq_enabled &= ~mask;
+ else
+ chip->sib_irq_enabled &= ~mask;
+}
+
+static void ext_cpld_irq_unmask(struct irq_data *data)
+{
+ struct ext_cpld_gpio *chip = irq_data_get_irq_chip_data(data);
+ struct pmb_boot_cpld_ext *cpld = chip->base;
+ u16 *addr = data->hwirq < 16 ?
+ &cpld->sib_presence_irq_en : &cpld->sib_irq_en;
+ u16 mask = 1 << (data->hwirq & 0x0f);
+
+ if (chip->irq)
+ iowrite16(ioread16(addr) | mask, addr);
+
+ if (data->hwirq < 16)
+ chip->sib_presence_irq_enabled |= mask;
+ else
+ chip->sib_irq_enabled |= mask;
+}
+
+static int ext_cpld_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct ext_cpld_gpio *chip = irq_data_get_irq_chip_data(data);
+
+ chip->irq_type[data->hwirq] = type & 0x0f;
+
+ return 0;
+}
+
+static void ext_cpld_irq_bus_lock(struct irq_data *data)
+{
+ struct ext_cpld_gpio *chip = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&chip->irq_lock);
+}
+
+static void ext_cpld_irq_bus_unlock(struct irq_data *data)
+{
+ struct ext_cpld_gpio *chip = irq_data_get_irq_chip_data(data);
+
+ /* Synchronize interrupts to chip */
+
+ mutex_unlock(&chip->irq_lock);
+}
+
+static struct irq_chip ext_cpld_irq_chip = {
+ .name = "gpio-ext-cpld",
+ .irq_mask = ext_cpld_irq_mask,
+ .irq_unmask = ext_cpld_irq_unmask,
+ .irq_set_type = ext_cpld_irq_set_type,
+ .irq_bus_lock = ext_cpld_irq_bus_lock,
+ .irq_bus_sync_unlock = ext_cpld_irq_bus_unlock,
+};
+
+static int ext_cpld_gpio_irq_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ pr_info("ext_cpld_gpio_irq_map irq %d hwirq %d\n", irq, (int)hwirq);
+
+ irq_set_chip_data(irq, domain->host_data);
+ irq_set_chip(irq, &ext_cpld_irq_chip);
+ irq_set_nested_thread(irq, true);
+
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops ext_cpld_gpio_irq_domain_ops = {
+ .map = ext_cpld_gpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static void __ext_cpld_gpio_irq_work(struct ext_cpld_gpio *chip,
+ u16 *datap, u16 *cachep,
+ unsigned long enabled, int base)
+{
+ u16 data, cache;
+ unsigned int pos;
+
+ cache = *cachep;
+ data = ioread16(datap);
+
+ for_each_set_bit(pos, &enabled, 16) {
+ u16 mask = 1 << pos;
+ u16 bit;
+ int type;
+
+ bit = data & mask;
+ if (bit == (cache & mask))
+ continue;
+
+ type = chip->irq_type[base + pos];
+ /*
+ * check irq->type for match. Only handle edge triggered
+ * interrupts; anything else doesn't make sense here.
+ * TBD: While this is correct for insertion status interrupts,
+ * we may need to support level triggered interrupts to handle
+ * the irq status register.
+ */
+ if (((type & IRQ_TYPE_EDGE_RISING) && bit) ||
+ ((type & IRQ_TYPE_EDGE_FALLING) && !bit)) {
+ int virq = irq_find_mapping(chip->domain, base + pos);
+
+ handle_nested_irq(virq);
+ }
+ }
+ *cachep = data;
+}
+
+static void ext_cpld_gpio_irq_work(struct ext_cpld_gpio *chip)
+{
+ struct pmb_boot_cpld_ext *cpld = chip->base;
+
+ mutex_lock(&chip->work_lock);
+
+ __ext_cpld_gpio_irq_work(chip, &cpld->sib_presence,
+ &chip->sib_presence_cache,
+ chip->sib_presence_irq_enabled,
+ 0);
+
+ __ext_cpld_gpio_irq_work(chip, &cpld->sib_irq_status,
+ &chip->sib_irq_status_cache,
+ chip->sib_irq_enabled,
+ 16);
+
+ mutex_unlock(&chip->work_lock);
+}
+
+static irqreturn_t ext_cpld_gpio_irq_handler(int irq, void *data)
+{
+ struct ext_cpld_gpio *chip = data;
+ struct pmb_boot_cpld_ext *cpld = chip->base;
+
+ pr_info("ext_cpld got interrupt %d 0x%x:0x%x\n", irq,
+ ioread16(&cpld->sib_presence),
+ ioread16(&cpld->sib_irq_status));
+
+ ext_cpld_gpio_irq_work(chip);
+
+ return IRQ_HANDLED;
+}
+
+static void ext_cpld_gpio_worker(struct work_struct *work)
+{
+ struct ext_cpld_gpio *chip = container_of(work, struct ext_cpld_gpio,
+ work.work);
+
+ ext_cpld_gpio_irq_work(chip);
+ schedule_delayed_work(&chip->work, 1);
+}
+
+static int ext_cpld_gpio_irq_setup(struct device *dev,
+ struct ext_cpld_gpio *chip)
+{
+ int ret;
+
+ chip->domain = irq_domain_add_linear(dev->of_node, EXT_CPLD_NGPIO,
+ &ext_cpld_gpio_irq_domain_ops,
+ chip);
+ if (!chip->domain)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&chip->work, ext_cpld_gpio_worker);
+
+ if (chip->irq) {
+ dev_info(dev, "Setting up interrupt %d\n", chip->irq);
+ ret = devm_request_threaded_irq(dev, chip->irq, NULL,
+ ext_cpld_gpio_irq_handler,
+ IRQF_ONESHOT,
+ dev_name(dev), chip);
+ if (ret)
+ goto out_remove_domain;
+ } else {
+ schedule_delayed_work(&chip->work, 1);
+ }
+
+ chip->gpio.to_irq = ext_cpld_gpio_to_irq;
+
+ return 0;
+
+out_remove_domain:
+ irq_domain_remove(chip->domain);
+ return ret;
+}
+
+static void ext_cpld_gpio_irq_teardown(struct device *dev,
+ struct ext_cpld_gpio *chip)
+{
+ struct pmb_boot_cpld_ext *cpld = chip->base;
+ int i;
+
+ if (chip->irq) {
+ iowrite16(0, &cpld->sib_presence_irq_en);
+ iowrite16(0, &cpld->sib_irq_en);
+ }
+
+ for (i = 0; i < EXT_CPLD_NGPIO; i++) {
+ int irq = irq_find_mapping(chip->domain, i);
+
+ if (irq > 0)
+ irq_dispose_mapping(irq);
+ }
+ irq_domain_remove(chip->domain);
+}
+
+static int ext_cpld_gpio_of_xlate(struct gpio_chip *gpio,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ if (WARN_ON(gpio->of_gpio_n_cells < 2))
+ return -EINVAL;
+
+ if (WARN_ON(gpiospec->args_count < gpio->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] > gpio->ngpio)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1] >> 16;
+
+ return gpiospec->args[0];
+}
+
+static void ext_cpld_gpio_setup(struct ext_cpld_gpio *chip)
+{
+ struct gpio_chip *gpio = &chip->gpio;
+
+ gpio->label = dev_name(chip->dev);
+ gpio->owner = THIS_MODULE;
+ gpio->get = ext_cpld_gpio_get;
+ gpio->direction_input = ext_cpld_gpio_direction_input;
+ gpio->dbg_show = NULL;
+ gpio->base = -1;
+ gpio->ngpio = EXT_CPLD_NGPIO;
+ gpio->can_sleep = 0;
+ gpio->of_node = chip->dev->of_node;
+ gpio->of_xlate = ext_cpld_gpio_of_xlate;
+ gpio->of_gpio_n_cells = 2;
+}
+
+static int ext_cpld_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pmb_boot_cpld_ext *cpld;
+ struct ext_cpld_gpio *chip;
+ struct resource *res;
+ int ret;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = dev;
+ platform_set_drvdata(pdev, chip);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ chip->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!chip->base)
+ return -ENOMEM;
+
+ cpld = chip->base;
+ chip->sib_presence_cache = ioread16(&cpld->sib_presence);
+
+ mutex_init(&chip->irq_lock);
+ mutex_init(&chip->work_lock);
+ ext_cpld_gpio_setup(chip);
+
+ ret = ext_cpld_gpio_irq_setup(dev, chip);
+ if (ret < 0)
+ return ret;
+
+ ret = gpiochip_add(&chip->gpio);
+ if (ret) {
+ dev_err(dev, "Extended CPLD gpio: Failed to register GPIO\n");
+ goto teardown;
+ }
+ return 0;
+
+teardown:
+ if (chip->domain)
+ ext_cpld_gpio_irq_teardown(dev, chip);
+ return ret;
+}
+
+static int ext_cpld_gpio_remove(struct platform_device *pdev)
+{
+ struct ext_cpld_gpio *chip = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&chip->work);
+ if (chip->domain)
+ ext_cpld_gpio_irq_teardown(&pdev->dev, chip);
+
+ gpiochip_remove(&chip->gpio);
+
+ return 0;
+}
+
+static const struct of_device_id ext_cpld_gpio_ids[] = {
+ { .compatible = "jnx,gpio-ptxpmb-ext-cpld", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ext_cpld_gpio_ids);
+
+static struct platform_driver ext_cpld_gpio_driver = {
+ .driver = {
+ .name = "gpio-ptxpmb-ext-cpld",
+ .owner = THIS_MODULE,
+ .of_match_table = ext_cpld_gpio_ids,
+ },
+ .probe = ext_cpld_gpio_probe,
+ .remove = ext_cpld_gpio_remove,
+};
+
+static int __init ext_cpld_gpio_init(void)
+{
+ return platform_driver_register(&ext_cpld_gpio_driver);
+}
+module_init(ext_cpld_gpio_init);
+
+static void __exit ext_cpld_gpio_exit(void)
+{
+ platform_driver_unregister(&ext_cpld_gpio_driver);
+}
+module_exit(ext_cpld_gpio_exit);
+
+MODULE_DESCRIPTION("Extended CPLD FPGA GPIO Driver");
+MODULE_LICENSE("GPL");
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 2/4] mfd: ptxpmb-ext-cpld: Add documentation for PTXPMB extended CPLD
From: Pantelis Antoniou @ 2016-10-07 15:19 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Pantelis Antoniou, devicetree,
linux-kernel, linux-gpio, linux-i2c, linux-mtd, linux-watchdog,
netdev
In-Reply-To: <1475853574-22339-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add DT bindings document for the PTXPMB extended CPLD device.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../bindings/mfd/jnx-ptxpmb-ext-cpld.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/jnx-ptxpmb-ext-cpld.txt
diff --git a/Documentation/devicetree/bindings/mfd/jnx-ptxpmb-ext-cpld.txt b/Documentation/devicetree/bindings/mfd/jnx-ptxpmb-ext-cpld.txt
new file mode 100644
index 0000000..098a548a
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/jnx-ptxpmb-ext-cpld.txt
@@ -0,0 +1,35 @@
+* Device tree bindings for Juniper's PTXPMB Extended CPLD FPGA MFD driver
+
+The device supports a gpio block which is described in the
+jnx-gpio-ptxpmb-ext-cpld document.
+
+Required properties:
+
+- compatible: "jnx,ptxpmb-ext-cpld"
+
+- reg: contains offset/length value for device state control
+ registers space.
+
+Optional properties:
+
+- interrupts: The interrupt line(s) the /IRQ signal(s) for the device is
+ connected to.
+
+- interrupt-parent: The parent interrupt controller.
+
+Example:
+
+ext-cpld@1,0 {
+ compatible = "jnx,ptxpmb-ext-cpld";
+ reg = <0x1 0 0x1000>;
+ interrupt-parent = <&mpic>;
+ interrupts = <7 2>, <8 2>;
+
+ gpio_ext_cpld: cpld-ext-gpio {
+ compatible = "jnx,gpio-ptxpmb-ext-cpld";
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ interrupt-controller;
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 1/4] mfd: ptxpmb: Add separate driver for extended CPLD
From: Pantelis Antoniou @ 2016-10-07 15:19 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1475853574-22339-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
From: Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Extended CPLD only exists on certain boards (SPMB) and by itself requires
an MFD driver, since it supports its own interrupts and sub-devices.
It also needs to provide support for SIB hotplug. It is cleaner and easier
to maintain it as separate driver.
Signed-off-by: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Signed-off-by: Tom Kavanagh <tkavanagh-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
---
drivers/mfd/Kconfig | 13 +++
drivers/mfd/Makefile | 1 +
drivers/mfd/ptxpmb-ext-cpld-core.c | 221 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/ptxpmb_ext_cpld.h | 42 +++++++
4 files changed, 277 insertions(+)
create mode 100644 drivers/mfd/ptxpmb-ext-cpld-core.c
create mode 100644 include/linux/mfd/ptxpmb_ext_cpld.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 75b46a1..7e1fa14 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1371,6 +1371,19 @@ config MFD_JUNIPER_SAM
This driver can be built as a module. If built as a module it will be
called "sam-core"
+config MFD_JUNIPER_EXT_CPLD
+ tristate "Juniper PTX PMB Extended CPLD"
+ depends on PTXPMB_COMMON
+ default y if PTXPMB_COMMON
+ select MFD_CORE
+ help
+ Select this to enable the PTX PMB Extended CPLD multi-function kernel
+ driver for the applicable Juniper platforms.
+
+ This driver can be built as a module. If built as a module it will be
+ called "ptxpmb-ext-cpld"
+
+
config MFD_TWL4030_AUDIO
bool "TI TWL4030 Audio"
depends on TWL4030_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 71a8ba6..da94482 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
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_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/ptxpmb-ext-cpld-core.c b/drivers/mfd/ptxpmb-ext-cpld-core.c
new file mode 100644
index 0000000..a1b1793
--- /dev/null
+++ b/drivers/mfd/ptxpmb-ext-cpld-core.c
@@ -0,0 +1,221 @@
+/*
+ * Juniper PTX PMB Extended CPLD multi-function core driver
+ *
+ * Copyright (C) 2012 Juniper Networks
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/mfd/ptxpmb_ext_cpld.h>
+#include <linux/jnx/jnx-subsys.h>
+
+struct pmb_ext_cpld_core {
+ struct device *dev;
+ struct pmb_boot_cpld_ext __iomem *cpld;
+ spinlock_t lock;
+ int irq0, irq1;
+ wait_queue_head_t wqh;
+};
+
+static irqreturn_t pmb_ext_cpld_core_interrupt(int irq, void *dev_data)
+{
+ struct pmb_ext_cpld_core *cpld = dev_data;
+ unsigned char __iomem *data;
+ int i;
+ u8 buffer[48];
+
+ dev_info(cpld->dev, "interrupt %d sib presence=0x%x irq status=0x%x\n",
+ irq, ioread16(&cpld->cpld->sib_presence),
+ ioread16(&cpld->cpld->sib_irq_status));
+
+ data = (u8 *)cpld->cpld;
+ for (i = 0; i < 48; i++)
+ buffer[i] = ioread8(data + i);
+
+ print_hex_dump(KERN_INFO, dev_name(cpld->dev), DUMP_PREFIX_OFFSET,
+ 16, 4, buffer, 48, false);
+
+ spin_lock(&cpld->wqh.lock);
+
+ /* clear interrupt, wake up any handlers */
+ wake_up_locked(&cpld->wqh);
+
+ spin_unlock(&cpld->wqh.lock);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t show_sib_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pmb_ext_cpld_core *cpld = dev_get_drvdata(dev);
+
+ WARN_ONCE(1,
+ "sib_status is deprecated and should no longer be used for presence detection\n");
+
+ return sprintf(buf, "0x%04x\n",
+ ioread16(&cpld->cpld->sib_presence));
+}
+
+static DEVICE_ATTR(sib_status, S_IRUGO, show_sib_status, NULL);
+
+static struct resource pmb_ext_cpld_resources[] = {
+ {
+ .start = 0,
+ .end = sizeof(struct pmb_boot_cpld_ext) - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mfd_cell pmb_ext_cpld_cells[] = {
+ {
+ .name = "gpio-ptxpmb-ext-cpld",
+ .num_resources = ARRAY_SIZE(pmb_ext_cpld_resources),
+ .resources = pmb_ext_cpld_resources,
+ .of_compatible = "jnx,gpio-ptxpmb-ext-cpld",
+ },
+};
+
+static int pmb_ext_cpld_core_probe(struct platform_device *pdev)
+{
+ static struct pmb_ext_cpld_core *cpld;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int error;
+ u16 asid;
+
+ cpld = devm_kzalloc(dev, sizeof(*cpld), GFP_KERNEL);
+ if (!cpld)
+ return -ENOMEM;
+
+ cpld->dev = dev;
+ dev_set_drvdata(dev, cpld);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cpld->cpld = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cpld->cpld))
+ return PTR_ERR(cpld->cpld);
+
+ cpld->irq0 = platform_get_irq(pdev, 0);
+ if (cpld->irq0 >= 0) {
+ error = devm_request_threaded_irq(dev, cpld->irq0, NULL,
+ pmb_ext_cpld_core_interrupt,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(dev), cpld);
+ if (error < 0)
+ return error;
+ }
+
+ cpld->irq1 = platform_get_irq(pdev, 1);
+ if (cpld->irq1 >= 0) {
+ error = devm_request_threaded_irq(dev, cpld->irq1, NULL,
+ pmb_ext_cpld_core_interrupt,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(dev), cpld);
+ if (error < 0)
+ return error;
+ }
+
+ spin_lock_init(&cpld->lock);
+ init_waitqueue_head(&cpld->wqh);
+
+ asid = ioread16be(&cpld->cpld->cb_assembly_id);
+
+ dev_info(dev, "Ext CPLD rev %u.%u CB asy 0x%x CB rev %u.%u\n",
+ ioread8(&cpld->cpld->major_rev),
+ ioread8(&cpld->cpld->minor_rev),
+ asid,
+ ioread8(&cpld->cpld->cb_major_version),
+ ioread8(&cpld->cpld->cb_minor_version));
+
+ dev_info(dev, "SIB status=0x%x SIB irq status=0x%x\n",
+ ioread16(&cpld->cpld->sib_presence),
+ ioread16(&cpld->cpld->sib_irq_status));
+
+ error = device_create_file(dev, &dev_attr_sib_status);
+ if (error)
+ return error;
+ error = mfd_add_devices(dev, pdev->id, pmb_ext_cpld_cells,
+ ARRAY_SIZE(pmb_ext_cpld_cells), res,
+ 0, NULL);
+ if (error < 0)
+ goto abort;
+
+ iowrite16(0xffff, &cpld->cpld->sib_presence_irq_en);
+ iowrite16(0xffff, &cpld->cpld->sib_irq_en);
+
+ return 0;
+
+abort:
+ device_remove_file(dev, &dev_attr_sib_status);
+ return error;
+}
+
+static int pmb_ext_cpld_core_remove(struct platform_device *pdev)
+{
+ mfd_remove_devices(&pdev->dev);
+ device_remove_file(&pdev->dev, &dev_attr_sib_status);
+ return 0;
+}
+
+static const struct of_device_id pmb_ext_cpld_of_ids[] = {
+ { .compatible = "jnx,ptxpmb-ext-cpld" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pmb_ext_cpld_of_ids);
+
+static struct platform_driver pmb_ext_cpld_core_driver = {
+ .probe = pmb_ext_cpld_core_probe,
+ .remove = pmb_ext_cpld_core_remove,
+ .driver = {
+ .name = "ptxpmb-ext-cpld",
+ .of_match_table = pmb_ext_cpld_of_ids,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init pmb_ext_cpld_core_init(void)
+{
+ return platform_driver_register(&pmb_ext_cpld_core_driver);
+}
+module_init(pmb_ext_cpld_core_init);
+
+static void __exit pmb_ext_cpld_core_exit(void)
+{
+ platform_driver_unregister(&pmb_ext_cpld_core_driver);
+}
+module_exit(pmb_ext_cpld_core_exit);
+
+MODULE_DESCRIPTION("Juniper PTX PMB Extended CPLD Core Driver");
+MODULE_AUTHOR("Guenter Roeck <groeck-3r7Miqu9kMnR7s880joybQ@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ptxpmb-cpld");
diff --git a/include/linux/mfd/ptxpmb_ext_cpld.h b/include/linux/mfd/ptxpmb_ext_cpld.h
new file mode 100644
index 0000000..bac9a73
--- /dev/null
+++ b/include/linux/mfd/ptxpmb_ext_cpld.h
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------------
+ *
+ * ptxpmb_ext_cpld_core.h
+ * Copyright (c) 2012, 2013 Juniper Networks
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifndef PTXPMB_EXT_CPLD_CORE_H
+#define PTXPMB_EXT_CPLD_CORE_H
+
+/*
+ * Extended CPLD registers
+ */
+struct pmb_boot_cpld_ext {
+ u8 major_rev; /* 0x00 */
+ u8 minor_rev; /* 0x01 */
+ u16 sib_presence; /* 0x02 */
+ u8 unused0[4]; /* 0x04 */
+ u16 sib_presence_irq_en; /* 0x08 */
+ u8 unused1[4]; /* 0x0a */
+ u16 sib_irq_status; /* 0x0e */
+ u8 unused2[4]; /* 0x10 */
+ u16 sib_irq_en; /* 0x14 */
+ u8 unused3[4]; /* 0x16 */
+ u8 i2c_group; /* 0x1a */
+ u8 i2c_mux_sel; /* 0x1b */
+ u8 re_status; /* 0x1c */
+ u8 unused4[0x24 - 0x1d]; /* 0x1d */
+ u16 cb_assembly_id; /* 0x24 */
+ u8 cb_major_version; /* 0x26 */
+ u8 cb_minor_version; /* 0x27 */
+ u8 unused5[0x40 - 0x28]; /* 0x28 */
+ u8 spare_a; /* 0x40 */
+ u8 spare_b; /* 0x41 */
+ u8 spare_c; /* 0x42 */
+ u8 spare_d; /* 0x43 */
+ u8 spare_e; /* 0x44 */
+ u8 unused6[0x3ff - 0x45]; /* 0x45 */
+};
+
+#endif /* PTXPMB_EXT_CPLD_CORE_H */
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 0/4] Introduce Juniper PTXPMB Extended driver
From: Pantelis Antoniou @ 2016-10-07 15:19 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Georgi Vlaev, Guenter Roeck,
JawaharBalaji Thirumalaisamy, Pantelis Antoniou, devicetree,
linux-kernel, linux-gpio, linux-i2c, linux-mtd, linux-watchdog,
netdev
Add Juniper's PTXPMB Extended FPGA driver. Those FPGAs
are present in Juniper's PTX series of routers.
The MFD driver provices a gpio device.
There are full device tree binding documents for the
master mfd driver and for the slave driver.
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.
Georgi Vlaev (2):
mfd: ptxpmb-ext-cpld: Add documentation for PTXPMB extended CPLD
gpio: ptxpmb-ext-cpld: Document bindings of PTXPMB extended CPLD
Guenter Roeck (2):
mfd: ptxpmb: Add separate driver for extended CPLD
gpio: ptxpmb-ext-cpld: Add driver for Juniper's PTXPMB extended CPLD
.../bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt | 36 ++
.../bindings/mfd/jnx-ptxpmb-ext-cpld.txt | 35 ++
drivers/gpio/Kconfig | 11 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ptxpmb-ext-cpld.c | 430 +++++++++++++++++++++
drivers/mfd/Kconfig | 13 +
drivers/mfd/Makefile | 1 +
drivers/mfd/ptxpmb-ext-cpld-core.c | 221 +++++++++++
include/linux/mfd/ptxpmb_ext_cpld.h | 42 ++
9 files changed, 790 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-ptxpmb-ext-cpld.txt
create mode 100644 Documentation/devicetree/bindings/mfd/jnx-ptxpmb-ext-cpld.txt
create mode 100644 drivers/gpio/gpio-ptxpmb-ext-cpld.c
create mode 100644 drivers/mfd/ptxpmb-ext-cpld-core.c
create mode 100644 include/linux/mfd/ptxpmb_ext_cpld.h
--
1.9.1
^ permalink raw reply
* [PATCH 10/10] net: mdio-sam: Add device tree documentation for SAM MDIO
From: Pantelis Antoniou @ 2016-10-07 15:18 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, David Woodhouse, Brian Norris,
Florian Fainelli, Wim Van Sebroeck, Peter Rosin, Debjit Ghosh,
Georgi Vlaev, Guenter Roeck, Maryam Seraj, Pantelis Antoniou,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1475853518-22264-1-git-send-email-pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
From: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
Add device tree bindings document for the SAM MDIO block
present in Juniper's SAM FPGA.
Signed-off-by: Georgi Vlaev <gvlaev-3r7Miqu9kMnR7s880joybQ@public.gmane.org>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
---
Documentation/devicetree/bindings/net/mdio-sam.txt | 48 ++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/mdio-sam.txt
diff --git a/Documentation/devicetree/bindings/net/mdio-sam.txt b/Documentation/devicetree/bindings/net/mdio-sam.txt
new file mode 100644
index 0000000..7d354e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mdio-sam.txt
@@ -0,0 +1,48 @@
+Juniper SAM FPGA MFD MDIO bus properties.
+
+Required properties:
+- compatible : "jnx,mdio-sam"
+- reg : The start offset of the MDIO bus range
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+
+Required properties for child nodes:
+- #address-cells = <1>;
+- #size-cells = <0>;
+- reg : The MDIO bus offset within the MDIO range.
+
+
+Example :
+
+ sam@10 {
+ compatible = "jnx,sam";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mdio-sam@10 {
+ compatible = "jnx,mdio-sam";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x40000>;
+
+ mdio0: mdio-sam@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0>;
+ };
+
+ mdio1: mdio-sam@4000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x4000>;
+ };
+
+ mdio2: mdio-sam@8000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x8000>;
+ };
+ };
+ };
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 08/10] mtd: flash-sam: Bindings for Juniper's SAM FPGA flash
From: Pantelis Antoniou @ 2016-10-07 15:18 UTC (permalink / raw)
To: Lee Jones
Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Frank Rowand, Wolfram Sang, David Woodhouse, Brian Norris,
Florian Fainelli, Wim Van Sebroeck, Peter Rosin, Debjit Ghosh,
Georgi Vlaev, Guenter Roeck, Maryam Seraj, Pantelis Antoniou,
devicetree, linux-kernel, linux-gpio, linux-i2c, linux-mtd
In-Reply-To: <1475853518-22264-1-git-send-email-pantelis.antoniou@konsulko.com>
From: Georgi Vlaev <gvlaev@juniper.net>
Add binding document for Junipers Flash IP block present
in the SAM FPGA on PTX series of routers.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
.../devicetree/bindings/mtd/flash-sam.txt | 31 ++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/flash-sam.txt
diff --git a/Documentation/devicetree/bindings/mtd/flash-sam.txt b/Documentation/devicetree/bindings/mtd/flash-sam.txt
new file mode 100644
index 0000000..bdf1d78
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/flash-sam.txt
@@ -0,0 +1,31 @@
+Flash device on a Juniper SAM FPGA
+
+These flash chips are found in the PTX series of Juniper routers.
+
+They are regular CFI compatible (Intel or AMD extended) flash chips with
+some special write protect/VPP bits that can be controlled by the machine's
+system controller.
+
+Required properties:
+- compatible : must be "jnx,flash-sam"
+
+Optional properties:
+- reg : memory address for the flash chip, note that this is not
+required since usually the device is a subdevice of the SAM MFD
+driver which fills in the register fields.
+
+For the rest of the properties, see mtd-physmap.txt.
+
+The device tree may optionally contain sub-nodes describing partitions of the
+address space. See partition.txt for more detail.
+
+Example:
+
+flash_sam {
+ compatible = "jnx,flash-sam";
+ partition@0 {
+ reg = <0x0 0x400000>;
+ label = "pic0-golden";
+ read-only;
+ };
+};
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox