diff options
Diffstat (limited to 'drivers/staging/media/atomisp/i2c/imx/otp_imx.c')
-rw-r--r-- | drivers/staging/media/atomisp/i2c/imx/otp_imx.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/drivers/staging/media/atomisp/i2c/imx/otp_imx.c b/drivers/staging/media/atomisp/i2c/imx/otp_imx.c new file mode 100644 index 000000000000..1ca27c26ef75 --- /dev/null +++ b/drivers/staging/media/atomisp/i2c/imx/otp_imx.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2013 Intel Corporation. All Rights Reserved. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <media/v4l2-device.h> +#include <asm/intel-mid.h> +#include "common.h" + +/* Defines for OTP Data Registers */ +#define IMX_OTP_START_ADDR 0x3B04 +#define IMX_OTP_PAGE_SIZE 64 +#define IMX_OTP_READY_REG 0x3B01 +#define IMX_OTP_PAGE_REG 0x3B02 +#define IMX_OTP_MODE_REG 0x3B00 +#define IMX_OTP_PAGE_MAX 20 +#define IMX_OTP_READY_REG_DONE 1 +#define IMX_OTP_READ_ONETIME 32 +#define IMX_OTP_MODE_READ 1 +#define IMX227_OTP_START_ADDR 0x0A04 +#define IMX227_OTP_ENABLE_REG 0x0A00 +#define IMX227_OTP_READY_REG 0x0A01 +#define IMX227_OTP_PAGE_REG 0x0A02 +#define IMX227_OTP_READY_REG_DONE 1 +#define IMX227_OTP_MODE_READ 1 + +static int +imx_read_otp_data(struct i2c_client *client, u16 len, u16 reg, void *val) +{ + struct i2c_msg msg[2]; + u16 data[IMX_SHORT_MAX] = { 0 }; + int err; + + if (len > IMX_BYTE_MAX) { + dev_err(&client->dev, "%s error, invalid data length\n", + __func__); + return -EINVAL; + } + + memset(msg, 0 , sizeof(msg)); + memset(data, 0 , sizeof(data)); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = I2C_MSG_LENGTH; + msg[0].buf = (u8 *)data; + /* high byte goes first */ + data[0] = cpu_to_be16(reg); + + msg[1].addr = client->addr; + msg[1].len = len; + msg[1].flags = I2C_M_RD; + msg[1].buf = (u8 *)data; + + err = i2c_transfer(client->adapter, msg, 2); + if (err != 2) { + if (err >= 0) + err = -EIO; + goto error; + } + + memcpy(val, data, len); + return 0; + +error: + dev_err(&client->dev, "read from offset 0x%x error %d", reg, err); + return err; +} + +static int imx_read_otp_reg_array(struct i2c_client *client, u16 size, u16 addr, + u8 *buf) +{ + u16 index; + int ret; + + for (index = 0; index + IMX_OTP_READ_ONETIME <= size; + index += IMX_OTP_READ_ONETIME) { + ret = imx_read_otp_data(client, IMX_OTP_READ_ONETIME, + addr + index, &buf[index]); + if (ret) + return ret; + } + return 0; +} + +void *imx_otp_read(struct v4l2_subdev *sd, u8 dev_addr, + u32 start_addr, u32 size) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 *buf; + int ret; + int i; + + buf = devm_kzalloc(&client->dev, size, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < IMX_OTP_PAGE_MAX; i++) { + + /*set page NO.*/ + ret = imx_write_reg(client, IMX_8BIT, + IMX_OTP_PAGE_REG, i & 0xff); + if (ret) + goto fail; + + /*set read mode*/ + ret = imx_write_reg(client, IMX_8BIT, + IMX_OTP_MODE_REG, IMX_OTP_MODE_READ); + if (ret) + goto fail; + + /* Reading the OTP data array */ + ret = imx_read_otp_reg_array(client, IMX_OTP_PAGE_SIZE, + IMX_OTP_START_ADDR, buf + i * IMX_OTP_PAGE_SIZE); + if (ret) + goto fail; + } + + return buf; +fail: + /* Driver has failed to find valid data */ + dev_err(&client->dev, "sensor found no valid OTP data\n"); + return ERR_PTR(ret); +} + +void *imx227_otp_read(struct v4l2_subdev *sd, u8 dev_addr, + u32 start_addr, u32 size) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 *buf; + int ret; + int i; + + buf = devm_kzalloc(&client->dev, size, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < IMX_OTP_PAGE_MAX; i++) { + + /*set page NO.*/ + ret = imx_write_reg(client, IMX_8BIT, + IMX227_OTP_PAGE_REG, i & 0xff); + if (ret) + goto fail; + + /*set read mode*/ + ret = imx_write_reg(client, IMX_8BIT, + IMX227_OTP_ENABLE_REG, IMX227_OTP_MODE_READ); + if (ret) + goto fail; + + /* Reading the OTP data array */ + ret = imx_read_otp_reg_array(client, IMX_OTP_PAGE_SIZE, + IMX227_OTP_START_ADDR, buf + i * IMX_OTP_PAGE_SIZE); + if (ret) + goto fail; + } + + return buf; +fail: + /* Driver has failed to find valid data */ + dev_err(&client->dev, "sensor found no valid OTP data\n"); + return ERR_PTR(ret); +} + |