diff options
Diffstat (limited to 'drivers/mfd/m4sensorhub-reg.c')
| -rw-r--r-- | drivers/mfd/m4sensorhub-reg.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/drivers/mfd/m4sensorhub-reg.c b/drivers/mfd/m4sensorhub-reg.c new file mode 100644 index 00000000000..cb9e8b1b371 --- /dev/null +++ b/drivers/mfd/m4sensorhub-reg.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2012 Motorola, Inc. + * + * 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. + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/m4sensorhub.h> +#include <linux/slab.h> + +#include "m4sensorhub-reg.h" /* auto-generated header defining registers */ + +#define I2C_RETRY_DELAY 5 +#define I2C_RETRIES 5 + +#define DEBUG_LINE_LENGTH 80 + +/* --------------- Global Declarations -------------- */ + +/* ------------ Local Function Prototypes ----------- */ +static int m4sensorhub_mapsize(enum m4sensorhub_reg reg); + +/* --------------- Local Declarations -------------- */ +static DEFINE_MUTEX(reg_access); + +/* -------------- Local Data Structures ------------- */ + +/* -------------- Global Functions ----------------- */ + +/* m4sensorhub_reg_init() + + Initialized register access data structures. + + Returns 0 on success or negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct +*/ + +int m4sensorhub_reg_init(struct m4sensorhub_data *m4sensorhub) +{ + return 0; +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_init); + +/* m4sensorhub_reg_shutdown() + + Clean up register subsystem on driver removal + + Returns 0 on success or negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct +*/ + +int m4sensorhub_reg_shutdown(struct m4sensorhub_data *m4sensorhub) +{ + return 0; +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_shutdown); + +/* m4sensorhub_reg_read_n() + + Read a n bytes from the M4 sensor hub starting at 'register'. Use + m4sensorhub_reg_read() instead where possible; + + Returns number of bytes read on success. + Returns negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct + reg - Register to be read + value - array to return data. Needs to be at least register's size + num - number of bytes to read +*/ + +int m4sensorhub_reg_read_n(struct m4sensorhub_data *m4sensorhub, + enum m4sensorhub_reg reg, unsigned char *value, + short num) +{ + int ret = -EINVAL; + u8 stack_buf[M4SH_MAX_STACK_BUF_SIZE]; + + if (!m4sensorhub || !value || !num) { + KDEBUG(M4SH_ERROR, "%s() invalid parameter\n", __func__); + return ret; + } + + if ((reg < M4SH_REG__NUM) && num <= M4SH_MAX_REG_SIZE &&\ + register_info_tbl[reg].offset + num <= + m4sensorhub_mapsize(reg)) { + u8 *buf = (num > (M4SH_MAX_STACK_BUF_SIZE-2))\ + ? kmalloc(num+2, GFP_KERNEL) : stack_buf; + if (!buf) { + KDEBUG(M4SH_ERROR, "%s() Failed alloc %d memeory\n"\ + , __func__, num+2); + return -ENOMEM; + } + buf[0] = register_info_tbl[reg].type; + buf[1] = register_info_tbl[reg].offset; + + mutex_lock(®_access); + ret = m4sensorhub_i2c_write_read(m4sensorhub, buf, 2, num); + mutex_unlock(®_access); + + if (ret != num) + KDEBUG(M4SH_ERROR, "%s() read failure\n", __func__); + else + memcpy(value, buf, num); + if (buf != stack_buf) + kfree(buf); + } else { + KDEBUG(M4SH_ERROR, "%s() invalid register access reg=%d " + "maxreg=%d size=%d maxsze=%d mapsize=%d\n", __func__, + reg, M4SH_REG__NUM, num, M4SH_MAX_REG_SIZE, + m4sensorhub_mapsize(reg)); + } + return ret; +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_read_n); + +/* m4sensorhub_reg_write() + + Write data to a register in the M4 sensor hub. Use + m4sensorhub_reg_write() instead where possible; + + Returns number of bytes written on success. + Returns negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct + reg - Register to be written to + value - array of data to write. Needs to be at least register's size + mask - mask representing which bits to change in register. If all bits + are to be changed, then &m4sh_no_mask can be passed here. + num - number of bytes to write +*/ + +int m4sensorhub_reg_write_n(struct m4sensorhub_data *m4sensorhub, + enum m4sensorhub_reg reg, unsigned char *value, + unsigned char *mask, short num) +{ + int i, ret = -EINVAL; + u8 stack_buf[M4SH_MAX_STACK_BUF_SIZE]; + + if (!m4sensorhub || !value || !num) { + KDEBUG(M4SH_ERROR, "%s() invalid parameter\n", __func__); + return ret; + } + + if ((reg < M4SH_REG__NUM) && num <= M4SH_MAX_REG_SIZE &&\ + register_info_tbl[reg].offset + num <= + m4sensorhub_mapsize(reg)) { + u8 *buf = (num > (M4SH_MAX_STACK_BUF_SIZE-2))\ + ? kmalloc(num+2, GFP_KERNEL) : stack_buf; + if (!buf) { + KDEBUG(M4SH_ERROR, "%s() Failed alloc %d memeory\n"\ + , __func__, num+2); + return -ENOMEM; + } + + buf[0] = register_info_tbl[reg].type; + buf[1] = register_info_tbl[reg].offset; + + mutex_lock(®_access); + if (mask) { + ret = m4sensorhub_i2c_write_read(m4sensorhub, buf, + 2, num); + if (ret != num) { + KDEBUG(M4SH_ERROR, "%s() register read" + "failure\n", __func__); + goto error; + } + /* move data right 2 positions and apply mask and + new data to prepare for writeback */ + for (i = num-1; i >= 0; i--) { + buf[i+2] = (buf[i] & ~mask[i]) | + (value[i] & mask[i]); + } + buf[0] = register_info_tbl[reg].type; + buf[1] = register_info_tbl[reg].offset; + } else + memcpy(&buf[2], value, num); + + ret = m4sensorhub_i2c_write_read(m4sensorhub, buf, + num + 2, 0); + if (ret != num + 2) { + KDEBUG(M4SH_ERROR, "%s() register write failure\n", + __func__); + ret = -EINVAL; + } else + ret -= 2; + +error: mutex_unlock(®_access); + if (buf != stack_buf) + kfree(buf); + } else { + KDEBUG(M4SH_ERROR, "%s() invalid register access reg=%d" + " maxreg=%d num=%d maxsze=%d mapsize=%d\n", __func__, + reg, M4SH_REG__NUM, num, M4SH_MAX_REG_SIZE, + m4sensorhub_mapsize(reg)); + } + + return ret; +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_write_n); + +/* m4sensorhub_reg_write_1byte() + + Write data to a 1 byte register in the M4 sensor hub. Avoids need to pass + data and mask by reference + + Returns number of bytes written on success. + Returns negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct + reg - Register to be written to + value - byte of data to write + mask - mask representing which bits to change in register. +*/ + +int m4sensorhub_reg_write_1byte(struct m4sensorhub_data *m4sensorhub, + enum m4sensorhub_reg reg, unsigned char value, + unsigned char mask) +{ + if (register_info_tbl[reg].size == 1) + return m4sensorhub_reg_write(m4sensorhub, reg, &value, &mask); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_write_1byte); + +/* m4sensorhub_reg_getsize() + + Get the size of an M4 register + + Returns size of register on success + Returns negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct + reg - Register to get size of +*/ + +int m4sensorhub_reg_getsize(struct m4sensorhub_data *m4sensorhub, + enum m4sensorhub_reg reg) +{ + if (reg < M4SH_REG__NUM) + return register_info_tbl[reg].size; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_getsize); + +/* m4sensorhub_reg_access_lock() + + Lock reg access to avoid broken I2C transmit process + +*/ + +void m4sensorhub_reg_access_lock(void) +{ + mutex_lock(®_access); +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_access_lock); + +/* m4sensorhub_reg_access_unlock() + + Unlock reg access to wake up blocked I2C transmit process + +*/ + +void m4sensorhub_reg_access_unlock(void) +{ + mutex_unlock(®_access); +} +EXPORT_SYMBOL_GPL(m4sensorhub_reg_access_unlock); + +/* m4sensorhub_i2c_write_read() + + Directly I2C access to communicate with the M4 sensor hub. + It always read after write if both write and read length are non-zero + + Returns number of bytes write on success if readlen is zero + Returns number of bytes read on success if readlen is non-zero + Returns negative error code on failure + + m4sensorhub - pointer to the main m4sensorhub data struct + buf - buffer to be used for write to and read from + writelen - number of bytes write to + readlen - number of bytes read from +*/ + +int m4sensorhub_i2c_write_read(struct m4sensorhub_data *m4sensorhub, + u8 *buf, int writelen, int readlen) +{ + int i, msglen, msgstart, err, tries = 0; + char buffer[DEBUG_LINE_LENGTH]; + struct i2c_msg msgs[] = { + { + .addr = m4sensorhub->i2c_client->addr, + .flags = m4sensorhub->i2c_client->flags, + .len = writelen, + .buf = buf, + }, + { + .addr = m4sensorhub->i2c_client->addr, + .flags = m4sensorhub->i2c_client->flags | I2C_M_RD, + .len = readlen, + .buf = buf, + }, + }; + + if (buf == NULL || (writelen == 0 && readlen == 0)) + return -EFAULT; + + /* Offset and size in msgs array depending on msg type */ + msglen = (writelen && readlen) ? 2 : 1; + msgstart = writelen ? 0 : 1; + + if (m4sensorhub_debug >= M4SH_VERBOSE_DEBUG && writelen) { + sprintf(buffer, "Writing to M4:"); + for (i = 0; i < writelen; i++) { + if (strlen(buffer) >= DEBUG_LINE_LENGTH-5) { + KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", buffer); + buffer[0] = '\0'; + } + sprintf(&buffer[strlen(buffer)], " 0x%02x", buf[i]); + } + KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", buffer); + } + + do { + err = i2c_transfer(m4sensorhub->i2c_client->adapter, + &msgs[msgstart], msglen); + if (err != msglen) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != msglen) && (++tries < I2C_RETRIES)); + if (err != msglen) { + dev_err(&m4sensorhub->i2c_client->dev, "i2c transfer error; " + "type=%d offset=%d\n", buf[0], buf[1]); + err = -EIO; + } else { + err = (readlen ? readlen : writelen); + + if (m4sensorhub_debug >= M4SH_VERBOSE_DEBUG && readlen) { + sprintf(buffer, "Read from M4:"); + for (i = 0; i < readlen; i++) { + if (strlen(buffer) >= DEBUG_LINE_LENGTH-5) { + KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", + buffer); + buffer[0] = '\0'; + } + sprintf(&buffer[strlen(buffer)], " 0x%02x", + buf[i]); + } + KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", buffer); + } + } + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_i2c_write_read); + +/* -------------- Local Functions ----------------- */ + +static int m4sensorhub_mapsize(enum m4sensorhub_reg reg) +{ + int retval = -EINVAL; + + if (reg < M4SH_REG__NUM) + retval = bank_size_tbl[register_info_tbl[reg].type]; + + return retval; +} |