blob: 8d934dfb124063653644f0b9f778e2ca38533169 [file] [log] [blame]
Abhijeet Dharmapurikarea799552014-01-08 20:16:37 -08001/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
Michael Bohan0ba63b82012-02-06 13:42:34 -08002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
Michael Bohane25e15f2012-04-12 17:28:26 -070013#define pr_fmt(fmt) "%s: " fmt, __func__
14
Michael Bohan0ba63b82012-02-06 13:42:34 -080015#include <linux/interrupt.h>
16#include <linux/types.h>
17#include <linux/spmi.h>
18#include <linux/platform_device.h>
Michael Bohana19dced2012-04-24 13:14:50 -070019#include <linux/debugfs.h>
Michael Bohan0ba63b82012-02-06 13:42:34 -080020#include <linux/gpio.h>
21#include <linux/slab.h>
22#include <linux/of.h>
23#include <linux/of_gpio.h>
24#include <linux/of_irq.h>
Michael Bohan0b24fb12012-06-01 10:30:12 -070025#include <linux/export.h>
26#include <linux/module.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070027#include <linux/export.h>
Michael Bohana05f4552012-05-24 15:58:11 -070028#include <linux/qpnp/pin.h>
Michael Bohan0ba63b82012-02-06 13:42:34 -080029
Michael Bohan0ba63b82012-02-06 13:42:34 -080030#define Q_REG_ADDR(q_spec, reg_index) \
31 ((q_spec)->offset + reg_index)
32
33#define Q_REG_STATUS1 0x8
Michael Bohanffa16812012-08-15 18:11:49 -070034#define Q_REG_STATUS1_VAL_MASK 0x1
Michael Bohanbfe64c72012-08-24 16:57:26 -070035#define Q_REG_STATUS1_GPIO_EN_REV0_MASK 0x2
36#define Q_REG_STATUS1_GPIO_EN_MASK 0x80
Michael Bohanffa16812012-08-15 18:11:49 -070037#define Q_REG_STATUS1_MPP_EN_MASK 0x80
38
Michael Bohan6b901572012-05-30 13:32:24 -070039#define Q_NUM_CTL_REGS 0xD
Michael Bohane25e15f2012-04-12 17:28:26 -070040
Michael Bohanbfe64c72012-08-24 16:57:26 -070041/* revision registers base address offsets */
42#define Q_REG_DIG_MINOR_REV 0x0
43#define Q_REG_DIG_MAJOR_REV 0x1
44#define Q_REG_ANA_MINOR_REV 0x2
45
Michael Bohane25e15f2012-04-12 17:28:26 -070046/* type registers base address offsets */
Michael Bohan6b901572012-05-30 13:32:24 -070047#define Q_REG_TYPE 0x4
48#define Q_REG_SUBTYPE 0x5
Michael Bohane25e15f2012-04-12 17:28:26 -070049
50/* gpio peripheral type and subtype values */
51#define Q_GPIO_TYPE 0x10
Michael Bohane824a6e2012-08-14 14:35:37 -070052#define Q_GPIO_SUBTYPE_GPIO_4CH 0x1
53#define Q_GPIO_SUBTYPE_GPIOC_4CH 0x5
54#define Q_GPIO_SUBTYPE_GPIO_8CH 0x9
55#define Q_GPIO_SUBTYPE_GPIOC_8CH 0xD
Michael Bohan6b901572012-05-30 13:32:24 -070056
57/* mpp peripheral type and subtype values */
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +053058#define Q_MPP_TYPE 0x11
59#define Q_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3
60#define Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT 0x4
61#define Q_MPP_SUBTYPE_4CH_NO_SINK 0x5
62#define Q_MPP_SUBTYPE_ULT_4CH_NO_SINK 0x6
63#define Q_MPP_SUBTYPE_4CH_FULL_FUNC 0x7
64#define Q_MPP_SUBTYPE_8CH_FULL_FUNC 0xF
Michael Bohan0ba63b82012-02-06 13:42:34 -080065
66/* control register base address offsets */
Michael Bohane25e15f2012-04-12 17:28:26 -070067#define Q_REG_MODE_CTL 0x40
Michael Bohan6b901572012-05-30 13:32:24 -070068#define Q_REG_DIG_VIN_CTL 0x41
Michael Bohane25e15f2012-04-12 17:28:26 -070069#define Q_REG_DIG_PULL_CTL 0x42
70#define Q_REG_DIG_IN_CTL 0x43
Michael Bohane25e15f2012-04-12 17:28:26 -070071#define Q_REG_DIG_OUT_CTL 0x45
72#define Q_REG_EN_CTL 0x46
Michael Bohan6b901572012-05-30 13:32:24 -070073#define Q_REG_AOUT_CTL 0x48
74#define Q_REG_AIN_CTL 0x4A
75#define Q_REG_SINK_CTL 0x4C
Michael Bohan0ba63b82012-02-06 13:42:34 -080076
77/* control register regs array indices */
Michael Bohane25e15f2012-04-12 17:28:26 -070078#define Q_REG_I_MODE_CTL 0
Michael Bohan6b901572012-05-30 13:32:24 -070079#define Q_REG_I_DIG_VIN_CTL 1
Michael Bohane25e15f2012-04-12 17:28:26 -070080#define Q_REG_I_DIG_PULL_CTL 2
81#define Q_REG_I_DIG_IN_CTL 3
Michael Bohane25e15f2012-04-12 17:28:26 -070082#define Q_REG_I_DIG_OUT_CTL 5
83#define Q_REG_I_EN_CTL 6
Michael Bohan6b901572012-05-30 13:32:24 -070084#define Q_REG_I_AOUT_CTL 8
85#define Q_REG_I_AIN_CTL 10
86#define Q_REG_I_SINK_CTL 12
Michael Bohan0ba63b82012-02-06 13:42:34 -080087
Michael Bohane25e15f2012-04-12 17:28:26 -070088/* control reg: mode */
Michael Bohan0ba63b82012-02-06 13:42:34 -080089#define Q_REG_OUT_INVERT_SHIFT 0
90#define Q_REG_OUT_INVERT_MASK 0x1
91#define Q_REG_SRC_SEL_SHIFT 1
92#define Q_REG_SRC_SEL_MASK 0xE
Michael Bohane25e15f2012-04-12 17:28:26 -070093#define Q_REG_MODE_SEL_SHIFT 4
94#define Q_REG_MODE_SEL_MASK 0x70
95
96/* control reg: dig_vin */
97#define Q_REG_VIN_SHIFT 0
98#define Q_REG_VIN_MASK 0x7
99
100/* control reg: dig_pull */
101#define Q_REG_PULL_SHIFT 0
102#define Q_REG_PULL_MASK 0x7
103
104/* control reg: dig_out */
105#define Q_REG_OUT_STRENGTH_SHIFT 0
106#define Q_REG_OUT_STRENGTH_MASK 0x3
107#define Q_REG_OUT_TYPE_SHIFT 4
108#define Q_REG_OUT_TYPE_MASK 0x30
109
110/* control reg: en */
Michael Bohan0ba63b82012-02-06 13:42:34 -0800111#define Q_REG_MASTER_EN_SHIFT 7
112#define Q_REG_MASTER_EN_MASK 0x80
113
Michael Bohan6b901572012-05-30 13:32:24 -0700114/* control reg: ana_out */
115#define Q_REG_AOUT_REF_SHIFT 0
116#define Q_REG_AOUT_REF_MASK 0x7
117
118/* control reg: ana_in */
119#define Q_REG_AIN_ROUTE_SHIFT 0
120#define Q_REG_AIN_ROUTE_MASK 0x7
121
122/* control reg: sink */
123#define Q_REG_CS_OUT_SHIFT 0
124#define Q_REG_CS_OUT_MASK 0x7
125
Michael Bohana05f4552012-05-24 15:58:11 -0700126enum qpnp_pin_param_type {
Michael Bohan6b901572012-05-30 13:32:24 -0700127 Q_PIN_CFG_MODE,
Michael Bohana05f4552012-05-24 15:58:11 -0700128 Q_PIN_CFG_OUTPUT_TYPE,
129 Q_PIN_CFG_INVERT,
130 Q_PIN_CFG_PULL,
131 Q_PIN_CFG_VIN_SEL,
132 Q_PIN_CFG_OUT_STRENGTH,
Michael Bohand734fb22012-10-30 14:19:22 -0700133 Q_PIN_CFG_SRC_SEL,
Michael Bohana05f4552012-05-24 15:58:11 -0700134 Q_PIN_CFG_MASTER_EN,
Michael Bohan6b901572012-05-30 13:32:24 -0700135 Q_PIN_CFG_AOUT_REF,
136 Q_PIN_CFG_AIN_ROUTE,
137 Q_PIN_CFG_CS_OUT,
Michael Bohana05f4552012-05-24 15:58:11 -0700138 Q_PIN_CFG_INVALID,
Michael Bohana19dced2012-04-24 13:14:50 -0700139};
140
Michael Bohana05f4552012-05-24 15:58:11 -0700141#define Q_NUM_PARAMS Q_PIN_CFG_INVALID
Michael Bohana19dced2012-04-24 13:14:50 -0700142
Michael Bohane25e15f2012-04-12 17:28:26 -0700143/* param error checking */
Michael Bohan1c3d94f2012-08-11 14:33:03 -0700144#define QPNP_PIN_GPIO_MODE_INVALID 3
145#define QPNP_PIN_MPP_MODE_INVALID 7
Michael Bohana05f4552012-05-24 15:58:11 -0700146#define QPNP_PIN_INVERT_INVALID 2
147#define QPNP_PIN_OUT_BUF_INVALID 3
Michael Bohan6b901572012-05-30 13:32:24 -0700148#define QPNP_PIN_VIN_4CH_INVALID 5
149#define QPNP_PIN_VIN_8CH_INVALID 8
150#define QPNP_PIN_GPIO_PULL_INVALID 6
151#define QPNP_PIN_MPP_PULL_INVALID 4
Michael Bohana05f4552012-05-24 15:58:11 -0700152#define QPNP_PIN_OUT_STRENGTH_INVALID 4
153#define QPNP_PIN_SRC_INVALID 8
154#define QPNP_PIN_MASTER_INVALID 2
Michael Bohan6b901572012-05-30 13:32:24 -0700155#define QPNP_PIN_AOUT_REF_INVALID 8
156#define QPNP_PIN_AIN_ROUTE_INVALID 8
157#define QPNP_PIN_CS_OUT_INVALID 8
Michael Bohan0ba63b82012-02-06 13:42:34 -0800158
Michael Bohana05f4552012-05-24 15:58:11 -0700159struct qpnp_pin_spec {
Michael Bohan0ba63b82012-02-06 13:42:34 -0800160 uint8_t slave; /* 0-15 */
161 uint16_t offset; /* 0-255 */
162 uint32_t gpio_chip_idx; /* offset from gpio_chip base */
Michael Bohana05f4552012-05-24 15:58:11 -0700163 uint32_t pmic_pin; /* PMIC pin number */
Michael Bohan0ba63b82012-02-06 13:42:34 -0800164 int irq; /* logical IRQ number */
165 u8 regs[Q_NUM_CTL_REGS]; /* Control regs */
Michael Bohan6b901572012-05-30 13:32:24 -0700166 u8 num_ctl_regs; /* usable number on this pin */
Michael Bohane25e15f2012-04-12 17:28:26 -0700167 u8 type; /* peripheral type */
168 u8 subtype; /* peripheral subtype */
Michael Bohanbfe64c72012-08-24 16:57:26 -0700169 u8 dig_major_rev;
Michael Bohande3942a2012-04-17 15:28:01 -0700170 struct device_node *node;
Michael Bohana05f4552012-05-24 15:58:11 -0700171 enum qpnp_pin_param_type params[Q_NUM_PARAMS];
172 struct qpnp_pin_chip *q_chip;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800173};
174
Michael Bohana05f4552012-05-24 15:58:11 -0700175struct qpnp_pin_chip {
Michael Bohan0ba63b82012-02-06 13:42:34 -0800176 struct gpio_chip gpio_chip;
177 struct spmi_device *spmi;
Michael Bohana05f4552012-05-24 15:58:11 -0700178 struct qpnp_pin_spec **pmic_pins;
179 struct qpnp_pin_spec **chip_gpios;
180 uint32_t pmic_pin_lowest;
181 uint32_t pmic_pin_highest;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800182 struct device_node *int_ctrl;
183 struct list_head chip_list;
Michael Bohana19dced2012-04-24 13:14:50 -0700184 struct dentry *dfs_dir;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800185};
186
Michael Bohana05f4552012-05-24 15:58:11 -0700187static LIST_HEAD(qpnp_pin_chips);
188static DEFINE_MUTEX(qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800189
Michael Bohana05f4552012-05-24 15:58:11 -0700190static inline void qpnp_pmic_pin_set_spec(struct qpnp_pin_chip *q_chip,
191 uint32_t pmic_pin,
192 struct qpnp_pin_spec *spec)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800193{
Michael Bohana05f4552012-05-24 15:58:11 -0700194 q_chip->pmic_pins[pmic_pin - q_chip->pmic_pin_lowest] = spec;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800195}
196
Michael Bohana05f4552012-05-24 15:58:11 -0700197static inline struct qpnp_pin_spec *qpnp_pmic_pin_get_spec(
198 struct qpnp_pin_chip *q_chip,
199 uint32_t pmic_pin)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800200{
Michael Bohana05f4552012-05-24 15:58:11 -0700201 if (pmic_pin < q_chip->pmic_pin_lowest ||
202 pmic_pin > q_chip->pmic_pin_highest)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800203 return NULL;
204
Michael Bohana05f4552012-05-24 15:58:11 -0700205 return q_chip->pmic_pins[pmic_pin - q_chip->pmic_pin_lowest];
Michael Bohan0ba63b82012-02-06 13:42:34 -0800206}
207
Michael Bohana05f4552012-05-24 15:58:11 -0700208static inline struct qpnp_pin_spec *qpnp_chip_gpio_get_spec(
209 struct qpnp_pin_chip *q_chip,
Michael Bohan0ba63b82012-02-06 13:42:34 -0800210 uint32_t chip_gpio)
211{
212 if (chip_gpio > q_chip->gpio_chip.ngpio)
213 return NULL;
214
215 return q_chip->chip_gpios[chip_gpio];
216}
217
Michael Bohana05f4552012-05-24 15:58:11 -0700218static inline void qpnp_chip_gpio_set_spec(struct qpnp_pin_chip *q_chip,
Michael Bohan0ba63b82012-02-06 13:42:34 -0800219 uint32_t chip_gpio,
Michael Bohana05f4552012-05-24 15:58:11 -0700220 struct qpnp_pin_spec *spec)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800221{
222 q_chip->chip_gpios[chip_gpio] = spec;
223}
224
Michael Bohan58d07802012-05-31 15:37:55 -0700225/*
226 * Determines whether a specified param's configuration is correct.
227 * This check is two tier. First a check is done whether the hardware
228 * supports this param and value requested. The second check validates
229 * that the configuration is correct, given the fact that the hardware
230 * supports it.
231 *
232 * Returns
Michael Bohan6b901572012-05-30 13:32:24 -0700233 * -ENXIO is the hardware does not support this param.
234 * -EINVAL if the the hardware does support this param, but the
235 * requested value is outside the supported range.
Michael Bohan58d07802012-05-31 15:37:55 -0700236 */
237static int qpnp_pin_check_config(enum qpnp_pin_param_type idx,
238 struct qpnp_pin_spec *q_spec, uint32_t val)
239{
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530240 u8 subtype = q_spec->subtype;
241
Michael Bohan58d07802012-05-31 15:37:55 -0700242 switch (idx) {
Michael Bohan6b901572012-05-30 13:32:24 -0700243 case Q_PIN_CFG_MODE:
Michael Bohan1c3d94f2012-08-11 14:33:03 -0700244 if (q_spec->type == Q_GPIO_TYPE &&
245 val >= QPNP_PIN_GPIO_MODE_INVALID)
246 return -EINVAL;
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530247 else if (q_spec->type == Q_MPP_TYPE) {
248 if (val >= QPNP_PIN_MPP_MODE_INVALID)
Michael Bohan1c3d94f2012-08-11 14:33:03 -0700249 return -EINVAL;
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530250 if ((subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
251 subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK) &&
252 (val == QPNP_PIN_MODE_BIDIR))
253 return -ENXIO;
254 }
Michael Bohan58d07802012-05-31 15:37:55 -0700255 break;
256 case Q_PIN_CFG_OUTPUT_TYPE:
Michael Bohan6b901572012-05-30 13:32:24 -0700257 if (q_spec->type != Q_GPIO_TYPE)
Michael Bohan58d07802012-05-31 15:37:55 -0700258 return -ENXIO;
Michael Bohan6b901572012-05-30 13:32:24 -0700259 if ((val == QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS ||
260 val == QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS) &&
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530261 (subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
262 (subtype == Q_GPIO_SUBTYPE_GPIOC_8CH)))
Michael Bohan6b901572012-05-30 13:32:24 -0700263 return -EINVAL;
Michael Bohan58d07802012-05-31 15:37:55 -0700264 else if (val >= QPNP_PIN_OUT_BUF_INVALID)
265 return -EINVAL;
266 break;
267 case Q_PIN_CFG_INVERT:
268 if (val >= QPNP_PIN_INVERT_INVALID)
269 return -EINVAL;
270 break;
271 case Q_PIN_CFG_PULL:
Michael Bohan6b901572012-05-30 13:32:24 -0700272 if (q_spec->type == Q_GPIO_TYPE &&
273 val >= QPNP_PIN_GPIO_PULL_INVALID)
274 return -EINVAL;
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530275 if (q_spec->type == Q_MPP_TYPE) {
276 if (val >= QPNP_PIN_MPP_PULL_INVALID)
277 return -EINVAL;
278 if (subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
279 subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK)
280 return -ENXIO;
281 }
Michael Bohan58d07802012-05-31 15:37:55 -0700282 break;
283 case Q_PIN_CFG_VIN_SEL:
Michael Bohan6b901572012-05-30 13:32:24 -0700284 if (val >= QPNP_PIN_VIN_8CH_INVALID)
Michael Bohan58d07802012-05-31 15:37:55 -0700285 return -EINVAL;
Michael Bohan6b901572012-05-30 13:32:24 -0700286 else if (val >= QPNP_PIN_VIN_4CH_INVALID) {
287 if (q_spec->type == Q_GPIO_TYPE &&
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530288 (subtype == Q_GPIO_SUBTYPE_GPIO_4CH ||
289 subtype == Q_GPIO_SUBTYPE_GPIOC_4CH))
Michael Bohan6b901572012-05-30 13:32:24 -0700290 return -EINVAL;
291 if (q_spec->type == Q_MPP_TYPE &&
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530292 (subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
293 subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
294 subtype == Q_MPP_SUBTYPE_4CH_FULL_FUNC ||
295 subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
296 subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK))
Michael Bohan6b901572012-05-30 13:32:24 -0700297 return -EINVAL;
298 }
Michael Bohan58d07802012-05-31 15:37:55 -0700299 break;
300 case Q_PIN_CFG_OUT_STRENGTH:
Michael Bohan6b901572012-05-30 13:32:24 -0700301 if (q_spec->type != Q_GPIO_TYPE)
302 return -ENXIO;
Michael Bohan58d07802012-05-31 15:37:55 -0700303 if (val >= QPNP_PIN_OUT_STRENGTH_INVALID ||
304 val == 0)
305 return -EINVAL;
306 break;
Michael Bohand734fb22012-10-30 14:19:22 -0700307 case Q_PIN_CFG_SRC_SEL:
Michael Bohan6b901572012-05-30 13:32:24 -0700308 if (q_spec->type == Q_MPP_TYPE &&
309 (val == QPNP_PIN_SEL_FUNC_1 ||
310 val == QPNP_PIN_SEL_FUNC_2))
311 return -EINVAL;
Michael Bohan58d07802012-05-31 15:37:55 -0700312 if (val >= QPNP_PIN_SRC_INVALID)
313 return -EINVAL;
314 break;
315 case Q_PIN_CFG_MASTER_EN:
316 if (val >= QPNP_PIN_MASTER_INVALID)
317 return -EINVAL;
318 break;
Michael Bohan6b901572012-05-30 13:32:24 -0700319 case Q_PIN_CFG_AOUT_REF:
320 if (q_spec->type != Q_MPP_TYPE)
321 return -ENXIO;
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530322 if (subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
323 subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
Michael Bohan6b901572012-05-30 13:32:24 -0700324 return -ENXIO;
325 if (val >= QPNP_PIN_AOUT_REF_INVALID)
326 return -EINVAL;
327 break;
328 case Q_PIN_CFG_AIN_ROUTE:
329 if (q_spec->type != Q_MPP_TYPE)
330 return -ENXIO;
331 if (val >= QPNP_PIN_AIN_ROUTE_INVALID)
332 return -EINVAL;
333 break;
334 case Q_PIN_CFG_CS_OUT:
335 if (q_spec->type != Q_MPP_TYPE)
336 return -ENXIO;
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530337 if (subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
338 subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK)
Michael Bohan6b901572012-05-30 13:32:24 -0700339 return -ENXIO;
340 if (val >= QPNP_PIN_CS_OUT_INVALID)
341 return -EINVAL;
342 break;
343
Michael Bohan58d07802012-05-31 15:37:55 -0700344 default:
345 pr_err("invalid param type %u specified\n", idx);
346 return -EINVAL;
347 }
348 return 0;
349}
350
351#define Q_CHK_INVALID(idx, q_spec, val) \
352 (qpnp_pin_check_config(idx, q_spec, val) == -EINVAL)
353
354static int qpnp_pin_check_constraints(struct qpnp_pin_spec *q_spec,
355 struct qpnp_pin_cfg *param)
Michael Bohane25e15f2012-04-12 17:28:26 -0700356{
Michael Bohan6b901572012-05-30 13:32:24 -0700357 int pin = q_spec->pmic_pin;
358 const char *name;
Michael Bohane25e15f2012-04-12 17:28:26 -0700359
Michael Bohan6b901572012-05-30 13:32:24 -0700360 name = (q_spec->type == Q_GPIO_TYPE) ? "gpio" : "mpp";
361
362 if (Q_CHK_INVALID(Q_PIN_CFG_MODE, q_spec, param->mode))
Michael Bohan91c5a042012-08-11 13:29:42 -0700363 pr_err("invalid direction value %d for %s %d\n",
364 param->mode, name, pin);
Michael Bohan58d07802012-05-31 15:37:55 -0700365 else if (Q_CHK_INVALID(Q_PIN_CFG_INVERT, q_spec, param->invert))
Michael Bohan91c5a042012-08-11 13:29:42 -0700366 pr_err("invalid invert polarity value %d for %s %d\n",
367 param->invert, name, pin);
Michael Bohand734fb22012-10-30 14:19:22 -0700368 else if (Q_CHK_INVALID(Q_PIN_CFG_SRC_SEL, q_spec, param->src_sel))
Michael Bohan91c5a042012-08-11 13:29:42 -0700369 pr_err("invalid source select value %d for %s %d\n",
Michael Bohand734fb22012-10-30 14:19:22 -0700370 param->src_sel, name, pin);
Michael Bohan58d07802012-05-31 15:37:55 -0700371 else if (Q_CHK_INVALID(Q_PIN_CFG_OUT_STRENGTH,
372 q_spec, param->out_strength))
Michael Bohan91c5a042012-08-11 13:29:42 -0700373 pr_err("invalid out strength value %d for %s %d\n",
374 param->out_strength, name, pin);
Michael Bohan58d07802012-05-31 15:37:55 -0700375 else if (Q_CHK_INVALID(Q_PIN_CFG_OUTPUT_TYPE,
376 q_spec, param->output_type))
Michael Bohan91c5a042012-08-11 13:29:42 -0700377 pr_err("invalid out type value %d for %s %d\n",
378 param->output_type, name, pin);
Michael Bohan58d07802012-05-31 15:37:55 -0700379 else if (Q_CHK_INVALID(Q_PIN_CFG_VIN_SEL, q_spec, param->vin_sel))
Michael Bohan91c5a042012-08-11 13:29:42 -0700380 pr_err("invalid vin select %d value for %s %d\n",
381 param->vin_sel, name, pin);
Michael Bohan58d07802012-05-31 15:37:55 -0700382 else if (Q_CHK_INVALID(Q_PIN_CFG_PULL, q_spec, param->pull))
Michael Bohan91c5a042012-08-11 13:29:42 -0700383 pr_err("invalid pull value %d for pin %s %d\n",
384 param->pull, name, pin);
Michael Bohan58d07802012-05-31 15:37:55 -0700385 else if (Q_CHK_INVALID(Q_PIN_CFG_MASTER_EN, q_spec, param->master_en))
Michael Bohan91c5a042012-08-11 13:29:42 -0700386 pr_err("invalid master_en value %d for %s %d\n",
387 param->master_en, name, pin);
Michael Bohan6b901572012-05-30 13:32:24 -0700388 else if (Q_CHK_INVALID(Q_PIN_CFG_AOUT_REF, q_spec, param->aout_ref))
Michael Bohan91c5a042012-08-11 13:29:42 -0700389 pr_err("invalid aout_reg value %d for %s %d\n",
390 param->aout_ref, name, pin);
Michael Bohan6b901572012-05-30 13:32:24 -0700391 else if (Q_CHK_INVALID(Q_PIN_CFG_AIN_ROUTE, q_spec, param->ain_route))
Michael Bohan91c5a042012-08-11 13:29:42 -0700392 pr_err("invalid ain_route value %d for %s %d\n",
393 param->ain_route, name, pin);
Michael Bohan6b901572012-05-30 13:32:24 -0700394 else if (Q_CHK_INVALID(Q_PIN_CFG_CS_OUT, q_spec, param->cs_out))
Michael Bohan91c5a042012-08-11 13:29:42 -0700395 pr_err("invalid cs_out value %d for %s %d\n",
396 param->cs_out, name, pin);
Michael Bohane25e15f2012-04-12 17:28:26 -0700397 else
398 return 0;
399
400 return -EINVAL;
401}
402
Michael Bohande3942a2012-04-17 15:28:01 -0700403static inline u8 q_reg_get(u8 *reg, int shift, int mask)
404{
405 return (*reg & mask) >> shift;
406}
407
Michael Bohane25e15f2012-04-12 17:28:26 -0700408static inline void q_reg_set(u8 *reg, int shift, int mask, int value)
409{
410 *reg |= (value << shift) & mask;
411}
412
413static inline void q_reg_clr_set(u8 *reg, int shift, int mask, int value)
414{
415 *reg &= ~mask;
416 *reg |= (value << shift) & mask;
417}
418
Michael Bohan6b901572012-05-30 13:32:24 -0700419/*
420 * Calculate the minimum number of registers that must be read / written
421 * in order to satisfy the full feature set of the given pin.
422 */
423static int qpnp_pin_ctl_regs_init(struct qpnp_pin_spec *q_spec)
424{
425 if (q_spec->type == Q_GPIO_TYPE)
426 q_spec->num_ctl_regs = 7;
427 else if (q_spec->type == Q_MPP_TYPE)
428 switch (q_spec->subtype) {
429 case Q_MPP_SUBTYPE_4CH_NO_SINK:
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530430 case Q_MPP_SUBTYPE_ULT_4CH_NO_SINK:
Michael Bohan6b901572012-05-30 13:32:24 -0700431 q_spec->num_ctl_regs = 12;
432 break;
433 case Q_MPP_SUBTYPE_4CH_NO_ANA_OUT:
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +0530434 case Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
Michael Bohan6b901572012-05-30 13:32:24 -0700435 case Q_MPP_SUBTYPE_4CH_FULL_FUNC:
436 case Q_MPP_SUBTYPE_8CH_FULL_FUNC:
437 q_spec->num_ctl_regs = 13;
438 break;
439 default:
440 pr_err("Invalid MPP subtype 0x%x\n", q_spec->subtype);
441 return -EINVAL;
442 }
443 else {
444 pr_err("Invalid type 0x%x\n", q_spec->type);
445 return -EINVAL;
446 }
447 return 0;
448}
449
450static int qpnp_pin_read_regs(struct qpnp_pin_chip *q_chip,
Michael Bohand3cf9b02012-08-15 23:23:52 -0700451 struct qpnp_pin_spec *q_spec)
Michael Bohan6b901572012-05-30 13:32:24 -0700452{
453 int bytes_left = q_spec->num_ctl_regs;
454 int rc;
Michael Bohand3cf9b02012-08-15 23:23:52 -0700455 char *buf_p = &q_spec->regs[0];
456 u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
Michael Bohan6b901572012-05-30 13:32:24 -0700457
458 while (bytes_left > 0) {
459 rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
Michael Bohand3cf9b02012-08-15 23:23:52 -0700460 reg_addr, buf_p, bytes_left < 8 ? bytes_left : 8);
Michael Bohan6b901572012-05-30 13:32:24 -0700461 if (rc)
462 return rc;
463 bytes_left -= 8;
Michael Bohand3cf9b02012-08-15 23:23:52 -0700464 buf_p += 8;
465 reg_addr += 8;
Michael Bohan6b901572012-05-30 13:32:24 -0700466 }
467 return 0;
468}
469
470static int qpnp_pin_write_regs(struct qpnp_pin_chip *q_chip,
Michael Bohand3cf9b02012-08-15 23:23:52 -0700471 struct qpnp_pin_spec *q_spec)
Michael Bohan6b901572012-05-30 13:32:24 -0700472{
473 int bytes_left = q_spec->num_ctl_regs;
474 int rc;
Michael Bohand3cf9b02012-08-15 23:23:52 -0700475 char *buf_p = &q_spec->regs[0];
476 u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
Michael Bohan6b901572012-05-30 13:32:24 -0700477
478 while (bytes_left > 0) {
479 rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
Michael Bohand3cf9b02012-08-15 23:23:52 -0700480 reg_addr, buf_p, bytes_left < 8 ? bytes_left : 8);
Michael Bohan6b901572012-05-30 13:32:24 -0700481 if (rc)
482 return rc;
483 bytes_left -= 8;
Michael Bohand3cf9b02012-08-15 23:23:52 -0700484 buf_p += 8;
485 reg_addr += 8;
Michael Bohan6b901572012-05-30 13:32:24 -0700486 }
487 return 0;
488}
489
Michael Bohana05f4552012-05-24 15:58:11 -0700490static int qpnp_pin_cache_regs(struct qpnp_pin_chip *q_chip,
Michael Bohan6b901572012-05-30 13:32:24 -0700491 struct qpnp_pin_spec *q_spec)
Michael Bohande3942a2012-04-17 15:28:01 -0700492{
493 int rc;
494 struct device *dev = &q_chip->spmi->dev;
495
Michael Bohand3cf9b02012-08-15 23:23:52 -0700496 rc = qpnp_pin_read_regs(q_chip, q_spec);
Michael Bohande3942a2012-04-17 15:28:01 -0700497 if (rc)
498 dev_err(dev, "%s: unable to read control regs\n", __func__);
499
500 return rc;
501}
502
Michael Bohan58d07802012-05-31 15:37:55 -0700503#define Q_HAVE_HW_SP(idx, q_spec, val) \
504 (qpnp_pin_check_config(idx, q_spec, val) == 0)
505
Michael Bohana05f4552012-05-24 15:58:11 -0700506static int _qpnp_pin_config(struct qpnp_pin_chip *q_chip,
Michael Bohan6b901572012-05-30 13:32:24 -0700507 struct qpnp_pin_spec *q_spec,
508 struct qpnp_pin_cfg *param)
Michael Bohane25e15f2012-04-12 17:28:26 -0700509{
510 struct device *dev = &q_chip->spmi->dev;
511 int rc;
512
Michael Bohan58d07802012-05-31 15:37:55 -0700513 rc = qpnp_pin_check_constraints(q_spec, param);
Michael Bohane25e15f2012-04-12 17:28:26 -0700514 if (rc)
515 goto gpio_cfg;
516
Michael Bohan6b901572012-05-30 13:32:24 -0700517 /* set mode */
518 if (Q_HAVE_HW_SP(Q_PIN_CFG_MODE, q_spec, param->mode))
Michael Bohan58d07802012-05-31 15:37:55 -0700519 q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700520 Q_REG_MODE_SEL_SHIFT, Q_REG_MODE_SEL_MASK,
Michael Bohan6b901572012-05-30 13:32:24 -0700521 param->mode);
Michael Bohane25e15f2012-04-12 17:28:26 -0700522
523 /* output specific configuration */
Michael Bohan58d07802012-05-31 15:37:55 -0700524 if (Q_HAVE_HW_SP(Q_PIN_CFG_INVERT, q_spec, param->invert))
525 q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700526 Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK,
527 param->invert);
Michael Bohand734fb22012-10-30 14:19:22 -0700528 if (Q_HAVE_HW_SP(Q_PIN_CFG_SRC_SEL, q_spec, param->src_sel))
Michael Bohan58d07802012-05-31 15:37:55 -0700529 q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700530 Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK,
Michael Bohand734fb22012-10-30 14:19:22 -0700531 param->src_sel);
Michael Bohan58d07802012-05-31 15:37:55 -0700532 if (Q_HAVE_HW_SP(Q_PIN_CFG_OUT_STRENGTH, q_spec, param->out_strength))
533 q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700534 Q_REG_OUT_STRENGTH_SHIFT, Q_REG_OUT_STRENGTH_MASK,
535 param->out_strength);
Michael Bohan58d07802012-05-31 15:37:55 -0700536 if (Q_HAVE_HW_SP(Q_PIN_CFG_OUTPUT_TYPE, q_spec, param->output_type))
537 q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700538 Q_REG_OUT_TYPE_SHIFT, Q_REG_OUT_TYPE_MASK,
539 param->output_type);
540
541 /* config applicable for both input / output */
Michael Bohan58d07802012-05-31 15:37:55 -0700542 if (Q_HAVE_HW_SP(Q_PIN_CFG_VIN_SEL, q_spec, param->vin_sel))
543 q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700544 Q_REG_VIN_SHIFT, Q_REG_VIN_MASK,
545 param->vin_sel);
Michael Bohan58d07802012-05-31 15:37:55 -0700546 if (Q_HAVE_HW_SP(Q_PIN_CFG_PULL, q_spec, param->pull))
547 q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_PULL_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700548 Q_REG_PULL_SHIFT, Q_REG_PULL_MASK,
549 param->pull);
Michael Bohan58d07802012-05-31 15:37:55 -0700550 if (Q_HAVE_HW_SP(Q_PIN_CFG_MASTER_EN, q_spec, param->master_en))
551 q_reg_clr_set(&q_spec->regs[Q_REG_I_EN_CTL],
Michael Bohane25e15f2012-04-12 17:28:26 -0700552 Q_REG_MASTER_EN_SHIFT, Q_REG_MASTER_EN_MASK,
553 param->master_en);
554
Michael Bohan6b901572012-05-30 13:32:24 -0700555 /* mpp specific config */
556 if (Q_HAVE_HW_SP(Q_PIN_CFG_AOUT_REF, q_spec, param->aout_ref))
557 q_reg_clr_set(&q_spec->regs[Q_REG_I_AOUT_CTL],
558 Q_REG_AOUT_REF_SHIFT, Q_REG_AOUT_REF_MASK,
559 param->aout_ref);
560 if (Q_HAVE_HW_SP(Q_PIN_CFG_AIN_ROUTE, q_spec, param->ain_route))
561 q_reg_clr_set(&q_spec->regs[Q_REG_I_AIN_CTL],
562 Q_REG_AIN_ROUTE_SHIFT, Q_REG_AIN_ROUTE_MASK,
563 param->ain_route);
564 if (Q_HAVE_HW_SP(Q_PIN_CFG_CS_OUT, q_spec, param->cs_out))
565 q_reg_clr_set(&q_spec->regs[Q_REG_I_SINK_CTL],
566 Q_REG_CS_OUT_SHIFT, Q_REG_CS_OUT_MASK,
567 param->cs_out);
568
Michael Bohand3cf9b02012-08-15 23:23:52 -0700569 rc = qpnp_pin_write_regs(q_chip, q_spec);
Michael Bohane25e15f2012-04-12 17:28:26 -0700570 if (rc) {
Michael Bohan6ea2cd22012-05-29 15:40:18 -0700571 dev_err(&q_chip->spmi->dev, "%s: unable to write master enable\n",
572 __func__);
Michael Bohane25e15f2012-04-12 17:28:26 -0700573 goto gpio_cfg;
574 }
575
576 return 0;
577
578gpio_cfg:
Michael Bohan91c5a042012-08-11 13:29:42 -0700579 dev_err(dev, "%s: unable to set default config for pmic pin %d\n",
Michael Bohana05f4552012-05-24 15:58:11 -0700580 __func__, q_spec->pmic_pin);
Michael Bohane25e15f2012-04-12 17:28:26 -0700581
582 return rc;
583}
584
Michael Bohana05f4552012-05-24 15:58:11 -0700585int qpnp_pin_config(int gpio, struct qpnp_pin_cfg *param)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800586{
587 int rc, chip_offset;
Michael Bohana05f4552012-05-24 15:58:11 -0700588 struct qpnp_pin_chip *q_chip;
589 struct qpnp_pin_spec *q_spec = NULL;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800590 struct gpio_chip *gpio_chip;
591
592 if (param == NULL)
593 return -EINVAL;
594
Michael Bohana05f4552012-05-24 15:58:11 -0700595 mutex_lock(&qpnp_pin_chips_lock);
596 list_for_each_entry(q_chip, &qpnp_pin_chips, chip_list) {
Michael Bohan0ba63b82012-02-06 13:42:34 -0800597 gpio_chip = &q_chip->gpio_chip;
598 if (gpio >= gpio_chip->base
599 && gpio < gpio_chip->base + gpio_chip->ngpio) {
600 chip_offset = gpio - gpio_chip->base;
601 q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset);
602 if (WARN_ON(!q_spec)) {
Michael Bohana05f4552012-05-24 15:58:11 -0700603 mutex_unlock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800604 return -ENODEV;
605 }
606 break;
607 }
608 }
Michael Bohana05f4552012-05-24 15:58:11 -0700609 mutex_unlock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800610
Zhenhua Huang30acf242013-07-11 01:52:36 +0800611 if (!q_spec)
612 return -ENODEV;
613
Michael Bohana05f4552012-05-24 15:58:11 -0700614 rc = _qpnp_pin_config(q_chip, q_spec, param);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800615
616 return rc;
617}
Michael Bohana05f4552012-05-24 15:58:11 -0700618EXPORT_SYMBOL(qpnp_pin_config);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800619
Michael Bohan6ea2cd22012-05-29 15:40:18 -0700620#define Q_MAX_CHIP_NAME 128
621int qpnp_pin_map(const char *name, uint32_t pmic_pin)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800622{
Michael Bohana05f4552012-05-24 15:58:11 -0700623 struct qpnp_pin_chip *q_chip;
624 struct qpnp_pin_spec *q_spec = NULL;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800625
Michael Bohana05f4552012-05-24 15:58:11 -0700626 mutex_lock(&qpnp_pin_chips_lock);
627 list_for_each_entry(q_chip, &qpnp_pin_chips, chip_list) {
Michael Bohan6ea2cd22012-05-29 15:40:18 -0700628 if (strncmp(q_chip->gpio_chip.label, name,
629 Q_MAX_CHIP_NAME) != 0)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800630 continue;
Michael Bohana05f4552012-05-24 15:58:11 -0700631 if (q_chip->pmic_pin_lowest <= pmic_pin &&
632 q_chip->pmic_pin_highest >= pmic_pin) {
633 q_spec = qpnp_pmic_pin_get_spec(q_chip, pmic_pin);
634 mutex_unlock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800635 if (WARN_ON(!q_spec))
636 return -ENODEV;
637 return q_chip->gpio_chip.base + q_spec->gpio_chip_idx;
638 }
639 }
Michael Bohana05f4552012-05-24 15:58:11 -0700640 mutex_unlock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800641 return -EINVAL;
642}
Michael Bohana05f4552012-05-24 15:58:11 -0700643EXPORT_SYMBOL(qpnp_pin_map);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800644
Michael Bohana05f4552012-05-24 15:58:11 -0700645static int qpnp_pin_to_irq(struct gpio_chip *gpio_chip, unsigned offset)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800646{
Michael Bohana05f4552012-05-24 15:58:11 -0700647 struct qpnp_pin_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
648 struct qpnp_pin_spec *q_spec;
Abhijeet Dharmapurikarea799552014-01-08 20:16:37 -0800649 u32 intspec[3];
Michael Bohan0ba63b82012-02-06 13:42:34 -0800650
651 q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
652 if (!q_spec)
653 return -EINVAL;
654
Abhijeet Dharmapurikarea799552014-01-08 20:16:37 -0800655 /* if we have mapped this pin previously return the virq */
656 if (q_spec->irq)
657 return q_spec->irq;
658
659 /* call into irq_domain to get irq mapping */
660 intspec[0] = q_chip->spmi->sid;
661 intspec[1] = (q_spec->offset >> 8) & 0xFF;
662 intspec[2] = 0;
663 q_spec->irq = irq_create_of_mapping(q_chip->int_ctrl, intspec, 3);
664 if (!q_spec->irq) {
665 dev_err(&q_chip->spmi->dev, "%s: invalid irq for gpio %u\n",
666 __func__, q_spec->pmic_pin);
667 WARN_ON(1);
668 return -EINVAL;
669 }
670
Michael Bohan0ba63b82012-02-06 13:42:34 -0800671 return q_spec->irq;
672}
673
Michael Bohana05f4552012-05-24 15:58:11 -0700674static int qpnp_pin_get(struct gpio_chip *gpio_chip, unsigned offset)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800675{
676 int rc, ret_val;
Michael Bohana05f4552012-05-24 15:58:11 -0700677 struct qpnp_pin_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
678 struct qpnp_pin_spec *q_spec = NULL;
Michael Bohanffa16812012-08-15 18:11:49 -0700679 u8 buf[1], en_mask;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800680
681 if (WARN_ON(!q_chip))
682 return -ENODEV;
683
684 q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
685 if (WARN_ON(!q_spec))
686 return -ENODEV;
687
688 /* gpio val is from RT status iff input is enabled */
Michael Bohane25e15f2012-04-12 17:28:26 -0700689 if ((q_spec->regs[Q_REG_I_MODE_CTL] & Q_REG_MODE_SEL_MASK)
Michael Bohan6b901572012-05-30 13:32:24 -0700690 == QPNP_PIN_MODE_DIG_IN) {
Michael Bohan0ba63b82012-02-06 13:42:34 -0800691 rc = spmi_ext_register_readl(q_chip->spmi->ctrl, q_spec->slave,
692 Q_REG_ADDR(q_spec, Q_REG_STATUS1),
693 &buf[0], 1);
Michael Bohanffa16812012-08-15 18:11:49 -0700694
Michael Bohanbfe64c72012-08-24 16:57:26 -0700695 if (q_spec->type == Q_GPIO_TYPE && q_spec->dig_major_rev == 0)
696 en_mask = Q_REG_STATUS1_GPIO_EN_REV0_MASK;
697 else if (q_spec->type == Q_GPIO_TYPE &&
698 q_spec->dig_major_rev > 0)
699 en_mask = Q_REG_STATUS1_GPIO_EN_MASK;
700 else /* MPP */
701 en_mask = Q_REG_STATUS1_MPP_EN_MASK;
702
Michael Bohanffa16812012-08-15 18:11:49 -0700703 if (!(buf[0] & en_mask))
704 return -EPERM;
705
706 return buf[0] & Q_REG_STATUS1_VAL_MASK;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800707
708 } else {
Michael Bohane25e15f2012-04-12 17:28:26 -0700709 ret_val = (q_spec->regs[Q_REG_I_MODE_CTL] &
Michael Bohan0ba63b82012-02-06 13:42:34 -0800710 Q_REG_OUT_INVERT_MASK) >> Q_REG_OUT_INVERT_SHIFT;
711 return ret_val;
712 }
713
714 return 0;
715}
716
Michael Bohana05f4552012-05-24 15:58:11 -0700717static int __qpnp_pin_set(struct qpnp_pin_chip *q_chip,
718 struct qpnp_pin_spec *q_spec, int value)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800719{
720 int rc;
721
722 if (!q_chip || !q_spec)
723 return -EINVAL;
724
Michael Bohan0ba63b82012-02-06 13:42:34 -0800725 if (value)
Michael Bohane25e15f2012-04-12 17:28:26 -0700726 q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
727 Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK, 1);
728 else
729 q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
730 Q_REG_OUT_INVERT_SHIFT, Q_REG_OUT_INVERT_MASK, 0);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800731
732 rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
Michael Bohand3cf9b02012-08-15 23:23:52 -0700733 Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
Michael Bohane25e15f2012-04-12 17:28:26 -0700734 &q_spec->regs[Q_REG_I_MODE_CTL], 1);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800735 if (rc)
736 dev_err(&q_chip->spmi->dev, "%s: spmi write failed\n",
737 __func__);
738 return rc;
739}
740
741
Michael Bohana05f4552012-05-24 15:58:11 -0700742static void qpnp_pin_set(struct gpio_chip *gpio_chip,
Michael Bohan0ba63b82012-02-06 13:42:34 -0800743 unsigned offset, int value)
744{
Michael Bohana05f4552012-05-24 15:58:11 -0700745 struct qpnp_pin_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
746 struct qpnp_pin_spec *q_spec;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800747
748 if (WARN_ON(!q_chip))
749 return;
750
751 q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
752 if (WARN_ON(!q_spec))
753 return;
754
Michael Bohana05f4552012-05-24 15:58:11 -0700755 __qpnp_pin_set(q_chip, q_spec, value);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800756}
757
Michael Bohan6b901572012-05-30 13:32:24 -0700758static int qpnp_pin_set_mode(struct qpnp_pin_chip *q_chip,
759 struct qpnp_pin_spec *q_spec, int mode)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800760{
761 int rc;
762
763 if (!q_chip || !q_spec)
764 return -EINVAL;
765
Michael Bohan1c3d94f2012-08-11 14:33:03 -0700766 if (qpnp_pin_check_config(Q_PIN_CFG_MODE, q_spec, mode)) {
Michael Bohan6b901572012-05-30 13:32:24 -0700767 pr_err("invalid mode specification %d\n", mode);
Michael Bohane25e15f2012-04-12 17:28:26 -0700768 return -EINVAL;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800769 }
770
Michael Bohane25e15f2012-04-12 17:28:26 -0700771 q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
772 Q_REG_MODE_SEL_SHIFT,
773 Q_REG_MODE_SEL_MASK,
Michael Bohan6b901572012-05-30 13:32:24 -0700774 mode);
Michael Bohane25e15f2012-04-12 17:28:26 -0700775
Michael Bohan0ba63b82012-02-06 13:42:34 -0800776 rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
Michael Bohand3cf9b02012-08-15 23:23:52 -0700777 Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
Michael Bohane25e15f2012-04-12 17:28:26 -0700778 &q_spec->regs[Q_REG_I_MODE_CTL], 1);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800779 return rc;
780}
781
Michael Bohana05f4552012-05-24 15:58:11 -0700782static int qpnp_pin_direction_input(struct gpio_chip *gpio_chip,
Michael Bohan0ba63b82012-02-06 13:42:34 -0800783 unsigned offset)
784{
Michael Bohana05f4552012-05-24 15:58:11 -0700785 struct qpnp_pin_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
786 struct qpnp_pin_spec *q_spec;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800787
788 if (WARN_ON(!q_chip))
789 return -ENODEV;
790
791 q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
792 if (WARN_ON(!q_spec))
793 return -ENODEV;
794
Michael Bohan6b901572012-05-30 13:32:24 -0700795 return qpnp_pin_set_mode(q_chip, q_spec, QPNP_PIN_MODE_DIG_IN);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800796}
797
Michael Bohana05f4552012-05-24 15:58:11 -0700798static int qpnp_pin_direction_output(struct gpio_chip *gpio_chip,
Michael Bohan0ba63b82012-02-06 13:42:34 -0800799 unsigned offset,
800 int val)
801{
802 int rc;
Michael Bohana05f4552012-05-24 15:58:11 -0700803 struct qpnp_pin_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
804 struct qpnp_pin_spec *q_spec;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800805
806 if (WARN_ON(!q_chip))
807 return -ENODEV;
808
809 q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
810 if (WARN_ON(!q_spec))
811 return -ENODEV;
812
Michael Bohana05f4552012-05-24 15:58:11 -0700813 rc = __qpnp_pin_set(q_chip, q_spec, val);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800814 if (rc)
815 return rc;
816
Michael Bohan6b901572012-05-30 13:32:24 -0700817 rc = qpnp_pin_set_mode(q_chip, q_spec, QPNP_PIN_MODE_DIG_OUT);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800818
819 return rc;
820}
821
Michael Bohana05f4552012-05-24 15:58:11 -0700822static int qpnp_pin_of_gpio_xlate(struct gpio_chip *gpio_chip,
Michael Bohan0b24fb12012-06-01 10:30:12 -0700823 const struct of_phandle_args *gpio_spec,
824 u32 *flags)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800825{
Michael Bohana05f4552012-05-24 15:58:11 -0700826 struct qpnp_pin_chip *q_chip = dev_get_drvdata(gpio_chip->dev);
827 struct qpnp_pin_spec *q_spec;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800828
829 if (WARN_ON(gpio_chip->of_gpio_n_cells < 2)) {
Michael Bohane25e15f2012-04-12 17:28:26 -0700830 pr_err("of_gpio_n_cells < 2\n");
Michael Bohan0ba63b82012-02-06 13:42:34 -0800831 return -EINVAL;
832 }
833
Michael Bohana05f4552012-05-24 15:58:11 -0700834 q_spec = qpnp_pmic_pin_get_spec(q_chip, gpio_spec->args[0]);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800835 if (!q_spec) {
Michael Bohan0b24fb12012-06-01 10:30:12 -0700836 pr_err("no such PMIC gpio %u in device topology\n",
837 gpio_spec->args[0]);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800838 return -EINVAL;
839 }
840
841 if (flags)
Michael Bohan0b24fb12012-06-01 10:30:12 -0700842 *flags = gpio_spec->args[1];
Michael Bohan0ba63b82012-02-06 13:42:34 -0800843
844 return q_spec->gpio_chip_idx;
845}
846
Michael Bohana05f4552012-05-24 15:58:11 -0700847static int qpnp_pin_apply_config(struct qpnp_pin_chip *q_chip,
848 struct qpnp_pin_spec *q_spec)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800849{
Michael Bohana05f4552012-05-24 15:58:11 -0700850 struct qpnp_pin_cfg param;
Michael Bohande3942a2012-04-17 15:28:01 -0700851 struct device_node *node = q_spec->node;
Michael Bohan0ba63b82012-02-06 13:42:34 -0800852 int rc;
853
Michael Bohan6b901572012-05-30 13:32:24 -0700854 param.mode = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
Michael Bohande3942a2012-04-17 15:28:01 -0700855 Q_REG_MODE_SEL_SHIFT,
856 Q_REG_MODE_SEL_MASK);
857 param.output_type = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
858 Q_REG_OUT_TYPE_SHIFT,
859 Q_REG_OUT_TYPE_MASK);
860 param.invert = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
Michael Bohand3cf9b02012-08-15 23:23:52 -0700861 Q_REG_OUT_INVERT_SHIFT,
Michael Bohande3942a2012-04-17 15:28:01 -0700862 Q_REG_OUT_INVERT_MASK);
Michael Bohan26324a22013-03-20 10:15:33 -0700863 param.pull = q_reg_get(&q_spec->regs[Q_REG_I_DIG_PULL_CTL],
Michael Bohande3942a2012-04-17 15:28:01 -0700864 Q_REG_PULL_SHIFT, Q_REG_PULL_MASK);
865 param.vin_sel = q_reg_get(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
866 Q_REG_VIN_SHIFT, Q_REG_VIN_MASK);
867 param.out_strength = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
868 Q_REG_OUT_STRENGTH_SHIFT,
869 Q_REG_OUT_STRENGTH_MASK);
Michael Bohand734fb22012-10-30 14:19:22 -0700870 param.src_sel = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
Michael Bohande3942a2012-04-17 15:28:01 -0700871 Q_REG_SRC_SEL_SHIFT, Q_REG_SRC_SEL_MASK);
872 param.master_en = q_reg_get(&q_spec->regs[Q_REG_I_EN_CTL],
873 Q_REG_MASTER_EN_SHIFT,
874 Q_REG_MASTER_EN_MASK);
Michael Bohan6b901572012-05-30 13:32:24 -0700875 param.aout_ref = q_reg_get(&q_spec->regs[Q_REG_I_AOUT_CTL],
876 Q_REG_AOUT_REF_SHIFT,
877 Q_REG_AOUT_REF_MASK);
878 param.ain_route = q_reg_get(&q_spec->regs[Q_REG_I_AIN_CTL],
879 Q_REG_AIN_ROUTE_SHIFT,
880 Q_REG_AIN_ROUTE_MASK);
881 param.cs_out = q_reg_get(&q_spec->regs[Q_REG_I_SINK_CTL],
882 Q_REG_CS_OUT_SHIFT,
883 Q_REG_CS_OUT_MASK);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800884
Michael Bohan6b901572012-05-30 13:32:24 -0700885 of_property_read_u32(node, "qcom,mode",
886 &param.mode);
Michael Bohande3942a2012-04-17 15:28:01 -0700887 of_property_read_u32(node, "qcom,output-type",
888 &param.output_type);
889 of_property_read_u32(node, "qcom,invert",
890 &param.invert);
891 of_property_read_u32(node, "qcom,pull",
892 &param.pull);
893 of_property_read_u32(node, "qcom,vin-sel",
894 &param.vin_sel);
895 of_property_read_u32(node, "qcom,out-strength",
896 &param.out_strength);
Michael Bohand734fb22012-10-30 14:19:22 -0700897 of_property_read_u32(node, "qcom,src-sel",
898 &param.src_sel);
Michael Bohan6b901572012-05-30 13:32:24 -0700899 of_property_read_u32(node, "qcom,master-en",
Michael Bohande3942a2012-04-17 15:28:01 -0700900 &param.master_en);
Michael Bohan6b901572012-05-30 13:32:24 -0700901 of_property_read_u32(node, "qcom,aout-ref",
902 &param.aout_ref);
903 of_property_read_u32(node, "qcom,ain-route",
904 &param.ain_route);
905 of_property_read_u32(node, "qcom,cs-out",
906 &param.cs_out);
Michael Bohana05f4552012-05-24 15:58:11 -0700907 rc = _qpnp_pin_config(q_chip, q_spec, &param);
Michael Bohande3942a2012-04-17 15:28:01 -0700908
Michael Bohan0ba63b82012-02-06 13:42:34 -0800909 return rc;
910}
911
Michael Bohana05f4552012-05-24 15:58:11 -0700912static int qpnp_pin_free_chip(struct qpnp_pin_chip *q_chip)
Michael Bohan0ba63b82012-02-06 13:42:34 -0800913{
914 struct spmi_device *spmi = q_chip->spmi;
915 int rc, i;
916
917 if (q_chip->chip_gpios)
918 for (i = 0; i < spmi->num_dev_node; i++)
919 kfree(q_chip->chip_gpios[i]);
920
Michael Bohana05f4552012-05-24 15:58:11 -0700921 mutex_lock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800922 list_del(&q_chip->chip_list);
Michael Bohana05f4552012-05-24 15:58:11 -0700923 mutex_unlock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800924 rc = gpiochip_remove(&q_chip->gpio_chip);
925 if (rc)
926 dev_err(&q_chip->spmi->dev, "%s: unable to remove gpio\n",
927 __func__);
928 kfree(q_chip->chip_gpios);
Michael Bohana05f4552012-05-24 15:58:11 -0700929 kfree(q_chip->pmic_pins);
Michael Bohan0ba63b82012-02-06 13:42:34 -0800930 kfree(q_chip);
931 return rc;
932}
933
Michael Bohana05f4552012-05-24 15:58:11 -0700934#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
935struct qpnp_pin_reg {
Michael Bohana19dced2012-04-24 13:14:50 -0700936 uint32_t addr;
937 uint32_t idx;
938 uint32_t shift;
939 uint32_t mask;
940};
941
942static struct dentry *driver_dfs_dir;
943
Michael Bohana05f4552012-05-24 15:58:11 -0700944static int qpnp_pin_reg_attr(enum qpnp_pin_param_type type,
945 struct qpnp_pin_reg *cfg)
Michael Bohana19dced2012-04-24 13:14:50 -0700946{
947 switch (type) {
Michael Bohan6b901572012-05-30 13:32:24 -0700948 case Q_PIN_CFG_MODE:
Michael Bohana19dced2012-04-24 13:14:50 -0700949 cfg->addr = Q_REG_MODE_CTL;
950 cfg->idx = Q_REG_I_MODE_CTL;
951 cfg->shift = Q_REG_MODE_SEL_SHIFT;
952 cfg->mask = Q_REG_MODE_SEL_MASK;
953 break;
Michael Bohana05f4552012-05-24 15:58:11 -0700954 case Q_PIN_CFG_OUTPUT_TYPE:
Michael Bohana19dced2012-04-24 13:14:50 -0700955 cfg->addr = Q_REG_DIG_OUT_CTL;
956 cfg->idx = Q_REG_I_DIG_OUT_CTL;
957 cfg->shift = Q_REG_OUT_TYPE_SHIFT;
958 cfg->mask = Q_REG_OUT_TYPE_MASK;
959 break;
Michael Bohana05f4552012-05-24 15:58:11 -0700960 case Q_PIN_CFG_INVERT:
Michael Bohana19dced2012-04-24 13:14:50 -0700961 cfg->addr = Q_REG_MODE_CTL;
962 cfg->idx = Q_REG_I_MODE_CTL;
963 cfg->shift = Q_REG_OUT_INVERT_SHIFT;
964 cfg->mask = Q_REG_OUT_INVERT_MASK;
965 break;
Michael Bohana05f4552012-05-24 15:58:11 -0700966 case Q_PIN_CFG_PULL:
Michael Bohana19dced2012-04-24 13:14:50 -0700967 cfg->addr = Q_REG_DIG_PULL_CTL;
968 cfg->idx = Q_REG_I_DIG_PULL_CTL;
969 cfg->shift = Q_REG_PULL_SHIFT;
970 cfg->mask = Q_REG_PULL_MASK;
971 break;
Michael Bohana05f4552012-05-24 15:58:11 -0700972 case Q_PIN_CFG_VIN_SEL:
Michael Bohana19dced2012-04-24 13:14:50 -0700973 cfg->addr = Q_REG_DIG_VIN_CTL;
974 cfg->idx = Q_REG_I_DIG_VIN_CTL;
975 cfg->shift = Q_REG_VIN_SHIFT;
976 cfg->mask = Q_REG_VIN_MASK;
977 break;
Michael Bohana05f4552012-05-24 15:58:11 -0700978 case Q_PIN_CFG_OUT_STRENGTH:
Michael Bohana19dced2012-04-24 13:14:50 -0700979 cfg->addr = Q_REG_DIG_OUT_CTL;
980 cfg->idx = Q_REG_I_DIG_OUT_CTL;
981 cfg->shift = Q_REG_OUT_STRENGTH_SHIFT;
982 cfg->mask = Q_REG_OUT_STRENGTH_MASK;
983 break;
Michael Bohand734fb22012-10-30 14:19:22 -0700984 case Q_PIN_CFG_SRC_SEL:
Michael Bohana19dced2012-04-24 13:14:50 -0700985 cfg->addr = Q_REG_MODE_CTL;
986 cfg->idx = Q_REG_I_MODE_CTL;
987 cfg->shift = Q_REG_SRC_SEL_SHIFT;
988 cfg->mask = Q_REG_SRC_SEL_MASK;
989 break;
Michael Bohana05f4552012-05-24 15:58:11 -0700990 case Q_PIN_CFG_MASTER_EN:
Michael Bohana19dced2012-04-24 13:14:50 -0700991 cfg->addr = Q_REG_EN_CTL;
992 cfg->idx = Q_REG_I_EN_CTL;
993 cfg->shift = Q_REG_MASTER_EN_SHIFT;
994 cfg->mask = Q_REG_MASTER_EN_MASK;
995 break;
Michael Bohan6b901572012-05-30 13:32:24 -0700996 case Q_PIN_CFG_AOUT_REF:
997 cfg->addr = Q_REG_AOUT_CTL;
998 cfg->idx = Q_REG_I_AOUT_CTL;
999 cfg->shift = Q_REG_AOUT_REF_SHIFT;
1000 cfg->mask = Q_REG_AOUT_REF_MASK;
1001 break;
1002 case Q_PIN_CFG_AIN_ROUTE:
1003 cfg->addr = Q_REG_AIN_CTL;
1004 cfg->idx = Q_REG_I_AIN_CTL;
1005 cfg->shift = Q_REG_AIN_ROUTE_SHIFT;
1006 cfg->mask = Q_REG_AIN_ROUTE_MASK;
1007 break;
1008 case Q_PIN_CFG_CS_OUT:
1009 cfg->addr = Q_REG_SINK_CTL;
1010 cfg->idx = Q_REG_I_SINK_CTL;
1011 cfg->shift = Q_REG_CS_OUT_SHIFT;
1012 cfg->mask = Q_REG_CS_OUT_MASK;
1013 break;
Michael Bohana19dced2012-04-24 13:14:50 -07001014 default:
1015 return -EINVAL;
1016 }
1017
1018 return 0;
1019}
1020
Michael Bohana05f4552012-05-24 15:58:11 -07001021static int qpnp_pin_debugfs_get(void *data, u64 *val)
Michael Bohana19dced2012-04-24 13:14:50 -07001022{
Michael Bohana05f4552012-05-24 15:58:11 -07001023 enum qpnp_pin_param_type *idx = data;
1024 struct qpnp_pin_spec *q_spec;
1025 struct qpnp_pin_reg cfg = {};
Michael Bohana19dced2012-04-24 13:14:50 -07001026 int rc;
1027
Michael Bohana05f4552012-05-24 15:58:11 -07001028 rc = qpnp_pin_reg_attr(*idx, &cfg);
Michael Bohana19dced2012-04-24 13:14:50 -07001029 if (rc)
1030 return rc;
Michael Bohana05f4552012-05-24 15:58:11 -07001031 q_spec = container_of(idx, struct qpnp_pin_spec, params[*idx]);
Michael Bohana19dced2012-04-24 13:14:50 -07001032 *val = q_reg_get(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask);
1033 return 0;
1034}
1035
Michael Bohana05f4552012-05-24 15:58:11 -07001036static int qpnp_pin_debugfs_set(void *data, u64 val)
Michael Bohana19dced2012-04-24 13:14:50 -07001037{
Michael Bohana05f4552012-05-24 15:58:11 -07001038 enum qpnp_pin_param_type *idx = data;
1039 struct qpnp_pin_spec *q_spec;
1040 struct qpnp_pin_chip *q_chip;
1041 struct qpnp_pin_reg cfg = {};
Michael Bohana19dced2012-04-24 13:14:50 -07001042 int rc;
1043
Michael Bohana05f4552012-05-24 15:58:11 -07001044 q_spec = container_of(idx, struct qpnp_pin_spec, params[*idx]);
Michael Bohana19dced2012-04-24 13:14:50 -07001045 q_chip = q_spec->q_chip;
1046
Michael Bohan58d07802012-05-31 15:37:55 -07001047 rc = qpnp_pin_check_config(*idx, q_spec, val);
Michael Bohana19dced2012-04-24 13:14:50 -07001048 if (rc)
1049 return rc;
1050
Michael Bohana05f4552012-05-24 15:58:11 -07001051 rc = qpnp_pin_reg_attr(*idx, &cfg);
Michael Bohana19dced2012-04-24 13:14:50 -07001052 if (rc)
1053 return rc;
1054 q_reg_clr_set(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask, val);
1055 rc = spmi_ext_register_writel(q_chip->spmi->ctrl, q_spec->slave,
1056 Q_REG_ADDR(q_spec, cfg.addr),
1057 &q_spec->regs[cfg.idx], 1);
1058
1059 return rc;
1060}
Michael Bohana05f4552012-05-24 15:58:11 -07001061DEFINE_SIMPLE_ATTRIBUTE(qpnp_pin_fops, qpnp_pin_debugfs_get,
1062 qpnp_pin_debugfs_set, "%llu\n");
Michael Bohana19dced2012-04-24 13:14:50 -07001063
1064#define DEBUGFS_BUF_SIZE 11 /* supports 2^32 in decimal */
1065
Michael Bohana05f4552012-05-24 15:58:11 -07001066struct qpnp_pin_debugfs_args {
1067 enum qpnp_pin_param_type type;
Michael Bohana19dced2012-04-24 13:14:50 -07001068 const char *filename;
1069};
1070
Michael Bohana05f4552012-05-24 15:58:11 -07001071static struct qpnp_pin_debugfs_args dfs_args[] = {
Michael Bohan6b901572012-05-30 13:32:24 -07001072 { Q_PIN_CFG_MODE, "mode" },
Michael Bohana05f4552012-05-24 15:58:11 -07001073 { Q_PIN_CFG_OUTPUT_TYPE, "output_type" },
1074 { Q_PIN_CFG_INVERT, "invert" },
1075 { Q_PIN_CFG_PULL, "pull" },
1076 { Q_PIN_CFG_VIN_SEL, "vin_sel" },
1077 { Q_PIN_CFG_OUT_STRENGTH, "out_strength" },
Michael Bohand734fb22012-10-30 14:19:22 -07001078 { Q_PIN_CFG_SRC_SEL, "src_sel" },
Michael Bohan6b901572012-05-30 13:32:24 -07001079 { Q_PIN_CFG_MASTER_EN, "master_en" },
1080 { Q_PIN_CFG_AOUT_REF, "aout_ref" },
1081 { Q_PIN_CFG_AIN_ROUTE, "ain_route" },
1082 { Q_PIN_CFG_CS_OUT, "cs_out" },
Michael Bohana19dced2012-04-24 13:14:50 -07001083};
1084
Michael Bohana05f4552012-05-24 15:58:11 -07001085static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
Michael Bohana19dced2012-04-24 13:14:50 -07001086{
1087 struct spmi_device *spmi = q_chip->spmi;
1088 struct device *dev = &spmi->dev;
Michael Bohana05f4552012-05-24 15:58:11 -07001089 struct qpnp_pin_spec *q_spec;
1090 enum qpnp_pin_param_type *params;
1091 enum qpnp_pin_param_type type;
1092 char pmic_pin[DEBUGFS_BUF_SIZE];
Michael Bohana19dced2012-04-24 13:14:50 -07001093 const char *filename;
1094 struct dentry *dfs, *dfs_io_dir;
Michael Bohan6b901572012-05-30 13:32:24 -07001095 int i, j, rc;
Michael Bohana19dced2012-04-24 13:14:50 -07001096
1097 BUG_ON(Q_NUM_PARAMS != ARRAY_SIZE(dfs_args));
1098
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001099 q_chip->dfs_dir = debugfs_create_dir(q_chip->gpio_chip.label,
Michael Bohana19dced2012-04-24 13:14:50 -07001100 driver_dfs_dir);
1101 if (q_chip->dfs_dir == NULL) {
1102 dev_err(dev, "%s: cannot register chip debugfs directory %s\n",
1103 __func__, dev->of_node->name);
1104 return -ENODEV;
1105 }
1106
1107 for (i = 0; i < spmi->num_dev_node; i++) {
1108 q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
1109 params = q_spec->params;
Michael Bohana05f4552012-05-24 15:58:11 -07001110 snprintf(pmic_pin, DEBUGFS_BUF_SIZE, "%u", q_spec->pmic_pin);
Michael Bohan6b901572012-05-30 13:32:24 -07001111 dfs_io_dir = debugfs_create_dir(pmic_pin, q_chip->dfs_dir);
Michael Bohana19dced2012-04-24 13:14:50 -07001112 if (dfs_io_dir == NULL)
1113 goto dfs_err;
1114
1115 for (j = 0; j < Q_NUM_PARAMS; j++) {
1116 type = dfs_args[j].type;
1117 filename = dfs_args[j].filename;
1118
Michael Bohan6b901572012-05-30 13:32:24 -07001119 /*
1120 * Use a value of '0' to see if the pin has even basic
1121 * support for a function. Do not create a file if
1122 * it doesn't.
1123 */
1124 rc = qpnp_pin_check_config(type, q_spec, 0);
1125 if (rc == -ENXIO)
1126 continue;
1127
Michael Bohana19dced2012-04-24 13:14:50 -07001128 params[type] = type;
1129 dfs = debugfs_create_file(
1130 filename,
1131 S_IRUGO | S_IWUSR,
1132 dfs_io_dir,
1133 &q_spec->params[type],
Michael Bohana05f4552012-05-24 15:58:11 -07001134 &qpnp_pin_fops);
Michael Bohana19dced2012-04-24 13:14:50 -07001135 if (dfs == NULL)
1136 goto dfs_err;
1137 }
1138 }
1139 return 0;
1140dfs_err:
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001141 dev_err(dev, "%s: cannot register debugfs for pmic gpio %u on chip %s\n",
1142 __func__, q_spec->pmic_pin, dev->of_node->name);
Michael Bohana19dced2012-04-24 13:14:50 -07001143 debugfs_remove_recursive(q_chip->dfs_dir);
1144 return -ENFILE;
1145}
1146#else
Michael Bohana05f4552012-05-24 15:58:11 -07001147static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
Michael Bohana19dced2012-04-24 13:14:50 -07001148{
1149 return 0;
1150}
1151#endif
1152
Michael Bohanbfe64c72012-08-24 16:57:26 -07001153static int qpnp_pin_is_valid_pin(struct qpnp_pin_spec *q_spec)
1154{
1155 if (q_spec->type == Q_GPIO_TYPE)
1156 switch (q_spec->subtype) {
1157 case Q_GPIO_SUBTYPE_GPIO_4CH:
1158 case Q_GPIO_SUBTYPE_GPIOC_4CH:
1159 case Q_GPIO_SUBTYPE_GPIO_8CH:
1160 case Q_GPIO_SUBTYPE_GPIOC_8CH:
1161 return 1;
1162 }
1163 else if (q_spec->type == Q_MPP_TYPE)
1164 switch (q_spec->subtype) {
1165 case Q_MPP_SUBTYPE_4CH_NO_ANA_OUT:
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +05301166 case Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
Michael Bohanbfe64c72012-08-24 16:57:26 -07001167 case Q_MPP_SUBTYPE_4CH_NO_SINK:
Ashay Jaiswal1c61bd62014-01-22 10:29:11 +05301168 case Q_MPP_SUBTYPE_ULT_4CH_NO_SINK:
Michael Bohanbfe64c72012-08-24 16:57:26 -07001169 case Q_MPP_SUBTYPE_4CH_FULL_FUNC:
1170 case Q_MPP_SUBTYPE_8CH_FULL_FUNC:
1171 return 1;
1172 }
1173
1174 return 0;
1175}
1176
Michael Bohana05f4552012-05-24 15:58:11 -07001177static int qpnp_pin_probe(struct spmi_device *spmi)
Michael Bohan0ba63b82012-02-06 13:42:34 -08001178{
Michael Bohana05f4552012-05-24 15:58:11 -07001179 struct qpnp_pin_chip *q_chip;
1180 struct qpnp_pin_spec *q_spec;
Michael Bohan0e5534d2012-05-22 17:33:45 -07001181 struct resource *res;
1182 struct spmi_resource *d_node;
Michael Bohan94e397b2012-04-25 15:21:55 -07001183 int i, rc;
1184 int lowest_gpio = UINT_MAX, highest_gpio = 0;
Abhijeet Dharmapurikarea799552014-01-08 20:16:37 -08001185 u32 gpio;
Michael Bohanbfe64c72012-08-24 16:57:26 -07001186 char version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV + 1];
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001187 const char *dev_name;
1188
1189 dev_name = spmi_get_primary_dev_name(spmi);
1190 if (!dev_name) {
1191 dev_err(&spmi->dev, "%s: label binding undefined for node %s\n",
1192 __func__, spmi->dev.of_node->full_name);
1193 return -EINVAL;
1194 }
Michael Bohan0ba63b82012-02-06 13:42:34 -08001195
1196 q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
1197 if (!q_chip) {
1198 dev_err(&spmi->dev, "%s: Can't allocate gpio_chip\n",
1199 __func__);
1200 return -ENOMEM;
1201 }
1202 q_chip->spmi = spmi;
1203 dev_set_drvdata(&spmi->dev, q_chip);
1204
Michael Bohana05f4552012-05-24 15:58:11 -07001205 mutex_lock(&qpnp_pin_chips_lock);
1206 list_add(&q_chip->chip_list, &qpnp_pin_chips);
1207 mutex_unlock(&qpnp_pin_chips_lock);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001208
1209 /* first scan through nodes to find the range required for allocation */
1210 for (i = 0; i < spmi->num_dev_node; i++) {
Michael Bohan94e397b2012-04-25 15:21:55 -07001211 rc = of_property_read_u32(spmi->dev_node[i].of_node,
Michael Bohana05f4552012-05-24 15:58:11 -07001212 "qcom,pin-num", &gpio);
Michael Bohan94e397b2012-04-25 15:21:55 -07001213 if (rc) {
Michael Bohana05f4552012-05-24 15:58:11 -07001214 dev_err(&spmi->dev, "%s: unable to get qcom,pin-num property\n",
1215 __func__);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001216 goto err_probe;
1217 }
1218
Michael Bohan0ba63b82012-02-06 13:42:34 -08001219 if (gpio < lowest_gpio)
1220 lowest_gpio = gpio;
1221 if (gpio > highest_gpio)
1222 highest_gpio = gpio;
1223 }
1224
1225 if (highest_gpio < lowest_gpio) {
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001226 dev_err(&spmi->dev, "%s: no device nodes specified in topology\n",
1227 __func__);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001228 rc = -EINVAL;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001229 goto err_probe;
1230 } else if (lowest_gpio == 0) {
1231 dev_err(&spmi->dev, "%s: 0 is not a valid PMIC GPIO\n",
1232 __func__);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001233 rc = -EINVAL;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001234 goto err_probe;
1235 }
1236
Michael Bohana05f4552012-05-24 15:58:11 -07001237 q_chip->pmic_pin_lowest = lowest_gpio;
1238 q_chip->pmic_pin_highest = highest_gpio;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001239
1240 /* allocate gpio lookup tables */
Michael Bohana05f4552012-05-24 15:58:11 -07001241 q_chip->pmic_pins = kzalloc(sizeof(struct qpnp_pin_spec *) *
Michael Bohan0ba63b82012-02-06 13:42:34 -08001242 highest_gpio - lowest_gpio + 1,
1243 GFP_KERNEL);
Michael Bohana05f4552012-05-24 15:58:11 -07001244 q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_pin_spec *) *
Michael Bohan0ba63b82012-02-06 13:42:34 -08001245 spmi->num_dev_node, GFP_KERNEL);
Michael Bohana05f4552012-05-24 15:58:11 -07001246 if (!q_chip->pmic_pins || !q_chip->chip_gpios) {
Michael Bohan0ba63b82012-02-06 13:42:34 -08001247 dev_err(&spmi->dev, "%s: unable to allocate memory\n",
1248 __func__);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001249 rc = -ENOMEM;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001250 goto err_probe;
1251 }
1252
1253 /* get interrupt controller device_node */
1254 q_chip->int_ctrl = of_irq_find_parent(spmi->dev.of_node);
1255 if (!q_chip->int_ctrl) {
1256 dev_err(&spmi->dev, "%s: Can't find interrupt parent\n",
1257 __func__);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001258 rc = -EINVAL;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001259 goto err_probe;
1260 }
1261
1262 /* now scan through again and populate the lookup table */
1263 for (i = 0; i < spmi->num_dev_node; i++) {
Michael Bohan0e5534d2012-05-22 17:33:45 -07001264 d_node = &spmi->dev_node[i];
1265 res = spmi_get_resource(spmi, d_node, IORESOURCE_MEM, 0);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001266 if (!res) {
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001267 dev_err(&spmi->dev, "%s: node %s is missing has no base address definition\n",
Michael Bohan0e5534d2012-05-22 17:33:45 -07001268 __func__, d_node->of_node->full_name);
Zhenhua Huang30acf242013-07-11 01:52:36 +08001269 rc = -EINVAL;
1270 goto err_probe;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001271 }
1272
Michael Bohan0e5534d2012-05-22 17:33:45 -07001273 rc = of_property_read_u32(d_node->of_node,
Michael Bohana05f4552012-05-24 15:58:11 -07001274 "qcom,pin-num", &gpio);
Michael Bohan94e397b2012-04-25 15:21:55 -07001275 if (rc) {
Michael Bohana05f4552012-05-24 15:58:11 -07001276 dev_err(&spmi->dev, "%s: unable to get qcom,pin-num property\n",
1277 __func__);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001278 goto err_probe;
1279 }
Michael Bohan0ba63b82012-02-06 13:42:34 -08001280
Michael Bohana05f4552012-05-24 15:58:11 -07001281 q_spec = kzalloc(sizeof(struct qpnp_pin_spec),
Michael Bohan0ba63b82012-02-06 13:42:34 -08001282 GFP_KERNEL);
1283 if (!q_spec) {
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001284 dev_err(&spmi->dev, "%s: unable to allocate memory\n",
1285 __func__);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001286 rc = -ENOMEM;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001287 goto err_probe;
1288 }
1289
1290 q_spec->slave = spmi->sid;
1291 q_spec->offset = res->start;
1292 q_spec->gpio_chip_idx = i;
Michael Bohana05f4552012-05-24 15:58:11 -07001293 q_spec->pmic_pin = gpio;
Michael Bohan0e5534d2012-05-22 17:33:45 -07001294 q_spec->node = d_node->of_node;
Michael Bohana19dced2012-04-24 13:14:50 -07001295 q_spec->q_chip = q_chip;
Michael Bohane25e15f2012-04-12 17:28:26 -07001296
1297 rc = spmi_ext_register_readl(spmi->ctrl, q_spec->slave,
Michael Bohanbfe64c72012-08-24 16:57:26 -07001298 Q_REG_ADDR(q_spec, Q_REG_DIG_MAJOR_REV),
1299 &version[0], ARRAY_SIZE(version));
Michael Bohane25e15f2012-04-12 17:28:26 -07001300 if (rc) {
1301 dev_err(&spmi->dev, "%s: unable to read type regs\n",
1302 __func__);
Michael Bohane25e15f2012-04-12 17:28:26 -07001303 goto err_probe;
1304 }
Michael Bohanbfe64c72012-08-24 16:57:26 -07001305 q_spec->dig_major_rev = version[Q_REG_DIG_MAJOR_REV -
1306 Q_REG_DIG_MAJOR_REV];
1307 q_spec->type = version[Q_REG_TYPE - Q_REG_DIG_MAJOR_REV];
1308 q_spec->subtype = version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV];
1309
1310 if (!qpnp_pin_is_valid_pin(q_spec)) {
1311 dev_err(&spmi->dev, "%s: invalid pin type (type=0x%x subtype=0x%x)\n",
1312 __func__, q_spec->type, q_spec->subtype);
1313 goto err_probe;
1314 }
Michael Bohan0ba63b82012-02-06 13:42:34 -08001315
Michael Bohan6b901572012-05-30 13:32:24 -07001316 rc = qpnp_pin_ctl_regs_init(q_spec);
1317 if (rc)
1318 goto err_probe;
1319
Michael Bohana19dced2012-04-24 13:14:50 -07001320 /* initialize lookup table params */
Michael Bohana05f4552012-05-24 15:58:11 -07001321 qpnp_pmic_pin_set_spec(q_chip, gpio, q_spec);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001322 qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
1323 }
1324
1325 q_chip->gpio_chip.base = -1;
1326 q_chip->gpio_chip.ngpio = spmi->num_dev_node;
Michael Bohan6ea2cd22012-05-29 15:40:18 -07001327 q_chip->gpio_chip.label = dev_name;
Michael Bohana05f4552012-05-24 15:58:11 -07001328 q_chip->gpio_chip.direction_input = qpnp_pin_direction_input;
1329 q_chip->gpio_chip.direction_output = qpnp_pin_direction_output;
1330 q_chip->gpio_chip.to_irq = qpnp_pin_to_irq;
1331 q_chip->gpio_chip.get = qpnp_pin_get;
1332 q_chip->gpio_chip.set = qpnp_pin_set;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001333 q_chip->gpio_chip.dev = &spmi->dev;
Michael Bohana05f4552012-05-24 15:58:11 -07001334 q_chip->gpio_chip.of_xlate = qpnp_pin_of_gpio_xlate;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001335 q_chip->gpio_chip.of_gpio_n_cells = 2;
1336 q_chip->gpio_chip.can_sleep = 0;
1337
1338 rc = gpiochip_add(&q_chip->gpio_chip);
1339 if (rc) {
1340 dev_err(&spmi->dev, "%s: Can't add gpio chip, rc = %d\n",
1341 __func__, rc);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001342 goto err_probe;
1343 }
1344
Michael Bohande3942a2012-04-17 15:28:01 -07001345 /* now configure gpio config defaults if they exist */
Michael Bohan0ba63b82012-02-06 13:42:34 -08001346 for (i = 0; i < spmi->num_dev_node; i++) {
1347 q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001348 if (WARN_ON(!q_spec)) {
1349 rc = -ENODEV;
1350 goto err_probe;
1351 }
Michael Bohan0ba63b82012-02-06 13:42:34 -08001352
Michael Bohana05f4552012-05-24 15:58:11 -07001353 rc = qpnp_pin_cache_regs(q_chip, q_spec);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001354 if (rc)
Michael Bohan0ba63b82012-02-06 13:42:34 -08001355 goto err_probe;
Michael Bohande3942a2012-04-17 15:28:01 -07001356
Michael Bohana05f4552012-05-24 15:58:11 -07001357 rc = qpnp_pin_apply_config(q_chip, q_spec);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001358 if (rc)
Michael Bohande3942a2012-04-17 15:28:01 -07001359 goto err_probe;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001360 }
1361
1362 dev_dbg(&spmi->dev, "%s: gpio_chip registered between %d-%u\n",
1363 __func__, q_chip->gpio_chip.base,
1364 (q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
Michael Bohana19dced2012-04-24 13:14:50 -07001365
Michael Bohana05f4552012-05-24 15:58:11 -07001366 rc = qpnp_pin_debugfs_create(q_chip);
Michael Bohana19dced2012-04-24 13:14:50 -07001367 if (rc) {
1368 dev_err(&spmi->dev, "%s: debugfs creation failed\n", __func__);
1369 goto err_probe;
1370 }
1371
Michael Bohan0ba63b82012-02-06 13:42:34 -08001372 return 0;
1373
1374err_probe:
Michael Bohana05f4552012-05-24 15:58:11 -07001375 qpnp_pin_free_chip(q_chip);
Michael Bohanfdcbed22012-04-23 17:44:27 -07001376 return rc;
Michael Bohan0ba63b82012-02-06 13:42:34 -08001377}
1378
Michael Bohana05f4552012-05-24 15:58:11 -07001379static int qpnp_pin_remove(struct spmi_device *spmi)
Michael Bohan0ba63b82012-02-06 13:42:34 -08001380{
Michael Bohana05f4552012-05-24 15:58:11 -07001381 struct qpnp_pin_chip *q_chip = dev_get_drvdata(&spmi->dev);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001382
Michael Bohana19dced2012-04-24 13:14:50 -07001383 debugfs_remove_recursive(q_chip->dfs_dir);
1384
Michael Bohana05f4552012-05-24 15:58:11 -07001385 return qpnp_pin_free_chip(q_chip);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001386}
1387
1388static struct of_device_id spmi_match_table[] = {
Michael Bohana05f4552012-05-24 15:58:11 -07001389 { .compatible = "qcom,qpnp-pin",
Michael Bohan0ba63b82012-02-06 13:42:34 -08001390 },
1391 {}
1392};
1393
Michael Bohana05f4552012-05-24 15:58:11 -07001394static const struct spmi_device_id qpnp_pin_id[] = {
1395 { "qcom,qpnp-pin", 0 },
Michael Bohan0ba63b82012-02-06 13:42:34 -08001396 { }
1397};
Michael Bohana05f4552012-05-24 15:58:11 -07001398MODULE_DEVICE_TABLE(spmi, qpnp_pin_id);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001399
Michael Bohana05f4552012-05-24 15:58:11 -07001400static struct spmi_driver qpnp_pin_driver = {
Michael Bohan0ba63b82012-02-06 13:42:34 -08001401 .driver = {
Michael Bohana05f4552012-05-24 15:58:11 -07001402 .name = "qcom,qpnp-pin",
Michael Bohan0ba63b82012-02-06 13:42:34 -08001403 .of_match_table = spmi_match_table,
1404 },
Michael Bohana05f4552012-05-24 15:58:11 -07001405 .probe = qpnp_pin_probe,
1406 .remove = qpnp_pin_remove,
1407 .id_table = qpnp_pin_id,
Michael Bohan0ba63b82012-02-06 13:42:34 -08001408};
1409
Michael Bohana05f4552012-05-24 15:58:11 -07001410static int __init qpnp_pin_init(void)
Michael Bohan0ba63b82012-02-06 13:42:34 -08001411{
Michael Bohana05f4552012-05-24 15:58:11 -07001412#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
1413 driver_dfs_dir = debugfs_create_dir("qpnp_pin", NULL);
Michael Bohana19dced2012-04-24 13:14:50 -07001414 if (driver_dfs_dir == NULL)
1415 pr_err("Cannot register top level debugfs directory\n");
1416#endif
1417
Michael Bohana05f4552012-05-24 15:58:11 -07001418 return spmi_driver_register(&qpnp_pin_driver);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001419}
1420
Michael Bohana05f4552012-05-24 15:58:11 -07001421static void __exit qpnp_pin_exit(void)
Michael Bohan0ba63b82012-02-06 13:42:34 -08001422{
Michael Bohana05f4552012-05-24 15:58:11 -07001423#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
Michael Bohana19dced2012-04-24 13:14:50 -07001424 debugfs_remove_recursive(driver_dfs_dir);
1425#endif
Michael Bohana05f4552012-05-24 15:58:11 -07001426 spmi_driver_unregister(&qpnp_pin_driver);
Michael Bohan0ba63b82012-02-06 13:42:34 -08001427}
1428
Michael Bohan0ba63b82012-02-06 13:42:34 -08001429MODULE_DESCRIPTION("QPNP PMIC gpio driver");
Michael Bohan7f0cc9d2012-04-16 17:16:09 -07001430MODULE_LICENSE("GPL v2");
Michael Bohan0ba63b82012-02-06 13:42:34 -08001431
Michael Bohana05f4552012-05-24 15:58:11 -07001432module_init(qpnp_pin_init);
1433module_exit(qpnp_pin_exit);