tegrakernel/kernel/nvidia/drivers/misc/nvs/nvs_gte.c

308 lines
6.0 KiB
C

/* Copyright (c) 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.
*/
#ifndef NVS_GTE
#define NVS_GTE (0)
#endif
#define NVS_GTE_VERSION (1)
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/nvs.h>
#include <linux/nvs_gte.h>
static void nvs_gte_exit_irq(struct nvs_gte_irq *ngi, unsigned int n)
{
unsigned int i;
unsigned int irq;
unsigned int j;
for (i = 0; i < n; i++) {
if (ngi[i].irq > 0 && ngi[i].gpio >= 0) {
for (j = 0; j < i; j++) {
if (ngi[j].irq == ngi[i].irq)
break;
}
if (j < i)
/* IRQ already done */
continue;
irq = ngi[i].irq;
free_irq(irq, &ngi[i]);
}
}
for (i = 0; i < n; i++) {
if (ngi[i].gpio >= 0)
ngi[i].irq = -1;
}
return;
}
static void nvs_gte_exit_gpio(struct nvs_gte_irq *ngi, unsigned int n)
{
unsigned int gpio;
unsigned int i;
unsigned int j;
for (i = 0; i < n; i++) {
if (ngi[i].gpio >= 0) {
for (j = 0; j < i; j++) {
if (ngi[j].gpio == ngi[i].gpio)
break;
}
if (j < i)
/* GPIO already done */
continue;
gpio = ngi[i].gpio;
gpio_free(gpio);
}
}
for (i = 0; i < n; i++)
ngi[i].gpio = -1;
return;
}
static int nvs_gte_init_gpio2irq(struct device *dev, struct nvs_gte_irq *ngi,
unsigned int n)
{
unsigned int i;
unsigned int j;
int ret = -EINVAL;
for (i = 0; i < n; i++) {
if (ngi[i].irq > 0) {
ret = 0;
continue;
}
if (ngi[i].gpio < 0)
continue;
for (j = 0; j < i; j++) {
if (ngi[j].gpio == ngi[i].gpio)
break;
}
if (j < i) {
/* GPIO already done */
ngi[i].irq = ngi[j].irq;
continue;
}
if (!gpio_is_valid(ngi[i].gpio)) {
ret = -EPROBE_DEFER;
goto nvs_gte_init_gpio_fie;
}
ret = gpio_request(ngi[i].gpio, ngi[i].dev_name);
if (ret) {
ret = -EPROBE_DEFER;
goto nvs_gte_init_gpio_fie;
}
ret = gpio_direction_input(ngi[i].gpio);
if (ret < 0) {
dev_err(dev, "%s %s gpio_dir_input(%d) ERR:%d\n",
__func__, ngi[i].dev_name, ngi[i].gpio, ret);
nvs_gte_exit_irq(ngi, n);
nvs_gte_exit_gpio(ngi, n);
ret = -ENODEV;
goto nvs_gte_init_gpio_fie;
}
ret = gpio_to_irq(ngi[i].gpio);
if (ret <= 0) {
dev_err(dev, "%s %s gpio_to_irq(%d) ERR:%d\n",
__func__, ngi[i].dev_name, ngi[i].gpio, ret);
nvs_gte_exit_irq(ngi, n);
nvs_gte_exit_gpio(ngi, n);
ret = -ENODEV;
goto nvs_gte_init_gpio_fie;
}
ngi[i].irq = ret;
ret = 0;
}
nvs_gte_init_gpio_fie:
return ret;
}
#if NVS_GTE
#include <linux/tegra-gte.h>
static char *nvs_gte_hw_str = "nvidia,tegra194-gte-aon";
int nvs_gte_ts(struct nvs_gte_irq *ngi)
{
struct tegra_gte_ev_desc *desc = (struct tegra_gte_ev_desc *)ngi->gte;
struct tegra_gte_ev_detail dtl;
int ret;
ret = tegra_gte_retrieve_event(desc, &dtl);
if (ret)
ngi->err_n++;
else
ngi->irq_ts = dtl.ts_ns;
return ret;
}
EXPORT_SYMBOL_GPL(nvs_gte_ts);
int nvs_gte_exit(struct device *dev, struct nvs_gte_irq *ngi, unsigned int n)
{
struct tegra_gte_ev_desc *desc;
unsigned int i;
int ret;
int ret_t = 0;
for (i = 0; i < n; i++) {
if (ngi[i].gte) {
desc = (struct tegra_gte_ev_desc *)ngi[i].gte;
ret = tegra_gte_unregister_event(desc);
if (ret) {
ngi[i].gte = NULL;
dev_err(dev,
"%s gte_unregister_event ERR: %d\n",
__func__, ret);
} else {
ret_t |= ret;
}
}
}
nvs_gte_exit_irq(ngi, n);
nvs_gte_exit_gpio(ngi, n);
return ret_t;
}
EXPORT_SYMBOL_GPL(nvs_gte_exit);
int nvs_gte_init(struct device *dev, struct nvs_gte_irq *ngi, unsigned int n)
{
struct tegra_gte_ev_desc *desc;
unsigned int gpio;
unsigned int i;
int ret;
/* Until the GTE API is improved we have to do this */
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, nvs_gte_hw_str);
ret = nvs_gte_init_gpio2irq(dev, ngi, n);
if (ret == -EPROBE_DEFER)
goto nvs_gte_init_fie;
if (ret < 0) {
nvs_gte_exit(dev, ngi, n);
goto nvs_gte_init_fie;
}
ret = -ENODEV;
for (i = 0; i < n; i++) {
if (ngi[i].gpio >= 0 && !ngi[i].gte) {
gpio = ngi[i].gpio;
desc = tegra_gte_register_event(np, gpio);
if (desc) {
ngi[i].gte = (void *)desc;
ret = 0;
}
}
}
nvs_gte_init_fie:
of_node_put(np);
return ret;
}
EXPORT_SYMBOL_GPL(nvs_gte_init);
int nvs_gte_sts(struct nvs_gte_irq *ngi, struct nvs_gte_sts *ngs)
{
struct tegra_gte_ev_desc *desc = (struct tegra_gte_ev_desc *)ngi->gte;
struct tegra_gte_ev_detail dtl;
int ret;
ngs->ver = NVS_GTE_VERSION;
ngs->gte = nvs_gte_hw_str;
ngs->ts_ns = 0;
ngs->ts_raw = 0;
if (desc) {
ret = tegra_gte_retrieve_event(desc, &dtl);
if (ret) {
ngs->err = "HW read err";
} else {
ngs->err = NULL;
ngs->ts_ns = dtl.ts_ns;
ngs->ts_raw = dtl.ts_raw;
}
} else {
ngs->err = "not initialized";
}
return 0;
}
EXPORT_SYMBOL_GPL(nvs_gte_sts);
#else /* !NVS_GTE ========================================================== */
int nvs_gte_ts(struct nvs_gte_irq *ngi)
{
ngi->irq_ts = nvs_timestamp();
return 0;
}
EXPORT_SYMBOL_GPL(nvs_gte_ts);
int nvs_gte_exit(struct device *dev, struct nvs_gte_irq *ngi, unsigned int n)
{
nvs_gte_exit_irq(ngi, n);
nvs_gte_exit_gpio(ngi, n);
return 0;
}
EXPORT_SYMBOL_GPL(nvs_gte_exit);
int nvs_gte_init(struct device *dev, struct nvs_gte_irq *ngi, unsigned int n)
{
int ret;
ret = nvs_gte_init_gpio2irq(dev, ngi, n);
return ret;
}
EXPORT_SYMBOL_GPL(nvs_gte_init);
int nvs_gte_sts(struct nvs_gte_irq *ngi, struct nvs_gte_sts *ngs)
{
ngs->ver = NVS_GTE_VERSION;
ngs->gte = "disabled";
ngs->err = NULL;
ngs->ts_ns = 0;
ngs->ts_raw = 0;
return 0;
}
EXPORT_SYMBOL_GPL(nvs_gte_sts);
#endif /* ! NVS_GTE */
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("NVidia Sensor Generic Timestamp Engine module");
MODULE_AUTHOR("NVIDIA Corporation");