mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 22:52:35 +02:00
rk30 phone loauqt: add touch screen synaptics s3202 standar drivers
This commit is contained in:
parent
76ca4c2356
commit
7b8ca11798
198
drivers/input/touchscreen/rmi4/Kconfig
Executable file
198
drivers/input/touchscreen/rmi4/Kconfig
Executable 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.
|
||||
34
drivers/input/touchscreen/rmi4/Makefile
Executable file
34
drivers/input/touchscreen/rmi4/Makefile
Executable 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
|
||||
|
||||
482
drivers/input/touchscreen/rmi4/rmi_bus.c
Executable file
482
drivers/input/touchscreen/rmi4/rmi_bus.c
Executable 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);
|
||||
448
drivers/input/touchscreen/rmi4/rmi_dev.c
Executable file
448
drivers/input/touchscreen/rmi4/rmi_dev.c
Executable 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");
|
||||
1616
drivers/input/touchscreen/rmi4/rmi_driver.c
Executable file
1616
drivers/input/touchscreen/rmi4/rmi_driver.c
Executable file
File diff suppressed because it is too large
Load Diff
405
drivers/input/touchscreen/rmi4/rmi_driver.h
Executable file
405
drivers/input/touchscreen/rmi4/rmi_driver.h
Executable 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
|
||||
|
||||
1389
drivers/input/touchscreen/rmi4/rmi_f01.c
Executable file
1389
drivers/input/touchscreen/rmi4/rmi_f01.c
Executable file
File diff suppressed because it is too large
Load Diff
771
drivers/input/touchscreen/rmi4/rmi_f09.c
Executable file
771
drivers/input/touchscreen/rmi4/rmi_f09.c
Executable file
|
|
@ -0,0 +1,771 @@
|
|||
/*
|
||||
* 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/kernel.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include "rmi_driver.h"
|
||||
#define QUERY_BASE_INDEX 1
|
||||
|
||||
/* data specific to fn $09 that needs to be kept around */
|
||||
struct f09_query {
|
||||
u8 limit_register_count;
|
||||
union {
|
||||
struct {
|
||||
u8 result_register_count:3;
|
||||
u8 reserved:3;
|
||||
u8 internal_limits:1;
|
||||
u8 host_test_enable:1;
|
||||
};
|
||||
u8 f09_bist_query1;
|
||||
};
|
||||
};
|
||||
|
||||
struct f09_control {
|
||||
union {
|
||||
struct {
|
||||
u8 test1_limit_low:8;
|
||||
u8 test1_limit_high:8;
|
||||
u8 test1_limit_diff:8;
|
||||
};
|
||||
u8 f09_control_test1[3];
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
u8 test2_limit_low:8;
|
||||
u8 test2_limit_high:8;
|
||||
u8 test2_limit_diff:8;
|
||||
};
|
||||
u8 f09_control_test2[3];
|
||||
};
|
||||
};
|
||||
|
||||
struct f09_data {
|
||||
u8 test_number_control;
|
||||
u8 overall_bist_result;
|
||||
u8 test_result1;
|
||||
u8 test_result2;
|
||||
u8 transmitter_number;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u8 receiver_number:6;
|
||||
u8 limit_failure_code:2;
|
||||
};
|
||||
u8 f09_bist_data2;
|
||||
};
|
||||
};
|
||||
|
||||
struct f09_cmd {
|
||||
union {
|
||||
struct {
|
||||
u8 run_bist:1;
|
||||
};
|
||||
u8 f09_bist_cmd0;
|
||||
};
|
||||
};
|
||||
|
||||
struct rmi_fn_09_data {
|
||||
struct f09_query query;
|
||||
struct f09_data data;
|
||||
struct f09_cmd cmd;
|
||||
struct f09_control control;
|
||||
signed char status;
|
||||
};
|
||||
|
||||
|
||||
static ssize_t rmi_f09_status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_status_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_f09_limit_register_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_host_test_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_host_test_enable_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_f09_internal_limits_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_result_register_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_overall_bist_result_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_test_number_control_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_test_number_control_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_f09_run_bist_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_run_bist_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_f09_test_result1_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_test_result2_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_control_test1_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_control_test1_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_f09_control_test2_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_f09_control_test2_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static int rmi_f09_alloc_memory(struct rmi_function_container *fc);
|
||||
|
||||
static void rmi_f09_free_memory(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f09_initialize(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f09_config(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f09_reset(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f09_create_sysfs(struct rmi_function_container *fc);
|
||||
|
||||
|
||||
static struct device_attribute attrs[] = {
|
||||
__ATTR(status, RMI_RW_ATTR,
|
||||
rmi_f09_status_show, rmi_f09_status_store),
|
||||
__ATTR(limitRegisterCount, RMI_RO_ATTR,
|
||||
rmi_f09_limit_register_count_show, rmi_store_error),
|
||||
__ATTR(hostTestEnable, RMI_RW_ATTR,
|
||||
rmi_f09_host_test_enable_show, rmi_f09_host_test_enable_store),
|
||||
__ATTR(internalLimits, RMI_RO_ATTR,
|
||||
rmi_f09_internal_limits_show, rmi_store_error),
|
||||
__ATTR(resultRegisterCount, RMI_RO_ATTR,
|
||||
rmi_f09_result_register_count_show, rmi_store_error),
|
||||
__ATTR(overall_bist_result, RMI_RO_ATTR,
|
||||
rmi_f09_overall_bist_result_show, rmi_store_error),
|
||||
__ATTR(test_number_control, RMI_RW_ATTR,
|
||||
rmi_f09_test_number_control_show,
|
||||
rmi_f09_test_number_control_store),
|
||||
__ATTR(test_result1, RMI_RO_ATTR,
|
||||
rmi_f09_test_result1_show, rmi_store_error),
|
||||
__ATTR(test_result2, RMI_RO_ATTR,
|
||||
rmi_f09_test_result2_show, rmi_store_error),
|
||||
__ATTR(run_bist, RMI_RW_ATTR,
|
||||
rmi_f09_run_bist_show, rmi_f09_run_bist_store),
|
||||
__ATTR(f09_control_test1, RMI_RW_ATTR,
|
||||
rmi_f09_control_test1_show, rmi_f09_control_test1_store),
|
||||
__ATTR(f09_control_test2, RMI_RW_ATTR,
|
||||
rmi_f09_control_test2_show, rmi_f09_control_test2_store),
|
||||
};
|
||||
|
||||
static int rmi_f09_init(struct rmi_function_container *fc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
dev_info(&fc->dev, "Intializing F09 values.");
|
||||
|
||||
rc = rmi_f09_alloc_memory(fc);
|
||||
if (rc < 0)
|
||||
goto error_exit;
|
||||
|
||||
rc = rmi_f09_initialize(fc);
|
||||
if (rc < 0)
|
||||
goto error_exit;
|
||||
|
||||
rc = rmi_f09_create_sysfs(fc);
|
||||
if (rc < 0)
|
||||
goto error_exit;
|
||||
|
||||
return 0;
|
||||
|
||||
error_exit:
|
||||
rmi_f09_free_memory(fc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rmi_f09_alloc_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_09_data *f09;
|
||||
|
||||
f09 = kzalloc(sizeof(struct rmi_fn_09_data), GFP_KERNEL);
|
||||
if (!f09) {
|
||||
dev_err(&fc->dev, "Failed to allocate rmi_fn_09_data.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
fc->data = f09;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f09_free_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
kfree(fc->data);
|
||||
fc->data = NULL;
|
||||
}
|
||||
|
||||
static int rmi_f09_initialize(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_device *rmi_dev = fc->rmi_dev;
|
||||
struct rmi_device_platform_data *pdata;
|
||||
struct rmi_fn_09_data *f09 = fc->data;
|
||||
u8 query_base_addr;
|
||||
int rc;
|
||||
|
||||
|
||||
pdata = to_rmi_platform_data(rmi_dev);
|
||||
query_base_addr = fc->fd.query_base_addr;
|
||||
|
||||
/* initial all default values for f09 query here */
|
||||
rc = rmi_read_block(rmi_dev, query_base_addr,
|
||||
(u8 *)&f09->query, sizeof(f09->query));
|
||||
if (rc < 0) {
|
||||
dev_err(&fc->dev, "Failed to read query register."
|
||||
" from 0x%04x\n", query_base_addr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f09_create_sysfs(struct rmi_function_container *fc)
|
||||
{
|
||||
int attr_count = 0;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&fc->dev, "Creating sysfs files.");
|
||||
/* Set up sysfs device attributes. */
|
||||
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
||||
if (sysfs_create_file
|
||||
(&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create sysfs file for %s.",
|
||||
attrs[attr_count].attr.name);
|
||||
rc = -ENODEV;
|
||||
goto err_remove_sysfs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_sysfs:
|
||||
for (attr_count--; attr_count >= 0; attr_count--)
|
||||
sysfs_remove_file(&fc->dev.kobj,
|
||||
&attrs[attr_count].attr);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rmi_f09_config(struct rmi_function_container *fc)
|
||||
{
|
||||
/*we do nothing here. instead reset should notify the user.*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f09_reset(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_09_data *instance_data = fc->data;
|
||||
|
||||
instance_data->status = -ECONNRESET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f09_remove(struct rmi_function_container *fc)
|
||||
{
|
||||
int attr_count;
|
||||
|
||||
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
|
||||
sysfs_remove_file(&fc->dev.kobj,
|
||||
&attrs[attr_count].attr);
|
||||
|
||||
rmi_f09_free_memory(fc);
|
||||
}
|
||||
|
||||
static int rmi_f09_attention(struct rmi_function_container *fc, u8 *irq_bits)
|
||||
{
|
||||
struct rmi_device *rmi_dev = fc->rmi_dev;
|
||||
struct rmi_fn_09_data *data = fc->data;
|
||||
int error;
|
||||
error = rmi_read_block(rmi_dev, fc->fd.command_base_addr,
|
||||
(u8 *)&data->cmd, sizeof(data->cmd));
|
||||
|
||||
if (error < 0) {
|
||||
dev_err(&fc->dev, "Failed to read command register.\n");
|
||||
return error;
|
||||
}
|
||||
/* If the command register is cleared, meaning the value is 0 */
|
||||
if (data->status == ECONNRESET) {
|
||||
dev_warn(&rmi_dev->dev, "RESET occured: %#04x.\n",
|
||||
data->status);
|
||||
} else if (data->cmd.run_bist) {
|
||||
dev_warn(&rmi_dev->dev,
|
||||
"Command register not cleared: %#04x.\n",
|
||||
data->cmd.run_bist);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rmi_function_handler function_handler = {
|
||||
.func = 0x09,
|
||||
.init = rmi_f09_init,
|
||||
.attention = rmi_f09_attention,
|
||||
.config = rmi_f09_config,
|
||||
.reset = rmi_f09_reset,
|
||||
.remove = rmi_f09_remove
|
||||
};
|
||||
|
||||
static int __init rmi_f09_module_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = rmi_register_function_driver(&function_handler);
|
||||
if (error < 0) {
|
||||
pr_err("%s: register failed!\n", __func__);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f09_module_exit(void)
|
||||
{
|
||||
rmi_unregister_function_driver(&function_handler);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_status_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_status_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
/* any write to status resets 1 */
|
||||
instance_data->status = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_limit_register_count_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->query.limit_register_count);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_host_test_enable_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->query.host_test_enable);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_host_test_enable_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
unsigned int new_value;
|
||||
int result;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
if (sscanf(buf, "%u", &new_value) != 1) {
|
||||
dev_err(dev,
|
||||
"%s: Error - hostTestEnable has an invalid length.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_value < 0 || new_value > 1) {
|
||||
dev_err(dev, "%s: Invalid hostTestEnable bit %s.",
|
||||
__func__, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->query.host_test_enable = new_value;
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.query_base_addr,
|
||||
data->query.host_test_enable);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s: Could not write hostTestEnable to 0x%x\n",
|
||||
__func__, fc->fd.query_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_internal_limits_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->query.internal_limits);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_result_register_count_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->query.result_register_count);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_overall_bist_result_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->data.overall_bist_result);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_test_result1_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->data.test_result1);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_test_result2_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->data.test_result2);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_run_bist_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->cmd.run_bist);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_run_bist_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
unsigned int new_value;
|
||||
int result;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
if (sscanf(buf, "%u", &new_value) != 1) {
|
||||
dev_err(dev,
|
||||
"%s: Error - run_bist_store has an "
|
||||
"invalid len.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_value < 0 || new_value > 1) {
|
||||
dev_err(dev, "%s: Invalid run_bist bit %s.", __func__, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->cmd.run_bist = new_value;
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
|
||||
data->cmd.run_bist);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write run_bist_store to 0x%x\n",
|
||||
__func__, fc->fd.command_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_control_test1_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u %u %u\n",
|
||||
data->control.test1_limit_low,
|
||||
data->control.test1_limit_high,
|
||||
data->control.test1_limit_diff);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_control_test1_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
unsigned int new_low, new_high, new_diff;
|
||||
int result;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
if (sscanf(buf, "%u %u %u", &new_low, &new_high, &new_diff) != 3) {
|
||||
dev_err(dev,
|
||||
"%s: Error - f09_control_test1_store has an "
|
||||
"invalid len.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_low < 0 || new_low > 1 || new_high < 0 || new_high
|
||||
|| new_diff < 0 || new_diff) {
|
||||
dev_err(dev, "%s: Invalid f09_control_test1_diff bit %s.",
|
||||
__func__, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->control.test1_limit_low = new_low;
|
||||
data->control.test1_limit_high = new_high;
|
||||
data->control.test1_limit_diff = new_diff;
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->control.test1_limit_low);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write f09_control_test1_limit_low to 0x%x\n",
|
||||
__func__, fc->fd.control_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->control.test1_limit_high);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write f09_control_test1_limit_high to 0x%x\n",
|
||||
__func__, fc->fd.control_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->control.test1_limit_diff);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write f09_control_test1_limit_diff to 0x%x\n",
|
||||
__func__, fc->fd.control_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_control_test2_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u %u %u\n",
|
||||
data->control.test2_limit_low,
|
||||
data->control.test2_limit_high,
|
||||
data->control.test2_limit_diff);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_control_test2_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
unsigned int new_low, new_high, new_diff;
|
||||
int result;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
if (sscanf(buf, "%u %u %u", &new_low, &new_high, &new_diff) != 3) {
|
||||
dev_err(dev,
|
||||
"%s: Error - f09_control_test1_store has an "
|
||||
"invalid len.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_low < 0 || new_low > 1 || new_high < 0 || new_high > 1 ||
|
||||
new_diff < 0 || new_diff > 1) {
|
||||
dev_err(dev, "%s: Invalid f09_control_test2_diff bit %s.",
|
||||
__func__, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->control.test2_limit_low = new_low;
|
||||
data->control.test2_limit_high = new_high;
|
||||
data->control.test2_limit_diff = new_diff;
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->control.test2_limit_low);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write f09_control_test2_limit_low to 0x%x\n",
|
||||
__func__, fc->fd.control_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->control.test2_limit_high);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write f09_control_test2_limit_high to 0x%x\n",
|
||||
__func__, fc->fd.control_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->control.test2_limit_diff);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write f09_control_test2_limit_diff to 0x%x\n",
|
||||
__func__, fc->fd.control_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static ssize_t rmi_f09_test_number_control_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
data->data.test_number_control);
|
||||
}
|
||||
|
||||
static ssize_t rmi_f09_test_number_control_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_09_data *data;
|
||||
unsigned int new_value;
|
||||
int result;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
if (sscanf(buf, "%u", &new_value) != 1) {
|
||||
dev_err(dev,
|
||||
"%s: Error - test_number_control_store has an "
|
||||
"invalid len.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_value < 0 || new_value > 1) {
|
||||
dev_err(dev, "%s: Invalid test_number_control bit %s.",
|
||||
__func__, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->data.test_number_control = new_value;
|
||||
result = rmi_write(fc->rmi_dev, fc->fd.control_base_addr,
|
||||
data->data.test_number_control);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write "
|
||||
"test_number_control_store to 0x%x\n",
|
||||
__func__, fc->fd.data_base_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
module_init(rmi_f09_module_init);
|
||||
module_exit(rmi_f09_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Allie Xiong <axiong@Synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI F09 module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
1947
drivers/input/touchscreen/rmi4/rmi_f11.c
Executable file
1947
drivers/input/touchscreen/rmi4/rmi_f11.c
Executable file
File diff suppressed because it is too large
Load Diff
731
drivers/input/touchscreen/rmi4/rmi_f17.c
Executable file
731
drivers/input/touchscreen/rmi4/rmi_f17.c
Executable file
|
|
@ -0,0 +1,731 @@
|
|||
/*
|
||||
* Copyright (c) 2012 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/kernel.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include "rmi_driver.h"
|
||||
#define QUERY_BASE_INDEX 1
|
||||
#define MAX_NAME_LENGTH 256
|
||||
|
||||
union f17_device_query {
|
||||
struct {
|
||||
u8 number_of_sticks:3;
|
||||
};
|
||||
u8 regs[1];
|
||||
};
|
||||
|
||||
#define F17_MANUFACTURER_SYNAPTICS 0
|
||||
#define F17_MANUFACTURER_NMB 1
|
||||
#define F17_MANUFACTURER_ALPS 2
|
||||
|
||||
struct f17_stick_query {
|
||||
union {
|
||||
struct {
|
||||
u8 manufacturer:4;
|
||||
u8 resistive:1;
|
||||
u8 ballistics:1;
|
||||
u8 reserved1:2;
|
||||
u8 has_relative:1;
|
||||
u8 has_absolute:1;
|
||||
u8 has_gestures:1;
|
||||
u8 has_dribble:1;
|
||||
u8 reserved2:4;
|
||||
};
|
||||
u8 regs[2];
|
||||
} general;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u8 has_single_tap:1;
|
||||
u8 has_tap_and_hold:1;
|
||||
u8 has_double_tap:1;
|
||||
u8 has_early_tap:1;
|
||||
u8 has_press:1;
|
||||
};
|
||||
u8 regs[1];
|
||||
} gestures;
|
||||
};
|
||||
|
||||
union f17_device_controls {
|
||||
struct {
|
||||
u8 reporting_mode:3;
|
||||
u8 dribble:1;
|
||||
};
|
||||
u8 regs[1];
|
||||
};
|
||||
|
||||
struct f17_stick_controls {
|
||||
union {
|
||||
struct {
|
||||
u8 z_force_threshold;
|
||||
u8 radial_force_threshold;
|
||||
};
|
||||
u8 regs[3];
|
||||
} general;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u8 motion_sensitivity:4;
|
||||
u8 antijitter:1;
|
||||
};
|
||||
u8 regs[1];
|
||||
} relative;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u8 single_tap:1;
|
||||
u8 tap_and_hold:1;
|
||||
|
||||
u8 double_tap:1;
|
||||
u8 early_tap:1;
|
||||
u8 press:1;
|
||||
};
|
||||
u8 regs[1];
|
||||
} enable;
|
||||
|
||||
u8 maximum_tap_time;
|
||||
u8 minimum_press_time;
|
||||
u8 maximum_radial_force;
|
||||
};
|
||||
|
||||
|
||||
union f17_device_commands {
|
||||
struct {
|
||||
u8 rezero:1;
|
||||
};
|
||||
u8 regs[1];
|
||||
};
|
||||
|
||||
struct f17_stick_data {
|
||||
union {
|
||||
struct {
|
||||
u8 x_force_high;
|
||||
u8 y_force_high;
|
||||
u8 y_force_low:4;
|
||||
u8 x_force_low:4;
|
||||
u8 z_force;
|
||||
};
|
||||
u8 regs[4];
|
||||
} abs;
|
||||
union {
|
||||
struct {
|
||||
s8 x_delta;
|
||||
s8 y_delta;
|
||||
};
|
||||
u8 regs[2];
|
||||
} rel;
|
||||
union {
|
||||
struct {
|
||||
u8 single_tap:1;
|
||||
u8 tap_and_hold:1;
|
||||
u8 double_tap:1;
|
||||
u8 early_tap:1;
|
||||
u8 press:1;
|
||||
};
|
||||
u8 regs[1];
|
||||
} gestures;
|
||||
};
|
||||
|
||||
|
||||
/* data specific to f17 that needs to be kept around */
|
||||
|
||||
struct rmi_f17_stick_data {
|
||||
struct f17_stick_query query;
|
||||
struct f17_stick_controls controls;
|
||||
struct f17_stick_data data;
|
||||
|
||||
u16 abs_data_address;
|
||||
u16 rel_data_address;
|
||||
u16 gesture_data_address;
|
||||
u16 control_address;
|
||||
|
||||
int index;
|
||||
|
||||
char input_name[MAX_NAME_LENGTH];
|
||||
char input_phys[MAX_NAME_LENGTH];
|
||||
struct input_dev *input;
|
||||
char mouse_name[MAX_NAME_LENGTH];
|
||||
char mouse_phys[MAX_NAME_LENGTH];
|
||||
struct input_dev *mouse;
|
||||
};
|
||||
|
||||
struct rmi_f17_device_data {
|
||||
u16 control_address;
|
||||
|
||||
union f17_device_query query;
|
||||
union f17_device_commands commands;
|
||||
union f17_device_controls controls;
|
||||
|
||||
struct rmi_f17_stick_data *sticks;
|
||||
|
||||
};
|
||||
|
||||
static ssize_t f17_rezero_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
static ssize_t f17_rezero_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
|
||||
|
||||
static int f17_alloc_memory(struct rmi_function_container *fc);
|
||||
|
||||
static void f17_free_memory(struct rmi_function_container *fc);
|
||||
|
||||
static int f17_initialize(struct rmi_function_container *fc);
|
||||
|
||||
static int f17_register_devices(struct rmi_function_container *fc);
|
||||
|
||||
static int f17_create_sysfs(struct rmi_function_container *fc);
|
||||
|
||||
static int f17_config(struct rmi_function_container *fc);
|
||||
|
||||
static int f17_reset(struct rmi_function_container *fc);
|
||||
|
||||
static struct device_attribute attrs[] = {
|
||||
__ATTR(rezero, RMI_RW_ATTR,
|
||||
f17_rezero_show, f17_rezero_store),
|
||||
};
|
||||
|
||||
|
||||
int f17_read_control_parameters(struct rmi_device *rmi_dev,
|
||||
struct rmi_f17_device_data *f17)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
// TODO: read this or delete the function
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int f17_init(struct rmi_function_container *fc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = f17_alloc_memory(fc);
|
||||
if (retval < 0)
|
||||
goto err_free_data;
|
||||
|
||||
retval = f17_initialize(fc);
|
||||
if (retval < 0)
|
||||
goto err_free_data;
|
||||
|
||||
retval = f17_register_devices(fc);
|
||||
if (retval < 0)
|
||||
goto err_free_data;
|
||||
|
||||
retval = f17_create_sysfs(fc);
|
||||
if (retval < 0)
|
||||
goto err_free_data;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_data:
|
||||
f17_free_memory(fc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_alloc_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_f17_device_data *f17;
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
f17 = kzalloc(sizeof(struct rmi_f17_device_data), GFP_KERNEL);
|
||||
if (!f17) {
|
||||
dev_err(&fc->dev, "Failed to allocate function data.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
fc->data = f17;
|
||||
|
||||
retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
|
||||
f17->query.regs, sizeof(f17->query.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Failed to read query register.\n");
|
||||
goto error_exit;
|
||||
}
|
||||
dev_info(&fc->dev, "Found F17 with %d sticks.\n",
|
||||
f17->query.number_of_sticks + 1);
|
||||
|
||||
f17->sticks = kcalloc(f17->query.number_of_sticks + 1,
|
||||
sizeof(struct rmi_f17_stick_data), GFP_KERNEL);
|
||||
if (!f17->sticks) {
|
||||
dev_err(&fc->dev, "Failed to allocate per stick data.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
|
||||
// TODO: allocate any per stick stuff (or remove this loop)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_exit:
|
||||
kfree(f17);
|
||||
fc->data = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void f17_free_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_f17_device_data *f17 = fc->data;
|
||||
int i;
|
||||
|
||||
if (f17) {
|
||||
if (f17->sticks) {
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
|
||||
// TODO: Free stick stuff
|
||||
}
|
||||
}
|
||||
kfree(f17->sticks);
|
||||
f17->sticks = NULL;
|
||||
}
|
||||
kfree(f17);
|
||||
fc->data = NULL;
|
||||
}
|
||||
|
||||
static int f17_init_stick(struct rmi_device *rmi_dev,
|
||||
struct rmi_f17_stick_data *stick,
|
||||
u16 *next_query_reg, u16 *next_data_reg,
|
||||
u16 *next_control_reg) {
|
||||
int retval = 0;
|
||||
|
||||
retval = rmi_read_block(rmi_dev, *next_query_reg,
|
||||
stick->query.general.regs,
|
||||
sizeof(stick->query.general.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev, "Failed to read stick general query.\n");
|
||||
return retval;
|
||||
}
|
||||
*next_query_reg += sizeof(stick->query.general.regs);
|
||||
|
||||
dev_info(&rmi_dev->dev, "Stick %d found\n", stick->index);
|
||||
dev_info(&rmi_dev->dev, " Manufacturer: %d.\n", stick->query.general.manufacturer);
|
||||
dev_info(&rmi_dev->dev, " Resistive: %d.\n", stick->query.general.resistive);
|
||||
dev_info(&rmi_dev->dev, " Ballistics: %d.\n", stick->query.general.ballistics);
|
||||
dev_info(&rmi_dev->dev, " Manufacturer: %d.\n", stick->query.general.ballistics);
|
||||
dev_info(&rmi_dev->dev, " Has relative: %d.\n", stick->query.general.has_relative);
|
||||
dev_info(&rmi_dev->dev, " Has absolute: %d.\n", stick->query.general.has_absolute);
|
||||
dev_info(&rmi_dev->dev, " Had dribble: %d.\n", stick->query.general.has_dribble);
|
||||
dev_info(&rmi_dev->dev, " Has gestures: %d.\n", stick->query.general.has_gestures);
|
||||
|
||||
if (stick->query.general.has_gestures) {
|
||||
retval = rmi_read_block(rmi_dev, *next_query_reg,
|
||||
stick->query.gestures.regs,
|
||||
sizeof(stick->query.gestures.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev, "Failed to read stick gestures query.\n");
|
||||
return retval;
|
||||
}
|
||||
*next_query_reg += sizeof(stick->query.gestures.regs);
|
||||
dev_info(&rmi_dev->dev, " single tap: %d.\n", stick->query.gestures.has_single_tap);
|
||||
dev_info(&rmi_dev->dev, " tap & hold: %d.\n", stick->query.gestures.has_tap_and_hold);
|
||||
dev_info(&rmi_dev->dev, " double tap: %d.\n", stick->query.gestures.has_double_tap);
|
||||
dev_info(&rmi_dev->dev, " early tap: %d.\n", stick->query.gestures.has_early_tap);
|
||||
dev_info(&rmi_dev->dev, " press: %d.\n", stick->query.gestures.has_press);
|
||||
}
|
||||
if (stick->query.general.has_absolute) {
|
||||
stick->abs_data_address = *next_data_reg;
|
||||
*next_data_reg += sizeof(stick->data.abs.regs);
|
||||
}
|
||||
if (stick->query.general.has_relative) {
|
||||
stick->rel_data_address = *next_data_reg;
|
||||
*next_data_reg += sizeof(stick->data.rel.regs);
|
||||
}
|
||||
if (stick->query.general.has_gestures) {
|
||||
stick->gesture_data_address = *next_data_reg;
|
||||
*next_data_reg += sizeof(stick->data.gestures.regs);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_initialize(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_device *rmi_dev = fc->rmi_dev;
|
||||
struct rmi_f17_device_data *f17 = fc->data;
|
||||
int i;
|
||||
int retval;
|
||||
u16 next_query_reg = fc->fd.query_base_addr;
|
||||
u16 next_data_reg = fc->fd.data_base_addr;
|
||||
u16 next_control_reg = fc->fd.control_base_addr;
|
||||
|
||||
dev_info(&fc->dev, "Intializing F17 values.");
|
||||
|
||||
retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
|
||||
f17->query.regs, sizeof(f17->query.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Failed to read query register.\n");
|
||||
return retval;
|
||||
}
|
||||
dev_info(&fc->dev, "Found F17 with %d sticks.\n",
|
||||
f17->query.number_of_sticks + 1);
|
||||
next_query_reg += sizeof(f17->query.regs);
|
||||
|
||||
retval = rmi_read_block(rmi_dev, fc->fd.command_base_addr,
|
||||
f17->commands.regs, sizeof(f17->commands.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Failed to read command register.\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
f17->control_address = fc->fd.control_base_addr;
|
||||
retval = f17_read_control_parameters(rmi_dev, f17);
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Failed to initialize F17 control params.\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
|
||||
f17->sticks[i].index = i;
|
||||
retval = f17_init_stick(rmi_dev, &f17->sticks[i],
|
||||
&next_query_reg, &next_data_reg,
|
||||
&next_control_reg);
|
||||
if (!retval) {
|
||||
dev_err(&fc->dev, "Failed to init stick %d.\n", i);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_register_stick(struct rmi_function_container *fc,
|
||||
struct rmi_f17_stick_data *stick) {
|
||||
struct rmi_device *rmi_dev = fc->rmi_dev;
|
||||
int retval = 0;
|
||||
|
||||
if (stick->query.general.has_absolute) {
|
||||
struct input_dev *input_dev;
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
dev_err(&rmi_dev->dev, "Failed to allocate stick device %d.\n",
|
||||
stick->index);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
snprintf(stick->input_name, sizeof(stick->input_name),
|
||||
"RMI F%02x Stick %d", 0x17, stick->index);
|
||||
snprintf(stick->input_phys, sizeof(stick->input_phys),
|
||||
"sensor00fn%02x/stick%d", 0x17, stick->index);
|
||||
input_dev->name = stick->input_name;
|
||||
input_dev->phys = stick->input_phys;
|
||||
input_dev->dev.parent = &fc->dev;
|
||||
input_set_drvdata(input_dev, stick);
|
||||
|
||||
retval = input_register_device(input_dev);
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev, "Failed to register stick device %d.\n",
|
||||
stick->index);
|
||||
goto error_free_device;
|
||||
}
|
||||
stick->input = input_dev;
|
||||
}
|
||||
|
||||
if (stick->query.general.has_relative) {
|
||||
struct input_dev *input_dev_mouse;
|
||||
/*create input device for mouse events */
|
||||
input_dev_mouse = input_allocate_device();
|
||||
if (!input_dev_mouse) {
|
||||
retval = -ENOMEM;
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
snprintf(stick->mouse_name, sizeof(stick->mouse_name),
|
||||
"RMI F%02x Mouse %d", 0x17, stick->index);
|
||||
snprintf(stick->mouse_phys, sizeof(stick->mouse_name),
|
||||
"sensor00fn%02x/mouse%d", 0x17, stick->index);
|
||||
input_dev_mouse->name = stick->mouse_name;
|
||||
input_dev_mouse->phys = stick->mouse_phys;
|
||||
input_dev_mouse->dev.parent = &fc->dev;
|
||||
|
||||
input_dev_mouse->id.vendor = 0x18d1;
|
||||
input_dev_mouse->id.product = 0x0210;
|
||||
input_dev_mouse->id.version = 0x0100;
|
||||
|
||||
set_bit(EV_REL, input_dev_mouse->evbit);
|
||||
set_bit(REL_X, input_dev_mouse->relbit);
|
||||
set_bit(REL_Y, input_dev_mouse->relbit);
|
||||
|
||||
set_bit(BTN_MOUSE, input_dev_mouse->evbit);
|
||||
/* Register device's buttons and keys */
|
||||
set_bit(EV_KEY, input_dev_mouse->evbit);
|
||||
set_bit(BTN_LEFT, input_dev_mouse->keybit);
|
||||
set_bit(BTN_MIDDLE, input_dev_mouse->keybit);
|
||||
set_bit(BTN_RIGHT, input_dev_mouse->keybit);
|
||||
|
||||
retval = input_register_device(input_dev_mouse);
|
||||
if (retval < 0)
|
||||
goto error_free_device;
|
||||
stick->mouse = input_dev_mouse;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_device:
|
||||
if (stick->input) {
|
||||
input_free_device(stick->input);
|
||||
stick->input = NULL;
|
||||
}
|
||||
if (stick->mouse) {
|
||||
input_free_device(stick->mouse);
|
||||
stick->mouse = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_register_devices(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_f17_device_data *f17 = fc->data;
|
||||
int i;
|
||||
int retval = 0;
|
||||
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++) {
|
||||
retval = f17_register_stick(fc, &f17->sticks[i]);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_create_sysfs(struct rmi_function_container *fc)
|
||||
{
|
||||
int attr_count = 0;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&fc->dev, "Creating sysfs files.\n");
|
||||
/* Set up sysfs device attributes. */
|
||||
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
||||
if (sysfs_create_file
|
||||
(&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
|
||||
dev_err(&fc->dev,
|
||||
"Failed to create sysfs file for %s.",
|
||||
attrs[attr_count].attr.name);
|
||||
rc = -ENODEV;
|
||||
goto err_remove_sysfs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_sysfs:
|
||||
for (attr_count--; attr_count >= 0; attr_count--)
|
||||
sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
static int f17_config(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_f17_device_data *f17 = fc->data;
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
retval = rmi_write_block(fc->rmi_dev, f17->control_address,
|
||||
f17->controls.regs, sizeof(f17->controls.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Could not write stick controls to 0x%04x\n",
|
||||
f17->control_address);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (f17->query.has_relative) {
|
||||
retval = rmi_write_block(fc->rmi_dev,
|
||||
f17->relative_control_address,
|
||||
f17->controls.relative.regs,
|
||||
sizeof(f17->controls.relative.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Could not write stick controls to 0x%04x\n",
|
||||
f17->control_address);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
|
||||
// TODO: Configure each styk
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_reset(struct rmi_function_container *fc)
|
||||
{
|
||||
/* we do nothing here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void f17_remove(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_f17_device_data *f17 = fc->data;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(attrs); i++)
|
||||
sysfs_remove_file(&fc->dev.kobj, &attrs[i].attr);
|
||||
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
|
||||
input_unregister_device(f17->sticks[i].input);
|
||||
}
|
||||
|
||||
f17_free_memory(fc);
|
||||
}
|
||||
|
||||
static int f17_process_stick(struct rmi_device *rmi_dev,
|
||||
struct rmi_f17_stick_data *stick) {
|
||||
int retval = 0;
|
||||
|
||||
if (stick->query.general.has_absolute) {
|
||||
retval = rmi_read_block(rmi_dev, stick->abs_data_address,
|
||||
stick->data.abs.regs, sizeof(stick->data.abs.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev, "Failed to read abs data for stick %d.\n",
|
||||
stick->index);
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
if (stick->query.general.has_relative) {
|
||||
retval = rmi_read_block(rmi_dev, stick->rel_data_address,
|
||||
stick->data.rel.regs, sizeof(stick->data.rel.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev, "Failed to read rel data for stick %d.\n",
|
||||
stick->index);
|
||||
goto error_exit;
|
||||
}
|
||||
dev_info(&rmi_dev->dev, "Reporting dX: %d, dy: %d\n", stick->data.rel.x_delta, stick->data.rel.y_delta);
|
||||
input_report_rel(stick->mouse, REL_X, stick->data.rel.x_delta);
|
||||
input_report_rel(stick->mouse, REL_Y, stick->data.rel.y_delta);
|
||||
}
|
||||
if (stick->query.general.has_gestures) {
|
||||
retval = rmi_read_block(rmi_dev, stick->gesture_data_address,
|
||||
stick->data.gestures.regs, sizeof(stick->data.gestures.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&rmi_dev->dev, "Failed to read gesture data for stick %d.\n",
|
||||
stick->index);
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
|
||||
error_exit:
|
||||
if (stick->input)
|
||||
input_sync(stick->input);
|
||||
if (stick->mouse)
|
||||
input_sync(stick->mouse);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int f17_attention(struct rmi_function_container *fc, u8 *irq_bits)
|
||||
{
|
||||
struct rmi_device *rmi_dev = fc->rmi_dev;
|
||||
struct rmi_f17_device_data *f17 = fc->data;
|
||||
int i;
|
||||
int retval = 0;
|
||||
|
||||
for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++) {
|
||||
retval = f17_process_stick(rmi_dev, &f17->sticks[i]);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct rmi_function_handler function_handler = {
|
||||
.func = 0x17,
|
||||
.init = f17_init,
|
||||
.config = f17_config,
|
||||
.reset = f17_reset,
|
||||
.attention = f17_attention,
|
||||
.remove = f17_remove
|
||||
};
|
||||
|
||||
static int __init f17_module_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = rmi_register_function_driver(&function_handler);
|
||||
if (error < 0) {
|
||||
pr_err("%s: register failed!\n", __func__);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void f17_module_exit(void)
|
||||
{
|
||||
rmi_unregister_function_driver(&function_handler);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t f17_rezero_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_f17_device_data *f17;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
f17 = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
f17->commands.rezero);
|
||||
|
||||
}
|
||||
|
||||
static ssize_t f17_rezero_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_f17_device_data *data;
|
||||
unsigned int new_value;
|
||||
int len;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
data = fc->data;
|
||||
len = sscanf(buf, "%u", &new_value);
|
||||
if (new_value != 0 && new_value != 1) {
|
||||
dev_err(dev,
|
||||
"%s: Error - rezero is not a valid value 0x%x.\n",
|
||||
__func__, new_value);
|
||||
return -EINVAL;
|
||||
}
|
||||
data->commands.rezero = new_value;
|
||||
len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
|
||||
data->commands.rezero);
|
||||
|
||||
if (len < 0) {
|
||||
dev_err(dev, "%s : Could not write rezero to 0x%x\n",
|
||||
__func__, fc->fd.command_base_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
module_init(f17_module_init);
|
||||
module_exit(f17_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI F17 module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
1505
drivers/input/touchscreen/rmi4/rmi_f19.c
Executable file
1505
drivers/input/touchscreen/rmi4/rmi_f19.c
Executable file
File diff suppressed because it is too large
Load Diff
1809
drivers/input/touchscreen/rmi4/rmi_f1a.c
Executable file
1809
drivers/input/touchscreen/rmi4/rmi_f1a.c
Executable file
File diff suppressed because it is too large
Load Diff
751
drivers/input/touchscreen/rmi4/rmi_f21.c
Executable file
751
drivers/input/touchscreen/rmi4/rmi_f21.c
Executable file
|
|
@ -0,0 +1,751 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2012 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.
|
||||
*/
|
||||
|
||||
#define FUNCTION_DATA rmi_fn_21_data
|
||||
#define FNUM 21
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include "rmi_driver.h"
|
||||
|
||||
union f21_2df_query {
|
||||
struct {
|
||||
/* Query 0 */
|
||||
u8 max_force_sensor_count:3;
|
||||
u8 f21_2df_query0_b3__6:4;
|
||||
u8 has_high_resolution:1;
|
||||
};
|
||||
struct {
|
||||
u8 regs[1];
|
||||
u16 address;
|
||||
};
|
||||
};
|
||||
|
||||
union f21_2df_control_0__3 {
|
||||
struct {
|
||||
/* Control 0 */
|
||||
u8 reporting_mode:2;
|
||||
u8 no_rezero:1;
|
||||
u8 f21_2df_control0_b5__7:4;
|
||||
|
||||
/* Control 1 */
|
||||
u8 force_click_threshold;
|
||||
|
||||
/* Control 2 */
|
||||
u8 int_en_force_0:1;
|
||||
u8 int_en_force_1:1;
|
||||
u8 int_en_force_2:1;
|
||||
u8 int_en_force_3:1;
|
||||
u8 int_en_force_4:1;
|
||||
u8 int_en_force_5:1;
|
||||
u8 int_en_force_6:1;
|
||||
u8 f21_ap_control2_b4__6:3;
|
||||
u8 int_en_click:1;
|
||||
|
||||
/* Control 3 */
|
||||
u8 force_interrupt_threshold;
|
||||
};
|
||||
struct {
|
||||
u8 regs[4];
|
||||
u16 address;
|
||||
};
|
||||
};
|
||||
|
||||
struct f21_2df_control_4n {
|
||||
/*Control 4.* */
|
||||
u8 one_newton_calibration:7;
|
||||
u8 use_cfg_cal:1;
|
||||
};
|
||||
|
||||
struct f21_2df_control_4{
|
||||
struct f21_2df_control_4n *regs;
|
||||
u16 address;
|
||||
u8 length;
|
||||
};
|
||||
|
||||
struct f21_2df_control_5n {
|
||||
/*Control 5.* */
|
||||
u8 x_location;
|
||||
};
|
||||
|
||||
struct f21_2df_control_5{
|
||||
struct f21_2df_control_5n *regs;
|
||||
u16 address;
|
||||
u8 length;
|
||||
};
|
||||
|
||||
struct f21_2df_control_6n {
|
||||
/*Control 6.* */
|
||||
u8 y_location;
|
||||
};
|
||||
|
||||
struct f21_2df_control_6{
|
||||
struct f21_2df_control_6n *regs;
|
||||
u16 address;
|
||||
u8 length;
|
||||
};
|
||||
|
||||
struct f21_2df_control_7n {
|
||||
/*Control 7.* */
|
||||
u8 transmitter_force_sensor;
|
||||
};
|
||||
|
||||
struct f21_2df_control_7{
|
||||
struct f21_2df_control_7n *regs;
|
||||
u16 address;
|
||||
u8 length;
|
||||
};
|
||||
|
||||
struct f21_2df_control_8n {
|
||||
/*Control 8.* */
|
||||
u8 receiver_force_sensor;
|
||||
};
|
||||
|
||||
struct f21_2df_control_8{
|
||||
struct f21_2df_control_8n *regs;
|
||||
u16 address;
|
||||
u8 length;
|
||||
};
|
||||
|
||||
|
||||
#define RMI_F21_NUM_CTRL_REGS 8
|
||||
struct f21_2df_control {
|
||||
/* Control 0-3 */
|
||||
union f21_2df_control_0__3 *reg_0__3;
|
||||
|
||||
/* Control 4 */
|
||||
struct f21_2df_control_4 *reg_4;
|
||||
|
||||
/* Control 5 */
|
||||
struct f21_2df_control_5 *reg_5;
|
||||
|
||||
/* Control 6 */
|
||||
struct f21_2df_control_6 *reg_6;
|
||||
|
||||
/* Control 7 */
|
||||
struct f21_2df_control_7 *reg_7;
|
||||
|
||||
/* Control 8 */
|
||||
struct f21_2df_control_8 *reg_8;
|
||||
};
|
||||
|
||||
union f21_2df_data_2 {
|
||||
struct {
|
||||
/* Data 2 */
|
||||
u8 force_click:1;
|
||||
u8 f21_2df_control0_b2__7:7;
|
||||
};
|
||||
struct {
|
||||
u8 regs[1];
|
||||
u16 address;
|
||||
};
|
||||
};
|
||||
|
||||
struct f21_2df_data {
|
||||
/* Data 0 */
|
||||
struct {
|
||||
u8 *force_hi_lo;
|
||||
u16 address;
|
||||
} reg_0__1;
|
||||
|
||||
/* Data 2 */
|
||||
union f21_2df_data_2 *reg_2;
|
||||
};
|
||||
|
||||
#define F21_REZERO_CMD 0x01
|
||||
|
||||
/* data specific to fn $21 that needs to be kept around */
|
||||
struct rmi_fn_21_data {
|
||||
union f21_2df_query query;
|
||||
struct f21_2df_control control;
|
||||
struct f21_2df_data data;
|
||||
|
||||
struct mutex control_mutex;
|
||||
struct mutex data_mutex;
|
||||
};
|
||||
|
||||
static int rmi_f21_alloc_memory(struct rmi_function_container *fc);
|
||||
|
||||
static void rmi_f21_free_memory(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f21_initialize(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f21_config(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f21_create_sysfs(struct rmi_function_container *fc);
|
||||
|
||||
/* Sysfs files */
|
||||
|
||||
/* Query sysfs files */
|
||||
|
||||
|
||||
show_union_struct_prototype(max_force_sensor_count)
|
||||
show_union_struct_prototype(has_high_resolution)
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
attrify(max_force_sensor_count),
|
||||
attrify(has_high_resolution),
|
||||
NULL
|
||||
};
|
||||
static struct attribute_group attrs_query = GROUP(attrs);
|
||||
/* Control sysfs files */
|
||||
|
||||
show_store_union_struct_prototype(reporting_mode)
|
||||
show_store_union_struct_prototype(no_rezero)
|
||||
show_store_union_struct_prototype(force_click_threshold)
|
||||
show_store_union_struct_prototype(int_en_force_0)
|
||||
show_store_union_struct_prototype(int_en_force_1)
|
||||
show_store_union_struct_prototype(int_en_force_2)
|
||||
show_store_union_struct_prototype(int_en_force_3)
|
||||
show_store_union_struct_prototype(int_en_force_4)
|
||||
show_store_union_struct_prototype(int_en_force_5)
|
||||
show_store_union_struct_prototype(int_en_force_6)
|
||||
show_store_union_struct_prototype(int_en_click)
|
||||
show_store_union_struct_prototype(force_interrupt_threshold)
|
||||
|
||||
show_store_union_struct_prototype(use_cfg_cal)
|
||||
show_store_union_struct_prototype(one_newton_calibration)
|
||||
show_store_union_struct_prototype(x_location)
|
||||
show_store_union_struct_prototype(y_location)
|
||||
show_store_union_struct_prototype(transmitter_force_sensor)
|
||||
show_store_union_struct_prototype(receiver_force_sensor)
|
||||
|
||||
|
||||
static struct attribute *attrs2[] = {
|
||||
attrify(reporting_mode),
|
||||
attrify(no_rezero),
|
||||
attrify(force_click_threshold),
|
||||
attrify(int_en_click),
|
||||
attrify(force_interrupt_threshold),
|
||||
attrify(use_cfg_cal),
|
||||
attrify(one_newton_calibration),
|
||||
attrify(x_location),
|
||||
attrify(y_location),
|
||||
attrify(transmitter_force_sensor),
|
||||
attrify(receiver_force_sensor),
|
||||
NULL
|
||||
};
|
||||
static struct attribute_group attrs_control = GROUP(attrs2);
|
||||
|
||||
/* Data sysfs files */
|
||||
show_union_struct_prototype(force)
|
||||
show_union_struct_prototype(force_click)
|
||||
|
||||
static struct attribute *attrs3[] = {
|
||||
attrify(force),
|
||||
attrify(force_click),
|
||||
NULL
|
||||
};
|
||||
static struct attribute_group attrs_data = GROUP(attrs3);
|
||||
|
||||
/* Command sysfs files */
|
||||
|
||||
static ssize_t rmi_fn_21_rezero_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
DEVICE_ATTR(rezero, RMI_WO_ATTR,
|
||||
rmi_show_error,
|
||||
rmi_fn_21_rezero_store);
|
||||
|
||||
static struct attribute *attrs4[] = {
|
||||
attrify(rezero),
|
||||
NULL
|
||||
};
|
||||
static struct attribute_group attrs_command = GROUP(attrs4);
|
||||
|
||||
static int rmi_f21_init(struct rmi_function_container *fc)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
dev_info(&fc->dev, "Intializing F21.");
|
||||
|
||||
retval = rmi_f21_alloc_memory(fc);
|
||||
if (retval < 0)
|
||||
goto error_exit;
|
||||
|
||||
retval = rmi_f21_initialize(fc);
|
||||
if (retval < 0)
|
||||
goto error_exit;
|
||||
|
||||
retval = rmi_f21_create_sysfs(fc);
|
||||
if (retval < 0)
|
||||
goto error_exit;
|
||||
|
||||
return retval;
|
||||
|
||||
error_exit:
|
||||
rmi_f21_free_memory(fc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rmi_f21_alloc_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_21_data *f21;
|
||||
|
||||
f21 = kzalloc(sizeof(struct rmi_fn_21_data), GFP_KERNEL);
|
||||
if (!f21) {
|
||||
dev_err(&fc->dev, "Failed to allocate rmi_fn_21_data.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
fc->data = f21;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void rmi_f21_free_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_21_data *f21 = fc->data;
|
||||
u8 int_num = f21->query.max_force_sensor_count;
|
||||
sysfs_remove_group(&fc->dev.kobj, &attrs_query);
|
||||
sysfs_remove_group(&fc->dev.kobj, &attrs_control);
|
||||
switch(int_num) {
|
||||
case 7:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_6));
|
||||
case 6:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_5));
|
||||
case 5:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_4));
|
||||
case 4:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_3));
|
||||
case 3:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_2));
|
||||
case 2:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_1));
|
||||
case 1:
|
||||
sysfs_remove_file(&fc->dev.kobj, attrify(int_en_force_0));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sysfs_remove_group(&fc->dev.kobj, &attrs_data);
|
||||
sysfs_remove_group(&fc->dev.kobj, &attrs_command);
|
||||
if (f21) {
|
||||
kfree(f21->control.reg_0__3);
|
||||
kfree(f21->control.reg_4->regs);
|
||||
kfree(f21->control.reg_4);
|
||||
kfree(f21->control.reg_5->regs);
|
||||
kfree(f21->control.reg_5);
|
||||
kfree(f21->control.reg_6->regs);
|
||||
kfree(f21->control.reg_6);
|
||||
kfree(f21->control.reg_7->regs);
|
||||
kfree(f21->control.reg_7);
|
||||
kfree(f21->control.reg_8->regs);
|
||||
kfree(f21->control.reg_8);
|
||||
kfree(f21);
|
||||
fc->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int rmi_f21_initialize(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_21_data *instance_data = fc->data;
|
||||
int retval = 0;
|
||||
u16 next_loc;
|
||||
|
||||
/* Read F21 Query Data */
|
||||
instance_data->query.address = fc->fd.query_base_addr;
|
||||
retval = rmi_read_block(fc->rmi_dev, instance_data->query.address,
|
||||
(u8 *)&instance_data->query, sizeof(instance_data->query.regs));
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Could not read query registers from 0x%04x\n", instance_data->query.address);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize Control Data */
|
||||
next_loc = fc->fd.control_base_addr;
|
||||
|
||||
instance_data->control.reg_0__3 =
|
||||
kzalloc(sizeof(union f21_2df_control_0__3), GFP_KERNEL);
|
||||
if (!instance_data->control.reg_0__3) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_0__3->address = next_loc;
|
||||
next_loc += sizeof(instance_data->control.reg_0__3->regs);
|
||||
|
||||
instance_data->control.reg_4 =
|
||||
kzalloc(sizeof(struct f21_2df_control_4), GFP_KERNEL);
|
||||
if (!instance_data->control.reg_4) {
|
||||
dev_err(&fc->dev, "Failed to allocate control register.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_4->length = instance_data->query.max_force_sensor_count;
|
||||
instance_data->control.reg_4->regs =
|
||||
kzalloc(sizeof(struct f21_2df_control_4n)
|
||||
* instance_data->control.reg_4->length, GFP_KERNEL);
|
||||
if (!instance_data->control.reg_4->regs) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_4->address = next_loc;
|
||||
next_loc += instance_data->control.reg_4->length;
|
||||
|
||||
instance_data->control.reg_5 =
|
||||
kzalloc(sizeof(struct f21_2df_control_5), GFP_KERNEL);
|
||||
if (!instance_data->control.reg_5) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_5->length = instance_data->query.max_force_sensor_count;
|
||||
instance_data->control.reg_5->regs =
|
||||
kzalloc(sizeof(struct f21_2df_control_5n)
|
||||
* instance_data->control.reg_5->length, GFP_KERNEL);
|
||||
if (!instance_data->control.reg_5->regs) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_5->address = next_loc;
|
||||
next_loc += instance_data->control.reg_5->length;
|
||||
|
||||
instance_data->control.reg_6 =
|
||||
kzalloc(sizeof(struct f21_2df_control_6), GFP_KERNEL);
|
||||
if (!instance_data->control.reg_6) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_6->length = instance_data->query.max_force_sensor_count;
|
||||
instance_data->control.reg_6->regs =
|
||||
kzalloc(sizeof(struct f21_2df_control_6n)
|
||||
* instance_data->control.reg_6->length, GFP_KERNEL);
|
||||
if (!instance_data->control.reg_6->regs) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_6->address = next_loc;
|
||||
next_loc += instance_data->control.reg_6->length;
|
||||
|
||||
instance_data->control.reg_7 =
|
||||
kzalloc(sizeof(struct f21_2df_control_7), GFP_KERNEL);
|
||||
if (!instance_data->control.reg_7) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_7->length = instance_data->query.max_force_sensor_count;
|
||||
instance_data->control.reg_7->regs =
|
||||
kzalloc(sizeof(struct f21_2df_control_7n)
|
||||
* instance_data->control.reg_7->length, GFP_KERNEL);
|
||||
if (!instance_data->control.reg_7->regs) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_7->address = next_loc;
|
||||
next_loc += instance_data->control.reg_7->length;
|
||||
|
||||
instance_data->control.reg_8 =
|
||||
kzalloc(sizeof(struct f21_2df_control_8), GFP_KERNEL);
|
||||
if (!instance_data->control.reg_8) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_8->length = instance_data->query.max_force_sensor_count;
|
||||
instance_data->control.reg_8->regs =
|
||||
kzalloc(sizeof(struct f21_2df_control_8n)
|
||||
* instance_data->control.reg_8->length, GFP_KERNEL);
|
||||
if (!instance_data->control.reg_8->regs) {
|
||||
dev_err(&fc->dev, "Failed to allocate control registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_8->address = next_loc;
|
||||
|
||||
|
||||
/* initialize data registers */
|
||||
next_loc = fc->fd.data_base_addr;
|
||||
|
||||
instance_data->data.reg_0__1.force_hi_lo = kzalloc(
|
||||
2 * instance_data->query.max_force_sensor_count * sizeof(u8),
|
||||
GFP_KERNEL);
|
||||
if (!instance_data->data.reg_0__1.force_hi_lo) {
|
||||
dev_err(&fc->dev, "Failed to allocate data registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->data.reg_0__1.address = next_loc;
|
||||
next_loc += 2 * instance_data->query.max_force_sensor_count;
|
||||
|
||||
instance_data->data.reg_2 =
|
||||
kzalloc(sizeof(union f21_2df_data_2), GFP_KERNEL);
|
||||
if (!instance_data->data.reg_2) {
|
||||
dev_err(&fc->dev, "Failed to allocate data registers.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
instance_data->control.reg_0__3->address = next_loc;
|
||||
|
||||
mutex_init(&instance_data->control_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int rmi_f21_create_sysfs(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_21_data *instance_data = fc->data;
|
||||
u8 int_num = instance_data->query.max_force_sensor_count;
|
||||
if (int_num > 7)
|
||||
int_num = 7;
|
||||
dev_dbg(&fc->dev, "Creating sysfs files.");
|
||||
|
||||
/* Set up sysfs device attributes. */
|
||||
if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0 ) {
|
||||
dev_err(&fc->dev, "Failed to create query sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (sysfs_create_group(&fc->dev.kobj, &attrs_control) < 0 ) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
switch(int_num) {
|
||||
case 7:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_6)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
case 6:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_5)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
case 5:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_4)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
case 4:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_3)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
case 3:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_2)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
case 2:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_1)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
case 1:
|
||||
if (sysfs_create_file(&fc->dev.kobj, attrify(int_en_force_0)) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create control sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sysfs_create_group(&fc->dev.kobj, &attrs_data) < 0 ) {
|
||||
dev_err(&fc->dev, "Failed to create data sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (sysfs_create_group(&fc->dev.kobj, &attrs_command) < 0 ) {
|
||||
dev_err(&fc->dev, "Failed to create command sysfs files.");
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int rmi_f21_config(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_21_data *data = fc->data;
|
||||
/* repeated register functions */
|
||||
|
||||
/* Write Control Register values back to device */
|
||||
rmi_write_block(fc->rmi_dev, data->control.reg_0__3->address,
|
||||
(u8 *)data->control.reg_0__3,
|
||||
sizeof(data->control.reg_0__3->regs));
|
||||
|
||||
rmi_write_block(fc->rmi_dev, data->control.reg_4->address,
|
||||
(u8*) data->control.reg_4->regs,
|
||||
data->query.max_force_sensor_count * sizeof(u8));
|
||||
|
||||
rmi_write_block(fc->rmi_dev, data->control.reg_5->address,
|
||||
(u8*) data->control.reg_5->regs,
|
||||
data->query.max_force_sensor_count * sizeof(u8));
|
||||
|
||||
rmi_write_block(fc->rmi_dev, data->control.reg_6->address,
|
||||
(u8*) data->control.reg_6->regs,
|
||||
data->query.max_force_sensor_count * sizeof(u8));
|
||||
|
||||
rmi_write_block(fc->rmi_dev, data->control.reg_7->address,
|
||||
(u8*) data->control.reg_7->regs,
|
||||
data->query.max_force_sensor_count * sizeof(u8));
|
||||
|
||||
rmi_write_block(fc->rmi_dev, data->control.reg_8->address,
|
||||
(u8*) data->control.reg_8->regs,
|
||||
data->query.max_force_sensor_count * sizeof(u8));
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f21_remove(struct rmi_function_container *fc)
|
||||
{
|
||||
|
||||
dev_info(&fc->dev, "Removing F21.");
|
||||
rmi_f21_free_memory(fc);
|
||||
}
|
||||
|
||||
/* sysfs functions */
|
||||
/* Query */
|
||||
simple_show_union_struct_unsigned(query, max_force_sensor_count)
|
||||
simple_show_union_struct_unsigned(query, has_high_resolution)
|
||||
|
||||
/* Control */
|
||||
show_store_union_struct_unsigned(control, reg_0__3, reporting_mode)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, no_rezero)
|
||||
|
||||
show_store_union_struct_unsigned(control, reg_0__3, force_click_threshold)
|
||||
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_0)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_1)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_2)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_3)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_4)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_5)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_force_6)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, int_en_click)
|
||||
show_store_union_struct_unsigned(control, reg_0__3, force_interrupt_threshold)
|
||||
|
||||
|
||||
/* repeated register functions */
|
||||
show_store_repeated_union_struct_unsigned(control, reg_4, use_cfg_cal)
|
||||
show_store_repeated_union_struct_unsigned(control, reg_4, one_newton_calibration)
|
||||
show_store_repeated_union_struct_unsigned(control, reg_5, x_location)
|
||||
show_store_repeated_union_struct_unsigned(control, reg_6, y_location)
|
||||
show_store_repeated_union_struct_unsigned(control, reg_7, transmitter_force_sensor)
|
||||
show_store_repeated_union_struct_unsigned(control, reg_8, receiver_force_sensor)
|
||||
|
||||
/* Data */
|
||||
static ssize_t rmi_fn_21_force_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;
|
||||
|
||||
/* Read current regtype values */
|
||||
reg_length = data->query.max_force_sensor_count;
|
||||
result = rmi_read_block(fc->rmi_dev, data->data.reg_0__1.address,
|
||||
data->data.reg_0__1.force_hi_lo,
|
||||
2 *reg_length * sizeof(u8));
|
||||
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not read regtype at 0x%x\nData may be outdated.", __func__,
|
||||
data->data.reg_0__1.address);
|
||||
}
|
||||
temp = buf;
|
||||
for (i = 0; i < reg_length; i++) {
|
||||
result = snprintf(temp, PAGE_SIZE - size, "%d ",
|
||||
data->data.reg_0__1.force_hi_lo[i] * (2 << 3)
|
||||
+ data->data.reg_0__1.force_hi_lo[i + reg_length]);
|
||||
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;
|
||||
}
|
||||
|
||||
show_union_struct_unsigned(data, reg_2, force_click)
|
||||
|
||||
/* Command */
|
||||
|
||||
static ssize_t rmi_fn_21_rezero_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count) {
|
||||
unsigned long val;
|
||||
int error, result;
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_21_data *instance_data;
|
||||
struct rmi_driver *driver;
|
||||
u8 command;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
driver = fc->rmi_dev->driver;
|
||||
|
||||
/* need to convert the string data to an actual value */
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
/* Do nothing if not set to 1. This prevents accidental commands. */
|
||||
if (val != 1)
|
||||
return count;
|
||||
|
||||
command = (unsigned char)F21_REZERO_CMD;
|
||||
|
||||
/* Write the command to the command register */
|
||||
result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
|
||||
&command, 1);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "%s : Could not write command to 0x%x\n",
|
||||
__func__, fc->fd.command_base_addr);
|
||||
return result;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct rmi_function_handler function_handler = {
|
||||
.func = 0x21,
|
||||
.init = rmi_f21_init,
|
||||
.config = rmi_f21_config,
|
||||
.reset = NULL,
|
||||
.attention = NULL,
|
||||
.remove = rmi_f21_remove
|
||||
};
|
||||
|
||||
static int __init rmi_f21_module_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = rmi_register_function_driver(&function_handler);
|
||||
if (error < 0) {
|
||||
pr_err("%s: register failed!\n", __func__);
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f21_module_exit(void)
|
||||
{
|
||||
rmi_unregister_function_driver(&function_handler);
|
||||
}
|
||||
|
||||
module_init(rmi_f21_module_init);
|
||||
module_exit(rmi_f21_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI F21 module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
962
drivers/input/touchscreen/rmi4/rmi_f34.c
Executable file
962
drivers/input/touchscreen/rmi4/rmi_f34.c
Executable file
|
|
@ -0,0 +1,962 @@
|
|||
/*
|
||||
* 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/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
#include "rmi_driver.h"
|
||||
|
||||
/* define fn $34 commands */
|
||||
#define WRITE_FW_BLOCK 0x2
|
||||
#define ERASE_ALL 0x3
|
||||
#define READ_CONFIG_BLOCK 0x5
|
||||
#define WRITE_CONFIG_BLOCK 0x6
|
||||
#define ERASE_CONFIG 0x7
|
||||
#define ENABLE_FLASH_PROG 0xf
|
||||
|
||||
#define STATUS_IN_PROGRESS 0xff
|
||||
#define STATUS_IDLE 0x80
|
||||
|
||||
#define PDT_START_SCAN_LOCATION 0x00e9
|
||||
#define PDT_END_SCAN_LOCATION 0x0005
|
||||
|
||||
#define BLK_SZ_OFF 3
|
||||
#define IMG_BLK_CNT_OFF 5
|
||||
#define CFG_BLK_CNT_OFF 7
|
||||
|
||||
#define BLK_NUM_OFF 2
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
||||
#define KERNEL_VERSION_ABOVE_2_6_32 1
|
||||
#endif
|
||||
|
||||
/* data specific to fn $34 that needs to be kept around */
|
||||
struct rmi_fn_34_data {
|
||||
unsigned char status;
|
||||
unsigned char cmd;
|
||||
unsigned short bootloaderid;
|
||||
unsigned short blocksize;
|
||||
unsigned short imageblockcount;
|
||||
unsigned short configblockcount;
|
||||
unsigned short blocknum;
|
||||
bool inflashprogmode;
|
||||
struct mutex attn_mutex;
|
||||
};
|
||||
|
||||
static ssize_t rmi_fn_34_status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
|
||||
static ssize_t rmi_fn_34_status_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_fn_34_cmd_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static ssize_t rmi_fn_34_cmd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
#ifdef KERNEL_VERSION_ABOVE_2_6_32
|
||||
static ssize_t rmi_fn_34_data_read(struct file *data_file, struct kobject *kobj,
|
||||
struct bin_attribute *attributes,
|
||||
char *buf, loff_t pos, size_t count);
|
||||
|
||||
static ssize_t rmi_fn_34_data_write(struct file *data_file,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *attributes, char *buf,
|
||||
loff_t pos, size_t count);
|
||||
#else
|
||||
static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
|
||||
struct bin_attribute *attributes,
|
||||
char *buf, loff_t pos, size_t count);
|
||||
|
||||
static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
|
||||
struct bin_attribute *attributes, char *buf,
|
||||
loff_t pos, size_t count);
|
||||
#endif
|
||||
|
||||
static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
|
||||
static int rmi_f34_alloc_memory(struct rmi_function_container *fc);
|
||||
|
||||
static void rmi_f34_free_memory(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f34_initialize(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f34_config(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f34_reset(struct rmi_function_container *fc);
|
||||
|
||||
static int rmi_f34_create_sysfs(struct rmi_function_container *fc);
|
||||
|
||||
|
||||
|
||||
static struct device_attribute attrs[] = {
|
||||
__ATTR(status, RMI_RW_ATTR,
|
||||
rmi_fn_34_status_show, rmi_fn_34_status_store),
|
||||
|
||||
/* Also, sysfs will need to have a file set up to distinguish
|
||||
* between commands - like Config write/read, Image write/verify. */
|
||||
__ATTR(cmd, RMI_RW_ATTR,
|
||||
rmi_fn_34_cmd_show, rmi_fn_34_cmd_store),
|
||||
__ATTR(bootloaderid, RMI_RW_ATTR,
|
||||
rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store),
|
||||
__ATTR(blocksize, RMI_RO_ATTR,
|
||||
rmi_fn_34_blocksize_show, rmi_store_error),
|
||||
__ATTR(imageblockcount, RMI_RO_ATTR,
|
||||
rmi_fn_34_imageblockcount_show, rmi_store_error),
|
||||
__ATTR(configblockcount, RMI_RO_ATTR,
|
||||
rmi_fn_34_configblockcount_show, rmi_store_error),
|
||||
__ATTR(blocknum, RMI_RW_ATTR,
|
||||
rmi_fn_34_blocknum_show, rmi_fn_34_blocknum_store),
|
||||
__ATTR(rescanPDT, RMI_WO_ATTR,
|
||||
rmi_show_error, rmi_fn_34_rescanPDT_store)
|
||||
};
|
||||
|
||||
struct bin_attribute dev_attr_data = {
|
||||
.attr = {
|
||||
.name = "data",
|
||||
.mode = 0666},
|
||||
.size = 0,
|
||||
.read = rmi_fn_34_data_read,
|
||||
.write = rmi_fn_34_data_write,
|
||||
};
|
||||
|
||||
|
||||
static int rmi_f34_init(struct rmi_function_container *fc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
dev_info(&fc->dev, "Intializing f34 values.");
|
||||
|
||||
/* init instance data, fill in values and create any sysfs files */
|
||||
retval = rmi_f34_alloc_memory(fc);
|
||||
if (retval < 0)
|
||||
goto exit_free_data;
|
||||
|
||||
retval = rmi_f34_initialize(fc);
|
||||
if (retval < 0)
|
||||
goto exit_free_data;
|
||||
|
||||
retval = rmi_f34_create_sysfs(fc);
|
||||
if (retval < 0)
|
||||
goto exit_free_data;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free_data:
|
||||
rmi_f34_free_memory(fc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rmi_f34_alloc_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_34_data *f34;
|
||||
|
||||
f34 = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL);
|
||||
if (!f34) {
|
||||
dev_err(&fc->dev, "Failed to allocate rmi_fn_34_data.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
fc->data = f34;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f34_free_memory(struct rmi_function_container *fc)
|
||||
{
|
||||
kfree(fc->data);
|
||||
fc->data = NULL;
|
||||
}
|
||||
|
||||
static int rmi_f34_initialize(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_device *rmi_dev = fc->rmi_dev;
|
||||
struct rmi_device_platform_data *pdata;
|
||||
int retval = 0;
|
||||
struct rmi_fn_34_data *f34 = fc->data;
|
||||
u16 query_base_addr;
|
||||
u16 control_base_addr;
|
||||
unsigned char buf[2];
|
||||
|
||||
pdata = to_rmi_platform_data(rmi_dev);
|
||||
dev_dbg(&fc->dev, "Initializing F34 values for %s.\n",
|
||||
pdata->sensor_name);
|
||||
|
||||
mutex_init(&f34->attn_mutex);
|
||||
|
||||
/* get the Bootloader ID and Block Size. */
|
||||
query_base_addr = fc->fd.query_base_addr;
|
||||
control_base_addr = fc->fd.control_base_addr;
|
||||
|
||||
retval = rmi_read_block(fc->rmi_dev, query_base_addr, buf,
|
||||
ARRAY_SIZE(buf));
|
||||
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Could not read bootloaderid from 0x%04x.\n",
|
||||
query_base_addr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
batohs(&f34->bootloaderid, buf);
|
||||
|
||||
retval = rmi_read_block(fc->rmi_dev, query_base_addr + BLK_SZ_OFF, buf,
|
||||
ARRAY_SIZE(buf));
|
||||
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Could not read block size from 0x%04x, "
|
||||
"error=%d.\n", query_base_addr + BLK_SZ_OFF, retval);
|
||||
return retval;
|
||||
}
|
||||
batohs(&f34->blocksize, buf);
|
||||
|
||||
/* Get firmware image block count and store it in the instance data */
|
||||
retval = rmi_read_block(fc->rmi_dev, query_base_addr + IMG_BLK_CNT_OFF,
|
||||
buf, ARRAY_SIZE(buf));
|
||||
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Couldn't read image block count from 0x%x, "
|
||||
"error=%d.\n", query_base_addr + IMG_BLK_CNT_OFF,
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
batohs(&f34->imageblockcount, buf);
|
||||
|
||||
/* Get config block count and store it in the instance data */
|
||||
retval = rmi_read_block(fc->rmi_dev, query_base_addr + 7, buf,
|
||||
ARRAY_SIZE(buf));
|
||||
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Couldn't read config block count from 0x%x, "
|
||||
"error=%d.\n", query_base_addr + CFG_BLK_CNT_OFF,
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
batohs(&f34->configblockcount, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34_create_sysfs(struct rmi_function_container *fc)
|
||||
{
|
||||
int attr_count = 0;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&fc->dev, "Creating sysfs files.");
|
||||
|
||||
/* We need a sysfs file for the image/config block to write or read.
|
||||
* Set up sysfs bin file for binary data block. Since the image is
|
||||
* already in our format there is no need to convert the data for
|
||||
* endianess. */
|
||||
rc = sysfs_create_bin_file(&fc->dev.kobj,
|
||||
&dev_attr_data);
|
||||
if (rc < 0) {
|
||||
dev_err(&fc->dev, "Failed to create sysfs file for F34 data "
|
||||
"(error = %d).\n", rc);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Set up sysfs device attributes. */
|
||||
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
|
||||
if (sysfs_create_file
|
||||
(&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
|
||||
dev_err(&fc->dev, "Failed to create sysfs file for %s.",
|
||||
attrs[attr_count].attr.name);
|
||||
rc = -ENODEV;
|
||||
goto err_remove_sysfs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_sysfs:
|
||||
sysfs_remove_bin_file(&fc->dev.kobj, &dev_attr_data);
|
||||
|
||||
for (attr_count--; attr_count >= 0; attr_count--)
|
||||
sysfs_remove_file(&fc->dev.kobj,
|
||||
&attrs[attr_count].attr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rmi_f34_config(struct rmi_function_container *fc)
|
||||
{
|
||||
/* for this function we should do nothing here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int rmi_f34_reset(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_34_data *instance_data = fc->data;
|
||||
|
||||
instance_data->status = ECONNRESET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f34_remove(struct rmi_function_container *fc)
|
||||
{
|
||||
int attr_count;
|
||||
|
||||
sysfs_remove_bin_file(&fc->dev.kobj,
|
||||
&dev_attr_data);
|
||||
|
||||
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
|
||||
sysfs_remove_file(&fc->dev.kobj,
|
||||
&attrs[attr_count].attr);
|
||||
|
||||
rmi_f34_free_memory(fc);
|
||||
}
|
||||
|
||||
static int f34_read_status(struct rmi_function_container *fc)
|
||||
{
|
||||
struct rmi_fn_34_data *instance_data = fc->data;
|
||||
u16 data_base_addr = fc->fd.data_base_addr;
|
||||
u8 status;
|
||||
int retval;
|
||||
|
||||
if (instance_data->status == ECONNRESET)
|
||||
return instance_data->status;
|
||||
|
||||
/* Read the Fn $34 status from F34_Flash_Data3 to see the previous
|
||||
* commands status. F34_Flash_Data3 will be the address after the
|
||||
* 2 block number registers plus blocksize Data registers.
|
||||
* inform user space - through a sysfs param. */
|
||||
retval = rmi_read(fc->rmi_dev,
|
||||
data_base_addr + instance_data->blocksize +
|
||||
BLK_NUM_OFF, &status);
|
||||
|
||||
if (retval < 0) {
|
||||
dev_err(&fc->dev, "Could not read status from 0x%x\n",
|
||||
data_base_addr + instance_data->blocksize + BLK_NUM_OFF);
|
||||
status = 0xff; /* failure */
|
||||
}
|
||||
|
||||
/* set a sysfs value that the user mode can read - only
|
||||
* upper 4 bits are the status. successful is $80, anything
|
||||
* else is failure */
|
||||
instance_data->status = status & 0xf0;
|
||||
|
||||
/* put mode into Flash Prog Mode when we successfully do
|
||||
* an Enable Flash Prog cmd. */
|
||||
if ((instance_data->status == STATUS_IDLE) &&
|
||||
(instance_data->cmd == ENABLE_FLASH_PROG))
|
||||
instance_data->inflashprogmode = true;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits)
|
||||
{
|
||||
int retval;
|
||||
struct rmi_fn_34_data *data = fc->data;
|
||||
|
||||
mutex_lock(&data->attn_mutex);
|
||||
retval = f34_read_status(fc);
|
||||
mutex_unlock(&data->attn_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct rmi_function_handler function_handler = {
|
||||
.func = 0x34,
|
||||
.init = rmi_f34_init,
|
||||
.config = rmi_f34_config,
|
||||
.reset = rmi_f34_reset,
|
||||
.attention = rmi_f34_attention,
|
||||
.remove = rmi_f34_remove
|
||||
};
|
||||
|
||||
static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->bootloaderid);
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int error;
|
||||
unsigned long val;
|
||||
unsigned char data[2];
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
u16 data_base_addr;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
/* need to convert the string data to an actual value */
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
instance_data->bootloaderid = val;
|
||||
|
||||
/* Write the Bootloader ID key data back to the first two Block
|
||||
* Data registers (F34_Flash_Data2.0 and F34_Flash_Data2.1). */
|
||||
hstoba(data, (unsigned short)val);
|
||||
data_base_addr = fc->fd.data_base_addr;
|
||||
|
||||
error = rmi_write_block(fc->rmi_dev,
|
||||
data_base_addr + BLK_NUM_OFF,
|
||||
data,
|
||||
ARRAY_SIZE(data));
|
||||
|
||||
if (error < 0) {
|
||||
dev_err(dev, "%s : Could not write bootloader id to 0x%x\n",
|
||||
__func__, data_base_addr + BLK_NUM_OFF);
|
||||
return error;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocksize);
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
instance_data->imageblockcount);
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||
instance_data->configblockcount);
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_status_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
int retval;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
mutex_lock(&instance_data->attn_mutex);
|
||||
retval = f34_read_status(fc);
|
||||
mutex_unlock(&instance_data->attn_mutex);
|
||||
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->status);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t rmi_fn_34_status_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
instance_data->status = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t rmi_fn_34_cmd_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->cmd);
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_cmd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
unsigned long val;
|
||||
u16 data_base_addr;
|
||||
int error;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
data_base_addr = fc->fd.data_base_addr;
|
||||
|
||||
/* need to convert the string data to an actual value */
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* make sure we are in Flash Prog mode for all cmds except the
|
||||
* Enable Flash Programming cmd - otherwise we are in error */
|
||||
if ((val != ENABLE_FLASH_PROG) && !instance_data->inflashprogmode) {
|
||||
dev_err(dev, "%s: CANNOT SEND CMD %d TO SENSOR - "
|
||||
"NOT IN FLASH PROG MODE\n"
|
||||
, __func__, data_base_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
instance_data->cmd = val;
|
||||
|
||||
/* Validate command value and (if necessary) write it to the command
|
||||
* register.
|
||||
*/
|
||||
switch (instance_data->cmd) {
|
||||
case ENABLE_FLASH_PROG:
|
||||
case ERASE_ALL:
|
||||
case ERASE_CONFIG:
|
||||
case WRITE_FW_BLOCK:
|
||||
case READ_CONFIG_BLOCK:
|
||||
case WRITE_CONFIG_BLOCK:
|
||||
/* Reset the status to indicate we are in progress on a cmd. */
|
||||
/* The status will change when the ATTN interrupt happens
|
||||
and the status of the cmd that was issued is read from
|
||||
the F34_Flash_Data3 register - result should be 0x80 for
|
||||
success - any other value indicates an error */
|
||||
|
||||
/* Issue the command to the device. */
|
||||
error = rmi_write(fc->rmi_dev,
|
||||
data_base_addr + instance_data->blocksize +
|
||||
BLK_NUM_OFF, instance_data->cmd);
|
||||
|
||||
if (error < 0) {
|
||||
dev_err(dev, "%s: Could not write command 0x%02x "
|
||||
"to 0x%04x\n", __func__, instance_data->cmd,
|
||||
data_base_addr + instance_data->blocksize +
|
||||
BLK_NUM_OFF);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (instance_data->cmd == ENABLE_FLASH_PROG)
|
||||
instance_data->inflashprogmode = true;
|
||||
|
||||
/* set status to indicate we are in progress */
|
||||
instance_data->status = STATUS_IN_PROGRESS;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "%s: RMI4 function $34 - "
|
||||
"unknown command 0x%02lx.\n", __func__, val);
|
||||
count = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocknum);
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int error;
|
||||
unsigned long val;
|
||||
unsigned char data[2];
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
u16 data_base_addr;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
data_base_addr = fc->fd.data_base_addr;
|
||||
|
||||
/* need to convert the string data to an actual value */
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
instance_data->blocknum = val;
|
||||
|
||||
/* Write the Block Number data back to the first two Block
|
||||
* Data registers (F34_Flash_Data_0 and F34_Flash_Data_1). */
|
||||
hstoba(data, (unsigned short)val);
|
||||
|
||||
error = rmi_write_block(fc->rmi_dev,
|
||||
data_base_addr,
|
||||
data,
|
||||
ARRAY_SIZE(data));
|
||||
|
||||
if (error < 0) {
|
||||
dev_err(dev, "%s : Could not write block number %u to 0x%x\n",
|
||||
__func__, instance_data->blocknum, data_base_addr);
|
||||
return error;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
struct rmi_device *rmi_dev;
|
||||
struct rmi_driver_data *driver_data;
|
||||
struct pdt_entry pdt_entry;
|
||||
bool fn01found = false;
|
||||
bool fn34found = false;
|
||||
unsigned int rescan;
|
||||
int irq_count = 0;
|
||||
int retval = 0;
|
||||
int i;
|
||||
|
||||
/* Rescan of the PDT is needed since issuing the Flash Enable cmd
|
||||
* the device registers for Fn$01 and Fn$34 moving around because
|
||||
* of the change from Bootloader mode to Flash Programming mode
|
||||
* may change to a different PDT with only Fn$01 and Fn$34 that
|
||||
* could have addresses for query, control, data, command registers
|
||||
* that differ from the PDT scan done at device initialization. */
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
rmi_dev = fc->rmi_dev;
|
||||
driver_data = rmi_get_driverdata(rmi_dev);
|
||||
|
||||
/* Make sure we are only in Flash Programming mode - DON'T
|
||||
* ALLOW THIS IN UI MODE. */
|
||||
if (instance_data->cmd != ENABLE_FLASH_PROG) {
|
||||
dev_err(dev, "%s: NOT IN FLASH PROG MODE - CAN'T RESCAN PDT.\n"
|
||||
, __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The only good value to write to this is 1, we allow 0, but with
|
||||
* no effect (this is consistent with the way the command bit works. */
|
||||
if (sscanf(buf, "%u", &rescan) != 1)
|
||||
return -EINVAL;
|
||||
if (rescan < 0 || rescan > 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* 0 has no effect, so we skip it entirely. */
|
||||
if (rescan) {
|
||||
/* rescan the PDT - filling in Fn01 and Fn34 addresses -
|
||||
* this is only temporary - the device will need to be reset
|
||||
* to return the PDT to the normal values. */
|
||||
|
||||
/* mini-parse the PDT - we only have to get Fn$01 and Fn$34 and
|
||||
since we are Flash Programming mode we only have page 0. */
|
||||
for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION;
|
||||
i -= sizeof(pdt_entry)) {
|
||||
retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
|
||||
sizeof(pdt_entry));
|
||||
if (retval != sizeof(pdt_entry)) {
|
||||
dev_err(dev, "%s: err frm rmi_read_block pdt "
|
||||
"entry data from PDT, "
|
||||
"error = %d.", __func__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if ((pdt_entry.function_number == 0x00) ||
|
||||
(pdt_entry.function_number == 0xff))
|
||||
break;
|
||||
|
||||
dev_dbg(dev, "%s: Found F%.2X\n",
|
||||
__func__, pdt_entry.function_number);
|
||||
|
||||
/* f01 found - just fill in the new addresses in
|
||||
* the existing fc. */
|
||||
if (pdt_entry.function_number == 0x01) {
|
||||
struct rmi_function_container *f01_fc =
|
||||
driver_data->f01_container;
|
||||
fn01found = true;
|
||||
f01_fc->fd.query_base_addr =
|
||||
pdt_entry.query_base_addr;
|
||||
f01_fc->fd.command_base_addr =
|
||||
pdt_entry.command_base_addr;
|
||||
f01_fc->fd.control_base_addr =
|
||||
pdt_entry.control_base_addr;
|
||||
f01_fc->fd.data_base_addr =
|
||||
pdt_entry.data_base_addr;
|
||||
f01_fc->fd.function_number =
|
||||
pdt_entry.function_number;
|
||||
f01_fc->fd.interrupt_source_count =
|
||||
pdt_entry.interrupt_source_count;
|
||||
f01_fc->num_of_irqs =
|
||||
pdt_entry.interrupt_source_count;
|
||||
f01_fc->irq_pos = irq_count;
|
||||
|
||||
irq_count += f01_fc->num_of_irqs;
|
||||
|
||||
if (fn34found)
|
||||
break;
|
||||
}
|
||||
|
||||
/* f34 found - just fill in the new addresses in
|
||||
* the existing fc. */
|
||||
if (pdt_entry.function_number == 0x34) {
|
||||
fn34found = true;
|
||||
fc->fd.query_base_addr =
|
||||
pdt_entry.query_base_addr;
|
||||
fc->fd.command_base_addr =
|
||||
pdt_entry.command_base_addr;
|
||||
fc->fd.control_base_addr =
|
||||
pdt_entry.control_base_addr;
|
||||
fc->fd.data_base_addr =
|
||||
pdt_entry.data_base_addr;
|
||||
fc->fd.function_number =
|
||||
pdt_entry.function_number;
|
||||
fc->fd.interrupt_source_count =
|
||||
pdt_entry.interrupt_source_count;
|
||||
fc->num_of_irqs =
|
||||
pdt_entry.interrupt_source_count;
|
||||
fc->irq_pos = irq_count;
|
||||
|
||||
irq_count += fc->num_of_irqs;
|
||||
|
||||
if (fn01found)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!fn01found || !fn34found) {
|
||||
dev_err(dev, "%s: failed to find fn$01 or fn$34 trying "
|
||||
"to do rescan PDT.\n"
|
||||
, __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef KERNEL_VERSION_ABOVE_2_6_32
|
||||
static ssize_t rmi_fn_34_data_read(struct file *data_file,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *attributes,
|
||||
char *buf,
|
||||
loff_t pos,
|
||||
size_t count)
|
||||
#else
|
||||
static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
|
||||
struct bin_attribute *attributes,
|
||||
char *buf,
|
||||
loff_t pos,
|
||||
size_t count)
|
||||
#endif
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
u16 data_base_addr;
|
||||
int error;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
data_base_addr = fc->fd.data_base_addr;
|
||||
|
||||
if (count != instance_data->blocksize) {
|
||||
dev_err(dev,
|
||||
"%s : Incorrect F34 block size %d. "
|
||||
"Expected size %d.\n",
|
||||
__func__, count, instance_data->blocksize);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read the data from flash into buf. The app layer will be blocked
|
||||
* at reading from the sysfs file. When we return the count (or
|
||||
* error if we fail) the app will resume. */
|
||||
error = rmi_read_block(fc->rmi_dev, data_base_addr + BLK_NUM_OFF,
|
||||
(unsigned char *)buf, count);
|
||||
|
||||
if (error < 0) {
|
||||
dev_err(dev, "%s : Could not read data from 0x%04x\n",
|
||||
__func__, data_base_addr + BLK_NUM_OFF);
|
||||
return error;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef KERNEL_VERSION_ABOVE_2_6_32
|
||||
static ssize_t rmi_fn_34_data_write(struct file *data_file,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *attributes,
|
||||
char *buf,
|
||||
loff_t pos,
|
||||
size_t count)
|
||||
#else
|
||||
static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
|
||||
struct bin_attribute *attributes,
|
||||
char *buf,
|
||||
loff_t pos,
|
||||
size_t count)
|
||||
#endif
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct rmi_function_container *fc;
|
||||
struct rmi_fn_34_data *instance_data;
|
||||
u16 data_base_addr;
|
||||
int error;
|
||||
|
||||
fc = to_rmi_function_container(dev);
|
||||
instance_data = fc->data;
|
||||
|
||||
data_base_addr = fc->fd.data_base_addr;
|
||||
|
||||
/* Write the data from buf to flash. The app layer will be
|
||||
* blocked at writing to the sysfs file. When we return the
|
||||
* count (or error if we fail) the app will resume. */
|
||||
|
||||
if (count != instance_data->blocksize) {
|
||||
dev_err(dev,
|
||||
"%s : Incorrect F34 block size %d. "
|
||||
"Expected size %d.\n",
|
||||
__func__, count, instance_data->blocksize);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Write the data block - only if the count is non-zero */
|
||||
if (count) {
|
||||
error = rmi_write_block(fc->rmi_dev,
|
||||
data_base_addr + BLK_NUM_OFF,
|
||||
(unsigned char *)buf,
|
||||
count);
|
||||
|
||||
if (error < 0) {
|
||||
dev_err(dev, "%s : Could not write block data "
|
||||
"to 0x%x\n", __func__,
|
||||
data_base_addr + BLK_NUM_OFF);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int __init rmi_f34_module_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = rmi_register_function_driver(&function_handler);
|
||||
if (error < 0) {
|
||||
pr_err("%s : register failed !\n", __func__);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmi_f34_module_exit(void)
|
||||
{
|
||||
rmi_unregister_function_driver(&function_handler);
|
||||
}
|
||||
|
||||
module_init(rmi_f34_module_init);
|
||||
module_exit(rmi_f34_module_exit);
|
||||
|
||||
MODULE_AUTHOR("William Manson <wmanson@synaptics.com");
|
||||
MODULE_DESCRIPTION("RMI F34 module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
2272
drivers/input/touchscreen/rmi4/rmi_f54.c
Executable file
2272
drivers/input/touchscreen/rmi4/rmi_f54.c
Executable file
File diff suppressed because it is too large
Load Diff
453
drivers/input/touchscreen/rmi4/rmi_i2c.c
Executable file
453
drivers/input/touchscreen/rmi4/rmi_i2c.c
Executable 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);
|
||||
670
drivers/input/touchscreen/rmi4/rmi_reflash.c
Normal file
670
drivers/input/touchscreen/rmi4/rmi_reflash.c
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
/*
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
Copyright (c) 2011 Synaptics, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
*/
|
||||
|
||||
// SynaFirmwareImage.h contains the data for both the entire image and the config block
|
||||
//#include "SynaFirmwareImage.h"
|
||||
//#include "config.h"
|
||||
|
||||
#include <linux/string.h>
|
||||
//#include <linux/printk.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/rmi.h>
|
||||
#include "rmi_reflash.h"
|
||||
|
||||
//need
|
||||
#define ASSERT 1
|
||||
#define TOUCH_CONTROLLER "s3202"
|
||||
#define FW_REVISION "DS4 R3.0"
|
||||
|
||||
void eraseConfigBlock(void);
|
||||
void SynaInitialize(void);
|
||||
void SynaReadConfigInfo(void);
|
||||
void SynaReadFirmwareInfo(void);
|
||||
int TouchControllerTypeCheck(void);
|
||||
void SynaEnableFlashing(void);
|
||||
int fimrwareRevisionCheck(void);
|
||||
void SynaBootloaderLock(void);
|
||||
void SynaProgramConfiguration(void);
|
||||
void SynaProgramFirmware(void);
|
||||
void SynaFinalizeReflash(void);
|
||||
void SynaWaitForATTN(void);
|
||||
void convertConfigBlockData(void);
|
||||
int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
|
||||
int len);
|
||||
int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
|
||||
int len);
|
||||
/* Variables for F34 functionality */
|
||||
unsigned short SynaF34DataBase;
|
||||
unsigned short SynaF34QueryBase;
|
||||
unsigned short SynaF01DataBase;
|
||||
unsigned short SynaF01CommandBase;
|
||||
unsigned short SynaF01QueryBase;
|
||||
|
||||
unsigned short SynaF34Reflash_BlockNum;
|
||||
unsigned short SynaF34Reflash_BlockData;
|
||||
unsigned short SynaF34ReflashQuery_BootID;
|
||||
unsigned short SynaF34ReflashQuery_FlashPropertyQuery;
|
||||
unsigned short SynaF34ReflashQuery_FirmwareBlockSize;
|
||||
unsigned short SynaF34ReflashQuery_FirmwareBlockCount;
|
||||
unsigned short SynaF34ReflashQuery_ConfigBlockSize;
|
||||
unsigned short SynaF34ReflashQuery_ConfigBlockCount;
|
||||
|
||||
unsigned short SynaFirmwareBlockSize;
|
||||
unsigned short SynaFirmwareBlockCount;
|
||||
unsigned long SynaImageSize;
|
||||
|
||||
unsigned short SynaConfigBlockSize;
|
||||
unsigned short SynaConfigBlockCount;
|
||||
unsigned long SynaConfigImageSize;
|
||||
|
||||
unsigned short SynaBootloadID;
|
||||
|
||||
unsigned short SynaF34_FlashControl;
|
||||
|
||||
unsigned char *SynafirmwareImgData;
|
||||
unsigned char *SynaconfigImgData;
|
||||
unsigned char *SynalockImgData;
|
||||
unsigned int SynafirmwareImgVersion;
|
||||
|
||||
unsigned char *ConfigBlock;
|
||||
|
||||
unsigned char *ConfigBlockData;
|
||||
struct rmi_phys_device *rmi_phys;
|
||||
|
||||
|
||||
|
||||
int readRMI(u16 addr, u8 *buf, int len) {
|
||||
rmi_i2c_read_block(rmi_phys, addr, buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writeRMI(u16 addr, u8 *buf, int len) {
|
||||
rmi_i2c_write_block(rmi_phys, addr, buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int waitATTN(u8 n, u16 delay) {
|
||||
#if 1
|
||||
unsigned char uStatus;
|
||||
do{
|
||||
readRMI((SynaF01DataBase + 1), &uStatus, 1);
|
||||
}while((uStatus & 0x01) == 0);
|
||||
#else
|
||||
msleep(1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* End: Variables for F34 functionality */
|
||||
|
||||
/* CompleteReflash reflashes the entire user image, including the configuration block and firmware
|
||||
*/
|
||||
void CompleteReflash(struct rmi_phys_device *phys)
|
||||
{
|
||||
rmi_phys = phys;
|
||||
SynaInitialize();
|
||||
|
||||
//if (TouchControllerTypeCheck())
|
||||
{
|
||||
if (fimrwareRevisionCheck())
|
||||
{
|
||||
printk("rmi find the current firmware revision is old, reflash now...");
|
||||
SynaReadConfigInfo();
|
||||
SynaReadFirmwareInfo();
|
||||
SynaF34_FlashControl = SynaF34DataBase + SynaFirmwareBlockSize + 2;
|
||||
SynaEnableFlashing();
|
||||
SynaBootloaderLock();
|
||||
SynaProgramFirmware();
|
||||
SynaProgramConfiguration();
|
||||
SynaFinalizeReflash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SynaSetup scans the Page Description Table (PDT) and sets up the necessary variables
|
||||
* for the reflash process. This function is a "slim" version of the PDT scan function in
|
||||
* in PDT.c, since only F34 and F01 are needed for reflash.
|
||||
*/
|
||||
void SynaSetup(void)
|
||||
{
|
||||
unsigned char address;
|
||||
unsigned char buffer[6] = {0};
|
||||
|
||||
for (address = 0xe9; address > 0xc0; address = address - 6)
|
||||
{
|
||||
readRMI(address, buffer, 6);
|
||||
|
||||
switch (buffer[5])
|
||||
{
|
||||
case 0x34:
|
||||
SynaF34DataBase = buffer[3];
|
||||
SynaF34QueryBase = buffer[0];
|
||||
break;
|
||||
case 0x01:
|
||||
SynaF01DataBase = buffer[3];
|
||||
SynaF01CommandBase = buffer[1];
|
||||
SynaF01QueryBase = buffer[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SynaF34Reflash_BlockNum = SynaF34DataBase;
|
||||
SynaF34Reflash_BlockData = SynaF34DataBase + 2;
|
||||
SynaF34ReflashQuery_BootID = SynaF34QueryBase;
|
||||
SynaF34ReflashQuery_FlashPropertyQuery = SynaF34QueryBase + 2;
|
||||
SynaF34ReflashQuery_FirmwareBlockSize = SynaF34QueryBase + 3;
|
||||
SynaF34ReflashQuery_FirmwareBlockCount = SynaF34QueryBase +5;
|
||||
SynaF34ReflashQuery_ConfigBlockSize = SynaF34QueryBase + 3;
|
||||
SynaF34ReflashQuery_ConfigBlockCount = SynaF34QueryBase + 7;
|
||||
|
||||
SynafirmwareImgData = (unsigned char *)((&SynaFirmware[0])+0x100);
|
||||
SynaconfigImgData = (unsigned char *)(SynafirmwareImgData+SynaImageSize);
|
||||
SynafirmwareImgVersion = (unsigned int)(SynaFirmware[7]);
|
||||
ConfigBlockData = SynaconfigImgData;
|
||||
switch (SynafirmwareImgVersion)
|
||||
{
|
||||
case 2:
|
||||
SynalockImgData = (unsigned char *)((&SynaFirmware[0]) + 0xD0);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
SynalockImgData = (unsigned char *)((&SynaFirmware[0]) + 0xC0);
|
||||
break;
|
||||
case 5:
|
||||
SynalockImgData = (unsigned char *)((&SynaFirmware[0]) + 0xB0);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* SynaInitialize sets up the reflahs process
|
||||
*/
|
||||
void SynaInitialize(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
unsigned char uStatus = 0;
|
||||
|
||||
//printk("\nInitializing Reflash Process...");
|
||||
uData[0] = 0;
|
||||
writeRMI(0xff, uData, 1); // switch to page0 //hhb
|
||||
|
||||
do {
|
||||
readRMI(0, &uStatus, 1);
|
||||
|
||||
if (uStatus & 0x80)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (uStatus & 0x40);
|
||||
SynaSetup();
|
||||
|
||||
SynafirmwareImgData = 0;
|
||||
|
||||
SynaconfigImgData = 0;
|
||||
|
||||
readRMI(SynaF34ReflashQuery_FirmwareBlockSize, &uData[0], 2);
|
||||
|
||||
SynaFirmwareBlockSize = uData[0] | (uData[1] << 8);
|
||||
}
|
||||
|
||||
/* SynaReadFirmwareInfo reads the F34 query registers and retrieves the block size and count
|
||||
* of the firmware section of the image to be reflashed
|
||||
*/
|
||||
void SynaReadFirmwareInfo(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
|
||||
printk("Read Firmware Info\n");
|
||||
|
||||
readRMI(SynaF34ReflashQuery_FirmwareBlockSize, &uData[0], 2);
|
||||
SynaFirmwareBlockSize = uData[0] | (uData[1] << 8);
|
||||
|
||||
readRMI(SynaF34ReflashQuery_FirmwareBlockCount, &uData[0], 2);
|
||||
SynaFirmwareBlockCount = uData[0] | (uData[1] << 8);
|
||||
SynaImageSize = SynaFirmwareBlockCount * SynaFirmwareBlockSize;
|
||||
printk("SynaFirmwareBlockSize:%d,SynaFirmwareBlockCount:%d \n", SynaFirmwareBlockSize, SynaFirmwareBlockCount);
|
||||
}
|
||||
|
||||
/* SynaReadConfigInfo reads the F34 query registers and retrieves the block size and count
|
||||
* of the configuration section of the image to be reflashed
|
||||
*/
|
||||
void SynaReadConfigInfo(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
|
||||
printk("Read Config Info\n");
|
||||
|
||||
readRMI(SynaF34ReflashQuery_ConfigBlockSize, &uData[0], 2);
|
||||
SynaConfigBlockSize = uData[0] | (uData[1] << 8);
|
||||
|
||||
readRMI(SynaF34ReflashQuery_ConfigBlockCount, &uData[0], 2);
|
||||
SynaConfigBlockCount = uData[0] | (uData[1] << 8);
|
||||
SynaConfigImageSize = SynaConfigBlockCount * SynaConfigBlockSize;
|
||||
printk("SynaConfigBlockSize:%d,SynaConfigBlockCount:%d \n", SynaConfigBlockSize, SynaConfigBlockCount);
|
||||
}
|
||||
|
||||
|
||||
/* SynaReadBootloadID reads the F34 query registers and retrieves the bootloader ID of the firmware
|
||||
*/
|
||||
void SynaReadBootloadID(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
|
||||
readRMI(SynaF34ReflashQuery_BootID, &uData[0], 2);
|
||||
SynaBootloadID = uData[0] + uData[1] * 0x100;
|
||||
}
|
||||
|
||||
/* SynaWriteBootloadID writes the bootloader ID to the F34 data register to unlock the reflash process
|
||||
*/
|
||||
void SynaWriteBootloadID(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
|
||||
uData[0] = SynaBootloadID % 0x100;
|
||||
uData[1] = SynaBootloadID / 0x100;
|
||||
|
||||
writeRMI(SynaF34Reflash_BlockData, &uData[0], 2);
|
||||
}
|
||||
|
||||
/* SynaEnableFlashing kicks off the reflash process
|
||||
*/
|
||||
void SynaEnableFlashing(void)
|
||||
{
|
||||
unsigned char uData = 0;
|
||||
unsigned char uStatus = 0;
|
||||
|
||||
printk("Enable Reflash...\n");
|
||||
|
||||
// Reflash is enabled by first reading the bootloader ID from the firmware and write it back
|
||||
SynaReadBootloadID();
|
||||
SynaWriteBootloadID();
|
||||
// Make sure Reflash is not already enabled
|
||||
do {
|
||||
readRMI(SynaF34_FlashControl, &uData, 1);
|
||||
} while (((uData & 0x0f) != 0x00));
|
||||
// Clear ATTN
|
||||
readRMI (SynaF01DataBase, &uStatus, 1);
|
||||
|
||||
if ((uStatus & 0x40) == 0)
|
||||
{
|
||||
// Write the "Enable Flash Programming command to F34 Control register
|
||||
// Wait for ATTN and then clear the ATTN.
|
||||
readRMI(SynaF34_FlashControl, &uData, 1);
|
||||
uData &= 0xf0;
|
||||
uData |= 0x0f;
|
||||
writeRMI(SynaF34_FlashControl, &uData, 1);
|
||||
SynaWaitForATTN();
|
||||
readRMI((SynaF01DataBase + 1), &uStatus, 1);
|
||||
|
||||
// Scan the PDT again to ensure all register offsets are correct
|
||||
SynaSetup();
|
||||
|
||||
// Read the "Program Enabled" bit of the F34 Control register, and proceed only if the
|
||||
// bit is set.
|
||||
readRMI(SynaF34_FlashControl, &uData, 1);
|
||||
|
||||
while (uData != 0x80)
|
||||
{
|
||||
// In practice, if uData!=0x80 happens for multiple counts, it indicates reflash
|
||||
// is failed to be enabled, and program should quit
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SynaWaitForATTN waits for ATTN to be asserted within a certain time threshold.
|
||||
*/
|
||||
void SynaWaitForATTN(void)
|
||||
{
|
||||
unsigned int error;
|
||||
|
||||
error = waitATTN(ASSERT, 300);
|
||||
}
|
||||
|
||||
/* SynaWaitATTN waits for ATTN to be asserted within a certain time threshold.
|
||||
* The function also checks for the F34 "Program Enabled" bit and clear ATTN accordingly.
|
||||
*/
|
||||
void SynaWaitATTN(void)
|
||||
{
|
||||
unsigned char uData = 0;
|
||||
unsigned char uStatus = 0;
|
||||
|
||||
waitATTN(ASSERT, 300);
|
||||
do {
|
||||
readRMI(SynaF34_FlashControl, &uData, 1);
|
||||
readRMI((SynaF01DataBase + 1), &uStatus, 1);
|
||||
} while (uData != 0x80);
|
||||
}
|
||||
|
||||
/* SynaProgramConfiguration writes the configuration section of the image block by block
|
||||
*/
|
||||
void SynaProgramConfiguration(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
unsigned char *puData = ConfigBlockData;
|
||||
unsigned short blockNum;
|
||||
|
||||
// eraseConfigBlock();
|
||||
for (blockNum = 0; blockNum < SynaConfigBlockCount; blockNum++)
|
||||
{
|
||||
uData[0] = blockNum & 0xff;
|
||||
uData[1] = (blockNum & 0xff00) >> 8;
|
||||
|
||||
//Block by blcok, write the block number and data to the corresponding F34 data registers
|
||||
writeRMI(SynaF34Reflash_BlockNum, &uData[0], 2);
|
||||
writeRMI(SynaF34Reflash_BlockData, puData, SynaConfigBlockSize);
|
||||
puData += SynaConfigBlockSize;
|
||||
|
||||
// Issue the "Write Configuration Block" command
|
||||
readRMI(SynaF34_FlashControl, &uData[0], 1);
|
||||
uData[0] &= 0xf0;
|
||||
uData[0] |= 0x06;
|
||||
writeRMI(SynaF34_FlashControl, &uData[0], 1);
|
||||
SynaWaitATTN();
|
||||
printk(".");
|
||||
}
|
||||
printk("\n");
|
||||
readRMI(SynaF01DataBase, uData, 1);
|
||||
printk("+++++++SynaProgramConfiguration++uData:%x+++++++%s:%d\n", uData[0], __func__, __LINE__);
|
||||
|
||||
}
|
||||
|
||||
/* SynaFinalizeReflash finalizes the reflash process
|
||||
*/
|
||||
void SynaFinalizeReflash(void)
|
||||
{
|
||||
unsigned char uData = 0;
|
||||
unsigned char uStatus = 0;
|
||||
|
||||
printk("Finalizing Reflash...\n");
|
||||
|
||||
// Issue the "Reset" command to F01 command register to reset the chip
|
||||
// This command will also test the new firmware image and check if its is valid
|
||||
uData = 1;
|
||||
writeRMI(SynaF01CommandBase, &uData, 1);
|
||||
msleep(100);
|
||||
//SynaWaitForATTN();
|
||||
readRMI(SynaF01DataBase, &uData, 1);
|
||||
printk("+++++++++uData:%x+++++++%s:%d\n", uData, __func__, __LINE__);
|
||||
// Sanity check that the reflash process is still enabled
|
||||
do {
|
||||
readRMI(SynaF34_FlashControl, &uStatus, 1);
|
||||
} while ((uStatus & 0x0f) != 0x00);
|
||||
printk("+++++++++uStatus:%x+++++++%s:%d\n", uStatus, __func__, __LINE__);
|
||||
readRMI((SynaF01DataBase + 1), &uStatus, 1);
|
||||
|
||||
SynaSetup();
|
||||
|
||||
uData = 0;
|
||||
// Check if the "Program Enabled" bit in F01 data register is cleared
|
||||
// Reflash is completed, and the image passes testing when the bit is cleared
|
||||
do {
|
||||
readRMI(SynaF01DataBase, &uData, 1);
|
||||
} while ((uData & 0x40) != 0);
|
||||
// Rescan PDT the update any changed register offsets
|
||||
SynaSetup();
|
||||
printk("\nReflash Completed. Please reboot.");
|
||||
}
|
||||
|
||||
/* SynaFlashFirmwareWrite writes the firmware section of the image block by block
|
||||
*/
|
||||
void SynaFlashFirmwareWrite(void)
|
||||
{
|
||||
unsigned char *puFirmwareData = (unsigned char *)(SynaFirmware+0x100); //SynafirmwareImgData; //hhb
|
||||
unsigned char uData[2];
|
||||
unsigned short blockNum;
|
||||
|
||||
for (blockNum = 0; blockNum < SynaFirmwareBlockCount; ++blockNum)
|
||||
{
|
||||
//Block by blcok, write the block number and data to the corresponding F34 data registers
|
||||
uData[0] = blockNum & 0xff;
|
||||
uData[1] = (blockNum & 0xff00) >> 8;
|
||||
writeRMI(SynaF34Reflash_BlockNum, &uData[0], 2);
|
||||
writeRMI(SynaF34Reflash_BlockData, puFirmwareData, SynaFirmwareBlockSize);
|
||||
puFirmwareData += SynaFirmwareBlockSize;
|
||||
// Issue the "Write Firmware Block" command
|
||||
readRMI(SynaF34_FlashControl, &uData[0], 1);
|
||||
uData[0] &= 0xf0;
|
||||
uData[0] |= 0x02;
|
||||
writeRMI(SynaF34_FlashControl, &uData[0], 1);
|
||||
if(blockNum % 128 == 0)
|
||||
printk(".");
|
||||
SynaWaitATTN();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* SynaProgramFirmware prepares the firmware writing process
|
||||
*/
|
||||
void SynaProgramFirmware(void)
|
||||
{
|
||||
unsigned char uData;
|
||||
|
||||
printk("Program Firmware Section...\n");
|
||||
|
||||
SynaReadBootloadID();
|
||||
SynaWriteBootloadID();
|
||||
readRMI(SynaF34_FlashControl, &uData, 1);
|
||||
uData &= 0xf0;
|
||||
uData |= 0x03;
|
||||
writeRMI(SynaF34_FlashControl, &uData, 1);
|
||||
msleep(5000);
|
||||
SynaWaitATTN();
|
||||
SynaFlashFirmwareWrite();
|
||||
readRMI(SynaF01DataBase, &uData, 1);
|
||||
printk("+++++++SynaProgramFirmware++uData:%x+++++++%s:%d\n", uData, __func__, __LINE__);
|
||||
}
|
||||
|
||||
/* SynaBootloaderLock locks down the bootloader
|
||||
*/
|
||||
void SynaBootloaderLock(void)
|
||||
{
|
||||
unsigned short lockBlockCount;
|
||||
unsigned char *puFirmwareData = SynalockImgData;
|
||||
unsigned char uData[2];
|
||||
unsigned short uBlockNum;
|
||||
|
||||
// Check if device is in unlocked state
|
||||
readRMI((SynaF34QueryBase+ 2), &uData[0], 1);
|
||||
|
||||
//Device is unlocked
|
||||
if (uData[0] & 0x02)
|
||||
{
|
||||
printk("Device unlocked. Lock it first...\n");
|
||||
// Different bootloader version has different block count for the lockdown data
|
||||
// Need to check the bootloader version from the image file being reflashed
|
||||
switch (SynafirmwareImgVersion)
|
||||
{
|
||||
case 2:
|
||||
lockBlockCount = 3;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
lockBlockCount = 4;
|
||||
break;
|
||||
case 5:
|
||||
lockBlockCount = 5;
|
||||
break;
|
||||
default:
|
||||
lockBlockCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the lockdown info block by block
|
||||
// This reference code of lockdown process does not check for bootloader version
|
||||
// currently programmed on the ASIC against the bootloader version of the image to
|
||||
// be reflashed. Such case should not happen in practice. Reflashing cross different
|
||||
// bootloader versions is not supported.
|
||||
for (uBlockNum = 0; uBlockNum < lockBlockCount; ++uBlockNum)
|
||||
{
|
||||
uData[0] = uBlockNum & 0xff;
|
||||
uData[1] = (uBlockNum & 0xff00) >> 8;
|
||||
|
||||
/* Write Block Number */
|
||||
readRMI(SynaF34Reflash_BlockNum, &uData[0], 2);
|
||||
|
||||
/* Write Data Block */
|
||||
writeRMI(SynaF34Reflash_BlockData, puFirmwareData, SynaFirmwareBlockSize);
|
||||
|
||||
/* Move to next data block */
|
||||
puFirmwareData += SynaFirmwareBlockSize;
|
||||
|
||||
/* Issue Write Lockdown Block command */
|
||||
readRMI(SynaF34_FlashControl, &uData[0], 1);
|
||||
uData[0] &= 0xf0;
|
||||
uData[0] |= 0x04;
|
||||
writeRMI(SynaF34_FlashControl, &uData[0], 1);
|
||||
|
||||
/* Wait ATTN until device is done writing the block and is ready for the next. */
|
||||
SynaWaitATTN();
|
||||
}
|
||||
printk("Device locking done.\n");
|
||||
|
||||
// Enable reflash again to finish the lockdown process.
|
||||
// Since this lockdown process is part of the reflash process, we are enabling
|
||||
// reflash instead, rather than resetting the device to finish the unlock procedure.
|
||||
SynaEnableFlashing();
|
||||
}
|
||||
else printk("Device already locked.\n");
|
||||
}
|
||||
|
||||
/* ConfigBlockReflash reflashes the config block only
|
||||
*/
|
||||
void ConfigBlockReflash(void)
|
||||
{
|
||||
unsigned char uData[2];
|
||||
|
||||
convertConfigBlockData();
|
||||
|
||||
SynaInitialize();
|
||||
|
||||
SynaReadConfigInfo();
|
||||
|
||||
SynaReadFirmwareInfo();
|
||||
|
||||
SynaF34_FlashControl = SynaF34DataBase + SynaFirmwareBlockSize + 2;
|
||||
|
||||
SynaEnableFlashing();
|
||||
|
||||
SynaBootloaderLock();
|
||||
|
||||
// Check if device is in unlocked state
|
||||
readRMI((SynaF34QueryBase + 2), &uData[0], 1);
|
||||
|
||||
//Device is unlocked
|
||||
if (uData[0] & 0x02)
|
||||
{
|
||||
SynaFinalizeReflash();
|
||||
return;
|
||||
// Do not reflash config block if not locked.
|
||||
}
|
||||
|
||||
eraseConfigBlock();
|
||||
SynaconfigImgData = (unsigned char *)ConfigBlock;
|
||||
|
||||
SynaProgramConfiguration();
|
||||
|
||||
SynaFinalizeReflash();
|
||||
}
|
||||
|
||||
/* eraseConfigBlock erases the config block
|
||||
*/
|
||||
void eraseConfigBlock(void)
|
||||
{
|
||||
unsigned char uData;
|
||||
|
||||
// Erase of config block is done by first entering into bootloader mode
|
||||
SynaReadBootloadID();
|
||||
SynaWriteBootloadID();
|
||||
|
||||
// Command 7 to erase config block
|
||||
readRMI(SynaF34_FlashControl, &uData, 1);
|
||||
uData &= 0xf0;
|
||||
uData |= 0x07;
|
||||
writeRMI(SynaF34_FlashControl, &uData, 1);
|
||||
|
||||
SynaWaitATTN();
|
||||
}
|
||||
|
||||
// This function is intended to convert the config data struct output by DS4 (read config.h) into an array that
|
||||
// the reflash code uses (read SynaFirmwareImage.h)
|
||||
// DS4 will output the array format in the next release and this function will not be necessary
|
||||
void convertConfigBlockData(void)
|
||||
{
|
||||
#if 0
|
||||
int i = 0;
|
||||
char value[32]; //hhb
|
||||
for (i = 0; value[i]!=NULL; i++)
|
||||
{
|
||||
ConfigBlock[i] = value[i].Value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function is to check the touch controller type of the touch controller matches with the firmware image
|
||||
int TouchControllerTypeCheck(void)
|
||||
{
|
||||
unsigned char uData[4];
|
||||
char buffer[4];
|
||||
|
||||
char controllerType[20];
|
||||
|
||||
int ID;
|
||||
//int revision;
|
||||
|
||||
readRMI((SynaF01QueryBase + 43), &uData[0], 1);
|
||||
if ((uData[0] & 0x0f) > 0)
|
||||
{
|
||||
readRMI((SynaF01QueryBase + 44), &uData[0], 1);
|
||||
if (uData[0] & 0x01)
|
||||
{
|
||||
readRMI((SynaF01QueryBase + 17), &uData[0], 2);
|
||||
|
||||
ID = (int)(uData[1] & (uData[0] << 8));
|
||||
sprintf(buffer, "%d", ID);
|
||||
if (strstr(controllerType, TOUCH_CONTROLLER) != 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int fimrwareRevisionCheck(void)
|
||||
{
|
||||
unsigned char uData[10];
|
||||
readRMI((SynaF01QueryBase + 11), uData, 10);
|
||||
return strcmp(uData, FW_REVISION);
|
||||
}
|
||||
|
||||
2916
drivers/input/touchscreen/rmi4/rmi_reflash.h
Normal file
2916
drivers/input/touchscreen/rmi4/rmi_reflash.h
Normal file
File diff suppressed because it is too large
Load Diff
909
drivers/input/touchscreen/rmi4/rmi_spi.c
Executable file
909
drivers/input/touchscreen/rmi4/rmi_spi.c
Executable file
|
|
@ -0,0 +1,909 @@
|
|||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/rmi.h>
|
||||
|
||||
#define COMMS_DEBUG 0
|
||||
#define FF_DEBUG 0
|
||||
|
||||
#define RMI_PROTOCOL_VERSION_ADDRESS 0xa0fd
|
||||
#define SPI_V2_UNIFIED_READ 0xc0
|
||||
#define SPI_V2_WRITE 0x40
|
||||
#define SPI_V2_PREPARE_SPLIT_READ 0xc8
|
||||
#define SPI_V2_EXECUTE_SPLIT_READ 0xca
|
||||
|
||||
#define RMI_SPI_BLOCK_DELAY_US 65
|
||||
#define RMI_SPI_BYTE_DELAY_US 65
|
||||
#define RMI_SPI_WRITE_DELAY_US 0
|
||||
|
||||
#define RMI_V1_READ_FLAG 0x80
|
||||
|
||||
#define RMI_PAGE_SELECT_REGISTER 0x00FF
|
||||
#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80)
|
||||
|
||||
#define DEFAULT_POLL_INTERVAL_MS 13
|
||||
|
||||
static char *spi_v1_proto_name = "spi";
|
||||
static char *spi_v2_proto_name = "spiv2";
|
||||
|
||||
struct rmi_spi_data {
|
||||
struct mutex page_mutex;
|
||||
int page;
|
||||
int (*set_page) (struct rmi_phys_device *phys, u8 page);
|
||||
bool split_read_pending;
|
||||
int enabled;
|
||||
int irq;
|
||||
int irq_flags;
|
||||
struct rmi_phys_device *phys;
|
||||
struct completion irq_comp;
|
||||
|
||||
/* Following are used when polling. */
|
||||
struct hrtimer poll_timer;
|
||||
struct work_struct poll_work;
|
||||
int poll_interval;
|
||||
|
||||
};
|
||||
|
||||
static irqreturn_t rmi_spi_hard_irq(int irq, void *p)
|
||||
{
|
||||
struct rmi_phys_device *phys = p;
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
struct rmi_device_platform_data *pdata = phys->dev->platform_data;
|
||||
|
||||
if (data->split_read_pending &&
|
||||
gpio_get_value(pdata->attn_gpio) ==
|
||||
pdata->attn_polarity) {
|
||||
phys->info.attn_count++;
|
||||
complete(&data->irq_comp);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t rmi_spi_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 (gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity) {
|
||||
phys->info.attn_count++;
|
||||
if (driver && driver->irq_handler)
|
||||
driver->irq_handler(rmi_dev, irq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void spi_poll_work(struct work_struct *work)
|
||||
{
|
||||
struct rmi_spi_data *data =
|
||||
container_of(work, struct rmi_spi_data, poll_work);
|
||||
struct rmi_device *rmi_dev = data->phys->rmi_dev;
|
||||
struct rmi_driver *driver = rmi_dev->driver;
|
||||
|
||||
if (driver && driver->irq_handler)
|
||||
driver->irq_handler(rmi_dev, 0);
|
||||
}
|
||||
|
||||
/* This is the timer function for polling - it simply has to schedule work
|
||||
* and restart the timer. */
|
||||
static enum hrtimer_restart spi_poll_timer(struct hrtimer *timer)
|
||||
{
|
||||
struct rmi_spi_data *data =
|
||||
container_of(timer, struct rmi_spi_data, poll_timer);
|
||||
|
||||
if (!work_pending(&data->poll_work))
|
||||
schedule_work(&data->poll_work);
|
||||
hrtimer_start(&data->poll_timer, ktime_set(0, data->poll_interval),
|
||||
HRTIMER_MODE_REL);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int rmi_spi_xfer(struct rmi_phys_device *phys,
|
||||
const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx)
|
||||
{
|
||||
struct spi_device *client = to_spi_device(phys->dev);
|
||||
struct rmi_spi_data *v2_data = phys->data;
|
||||
struct rmi_device_platform_data *pdata = phys->dev->platform_data;
|
||||
int status;
|
||||
struct spi_message message;
|
||||
struct spi_transfer *xfers;
|
||||
int total_bytes = n_tx + n_rx;
|
||||
u8 local_buf[total_bytes];
|
||||
int xfer_count = 0;
|
||||
int xfer_index = 0;
|
||||
int block_delay = n_rx > 0 ? pdata->spi_data.block_delay_us : 0;
|
||||
int byte_delay = n_rx > 1 ? pdata->spi_data.read_delay_us : 0;
|
||||
int write_delay = n_tx > 1 ? pdata->spi_data.write_delay_us : 0;
|
||||
#if FF_DEBUG
|
||||
bool bad_data = true;
|
||||
#endif
|
||||
#if COMMS_DEBUG || FF_DEBUG
|
||||
int i;
|
||||
#endif
|
||||
|
||||
if (v2_data->split_read_pending) {
|
||||
block_delay =
|
||||
n_rx > 0 ? pdata->spi_data.split_read_block_delay_us : 0;
|
||||
byte_delay =
|
||||
n_rx > 1 ? pdata->spi_data.split_read_byte_delay_us : 0;
|
||||
write_delay = 0;
|
||||
}
|
||||
|
||||
if (n_tx) {
|
||||
phys->info.tx_count++;
|
||||
phys->info.tx_bytes += n_tx;
|
||||
if (write_delay)
|
||||
xfer_count += n_tx;
|
||||
else
|
||||
xfer_count += 1;
|
||||
}
|
||||
|
||||
if (n_rx) {
|
||||
phys->info.rx_count++;
|
||||
phys->info.rx_bytes += n_rx;
|
||||
if (byte_delay)
|
||||
xfer_count += n_rx;
|
||||
else
|
||||
xfer_count += 1;
|
||||
}
|
||||
|
||||
xfers = kcalloc(xfer_count,
|
||||
sizeof(struct spi_transfer), GFP_KERNEL);
|
||||
if (!xfers)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&message);
|
||||
|
||||
if (n_tx) {
|
||||
if (write_delay) {
|
||||
for (xfer_index = 0; xfer_index < n_tx;
|
||||
xfer_index++) {
|
||||
memset(&xfers[xfer_index], 0,
|
||||
sizeof(struct spi_transfer));
|
||||
xfers[xfer_index].len = 1;
|
||||
xfers[xfer_index].delay_usecs = write_delay;
|
||||
xfers[xfer_index].tx_buf = txbuf + xfer_index;
|
||||
spi_message_add_tail(&xfers[xfer_index],
|
||||
&message);
|
||||
}
|
||||
} else {
|
||||
memset(&xfers[0], 0, sizeof(struct spi_transfer));
|
||||
xfers[0].len = n_tx;
|
||||
spi_message_add_tail(&xfers[0], &message);
|
||||
memcpy(local_buf, txbuf, n_tx);
|
||||
xfers[0].tx_buf = local_buf;
|
||||
xfer_index++;
|
||||
}
|
||||
if (block_delay)
|
||||
xfers[xfer_index-1].delay_usecs = block_delay;
|
||||
}
|
||||
if (n_rx) {
|
||||
if (byte_delay) {
|
||||
int buffer_offset = n_tx;
|
||||
for (; xfer_index < xfer_count; xfer_index++) {
|
||||
memset(&xfers[xfer_index], 0,
|
||||
sizeof(struct spi_transfer));
|
||||
xfers[xfer_index].len = 1;
|
||||
xfers[xfer_index].delay_usecs = byte_delay;
|
||||
xfers[xfer_index].rx_buf =
|
||||
local_buf + buffer_offset;
|
||||
buffer_offset++;
|
||||
spi_message_add_tail(&xfers[xfer_index],
|
||||
&message);
|
||||
}
|
||||
} else {
|
||||
memset(&xfers[xfer_index], 0,
|
||||
sizeof(struct spi_transfer));
|
||||
xfers[xfer_index].len = n_rx;
|
||||
xfers[xfer_index].rx_buf = local_buf + n_tx;
|
||||
spi_message_add_tail(&xfers[xfer_index], &message);
|
||||
xfer_index++;
|
||||
}
|
||||
}
|
||||
|
||||
#if COMMS_DEBUG
|
||||
if (n_tx) {
|
||||
dev_dbg(&client->dev, "SPI sends %d bytes: ", n_tx);
|
||||
for (i = 0; i < n_tx; i++)
|
||||
pr_info("%02X ", txbuf[i]);
|
||||
pr_info("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* do the i/o */
|
||||
if (pdata->spi_data.cs_assert) {
|
||||
status = pdata->spi_data.cs_assert(
|
||||
pdata->spi_data.cs_assert_data, true);
|
||||
if (status) {
|
||||
dev_err(phys->dev, "Failed to assert CS, code %d.\n",
|
||||
status);
|
||||
/* nonzero means error */
|
||||
status = -1;
|
||||
goto error_exit;
|
||||
} else
|
||||
status = 0;
|
||||
}
|
||||
|
||||
if (pdata->spi_data.pre_delay_us)
|
||||
udelay(pdata->spi_data.pre_delay_us);
|
||||
|
||||
status = spi_sync(client, &message);
|
||||
|
||||
if (pdata->spi_data.post_delay_us)
|
||||
udelay(pdata->spi_data.post_delay_us);
|
||||
|
||||
if (pdata->spi_data.cs_assert) {
|
||||
status = pdata->spi_data.cs_assert(
|
||||
pdata->spi_data.cs_assert_data, false);
|
||||
if (status) {
|
||||
dev_err(phys->dev, "Failed to deassert CS. code %d.\n",
|
||||
status);
|
||||
/* nonzero means error */
|
||||
status = -1;
|
||||
goto error_exit;
|
||||
} else
|
||||
status = 0;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
memcpy(rxbuf, local_buf + n_tx, n_rx);
|
||||
status = message.status;
|
||||
} else {
|
||||
if (n_tx) phys->info.tx_errs++;
|
||||
if (n_rx) phys->info.rx_errs++;
|
||||
dev_err(phys->dev, "spi_sync failed with error code %d.",
|
||||
status);
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
#if COMMS_DEBUG
|
||||
if (n_rx) {
|
||||
dev_dbg(&client->dev, "SPI received %d bytes: ", n_rx);
|
||||
for (i = 0; i < n_rx; i++)
|
||||
pr_info("%02X ", rxbuf[i]);
|
||||
pr_info("\n");
|
||||
}
|
||||
#endif
|
||||
#if FF_DEBUG
|
||||
if (n_rx) {
|
||||
for (i = 0; i < n_rx; i++) {
|
||||
if (rxbuf[i] != 0xFF) {
|
||||
bad_data = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bad_data) {
|
||||
phys->info.rx_errs++;
|
||||
dev_err(phys->dev, "BAD READ %lu out of %lu.\n",
|
||||
phys->info.rx_errs, phys->info.rx_count);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
error_exit:
|
||||
kfree(xfers);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int rmi_spi_v2_write_block(struct rmi_phys_device *phys, u16 addr,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
u8 txbuf[len + 4];
|
||||
int error;
|
||||
|
||||
txbuf[0] = SPI_V2_WRITE;
|
||||
txbuf[1] = (addr >> 8) & 0x00FF;
|
||||
txbuf[2] = addr & 0x00FF;
|
||||
txbuf[3] = len;
|
||||
|
||||
memcpy(&txbuf[4], buf, len);
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_SPI_PAGE(addr) != data->page) {
|
||||
error = data->set_page(phys, RMI_SPI_PAGE(addr));
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
error = rmi_spi_xfer(phys, buf, len + 4, NULL, 0);
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
error = len;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v2_write(struct rmi_phys_device *phys, u16 addr, u8 data)
|
||||
{
|
||||
int error = rmi_spi_v2_write_block(phys, addr, &data, 1);
|
||||
|
||||
return (error == 1) ? 0 : error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v1_write_block(struct rmi_phys_device *phys, u16 addr,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
unsigned char txbuf[len + 2];
|
||||
int error;
|
||||
|
||||
txbuf[0] = (addr >> 8) & ~RMI_V1_READ_FLAG;
|
||||
txbuf[1] = addr;
|
||||
memcpy(txbuf+2, buf, len);
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_SPI_PAGE(addr) != data->page) {
|
||||
error = data->set_page(phys, RMI_SPI_PAGE(addr));
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, len + 2, NULL, 0);
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
error = len;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v1_write(struct rmi_phys_device *phys, u16 addr, u8 data)
|
||||
{
|
||||
int error = rmi_spi_v1_write_block(phys, addr, &data, 1);
|
||||
|
||||
return (error == 1) ? 0 : error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v2_split_read_block(struct rmi_phys_device *phys, u16 addr,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
u8 txbuf[4];
|
||||
u8 rxbuf[len + 1]; /* one extra byte for read length */
|
||||
int error;
|
||||
|
||||
txbuf[0] = SPI_V2_PREPARE_SPLIT_READ;
|
||||
txbuf[1] = (addr >> 8) & 0x00FF;
|
||||
txbuf[2] = addr & 0x00ff;
|
||||
txbuf[3] = len;
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_SPI_PAGE(addr) != data->page) {
|
||||
error = data->set_page(phys, RMI_SPI_PAGE(addr));
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data->split_read_pending = true;
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, 4, NULL, 0);
|
||||
if (error < 0) {
|
||||
data->split_read_pending = false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wait_for_completion(&data->irq_comp);
|
||||
|
||||
txbuf[0] = SPI_V2_EXECUTE_SPLIT_READ;
|
||||
txbuf[1] = 0;
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, 2, rxbuf, len + 1);
|
||||
data->split_read_pending = false;
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
|
||||
/* first byte is length */
|
||||
if (rxbuf[0] != len) {
|
||||
error = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy(buf, rxbuf + 1, len);
|
||||
error = len;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v2_read_block(struct rmi_phys_device *phys, u16 addr,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
u8 txbuf[4];
|
||||
int error;
|
||||
|
||||
txbuf[0] = SPI_V2_UNIFIED_READ;
|
||||
txbuf[1] = (addr >> 8) & 0x00FF;
|
||||
txbuf[2] = addr & 0x00ff;
|
||||
txbuf[3] = len;
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_SPI_PAGE(addr) != data->page) {
|
||||
error = data->set_page(phys, RMI_SPI_PAGE(addr));
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, 4, buf, len);
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
error = len;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v2_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
|
||||
{
|
||||
int error = rmi_spi_v2_read_block(phys, addr, buf, 1);
|
||||
|
||||
return (error == 1) ? 0 : error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v1_read_block(struct rmi_phys_device *phys, u16 addr,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
u8 txbuf[2];
|
||||
int error;
|
||||
|
||||
txbuf[0] = (addr >> 8) | RMI_V1_READ_FLAG;
|
||||
txbuf[1] = addr;
|
||||
|
||||
mutex_lock(&data->page_mutex);
|
||||
|
||||
if (RMI_SPI_PAGE(addr) != data->page) {
|
||||
error = data->set_page(phys, RMI_SPI_PAGE(addr));
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, 2, buf, len);
|
||||
if (error < 0)
|
||||
goto exit;
|
||||
error = len;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&data->page_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rmi_spi_v1_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
|
||||
{
|
||||
int error = rmi_spi_v1_read_block(phys, addr, buf, 1);
|
||||
|
||||
return (error == 1) ? 0 : error;
|
||||
}
|
||||
|
||||
#define RMI_SPI_PAGE_SELECT_WRITE_LENGTH 1
|
||||
|
||||
static int rmi_spi_v1_set_page(struct rmi_phys_device *phys, u8 page)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
u8 txbuf[] = {RMI_PAGE_SELECT_REGISTER >> 8,
|
||||
RMI_PAGE_SELECT_REGISTER & 0xFF, page};
|
||||
int error;
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0);
|
||||
if (error < 0) {
|
||||
dev_err(phys->dev, "Failed to set page select, code: %d.\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
data->page = page;
|
||||
|
||||
return RMI_SPI_PAGE_SELECT_WRITE_LENGTH;
|
||||
}
|
||||
|
||||
static int rmi_spi_v2_set_page(struct rmi_phys_device *phys, u8 page)
|
||||
{
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
u8 txbuf[] = {SPI_V2_WRITE, RMI_PAGE_SELECT_REGISTER >> 8,
|
||||
RMI_PAGE_SELECT_REGISTER & 0xFF,
|
||||
RMI_SPI_PAGE_SELECT_WRITE_LENGTH, page};
|
||||
int error;
|
||||
|
||||
error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0);
|
||||
if (error < 0) {
|
||||
dev_err(phys->dev, "Failed to set page select, code: %d.\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
data->page = page;
|
||||
|
||||
return RMI_SPI_PAGE_SELECT_WRITE_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
static int acquire_attn_irq(struct rmi_spi_data *data)
|
||||
{
|
||||
int retval;
|
||||
struct rmi_phys_device *rmi_phys = data->phys;
|
||||
|
||||
retval = request_threaded_irq(data->irq, rmi_spi_hard_irq,
|
||||
rmi_spi_irq_thread, data->irq_flags,
|
||||
dev_name(rmi_phys->dev), rmi_phys);
|
||||
if (retval < 0) {
|
||||
dev_err(&(rmi_phys->rmi_dev->dev), "request_threaded_irq "
|
||||
"failed, code: %d.\n", retval);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int setup_attn(struct rmi_spi_data *data)
|
||||
{
|
||||
int retval;
|
||||
struct rmi_phys_device *rmi_phys = data->phys;
|
||||
struct rmi_device_platform_data *pdata = rmi_phys->dev->platform_data;
|
||||
|
||||
retval = acquire_attn_irq(data);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
#if defined(CONFIG_RMI4_DEV)
|
||||
retval = gpio_export(pdata->attn_gpio, false);
|
||||
if (retval) {
|
||||
dev_warn(&(rmi_phys->rmi_dev->dev),
|
||||
"WARNING: Failed to export ATTN gpio!\n");
|
||||
retval = 0;
|
||||
} else {
|
||||
retval = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
|
||||
pdata->attn_gpio);
|
||||
if (retval) {
|
||||
dev_warn(&(rmi_phys->rmi_dev->dev), "WARNING: "
|
||||
"Failed to symlink ATTN gpio!\n");
|
||||
retval = 0;
|
||||
} else {
|
||||
dev_info(&(rmi_phys->rmi_dev->dev),
|
||||
"%s: Exported GPIO %d.", __func__,
|
||||
pdata->attn_gpio);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_RMI4_DEV */
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int setup_polling(struct rmi_spi_data *data)
|
||||
{
|
||||
INIT_WORK(&data->poll_work, spi_poll_work);
|
||||
hrtimer_init(&data->poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
data->poll_timer.function = spi_poll_timer;
|
||||
hrtimer_start(&data->poll_timer, ktime_set(1, 0), HRTIMER_MODE_REL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_device(struct rmi_phys_device *phys)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
struct rmi_spi_data *data = phys->data;
|
||||
|
||||
if (data->enabled) {
|
||||
dev_dbg(phys->dev, "Physical device already enabled.\n");
|
||||
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_spi_data *data = phys->data;
|
||||
|
||||
if (!data->enabled) {
|
||||
dev_warn(phys->dev, "Physical device already disabled.\n");
|
||||
return;
|
||||
}
|
||||
disable_irq(data->irq);
|
||||
free_irq(data->irq, data->phys);
|
||||
|
||||
dev_dbg(phys->dev, "Physical device disabled.\n");
|
||||
data->enabled = false;
|
||||
}
|
||||
|
||||
#define DUMMY_READ_SLEEP_US 10
|
||||
|
||||
static int rmi_spi_check_device(struct rmi_phys_device *rmi_phys)
|
||||
{
|
||||
u8 buf[6];
|
||||
int error;
|
||||
int i;
|
||||
|
||||
/* Some SPI subsystems return 0 for the very first read you do. So
|
||||
* we use this dummy read to get that out of the way.
|
||||
*/
|
||||
error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION,
|
||||
buf, sizeof(buf));
|
||||
if (error < 0) {
|
||||
dev_err(rmi_phys->dev, "dummy read failed with %d.\n", error);
|
||||
return error;
|
||||
}
|
||||
udelay(DUMMY_READ_SLEEP_US);
|
||||
|
||||
/* Force page select to 0.
|
||||
*/
|
||||
error = rmi_spi_v1_set_page(rmi_phys, 0x00);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* Now read the first PDT entry. We know where this is, and if the
|
||||
* RMI4 device is out there, these 6 bytes will be something other
|
||||
* than all 0x00 or 0xFF. We need to check for 0x00 and 0xFF,
|
||||
* because many (maybe all) SPI implementations will return all 0x00
|
||||
* or all 0xFF on read if the device is not connected.
|
||||
*/
|
||||
error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION,
|
||||
buf, sizeof(buf));
|
||||
if (error < 0) {
|
||||
dev_err(rmi_phys->dev, "probe read failed with %d.\n", error);
|
||||
return error;
|
||||
}
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
if (buf[i] != 0x00 && buf[i] != 0xFF)
|
||||
return error;
|
||||
}
|
||||
|
||||
dev_err(rmi_phys->dev, "probe read returned invalid block.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static int __devinit rmi_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct rmi_phys_device *rmi_phys;
|
||||
struct rmi_spi_data *data;
|
||||
struct rmi_device_platform_data *pdata = spi->dev.platform_data;
|
||||
u8 buf[2];
|
||||
int retval;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&spi->dev, "no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX)
|
||||
return -EINVAL;
|
||||
|
||||
spi->bits_per_word = 8;
|
||||
spi->mode = SPI_MODE_3;
|
||||
retval = spi_setup(spi);
|
||||
if (retval < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed!\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
|
||||
if (!rmi_phys)
|
||||
return -ENOMEM;
|
||||
|
||||
data = kzalloc(sizeof(struct rmi_spi_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
retval = -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 = &spi->dev;
|
||||
|
||||
rmi_phys->write = rmi_spi_v1_write;
|
||||
rmi_phys->write_block = rmi_spi_v1_write_block;
|
||||
rmi_phys->read = rmi_spi_v1_read;
|
||||
rmi_phys->read_block = rmi_spi_v1_read_block;
|
||||
rmi_phys->enable_device = enable_device;
|
||||
rmi_phys->disable_device = disable_device;
|
||||
data->set_page = rmi_spi_v1_set_page;
|
||||
|
||||
rmi_phys->info.proto = spi_v1_proto_name;
|
||||
|
||||
mutex_init(&data->page_mutex);
|
||||
|
||||
dev_set_drvdata(&spi->dev, rmi_phys);
|
||||
|
||||
pdata->spi_data.block_delay_us = pdata->spi_data.block_delay_us ?
|
||||
pdata->spi_data.block_delay_us : RMI_SPI_BLOCK_DELAY_US;
|
||||
pdata->spi_data.read_delay_us = pdata->spi_data.read_delay_us ?
|
||||
pdata->spi_data.read_delay_us : RMI_SPI_BYTE_DELAY_US;
|
||||
pdata->spi_data.write_delay_us = pdata->spi_data.write_delay_us ?
|
||||
pdata->spi_data.write_delay_us : RMI_SPI_BYTE_DELAY_US;
|
||||
pdata->spi_data.split_read_block_delay_us =
|
||||
pdata->spi_data.split_read_block_delay_us ?
|
||||
pdata->spi_data.split_read_block_delay_us :
|
||||
RMI_SPI_BLOCK_DELAY_US;
|
||||
pdata->spi_data.split_read_byte_delay_us =
|
||||
pdata->spi_data.split_read_byte_delay_us ?
|
||||
pdata->spi_data.split_read_byte_delay_us :
|
||||
RMI_SPI_BYTE_DELAY_US;
|
||||
|
||||
if (pdata->gpio_config) {
|
||||
retval = pdata->gpio_config(pdata->gpio_data, true);
|
||||
if (retval < 0) {
|
||||
dev_err(&spi->dev, "Failed to setup GPIOs, code: %d.\n",
|
||||
retval);
|
||||
goto err_data;
|
||||
}
|
||||
}
|
||||
|
||||
retval = rmi_spi_check_device(rmi_phys);
|
||||
if (retval < 0)
|
||||
goto err_gpio;
|
||||
|
||||
/* check if this is an SPI v2 device */
|
||||
retval = rmi_spi_v1_read_block(rmi_phys, RMI_PROTOCOL_VERSION_ADDRESS,
|
||||
buf, 2);
|
||||
if (retval < 0) {
|
||||
dev_err(&spi->dev, "failed to get SPI version number!\n");
|
||||
goto err_gpio;
|
||||
}
|
||||
dev_dbg(&spi->dev, "SPI version is %d", buf[0]);
|
||||
|
||||
if (buf[0] == 1) {
|
||||
/* SPIv2 */
|
||||
rmi_phys->write = rmi_spi_v2_write;
|
||||
rmi_phys->write_block = rmi_spi_v2_write_block;
|
||||
rmi_phys->read = rmi_spi_v2_read;
|
||||
data->set_page = rmi_spi_v2_set_page;
|
||||
|
||||
rmi_phys->info.proto = spi_v2_proto_name;
|
||||
|
||||
if (pdata->attn_gpio > 0) {
|
||||
init_completion(&data->irq_comp);
|
||||
rmi_phys->read_block = rmi_spi_v2_split_read_block;
|
||||
} else {
|
||||
dev_warn(&spi->dev, "WARNING: SPI V2 detected, but no "
|
||||
"attention GPIO was specified. This is unlikely"
|
||||
" to work well.\n");
|
||||
rmi_phys->read_block = rmi_spi_v2_read_block;
|
||||
}
|
||||
} else if (buf[0] != 0) {
|
||||
dev_err(&spi->dev, "Unrecognized SPI version %d.\n", buf[0]);
|
||||
retval = -ENODEV;
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
retval = rmi_register_phys_device(rmi_phys);
|
||||
if (retval) {
|
||||
dev_err(&spi->dev, "failed to register physical driver\n");
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
if (pdata->attn_gpio > 0) {
|
||||
retval = setup_attn(data);
|
||||
if (retval < 0)
|
||||
goto err_unregister;
|
||||
} else {
|
||||
retval = setup_polling(data);
|
||||
if (retval < 0)
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "registered RMI SPI driver\n");
|
||||
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 retval;
|
||||
}
|
||||
|
||||
static int __devexit rmi_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct rmi_phys_device *phys = dev_get_drvdata(&spi->dev);
|
||||
struct rmi_device_platform_data *pd = spi->dev.platform_data;
|
||||
|
||||
disable_device(phys);
|
||||
rmi_unregister_phys_device(phys);
|
||||
kfree(phys->data);
|
||||
kfree(phys);
|
||||
|
||||
if (pd->gpio_config)
|
||||
pd->gpio_config(pdata->gpio_data, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id rmi_id[] = {
|
||||
{ "rmi", 0 },
|
||||
{ "rmi_spi", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, rmi_id);
|
||||
|
||||
static struct spi_driver rmi_spi_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "rmi_spi",
|
||||
},
|
||||
.id_table = rmi_id,
|
||||
.probe = rmi_spi_probe,
|
||||
.remove = __devexit_p(rmi_spi_remove),
|
||||
};
|
||||
|
||||
static int __init rmi_spi_init(void)
|
||||
{
|
||||
return spi_register_driver(&rmi_spi_driver);
|
||||
}
|
||||
|
||||
static void __exit rmi_spi_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&rmi_spi_driver);
|
||||
}
|
||||
|
||||
module_init(rmi_spi_init);
|
||||
module_exit(rmi_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
|
||||
MODULE_DESCRIPTION("RMI SPI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(RMI_DRIVER_VERSION);
|
||||
609
include/linux/rmi.h
Executable file
609
include/linux/rmi.h
Executable file
|
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* 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_H
|
||||
#define _RMI_H
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#ifdef CONFIG_RMI_DEBUG
|
||||
#include <linux/debugfs.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
#include <linux/earlysuspend.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* Permissions for sysfs attributes. Since the permissions policy will change
|
||||
* on a global basis in the future, rather than edit all sysfs attrs everywhere
|
||||
* in the driver (and risk screwing that up in the process), we use this handy
|
||||
* set of #defines. That way when we change the policy for sysfs permissions,
|
||||
* we only need to change them here.
|
||||
*/
|
||||
#define RMI_RO_ATTR S_IRUGO
|
||||
#define RMI_RW_ATTR (S_IRUGO | S_IWUGO)
|
||||
#define RMI_WO_ATTR S_IWUGO
|
||||
|
||||
#define PDT_START_SCAN_LOCATION 0x00e9
|
||||
|
||||
enum rmi_attn_polarity {
|
||||
RMI_ATTN_ACTIVE_LOW = 0,
|
||||
RMI_ATTN_ACTIVE_HIGH = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_f11_axis_alignment - target axis alignment
|
||||
* @swap_axes: set to TRUE if desired to swap x- and y-axis
|
||||
* @flip_x: set to TRUE if desired to flip direction on x-axis
|
||||
* @flip_y: set to TRUE if desired to flip direction on y-axis
|
||||
*/
|
||||
struct rmi_f11_2d_axis_alignment {
|
||||
bool swap_axes;
|
||||
bool flip_x;
|
||||
bool flip_y;
|
||||
int clip_X_low;
|
||||
int clip_Y_low;
|
||||
int clip_X_high;
|
||||
int clip_Y_high;
|
||||
int offset_X;
|
||||
int offset_Y;
|
||||
int rel_report_enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_f01_power - override default power management settings.
|
||||
*
|
||||
*/
|
||||
enum rmi_f01_nosleep {
|
||||
RMI_F01_NOSLEEP_DEFAULT = 0,
|
||||
RMI_F01_NOSLEEP_OFF = 1,
|
||||
RMI_F01_NOSLEEP_ON = 2
|
||||
};
|
||||
|
||||
struct rmi_f01_power_management {
|
||||
enum rmi_f01_nosleep nosleep;
|
||||
u8 wakeup_threshold;
|
||||
u8 doze_holdoff;
|
||||
u8 doze_interval;
|
||||
};
|
||||
|
||||
struct rmi_f19_button_map {
|
||||
unsigned char nbuttons;
|
||||
unsigned char *map;
|
||||
};
|
||||
|
||||
struct rmi_f1a_button_map {
|
||||
unsigned char nbuttons;
|
||||
unsigned char *map;
|
||||
};
|
||||
|
||||
struct virtualbutton_map {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u16 code;
|
||||
};
|
||||
|
||||
struct rmi_f11_virtualbutton_map {
|
||||
u8 buttons;
|
||||
struct virtualbutton_map *map;
|
||||
};
|
||||
struct rmi_device_platform_data_spi {
|
||||
int block_delay_us;
|
||||
int split_read_block_delay_us;
|
||||
int read_delay_us;
|
||||
int write_delay_us;
|
||||
int split_read_byte_delay_us;
|
||||
int pre_delay_us;
|
||||
int post_delay_us;
|
||||
|
||||
void *cs_assert_data;
|
||||
int (*cs_assert) (const void *cs_assert_data, const bool assert);
|
||||
};
|
||||
|
||||
struct rmi_device_platform_data {
|
||||
char *driver_name;
|
||||
char *sensor_name; /* Used for diagnostics. */
|
||||
|
||||
int attn_gpio;
|
||||
enum rmi_attn_polarity attn_polarity;
|
||||
bool level_triggered;
|
||||
void *gpio_data;
|
||||
int (*gpio_config)(void *gpio_data, bool configure);
|
||||
|
||||
int reset_delay_ms;
|
||||
|
||||
struct rmi_device_platform_data_spi spi_data;
|
||||
|
||||
/* function handler pdata */
|
||||
struct rmi_f01_power_management power_management;
|
||||
struct rmi_f11_2d_axis_alignment axis_align;
|
||||
struct rmi_f19_button_map *button_map;
|
||||
struct rmi_f1a_button_map *f1a_button_map;
|
||||
struct rmi_f11_virtualbutton_map *virtualbutton_map;
|
||||
int (*init_hw)(void);
|
||||
#ifdef CONFIG_PM
|
||||
void *pm_data;
|
||||
int (*pre_suspend) (const void *pm_data);
|
||||
int (*post_resume) (const void *pm_data);
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_function_descriptor - RMI function base addresses
|
||||
* @query_base_addr: The RMI Query base address
|
||||
* @command_base_addr: The RMI Command base address
|
||||
* @control_base_addr: The RMI Control base address
|
||||
* @data_base_addr: The RMI Data base address
|
||||
* @interrupt_source_count: The number of irqs this RMI function needs
|
||||
* @function_number: The RMI function number
|
||||
*
|
||||
* This struct is used when iterating the Page Description Table. The addresses
|
||||
* are 16-bit values to include the current page address.
|
||||
*
|
||||
*/
|
||||
struct rmi_function_descriptor {
|
||||
u16 query_base_addr;
|
||||
u16 command_base_addr;
|
||||
u16 control_base_addr;
|
||||
u16 data_base_addr;
|
||||
u8 interrupt_source_count;
|
||||
u8 function_number;
|
||||
u8 function_version;
|
||||
};
|
||||
|
||||
struct rmi_function_container;
|
||||
struct rmi_device;
|
||||
|
||||
/**
|
||||
* struct rmi_function_handler - an RMI function handler
|
||||
* @func: The RMI function number
|
||||
* @init: Callback for RMI function init
|
||||
* @attention: Callback for RMI function attention
|
||||
* @suspend: Callback for function suspend, returns 0 for success.
|
||||
* @resume: Callback for RMI function resume, returns 0 for success.
|
||||
* @remove: Callback for RMI function removal
|
||||
*
|
||||
* This struct describes the interface of an RMI function. These are
|
||||
* registered to the bus using the rmi_register_function_driver() call.
|
||||
*
|
||||
*/
|
||||
struct rmi_function_handler {
|
||||
int func;
|
||||
int (*init)(struct rmi_function_container *fc);
|
||||
int (*config)(struct rmi_function_container *fc);
|
||||
int (*reset)(struct rmi_function_container *fc);
|
||||
int (*attention)(struct rmi_function_container *fc, u8 *irq_bits);
|
||||
#ifdef CONFIG_PM
|
||||
int (*suspend)(struct rmi_function_container *fc);
|
||||
int (*resume)(struct rmi_function_container *fc);
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
int (*early_suspend)(struct rmi_function_container *fc);
|
||||
int (*late_resume)(struct rmi_function_container *fc);
|
||||
#endif
|
||||
#endif
|
||||
void (*remove)(struct rmi_function_container *fc);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_function_container - an element in a function handler list
|
||||
* @list: The list
|
||||
* @fd: The function descriptor of the RMI function
|
||||
* @rmi_dev: Pointer to the RMI device associated with this function container
|
||||
* @fh: The callbacks connected to this function
|
||||
* @num_of_irqs: The number of irqs needed by this function
|
||||
* @irq_pos: The position in the irq bitfield this function holds
|
||||
* @data: Private data pointer
|
||||
*
|
||||
*/
|
||||
struct rmi_function_container {
|
||||
struct list_head list;
|
||||
|
||||
struct rmi_function_descriptor fd;
|
||||
struct rmi_device *rmi_dev;
|
||||
struct rmi_function_handler *fh;
|
||||
struct device dev;
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
struct dentry *debugfs_root;
|
||||
#endif
|
||||
|
||||
int num_of_irqs;
|
||||
int irq_pos;
|
||||
u8 *irq_mask;
|
||||
|
||||
void *data;
|
||||
};
|
||||
#define to_rmi_function_container(d) \
|
||||
container_of(d, struct rmi_function_container, dev);
|
||||
|
||||
|
||||
/**
|
||||
* struct rmi_driver - represents an RMI driver
|
||||
* @driver: Device driver model driver
|
||||
* @probe: Callback for device probe
|
||||
* @remove: Callback for device removal
|
||||
* @shutdown: Callback for device shutdown
|
||||
* @irq_handler: Callback for handling irqs
|
||||
* @fh_add: Callback for function handler add
|
||||
* @fh_remove: Callback for function handler remove
|
||||
* @get_func_irq_mask: Callback for calculating interrupt mask
|
||||
* @store_irq_mask: Callback for storing and replacing interrupt mask
|
||||
* @restore_irq_mask: Callback for restoring previously stored interrupt mask
|
||||
* @data: Private data pointer
|
||||
*
|
||||
* The RMI driver implements a driver on the RMI bus.
|
||||
*
|
||||
*/
|
||||
struct rmi_driver {
|
||||
struct device_driver driver;
|
||||
|
||||
int (*probe)(struct rmi_device *rmi_dev);
|
||||
int (*remove)(struct rmi_device *rmi_dev);
|
||||
void (*shutdown)(struct rmi_device *rmi_dev);
|
||||
int (*irq_handler)(struct rmi_device *rmi_dev, int irq);
|
||||
int (*reset_handler)(struct rmi_device *rmi_dev);
|
||||
void (*fh_add)(struct rmi_device *rmi_dev,
|
||||
struct rmi_function_handler *fh);
|
||||
void (*fh_remove)(struct rmi_device *rmi_dev,
|
||||
struct rmi_function_handler *fh);
|
||||
u8* (*get_func_irq_mask)(struct rmi_device *rmi_dev,
|
||||
struct rmi_function_container *fc);
|
||||
int (*store_irq_mask)(struct rmi_device *rmi_dev, u8* new_interupts);
|
||||
int (*restore_irq_mask)(struct rmi_device *rmi_dev);
|
||||
void *data;
|
||||
};
|
||||
#define to_rmi_driver(d) \
|
||||
container_of(d, struct rmi_driver, driver);
|
||||
|
||||
/** struct rmi_phys_info - diagnostic information about the RMI physical
|
||||
* device, used in the phys sysfs file.
|
||||
* @proto String indicating the protocol being used.
|
||||
* @tx_count Number of transmit operations.
|
||||
* @tx_bytes Number of bytes transmitted.
|
||||
* @tx_errs Number of errors encountered during transmit operations.
|
||||
* @rx_count Number of receive operations.
|
||||
* @rx_bytes Number of bytes received.
|
||||
* @rx_errs Number of errors encountered during receive operations.
|
||||
* @att_count Number of times ATTN assertions have been handled.
|
||||
*/
|
||||
struct rmi_phys_info {
|
||||
char *proto;
|
||||
long tx_count;
|
||||
long tx_bytes;
|
||||
long tx_errs;
|
||||
long rx_count;
|
||||
long rx_bytes;
|
||||
long rx_errs;
|
||||
long attn_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_phys_device - represent an RMI physical device
|
||||
* @dev: Pointer to the communication device, e.g. i2c or spi
|
||||
* @rmi_dev: Pointer to the RMI device
|
||||
* @write: Callback for write
|
||||
* @write_block: Callback for writing a block of data
|
||||
* @read: Callback for read
|
||||
* @read_block: Callback for reading a block of data
|
||||
* @data: Private data pointer
|
||||
*
|
||||
* The RMI physical device implements the glue between different communication
|
||||
* buses such as I2C and SPI.
|
||||
*
|
||||
*/
|
||||
struct rmi_phys_device {
|
||||
struct device *dev;
|
||||
struct rmi_device *rmi_dev;
|
||||
|
||||
int (*write)(struct rmi_phys_device *phys, u16 addr, u8 data);
|
||||
int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
|
||||
int len);
|
||||
int (*read)(struct rmi_phys_device *phys, u16 addr, u8 *buf);
|
||||
int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
|
||||
int len);
|
||||
|
||||
int (*enable_device) (struct rmi_phys_device *phys);
|
||||
void (*disable_device) (struct rmi_phys_device *phys);
|
||||
|
||||
void *data;
|
||||
|
||||
struct rmi_phys_info info;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rmi_device - represents an RMI device
|
||||
* @dev: The device created for the RMI bus
|
||||
* @number: Unique number for the device on the bus.
|
||||
* @driver: Pointer to associated driver
|
||||
* @phys: Pointer to the physical interface
|
||||
* @early_suspend_handler: Pointers to early_suspend and late_resume, if
|
||||
* configured.
|
||||
*
|
||||
* This structs represent an RMI device.
|
||||
*
|
||||
*/
|
||||
struct rmi_device {
|
||||
struct device dev;
|
||||
int number;
|
||||
|
||||
struct rmi_driver *driver;
|
||||
struct rmi_phys_device *phys;
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
struct early_suspend early_suspend_handler;
|
||||
#endif
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
struct dentry *debugfs_root;
|
||||
#endif
|
||||
};
|
||||
#define to_rmi_device(d) container_of(d, struct rmi_device, dev);
|
||||
#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data);
|
||||
|
||||
static inline void rmi_set_driverdata(struct rmi_device *d, void *data)
|
||||
{
|
||||
dev_set_drvdata(&d->dev, data);
|
||||
}
|
||||
|
||||
static inline void *rmi_get_driverdata(struct rmi_device *d)
|
||||
{
|
||||
return dev_get_drvdata(&d->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* rmi_read - RMI read byte
|
||||
* @d: Pointer to an RMI device
|
||||
* @addr: The address to read from
|
||||
* @buf: The read buffer
|
||||
*
|
||||
* Reads a byte of data using the underlaying physical protocol in to buf. It
|
||||
* returns zero or a negative error code.
|
||||
*/
|
||||
static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf)
|
||||
{
|
||||
return d->phys->read(d->phys, addr, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* rmi_read_block - RMI read block
|
||||
* @d: Pointer to an RMI device
|
||||
* @addr: The start address to read from
|
||||
* @buf: The read buffer
|
||||
* @len: Length of the read buffer
|
||||
*
|
||||
* Reads a block of byte data using the underlaying physical protocol in to buf.
|
||||
* It returns the amount of bytes read or a negative error code.
|
||||
*/
|
||||
static inline int rmi_read_block(struct rmi_device *d, u16 addr, u8 *buf,
|
||||
int len)
|
||||
{
|
||||
return d->phys->read_block(d->phys, addr, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* rmi_write - RMI write byte
|
||||
* @d: Pointer to an RMI device
|
||||
* @addr: The address to write to
|
||||
* @data: The data to write
|
||||
*
|
||||
* Writes a byte from buf using the underlaying physical protocol. It
|
||||
* returns zero or a negative error code.
|
||||
*/
|
||||
static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data)
|
||||
{
|
||||
return d->phys->write(d->phys, addr, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* rmi_write_block - RMI write block
|
||||
* @d: Pointer to an RMI device
|
||||
* @addr: The start address to write to
|
||||
* @buf: The write buffer
|
||||
* @len: Length of the write buffer
|
||||
*
|
||||
* Writes a block of byte data from buf using the underlaying physical protocol.
|
||||
* It returns the amount of bytes written or a negative error code.
|
||||
*/
|
||||
static inline int rmi_write_block(struct rmi_device *d, u16 addr, u8 *buf,
|
||||
int len)
|
||||
{
|
||||
return d->phys->write_block(d->phys, addr, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* rmi_register_driver - register rmi driver
|
||||
* @driver: the driver to register
|
||||
*
|
||||
* This function registers an RMI driver to the RMI bus.
|
||||
*/
|
||||
int rmi_register_driver(struct rmi_driver *driver);
|
||||
|
||||
/**
|
||||
* rmi_unregister_driver - unregister rmi driver
|
||||
* @driver: the driver to unregister
|
||||
*
|
||||
* This function unregisters an RMI driver to the RMI bus.
|
||||
*/
|
||||
void rmi_unregister_driver(struct rmi_driver *driver);
|
||||
|
||||
/**
|
||||
* rmi_register_phys_device - register a physical device connection
|
||||
* @phys: the physical driver to register
|
||||
*
|
||||
* This function registers a physical driver to the RMI bus. These drivers
|
||||
* provide a communication layer for the drivers connected to the bus, e.g.
|
||||
* I2C, SPI and so on.
|
||||
*/
|
||||
int rmi_register_phys_device(struct rmi_phys_device *phys);
|
||||
|
||||
/**
|
||||
* rmi_unregister_phys_device - unregister a physical device connection
|
||||
* @phys: the physical driver to unregister
|
||||
*
|
||||
* This function unregisters a physical driver from the RMI bus.
|
||||
*/
|
||||
void rmi_unregister_phys_device(struct rmi_phys_device *phys);
|
||||
|
||||
/**
|
||||
* rmi_register_function_driver - register an RMI function driver
|
||||
* @fh: the function handler to register
|
||||
*
|
||||
* This function registers support for a new RMI function to the bus. All
|
||||
* drivers on the bus will be notified of the presence of the new function
|
||||
* driver.
|
||||
*/
|
||||
int rmi_register_function_driver(struct rmi_function_handler *fh);
|
||||
|
||||
/**
|
||||
* rmi_unregister_function_driver - unregister an RMI function driver
|
||||
* @fh: the function handler to unregister
|
||||
*
|
||||
* This function unregisters a RMI function from the RMI bus. All drivers on
|
||||
* the bus will be notified of the removal of a function driver.
|
||||
*/
|
||||
void rmi_unregister_function_driver(struct rmi_function_handler *fh);
|
||||
|
||||
/**
|
||||
* rmi_get_function_handler - get a pointer to specified RMI function
|
||||
* @id: the RMI function id
|
||||
*
|
||||
* This function gets the specified RMI function handler from the list of
|
||||
* supported functions.
|
||||
*/
|
||||
struct rmi_function_handler *rmi_get_function_handler(int id);
|
||||
|
||||
|
||||
struct rmi_char_device;
|
||||
|
||||
/**
|
||||
* rmi_char_driver - a general driver that doesn't handle specific functions,
|
||||
* operating outside the bus::sensor::functions
|
||||
* @match: returns 1 if the driver wants to talk to the specified rmi_dev.
|
||||
*
|
||||
* All of the above are optional except driver and init which are required.
|
||||
*
|
||||
*/
|
||||
struct rmi_char_driver {
|
||||
struct device_driver driver;
|
||||
|
||||
int (*match)(struct rmi_device *rmi_dev);
|
||||
int (*init)(struct rmi_char_device *cd);
|
||||
int (*attention)(struct rmi_char_device *cd, u8 *irq_bits);
|
||||
#ifdef CONFIG_PM
|
||||
int (*suspend)(struct rmi_char_device *cd);
|
||||
int (*resume)(struct rmi_char_device *cd);
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
int (*early_suspend)(struct rmi_char_device *cd);
|
||||
int (*late_resume)(struct rmi_char_device *cd);
|
||||
#endif
|
||||
#endif
|
||||
void (*remove)(struct rmi_char_device *cd);
|
||||
|
||||
struct list_head devices;
|
||||
};
|
||||
|
||||
struct rmi_char_device {
|
||||
struct list_head list;
|
||||
|
||||
struct rmi_device *rmi_dev;
|
||||
struct rmi_char_driver *driver;
|
||||
struct device dev;
|
||||
|
||||
#ifdef CONFIG_RMI4_DEBUG
|
||||
struct dentry *debugfs_root;
|
||||
#endif
|
||||
|
||||
void *data;
|
||||
};
|
||||
#define to_rmi_char_device(d) \
|
||||
container_of(d, struct rmi_char_device, dev)
|
||||
|
||||
int rmi_register_character_driver(struct rmi_char_driver *char_driver);
|
||||
int rmi_unregister_character_driver(struct rmi_char_driver *char_driver);
|
||||
|
||||
|
||||
/* Helper fn to convert a byte array representing a short in the RMI
|
||||
* endian-ness to a short in the native processor's specific endianness.
|
||||
* We don't use ntohs/htons here because, well, we're not dealing with
|
||||
* a pair of shorts. And casting dest to short* wouldn't work, because
|
||||
* that would imply knowing the byte order of short in the first place.
|
||||
*/
|
||||
static inline void batohs(unsigned short *dest, unsigned char *src)
|
||||
{
|
||||
*dest = src[1] * 0x100 + src[0];
|
||||
}
|
||||
|
||||
/* Helper function to convert a short (in host processor endianess) to
|
||||
* a byte array in the RMI endianess for shorts. See above comment for
|
||||
* why we dont us htons or something like that.
|
||||
*/
|
||||
static inline void hstoba(unsigned char *dest, unsigned short src)
|
||||
{
|
||||
dest[0] = src % 0x100;
|
||||
dest[1] = src / 0x100;
|
||||
}
|
||||
|
||||
/* Utility routine to handle writes to read-only attributes. Hopefully
|
||||
* this will never happen, but if the user does something stupid, we don't
|
||||
* want to accept it quietly (which is what can happen if you just put NULL
|
||||
* for the attribute's store function).
|
||||
*/
|
||||
static inline ssize_t rmi_store_error(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
dev_warn(dev,
|
||||
"RMI4 WARNING: Attempt to write %d characters to read-only "
|
||||
"attribute %s.", count, attr->attr.name);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* Utility routine to handle reads of write-only attributes. Hopefully
|
||||
* this will never happen, but if the user does something stupid, we don't
|
||||
* want to accept it quietly (which is what can happen if you just put NULL
|
||||
* for the attribute's show function).
|
||||
*/
|
||||
static inline ssize_t rmi_show_error(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
dev_warn(dev,
|
||||
"RMI4 WARNING: Attempt to read from write-only attribute %s.",
|
||||
attr->attr.name);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* utility function for bit access of u8*'s */
|
||||
void u8_set_bit(u8 *target, int pos);
|
||||
void u8_clear_bit(u8 *target, int pos);
|
||||
bool u8_is_set(u8 *target, int pos);
|
||||
bool u8_is_any_set(u8 *target, int size);
|
||||
void u8_or(u8 *dest, u8* target1, u8* target2, int size);
|
||||
void u8_and(u8 *dest, u8* target1, u8* target2, int size);
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user