M7350/kernel/drivers/input/touchscreen/synaptics/rmi_f34.c
2024-09-09 08:52:07 +00:00

558 lines
18 KiB
C

/**
*
* Synaptics Register Mapped Interface (RMI4) Function $34 support for sensor
* firmware reflashing.
*
* Copyright (c) 2007 - 2011, Synaptics Incorporated
*
*/
/*
* This file is licensed under the GPL2 license.
*
*#############################################################################
* GPL
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
*#############################################################################
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/input.h>
#include <linux/sysfs.h>
#include <linux/math64.h>
#include <linux/module.h>
#include "rmi_drvr.h"
#include "rmi_bus.h"
#include "rmi_sensor.h"
#include "rmi_function.h"
#include "rmi_f34.h"
/* 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;
};
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);
static ssize_t rmi_fn_34_data_read(struct 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 *,
struct kobject *kobj,
struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
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_blocksize_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
/* define the device attributes using DEVICE_ATTR macros */
DEVICE_ATTR(status, 0444, rmi_fn_34_status_show, rmi_fn_34_status_store); /* RO attr */
DEVICE_ATTR(cmd, 0664, rmi_fn_34_cmd_show, rmi_fn_34_cmd_store); /* RW attr */
DEVICE_ATTR(bootloaderid, 0644, rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store); /* RW attr */
DEVICE_ATTR(blocksize, 0444, rmi_fn_34_blocksize_show, rmi_fn_34_blocksize_store); /* RO attr */
struct bin_attribute dev_attr_data = {
.attr = {
.name = "data",
.mode = 0644
},
.size = 0,
.read = rmi_fn_34_data_read,
.write = rmi_fn_34_data_write,
};
/* Helper fn to convert from processor specific data to our firmware specific endianness.
* TODO: Should we use ntohs or something like that?
*/
void copyEndianAgnostic(unsigned char *dest, unsigned short src)
{
dest[0] = src%0x100;
dest[1] = src/0x100;
}
/*.
* The interrupt handler for Fn $34.
*/
void FN_34_inthandler(struct rmi_function_info *rmifninfo,
unsigned int assertedIRQs)
{
unsigned int status;
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)rmifninfo->fndata;
/* Read the Fn $34 status register to see whether the previous command executed OK */
/* inform user space - through a sysfs param. */
if (rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.dataBaseAddr+3,
(unsigned char *)&status, 1)) {
printk(KERN_ERR "%s : Could not read status from 0x%x\n",
__func__, rmifninfo->funcDescriptor.dataBaseAddr+3);
status = 0xff; /* failure */
}
/* set a sysfs value that the user mode can read - only upper 4 bits are the status */
fn34data->status = status & 0xf0; /* successful is $80, anything else is failure */
}
EXPORT_SYMBOL(FN_34_inthandler);
void FN_34_attention(struct rmi_function_info *rmifninfo)
{
}
EXPORT_SYMBOL(FN_34_attention);
int FN_34_config(struct rmi_function_info *rmifninfo)
{
pr_debug("%s: RMI4 function $34 config\n", __func__);
return 0;
}
EXPORT_SYMBOL(FN_34_config);
int FN_34_init(struct rmi_function_device *function_device)
{
int retval = 0;
unsigned char uData[2];
struct rmi_function_info *rmifninfo = function_device->rfi;
struct rmi_fn_34_data *fn34data;
pr_debug("%s: RMI4 function $34 init\n", __func__);
/* Here we will need to set up sysfs files for Bootloader ID and Block size */
fn34data = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL);
if (!fn34data) {
printk(KERN_ERR "%s: Error allocating memeory for rmi_fn_34_data.\n", __func__);
return -ENOMEM;
}
rmifninfo->fndata = (void *)fn34data;
/* set up sysfs file for Bootloader ID. */
if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_bootloaderid.attr) < 0) {
printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 bootloaderid.\n", __func__);
return -ENODEV;
}
/* set up sysfs file for Block Size. */
if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_blocksize.attr) < 0) {
printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 blocksize.\n", __func__);
return -ENODEV;
}
/* get the Bootloader ID and Block Size and store in the sysfs attributes. */
retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.queryBaseAddr,
uData, 2);
if (retval) {
printk(KERN_ERR "%s : Could not read bootloaderid from 0x%x\n",
__func__, function_device->function->functionQueryBaseAddr);
return retval;
}
/* need to convert from our firmware storage to processore specific data */
fn34data->bootloaderid = (unsigned int)uData[0] + (unsigned int)uData[1]*0x100;
retval = rmi_read_multiple(rmifninfo->sensor, rmifninfo->funcDescriptor.queryBaseAddr+3,
uData, 2);
if (retval) {
printk(KERN_ERR "%s : Could not read block size from 0x%x\n",
__func__, rmifninfo->funcDescriptor.queryBaseAddr+3);
return retval;
}
/* need to convert from our firmware storage to processor specific data */
fn34data->blocksize = (unsigned int)uData[0] + (unsigned int)uData[1]*0x100;
/* set up sysfs file for status. */
if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_status.attr) < 0) {
printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 status.\n", __func__);
return -ENODEV;
}
/* Also, sysfs will need to have a file set up to distinguish between commands - like
Config write/read, Image write/verify.*/
/* set up sysfs file for command code. */
if (sysfs_create_file(&function_device->dev.kobj, &dev_attr_cmd.attr) < 0) {
printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 cmd.\n", __func__);
return -ENODEV;
}
/* We will also 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. */
if (sysfs_create_bin_file(&function_device->dev.kobj, &dev_attr_data) < 0) {
printk(KERN_ERR "%s: Failed to create sysfs file for fn 34 data.\n", __func__);
return -ENODEV;
}
return retval;
}
EXPORT_SYMBOL(FN_34_init);
int FN_34_detect(struct rmi_function_info *rmifninfo,
struct rmi_function_descriptor *fndescr, unsigned int interruptCount)
{
int i;
int InterruptOffset;
int retval = 0;
pr_debug("%s: RMI4 function $34 detect\n", __func__);
if (rmifninfo->sensor == NULL) {
printk(KERN_ERR "%s: NULL sensor passed in!", __func__);
return -EINVAL;
}
/* Store addresses - used elsewhere to read data,
* control, query, etc. */
rmifninfo->funcDescriptor.queryBaseAddr = fndescr->queryBaseAddr;
rmifninfo->funcDescriptor.commandBaseAddr = fndescr->commandBaseAddr;
rmifninfo->funcDescriptor.controlBaseAddr = fndescr->controlBaseAddr;
rmifninfo->funcDescriptor.dataBaseAddr = fndescr->dataBaseAddr;
rmifninfo->funcDescriptor.interruptSrcCnt = fndescr->interruptSrcCnt;
rmifninfo->funcDescriptor.functionNum = fndescr->functionNum;
rmifninfo->numSources = fndescr->interruptSrcCnt;
/* Need to get interrupt info to be used later when handling
interrupts. */
rmifninfo->interruptRegister = interruptCount/8;
/* loop through interrupts for each source and or in a bit
to the interrupt mask for each. */
InterruptOffset = interruptCount % 8;
for (i = InterruptOffset;
i < ((fndescr->interruptSrcCnt & 0x7) + InterruptOffset);
i++) {
rmifninfo->interruptMask |= 1 << i;
}
return retval;
}
EXPORT_SYMBOL(FN_34_detect);
static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rmi_function_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
return sprintf(buf, "%u\n", fn34data->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 uData[2];
struct rmi_function_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
/* need to convert the string data to an actual value */
error = strict_strtoul(buf, 10, &val);
if (error)
return error;
fn34data->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).*/
copyEndianAgnostic(uData, (unsigned short)val);
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr,
uData, 2);
if (error) {
printk(KERN_ERR "%s : Could not write bootloader id to 0x%x\n",
__func__, fn->function->functionDataBaseAddr);
return error;
}
return count;
}
static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rmi_function_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
return sprintf(buf, "%u\n", fn34data->blocksize);
}
static ssize_t rmi_fn_34_blocksize_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
/* Block Size is RO so we shouldn't do anything if the
user space writes to the sysfs file. */
return -EPERM;
}
static ssize_t rmi_fn_34_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rmi_function_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
return sprintf(buf, "%u\n", fn34data->status);
}
static ssize_t rmi_fn_34_status_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
/* Status is RO so we shouldn't do anything if the user
app writes to the sysfs file. */
return -EPERM;
}
static ssize_t rmi_fn_34_cmd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rmi_function_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
return sprintf(buf, "%u\n", fn34data->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_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
unsigned long val;
unsigned char cmd;
int error;
/* need to convert the string data to an actual value */
error = strict_strtoul(buf, 10, &val);
if (error)
return error;
fn34data->cmd = val;
/* determine the proper command to issue.
*/
switch (val) {
case ENABLE_FLASH_PROG:
/* Issue a Flash Program Enable ($0F) command to the Flash Command
(F34_Flash_Data3, bits 3:0) field.*/
cmd = 0x0F;
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
(unsigned char *)&cmd, 1);
if (error) {
printk(KERN_ERR "%s : Could not write Flash Program Enable cmd to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+3);
return error;
}
break;
case ERASE_ALL:
/* Issue a Erase All ($03) command to the Flash Command
(F34_Flash_Data3, bits 3:0) field.*/
cmd = 0x03;
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
(unsigned char *)&cmd, 1);
if (error) {
printk(KERN_ERR "%s : Could not write Erase All cmd to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+3);
return error;
}
break;
case ERASE_CONFIG:
/* Issue a Erase Configuration ($07) command to the Flash Command
(F34_Flash_Data3, bits 3:0) field.*/
cmd = 0x07;
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
(unsigned char *)&cmd, 1);
if (error) {
printk(KERN_ERR "%s : Could not write Erase Configuration cmd to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+3);
return error;
}
break;
case WRITE_FW_BLOCK:
/* Issue a Write Firmware Block ($02) command to the Flash Command
(F34_Flash_Data3, bits 3:0) field.*/
cmd = 0x02;
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
(unsigned char *)&cmd, 1);
if (error) {
printk(KERN_ERR "%s : Could not write Write Firmware Block cmd to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+3);
return error;
}
break;
case WRITE_CONFIG_BLOCK:
/* Issue a Write Config Block ($06) command to the Flash Command
(F34_Flash_Data3, bits 3:0) field.*/
cmd = 0x06;
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
(unsigned char *)&cmd, 1);
if (error) {
printk(KERN_ERR "%s : Could not write Write Config Block cmd to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+3);
return error;
}
break;
case READ_CONFIG_BLOCK:
/* Issue a Read Config Block ($05) command to the Flash Command
(F34_Flash_Data3, bits 3:0) field.*/
cmd = 0x05;
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+3,
(unsigned char *)&cmd, 1);
if (error) {
printk(KERN_ERR "%s : Could not write Read Config Block cmd to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+3);
return error;
}
break;
case DISABLE_FLASH_PROG:
/* Issue a reset command ($01) - this will reboot the sensor and ATTN will now go to
the Fn $01 instead of the Fn $34 since the sensor will no longer be in Flash mode. */
cmd = 0x01;
/*if ((error = rmi_write_multiple(fn->sensor, fn->sensor->sensorCommandBaseAddr,
(unsigned char *)&cmd, 1))) {
printk(KERN_ERR "%s : Could not write Reset cmd to 0x%x\n",
__func__, fn->sensor->sensorCommandBaseAddr);
return error;
}*/
break;
default:
pr_debug("%s: RMI4 function $34 - unknown command.\n", __func__);
break;
}
return count;
}
static ssize_t rmi_fn_34_data_read(struct file * filp,
struct kobject *kobj,
struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct rmi_function_device *fn = dev_get_drvdata(dev);
int error;
/* TODO: add check for count to verify it's the correct blocksize */
/* 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_multiple(fn->sensor, fn->function->functionDataBaseAddr+pos,
(unsigned char *)buf, count);
if (error) {
printk(KERN_ERR "%s : Could not read data from 0x%llx\n",
__func__, fn->function->functionDataBaseAddr+pos);
return error;
}
return count;
}
static ssize_t rmi_fn_34_data_write(struct file *filp,
struct kobject *kobj,
struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct rmi_function_device *fn = dev_get_drvdata(dev);
struct rmi_fn_34_data *fn34data = (struct rmi_fn_34_data *)fn->rfi->fndata;
unsigned int blocknum;
int error;
/* 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. */
/* TODO: Add check on count - if non-zero veriy it's the correct blocksize */
/* Verify that the byte offset is always aligned on a block boundary and if not
return an error. We can't just use the mod operator % and do a (pos % fn34data->blocksize) because of a gcc
bug that results in undefined symbols. So we have to compute it the hard
way. Grumble. */
unsigned int remainder;
div_u64_rem(pos, fn34data->blocksize, &remainder);
if (remainder) {
printk(KERN_ERR "%s : Invalid byte offset of %llx leads to invalid block number.\n",
__func__, pos);
return -EINVAL;
}
/* Compute the block number using the byte offset (pos) and the block size.
once again, we can't just do a divide due to a gcc bug. */
blocknum = div_u64(pos, fn34data->blocksize);
/* Write the block number first */
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr,
(unsigned char *)&blocknum, 2);
if (error) {
printk(KERN_ERR "%s : Could not write block number to 0x%x\n",
__func__, fn->function->functionDataBaseAddr);
return error;
}
/* Write the data block - only if the count is non-zero */
if (count) {
error = rmi_write_multiple(fn->sensor, fn->function->functionDataBaseAddr+2,
(unsigned char *)buf, count);
if (error) {
printk(KERN_ERR "%s : Could not write block data to 0x%x\n",
__func__, fn->function->functionDataBaseAddr+2);
return error;
}
}
return count;
}