| /* |
| * Copyright (C) 2012-2014 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_MIN 20 |
| #define I2C_RETRY_DELAY_MAX 50 |
| #define I2C_RETRIES 10 |
| |
| #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) |
| { |
| if (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } |
| |
| 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) |
| { |
| if (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } |
| |
| 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]; |
| u8 *buf = NULL; |
| |
| if (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } else if (value == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: value array is NULL\n", __func__); |
| return -ENODATA; |
| } else if (num == 0) { |
| KDEBUG(M4SH_ERROR, "%s: Bytes requested is 0\n", __func__); |
| return -EINVAL; |
| } |
| |
| if ((reg < M4SH_REG__NUM) && num <= M4SH_MAX_REG_SIZE && |
| register_info_tbl[reg].offset + num <= |
| m4sensorhub_mapsize(reg)) { |
| buf = (num > (M4SH_MAX_STACK_BUF_SIZE-2)) |
| ? kmalloc(num+2, GFP_KERNEL) : stack_buf; |
| if (!buf) { |
| KDEBUG(M4SH_ERROR, "%s() Failed alloc %d memory\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 (ret=%d)\n", |
| __func__, ret); |
| } 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 = 0; |
| int ret = -EINVAL; |
| u8 stack_buf[M4SH_MAX_STACK_BUF_SIZE]; |
| u8 *buf = NULL; |
| |
| if (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } else if (value == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: value array is NULL\n", __func__); |
| return -ENODATA; |
| } else if (num == 0) { |
| KDEBUG(M4SH_ERROR, "%s: Bytes requested is 0\n", __func__); |
| return -EINVAL; |
| } |
| |
| if ((reg < M4SH_REG__NUM) && num <= M4SH_MAX_REG_SIZE && |
| register_info_tbl[reg].offset + num <= |
| m4sensorhub_mapsize(reg)) { |
| buf = (num > (M4SH_MAX_STACK_BUF_SIZE-2)) |
| ? kmalloc(num+2, GFP_KERNEL) : stack_buf; |
| if (!buf) { |
| KDEBUG(M4SH_ERROR, "%s() Failed alloc %d memory\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 (ret=%d)\n", |
| __func__, ret); |
| 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 (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } |
| |
| if (register_info_tbl[reg].size == 1) { |
| if (mask == 0xFF) { |
| return m4sensorhub_reg_write( |
| m4sensorhub, reg, &value, NULL |
| ); |
| } else { |
| return m4sensorhub_reg_write( |
| m4sensorhub, reg, &value, &mask |
| ); |
| } |
| } else { |
| KDEBUG(M4SH_ERROR, "%s: Size is %hu instead of 1\n", |
| __func__, register_info_tbl[reg].size); |
| } |
| 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 (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } |
| |
| 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. |
| Reads are always after writes if both write and read lengths 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[2]; |
| |
| if (m4sensorhub == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: M4 data is NULL\n", __func__); |
| return -ENODATA; |
| } else if (buf == NULL) { |
| KDEBUG(M4SH_ERROR, "%s: Buffer is NULL\n", __func__); |
| return -ENODATA; |
| } else if ((writelen == 0) && (readlen == 0)) { |
| KDEBUG(M4SH_ERROR, "%s: Lengths are both 0\n", __func__); |
| return -EINVAL; |
| } |
| |
| msgs[0].addr = m4sensorhub->i2c_client->addr; |
| msgs[0].flags = m4sensorhub->i2c_client->flags; |
| msgs[0].len = writelen; |
| msgs[0].buf = buf; |
| |
| msgs[1].addr = m4sensorhub->i2c_client->addr; |
| msgs[1].flags = m4sensorhub->i2c_client->flags | I2C_M_RD; |
| msgs[1].len = readlen; |
| msgs[1].buf = buf; |
| |
| /* 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) { |
| /* TODO: Print on try 0 after M4 stops NACKing */ |
| if (tries != 0) { |
| KDEBUG(M4SH_ERROR, |
| "%s: Fail on try %d (err=%d)\n", |
| __func__, tries, err); |
| } |
| usleep_range(I2C_RETRY_DELAY_MIN, I2C_RETRY_DELAY_MAX); |
| } |
| } 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; |
| } |