blob: 4483a7fe996c2aa6bb01a5acfb21b1e59b62a19c [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/amlogic/pmic/meson_pmic6.h>
enum {
PMIC6_GPIO1,
PMIC6_GPIO2,
PMIC6_GPIO3,
PMIC6_GPIO4,
PMIC6_MAX_GPIO,
};
struct pmic6_gpio {
struct gpio_chip gpio_chip;
struct regmap *regmap;
struct device *dev;
};
static int pmic6_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct pmic6_gpio *pmic6_gpio = gpiochip_get_data(gc);
unsigned int val = 0, temp = 0;
int ret;
int i_offset = 0;
ret = regmap_read(pmic6_gpio->regmap, PMIC6_PIN_MUX_REG7, &temp);
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
switch (offset) {
case 0:
i_offset = offset + 0;
break;
case 1:
i_offset = offset + 1;
break;
case 2:
i_offset = offset + 2;
break;
case 3:
i_offset = offset + 3;
break;
default:
dev_err(pmic6_gpio->dev,
"offset fault in: %d\n", __LINE__);
break;
}
if (temp & BIT(i_offset)) {
ret = regmap_read(pmic6_gpio->regmap,
PMIC6_GPIO_IN_LEVEL, &val);
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
} else {
ret = regmap_read(pmic6_gpio->regmap,
PMIC6_GPIO_OUT_LEVEL, &val);
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
}
return !!(val & BIT(offset));
}
static void pmic6_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
{
struct pmic6_gpio *pmic6_gpio = gpiochip_get_data(gc);
int ret;
if (val) {
ret = regmap_update_bits(pmic6_gpio->regmap,
PMIC6_GPIO_OUT_LEVEL,
BIT(offset), BIT(offset));
if (ret)
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
} else {
ret = regmap_update_bits(pmic6_gpio->regmap,
PMIC6_GPIO_OUT_LEVEL,
BIT(offset), 0);
if (ret)
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
}
}
static int pmic6_gpio_dir_input(struct gpio_chip *gc, unsigned int offset)
{
struct pmic6_gpio *pmic6_gpio = gpiochip_get_data(gc);
int ret;
int i_offset = 0, o_offset = 0;
switch (offset) {
case 0:
o_offset = offset + 1;
i_offset = offset + 0;
break;
case 1:
o_offset = offset + 2;
i_offset = offset + 1;
break;
case 2:
o_offset = offset + 3;
i_offset = offset + 2;
break;
case 3:
o_offset = offset + 4;
i_offset = offset + 3;
break;
default:
dev_err(pmic6_gpio->dev,
"offset fault in: %d\n", __LINE__);
break;
}
/*disable output bit*/
ret = regmap_update_bits(pmic6_gpio->regmap,
PMIC6_PIN_MUX_REG7,
BIT(o_offset), 0);
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
udelay(10);
ret = regmap_update_bits(pmic6_gpio->regmap,
PMIC6_PIN_MUX_REG7,
BIT(i_offset), BIT(i_offset));
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
return 0;
}
static int pmic6_gpio_dir_output(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct pmic6_gpio *pmic6_gpio = gpiochip_get_data(gc);
int ret;
int i_offset = 0, o_offset = 0;
switch (offset) {
case 0:
o_offset = offset + 1;
i_offset = offset + 0;
break;
case 1:
o_offset = offset + 2;
i_offset = offset + 1;
break;
case 2:
o_offset = offset + 3;
i_offset = offset + 2;
break;
case 3:
o_offset = offset + 4;
i_offset = offset + 3;
break;
default:
dev_err(pmic6_gpio->dev,
"offset fault in: %d\n", __LINE__);
break;
}
pmic6_gpio_set(gc, offset, value);
/*disable gpio input bit*/
ret = regmap_update_bits(pmic6_gpio->regmap,
PMIC6_PIN_MUX_REG7,
BIT(i_offset), 0);
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
udelay(10);
ret = regmap_update_bits(pmic6_gpio->regmap,
PMIC6_PIN_MUX_REG7,
BIT(o_offset), BIT(o_offset));
if (ret < 0) {
dev_err(pmic6_gpio->dev,
"Failed in: %d\n", __LINE__);
return ret;
}
return 0;
}
static int pmic6_gpio_probe(struct platform_device *pdev)
{
struct meson_pmic *pmic6 = dev_get_drvdata(pdev->dev.parent);
struct pmic6_gpio *pmic6_gpio;
pmic6_gpio = devm_kzalloc(&pdev->dev, sizeof(*pmic6_gpio),
GFP_KERNEL);
if (!pmic6_gpio) {
dev_err(&pdev->dev, "Failed to allocated device.\n");
return -ENOMEM;
}
pmic6_gpio->dev = &pdev->dev;
pmic6_gpio->regmap = pmic6->regmap;
pmic6_gpio->gpio_chip.label = "pmic6-gpio",
pmic6_gpio->gpio_chip.owner = THIS_MODULE,
pmic6_gpio->gpio_chip.direction_input = pmic6_gpio_dir_input,
pmic6_gpio->gpio_chip.direction_output = pmic6_gpio_dir_output,
pmic6_gpio->gpio_chip.set = pmic6_gpio_set,
pmic6_gpio->gpio_chip.get = pmic6_gpio_get,
pmic6_gpio->gpio_chip.ngpio = PMIC6_MAX_GPIO,
pmic6_gpio->gpio_chip.can_sleep = true,
pmic6_gpio->gpio_chip.parent = &pdev->dev;
pmic6_gpio->gpio_chip.base = -1;
platform_set_drvdata(pdev, pmic6_gpio);
return devm_gpiochip_add_data(&pdev->dev, &pmic6_gpio->gpio_chip,
pmic6_gpio);
}
static const struct of_device_id pmic6_gpio_match_table[] = {
{ .compatible = "amlogic,pmic6-gpio"},
{ },
};
MODULE_DEVICE_TABLE(of, pmic6_gpio_match_table);
static struct platform_driver pmic6_gpio_driver = {
.driver = {
.name = "pmic6-gpio",
.of_match_table = pmic6_gpio_match_table,
},
.probe = pmic6_gpio_probe,
};
module_platform_driver(pmic6_gpio_driver);
MODULE_AUTHOR("Amlogic");
MODULE_DESCRIPTION("PMIC6 gpio driver");
MODULE_LICENSE("GPL v2");