diff mbox

[v3] pinctrl: Add SX150X GPIO Extender Pinctrl Driver

Message ID 1477040998-2016-1-git-send-email-narmstrong@baylibre.com
State New
Headers show

Commit Message

Neil Armstrong Oct. 21, 2016, 9:09 a.m. UTC
Since the I2C sx150x GPIO expander driver uses platform_data to manage
the pins configurations, rewrite the driver as a pinctrl driver using
pinconf to get/set pin configurations from DT or debugfs.

The pinctrl driver is functionnally equivalent as the gpio-only driver
and can use DT for pinconf. The platform_data confirmation is dropped.

This patchset removed the gpio-only driver and selects the Pinctrl driver
config instead. This patchset also migrates the gpio dt-bindings to pinctrl
and add the pinctrl optional properties.

The driver was tested with a SX1509 device on a BeagleBone black with
interrupt support and on an X86_64 machine over an I2C to USB converter.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
This is a fixed version that builds and runs on non-OF platforms and on
arm based OF. The GPIO version is removed and the bindings are also moved
to the pinctrl bindings.

Changes since v2 at http://lkml.kernel.org/r/1474991325-1754-1-git-send-email-narmstrong@baylibre.com:
 - rebased on v4.9-rc1
 - removed MODULE_DEVICE_TABLE as in upstream bb411e771b0e [1]

Changes since v1 at http://lkml.kernel.org/r/1473166599-29266-1-git-send-email-narmstrong@baylibre.com:
 - Fix Kconfig descriptions on pinctrl and gpio
 - Fix Kconfig dependency
 - Remove oscio support for non-789 devices
 - correct typo in dt bindings
 - remove probe reset for non-789 devices

Changes since RFC at http://lkml.kernel.org/r/1472130692-14404-1-git-send-email-narmstrong@baylibre.com:
 - Put #ifdef CONFIG_OF/CONFIG_OF_GPIO to remove OF code for non-of platforms
 - No more rely on OF_GPIO config
 - Moved and enhanced bindings to pinctrl bindings
 - Removed gpio-sx150x.c
 - Temporary select PINCTRL_SX150X when GPIO_SX150X
 - Temporary mark GPIO_SX150X as deprecated

[1] gpio: sx150x: fix implicit assumption module.h is present

 .../gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} |   46 +-
 drivers/gpio/Kconfig                               |   13 +-
 drivers/gpio/Makefile                              |    1 -
 drivers/pinctrl/Kconfig                            |   14 +
 drivers/pinctrl/Makefile                           |    1 +
 .../gpio-sx150x.c => pinctrl/pinctrl-sx150x.c}     | 1174 ++++++++++++--------
 6 files changed, 779 insertions(+), 470 deletions(-)
 rename Documentation/devicetree/bindings/{gpio/gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} (40%)
 rename drivers/{gpio/gpio-sx150x.c => pinctrl/pinctrl-sx150x.c} (23%)

Comments

Neil Armstrong Oct. 21, 2016, 9:19 a.m. UTC | #1
On 10/21/2016 11:09 AM, Neil Armstrong wrote:
> Since the I2C sx150x GPIO expander driver uses platform_data to manage
> the pins configurations, rewrite the driver as a pinctrl driver using
> pinconf to get/set pin configurations from DT or debugfs.
> 
> The pinctrl driver is functionnally equivalent as the gpio-only driver
> and can use DT for pinconf. The platform_data confirmation is dropped.
> 
> This patchset removed the gpio-only driver and selects the Pinctrl driver
> config instead. This patchset also migrates the gpio dt-bindings to pinctrl
> and add the pinctrl optional properties.
> 
> The driver was tested with a SX1509 device on a BeagleBone black with
> interrupt support and on an X86_64 machine over an I2C to USB converter.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>

Forgot to add :

Tested-by: Peter Rosin <peda@axentia.se>
Acked-by: Rob Herring <robh@kernel.org>

reported on the v2 patch.

> ---
> This is a fixed version that builds and runs on non-OF platforms and on
> arm based OF. The GPIO version is removed and the bindings are also moved
> to the pinctrl bindings.
> 
> Changes since v2 at http://lkml.kernel.org/r/1474991325-1754-1-git-send-email-narmstrong@baylibre.com:
>  - rebased on v4.9-rc1
>  - removed MODULE_DEVICE_TABLE as in upstream bb411e771b0e [1]
> 
> Changes since v1 at http://lkml.kernel.org/r/1473166599-29266-1-git-send-email-narmstrong@baylibre.com:
>  - Fix Kconfig descriptions on pinctrl and gpio
>  - Fix Kconfig dependency
>  - Remove oscio support for non-789 devices
>  - correct typo in dt bindings
>  - remove probe reset for non-789 devices
> 
> Changes since RFC at http://lkml.kernel.org/r/1472130692-14404-1-git-send-email-narmstrong@baylibre.com:
>  - Put #ifdef CONFIG_OF/CONFIG_OF_GPIO to remove OF code for non-of platforms
>  - No more rely on OF_GPIO config
>  - Moved and enhanced bindings to pinctrl bindings
>  - Removed gpio-sx150x.c
>  - Temporary select PINCTRL_SX150X when GPIO_SX150X
>  - Temporary mark GPIO_SX150X as deprecated
> 
> [1] gpio: sx150x: fix implicit assumption module.h is present
> 
>  .../gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} |   46 +-
>  drivers/gpio/Kconfig                               |   13 +-
>  drivers/gpio/Makefile                              |    1 -
>  drivers/pinctrl/Kconfig                            |   14 +
>  drivers/pinctrl/Makefile                           |    1 +
>  .../gpio-sx150x.c => pinctrl/pinctrl-sx150x.c}     | 1174 ++++++++++++--------
>  6 files changed, 779 insertions(+), 470 deletions(-)
>  rename Documentation/devicetree/bindings/{gpio/gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} (40%)
>  rename drivers/{gpio/gpio-sx150x.c => pinctrl/pinctrl-sx150x.c} (23%)
> 
> diff --git a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> similarity index 40%
> rename from Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
> rename to Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> index c809acb..c293c8a 100644
> --- a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> @@ -1,34 +1,54 @@
>  SEMTECH SX150x GPIO expander bindings
>  
> +Please refer to pinctrl-bindings.txt, ../gpio/gpio.txt, and
> +../interrupt-controller/interrupts.txt for generic information regarding
> +pin controller, GPIO, and interrupt bindings.
>  
>  Required properties:
> -
> -- compatible: should be "semtech,sx1506q",
> +- compatible: should be one of :
> +			"semtech,sx1506q",
>  			"semtech,sx1508q",
>  			"semtech,sx1509q",
>  			"semtech,sx1502q".
>  
>  - reg: The I2C slave address for this device.
>  
> -- interrupt-parent: phandle of the parent interrupt controller.
> -
> -- interrupts: Interrupt specifier for the controllers interrupt.
> -
>  - #gpio-cells: Should be 2. The first cell is the GPIO number and the
>  		second cell is used to specify optional parameters:
>  		bit 0: polarity (0: normal, 1: inverted)
>  
>  - gpio-controller: Marks the device as a GPIO controller.
>  
> +Optional properties :
> +- interrupt-parent: phandle of the parent interrupt controller.
> +
> +- interrupts: Interrupt specifier for the controllers interrupt.
> +
>  - interrupt-controller: Marks the device as a interrupt controller.
>  
> +- semtech,probe-reset: Will trigger a reset of the GPIO expander on probe,
> +		only for sx1508q and sx1509q
> +
>  The GPIO expander can optionally be used as an interrupt controller, in
> -which case it uses the default two cell specifier as described in
> -Documentation/devicetree/bindings/interrupt-controller/interrupts.txt.
> +which case it uses the default two cell specifier.
> +
> +Required properties for pin configuration sub-nodes:
> + - pins: List of pins to which the configuration applies.
> +
> +Optional properties for pin configuration sub-nodes:
> +----------------------------------------------------
> + - bias-disable: disable any pin bias, except the OSCIO pin
> + - bias-pull-up: pull up the pin, except the OSCIO pin
> + - bias-pull-down: pull down the pin, except the OSCIO pin
> + - bias-pull-pin-default: use pin-default pull state, except the OSCIO pin
> + - drive-push-pull: drive actively high and low
> + - drive-open-drain: drive with open drain only for sx1508q and sx1509q and except the OSCIO pin
> + - output-low: set the pin to output mode with low level
> + - output-high: set the pin to output mode with high level
>  
>  Example:
>  
> -	i2c_gpio_expander@20{
> +	i2c0gpio-expander@20{
>  		#gpio-cells = <2>;
>  		#interrupt-cells = <2>;
>  		compatible = "semtech,sx1506q";
> @@ -38,4 +58,12 @@ Example:
>  
>  		gpio-controller;
>  		interrupt-controller;
> +
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&gpio1_cfg_pins>;
> +
> +		gpio1_cfg_pins: gpio1-cfg {
> +			pins = "gpio1";
> +			bias-pull-up;
> +		};
>  	};
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 26ee00f..db3f7aa 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -781,16 +781,13 @@ config GPIO_PCF857X
>  	  platform-neutral GPIO calls.
>  
>  config GPIO_SX150X
> -	bool "Semtech SX150x I2C GPIO expander"
> -	depends on I2C=y
> -	select GPIOLIB_IRQCHIP
> +	bool "Semtech SX150x I2C GPIO expander (deprecated)"
> +	depends on PINCTRL && I2C=y
> +	select PINCTRL_SX150X
>  	default n
>  	help
> -	  Say yes here to provide support for Semtech SX150-series I2C
> -	  GPIO expanders. Compatible models include:
> -
> -	  8 bits:  sx1508q
> -	  16 bits: sx1509q
> +	  Say yes here to provide support for Semtech SX150x-series I2C
> +	  GPIO expanders. The GPIO driver was replaced by a Pinctrl version.
>  
>  config GPIO_TPIC2810
>  	tristate "TPIC2810 8-Bit I2C GPO expander"
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index ab28a2d..76c7af6 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_SPEAR_SPICS)	+= gpio-spear-spics.o
>  obj-$(CONFIG_GPIO_STA2X11)	+= gpio-sta2x11.o
>  obj-$(CONFIG_GPIO_STMPE)	+= gpio-stmpe.o
>  obj-$(CONFIG_GPIO_STP_XWAY)	+= gpio-stp-xway.o
> -obj-$(CONFIG_GPIO_SX150X)	+= gpio-sx150x.o
>  obj-$(CONFIG_GPIO_SYSCON)	+= gpio-syscon.o
>  obj-$(CONFIG_GPIO_TB10X)	+= gpio-tb10x.o
>  obj-$(CONFIG_GPIO_TC3589X)	+= gpio-tc3589x.o
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 0e75d94..801fa8b 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -164,6 +164,20 @@ config PINCTRL_SIRF
>  	select GENERIC_PINCONF
>  	select GPIOLIB_IRQCHIP
>  
> +config PINCTRL_SX150X
> +	bool "Semtech SX150x I2C GPIO expander pinctrl driver"
> +	depends on GPIOLIB && I2C=y
> +	select PINMUX
> +	select PINCONF
> +	select GENERIC_PINCONF
> +	select GPIOLIB_IRQCHIP
> +	help
> +	  Say yes here to provide support for Semtech SX150x-series I2C
> +	  GPIO expanders as pinctrl module.
> +	  Compatible models include:
> +	  - 8 bits:  sx1508q, sx1502q
> +	  - 16 bits: sx1509q, sx1506q
> +
>  config PINCTRL_PISTACHIO
>  	def_bool y if MACH_PISTACHIO
>  	depends on GPIOLIB
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 11bad37..3b8e6f7 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PINCTRL_PISTACHIO)	+= pinctrl-pistachio.o
>  obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
>  obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
>  obj-$(CONFIG_PINCTRL_SIRF)	+= sirf/
> +obj-$(CONFIG_PINCTRL_SX150X)	+= pinctrl-sx150x.o
>  obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
>  obj-$(CONFIG_PINCTRL_TZ1090)	+= pinctrl-tz1090.o
>  obj-$(CONFIG_PINCTRL_TZ1090_PDC)	+= pinctrl-tz1090-pdc.o
> diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c
> similarity index 23%
> rename from drivers/gpio/gpio-sx150x.c
> rename to drivers/pinctrl/pinctrl-sx150x.c
> index af95de8..e985471 100644
> --- a/drivers/gpio/gpio-sx150x.c
> +++ b/drivers/pinctrl/pinctrl-sx150x.c
> @@ -1,4 +1,8 @@
> -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
> +/*
> + * Copyright (c) 2016, BayLibre, SAS. All rights reserved.
> + * Author: Neil Armstrong <narmstrong@baylibre.com>
> + *
> + * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
>   *
>   * Driver for Semtech SX150X I2C GPIO Expanders
>   *
> @@ -12,13 +16,8 @@
>   * 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.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> - * 02110-1301, USA.
>   */
> -#include <linux/gpio.h>
> +
>  #include <linux/i2c.h>
>  #include <linux/init.h>
>  #include <linux/interrupt.h>
> @@ -26,17 +25,23 @@
>  #include <linux/mutex.h>
>  #include <linux/slab.h>
>  #include <linux/of.h>
> -#include <linux/of_address.h>
> -#include <linux/of_irq.h>
> -#include <linux/of_gpio.h>
> -#include <linux/of_device.h>
> +#include <linux/gpio.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
>  
> -#define NO_UPDATE_PENDING	-1
> +#include "core.h"
> +#include "pinconf.h"
> +#include "pinctrl-utils.h"
>  
>  /* The chip models of sx150x */
> -#define SX150X_123 0
> -#define SX150X_456 1
> -#define SX150X_789 2
> +enum {
> +	SX150X_123 = 0,
> +	SX150X_456,
> +	SX150X_789,
> +};
>  
>  struct sx150x_123_pri {
>  	u8 reg_pld_mode;
> @@ -82,167 +87,146 @@ struct sx150x_device_data {
>  		struct sx150x_456_pri x456;
>  		struct sx150x_789_pri x789;
>  	} pri;
> +	const struct pinctrl_pin_desc *pins;
> +	unsigned int npins;
>  };
>  
> -/**
> - * struct sx150x_platform_data - config data for SX150x driver
> - * @gpio_base: The index number of the first GPIO assigned to this
> - *             GPIO expander.  The expander will create a block of
> - *             consecutively numbered gpios beginning at the given base,
> - *             with the size of the block depending on the model of the
> - *             expander chip.
> - * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO
> - *                instead of as an oscillator, increasing the size of the
> - *                GP(I)O pool created by this expander by one.  The
> - *                output-only GPO pin will be added at the end of the block.
> - * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor
> - *                 for each IO line in the expander.  Setting the bit at
> - *                 position n will enable the pull-up for the IO at
> - *                 the corresponding offset.  For chips with fewer than
> - *                 16 IO pins, high-end bits are ignored.
> - * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down
> - *                 resistor for each IO line in the expander. Setting the
> - *                 bit at position n will enable the pull-down for the IO at
> - *                 the corresponding offset.  For chips with fewer than
> - *                 16 IO pins, high-end bits are ignored.
> - * @io_polarity: A bit-mask which enables polarity inversion for each IO line
> - *               in the expander.  Setting the bit at position n inverts
> - *               the polarity of that IO line, while clearing it results
> - *               in normal polarity. For chips with fewer than 16 IO pins,
> - *               high-end bits are ignored.
> - * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line
> - *               is connected, via which it reports interrupt events
> - *               across all GPIO lines.  This must be a real,
> - *               pre-existing IRQ line.
> - *               Setting this value < 0 disables the irq_chip functionality
> - *               of the driver.
> - * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based
> - *            IRQ lines will appear.  Similarly to gpio_base, the expander
> - *            will create a block of irqs beginning at this number.
> - *            This value is ignored if irq_summary is < 0.
> - * @reset_during_probe: If set to true, the driver will trigger a full
> - *                      reset of the chip at the beginning of the probe
> - *                      in order to place it in a known state.
> - */
> -struct sx150x_platform_data {
> -	unsigned gpio_base;
> -	bool     oscio_is_gpo;
> -	u16      io_pullup_ena;
> -	u16      io_pulldn_ena;
> -	u16      io_polarity;
> -	int      irq_summary;
> -	unsigned irq_base;
> -	bool     reset_during_probe;
> +struct sx150x_pinctrl {
> +	struct device *dev;
> +	struct i2c_client *client;
> +	struct pinctrl_dev *pctldev;
> +	struct pinctrl_desc pinctrl_desc;
> +	struct gpio_chip gpio;
> +	struct irq_chip irq_chip;
> +	struct {
> +		int update;
> +		u32 sense;
> +		u32 masked;
> +		u32 dev_sense;
> +		u32 dev_masked;
> +	} irq;
> +	struct mutex lock;
> +	const struct sx150x_device_data *data;
>  };
>  
> -struct sx150x_chip {
> -	struct gpio_chip                 gpio_chip;
> -	struct i2c_client               *client;
> -	const struct sx150x_device_data *dev_cfg;
> -	int                              irq_summary;
> -	int                              irq_base;
> -	int				 irq_update;
> -	u32                              irq_sense;
> -	u32				 irq_masked;
> -	u32				 dev_sense;
> -	u32				 dev_masked;
> -	struct irq_chip                  irq_chip;
> -	struct mutex                     lock;
> +static const struct pinctrl_pin_desc sx150x_8_pins[] = {
> +	PINCTRL_PIN(0, "gpio0"),
> +	PINCTRL_PIN(1, "gpio1"),
> +	PINCTRL_PIN(2, "gpio2"),
> +	PINCTRL_PIN(3, "gpio3"),
> +	PINCTRL_PIN(4, "gpio4"),
> +	PINCTRL_PIN(5, "gpio5"),
> +	PINCTRL_PIN(6, "gpio6"),
> +	PINCTRL_PIN(7, "gpio7"),
> +	PINCTRL_PIN(8, "oscio"),
>  };
>  
> -static const struct sx150x_device_data sx150x_devices[] = {
> -	[0] = { /* sx1508q */
> -		.model = SX150X_789,
> -		.reg_pullup	= 0x03,
> -		.reg_pulldn	= 0x04,
> -		.reg_dir	= 0x07,
> -		.reg_data	= 0x08,
> -		.reg_irq_mask	= 0x09,
> -		.reg_irq_src	= 0x0c,
> -		.reg_sense	= 0x0b,
> -		.pri.x789 = {
> -			.reg_drain	= 0x05,
> -			.reg_polarity	= 0x06,
> -			.reg_clock	= 0x0f,
> -			.reg_misc	= 0x10,
> -			.reg_reset	= 0x7d,
> -		},
> -		.ngpios = 8,
> -	},
> -	[1] = { /* sx1509q */
> -		.model = SX150X_789,
> -		.reg_pullup	= 0x07,
> -		.reg_pulldn	= 0x09,
> -		.reg_dir	= 0x0f,
> -		.reg_data	= 0x11,
> -		.reg_irq_mask	= 0x13,
> -		.reg_irq_src	= 0x19,
> -		.reg_sense	= 0x17,
> -		.pri.x789 = {
> -			.reg_drain	= 0x0b,
> -			.reg_polarity	= 0x0d,
> -			.reg_clock	= 0x1e,
> -			.reg_misc	= 0x1f,
> -			.reg_reset	= 0x7d,
> -		},
> -		.ngpios	= 16
> -	},
> -	[2] = { /* sx1506q */
> -		.model = SX150X_456,
> -		.reg_pullup	= 0x05,
> -		.reg_pulldn	= 0x07,
> -		.reg_dir	= 0x03,
> -		.reg_data	= 0x01,
> -		.reg_irq_mask	= 0x09,
> -		.reg_irq_src	= 0x0f,
> -		.reg_sense	= 0x0d,
> -		.pri.x456 = {
> -			.reg_pld_mode	= 0x21,
> -			.reg_pld_table0	= 0x23,
> -			.reg_pld_table1	= 0x25,
> -			.reg_pld_table2	= 0x27,
> -			.reg_pld_table3	= 0x29,
> -			.reg_pld_table4	= 0x2b,
> -			.reg_advance	= 0xad,
> -		},
> -		.ngpios	= 16
> +static const struct pinctrl_pin_desc sx150x_16_pins[] = {
> +	PINCTRL_PIN(0, "gpio0"),
> +	PINCTRL_PIN(1, "gpio1"),
> +	PINCTRL_PIN(2, "gpio2"),
> +	PINCTRL_PIN(3, "gpio3"),
> +	PINCTRL_PIN(4, "gpio4"),
> +	PINCTRL_PIN(5, "gpio5"),
> +	PINCTRL_PIN(6, "gpio6"),
> +	PINCTRL_PIN(7, "gpio7"),
> +	PINCTRL_PIN(8, "gpio8"),
> +	PINCTRL_PIN(9, "gpio9"),
> +	PINCTRL_PIN(10, "gpio10"),
> +	PINCTRL_PIN(11, "gpio11"),
> +	PINCTRL_PIN(12, "gpio12"),
> +	PINCTRL_PIN(13, "gpio13"),
> +	PINCTRL_PIN(14, "gpio14"),
> +	PINCTRL_PIN(15, "gpio15"),
> +	PINCTRL_PIN(16, "oscio"),
> +};
> +
> +static const struct sx150x_device_data sx1508q_device_data = {
> +	.model = SX150X_789,
> +	.reg_pullup	= 0x03,
> +	.reg_pulldn	= 0x04,
> +	.reg_dir	= 0x07,
> +	.reg_data	= 0x08,
> +	.reg_irq_mask	= 0x09,
> +	.reg_irq_src	= 0x0c,
> +	.reg_sense	= 0x0b,
> +	.pri.x789 = {
> +		.reg_drain	= 0x05,
> +		.reg_polarity	= 0x06,
> +		.reg_clock	= 0x0f,
> +		.reg_misc	= 0x10,
> +		.reg_reset	= 0x7d,
>  	},
> -	[3] = { /* sx1502q */
> -		.model = SX150X_123,
> -		.reg_pullup	= 0x02,
> -		.reg_pulldn	= 0x03,
> -		.reg_dir	= 0x01,
> -		.reg_data	= 0x00,
> -		.reg_irq_mask	= 0x05,
> -		.reg_irq_src	= 0x08,
> -		.reg_sense	= 0x07,
> -		.pri.x123 = {
> -			.reg_pld_mode	= 0x10,
> -			.reg_pld_table0	= 0x11,
> -			.reg_pld_table1	= 0x12,
> -			.reg_pld_table2	= 0x13,
> -			.reg_pld_table3	= 0x14,
> -			.reg_pld_table4	= 0x15,
> -			.reg_advance	= 0xad,
> -		},
> -		.ngpios	= 8,
> +	.ngpios = 8,
> +	.pins = sx150x_8_pins,
> +	.npins = ARRAY_SIZE(sx150x_8_pins),
> +};
> +
> +static const struct sx150x_device_data sx1509q_device_data = {
> +	.model = SX150X_789,
> +	.reg_pullup	= 0x07,
> +	.reg_pulldn	= 0x09,
> +	.reg_dir	= 0x0f,
> +	.reg_data	= 0x11,
> +	.reg_irq_mask	= 0x13,
> +	.reg_irq_src	= 0x19,
> +	.reg_sense	= 0x17,
> +	.pri.x789 = {
> +		.reg_drain	= 0x0b,
> +		.reg_polarity	= 0x0d,
> +		.reg_clock	= 0x1e,
> +		.reg_misc	= 0x1f,
> +		.reg_reset	= 0x7d,
>  	},
> +	.ngpios	= 16,
> +	.pins = sx150x_16_pins,
> +	.npins = ARRAY_SIZE(sx150x_16_pins),
>  };
>  
> -static const struct i2c_device_id sx150x_id[] = {
> -	{"sx1508q", 0},
> -	{"sx1509q", 1},
> -	{"sx1506q", 2},
> -	{"sx1502q", 3},
> -	{}
> +static const struct sx150x_device_data sx1506q_device_data = {
> +	.model = SX150X_456,
> +	.reg_pullup	= 0x05,
> +	.reg_pulldn	= 0x07,
> +	.reg_dir	= 0x03,
> +	.reg_data	= 0x01,
> +	.reg_irq_mask	= 0x09,
> +	.reg_irq_src	= 0x0f,
> +	.reg_sense	= 0x0d,
> +	.pri.x456 = {
> +		.reg_pld_mode	= 0x21,
> +		.reg_pld_table0	= 0x23,
> +		.reg_pld_table1	= 0x25,
> +		.reg_pld_table2	= 0x27,
> +		.reg_pld_table3	= 0x29,
> +		.reg_pld_table4	= 0x2b,
> +		.reg_advance	= 0xad,
> +	},
> +	.ngpios	= 16,
> +	.pins = sx150x_16_pins,
> +	.npins = 16, /* oscio not available */
>  };
>  
> -static const struct of_device_id sx150x_of_match[] = {
> -	{ .compatible = "semtech,sx1508q" },
> -	{ .compatible = "semtech,sx1509q" },
> -	{ .compatible = "semtech,sx1506q" },
> -	{ .compatible = "semtech,sx1502q" },
> -	{},
> +static const struct sx150x_device_data sx1502q_device_data = {
> +	.model = SX150X_123,
> +	.reg_pullup	= 0x02,
> +	.reg_pulldn	= 0x03,
> +	.reg_dir	= 0x01,
> +	.reg_data	= 0x00,
> +	.reg_irq_mask	= 0x05,
> +	.reg_irq_src	= 0x08,
> +	.reg_sense	= 0x07,
> +	.pri.x123 = {
> +		.reg_pld_mode	= 0x10,
> +		.reg_pld_table0	= 0x11,
> +		.reg_pld_table1	= 0x12,
> +		.reg_pld_table2	= 0x13,
> +		.reg_pld_table3	= 0x14,
> +		.reg_pld_table4	= 0x15,
> +		.reg_advance	= 0xad,
> +	},
> +	.ngpios	= 8,
> +	.pins = sx150x_8_pins,
> +	.npins = 8, /* oscio not available */
>  };
>  
>  static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
> @@ -269,11 +253,6 @@ static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
>  	return err;
>  }
>  
> -static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
> -{
> -	return (chip->dev_cfg->ngpios == offset);
> -}
> -
>  /*
>   * These utility functions solve the common problem of locating and setting
>   * configuration bits.  Configuration bits are grouped into registers
> @@ -294,7 +273,7 @@ static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
>   * register and mask the correct bits.
>   */
>  static inline void sx150x_find_cfg(u8 offset, u8 width,
> -				u8 *reg, u8 *mask, u8 *shift)
> +				   u8 *reg, u8 *mask, u8 *shift)
>  {
>  	*reg   -= offset * width / 8;
>  	*mask   = (1 << width) - 1;
> @@ -302,185 +281,241 @@ static inline void sx150x_find_cfg(u8 offset, u8 width,
>  	*mask <<= *shift;
>  }
>  
> -static s32 sx150x_write_cfg(struct sx150x_chip *chip,
> -			u8 offset, u8 width, u8 reg, u8 val)
> +static int sx150x_write_cfg(struct i2c_client *client,
> +			    u8 offset, u8 width, u8 reg, u8 val)
>  {
>  	u8  mask;
>  	u8  data;
>  	u8  shift;
> -	s32 err;
> +	int err;
>  
>  	sx150x_find_cfg(offset, width, &reg, &mask, &shift);
> -	err = sx150x_i2c_read(chip->client, reg, &data);
> +	err = sx150x_i2c_read(client, reg, &data);
>  	if (err < 0)
>  		return err;
>  
>  	data &= ~mask;
>  	data |= (val << shift) & mask;
> -	return sx150x_i2c_write(chip->client, reg, data);
> +	return sx150x_i2c_write(client, reg, data);
>  }
>  
> -static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
> +static int sx150x_read_cfg(struct i2c_client *client,
> +			   u8 offset, u8 width, u8 reg)
>  {
> -	u8  reg = chip->dev_cfg->reg_data;
>  	u8  mask;
>  	u8  data;
>  	u8  shift;
> -	s32 err;
> +	int err;
>  
> -	sx150x_find_cfg(offset, 1, &reg, &mask, &shift);
> -	err = sx150x_i2c_read(chip->client, reg, &data);
> -	if (err >= 0)
> -		err = (data & mask) != 0 ? 1 : 0;
> +	sx150x_find_cfg(offset, width, &reg, &mask, &shift);
> +	err = sx150x_i2c_read(client, reg, &data);
> +	if (err < 0)
> +		return err;
>  
> -	return err;
> +	return (data & mask);
>  }
>  
> -static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
> +static int sx150x_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
>  {
> -	sx150x_i2c_write(chip->client,
> -			chip->dev_cfg->pri.x789.reg_clock,
> -			(val ? 0x1f : 0x10));
> +	return 0;
>  }
>  
> -static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
> +static const char *sx150x_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
> +						unsigned int group)
>  {
> -	sx150x_write_cfg(chip,
> -			offset,
> -			1,
> -			chip->dev_cfg->reg_data,
> -			(val ? 1 : 0));
> +	return NULL;
>  }
>  
> -static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
> +static int sx150x_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
> +					unsigned int group,
> +					const unsigned int **pins,
> +					unsigned int *num_pins)
>  {
> -	return sx150x_write_cfg(chip,
> -				offset,
> -				1,
> -				chip->dev_cfg->reg_dir,
> -				1);
> +	return -ENOTSUPP;
>  }
>  
> -static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
> +static const struct pinctrl_ops sx150x_pinctrl_ops = {
> +	.get_groups_count = sx150x_pinctrl_get_groups_count,
> +	.get_group_name = sx150x_pinctrl_get_group_name,
> +	.get_group_pins = sx150x_pinctrl_get_group_pins,
> +#ifdef CONFIG_OF
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
> +	.dt_free_map = pinctrl_utils_free_map,
> +#endif
> +};
> +
> +static bool sx150x_pin_is_oscio(struct sx150x_pinctrl *pctl, unsigned int pin)
>  {
> -	int err;
> +	if (pin >= pctl->data->npins)
> +		return false;
>  
> -	err = sx150x_write_cfg(chip,
> -			offset,
> -			1,
> -			chip->dev_cfg->reg_data,
> -			(val ? 1 : 0));
> -	if (err >= 0)
> -		err = sx150x_write_cfg(chip,
> -				offset,
> -				1,
> -				chip->dev_cfg->reg_dir,
> -				0);
> -	return err;
> +	/* OSCIO pin is only present in 789 devices */
> +	if (pctl->data->model != SX150X_789)
> +		return false;
> +
> +	return !strcmp(pctl->data->pins[pin].name, "oscio");
>  }
>  
> -static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
> +static int sx150x_gpio_get_direction(struct gpio_chip *chip,
> +				      unsigned int offset)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(gc);
> -	int status = -EINVAL;
> +	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +	int status;
>  
> -	if (!offset_is_oscio(chip, offset)) {
> -		mutex_lock(&chip->lock);
> -		status = sx150x_get_io(chip, offset);
> -		mutex_unlock(&chip->lock);
> -	}
> +	if (sx150x_pin_is_oscio(pctl, offset))
> +		return false;
> +
> +	status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_dir);
> +	if (status >= 0)
> +		status = !!status;
>  
> -	return (status < 0) ? status : !!status;
> +	return status;
>  }
>  
> -static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
> +static int sx150x_gpio_get(struct gpio_chip *chip, unsigned int offset)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(gc);
> +	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +	int status;
>  
> -	mutex_lock(&chip->lock);
> -	if (offset_is_oscio(chip, offset))
> -		sx150x_set_oscio(chip, val);
> -	else
> -		sx150x_set_io(chip, offset, val);
> -	mutex_unlock(&chip->lock);
> +	if (sx150x_pin_is_oscio(pctl, offset))
> +		return -EINVAL;
> +
> +	status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_data);
> +	if (status >= 0)
> +		status = !!status;
> +
> +	return status;
>  }
>  
> -static int sx150x_gpio_set_single_ended(struct gpio_chip *gc,
> -					unsigned offset,
> -                                        enum single_ended_mode mode)
> +static int sx150x_gpio_set_single_ended(struct gpio_chip *chip,
> +					unsigned int offset,
> +					enum single_ended_mode mode)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(gc);
> -
> -	/* On the SX160X 789 we can set open drain */
> -	if (chip->dev_cfg->model != SX150X_789)
> +	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +	int ret;
> +
> +	switch (mode) {
> +	case LINE_MODE_PUSH_PULL:
> +		if (pctl->data->model != SX150X_789 ||
> +		    sx150x_pin_is_oscio(pctl, offset))
> +			return 0;
> +
> +		mutex_lock(&pctl->lock);
> +		ret = sx150x_write_cfg(pctl->client, offset, 1,
> +				       pctl->data->pri.x789.reg_drain,
> +				       0);
> +		mutex_unlock(&pctl->lock);
> +		if (ret < 0)
> +			return ret;
> +		break;
> +
> +	case LINE_MODE_OPEN_DRAIN:
> +		if (pctl->data->model != SX150X_789 ||
> +		    sx150x_pin_is_oscio(pctl, offset))
> +			return -ENOTSUPP;
> +
> +		mutex_lock(&pctl->lock);
> +		ret = sx150x_write_cfg(pctl->client, offset, 1,
> +				       pctl->data->pri.x789.reg_drain,
> +				       1);
> +		mutex_unlock(&pctl->lock);
> +		if (ret < 0)
> +			return ret;
> +		break;
> +
> +	default:
>  		return -ENOTSUPP;
> +	}
>  
> -	if (mode == LINE_MODE_PUSH_PULL)
> -		return sx150x_write_cfg(chip,
> -					offset,
> -					1,
> -					chip->dev_cfg->pri.x789.reg_drain,
> -					0);
> -
> -	if (mode == LINE_MODE_OPEN_DRAIN)
> -		return sx150x_write_cfg(chip,
> -					offset,
> -					1,
> -					chip->dev_cfg->pri.x789.reg_drain,
> -					1);
> -	return -ENOTSUPP;
> +	return 0;
>  }
>  
> -static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
> +static void sx150x_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +			       int value)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(gc);
> -	int status = -EINVAL;
> +	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +
> +	if (sx150x_pin_is_oscio(pctl, offset)) {
>  
> -	if (!offset_is_oscio(chip, offset)) {
> -		mutex_lock(&chip->lock);
> -		status = sx150x_io_input(chip, offset);
> -		mutex_unlock(&chip->lock);
> +		mutex_lock(&pctl->lock);
> +		sx150x_i2c_write(pctl->client,
> +				       pctl->data->pri.x789.reg_clock,
> +				       (value ? 0x1f : 0x10));
> +		mutex_unlock(&pctl->lock);
> +	} else {
> +		mutex_lock(&pctl->lock);
> +		sx150x_write_cfg(pctl->client, offset, 1,
> +				       pctl->data->reg_data,
> +				       (value ? 1 : 0));
> +		mutex_unlock(&pctl->lock);
>  	}
> -	return status;
>  }
>  
> -static int sx150x_gpio_direction_output(struct gpio_chip *gc,
> -					unsigned offset,
> -					int val)
> +static int sx150x_gpio_direction_input(struct gpio_chip *chip,
> +				      unsigned int offset)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(gc);
> -	int status = 0;
> +	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +	int ret;
>  
> -	if (!offset_is_oscio(chip, offset)) {
> -		mutex_lock(&chip->lock);
> -		status = sx150x_io_output(chip, offset, val);
> -		mutex_unlock(&chip->lock);
> +	if (sx150x_pin_is_oscio(pctl, offset))
> +		return -EINVAL;
> +
> +	mutex_lock(&pctl->lock);
> +	ret = sx150x_write_cfg(pctl->client, offset, 1,
> +				pctl->data->reg_dir, 1);
> +	mutex_unlock(&pctl->lock);
> +
> +	return ret;
> +}
> +
> +static int sx150x_gpio_direction_output(struct gpio_chip *chip,
> +				       unsigned int offset, int value)
> +{
> +	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +	int status;
> +
> +	if (sx150x_pin_is_oscio(pctl, offset)) {
> +		sx150x_gpio_set(chip, offset, value);
> +		return 0;
>  	}
> +
> +	mutex_lock(&pctl->lock);
> +	status = sx150x_write_cfg(pctl->client, offset, 1,
> +				  pctl->data->reg_data,
> +				  (value ? 1 : 0));
> +	if (status >= 0)
> +		status = sx150x_write_cfg(pctl->client, offset, 1,
> +					  pctl->data->reg_dir, 0);
> +	mutex_unlock(&pctl->lock);
> +
>  	return status;
>  }
>  
>  static void sx150x_irq_mask(struct irq_data *d)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -	unsigned n = d->hwirq;
> +	struct sx150x_pinctrl *pctl =
> +			gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +	unsigned int n = d->hwirq;
>  
> -	chip->irq_masked |= (1 << n);
> -	chip->irq_update = n;
> +	pctl->irq.masked |= (1 << n);
> +	pctl->irq.update = n;
>  }
>  
>  static void sx150x_irq_unmask(struct irq_data *d)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -	unsigned n = d->hwirq;
> +	struct sx150x_pinctrl *pctl =
> +			gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +	unsigned int n = d->hwirq;
>  
> -	chip->irq_masked &= ~(1 << n);
> -	chip->irq_update = n;
> +	pctl->irq.masked &= ~(1 << n);
> +	pctl->irq.update = n;
>  }
>  
>  static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -	unsigned n, val = 0;
> +	struct sx150x_pinctrl *pctl =
> +			gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +	unsigned int n, val = 0;
>  
>  	if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
>  		return -EINVAL;
> @@ -492,37 +527,40 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
>  	if (flow_type & IRQ_TYPE_EDGE_FALLING)
>  		val |= 0x2;
>  
> -	chip->irq_sense &= ~(3UL << (n * 2));
> -	chip->irq_sense |= val << (n * 2);
> -	chip->irq_update = n;
> +	pctl->irq.sense &= ~(3UL << (n * 2));
> +	pctl->irq.sense |= val << (n * 2);
> +	pctl->irq.update = n;
>  	return 0;
>  }
>  
>  static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
>  {
> -	struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
> -	unsigned nhandled = 0;
> -	unsigned sub_irq;
> -	unsigned n;
> +	struct sx150x_pinctrl *pctl = (struct sx150x_pinctrl *)dev_id;
> +	unsigned int nhandled = 0;
> +	unsigned int sub_irq;
> +	unsigned int n;
>  	s32 err;
>  	u8 val;
>  	int i;
>  
> -	for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
> -		err = sx150x_i2c_read(chip->client,
> -				      chip->dev_cfg->reg_irq_src - i,
> +	for (i = (pctl->data->ngpios / 8) - 1; i >= 0; --i) {
> +		err = sx150x_i2c_read(pctl->client,
> +				      pctl->data->reg_irq_src - i,
>  				      &val);
>  		if (err < 0)
>  			continue;
>  
> -		sx150x_i2c_write(chip->client,
> -				chip->dev_cfg->reg_irq_src - i,
> -				val);
> +		err = sx150x_i2c_write(pctl->client,
> +				       pctl->data->reg_irq_src - i,
> +				       val);
> +		if (err < 0)
> +			continue;
> +
>  		for (n = 0; n < 8; ++n) {
>  			if (val & (1 << n)) {
>  				sub_irq = irq_find_mapping(
> -					chip->gpio_chip.irqdomain,
> -					(i * 8) + n);
> +						pctl->gpio.irqdomain,
> +						(i * 8) + n);
>  				handle_nested_irq(sub_irq);
>  				++nhandled;
>  			}
> @@ -534,251 +572,483 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
>  
>  static void sx150x_irq_bus_lock(struct irq_data *d)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +	struct sx150x_pinctrl *pctl =
> +			gpiochip_get_data(irq_data_get_irq_chip_data(d));
>  
> -	mutex_lock(&chip->lock);
> +	mutex_lock(&pctl->lock);
>  }
>  
>  static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
>  {
> -	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -	unsigned n;
> +	struct sx150x_pinctrl *pctl =
> +			gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +	unsigned int n;
>  
> -	if (chip->irq_update == NO_UPDATE_PENDING)
> +	if (pctl->irq.update < 0)
>  		goto out;
>  
> -	n = chip->irq_update;
> -	chip->irq_update = NO_UPDATE_PENDING;
> +	n = pctl->irq.update;
> +	pctl->irq.update = -1;
>  
>  	/* Avoid updates if nothing changed */
> -	if (chip->dev_sense == chip->irq_sense &&
> -	    chip->dev_masked == chip->irq_masked)
> +	if (pctl->irq.dev_sense == pctl->irq.sense &&
> +	    pctl->irq.dev_masked == pctl->irq.masked)
>  		goto out;
>  
> -	chip->dev_sense = chip->irq_sense;
> -	chip->dev_masked = chip->irq_masked;
> +	pctl->irq.dev_sense = pctl->irq.sense;
> +	pctl->irq.dev_masked = pctl->irq.masked;
>  
> -	if (chip->irq_masked & (1 << n)) {
> -		sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
> -		sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
> +	if (pctl->irq.masked & (1 << n)) {
> +		sx150x_write_cfg(pctl->client, n, 1,
> +				 pctl->data->reg_irq_mask, 1);
> +		sx150x_write_cfg(pctl->client, n, 2,
> +				 pctl->data->reg_sense, 0);
>  	} else {
> -		sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
> -		sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
> -				 chip->irq_sense >> (n * 2));
> +		sx150x_write_cfg(pctl->client, n, 1,
> +				 pctl->data->reg_irq_mask, 0);
> +		sx150x_write_cfg(pctl->client, n, 2,
> +				 pctl->data->reg_sense,
> +				 pctl->irq.sense >> (n * 2));
>  	}
>  out:
> -	mutex_unlock(&chip->lock);
> +	mutex_unlock(&pctl->lock);
>  }
>  
> -static void sx150x_init_chip(struct sx150x_chip *chip,
> -			struct i2c_client *client,
> -			kernel_ulong_t driver_data,
> -			struct sx150x_platform_data *pdata)
> +static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
> +			      unsigned long *config)
>  {
> -	mutex_init(&chip->lock);
> -
> -	chip->client                     = client;
> -	chip->dev_cfg                    = &sx150x_devices[driver_data];
> -	chip->gpio_chip.parent              = &client->dev;
> -	chip->gpio_chip.label            = client->name;
> -	chip->gpio_chip.direction_input  = sx150x_gpio_direction_input;
> -	chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
> -	chip->gpio_chip.get              = sx150x_gpio_get;
> -	chip->gpio_chip.set              = sx150x_gpio_set;
> -	chip->gpio_chip.set_single_ended = sx150x_gpio_set_single_ended;
> -	chip->gpio_chip.base             = pdata->gpio_base;
> -	chip->gpio_chip.can_sleep        = true;
> -	chip->gpio_chip.ngpio            = chip->dev_cfg->ngpios;
> -#ifdef CONFIG_OF_GPIO
> -	chip->gpio_chip.of_node          = client->dev.of_node;
> -	chip->gpio_chip.of_gpio_n_cells  = 2;
> -#endif
> -	if (pdata->oscio_is_gpo)
> -		++chip->gpio_chip.ngpio;
> -
> -	chip->irq_chip.name                = client->name;
> -	chip->irq_chip.irq_mask            = sx150x_irq_mask;
> -	chip->irq_chip.irq_unmask          = sx150x_irq_unmask;
> -	chip->irq_chip.irq_set_type        = sx150x_irq_set_type;
> -	chip->irq_chip.irq_bus_lock        = sx150x_irq_bus_lock;
> -	chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
> -	chip->irq_summary                  = -1;
> -	chip->irq_base                     = -1;
> -	chip->irq_masked                   = ~0;
> -	chip->irq_sense                    = 0;
> -	chip->dev_masked                   = ~0;
> -	chip->dev_sense                    = 0;
> -	chip->irq_update		   = NO_UPDATE_PENDING;
> +	struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +	unsigned int param = pinconf_to_config_param(*config);
> +	int ret;
> +	u32 arg;
> +
> +	if (sx150x_pin_is_oscio(pctl, pin)) {
> +		u8 data;
> +
> +		switch (param) {
> +		case PIN_CONFIG_DRIVE_PUSH_PULL:
> +		case PIN_CONFIG_OUTPUT:
> +			mutex_lock(&pctl->lock);
> +			ret = sx150x_i2c_read(pctl->client,
> +					pctl->data->pri.x789.reg_clock,
> +					&data);
> +			mutex_unlock(&pctl->lock);
> +
> +			if (ret < 0)
> +				return ret;
> +
> +			if (param == PIN_CONFIG_DRIVE_PUSH_PULL)
> +				arg = (data & 0x1f) ? 1 : 0;
> +			else {
> +				if ((data & 0x1f) == 0x1f)
> +					arg = 1;
> +				else if ((data & 0x1f) == 0x10)
> +					arg = 0;
> +				else
> +					return -EINVAL;
> +			}
> +
> +			break;
> +		default:
> +			return -ENOTSUPP;
> +		}
> +
> +		goto out;
> +	}
> +
> +	switch (param) {
> +	case PIN_CONFIG_BIAS_PULL_DOWN:
> +		mutex_lock(&pctl->lock);
> +		ret = sx150x_read_cfg(pctl->client, pin, 1,
> +				      pctl->data->reg_pulldn);
> +		mutex_unlock(&pctl->lock);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		if (!ret)
> +			return -EINVAL;
> +
> +		arg = 1;
> +		break;
> +
> +	case PIN_CONFIG_BIAS_PULL_UP:
> +		mutex_lock(&pctl->lock);
> +		ret = sx150x_read_cfg(pctl->client, pin, 1,
> +				      pctl->data->reg_pullup);
> +		mutex_unlock(&pctl->lock);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		if (!ret)
> +			return -EINVAL;
> +
> +		arg = 1;
> +		break;
> +
> +	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +		if (pctl->data->model != SX150X_789)
> +			return -ENOTSUPP;
> +
> +		mutex_lock(&pctl->lock);
> +		ret = sx150x_read_cfg(pctl->client, pin, 1,
> +				      pctl->data->pri.x789.reg_drain);
> +		mutex_unlock(&pctl->lock);
> +
> +		if (ret < 0)
> +			return ret;
> +
> +		if (!ret)
> +			return -EINVAL;
> +
> +		arg = 1;
> +		break;
> +
> +	case PIN_CONFIG_DRIVE_PUSH_PULL:
> +		if (pctl->data->model != SX150X_789)
> +			arg = true;
> +		else {
> +			mutex_lock(&pctl->lock);
> +			ret = sx150x_read_cfg(pctl->client, pin, 1,
> +					      pctl->data->pri.x789.reg_drain);
> +			mutex_unlock(&pctl->lock);
> +
> +			if (ret < 0)
> +				return ret;
> +
> +			if (ret)
> +				return -EINVAL;
> +
> +			arg = 1;
> +		}
> +		break;
> +
> +	case PIN_CONFIG_OUTPUT:
> +		ret = sx150x_gpio_get_direction(&pctl->gpio, pin);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (ret)
> +			return -EINVAL;
> +
> +		ret = sx150x_gpio_get(&pctl->gpio, pin);
> +		if (ret < 0)
> +			return ret;
> +
> +		arg = ret;
> +		break;
> +
> +	default:
> +		return -ENOTSUPP;
> +	}
> +
> +out:
> +	*config = pinconf_to_config_packed(param, arg);
> +
> +	return 0;
> +}
> +
> +static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
> +			      unsigned long *configs, unsigned int num_configs)
> +{
> +	struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +	enum pin_config_param param;
> +	u32 arg;
> +	int i;
> +	int ret;
> +
> +	for (i = 0; i < num_configs; i++) {
> +		param = pinconf_to_config_param(configs[i]);
> +		arg = pinconf_to_config_argument(configs[i]);
> +
> +		if (sx150x_pin_is_oscio(pctl, pin)) {
> +			if (param == PIN_CONFIG_OUTPUT) {
> +				ret = sx150x_gpio_direction_output(&pctl->gpio,
> +								   pin, arg);
> +				if (ret < 0)
> +					return ret;
> +
> +				continue;
> +			} else
> +				return -ENOTSUPP;
> +		}
> +
> +		switch (param) {
> +		case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
> +		case PIN_CONFIG_BIAS_DISABLE:
> +			mutex_lock(&pctl->lock);
> +			ret = sx150x_write_cfg(pctl->client, pin, 1,
> +					       pctl->data->reg_pulldn, 0);
> +			mutex_unlock(&pctl->lock);
> +			if (ret < 0)
> +				return ret;
> +
> +			mutex_lock(&pctl->lock);
> +			ret = sx150x_write_cfg(pctl->client, pin, 1,
> +					       pctl->data->reg_pullup, 0);
> +			mutex_unlock(&pctl->lock);
> +			if (ret < 0)
> +				return ret;
> +
> +			break;
> +
> +		case PIN_CONFIG_BIAS_PULL_UP:
> +			mutex_lock(&pctl->lock);
> +			ret = sx150x_write_cfg(pctl->client, pin, 1,
> +					       pctl->data->reg_pullup,
> +					       1);
> +			mutex_unlock(&pctl->lock);
> +			if (ret < 0)
> +				return ret;
> +
> +			break;
> +
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			mutex_lock(&pctl->lock);
> +			ret = sx150x_write_cfg(pctl->client, pin, 1,
> +					       pctl->data->reg_pulldn,
> +					       1);
> +			mutex_unlock(&pctl->lock);
> +			if (ret < 0)
> +				return ret;
> +
> +			break;
> +
> +		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +			ret = sx150x_gpio_set_single_ended(&pctl->gpio,
> +						pin, LINE_MODE_OPEN_DRAIN);
> +			if (ret < 0)
> +				return ret;
> +
> +			break;
> +
> +		case PIN_CONFIG_DRIVE_PUSH_PULL:
> +			ret = sx150x_gpio_set_single_ended(&pctl->gpio,
> +						pin, LINE_MODE_PUSH_PULL);
> +			if (ret < 0)
> +				return ret;
> +
> +			break;
> +
> +		case PIN_CONFIG_OUTPUT:
> +			ret = sx150x_gpio_direction_output(&pctl->gpio,
> +							   pin, arg);
> +			if (ret < 0)
> +				return ret;
> +
> +			break;
> +
> +		default:
> +			return -ENOTSUPP;
> +		}
> +	} /* for each config */
> +
> +	return 0;
>  }
>  
> -static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
> +static const struct pinconf_ops sx150x_pinconf_ops = {
> +	.pin_config_get = sx150x_pinconf_get,
> +	.pin_config_set = sx150x_pinconf_set,
> +	.is_generic = true,
> +};
> +
> +static const struct i2c_device_id sx150x_id[] = {
> +	{"sx1508q", (kernel_ulong_t) &sx1508q_device_data },
> +	{"sx1509q", (kernel_ulong_t) &sx1509q_device_data },
> +	{"sx1506q", (kernel_ulong_t) &sx1506q_device_data },
> +	{"sx1502q", (kernel_ulong_t) &sx1502q_device_data },
> +	{}
> +};
> +
> +static const struct of_device_id sx150x_of_match[] = {
> +	{ .compatible = "semtech,sx1508q" },
> +	{ .compatible = "semtech,sx1509q" },
> +	{ .compatible = "semtech,sx1506q" },
> +	{ .compatible = "semtech,sx1502q" },
> +	{},
> +};
> +
> +static int sx150x_init_io(struct sx150x_pinctrl *pctl, u8 base, u16 cfg)
>  {
>  	int err = 0;
> -	unsigned n;
> +	unsigned int n;
>  
> -	for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
> -		err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
> +	for (n = 0; err >= 0 && n < (pctl->data->ngpios / 8); ++n)
> +		err = sx150x_i2c_write(pctl->client, base - n, cfg >> (n * 8));
>  	return err;
>  }
>  
> -static int sx150x_reset(struct sx150x_chip *chip)
> +static int sx150x_reset(struct sx150x_pinctrl *pctl)
>  {
>  	int err;
>  
> -	err = i2c_smbus_write_byte_data(chip->client,
> -					chip->dev_cfg->pri.x789.reg_reset,
> +	err = i2c_smbus_write_byte_data(pctl->client,
> +					pctl->data->pri.x789.reg_reset,
>  					0x12);
>  	if (err < 0)
>  		return err;
>  
> -	err = i2c_smbus_write_byte_data(chip->client,
> -					chip->dev_cfg->pri.x789.reg_reset,
> +	err = i2c_smbus_write_byte_data(pctl->client,
> +					pctl->data->pri.x789.reg_reset,
>  					0x34);
>  	return err;
>  }
>  
> -static int sx150x_init_hw(struct sx150x_chip *chip,
> -			struct sx150x_platform_data *pdata)
> +static int sx150x_init_hw(struct sx150x_pinctrl *pctl)
>  {
> -	int err = 0;
> +	int err;
>  
> -	if (pdata->reset_during_probe) {
> -		err = sx150x_reset(chip);
> +	if (pctl->data->model == SX150X_789 &&
> +	    of_property_read_bool(pctl->dev->of_node, "semtech,probe-reset")) {
> +		err = sx150x_reset(pctl);
>  		if (err < 0)
>  			return err;
>  	}
>  
> -	if (chip->dev_cfg->model == SX150X_789)
> -		err = sx150x_i2c_write(chip->client,
> -				chip->dev_cfg->pri.x789.reg_misc,
> +	if (pctl->data->model == SX150X_789)
> +		err = sx150x_i2c_write(pctl->client,
> +				pctl->data->pri.x789.reg_misc,
>  				0x01);
> -	else if (chip->dev_cfg->model == SX150X_456)
> -		err = sx150x_i2c_write(chip->client,
> -				chip->dev_cfg->pri.x456.reg_advance,
> +	else if (pctl->data->model == SX150X_456)
> +		err = sx150x_i2c_write(pctl->client,
> +				pctl->data->pri.x456.reg_advance,
>  				0x04);
>  	else
> -		err = sx150x_i2c_write(chip->client,
> -				chip->dev_cfg->pri.x123.reg_advance,
> +		err = sx150x_i2c_write(pctl->client,
> +				pctl->data->pri.x123.reg_advance,
>  				0x00);
>  	if (err < 0)
>  		return err;
>  
> -	err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
> -			pdata->io_pullup_ena);
> -	if (err < 0)
> -		return err;
> -
> -	err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
> -			pdata->io_pulldn_ena);
> -	if (err < 0)
> -		return err;
> -
> -	if (chip->dev_cfg->model == SX150X_789) {
> -		err = sx150x_init_io(chip,
> -				chip->dev_cfg->pri.x789.reg_polarity,
> -				pdata->io_polarity);
> +	/* Set all pins to work in normal mode */
> +	if (pctl->data->model == SX150X_789) {
> +		err = sx150x_init_io(pctl,
> +				pctl->data->pri.x789.reg_polarity,
> +				0);
>  		if (err < 0)
>  			return err;
> -	} else if (chip->dev_cfg->model == SX150X_456) {
> +	} else if (pctl->data->model == SX150X_456) {
>  		/* Set all pins to work in normal mode */
> -		err = sx150x_init_io(chip,
> -				chip->dev_cfg->pri.x456.reg_pld_mode,
> +		err = sx150x_init_io(pctl,
> +				pctl->data->pri.x456.reg_pld_mode,
>  				0);
>  		if (err < 0)
>  			return err;
>  	} else {
>  		/* Set all pins to work in normal mode */
> -		err = sx150x_init_io(chip,
> -				chip->dev_cfg->pri.x123.reg_pld_mode,
> +		err = sx150x_init_io(pctl,
> +				pctl->data->pri.x123.reg_pld_mode,
>  				0);
>  		if (err < 0)
>  			return err;
>  	}
>  
> -
> -	if (pdata->oscio_is_gpo)
> -		sx150x_set_oscio(chip, 0);
> -
> -	return err;
> -}
> -
> -static int sx150x_install_irq_chip(struct sx150x_chip *chip,
> -				int irq_summary,
> -				int irq_base)
> -{
> -	int err;
> -
> -	chip->irq_summary = irq_summary;
> -	chip->irq_base    = irq_base;
> -
> -	/* Add gpio chip to irq subsystem */
> -	err = gpiochip_irqchip_add(&chip->gpio_chip,
> -		&chip->irq_chip, chip->irq_base,
> -		handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
> -	if (err) {
> -		dev_err(&chip->client->dev,
> -			"could not connect irqchip to gpiochip\n");
> -		return  err;
> -	}
> -
> -	err = devm_request_threaded_irq(&chip->client->dev,
> -			irq_summary, NULL, sx150x_irq_thread_fn,
> -			IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING,
> -			chip->irq_chip.name, chip);
> -	if (err < 0) {
> -		chip->irq_summary = -1;
> -		chip->irq_base    = -1;
> -	}
> -
> -	return err;
> +	return 0;
>  }
>  
>  static int sx150x_probe(struct i2c_client *client,
> -				const struct i2c_device_id *id)
> +			const struct i2c_device_id *id)
>  {
>  	static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
>  				     I2C_FUNC_SMBUS_WRITE_WORD_DATA;
> -	struct sx150x_platform_data *pdata;
> -	struct sx150x_chip *chip;
> -	int rc;
> +	struct device *dev = &client->dev;
> +	struct sx150x_pinctrl *pctl;
> +	int ret;
>  
> -	pdata = dev_get_platdata(&client->dev);
> -	if (!pdata)
> +	if (!id->driver_data)
>  		return -EINVAL;
>  
>  	if (!i2c_check_functionality(client->adapter, i2c_funcs))
>  		return -ENOSYS;
>  
> -	chip = devm_kzalloc(&client->dev,
> -		sizeof(struct sx150x_chip), GFP_KERNEL);
> -	if (!chip)
> +	pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
> +	if (!pctl)
>  		return -ENOMEM;
>  
> -	sx150x_init_chip(chip, client, id->driver_data, pdata);
> -	rc = sx150x_init_hw(chip, pdata);
> -	if (rc < 0)
> -		return rc;
> -
> -	rc = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
> -	if (rc)
> -		return rc;
> -
> -	if (pdata->irq_summary >= 0) {
> -		rc = sx150x_install_irq_chip(chip,
> -					pdata->irq_summary,
> -					pdata->irq_base);
> -		if (rc < 0)
> -			return rc;
> +	pctl->dev = dev;
> +	pctl->client = client;
> +	pctl->data = (void *)id->driver_data;
> +
> +	mutex_init(&pctl->lock);
> +
> +	ret = sx150x_init_hw(pctl);
> +	if (ret)
> +		return ret;
> +
> +	/* Register GPIO controller */
> +	pctl->gpio.label = devm_kstrdup(dev, client->name, GFP_KERNEL);
> +	pctl->gpio.base = -1;
> +	pctl->gpio.ngpio = pctl->data->npins;
> +	pctl->gpio.get_direction = sx150x_gpio_get_direction;
> +	pctl->gpio.direction_input = sx150x_gpio_direction_input;
> +	pctl->gpio.direction_output = sx150x_gpio_direction_output;
> +	pctl->gpio.get = sx150x_gpio_get;
> +	pctl->gpio.set = sx150x_gpio_set;
> +	pctl->gpio.set_single_ended = sx150x_gpio_set_single_ended;
> +	pctl->gpio.parent = dev;
> +#ifdef CONFIG_OF_GPIO
> +	pctl->gpio.of_node = dev->of_node;
> +#endif
> +	pctl->gpio.can_sleep = true;
> +
> +	ret = devm_gpiochip_add_data(dev, &pctl->gpio, pctl);
> +	if (ret)
> +		return ret;
> +
> +	/* Add Interrupt support if an irq is specified */
> +	if (client->irq > 0) {
> +		pctl->irq_chip.name = devm_kstrdup(dev, client->name,
> +						   GFP_KERNEL);
> +		pctl->irq_chip.irq_mask = sx150x_irq_mask;
> +		pctl->irq_chip.irq_unmask = sx150x_irq_unmask;
> +		pctl->irq_chip.irq_set_type = sx150x_irq_set_type;
> +		pctl->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
> +		pctl->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
> +
> +		pctl->irq.masked = ~0;
> +		pctl->irq.sense = 0;
> +		pctl->irq.dev_masked = ~0;
> +		pctl->irq.dev_sense = 0;
> +		pctl->irq.update = -1;
> +
> +		ret = gpiochip_irqchip_add(&pctl->gpio,
> +					   &pctl->irq_chip, 0,
> +					   handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
> +		if (ret) {
> +			dev_err(dev, "could not connect irqchip to gpiochip\n");
> +			return ret;
> +		}
> +
> +		ret = devm_request_threaded_irq(dev, client->irq, NULL,
> +						sx150x_irq_thread_fn,
> +						IRQF_ONESHOT | IRQF_SHARED |
> +						IRQF_TRIGGER_FALLING,
> +						pctl->irq_chip.name, pctl);
> +		if (ret < 0)
> +			return ret;
>  	}
>  
> -	i2c_set_clientdata(client, chip);
> +	/* Pinctrl_desc */
> +	pctl->pinctrl_desc.name = "sx150x-pinctrl";
> +	pctl->pinctrl_desc.pctlops = &sx150x_pinctrl_ops;
> +	pctl->pinctrl_desc.confops = &sx150x_pinconf_ops;
> +	pctl->pinctrl_desc.pins = pctl->data->pins;
> +	pctl->pinctrl_desc.npins = pctl->data->npins;
> +	pctl->pinctrl_desc.owner = THIS_MODULE;
> +
> +	pctl->pctldev = pinctrl_register(&pctl->pinctrl_desc, dev, pctl);
> +	if (IS_ERR(pctl->pctldev)) {
> +		dev_err(dev, "Failed to register pinctrl device\n");
> +		return PTR_ERR(pctl->pctldev);
> +	}
>  
>  	return 0;
>  }
>  
>  static struct i2c_driver sx150x_driver = {
>  	.driver = {
> -		.name = "sx150x",
> +		.name = "sx150x-pinctrl",
>  		.of_match_table = of_match_ptr(sx150x_of_match),
>  	},
>  	.probe    = sx150x_probe,
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrey Smirnov Oct. 23, 2016, 2:50 a.m. UTC | #2
On Fri, Oct 21, 2016 at 2:09 AM, Neil Armstrong <narmstrong@baylibre.com> wrote:
> Since the I2C sx150x GPIO expander driver uses platform_data to manage
> the pins configurations, rewrite the driver as a pinctrl driver using
> pinconf to get/set pin configurations from DT or debugfs.
>
> The pinctrl driver is functionnally equivalent as the gpio-only driver
> and can use DT for pinconf. The platform_data confirmation is dropped.
>
> This patchset removed the gpio-only driver and selects the Pinctrl driver
> config instead. This patchset also migrates the gpio dt-bindings to pinctrl
> and add the pinctrl optional properties.
>
> The driver was tested with a SX1509 device on a BeagleBone black with
> interrupt support and on an X86_64 machine over an I2C to USB converter.
>
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> This is a fixed version that builds and runs on non-OF platforms and on
> arm based OF. The GPIO version is removed and the bindings are also moved
> to the pinctrl bindings.
>
> Changes since v2 at http://lkml.kernel.org/r/1474991325-1754-1-git-send-email-narmstrong@baylibre.com:
>  - rebased on v4.9-rc1
>  - removed MODULE_DEVICE_TABLE as in upstream bb411e771b0e [1]
>
> Changes since v1 at http://lkml.kernel.org/r/1473166599-29266-1-git-send-email-narmstrong@baylibre.com:
>  - Fix Kconfig descriptions on pinctrl and gpio
>  - Fix Kconfig dependency
>  - Remove oscio support for non-789 devices
>  - correct typo in dt bindings
>  - remove probe reset for non-789 devices
>
> Changes since RFC at http://lkml.kernel.org/r/1472130692-14404-1-git-send-email-narmstrong@baylibre.com:
>  - Put #ifdef CONFIG_OF/CONFIG_OF_GPIO to remove OF code for non-of platforms
>  - No more rely on OF_GPIO config
>  - Moved and enhanced bindings to pinctrl bindings
>  - Removed gpio-sx150x.c
>  - Temporary select PINCTRL_SX150X when GPIO_SX150X
>  - Temporary mark GPIO_SX150X as deprecated
>
> [1] gpio: sx150x: fix implicit assumption module.h is present
>
>  .../gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} |   46 +-
>  drivers/gpio/Kconfig                               |   13 +-
>  drivers/gpio/Makefile                              |    1 -
>  drivers/pinctrl/Kconfig                            |   14 +
>  drivers/pinctrl/Makefile                           |    1 +
>  .../gpio-sx150x.c => pinctrl/pinctrl-sx150x.c}     | 1174 ++++++++++++--------
>  6 files changed, 779 insertions(+), 470 deletions(-)
>  rename Documentation/devicetree/bindings/{gpio/gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} (40%)
>  rename drivers/{gpio/gpio-sx150x.c => pinctrl/pinctrl-sx150x.c} (23%)
>
> diff --git a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> similarity index 40%
> rename from Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
> rename to Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> index c809acb..c293c8a 100644
> --- a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> @@ -1,34 +1,54 @@
>  SEMTECH SX150x GPIO expander bindings
>
> +Please refer to pinctrl-bindings.txt, ../gpio/gpio.txt, and
> +../interrupt-controller/interrupts.txt for generic information regarding
> +pin controller, GPIO, and interrupt bindings.
>
>  Required properties:
> -
> -- compatible: should be "semtech,sx1506q",
> +- compatible: should be one of :
> +                       "semtech,sx1506q",
>                         "semtech,sx1508q",
>                         "semtech,sx1509q",
>                         "semtech,sx1502q".
>
>  - reg: The I2C slave address for this device.
>
> -- interrupt-parent: phandle of the parent interrupt controller.
> -
> -- interrupts: Interrupt specifier for the controllers interrupt.
> -
>  - #gpio-cells: Should be 2. The first cell is the GPIO number and the
>                 second cell is used to specify optional parameters:
>                 bit 0: polarity (0: normal, 1: inverted)
>
>  - gpio-controller: Marks the device as a GPIO controller.
>
> +Optional properties :
> +- interrupt-parent: phandle of the parent interrupt controller.
> +
> +- interrupts: Interrupt specifier for the controllers interrupt.
> +
>  - interrupt-controller: Marks the device as a interrupt controller.
>
> +- semtech,probe-reset: Will trigger a reset of the GPIO expander on probe,
> +               only for sx1508q and sx1509q
> +
>  The GPIO expander can optionally be used as an interrupt controller, in
> -which case it uses the default two cell specifier as described in
> -Documentation/devicetree/bindings/interrupt-controller/interrupts.txt.
> +which case it uses the default two cell specifier.
> +
> +Required properties for pin configuration sub-nodes:
> + - pins: List of pins to which the configuration applies.
> +
> +Optional properties for pin configuration sub-nodes:
> +----------------------------------------------------
> + - bias-disable: disable any pin bias, except the OSCIO pin
> + - bias-pull-up: pull up the pin, except the OSCIO pin
> + - bias-pull-down: pull down the pin, except the OSCIO pin
> + - bias-pull-pin-default: use pin-default pull state, except the OSCIO pin
> + - drive-push-pull: drive actively high and low
> + - drive-open-drain: drive with open drain only for sx1508q and sx1509q and except the OSCIO pin
> + - output-low: set the pin to output mode with low level
> + - output-high: set the pin to output mode with high level
>
>  Example:
>
> -       i2c_gpio_expander@20{
> +       i2c0gpio-expander@20{
>                 #gpio-cells = <2>;
>                 #interrupt-cells = <2>;
>                 compatible = "semtech,sx1506q";
> @@ -38,4 +58,12 @@ Example:
>
>                 gpio-controller;
>                 interrupt-controller;
> +
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&gpio1_cfg_pins>;
> +
> +               gpio1_cfg_pins: gpio1-cfg {
> +                       pins = "gpio1";
> +                       bias-pull-up;
> +               };
>         };
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 26ee00f..db3f7aa 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -781,16 +781,13 @@ config GPIO_PCF857X
>           platform-neutral GPIO calls.
>
>  config GPIO_SX150X
> -       bool "Semtech SX150x I2C GPIO expander"
> -       depends on I2C=y
> -       select GPIOLIB_IRQCHIP
> +       bool "Semtech SX150x I2C GPIO expander (deprecated)"
> +       depends on PINCTRL && I2C=y
> +       select PINCTRL_SX150X
>         default n
>         help
> -         Say yes here to provide support for Semtech SX150-series I2C
> -         GPIO expanders. Compatible models include:
> -
> -         8 bits:  sx1508q
> -         16 bits: sx1509q
> +         Say yes here to provide support for Semtech SX150x-series I2C
> +         GPIO expanders. The GPIO driver was replaced by a Pinctrl version.
>
>  config GPIO_TPIC2810
>         tristate "TPIC2810 8-Bit I2C GPO expander"
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index ab28a2d..76c7af6 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_SPEAR_SPICS)      += gpio-spear-spics.o
>  obj-$(CONFIG_GPIO_STA2X11)     += gpio-sta2x11.o
>  obj-$(CONFIG_GPIO_STMPE)       += gpio-stmpe.o
>  obj-$(CONFIG_GPIO_STP_XWAY)    += gpio-stp-xway.o
> -obj-$(CONFIG_GPIO_SX150X)      += gpio-sx150x.o
>  obj-$(CONFIG_GPIO_SYSCON)      += gpio-syscon.o
>  obj-$(CONFIG_GPIO_TB10X)       += gpio-tb10x.o
>  obj-$(CONFIG_GPIO_TC3589X)     += gpio-tc3589x.o
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 0e75d94..801fa8b 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -164,6 +164,20 @@ config PINCTRL_SIRF
>         select GENERIC_PINCONF
>         select GPIOLIB_IRQCHIP
>
> +config PINCTRL_SX150X
> +       bool "Semtech SX150x I2C GPIO expander pinctrl driver"
> +       depends on GPIOLIB && I2C=y
> +       select PINMUX
> +       select PINCONF
> +       select GENERIC_PINCONF
> +       select GPIOLIB_IRQCHIP
> +       help
> +         Say yes here to provide support for Semtech SX150x-series I2C
> +         GPIO expanders as pinctrl module.
> +         Compatible models include:
> +         - 8 bits:  sx1508q, sx1502q
> +         - 16 bits: sx1509q, sx1506q
> +
>  config PINCTRL_PISTACHIO
>         def_bool y if MACH_PISTACHIO
>         depends on GPIOLIB
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 11bad37..3b8e6f7 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PINCTRL_PISTACHIO)       += pinctrl-pistachio.o
>  obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
>  obj-$(CONFIG_PINCTRL_SINGLE)   += pinctrl-single.o
>  obj-$(CONFIG_PINCTRL_SIRF)     += sirf/
> +obj-$(CONFIG_PINCTRL_SX150X)   += pinctrl-sx150x.o
>  obj-$(CONFIG_ARCH_TEGRA)       += tegra/
>  obj-$(CONFIG_PINCTRL_TZ1090)   += pinctrl-tz1090.o
>  obj-$(CONFIG_PINCTRL_TZ1090_PDC)       += pinctrl-tz1090-pdc.o
> diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c
> similarity index 23%
> rename from drivers/gpio/gpio-sx150x.c
> rename to drivers/pinctrl/pinctrl-sx150x.c
> index af95de8..e985471 100644
> --- a/drivers/gpio/gpio-sx150x.c
> +++ b/drivers/pinctrl/pinctrl-sx150x.c
> @@ -1,4 +1,8 @@
> -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
> +/*
> + * Copyright (c) 2016, BayLibre, SAS. All rights reserved.
> + * Author: Neil Armstrong <narmstrong@baylibre.com>
> + *
> + * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
>   *
>   * Driver for Semtech SX150X I2C GPIO Expanders
>   *
> @@ -12,13 +16,8 @@
>   * 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.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> - * 02110-1301, USA.
>   */
> -#include <linux/gpio.h>
> +
>  #include <linux/i2c.h>
>  #include <linux/init.h>
>  #include <linux/interrupt.h>
> @@ -26,17 +25,23 @@
>  #include <linux/mutex.h>
>  #include <linux/slab.h>
>  #include <linux/of.h>
> -#include <linux/of_address.h>
> -#include <linux/of_irq.h>
> -#include <linux/of_gpio.h>
> -#include <linux/of_device.h>
> +#include <linux/gpio.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
>
> -#define NO_UPDATE_PENDING      -1
> +#include "core.h"
> +#include "pinconf.h"
> +#include "pinctrl-utils.h"
>
>  /* The chip models of sx150x */
> -#define SX150X_123 0
> -#define SX150X_456 1
> -#define SX150X_789 2
> +enum {
> +       SX150X_123 = 0,
> +       SX150X_456,
> +       SX150X_789,
> +};
>
>  struct sx150x_123_pri {
>         u8 reg_pld_mode;
> @@ -82,167 +87,146 @@ struct sx150x_device_data {
>                 struct sx150x_456_pri x456;
>                 struct sx150x_789_pri x789;
>         } pri;
> +       const struct pinctrl_pin_desc *pins;
> +       unsigned int npins;
>  };
>
> -/**
> - * struct sx150x_platform_data - config data for SX150x driver
> - * @gpio_base: The index number of the first GPIO assigned to this
> - *             GPIO expander.  The expander will create a block of
> - *             consecutively numbered gpios beginning at the given base,
> - *             with the size of the block depending on the model of the
> - *             expander chip.
> - * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO
> - *                instead of as an oscillator, increasing the size of the
> - *                GP(I)O pool created by this expander by one.  The
> - *                output-only GPO pin will be added at the end of the block.
> - * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor
> - *                 for each IO line in the expander.  Setting the bit at
> - *                 position n will enable the pull-up for the IO at
> - *                 the corresponding offset.  For chips with fewer than
> - *                 16 IO pins, high-end bits are ignored.
> - * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down
> - *                 resistor for each IO line in the expander. Setting the
> - *                 bit at position n will enable the pull-down for the IO at
> - *                 the corresponding offset.  For chips with fewer than
> - *                 16 IO pins, high-end bits are ignored.
> - * @io_polarity: A bit-mask which enables polarity inversion for each IO line
> - *               in the expander.  Setting the bit at position n inverts
> - *               the polarity of that IO line, while clearing it results
> - *               in normal polarity. For chips with fewer than 16 IO pins,
> - *               high-end bits are ignored.
> - * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line
> - *               is connected, via which it reports interrupt events
> - *               across all GPIO lines.  This must be a real,
> - *               pre-existing IRQ line.
> - *               Setting this value < 0 disables the irq_chip functionality
> - *               of the driver.
> - * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based
> - *            IRQ lines will appear.  Similarly to gpio_base, the expander
> - *            will create a block of irqs beginning at this number.
> - *            This value is ignored if irq_summary is < 0.
> - * @reset_during_probe: If set to true, the driver will trigger a full
> - *                      reset of the chip at the beginning of the probe
> - *                      in order to place it in a known state.
> - */
> -struct sx150x_platform_data {
> -       unsigned gpio_base;
> -       bool     oscio_is_gpo;
> -       u16      io_pullup_ena;
> -       u16      io_pulldn_ena;
> -       u16      io_polarity;
> -       int      irq_summary;
> -       unsigned irq_base;
> -       bool     reset_during_probe;
> +struct sx150x_pinctrl {
> +       struct device *dev;
> +       struct i2c_client *client;
> +       struct pinctrl_dev *pctldev;
> +       struct pinctrl_desc pinctrl_desc;
> +       struct gpio_chip gpio;
> +       struct irq_chip irq_chip;
> +       struct {
> +               int update;
> +               u32 sense;
> +               u32 masked;
> +               u32 dev_sense;
> +               u32 dev_masked;
> +       } irq;
> +       struct mutex lock;
> +       const struct sx150x_device_data *data;
>  };
>
> -struct sx150x_chip {
> -       struct gpio_chip                 gpio_chip;
> -       struct i2c_client               *client;
> -       const struct sx150x_device_data *dev_cfg;
> -       int                              irq_summary;
> -       int                              irq_base;
> -       int                              irq_update;
> -       u32                              irq_sense;
> -       u32                              irq_masked;
> -       u32                              dev_sense;
> -       u32                              dev_masked;
> -       struct irq_chip                  irq_chip;
> -       struct mutex                     lock;
> +static const struct pinctrl_pin_desc sx150x_8_pins[] = {
> +       PINCTRL_PIN(0, "gpio0"),
> +       PINCTRL_PIN(1, "gpio1"),
> +       PINCTRL_PIN(2, "gpio2"),
> +       PINCTRL_PIN(3, "gpio3"),
> +       PINCTRL_PIN(4, "gpio4"),
> +       PINCTRL_PIN(5, "gpio5"),
> +       PINCTRL_PIN(6, "gpio6"),
> +       PINCTRL_PIN(7, "gpio7"),
> +       PINCTRL_PIN(8, "oscio"),
>  };
>
> -static const struct sx150x_device_data sx150x_devices[] = {
> -       [0] = { /* sx1508q */
> -               .model = SX150X_789,
> -               .reg_pullup     = 0x03,
> -               .reg_pulldn     = 0x04,
> -               .reg_dir        = 0x07,
> -               .reg_data       = 0x08,
> -               .reg_irq_mask   = 0x09,
> -               .reg_irq_src    = 0x0c,
> -               .reg_sense      = 0x0b,
> -               .pri.x789 = {
> -                       .reg_drain      = 0x05,
> -                       .reg_polarity   = 0x06,
> -                       .reg_clock      = 0x0f,
> -                       .reg_misc       = 0x10,
> -                       .reg_reset      = 0x7d,
> -               },
> -               .ngpios = 8,
> -       },
> -       [1] = { /* sx1509q */
> -               .model = SX150X_789,
> -               .reg_pullup     = 0x07,
> -               .reg_pulldn     = 0x09,
> -               .reg_dir        = 0x0f,
> -               .reg_data       = 0x11,
> -               .reg_irq_mask   = 0x13,
> -               .reg_irq_src    = 0x19,
> -               .reg_sense      = 0x17,
> -               .pri.x789 = {
> -                       .reg_drain      = 0x0b,
> -                       .reg_polarity   = 0x0d,
> -                       .reg_clock      = 0x1e,
> -                       .reg_misc       = 0x1f,
> -                       .reg_reset      = 0x7d,
> -               },
> -               .ngpios = 16
> -       },
> -       [2] = { /* sx1506q */
> -               .model = SX150X_456,
> -               .reg_pullup     = 0x05,
> -               .reg_pulldn     = 0x07,
> -               .reg_dir        = 0x03,
> -               .reg_data       = 0x01,
> -               .reg_irq_mask   = 0x09,
> -               .reg_irq_src    = 0x0f,
> -               .reg_sense      = 0x0d,
> -               .pri.x456 = {
> -                       .reg_pld_mode   = 0x21,
> -                       .reg_pld_table0 = 0x23,
> -                       .reg_pld_table1 = 0x25,
> -                       .reg_pld_table2 = 0x27,
> -                       .reg_pld_table3 = 0x29,
> -                       .reg_pld_table4 = 0x2b,
> -                       .reg_advance    = 0xad,
> -               },
> -               .ngpios = 16
> +static const struct pinctrl_pin_desc sx150x_16_pins[] = {
> +       PINCTRL_PIN(0, "gpio0"),
> +       PINCTRL_PIN(1, "gpio1"),
> +       PINCTRL_PIN(2, "gpio2"),
> +       PINCTRL_PIN(3, "gpio3"),
> +       PINCTRL_PIN(4, "gpio4"),
> +       PINCTRL_PIN(5, "gpio5"),
> +       PINCTRL_PIN(6, "gpio6"),
> +       PINCTRL_PIN(7, "gpio7"),
> +       PINCTRL_PIN(8, "gpio8"),
> +       PINCTRL_PIN(9, "gpio9"),
> +       PINCTRL_PIN(10, "gpio10"),
> +       PINCTRL_PIN(11, "gpio11"),
> +       PINCTRL_PIN(12, "gpio12"),
> +       PINCTRL_PIN(13, "gpio13"),
> +       PINCTRL_PIN(14, "gpio14"),
> +       PINCTRL_PIN(15, "gpio15"),
> +       PINCTRL_PIN(16, "oscio"),
> +};
> +
> +static const struct sx150x_device_data sx1508q_device_data = {
> +       .model = SX150X_789,
> +       .reg_pullup     = 0x03,
> +       .reg_pulldn     = 0x04,
> +       .reg_dir        = 0x07,
> +       .reg_data       = 0x08,
> +       .reg_irq_mask   = 0x09,
> +       .reg_irq_src    = 0x0c,
> +       .reg_sense      = 0x0b,
> +       .pri.x789 = {
> +               .reg_drain      = 0x05,
> +               .reg_polarity   = 0x06,
> +               .reg_clock      = 0x0f,
> +               .reg_misc       = 0x10,
> +               .reg_reset      = 0x7d,
>         },
> -       [3] = { /* sx1502q */
> -               .model = SX150X_123,
> -               .reg_pullup     = 0x02,
> -               .reg_pulldn     = 0x03,
> -               .reg_dir        = 0x01,
> -               .reg_data       = 0x00,
> -               .reg_irq_mask   = 0x05,
> -               .reg_irq_src    = 0x08,
> -               .reg_sense      = 0x07,
> -               .pri.x123 = {
> -                       .reg_pld_mode   = 0x10,
> -                       .reg_pld_table0 = 0x11,
> -                       .reg_pld_table1 = 0x12,
> -                       .reg_pld_table2 = 0x13,
> -                       .reg_pld_table3 = 0x14,
> -                       .reg_pld_table4 = 0x15,
> -                       .reg_advance    = 0xad,
> -               },
> -               .ngpios = 8,
> +       .ngpios = 8,
> +       .pins = sx150x_8_pins,
> +       .npins = ARRAY_SIZE(sx150x_8_pins),
> +};
> +
> +static const struct sx150x_device_data sx1509q_device_data = {
> +       .model = SX150X_789,
> +       .reg_pullup     = 0x07,
> +       .reg_pulldn     = 0x09,
> +       .reg_dir        = 0x0f,
> +       .reg_data       = 0x11,
> +       .reg_irq_mask   = 0x13,
> +       .reg_irq_src    = 0x19,
> +       .reg_sense      = 0x17,
> +       .pri.x789 = {
> +               .reg_drain      = 0x0b,
> +               .reg_polarity   = 0x0d,
> +               .reg_clock      = 0x1e,
> +               .reg_misc       = 0x1f,
> +               .reg_reset      = 0x7d,
>         },
> +       .ngpios = 16,
> +       .pins = sx150x_16_pins,
> +       .npins = ARRAY_SIZE(sx150x_16_pins),
>  };
>
> -static const struct i2c_device_id sx150x_id[] = {
> -       {"sx1508q", 0},
> -       {"sx1509q", 1},
> -       {"sx1506q", 2},
> -       {"sx1502q", 3},
> -       {}
> +static const struct sx150x_device_data sx1506q_device_data = {
> +       .model = SX150X_456,
> +       .reg_pullup     = 0x05,
> +       .reg_pulldn     = 0x07,
> +       .reg_dir        = 0x03,
> +       .reg_data       = 0x01,
> +       .reg_irq_mask   = 0x09,
> +       .reg_irq_src    = 0x0f,
> +       .reg_sense      = 0x0d,
> +       .pri.x456 = {
> +               .reg_pld_mode   = 0x21,
> +               .reg_pld_table0 = 0x23,
> +               .reg_pld_table1 = 0x25,
> +               .reg_pld_table2 = 0x27,
> +               .reg_pld_table3 = 0x29,
> +               .reg_pld_table4 = 0x2b,
> +               .reg_advance    = 0xad,
> +       },
> +       .ngpios = 16,
> +       .pins = sx150x_16_pins,
> +       .npins = 16, /* oscio not available */
>  };
>
> -static const struct of_device_id sx150x_of_match[] = {
> -       { .compatible = "semtech,sx1508q" },
> -       { .compatible = "semtech,sx1509q" },
> -       { .compatible = "semtech,sx1506q" },
> -       { .compatible = "semtech,sx1502q" },
> -       {},
> +static const struct sx150x_device_data sx1502q_device_data = {
> +       .model = SX150X_123,
> +       .reg_pullup     = 0x02,
> +       .reg_pulldn     = 0x03,
> +       .reg_dir        = 0x01,
> +       .reg_data       = 0x00,
> +       .reg_irq_mask   = 0x05,
> +       .reg_irq_src    = 0x08,
> +       .reg_sense      = 0x07,
> +       .pri.x123 = {
> +               .reg_pld_mode   = 0x10,
> +               .reg_pld_table0 = 0x11,
> +               .reg_pld_table1 = 0x12,
> +               .reg_pld_table2 = 0x13,
> +               .reg_pld_table3 = 0x14,
> +               .reg_pld_table4 = 0x15,
> +               .reg_advance    = 0xad,
> +       },
> +       .ngpios = 8,
> +       .pins = sx150x_8_pins,
> +       .npins = 8, /* oscio not available */
>  };
>
>  static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
> @@ -269,11 +253,6 @@ static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
>         return err;
>  }
>
> -static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
> -{
> -       return (chip->dev_cfg->ngpios == offset);
> -}
> -
>  /*
>   * These utility functions solve the common problem of locating and setting
>   * configuration bits.  Configuration bits are grouped into registers
> @@ -294,7 +273,7 @@ static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
>   * register and mask the correct bits.
>   */
>  static inline void sx150x_find_cfg(u8 offset, u8 width,
> -                               u8 *reg, u8 *mask, u8 *shift)
> +                                  u8 *reg, u8 *mask, u8 *shift)
>  {
>         *reg   -= offset * width / 8;
>         *mask   = (1 << width) - 1;
> @@ -302,185 +281,241 @@ static inline void sx150x_find_cfg(u8 offset, u8 width,
>         *mask <<= *shift;
>  }
>
> -static s32 sx150x_write_cfg(struct sx150x_chip *chip,
> -                       u8 offset, u8 width, u8 reg, u8 val)
> +static int sx150x_write_cfg(struct i2c_client *client,
> +                           u8 offset, u8 width, u8 reg, u8 val)
>  {
>         u8  mask;
>         u8  data;
>         u8  shift;
> -       s32 err;
> +       int err;
>
>         sx150x_find_cfg(offset, width, &reg, &mask, &shift);
> -       err = sx150x_i2c_read(chip->client, reg, &data);
> +       err = sx150x_i2c_read(client, reg, &data);
>         if (err < 0)
>                 return err;
>
>         data &= ~mask;
>         data |= (val << shift) & mask;
> -       return sx150x_i2c_write(chip->client, reg, data);
> +       return sx150x_i2c_write(client, reg, data);
>  }
>
> -static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
> +static int sx150x_read_cfg(struct i2c_client *client,
> +                          u8 offset, u8 width, u8 reg)
>  {
> -       u8  reg = chip->dev_cfg->reg_data;
>         u8  mask;
>         u8  data;
>         u8  shift;
> -       s32 err;
> +       int err;
>
> -       sx150x_find_cfg(offset, 1, &reg, &mask, &shift);
> -       err = sx150x_i2c_read(chip->client, reg, &data);
> -       if (err >= 0)
> -               err = (data & mask) != 0 ? 1 : 0;
> +       sx150x_find_cfg(offset, width, &reg, &mask, &shift);
> +       err = sx150x_i2c_read(client, reg, &data);
> +       if (err < 0)
> +               return err;
>
> -       return err;
> +       return (data & mask);
>  }
>
> -static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
> +static int sx150x_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
>  {
> -       sx150x_i2c_write(chip->client,
> -                       chip->dev_cfg->pri.x789.reg_clock,
> -                       (val ? 0x1f : 0x10));
> +       return 0;
>  }
>
> -static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
> +static const char *sx150x_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
> +                                               unsigned int group)
>  {
> -       sx150x_write_cfg(chip,
> -                       offset,
> -                       1,
> -                       chip->dev_cfg->reg_data,
> -                       (val ? 1 : 0));
> +       return NULL;
>  }
>
> -static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
> +static int sx150x_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
> +                                       unsigned int group,
> +                                       const unsigned int **pins,
> +                                       unsigned int *num_pins)
>  {
> -       return sx150x_write_cfg(chip,
> -                               offset,
> -                               1,
> -                               chip->dev_cfg->reg_dir,
> -                               1);
> +       return -ENOTSUPP;
>  }
>
> -static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
> +static const struct pinctrl_ops sx150x_pinctrl_ops = {
> +       .get_groups_count = sx150x_pinctrl_get_groups_count,
> +       .get_group_name = sx150x_pinctrl_get_group_name,
> +       .get_group_pins = sx150x_pinctrl_get_group_pins,
> +#ifdef CONFIG_OF
> +       .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
> +       .dt_free_map = pinctrl_utils_free_map,
> +#endif
> +};
> +
> +static bool sx150x_pin_is_oscio(struct sx150x_pinctrl *pctl, unsigned int pin)
>  {
> -       int err;
> +       if (pin >= pctl->data->npins)
> +               return false;
>
> -       err = sx150x_write_cfg(chip,
> -                       offset,
> -                       1,
> -                       chip->dev_cfg->reg_data,
> -                       (val ? 1 : 0));
> -       if (err >= 0)
> -               err = sx150x_write_cfg(chip,
> -                               offset,
> -                               1,
> -                               chip->dev_cfg->reg_dir,
> -                               0);
> -       return err;
> +       /* OSCIO pin is only present in 789 devices */
> +       if (pctl->data->model != SX150X_789)
> +               return false;
> +
> +       return !strcmp(pctl->data->pins[pin].name, "oscio");
>  }
>
> -static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
> +static int sx150x_gpio_get_direction(struct gpio_chip *chip,
> +                                     unsigned int offset)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(gc);
> -       int status = -EINVAL;
> +       struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +       int status;
>
> -       if (!offset_is_oscio(chip, offset)) {
> -               mutex_lock(&chip->lock);
> -               status = sx150x_get_io(chip, offset);
> -               mutex_unlock(&chip->lock);
> -       }
> +       if (sx150x_pin_is_oscio(pctl, offset))
> +               return false;
> +
> +       status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_dir);
> +       if (status >= 0)
> +               status = !!status;
>
> -       return (status < 0) ? status : !!status;
> +       return status;
>  }
>
> -static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
> +static int sx150x_gpio_get(struct gpio_chip *chip, unsigned int offset)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(gc);
> +       struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +       int status;
>
> -       mutex_lock(&chip->lock);
> -       if (offset_is_oscio(chip, offset))
> -               sx150x_set_oscio(chip, val);
> -       else
> -               sx150x_set_io(chip, offset, val);
> -       mutex_unlock(&chip->lock);
> +       if (sx150x_pin_is_oscio(pctl, offset))
> +               return -EINVAL;
> +
> +       status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_data);
> +       if (status >= 0)
> +               status = !!status;
> +
> +       return status;
>  }
>
> -static int sx150x_gpio_set_single_ended(struct gpio_chip *gc,
> -                                       unsigned offset,
> -                                        enum single_ended_mode mode)
> +static int sx150x_gpio_set_single_ended(struct gpio_chip *chip,
> +                                       unsigned int offset,
> +                                       enum single_ended_mode mode)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(gc);
> -
> -       /* On the SX160X 789 we can set open drain */
> -       if (chip->dev_cfg->model != SX150X_789)
> +       struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +       int ret;
> +
> +       switch (mode) {
> +       case LINE_MODE_PUSH_PULL:
> +               if (pctl->data->model != SX150X_789 ||
> +                   sx150x_pin_is_oscio(pctl, offset))
> +                       return 0;
> +
> +               mutex_lock(&pctl->lock);
> +               ret = sx150x_write_cfg(pctl->client, offset, 1,
> +                                      pctl->data->pri.x789.reg_drain,
> +                                      0);
> +               mutex_unlock(&pctl->lock);
> +               if (ret < 0)
> +                       return ret;
> +               break;
> +
> +       case LINE_MODE_OPEN_DRAIN:
> +               if (pctl->data->model != SX150X_789 ||
> +                   sx150x_pin_is_oscio(pctl, offset))
> +                       return -ENOTSUPP;
> +
> +               mutex_lock(&pctl->lock);
> +               ret = sx150x_write_cfg(pctl->client, offset, 1,
> +                                      pctl->data->pri.x789.reg_drain,
> +                                      1);
> +               mutex_unlock(&pctl->lock);
> +               if (ret < 0)
> +                       return ret;
> +               break;
> +
> +       default:
>                 return -ENOTSUPP;
> +       }
>
> -       if (mode == LINE_MODE_PUSH_PULL)
> -               return sx150x_write_cfg(chip,
> -                                       offset,
> -                                       1,
> -                                       chip->dev_cfg->pri.x789.reg_drain,
> -                                       0);
> -
> -       if (mode == LINE_MODE_OPEN_DRAIN)
> -               return sx150x_write_cfg(chip,
> -                                       offset,
> -                                       1,
> -                                       chip->dev_cfg->pri.x789.reg_drain,
> -                                       1);
> -       return -ENOTSUPP;
> +       return 0;
>  }
>
> -static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
> +static void sx150x_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +                              int value)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(gc);
> -       int status = -EINVAL;
> +       struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +
> +       if (sx150x_pin_is_oscio(pctl, offset)) {
>
> -       if (!offset_is_oscio(chip, offset)) {
> -               mutex_lock(&chip->lock);
> -               status = sx150x_io_input(chip, offset);
> -               mutex_unlock(&chip->lock);
> +               mutex_lock(&pctl->lock);
> +               sx150x_i2c_write(pctl->client,
> +                                      pctl->data->pri.x789.reg_clock,
> +                                      (value ? 0x1f : 0x10));
> +               mutex_unlock(&pctl->lock);
> +       } else {
> +               mutex_lock(&pctl->lock);
> +               sx150x_write_cfg(pctl->client, offset, 1,
> +                                      pctl->data->reg_data,
> +                                      (value ? 1 : 0));
> +               mutex_unlock(&pctl->lock);
>         }
> -       return status;
>  }
>
> -static int sx150x_gpio_direction_output(struct gpio_chip *gc,
> -                                       unsigned offset,
> -                                       int val)
> +static int sx150x_gpio_direction_input(struct gpio_chip *chip,
> +                                     unsigned int offset)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(gc);
> -       int status = 0;
> +       struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +       int ret;
>
> -       if (!offset_is_oscio(chip, offset)) {
> -               mutex_lock(&chip->lock);
> -               status = sx150x_io_output(chip, offset, val);
> -               mutex_unlock(&chip->lock);
> +       if (sx150x_pin_is_oscio(pctl, offset))
> +               return -EINVAL;
> +
> +       mutex_lock(&pctl->lock);
> +       ret = sx150x_write_cfg(pctl->client, offset, 1,
> +                               pctl->data->reg_dir, 1);
> +       mutex_unlock(&pctl->lock);
> +
> +       return ret;
> +}
> +
> +static int sx150x_gpio_direction_output(struct gpio_chip *chip,
> +                                      unsigned int offset, int value)
> +{
> +       struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +       int status;
> +
> +       if (sx150x_pin_is_oscio(pctl, offset)) {
> +               sx150x_gpio_set(chip, offset, value);
> +               return 0;
>         }
> +
> +       mutex_lock(&pctl->lock);
> +       status = sx150x_write_cfg(pctl->client, offset, 1,
> +                                 pctl->data->reg_data,
> +                                 (value ? 1 : 0));
> +       if (status >= 0)
> +               status = sx150x_write_cfg(pctl->client, offset, 1,
> +                                         pctl->data->reg_dir, 0);
> +       mutex_unlock(&pctl->lock);
> +
>         return status;
>  }
>
>  static void sx150x_irq_mask(struct irq_data *d)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -       unsigned n = d->hwirq;
> +       struct sx150x_pinctrl *pctl =
> +                       gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +       unsigned int n = d->hwirq;
>
> -       chip->irq_masked |= (1 << n);
> -       chip->irq_update = n;
> +       pctl->irq.masked |= (1 << n);
> +       pctl->irq.update = n;
>  }
>
>  static void sx150x_irq_unmask(struct irq_data *d)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -       unsigned n = d->hwirq;
> +       struct sx150x_pinctrl *pctl =
> +                       gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +       unsigned int n = d->hwirq;
>
> -       chip->irq_masked &= ~(1 << n);
> -       chip->irq_update = n;
> +       pctl->irq.masked &= ~(1 << n);
> +       pctl->irq.update = n;
>  }
>
>  static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -       unsigned n, val = 0;
> +       struct sx150x_pinctrl *pctl =
> +                       gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +       unsigned int n, val = 0;
>
>         if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
>                 return -EINVAL;
> @@ -492,37 +527,40 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
>         if (flow_type & IRQ_TYPE_EDGE_FALLING)
>                 val |= 0x2;
>
> -       chip->irq_sense &= ~(3UL << (n * 2));
> -       chip->irq_sense |= val << (n * 2);
> -       chip->irq_update = n;
> +       pctl->irq.sense &= ~(3UL << (n * 2));
> +       pctl->irq.sense |= val << (n * 2);
> +       pctl->irq.update = n;
>         return 0;
>  }
>
>  static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
>  {
> -       struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
> -       unsigned nhandled = 0;
> -       unsigned sub_irq;
> -       unsigned n;
> +       struct sx150x_pinctrl *pctl = (struct sx150x_pinctrl *)dev_id;
> +       unsigned int nhandled = 0;
> +       unsigned int sub_irq;
> +       unsigned int n;
>         s32 err;
>         u8 val;
>         int i;
>
> -       for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
> -               err = sx150x_i2c_read(chip->client,
> -                                     chip->dev_cfg->reg_irq_src - i,
> +       for (i = (pctl->data->ngpios / 8) - 1; i >= 0; --i) {
> +               err = sx150x_i2c_read(pctl->client,
> +                                     pctl->data->reg_irq_src - i,
>                                       &val);
>                 if (err < 0)
>                         continue;
>
> -               sx150x_i2c_write(chip->client,
> -                               chip->dev_cfg->reg_irq_src - i,
> -                               val);
> +               err = sx150x_i2c_write(pctl->client,
> +                                      pctl->data->reg_irq_src - i,
> +                                      val);
> +               if (err < 0)
> +                       continue;
> +
>                 for (n = 0; n < 8; ++n) {
>                         if (val & (1 << n)) {
>                                 sub_irq = irq_find_mapping(
> -                                       chip->gpio_chip.irqdomain,
> -                                       (i * 8) + n);
> +                                               pctl->gpio.irqdomain,
> +                                               (i * 8) + n);
>                                 handle_nested_irq(sub_irq);
>                                 ++nhandled;
>                         }
> @@ -534,251 +572,483 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
>
>  static void sx150x_irq_bus_lock(struct irq_data *d)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +       struct sx150x_pinctrl *pctl =
> +                       gpiochip_get_data(irq_data_get_irq_chip_data(d));
>
> -       mutex_lock(&chip->lock);
> +       mutex_lock(&pctl->lock);
>  }
>
>  static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
>  {
> -       struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> -       unsigned n;
> +       struct sx150x_pinctrl *pctl =
> +                       gpiochip_get_data(irq_data_get_irq_chip_data(d));
> +       unsigned int n;
>
> -       if (chip->irq_update == NO_UPDATE_PENDING)
> +       if (pctl->irq.update < 0)
>                 goto out;
>
> -       n = chip->irq_update;
> -       chip->irq_update = NO_UPDATE_PENDING;
> +       n = pctl->irq.update;
> +       pctl->irq.update = -1;
>
>         /* Avoid updates if nothing changed */
> -       if (chip->dev_sense == chip->irq_sense &&
> -           chip->dev_masked == chip->irq_masked)
> +       if (pctl->irq.dev_sense == pctl->irq.sense &&
> +           pctl->irq.dev_masked == pctl->irq.masked)
>                 goto out;
>
> -       chip->dev_sense = chip->irq_sense;
> -       chip->dev_masked = chip->irq_masked;
> +       pctl->irq.dev_sense = pctl->irq.sense;
> +       pctl->irq.dev_masked = pctl->irq.masked;
>
> -       if (chip->irq_masked & (1 << n)) {
> -               sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
> -               sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
> +       if (pctl->irq.masked & (1 << n)) {
> +               sx150x_write_cfg(pctl->client, n, 1,
> +                                pctl->data->reg_irq_mask, 1);
> +               sx150x_write_cfg(pctl->client, n, 2,
> +                                pctl->data->reg_sense, 0);
>         } else {
> -               sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
> -               sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
> -                                chip->irq_sense >> (n * 2));
> +               sx150x_write_cfg(pctl->client, n, 1,
> +                                pctl->data->reg_irq_mask, 0);
> +               sx150x_write_cfg(pctl->client, n, 2,
> +                                pctl->data->reg_sense,
> +                                pctl->irq.sense >> (n * 2));
>         }
>  out:
> -       mutex_unlock(&chip->lock);
> +       mutex_unlock(&pctl->lock);
>  }
>
> -static void sx150x_init_chip(struct sx150x_chip *chip,
> -                       struct i2c_client *client,
> -                       kernel_ulong_t driver_data,
> -                       struct sx150x_platform_data *pdata)
> +static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
> +                             unsigned long *config)
>  {
> -       mutex_init(&chip->lock);
> -
> -       chip->client                     = client;
> -       chip->dev_cfg                    = &sx150x_devices[driver_data];
> -       chip->gpio_chip.parent              = &client->dev;
> -       chip->gpio_chip.label            = client->name;
> -       chip->gpio_chip.direction_input  = sx150x_gpio_direction_input;
> -       chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
> -       chip->gpio_chip.get              = sx150x_gpio_get;
> -       chip->gpio_chip.set              = sx150x_gpio_set;
> -       chip->gpio_chip.set_single_ended = sx150x_gpio_set_single_ended;
> -       chip->gpio_chip.base             = pdata->gpio_base;
> -       chip->gpio_chip.can_sleep        = true;
> -       chip->gpio_chip.ngpio            = chip->dev_cfg->ngpios;
> -#ifdef CONFIG_OF_GPIO
> -       chip->gpio_chip.of_node          = client->dev.of_node;
> -       chip->gpio_chip.of_gpio_n_cells  = 2;
> -#endif
> -       if (pdata->oscio_is_gpo)
> -               ++chip->gpio_chip.ngpio;
> -
> -       chip->irq_chip.name                = client->name;
> -       chip->irq_chip.irq_mask            = sx150x_irq_mask;
> -       chip->irq_chip.irq_unmask          = sx150x_irq_unmask;
> -       chip->irq_chip.irq_set_type        = sx150x_irq_set_type;
> -       chip->irq_chip.irq_bus_lock        = sx150x_irq_bus_lock;
> -       chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
> -       chip->irq_summary                  = -1;
> -       chip->irq_base                     = -1;
> -       chip->irq_masked                   = ~0;
> -       chip->irq_sense                    = 0;
> -       chip->dev_masked                   = ~0;
> -       chip->dev_sense                    = 0;
> -       chip->irq_update                   = NO_UPDATE_PENDING;
> +       struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +       unsigned int param = pinconf_to_config_param(*config);
> +       int ret;
> +       u32 arg;
> +
> +       if (sx150x_pin_is_oscio(pctl, pin)) {
> +               u8 data;
> +
> +               switch (param) {
> +               case PIN_CONFIG_DRIVE_PUSH_PULL:
> +               case PIN_CONFIG_OUTPUT:
> +                       mutex_lock(&pctl->lock);
> +                       ret = sx150x_i2c_read(pctl->client,
> +                                       pctl->data->pri.x789.reg_clock,
> +                                       &data);
> +                       mutex_unlock(&pctl->lock);
> +
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       if (param == PIN_CONFIG_DRIVE_PUSH_PULL)
> +                               arg = (data & 0x1f) ? 1 : 0;
> +                       else {
> +                               if ((data & 0x1f) == 0x1f)
> +                                       arg = 1;
> +                               else if ((data & 0x1f) == 0x10)
> +                                       arg = 0;
> +                               else
> +                                       return -EINVAL;
> +                       }
> +
> +                       break;
> +               default:
> +                       return -ENOTSUPP;
> +               }
> +
> +               goto out;
> +       }
> +
> +       switch (param) {
> +       case PIN_CONFIG_BIAS_PULL_DOWN:
> +               mutex_lock(&pctl->lock);
> +               ret = sx150x_read_cfg(pctl->client, pin, 1,
> +                                     pctl->data->reg_pulldn);
> +               mutex_unlock(&pctl->lock);
> +
> +               if (ret < 0)
> +                       return ret;
> +
> +               if (!ret)
> +                       return -EINVAL;
> +
> +               arg = 1;
> +               break;
> +
> +       case PIN_CONFIG_BIAS_PULL_UP:
> +               mutex_lock(&pctl->lock);
> +               ret = sx150x_read_cfg(pctl->client, pin, 1,
> +                                     pctl->data->reg_pullup);
> +               mutex_unlock(&pctl->lock);
> +
> +               if (ret < 0)
> +                       return ret;
> +
> +               if (!ret)
> +                       return -EINVAL;
> +
> +               arg = 1;
> +               break;
> +
> +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +               if (pctl->data->model != SX150X_789)
> +                       return -ENOTSUPP;
> +
> +               mutex_lock(&pctl->lock);
> +               ret = sx150x_read_cfg(pctl->client, pin, 1,
> +                                     pctl->data->pri.x789.reg_drain);
> +               mutex_unlock(&pctl->lock);
> +
> +               if (ret < 0)
> +                       return ret;
> +
> +               if (!ret)
> +                       return -EINVAL;
> +
> +               arg = 1;
> +               break;
> +
> +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> +               if (pctl->data->model != SX150X_789)
> +                       arg = true;
> +               else {
> +                       mutex_lock(&pctl->lock);
> +                       ret = sx150x_read_cfg(pctl->client, pin, 1,
> +                                             pctl->data->pri.x789.reg_drain);
> +                       mutex_unlock(&pctl->lock);
> +
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       if (ret)
> +                               return -EINVAL;
> +
> +                       arg = 1;
> +               }
> +               break;
> +
> +       case PIN_CONFIG_OUTPUT:
> +               ret = sx150x_gpio_get_direction(&pctl->gpio, pin);
> +               if (ret < 0)
> +                       return ret;
> +
> +               if (ret)
> +                       return -EINVAL;
> +
> +               ret = sx150x_gpio_get(&pctl->gpio, pin);
> +               if (ret < 0)
> +                       return ret;
> +
> +               arg = ret;
> +               break;
> +
> +       default:
> +               return -ENOTSUPP;
> +       }
> +
> +out:
> +       *config = pinconf_to_config_packed(param, arg);
> +
> +       return 0;
> +}
> +
> +static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
> +                             unsigned long *configs, unsigned int num_configs)
> +{
> +       struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
> +       enum pin_config_param param;
> +       u32 arg;
> +       int i;
> +       int ret;
> +
> +       for (i = 0; i < num_configs; i++) {
> +               param = pinconf_to_config_param(configs[i]);
> +               arg = pinconf_to_config_argument(configs[i]);
> +
> +               if (sx150x_pin_is_oscio(pctl, pin)) {
> +                       if (param == PIN_CONFIG_OUTPUT) {
> +                               ret = sx150x_gpio_direction_output(&pctl->gpio,
> +                                                                  pin, arg);
> +                               if (ret < 0)
> +                                       return ret;
> +
> +                               continue;
> +                       } else
> +                               return -ENOTSUPP;
> +               }
> +
> +               switch (param) {
> +               case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
> +               case PIN_CONFIG_BIAS_DISABLE:
> +                       mutex_lock(&pctl->lock);
> +                       ret = sx150x_write_cfg(pctl->client, pin, 1,
> +                                              pctl->data->reg_pulldn, 0);
> +                       mutex_unlock(&pctl->lock);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       mutex_lock(&pctl->lock);
> +                       ret = sx150x_write_cfg(pctl->client, pin, 1,
> +                                              pctl->data->reg_pullup, 0);
> +                       mutex_unlock(&pctl->lock);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       break;
> +
> +               case PIN_CONFIG_BIAS_PULL_UP:
> +                       mutex_lock(&pctl->lock);
> +                       ret = sx150x_write_cfg(pctl->client, pin, 1,
> +                                              pctl->data->reg_pullup,
> +                                              1);
> +                       mutex_unlock(&pctl->lock);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       break;
> +
> +               case PIN_CONFIG_BIAS_PULL_DOWN:
> +                       mutex_lock(&pctl->lock);
> +                       ret = sx150x_write_cfg(pctl->client, pin, 1,
> +                                              pctl->data->reg_pulldn,
> +                                              1);
> +                       mutex_unlock(&pctl->lock);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       break;
> +
> +               case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +                       ret = sx150x_gpio_set_single_ended(&pctl->gpio,
> +                                               pin, LINE_MODE_OPEN_DRAIN);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       break;
> +
> +               case PIN_CONFIG_DRIVE_PUSH_PULL:
> +                       ret = sx150x_gpio_set_single_ended(&pctl->gpio,
> +                                               pin, LINE_MODE_PUSH_PULL);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       break;
> +
> +               case PIN_CONFIG_OUTPUT:
> +                       ret = sx150x_gpio_direction_output(&pctl->gpio,
> +                                                          pin, arg);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       break;
> +
> +               default:
> +                       return -ENOTSUPP;
> +               }
> +       } /* for each config */
> +
> +       return 0;
>  }
>
> -static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
> +static const struct pinconf_ops sx150x_pinconf_ops = {
> +       .pin_config_get = sx150x_pinconf_get,
> +       .pin_config_set = sx150x_pinconf_set,
> +       .is_generic = true,
> +};
> +
> +static const struct i2c_device_id sx150x_id[] = {
> +       {"sx1508q", (kernel_ulong_t) &sx1508q_device_data },
> +       {"sx1509q", (kernel_ulong_t) &sx1509q_device_data },
> +       {"sx1506q", (kernel_ulong_t) &sx1506q_device_data },
> +       {"sx1502q", (kernel_ulong_t) &sx1502q_device_data },
> +       {}
> +};
> +
> +static const struct of_device_id sx150x_of_match[] = {
> +       { .compatible = "semtech,sx1508q" },
> +       { .compatible = "semtech,sx1509q" },
> +       { .compatible = "semtech,sx1506q" },
> +       { .compatible = "semtech,sx1502q" },
> +       {},
> +};
> +
> +static int sx150x_init_io(struct sx150x_pinctrl *pctl, u8 base, u16 cfg)
>  {
>         int err = 0;
> -       unsigned n;
> +       unsigned int n;
>
> -       for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
> -               err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
> +       for (n = 0; err >= 0 && n < (pctl->data->ngpios / 8); ++n)
> +               err = sx150x_i2c_write(pctl->client, base - n, cfg >> (n * 8));
>         return err;
>  }
>
> -static int sx150x_reset(struct sx150x_chip *chip)
> +static int sx150x_reset(struct sx150x_pinctrl *pctl)
>  {
>         int err;
>
> -       err = i2c_smbus_write_byte_data(chip->client,
> -                                       chip->dev_cfg->pri.x789.reg_reset,
> +       err = i2c_smbus_write_byte_data(pctl->client,
> +                                       pctl->data->pri.x789.reg_reset,
>                                         0x12);
>         if (err < 0)
>                 return err;
>
> -       err = i2c_smbus_write_byte_data(chip->client,
> -                                       chip->dev_cfg->pri.x789.reg_reset,
> +       err = i2c_smbus_write_byte_data(pctl->client,
> +                                       pctl->data->pri.x789.reg_reset,
>                                         0x34);
>         return err;
>  }
>
> -static int sx150x_init_hw(struct sx150x_chip *chip,
> -                       struct sx150x_platform_data *pdata)
> +static int sx150x_init_hw(struct sx150x_pinctrl *pctl)
>  {
> -       int err = 0;
> +       int err;
>
> -       if (pdata->reset_during_probe) {
> -               err = sx150x_reset(chip);
> +       if (pctl->data->model == SX150X_789 &&
> +           of_property_read_bool(pctl->dev->of_node, "semtech,probe-reset")) {
> +               err = sx150x_reset(pctl);
>                 if (err < 0)
>                         return err;
>         }
>
> -       if (chip->dev_cfg->model == SX150X_789)
> -               err = sx150x_i2c_write(chip->client,
> -                               chip->dev_cfg->pri.x789.reg_misc,
> +       if (pctl->data->model == SX150X_789)
> +               err = sx150x_i2c_write(pctl->client,
> +                               pctl->data->pri.x789.reg_misc,
>                                 0x01);
> -       else if (chip->dev_cfg->model == SX150X_456)
> -               err = sx150x_i2c_write(chip->client,
> -                               chip->dev_cfg->pri.x456.reg_advance,
> +       else if (pctl->data->model == SX150X_456)
> +               err = sx150x_i2c_write(pctl->client,
> +                               pctl->data->pri.x456.reg_advance,
>                                 0x04);
>         else
> -               err = sx150x_i2c_write(chip->client,
> -                               chip->dev_cfg->pri.x123.reg_advance,
> +               err = sx150x_i2c_write(pctl->client,
> +                               pctl->data->pri.x123.reg_advance,
>                                 0x00);
>         if (err < 0)
>                 return err;
>
> -       err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
> -                       pdata->io_pullup_ena);
> -       if (err < 0)
> -               return err;
> -
> -       err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
> -                       pdata->io_pulldn_ena);
> -       if (err < 0)
> -               return err;
> -
> -       if (chip->dev_cfg->model == SX150X_789) {
> -               err = sx150x_init_io(chip,
> -                               chip->dev_cfg->pri.x789.reg_polarity,
> -                               pdata->io_polarity);
> +       /* Set all pins to work in normal mode */
> +       if (pctl->data->model == SX150X_789) {
> +               err = sx150x_init_io(pctl,
> +                               pctl->data->pri.x789.reg_polarity,
> +                               0);
>                 if (err < 0)
>                         return err;
> -       } else if (chip->dev_cfg->model == SX150X_456) {
> +       } else if (pctl->data->model == SX150X_456) {
>                 /* Set all pins to work in normal mode */
> -               err = sx150x_init_io(chip,
> -                               chip->dev_cfg->pri.x456.reg_pld_mode,
> +               err = sx150x_init_io(pctl,
> +                               pctl->data->pri.x456.reg_pld_mode,
>                                 0);
>                 if (err < 0)
>                         return err;
>         } else {
>                 /* Set all pins to work in normal mode */
> -               err = sx150x_init_io(chip,
> -                               chip->dev_cfg->pri.x123.reg_pld_mode,
> +               err = sx150x_init_io(pctl,
> +                               pctl->data->pri.x123.reg_pld_mode,
>                                 0);
>                 if (err < 0)
>                         return err;
>         }
>
> -
> -       if (pdata->oscio_is_gpo)
> -               sx150x_set_oscio(chip, 0);
> -
> -       return err;
> -}
> -
> -static int sx150x_install_irq_chip(struct sx150x_chip *chip,
> -                               int irq_summary,
> -                               int irq_base)
> -{
> -       int err;
> -
> -       chip->irq_summary = irq_summary;
> -       chip->irq_base    = irq_base;
> -
> -       /* Add gpio chip to irq subsystem */
> -       err = gpiochip_irqchip_add(&chip->gpio_chip,
> -               &chip->irq_chip, chip->irq_base,
> -               handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
> -       if (err) {
> -               dev_err(&chip->client->dev,
> -                       "could not connect irqchip to gpiochip\n");
> -               return  err;
> -       }
> -
> -       err = devm_request_threaded_irq(&chip->client->dev,
> -                       irq_summary, NULL, sx150x_irq_thread_fn,
> -                       IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING,
> -                       chip->irq_chip.name, chip);
> -       if (err < 0) {
> -               chip->irq_summary = -1;
> -               chip->irq_base    = -1;
> -       }
> -
> -       return err;
> +       return 0;
>  }
>
>  static int sx150x_probe(struct i2c_client *client,
> -                               const struct i2c_device_id *id)
> +                       const struct i2c_device_id *id)
>  {
>         static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
>                                      I2C_FUNC_SMBUS_WRITE_WORD_DATA;
> -       struct sx150x_platform_data *pdata;
> -       struct sx150x_chip *chip;
> -       int rc;
> +       struct device *dev = &client->dev;
> +       struct sx150x_pinctrl *pctl;
> +       int ret;
>
> -       pdata = dev_get_platdata(&client->dev);
> -       if (!pdata)
> +       if (!id->driver_data)
>                 return -EINVAL;
>
>         if (!i2c_check_functionality(client->adapter, i2c_funcs))
>                 return -ENOSYS;
>
> -       chip = devm_kzalloc(&client->dev,
> -               sizeof(struct sx150x_chip), GFP_KERNEL);
> -       if (!chip)
> +       pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
> +       if (!pctl)
>                 return -ENOMEM;
>
> -       sx150x_init_chip(chip, client, id->driver_data, pdata);
> -       rc = sx150x_init_hw(chip, pdata);
> -       if (rc < 0)
> -               return rc;
> -
> -       rc = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
> -       if (rc)
> -               return rc;
> -
> -       if (pdata->irq_summary >= 0) {
> -               rc = sx150x_install_irq_chip(chip,
> -                                       pdata->irq_summary,
> -                                       pdata->irq_base);
> -               if (rc < 0)
> -                       return rc;
> +       pctl->dev = dev;
> +       pctl->client = client;
> +       pctl->data = (void *)id->driver_data;
> +
> +       mutex_init(&pctl->lock);
> +
> +       ret = sx150x_init_hw(pctl);
> +       if (ret)
> +               return ret;
> +
> +       /* Register GPIO controller */
> +       pctl->gpio.label = devm_kstrdup(dev, client->name, GFP_KERNEL);
> +       pctl->gpio.base = -1;
> +       pctl->gpio.ngpio = pctl->data->npins;
> +       pctl->gpio.get_direction = sx150x_gpio_get_direction;
> +       pctl->gpio.direction_input = sx150x_gpio_direction_input;
> +       pctl->gpio.direction_output = sx150x_gpio_direction_output;
> +       pctl->gpio.get = sx150x_gpio_get;
> +       pctl->gpio.set = sx150x_gpio_set;
> +       pctl->gpio.set_single_ended = sx150x_gpio_set_single_ended;
> +       pctl->gpio.parent = dev;
> +#ifdef CONFIG_OF_GPIO
> +       pctl->gpio.of_node = dev->of_node;
> +#endif
> +       pctl->gpio.can_sleep = true;
> +
> +       ret = devm_gpiochip_add_data(dev, &pctl->gpio, pctl);
> +       if (ret)
> +               return ret;
> +
> +       /* Add Interrupt support if an irq is specified */
> +       if (client->irq > 0) {
> +               pctl->irq_chip.name = devm_kstrdup(dev, client->name,
> +                                                  GFP_KERNEL);
> +               pctl->irq_chip.irq_mask = sx150x_irq_mask;
> +               pctl->irq_chip.irq_unmask = sx150x_irq_unmask;
> +               pctl->irq_chip.irq_set_type = sx150x_irq_set_type;
> +               pctl->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
> +               pctl->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
> +
> +               pctl->irq.masked = ~0;
> +               pctl->irq.sense = 0;
> +               pctl->irq.dev_masked = ~0;
> +               pctl->irq.dev_sense = 0;
> +               pctl->irq.update = -1;
> +
> +               ret = gpiochip_irqchip_add(&pctl->gpio,
> +                                          &pctl->irq_chip, 0,
> +                                          handle_edge_irq, IRQ_TYPE_EDGE_BOTH);

Adding irqchip with IRQ_TYPE_EDGE_BOTH triggers a WARN in
drivers/gpio/gpiolib.c:1671, on a custom Vybrid board that I have with
the chip, so maybe it should be replaced with IRQ_TYPE_NONE? That's
what I did for my testing and with that change

Tested-by: Andrey Smirnov <andrew.smirnov@gmail.com>

Thanks,
Andrey Smirnov
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Oct. 23, 2016, 10:41 a.m. UTC | #3
On Fri, Oct 21, 2016 at 11:09 AM, Neil Armstrong
<narmstrong@baylibre.com> wrote:

> Since the I2C sx150x GPIO expander driver uses platform_data to manage
> the pins configurations, rewrite the driver as a pinctrl driver using
> pinconf to get/set pin configurations from DT or debugfs.

Thanks! Patch applied for an immutable branch that I'll pull into
both pinctrl and gpio. All acks and test tags added too.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Oct. 23, 2016, 10:47 a.m. UTC | #4
On Sun, Oct 23, 2016 at 4:50 AM, Andrey Smirnov
<andrew.smirnov@gmail.com> wrote:
> On Fri, Oct 21, 2016 at 2:09 AM, Neil Armstrong <narmstrong@baylibre.com> wrote:
>> Since the I2C sx150x GPIO expander driver uses platform_data to manage
>> the pins configurations, rewrite the driver as a pinctrl driver using
>> pinconf to get/set pin configurations from DT or debugfs.
>>
>> The pinctrl driver is functionnally equivalent as the gpio-only driver
>> and can use DT for pinconf. The platform_data confirmation is dropped.
>>
>> This patchset removed the gpio-only driver and selects the Pinctrl driver
>> config instead. This patchset also migrates the gpio dt-bindings to pinctrl
>> and add the pinctrl optional properties.
>>
>> The driver was tested with a SX1509 device on a BeagleBone black with
>> interrupt support and on an X86_64 machine over an I2C to USB converter.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
(...)

>> +               ret = gpiochip_irqchip_add(&pctl->gpio,
>> +                                          &pctl->irq_chip, 0,
>> +                                          handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
>
> Adding irqchip with IRQ_TYPE_EDGE_BOTH triggers a WARN in
> drivers/gpio/gpiolib.c:1671, on a custom Vybrid board that I have with
> the chip, so maybe it should be replaced with IRQ_TYPE_NONE? That's
> what I did for my testing and with that change

I fixed this up when applying. It is corect, it should always be IRQ_TYPE_NONE
unless it is a very old driver using boardfiles. The proper type is set up
when the driver using it requests the IRQ.

Some drivers also need to set the default handler to handle_bad_irq
and then also set that up in the irqchip .set_type() callback, especially
those hardwares that have an ACK register for edge IRQs so that a second
edge irq can come in when handling a first edge IRQ. Level IRQs don't
have this problem for obvious reasons, so we need to select between
handle_edge_irq() or handle_level_irq() on these hardwares.

Could you or Neil or both check if this applies to sx150x?

> Tested-by: Andrey Smirnov <andrew.smirnov@gmail.com>

Thanks a lot Andrey!

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrey Smirnov Oct. 24, 2016, 4:51 a.m. UTC | #5
On Sun, Oct 23, 2016 at 3:47 AM, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Sun, Oct 23, 2016 at 4:50 AM, Andrey Smirnov
> <andrew.smirnov@gmail.com> wrote:
>> On Fri, Oct 21, 2016 at 2:09 AM, Neil Armstrong <narmstrong@baylibre.com> wrote:
>>> Since the I2C sx150x GPIO expander driver uses platform_data to manage
>>> the pins configurations, rewrite the driver as a pinctrl driver using
>>> pinconf to get/set pin configurations from DT or debugfs.
>>>
>>> The pinctrl driver is functionnally equivalent as the gpio-only driver
>>> and can use DT for pinconf. The platform_data confirmation is dropped.
>>>
>>> This patchset removed the gpio-only driver and selects the Pinctrl driver
>>> config instead. This patchset also migrates the gpio dt-bindings to pinctrl
>>> and add the pinctrl optional properties.
>>>
>>> The driver was tested with a SX1509 device on a BeagleBone black with
>>> interrupt support and on an X86_64 machine over an I2C to USB converter.
>>>
>>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> (...)
>
>>> +               ret = gpiochip_irqchip_add(&pctl->gpio,
>>> +                                          &pctl->irq_chip, 0,
>>> +                                          handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
>>
>> Adding irqchip with IRQ_TYPE_EDGE_BOTH triggers a WARN in
>> drivers/gpio/gpiolib.c:1671, on a custom Vybrid board that I have with
>> the chip, so maybe it should be replaced with IRQ_TYPE_NONE? That's
>> what I did for my testing and with that change
>
> I fixed this up when applying. It is corect, it should always be IRQ_TYPE_NONE
> unless it is a very old driver using boardfiles. The proper type is set up
> when the driver using it requests the IRQ.
>
> Some drivers also need to set the default handler to handle_bad_irq
> and then also set that up in the irqchip .set_type() callback, especially
> those hardwares that have an ACK register for edge IRQs so that a second
> edge irq can come in when handling a first edge IRQ. Level IRQs don't
> have this problem for obvious reasons, so we need to select between
> handle_edge_irq() or handle_level_irq() on these hardwares.
>
> Could you or Neil or both check if this applies to sx150x?

These chips only support edge triggered interrupts, so I doesn't seem
to me that it does. However, trying to answer that question lead me to
read IRQ related code of the driver, some of which didn't make as much
sense as I'd like it to. It may be due to my ignorance of how some of
the irqchip internals work, but I'll mention it anyway just in case.

It seem strange to me that the driver uses "handle_edge_irq", given
how none of the individual interrupts seem to require any ACKing,
since it is all handled in sx150x_irq_thread_fn(), line 533. More so,
I had trouble finding who/where sets .irq_ack() callback, which AFAIU
is mandatory for handle_edge_irq().

Unfortunately sx150x HW setup that I have doesn't permit to do any
testing of IRQ functionality (the chip is mostly used as GPO), so I
wasn't able to experiment with any of the code in question and shed
any light on my confusion. Apologies if it is just a false alarm.

Thanks,
Andrey Smirnov
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Oct. 24, 2016, 2:39 p.m. UTC | #6
On Mon, Oct 24, 2016 at 6:51 AM, Andrey Smirnov
<andrew.smirnov@gmail.com> wrote:

> It seem strange to me that the driver uses "handle_edge_irq", given
> how none of the individual interrupts seem to require any ACKing,
> since it is all handled in sx150x_irq_thread_fn(), line 533. More so,
> I had trouble finding who/where sets .irq_ack() callback, which AFAIU
> is mandatory for handle_edge_irq().

Yes that looks strange.

Neil have you tested IRQs with this code?

If there is trouble, please follow up with a fix for the edge handler.
Maybe it should just be handle_simple_irq().

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Neil Armstrong Oct. 25, 2016, 6:57 a.m. UTC | #7
Hi Linus,

Le 24/10/2016 16:39, Linus Walleij a écrit :
> On Mon, Oct 24, 2016 at 6:51 AM, Andrey Smirnov
> <andrew.smirnov@gmail.com> wrote:
> 
>> It seem strange to me that the driver uses "handle_edge_irq", given
>> how none of the individual interrupts seem to require any ACKing,
>> since it is all handled in sx150x_irq_thread_fn(), line 533. More so,
>> I had trouble finding who/where sets .irq_ack() callback, which AFAIU
>> is mandatory for handle_edge_irq().
> 
> Yes that looks strange.
> 
> Neil have you tested IRQs with this code?

To be frank, I took the IRQ code from the GPIO driver verbatim, and only tested
a simple use case on beagle bone black.
The interrupt code is very complex and sincerely I was not able to understand
clearly how it worked.

The IRQ_TYPE_EDGE_BOTH did work for me since the BBB irq controller dealt with it.

I will dig in the datasheet and run more uses cases next week, but since the
code hasn't changed since the GPIO driver, so this is a nice to have !

> 
> If there is trouble, please follow up with a fix for the edge handler.
> Maybe it should just be handle_simple_irq().
> 
> Yours,
> Linus Walleij
> 

Andrey, can we talk over irc somehow ? I'm idling as narmstrong on freenode.

Neil
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
similarity index 40%
rename from Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
rename to Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
index c809acb..c293c8a 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
@@ -1,34 +1,54 @@ 
 SEMTECH SX150x GPIO expander bindings
 
+Please refer to pinctrl-bindings.txt, ../gpio/gpio.txt, and
+../interrupt-controller/interrupts.txt for generic information regarding
+pin controller, GPIO, and interrupt bindings.
 
 Required properties:
-
-- compatible: should be "semtech,sx1506q",
+- compatible: should be one of :
+			"semtech,sx1506q",
 			"semtech,sx1508q",
 			"semtech,sx1509q",
 			"semtech,sx1502q".
 
 - reg: The I2C slave address for this device.
 
-- interrupt-parent: phandle of the parent interrupt controller.
-
-- interrupts: Interrupt specifier for the controllers interrupt.
-
 - #gpio-cells: Should be 2. The first cell is the GPIO number and the
 		second cell is used to specify optional parameters:
 		bit 0: polarity (0: normal, 1: inverted)
 
 - gpio-controller: Marks the device as a GPIO controller.
 
+Optional properties :
+- interrupt-parent: phandle of the parent interrupt controller.
+
+- interrupts: Interrupt specifier for the controllers interrupt.
+
 - interrupt-controller: Marks the device as a interrupt controller.
 
+- semtech,probe-reset: Will trigger a reset of the GPIO expander on probe,
+		only for sx1508q and sx1509q
+
 The GPIO expander can optionally be used as an interrupt controller, in
-which case it uses the default two cell specifier as described in
-Documentation/devicetree/bindings/interrupt-controller/interrupts.txt.
+which case it uses the default two cell specifier.
+
+Required properties for pin configuration sub-nodes:
+ - pins: List of pins to which the configuration applies.
+
+Optional properties for pin configuration sub-nodes:
+----------------------------------------------------
+ - bias-disable: disable any pin bias, except the OSCIO pin
+ - bias-pull-up: pull up the pin, except the OSCIO pin
+ - bias-pull-down: pull down the pin, except the OSCIO pin
+ - bias-pull-pin-default: use pin-default pull state, except the OSCIO pin
+ - drive-push-pull: drive actively high and low
+ - drive-open-drain: drive with open drain only for sx1508q and sx1509q and except the OSCIO pin
+ - output-low: set the pin to output mode with low level
+ - output-high: set the pin to output mode with high level
 
 Example:
 
-	i2c_gpio_expander@20{
+	i2c0gpio-expander@20{
 		#gpio-cells = <2>;
 		#interrupt-cells = <2>;
 		compatible = "semtech,sx1506q";
@@ -38,4 +58,12 @@  Example:
 
 		gpio-controller;
 		interrupt-controller;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&gpio1_cfg_pins>;
+
+		gpio1_cfg_pins: gpio1-cfg {
+			pins = "gpio1";
+			bias-pull-up;
+		};
 	};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 26ee00f..db3f7aa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -781,16 +781,13 @@  config GPIO_PCF857X
 	  platform-neutral GPIO calls.
 
 config GPIO_SX150X
-	bool "Semtech SX150x I2C GPIO expander"
-	depends on I2C=y
-	select GPIOLIB_IRQCHIP
+	bool "Semtech SX150x I2C GPIO expander (deprecated)"
+	depends on PINCTRL && I2C=y
+	select PINCTRL_SX150X
 	default n
 	help
-	  Say yes here to provide support for Semtech SX150-series I2C
-	  GPIO expanders. Compatible models include:
-
-	  8 bits:  sx1508q
-	  16 bits: sx1509q
+	  Say yes here to provide support for Semtech SX150x-series I2C
+	  GPIO expanders. The GPIO driver was replaced by a Pinctrl version.
 
 config GPIO_TPIC2810
 	tristate "TPIC2810 8-Bit I2C GPO expander"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ab28a2d..76c7af6 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -102,7 +102,6 @@  obj-$(CONFIG_GPIO_SPEAR_SPICS)	+= gpio-spear-spics.o
 obj-$(CONFIG_GPIO_STA2X11)	+= gpio-sta2x11.o
 obj-$(CONFIG_GPIO_STMPE)	+= gpio-stmpe.o
 obj-$(CONFIG_GPIO_STP_XWAY)	+= gpio-stp-xway.o
-obj-$(CONFIG_GPIO_SX150X)	+= gpio-sx150x.o
 obj-$(CONFIG_GPIO_SYSCON)	+= gpio-syscon.o
 obj-$(CONFIG_GPIO_TB10X)	+= gpio-tb10x.o
 obj-$(CONFIG_GPIO_TC3589X)	+= gpio-tc3589x.o
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0e75d94..801fa8b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -164,6 +164,20 @@  config PINCTRL_SIRF
 	select GENERIC_PINCONF
 	select GPIOLIB_IRQCHIP
 
+config PINCTRL_SX150X
+	bool "Semtech SX150x I2C GPIO expander pinctrl driver"
+	depends on GPIOLIB && I2C=y
+	select PINMUX
+	select PINCONF
+	select GENERIC_PINCONF
+	select GPIOLIB_IRQCHIP
+	help
+	  Say yes here to provide support for Semtech SX150x-series I2C
+	  GPIO expanders as pinctrl module.
+	  Compatible models include:
+	  - 8 bits:  sx1508q, sx1502q
+	  - 16 bits: sx1509q, sx1506q
+
 config PINCTRL_PISTACHIO
 	def_bool y if MACH_PISTACHIO
 	depends on GPIOLIB
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 11bad37..3b8e6f7 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -25,6 +25,7 @@  obj-$(CONFIG_PINCTRL_PISTACHIO)	+= pinctrl-pistachio.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
 obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)	+= sirf/
+obj-$(CONFIG_PINCTRL_SX150X)	+= pinctrl-sx150x.o
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
 obj-$(CONFIG_PINCTRL_TZ1090)	+= pinctrl-tz1090.o
 obj-$(CONFIG_PINCTRL_TZ1090_PDC)	+= pinctrl-tz1090-pdc.o
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c
similarity index 23%
rename from drivers/gpio/gpio-sx150x.c
rename to drivers/pinctrl/pinctrl-sx150x.c
index af95de8..e985471 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/pinctrl/pinctrl-sx150x.c
@@ -1,4 +1,8 @@ 
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/*
+ * Copyright (c) 2016, BayLibre, SAS. All rights reserved.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
  *
  * Driver for Semtech SX150X I2C GPIO Expanders
  *
@@ -12,13 +16,8 @@ 
  * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
  */
-#include <linux/gpio.h>
+
 #include <linux/i2c.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -26,17 +25,23 @@ 
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf-generic.h>
 
-#define NO_UPDATE_PENDING	-1
+#include "core.h"
+#include "pinconf.h"
+#include "pinctrl-utils.h"
 
 /* The chip models of sx150x */
-#define SX150X_123 0
-#define SX150X_456 1
-#define SX150X_789 2
+enum {
+	SX150X_123 = 0,
+	SX150X_456,
+	SX150X_789,
+};
 
 struct sx150x_123_pri {
 	u8 reg_pld_mode;
@@ -82,167 +87,146 @@  struct sx150x_device_data {
 		struct sx150x_456_pri x456;
 		struct sx150x_789_pri x789;
 	} pri;
+	const struct pinctrl_pin_desc *pins;
+	unsigned int npins;
 };
 
-/**
- * struct sx150x_platform_data - config data for SX150x driver
- * @gpio_base: The index number of the first GPIO assigned to this
- *             GPIO expander.  The expander will create a block of
- *             consecutively numbered gpios beginning at the given base,
- *             with the size of the block depending on the model of the
- *             expander chip.
- * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO
- *                instead of as an oscillator, increasing the size of the
- *                GP(I)O pool created by this expander by one.  The
- *                output-only GPO pin will be added at the end of the block.
- * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor
- *                 for each IO line in the expander.  Setting the bit at
- *                 position n will enable the pull-up for the IO at
- *                 the corresponding offset.  For chips with fewer than
- *                 16 IO pins, high-end bits are ignored.
- * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down
- *                 resistor for each IO line in the expander. Setting the
- *                 bit at position n will enable the pull-down for the IO at
- *                 the corresponding offset.  For chips with fewer than
- *                 16 IO pins, high-end bits are ignored.
- * @io_polarity: A bit-mask which enables polarity inversion for each IO line
- *               in the expander.  Setting the bit at position n inverts
- *               the polarity of that IO line, while clearing it results
- *               in normal polarity. For chips with fewer than 16 IO pins,
- *               high-end bits are ignored.
- * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line
- *               is connected, via which it reports interrupt events
- *               across all GPIO lines.  This must be a real,
- *               pre-existing IRQ line.
- *               Setting this value < 0 disables the irq_chip functionality
- *               of the driver.
- * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based
- *            IRQ lines will appear.  Similarly to gpio_base, the expander
- *            will create a block of irqs beginning at this number.
- *            This value is ignored if irq_summary is < 0.
- * @reset_during_probe: If set to true, the driver will trigger a full
- *                      reset of the chip at the beginning of the probe
- *                      in order to place it in a known state.
- */
-struct sx150x_platform_data {
-	unsigned gpio_base;
-	bool     oscio_is_gpo;
-	u16      io_pullup_ena;
-	u16      io_pulldn_ena;
-	u16      io_polarity;
-	int      irq_summary;
-	unsigned irq_base;
-	bool     reset_during_probe;
+struct sx150x_pinctrl {
+	struct device *dev;
+	struct i2c_client *client;
+	struct pinctrl_dev *pctldev;
+	struct pinctrl_desc pinctrl_desc;
+	struct gpio_chip gpio;
+	struct irq_chip irq_chip;
+	struct {
+		int update;
+		u32 sense;
+		u32 masked;
+		u32 dev_sense;
+		u32 dev_masked;
+	} irq;
+	struct mutex lock;
+	const struct sx150x_device_data *data;
 };
 
-struct sx150x_chip {
-	struct gpio_chip                 gpio_chip;
-	struct i2c_client               *client;
-	const struct sx150x_device_data *dev_cfg;
-	int                              irq_summary;
-	int                              irq_base;
-	int				 irq_update;
-	u32                              irq_sense;
-	u32				 irq_masked;
-	u32				 dev_sense;
-	u32				 dev_masked;
-	struct irq_chip                  irq_chip;
-	struct mutex                     lock;
+static const struct pinctrl_pin_desc sx150x_8_pins[] = {
+	PINCTRL_PIN(0, "gpio0"),
+	PINCTRL_PIN(1, "gpio1"),
+	PINCTRL_PIN(2, "gpio2"),
+	PINCTRL_PIN(3, "gpio3"),
+	PINCTRL_PIN(4, "gpio4"),
+	PINCTRL_PIN(5, "gpio5"),
+	PINCTRL_PIN(6, "gpio6"),
+	PINCTRL_PIN(7, "gpio7"),
+	PINCTRL_PIN(8, "oscio"),
 };
 
-static const struct sx150x_device_data sx150x_devices[] = {
-	[0] = { /* sx1508q */
-		.model = SX150X_789,
-		.reg_pullup	= 0x03,
-		.reg_pulldn	= 0x04,
-		.reg_dir	= 0x07,
-		.reg_data	= 0x08,
-		.reg_irq_mask	= 0x09,
-		.reg_irq_src	= 0x0c,
-		.reg_sense	= 0x0b,
-		.pri.x789 = {
-			.reg_drain	= 0x05,
-			.reg_polarity	= 0x06,
-			.reg_clock	= 0x0f,
-			.reg_misc	= 0x10,
-			.reg_reset	= 0x7d,
-		},
-		.ngpios = 8,
-	},
-	[1] = { /* sx1509q */
-		.model = SX150X_789,
-		.reg_pullup	= 0x07,
-		.reg_pulldn	= 0x09,
-		.reg_dir	= 0x0f,
-		.reg_data	= 0x11,
-		.reg_irq_mask	= 0x13,
-		.reg_irq_src	= 0x19,
-		.reg_sense	= 0x17,
-		.pri.x789 = {
-			.reg_drain	= 0x0b,
-			.reg_polarity	= 0x0d,
-			.reg_clock	= 0x1e,
-			.reg_misc	= 0x1f,
-			.reg_reset	= 0x7d,
-		},
-		.ngpios	= 16
-	},
-	[2] = { /* sx1506q */
-		.model = SX150X_456,
-		.reg_pullup	= 0x05,
-		.reg_pulldn	= 0x07,
-		.reg_dir	= 0x03,
-		.reg_data	= 0x01,
-		.reg_irq_mask	= 0x09,
-		.reg_irq_src	= 0x0f,
-		.reg_sense	= 0x0d,
-		.pri.x456 = {
-			.reg_pld_mode	= 0x21,
-			.reg_pld_table0	= 0x23,
-			.reg_pld_table1	= 0x25,
-			.reg_pld_table2	= 0x27,
-			.reg_pld_table3	= 0x29,
-			.reg_pld_table4	= 0x2b,
-			.reg_advance	= 0xad,
-		},
-		.ngpios	= 16
+static const struct pinctrl_pin_desc sx150x_16_pins[] = {
+	PINCTRL_PIN(0, "gpio0"),
+	PINCTRL_PIN(1, "gpio1"),
+	PINCTRL_PIN(2, "gpio2"),
+	PINCTRL_PIN(3, "gpio3"),
+	PINCTRL_PIN(4, "gpio4"),
+	PINCTRL_PIN(5, "gpio5"),
+	PINCTRL_PIN(6, "gpio6"),
+	PINCTRL_PIN(7, "gpio7"),
+	PINCTRL_PIN(8, "gpio8"),
+	PINCTRL_PIN(9, "gpio9"),
+	PINCTRL_PIN(10, "gpio10"),
+	PINCTRL_PIN(11, "gpio11"),
+	PINCTRL_PIN(12, "gpio12"),
+	PINCTRL_PIN(13, "gpio13"),
+	PINCTRL_PIN(14, "gpio14"),
+	PINCTRL_PIN(15, "gpio15"),
+	PINCTRL_PIN(16, "oscio"),
+};
+
+static const struct sx150x_device_data sx1508q_device_data = {
+	.model = SX150X_789,
+	.reg_pullup	= 0x03,
+	.reg_pulldn	= 0x04,
+	.reg_dir	= 0x07,
+	.reg_data	= 0x08,
+	.reg_irq_mask	= 0x09,
+	.reg_irq_src	= 0x0c,
+	.reg_sense	= 0x0b,
+	.pri.x789 = {
+		.reg_drain	= 0x05,
+		.reg_polarity	= 0x06,
+		.reg_clock	= 0x0f,
+		.reg_misc	= 0x10,
+		.reg_reset	= 0x7d,
 	},
-	[3] = { /* sx1502q */
-		.model = SX150X_123,
-		.reg_pullup	= 0x02,
-		.reg_pulldn	= 0x03,
-		.reg_dir	= 0x01,
-		.reg_data	= 0x00,
-		.reg_irq_mask	= 0x05,
-		.reg_irq_src	= 0x08,
-		.reg_sense	= 0x07,
-		.pri.x123 = {
-			.reg_pld_mode	= 0x10,
-			.reg_pld_table0	= 0x11,
-			.reg_pld_table1	= 0x12,
-			.reg_pld_table2	= 0x13,
-			.reg_pld_table3	= 0x14,
-			.reg_pld_table4	= 0x15,
-			.reg_advance	= 0xad,
-		},
-		.ngpios	= 8,
+	.ngpios = 8,
+	.pins = sx150x_8_pins,
+	.npins = ARRAY_SIZE(sx150x_8_pins),
+};
+
+static const struct sx150x_device_data sx1509q_device_data = {
+	.model = SX150X_789,
+	.reg_pullup	= 0x07,
+	.reg_pulldn	= 0x09,
+	.reg_dir	= 0x0f,
+	.reg_data	= 0x11,
+	.reg_irq_mask	= 0x13,
+	.reg_irq_src	= 0x19,
+	.reg_sense	= 0x17,
+	.pri.x789 = {
+		.reg_drain	= 0x0b,
+		.reg_polarity	= 0x0d,
+		.reg_clock	= 0x1e,
+		.reg_misc	= 0x1f,
+		.reg_reset	= 0x7d,
 	},
+	.ngpios	= 16,
+	.pins = sx150x_16_pins,
+	.npins = ARRAY_SIZE(sx150x_16_pins),
 };
 
-static const struct i2c_device_id sx150x_id[] = {
-	{"sx1508q", 0},
-	{"sx1509q", 1},
-	{"sx1506q", 2},
-	{"sx1502q", 3},
-	{}
+static const struct sx150x_device_data sx1506q_device_data = {
+	.model = SX150X_456,
+	.reg_pullup	= 0x05,
+	.reg_pulldn	= 0x07,
+	.reg_dir	= 0x03,
+	.reg_data	= 0x01,
+	.reg_irq_mask	= 0x09,
+	.reg_irq_src	= 0x0f,
+	.reg_sense	= 0x0d,
+	.pri.x456 = {
+		.reg_pld_mode	= 0x21,
+		.reg_pld_table0	= 0x23,
+		.reg_pld_table1	= 0x25,
+		.reg_pld_table2	= 0x27,
+		.reg_pld_table3	= 0x29,
+		.reg_pld_table4	= 0x2b,
+		.reg_advance	= 0xad,
+	},
+	.ngpios	= 16,
+	.pins = sx150x_16_pins,
+	.npins = 16, /* oscio not available */
 };
 
-static const struct of_device_id sx150x_of_match[] = {
-	{ .compatible = "semtech,sx1508q" },
-	{ .compatible = "semtech,sx1509q" },
-	{ .compatible = "semtech,sx1506q" },
-	{ .compatible = "semtech,sx1502q" },
-	{},
+static const struct sx150x_device_data sx1502q_device_data = {
+	.model = SX150X_123,
+	.reg_pullup	= 0x02,
+	.reg_pulldn	= 0x03,
+	.reg_dir	= 0x01,
+	.reg_data	= 0x00,
+	.reg_irq_mask	= 0x05,
+	.reg_irq_src	= 0x08,
+	.reg_sense	= 0x07,
+	.pri.x123 = {
+		.reg_pld_mode	= 0x10,
+		.reg_pld_table0	= 0x11,
+		.reg_pld_table1	= 0x12,
+		.reg_pld_table2	= 0x13,
+		.reg_pld_table3	= 0x14,
+		.reg_pld_table4	= 0x15,
+		.reg_advance	= 0xad,
+	},
+	.ngpios	= 8,
+	.pins = sx150x_8_pins,
+	.npins = 8, /* oscio not available */
 };
 
 static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
@@ -269,11 +253,6 @@  static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
 	return err;
 }
 
-static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
-{
-	return (chip->dev_cfg->ngpios == offset);
-}
-
 /*
  * These utility functions solve the common problem of locating and setting
  * configuration bits.  Configuration bits are grouped into registers
@@ -294,7 +273,7 @@  static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
  * register and mask the correct bits.
  */
 static inline void sx150x_find_cfg(u8 offset, u8 width,
-				u8 *reg, u8 *mask, u8 *shift)
+				   u8 *reg, u8 *mask, u8 *shift)
 {
 	*reg   -= offset * width / 8;
 	*mask   = (1 << width) - 1;
@@ -302,185 +281,241 @@  static inline void sx150x_find_cfg(u8 offset, u8 width,
 	*mask <<= *shift;
 }
 
-static s32 sx150x_write_cfg(struct sx150x_chip *chip,
-			u8 offset, u8 width, u8 reg, u8 val)
+static int sx150x_write_cfg(struct i2c_client *client,
+			    u8 offset, u8 width, u8 reg, u8 val)
 {
 	u8  mask;
 	u8  data;
 	u8  shift;
-	s32 err;
+	int err;
 
 	sx150x_find_cfg(offset, width, &reg, &mask, &shift);
-	err = sx150x_i2c_read(chip->client, reg, &data);
+	err = sx150x_i2c_read(client, reg, &data);
 	if (err < 0)
 		return err;
 
 	data &= ~mask;
 	data |= (val << shift) & mask;
-	return sx150x_i2c_write(chip->client, reg, data);
+	return sx150x_i2c_write(client, reg, data);
 }
 
-static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
+static int sx150x_read_cfg(struct i2c_client *client,
+			   u8 offset, u8 width, u8 reg)
 {
-	u8  reg = chip->dev_cfg->reg_data;
 	u8  mask;
 	u8  data;
 	u8  shift;
-	s32 err;
+	int err;
 
-	sx150x_find_cfg(offset, 1, &reg, &mask, &shift);
-	err = sx150x_i2c_read(chip->client, reg, &data);
-	if (err >= 0)
-		err = (data & mask) != 0 ? 1 : 0;
+	sx150x_find_cfg(offset, width, &reg, &mask, &shift);
+	err = sx150x_i2c_read(client, reg, &data);
+	if (err < 0)
+		return err;
 
-	return err;
+	return (data & mask);
 }
 
-static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+static int sx150x_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
 {
-	sx150x_i2c_write(chip->client,
-			chip->dev_cfg->pri.x789.reg_clock,
-			(val ? 0x1f : 0x10));
+	return 0;
 }
 
-static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
+static const char *sx150x_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+						unsigned int group)
 {
-	sx150x_write_cfg(chip,
-			offset,
-			1,
-			chip->dev_cfg->reg_data,
-			(val ? 1 : 0));
+	return NULL;
 }
 
-static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
+static int sx150x_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					unsigned int group,
+					const unsigned int **pins,
+					unsigned int *num_pins)
 {
-	return sx150x_write_cfg(chip,
-				offset,
-				1,
-				chip->dev_cfg->reg_dir,
-				1);
+	return -ENOTSUPP;
 }
 
-static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
+static const struct pinctrl_ops sx150x_pinctrl_ops = {
+	.get_groups_count = sx150x_pinctrl_get_groups_count,
+	.get_group_name = sx150x_pinctrl_get_group_name,
+	.get_group_pins = sx150x_pinctrl_get_group_pins,
+#ifdef CONFIG_OF
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map = pinctrl_utils_free_map,
+#endif
+};
+
+static bool sx150x_pin_is_oscio(struct sx150x_pinctrl *pctl, unsigned int pin)
 {
-	int err;
+	if (pin >= pctl->data->npins)
+		return false;
 
-	err = sx150x_write_cfg(chip,
-			offset,
-			1,
-			chip->dev_cfg->reg_data,
-			(val ? 1 : 0));
-	if (err >= 0)
-		err = sx150x_write_cfg(chip,
-				offset,
-				1,
-				chip->dev_cfg->reg_dir,
-				0);
-	return err;
+	/* OSCIO pin is only present in 789 devices */
+	if (pctl->data->model != SX150X_789)
+		return false;
+
+	return !strcmp(pctl->data->pins[pin].name, "oscio");
 }
 
-static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
+static int sx150x_gpio_get_direction(struct gpio_chip *chip,
+				      unsigned int offset)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(gc);
-	int status = -EINVAL;
+	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
+	int status;
 
-	if (!offset_is_oscio(chip, offset)) {
-		mutex_lock(&chip->lock);
-		status = sx150x_get_io(chip, offset);
-		mutex_unlock(&chip->lock);
-	}
+	if (sx150x_pin_is_oscio(pctl, offset))
+		return false;
+
+	status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_dir);
+	if (status >= 0)
+		status = !!status;
 
-	return (status < 0) ? status : !!status;
+	return status;
 }
 
-static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
+static int sx150x_gpio_get(struct gpio_chip *chip, unsigned int offset)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(gc);
+	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
+	int status;
 
-	mutex_lock(&chip->lock);
-	if (offset_is_oscio(chip, offset))
-		sx150x_set_oscio(chip, val);
-	else
-		sx150x_set_io(chip, offset, val);
-	mutex_unlock(&chip->lock);
+	if (sx150x_pin_is_oscio(pctl, offset))
+		return -EINVAL;
+
+	status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_data);
+	if (status >= 0)
+		status = !!status;
+
+	return status;
 }
 
-static int sx150x_gpio_set_single_ended(struct gpio_chip *gc,
-					unsigned offset,
-                                        enum single_ended_mode mode)
+static int sx150x_gpio_set_single_ended(struct gpio_chip *chip,
+					unsigned int offset,
+					enum single_ended_mode mode)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(gc);
-
-	/* On the SX160X 789 we can set open drain */
-	if (chip->dev_cfg->model != SX150X_789)
+	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
+	int ret;
+
+	switch (mode) {
+	case LINE_MODE_PUSH_PULL:
+		if (pctl->data->model != SX150X_789 ||
+		    sx150x_pin_is_oscio(pctl, offset))
+			return 0;
+
+		mutex_lock(&pctl->lock);
+		ret = sx150x_write_cfg(pctl->client, offset, 1,
+				       pctl->data->pri.x789.reg_drain,
+				       0);
+		mutex_unlock(&pctl->lock);
+		if (ret < 0)
+			return ret;
+		break;
+
+	case LINE_MODE_OPEN_DRAIN:
+		if (pctl->data->model != SX150X_789 ||
+		    sx150x_pin_is_oscio(pctl, offset))
+			return -ENOTSUPP;
+
+		mutex_lock(&pctl->lock);
+		ret = sx150x_write_cfg(pctl->client, offset, 1,
+				       pctl->data->pri.x789.reg_drain,
+				       1);
+		mutex_unlock(&pctl->lock);
+		if (ret < 0)
+			return ret;
+		break;
+
+	default:
 		return -ENOTSUPP;
+	}
 
-	if (mode == LINE_MODE_PUSH_PULL)
-		return sx150x_write_cfg(chip,
-					offset,
-					1,
-					chip->dev_cfg->pri.x789.reg_drain,
-					0);
-
-	if (mode == LINE_MODE_OPEN_DRAIN)
-		return sx150x_write_cfg(chip,
-					offset,
-					1,
-					chip->dev_cfg->pri.x789.reg_drain,
-					1);
-	return -ENOTSUPP;
+	return 0;
 }
 
-static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+static void sx150x_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			       int value)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(gc);
-	int status = -EINVAL;
+	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
+
+	if (sx150x_pin_is_oscio(pctl, offset)) {
 
-	if (!offset_is_oscio(chip, offset)) {
-		mutex_lock(&chip->lock);
-		status = sx150x_io_input(chip, offset);
-		mutex_unlock(&chip->lock);
+		mutex_lock(&pctl->lock);
+		sx150x_i2c_write(pctl->client,
+				       pctl->data->pri.x789.reg_clock,
+				       (value ? 0x1f : 0x10));
+		mutex_unlock(&pctl->lock);
+	} else {
+		mutex_lock(&pctl->lock);
+		sx150x_write_cfg(pctl->client, offset, 1,
+				       pctl->data->reg_data,
+				       (value ? 1 : 0));
+		mutex_unlock(&pctl->lock);
 	}
-	return status;
 }
 
-static int sx150x_gpio_direction_output(struct gpio_chip *gc,
-					unsigned offset,
-					int val)
+static int sx150x_gpio_direction_input(struct gpio_chip *chip,
+				      unsigned int offset)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(gc);
-	int status = 0;
+	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
+	int ret;
 
-	if (!offset_is_oscio(chip, offset)) {
-		mutex_lock(&chip->lock);
-		status = sx150x_io_output(chip, offset, val);
-		mutex_unlock(&chip->lock);
+	if (sx150x_pin_is_oscio(pctl, offset))
+		return -EINVAL;
+
+	mutex_lock(&pctl->lock);
+	ret = sx150x_write_cfg(pctl->client, offset, 1,
+				pctl->data->reg_dir, 1);
+	mutex_unlock(&pctl->lock);
+
+	return ret;
+}
+
+static int sx150x_gpio_direction_output(struct gpio_chip *chip,
+				       unsigned int offset, int value)
+{
+	struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
+	int status;
+
+	if (sx150x_pin_is_oscio(pctl, offset)) {
+		sx150x_gpio_set(chip, offset, value);
+		return 0;
 	}
+
+	mutex_lock(&pctl->lock);
+	status = sx150x_write_cfg(pctl->client, offset, 1,
+				  pctl->data->reg_data,
+				  (value ? 1 : 0));
+	if (status >= 0)
+		status = sx150x_write_cfg(pctl->client, offset, 1,
+					  pctl->data->reg_dir, 0);
+	mutex_unlock(&pctl->lock);
+
 	return status;
 }
 
 static void sx150x_irq_mask(struct irq_data *d)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
-	unsigned n = d->hwirq;
+	struct sx150x_pinctrl *pctl =
+			gpiochip_get_data(irq_data_get_irq_chip_data(d));
+	unsigned int n = d->hwirq;
 
-	chip->irq_masked |= (1 << n);
-	chip->irq_update = n;
+	pctl->irq.masked |= (1 << n);
+	pctl->irq.update = n;
 }
 
 static void sx150x_irq_unmask(struct irq_data *d)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
-	unsigned n = d->hwirq;
+	struct sx150x_pinctrl *pctl =
+			gpiochip_get_data(irq_data_get_irq_chip_data(d));
+	unsigned int n = d->hwirq;
 
-	chip->irq_masked &= ~(1 << n);
-	chip->irq_update = n;
+	pctl->irq.masked &= ~(1 << n);
+	pctl->irq.update = n;
 }
 
 static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
-	unsigned n, val = 0;
+	struct sx150x_pinctrl *pctl =
+			gpiochip_get_data(irq_data_get_irq_chip_data(d));
+	unsigned int n, val = 0;
 
 	if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
 		return -EINVAL;
@@ -492,37 +527,40 @@  static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
 	if (flow_type & IRQ_TYPE_EDGE_FALLING)
 		val |= 0x2;
 
-	chip->irq_sense &= ~(3UL << (n * 2));
-	chip->irq_sense |= val << (n * 2);
-	chip->irq_update = n;
+	pctl->irq.sense &= ~(3UL << (n * 2));
+	pctl->irq.sense |= val << (n * 2);
+	pctl->irq.update = n;
 	return 0;
 }
 
 static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
 {
-	struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
-	unsigned nhandled = 0;
-	unsigned sub_irq;
-	unsigned n;
+	struct sx150x_pinctrl *pctl = (struct sx150x_pinctrl *)dev_id;
+	unsigned int nhandled = 0;
+	unsigned int sub_irq;
+	unsigned int n;
 	s32 err;
 	u8 val;
 	int i;
 
-	for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
-		err = sx150x_i2c_read(chip->client,
-				      chip->dev_cfg->reg_irq_src - i,
+	for (i = (pctl->data->ngpios / 8) - 1; i >= 0; --i) {
+		err = sx150x_i2c_read(pctl->client,
+				      pctl->data->reg_irq_src - i,
 				      &val);
 		if (err < 0)
 			continue;
 
-		sx150x_i2c_write(chip->client,
-				chip->dev_cfg->reg_irq_src - i,
-				val);
+		err = sx150x_i2c_write(pctl->client,
+				       pctl->data->reg_irq_src - i,
+				       val);
+		if (err < 0)
+			continue;
+
 		for (n = 0; n < 8; ++n) {
 			if (val & (1 << n)) {
 				sub_irq = irq_find_mapping(
-					chip->gpio_chip.irqdomain,
-					(i * 8) + n);
+						pctl->gpio.irqdomain,
+						(i * 8) + n);
 				handle_nested_irq(sub_irq);
 				++nhandled;
 			}
@@ -534,251 +572,483 @@  static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
 
 static void sx150x_irq_bus_lock(struct irq_data *d)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
+	struct sx150x_pinctrl *pctl =
+			gpiochip_get_data(irq_data_get_irq_chip_data(d));
 
-	mutex_lock(&chip->lock);
+	mutex_lock(&pctl->lock);
 }
 
 static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
 {
-	struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
-	unsigned n;
+	struct sx150x_pinctrl *pctl =
+			gpiochip_get_data(irq_data_get_irq_chip_data(d));
+	unsigned int n;
 
-	if (chip->irq_update == NO_UPDATE_PENDING)
+	if (pctl->irq.update < 0)
 		goto out;
 
-	n = chip->irq_update;
-	chip->irq_update = NO_UPDATE_PENDING;
+	n = pctl->irq.update;
+	pctl->irq.update = -1;
 
 	/* Avoid updates if nothing changed */
-	if (chip->dev_sense == chip->irq_sense &&
-	    chip->dev_masked == chip->irq_masked)
+	if (pctl->irq.dev_sense == pctl->irq.sense &&
+	    pctl->irq.dev_masked == pctl->irq.masked)
 		goto out;
 
-	chip->dev_sense = chip->irq_sense;
-	chip->dev_masked = chip->irq_masked;
+	pctl->irq.dev_sense = pctl->irq.sense;
+	pctl->irq.dev_masked = pctl->irq.masked;
 
-	if (chip->irq_masked & (1 << n)) {
-		sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
-		sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+	if (pctl->irq.masked & (1 << n)) {
+		sx150x_write_cfg(pctl->client, n, 1,
+				 pctl->data->reg_irq_mask, 1);
+		sx150x_write_cfg(pctl->client, n, 2,
+				 pctl->data->reg_sense, 0);
 	} else {
-		sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
-		sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
-				 chip->irq_sense >> (n * 2));
+		sx150x_write_cfg(pctl->client, n, 1,
+				 pctl->data->reg_irq_mask, 0);
+		sx150x_write_cfg(pctl->client, n, 2,
+				 pctl->data->reg_sense,
+				 pctl->irq.sense >> (n * 2));
 	}
 out:
-	mutex_unlock(&chip->lock);
+	mutex_unlock(&pctl->lock);
 }
 
-static void sx150x_init_chip(struct sx150x_chip *chip,
-			struct i2c_client *client,
-			kernel_ulong_t driver_data,
-			struct sx150x_platform_data *pdata)
+static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+			      unsigned long *config)
 {
-	mutex_init(&chip->lock);
-
-	chip->client                     = client;
-	chip->dev_cfg                    = &sx150x_devices[driver_data];
-	chip->gpio_chip.parent              = &client->dev;
-	chip->gpio_chip.label            = client->name;
-	chip->gpio_chip.direction_input  = sx150x_gpio_direction_input;
-	chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
-	chip->gpio_chip.get              = sx150x_gpio_get;
-	chip->gpio_chip.set              = sx150x_gpio_set;
-	chip->gpio_chip.set_single_ended = sx150x_gpio_set_single_ended;
-	chip->gpio_chip.base             = pdata->gpio_base;
-	chip->gpio_chip.can_sleep        = true;
-	chip->gpio_chip.ngpio            = chip->dev_cfg->ngpios;
-#ifdef CONFIG_OF_GPIO
-	chip->gpio_chip.of_node          = client->dev.of_node;
-	chip->gpio_chip.of_gpio_n_cells  = 2;
-#endif
-	if (pdata->oscio_is_gpo)
-		++chip->gpio_chip.ngpio;
-
-	chip->irq_chip.name                = client->name;
-	chip->irq_chip.irq_mask            = sx150x_irq_mask;
-	chip->irq_chip.irq_unmask          = sx150x_irq_unmask;
-	chip->irq_chip.irq_set_type        = sx150x_irq_set_type;
-	chip->irq_chip.irq_bus_lock        = sx150x_irq_bus_lock;
-	chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
-	chip->irq_summary                  = -1;
-	chip->irq_base                     = -1;
-	chip->irq_masked                   = ~0;
-	chip->irq_sense                    = 0;
-	chip->dev_masked                   = ~0;
-	chip->dev_sense                    = 0;
-	chip->irq_update		   = NO_UPDATE_PENDING;
+	struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	unsigned int param = pinconf_to_config_param(*config);
+	int ret;
+	u32 arg;
+
+	if (sx150x_pin_is_oscio(pctl, pin)) {
+		u8 data;
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+		case PIN_CONFIG_OUTPUT:
+			mutex_lock(&pctl->lock);
+			ret = sx150x_i2c_read(pctl->client,
+					pctl->data->pri.x789.reg_clock,
+					&data);
+			mutex_unlock(&pctl->lock);
+
+			if (ret < 0)
+				return ret;
+
+			if (param == PIN_CONFIG_DRIVE_PUSH_PULL)
+				arg = (data & 0x1f) ? 1 : 0;
+			else {
+				if ((data & 0x1f) == 0x1f)
+					arg = 1;
+				else if ((data & 0x1f) == 0x10)
+					arg = 0;
+				else
+					return -EINVAL;
+			}
+
+			break;
+		default:
+			return -ENOTSUPP;
+		}
+
+		goto out;
+	}
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		mutex_lock(&pctl->lock);
+		ret = sx150x_read_cfg(pctl->client, pin, 1,
+				      pctl->data->reg_pulldn);
+		mutex_unlock(&pctl->lock);
+
+		if (ret < 0)
+			return ret;
+
+		if (!ret)
+			return -EINVAL;
+
+		arg = 1;
+		break;
+
+	case PIN_CONFIG_BIAS_PULL_UP:
+		mutex_lock(&pctl->lock);
+		ret = sx150x_read_cfg(pctl->client, pin, 1,
+				      pctl->data->reg_pullup);
+		mutex_unlock(&pctl->lock);
+
+		if (ret < 0)
+			return ret;
+
+		if (!ret)
+			return -EINVAL;
+
+		arg = 1;
+		break;
+
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		if (pctl->data->model != SX150X_789)
+			return -ENOTSUPP;
+
+		mutex_lock(&pctl->lock);
+		ret = sx150x_read_cfg(pctl->client, pin, 1,
+				      pctl->data->pri.x789.reg_drain);
+		mutex_unlock(&pctl->lock);
+
+		if (ret < 0)
+			return ret;
+
+		if (!ret)
+			return -EINVAL;
+
+		arg = 1;
+		break;
+
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		if (pctl->data->model != SX150X_789)
+			arg = true;
+		else {
+			mutex_lock(&pctl->lock);
+			ret = sx150x_read_cfg(pctl->client, pin, 1,
+					      pctl->data->pri.x789.reg_drain);
+			mutex_unlock(&pctl->lock);
+
+			if (ret < 0)
+				return ret;
+
+			if (ret)
+				return -EINVAL;
+
+			arg = 1;
+		}
+		break;
+
+	case PIN_CONFIG_OUTPUT:
+		ret = sx150x_gpio_get_direction(&pctl->gpio, pin);
+		if (ret < 0)
+			return ret;
+
+		if (ret)
+			return -EINVAL;
+
+		ret = sx150x_gpio_get(&pctl->gpio, pin);
+		if (ret < 0)
+			return ret;
+
+		arg = ret;
+		break;
+
+	default:
+		return -ENOTSUPP;
+	}
+
+out:
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+			      unsigned long *configs, unsigned int num_configs)
+{
+	struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param;
+	u32 arg;
+	int i;
+	int ret;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		if (sx150x_pin_is_oscio(pctl, pin)) {
+			if (param == PIN_CONFIG_OUTPUT) {
+				ret = sx150x_gpio_direction_output(&pctl->gpio,
+								   pin, arg);
+				if (ret < 0)
+					return ret;
+
+				continue;
+			} else
+				return -ENOTSUPP;
+		}
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+		case PIN_CONFIG_BIAS_DISABLE:
+			mutex_lock(&pctl->lock);
+			ret = sx150x_write_cfg(pctl->client, pin, 1,
+					       pctl->data->reg_pulldn, 0);
+			mutex_unlock(&pctl->lock);
+			if (ret < 0)
+				return ret;
+
+			mutex_lock(&pctl->lock);
+			ret = sx150x_write_cfg(pctl->client, pin, 1,
+					       pctl->data->reg_pullup, 0);
+			mutex_unlock(&pctl->lock);
+			if (ret < 0)
+				return ret;
+
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_UP:
+			mutex_lock(&pctl->lock);
+			ret = sx150x_write_cfg(pctl->client, pin, 1,
+					       pctl->data->reg_pullup,
+					       1);
+			mutex_unlock(&pctl->lock);
+			if (ret < 0)
+				return ret;
+
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			mutex_lock(&pctl->lock);
+			ret = sx150x_write_cfg(pctl->client, pin, 1,
+					       pctl->data->reg_pulldn,
+					       1);
+			mutex_unlock(&pctl->lock);
+			if (ret < 0)
+				return ret;
+
+			break;
+
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			ret = sx150x_gpio_set_single_ended(&pctl->gpio,
+						pin, LINE_MODE_OPEN_DRAIN);
+			if (ret < 0)
+				return ret;
+
+			break;
+
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+			ret = sx150x_gpio_set_single_ended(&pctl->gpio,
+						pin, LINE_MODE_PUSH_PULL);
+			if (ret < 0)
+				return ret;
+
+			break;
+
+		case PIN_CONFIG_OUTPUT:
+			ret = sx150x_gpio_direction_output(&pctl->gpio,
+							   pin, arg);
+			if (ret < 0)
+				return ret;
+
+			break;
+
+		default:
+			return -ENOTSUPP;
+		}
+	} /* for each config */
+
+	return 0;
 }
 
-static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
+static const struct pinconf_ops sx150x_pinconf_ops = {
+	.pin_config_get = sx150x_pinconf_get,
+	.pin_config_set = sx150x_pinconf_set,
+	.is_generic = true,
+};
+
+static const struct i2c_device_id sx150x_id[] = {
+	{"sx1508q", (kernel_ulong_t) &sx1508q_device_data },
+	{"sx1509q", (kernel_ulong_t) &sx1509q_device_data },
+	{"sx1506q", (kernel_ulong_t) &sx1506q_device_data },
+	{"sx1502q", (kernel_ulong_t) &sx1502q_device_data },
+	{}
+};
+
+static const struct of_device_id sx150x_of_match[] = {
+	{ .compatible = "semtech,sx1508q" },
+	{ .compatible = "semtech,sx1509q" },
+	{ .compatible = "semtech,sx1506q" },
+	{ .compatible = "semtech,sx1502q" },
+	{},
+};
+
+static int sx150x_init_io(struct sx150x_pinctrl *pctl, u8 base, u16 cfg)
 {
 	int err = 0;
-	unsigned n;
+	unsigned int n;
 
-	for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
-		err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
+	for (n = 0; err >= 0 && n < (pctl->data->ngpios / 8); ++n)
+		err = sx150x_i2c_write(pctl->client, base - n, cfg >> (n * 8));
 	return err;
 }
 
-static int sx150x_reset(struct sx150x_chip *chip)
+static int sx150x_reset(struct sx150x_pinctrl *pctl)
 {
 	int err;
 
-	err = i2c_smbus_write_byte_data(chip->client,
-					chip->dev_cfg->pri.x789.reg_reset,
+	err = i2c_smbus_write_byte_data(pctl->client,
+					pctl->data->pri.x789.reg_reset,
 					0x12);
 	if (err < 0)
 		return err;
 
-	err = i2c_smbus_write_byte_data(chip->client,
-					chip->dev_cfg->pri.x789.reg_reset,
+	err = i2c_smbus_write_byte_data(pctl->client,
+					pctl->data->pri.x789.reg_reset,
 					0x34);
 	return err;
 }
 
-static int sx150x_init_hw(struct sx150x_chip *chip,
-			struct sx150x_platform_data *pdata)
+static int sx150x_init_hw(struct sx150x_pinctrl *pctl)
 {
-	int err = 0;
+	int err;
 
-	if (pdata->reset_during_probe) {
-		err = sx150x_reset(chip);
+	if (pctl->data->model == SX150X_789 &&
+	    of_property_read_bool(pctl->dev->of_node, "semtech,probe-reset")) {
+		err = sx150x_reset(pctl);
 		if (err < 0)
 			return err;
 	}
 
-	if (chip->dev_cfg->model == SX150X_789)
-		err = sx150x_i2c_write(chip->client,
-				chip->dev_cfg->pri.x789.reg_misc,
+	if (pctl->data->model == SX150X_789)
+		err = sx150x_i2c_write(pctl->client,
+				pctl->data->pri.x789.reg_misc,
 				0x01);
-	else if (chip->dev_cfg->model == SX150X_456)
-		err = sx150x_i2c_write(chip->client,
-				chip->dev_cfg->pri.x456.reg_advance,
+	else if (pctl->data->model == SX150X_456)
+		err = sx150x_i2c_write(pctl->client,
+				pctl->data->pri.x456.reg_advance,
 				0x04);
 	else
-		err = sx150x_i2c_write(chip->client,
-				chip->dev_cfg->pri.x123.reg_advance,
+		err = sx150x_i2c_write(pctl->client,
+				pctl->data->pri.x123.reg_advance,
 				0x00);
 	if (err < 0)
 		return err;
 
-	err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
-			pdata->io_pullup_ena);
-	if (err < 0)
-		return err;
-
-	err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
-			pdata->io_pulldn_ena);
-	if (err < 0)
-		return err;
-
-	if (chip->dev_cfg->model == SX150X_789) {
-		err = sx150x_init_io(chip,
-				chip->dev_cfg->pri.x789.reg_polarity,
-				pdata->io_polarity);
+	/* Set all pins to work in normal mode */
+	if (pctl->data->model == SX150X_789) {
+		err = sx150x_init_io(pctl,
+				pctl->data->pri.x789.reg_polarity,
+				0);
 		if (err < 0)
 			return err;
-	} else if (chip->dev_cfg->model == SX150X_456) {
+	} else if (pctl->data->model == SX150X_456) {
 		/* Set all pins to work in normal mode */
-		err = sx150x_init_io(chip,
-				chip->dev_cfg->pri.x456.reg_pld_mode,
+		err = sx150x_init_io(pctl,
+				pctl->data->pri.x456.reg_pld_mode,
 				0);
 		if (err < 0)
 			return err;
 	} else {
 		/* Set all pins to work in normal mode */
-		err = sx150x_init_io(chip,
-				chip->dev_cfg->pri.x123.reg_pld_mode,
+		err = sx150x_init_io(pctl,
+				pctl->data->pri.x123.reg_pld_mode,
 				0);
 		if (err < 0)
 			return err;
 	}
 
-
-	if (pdata->oscio_is_gpo)
-		sx150x_set_oscio(chip, 0);
-
-	return err;
-}
-
-static int sx150x_install_irq_chip(struct sx150x_chip *chip,
-				int irq_summary,
-				int irq_base)
-{
-	int err;
-
-	chip->irq_summary = irq_summary;
-	chip->irq_base    = irq_base;
-
-	/* Add gpio chip to irq subsystem */
-	err = gpiochip_irqchip_add(&chip->gpio_chip,
-		&chip->irq_chip, chip->irq_base,
-		handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
-	if (err) {
-		dev_err(&chip->client->dev,
-			"could not connect irqchip to gpiochip\n");
-		return  err;
-	}
-
-	err = devm_request_threaded_irq(&chip->client->dev,
-			irq_summary, NULL, sx150x_irq_thread_fn,
-			IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING,
-			chip->irq_chip.name, chip);
-	if (err < 0) {
-		chip->irq_summary = -1;
-		chip->irq_base    = -1;
-	}
-
-	return err;
+	return 0;
 }
 
 static int sx150x_probe(struct i2c_client *client,
-				const struct i2c_device_id *id)
+			const struct i2c_device_id *id)
 {
 	static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
 				     I2C_FUNC_SMBUS_WRITE_WORD_DATA;
-	struct sx150x_platform_data *pdata;
-	struct sx150x_chip *chip;
-	int rc;
+	struct device *dev = &client->dev;
+	struct sx150x_pinctrl *pctl;
+	int ret;
 
-	pdata = dev_get_platdata(&client->dev);
-	if (!pdata)
+	if (!id->driver_data)
 		return -EINVAL;
 
 	if (!i2c_check_functionality(client->adapter, i2c_funcs))
 		return -ENOSYS;
 
-	chip = devm_kzalloc(&client->dev,
-		sizeof(struct sx150x_chip), GFP_KERNEL);
-	if (!chip)
+	pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
+	if (!pctl)
 		return -ENOMEM;
 
-	sx150x_init_chip(chip, client, id->driver_data, pdata);
-	rc = sx150x_init_hw(chip, pdata);
-	if (rc < 0)
-		return rc;
-
-	rc = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
-	if (rc)
-		return rc;
-
-	if (pdata->irq_summary >= 0) {
-		rc = sx150x_install_irq_chip(chip,
-					pdata->irq_summary,
-					pdata->irq_base);
-		if (rc < 0)
-			return rc;
+	pctl->dev = dev;
+	pctl->client = client;
+	pctl->data = (void *)id->driver_data;
+
+	mutex_init(&pctl->lock);
+
+	ret = sx150x_init_hw(pctl);
+	if (ret)
+		return ret;
+
+	/* Register GPIO controller */
+	pctl->gpio.label = devm_kstrdup(dev, client->name, GFP_KERNEL);
+	pctl->gpio.base = -1;
+	pctl->gpio.ngpio = pctl->data->npins;
+	pctl->gpio.get_direction = sx150x_gpio_get_direction;
+	pctl->gpio.direction_input = sx150x_gpio_direction_input;
+	pctl->gpio.direction_output = sx150x_gpio_direction_output;
+	pctl->gpio.get = sx150x_gpio_get;
+	pctl->gpio.set = sx150x_gpio_set;
+	pctl->gpio.set_single_ended = sx150x_gpio_set_single_ended;
+	pctl->gpio.parent = dev;
+#ifdef CONFIG_OF_GPIO
+	pctl->gpio.of_node = dev->of_node;
+#endif
+	pctl->gpio.can_sleep = true;
+
+	ret = devm_gpiochip_add_data(dev, &pctl->gpio, pctl);
+	if (ret)
+		return ret;
+
+	/* Add Interrupt support if an irq is specified */
+	if (client->irq > 0) {
+		pctl->irq_chip.name = devm_kstrdup(dev, client->name,
+						   GFP_KERNEL);
+		pctl->irq_chip.irq_mask = sx150x_irq_mask;
+		pctl->irq_chip.irq_unmask = sx150x_irq_unmask;
+		pctl->irq_chip.irq_set_type = sx150x_irq_set_type;
+		pctl->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
+		pctl->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
+
+		pctl->irq.masked = ~0;
+		pctl->irq.sense = 0;
+		pctl->irq.dev_masked = ~0;
+		pctl->irq.dev_sense = 0;
+		pctl->irq.update = -1;
+
+		ret = gpiochip_irqchip_add(&pctl->gpio,
+					   &pctl->irq_chip, 0,
+					   handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
+		if (ret) {
+			dev_err(dev, "could not connect irqchip to gpiochip\n");
+			return ret;
+		}
+
+		ret = devm_request_threaded_irq(dev, client->irq, NULL,
+						sx150x_irq_thread_fn,
+						IRQF_ONESHOT | IRQF_SHARED |
+						IRQF_TRIGGER_FALLING,
+						pctl->irq_chip.name, pctl);
+		if (ret < 0)
+			return ret;
 	}
 
-	i2c_set_clientdata(client, chip);
+	/* Pinctrl_desc */
+	pctl->pinctrl_desc.name = "sx150x-pinctrl";
+	pctl->pinctrl_desc.pctlops = &sx150x_pinctrl_ops;
+	pctl->pinctrl_desc.confops = &sx150x_pinconf_ops;
+	pctl->pinctrl_desc.pins = pctl->data->pins;
+	pctl->pinctrl_desc.npins = pctl->data->npins;
+	pctl->pinctrl_desc.owner = THIS_MODULE;
+
+	pctl->pctldev = pinctrl_register(&pctl->pinctrl_desc, dev, pctl);
+	if (IS_ERR(pctl->pctldev)) {
+		dev_err(dev, "Failed to register pinctrl device\n");
+		return PTR_ERR(pctl->pctldev);
+	}
 
 	return 0;
 }
 
 static struct i2c_driver sx150x_driver = {
 	.driver = {
-		.name = "sx150x",
+		.name = "sx150x-pinctrl",
 		.of_match_table = of_match_ptr(sx150x_of_match),
 	},
 	.probe    = sx150x_probe,