744 lines
18 KiB
C
744 lines
18 KiB
C
|
/*
|
||
|
* Copyright (C) 2017-2020 NVIDIA Corporation. All rights reserved.
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* 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/debugfs.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/i2c-algo-bit.h>
|
||
|
#include <linux/list.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/slab.h>
|
||
|
|
||
|
#include "soc/tegra/tegra-ivc-rpc.h"
|
||
|
#include "soc/tegra/tegra-i2c-rtcpu.h"
|
||
|
#include "soc/tegra/camrtc-i2c-common.h"
|
||
|
#include "i2c-rtcpu-common.h"
|
||
|
|
||
|
/*
|
||
|
* I2C IVC Multi driver internal data structure
|
||
|
*/
|
||
|
|
||
|
#define TEGRA_I2C_MULTI_MAX_DEV 4
|
||
|
|
||
|
#define I2C_CAMRTC_RPC_IVC_MULTI_TIMEOUT_MS 250
|
||
|
|
||
|
struct tegra_i2c_ivc_multi_dev {
|
||
|
/* IVC RPC */
|
||
|
struct tegra_ivc_channel *chan;
|
||
|
bool is_valid;
|
||
|
bool is_failed;
|
||
|
bool is_added;
|
||
|
bool is_online;
|
||
|
|
||
|
/* I2C */
|
||
|
u32 bus_id;
|
||
|
/* parameters from DT */
|
||
|
u32 reg_base;
|
||
|
u32 bus_clk_rate;
|
||
|
|
||
|
struct list_head sensors;
|
||
|
/* statistics */
|
||
|
struct {
|
||
|
unsigned int xfer_requests;
|
||
|
unsigned int total_bytes;
|
||
|
unsigned int reads, read_bytes;
|
||
|
unsigned int writes, write_bytes;
|
||
|
unsigned int errors;
|
||
|
} stat;
|
||
|
};
|
||
|
|
||
|
static struct tegra_i2c_ivc_multi_dev
|
||
|
g_i2c_i2c_ivc_devs[TEGRA_I2C_MULTI_MAX_DEV];
|
||
|
|
||
|
/*
|
||
|
* Per sensor data structure
|
||
|
*/
|
||
|
|
||
|
struct tegra_i2c_rtcpu_sensor {
|
||
|
struct list_head node;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev;
|
||
|
bool is_registered;
|
||
|
unsigned int sensor_id;
|
||
|
/* config */
|
||
|
struct tegra_i2c_rtcpu_config config;
|
||
|
/* from device tree */
|
||
|
unsigned int addr;
|
||
|
unsigned int flag;
|
||
|
unsigned int mp_type;
|
||
|
unsigned int mp_addr;
|
||
|
unsigned int mp_channel;
|
||
|
/* runtime information */
|
||
|
bool in_agg; /* in the middle of aggregation */
|
||
|
unsigned int last_addr; /* is_valid if in_agg. last register address */
|
||
|
int frame_id; /* is_valid if in_agg. frame ID */
|
||
|
/* RPC call for I2C_REQUEST_MULTI */
|
||
|
unsigned int req_len;
|
||
|
u8 *req_cur, *req_last_len;
|
||
|
struct tegra_ivc_rpc_call_param rpc_i2c_req;
|
||
|
u8 rpc_i2c_req_buf[CAMRTC_I2C_REQUEST_MAX_LEN];
|
||
|
struct camrtc_rpc_i2c_response rpc_i2c_rsp;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* Sensor APIs
|
||
|
*/
|
||
|
static int tegra_i2c_ivc_register_sensor(struct tegra_ivc_channel *chan,
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor);
|
||
|
|
||
|
struct tegra_i2c_rtcpu_sensor *tegra_i2c_rtcpu_register_sensor(
|
||
|
struct i2c_client *client,
|
||
|
const struct tegra_i2c_rtcpu_config *config)
|
||
|
{
|
||
|
int i, ret;
|
||
|
u32 data_u32;
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor;
|
||
|
struct device_node *node = client->dev.of_node;
|
||
|
struct device_node *np_mux, *np_i2c;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev;
|
||
|
|
||
|
if (config->reg_bytes <= 0 || config->reg_bytes > 2)
|
||
|
return NULL;
|
||
|
|
||
|
sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
|
||
|
if (sensor == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
sensor->config = *config;
|
||
|
|
||
|
/* Example of a Sensor node
|
||
|
* behind an I2C multiplexer: /i2c@3180000/tca9548@77/i2c@0/ov5693_a@36
|
||
|
* without an I2C multiplexer: /i2c@3180000/ov5693_c@36
|
||
|
*/
|
||
|
|
||
|
/* Sensor node */
|
||
|
ret = of_property_read_u32(node, "reg", &data_u32);
|
||
|
if (ret)
|
||
|
goto fail;
|
||
|
sensor->addr = data_u32;
|
||
|
|
||
|
if (of_property_read_bool(node, "i2c-10bits"))
|
||
|
sensor->flag |= CAMRTC_I2C_SENSOR_FLAG_TEN;
|
||
|
if (of_property_read_bool(node, "i2c-fast-mode-plus"))
|
||
|
sensor->flag |= CAMRTC_I2C_SENSOR_FLAG_FM_PLUS;
|
||
|
if (of_property_read_bool(node, "i2c-high-speed-mode"))
|
||
|
sensor->flag |= CAMRTC_I2C_SENSOR_FLAG_HS;
|
||
|
|
||
|
/* Parent of sensor is either I2C bus, or a channel under multiplexer.
|
||
|
* Detect a multiplexer.
|
||
|
*/
|
||
|
np_i2c = of_get_parent(node);
|
||
|
np_mux = of_get_parent(np_i2c);
|
||
|
|
||
|
sensor->mp_type = CAMRTC_I2C_MP_NONE;
|
||
|
ret = of_property_read_u32(np_mux, "nvidia,camrtc-mux-type",
|
||
|
&data_u32);
|
||
|
|
||
|
if (ret == 0) {
|
||
|
sensor->mp_type = data_u32;
|
||
|
|
||
|
/* Information about the multiplexer */
|
||
|
if (sensor->mp_type != CAMRTC_I2C_MP_NONE) {
|
||
|
ret = of_property_read_u32(np_i2c, "reg",
|
||
|
&data_u32);
|
||
|
if (ret)
|
||
|
goto fail;
|
||
|
sensor->mp_channel = data_u32;
|
||
|
|
||
|
ret = of_property_read_u32(np_mux, "reg",
|
||
|
&data_u32);
|
||
|
if (ret)
|
||
|
goto fail;
|
||
|
|
||
|
sensor->mp_addr = data_u32;
|
||
|
np_i2c = of_get_parent(np_mux);
|
||
|
}
|
||
|
} else
|
||
|
np_mux = NULL;
|
||
|
|
||
|
/* Detect whether to enable I2C multi */
|
||
|
if (!of_property_read_bool(np_i2c, "nvidia,camrtc-use-multi"))
|
||
|
goto fail;
|
||
|
|
||
|
/* I2C controller address */
|
||
|
ret = of_property_read_u32_index(np_i2c, "reg", 1, &data_u32);
|
||
|
if (ret)
|
||
|
goto fail;
|
||
|
|
||
|
/* Find an IVC channel */
|
||
|
for (i = 0; i < TEGRA_I2C_MULTI_MAX_DEV; ++i) {
|
||
|
if (g_i2c_i2c_ivc_devs[i].is_valid) {
|
||
|
if (g_i2c_i2c_ivc_devs[i].reg_base == (u32) data_u32) {
|
||
|
if (g_i2c_i2c_ivc_devs[i].is_failed)
|
||
|
goto fail;
|
||
|
break;
|
||
|
}
|
||
|
} else
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
if (i == TEGRA_I2C_MULTI_MAX_DEV)
|
||
|
goto fail;
|
||
|
|
||
|
i2c_ivc_dev = g_i2c_i2c_ivc_devs + i;
|
||
|
sensor->i2c_ivc_dev = i2c_ivc_dev;
|
||
|
|
||
|
/* Add i2c device */
|
||
|
ret = tegra_i2c_ivc_register_sensor(i2c_ivc_dev->chan, sensor);
|
||
|
if (ret != 0)
|
||
|
goto fail;
|
||
|
|
||
|
/* Sensor information */
|
||
|
if (sensor->mp_type != CAMRTC_I2C_MP_NONE) {
|
||
|
dev_info(&i2c_ivc_dev->chan->dev,
|
||
|
"Bus: %u, Multiplexer type: %u, Address: 0x%x:%u:0x%x\n",
|
||
|
i2c_ivc_dev->bus_id,
|
||
|
sensor->mp_type,
|
||
|
sensor->mp_addr, sensor->mp_channel, sensor->addr);
|
||
|
} else {
|
||
|
dev_info(&i2c_ivc_dev->chan->dev,
|
||
|
"Bus: %u, Address: 0x%x\n",
|
||
|
i2c_ivc_dev->bus_id,
|
||
|
sensor->addr);
|
||
|
}
|
||
|
|
||
|
list_add_tail(&sensor->node, &i2c_ivc_dev->sensors);
|
||
|
return sensor;
|
||
|
|
||
|
fail:
|
||
|
kfree(sensor);
|
||
|
return NULL;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tegra_i2c_rtcpu_register_sensor);
|
||
|
|
||
|
/*
|
||
|
* I2C transfer
|
||
|
*/
|
||
|
|
||
|
static int tegra_i2c_ivc_multi_xfer(
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
tegra_ivc_channel_runtime_get(sensor->i2c_ivc_dev->chan);
|
||
|
|
||
|
if (sensor->req_len == CAMRTC_I2C_MULTI_HEADER_SIZE) {
|
||
|
ret = 0;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (!sensor->is_registered) {
|
||
|
unsigned int req_len = sensor->req_len;
|
||
|
|
||
|
ret = tegra_i2c_ivc_register_sensor(sensor->i2c_ivc_dev->chan,
|
||
|
sensor);
|
||
|
|
||
|
if (ret != 0)
|
||
|
goto exit;
|
||
|
|
||
|
/* restore len since register sensor inits to a differnet
|
||
|
* value, which is used during first init at boot
|
||
|
*/
|
||
|
sensor->req_len = req_len;
|
||
|
}
|
||
|
|
||
|
sensor->rpc_i2c_req_buf[1] = (sensor->frame_id > 0) ?
|
||
|
CAMRTC_I2C_REQUEST_MULTI_FLAG_FRAMEID : 0;
|
||
|
sensor->rpc_i2c_req_buf[2] = (sensor->frame_id >> 0) & 0xff;
|
||
|
sensor->rpc_i2c_req_buf[3] = (sensor->frame_id >> 8) & 0xff;
|
||
|
|
||
|
sensor->rpc_i2c_req.request_len = sensor->req_len;
|
||
|
ret = tegra_ivc_rpc_call(sensor->i2c_ivc_dev->chan,
|
||
|
&sensor->rpc_i2c_req);
|
||
|
|
||
|
/* reset request buffer pointers */
|
||
|
sensor->last_addr = (unsigned int) -1;
|
||
|
sensor->req_len = CAMRTC_I2C_MULTI_HEADER_SIZE;
|
||
|
sensor->req_cur = sensor->rpc_i2c_req_buf +
|
||
|
CAMRTC_I2C_MULTI_HEADER_SIZE;
|
||
|
sensor->req_last_len = NULL;
|
||
|
|
||
|
if (ret < 0) {
|
||
|
++sensor->i2c_ivc_dev->stat.errors;
|
||
|
dev_err(&sensor->i2c_ivc_dev->chan->dev,
|
||
|
"I2C transaction to sensor %u failed: %d\n",
|
||
|
sensor->sensor_id, ret);
|
||
|
ret = -EIO;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
tegra_ivc_channel_runtime_put(sensor->i2c_ivc_dev->chan);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* I2C APIs
|
||
|
*/
|
||
|
|
||
|
int tegra_i2c_rtcpu_aggregate(
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor,
|
||
|
bool start)
|
||
|
{
|
||
|
if (sensor->in_agg) {
|
||
|
if (!start) {
|
||
|
sensor->in_agg = false;
|
||
|
return tegra_i2c_ivc_multi_xfer(sensor);
|
||
|
}
|
||
|
} else {
|
||
|
sensor->in_agg = start;
|
||
|
sensor->last_addr = (unsigned int) -1;
|
||
|
sensor->frame_id = -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tegra_i2c_rtcpu_aggregate);
|
||
|
|
||
|
int tegra_i2c_rtcpu_set_frame_id(
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor,
|
||
|
int frame_id)
|
||
|
{
|
||
|
if (!sensor->in_agg)
|
||
|
return -EINVAL;
|
||
|
|
||
|
sensor->frame_id = frame_id;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tegra_i2c_rtcpu_set_frame_id);
|
||
|
|
||
|
int tegra_i2c_rtcpu_read_reg8(
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor,
|
||
|
unsigned int addr,
|
||
|
u8 *data,
|
||
|
unsigned int count)
|
||
|
{
|
||
|
int ret;
|
||
|
int this_len;
|
||
|
u8 *req;
|
||
|
|
||
|
/* Read requires writing address first */
|
||
|
this_len = (CAMRTC_I2C_MULTI_DATA_OFFSET * 2) +
|
||
|
sensor->config.reg_bytes;
|
||
|
|
||
|
/* If there is no room, flush current transfer */
|
||
|
if (sensor->req_len + this_len > CAMRTC_I2C_REQUEST_MAX_LEN) {
|
||
|
ret = tegra_i2c_ivc_multi_xfer(sensor);
|
||
|
if (ret != 0)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Write register address */
|
||
|
req = sensor->req_cur;
|
||
|
|
||
|
if (!sensor->in_agg || sensor->last_addr != addr) {
|
||
|
*req++ = 0;
|
||
|
*req++ = (u8) sensor->config.reg_bytes;
|
||
|
if (sensor->config.reg_bytes == 2)
|
||
|
*req++ = (u8) (addr >> 8);
|
||
|
*req++ = (u8) addr;
|
||
|
sensor->req_len += CAMRTC_I2C_MULTI_DATA_OFFSET +
|
||
|
sensor->config.reg_bytes;
|
||
|
}
|
||
|
|
||
|
/* Read register */
|
||
|
*req++ = CAMRTC_I2C_REQUEST_FLAG_READ;
|
||
|
*req = (u8) count;
|
||
|
sensor->req_len += CAMRTC_I2C_MULTI_DATA_OFFSET;
|
||
|
|
||
|
/* Read transaction always start a transaction */
|
||
|
ret = tegra_i2c_ivc_multi_xfer(sensor);
|
||
|
if (ret == 0)
|
||
|
memcpy(data, sensor->rpc_i2c_rsp.read_data, count);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tegra_i2c_rtcpu_read_reg8);
|
||
|
|
||
|
int tegra_i2c_rtcpu_write_reg8(
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor,
|
||
|
unsigned int addr,
|
||
|
const u8 *data,
|
||
|
unsigned int count)
|
||
|
{
|
||
|
int ret;
|
||
|
int this_len;
|
||
|
u8 *req;
|
||
|
|
||
|
this_len = CAMRTC_I2C_MULTI_DATA_OFFSET +
|
||
|
sensor->config.reg_bytes + count;
|
||
|
|
||
|
/* If there is no room, flush current transfer */
|
||
|
if (sensor->req_len + this_len > CAMRTC_I2C_REQUEST_MAX_LEN) {
|
||
|
ret = tegra_i2c_ivc_multi_xfer(sensor);
|
||
|
if (ret != 0)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Write transfer */
|
||
|
req = sensor->req_cur;
|
||
|
|
||
|
if (!sensor->in_agg || sensor->last_addr != addr) {
|
||
|
*req++ = 0;
|
||
|
sensor->req_last_len = req;
|
||
|
*req++ = (u8) (sensor->config.reg_bytes + count);
|
||
|
if (sensor->config.reg_bytes == 2)
|
||
|
*req++ = (u8) (addr >> 8);
|
||
|
*req++ = (u8) addr;
|
||
|
sensor->req_len += CAMRTC_I2C_MULTI_DATA_OFFSET +
|
||
|
sensor->config.reg_bytes;
|
||
|
} else {
|
||
|
/* append to previous transfer */
|
||
|
*sensor->req_last_len += (u8) count;
|
||
|
}
|
||
|
|
||
|
switch (count) {
|
||
|
case 4:
|
||
|
*req++ = *data++;
|
||
|
/* fallthrough */
|
||
|
case 3:
|
||
|
*req++ = *data++;
|
||
|
/* fallthrough */
|
||
|
case 2:
|
||
|
*req++ = *data++;
|
||
|
/* fallthrough */
|
||
|
case 1:
|
||
|
*req++ = *data++;
|
||
|
/* fallthrough */
|
||
|
case 0:
|
||
|
break;
|
||
|
default:
|
||
|
memcpy(req, data, count);
|
||
|
req += count;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
sensor->req_cur = req;
|
||
|
sensor->req_len += count;
|
||
|
sensor->last_addr = addr + count;
|
||
|
|
||
|
if (!sensor->in_agg)
|
||
|
return tegra_i2c_ivc_multi_xfer(sensor);
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tegra_i2c_rtcpu_write_reg8);
|
||
|
|
||
|
int tegra_i2c_rtcpu_write_table_8(
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor,
|
||
|
const struct reg_8 table[],
|
||
|
const struct reg_8 override_list[],
|
||
|
int num_override_regs, u16 wait_ms_addr, u16 end_addr)
|
||
|
{
|
||
|
const struct reg_8 *next;
|
||
|
int i;
|
||
|
|
||
|
tegra_i2c_rtcpu_aggregate(sensor, true);
|
||
|
|
||
|
for (next = table;; ++next) {
|
||
|
u8 val;
|
||
|
|
||
|
if (next->addr == end_addr)
|
||
|
break;
|
||
|
if (next->addr == wait_ms_addr) {
|
||
|
tegra_i2c_rtcpu_aggregate(sensor, false);
|
||
|
msleep_range(next->val);
|
||
|
tegra_i2c_rtcpu_aggregate(sensor, true);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
val = next->val;
|
||
|
|
||
|
if (override_list) {
|
||
|
for (i = 0; i < num_override_regs; ++i) {
|
||
|
if (next->addr == override_list[i].addr) {
|
||
|
val = override_list[i].val;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tegra_i2c_rtcpu_write_reg8(sensor,
|
||
|
next->addr, &val, 1);
|
||
|
}
|
||
|
|
||
|
return tegra_i2c_rtcpu_aggregate(sensor, false);
|
||
|
}
|
||
|
EXPORT_SYMBOL(tegra_i2c_rtcpu_write_table_8);
|
||
|
|
||
|
/*
|
||
|
* IVC channel Debugfs
|
||
|
*/
|
||
|
|
||
|
#define DEFINE_SEQ_FOPS(_fops_, _show_) \
|
||
|
static int _fops_ ## _open(struct inode *inode, struct file *file) \
|
||
|
{ \
|
||
|
return single_open(file, _show_, inode->i_private); \
|
||
|
} \
|
||
|
static const struct file_operations _fops_ = { \
|
||
|
.open = _fops_ ## _open, \
|
||
|
.read = seq_read, \
|
||
|
.llseek = seq_lseek, \
|
||
|
.release = single_release }
|
||
|
|
||
|
static int tegra_i2c_ivc_multi_stat_show(
|
||
|
struct seq_file *file, void *data)
|
||
|
{
|
||
|
struct tegra_ivc_channel *chan = file->private;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev =
|
||
|
tegra_ivc_channel_get_drvdata(chan);
|
||
|
|
||
|
seq_printf(file, "Xfer requests: %u\n",
|
||
|
i2c_ivc_dev->stat.xfer_requests);
|
||
|
seq_printf(file, "Total bytes: %u\n", i2c_ivc_dev->stat.total_bytes);
|
||
|
seq_printf(file, "Read requests: %u\n", i2c_ivc_dev->stat.reads);
|
||
|
seq_printf(file, "Read bytes: %u\n", i2c_ivc_dev->stat.read_bytes);
|
||
|
seq_printf(file, "Write requests: %u\n", i2c_ivc_dev->stat.writes);
|
||
|
seq_printf(file, "Write bytes: %u\n", i2c_ivc_dev->stat.write_bytes);
|
||
|
seq_printf(file, "Errors: %u\n", i2c_ivc_dev->stat.errors);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DEFINE_SEQ_FOPS(tegra_i2c_ivc_debugfs_stats,
|
||
|
tegra_i2c_ivc_multi_stat_show);
|
||
|
|
||
|
static void tegra_i2c_ivc_multi_create_debugfs(
|
||
|
struct tegra_ivc_channel *chan,
|
||
|
struct dentry *debugfs_root)
|
||
|
{
|
||
|
debugfs_create_file("stats", 0444,
|
||
|
debugfs_root, chan,
|
||
|
&tegra_i2c_ivc_debugfs_stats);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* IVC channel driver interface
|
||
|
*/
|
||
|
static int tegra_i2c_ivc_add_multi(struct tegra_ivc_channel *chan)
|
||
|
{
|
||
|
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev =
|
||
|
tegra_ivc_channel_get_drvdata(chan);
|
||
|
struct camrtc_rpc_i2c_add_multi rpc_add_multi;
|
||
|
u32 bus_id;
|
||
|
int ret;
|
||
|
|
||
|
rpc_add_multi.reg_base = i2c_ivc_dev->reg_base;
|
||
|
|
||
|
/* Register an I2C device to CamRTC */
|
||
|
ret = tegra_ivc_rpc_call_pl(chan,
|
||
|
CAMRTC_RPC_REQ_I2C_ADD_MULTI_DEV,
|
||
|
sizeof(rpc_add_multi), &rpc_add_multi,
|
||
|
TEGRA_IVC_RPC_RSP_RET_CODE,
|
||
|
sizeof(bus_id), &bus_id,
|
||
|
NULL, NULL, 0);
|
||
|
if (ret < 0) {
|
||
|
dev_err(&chan->dev,
|
||
|
"failed to register an I2C device at 0x%08x: %d\n",
|
||
|
i2c_ivc_dev->reg_base, ret);
|
||
|
ret = -EIO;
|
||
|
goto fail_remove_chan;
|
||
|
}
|
||
|
|
||
|
dev_info(&chan->dev,
|
||
|
"Registered an I2C multi device at 0x%08x to bus %d\n",
|
||
|
i2c_ivc_dev->reg_base, bus_id);
|
||
|
|
||
|
i2c_ivc_dev->bus_id = bus_id;
|
||
|
i2c_ivc_dev->is_added = true;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
fail_remove_chan:
|
||
|
tegra_ivc_rpc_channel_remove(chan);
|
||
|
i2c_ivc_dev->is_failed = true;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int tegra_i2c_ivc_add_sensor(struct tegra_ivc_channel *chan,
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
u32 sensor_id;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev =
|
||
|
tegra_ivc_channel_get_drvdata(chan);
|
||
|
struct camrtc_rpc_i2c_add_sensor rpc_add_sensor = {0};
|
||
|
|
||
|
WARN_ON(!chan);
|
||
|
WARN_ON(!i2c_ivc_dev->is_added);
|
||
|
|
||
|
rpc_add_sensor.bus_id = i2c_ivc_dev->bus_id;
|
||
|
rpc_add_sensor.addr = sensor->addr;
|
||
|
rpc_add_sensor.flag = sensor->flag;
|
||
|
rpc_add_sensor.mp_type = sensor->mp_type;
|
||
|
rpc_add_sensor.mp_addr = sensor->mp_addr;
|
||
|
rpc_add_sensor.mp_channel = sensor->mp_channel;
|
||
|
|
||
|
ret = tegra_ivc_rpc_call_pl(chan,
|
||
|
CAMRTC_RPC_REQ_I2C_ADD_SENSOR,
|
||
|
sizeof(rpc_add_sensor), &rpc_add_sensor,
|
||
|
TEGRA_IVC_RPC_RSP_RET_CODE,
|
||
|
sizeof(sensor_id), &sensor_id,
|
||
|
NULL, NULL, 0);
|
||
|
if (ret < 0) {
|
||
|
dev_err(&chan->dev,
|
||
|
"Failed to register a sensor: %d\n", ret);
|
||
|
ret = -EIO;
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
sensor->sensor_id = sensor_id;
|
||
|
dev_info(&chan->dev,
|
||
|
"Registered a sensor as ID: %u\n", sensor_id);
|
||
|
|
||
|
/* Initialization of runtime field */
|
||
|
sensor->in_agg = false;
|
||
|
sensor->last_addr = (unsigned int) -1;
|
||
|
sensor->req_len = CAMRTC_I2C_MULTI_HEADER_SIZE;
|
||
|
sensor->req_cur = sensor->rpc_i2c_req_buf +
|
||
|
CAMRTC_I2C_MULTI_HEADER_SIZE;
|
||
|
sensor->req_last_len = NULL;
|
||
|
|
||
|
sensor->rpc_i2c_req_buf[0] = (u8) sensor_id;
|
||
|
sensor->rpc_i2c_req.request_id = CAMRTC_RPC_REQ_I2C_REQUEST_MULTI;
|
||
|
sensor->rpc_i2c_req.request = sensor->rpc_i2c_req_buf;
|
||
|
sensor->rpc_i2c_req.response_id = CAMRTC_RPC_RSP_I2C_RESPONSE;
|
||
|
sensor->rpc_i2c_req.response_len = sizeof(sensor->rpc_i2c_rsp);
|
||
|
sensor->rpc_i2c_req.response = &sensor->rpc_i2c_rsp;
|
||
|
sensor->rpc_i2c_req.callback = NULL;
|
||
|
sensor->rpc_i2c_req.callback_param = NULL;
|
||
|
sensor->rpc_i2c_req.timeout_ms = I2C_CAMRTC_RPC_IVC_MULTI_TIMEOUT_MS;
|
||
|
sensor->is_registered = true;
|
||
|
|
||
|
return 0;
|
||
|
fail:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int tegra_i2c_ivc_register_sensor(struct tegra_ivc_channel *chan,
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev =
|
||
|
tegra_ivc_channel_get_drvdata(chan);
|
||
|
|
||
|
WARN_ON(!i2c_ivc_dev->is_online);
|
||
|
|
||
|
if (!i2c_ivc_dev->is_added) {
|
||
|
ret = tegra_i2c_ivc_add_multi(chan);
|
||
|
if (ret != 0) {
|
||
|
dev_err(&chan->dev,
|
||
|
"I2C device not added\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Add a sensor */
|
||
|
ret = tegra_i2c_ivc_add_sensor(chan,
|
||
|
sensor);
|
||
|
if (ret != 0)
|
||
|
dev_err(&chan->dev,
|
||
|
"I2C sensor not added\n");
|
||
|
|
||
|
fail:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void tegra_i2c_ivc_multi_ready(
|
||
|
struct tegra_ivc_channel *chan, bool online)
|
||
|
{
|
||
|
struct tegra_i2c_rtcpu_sensor *sensor = NULL;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev =
|
||
|
tegra_ivc_channel_get_drvdata(chan);
|
||
|
|
||
|
i2c_ivc_dev->is_online = online;
|
||
|
|
||
|
if (!online) {
|
||
|
i2c_ivc_dev->is_added = false;
|
||
|
|
||
|
list_for_each_entry(sensor, &i2c_ivc_dev->sensors, node)
|
||
|
sensor->is_registered = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static struct tegra_ivc_rpc_ops tegra_ivc_rpc_user_ops = {
|
||
|
.create_debugfs = tegra_i2c_ivc_multi_create_debugfs,
|
||
|
.ready = tegra_i2c_ivc_multi_ready,
|
||
|
};
|
||
|
|
||
|
static int tegra_ivc_rpc_i2c_multi_probe(struct tegra_ivc_channel *chan)
|
||
|
{
|
||
|
int ret;
|
||
|
int i;
|
||
|
struct tegra_i2c_ivc_multi_dev *i2c_ivc_dev;
|
||
|
struct device_node *i2c_node;
|
||
|
|
||
|
/* Find an empty slot */
|
||
|
for (i = 0; i < TEGRA_I2C_MULTI_MAX_DEV; ++i) {
|
||
|
if (!g_i2c_i2c_ivc_devs[i].is_valid)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (i == TEGRA_I2C_MULTI_MAX_DEV)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
i2c_ivc_dev = g_i2c_i2c_ivc_devs + i;
|
||
|
|
||
|
i2c_node = of_parse_phandle(chan->dev.of_node, "device", 0);
|
||
|
if (i2c_node == NULL) {
|
||
|
dev_err(&chan->dev, "Cannot get i2c device node");
|
||
|
ret = -ENODEV;
|
||
|
goto fail_free;
|
||
|
}
|
||
|
|
||
|
/* Register IVC/RPC channel */
|
||
|
ret = tegra_ivc_rpc_channel_probe(chan, &tegra_ivc_rpc_user_ops);
|
||
|
if (ret < 0) {
|
||
|
dev_err(&chan->dev, "Cannot start IVC/RPC interface");
|
||
|
goto fail_free;
|
||
|
}
|
||
|
|
||
|
i2c_ivc_dev->is_valid = true;
|
||
|
i2c_ivc_dev->is_added = false;
|
||
|
i2c_ivc_dev->is_failed = false;
|
||
|
i2c_ivc_dev->is_online = false;
|
||
|
i2c_ivc_dev->chan = chan;
|
||
|
i2c_ivc_dev->reg_base = tegra_i2c_get_reg_base(i2c_node);
|
||
|
i2c_ivc_dev->bus_clk_rate = tegra_i2c_get_clk_freq(i2c_node);
|
||
|
INIT_LIST_HEAD(&i2c_ivc_dev->sensors);
|
||
|
tegra_ivc_channel_set_drvdata(chan, i2c_ivc_dev);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
fail_free:
|
||
|
devm_kfree(&chan->dev, i2c_ivc_dev);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void tegra_ivc_rpc_i2c_multi_remove(struct tegra_ivc_channel *chan)
|
||
|
{
|
||
|
tegra_ivc_rpc_channel_remove(chan);
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id tegra_ivc_rpc_i2c_multi_of_match[] = {
|
||
|
{ .compatible = "nvidia,tegra186-camera-ivc-rpc-i2c-multi", },
|
||
|
{ },
|
||
|
};
|
||
|
TEGRA_IVC_RPC_DRIVER_DEFINE(i2c_multi, "tegra-ivc-rpc-i2c-multi")
|
||
|
|
||
|
MODULE_AUTHOR("Kai Lee <kailee@nvidia.com>");
|
||
|
MODULE_DESCRIPTION("NVIDIA Tegra CAMRTC I2C IVC multi driver");
|
||
|
MODULE_LICENSE("GPL");
|