Merge "qcom: wcd934x: add pinctrl driver for wcd934x"
This commit is contained in:
commit
25a5fb85c0
7 changed files with 751 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;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
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