| // 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"); |