media: camera: Build ISC as OOT module

Convert automotive ISC kernel driver to OOT module.

Jira CAMERASW-7592

Change-Id: I0a81b58df941ef38bf0fb3226086e13b6d68d012
Signed-off-by: Frank Chen <frankc@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2811344
Reviewed-by: Justin Kim (SW-TEGRA) <juskim@nvidia.com>
Reviewed-by: Shiva Dubey <sdubey@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Frank Chen
2022-11-17 11:26:13 -08:00
committed by mobile promotions
parent c126c2ee93
commit 968c9df390
15 changed files with 2948 additions and 0 deletions

View File

@@ -2,3 +2,4 @@
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
obj-m += cdi/ obj-m += cdi/
obj-m += isc/

View File

@@ -0,0 +1,6 @@
GCOV_PROFILE := y
obj-m += isc_gpio.o
obj-m += isc_mgr.o
obj-m += isc_dev.o
obj-m += isc_pwm.o

View File

@@ -0,0 +1,244 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/siginfo.h>
#include <media/isc-dev.h>
#include <media/isc-mgr.h>
#include "isc-mgr-priv.h"
#include "isc-dev-priv.h"
static int isc_mgr_status_show(struct seq_file *s, void *data)
{
struct isc_mgr_priv *isc_mgr = s->private;
struct isc_mgr_client *isc_dev;
if (isc_mgr == NULL)
return 0;
pr_info("%s - %s\n", __func__, isc_mgr->devname);
if (list_empty(&isc_mgr->dev_list)) {
seq_printf(s, "%s: No devices supported.\n", isc_mgr->devname);
return 0;
}
mutex_lock(&isc_mgr->mutex);
list_for_each_entry_reverse(isc_dev, &isc_mgr->dev_list, list) {
seq_printf(s, " %02d -- @0x%02x, %02d, %d, %s\n",
isc_dev->id,
isc_dev->cfg.addr,
isc_dev->cfg.reg_bits,
isc_dev->cfg.val_bits,
isc_dev->cfg.drv_name
);
}
mutex_unlock(&isc_mgr->mutex);
return 0;
}
static ssize_t isc_mgr_attr_set(struct file *s,
const char __user *user_buf, size_t count, loff_t *ppos)
{
return count;
}
static int isc_mgr_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, isc_mgr_status_show, inode->i_private);
}
static const struct file_operations isc_mgr_debugfs_fops = {
.open = isc_mgr_debugfs_open,
.read = seq_read,
.write = isc_mgr_attr_set,
.llseek = seq_lseek,
.release = single_release,
};
static int pwr_on_get(void *data, u64 *val)
{
struct isc_mgr_priv *isc_mgr = data;
if (isc_mgr->pdata == NULL || !isc_mgr->pdata->num_pwr_gpios) {
*val = 0ULL;
return 0;
}
*val = (isc_mgr->pwr_state & (BIT(28) - 1)) |
((isc_mgr->pdata->num_pwr_gpios & 0x0f) << 28);
return 0;
}
static int pwr_on_set(void *data, u64 val)
{
return isc_mgr_power_up((struct isc_mgr_priv *)data, val);
}
DEFINE_SIMPLE_ATTRIBUTE(pwr_on_fops, pwr_on_get, pwr_on_set, "0x%02llx\n");
static int pwr_off_get(void *data, u64 *val)
{
struct isc_mgr_priv *isc_mgr = data;
if (isc_mgr->pdata == NULL || !isc_mgr->pdata->num_pwr_gpios) {
*val = 0ULL;
return 0;
}
*val = (~isc_mgr->pwr_state) & (BIT(isc_mgr->pdata->num_pwr_gpios) - 1);
*val = (*val & (BIT(28) - 1)) |
((isc_mgr->pdata->num_pwr_gpios & 0x0f) << 28);
return 0;
}
static int pwr_off_set(void *data, u64 val)
{
return isc_mgr_power_down((struct isc_mgr_priv *)data, val);
}
DEFINE_SIMPLE_ATTRIBUTE(pwr_off_fops, pwr_off_get, pwr_off_set, "0x%02llx\n");
int isc_mgr_debugfs_init(struct isc_mgr_priv *isc_mgr)
{
struct dentry *d;
dev_dbg(isc_mgr->dev, "%s %s\n", __func__, isc_mgr->devname);
isc_mgr->d_entry = debugfs_create_dir(
isc_mgr->devname, NULL);
if (isc_mgr->d_entry == NULL) {
dev_err(isc_mgr->dev, "%s: create dir failed\n", __func__);
return -ENOMEM;
}
d = debugfs_create_file("map", S_IRUGO|S_IWUSR, isc_mgr->d_entry,
(void *)isc_mgr, &isc_mgr_debugfs_fops);
if (!d)
goto debugfs_init_err;
d = debugfs_create_file("pwr-on", S_IRUGO|S_IWUSR, isc_mgr->d_entry,
(void *)isc_mgr, &pwr_on_fops);
if (!d)
goto debugfs_init_err;
d = debugfs_create_file("pwr-off", S_IRUGO|S_IWUSR, isc_mgr->d_entry,
(void *)isc_mgr, &pwr_off_fops);
if (!d)
goto debugfs_init_err;
return 0;
debugfs_init_err:
dev_err(isc_mgr->dev, "%s: create file failed\n", __func__);
debugfs_remove_recursive(isc_mgr->d_entry);
isc_mgr->d_entry = NULL;
return -ENOMEM;
}
int isc_mgr_debugfs_remove(struct isc_mgr_priv *isc_mgr)
{
if (isc_mgr->d_entry == NULL)
return 0;
debugfs_remove_recursive(isc_mgr->d_entry);
isc_mgr->d_entry = NULL;
return 0;
}
static int i2c_val_get(void *data, u64 *val)
{
struct isc_dev_info *isc_dev = data;
u8 temp = 0;
if (isc_dev_raw_rd(isc_dev, isc_dev->reg_off, 0, &temp, 1)) {
dev_err(isc_dev->dev, "ERR:%s failed\n", __func__);
return -EIO;
}
*val = (u64)temp;
return 0;
}
static int i2c_val_set(void *data, u64 val)
{
struct isc_dev_info *isc_dev = data;
u8 temp[3];
temp[2] = val & 0xff;
if (isc_dev_raw_wr(isc_dev, isc_dev->reg_off, temp, 1)) {
dev_err(isc_dev->dev, "ERR:%s failed\n", __func__);
return -EIO;
}
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(isc_val_fops, i2c_val_get, i2c_val_set, "0x%02llx\n");
static int i2c_oft_get(void *data, u64 *val)
{
struct isc_dev_info *isc_dev = data;
*val = (u64)isc_dev->reg_off;
return 0;
}
static int i2c_oft_set(void *data, u64 val)
{
struct isc_dev_info *isc_dev = data;
isc_dev->reg_off = (typeof(isc_dev->reg_off))val;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(isc_oft_fops, i2c_oft_get, i2c_oft_set, "0x%02llx\n");
int isc_dev_debugfs_init(struct isc_dev_info *isc_dev)
{
struct isc_mgr_priv *isc_mgr = NULL;
struct dentry *d;
dev_dbg(isc_dev->dev, "%s %s\n", __func__, isc_dev->devname);
if (isc_dev->pdata)
isc_mgr = dev_get_drvdata(isc_dev->pdata->pdev);
isc_dev->d_entry = debugfs_create_dir(
isc_dev->devname,
isc_mgr ? isc_mgr->d_entry : NULL);
if (isc_dev->d_entry == NULL) {
dev_err(isc_dev->dev, "%s: create dir failed\n", __func__);
return -ENOMEM;
}
d = debugfs_create_file("val", S_IRUGO|S_IWUSR, isc_dev->d_entry,
(void *)isc_dev, &isc_val_fops);
if (!d) {
dev_err(isc_dev->dev, "%s: create file failed\n", __func__);
debugfs_remove_recursive(isc_dev->d_entry);
isc_dev->d_entry = NULL;
}
d = debugfs_create_file("offset", S_IRUGO|S_IWUSR, isc_dev->d_entry,
(void *)isc_dev, &isc_oft_fops);
if (!d) {
dev_err(isc_dev->dev, "%s: create file failed\n", __func__);
debugfs_remove_recursive(isc_dev->d_entry);
isc_dev->d_entry = NULL;
}
return 0;
}
int isc_dev_debugfs_remove(struct isc_dev_info *isc_dev)
{
if (isc_dev->d_entry == NULL)
return 0;
debugfs_remove_recursive(isc_dev->d_entry);
isc_dev->d_entry = NULL;
return 0;
}

View File

@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __ISC_DEV_PRIV_H__
#define __ISC_DEV_PRIV_H__
#include <linux/cdev.h>
struct isc_dev_info {
struct i2c_client *i2c_client;
struct device *dev;
struct cdev cdev;
struct isc_dev_platform_data *pdata;
atomic_t in_use;
struct mutex mutex;
struct isc_dev_package rw_pkg;
struct dentry *d_entry;
u32 reg_len;
u32 reg_off;
char devname[32];
u8 power_is_on;
};
int isc_dev_raw_rd(struct isc_dev_info *, unsigned int,
unsigned int, u8 *, size_t);
int isc_dev_raw_wr(struct isc_dev_info *, unsigned int, u8 *, size_t);
int isc_dev_debugfs_init(struct isc_dev_info *isc_dev);
int isc_dev_debugfs_remove(struct isc_dev_info *isc_dev);
#endif /* __ISC_DEV_PRIV_H__ */

View File

@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __ISC_GPIO_PRIV_H__
#define __ISC_GPIO_PRIV_H__
struct isc_gpio_plat_data {
const char *gpio_prnt_chip;
u32 max_gpio;
};
struct isc_gpio_desc {
u32 gpio;
atomic_t ref_cnt;
};
struct isc_gpio_priv {
struct device *pdev;
struct isc_gpio_plat_data pdata;
struct mutex mutex;
struct gpio_chip gpio_chip;
struct gpio_chip *tgc;
struct isc_gpio_desc *gpios;
u32 num_gpio;
};
#endif /* __ISC_GPIO_PRIV_H__ */

View File

@@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __ISC_MGR_PRIV_H__
#define __ISC_MGR_PRIV_H__
#include <linux/cdev.h>
#include <linux/version.h>
struct isc_mgr_priv {
struct device *pdev; /* parent device */
struct device *dev; /* this device */
dev_t devt;
struct cdev cdev;
struct class *isc_class;
struct i2c_adapter *adap;
struct isc_mgr_platform_data *pdata;
struct list_head dev_list;
struct mutex mutex;
struct dentry *d_entry;
struct work_struct ins_work;
struct task_struct *t;
struct kernel_siginfo sinfo;
int sig_no; /* store signal number from user space */
spinlock_t spinlock;
atomic_t in_use;
int err_irq;
char devname[32];
u32 pwr_state;
atomic_t irq_in_use;
struct pwm_device *pwm;
wait_queue_head_t err_queue;
bool err_irq_recvd;
};
int isc_mgr_power_up(struct isc_mgr_priv *isc_mgr, unsigned long arg);
int isc_mgr_power_down(struct isc_mgr_priv *isc_mgr, unsigned long arg);
int isc_mgr_debugfs_init(struct isc_mgr_priv *isc_mgr);
int isc_mgr_debugfs_remove(struct isc_mgr_priv *isc_mgr);
#endif /* __ISC_MGR_PRIV_H__ */

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __ISC_PWM_PRIV_H__
#define __ISC_PWM_PRIV_H__
struct isc_pwm_info {
struct pwm_chip chip;
struct pwm_device *pwm;
atomic_t in_use;
struct mutex mutex;
bool force_on;
};
#endif /* __ISC_PWM_PRIV_H__ */

View File

@@ -0,0 +1,626 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <media/isc-dev.h>
#include <uapi/media/isc-mgr.h>
#include "isc-dev-priv.h"
#include "isc-mgr-priv.h"
/* i2c payload size is only 12 bit */
#define MAX_MSG_SIZE (0xFFF - 1)
/*#define DEBUG_I2C_TRAFFIC*/
/* ISC Dev Debugfs functions
*
* - isc_dev_debugfs_init
* - isc_dev_debugfs_remove
* - i2c_oft_get
* - i2c_oft_set
* - i2c_val_get
* - i2c_oft_set
*/
static int i2c_val_get(void *data, u64 *val)
{
struct isc_dev_info *isc_dev = data;
u8 temp = 0;
if (isc_dev_raw_rd(isc_dev, isc_dev->reg_off, 0, &temp, 1)) {
dev_err(isc_dev->dev, "ERR:%s failed\n", __func__);
return -EIO;
}
*val = (u64)temp;
return 0;
}
static int i2c_val_set(void *data, u64 val)
{
struct isc_dev_info *isc_dev = data;
u8 temp[3];
temp[2] = val & 0xff;
if (isc_dev_raw_wr(isc_dev, isc_dev->reg_off, temp, 1)) {
dev_err(isc_dev->dev, "ERR:%s failed\n", __func__);
return -EIO;
}
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(isc_val_fops, i2c_val_get, i2c_val_set, "0x%02llx\n");
static int i2c_oft_get(void *data, u64 *val)
{
struct isc_dev_info *isc_dev = data;
*val = (u64)isc_dev->reg_off;
return 0;
}
static int i2c_oft_set(void *data, u64 val)
{
struct isc_dev_info *isc_dev = data;
isc_dev->reg_off = (typeof(isc_dev->reg_off))val;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(isc_oft_fops, i2c_oft_get, i2c_oft_set, "0x%02llx\n");
int isc_dev_debugfs_init(struct isc_dev_info *isc_dev)
{
struct isc_mgr_priv *isc_mgr = NULL;
struct dentry *d;
dev_dbg(isc_dev->dev, "%s %s\n", __func__, isc_dev->devname);
if (isc_dev->pdata)
isc_mgr = dev_get_drvdata(isc_dev->pdata->pdev);
isc_dev->d_entry = debugfs_create_dir(
isc_dev->devname,
isc_mgr ? isc_mgr->d_entry : NULL);
if (isc_dev->d_entry == NULL) {
dev_err(isc_dev->dev, "%s: create dir failed\n", __func__);
return -ENOMEM;
}
d = debugfs_create_file("val", S_IRUGO|S_IWUSR, isc_dev->d_entry,
(void *)isc_dev, &isc_val_fops);
if (!d) {
dev_err(isc_dev->dev, "%s: create file failed\n", __func__);
debugfs_remove_recursive(isc_dev->d_entry);
isc_dev->d_entry = NULL;
}
d = debugfs_create_file("offset", S_IRUGO|S_IWUSR, isc_dev->d_entry,
(void *)isc_dev, &isc_oft_fops);
if (!d) {
dev_err(isc_dev->dev, "%s: create file failed\n", __func__);
debugfs_remove_recursive(isc_dev->d_entry);
isc_dev->d_entry = NULL;
}
return 0;
}
int isc_dev_debugfs_remove(struct isc_dev_info *isc_dev)
{
if (isc_dev->d_entry == NULL)
return 0;
debugfs_remove_recursive(isc_dev->d_entry);
isc_dev->d_entry = NULL;
return 0;
}
static void isc_dev_dump(
const char *str,
struct isc_dev_info *info,
unsigned int offset,
u8 *buf, size_t size)
{
#if (defined(DEBUG) || defined(DEBUG_I2C_TRAFFIC))
char *dump_buf;
int len, i, off;
/* alloc enough memory for function name + offset + data */
len = strlen(str) + size * 3 + 10;
dump_buf = kzalloc(len, GFP_KERNEL);
if (dump_buf == NULL) {
dev_err(info->dev, "%s: Memory alloc ERROR!\n", __func__);
return;
}
off = sprintf(dump_buf, "%s %04x =", str, offset);
for (i = 0; i < size && off < len - 1; i++)
off += sprintf(dump_buf + off, " %02x", buf[i]);
dump_buf[off] = 0;
dev_notice(info->dev, "%s\n", dump_buf);
kfree(dump_buf);
#endif
}
/* i2c read from device.
* val - buffer contains data to write.
* size - number of bytes to be written to device.
* offset - address in the device's register space to start with.
*/
int isc_dev_raw_rd(
struct isc_dev_info *info, unsigned int offset,
unsigned int offset_len, u8 *val, size_t size)
{
int ret = -ENODEV;
u8 data[2];
struct i2c_msg i2cmsg[2];
dev_dbg(info->dev, "%s\n", __func__);
mutex_lock(&info->mutex);
if (!info->power_is_on) {
dev_err(info->dev, "%s: power is off.\n", __func__);
mutex_unlock(&info->mutex);
return ret;
}
/* when user read device from debugfs, the offset_len will be 0.
* And the offset_len should come from device info
*/
if (!offset_len)
offset_len = info->reg_len;
if (offset_len == 2) {
data[0] = (u8)((offset >> 8) & 0xff);
data[1] = (u8)(offset & 0xff);
} else if (offset_len == 1)
data[0] = (u8)(offset & 0xff);
i2cmsg[0].addr = info->i2c_client->addr;
i2cmsg[0].len = offset_len;
i2cmsg[0].buf = (__u8 *)data;
i2cmsg[0].flags = I2C_M_NOSTART;
i2cmsg[1].addr = info->i2c_client->addr;
i2cmsg[1].flags = I2C_M_RD;
i2cmsg[1].len = size;
i2cmsg[1].buf = (__u8 *)val;
ret = i2c_transfer(info->i2c_client->adapter, i2cmsg, 2);
if (ret > 0)
ret = 0;
mutex_unlock(&info->mutex);
if (!ret)
isc_dev_dump(__func__, info, offset, val, size);
return ret;
}
/* i2c write to device.
* val - buffer contains data to write.
* size - number of bytes to be written to device.
* offset - address in the device's register space to start with.
* if offset == -1, it will be ignored and no offset
* value will be integrated into the data buffer.
*/
int isc_dev_raw_wr(
struct isc_dev_info *info, unsigned int offset, u8 *val, size_t size)
{
int ret = -ENODEV;
u8 *buf_start = NULL;
struct i2c_msg *i2cmsg;
unsigned int num_msgs = 0, total_size, i;
dev_dbg(info->dev, "%s\n", __func__);
mutex_lock(&info->mutex);
if (size == 0) {
dev_dbg(info->dev, "%s: size is 0.\n", __func__);
mutex_unlock(&info->mutex);
return 0;
}
if (!info->power_is_on) {
dev_err(info->dev, "%s: power is off.\n", __func__);
mutex_unlock(&info->mutex);
return ret;
}
if (offset != (unsigned int)-1) { /* offset is valid */
if (info->reg_len == 2) {
val[0] = (u8)((offset >> 8) & 0xff);
val[1] = (u8)(offset & 0xff);
size += 2;
} else if (info->reg_len == 1) {
val++;
val[0] = (u8)(offset & 0xff);
size += 1;
} else
val += 2;
}
isc_dev_dump(__func__, info, offset, val, size);
num_msgs = size / MAX_MSG_SIZE;
num_msgs += (size % MAX_MSG_SIZE) ? 1 : 0;
i2cmsg = kzalloc((sizeof(struct i2c_msg)*num_msgs), GFP_KERNEL);
if (!i2cmsg) {
dev_err(info->dev, "%s: failed to allocate memory\n",
__func__);
mutex_unlock(&info->mutex);
return -ENOMEM;
}
buf_start = val;
total_size = size;
dev_dbg(info->dev, "%s: num_msgs: %d\n", __func__, num_msgs);
for (i = 0; i < num_msgs; i++) {
i2cmsg[i].addr = info->i2c_client->addr;
i2cmsg[i].buf = (__u8 *)buf_start;
if (i > 0)
i2cmsg[i].flags = I2C_M_NOSTART;
else
i2cmsg[i].flags = 0;
if (total_size > MAX_MSG_SIZE) {
i2cmsg[i].len = MAX_MSG_SIZE;
buf_start += MAX_MSG_SIZE;
total_size -= MAX_MSG_SIZE;
} else {
i2cmsg[i].len = total_size;
}
dev_dbg(info->dev, "%s: addr:%x buf:%p, flags:%u len:%u\n",
__func__, i2cmsg[i].addr, (void *)i2cmsg[i].buf,
i2cmsg[i].flags, i2cmsg[i].len);
}
ret = i2c_transfer(info->i2c_client->adapter, i2cmsg, num_msgs);
if (ret > 0)
ret = 0;
kfree(i2cmsg);
mutex_unlock(&info->mutex);
return ret;
}
static int isc_dev_raw_rw(struct isc_dev_info *info)
{
struct isc_dev_package *pkg = &info->rw_pkg;
void *u_ptr = (void *)pkg->buffer;
u8 *buf;
int ret = -ENODEV;
dev_dbg(info->dev, "%s\n", __func__);
buf = kzalloc(pkg->size, GFP_KERNEL);
if (buf == NULL) {
dev_err(info->dev, "%s: Unable to allocate memory!\n",
__func__);
return -ENOMEM;
}
if (pkg->flags & ISC_DEV_PKG_FLAG_WR) {
/* write to device */
if (copy_from_user(buf,
(const void __user *)u_ptr, pkg->size)) {
dev_err(info->dev, "%s copy_from_user err line %d\n",
__func__, __LINE__);
kfree(buf);
return -EFAULT;
}
/* in the user access case, the offset is integrated in the
* buffer to be transferred, so pass -1 as the offset
*/
ret = isc_dev_raw_wr(info, -1, buf, pkg->size);
} else {
/* read from device */
ret = isc_dev_raw_rd(info, pkg->offset,
pkg->offset_len, buf, pkg->size);
if (!ret && copy_to_user(
(void __user *)u_ptr, buf, pkg->size)) {
dev_err(info->dev, "%s copy_to_user err line %d\n",
__func__, __LINE__);
ret = -EINVAL;
}
}
kfree(buf);
return ret;
}
static int isc_dev_get_package(
struct isc_dev_info *info, unsigned long arg, bool is_compat)
{
if (is_compat) {
struct isc_dev_package32 pkg32;
if (copy_from_user(&pkg32,
(const void __user *)arg, sizeof(pkg32))) {
dev_err(info->dev, "%s copy_from_user err line %d\n",
__func__, __LINE__);
return -EFAULT;
}
info->rw_pkg.offset = pkg32.offset;
info->rw_pkg.offset_len = pkg32.offset_len;
info->rw_pkg.size = pkg32.size;
info->rw_pkg.flags = pkg32.flags;
info->rw_pkg.buffer = (unsigned long)pkg32.buffer;
} else {
struct isc_dev_package pkg;
if (copy_from_user(&pkg,
(const void __user *)arg, sizeof(pkg))) {
dev_err(info->dev, "%s copy_from_user err line %d\n",
__func__, __LINE__);
return -EFAULT;
}
info->rw_pkg.offset = pkg.offset;
info->rw_pkg.offset_len = pkg.offset_len;
info->rw_pkg.size = pkg.size;
info->rw_pkg.flags = pkg.flags;
info->rw_pkg.buffer = pkg.buffer;
}
if ((void __user *)info->rw_pkg.buffer == NULL) {
dev_err(info->dev, "%s package buffer NULL\n", __func__);
return -EINVAL;
}
if (!info->rw_pkg.size) {
dev_err(info->dev, "%s invalid package size %d\n",
__func__, info->rw_pkg.size);
return -EINVAL;
}
return 0;
}
static long isc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct isc_dev_info *info = file->private_data;
int err = 0;
switch (cmd) {
case ISC_DEV_IOCTL_RW:
err = isc_dev_get_package(info, arg, false);
if (err)
break;
err = isc_dev_raw_rw(info);
break;
default:
dev_dbg(info->dev, "%s: invalid cmd %x\n", __func__, cmd);
return -EINVAL;
}
return err;
}
#ifdef CONFIG_COMPAT
static long isc_dev_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct isc_dev_info *info = file->private_data;
int err = 0;
switch (cmd) {
case ISC_DEV_IOCTL_RW32:
err = isc_dev_get_package(info, arg, true);
if (err)
break;
err = isc_dev_raw_rw(info);
break;
default:
return isc_dev_ioctl(file, cmd, arg);
}
return err;
}
#endif
static int isc_dev_open(struct inode *inode, struct file *file)
{
struct isc_dev_info *info;
if (inode == NULL)
return -ENODEV;
info = container_of(inode->i_cdev, struct isc_dev_info, cdev);
if (atomic_xchg(&info->in_use, 1))
return -EBUSY;
file->private_data = info;
dev_dbg(info->dev, "%s\n", __func__);
return 0;
}
static int isc_dev_release(struct inode *inode, struct file *file)
{
struct isc_dev_info *info = file->private_data;
dev_dbg(info->dev, "%s\n", __func__);
file->private_data = NULL;
WARN_ON(!atomic_xchg(&info->in_use, 0));
return 0;
}
static const struct file_operations isc_dev_fileops = {
.owner = THIS_MODULE,
.open = isc_dev_open,
.unlocked_ioctl = isc_dev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = isc_dev_ioctl32,
#endif
.release = isc_dev_release,
};
static int isc_dev_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct isc_dev_info *info;
struct device *pdev;
int err;
dev_dbg(&client->dev, "%s: initializing link @%x-%04x\n",
__func__, client->adapter->nr, client->addr);
info = devm_kzalloc(
&client->dev, sizeof(struct isc_dev_info), GFP_KERNEL);
if (!info) {
pr_err("%s: Unable to allocate memory!\n", __func__);
return -ENOMEM;
}
mutex_init(&info->mutex);
if (client->dev.platform_data) {
info->pdata = client->dev.platform_data;
dev_dbg(&client->dev, "pdata: %p\n", info->pdata);
} else {
dev_notice(&client->dev, "%s NO platform data\n", __func__);
return -ENODEV;
}
if (info->pdata->reg_bits)
info->reg_len = info->pdata->reg_bits / 8;
else
info->reg_len = 2;
if (info->reg_len > 2) {
dev_err(&client->dev,
"device offset length invalid: %d\n", info->reg_len);
devm_kfree(&client->dev, info);
return -ENODEV;
}
info->i2c_client = client;
info->dev = &client->dev;
if (info->pdata)
snprintf(info->devname, sizeof(info->devname),
"%s", info->pdata->drv_name);
else
snprintf(info->devname, sizeof(info->devname),
"isc-dev.%u.%02x", client->adapter->nr, client->addr);
if (info->pdata->pdev == NULL)
return -ENODEV;
cdev_init(&info->cdev, &isc_dev_fileops);
info->cdev.owner = THIS_MODULE;
pdev = info->pdata->pdev;
err = cdev_add(&info->cdev, MKDEV(MAJOR(pdev->devt), client->addr), 1);
if (err < 0) {
dev_err(&client->dev,
"%s: Could not add cdev for %d\n", __func__,
MKDEV(MAJOR(pdev->devt), client->addr));
devm_kfree(&client->dev, info);
return err;
}
/* send uevents to udev, it will create /dev node for isc-mgr */
info->dev = device_create(pdev->class, &client->dev,
info->cdev.dev,
info, info->devname);
if (IS_ERR(info->dev)) {
info->dev = NULL;
cdev_del(&info->cdev);
devm_kfree(&client->dev, info);
return PTR_ERR(info->dev);
}
info->power_is_on = 1;
i2c_set_clientdata(client, info);
isc_dev_debugfs_init(info);
return 0;
}
static int isc_dev_remove(struct i2c_client *client)
{
struct isc_dev_info *info = i2c_get_clientdata(client);
struct device *pdev;
dev_dbg(&client->dev, "%s\n", __func__);
isc_dev_debugfs_remove(info);
/* remove only isc_dev_info not i2c_client itself */
pdev = info->pdata->pdev;
if (info->dev)
device_destroy(pdev->class, info->cdev.dev);
if (info->cdev.dev)
cdev_del(&info->cdev);
return 0;
}
#ifdef CONFIG_PM
static int isc_dev_suspend(struct device *dev)
{
struct isc_dev_info *isc = (struct isc_dev_info *)dev_get_drvdata(dev);
dev_info(dev, "Suspending\n");
mutex_lock(&isc->mutex);
isc->power_is_on = 0;
mutex_unlock(&isc->mutex);
return 0;
}
static int isc_dev_resume(struct device *dev)
{
struct isc_dev_info *isc = (struct isc_dev_info *)dev_get_drvdata(dev);
dev_info(dev, "Resuming\n");
mutex_lock(&isc->mutex);
isc->power_is_on = 1;
mutex_unlock(&isc->mutex);
return 0;
}
#endif
static const struct i2c_device_id isc_dev_id[] = {
{ "isc-dev", 0 },
{ },
};
static const struct dev_pm_ops isc_dev_pm_ops = {
SET_RUNTIME_PM_OPS(isc_dev_suspend,
isc_dev_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(isc_dev_suspend,
isc_dev_resume)
};
static struct i2c_driver isc_dev_drv = {
.driver = {
.name = "isc-dev",
.owner = THIS_MODULE,
.pm = &isc_dev_pm_ops,
},
.id_table = isc_dev_id,
.probe = isc_dev_probe,
.remove = isc_dev_remove,
};
module_i2c_driver(isc_dev_drv);
MODULE_DESCRIPTION("ISC Generic I2C driver");
MODULE_AUTHOR("Charlie Huang <chahuang@nvidia.com>");
MODULE_LICENSE("GPL v2");
MODULE_SOFTDEP("pre: isc_gpio");

View File

@@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/atomic.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/nospec.h>
#include "isc-gpio-priv.h"
#define MAX_STR_SIZE 255
static int of_isc_gpio_pdata(struct platform_device *pdev,
struct isc_gpio_plat_data *pdata)
{
struct device_node *np = pdev->dev.of_node;
int err;
err = of_property_read_string(np, "parent-gpio-chip",
&pdata->gpio_prnt_chip);
if (err < 0)
return err;
err = of_property_read_u32(pdev->dev.of_node,
"max-gpios", &pdata->max_gpio);
return err;
}
static int isc_gpio_chip_match(struct gpio_chip *chip, void *data)
{
return !strcmp(chip->label, data);
}
static struct gpio_chip *isc_gpio_get_chip(struct platform_device *pdev,
struct isc_gpio_plat_data *pd)
{
struct gpio_chip *gc = NULL;
char name[MAX_STR_SIZE];
if (strlen(pd->gpio_prnt_chip) > MAX_STR_SIZE) {
dev_err(&pdev->dev, "%s: gpio chip name is too long: %s\n",
__func__, pd->gpio_prnt_chip);
return NULL;
}
strcpy(name, pd->gpio_prnt_chip);
gc = gpiochip_find(name, isc_gpio_chip_match);
if (!gc) {
dev_err(&pdev->dev, "%s: unable to find gpio parent chip %s\n",
__func__, pd->gpio_prnt_chip);
return NULL;
}
return gc;
}
static int isc_gpio_init_desc(struct platform_device *pdev,
struct isc_gpio_priv *isc_gpio)
{
struct isc_gpio_desc *desc = NULL;
u32 i;
desc = devm_kzalloc(&pdev->dev,
(sizeof(struct isc_gpio_desc) *
isc_gpio->pdata.max_gpio),
GFP_KERNEL);
if (!desc) {
dev_err(&pdev->dev, "Unable to allocate memory!\n");
return -ENOMEM;
}
for (i = 0; i < isc_gpio->pdata.max_gpio; i++) {
desc[i].gpio = 0;
atomic_set(&desc[i].ref_cnt, 0);
}
isc_gpio->gpios = desc;
return 0;
}
static int isc_gpio_get_index(struct device *dev,
struct isc_gpio_priv *isc_gpio, u32 gpio)
{
u32 i;
int idx = -1;
/* find gpio in array */
for (i = 0; i < isc_gpio->num_gpio; i++) {
if (isc_gpio->gpios[i].gpio == gpio) {
idx = i;
break;
}
}
/* gpio exists, return idx */
if (idx >= 0)
return idx;
/* add gpio if it doesn't exist and there is memory available */
if (isc_gpio->num_gpio < isc_gpio->pdata.max_gpio) {
idx = isc_gpio->num_gpio;
isc_gpio->gpios[idx].gpio = gpio;
isc_gpio->num_gpio++;
return idx;
}
dev_err(dev, "%s: Unable to add gpio to desc\n", __func__);
return -EFAULT;
}
static int isc_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
{
struct gpio_chip *tgc = NULL;
struct isc_gpio_priv *isc_gpio = NULL;
int err;
isc_gpio = gpiochip_get_data(gc);
if (!isc_gpio)
return -EFAULT;
mutex_lock(&isc_gpio->mutex);
tgc = isc_gpio->tgc;
err = tgc->direction_input(tgc, off);
mutex_unlock(&isc_gpio->mutex);
return err;
}
static int isc_gpio_direction_output(struct gpio_chip *gc, unsigned int off,
int val)
{
struct gpio_chip *tgc = NULL;
struct isc_gpio_priv *isc_gpio = NULL;
int err;
isc_gpio = gpiochip_get_data(gc);
if (!isc_gpio)
return -EFAULT;
mutex_lock(&isc_gpio->mutex);
tgc = isc_gpio->tgc;
err = tgc->direction_output(tgc, off, val);
mutex_unlock(&isc_gpio->mutex);
return err;
}
static int isc_gpio_get_value(struct gpio_chip *gc, unsigned int off)
{
int gpio_val;
struct gpio_chip *tgc = NULL;
struct isc_gpio_priv *isc_gpio = NULL;
isc_gpio = gpiochip_get_data(gc);
if (!isc_gpio)
return -EFAULT;
mutex_lock(&isc_gpio->mutex);
tgc = isc_gpio->tgc;
gpio_val = tgc->get(tgc, off);
mutex_unlock(&isc_gpio->mutex);
return gpio_val;
}
static void isc_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val)
{
int idx;
struct gpio_chip *tgc = NULL;
struct isc_gpio_priv *isc_gpio = NULL;
atomic_t *ref_cnt;
struct device *dev = NULL;
isc_gpio = gpiochip_get_data(gc);
if (!isc_gpio)
return;
mutex_lock(&isc_gpio->mutex);
dev = isc_gpio->pdev;
tgc = isc_gpio->tgc;
idx = isc_gpio_get_index(dev, isc_gpio, off);
if (idx < 0) {
mutex_unlock(&isc_gpio->mutex);
return;
}
idx = array_index_nospec(idx, isc_gpio->pdata.max_gpio);
/* set gpio value based on refcount */
ref_cnt = &isc_gpio->gpios[idx].ref_cnt;
switch (val) {
case 0:
if ((atomic_read(ref_cnt) > 0) &&
atomic_dec_and_test(ref_cnt)) {
tgc->set(tgc, off, val);
}
dev_info(dev, "%s: gpio idx: %d, val to set: %d, refcount: %d\n",
__func__, idx, val, atomic_read(ref_cnt));
break;
case 1:
if (!atomic_inc_and_test(ref_cnt))
tgc->set(tgc, off, val);
dev_info(dev, "%s: gpio idx: %d, val to set: %d, refcount: %d\n",
__func__, idx, val, atomic_read(ref_cnt));
break;
default:
dev_err(dev, "%s: Invalid gpio value provided\n",
__func__);
break;
}
mutex_unlock(&isc_gpio->mutex);
}
static int isc_gpio_probe(struct platform_device *pdev)
{
struct isc_gpio_priv *isc_gpio;
struct isc_gpio_plat_data *pd = NULL;
struct gpio_chip *tgc, *gc;
int err;
dev_info(&pdev->dev, "probing %s...\n", __func__);
isc_gpio = devm_kzalloc(&pdev->dev,
sizeof(struct isc_gpio_priv),
GFP_KERNEL);
if (!isc_gpio) {
dev_err(&pdev->dev, "Unable to allocate memory!\n");
return -ENOMEM;
}
/* get platform data from device tree */
err = of_isc_gpio_pdata(pdev, &isc_gpio->pdata);
if (err < 0)
return err;
pd = &isc_gpio->pdata;
/* get tegra gpio chip */
tgc = isc_gpio_get_chip(pdev, pd);
if (!tgc)
return -ENXIO;
isc_gpio->tgc = tgc;
/* initialize gpio desc */
err = isc_gpio_init_desc(pdev, isc_gpio);
if (err < 0)
return err;
isc_gpio->num_gpio = 0;
/* setup gpio chip */
gc = &isc_gpio->gpio_chip;
gc->direction_input = isc_gpio_direction_input;
gc->direction_output = isc_gpio_direction_output;
gc->get = isc_gpio_get_value;
gc->set = isc_gpio_set_value;
gc->can_sleep = false;
gc->base = -1;
gc->ngpio = pd->max_gpio;
gc->label = "isc-gpio";
gc->of_node = pdev->dev.of_node;
gc->owner = THIS_MODULE;
err = gpiochip_add_data(gc, isc_gpio);
if (err) {
dev_err(&pdev->dev, "failed to add GPIO controller\n");
return err;
}
mutex_init(&isc_gpio->mutex);
isc_gpio->pdev = &pdev->dev;
dev_set_drvdata(&pdev->dev, isc_gpio);
dev_info(&pdev->dev, "%s: successfully registered gpio device\n",
__func__);
return 0;
}
static int isc_gpio_remove(struct platform_device *pdev)
{
struct isc_gpio_priv *isc_gpio = platform_get_drvdata(pdev);
gpiochip_remove(&isc_gpio->gpio_chip);
return 0;
}
static const struct of_device_id isc_gpio_dt_ids[] = {
{ .compatible = "nvidia,isc-gpio", },
{},
};
MODULE_DEVICE_TABLE(of, isc_gpio_dt_ids);
static struct platform_driver isc_gpio_driver = {
.probe = isc_gpio_probe,
.remove = isc_gpio_remove,
.driver = {
.name = "isc-gpio",
.of_match_table = isc_gpio_dt_ids,
.owner = THIS_MODULE,
}
};
static int __init isc_gpio_init(void)
{
return platform_driver_register(&isc_gpio_driver);
}
static void __exit isc_gpio_exit(void)
{
platform_driver_unregister(&isc_gpio_driver);
}
/* call in subsys so that this module loads before isc-mgr driver */
subsys_initcall(isc_gpio_init);
module_exit(isc_gpio_exit);
MODULE_DESCRIPTION("Tegra Auto ISC GPIO Driver");
MODULE_AUTHOR("Anurag Dosapati <adosapati@nvidia.com>");
MODULE_LICENSE("GPL v2");

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2016-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/pwm.h>
#include <linux/atomic.h>
#include "isc-pwm-priv.h"
/*
* Below values are configured during suspend.
* Invalid values are chosen so that PWM
* configuration in resume works fine.
* Period is chosen as least non-zero value
* and duty-ratio zero.
*/
#define PWM_SUSPEND_PERIOD 1
#define PWM_SUSPEND_DUTY_RATIO 0
static const struct of_device_id isc_pwm_of_match[] = {
{ .compatible = "nvidia, isc-pwm", .data = NULL },
{},
};
static inline struct isc_pwm_info *to_isc_pwm_info(struct pwm_chip *chip)
{
return container_of(chip, struct isc_pwm_info, chip);
}
static int isc_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct isc_pwm_info *info = to_isc_pwm_info(chip);
int err = 0;
if (!chip || !pwm)
return -EINVAL;
if (info->force_on)
return err;
mutex_lock(&info->mutex);
if (atomic_inc_return(&info->in_use) == 1)
err = pwm_enable(info->pwm);
mutex_unlock(&info->mutex);
return err;
}
static void isc_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct isc_pwm_info *info = to_isc_pwm_info(chip);
int atomic_val;
mutex_lock(&info->mutex);
atomic_val = atomic_read(&info->in_use);
if (atomic_val > 0) {
if (atomic_dec_and_test(&info->in_use))
pwm_disable(info->pwm);
}
mutex_unlock(&info->mutex);
}
static int isc_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct isc_pwm_info *info = to_isc_pwm_info(chip);
int err = 0;
if (info->force_on)
return err;
mutex_lock(&info->mutex);
err = pwm_config(info->pwm, duty_ns, period_ns);
mutex_unlock(&info->mutex);
return err;
}
static struct pwm_device *of_isc_pwm_xlate(struct pwm_chip *pc,
const struct of_phandle_args *args)
{
struct pwm_device *pwm;
struct isc_pwm_info *info = to_isc_pwm_info(pc);
int err = 0;
pwm = pwm_request_from_chip(pc, args->args[0], NULL);
if (!args->args[1]) {
dev_err(pc->dev, "Period should be larger than 0\n");
return NULL;
}
if (info->force_on) {
err = pwm_config(info->pwm, args->args[1]/4, args->args[1]);
if (err) {
dev_err(pc->dev, "can't config PWM: %d\n", err);
return NULL;
}
err = pwm_enable(info->pwm);
if (err) {
dev_err(pc->dev, "can't enable PWM: %d\n", err);
return NULL;
}
} else {
err = pwm_config(pwm, args->args[1]/4, args->args[1]);
if (err) {
dev_err(pc->dev, "can't config PWM: %d\n", err);
return NULL;
}
}
return pwm;
}
static const struct pwm_ops isc_pwm_ops = {
.config = isc_pwm_config,
.enable = isc_pwm_enable,
.disable = isc_pwm_disable,
.owner = THIS_MODULE,
};
static int isc_pwm_probe(struct platform_device *pdev)
{
struct isc_pwm_info *info = NULL;
int err = 0, npwm;
bool force_on = false;
dev_info(&pdev->dev, "%sing...\n", __func__);
info = devm_kzalloc(
&pdev->dev, sizeof(struct isc_pwm_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
atomic_set(&info->in_use, 0);
mutex_init(&info->mutex);
err = of_property_read_u32(pdev->dev.of_node, "npwm", &npwm);
if (err < 0) {
dev_err(&pdev->dev, "npwm is not defined: %d\n", err);
return err;
}
force_on = of_property_read_bool(pdev->dev.of_node, "force_on");
info->chip.dev = &pdev->dev;
info->chip.ops = &isc_pwm_ops;
info->chip.base = -1;
info->chip.npwm = npwm;
info->chip.of_xlate = of_isc_pwm_xlate;
info->chip.of_pwm_n_cells = 2;
info->force_on = force_on;
err = pwmchip_add(&info->chip);
if (err < 0) {
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", err);
return err;
}
platform_set_drvdata(pdev, info);
info->pwm = devm_pwm_get(&pdev->dev, NULL);
if (!IS_ERR(info->pwm)) {
pwm_disable(info->pwm);
dev_info(&pdev->dev, "%s success to get PWM\n", __func__);
} else {
pwmchip_remove(&info->chip);
err = PTR_ERR(info->pwm);
if (err != -EPROBE_DEFER)
dev_err(&pdev->dev,
"%s: fail to get PWM\n", __func__);
}
return err;
}
static int isc_pwm_remove(struct platform_device *pdev)
{
struct isc_pwm_info *info = platform_get_drvdata(pdev);
pwmchip_remove(&info->chip);
return 0;
}
static int isc_pwm_suspend(struct device *dev)
{
int err = 0;
struct isc_pwm_info *info = dev_get_drvdata(dev);
if (info == NULL) {
dev_err(dev, "%s: fail to get info\n", __func__);
} else {
if (!IS_ERR(info->pwm)) {
pwm_disable(info->pwm);
err = pwm_config(info->pwm, PWM_SUSPEND_DUTY_RATIO,
PWM_SUSPEND_PERIOD);
}
}
return 0;
}
static int isc_pwm_resume(struct device *dev)
{
/* Do nothing */
return 0;
}
static const struct dev_pm_ops isc_pwm_pm_ops = {
.suspend = isc_pwm_suspend,
.resume = isc_pwm_resume,
.runtime_suspend = isc_pwm_suspend,
.runtime_resume = isc_pwm_resume,
};
static struct platform_driver isc_pwm_driver = {
.driver = {
.name = "isc-pwm",
.owner = THIS_MODULE,
.of_match_table = isc_pwm_of_match,
.pm = &isc_pwm_pm_ops,
},
.probe = isc_pwm_probe,
.remove = isc_pwm_remove,
};
module_platform_driver(isc_pwm_driver);
MODULE_AUTHOR("Junghyun Kim <juskim@nvidia.com>");
MODULE_DESCRIPTION("ISC PWM driver");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(of, isc_pwm_of_match);
MODULE_SOFTDEP("pre: isc_dev");

21
include/media/isc-dev.h Normal file
View File

@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __ISC_DEV_H__
#define __ISC_DEV_H__
#include <uapi/media/isc-dev.h>
#include <linux/regmap.h>
#define MAX_ISC_NAME_LENGTH 32
struct isc_dev_platform_data {
struct device *pdev; /* parent device of isc_dev */
int reg_bits;
int val_bits;
char drv_name[MAX_ISC_NAME_LENGTH];
};
#endif /* __ISC_DEV_H__ */

41
include/media/isc-mgr.h Normal file
View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __TEGRA_ISC_MGR_H__
#define __TEGRA_ISC_MGR_H__
#include <uapi/media/isc-mgr.h>
#define MAX_ISC_GPIOS 8
struct isc_mgr_client {
struct mutex mutex;
struct list_head list;
struct i2c_client *client;
struct isc_mgr_new_dev cfg;
struct isc_dev_platform_data pdata;
int id;
};
struct isc_mgr_platform_data {
int bus;
int num_pwr_gpios;
u32 pwr_gpios[MAX_ISC_GPIOS];
u32 pwr_flags[MAX_ISC_GPIOS];
int num_pwr_map;
u32 pwr_mapping[MAX_ISC_GPIOS];
int num_misc_gpios;
u32 misc_gpios[MAX_ISC_GPIOS];
u32 misc_flags[MAX_ISC_GPIOS];
int csi_port;
bool default_pwr_on;
bool runtime_pwrctrl_off;
char *drv_name;
u8 ext_pwr_ctrl; /* bit 0 - des, bit 1 - sensor */
};
int isc_delete_lst(struct device *dev, struct i2c_client *client);
#endif /* __TEGRA_ISC_MGR_H__ */

View File

@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __UAPI_ISC_DEV_H__
#define __UAPI_ISC_DEV_H__
#include <linux/ioctl.h>
#include <linux/types.h>
#define ISC_DEV_PKG_FLAG_WR 1
#define ISC_DEV_IOCTL_RW _IOW('o', 1, struct isc_dev_package)
struct __attribute__ ((__packed__)) isc_dev_package {
__u16 offset;
__u16 offset_len;
__u32 size;
__u32 flags;
unsigned long buffer;
};
#ifdef __KERNEL__
#ifdef CONFIG_COMPAT
#define ISC_DEV_IOCTL_RW32 _IOW('o', 1, struct isc_dev_package32)
struct __attribute__ ((__packed__)) isc_dev_package32 {
__u16 offset;
__u16 offset_len;
__u32 size;
__u32 flags;
__u32 buffer;
};
#endif /* CONFIG_COMPAT */
#endif /* __KERNEL__ */
#endif /* __UAPI_ISC_DEV_H__ */

View File

@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#ifndef __UAPI_TEGRA_ISC_MGR_H__
#define __UAPI_TEGRA_ISC_MGR_H__
#include <linux/ioctl.h>
#include <linux/types.h>
#define ISC_MGR_IOCTL_PWR_DN _IOW('o', 1, __s16)
#define ISC_MGR_IOCTL_PWR_UP _IOR('o', 2, __s16)
#define ISC_MGR_IOCTL_SET_PID _IOW('o', 3, struct isc_mgr_sinfo)
#define ISC_MGR_IOCTL_SIGNAL _IOW('o', 4, int)
#define ISC_MGR_IOCTL_DEV_ADD _IOW('o', 5, struct isc_mgr_new_dev)
#define ISC_MGR_IOCTL_DEV_DEL _IOW('o', 6, int)
#define ISC_MGR_IOCTL_PWR_INFO _IOW('o', 7, struct isc_mgr_pwr_info)
#define ISC_MGR_IOCTL_PWM_ENABLE _IOW('o', 8, int)
#define ISC_MGR_IOCTL_PWM_CONFIG _IOW('o', 9, struct isc_mgr_pwm_info)
#define ISC_MGR_IOCTL_WAIT_ERR _IO('o', 10)
#define ISC_MGR_IOCTL_ABORT_WAIT_ERR _IO('o', 11)
#define ISC_MGR_IOCTL_GET_EXT_PWR_CTRL _IOR('o', 12, __u8)
#define ISC_MGR_POWER_ALL 5
#define MAX_ISC_NAME_LENGTH 32
struct isc_mgr_new_dev {
__u16 addr;
__u8 reg_bits;
__u8 val_bits;
__u8 drv_name[MAX_ISC_NAME_LENGTH];
};
struct isc_mgr_sinfo {
__s32 pid;
__s32 sig_no;
__u64 context;
};
struct isc_mgr_pwr_info {
__s32 pwr_gpio;
__s32 pwr_status;
};
struct isc_mgr_pwm_info {
__u64 duty_ns;
__u64 period_ns;
};
enum {
ISC_MGR_PWM_DISABLE = 0,
ISC_MGR_PWM_ENABLE,
};
enum {
ISC_MGR_SIGNAL_RESUME = 0,
ISC_MGR_SIGNAL_SUSPEND,
};
#endif /* __UAPI_TEGRA_ISC_MGR_H__ */