mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
media: tegra: cdi-mgr: support interrupt timeouts
Add support for configurable interrupt timers and event notification queues to SIPL Device Block to mirror the CDAC (QNX) pulse channel interface. The CDI_MGR_IOCTL_INTR_CONFIG IOCTL is added to set the timeout duration at initialization, and existing GPIO interrupt code is refactored. Also removes the GPIO index limitation to the range of [0,31] by returning the complete indices one at a time instead of a bit flag. Jira CAMERASW-11100 Jira CAMERASW-11385 Jira CAMERASW-9366 Bug 3902416 Bug 3792904 Signed-off-by: Vincent Chung <vincentc@nvidia.com> Change-Id: I62b6c0bd8be18922ab1fe5d40485a69274f2a18e Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2893327 Reviewed-by: Semi Malinen <smalinen@nvidia.com> Reviewed-by: Shiva Dubey <sdubey@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
5dcd80a533
commit
be01df015d
@@ -1,10 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2015-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// Copyright (c) 2015-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
|
||||
#ifndef __CDI_MGR_PRIV_H__
|
||||
#define __CDI_MGR_PRIV_H__
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <media/cdi-mgr.h>
|
||||
#include "cdi-tca-priv.h"
|
||||
|
||||
@@ -12,17 +13,37 @@
|
||||
#define CDI_MGR_TCA9539_REGISTER_COUNT (8)
|
||||
#define CDI_MGR_TCA9539_BASE_REG_ADDR (0x00)
|
||||
|
||||
#define CDI_MGR_GPIO_EVENT_QUEUE_SIZE (8)
|
||||
#define CDI_MGR_GPIO_TIMER_QUEUE_SIZE (4)
|
||||
|
||||
enum cam_gpio_direction {
|
||||
CAM_DEVBLK_GPIO_UNSUSED = 0,
|
||||
CAM_DEVBLK_GPIO_INPUT_INTERRUPT,
|
||||
CAM_DEVBLK_GPIO_UNUSED = 0,
|
||||
CAM_DEVBLK_GPIO_INPUT_INTR,
|
||||
CAM_DEVBLK_GPIO_OUTPUT
|
||||
};
|
||||
|
||||
struct cam_gpio_timer_queue {
|
||||
struct hrtimer timer;
|
||||
ktime_t expires[CDI_MGR_GPIO_TIMER_QUEUE_SIZE];
|
||||
u32 head;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct cam_gpio_config {
|
||||
int index;
|
||||
struct cdi_mgr_priv *mgr;
|
||||
u32 idx;
|
||||
enum cam_gpio_direction gpio_dir;
|
||||
struct gpio_desc *desc;
|
||||
int gpio_intr_irq;
|
||||
int intr_irq;
|
||||
struct cam_gpio_timer_queue timers;
|
||||
ktime_t timeout;
|
||||
};
|
||||
|
||||
struct cam_gpio_event_queue {
|
||||
wait_queue_head_t wait;
|
||||
struct cdi_mgr_gpio_intr events[CDI_MGR_GPIO_EVENT_QUEUE_SIZE];
|
||||
u32 head;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct cdi_mgr_priv {
|
||||
@@ -46,14 +67,13 @@ struct cdi_mgr_priv {
|
||||
u32 pwr_state;
|
||||
atomic_t irq_in_use;
|
||||
struct pwm_device *pwm;
|
||||
wait_queue_head_t err_queue;
|
||||
bool err_irq_reported;
|
||||
u8 des_pwr_method;
|
||||
u8 des_pwr_i2c_addr;
|
||||
struct tca9539_priv tca9539;
|
||||
struct cam_gpio_config gpio_arr[MAX_CDI_GPIOS];
|
||||
uint32_t gpio_count;
|
||||
uint32_t err_irq_recvd_status_mask;
|
||||
struct cam_gpio_config gpios[MAX_CDI_GPIOS];
|
||||
uint32_t num_gpios;
|
||||
bool intrs_enable;
|
||||
struct cam_gpio_event_queue gpio_events;
|
||||
bool stop_err_irq_wait;
|
||||
u8 cim_ver; /* 1 - P3714 A01, 2 - P3714 A02/A03 */
|
||||
u32 cim_frsync[3]; /* FRSYNC source selection for each muxer */
|
||||
|
||||
@@ -223,24 +223,168 @@ int cdi_mgr_debugfs_remove(struct cdi_mgr_priv *cdi_mgr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_eq_event(
|
||||
struct cdi_mgr_priv *cdi_mgr, struct cdi_mgr_gpio_intr evt)
|
||||
{
|
||||
struct cam_gpio_event_queue *queue;
|
||||
u32 queue_cap;
|
||||
|
||||
if (!cdi_mgr)
|
||||
return -EINVAL;
|
||||
|
||||
queue = &cdi_mgr->gpio_events;
|
||||
queue_cap = ARRAY_SIZE(queue->events);
|
||||
|
||||
if (queue->count >= queue_cap) {
|
||||
dev_err(cdi_mgr->dev,
|
||||
"%s: failed to enqueue interrupt event, overflow\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
queue->events[(queue->head+queue->count)%queue_cap] = evt;
|
||||
queue->count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_dq_event(
|
||||
struct cdi_mgr_priv *cdi_mgr, struct cdi_mgr_gpio_intr *evt)
|
||||
{
|
||||
struct cam_gpio_event_queue *queue;
|
||||
u32 queue_cap;
|
||||
|
||||
if (!cdi_mgr || !evt)
|
||||
return -EINVAL;
|
||||
|
||||
queue = &cdi_mgr->gpio_events;
|
||||
queue_cap = ARRAY_SIZE(queue->events);
|
||||
|
||||
if (queue->count == 0)
|
||||
return -EIO;
|
||||
|
||||
*evt = queue->events[queue->head];
|
||||
queue->head = (queue->head+1) % queue_cap;
|
||||
queue->count--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_eq_intr_timer(
|
||||
struct cam_gpio_config *pin)
|
||||
{
|
||||
struct cdi_mgr_priv *cdi_mgr;
|
||||
struct cam_gpio_timer_queue *queue;
|
||||
u32 queue_cap;
|
||||
ktime_t timeout_abs;
|
||||
|
||||
if (!pin || !pin->mgr)
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip enqueue if timeout is disabled */
|
||||
if (ktime_to_ms(pin->timeout) <= 0)
|
||||
return 0;
|
||||
|
||||
cdi_mgr = pin->mgr;
|
||||
queue = &pin->timers;
|
||||
queue_cap = ARRAY_SIZE(queue->expires);
|
||||
|
||||
if (queue->count >= queue_cap) {
|
||||
dev_err(cdi_mgr->dev,
|
||||
"%s: failed to enqueue interrupt timer, overflow\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
timeout_abs = ktime_add(ktime_get(), pin->timeout);
|
||||
|
||||
if (!hrtimer_active(&queue->timer)) {
|
||||
/* Start the interrupt timer immediately */
|
||||
hrtimer_start(&queue->timer, timeout_abs, HRTIMER_MODE_ABS);
|
||||
} else {
|
||||
/* Enqueue interrupt timer */
|
||||
queue->expires[(queue->head+queue->count)%queue_cap] = timeout_abs;
|
||||
queue->count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_dq_intr_timer(
|
||||
struct cam_gpio_config *pin)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct cdi_mgr_priv *cdi_mgr;
|
||||
struct cam_gpio_timer_queue *queue;
|
||||
u32 queue_cap;
|
||||
ktime_t *expire;
|
||||
struct cdi_mgr_gpio_intr event;
|
||||
|
||||
if (!pin || !pin->mgr)
|
||||
return -EINVAL;
|
||||
|
||||
cdi_mgr = pin->mgr;
|
||||
queue = &pin->timers;
|
||||
queue_cap = ARRAY_SIZE(queue->expires);
|
||||
|
||||
spin_lock_irqsave(&cdi_mgr->spinlock, flags);
|
||||
|
||||
/* Report timer expiry */
|
||||
event.idx = pin->idx;
|
||||
event.code = CDI_MGR_GPIO_INTR_TIMEOUT;
|
||||
if (cdi_mgr_gpio_eq_event(cdi_mgr, event) != 0)
|
||||
dev_err(cdi_mgr->dev,
|
||||
"%s: failed to enqueue interrupt timeout event, "
|
||||
"idx %d\n", __func__, pin->idx);
|
||||
|
||||
/* Dequeue next interrupt timer */
|
||||
if (queue->count == 0)
|
||||
goto unlock;
|
||||
|
||||
expire = &queue->expires[queue->head];
|
||||
hrtimer_start(&queue->timer, *expire, HRTIMER_MODE_ABS);
|
||||
queue->head = (queue->head+1) % queue_cap;
|
||||
queue->count--;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&cdi_mgr->spinlock, flags);
|
||||
|
||||
wake_up_interruptible(&cdi_mgr->gpio_events.wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t cdi_mgr_isr(int irq, void *data)
|
||||
{
|
||||
struct cdi_mgr_priv *cdi_mgr;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
int i = 0, gpio_mask = 0;
|
||||
int i = 0;
|
||||
struct cam_gpio_config *pin;
|
||||
struct cdi_mgr_gpio_intr event;
|
||||
|
||||
if (data) {
|
||||
cdi_mgr = (struct cdi_mgr_priv *)data;
|
||||
if (!cdi_mgr->intrs_enable)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
spin_lock_irqsave(&cdi_mgr->spinlock, flags);
|
||||
for (i = 0; i < cdi_mgr->gpio_count; i++) {
|
||||
if (irq == cdi_mgr->gpio_arr[i].gpio_intr_irq)
|
||||
gpio_mask |= (1 << cdi_mgr->gpio_arr[i].index);
|
||||
for (i = 0; i < cdi_mgr->num_gpios; i++) {
|
||||
pin = &cdi_mgr->gpios[i];
|
||||
if (irq != pin->intr_irq)
|
||||
continue;
|
||||
event.idx = pin->idx;
|
||||
event.code = CDI_MGR_GPIO_INTR;
|
||||
if (cdi_mgr_gpio_eq_event(cdi_mgr, event) != 0)
|
||||
pr_err("Failed to enqueue interrupt event, "
|
||||
"idx %d\n", pin->idx);
|
||||
if (cdi_mgr_gpio_eq_intr_timer(pin) != 0)
|
||||
pr_err("Failed to enqueue interrupt timer, "
|
||||
"idx %d\n", pin->idx);
|
||||
}
|
||||
spin_unlock_irqrestore(&cdi_mgr->spinlock, flags);
|
||||
cdi_mgr->err_irq_recvd_status_mask = gpio_mask;
|
||||
wake_up_interruptible(&cdi_mgr->err_queue);
|
||||
|
||||
wake_up_interruptible(&cdi_mgr->gpio_events.wait);
|
||||
|
||||
spin_lock_irqsave(&cdi_mgr->spinlock, flags);
|
||||
if (cdi_mgr->sinfo.si_signo && cdi_mgr->t) {
|
||||
@@ -250,9 +394,6 @@ static irqreturn_t cdi_mgr_isr(int irq, void *data)
|
||||
cdi_mgr->t);
|
||||
if (ret < 0) {
|
||||
pr_err("error sending signal\n");
|
||||
spin_unlock_irqrestore(&cdi_mgr->spinlock,
|
||||
flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&cdi_mgr->spinlock, flags);
|
||||
@@ -261,6 +402,29 @@ static irqreturn_t cdi_mgr_isr(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart cdi_mgr_intr_timer(struct hrtimer *timer)
|
||||
{
|
||||
struct cam_gpio_timer_queue *queue;
|
||||
struct cam_gpio_config *pin;
|
||||
|
||||
if (timer == NULL)
|
||||
goto error;
|
||||
|
||||
queue = container_of(timer, struct cam_gpio_timer_queue, timer);
|
||||
pin = container_of(queue, struct cam_gpio_config, timers);
|
||||
|
||||
if (cdi_mgr_gpio_dq_intr_timer(pin) != 0)
|
||||
goto error;
|
||||
|
||||
goto done;
|
||||
|
||||
error:
|
||||
pr_err("Failed to handle GPIO interrupt timer expiry\n");
|
||||
|
||||
done:
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
int cdi_delete_lst(struct device *dev, struct i2c_client *client)
|
||||
{
|
||||
struct cdi_mgr_priv *cdi_mgr;
|
||||
@@ -658,51 +822,191 @@ static int cdi_mgr_pwm_config(
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cdi_mgr_wait_err(
|
||||
struct cdi_mgr_priv *cdi_mgr, void __user *arg)
|
||||
static u32 gpio_to_idx(
|
||||
struct cdi_mgr_priv *cdi_mgr, u32 idx)
|
||||
{
|
||||
int err = 0, i = 0;
|
||||
uint32_t gpio_irq_monitor_mask = 0;
|
||||
uint32_t gpio_irq_status_mask = 0;
|
||||
int i;
|
||||
|
||||
if (!atomic_xchg(&cdi_mgr->irq_in_use, 1)) {
|
||||
for (i = 0; i < cdi_mgr->gpio_count; i++) {
|
||||
if ((cdi_mgr->gpio_arr[i].gpio_dir == CAM_DEVBLK_GPIO_INPUT_INTERRUPT) &&
|
||||
cdi_mgr->gpio_arr[i].gpio_intr_irq >= 0)
|
||||
enable_irq(cdi_mgr->gpio_arr[i].gpio_intr_irq);
|
||||
}
|
||||
cdi_mgr->err_irq_recvd_status_mask = 0;
|
||||
cdi_mgr->stop_err_irq_wait = false;
|
||||
for (i = 0; i < cdi_mgr->num_gpios; i++)
|
||||
if (cdi_mgr->gpios[i].idx == idx)
|
||||
return i;
|
||||
|
||||
return MAX_CDI_GPIOS;
|
||||
}
|
||||
|
||||
if (get_user(gpio_irq_monitor_mask, (uint32_t __user *)arg)) {
|
||||
static int cdi_mgr_gpio_config(
|
||||
struct cdi_mgr_priv *cdi_mgr, const void __user *arg)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
struct cdi_mgr_gpio_info config;
|
||||
u32 idx;
|
||||
struct cam_gpio_config *pin;
|
||||
|
||||
if (!cdi_mgr)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&config, arg, sizeof(config))) {
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: failed to get_user\n", __func__);
|
||||
"%s: failed to copy from user\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
do {
|
||||
err = wait_event_interruptible(cdi_mgr->err_queue,
|
||||
(cdi_mgr->err_irq_recvd_status_mask & gpio_irq_monitor_mask) != 0);
|
||||
if (err < 0) {
|
||||
dev_err(cdi_mgr->pdev, "%s: wait_event_interruptible failed\n", __func__);
|
||||
break;
|
||||
idx = gpio_to_idx(cdi_mgr, config.idx);
|
||||
if (idx >= MAX_CDI_GPIOS) {
|
||||
dev_err(cdi_mgr->pdev, "%s: unknown gpio idx %u\n",
|
||||
__func__, config.idx);
|
||||
/* WAR: nvbugs/4087689 */
|
||||
/* return -EFAULT; */
|
||||
return 0;
|
||||
}
|
||||
|
||||
gpio_irq_status_mask = cdi_mgr->err_irq_recvd_status_mask & gpio_irq_monitor_mask;
|
||||
pin = &cdi_mgr->gpios[idx];
|
||||
|
||||
if (!cdi_mgr->stop_err_irq_wait &&
|
||||
put_user(gpio_irq_status_mask, (u32 __user *) arg)) {
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: failed to put_user\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
cdi_mgr->err_irq_recvd_status_mask = 0;
|
||||
} while (cdi_mgr->err_irq_reported == false);
|
||||
/* Interrupt timeout */
|
||||
if ((config.timeout_ms > 0) &&
|
||||
(pin->gpio_dir != CAM_DEVBLK_GPIO_INPUT_INTR))
|
||||
dev_warn(cdi_mgr->pdev,
|
||||
"%s: gpio idx %u setting timeout for non-interrupt "
|
||||
"type pin has no effect\n", __func__, idx);
|
||||
|
||||
pin->timeout = ms_to_ktime(config.timeout_ms);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_intr_arm(
|
||||
struct cdi_mgr_priv *cdi_mgr)
|
||||
{
|
||||
int i;
|
||||
struct cam_gpio_config *pin;
|
||||
|
||||
if (!cdi_mgr)
|
||||
return -EINVAL;
|
||||
|
||||
if (!atomic_xchg(&cdi_mgr->irq_in_use, 1))
|
||||
for (i = 0; i < cdi_mgr->num_gpios; i++) {
|
||||
pin = &cdi_mgr->gpios[i];
|
||||
if ((pin->gpio_dir == CAM_DEVBLK_GPIO_INPUT_INTR) &&
|
||||
(pin->intr_irq >= 0))
|
||||
enable_irq(pin->intr_irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_intr_reset(struct cdi_mgr_priv *cdi_mgr)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
struct cam_gpio_config *pin;
|
||||
struct cam_gpio_event_queue *evts_queue;
|
||||
struct cam_gpio_timer_queue *tmrs_queue;
|
||||
|
||||
if (!cdi_mgr)
|
||||
return -EINVAL;
|
||||
|
||||
evts_queue = &cdi_mgr->gpio_events;
|
||||
|
||||
spin_lock_irqsave(&cdi_mgr->spinlock, flags);
|
||||
|
||||
// Reset GPIO pin timer queues
|
||||
for (i = 0; i < cdi_mgr->num_gpios; i++) {
|
||||
pin = &cdi_mgr->gpios[i];
|
||||
tmrs_queue = &pin->timers;
|
||||
(void)hrtimer_try_to_cancel(&tmrs_queue->timer);
|
||||
tmrs_queue->head = 0;
|
||||
tmrs_queue->count = 0;
|
||||
}
|
||||
|
||||
// Reset GPIO events queue
|
||||
evts_queue->head = 0;
|
||||
evts_queue->count = 0;
|
||||
|
||||
cdi_mgr->intrs_enable = false;
|
||||
cdi_mgr->stop_err_irq_wait = false;
|
||||
|
||||
spin_unlock_irqrestore(&cdi_mgr->spinlock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_intr_wait(
|
||||
struct cdi_mgr_priv *cdi_mgr, void __user *arg)
|
||||
{
|
||||
int err = 0;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct cdi_mgr_gpio_intr event;
|
||||
|
||||
if (!cdi_mgr) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = cdi_mgr_gpio_intr_arm(cdi_mgr);
|
||||
if (err != 0) {
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: cdi_mgr_gpio_intr_arm failed %d\n",
|
||||
__func__, err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = wait_event_interruptible(cdi_mgr->gpio_events.wait,
|
||||
(cdi_mgr->stop_err_irq_wait
|
||||
|| (cdi_mgr->gpio_events.count > 0)));
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: wait_event_interruptible failed %d\n",
|
||||
__func__, err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (cdi_mgr->stop_err_irq_wait) {
|
||||
event.idx = 0;
|
||||
event.code = CDI_MGR_GPIO_INTR_UNBLOCK;
|
||||
if (copy_to_user(arg, &event, sizeof(event))) {
|
||||
err = -EFAULT;
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: failed to copy to user\n", __func__);
|
||||
}
|
||||
cdi_mgr->stop_err_irq_wait = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cdi_mgr->spinlock, flags);
|
||||
ret = cdi_mgr_gpio_dq_event(cdi_mgr, &event);
|
||||
spin_unlock_irqrestore(&cdi_mgr->spinlock, flags);
|
||||
if (ret != 0) {
|
||||
err = ret;
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: failed to dequeue interrupt event\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &event, sizeof(event))) {
|
||||
err = -EFAULT;
|
||||
dev_err(cdi_mgr->pdev,
|
||||
"%s: failed to copy to user\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cdi_mgr_gpio_intr_wait_abort(struct cdi_mgr_priv *cdi_mgr)
|
||||
{
|
||||
if (!cdi_mgr)
|
||||
return -EINVAL;
|
||||
|
||||
cdi_mgr->stop_err_irq_wait = true;
|
||||
wake_up_interruptible(&cdi_mgr->gpio_events.wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdi_mgr_des_power(
|
||||
struct cdi_mgr_priv *cdi_mgr, bool enable)
|
||||
{
|
||||
@@ -771,11 +1075,11 @@ static long cdi_mgr_ioctl(
|
||||
* and then register PID
|
||||
*/
|
||||
if (!atomic_xchg(&cdi_mgr->irq_in_use, 1)) {
|
||||
for (i = 0; i < cdi_mgr->gpio_count; i++) {
|
||||
if ((cdi_mgr->gpio_arr[i].gpio_dir ==
|
||||
CAM_DEVBLK_GPIO_INPUT_INTERRUPT) &&
|
||||
cdi_mgr->gpio_arr[i].gpio_intr_irq >= 0)
|
||||
enable_irq(cdi_mgr->gpio_arr[i].gpio_intr_irq);
|
||||
for (i = 0; i < cdi_mgr->num_gpios; i++) {
|
||||
if ((cdi_mgr->gpios[i].gpio_dir ==
|
||||
CAM_DEVBLK_GPIO_INPUT_INTR) &&
|
||||
cdi_mgr->gpios[i].intr_irq >= 0)
|
||||
enable_irq(cdi_mgr->gpios[i].intr_irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -812,17 +1116,18 @@ static long cdi_mgr_ioctl(
|
||||
case CDI_MGR_IOCTL_PWM_CONFIG:
|
||||
err = cdi_mgr_pwm_config(cdi_mgr, (const void __user *)arg);
|
||||
break;
|
||||
case CDI_MGR_IOCTL_WAIT_ERR:
|
||||
err = cdi_mgr_wait_err(cdi_mgr, (void __user *)arg);
|
||||
case CDI_MGR_IOCTL_INTR_CONFIG:
|
||||
err = cdi_mgr_gpio_config(cdi_mgr, (const void __user *)arg);
|
||||
break;
|
||||
case CDI_MGR_IOCTL_ABORT_WAIT_ERR:
|
||||
cdi_mgr->err_irq_recvd_status_mask = CDI_MGR_STOP_GPIO_INTR_EVENT_WAIT;
|
||||
cdi_mgr->err_irq_reported = true;
|
||||
cdi_mgr->stop_err_irq_wait = true;
|
||||
wake_up_interruptible(&cdi_mgr->err_queue);
|
||||
case CDI_MGR_IOCTL_INTR_ENABLE:
|
||||
err = cdi_mgr_gpio_intr_reset(cdi_mgr);
|
||||
cdi_mgr->intrs_enable = true;
|
||||
break;
|
||||
case CDI_MGR_IOCTL_ENABLE_ERROR_REPORT:
|
||||
cdi_mgr->err_irq_reported = true;
|
||||
case CDI_MGR_IOCTL_INTR_WAIT:
|
||||
err = cdi_mgr_gpio_intr_wait(cdi_mgr, (void __user *)arg);
|
||||
break;
|
||||
case CDI_MGR_IOCTL_INTR_WAIT_ABORT:
|
||||
err = cdi_mgr_gpio_intr_wait_abort(cdi_mgr);
|
||||
break;
|
||||
case CDI_MGR_IOCTL_GET_PWR_INFO:
|
||||
err = cdi_mgr_get_pwr_ctrl_info(cdi_mgr, (void __user *)arg);
|
||||
@@ -854,11 +1159,11 @@ static int cdi_mgr_open(struct inode *inode, struct file *file)
|
||||
if (atomic_xchg(&cdi_mgr->in_use, 1))
|
||||
return -EBUSY;
|
||||
|
||||
cdi_mgr->err_irq_reported = false;
|
||||
|
||||
dev_dbg(cdi_mgr->pdev, "%s\n", __func__);
|
||||
file->private_data = cdi_mgr;
|
||||
|
||||
cdi_mgr->stop_err_irq_wait = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -868,7 +1173,6 @@ static int cdi_mgr_release(struct inode *inode, struct file *file)
|
||||
int i = 0;
|
||||
struct cdi_mgr_priv *cdi_mgr = file->private_data;
|
||||
|
||||
cdi_mgr->err_irq_reported = true;
|
||||
if (cdi_mgr->tca9539.enable) {
|
||||
if (down_timeout(&tca9539_sem,
|
||||
usecs_to_jiffies(TIMEOUT_US)) != 0)
|
||||
@@ -898,15 +1202,15 @@ static int cdi_mgr_release(struct inode *inode, struct file *file)
|
||||
cdi_mgr_mcdi_ctrl(cdi_mgr, false);
|
||||
|
||||
/* disable irq if irq is in use, when device is closed */
|
||||
(void)cdi_mgr_gpio_intr_reset(cdi_mgr);
|
||||
if (atomic_xchg(&cdi_mgr->irq_in_use, 0)) {
|
||||
for (i = 0; i < cdi_mgr->gpio_count; i++) {
|
||||
if ((cdi_mgr->gpio_arr[i].gpio_dir == CAM_DEVBLK_GPIO_INPUT_INTERRUPT) &&
|
||||
cdi_mgr->gpio_arr[i].gpio_intr_irq >= 0)
|
||||
disable_irq(cdi_mgr->gpio_arr[i].gpio_intr_irq);
|
||||
for (i = 0; i < cdi_mgr->num_gpios; i++) {
|
||||
if ((cdi_mgr->gpios[i].gpio_dir == CAM_DEVBLK_GPIO_INPUT_INTR) &&
|
||||
cdi_mgr->gpios[i].intr_irq >= 0)
|
||||
disable_irq(cdi_mgr->gpios[i].intr_irq);
|
||||
}
|
||||
cdi_mgr->err_irq_recvd_status_mask = CDI_MGR_STOP_GPIO_INTR_EVENT_WAIT;
|
||||
cdi_mgr->stop_err_irq_wait = true;
|
||||
wake_up_interruptible(&cdi_mgr->err_queue);
|
||||
wake_up_interruptible(&cdi_mgr->gpio_events.wait);
|
||||
}
|
||||
|
||||
/* if runtime_pwrctrl_off is not true, power off all here */
|
||||
@@ -954,8 +1258,8 @@ static void cdi_mgr_del(struct cdi_mgr_priv *cdi_mgr)
|
||||
i2c_put_adapter(cdi_mgr->adap);
|
||||
|
||||
for (i = 0; i < MAX_CDI_GPIOS; i++) {
|
||||
if (cdi_mgr->gpio_arr[i].desc)
|
||||
devm_gpiod_put(cdi_mgr->dev, cdi_mgr->gpio_arr[i].desc);
|
||||
if (cdi_mgr->gpios[i].desc)
|
||||
devm_gpiod_put(cdi_mgr->dev, cdi_mgr->gpios[i].desc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1251,32 +1555,32 @@ static int cdi_mgr_setup_gpio_interrupt(struct device *dev, struct cdi_mgr_priv
|
||||
int ret = 0;
|
||||
int gpio_irq = 0;
|
||||
|
||||
ret = gpiod_direction_input(cdi_mgr->gpio_arr[idx].desc);
|
||||
ret = gpiod_direction_input(cdi_mgr->gpios[idx].desc);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s Failed to gpio direction : input 0\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_irq = gpiod_to_irq(cdi_mgr->gpio_arr[idx].desc);
|
||||
gpio_irq = gpiod_to_irq(cdi_mgr->gpios[idx].desc);
|
||||
if (gpio_irq < 0) {
|
||||
dev_err(dev, "gpiod_to_irq() failed: %d\n", gpio_irq);
|
||||
return gpio_irq;
|
||||
}
|
||||
|
||||
cdi_mgr->gpio_arr[idx].gpio_intr_irq = gpio_irq;
|
||||
cdi_mgr->gpios[idx].intr_irq = gpio_irq;
|
||||
ret = devm_request_irq(dev,
|
||||
cdi_mgr->gpio_arr[idx].gpio_intr_irq,
|
||||
cdi_mgr->gpios[idx].intr_irq,
|
||||
cdi_mgr_isr, intr_edge, dev_name(dev), cdi_mgr);
|
||||
if (ret) {
|
||||
dev_err(dev, "devm_request_irq failed with err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
disable_irq(cdi_mgr->gpio_arr[idx].gpio_intr_irq);
|
||||
disable_irq(cdi_mgr->gpios[idx].intr_irq);
|
||||
atomic_set(&cdi_mgr->irq_in_use, 0);
|
||||
|
||||
cdi_mgr->gpio_arr[idx].gpio_dir = CAM_DEVBLK_GPIO_INPUT_INTERRUPT;
|
||||
cdi_mgr->gpio_arr[idx].index = gpio_idx;
|
||||
cdi_mgr->gpios[idx].gpio_dir = CAM_DEVBLK_GPIO_INPUT_INTR;
|
||||
cdi_mgr->gpios[idx].idx = gpio_idx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1284,7 +1588,7 @@ static int cdi_mgr_setup_gpio_interrupt(struct device *dev, struct cdi_mgr_priv
|
||||
static int cdi_mgr_configure_gpios(struct device *dev, struct cdi_mgr_priv *cdi_mgr)
|
||||
{
|
||||
struct device_node *node = NULL;
|
||||
int gpio_count = 0;
|
||||
int num_gpios = 0;
|
||||
uint32_t i = 0, j = 0;
|
||||
int ret = 0;
|
||||
|
||||
@@ -1296,13 +1600,13 @@ static int cdi_mgr_configure_gpios(struct device *dev, struct cdi_mgr_priv *cdi_
|
||||
if (node != NULL) {
|
||||
struct device_node *child = NULL;
|
||||
|
||||
gpio_count = of_get_child_count(node);
|
||||
if (!gpio_count || gpio_count > MAX_CDI_GPIOS) {
|
||||
num_gpios = of_get_child_count(node);
|
||||
if (!num_gpios || num_gpios > MAX_CDI_GPIOS) {
|
||||
dev_err(dev, "%s Invalid number of gpios : %d\n",
|
||||
__func__, gpio_count);
|
||||
__func__, num_gpios);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(dev, "%s gpio node count : %d\n", __func__, gpio_count);
|
||||
dev_dbg(dev, "%s gpio node count : %d\n", __func__, num_gpios);
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
uint32_t gpio_index = 0;
|
||||
@@ -1320,22 +1624,22 @@ static int cdi_mgr_configure_gpios(struct device *dev, struct cdi_mgr_priv *cdi_
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (j = 0; j < cdi_mgr->gpio_count; j++) {
|
||||
if (cdi_mgr->gpio_arr[j].index == gpio_index) {
|
||||
for (j = 0; j < cdi_mgr->num_gpios; j++) {
|
||||
if (cdi_mgr->gpios[j].idx == gpio_index) {
|
||||
dev_err(dev, "%s GPIO already in use\n", __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
|
||||
cdi_mgr->gpio_arr[i].desc = devm_fwnode_get_gpiod_from_child(dev,
|
||||
cdi_mgr->gpios[i].desc = devm_fwnode_get_gpiod_from_child(dev,
|
||||
"devblk", &child->fwnode, GPIOD_ASIS, NULL);
|
||||
#else
|
||||
cdi_mgr->gpio_arr[i].desc = devm_fwnode_gpiod_get_index(dev,
|
||||
cdi_mgr->gpios[i].desc = devm_fwnode_gpiod_get_index(dev,
|
||||
&child->fwnode, "devblk", 0, GPIOD_ASIS, NULL);
|
||||
#endif
|
||||
if (IS_ERR(cdi_mgr->gpio_arr[i].desc)) {
|
||||
ret = PTR_ERR(cdi_mgr->gpio_arr[i].desc);
|
||||
if (IS_ERR(cdi_mgr->gpios[i].desc)) {
|
||||
ret = PTR_ERR(cdi_mgr->gpios[i].desc);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "%s Failed to allocate gpio desc\n",
|
||||
__func__);
|
||||
@@ -1365,7 +1669,7 @@ static int cdi_mgr_configure_gpios(struct device *dev, struct cdi_mgr_priv *cdi_
|
||||
return -EINVAL;
|
||||
}
|
||||
i++;
|
||||
cdi_mgr->gpio_count++;
|
||||
cdi_mgr->num_gpios++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1422,6 +1726,7 @@ static int cdi_mgr_probe(struct platform_device *pdev)
|
||||
struct cdi_mgr_priv *cdi_mgr;
|
||||
struct cdi_mgr_platform_data *pd;
|
||||
unsigned int i;
|
||||
struct cam_gpio_config *pin = NULL;
|
||||
struct device_node *child = NULL;
|
||||
struct device_node *child_tca9539 = NULL;
|
||||
|
||||
@@ -1439,11 +1744,17 @@ static int cdi_mgr_probe(struct platform_device *pdev)
|
||||
atomic_set(&cdi_mgr->in_use, 0);
|
||||
INIT_LIST_HEAD(&cdi_mgr->dev_list);
|
||||
mutex_init(&cdi_mgr->mutex);
|
||||
init_waitqueue_head(&cdi_mgr->err_queue);
|
||||
cdi_mgr->err_irq_recvd_status_mask = 0;
|
||||
cdi_mgr->err_irq_reported = false;
|
||||
init_waitqueue_head(&cdi_mgr->gpio_events.wait);
|
||||
cdi_mgr->pwm = NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cdi_mgr->gpios); i++) {
|
||||
pin = &cdi_mgr->gpios[i];
|
||||
pin->mgr = cdi_mgr;
|
||||
hrtimer_init(&pin->timers.timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
pin->timers.timer.function = &cdi_mgr_intr_timer;
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
pd = of_cdi_mgr_pdata(pdev);
|
||||
if (IS_ERR(pd))
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
#define CDI_MGR_IOCTL_PWR_INFO _IOW('o', 7, struct cdi_mgr_pwr_info)
|
||||
#define CDI_MGR_IOCTL_PWM_ENABLE _IOW('o', 8, int)
|
||||
#define CDI_MGR_IOCTL_PWM_CONFIG _IOW('o', 9, struct cdi_mgr_pwm_info)
|
||||
#define CDI_MGR_IOCTL_WAIT_ERR _IOWR('o', 10, __u32)
|
||||
#define CDI_MGR_IOCTL_ABORT_WAIT_ERR _IO('o', 11)
|
||||
#define CDI_MGR_IOCTL_GET_EXT_PWR_CTRL _IOR('o', 12, __u8)
|
||||
#define CDI_MGR_IOCTL_ENABLE_ERROR_REPORT _IO('o', 13)
|
||||
#define CDI_MGR_IOCTL_GET_PWR_INFO _IOW('o', 14, struct cdi_mgr_pwr_ctrl_info)
|
||||
#define CDI_MGR_IOCTL_ENABLE_DES_POWER _IO('o', 15)
|
||||
#define CDI_MGR_IOCTL_DISABLE_DES_POWER _IO('o', 16)
|
||||
#define CDI_MGR_IOCTL_INTR_CONFIG _IOW('o', 10, struct cdi_mgr_gpio_info)
|
||||
#define CDI_MGR_IOCTL_INTR_ENABLE _IO('o', 11)
|
||||
#define CDI_MGR_IOCTL_INTR_WAIT _IOR('o', 12, __u32)
|
||||
#define CDI_MGR_IOCTL_INTR_WAIT_ABORT _IO('o', 13)
|
||||
#define CDI_MGR_IOCTL_GET_EXT_PWR_CTRL _IOR('o', 14, __u8)
|
||||
#define CDI_MGR_IOCTL_GET_PWR_INFO _IOW('o', 15, struct cdi_mgr_pwr_ctrl_info)
|
||||
#define CDI_MGR_IOCTL_ENABLE_DES_POWER _IO('o', 16)
|
||||
#define CDI_MGR_IOCTL_DISABLE_DES_POWER _IO('o', 17)
|
||||
|
||||
#define CDI_MGR_POWER_ALL 5
|
||||
#define MAX_CDI_NAME_LENGTH 32
|
||||
@@ -57,14 +58,26 @@ struct cdi_mgr_pwr_ctrl_info {
|
||||
__s8 des_pwr_i2c_addr;
|
||||
};
|
||||
|
||||
|
||||
struct cdi_mgr_gpio_info {
|
||||
__u32 idx;
|
||||
__u32 timeout_ms;
|
||||
};
|
||||
|
||||
struct cdi_mgr_gpio_intr {
|
||||
__u32 idx;
|
||||
__u32 code;
|
||||
};
|
||||
|
||||
struct cdi_mgr_pwm_info {
|
||||
__u64 duty_ns;
|
||||
__u64 period_ns;
|
||||
};
|
||||
|
||||
enum {
|
||||
CDI_MGR_PWM_DISABLE = 0,
|
||||
CDI_MGR_PWM_ENABLE,
|
||||
CDI_MGR_GPIO_INTR_UNBLOCK = 0,
|
||||
CDI_MGR_GPIO_INTR,
|
||||
CDI_MGR_GPIO_INTR_TIMEOUT,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -72,4 +85,9 @@ enum {
|
||||
CDI_MGR_SIGNAL_SUSPEND,
|
||||
};
|
||||
|
||||
enum {
|
||||
CDI_MGR_PWM_DISABLE = 0,
|
||||
CDI_MGR_PWM_ENABLE,
|
||||
};
|
||||
|
||||
#endif /* __UAPI_TEGRA_CDI_MGR_H__ */
|
||||
|
||||
Reference in New Issue
Block a user