mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-11 07:03:09 -04:00
gpio: pcal6408: add initial module
Add gpio expander driver. Supports both TCA6418E and PCAL6408A chips. Does not currently support interrupts. Change-Id: I4c4e59dd2c392e1ec4296251cb26b9ec839144c6 Signed-off-by: Ryan Lattrel <ryanl@motorola.com> Reviewed-on: https://gerrit.mot.com/1810602 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Konstantin Makariev <kmakariev@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
e78340eabb
commit
c9d55c366a
3 changed files with 631 additions and 0 deletions
8
drivers/gpio/pcal6408/Android.mk
Executable file
8
drivers/gpio/pcal6408/Android.mk
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := gpio-pcal6408.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
||||
9
drivers/gpio/pcal6408/Kbuild
Executable file
9
drivers/gpio/pcal6408/Kbuild
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
ifeq ($(TARGET_BUILD_VARIANT),userdebug)
|
||||
EXTRA_CFLAGS += -DGPIOI2C_USER_DEBUG
|
||||
endif
|
||||
|
||||
obj-m += gpio-pcal6408.o
|
||||
614
drivers/gpio/pcal6408/gpio-pcal6408.c
Executable file
614
drivers/gpio/pcal6408/gpio-pcal6408.c
Executable file
|
|
@ -0,0 +1,614 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Motorola Mobility, LLC.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#define PCAL6408_DBG(msg, ...) \
|
||||
pr_debug("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__)
|
||||
#define PCAL6408_ERR(msg, ...) \
|
||||
pr_err("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__)
|
||||
#define PCAL6408_LOG(msg, ...) \
|
||||
pr_info("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__)
|
||||
|
||||
#define PCAL6408_ADDR_INPUT 0x00
|
||||
#define PCAL6408_ADDR_OUTPUT 0x01
|
||||
#define PCAL6408_ADDR_CONFIG 0x03
|
||||
#define PCAL6408_ADDR_PULL_EN 0x43
|
||||
#define PCAL6408_ADDR_PULL_CFG 0x44
|
||||
|
||||
#define TCA6418_ADDR_INPUT 0x14
|
||||
#define TCA6418_ADDR_OUTPUT 0x17
|
||||
#define TCA6418_ADDR_CONFIG 0x23
|
||||
#define TCA6418_ADDR_PULL_CFG 0x2C
|
||||
|
||||
#define BIAS_PULL_UP 0
|
||||
#define BIAS_PULL_DOWN 1
|
||||
#define BIAS_NO_PULL 2
|
||||
|
||||
struct pcal6048_dev;
|
||||
|
||||
struct pcal6048_config {
|
||||
char *name;
|
||||
int nr_gpio;
|
||||
int dir_out_val;
|
||||
int direction;
|
||||
int output;
|
||||
int input;
|
||||
int (*get_bit)(int);
|
||||
int (*set_pulldown)(int, struct pcal6048_dev *);
|
||||
int (*set_pullup)(int, struct pcal6048_dev *);
|
||||
int (*set_nopull)(int, struct pcal6048_dev *);
|
||||
int (*get_pull)(int, struct pcal6048_dev *);
|
||||
};
|
||||
|
||||
struct pcal6048_dev {
|
||||
const char *name;
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct gpio_chip gpio_chip;
|
||||
const struct pcal6048_config *conf;
|
||||
struct pinctrl_desc ctrldesc;
|
||||
struct pinctrl_dev *pctl_dev;
|
||||
struct pinctrl_gpio_range grange;
|
||||
int reset_gpio;
|
||||
};
|
||||
|
||||
static int pcal6048_set_reg(struct pcal6048_dev *chip, int gpio, int reg);
|
||||
static int pcal6048_clear_reg(struct pcal6048_dev *chip, int gpio, int reg);
|
||||
static int pcal6048_read_reg(struct pcal6048_dev *chip, int gpio, int reg);
|
||||
|
||||
static int pcal6408_get_bit(int gpio)
|
||||
{
|
||||
return gpio;
|
||||
}
|
||||
|
||||
static int pcal6408_set_pulldown(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_clear_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6408_set_pullup(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6408_set_nopull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_clear_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6408_get_pull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (!pcal6048_read_reg(chip, gpio, PCAL6408_ADDR_PULL_EN))
|
||||
return BIAS_NO_PULL;
|
||||
else if (pcal6048_read_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG))
|
||||
return BIAS_PULL_UP;
|
||||
else
|
||||
return BIAS_PULL_DOWN;
|
||||
}
|
||||
|
||||
static const struct pcal6048_config pcal6408a_conf = {
|
||||
.name = "pcal6408a_conf",
|
||||
.nr_gpio = 8,
|
||||
.set_pulldown = pcal6408_set_pulldown,
|
||||
.set_pullup = pcal6408_set_pullup,
|
||||
.set_nopull = pcal6408_set_nopull,
|
||||
.get_pull = pcal6408_get_pull,
|
||||
.get_bit = pcal6408_get_bit,
|
||||
.dir_out_val = 0, /* Set 0 to direction for output */
|
||||
.direction = PCAL6408_ADDR_CONFIG,
|
||||
.output = PCAL6408_ADDR_OUTPUT,
|
||||
.input = PCAL6408_ADDR_INPUT,
|
||||
};
|
||||
|
||||
static int tca6418_get_bit(int gpio)
|
||||
{
|
||||
/* Bitmapping is strange, gpio7 is bit 0 of first addr,
|
||||
* but gpio8 is bit 0 of second addr, gpio 16 is bit 0
|
||||
* of third addr
|
||||
*/
|
||||
if (gpio <= 7)
|
||||
return (7 - gpio);
|
||||
else if (gpio > 7 && gpio <= 15)
|
||||
return gpio - 8;
|
||||
else
|
||||
return gpio - 16;
|
||||
}
|
||||
|
||||
static int tca6418_set_pulldown(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_clear_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tca6418_set_nopull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (pcal6048_set_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) {
|
||||
PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tca6418_get_pull(int gpio, struct pcal6048_dev *chip)
|
||||
{
|
||||
if (!pcal6048_read_reg(chip, gpio, TCA6418_ADDR_PULL_CFG))
|
||||
return BIAS_PULL_DOWN;
|
||||
else
|
||||
return BIAS_NO_PULL;
|
||||
}
|
||||
|
||||
static const struct pcal6048_config tca6418e_conf = {
|
||||
.name = "tca6418e_conf",
|
||||
.nr_gpio = 18,
|
||||
.set_pulldown = tca6418_set_pulldown,
|
||||
.set_pullup = NULL,
|
||||
.set_nopull = tca6418_set_nopull,
|
||||
.get_pull = tca6418_get_pull,
|
||||
.get_bit = tca6418_get_bit,
|
||||
.dir_out_val = 1, /* Set 1 to direction for output */
|
||||
.direction = TCA6418_ADDR_CONFIG,
|
||||
.output = TCA6418_ADDR_OUTPUT,
|
||||
.input = TCA6418_ADDR_INPUT,
|
||||
};
|
||||
|
||||
static int pcal6048_read_reg(struct pcal6048_dev *chip, int gpio, int reg)
|
||||
{
|
||||
int bit_num = chip->conf->get_bit(gpio);
|
||||
uint8_t bit = BIT(bit_num);
|
||||
int reg_off = (gpio / 8);
|
||||
int val;
|
||||
int ret = -1;
|
||||
|
||||
PCAL6408_DBG("%s (gpio %d), read bit %d from 0x%x\n",
|
||||
chip->conf->name, gpio, bit_num, reg + reg_off);
|
||||
|
||||
ret = regmap_read(chip->regmap, reg + reg_off, &val);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return !!(val & bit);
|
||||
}
|
||||
|
||||
static int pcal6048_set_reg(struct pcal6048_dev *chip, int gpio, int reg)
|
||||
{
|
||||
int bit_num = chip->conf->get_bit(gpio);
|
||||
uint8_t bit = BIT(bit_num);
|
||||
int reg_off = (gpio / 8);
|
||||
|
||||
PCAL6408_DBG("%s (gpio %d), set bit %d in 0x%x\n",
|
||||
chip->conf->name, gpio, bit_num, reg + reg_off);
|
||||
|
||||
return regmap_write_bits(chip->regmap, reg + reg_off, bit, bit);
|
||||
}
|
||||
|
||||
static int pcal6048_clear_reg(struct pcal6048_dev *chip, int gpio, int reg)
|
||||
{
|
||||
int bit_num = chip->conf->get_bit(gpio);
|
||||
uint8_t bit = BIT(bit_num);
|
||||
int reg_off = (gpio / 8);
|
||||
|
||||
PCAL6408_DBG("%s (gpio %d), clear bit %d in 0x%x\n",
|
||||
chip->conf->name, gpio, bit_num, reg + reg_off);
|
||||
|
||||
return regmap_write_bits(chip->regmap, reg + reg_off, bit, 0);
|
||||
}
|
||||
|
||||
static int pcal6048_direction_in(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
if (!chip->conf->dir_out_val)
|
||||
ret = pcal6048_set_reg(chip, offset, chip->conf->direction);
|
||||
else
|
||||
ret = pcal6048_clear_reg(chip, offset, chip->conf->direction);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcal6048_direction_out(struct gpio_chip *gc, unsigned offset, int value)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
ret = pcal6048_set_reg(chip, offset, chip->conf->output);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (chip->conf->dir_out_val)
|
||||
ret = pcal6048_set_reg(chip, offset, chip->conf->direction);
|
||||
else
|
||||
ret = pcal6048_clear_reg(chip, offset, chip->conf->direction);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 0 is out, 1 is in */
|
||||
static int pcal6048_get_direction(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
int val = pcal6048_read_reg(chip, offset, chip->conf->direction);
|
||||
|
||||
if (val == chip->conf->dir_out_val)
|
||||
return 0;
|
||||
else;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pcal6048_get_gpio(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
|
||||
/* TODO do I need to read twice to make sure it is cleared? (TCA6418e pg 12) */
|
||||
return pcal6048_read_reg(chip, offset, chip->conf->input);
|
||||
}
|
||||
|
||||
static void pcal6048_set_gpio(struct gpio_chip *gc, unsigned offset, int value)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
|
||||
if (value)
|
||||
pcal6048_set_reg(chip, offset, chip->conf->output);
|
||||
else
|
||||
pcal6048_clear_reg(chip, offset, chip->conf->output);
|
||||
}
|
||||
|
||||
static int pcal6048_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
unsigned arg = pinconf_to_config_argument(config);
|
||||
unsigned param = pinconf_to_config_param(config);
|
||||
|
||||
PCAL6408_DBG("Set config for gpio %d!\n", offset);
|
||||
|
||||
switch (param) {
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
if (chip->conf->set_pullup)
|
||||
return chip->conf->set_pullup(offset, chip);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
if (chip->conf->set_pulldown)
|
||||
return chip->conf->set_pulldown(offset, chip);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
if (chip->conf->set_nopull)
|
||||
return chip->conf->set_nopull(offset, chip);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
case PIN_CONFIG_INPUT_ENABLE:
|
||||
return pcal6048_direction_in(gc, offset);
|
||||
case PIN_CONFIG_OUTPUT_ENABLE:
|
||||
return pcal6048_direction_out(gc, offset, 1);
|
||||
case PIN_CONFIG_OUTPUT:
|
||||
return pcal6048_direction_out(gc, offset, !!arg);
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static void pcal6048_dbg_show(struct seq_file *s, struct gpio_chip *gc)
|
||||
{
|
||||
struct pcal6048_dev *chip = gpiochip_get_data(gc);
|
||||
unsigned gpio = gc->base;
|
||||
unsigned i;
|
||||
int dir = 0;
|
||||
int pull = 0;
|
||||
|
||||
for (i = 0; i < gc->ngpio; i++, gpio++) {
|
||||
seq_printf(s, " gpio%d:", i);
|
||||
|
||||
dir = pcal6048_get_direction(gc, i);
|
||||
/* Input is 1 */
|
||||
if (dir)
|
||||
seq_printf(s, " in %d", pcal6048_get_gpio(gc, i));
|
||||
else
|
||||
seq_printf(s, " out (set to %d)",
|
||||
pcal6048_read_reg(chip, i, chip->conf->output));
|
||||
|
||||
if (chip->conf->get_pull) {
|
||||
pull = chip->conf->get_pull(i, chip);
|
||||
switch(pull) {
|
||||
case BIAS_NO_PULL:
|
||||
seq_printf(s, " bias-no-pull");
|
||||
break;
|
||||
case BIAS_PULL_DOWN:
|
||||
seq_printf(s, " bias-pull-down");
|
||||
break;
|
||||
case BIAS_PULL_UP:
|
||||
seq_printf(s, " bias-pull-up");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pcal6048_setup_gpio_chip(struct pcal6048_dev *chip)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
|
||||
gc = &chip->gpio_chip;
|
||||
|
||||
gc->label = chip->conf->name;
|
||||
gc->base = -1;
|
||||
gc->ngpio = chip->conf->nr_gpio;
|
||||
gc->parent = chip->dev;
|
||||
gc->owner = THIS_MODULE;
|
||||
gc->direction_input = pcal6048_direction_in;
|
||||
gc->direction_output = pcal6048_direction_out;
|
||||
gc->get_direction = pcal6048_get_direction;
|
||||
gc->get = pcal6048_get_gpio;
|
||||
gc->set = pcal6048_set_gpio;
|
||||
gc->set_config = pcal6048_set_config;
|
||||
gc->dbg_show = pcal6048_dbg_show;
|
||||
gc->can_sleep = true;
|
||||
}
|
||||
|
||||
static int pcal6048_pinconf_get(struct pinctrl_dev *pctldev,
|
||||
unsigned pin, unsigned long *config)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int pcal6048_pinconf_set(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin, unsigned long *configs,
|
||||
unsigned int num_configs)
|
||||
{
|
||||
struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev);
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_configs; i++) {
|
||||
chip->gpio_chip.set_config(&chip->gpio_chip, pin, configs[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6048_get_groups_count(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
return chip->conf->nr_gpio;
|
||||
}
|
||||
|
||||
static const char *pcal6048_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned selector)
|
||||
{
|
||||
struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
return chip->conf->name;
|
||||
}
|
||||
|
||||
static const struct pinctrl_ops pcal6048_pctl_ops = {
|
||||
.get_groups_count = pcal6048_get_groups_count,
|
||||
.get_group_name = pcal6048_get_group_name,
|
||||
.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
|
||||
};
|
||||
|
||||
static const struct pinconf_ops pcal6048_pinconf_ops = {
|
||||
.is_generic = true,
|
||||
.pin_config_get = pcal6048_pinconf_get,
|
||||
.pin_config_set = pcal6048_pinconf_set,
|
||||
};
|
||||
|
||||
static int pcal6048_register_pinctrl(struct pcal6048_dev *chip)
|
||||
{
|
||||
struct pinctrl_desc *ctrldesc = &chip->ctrldesc;
|
||||
struct pinctrl_pin_desc *pindesc, *pdesc;
|
||||
int pin;
|
||||
|
||||
ctrldesc->name = chip->conf->name;
|
||||
ctrldesc->owner = THIS_MODULE;
|
||||
ctrldesc->confops = &pcal6048_pinconf_ops;
|
||||
ctrldesc->pctlops = &pcal6048_pctl_ops;
|
||||
|
||||
pindesc = devm_kcalloc(chip->dev,
|
||||
chip->conf->nr_gpio, sizeof(*pindesc),
|
||||
GFP_KERNEL);
|
||||
|
||||
ctrldesc->pins = pindesc;
|
||||
ctrldesc->npins = chip->conf->nr_gpio;
|
||||
|
||||
pdesc = pindesc;
|
||||
for (pin = 0; pin < chip->conf->nr_gpio; pin++) {
|
||||
pdesc->number = pin;
|
||||
pdesc->name = kasprintf(GFP_KERNEL, "gpio%d", pin);
|
||||
pdesc++;
|
||||
}
|
||||
|
||||
chip->pctl_dev = devm_pinctrl_register(chip->dev, ctrldesc, chip);
|
||||
|
||||
if (IS_ERR(chip->pctl_dev)) {
|
||||
PCAL6408_ERR("Failed to register pinctrl\n");
|
||||
return PTR_ERR(chip->pctl_dev);
|
||||
}
|
||||
|
||||
chip->grange.base = chip->gpio_chip.base;
|
||||
chip->grange.npins = chip->conf->nr_gpio;
|
||||
chip->grange.gc = &chip->gpio_chip;
|
||||
|
||||
pinctrl_add_gpio_range(chip->pctl_dev, &chip->grange);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config pcal6048_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xFF,
|
||||
};
|
||||
|
||||
static const struct of_device_id pcal6048_match_table[] = {
|
||||
{ .compatible = "ti,tca6418e", .data = &tca6418e_conf },
|
||||
{ .compatible = "nxp,pcal6408a", .data = &pcal6408a_conf },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int pcal6048_config_reset(struct pcal6048_dev *chip)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
chip->reset_gpio = of_get_gpio(chip->dev->of_node, 0);
|
||||
if (gpio_is_valid(chip->reset_gpio)) {
|
||||
err = devm_gpio_request(chip->dev, chip->reset_gpio, "pcal6048_reset");
|
||||
if (err) {
|
||||
PCAL6408_ERR("Reset gpio request failed\n");
|
||||
return err;
|
||||
} else {
|
||||
/* Active low, so set it high */
|
||||
err = gpio_direction_output(chip->reset_gpio, 1);
|
||||
if (err) {
|
||||
PCAL6408_ERR("Reset gpio set failed\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else
|
||||
PCAL6408_LOG("No reset gpio set in dtb\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcal6048_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pcal6048_dev *chip;
|
||||
const struct of_device_id *match;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
chip->dev = &client->dev;
|
||||
chip->name = "pcal6048";
|
||||
|
||||
chip->regmap = regmap_init_i2c(client, &pcal6048_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
PCAL6408_ERR("Couldn't initialize register regmap rc = %ld\n",
|
||||
PTR_ERR(chip->regmap));
|
||||
rc = PTR_ERR(chip->regmap);
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
match = of_match_device(pcal6048_match_table, chip->dev);
|
||||
if (!match || !match->data) {
|
||||
PCAL6408_ERR("Missing config data!\n");
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
chip->conf = match->data;
|
||||
PCAL6408_DBG("Using config %s\n", chip->conf->name);
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
|
||||
pcal6048_setup_gpio_chip(chip);
|
||||
pcal6048_config_reset(chip);
|
||||
|
||||
rc = devm_gpiochip_add_data(chip->dev, &chip->gpio_chip, chip);
|
||||
if (rc) {
|
||||
PCAL6408_ERR("Couldn't add gpio chip rc=%d\n", rc);
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
pcal6048_register_pinctrl(chip);
|
||||
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
devm_kfree(chip->dev, chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pcal6048_remove(struct i2c_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pcal6048_driver = {
|
||||
.driver = {
|
||||
.name = "pcal6048",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = pcal6048_match_table,
|
||||
},
|
||||
.probe = pcal6048_probe,
|
||||
.remove = pcal6048_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(pcal6048_driver);
|
||||
|
||||
MODULE_DESCRIPTION("pcal6048");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Loading…
Reference in a new issue