Merge "qcom: wcd934x: add pinctrl driver for wcd934x"

This commit is contained in:
Linux Build Service Account 2016-07-19 18:47:59 -07:00 committed by Gerrit - the friendly Code Review server
commit 25a5fb85c0
7 changed files with 751 additions and 0 deletions

View file

@ -0,0 +1,138 @@
Qualcomm Technologies, Inc. WCD GPIO block
This binding describes the GPIO block found in the WCD934X series of
audio codec's from QTI.
- compatible:
Usage: required
Value type: <string>
Definition: must be "qcom,wcd-pinctrl"
- qcom,num-gpios:
Usage: required
Value type: <u32>
Definition: Number of GPIO's supported by the controller
- gpio-controller:
Usage: required
Value type: <none>
Definition: Mark the device node as a GPIO controller
- #gpio-cells:
Usage: required
Value type: <u32>
Definition: Must be 2;
the first cell will be used to define gpio number and the
second denotes the flags for this gpio
Please refer to ../gpio/gpio.txt for a general description of GPIO bindings.
Please refer to pinctrl-bindings.txt in this directory for details of the
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
The pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin or a list of pins. This configuration can include the
mux function to select on those pin(s), and various pin configuration
parameters, as listed below.
SUBNODES:
The name of each subnode is not important; all subnodes should be enumerated
and processed purely based on their content.
Each subnode only affects those parameters that are explicitly listed. In
other words, a subnode that lists a mux function but no pin configuration
parameters implies no information about any pin configuration parameters.
Similarly, a pin subnode that describes a pullup parameter implies no
information about e.g. the mux function.
The following generic properties as defined in pinctrl-bindings.txt are valid
to specify in a pin configuration subnode:
- pins:
Usage: required
Value type: <string-array>
Definition: List of gpio pins affected by the properties specified in
this subnode. Valid pins are:
gpio1-gpio5 for wcd9340
- bias-disable:
Usage: optional
Value type: <none>
Definition: The specified pins should be configured as no pull.
- bias-pull-down:
Usage: optional
Value type: <none>
Definition: The specified pins should be configured as pull down.
- bias-pull-up:
Usage: optional
Value type: <empty>
Definition: The specified pins should be configured as pull up.
- qcom,pull-up-strength:
Usage: optional
Value type: <u32>
Definition: Specifies the strength to use for pull up, if selected.
- bias-high-impedance:
Usage: optional
Value type: <none>
Definition: The specified pins will put in high-Z mode and disabled.
- input-enable:
Usage: optional
Value type: <none>
Definition: The specified pins are put in input mode.
- output-high:
Usage: optional
Value type: <none>
Definition: The specified pins are configured in output mode, driven
high.
- output-low:
Usage: optional
Value type: <none>
Definition: The specified pins are configured in output mode, driven
low.
- qcom,drive-strength:
Usage: optional
Value type: <u32>
Definition: Selects the drive strength for the specified pins.
Example:
wcd: wcd_pinctrl@5 {
compatible = "qcom,wcd-pinctl";
qcom,num-gpios = <5>
gpio-controller;
#gpio-cells = <2>;
spkr_1_wcd_en_active: spkr_1_wcd_en_active {
mux {
pins = "gpio2";
};
config {
pins = "gpio2";
output-high;
};
};
spkr_1_wcd_en_sleep: spkr_1_wcd_en_sleep {
mux {
pins = "gpio2";
};
config {
pins = "gpio2";
input-enable;
};
};
};

View file

@ -109,6 +109,74 @@
"SpkrLeft", "SpkrRight";
};
sound-tavil {
compatible = "qcom,msmcobalt-asoc-snd-tavil";
qcom,model = "msmcobalt-tavil-snd-card";
qcom,audio-routing =
"RX_BIAS", "MCLK",
"AMIC2", "MIC BIAS2",
"MIC BIAS2", "Headset Mic",
"AMIC3", "MIC BIAS2",
"MIC BIAS2", "ANCRight Headset Mic",
"AMIC4", "MIC BIAS2",
"MIC BIAS2", "ANCLeft Headset Mic",
"AMIC5", "MIC BIAS3",
"MIC BIAS3", "Handset Mic",
"DMIC0", "MIC BIAS1",
"MIC BIAS1", "Digital Mic0",
"DMIC1", "MIC BIAS1",
"MIC BIAS1", "Digital Mic1",
"DMIC2", "MIC BIAS3",
"MIC BIAS3", "Digital Mic2",
"DMIC3", "MIC BIAS3",
"MIC BIAS3", "Digital Mic3",
"DMIC4", "MIC BIAS4",
"MIC BIAS4", "Digital Mic4",
"DMIC5", "MIC BIAS4",
"MIC BIAS4", "Digital Mic5",
"SpkrLeft IN", "SPK1 OUT",
"SpkrRight IN", "SPK2 OUT";
qcom,msm-mbhc-hphl-swh = <0>;
qcom,msm-mbhc-gnd-swh = <0>;
qcom,tavil-mclk-clk-freq = <9600000>;
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
"msm-pcm-routing", "msm-cpe-lsm",
"msm-compr-dsp";
asoc-cpu = <&dai_hdmi>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
<&afe_proxy_tx>, <&incall_record_rx>,
<&incall_record_tx>, <&incall_music_rx>,
<&incall_music_2_rx>, <&sb_5_rx>,
<&usb_audio_rx>, <&usb_audio_tx>;
asoc-cpu-names = "msm-dai-q6-hdmi.8",
"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394",
"msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673";
asoc-codec = <&stub_codec>;
asoc-codec-names = "msm-stub-codec.1";
qcom,wsa-max-devs = <0>;
};
cpe: qcom,msm-cpe-lsm {
compatible = "qcom,msm-cpe-lsm";
};
@ -141,6 +209,15 @@
#clock-cells = <1>;
};
clock_audio_lnbb: audio_ext_clk_lnbb {
status = "ok";
compatible = "qcom,audio-ref-clk";
clock-names = "osr_clk";
clocks = <&clock_gcc clk_ln_bb_clk2>;
qcom,node_has_rpm_clock;
#clock-cells = <1>;
};
wcd_rst_gpio: msm_cdc_pinctrl@64 {
compatible = "qcom,msm-cdc-pinctrl";
qcom,cdc-rst-n-gpio = <&tlmm 64 0>;
@ -208,4 +285,56 @@
qcom,cdc-dmic-sample-rate = <4800000>;
qcom,cdc-mad-dmic-rate = <600000>;
};
tavil_codec {
compatible = "qcom,tavil-slim-pgd";
elemental-addr = [00 01 50 02 17 02];
interrupt-parent = <&wcd9xxx_intc>;
interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24 25 26 27 28 29
30 31>;
qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
clock-names = "wcd_clk";
clocks = <&clock_audio_lnbb clk_audio_pmi_lnbb_clk>;
cdc-vdd-buck-supply = <&pmcobalt_s4>;
qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
qcom,cdc-vdd-buck-current = <650000>;
cdc-buck-sido-supply = <&pmcobalt_s4>;
qcom,cdc-buck-sido-voltage = <1800000 1800000>;
qcom,cdc-buck-sido-current = <250000>;
cdc-vdd-tx-h-supply = <&pmcobalt_s4>;
qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
qcom,cdc-vdd-tx-h-current = <25000>;
cdc-vdd-rx-h-supply = <&pmcobalt_s4>;
qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
qcom,cdc-vdd-rx-h-current = <25000>;
cdc-vddpx-1-supply = <&pmcobalt_s4>;
qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
qcom,cdc-vddpx-1-current = <10000>;
qcom,cdc-static-supplies = "cdc-vdd-buck",
"cdc-buck-sido",
"cdc-vdd-tx-h",
"cdc-vdd-rx-h",
"cdc-vddpx-1";
qcom,cdc-micbias1-mv = <1800>;
qcom,cdc-micbias2-mv = <1800>;
qcom,cdc-micbias3-mv = <1800>;
qcom,cdc-micbias4-mv = <1800>;
qcom,cdc-mclk-clk-rate = <9600000>;
qcom,cdc-slim-ifd = "tavil-slim-ifd";
qcom,cdc-slim-ifd-elemental-addr = [00 00 50 02 17 02];
qcom,cdc-dmic-sample-rate = <4800000>;
qcom,cdc-mad-dmic-rate = <600000>;
};
};

View file

@ -60,4 +60,36 @@
};
};
};
tavil_codec {
swr_master {
compatible = "qcom,swr-wcd";
#address-cells = <2>;
#size-cells = <0>;
wsa881x_0211: wsa881x@20170211 {
compatible = "qcom,wsa881x";
reg = <0x00 0x20170211>;
qcom,spkr-sd-n-node = <&wsa_spkr_sd1>;
};
wsa881x_0212: wsa881x@20170212 {
compatible = "qcom,wsa881x";
reg = <0x00 0x20170212>;
qcom,spkr-sd-n-node = <&wsa_spkr_sd2>;
};
wsa881x_0213: wsa881x@21170213 {
compatible = "qcom,wsa881x";
reg = <0x00 0x21170213>;
qcom,spkr-sd-n-node = <&wsa_spkr_sd1>;
};
wsa881x_0214: wsa881x@21170214 {
compatible = "qcom,wsa881x";
reg = <0x00 0x21170214>;
qcom,spkr-sd-n-node = <&wsa_spkr_sd2>;
};
};
};
};

View file

@ -1572,6 +1572,7 @@ config WCD934X_CODEC
select MSM_CDC_SUPPLY
select MSM_CDC_PINCTRL
select REGMAP_ALLOW_WRITE_DEBUGFS
select PINCTRL_WCD
help
Enables the WCD9xxx codec core driver. The core driver provides
read/write capability to registers which are part of the

View file

@ -120,4 +120,11 @@ config PINCTRL_MSMFALCON
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm TLMM block found in the Qualcomm MSMFALCON platform.
config PINCTRL_WCD
tristate "Qualcomm Technologies, Inc WCD pin controller driver"
depends on WCD934X_CODEC
help
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
WCD gpio controller block.
endif

View file

@ -15,3 +15,4 @@ obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
obj-$(CONFIG_PINCTRL_MSM8996) += pinctrl-msm8996.o
obj-$(CONFIG_PINCTRL_MSMCOBALT) += pinctrl-msmcobalt.o
obj-$(CONFIG_PINCTRL_MSMFALCON) += pinctrl-msmfalcon.o
obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o

View file

@ -0,0 +1,443 @@
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mfd/wcd934x/registers.h>
#include "../core.h"
#include "../pinctrl-utils.h"
#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE
#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA
#define WCD_GPIO_PULL_UP 1
#define WCD_GPIO_PULL_DOWN 2
#define WCD_GPIO_BIAS_DISABLE 3
#define WCD_GPIO_STRING_LEN 20
/**
* struct wcd_gpio_pad - keep current GPIO settings
* @offset: offset of gpio.
* @is_valid: Set to false, when GPIO in high Z state.
* @value: value of a pin
* @output_enabled: Set to true if GPIO is output and false if it is input
* @pullup: Constant current which flow through GPIO output buffer.
* @strength: Drive strength of a pin
*/
struct wcd_gpio_pad {
u16 offset;
bool is_valid;
bool value;
bool output_enabled;
unsigned int pullup;
unsigned int strength;
};
struct wcd_gpio_priv {
struct device *dev;
struct regmap *map;
struct pinctrl_dev *ctrl;
struct gpio_chip chip;
};
static inline struct wcd_gpio_priv *to_gpio_state(struct gpio_chip *chip)
{
return container_of(chip, struct wcd_gpio_priv, chip);
};
static int wcd_gpio_read(struct wcd_gpio_priv *priv_data,
struct wcd_gpio_pad *pad, unsigned int addr)
{
unsigned int val;
int ret;
ret = regmap_read(priv_data->map, addr, &val);
if (ret < 0)
dev_err(priv_data->dev, "%s: read 0x%x failed\n",
__func__, addr);
else
ret = (val >> pad->offset);
return ret;
}
static int wcd_gpio_write(struct wcd_gpio_priv *priv_data,
struct wcd_gpio_pad *pad, unsigned int addr,
unsigned int val)
{
int ret;
ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset),
val << pad->offset);
if (ret < 0)
dev_err(priv_data->dev, "write 0x%x failed\n", addr);
return ret;
}
static int wcd_get_groups_count(struct pinctrl_dev *pctldev)
{
return pctldev->desc->npins;
}
static const char *wcd_get_group_name(struct pinctrl_dev *pctldev,
unsigned pin)
{
return pctldev->desc->pins[pin].name;
}
static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin,
const unsigned **pins, unsigned *num_pins)
{
*pins = &pctldev->desc->pins[pin].number;
*num_pins = 1;
return 0;
}
static const struct pinctrl_ops wcd_pinctrl_ops = {
.get_groups_count = wcd_get_groups_count,
.get_group_name = wcd_get_group_name,
.get_group_pins = wcd_get_group_pins,
.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
.dt_free_map = pinctrl_utils_dt_free_map,
};
static int wcd_config_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
unsigned param = pinconf_to_config_param(*config);
struct wcd_gpio_pad *pad;
unsigned arg;
pad = pctldev->desc->pins[pin].drv_data;
switch (param) {
case PIN_CONFIG_BIAS_PULL_DOWN:
arg = pad->pullup == WCD_GPIO_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_DISABLE:
arg = pad->pullup = WCD_GPIO_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_PULL_UP:
arg = pad->pullup == WCD_GPIO_PULL_UP;
break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
arg = !pad->is_valid;
break;
case PIN_CONFIG_INPUT_ENABLE:
arg = pad->output_enabled;
break;
case PIN_CONFIG_OUTPUT:
arg = pad->value;
break;
default:
return -EINVAL;
}
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *configs, unsigned nconfs)
{
struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev);
struct wcd_gpio_pad *pad;
unsigned param, arg;
int i, ret;
pad = pctldev->desc->pins[pin].drv_data;
for (i = 0; i < nconfs; i++) {
param = pinconf_to_config_param(configs[i]);
arg = pinconf_to_config_argument(configs[i]);
dev_dbg(priv_data->dev, "%s: param: %d arg: %d",
__func__, param, arg);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
pad->pullup = WCD_GPIO_BIAS_DISABLE;
break;
case PIN_CONFIG_BIAS_PULL_UP:
pad->pullup = WCD_GPIO_PULL_UP;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
pad->pullup = WCD_GPIO_PULL_DOWN;
break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
pad->is_valid = false;
break;
case PIN_CONFIG_INPUT_ENABLE:
pad->output_enabled = false;
break;
case PIN_CONFIG_OUTPUT:
pad->output_enabled = true;
pad->value = arg;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
pad->strength = arg;
break;
default:
ret = -EINVAL;
goto done;
}
}
if (pad->output_enabled) {
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
pad->output_enabled);
if (ret < 0)
goto done;
ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL,
pad->value);
} else
ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL,
pad->output_enabled);
done:
return ret;
}
static const struct pinconf_ops wcd_pinconf_ops = {
.is_generic = true,
.pin_config_group_get = wcd_config_get,
.pin_config_group_set = wcd_config_set,
};
static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
{
struct wcd_gpio_priv *priv_data = to_gpio_state(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
}
static int wcd_gpio_direction_output(struct gpio_chip *chip,
unsigned pin, int val)
{
struct wcd_gpio_priv *priv_data = to_gpio_state(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
return wcd_config_set(priv_data->ctrl, pin, &config, 1);
}
static int wcd_gpio_get(struct gpio_chip *chip, unsigned pin)
{
struct wcd_gpio_priv *priv_data = to_gpio_state(chip);
struct wcd_gpio_pad *pad;
int value;
pad = priv_data->ctrl->desc->pins[pin].drv_data;
if (!pad->is_valid)
return -EINVAL;
value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL);
return value;
}
static void wcd_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
{
struct wcd_gpio_priv *priv_data = to_gpio_state(chip);
unsigned long config;
config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
wcd_config_set(priv_data->ctrl, pin, &config, 1);
}
static const struct gpio_chip wcd_gpio_chip = {
.direction_input = wcd_gpio_direction_input,
.direction_output = wcd_gpio_direction_output,
.get = wcd_gpio_get,
.set = wcd_gpio_set,
};
static int wcd_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pinctrl_pin_desc *pindesc;
struct pinctrl_desc *pctrldesc;
struct wcd_gpio_pad *pad, *pads;
struct wcd_gpio_priv *priv_data;
int ret, i, j;
u32 npins;
char **name;
ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins);
if (ret) {
dev_err(dev, "%s: Looking up %s property in node %s failed\n",
__func__, "qcom,num-gpios", dev->of_node->full_name);
ret = -EINVAL;
goto err_priv_alloc;
}
if (!npins) {
dev_err(dev, "%s: no.of pins are 0\n", __func__);
ret = -EINVAL;
goto err_priv_alloc;
}
priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL);
if (!priv_data) {
ret = -ENOMEM;
goto err_priv_alloc;
}
priv_data->dev = dev;
priv_data->map = dev_get_regmap(dev->parent, NULL);
if (!priv_data->map) {
dev_err(dev, "%s: failed to get regmap\n", __func__);
ret = -EINVAL;
goto err_regmap;
}
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
if (!pindesc) {
ret = -ENOMEM;
goto err_pinsec_alloc;
}
pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
if (!pads) {
ret = -ENOMEM;
goto err_pads_alloc;
}
pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
if (!pctrldesc) {
ret = -ENOMEM;
goto err_pinctrl_alloc;
}
pctrldesc->pctlops = &wcd_pinctrl_ops;
pctrldesc->confops = &wcd_pinconf_ops;
pctrldesc->owner = THIS_MODULE;
pctrldesc->name = dev_name(dev);
pctrldesc->pins = pindesc;
pctrldesc->npins = npins;
name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err_name_alloc;
}
for (i = 0; i < npins; i++, pindesc++) {
name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN,
GFP_KERNEL);
if (!name[i]) {
ret = -ENOMEM;
goto err_pin;
}
pad = &pads[i];
pindesc->drv_data = pad;
pindesc->number = i;
snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1));
pindesc->name = name[i];
pad->offset = i;
pad->is_valid = true;
}
priv_data->chip = wcd_gpio_chip;
priv_data->chip.dev = dev;
priv_data->chip.base = -1;
priv_data->chip.ngpio = npins;
priv_data->chip.label = dev_name(dev);
priv_data->chip.of_gpio_n_cells = 2;
priv_data->chip.can_sleep = false;
priv_data->ctrl = pinctrl_register(pctrldesc, dev, priv_data);
if (IS_ERR(priv_data->ctrl)) {
dev_err(dev, "%s: failed to register to pinctrl\n", __func__);
ret = PTR_ERR(priv_data->ctrl);
goto err_pin;
}
ret = gpiochip_add(&priv_data->chip);
if (ret) {
dev_err(dev, "%s: can't add gpio chip\n", __func__);
goto err_chip;
}
ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0,
npins);
if (ret) {
dev_err(dev, "%s: failed to add pin range\n", __func__);
goto err_range;
}
platform_set_drvdata(pdev, priv_data);
return 0;
err_range:
gpiochip_remove(&priv_data->chip);
err_chip:
pinctrl_unregister(priv_data->ctrl);
err_pin:
for (j = 0; j < i; j++)
devm_kfree(dev, name[j]);
devm_kfree(dev, name);
err_name_alloc:
devm_kfree(dev, pctrldesc);
err_pinctrl_alloc:
devm_kfree(dev, pads);
err_pads_alloc:
devm_kfree(dev, pindesc);
err_pinsec_alloc:
err_regmap:
devm_kfree(dev, priv_data);
err_priv_alloc:
return ret;
}
static int wcd_pinctrl_remove(struct platform_device *pdev)
{
struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev);
gpiochip_remove(&priv_data->chip);
pinctrl_unregister(priv_data->ctrl);
return 0;
}
static const struct of_device_id wcd_pinctrl_of_match[] = {
{ .compatible = "qcom,wcd-pinctrl" },
{ },
};
MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match);
static struct platform_driver wcd_pinctrl_driver = {
.driver = {
.name = "qcom-wcd-pinctrl",
.of_match_table = wcd_pinctrl_of_match,
},
.probe = wcd_pinctrl_probe,
.remove = wcd_pinctrl_remove,
};
module_platform_driver(wcd_pinctrl_driver);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver");
MODULE_LICENSE("GPL v2");