From bcc0fa89015bca8b7ff07dd29f625c31be31c752 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 11 Jan 2013 14:36:48 -0800 Subject: [PATCH] gpio: Add pm8xxx RPC gpio driver Signed-off-by: Stephen Boyd --- drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-pm8xxx-rpc.c | 241 ++++++++++++++++++++++++++++++++ include/linux/gpio-pm8xxx-rpc.h | 35 +++++ 4 files changed, 284 insertions(+) create mode 100644 drivers/gpio/gpio-pm8xxx-rpc.c create mode 100644 include/linux/gpio-pm8xxx-rpc.h diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index cd02764ff34..ccc071f20b5 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -539,4 +539,11 @@ config GPIO_PM8XXX_MPP This is the multi-purpose pin (MPP) driver for Qualcomm PM 8xxx PMIC chips. +config GPIO_PM8XXX_RPC + tristate "Qualcomm PM8xxx RPC based GPIO support" + depends on MSM_SMD + help + This option enables support for on-chip GPIO found on Qualcomm PM8xxx + PMICs through RPC. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 19f91727d8a..952c60b0159 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PM8XXX) += pm8xxx-gpio.o obj-$(CONFIG_GPIO_PM8XXX_MPP) += pm8xxx-mpp.o +obj-$(CONFIG_GPIO_PM8XXX_RPC) += gpio-pm8xxx-rpc.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o diff --git a/drivers/gpio/gpio-pm8xxx-rpc.c b/drivers/gpio/gpio-pm8xxx-rpc.c new file mode 100644 index 00000000000..1acc7411b29 --- /dev/null +++ b/drivers/gpio/gpio-pm8xxx-rpc.c @@ -0,0 +1,241 @@ +/* + * Qualcomm PMIC8XXX GPIO driver based on RPC + * + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct pm8xxx_gpio_rpc_chip { + struct list_head link; + struct gpio_chip gpio_chip; +}; + +static LIST_HEAD(pm8xxx_gpio_rpc_chips); +static DEFINE_MUTEX(pm8xxx_gpio_chips_lock); + +static int pm8xxx_gpio_rpc_get(struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip, + unsigned gpio) +{ + int rc; + + if (gpio >= pm8xxx_gpio_chip->gpio_chip.ngpio + || pm8xxx_gpio_chip == NULL) + return -EINVAL; + + rc = pmic_gpio_get_value(gpio); + + return rc; +} + +static int pm8xxx_gpio_rpc_set(struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip, + unsigned gpio, int value) +{ + int rc; + + if (gpio >= pm8xxx_gpio_chip->gpio_chip.ngpio || + pm8xxx_gpio_chip == NULL) + return -EINVAL; + + rc = pmic_gpio_set_value(gpio, value); + + return rc; +} + +static int pm8xxx_gpio_rpc_set_direction(struct pm8xxx_gpio_rpc_chip + *pm8xxx_gpio_chip, unsigned gpio, int direction) +{ + int rc = 0; + + if (!direction || pm8xxx_gpio_chip == NULL) + return -EINVAL; + + if (direction == PM_GPIO_DIR_IN) + rc = pmic_gpio_direction_input(gpio); + else if (direction == PM_GPIO_DIR_OUT) + rc = pmic_gpio_direction_output(gpio); + + return rc; +} + +static int pm8xxx_gpio_rpc_read(struct gpio_chip *gpio_chip, unsigned offset) +{ + struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip = + dev_get_drvdata(gpio_chip->dev); + + return pm8xxx_gpio_rpc_get(pm8xxx_gpio_chip, offset); +} + +static void pm8xxx_gpio_rpc_write(struct gpio_chip *gpio_chip, + unsigned offset, int val) +{ + struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip = + dev_get_drvdata(gpio_chip->dev); + + pm8xxx_gpio_rpc_set(pm8xxx_gpio_chip, offset, !!val); +} + +static int pm8xxx_gpio_rpc_direction_input(struct gpio_chip *gpio_chip, + unsigned offset) +{ + struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip = + dev_get_drvdata(gpio_chip->dev); + + return pm8xxx_gpio_rpc_set_direction(pm8xxx_gpio_chip, offset, + PM_GPIO_DIR_IN); +} + +static int pm8xxx_gpio_rpc_direction_output(struct gpio_chip *gpio_chip, + unsigned offset, int val) +{ + int ret = 0; + + struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip = + dev_get_drvdata(gpio_chip->dev); + + ret = pm8xxx_gpio_rpc_set_direction(pm8xxx_gpio_chip, offset, + PM_GPIO_DIR_OUT); + if (!ret) + ret = pm8xxx_gpio_rpc_set(pm8xxx_gpio_chip, offset, !!val); + + return ret; +} + +static void pm8xxx_gpio_rpc_dbg_show(struct seq_file *s, struct gpio_chip + *gpio_chip) +{ + struct pm8xxx_gpio_rpc_chip *pmxx_gpio_chip = + dev_get_drvdata(gpio_chip->dev); + u8 state, mode; + const char *label; + int i; + + for (i = 0; i < gpio_chip->ngpio; i++) { + label = gpiochip_is_requested(gpio_chip, i); + state = pm8xxx_gpio_rpc_get(pmxx_gpio_chip, i); + mode = pmic_gpio_get_direction(i); + seq_printf(s, "gpio-%-3d (%-12.12s) %s %s", + gpio_chip->base + i, + label ? label : " ", mode ? "out" : "in", + state ? "hi" : "lo"); + seq_printf(s, "\n"); + } +} + +static int __devinit pm8xxx_gpio_rpc_probe(struct platform_device *pdev) +{ + int ret; + struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip; + const struct pm8xxx_gpio_rpc_platform_data *pdata = + pdev->dev.platform_data; + + if (!pdata) { + pr_err("missing platform data\n"); + return -EINVAL; + } + + pm8xxx_gpio_chip = kzalloc(sizeof(struct pm8xxx_gpio_rpc_chip), + GFP_KERNEL); + if (!pm8xxx_gpio_chip) { + pr_err("Cannot allocate pm8xxx_gpio_chip\n"); + return -ENOMEM; + } + + pm8xxx_gpio_chip->gpio_chip.label = "pm8xxx-gpio-rpc"; + pm8xxx_gpio_chip->gpio_chip.direction_input = + pm8xxx_gpio_rpc_direction_input; + pm8xxx_gpio_chip->gpio_chip.direction_output = + pm8xxx_gpio_rpc_direction_output; + pm8xxx_gpio_chip->gpio_chip.get = pm8xxx_gpio_rpc_read; + pm8xxx_gpio_chip->gpio_chip.set = pm8xxx_gpio_rpc_write; + pm8xxx_gpio_chip->gpio_chip.dbg_show = pm8xxx_gpio_rpc_dbg_show; + pm8xxx_gpio_chip->gpio_chip.ngpio = pdata->ngpios; + pm8xxx_gpio_chip->gpio_chip.can_sleep = 1; + pm8xxx_gpio_chip->gpio_chip.dev = &pdev->dev; + pm8xxx_gpio_chip->gpio_chip.base = pdata->gpio_base; + + mutex_lock(&pm8xxx_gpio_chips_lock); + list_add(&pm8xxx_gpio_chip->link, &pm8xxx_gpio_rpc_chips); + mutex_unlock(&pm8xxx_gpio_chips_lock); + platform_set_drvdata(pdev, pm8xxx_gpio_chip); + + ret = gpiochip_add(&pm8xxx_gpio_chip->gpio_chip); + if (ret) { + pr_err("gpiochip_add failed ret = %d\n", ret); + goto reset_drvdata; + } + + pr_info("OK: base=%d, ngpio=%d\n", pm8xxx_gpio_chip->gpio_chip.base, + pm8xxx_gpio_chip->gpio_chip.ngpio); + + return 0; + +reset_drvdata: + mutex_lock(&pm8xxx_gpio_chips_lock); + list_del(&pm8xxx_gpio_chip->link); + mutex_unlock(&pm8xxx_gpio_chips_lock); + platform_set_drvdata(pdev, NULL); + kfree(pm8xxx_gpio_chip); + mutex_destroy(&pm8xxx_gpio_chips_lock); + return ret; +} + +static int __devexit pm8xxx_gpio_rpc_remove(struct platform_device *pdev) +{ + struct pm8xxx_gpio_rpc_chip *pm8xxx_gpio_chip = + platform_get_drvdata(pdev); + + mutex_lock(&pm8xxx_gpio_chips_lock); + list_del(&pm8xxx_gpio_chip->link); + mutex_unlock(&pm8xxx_gpio_chips_lock); + platform_set_drvdata(pdev, NULL); + if (gpiochip_remove(&pm8xxx_gpio_chip->gpio_chip)) + pr_err("failed to remove gpio chip\n"); + kfree(pm8xxx_gpio_chip); + mutex_destroy(&pm8xxx_gpio_chips_lock); + return 0; +} + +static struct platform_driver pm8xxx_gpio_rpc_driver = { + .probe = pm8xxx_gpio_rpc_probe, + .remove = __devexit_p(pm8xxx_gpio_rpc_remove), + .driver = { + .name = PM8XXX_GPIO_DEV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pm8xxx_gpio_rpc_init(void) +{ + return platform_driver_register(&pm8xxx_gpio_rpc_driver); +} +postcore_initcall(pm8xxx_gpio_rpc_init); + +static void __exit pm8xxx_gpio_rpc_exit(void) +{ + platform_driver_unregister(&pm8xxx_gpio_rpc_driver); +} +module_exit(pm8xxx_gpio_rpc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PMIC GPIO driver based on RPC"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:" PM8XXX_GPIO_DEV_NAME); diff --git a/include/linux/gpio-pm8xxx-rpc.h b/include/linux/gpio-pm8xxx-rpc.h new file mode 100644 index 00000000000..5b6f09787bf --- /dev/null +++ b/include/linux/gpio-pm8xxx-rpc.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Qualcomm PMIC8XXX gpio rpc driver header file + * + */ + +#ifndef __GPIO_PM8XXX_RPC_H +#define __GPIO_PM8XXX_RPC_H + +#define PM8XXX_GPIO_DEV_NAME "pm8xxx-gpio-rpc" + +struct pm8xxx_gpio_rpc_platform_data { + int ngpios; + int gpio_base; +}; + +/* GPIO parameters */ +/* direction */ +#define PM_GPIO_DIR_OUT 0x01 +#define PM_GPIO_DIR_IN 0x02 +#define PM_GPIO_DIR_BOTH (PM_GPIO_DIR_OUT | PM_GPIO_DIR_IN) + +#endif