/*
* Copyright (c) 2011-2016 Synaptics Incorporated
* Copyright (c) 2011 Unixphere
*
* This driver provides the core support for a single RMI4-based device.
*
* The RMI4 specification can be found here (URL split for line length):
*
* http://www.synaptics.com/sites/default/files/
* 511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/kconfig.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <uapi/linux/input.h>
#include <linux/rmi.h>
#include "rmi_bus.h"
#include "rmi_driver.h"
#define HAS_NONSTANDARD_PDT_MASK 0x40
#define RMI4_MAX_PAGE 0xff
#define RMI4_PAGE_SIZE 0x100
#define RMI4_PAGE_MASK 0xFF00
#define RMI_DEVICE_RESET_CMD 0x01
#define DEFAULT_RESET_DELAY_MS 100
static void rmi_free_function_list(struct rmi_device *rmi_dev)
{
struct rmi_function *fn, *tmp;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
data->f01_container = NULL;
/* Doing it in the reverse order so F01 will be removed last */
list_for_each_entry_safe_reverse(fn, tmp,
&data->function_list, node) {
list_del(&fn->node);
rmi_unregister_function(fn);
}
}
static int reset_one_function(struct rmi_function *fn)
{
struct rmi_function_handler *fh;
int retval = 0;
if (!fn || !fn->dev.driver)
return 0;
fh = to_rmi_function_handler(fn->dev.driver);
if (fh->reset) {
retval = fh->reset(fn);
if (retval < 0)
dev_err(&fn->dev, "Reset failed with code %d.\n",
retval);
}
return retval;
}
static int configure_one_function(struct rmi_function *fn)
{
struct rmi_function_handler *fh;
int retval = 0;
if (!fn || !fn->dev.driver)
return 0;
fh = to_rmi_function_handler(fn->dev.driver);
if (fh->config) {
retval = fh->config(fn);
if (retval < 0)
dev_err(&fn->dev, "Config failed with code %d.\n",
retval);
}
return retval;
}
static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev)
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
struct rmi_function *entry;
int retval;
list_for_each_entry(entry, &data->function_list, node) {
retval = reset_one_function(entry);
if (retval < 0)
return retval;
}
return 0;
}
static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev)
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
struct rmi_function *entry;
int retval;
list_for_each_entry(entry, &data->function_list, node) {
retval = configure_one_function(entry);
if (retval < 0)
return retval;
}
return 0;
}
static void process_one_interrupt(struct rmi_driver_data *data,
struct rmi_function *fn)
{
struct rmi_function_handler *fh;
if (!fn || !fn->dev.driver)
return;
fh = to_rmi_function_handler(fn->dev.driver);
if (fh->attention) {
bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask,
data->irq_count);
if (!bitmap_empty(data->fn_irq_bits, data->irq_count))
fh->attention(fn, data->fn_irq_bits);
}
}
int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
struct device *dev = &rmi_dev->dev;
struct rmi_function *entry;
int error;
if (!data)
return 0;
if (!rmi_dev->xport->attn_data) {
error = rmi_read_block(rmi_dev,
data->f01_container->fd.data_base_addr + 1,
data->irq_status, data->num_of_irq_regs);
if (error < 0) {
dev_err(dev, "Failed to read irqs, code=%d\n", error);
return error;
}
}
mutex_lock(&data->irq_mutex);
bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask,
data->irq_count);
/*
* At this point, irq_status has all bits that are set in the
* interrupt status register and are enabled.
*/
mutex_unlock(&data->irq_mutex);
/*
* It would be nice to be able to use irq_chip to handle these
* nested IRQs. Unfortunately, most of the current customers for
* this driver are using older kernels (3.0.x) that don't support
* the features required for that. Once they've shifted to more
* recent kernels (say, 3.3 and higher), this should be switched to
* use irq_chip.
*/
list_for_each_entry(entry, &data->function_list, node)
process_one_interrupt(data, entry);
if (data->input)
input_sync(data->input);
return