123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- /**
- *
- * 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;
- }
|