qcom: wcd934x: add pinctrl driver for wcd934x
WCD934X audio codec has a GPIO controller which can support 5 GPIO's, add pinctrl driver to handle GPIO's of wcd934x. CRs-Fixed: 1041199 Change-Id: I0489f9149cfd6ec7af056d074cb1869a705f9eff Signed-off-by: Yeleswarapu Nagaradhesh <nagaradh@codeaurora.org>
This commit is contained in:
parent
00f153a5b1
commit
8aa9aba222
5 changed files with 590 additions and 0 deletions
138
Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt
Normal file
138
Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -1572,6 +1572,7 @@ config WCD934X_CODEC
|
||||||
select MSM_CDC_SUPPLY
|
select MSM_CDC_SUPPLY
|
||||||
select MSM_CDC_PINCTRL
|
select MSM_CDC_PINCTRL
|
||||||
select REGMAP_ALLOW_WRITE_DEBUGFS
|
select REGMAP_ALLOW_WRITE_DEBUGFS
|
||||||
|
select PINCTRL_WCD
|
||||||
help
|
help
|
||||||
Enables the WCD9xxx codec core driver. The core driver provides
|
Enables the WCD9xxx codec core driver. The core driver provides
|
||||||
read/write capability to registers which are part of the
|
read/write capability to registers which are part of the
|
||||||
|
|
|
@ -120,4 +120,11 @@ config PINCTRL_MSMFALCON
|
||||||
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
|
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
|
||||||
Qualcomm TLMM block found in the Qualcomm MSMFALCON platform.
|
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
|
endif
|
||||||
|
|
|
@ -15,3 +15,4 @@ obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
|
||||||
obj-$(CONFIG_PINCTRL_MSM8996) += pinctrl-msm8996.o
|
obj-$(CONFIG_PINCTRL_MSM8996) += pinctrl-msm8996.o
|
||||||
obj-$(CONFIG_PINCTRL_MSMCOBALT) += pinctrl-msmcobalt.o
|
obj-$(CONFIG_PINCTRL_MSMCOBALT) += pinctrl-msmcobalt.o
|
||||||
obj-$(CONFIG_PINCTRL_MSMFALCON) += pinctrl-msmfalcon.o
|
obj-$(CONFIG_PINCTRL_MSMFALCON) += pinctrl-msmfalcon.o
|
||||||
|
obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o
|
||||||
|
|
443
drivers/pinctrl/qcom/pinctrl-wcd.c
Normal file
443
drivers/pinctrl/qcom/pinctrl-wcd.c
Normal 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");
|
Loading…
Add table
Reference in a new issue