android_kernel_modules_leno.../drivers/platform/x86/intel_msic_gpio.c

242 lines
6.1 KiB
C

/* MSIC GPIO (access through IPC) driver for Cloverview
* (C) Copyright 2011 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/rpmsg.h>
#include <linux/mfd/intel_msic.h>
#include <asm/intel_mid_rpmsg.h>
#include <asm/intel_scu_pmic.h>
#define DRIVER_NAME "msic_gpio"
#define CTLO_DOUT_MASK (1 << 0)
#define CTLO_DOUT_H (1 << 0)
#define CTLO_DOUT_L (0 << 0)
#define CTLO_DIR_MASK (1 << 5)
#define CTLO_DIR_O (1 << 5)
#define CTLO_DIR_I (0 << 5)
#define CTLO_OUT_DEF (0x38)
#define CTLO_IN_DEF (0x18)
#define CTL_VALUE_MASK (1 << 0)
struct msic_gpio {
struct gpio_chip chip;
int ngpio_lv; /* number of low voltage gpio */
u16 gpio0_lv_ctlo;
u16 gpio0_lv_ctli;
u16 gpio0_hv_ctlo;
u16 gpio0_hv_ctli;
};
static struct msic_gpio msic_gpio;
static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct msic_gpio *mg = &msic_gpio;
u16 ctlo = offset < mg->ngpio_lv ? mg->gpio0_lv_ctlo + offset
: mg->gpio0_hv_ctlo + (offset - mg->ngpio_lv);
return intel_scu_ipc_iowrite8(ctlo, CTLO_IN_DEF);
}
static int msic_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
struct msic_gpio *mg = &msic_gpio;
u16 ctlo = offset < mg->ngpio_lv ? mg->gpio0_lv_ctlo + offset
: mg->gpio0_hv_ctlo + (offset - mg->ngpio_lv);
return intel_scu_ipc_iowrite8(ctlo,
CTLO_OUT_DEF | (value ? CTLO_DOUT_H : CTLO_DOUT_L));
}
static int msic_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct msic_gpio *mg = &msic_gpio;
u8 value;
int ret;
u16 ctlo, ctli, reg;
ctlo = offset < mg->ngpio_lv ? mg->gpio0_lv_ctlo + offset
: mg->gpio0_hv_ctlo + (offset - mg->ngpio_lv);
ctli = offset < mg->ngpio_lv ? mg->gpio0_lv_ctli + offset
: mg->gpio0_hv_ctli + (offset - mg->ngpio_lv);
/* First get pin direction */
ret = intel_scu_ipc_ioread8(ctlo, &value);
if (ret)
return -EIO;
/* The pin values for output and input direction
* are stored in different registers.
*/
reg = (value & CTLO_DIR_O) ? ctlo : ctli;
ret = intel_scu_ipc_ioread8(reg, &value);
if (ret)
return -EIO;
return value & CTL_VALUE_MASK;
}
static void msic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct msic_gpio *mg = &msic_gpio;
u16 ctlo = offset < mg->ngpio_lv ? mg->gpio0_lv_ctlo + offset
: mg->gpio0_hv_ctlo + (offset - mg->ngpio_lv);
intel_scu_ipc_update_register(ctlo,
value ? CTLO_DOUT_H : CTLO_DOUT_L, CTLO_DOUT_MASK);
}
static int msic_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct intel_msic_gpio_pdata *pdata = dev->platform_data;
struct msic_gpio *mg = &msic_gpio;
int retval;
dev_dbg(dev, "base %d\n", pdata->gpio_base);
if (!pdata || !pdata->gpio_base) {
dev_err(dev, "incorrect or missing platform data\n");
return -ENOMEM;
}
dev_set_drvdata(dev, mg);
mg->ngpio_lv = pdata->ngpio_lv;
mg->gpio0_lv_ctlo = pdata->gpio0_lv_ctlo;
mg->gpio0_lv_ctli = pdata->gpio0_lv_ctli;
mg->gpio0_hv_ctlo = pdata->gpio0_hv_ctlo;
mg->gpio0_hv_ctli = pdata->gpio0_hv_ctli;
mg->chip.label = dev_name(&pdev->dev);
mg->chip.direction_input = msic_gpio_direction_input;
mg->chip.direction_output = msic_gpio_direction_output;
mg->chip.get = msic_gpio_get;
mg->chip.set = msic_gpio_set;
mg->chip.base = pdata->gpio_base;
mg->chip.ngpio = pdata->ngpio_lv + pdata->ngpio_hv;
mg->chip.can_sleep = pdata->can_sleep;
mg->chip.dev = dev;
retval = gpiochip_add(&mg->chip);
if (retval)
dev_err(dev, "%s: Can not add msic gpio chip.\n", __func__);
return retval;
}
static int msic_gpio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
dev_set_drvdata(dev, NULL);
return 0;
}
static struct platform_driver msic_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = msic_gpio_probe,
.remove = msic_gpio_remove,
};
static int msic_gpio_init(void)
{
return platform_driver_register(&msic_gpio_driver);
}
static void msic_gpio_exit(void)
{
return platform_driver_unregister(&msic_gpio_driver);
}
static int msic_gpio_rpmsg_probe(struct rpmsg_channel *rpdev)
{
int ret = 0;
if (rpdev == NULL) {
pr_err("msic_gpio rpmsg channel not created\n");
ret = -ENODEV;
goto out;
}
dev_info(&rpdev->dev, "Probed msic_gpio rpmsg device\n");
ret = msic_gpio_init();
out:
return ret;
}
static void msic_gpio_rpmsg_remove(struct rpmsg_channel *rpdev)
{
msic_gpio_exit();
dev_info(&rpdev->dev, "Removed msic_gpio rpmsg device\n");
}
static void msic_gpio_rpmsg_cb(struct rpmsg_channel *rpdev, void *data,
int len, void *priv, u32 src)
{
dev_warn(&rpdev->dev, "unexpected, message\n");
print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1,
data, len, true);
}
static struct rpmsg_device_id msic_gpio_rpmsg_id_table[] = {
{ .name = "rpmsg_msic_gpio" },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, msic_gpio_rpmsg_id_table);
static struct rpmsg_driver msic_gpio_rpmsg = {
.drv.name = KBUILD_MODNAME,
.drv.owner = THIS_MODULE,
.id_table = msic_gpio_rpmsg_id_table,
.probe = msic_gpio_rpmsg_probe,
.callback = msic_gpio_rpmsg_cb,
.remove = msic_gpio_rpmsg_remove,
};
static int __init msic_gpio_rpmsg_init(void)
{
return register_rpmsg_driver(&msic_gpio_rpmsg);
}
fs_initcall(msic_gpio_rpmsg_init);
static void __exit msic_gpio_rpmsg_exit(void)
{
return unregister_rpmsg_driver(&msic_gpio_rpmsg);
}
module_exit(msic_gpio_rpmsg_exit);
MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>");
MODULE_DESCRIPTION("Intel MSIC GPIO driver");
MODULE_LICENSE("GPL v2");