rk30 phone loauqt: add touch screen synaptics s3202 standar drivers

This commit is contained in:
hhb
2012-05-27 15:52:01 +08:00
parent 76ca4c2356
commit 7b8ca11798
20 changed files with 20877 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
#
# RMI4 configuration
#
config RMI4_BUS
bool "Synaptics RMI4 bus support"
depends on TOUCHSCREEN_SYNAPTICS_RMI4_I2C_RK
help
Say Y here if you want to support the Synaptics RMI4 bus.
If unsure, say Y.
This feature is not currently available as
a loadable module.
config RMI4_DEBUG
bool "Synaptics RMI4 debugging"
depends on RMI4_BUS
help
Say Y here to enable debug feature in the RMI4 driver.
Note that the RMI4 driver debug features can generate a lot of
output (potentially clogging up your dmesg output) and generally
slow down driver operation. It's recommended to enable them only
if you are actively developing/debugging RMI4 features.
If unsure, say N.
config RMI4_I2C
bool "RMI4 I2C Support"
depends on RMI4_BUS && I2C
help
Say Y here if you want to support RMI4 devices connected to an I2C
bus.
If unsure, say Y.
This feature is not currently available as a loadable module.
config RMI4_I2C_SCL_RATE
int "RMI4 I2C SCL RATE Support"
depends on RMI4_I2C
config RMI4_SPI
bool "RMI4 SPI Support"
depends on RMI4_BUS && SPI
help
Say Y here if you want to support RMI4 devices connected to an SPI
bus.
If unsure, say Y.
This feature is not currently available as a loadable module.
config RMI4_GENERIC
bool "RMI4 Generic driver"
depends on RMI4_BUS
help
Say Y here if you want to support generic RMI4 devices.
This is pretty much required if you want to do anything useful with
your RMI device.
This feature is not currently available as a loadable module.
config RMI4_F1A
tristate "RMI4 Function 1A (capacitive button sensor)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 1A.
Function 1A provides self testing for touchscreens and touchpads.
To compile this driver as a module, choose M here: the
module will be called rmi-f1a.
config RMI4_F09
tristate "RMI4 Function 09 (self testing)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 09.
Function 09 provides self testing for touchscreens and touchpads.
To compile this driver as a module, choose M here: the
module will be called rmi-f09.
config RMI4_F11
tristate "RMI4 Function 11 (2D pointing)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 11.
Function 11 provides 2D multifinger pointing for touchscreens and
touchpads. For sensors that support relative pointing, F11 also
provides mouse input.
To compile this driver as a module, choose M here: the
module will be called rmi-f11.
config RMI4_F11_PEN
bool "RMI4 F11 Pen Support"
depends on RMI4_F11
help
Say Y here to add support for pen input to RMI4 function 11.
If this feature is enabled, when pen inputs are detected they
will be reported to the input stream as MT_TOOL_PEN. Otherwise,
pens will be treated the same as fingers.
Not all UI implementations deal gracefully with pen discrimination.
If your system is not recognizing pen touches and you know your
sensor supports pen input, you probably want to turn this feature
off.
config RMI4_VIRTUAL_BUTTON
tristate "RMI4 Vitual Button"
depends on RMI4_F11
help
Say Y here if you want to add support for RMI4 virtual button to F11.
The virtual button feature implement the virtual button device in
certain RMI4 touch sensors.
This works only if your sensor supports F11 gestures.
config RMI4_F17
tristate "RMI4 Function 17 (pointing sticks)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 17.
Function 19 provides support for capacitive and resistive
pointing sticks.
To compile this driver as a module, choose M here: the
module will be called rmi-f17.
config RMI4_F19
tristate "RMI4 Function 19 (0D pointing)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 19.
Function 19 provides support for capacitive buttons for sensors
that implement capacitive buttons.
To compile this driver as a module, choose M here: the
module will be called rmi-f19.
config RMI4_F21
tristate "RMI4 Function 21 (2D Force)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 21.
Function 21 provides 2D Force Sensing for ForcePad products.
To compile this driver as a module, choose M here: the
module will be called rmi-f21.
config RMI4_F34
tristate "RMI4 Function 34 (device reflash)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 34.
Function 34 provides firmware upgrade capability for your sensor.
To compile this driver as a module, choose M here: the
module will be called rmi-f34.
config RMI4_REFLASH_WHEN_BOOT
tristate "RMI4 Function 34 (device reflash when system boot)"
depends on RMI4_BUS && RMI4_GENERIC
config RMI4_F54
tristate "RMI4 Function 54 (analog diagnostics)"
depends on RMI4_BUS && RMI4_GENERIC
help
Say Y here if you want to add support for RMI4 function 54.
Function 54 provides access to various diagnostic features in
certain RMI4 touch sensors.
To compile this driver as a module, choose M here: the
module will be called rmi-f54.
config RMI4_DEV
tristate "Synaptics direct RMI device support (rmidev)"
depends on GPIO_SYSFS && (RMI4_I2C || RMI4_SPI)
help
Say Y here to add support for rmidev.
The rmidev feature implements a character device providing access
to RMI4 sensor register maps.
To compile this driver as a module, choose M here: the
module will be called rmi-dev.

View File

@@ -0,0 +1,34 @@
obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
obj-$(CONFIG_RMI4_SPI) += rmi_spi.o
obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
obj-$(CONFIG_RMI4_F09) += rmi_f09.o
obj-$(CONFIG_RMI4_F1A) += rmi_f1a.o
obj-$(CONFIG_RMI4_F11) += rmi_f11.o
obj-$(CONFIG_RMI4_F17) += rmi_f17.o
obj-$(CONFIG_RMI4_F19) += rmi_f19.o
obj-$(CONFIG_RMI4_F21) += rmi_f21.o
obj-$(CONFIG_RMI4_F34) += rmi_f34.o
obj-$(CONFIG_RMI4_F54) += rmi_f54.o
obj-$(CONFIG_RMI4_DEV) += rmi_dev.o
obj-y += rmi_reflash.o
ifeq ($(KERNELRELEASE),)
# KERNELDIR ?= /home/<AndroidKernelDirectory>
PWD := $(shell pwd)
.PHONY: build clean
build:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
else
$(info Building with KERNELRELEASE = ${KERNELRELEASE})
obj-m += rmi_dev.o
endif

View File

@@ -0,0 +1,482 @@
/*
* Copyright (c) 2011 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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/kernel.h>
#include <linux/device.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/rmi.h>
#include <linux/types.h>
#ifdef CONFIG_RMI4_DEBUG
#include <linux/debugfs.h>
#endif
#include "rmi_driver.h"
DEFINE_MUTEX(rmi_bus_mutex);
static struct rmi_function_list {
struct list_head list;
struct rmi_function_handler *fh;
} rmi_supported_functions;
static struct rmi_character_driver_list {
struct list_head list;
struct rmi_char_driver *cd;
} rmi_character_drivers;
static atomic_t physical_device_count;
#ifdef CONFIG_RMI4_DEBUG
static struct dentry *rmi_debugfs_root;
#endif
static int rmi_bus_match(struct device *dev, struct device_driver *driver)
{
struct rmi_driver *rmi_driver;
struct rmi_device *rmi_dev;
struct rmi_device_platform_data *pdata;
rmi_driver = to_rmi_driver(driver);
rmi_dev = to_rmi_device(dev);
pdata = to_rmi_platform_data(rmi_dev);
dev_dbg(dev, "%s: Matching %s.\n", __func__, pdata->sensor_name);
if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) {
rmi_dev->driver = rmi_driver;
dev_dbg(dev, "%s: Match %s to %s succeeded.\n", __func__,
pdata->driver_name, rmi_driver->driver.name);
return 1;
}
dev_vdbg(dev, "%s: Match %s to %s failed.\n", __func__,
pdata->driver_name, rmi_driver->driver.name);
return 0;
}
#ifdef CONFIG_PM
static int rmi_bus_suspend(struct device *dev)
{
#ifdef GENERIC_SUBSYS_PM_OPS
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (pm && pm->suspend)
return pm->suspend(dev);
#endif
return 0;
}
static int rmi_bus_resume(struct device *dev)
{
#ifdef GENERIC_SUBSYS_PM_OPS
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (pm && pm->resume)
return pm->resume(dev);
else if (dev->driver && dev->driver->resume)
return dev->driver->resume(dev);
#else
if (dev->driver && dev->driver->resume)
return dev->driver->resume(dev);
#endif
return 0;
}
#endif
static int rmi_bus_probe(struct device *dev)
{
struct rmi_driver *driver;
struct rmi_device *rmi_dev = to_rmi_device(dev);
driver = rmi_dev->driver;
if (driver && driver->probe)
return driver->probe(rmi_dev);
return 0;
}
static int rmi_bus_remove(struct device *dev)
{
struct rmi_driver *driver;
struct rmi_device *rmi_dev = to_rmi_device(dev);
driver = rmi_dev->driver;
if (driver && driver->remove)
return driver->remove(rmi_dev);
return 0;
}
static void rmi_bus_shutdown(struct device *dev)
{
struct rmi_driver *driver;
struct rmi_device *rmi_dev = to_rmi_device(dev);
driver = rmi_dev->driver;
if (driver && driver->shutdown)
driver->shutdown(rmi_dev);
}
static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops,
rmi_bus_suspend, rmi_bus_resume);
struct bus_type rmi_bus_type = {
.name = "rmi",
.match = rmi_bus_match,
.probe = rmi_bus_probe,
.remove = rmi_bus_remove,
.shutdown = rmi_bus_shutdown,
.pm = &rmi_bus_pm_ops
};
static void release_rmidev_device(struct device *dev) {
device_unregister(dev);
}
int rmi_register_phys_device(struct rmi_phys_device *phys)
{
struct rmi_device_platform_data *pdata = phys->dev->platform_data;
struct rmi_device *rmi_dev;
if (!pdata) {
dev_err(phys->dev, "no platform data!\n");
return -EINVAL;
}
rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
if (!rmi_dev)
return -ENOMEM;
rmi_dev->phys = phys;
rmi_dev->dev.bus = &rmi_bus_type;
rmi_dev->number = atomic_inc_return(&physical_device_count) - 1;
rmi_dev->dev.release = release_rmidev_device;
dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
pdata->sensor_name, dev_name(&rmi_dev->dev));
#ifdef CONFIG_RMI4_DEBUG
if (rmi_debugfs_root) {
rmi_dev->debugfs_root = debugfs_create_dir(
dev_name(&rmi_dev->dev), rmi_debugfs_root);
if (!rmi_dev->debugfs_root)
dev_err(&rmi_dev->dev, "Failed to create debugfs root.\n");
}
#endif
phys->rmi_dev = rmi_dev;
return device_register(&rmi_dev->dev);
}
EXPORT_SYMBOL(rmi_register_phys_device);
void rmi_unregister_phys_device(struct rmi_phys_device *phys)
{
struct rmi_device *rmi_dev = phys->rmi_dev;
#ifdef CONFIG_RMI4_DEBUG
if (rmi_dev->debugfs_root)
debugfs_remove(rmi_dev->debugfs_root);
#endif
kfree(rmi_dev);
}
EXPORT_SYMBOL(rmi_unregister_phys_device);
int rmi_register_driver(struct rmi_driver *driver)
{
driver->driver.bus = &rmi_bus_type;
return driver_register(&driver->driver);
}
EXPORT_SYMBOL(rmi_register_driver);
static int __rmi_driver_remove(struct device *dev, void *data)
{
struct rmi_driver *driver = data;
struct rmi_device *rmi_dev = to_rmi_device(dev);
if (rmi_dev->driver == driver)
rmi_dev->driver = NULL;
return 0;
}
void rmi_unregister_driver(struct rmi_driver *driver)
{
bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove);
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(rmi_unregister_driver);
static int __rmi_bus_fh_add(struct device *dev, void *data)
{
struct rmi_driver *driver;
struct rmi_device *rmi_dev = to_rmi_device(dev);
driver = rmi_dev->driver;
if (driver && driver->fh_add)
driver->fh_add(rmi_dev, data);
return 0;
}
int rmi_register_function_driver(struct rmi_function_handler *fh)
{
struct rmi_function_list *entry;
struct rmi_function_handler *fh_dup;
fh_dup = rmi_get_function_handler(fh->func);
if (fh_dup) {
pr_err("%s: function f%.2x already registered!\n", __func__,
fh->func);
return -EINVAL;
}
entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->fh = fh;
INIT_LIST_HEAD(&entry->list);
list_add_tail(&entry->list, &rmi_supported_functions.list);
/* notify devices of the new function handler */
bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add);
return 0;
}
EXPORT_SYMBOL(rmi_register_function_driver);
static int __rmi_bus_fh_remove(struct device *dev, void *data)
{
struct rmi_driver *driver;
struct rmi_device *rmi_dev = to_rmi_device(dev);
driver = rmi_dev->driver;
if (driver && driver->fh_remove)
driver->fh_remove(rmi_dev, data);
return 0;
}
void rmi_unregister_function_driver(struct rmi_function_handler *fh)
{
struct rmi_function_list *entry, *n;
/* notify devices of the removal of the function handler */
bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove);
if (list_empty(&rmi_supported_functions.list))
return;
list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
list) {
if (entry->fh->func == fh->func) {
list_del(&entry->list);
kfree(entry);
}
}
}
EXPORT_SYMBOL(rmi_unregister_function_driver);
struct rmi_function_handler *rmi_get_function_handler(int id)
{
struct rmi_function_list *entry;
if (list_empty(&rmi_supported_functions.list))
return NULL;
list_for_each_entry(entry, &rmi_supported_functions.list, list)
if (entry->fh->func == id)
return entry->fh;
return NULL;
}
EXPORT_SYMBOL(rmi_get_function_handler);
static void rmi_release_character_device(struct device *dev)
{
dev_dbg(dev, "%s: Called.\n", __func__);
return;
}
static int rmi_register_character_device(struct device *dev, void *data)
{
struct rmi_device *rmi_dev;
struct rmi_char_driver *char_driver = data;
struct rmi_char_device *char_dev;
int retval;
dev_dbg(dev, "Attaching character device.\n");
rmi_dev = to_rmi_device(dev);
if (char_driver->match && !char_driver->match(rmi_dev))
return 0;
if (!char_driver->init) {
dev_err(dev, "ERROR: No init() function in %s.\n", __func__);
return -EINVAL;
}
char_dev = kzalloc(sizeof(struct rmi_char_device), GFP_KERNEL);
if (!char_dev)
return -ENOMEM;
char_dev->rmi_dev = rmi_dev;
char_dev->driver = char_driver;
char_dev->dev.parent = dev;
char_dev->dev.release = rmi_release_character_device;
char_dev->dev.driver = &char_driver->driver;
retval = device_register(&char_dev->dev);
if (!retval) {
dev_err(dev, "Failed to register character device.\n");
goto error_exit;
}
retval = char_driver->init(char_dev);
if (retval) {
dev_err(dev, "Failed to initialize character device.\n");
goto error_exit;
}
mutex_lock(&rmi_bus_mutex);
list_add_tail(&char_dev->list, &char_driver->devices);
mutex_unlock(&rmi_bus_mutex);
dev_info(&char_dev->dev, "Registered a device.\n");
return retval;
error_exit:
kfree(char_dev);
return retval;
}
int rmi_register_character_driver(struct rmi_char_driver *char_driver)
{
struct rmi_character_driver_list *entry;
int retval;
pr_debug("%s: Registering character driver %s.\n", __func__,
char_driver->driver.name);
char_driver->driver.bus = &rmi_bus_type;
INIT_LIST_HEAD(&char_driver->devices);
retval = driver_register(&char_driver->driver);
if (retval) {
pr_err("%s: Failed to register %s, code: %d.\n", __func__,
char_driver->driver.name, retval);
return retval;
}
entry = kzalloc(sizeof(struct rmi_character_driver_list), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->cd = char_driver;
mutex_lock(&rmi_bus_mutex);
list_add_tail(&entry->list, &rmi_character_drivers.list);
mutex_unlock(&rmi_bus_mutex);
/* notify devices of the removal of the function handler */
bus_for_each_dev(&rmi_bus_type, NULL, char_driver,
rmi_register_character_device);
return 0;
}
EXPORT_SYMBOL(rmi_register_character_driver);
int rmi_unregister_character_driver(struct rmi_char_driver *char_driver)
{
struct rmi_character_driver_list *entry, *n;
struct rmi_char_device *char_dev, *m;
pr_debug("%s: Unregistering character driver %s.\n", __func__,
char_driver->driver.name);
mutex_lock(&rmi_bus_mutex);
list_for_each_entry_safe(char_dev, m, &char_driver->devices,
list) {
list_del(&char_dev->list);
char_dev->driver->remove(char_dev);
}
list_for_each_entry_safe(entry, n, &rmi_character_drivers.list,
list) {
if (entry->cd == char_driver) {
list_del(&entry->list);
kfree(entry);
}
}
mutex_unlock(&rmi_bus_mutex);
driver_unregister(&char_driver->driver);
return 0;
}
EXPORT_SYMBOL(rmi_unregister_character_driver);
static int __init rmi_bus_init(void)
{
int error;
mutex_init(&rmi_bus_mutex);
INIT_LIST_HEAD(&rmi_supported_functions.list);
INIT_LIST_HEAD(&rmi_character_drivers.list);
#ifdef CONFIG_RMI4_DEBUG
rmi_debugfs_root = debugfs_create_dir(rmi_bus_type.name, NULL);
if (!rmi_debugfs_root)
pr_err("%s: Failed to create debugfs root.\n", __func__);
else if (IS_ERR(rmi_debugfs_root)) {
pr_err("%s: Kernel may not contain debugfs support, code=%ld\n",
__func__, PTR_ERR(rmi_debugfs_root));
rmi_debugfs_root = NULL;
}
#endif
error = bus_register(&rmi_bus_type);
if (error < 0) {
pr_err("%s: error registering the RMI bus: %d\n", __func__,
error);
return error;
}
pr_debug("%s: successfully registered RMI bus.\n", __func__);
return 0;
}
static void __exit rmi_bus_exit(void)
{
/* We should only ever get here if all drivers are unloaded, so
* all we have to do at this point is unregister ourselves.
*/
#ifdef CONFIG_RMI4_DEBUG
if (rmi_debugfs_root)
debugfs_remove(rmi_debugfs_root);
#endif
bus_unregister(&rmi_bus_type);
}
module_init(rmi_bus_init);
module_exit(rmi_bus_exit);
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
MODULE_DESCRIPTION("RMI bus");
MODULE_LICENSE("GPL");
MODULE_VERSION(RMI_DRIVER_VERSION);

View File

@@ -0,0 +1,448 @@
/*
* Copyright (c) 2011 Synaptics Incorporated
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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/fs.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/syscalls.h>
#include <linux/rmi.h>
#include "rmi_driver.h"
#define CHAR_DEVICE_NAME "rmi"
#define DEVICE_CLASS_NAME "rmidev"
#define RMI_CHAR_DEV_TMPBUF_SZ 128
#define RMI_REG_ADDR_PAGE_SELECT 0xFF
#define REG_ADDR_LIMIT 0xFFFF
struct rmidev_data {
/* mutex for file operation*/
struct mutex file_mutex;
/* main char dev structure */
struct cdev main_dev;
/* pointer to the corresponding RMI4 device. We use this to do */
/* read, write, etc. */
struct rmi_device *rmi_dev;
/* reference count */
int ref_count;
struct class *device_class;
};
/*store dynamically allocated major number of char device*/
static int rmidev_major_num;
static struct class *rmidev_device_class;
/* file operations for RMI char device */
/*
* rmidev_llseek: - use to setup register address
*
* @filp: file structure for seek
* @off: offset
* if whence == SEEK_SET,
* high 16 bits: page address
* low 16 bits: register address
*
* if whence == SEEK_CUR,
* offset from current position
*
* if whence == SEEK_END,
* offset from END(0xFFFF)
*
* @whence: SEEK_SET , SEEK_CUR or SEEK_END
*/
static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
{
loff_t newpos;
struct rmidev_data *data = filp->private_data;
if (IS_ERR(data)) {
pr_err("%s: pointer of char device is invalid", __func__);
return -EBADF;
}
mutex_lock(&(data->file_mutex));
switch (whence) {
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = filp->f_pos + off;
break;
case SEEK_END:
newpos = REG_ADDR_LIMIT + off;
break;
default: /* can't happen */
newpos = -EINVAL;
goto clean_up;
}
if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
dev_err(&data->rmi_dev->dev, "newpos 0x%04x is invalid.\n",
(unsigned int)newpos);
newpos = -EINVAL;
goto clean_up;
}
filp->f_pos = newpos;
clean_up:
mutex_unlock(&(data->file_mutex));
return newpos;
}
/*
* rmidev_read: - use to read data from RMI stream
*
* @filp: file structure for read
* @buf: user-level buffer pointer
*
* @count: number of byte read
* @f_pos: offset (starting register address)
*
* @return number of bytes read into user buffer (buf) if succeeds
* negative number if error occurs.
*/
static ssize_t rmidev_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct rmidev_data *data = filp->private_data;
ssize_t retval = 0;
unsigned char tmpbuf[count+1];
/* limit offset to REG_ADDR_LIMIT-1 */
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
if (count == 0)
return 0;
if (IS_ERR(data)) {
pr_err("%s: pointer of char device is invalid", __func__);
return -EBADF;
}
mutex_lock(&(data->file_mutex));
retval = rmi_read_block(data->rmi_dev, *f_pos, tmpbuf, count);
if (retval < 0)
goto clean_up;
if (copy_to_user(buf, tmpbuf, count))
retval = -EFAULT;
else
*f_pos += retval;
clean_up:
mutex_unlock(&(data->file_mutex));
return retval;
}
/*
* rmidev_write: - use to write data into RMI stream
*
* @filep : file structure for write
* @buf: user-level buffer pointer contains data to be written
* @count: number of byte be be written
* @f_pos: offset (starting register address)
*
* @return number of bytes written from user buffer (buf) if succeeds
* negative number if error occurs.
*/
static ssize_t rmidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct rmidev_data *data = filp->private_data;
ssize_t retval = 0;
unsigned char tmpbuf[count+1];
/* limit offset to REG_ADDR_LIMIT-1 */
if (count > (REG_ADDR_LIMIT - *f_pos))
count = REG_ADDR_LIMIT - *f_pos;
if (count == 0)
return 0;
if (IS_ERR(data)) {
pr_err("%s: pointer of char device is invalid", __func__);
return -EBADF;
}
if (copy_from_user(tmpbuf, buf, count))
return -EFAULT;
mutex_lock(&(data->file_mutex));
retval = rmi_write_block(data->rmi_dev, *f_pos, tmpbuf, count);
if (retval >= 0)
*f_pos += count;
mutex_unlock(&(data->file_mutex));
return retval;
}
/*
* rmidev_open: - get a new handle for from RMI stream
* @inp : inode struture
* @filp: file structure for read/write
*
* @return 0 if succeeds
*/
static int rmidev_open(struct inode *inp, struct file *filp)
{
struct rmidev_data *data = container_of(inp->i_cdev,
struct rmidev_data, main_dev);
int retval = 0;
filp->private_data = data;
if (!data->rmi_dev)
return -EACCES;
mutex_lock(&(data->file_mutex));
if (data->ref_count < 1)
data->ref_count++;
else
retval = -EACCES;
mutex_unlock(&(data->file_mutex));
return retval;
}
/*
* rmidev_release: - release an existing handle
* @inp: inode structure
* @filp: file structure for read/write
*
* @return 0 if succeeds
*/
static int rmidev_release(struct inode *inp, struct file *filp)
{
struct rmidev_data *data = container_of(inp->i_cdev,
struct rmidev_data, main_dev);
if (!data->rmi_dev)
return -EACCES;
mutex_lock(&(data->file_mutex));
data->ref_count--;
if (data->ref_count < 0)
data->ref_count = 0;
mutex_unlock(&(data->file_mutex));
return 0;
}
static const struct file_operations rmidev_fops = {
.owner = THIS_MODULE,
.llseek = rmidev_llseek,
.read = rmidev_read,
.write = rmidev_write,
.open = rmidev_open,
.release = rmidev_release,
};
/*
* rmidev_device_cleanup - release memory or unregister driver
* @rmidev_data: instance data for a particular device.
*
*/
static void rmidev_device_cleanup(struct rmidev_data *data)
{
dev_t devno;
/* Get rid of our char dev entries */
if (data) {
devno = data->main_dev.dev;
if (data->device_class)
device_destroy(data->device_class, devno);
cdev_del(&data->main_dev);
kfree(data);
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, 1);
pr_debug("%s: rmidev device is removed\n", __func__);
}
}
/*
* rmi_char_devnode - return device permission
*
* @dev: char device structure
* @mode: file permission
*
*/
static char *rmi_char_devnode(struct device *dev, mode_t *mode)
{
if (!mode)
return NULL;
/**mode = 0666*/
*mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
}
static int rmidev_init_device(struct rmi_char_device *cd)
{
struct rmi_device *rmi_dev = cd->rmi_dev;
struct rmidev_data *data;
dev_t dev_no;
int retval;
struct device *device_ptr;
if (rmidev_major_num) {
dev_no = MKDEV(rmidev_major_num, cd->rmi_dev->number);
retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
} else {
retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
/* let kernel allocate a major for us */
rmidev_major_num = MAJOR(dev_no);
dev_info(&rmi_dev->dev, "Major number of rmidev: %d\n",
rmidev_major_num);
}
if (retval < 0) {
dev_err(&rmi_dev->dev,
"Failed to get minor dev number %d, code %d.\n",
cd->rmi_dev->number, retval);
return retval;
} else
dev_info(&rmi_dev->dev, "Allocated rmidev %d %d.\n",
MAJOR(dev_no), MINOR(dev_no));
data = kzalloc(sizeof(struct rmidev_data), GFP_KERNEL);
if (!data) {
dev_err(&rmi_dev->dev, "Failed to allocate rmidev_data.\n");
/* unregister the char device region */
__unregister_chrdev(rmidev_major_num, MINOR(dev_no), 1,
CHAR_DEVICE_NAME);
return -ENOMEM;
}
mutex_init(&data->file_mutex);
data->rmi_dev = cd->rmi_dev;
cd->data = data;
cdev_init(&data->main_dev, &rmidev_fops);
retval = cdev_add(&data->main_dev, dev_no, 1);
if (retval) {
dev_err(&cd->rmi_dev->dev, "Error %d adding rmi_char_dev.\n",
retval);
rmidev_device_cleanup(data);
return retval;
}
dev_set_name(&cd->dev, "rmidev%d", MINOR(dev_no));
data->device_class = rmidev_device_class;
device_ptr = device_create(
data->device_class,
NULL, dev_no, NULL,
CHAR_DEVICE_NAME"%d",
MINOR(dev_no));
if (IS_ERR(device_ptr)) {
dev_err(&cd->rmi_dev->dev, "Failed to create rmi device.\n");
rmidev_device_cleanup(data);
return -ENODEV;
}
return 0;
}
static void rmidev_remove_device(struct rmi_char_device *cd)
{
struct rmidev_data *data;
dev_dbg(&cd->dev, "%s: removing an rmidev device.\n", __func__);
if (!cd)
return;
data = cd->data;
if (data)
rmidev_device_cleanup(data);
}
static struct rmi_char_driver rmidev_driver = {
.driver = {
.name = "rmidev",
.owner = THIS_MODULE,
},
.init = rmidev_init_device,
.remove = rmidev_remove_device,
};
static int __init rmidev_init(void)
{
int error = 0;
pr_debug("%s: rmi_dev initialization.\n", __func__);
/* create device node */
rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
if (IS_ERR(rmidev_device_class)) {
pr_err("%s: ERROR - Failed to create /dev/%s.\n", __func__,
CHAR_DEVICE_NAME);
return -ENODEV;
}
/* setup permission */
rmidev_device_class->devnode = rmi_char_devnode;
error = rmi_register_character_driver(&rmidev_driver);
if (error)
class_destroy(rmidev_device_class);
return error;
}
static void __exit rmidev_exit(void)
{
pr_debug("%s: exiting.\n", __func__);
rmi_unregister_character_driver(&rmidev_driver);
class_destroy(rmidev_device_class);
}
module_init(rmidev_init);
module_exit(rmidev_exit);
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
MODULE_DESCRIPTION("RMI4 Char Device");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,405 @@
/*
* Copyright (c) 2011 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*/
#ifndef _RMI_DRIVER_H
#define _RMI_DRIVER_H
#define RMI_DRIVER_VERSION "1.3"
#define RMI_PRODUCT_ID_LENGTH 10
#define RMI_PRODUCT_INFO_LENGTH 2
#define RMI_DATE_CODE_LENGTH 3
#include <linux/ctype.h>
/* Sysfs related macros */
/* You must define FUNCTION_DATA and FNUM to use these functions. */
#define RMI4_SYSFS_DEBUG defined(CONFIG_RMI4_DEBUG) || defined(CONFIG_ANDROID))
#if defined(FNUM) && defined(FUNCTION_DATA)
#define tricat(x,y,z) tricat_(x,y,z)
#define tricat_(x,y,z) x##y##z
#define show_union_struct_prototype(propname)\
static ssize_t tricat(rmi_fn_,FNUM,_##propname##_show)(\
struct device *dev,\
struct device_attribute *attr,\
char *buf);\
\
DEVICE_ATTR(propname, RMI_RO_ATTR,\
tricat(rmi_fn_,FNUM,_##propname##_show),\
rmi_store_error);
#define store_union_struct_prototype(propname)\
static ssize_t tricat(rmi_fn_,FNUM,_##propname##_store)(\
struct device *dev,\
struct device_attribute *attr,\
const char *buf, size_t count);\
\
DEVICE_ATTR(propname, RMI_WO_ATTR,\
rmi_show_error,\
tricat(rmi_fn_, FNUM, _##propname##_store));
#define show_store_union_struct_prototype(propname)\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
struct device *dev,\
struct device_attribute *attr,\
char *buf);\
\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
struct device *dev,\
struct device_attribute *attr,\
const char *buf, size_t count);\
\
DEVICE_ATTR(propname, RMI_RW_ATTR,\
tricat(rmi_fn_, FNUM, _##propname##_show),\
tricat(rmi_fn_, FNUM, _##propname##_store));
#define simple_show_union_struct(regtype, propname, fmt)\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
struct device_attribute *attr, char *buf) {\
struct rmi_function_container *fc;\
struct FUNCTION_DATA *data;\
\
fc = to_rmi_function_container(dev);\
data = fc->data;\
\
return snprintf(buf, PAGE_SIZE, fmt,\
data->regtype.propname);\
}
#define show_union_struct(regtype, reg_group, propname, fmt)\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
struct device *dev,\
struct device_attribute *attr,\
char *buf) {\
struct rmi_function_container *fc;\
struct FUNCTION_DATA *data;\
int result;\
\
fc = to_rmi_function_container(dev);\
data = fc->data;\
\
mutex_lock(&data->regtype##_mutex);\
/* Read current regtype values */\
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8 *)data->regtype.reg_group,\
sizeof(data->regtype.reg_group->regs));\
mutex_unlock(&data->regtype##_mutex);\
if (result < 0) {\
dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
__func__, data->regtype.reg_group->address);\
return result;\
}\
return snprintf(buf, PAGE_SIZE, fmt,\
data->regtype.reg_group->propname);\
}\
#define show_store_union_struct(regtype, reg_group, propname, fmt)\
show_union_struct(regtype, reg_group, propname, fmt)\
\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
struct device *dev,\
struct device_attribute *attr,\
const char *buf, size_t count) {\
int result;\
unsigned long val;\
unsigned long old_val;\
struct rmi_function_container *fc;\
struct FUNCTION_DATA *data;\
\
fc = to_rmi_function_container(dev);\
data = fc->data;\
\
/* need to convert the string data to an actual value */\
result = strict_strtoul(buf, 10, &val);\
\
/* if an error occured, return it */\
if (result)\
return result;\
/* Check value maybe */\
\
/* Read current regtype values */\
mutex_lock(&data->regtype##_mutex);\
result =\
rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8 *)data->regtype.reg_group,\
sizeof(data->regtype.reg_group->regs));\
\
if (result < 0) {\
mutex_unlock(&data->regtype##_mutex);\
dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
__func__,\
data->regtype.reg_group->address);\
return result;\
}\
/* if the current regtype registers are already set as we want them,\
* do nothing to them */\
if (data->regtype.reg_group->propname == val) {\
mutex_unlock(&data->regtype##_mutex);\
return count;\
}\
/* Write the regtype back to the regtype register */\
old_val = data->regtype.reg_group->propname;\
data->regtype.reg_group->propname = val;\
result =\
rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8 *)data->regtype.reg_group,\
sizeof(data->regtype.reg_group->regs));\
if (result < 0) {\
dev_dbg(dev, "%s : Could not write regtype to 0x%x\\n",\
__func__,\
data->regtype.reg_group->address);\
/* revert change to local value if value not written */\
data->regtype.reg_group->propname = old_val;\
mutex_unlock(&data->regtype##_mutex);\
return result;\
}\
mutex_unlock(&data->regtype##_mutex);\
return count;\
}
#define show_repeated_union_struct(regtype, reg_group, propname, fmt)\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
struct device_attribute *attr,\
char *buf) {\
struct rmi_function_container *fc;\
struct FUNCTION_DATA *data;\
int reg_length;\
int result, size = 0;\
char *temp;\
int i;\
\
fc = to_rmi_function_container(dev);\
data = fc->data;\
mutex_lock(&data->regtype##_mutex);\
\
/* Read current regtype values */\
reg_length = data->regtype.reg_group->length;\
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8*) data->regtype.reg_group->regs,\
reg_length * sizeof(u8));\
mutex_unlock(&data->regtype##_mutex);\
if (result < 0) {\
dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
"Data may be outdated.", __func__,\
data->regtype.reg_group->address);\
}\
temp = buf;\
for (i = 0; i < reg_length; i++) {\
result = snprintf(temp, PAGE_SIZE - size, fmt " ",\
data->regtype.reg_group->regs[i].propname);\
if (result < 0) {\
dev_err(dev, "%s : Could not write output.", __func__);\
return result;\
}\
size += result;\
temp += result;\
}\
result = snprintf(temp, PAGE_SIZE - size, "\n");\
if (result < 0) {\
dev_err(dev, "%s : Could not write output.", __func__);\
return result;\
}\
return size + result;\
}
#define show_store_repeated_union_struct(regtype, reg_group, propname, fmt)\
show_repeated_union_struct(regtype, reg_group, propname, fmt)\
\
static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(struct device *dev,\
struct device_attribute *attr,\
const char *buf, size_t count) {\
struct rmi_function_container *fc;\
struct FUNCTION_DATA *data;\
int reg_length;\
int result;\
const char *temp;\
int i;\
unsigned int newval;\
\
fc = to_rmi_function_container(dev);\
data = fc->data;\
mutex_lock(&data->regtype##_mutex);\
\
/* Read current regtype values */\
\
reg_length = data->regtype.reg_group->length;\
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8*) data->regtype.reg_group->regs,\
reg_length * sizeof(u8));\
\
if (result < 0) {\
dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
"Data may be outdated.", __func__,\
data->regtype.reg_group->address);\
}\
/* parse input */\
\
temp = buf;\
for (i = 0; i < reg_length; i++) {\
if(sscanf(temp, fmt, &newval) == 1) {\
data->regtype.reg_group->regs[i].propname = newval;\
} else {\
/* If we don't read a value for each position, abort, restore
* previous values locally by rereading */\
result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8*) data->regtype.reg_group->regs,\
reg_length * sizeof(u8));\
\
if (result < 0) {\
dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
"Local data may be innacurrate.", __func__,\
data->regtype.reg_group->address);\
}\
return -EINVAL;\
}\
/* move to next number */\
while (*temp != 0) {\
temp++;\
if (isspace(*(temp - 1)) && !isspace(*temp))\
break;\
}\
}\
result = rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
(u8*) data->regtype.reg_group->regs,\
reg_length * sizeof(u8));\
mutex_unlock(&data->regtype##_mutex);\
if (result < 0) {\
dev_dbg(dev, "%s : Could not write new values"\
" to 0x%x\n", __func__, data->regtype.reg_group->address);\
return result;\
}\
return count;\
}
/* Create templates for given types */
#define simple_show_union_struct_unsigned(regtype, propname)\
simple_show_union_struct(regtype, propname, "%u\n")
#define show_union_struct_unsigned(regtype, reg_group, propname)\
show_union_struct(regtype, reg_group, propname, "%u\n")
#define show_store_union_struct_unsigned(regtype, reg_group, propname)\
show_store_union_struct(regtype, reg_group, propname, "%u\n")
#define show_repeated_union_struct_unsigned(regtype, reg_group, propname)\
show_repeated_union_struct(regtype, reg_group, propname, "%u")
#define show_store_repeated_union_struct_unsigned(regtype, reg_group, propname)\
show_store_repeated_union_struct(regtype, reg_group, propname, "%u")
/* Remove access to raw format string versions */
/*#undef simple_show_union_struct
#undef show_union_struct_unsigned
#undef show_store_union_struct
#undef show_repeated_union_struct
#undef show_store_repeated_union_struct*/
#endif
#define GROUP(_attrs) { \
.attrs = _attrs, \
}
#define attrify(nm) &dev_attr_##nm.attr
union f01_device_status {
struct {
u8 status_code:4;
u8 reserved:2;
u8 flash_prog:1;
u8 unconfigured:1;
};
u8 reg;
};
struct rmi_driver_data {
struct rmi_function_container rmi_functions;
struct rmi_function_container *f01_container;
bool f01_bootloader_mode;
int num_of_irq_regs;
int irq_count;
u8 *current_irq_mask;
u8 *irq_mask_store;
bool irq_stored;
struct mutex irq_mutex;
struct mutex pdt_mutex;
unsigned char pdt_props;
unsigned char bsr;
bool enabled;
#ifdef CONFIG_PM
bool suspended;
struct mutex suspend_mutex;
void *pm_data;
int (*pre_suspend) (const void *pm_data);
int (*post_resume) (const void *pm_data);
#endif
#ifdef CONFIG_RMI4_DEBUG
#ifdef CONFIG_RMI4_SPI
struct dentry *debugfs_delay;
#endif
struct dentry *debugfs_phys;
struct dentry *debugfs_reg_ctl;
struct dentry *debugfs_reg;
u16 reg_debug_addr;
u8 reg_debug_size;
#endif
void *data;
};
struct pdt_entry {
u8 query_base_addr:8;
u8 command_base_addr:8;
u8 control_base_addr:8;
u8 data_base_addr:8;
u8 interrupt_source_count:3;
u8 bits3and4:2;
u8 function_version:2;
u8 bit7:1;
u8 function_number:8;
};
int rmi_driver_f01_init(struct rmi_device *rmi_dev);
static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
struct rmi_function_descriptor *fd,
u16 page_start)
{
fd->query_base_addr = pdt->query_base_addr + page_start;
fd->command_base_addr = pdt->command_base_addr + page_start;
fd->control_base_addr = pdt->control_base_addr + page_start;
fd->data_base_addr = pdt->data_base_addr + page_start;
fd->function_number = pdt->function_number;
fd->interrupt_source_count = pdt->interrupt_source_count;
fd->function_version = pdt->function_version;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,453 @@
/*
* Copyright (c) 2011 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*/
#define COMMS_DEBUG 0
#define IRQ_DEBUG 0
#if COMMS_DEBUG || IRQ_DEBUG
#define DEBUG
#endif
#include <linux/kernel.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/rmi.h>
#include "rmi_driver.h"
#define RMI_PAGE_SELECT_REGISTER 0xff
#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
#ifndef CONFIG_RMI4_I2C_SCL_RATE
#define CONFIG_RMI4_I2C_SCL_RATE 100000 // 100kHz
#endif
static char *phys_proto_name = "i2c";
struct rmi_i2c_data {
struct mutex page_mutex;
int page;
int enabled;
int irq;
int irq_flags;
struct rmi_phys_device *phys;
};
static irqreturn_t rmi_i2c_irq_thread(int irq, void *p)
{
struct rmi_phys_device *phys = p;
struct rmi_device *rmi_dev = phys->rmi_dev;
struct rmi_driver *driver = rmi_dev->driver;
struct rmi_device_platform_data *pdata = phys->dev->platform_data;
#if IRQ_DEBUG
dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
gpio_get_value(pdata->attn_gpio));
#endif
if (gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity) {
phys->info.attn_count++;
if (driver && driver->irq_handler && rmi_dev)
driver->irq_handler(rmi_dev, irq);
}
return IRQ_HANDLED;
}
/*
* rmi_set_page - Set RMI page
* @phys: The pointer to the rmi_phys_device struct
* @page: The new page address.
*
* RMI devices have 16-bit addressing, but some of the physical
* implementations (like SMBus) only have 8-bit addressing. So RMI implements
* a page address at 0xff of every page so we can reliable page addresses
* every 256 registers.
*
* The page_mutex lock must be held when this function is entered.
*
* Returns zero on success, non-zero on failure.
*/
static int rmi_set_page(struct rmi_phys_device *phys, unsigned int page)
{
struct i2c_client *client = to_i2c_client(phys->dev);
struct rmi_i2c_data *data = phys->data;
char txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
int retval;
#if COMMS_DEBUG
dev_dbg(&client->dev, "RMI4 I2C writes 3 bytes: %02x %02x\n",
txbuf[0], txbuf[1]);
#endif
phys->info.tx_count++;
phys->info.tx_bytes += sizeof(txbuf);
retval = i2c_master_normal_send(client, txbuf, sizeof(txbuf), CONFIG_RMI4_I2C_SCL_RATE);
if (retval != sizeof(txbuf)) {
phys->info.tx_errs++;
dev_err(&client->dev,
"%s: set page failed: %d.", __func__, retval);
return (retval < 0) ? retval : -EIO;
}
data->page = page;
return 0;
}
int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
int len)
{
struct i2c_client *client = to_i2c_client(phys->dev);
struct rmi_i2c_data *data = phys->data;
u8 txbuf[len + 1];
int retval;
#if COMMS_DEBUG
int i;
#endif
txbuf[0] = addr & 0xff;
memcpy(txbuf + 1, buf, len);
mutex_lock(&data->page_mutex);
if (RMI_I2C_PAGE(addr) != data->page) {
retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
if (retval < 0)
goto exit;
}
#if COMMS_DEBUG
dev_dbg(&client->dev, "RMI4 I2C writes %d bytes: ", sizeof(txbuf));
for (i = 0; i < sizeof(txbuf); i++)
dev_dbg(&client->dev, "%02x ", txbuf[i]);
dev_dbg(&client->dev, "\n");
#endif
phys->info.tx_count++;
phys->info.tx_bytes += sizeof(txbuf);
retval = i2c_master_normal_send(client, txbuf, sizeof(txbuf), CONFIG_RMI4_I2C_SCL_RATE);
if (retval < 0)
phys->info.tx_errs++;
else
retval--; /* don't count the address byte */
exit:
mutex_unlock(&data->page_mutex);
return retval;
}
static int rmi_i2c_write(struct rmi_phys_device *phys, u16 addr, u8 data)
{
int retval = rmi_i2c_write_block(phys, addr, &data, 1);
return (retval < 0) ? retval : 0;
}
int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
int len)
{
struct i2c_client *client = to_i2c_client(phys->dev);
struct rmi_i2c_data *data = phys->data;
u8 txbuf[1] = {addr & 0xff};
int retval;
#if COMMS_DEBUG
int i;
#endif
mutex_lock(&data->page_mutex);
if (RMI_I2C_PAGE(addr) != data->page) {
retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
if (retval < 0)
goto exit;
}
#if COMMS_DEBUG
dev_dbg(&client->dev, "RMI4 I2C writes 1 bytes: %02x\n", txbuf[0]);
#endif
phys->info.tx_count++;
phys->info.tx_bytes += sizeof(txbuf);
retval = i2c_master_normal_send(client, txbuf, sizeof(txbuf), CONFIG_RMI4_I2C_SCL_RATE);
if (retval != sizeof(txbuf)) {
phys->info.tx_errs++;
retval = (retval < 0) ? retval : -EIO;
goto exit;
}
retval = i2c_master_normal_recv(client, buf, len, CONFIG_RMI4_I2C_SCL_RATE);
phys->info.rx_count++;
phys->info.rx_bytes += len;
if (retval < 0)
phys->info.rx_errs++;
#if COMMS_DEBUG
else {
dev_dbg(&client->dev, "RMI4 I2C received %d bytes: ", len);
for (i = 0; i < len; i++)
dev_dbg(&client->dev, "%02x ", buf[i]);
dev_dbg(&client->dev, "\n");
}
#endif
exit:
mutex_unlock(&data->page_mutex);
return retval;
}
static int rmi_i2c_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
{
int retval = rmi_i2c_read_block(phys, addr, buf, 1);
return (retval < 0) ? retval : 0;
}
static int acquire_attn_irq(struct rmi_i2c_data *data)
{
return request_threaded_irq(data->irq, NULL, rmi_i2c_irq_thread,
data->irq_flags, dev_name(data->phys->dev), data->phys);
}
static int enable_device(struct rmi_phys_device *phys)
{
int retval = 0;
struct rmi_i2c_data *data = phys->data;
if (data->enabled)
return 0;
retval = acquire_attn_irq(data);
if (retval)
goto error_exit;
data->enabled = true;
dev_dbg(phys->dev, "Physical device enabled.\n");
return 0;
error_exit:
dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n",
retval);
return retval;
}
static void disable_device(struct rmi_phys_device *phys)
{
struct rmi_i2c_data *data = phys->data;
if (!data->enabled)
return;
disable_irq(data->irq);
free_irq(data->irq, data->phys);
dev_dbg(phys->dev, "Physical device disabled.\n");
data->enabled = false;
}
void CompleteReflash(struct rmi_phys_device *phys);
static int __devinit rmi_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct rmi_phys_device *rmi_phys;
struct rmi_i2c_data *data;
struct rmi_device_platform_data *pdata = client->dev.platform_data;
int error;
if (!pdata) {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
pr_info("%s: Probing %s at %#02x (IRQ %d).\n", __func__,
pdata->sensor_name ? pdata->sensor_name : "-no name-",
client->addr, pdata->attn_gpio);
error = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
if (!error) {
dev_err(&client->dev, "i2c_check_functionality error %d.\n",
error);
return error;
}
rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
if (!rmi_phys)
return -ENOMEM;
data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);
if (!data) {
error = -ENOMEM;
goto err_phys;
}
data->enabled = true; /* We plan to come up enabled. */
data->irq = gpio_to_irq(pdata->attn_gpio);
if (pdata->level_triggered) {
data->irq_flags = IRQF_ONESHOT |
((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
} else {
data->irq_flags =
(pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH) ?
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
}
data->phys = rmi_phys;
rmi_phys->data = data;
rmi_phys->dev = &client->dev;
rmi_phys->write = rmi_i2c_write;
rmi_phys->write_block = rmi_i2c_write_block;
rmi_phys->read = rmi_i2c_read;
rmi_phys->read_block = rmi_i2c_read_block;
rmi_phys->enable_device = enable_device;
rmi_phys->disable_device = disable_device;
rmi_phys->info.proto = phys_proto_name;
mutex_init(&data->page_mutex);
/* Setting the page to zero will (a) make sure the PSR is in a
* known state, and (b) make sure we can talk to the device.
*/
error = rmi_set_page(rmi_phys, 0);
if (error) {
dev_err(&client->dev, "Failed to set page select to 0.\n");
goto err_data;
}
if (pdata->gpio_config) {
error = pdata->gpio_config(pdata->gpio_data, true);
if (error < 0) {
dev_err(&client->dev, "failed to setup irq %d\n",
pdata->attn_gpio);
goto err_data;
}
}
error = rmi_register_phys_device(rmi_phys);
if (error) {
dev_err(&client->dev,
"failed to register physical driver at 0x%.2X.\n",
client->addr);
goto err_gpio;
}
i2c_set_clientdata(client, rmi_phys);
if (pdata->attn_gpio > 0) {
error = acquire_attn_irq(data);
if (error < 0) {
dev_err(&client->dev,
"request_threaded_irq failed %d\n",
pdata->attn_gpio);
goto err_unregister;
}
}
#if defined(CONFIG_RMI4_DEV)
error = gpio_export(pdata->attn_gpio, false);
if (error) {
dev_warn(&client->dev, "%s: WARNING: Failed to "
"export ATTN gpio!\n", __func__);
error = 0;
} else {
error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
pdata->attn_gpio);
if (error) {
dev_warn(&(rmi_phys->rmi_dev->dev),
"%s: WARNING: Failed to symlink ATTN gpio!\n",
__func__);
error = 0;
} else {
dev_info(&(rmi_phys->rmi_dev->dev),
"%s: Exported GPIO %d.", __func__,
pdata->attn_gpio);
}
}
#endif /* CONFIG_RMI4_DEV */
dev_info(&client->dev, "registered rmi i2c driver at 0x%.2X.\n",
client->addr);
//reflash the new firmware revision hhb@rock-chips.com
#ifdef CONFIG_RMI4_REFLASH_WHEN_BOOT
CompleteReflash(rmi_phys);
#endif
return 0;
err_unregister:
rmi_unregister_phys_device(rmi_phys);
err_gpio:
if (pdata->gpio_config)
pdata->gpio_config(pdata->gpio_data, false);
err_data:
kfree(data);
err_phys:
kfree(rmi_phys);
return error;
}
static int __devexit rmi_i2c_remove(struct i2c_client *client)
{
struct rmi_phys_device *phys = i2c_get_clientdata(client);
struct rmi_device_platform_data *pd = client->dev.platform_data;
disable_device(phys);
rmi_unregister_phys_device(phys);
kfree(phys->data);
kfree(phys);
if (pd->gpio_config)
pd->gpio_config(&pd->gpio_data, false);
return 0;
}
static const struct i2c_device_id rmi_id[] = {
{ "rmi", 0 },
{ "rmi_i2c", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rmi_id);
static struct i2c_driver rmi_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "rmi_i2c"
},
.id_table = rmi_id,
.probe = rmi_i2c_probe,
.remove = __devexit_p(rmi_i2c_remove),
};
static int __init rmi_i2c_init(void)
{
return i2c_add_driver(&rmi_i2c_driver);
}
static void __exit rmi_i2c_exit(void)
{
i2c_del_driver(&rmi_i2c_driver);
}
module_init(rmi_i2c_init);
module_exit(rmi_i2c_exit);
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
MODULE_DESCRIPTION("RMI I2C driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(RMI_DRIVER_VERSION);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

609
include/linux/rmi.h Executable file

File diff suppressed because it is too large Load Diff