Merge changes I04eb39d9,If3ce2aaa into msm-3.0
* changes: msm: qpnp: Add gpiolib support for PMIC GPIOs of: of_spmi: Add support for spmi-dev-container binding
This commit is contained in:
committed by
QuIC Gerrit Code Review
commit
c17ab9bb48
142
Documentation/devicetree/bindings/spmi/msm-qpnp-gpio.txt
Normal file
142
Documentation/devicetree/bindings/spmi/msm-qpnp-gpio.txt
Normal file
@@ -0,0 +1,142 @@
|
||||
* msm-qpnp-gpio
|
||||
|
||||
msm-qpnp-gpio is a GPIO chip driver for the MSM SPMI implementation.
|
||||
It creates a spmi_device for every spmi-dev-container block of device_nodes.
|
||||
These device_nodes contained within specify the PMIC GPIO number associated
|
||||
with each GPIO chip. The driver will map these to Linux GPIO numbers.
|
||||
|
||||
[PMIC GPIO Device Declarations]
|
||||
|
||||
-Root Node-
|
||||
|
||||
Required properties :
|
||||
- spmi-dev-container : Used to specify the following child nodes as part of the
|
||||
same SPMI device.
|
||||
- gpio-controller : Specify as gpio-contoller. All child nodes will belong to this
|
||||
gpio_chip.
|
||||
- #gpio-cells: We encode a PMIC GPIO number and a 32-bit flag field to
|
||||
specify the gpio configuration. This must be set to '2'.
|
||||
- #address-cells: Specify one address field. This must be set to '1'.
|
||||
- #size-cells: Specify one size-cell. This must be set to '1'.
|
||||
- compatible = "qcom,qpnp-gpio" : Specify driver matching for this driver.
|
||||
|
||||
-Child Nodes-
|
||||
|
||||
Required properties :
|
||||
- reg : Specify the spmi offset and size for this gpio device.
|
||||
- qcom,qpnp-gpio-num : Specify the PMIC GPIO number for this gpio device.
|
||||
|
||||
Optional properties :
|
||||
- qcom,qpnp-gpio-cfg : Specify the PMIC gpio configuration.
|
||||
The format of this configuration is specified in a tuple of 9 entries.
|
||||
These entries should be specified in the same order as the entries listed
|
||||
in this following discription.
|
||||
|
||||
@direction: indicates whether the gpio should be input, output, or
|
||||
both.
|
||||
QPNP_GPIO_DIR_OUT = 1,
|
||||
QPNP_GPIO_DIR_IN = 2,
|
||||
QPNP_GPIO_DIR_BOTH = 3
|
||||
|
||||
@output_type: indicates gpio should be configured as CMOS or open
|
||||
drain.
|
||||
QPNP_GPIO_OUT_BUF_OPEN_DRAIN = 1,
|
||||
QPNP_GPIO_OUT_BUF_CMOS = 0
|
||||
|
||||
@output_value: The gpio output value of the gpio line - 0 or 1
|
||||
@pull: Indicates whether a pull up or pull down should be
|
||||
applied. If a pullup is required the current strength
|
||||
needs to be specified. Current values of 30uA, 1.5uA,
|
||||
31.5uA, 1.5uA with 30uA boost are supported.
|
||||
QPNP_GPIO_PULL_UP_30 = 0,
|
||||
QPNP_GPIO_PULL_UP_1P5 = 1,
|
||||
QPNP_GPIO_PULL_UP_31P5 = 2,
|
||||
QPNP_GPIO_PULL_UP_1P5_30 = 3,
|
||||
QPNP_GPIO_PULL_DN = 4,
|
||||
QPNP_GPIO_PULL_NO = 5
|
||||
|
||||
@vin_sel: specifies the voltage level when the output is set to 1.
|
||||
For an input gpio specifies the voltage level at which
|
||||
the input is interpreted as a logical 1.
|
||||
QPNP_GPIO_VIN0 = 0,
|
||||
QPNP_GPIO_VIN1 = 1,
|
||||
QPNP_GPIO_VIN2 = 2,
|
||||
QPNP_GPIO_VIN3 = 3,
|
||||
QPNP_GPIO_VIN4 = 4,
|
||||
QPNP_GPIO_VIN5 = 5,
|
||||
QPNP_GPIO_VIN6 = 6,
|
||||
QPNP_GPIO_VIN7 = 7
|
||||
|
||||
@out_strength: the amount of current supplied for an output gpio.
|
||||
QPNP_GPIO_OUT_STRENGTH_HIGH = 1,
|
||||
QPNP_GPIO_OUT_STRENGTH_MED = 2,
|
||||
QPNP_GPIO_OUT_STRENGTH_LOW = 3
|
||||
|
||||
@source_sel: choose alternate function for the gpio. Certain gpios
|
||||
can be paired (shorted) with each other. Some gpio pin
|
||||
can act as alternate functions.
|
||||
QPNP_GPIO_FUNC_NORMAL = 0,
|
||||
QPNP_GPIO_FUNC_PAIRED = 1
|
||||
QPNP_GPIO_FUNC_1 = 2,
|
||||
QPNP_GPIO_FUNC_3 = 3,
|
||||
QPNP_GPIO_DTEST1 = 4,
|
||||
QPNP_GPIO_DTEST2 = 5,
|
||||
QPNP_GPIO_DTEST3 = 6,
|
||||
QPNP_GPIO_DTEST4 = 7
|
||||
|
||||
@inv_int_pol: Invert polarity before feeding the line to the interrupt
|
||||
module in pmic. This feature will almost be never used
|
||||
since the pm8xxx interrupt block can detect both edges
|
||||
and both levels.
|
||||
@master_en: 1 = Enable features within the GPIO block based on
|
||||
configurations.
|
||||
0 = Completely disable the GPIO block and let the pin
|
||||
float with high impedance regardless of other settings.
|
||||
|
||||
[PMIC GPIO clients]
|
||||
|
||||
Required properties :
|
||||
- gpios : Contains 3 fields of the form <&gpio_controller pmic_gpio_num flags>
|
||||
|
||||
[Example]
|
||||
|
||||
qpnp: qcom,spmi@fc4c0000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
|
||||
qcom,pm8941@0 {
|
||||
spmi-slave-container;
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
pm8941_gpios: gpios {
|
||||
spmi-dev-container;
|
||||
compatible = "qcom,qpnp-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
qcom,pm8941_gpio1@0xc000 {
|
||||
reg = <0xc000 0x100>;
|
||||
qcom,qpnp-gpio-num = <62>;
|
||||
};
|
||||
|
||||
qcom,pm8941_gpio2@0xc100 {
|
||||
reg = <0xc100 0x100>;
|
||||
qcom,qpnp-gpio-num = <20>;
|
||||
qcom,qpnp-gpio-cfg = <0x1 0x1 0x1 0x2 0x3 0x2 0x0 0x0 0x1>;
|
||||
};
|
||||
};
|
||||
|
||||
qcom,testgpio@1000 {
|
||||
compatible = "qcom,qpnp-testgpio";
|
||||
reg = <0x1000 0x1000>;
|
||||
gpios = <&pm8941_gpios 62 0x0 &pm8941_gpios 20 0x1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,13 +1,28 @@
|
||||
* SPMI
|
||||
|
||||
The spmi Device Tree support interprets up to two levels of Device Tree
|
||||
The SPMI Device Tree support interprets up to three levels of Device Tree
|
||||
topology. The first level is required and specifies only a slave address.
|
||||
The second level is optional and allows for the specification of different
|
||||
offsets within the same 16-bit address space underneath a particular SPMI
|
||||
slave ID. Within the second level, any number of address ranges can be
|
||||
associated with a particular device within that 16-bit range.
|
||||
device nodes within the same 16-bit address space underneath a particular
|
||||
SPMI slave ID. Within the second level, any number of address ranges can be
|
||||
associated with a particular device within that 16-bit range. An additional
|
||||
flag allows for the possiblity to specify that all device nodes should
|
||||
have their resources dedicated to only one spmi_device. This flag can
|
||||
be specified at the second level, or an optional third level. By default
|
||||
without this flag, one spmi_device is created for each device_node.
|
||||
|
||||
First level
|
||||
[Root Node]
|
||||
|
||||
Recommended properties :
|
||||
- interrupt-controller : Used to specify the root node as the
|
||||
interrupt controller for SPMI devices.
|
||||
- #interrupt-cells : The number of cells used to express one interrupt.
|
||||
|
||||
Notes :
|
||||
- It is considered an error to include either spmi-container-dev or
|
||||
spmi-slave-dev in the Root Node.
|
||||
|
||||
[First Level Nodes]
|
||||
|
||||
Required properites :
|
||||
|
||||
@@ -22,51 +37,124 @@ Recommended properties :
|
||||
- interrupt-parent : the phandle for the interrupt controller that
|
||||
services interrupts for this device.
|
||||
|
||||
Second level
|
||||
[Second Level Nodes]
|
||||
|
||||
Required properties :
|
||||
- spmi-slave-container: Used by the parser to understand that this is the
|
||||
second level of the tree.
|
||||
second level of the tree that includes device nodes associated with the
|
||||
same slave_id.
|
||||
- reg: <a b> where a is < 65536 and b is a size. Each device supports an
|
||||
arbitrary number of address ranges.
|
||||
- compatible : "qcom," prefixed string to match against the driver.
|
||||
|
||||
Recommended properties :
|
||||
|
||||
- interrupts : <a b c> where a is the slave ID, b is is the peripheral ID,
|
||||
- interrupts : <a b c> where a is the slave ID, b is the peripheral ID,
|
||||
c is the device interrupt number (0-7). Each device supports any arbitrary
|
||||
number of interrupts.
|
||||
- interrupt-parent : the phandle for the interrupt controller that
|
||||
services interrupts for this device.
|
||||
|
||||
Example:
|
||||
Optional properties :
|
||||
|
||||
- spmi-dev-container: This specifies that all the device nodes specified for
|
||||
this slave_id should have their resources coalesced into only one
|
||||
spmi_device.
|
||||
|
||||
[Third Level Nodes]
|
||||
|
||||
Required properties :
|
||||
|
||||
- spmi-dev-container: This specifies that all the device nodes specified for
|
||||
this slave_id should have their resources coalesced into only one
|
||||
spmi_device.
|
||||
- reg: <a b> where a is < 65536 and b is a size. Each device supports an
|
||||
arbitrary number of address ranges.
|
||||
- compatible : "qcom," prefixed string to match against the driver.
|
||||
|
||||
Recommended properties :
|
||||
|
||||
- interrupts : <a b c> where a is the slave ID, b is the peripheral ID,
|
||||
c is the device interrupt number (0-7). Each device supports any arbitrary
|
||||
number of interrupts.
|
||||
- interrupt-parent : the phandle for the interrupt controller that
|
||||
services interrupts for this device.
|
||||
|
||||
Notes :
|
||||
- It is considered an error to include spmi-slave-dev at this level.
|
||||
|
||||
[Example]
|
||||
|
||||
/ {
|
||||
qcom,spmi@fc4c0000 {
|
||||
qpnp: qcom,spmi@fc4c0000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupt-parent = <&qpnpint>;
|
||||
pmic8941@d {
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
|
||||
testint@f {
|
||||
interrupt-parent = <&qpnp>;
|
||||
compatible = "qcom,qpnp-testint";
|
||||
reg = <0xf>;
|
||||
interrupts = <0x3 0x15 0x0 0x3 0x15 0x02 0x1 0x47 0x0>;
|
||||
|
||||
};
|
||||
|
||||
pm8941@0 {
|
||||
spmi-slave-container;
|
||||
reg = <0x0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0xd>;
|
||||
|
||||
pm8941_gpios: gpios {
|
||||
spmi-dev-container;
|
||||
compatible = "qcom,qpnp-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
pm8941_gpio1@0xc000 {
|
||||
compatible = "qcom,qpnp-gpio";
|
||||
reg = <0xc000 0x100>;
|
||||
qcom,qpnp_gpio = <1>;
|
||||
interrupt-parent = <&qpnp>;
|
||||
interrupts = <0x3 0x15 0x02 0x1 0x47 0x0>;
|
||||
};
|
||||
|
||||
pm8941_gpio2@0xc100 {
|
||||
compatible = "qcom,qpnp-gpio";
|
||||
reg = <0xc100 0x100>;
|
||||
qcom,qpnp_gpio = <2>;
|
||||
interrupt-parent = <&qpnp>;
|
||||
interrupts = <0x3 0x15 0x0>;
|
||||
};
|
||||
};
|
||||
|
||||
testgpio@0x1000 {
|
||||
compatible = "qcom,qpnp-testgpio";
|
||||
reg = <0x1000 0x1000>;
|
||||
qpnp-gpios = <&pm8941_gpios 0x0>;
|
||||
};
|
||||
};
|
||||
pm8841@2 {
|
||||
spmi-slave-container;
|
||||
|
||||
coincell@2800 {
|
||||
compatible = "qcom,qpnp-coincell";
|
||||
reg = <0x2800 0x4000>;
|
||||
interrupts = <0xd 0x28 0x6 0xd 0x28 0x3>;
|
||||
|
||||
};
|
||||
pon@800 {
|
||||
compatible = "qcom,qpnp-pon";
|
||||
reg = <0x800 0x4000>;
|
||||
};
|
||||
};
|
||||
customer_dev@2 {
|
||||
compatible = "qcom,qpnp-pon";
|
||||
reg = <0x2>;
|
||||
interrupts = <0x2 0x08 0x1 0x2 0x8 0x3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
spmi-dev-container;
|
||||
compatible = "qcom,qpnp-gpio";
|
||||
|
||||
pm8841_gpio1@0xc000 {
|
||||
reg = <0xc000 0x100>;
|
||||
qcom,qpnp_gpio = <1>;
|
||||
};
|
||||
|
||||
pm8841_gpio2@0xc100 {
|
||||
reg = <0xc100 0x100>;
|
||||
qcom,qpnp_gpio = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -477,4 +477,12 @@ config GPIO_PM8XXX_RPC
|
||||
This option enables support for on-chip GPIO found on Qualcomm PM8xxx
|
||||
PMICs through RPC.
|
||||
|
||||
config GPIO_QPNP
|
||||
depends on ARCH_MSMCOPPER
|
||||
depends on OF_SPMI
|
||||
depends on MSM_QPNP_INT
|
||||
tristate "Qualcomm QPNP GPIO support"
|
||||
help
|
||||
Say 'y' here to include support for the Qualcomm QPNP gpio
|
||||
support. QPNP is a SPMI based PMIC implementation.
|
||||
endif
|
||||
|
||||
@@ -51,3 +51,4 @@ obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
|
||||
obj-$(CONFIG_GPIO_PM8XXX) += pm8xxx-gpio.o
|
||||
obj-$(CONFIG_GPIO_PM8XXX_MPP) += pm8xxx-mpp.o
|
||||
obj-$(CONFIG_GPIO_PM8XXX_RPC) += gpio-pm8xxx-rpc.o
|
||||
obj-$(CONFIG_GPIO_QPNP) += qpnp-gpio.o
|
||||
|
||||
706
drivers/gpio/qpnp-gpio.c
Normal file
706
drivers/gpio/qpnp-gpio.c
Normal file
@@ -0,0 +1,706 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/qpnp/gpio.h>
|
||||
|
||||
#include <mach/qpnp.h>
|
||||
|
||||
#define Q_REG_ADDR(q_spec, reg_index) \
|
||||
((q_spec)->offset + reg_index)
|
||||
|
||||
#define Q_REG_STATUS1 0x8
|
||||
#define Q_NUM_CTL_REGS 5
|
||||
|
||||
/* control register base address offsets */
|
||||
#define Q_REG_IO_CTL1 0x42
|
||||
#define Q_REG_INPUT_CTL1 0x43
|
||||
#define Q_REG_OUTPUT_CTL1 0x44
|
||||
#define Q_REG_OUTPUT_CTL2 0x45
|
||||
#define Q_REG_EN_CTL1 0x46
|
||||
|
||||
/* control register regs array indices */
|
||||
#define Q_REG_I_IO_CTL1 0
|
||||
#define Q_REG_I_INPUT_CTL1 1
|
||||
#define Q_REG_I_OUTPUT_CTL1 2
|
||||
#define Q_REG_I_OUTPUT_CTL2 3
|
||||
#define Q_REG_I_EN_CTL1 4
|
||||
|
||||
/* control register configuration */
|
||||
#define Q_REG_VIN_SHIFT 0
|
||||
#define Q_REG_VIN_MASK 0x7
|
||||
#define Q_REG_PULL_SHIFT 4
|
||||
#define Q_REG_PULL_MASK 0x70
|
||||
#define Q_REG_INPUT_EN_SHIFT 7
|
||||
#define Q_REG_INPUT_EN_MASK 0x80
|
||||
#define Q_REG_OUT_STRENGTH_SHIFT 0
|
||||
#define Q_REG_OUT_STRENGTH_MASK 0x3
|
||||
#define Q_REG_OUT_TYPE_SHIFT 6
|
||||
#define Q_REG_OUT_TYPE_MASK 0x40
|
||||
#define Q_REG_OUT_INVERT_SHIFT 0
|
||||
#define Q_REG_OUT_INVERT_MASK 0x1
|
||||
#define Q_REG_SRC_SEL_SHIFT 1
|
||||
#define Q_REG_SRC_SEL_MASK 0xE
|
||||
#define Q_REG_OUTPUT_EN_SHIFT 7
|
||||
#define Q_REG_OUTPUT_EN_MASK 0x80
|
||||
#define Q_REG_MASTER_EN_SHIFT 7
|
||||
#define Q_REG_MASTER_EN_MASK 0x80
|
||||
|
||||
|
||||
struct qpnp_gpio_spec {
|
||||
uint8_t slave; /* 0-15 */
|
||||
uint16_t offset; /* 0-255 */
|
||||
uint32_t gpio_chip_idx; /* offset from gpio_chip base */
|
||||
int irq; /* logical IRQ number */
|
||||
u8 regs[Q_NUM_CTL_REGS]; /* Control regs */
|
||||
};
|
||||
|
||||
struct qpnp_gpio_chip {
|
||||
struct gpio_chip gpio_chip;
|
||||
struct spmi_device *spmi;
|
||||
struct qpnp_gpio_spec **pmic_gpios;
|
||||
struct qpnp_gpio_spec **chip_gpios;
|
||||
uint32_t pmic_gpio_lowest;
|
||||
uint32_t pmic_gpio_highest;
|
||||
struct device_node *int_ctrl;
|
||||
struct list_head chip_list;
|
||||
};
|
||||
|
||||
static LIST_HEAD(qpnp_gpio_chips);
|
||||
static DEFINE_MUTEX(qpnp_gpio_chips_lock);
|
||||
|
||||
static inline void qpnp_pmic_gpio_set_spec(struct qpnp_gpio_chip *q_chip,
|
||||
uint32_t pmic_gpio,
|
||||
struct qpnp_gpio_spec *spec)
|
||||
{
|
||||
q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest] = spec;
|
||||
}
|
||||
|
||||
static inline struct qpnp_gpio_spec *qpnp_pmic_gpio_get_spec(
|
||||
struct qpnp_gpio_chip *q_chip,
|
||||
uint32_t pmic_gpio)
|
||||
{
|
||||
if (pmic_gpio < q_chip->pmic_gpio_lowest ||
|
||||
pmic_gpio > q_chip->pmic_gpio_highest)
|
||||
return NULL;
|
||||
|
||||
return q_chip->pmic_gpios[pmic_gpio - q_chip->pmic_gpio_lowest];
|
||||
}
|
||||
|
||||
static inline struct qpnp_gpio_spec *qpnp_chip_gpio_get_spec(
|
||||
struct qpnp_gpio_chip *q_chip,
|
||||
uint32_t chip_gpio)
|
||||
{
|
||||
if (chip_gpio > q_chip->gpio_chip.ngpio)
|
||||
return NULL;
|
||||
|
||||
return q_chip->chip_gpios[chip_gpio];
|
||||
}
|
||||
|
||||
static inline void qpnp_chip_gpio_set_spec(struct qpnp_gpio_chip *q_chip,
|
||||
uint32_t chip_gpio,
|
||||
struct qpnp_gpio_spec *spec)
|
||||
{
|
||||
q_chip->chip_gpios[chip_gpio] = spec;
|
||||
}
|
||||
|
||||
int qpnp_gpio_config(int gpio, struct qpnp_gpio_cfg *param)
|
||||
{
|
||||
int rc, chip_offset;
|
||||
struct qpnp_gpio_chip *q_chip;
|
||||
struct qpnp_gpio_spec *q_spec = NULL;
|
||||
struct gpio_chip *gpio_chip;
|
||||
|
||||
if (param == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&qpnp_gpio_chips_lock);
|
||||
list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) {
|
||||
gpio_chip = &q_chip->gpio_chip;
|
||||
if (gpio >= gpio_chip->base
|
||||
&& gpio < gpio_chip->base + gpio_chip->ngpio) {
|
||||
chip_offset = gpio - gpio_chip->base;
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset);
|
||||
if (WARN_ON(!q_spec)) {
|
||||
mutex_unlock(&qpnp_gpio_chips_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&qpnp_gpio_chips_lock);
|
||||
if (!q_spec) {
|
||||
pr_err("gpio %d not handled by any pmic\n", gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
q_spec->regs[Q_REG_I_IO_CTL1] = (param->vin_sel <<
|
||||
Q_REG_VIN_SHIFT) & Q_REG_VIN_MASK;
|
||||
q_spec->regs[Q_REG_I_IO_CTL1] |= (param->pull <<
|
||||
Q_REG_PULL_SHIFT) & Q_REG_PULL_MASK;
|
||||
q_spec->regs[Q_REG_I_INPUT_CTL1] = ((param->direction &
|
||||
QPNP_GPIO_DIR_IN) ? ((1 << Q_REG_INPUT_EN_SHIFT)) : 0);
|
||||
|
||||
if (param->direction & QPNP_GPIO_DIR_OUT) {
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL1] = (param->out_strength
|
||||
<< Q_REG_OUT_STRENGTH_SHIFT) & Q_REG_OUT_STRENGTH_MASK;
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL1] |= (param->output_type
|
||||
<< Q_REG_OUT_TYPE_SHIFT) & Q_REG_OUT_TYPE_MASK;
|
||||
} else {
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL1] = 0;
|
||||
}
|
||||
|
||||
if (param->direction & QPNP_GPIO_DIR_OUT) {
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] = (param->inv_int_pol
|
||||
<< Q_REG_OUT_INVERT_SHIFT) & Q_REG_OUT_INVERT_MASK;
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] |= (param->src_select
|
||||
<< Q_REG_SRC_SEL_SHIFT) & Q_REG_SRC_SEL_MASK;
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] |= (1 <<
|
||||
Q_REG_OUTPUT_EN_SHIFT) & Q_REG_OUTPUT_EN_MASK;
|
||||
} else {
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] = 0;
|
||||
}
|
||||
|
||||
q_spec->regs[Q_REG_I_EN_CTL1] = (param->master_en <<
|
||||
Q_REG_MASTER_EN_SHIFT) & Q_REG_MASTER_EN_MASK;
|
||||
|
||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
||||
Q_REG_ADDR(q_spec, Q_REG_IO_CTL1),
|
||||
&q_spec->regs[Q_REG_I_IO_CTL1], Q_NUM_CTL_REGS);
|
||||
if (rc)
|
||||
dev_err(&q_chip->spmi->dev, "%s: unable to write master"
|
||||
" enable\n", __func__);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(qpnp_gpio_config);
|
||||
|
||||
int qpnp_gpio_map_gpio(uint16_t slave_id, uint32_t pmic_gpio)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip;
|
||||
struct qpnp_gpio_spec *q_spec = NULL;
|
||||
|
||||
mutex_lock(&qpnp_gpio_chips_lock);
|
||||
list_for_each_entry(q_chip, &qpnp_gpio_chips, chip_list) {
|
||||
if (q_chip->spmi->sid != slave_id)
|
||||
continue;
|
||||
if (q_chip->pmic_gpio_lowest <= pmic_gpio &&
|
||||
q_chip->pmic_gpio_highest >= pmic_gpio) {
|
||||
q_spec = qpnp_pmic_gpio_get_spec(q_chip, pmic_gpio);
|
||||
mutex_unlock(&qpnp_gpio_chips_lock);
|
||||
if (WARN_ON(!q_spec))
|
||||
return -ENODEV;
|
||||
return q_chip->gpio_chip.base + q_spec->gpio_chip_idx;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&qpnp_gpio_chips_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(qpnp_gpio_map_gpio);
|
||||
|
||||
static int qpnp_gpio_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
|
||||
struct qpnp_gpio_spec *q_spec;
|
||||
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
|
||||
if (!q_spec)
|
||||
return -EINVAL;
|
||||
|
||||
return q_spec->irq;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_get(struct gpio_chip *gpio_chip, unsigned offset)
|
||||
{
|
||||
int rc, ret_val;
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
|
||||
struct qpnp_gpio_spec *q_spec = NULL;
|
||||
u8 buf[1];
|
||||
|
||||
if (WARN_ON(!q_chip))
|
||||
return -ENODEV;
|
||||
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
|
||||
if (WARN_ON(!q_spec))
|
||||
return -ENODEV;
|
||||
|
||||
/* gpio val is from RT status iff input is enabled */
|
||||
if (q_spec->regs[Q_REG_I_INPUT_CTL1] & Q_REG_INPUT_EN_MASK) {
|
||||
/* INT_RT_STS */
|
||||
rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
|
||||
Q_REG_ADDR(q_spec, Q_REG_STATUS1),
|
||||
&buf[0], 1);
|
||||
return buf[0];
|
||||
|
||||
} else {
|
||||
ret_val = (q_spec->regs[Q_REG_I_OUTPUT_CTL2] &
|
||||
Q_REG_OUT_INVERT_MASK) >> Q_REG_OUT_INVERT_SHIFT;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __qpnp_gpio_set(struct qpnp_gpio_chip *q_chip,
|
||||
struct qpnp_gpio_spec *q_spec, int value)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!q_chip || !q_spec)
|
||||
return -EINVAL;
|
||||
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] &= ~(1 << Q_REG_OUT_INVERT_SHIFT);
|
||||
|
||||
if (value)
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] |=
|
||||
(1 << Q_REG_OUT_INVERT_SHIFT);
|
||||
|
||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
||||
Q_REG_ADDR(q_spec, Q_REG_OUTPUT_CTL2),
|
||||
&q_spec->regs[Q_REG_I_OUTPUT_CTL2], 1);
|
||||
if (rc)
|
||||
dev_err(&q_chip->spmi->dev, "%s: spmi write failed\n",
|
||||
__func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void qpnp_gpio_set(struct gpio_chip *gpio_chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
|
||||
struct qpnp_gpio_spec *q_spec;
|
||||
|
||||
if (WARN_ON(!q_chip))
|
||||
return;
|
||||
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
|
||||
if (WARN_ON(!q_spec))
|
||||
return;
|
||||
|
||||
__qpnp_gpio_set(q_chip, q_spec, value);
|
||||
}
|
||||
|
||||
static int qpnp_gpio_set_direction(struct qpnp_gpio_chip *q_chip,
|
||||
struct qpnp_gpio_spec *q_spec, int direction)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!q_chip || !q_spec)
|
||||
return -EINVAL;
|
||||
|
||||
if (direction & QPNP_GPIO_DIR_IN) {
|
||||
q_spec->regs[Q_REG_I_INPUT_CTL1] |=
|
||||
(1 << Q_REG_INPUT_EN_SHIFT);
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] &=
|
||||
~(1 << Q_REG_OUTPUT_EN_SHIFT);
|
||||
} else {
|
||||
q_spec->regs[Q_REG_I_INPUT_CTL1] &=
|
||||
~(1 << Q_REG_INPUT_EN_SHIFT);
|
||||
q_spec->regs[Q_REG_I_OUTPUT_CTL2] |=
|
||||
(1 << Q_REG_OUTPUT_EN_SHIFT);
|
||||
}
|
||||
|
||||
rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
|
||||
Q_REG_ADDR(q_spec, Q_REG_INPUT_CTL1),
|
||||
&q_spec->regs[Q_REG_I_INPUT_CTL1], 3);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_direction_input(struct gpio_chip *gpio_chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
|
||||
struct qpnp_gpio_spec *q_spec;
|
||||
|
||||
if (WARN_ON(!q_chip))
|
||||
return -ENODEV;
|
||||
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
|
||||
if (WARN_ON(!q_spec))
|
||||
return -ENODEV;
|
||||
|
||||
return qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_IN);
|
||||
}
|
||||
|
||||
static int qpnp_gpio_direction_output(struct gpio_chip *gpio_chip,
|
||||
unsigned offset,
|
||||
int val)
|
||||
{
|
||||
int rc;
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
|
||||
struct qpnp_gpio_spec *q_spec;
|
||||
|
||||
if (WARN_ON(!q_chip))
|
||||
return -ENODEV;
|
||||
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
|
||||
if (WARN_ON(!q_spec))
|
||||
return -ENODEV;
|
||||
|
||||
rc = __qpnp_gpio_set(q_chip, q_spec, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = qpnp_gpio_set_direction(q_chip, q_spec, QPNP_GPIO_DIR_OUT);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_of_gpio_xlate(struct gpio_chip *gpio_chip,
|
||||
struct device_node *np,
|
||||
const void *gpio_spec, u32 *flags)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
|
||||
struct qpnp_gpio_spec *q_spec;
|
||||
const __be32 *gpio = gpio_spec;
|
||||
u32 n = be32_to_cpup(gpio);
|
||||
|
||||
if (WARN_ON(gpio_chip->of_gpio_n_cells < 2)) {
|
||||
pr_err("%s: of_gpio_n_cells < 2\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
q_spec = qpnp_pmic_gpio_get_spec(q_chip, n);
|
||||
if (!q_spec) {
|
||||
pr_err("%s: no such PMIC gpio %u in device topology\n",
|
||||
__func__, n);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (flags)
|
||||
*flags = be32_to_cpu(gpio[1]);
|
||||
|
||||
return q_spec->gpio_chip_idx;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_config_default(struct spmi_device *spmi,
|
||||
const __be32 *prop, int gpio)
|
||||
{
|
||||
struct qpnp_gpio_cfg param;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&spmi->dev, "%s: p[0]: 0x%x p[1]: 0x%x p[2]: 0x%x p[3]:"
|
||||
" 0x%x p[4]: 0x%x p[5]: 0x%x p[6]: 0x%x p[7]: 0x%x"
|
||||
" p[8]: 0x%x\n", __func__,
|
||||
be32_to_cpup(&prop[0]), be32_to_cpup(&prop[1]),
|
||||
be32_to_cpup(&prop[2]), be32_to_cpup(&prop[3]),
|
||||
be32_to_cpup(&prop[4]), be32_to_cpup(&prop[5]),
|
||||
be32_to_cpup(&prop[6]), be32_to_cpup(&prop[7]),
|
||||
be32_to_cpup(&prop[8]));
|
||||
|
||||
param.direction = be32_to_cpup(&prop[0]);
|
||||
param.output_type = be32_to_cpup(&prop[1]);
|
||||
param.output_value = be32_to_cpup(&prop[2]);
|
||||
param.pull = be32_to_cpup(&prop[3]);
|
||||
param.vin_sel = be32_to_cpup(&prop[4]);
|
||||
param.out_strength = be32_to_cpup(&prop[5]);
|
||||
param.src_select = be32_to_cpup(&prop[6]);
|
||||
param.inv_int_pol = be32_to_cpup(&prop[7]);
|
||||
param.master_en = be32_to_cpup(&prop[8]);
|
||||
|
||||
rc = qpnp_gpio_config(gpio, ¶m);
|
||||
if (rc)
|
||||
dev_err(&spmi->dev, "%s: unable to set default config for"
|
||||
" gpio %d\n", __func__, gpio);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_free_chip(struct qpnp_gpio_chip *q_chip)
|
||||
{
|
||||
struct spmi_device *spmi = q_chip->spmi;
|
||||
int rc, i;
|
||||
|
||||
if (q_chip->chip_gpios)
|
||||
for (i = 0; i < spmi->num_dev_node; i++)
|
||||
kfree(q_chip->chip_gpios[i]);
|
||||
|
||||
mutex_lock(&qpnp_gpio_chips_lock);
|
||||
list_del(&q_chip->chip_list);
|
||||
mutex_unlock(&qpnp_gpio_chips_lock);
|
||||
rc = gpiochip_remove(&q_chip->gpio_chip);
|
||||
if (rc)
|
||||
dev_err(&q_chip->spmi->dev, "%s: unable to remove gpio\n",
|
||||
__func__);
|
||||
kfree(q_chip->chip_gpios);
|
||||
kfree(q_chip->pmic_gpios);
|
||||
kfree(q_chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_probe(struct spmi_device *spmi)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip;
|
||||
struct resource *res;
|
||||
struct qpnp_gpio_spec *q_spec;
|
||||
const __be32 *prop;
|
||||
int i, rc, ret, gpio, len;
|
||||
int lowest_gpio = INT_MAX, highest_gpio = INT_MIN;
|
||||
u32 intspec[3];
|
||||
|
||||
q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
|
||||
if (!q_chip) {
|
||||
dev_err(&spmi->dev, "%s: Can't allocate gpio_chip\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
q_chip->spmi = spmi;
|
||||
dev_set_drvdata(&spmi->dev, q_chip);
|
||||
|
||||
mutex_lock(&qpnp_gpio_chips_lock);
|
||||
list_add(&q_chip->chip_list, &qpnp_gpio_chips);
|
||||
mutex_unlock(&qpnp_gpio_chips_lock);
|
||||
|
||||
/* first scan through nodes to find the range required for allocation */
|
||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
||||
prop = of_get_property(spmi->dev_node[i].of_node,
|
||||
"qcom,qpnp-gpio-num", &len);
|
||||
if (!prop) {
|
||||
dev_err(&spmi->dev, "%s: unable to get"
|
||||
" qcom,qpnp-gpio-num property\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
} else if (len != sizeof(__be32)) {
|
||||
dev_err(&spmi->dev, "%s: Invalid qcom,qpnp-gpio-num"
|
||||
" property\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
gpio = be32_to_cpup(prop);
|
||||
if (gpio < lowest_gpio)
|
||||
lowest_gpio = gpio;
|
||||
if (gpio > highest_gpio)
|
||||
highest_gpio = gpio;
|
||||
}
|
||||
|
||||
if (highest_gpio < lowest_gpio) {
|
||||
dev_err(&spmi->dev, "%s: no device nodes specified in"
|
||||
" topology\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
} else if (lowest_gpio == 0) {
|
||||
dev_err(&spmi->dev, "%s: 0 is not a valid PMIC GPIO\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
q_chip->pmic_gpio_lowest = lowest_gpio;
|
||||
q_chip->pmic_gpio_highest = highest_gpio;
|
||||
|
||||
/* allocate gpio lookup tables */
|
||||
q_chip->pmic_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) *
|
||||
highest_gpio - lowest_gpio + 1,
|
||||
GFP_KERNEL);
|
||||
q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_gpio_spec *) *
|
||||
spmi->num_dev_node, GFP_KERNEL);
|
||||
if (!q_chip->pmic_gpios || !q_chip->chip_gpios) {
|
||||
dev_err(&spmi->dev, "%s: unable to allocate memory\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
/* get interrupt controller device_node */
|
||||
q_chip->int_ctrl = of_irq_find_parent(spmi->dev.of_node);
|
||||
if (!q_chip->int_ctrl) {
|
||||
dev_err(&spmi->dev, "%s: Can't find interrupt parent\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
/* now scan through again and populate the lookup table */
|
||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
||||
res = qpnp_get_resource(spmi, i, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&spmi->dev, "%s: node %s is missing has no"
|
||||
" base address definition\n",
|
||||
__func__, spmi->dev_node[i].of_node->full_name);
|
||||
}
|
||||
|
||||
prop = of_get_property(spmi->dev_node[i].of_node,
|
||||
"qcom,qpnp-gpio-num", &len);
|
||||
if (!prop) {
|
||||
dev_err(&spmi->dev, "%s: unable to get"
|
||||
" qcom,qpnp-gpio-num property\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
} else if (len != sizeof(__be32)) {
|
||||
dev_err(&spmi->dev, "%s: Invalid qcom,qpnp-gpio-num"
|
||||
" property\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
}
|
||||
gpio = be32_to_cpup(prop);
|
||||
|
||||
q_spec = kzalloc(sizeof(struct qpnp_gpio_spec),
|
||||
GFP_KERNEL);
|
||||
if (!q_spec) {
|
||||
dev_err(&spmi->dev, "%s: unable to allocate"
|
||||
" memory\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
q_spec->slave = spmi->sid;
|
||||
q_spec->offset = res->start;
|
||||
q_spec->gpio_chip_idx = i;
|
||||
|
||||
/* call into irq_domain to get irq mapping */
|
||||
intspec[0] = q_chip->spmi->sid;
|
||||
intspec[1] = (q_spec->offset >> 8) & 0xFF;
|
||||
intspec[2] = 0;
|
||||
q_spec->irq = irq_create_of_mapping(q_chip->int_ctrl,
|
||||
intspec, 3);
|
||||
if (!q_spec->irq) {
|
||||
dev_err(&spmi->dev, "%s: invalid irq for gpio"
|
||||
" %u\n", __func__, gpio);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
}
|
||||
/* initialize lookup table entries */
|
||||
qpnp_pmic_gpio_set_spec(q_chip, gpio, q_spec);
|
||||
qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
|
||||
}
|
||||
|
||||
q_chip->gpio_chip.base = -1;
|
||||
q_chip->gpio_chip.ngpio = spmi->num_dev_node;
|
||||
q_chip->gpio_chip.label = "qpnp-gpio";
|
||||
q_chip->gpio_chip.direction_input = qpnp_gpio_direction_input;
|
||||
q_chip->gpio_chip.direction_output = qpnp_gpio_direction_output;
|
||||
q_chip->gpio_chip.to_irq = qpnp_gpio_to_irq;
|
||||
q_chip->gpio_chip.get = qpnp_gpio_get;
|
||||
q_chip->gpio_chip.set = qpnp_gpio_set;
|
||||
q_chip->gpio_chip.dev = &spmi->dev;
|
||||
q_chip->gpio_chip.of_xlate = qpnp_gpio_of_gpio_xlate;
|
||||
q_chip->gpio_chip.of_gpio_n_cells = 2;
|
||||
q_chip->gpio_chip.can_sleep = 0;
|
||||
|
||||
rc = gpiochip_add(&q_chip->gpio_chip);
|
||||
if (rc) {
|
||||
dev_err(&spmi->dev, "%s: Can't add gpio chip, rc = %d\n",
|
||||
__func__, rc);
|
||||
ret = rc;
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
/* now configure gpio defaults if they exist */
|
||||
for (i = 0; i < spmi->num_dev_node; i++) {
|
||||
q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
|
||||
if (WARN_ON(!q_spec))
|
||||
return -ENODEV;
|
||||
|
||||
/* It's not an error to not config a default */
|
||||
prop = of_get_property(spmi->dev_node[i].of_node,
|
||||
"qcom,qpnp-gpio-cfg", &len);
|
||||
/* 9 data values constitute one tuple */
|
||||
if (prop && (len != (9 * sizeof(__be32)))) {
|
||||
dev_err(&spmi->dev, "%s: invalid format for"
|
||||
" qcom,qpnp-gpio-cfg property\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto err_probe;
|
||||
} else if (prop) {
|
||||
rc = qpnp_gpio_config_default(spmi, prop,
|
||||
q_chip->gpio_chip.base + i);
|
||||
if (rc) {
|
||||
ret = rc;
|
||||
goto err_probe;
|
||||
}
|
||||
} else {
|
||||
/* initialize with hardware defaults */
|
||||
rc = spmi_ext_register_readl(
|
||||
q_chip->spmi->ctrl, q_spec->slave,
|
||||
Q_REG_ADDR(q_spec, Q_REG_IO_CTL1),
|
||||
&q_spec->regs[Q_REG_I_IO_CTL1],
|
||||
Q_NUM_CTL_REGS);
|
||||
q_spec->regs[Q_REG_I_EN_CTL1] |=
|
||||
(1 << Q_REG_MASTER_EN_SHIFT);
|
||||
rc = spmi_ext_register_writel(
|
||||
q_chip->spmi->ctrl, q_spec->slave,
|
||||
Q_REG_ADDR(q_spec, Q_REG_EN_CTL1),
|
||||
&q_spec->regs[Q_REG_EN_CTL1], 1);
|
||||
if (rc) {
|
||||
dev_err(&spmi->dev, "%s: spmi write"
|
||||
" failed\n", __func__);
|
||||
ret = rc;
|
||||
goto err_probe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&spmi->dev, "%s: gpio_chip registered between %d-%u\n",
|
||||
__func__, q_chip->gpio_chip.base,
|
||||
(q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
|
||||
return 0;
|
||||
|
||||
err_probe:
|
||||
qpnp_gpio_free_chip(q_chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qpnp_gpio_remove(struct spmi_device *spmi)
|
||||
{
|
||||
struct qpnp_gpio_chip *q_chip = dev_get_drvdata(&spmi->dev);
|
||||
|
||||
return qpnp_gpio_free_chip(q_chip);
|
||||
}
|
||||
|
||||
static struct of_device_id spmi_match_table[] = {
|
||||
{ .compatible = "qcom,qpnp-gpio",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct spmi_device_id qpnp_gpio_id[] = {
|
||||
{ "qcom,qpnp-gpio", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spmi, qpnp_gpio_id);
|
||||
|
||||
static struct spmi_driver qpnp_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "qcom,qpnp-gpio",
|
||||
.of_match_table = spmi_match_table,
|
||||
},
|
||||
.probe = qpnp_gpio_probe,
|
||||
.remove = qpnp_gpio_remove,
|
||||
.id_table = qpnp_gpio_id,
|
||||
};
|
||||
|
||||
static int __init qpnp_gpio_init(void)
|
||||
{
|
||||
return spmi_driver_register(&qpnp_gpio_driver);
|
||||
}
|
||||
|
||||
static void __exit qpnp_gpio_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
MODULE_AUTHOR(
|
||||
"Michael Bohan <mbohan@codeaurora.org>");
|
||||
MODULE_DESCRIPTION("QPNP PMIC gpio driver");
|
||||
MODULE_LICENSE("GPLv2");
|
||||
|
||||
module_init(qpnp_gpio_init);
|
||||
module_exit(qpnp_gpio_exit);
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/of_spmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct of_spmi_dev_info {
|
||||
struct spmi_controller *ctrl;
|
||||
@@ -30,14 +31,49 @@ struct of_spmi_res_info {
|
||||
uint32_t num_irq;
|
||||
};
|
||||
|
||||
static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg)
|
||||
/*
|
||||
* Initialize r_info structure for safe usage
|
||||
*/
|
||||
static inline void of_spmi_init_resource(struct of_spmi_res_info *r_info,
|
||||
struct device_node *node)
|
||||
{
|
||||
r_info->node = node;
|
||||
r_info->num_reg = 0;
|
||||
r_info->num_irq = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate dev_node array for spmi_device
|
||||
*/
|
||||
static inline int of_spmi_alloc_device_store(struct of_spmi_dev_info *d_info,
|
||||
uint32_t num_dev_node)
|
||||
{
|
||||
d_info->b_info.num_dev_node = num_dev_node;
|
||||
d_info->b_info.dev_node = kzalloc(sizeof(struct spmi_resource) *
|
||||
num_dev_node, GFP_KERNEL);
|
||||
if (!d_info->b_info.dev_node)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the number of resources to allocate
|
||||
*
|
||||
* The caller is responsible for initializing the of_spmi_res_info structure.
|
||||
*/
|
||||
static void of_spmi_sum_node_resources(struct of_spmi_res_info *r_info,
|
||||
bool has_reg)
|
||||
{
|
||||
struct of_irq oirq;
|
||||
uint64_t size;
|
||||
uint32_t flags;
|
||||
int i = 0;
|
||||
|
||||
while (of_irq_map_one(r_info->node, r_info->num_irq, &oirq) == 0)
|
||||
r_info->num_irq++;
|
||||
while (of_irq_map_one(r_info->node, i, &oirq) == 0)
|
||||
i++;
|
||||
|
||||
r_info->num_irq += i;
|
||||
|
||||
if (!has_reg)
|
||||
return;
|
||||
@@ -48,16 +84,32 @@ static void of_spmi_sum_resources(struct of_spmi_res_info *r_info, bool has_reg)
|
||||
* parent buses have a size-cell of 0. But SPMI does have a
|
||||
* size-cell of 0.
|
||||
*/
|
||||
while (of_get_address(r_info->node, r_info->num_reg,
|
||||
&size, &flags) != NULL)
|
||||
r_info->num_reg++;
|
||||
i = 0;
|
||||
while (of_get_address(r_info->node, i, &size, &flags) != NULL)
|
||||
i++;
|
||||
|
||||
r_info->num_reg += i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate resources for a child of a spmi-slave node.
|
||||
/*
|
||||
* free spmi_resource for the spmi_device
|
||||
*/
|
||||
static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info,
|
||||
struct of_spmi_res_info *r_info)
|
||||
static void of_spmi_free_device_resources(struct of_spmi_dev_info *d_info)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < d_info->b_info.num_dev_node; i++)
|
||||
kfree(d_info->b_info.dev_node[i].resource);
|
||||
|
||||
kfree(d_info->b_info.dev_node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather node resources and populate
|
||||
*/
|
||||
static void of_spmi_populate_node_resources(struct of_spmi_dev_info *d_info,
|
||||
struct of_spmi_res_info *r_info,
|
||||
int idx)
|
||||
|
||||
{
|
||||
uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg;
|
||||
@@ -67,13 +119,10 @@ static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info,
|
||||
uint64_t size;
|
||||
uint32_t flags;
|
||||
|
||||
if (num_irq || num_reg) {
|
||||
res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
res = d_info->b_info.dev_node[idx].resource;
|
||||
d_info->b_info.dev_node[idx].of_node = r_info->node;
|
||||
|
||||
d_info->b_info.num_resources = num_reg + num_irq;
|
||||
d_info->b_info.resource = res;
|
||||
if ((num_irq || num_reg) && (res != NULL)) {
|
||||
for (i = 0; i < num_reg; i++, res++) {
|
||||
/* Addresses are always 16 bits */
|
||||
addrp = of_get_address(r_info->node, i, &size, &flags);
|
||||
@@ -85,10 +134,34 @@ static int of_spmi_allocate_resources(struct of_spmi_dev_info *d_info,
|
||||
WARN_ON(of_irq_to_resource_table(r_info->node, res, num_irq) !=
|
||||
num_irq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate enough memory to handle the resources associated with the
|
||||
* device_node. The number of device nodes included in this allocation
|
||||
* depends on whether the spmi-dev-container flag is specified or not.
|
||||
*/
|
||||
static int of_spmi_allocate_node_resources(struct of_spmi_dev_info *d_info,
|
||||
struct of_spmi_res_info *r_info,
|
||||
uint32_t idx)
|
||||
{
|
||||
uint32_t num_irq = r_info->num_irq, num_reg = r_info->num_reg;
|
||||
struct resource *res = NULL;
|
||||
|
||||
if (num_irq || num_reg) {
|
||||
res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
}
|
||||
d_info->b_info.dev_node[idx].num_resources = num_reg + num_irq;
|
||||
d_info->b_info.dev_node[idx].resource = res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a single spmi_device
|
||||
*/
|
||||
static int of_spmi_create_device(struct of_spmi_dev_info *d_info,
|
||||
struct device_node *node)
|
||||
{
|
||||
@@ -117,6 +190,71 @@ static int of_spmi_create_device(struct of_spmi_dev_info *d_info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walks all children of a node containing the spmi-dev-container
|
||||
* binding. This special type of spmi_device can include resources
|
||||
* from more than one device node.
|
||||
*/
|
||||
static void of_spmi_walk_dev_container(struct of_spmi_dev_info *d_info,
|
||||
struct device_node *container)
|
||||
{
|
||||
struct of_spmi_res_info r_info = {};
|
||||
struct spmi_controller *ctrl = d_info->ctrl;
|
||||
struct device_node *node;
|
||||
int rc, i, num_dev_node = 0;
|
||||
|
||||
/* first count the total number of device_nodes */
|
||||
for_each_child_of_node(container, node)
|
||||
num_dev_node++;
|
||||
|
||||
rc = of_spmi_alloc_device_store(d_info, num_dev_node);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to allocate"
|
||||
" device resources\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate resources per device_node */
|
||||
i = 0;
|
||||
for_each_child_of_node(container, node) {
|
||||
of_spmi_init_resource(&r_info, node);
|
||||
of_spmi_sum_node_resources(&r_info, 1);
|
||||
rc = of_spmi_allocate_node_resources(d_info, &r_info, i);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to allocate"
|
||||
" resources\n", __func__);
|
||||
of_spmi_free_device_resources(d_info);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Now we need to cycle through again and actually populate
|
||||
* the resources for each node.
|
||||
*/
|
||||
i = 0;
|
||||
for_each_child_of_node(container, node) {
|
||||
of_spmi_init_resource(&r_info, node);
|
||||
of_spmi_sum_node_resources(&r_info, 1);
|
||||
of_spmi_populate_node_resources(d_info, &r_info, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
rc = of_spmi_create_device(d_info, container);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to create device for"
|
||||
" node %s\n", __func__, container->full_name);
|
||||
of_spmi_free_device_resources(d_info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walks all children of a node containing the spmi-slave-container
|
||||
* binding. This indicates that all spmi_devices created from this
|
||||
* point all share the same slave_id.
|
||||
*/
|
||||
static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info,
|
||||
struct device_node *container)
|
||||
{
|
||||
@@ -127,59 +265,133 @@ static void of_spmi_walk_slave_container(struct of_spmi_dev_info *d_info,
|
||||
for_each_child_of_node(container, node) {
|
||||
struct of_spmi_res_info r_info;
|
||||
|
||||
r_info.node = node;
|
||||
of_spmi_sum_resources(&r_info, 1);
|
||||
/**
|
||||
* Check to see if this node contains children which
|
||||
* should be all created as the same spmi_device.
|
||||
*/
|
||||
if (of_get_property(node, "spmi-dev-container", NULL)) {
|
||||
of_spmi_walk_dev_container(d_info, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = of_spmi_allocate_resources(d_info, &r_info);
|
||||
rc = of_spmi_alloc_device_store(d_info, 1);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to allocate"
|
||||
" device resources\n", __func__);
|
||||
goto slave_err;
|
||||
}
|
||||
|
||||
of_spmi_init_resource(&r_info, node);
|
||||
of_spmi_sum_node_resources(&r_info, 1);
|
||||
|
||||
rc = of_spmi_allocate_node_resources(d_info, &r_info, 0);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to allocate"
|
||||
" resources\n", __func__);
|
||||
return;
|
||||
goto slave_err;
|
||||
}
|
||||
|
||||
of_spmi_populate_node_resources(d_info, &r_info, 0);
|
||||
|
||||
rc = of_spmi_create_device(d_info, node);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to create device for"
|
||||
" node %s\n", __func__, node->full_name);
|
||||
return;
|
||||
goto slave_err;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
slave_err:
|
||||
of_spmi_free_device_resources(d_info);
|
||||
}
|
||||
|
||||
int of_spmi_register_devices(struct spmi_controller *ctrl)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct device_node *node = ctrl->dev.of_node;
|
||||
|
||||
/* Only register child devices if the ctrl has a node pointer set */
|
||||
if (!ctrl->dev.of_node)
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_get_property(node, "spmi-slave-container", NULL)) {
|
||||
dev_err(&ctrl->dev, "%s: structural error: spmi-slave-container"
|
||||
" is prohibited at the root level\n", __func__);
|
||||
return -EINVAL;
|
||||
} else if (of_get_property(node, "spmi-dev-container", NULL)) {
|
||||
dev_err(&ctrl->dev, "%s: structural error: spmi-dev-container"
|
||||
" is prohibited at the root level\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make best effort to launch as many nodes as possible. If there are
|
||||
* syntax errors, we will simply ignore that subtree and keep going.
|
||||
*/
|
||||
for_each_child_of_node(ctrl->dev.of_node, node) {
|
||||
struct of_spmi_dev_info d_info = {};
|
||||
const __be32 *slave_id;
|
||||
int len, rc;
|
||||
int len, rc, have_dev_container = 0;
|
||||
|
||||
slave_id = of_get_property(node, "reg", &len);
|
||||
if (!slave_id) {
|
||||
dev_err(&ctrl->dev, "of_spmi: invalid sid "
|
||||
"on %s\n", node->full_name);
|
||||
dev_err(&ctrl->dev, "%s: invalid sid "
|
||||
"on %s\n", __func__, node->full_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
d_info.b_info.slave_id = be32_to_cpup(slave_id);
|
||||
d_info.ctrl = ctrl;
|
||||
|
||||
if (of_get_property(node, "spmi-dev-container", NULL))
|
||||
have_dev_container = 1;
|
||||
if (of_get_property(node, "spmi-slave-container", NULL)) {
|
||||
of_spmi_walk_slave_container(&d_info, node);
|
||||
continue;
|
||||
if (have_dev_container)
|
||||
of_spmi_walk_dev_container(&d_info, node);
|
||||
else
|
||||
of_spmi_walk_slave_container(&d_info, node);
|
||||
} else {
|
||||
struct of_spmi_res_info r_info;
|
||||
|
||||
r_info.node = node;
|
||||
of_spmi_sum_resources(&r_info, 0);
|
||||
rc = of_spmi_allocate_resources(&d_info, &r_info);
|
||||
if (rc)
|
||||
/**
|
||||
* A dev container at the second level without a slave
|
||||
* container is considered an error.
|
||||
*/
|
||||
if (have_dev_container) {
|
||||
dev_err(&ctrl->dev, "%s: structural error,"
|
||||
" node %s has spmi-dev-container without"
|
||||
" specifying spmi-slave-container\n",
|
||||
__func__, node->full_name);
|
||||
continue;
|
||||
of_spmi_create_device(&d_info, node);
|
||||
}
|
||||
|
||||
rc = of_spmi_alloc_device_store(&d_info, 1);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to allocate"
|
||||
" device resources\n", __func__);
|
||||
continue;
|
||||
}
|
||||
|
||||
of_spmi_init_resource(&r_info, node);
|
||||
of_spmi_sum_node_resources(&r_info, 0);
|
||||
rc = of_spmi_allocate_node_resources(&d_info,
|
||||
&r_info, 0);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to allocate"
|
||||
" resources\n", __func__);
|
||||
of_spmi_free_device_resources(&d_info);
|
||||
continue;
|
||||
}
|
||||
|
||||
of_spmi_populate_node_resources(&d_info, &r_info, 0);
|
||||
|
||||
rc = of_spmi_create_device(&d_info, node);
|
||||
if (rc) {
|
||||
dev_err(&ctrl->dev, "%s: unable to create"
|
||||
" device\n", __func__);
|
||||
of_spmi_free_device_resources(&d_info);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,9 +233,9 @@ struct spmi_device *spmi_new_device(struct spmi_controller *ctrl,
|
||||
spmidev->name = info->name;
|
||||
spmidev->sid = info->slave_id;
|
||||
spmidev->dev.of_node = info->of_node;
|
||||
spmidev->resource = info->resource;
|
||||
spmidev->num_resources = info->num_resources;
|
||||
spmidev->dev.platform_data = (void *)info->platform_data;
|
||||
spmidev->num_dev_node = info->num_dev_node;
|
||||
spmidev->dev_node = info->dev_node;
|
||||
|
||||
rc = spmi_add_device(spmidev);
|
||||
if (rc < 0) {
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#ifdef CONFIG_OF_SPMI
|
||||
/**
|
||||
* of_spmi_register_devices() - Register devices in the SPMI Device Tree
|
||||
* @ctrl: spmi_controller which devices should be registered to.
|
||||
*
|
||||
* This routine scans the SPMI Device Tree, allocating resources and
|
||||
* creating spmi_devices according to the SPMI bus Device Tree
|
||||
* hierarchy. Details of this hierarchy can be found in
|
||||
* Documentation/devicetree/bindings/spmi. This routine is normally
|
||||
* called from the probe routine of the driver registering as a
|
||||
* spmi_controller.
|
||||
*/
|
||||
int of_spmi_register_devices(struct spmi_controller *ctrl);
|
||||
#else
|
||||
static int of_spmi_register_devices(struct spmi_controller *ctrl)
|
||||
|
||||
114
include/linux/qpnp/gpio.h
Normal file
114
include/linux/qpnp/gpio.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/* Copyright (c) 2012, Code Aurora Forum. 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 <mach/qpnp.h>
|
||||
|
||||
#define QPNP_GPIO_DIR_OUT 1
|
||||
#define QPNP_GPIO_DIR_IN 2
|
||||
#define QPNP_GPIO_DIR_BOTH (QPNP_GPIO_DIR_OUT | QPNP_GPIO_DIR_IN)
|
||||
|
||||
#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN 1
|
||||
#define QPNP_GPIO_OUT_BUF_CMOS 0
|
||||
|
||||
#define QPNP_GPIO_VIN0 0
|
||||
#define QPNP_GPIO_VIN1 1
|
||||
#define QPNP_GPIO_VIN2 2
|
||||
#define QPNP_GPIO_VIN3 3
|
||||
#define QPNP_GPIO_VIN4 4
|
||||
#define QPNP_GPIO_VIN5 5
|
||||
#define QPNP_GPIO_VIN6 6
|
||||
#define QPNP_GPIO_VIN7 7
|
||||
|
||||
#define QPNP_GPIO_PULL_UP_30 0
|
||||
#define QPNP_GPIO_PULL_UP_1P5 1
|
||||
#define QPNP_GPIO_PULL_UP_31P5 2
|
||||
#define QPNP_GPIO_PULL_UP_1P5_30 3
|
||||
#define QPNP_GPIO_PULL_DN 4
|
||||
#define QPNP_GPIO_PULL_NO 5
|
||||
|
||||
#define QPNP_GPIO_OUT_STRENGTH_LOW 1
|
||||
#define QPNP_GPIO_OUT_STRENGTH_MED 2
|
||||
#define QPNP_GPIO_OUT_STRENGTH_HIGH 3
|
||||
|
||||
#define QPNP_GPIO_FUNC_NORMAL 0
|
||||
#define QPNP_GPIO_FUNC_PAIRED 1
|
||||
#define QPNP_GPIO_FUNC_1 2
|
||||
#define QPNP_GPIO_FUNC_2 3
|
||||
#define QPNP_GPIO_DTEST1 4
|
||||
#define QPNP_GPIO_DTEST2 5
|
||||
#define QPNP_GPIO_DTEST3 6
|
||||
#define QPNP_GPIO_DTEST4 7
|
||||
|
||||
/**
|
||||
* struct qpnp_gpio_cfg - structure to specify gpio configurtion values
|
||||
* @direction: indicates whether the gpio should be input, output, or
|
||||
* both. Should be of the type QPNP_GPIO_DIR_*
|
||||
* @output_type: indicates gpio should be configured as CMOS or open
|
||||
* drain. Should be of the type QPNP_GPIO_OUT_BUF_*
|
||||
* @output_value: The gpio output value of the gpio line - 0 or 1
|
||||
* @pull: Indicates whether a pull up or pull down should be
|
||||
* applied. If a pullup is required the current strength
|
||||
* needs to be specified. Current values of 30uA, 1.5uA,
|
||||
* 31.5uA, 1.5uA with 30uA boost are supported. This value
|
||||
* should be one of the QPNP_GPIO_PULL_*
|
||||
* @vin_sel: specifies the voltage level when the output is set to 1.
|
||||
* For an input gpio specifies the voltage level at which
|
||||
* the input is interpreted as a logical 1.
|
||||
* @out_strength: the amount of current supplied for an output gpio,
|
||||
* should be of the type QPNP_GPIO_STRENGTH_*
|
||||
* @source_sel: choose alternate function for the gpio. Certain gpios
|
||||
* can be paired (shorted) with each other. Some gpio pin
|
||||
* can act as alternate functions. This parameter should
|
||||
* be of type QPNP_GPIO_FUNC_*
|
||||
* @inv_int_pol: Invert polarity before feeding the line to the interrupt
|
||||
* module in pmic. This feature will almost be never used
|
||||
* since the pm8xxx interrupt block can detect both edges
|
||||
* and both levels.
|
||||
* @master_en: 1 = Enable features within the GPIO block based on
|
||||
* configurations.
|
||||
* 0 = Completely disable the GPIO block and let the pin
|
||||
* float with high impedance regardless of other settings.
|
||||
*/
|
||||
struct qpnp_gpio_cfg {
|
||||
int direction;
|
||||
int output_type;
|
||||
int output_value;
|
||||
int pull;
|
||||
int vin_sel;
|
||||
int out_strength;
|
||||
int src_select;
|
||||
int inv_int_pol;
|
||||
int master_en;
|
||||
};
|
||||
|
||||
/**
|
||||
* qpnp_gpio_config - Apply gpio configuration for Linux gpio
|
||||
* @gpio: Linux gpio number to configure.
|
||||
* @param: parameters to configure.
|
||||
*
|
||||
* This routine takes a Linux gpio number that corresponds with a
|
||||
* PMIC gpio and applies the configuration specified in 'param'.
|
||||
* This gpio number can be ascertained by of_get_gpio_flags() or
|
||||
* the qpnp_gpio_map_gpio() API.
|
||||
*/
|
||||
int qpnp_gpio_config(int gpio, struct qpnp_gpio_cfg *param);
|
||||
|
||||
/**
|
||||
* qpnp_gpio_map_gpio - Obtain Linux GPIO number from device spec
|
||||
* @slave_id: slave_id of the spmi_device for the gpio in question.
|
||||
* @pmic_gpio: PMIC gpio number to lookup.
|
||||
*
|
||||
* This routine is used in legacy configurations that do not support
|
||||
* Device Tree. If you are using Device Tree, you should not use this.
|
||||
* For such cases, use of_get_gpio() instead.
|
||||
*/
|
||||
int qpnp_gpio_map_gpio(uint16_t slave_id, uint32_t pmic_gpio);
|
||||
@@ -87,25 +87,37 @@ struct spmi_driver {
|
||||
};
|
||||
#define to_spmi_driver(d) container_of(d, struct spmi_driver, driver)
|
||||
|
||||
/**
|
||||
* struct spmi_resource: spmi_resource for one device_node
|
||||
* @num_resources: number of resources for this device node
|
||||
* @resources: array of resources for this device_node
|
||||
* @of_node: device_node of the resource in question
|
||||
*/
|
||||
struct spmi_resource {
|
||||
struct resource *resource;
|
||||
u32 num_resources;
|
||||
struct device_node *of_node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Client/device handle (struct spmi_device):
|
||||
* ------------------------------------------
|
||||
* This is the client/device handle returned when a SPMI device
|
||||
* is registered with a controller.
|
||||
* Pointer to this structure is used by client-driver as a handle.
|
||||
* @dev: driver model representation of the device.
|
||||
* @name: name of driver to use with this device.
|
||||
* @ctrl: SPMI controller managing the bus hosting this device.
|
||||
* @resource: array of resources for this device_node
|
||||
* @num_resources: number of resources for this device node
|
||||
* @sid: slave identifier.
|
||||
* This is the client/device handle returned when a SPMI device
|
||||
* is registered with a controller.
|
||||
* Pointer to this structure is used by client-driver as a handle.
|
||||
* @dev: Driver model representation of the device.
|
||||
* @name: Name of driver to use with this device.
|
||||
* @ctrl: SPMI controller managing the bus hosting this device.
|
||||
* @dev_node: array of SPMI resources - one entry per device_node.
|
||||
* @num_dev_node: number of device_node structures.
|
||||
* @sid: Slave Identifier.
|
||||
*/
|
||||
struct spmi_device {
|
||||
struct device dev;
|
||||
const char *name;
|
||||
struct spmi_controller *ctrl;
|
||||
struct resource *resource;
|
||||
u32 num_resources;
|
||||
struct spmi_resource *dev_node;
|
||||
u32 num_dev_node;
|
||||
u8 sid;
|
||||
};
|
||||
#define to_spmi_device(d) container_of(d, struct spmi_device, dev)
|
||||
@@ -115,16 +127,16 @@ struct spmi_device {
|
||||
* @slave_id: slave identifier.
|
||||
* @spmi_device: device to be registered with the SPMI framework.
|
||||
* @of_node: pointer to the OpenFirmware device node.
|
||||
* @num_resources: number of resources for this device node
|
||||
* @resource: array of resources for this device_node
|
||||
* @dev_node: one spmi_resource for each device_node.
|
||||
* @num_dev_node: number of device_node structures.
|
||||
* @platform_data: goes to spmi_device.dev.platform_data
|
||||
*/
|
||||
struct spmi_boardinfo {
|
||||
char name[SPMI_NAME_SIZE];
|
||||
uint8_t slave_id;
|
||||
struct device_node *of_node;
|
||||
u32 num_resources;
|
||||
struct resource *resource;
|
||||
struct spmi_resource *dev_node;
|
||||
u32 num_dev_node;
|
||||
const void *platform_data;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user