diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-08 20:02:54 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-08 20:02:54 -0800 |
commit | 3a7dbed7f23cdde8394e9adf92cc222856e0fc1e (patch) | |
tree | a83fa52d24d2457acadf4c1bc72ea3a3e294b57d | |
parent | f2fb38049c724558c590c31e57627f6ba8d48a5b (diff) | |
parent | a3b63979f8a32af9e975a793fd0f612d42072740 (diff) |
Merge tag 'mfd-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones:
"Changes to the core:
- Honour PLATFORM_DEVID_NONE and PLATFORM_DEVID_AUTO dev IDs
Changes to existing drivers:
- IRQ additions/fixes; axp20x, da9063-core
- Code simplification; i2c-dln2
- Regmap additions/fixes; max77693
- Error checking/handling improvements; dln2, db8500-prcmu
- Bug fixes; dln2, wm8350-core
- DT support/documentation; max77693, max77686, tps65217, twl4030-power,
gpio-tc3589x
- Decouple syscon interface from platform devices
- Use MFD hotplug registration; rtsx_usb, viperboard, hid-sensor-hub
- Regulator fixups; sec-core
- Power Management additions/fixes; rts5227, tc6393xb
- Remove relic/redundant code; ab8500-sysctrl, lpc_sch, max77693-private
- Clean-up/coding style changes; tps65090
- Clk additions/fixes; tc6393xb, tc6387xb, t7l66xb
- Add USB-SPI support; dln2
- Trivial changes; max14577, arizona-spi, lpc_sch, wm8997-tables, wm5102-tables
wm5110-tables, axp20x, atmel-hlcdc, rtsx_pci
New drivers/supported devices:
- axp288 PMIC support added to axp20x
- s2mps13 support added to sec-core
- New support for Diolan DLN-2
- New support for atmel-hlcdc"
* tag 'mfd-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (55 commits)
mfd: rtsx: Add func to split u32 into register
mfd: atmel-hlcdc: Add Kconfig option description and name
mfd: da9063: Get irq base dynamically before registering device
mfd: max14577: Fix obvious typo in company name in copyright
mfd: axp20x: Constify axp20x_acpi_match and rid unused warning
mfd: t7l66xb: prepare/unprepare clocks
mfd: tc6387xb: prepare/unprepare clocks
mfd: dln2: add support for USB-SPI module
mfd: wm5110: Add missing registers for AIF2 channels 3-6
mfd: tc3589x: get rid of static base
mfd: arizona: Document HP_CTRL_1L and HP_CTRL_1R registers
mfd: wm8997: Mark INTERRUPT_STATUS_2_MASK as readable
mfd: tc6393xb: Prepare/unprepare clocks
mfd: tps65090: Fix bonkers indenting strategy
mfd: tc6393xb: Fail ohci suspend if full state restore is required
mfd: lpc_sch: Don't call mfd_remove_devices()
mfd: wm8350-core: Fix probable mask then right shift defect
mfd: ab8500-sysctrl: Drop ab8500_restart
mfd: db8500-prcmu: Provide sane error path values
mfd: db8500-prcmu: Check return of devm_ioremap for error
...
63 files changed, 3431 insertions, 273 deletions
diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt new file mode 100644 index 000000000000..f64de95a8e8b --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt @@ -0,0 +1,51 @@ +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver + +Required properties: + - compatible: value should be one of the following: + "atmel,sama5d3-hlcdc" + - reg: base address and size of the HLCDC device registers. + - clock-names: the name of the 3 clocks requested by the HLCDC device. + Should contain "periph_clk", "sys_clk" and "slow_clk". + - clocks: should contain the 3 clocks requested by the HLCDC device. + - interrupts: should contain the description of the HLCDC interrupt line + +The HLCDC IP exposes two subdevices: + - a PWM chip: see ../pwm/atmel-hlcdc-pwm.txt + - a Display Controller: see ../drm/atmel-hlcdc-dc.txt + +Example: + + hlcdc: hlcdc@f0030000 { + compatible = "atmel,sama5d3-hlcdc"; + reg = <0xf0030000 0x2000>; + clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>; + clock-names = "periph_clk","sys_clk", "slow_clk"; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>; + status = "disabled"; + + hlcdc-display-controller { + compatible = "atmel,hlcdc-display-controller"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + hlcdc_panel_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&panel_input>; + }; + }; + }; + + hlcdc_pwm: hlcdc-pwm { + compatible = "atmel,hlcdc-pwm"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd_pwm>; + #pwm-cells = <3>; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/max77686.txt b/Documentation/devicetree/bindings/mfd/max77686.txt index 678f3cf0b8f0..75fdfaf41831 100644 --- a/Documentation/devicetree/bindings/mfd/max77686.txt +++ b/Documentation/devicetree/bindings/mfd/max77686.txt @@ -34,6 +34,12 @@ to get matched with their hardware counterparts as follow: -BUCKn : for BUCKs, where n can lie in range 1 to 9. example: BUCK1, BUCK5, BUCK9. + Regulators which can be turned off during system suspend: + -LDOn : 2, 6-8, 10-12, 14-16, + -BUCKn : 1-4. + Use standard regulator bindings for it ('regulator-off-in-suspend'). + + Example: max77686@09 { diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt index 11921cc417bf..01e9f30fe678 100644 --- a/Documentation/devicetree/bindings/mfd/max77693.txt +++ b/Documentation/devicetree/bindings/mfd/max77693.txt @@ -27,6 +27,20 @@ Optional properties: [*] refer Documentation/devicetree/bindings/regulator/regulator.txt +- haptic : The MAX77693 haptic device utilises a PWM controlled motor to provide + users with tactile feedback. PWM period and duty-cycle are varied in + order to provide the approprite level of feedback. + + Required properties: + - compatible : Must be "maxim,max77693-hpatic" + - haptic-supply : power supply for the haptic motor + [*] refer Documentation/devicetree/bindings/regulator/regulator.txt + - pwms : phandle to the physical PWM(Pulse Width Modulation) device. + PWM properties should be named "pwms". And number of cell is different + for each pwm device. + To get more informations, please refer to documentaion. + [*] refer Documentation/devicetree/bindings/pwm/pwm.txt + Example: max77693@66 { compatible = "maxim,max77693"; @@ -52,4 +66,11 @@ Example: regulator-boot-on; }; }; + + haptic { + compatible = "maxim,max77693-haptic"; + haptic-supply = <&haptic_supply>; + pwms = <&pwm 0 40000 0>; + pwm-names = "haptic"; + }; }; diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt index 0e4026a6cbbf..57a045016fca 100644 --- a/Documentation/devicetree/bindings/mfd/s2mps11.txt +++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt @@ -1,5 +1,5 @@ -* Samsung S2MPS11, S2MPS14 and S2MPU02 Voltage and Current Regulator +* Samsung S2MPS11, S2MPS13, S2MPS14 and S2MPU02 Voltage and Current Regulator The Samsung S2MPS11 is a multi-function device which includes voltage and current regulators, RTC, charger controller and other sub-blocks. It is @@ -7,8 +7,8 @@ interfaced to the host controller using an I2C interface. Each sub-block is addressed by the host system using different I2C slave addresses. Required properties: -- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps14-pmic" - or "samsung,s2mpu02-pmic". +- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps13-pmic" + or "samsung,s2mps14-pmic" or "samsung,s2mpu02-pmic". - reg: Specifies the I2C slave address of the pmic block. It should be 0x66. Optional properties: @@ -17,8 +17,8 @@ Optional properties: - interrupts: Interrupt specifiers for interrupt sources. Optional nodes: -- clocks: s2mps11 and s5m8767 provide three(AP/CP/BT) buffered 32.768 KHz - outputs, so to register these as clocks with common clock framework +- clocks: s2mps11, s2mps13 and s5m8767 provide three(AP/CP/BT) buffered 32.768 + KHz outputs, so to register these as clocks with common clock framework instantiate a sub-node named "clocks". It uses the common clock binding documented in : [Documentation/devicetree/bindings/clock/clock-bindings.txt] @@ -30,12 +30,12 @@ Optional nodes: the clock which they consume. Clock ID Devices ---------------------------------------------------------- - 32KhzAP 0 S2MPS11, S2MPS14, S5M8767 - 32KhzCP 1 S2MPS11, S5M8767 - 32KhzBT 2 S2MPS11, S2MPS14, S5M8767 + 32KhzAP 0 S2MPS11, S2MPS13, S2MPS14, S5M8767 + 32KhzCP 1 S2MPS11, S2MPS13, S5M8767 + 32KhzBT 2 S2MPS11, S2MPS13, S2MPS14, S5M8767 - - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps14-clk", - "samsung,s5m8767-clk" + - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps13-clk", + "samsung,s2mps14-clk", "samsung,s5m8767-clk" - regulators: The regulators of s2mps11 that have to be instantiated should be included in a sub-node named 'regulators'. Regulator nodes included in this @@ -81,12 +81,14 @@ as per the datasheet of s2mps11. - LDOn - valid values for n are: - S2MPS11: 1 to 38 + - S2MPS13: 1 to 40 - S2MPS14: 1 to 25 - S2MPU02: 1 to 28 - Example: LDO1, LDO2, LDO28 - BUCKn - valid values for n are: - S2MPS11: 1 to 10 + - S2MPS13: 1 to 10 - S2MPS14: 1 to 5 - S2MPU02: 1 to 7 - Example: BUCK1, BUCK2, BUCK9 diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index b7797fb12e12..7bb13af8e214 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -23,6 +23,7 @@ #include <linux/clk-provider.h> #include <linux/platform_device.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps13.h> #include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s5m8767.h> #include <linux/mfd/samsung/core.h> @@ -120,6 +121,24 @@ static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = { }, }; +static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = { + [S2MPS11_CLK_AP] = { + .name = "s2mps13_ap", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_CP] = { + .name = "s2mps13_cp", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, + [S2MPS11_CLK_BT] = { + .name = "s2mps13_bt", + .ops = &s2mps11_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = { [S2MPS11_CLK_AP] = { .name = "s2mps14_ap", @@ -184,6 +203,10 @@ static int s2mps11_clk_probe(struct platform_device *pdev) s2mps11_reg = S2MPS11_REG_RTC_CTRL; clks_init = s2mps11_clks_init; break; + case S2MPS13X: + s2mps11_reg = S2MPS13_REG_RTCCTRL; + clks_init = s2mps13_clks_init; + break; case S2MPS14X: s2mps11_reg = S2MPS14_REG_RTCCTRL; clks_init = s2mps14_clks_init; @@ -279,6 +302,7 @@ static int s2mps11_clk_remove(struct platform_device *pdev) static const struct platform_device_id s2mps11_clk_id[] = { { "s2mps11-clk", S2MPS11X}, + { "s2mps13-clk", S2MPS13X}, { "s2mps14-clk", S2MPS14X}, { "s5m8767-clk", S5M8767X}, { }, diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0959ca9b6b27..23dfd5f59b39 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -905,4 +905,16 @@ config GPIO_VIPERBOARD River Tech's viperboard.h for detailed meaning of the module parameters. +config GPIO_DLN2 + tristate "Diolan DLN2 GPIO support" + depends on MFD_DLN2 + select GPIOLIB_IRQCHIP + + help + Select this option to enable GPIO driver for the Diolan DLN2 + board. + + This driver can also be built as a module. If so, the module + will be called gpio-dln2. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e5d346cf3b6e..e60677b8ccb4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o +obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EM) += gpio-em.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c new file mode 100644 index 000000000000..978b51eae2ec --- /dev/null +++ b/drivers/gpio/gpio-dln2.c @@ -0,0 +1,553 @@ +/* + * Driver for the Diolan DLN-2 USB-GPIO adapter + * + * Copyright (c) 2014 Intel Corporation + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#define DLN2_GPIO_ID 0x01 + +#define DLN2_GPIO_GET_PIN_COUNT DLN2_CMD(0x01, DLN2_GPIO_ID) +#define DLN2_GPIO_SET_DEBOUNCE DLN2_CMD(0x04, DLN2_GPIO_ID) +#define DLN2_GPIO_GET_DEBOUNCE DLN2_CMD(0x05, DLN2_GPIO_ID) +#define DLN2_GPIO_PORT_GET_VAL DLN2_CMD(0x06, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_VAL DLN2_CMD(0x0B, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_OUT_VAL DLN2_CMD(0x0C, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_OUT_VAL DLN2_CMD(0x0D, DLN2_GPIO_ID) +#define DLN2_GPIO_CONDITION_MET_EV DLN2_CMD(0x0F, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_ENABLE DLN2_CMD(0x10, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_DISABLE DLN2_CMD(0x11, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_DIRECTION DLN2_CMD(0x13, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_DIRECTION DLN2_CMD(0x14, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_SET_EVENT_CFG DLN2_CMD(0x1E, DLN2_GPIO_ID) +#define DLN2_GPIO_PIN_GET_EVENT_CFG DLN2_CMD(0x1F, DLN2_GPIO_ID) + +#define DLN2_GPIO_EVENT_NONE 0 +#define DLN2_GPIO_EVENT_CHANGE 1 +#define DLN2_GPIO_EVENT_LVL_HIGH 2 +#define DLN2_GPIO_EVENT_LVL_LOW 3 +#define DLN2_GPIO_EVENT_CHANGE_RISING 0x11 +#define DLN2_GPIO_EVENT_CHANGE_FALLING 0x21 +#define DLN2_GPIO_EVENT_MASK 0x0F + +#define DLN2_GPIO_MAX_PINS 32 + +struct dln2_irq_work { + struct work_struct work; + struct dln2_gpio *dln2; + int pin; + int type; +}; + +struct dln2_gpio { + struct platform_device *pdev; + struct gpio_chip gpio; + + /* + * Cache pin direction to save us one transfer, since the hardware has + * separate commands to read the in and out values. + */ + DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS); + + DECLARE_BITMAP(irqs_masked, DLN2_GPIO_MAX_PINS); + DECLARE_BITMAP(irqs_enabled, DLN2_GPIO_MAX_PINS); + DECLARE_BITMAP(irqs_pending, DLN2_GPIO_MAX_PINS); + struct dln2_irq_work *irq_work; +}; + +struct dln2_gpio_pin { + __le16 pin; +}; + +struct dln2_gpio_pin_val { + __le16 pin __packed; + u8 value; +}; + +static int dln2_gpio_get_pin_count(struct platform_device *pdev) +{ + int ret; + __le16 count; + int len = sizeof(count); + + ret = dln2_transfer_rx(pdev, DLN2_GPIO_GET_PIN_COUNT, &count, &len); + if (ret < 0) + return ret; + if (len < sizeof(count)) + return -EPROTO; + + return le16_to_cpu(count); +} + +static int dln2_gpio_pin_cmd(struct dln2_gpio *dln2, int cmd, unsigned pin) +{ + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(pin), + }; + + return dln2_transfer_tx(dln2->pdev, cmd, &req, sizeof(req)); +} + +static int dln2_gpio_pin_val(struct dln2_gpio *dln2, int cmd, unsigned int pin) +{ + int ret; + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(pin), + }; + struct dln2_gpio_pin_val rsp; + int len = sizeof(rsp); + + ret = dln2_transfer(dln2->pdev, cmd, &req, sizeof(req), &rsp, &len); + if (ret < 0) + return ret; + if (len < sizeof(rsp) || req.pin != rsp.pin) + return -EPROTO; + + return rsp.value; +} + +static int dln2_gpio_pin_get_in_val(struct dln2_gpio *dln2, unsigned int pin) +{ + int ret; + + ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_VAL, pin); + if (ret < 0) + return ret; + return !!ret; +} + +static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin) +{ + int ret; + + ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_OUT_VAL, pin); + if (ret < 0) + return ret; + return !!ret; +} + +static void dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2, + unsigned int pin, int value) +{ + struct dln2_gpio_pin_val req = { + .pin = cpu_to_le16(pin), + .value = value, + }; + + dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req, + sizeof(req)); +} + +#define DLN2_GPIO_DIRECTION_IN 0 +#define DLN2_GPIO_DIRECTION_OUT 1 + +static int dln2_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + struct dln2_gpio_pin req = { + .pin = cpu_to_le16(offset), + }; + struct dln2_gpio_pin_val rsp; + int len = sizeof(rsp); + int ret; + + ret = dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_ENABLE, offset); + if (ret < 0) + return ret; + + /* cache the pin direction */ + ret = dln2_transfer(dln2->pdev, DLN2_GPIO_PIN_GET_DIRECTION, + &req, sizeof(req), &rsp, &len); + if (ret < 0) + return ret; + if (len < sizeof(rsp) || req.pin != rsp.pin) { + ret = -EPROTO; + goto out_disable; + } + + switch (rsp.value) { + case DLN2_GPIO_DIRECTION_IN: + clear_bit(offset, dln2->output_enabled); + return 0; + case DLN2_GPIO_DIRECTION_OUT: + set_bit(offset, dln2->output_enabled); + return 0; + default: + ret = -EPROTO; + goto out_disable; + } + +out_disable: + dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); + return ret; +} + +static void dln2_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); +} + +static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + if (test_bit(offset, dln2->output_enabled)) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + int dir; + + dir = dln2_gpio_get_direction(chip, offset); + if (dir < 0) + return dir; + + if (dir == GPIOF_DIR_IN) + return dln2_gpio_pin_get_in_val(dln2, offset); + + return dln2_gpio_pin_get_out_val(dln2, offset); +} + +static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + + dln2_gpio_pin_set_out_val(dln2, offset, value); +} + +static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, + unsigned dir) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + struct dln2_gpio_pin_val req = { + .pin = cpu_to_le16(offset), + .value = dir, + }; + int ret; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_DIRECTION, + &req, sizeof(req)); + if (ret < 0) + return ret; + + if (dir == DLN2_GPIO_DIRECTION_OUT) + set_bit(offset, dln2->output_enabled); + else + clear_bit(offset, dln2->output_enabled); + + return ret; +} + +static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_IN); +} + +static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT); +} + +static int dln2_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + __le32 duration = cpu_to_le32(debounce); + + return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_SET_DEBOUNCE, + &duration, sizeof(duration)); +} + +static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin, + unsigned type, unsigned period) +{ + struct { + __le16 pin; + u8 type; + __le16 period; + } __packed req = { + .pin = cpu_to_le16(pin), + .type = type, + .period = cpu_to_le16(period), + }; + + return dln2_transfer_tx(dln2 |