Files
Jon Hunter 74171df1a4 platform: tegra: uss-io-proxy: Migrate to GPIOD
Upstream commit a55893133830 ("gpiolib: Remove unused
devm_gpio_request()") removed the devm_gpio_request() function and this
breaks the Tegra USS IO Proxy driver. The devm_gpiod_get() function has
been supported in the Linux kernel since v4.3 and now that the legacy
GPIO functions are being removed, migrate the Tegra USS IO Proxy driver
over to using GPIO descriptors.

Bug 4387902
Bug 5420210

Change-Id: I0c62977dc4ec829e14f105babdd08bbd64113fa2
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3423827
(cherry picked from commit e4572a5ff434f778178b1dfb39b4c1cf7923db6d)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3462463
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2025-10-02 10:13:50 -07:00

530 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Tegra Ultrasonics Sensor Subsystem IO-Proxy driver
*
* SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include <nvidia/conftest.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#define SAFETY_I2S_CTRL_0 (0x114)
#define MASTER (1 << 5)
struct tegra_uss_io_proxy {
void __iomem *i2s8_base;
struct reset_control *i2s8_reset;
struct clk *uss_clk;
unsigned int hss_version;
struct gpio_desc *uss_reset_gpio;
struct gpio_desc *vsup_dia_gpio;
struct gpio_desc *vsup_fault_gpio;
struct gpio_desc *vsup_latch_gpio;
struct gpio_descs *g1_gpiods;
struct gpio_descs *g2_gpiods;
unsigned long g1_bitmap;
unsigned long g2_bitmap;
};
static const struct of_device_id tegra_uss_io_proxy_of_match[] = {
{ .compatible = "nvidia,uss-io-proxy", },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_uss_io_proxy_of_match);
static bool uss_clk_enabled;
static ssize_t uss_clk_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
int val, rc;
u32 reg;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
if (val == 0) {
rc = reset_control_assert(proxy->i2s8_reset);
if (rc) {
dev_err(dev, "i2s8 reset assert failed: %d\n", rc);
return -EIO;
}
clk_disable_unprepare(proxy->uss_clk);
uss_clk_enabled = false;
} else {
rc = clk_prepare_enable(proxy->uss_clk);
if (rc) {
dev_err(dev, "i2s8 clock enable failed: %d\n", rc);
return -EIO;
}
rc = reset_control_deassert(proxy->i2s8_reset);
if (rc) {
dev_err(dev, "i2s8 reset deassert failed: %d\n", rc);
clk_disable_unprepare(proxy->uss_clk);
return -EIO;
}
reg = ioread32(proxy->i2s8_base + SAFETY_I2S_CTRL_0);
reg |= MASTER;
iowrite32(reg, proxy->i2s8_base + SAFETY_I2S_CTRL_0);
uss_clk_enabled = true;
}
return count;
}
static ssize_t uss_clk_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
if (uss_clk_enabled)
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR_RW(uss_clk);
static ssize_t uss_reset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
int val;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
if (val == 0)
gpiod_set_raw_value(proxy->uss_reset_gpio, 1);
else
gpiod_set_raw_value(proxy->uss_reset_gpio, 0);
return count;
}
static ssize_t uss_reset_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", !gpiod_get_raw_value(proxy->uss_reset_gpio));
}
static DEVICE_ATTR_RW(uss_reset);
static ssize_t vsup_dia_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
int val;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
if (val == 0)
gpiod_set_raw_value(proxy->vsup_dia_gpio, 0);
else
gpiod_set_raw_value(proxy->vsup_dia_gpio, 1);
return count;
}
static ssize_t vsup_dia_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", gpiod_get_raw_value(proxy->vsup_dia_gpio));
}
static DEVICE_ATTR_RW(vsup_dia);
static ssize_t vsup_latch_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
int val;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
if (val == 0)
gpiod_set_raw_value(proxy->vsup_latch_gpio, 0);
else
gpiod_set_raw_value(proxy->vsup_latch_gpio, 1);
return count;
}
static ssize_t vsup_latch_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", gpiod_get_raw_value(proxy->vsup_latch_gpio));
}
static DEVICE_ATTR_RW(vsup_latch);
static ssize_t vsup_fault_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", gpiod_get_raw_value(proxy->vsup_fault_gpio));
}
static DEVICE_ATTR_ADMIN_RO(vsup_fault);
static ssize_t sensor_gpios_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
unsigned long bitmap;
int rc;
u32 val;
u8 *b = (u8 *)&val;
if (kstrtou32(buf, 16, &val))
return -EINVAL;
if (b[2] != 0) {
bitmap = proxy->g1_bitmap & (~b[2]);
bitmap |= (b[0] & b[2]);
rc = gpiod_set_array_value_cansleep(proxy->g1_gpiods->ndescs,
proxy->g1_gpiods->desc,
proxy->g1_gpiods->info,
&bitmap);
if (rc) {
dev_err(dev, "set group-1 GPIOs failed %d\n", rc);
return rc;
}
proxy->g1_bitmap = bitmap;
}
if (b[3] != 0) {
bitmap = proxy->g2_bitmap & (~b[3]);
bitmap |= (b[1] & b[3]);
rc = gpiod_set_array_value_cansleep(proxy->g2_gpiods->ndescs,
proxy->g2_gpiods->desc,
proxy->g2_gpiods->info,
&bitmap);
if (rc) {
dev_err(dev, "set group-2 GPIOs failed %d\n", rc);
return rc;
}
proxy->g2_bitmap = bitmap;
}
return count;
}
static ssize_t sensor_gpios_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tegra_uss_io_proxy *proxy = dev_get_drvdata(dev);
size_t n = PAGE_SIZE;
int i;
n -= snprintf(&buf[PAGE_SIZE - n], n, "Sensor Group 1\n");
for (i = 0; i < 6; i++) {
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tSensor %d: %d\n",
i + 1, !!(proxy->g1_bitmap & (1 << i)));
}
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tVSUP_EN1: %d\n",
!!(proxy->g1_bitmap & (1 << 6)));
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tVSUP_SEL1: %d\n",
!!(proxy->g1_bitmap & (1 << 7)));
n -= snprintf(&buf[PAGE_SIZE - n], n, "Sensor Group 2\n");
for (i = 0; i < 6; i++) {
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tSensor %d: %d\n",
i + 1, !!(proxy->g2_bitmap & (1 << i)));
}
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tVSUP_EN2: %d\n",
!!(proxy->g2_bitmap & (1 << 6)));
if (proxy->hss_version == 1) {
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tVSUP_SEL2: %d\n",
!!(proxy->g2_bitmap & (1 << 7)));
}
if (proxy->hss_version == 2) {
n -= snprintf(&buf[PAGE_SIZE - n], n, "\tVSUP_LATCH: %d\n",
!!(proxy->g2_bitmap & (1 << 7)));
}
return PAGE_SIZE - n;
}
static DEVICE_ATTR_RW(sensor_gpios);
static int tegra_uss_create_dev_attrs(struct platform_device *pdev)
{
struct tegra_uss_io_proxy *proxy = platform_get_drvdata(pdev);
int err;
err = device_create_file(&pdev->dev, &dev_attr_uss_clk);
if (err)
return err;
err = device_create_file(&pdev->dev, &dev_attr_uss_reset);
if (err)
goto err_reset;
err = device_create_file(&pdev->dev, &dev_attr_vsup_dia);
if (err)
goto err_vsup_dia;
if (proxy->hss_version == 1)
err = device_create_file(&pdev->dev, &dev_attr_vsup_latch);
if (proxy->hss_version == 2)
err = device_create_file(&pdev->dev, &dev_attr_vsup_fault);
if (err)
goto err_vsup_latch_fault;
err = device_create_file(&pdev->dev, &dev_attr_sensor_gpios);
if (err)
goto err_sensor_gpios;
return 0;
err_sensor_gpios:
if (proxy->hss_version == 1)
device_remove_file(&pdev->dev, &dev_attr_vsup_latch);
if (proxy->hss_version == 2)
device_remove_file(&pdev->dev, &dev_attr_vsup_fault);
err_vsup_latch_fault:
device_remove_file(&pdev->dev, &dev_attr_vsup_dia);
err_vsup_dia:
device_remove_file(&pdev->dev, &dev_attr_uss_reset);
err_reset:
device_remove_file(&pdev->dev, &dev_attr_uss_clk);
return err;
}
static void tegra_uss_remove_dev_attrs(struct platform_device *pdev)
{
struct tegra_uss_io_proxy *proxy = platform_get_drvdata(pdev);
device_remove_file(&pdev->dev, &dev_attr_uss_clk);
device_remove_file(&pdev->dev, &dev_attr_uss_reset);
device_remove_file(&pdev->dev, &dev_attr_vsup_dia);
if (proxy->hss_version == 1)
device_remove_file(&pdev->dev, &dev_attr_vsup_latch);
if (proxy->hss_version == 2)
device_remove_file(&pdev->dev, &dev_attr_vsup_fault);
device_remove_file(&pdev->dev, &dev_attr_sensor_gpios);
}
static struct gpio_desc *tegra_uss_request_gpio(struct platform_device *pdev, const char *name,
bool is_output)
{
struct gpio_desc *gpiod;
int rc;
gpiod = devm_gpiod_get(&pdev->dev, name, GPIOD_ASIS);
if (IS_ERR(gpiod)) {
dev_err(&pdev->dev, "could not request %s %ld\n", name,
PTR_ERR(gpiod));
return gpiod;
}
if (is_output)
rc = gpiod_direction_output_raw(gpiod, 0);
else
rc = gpiod_direction_input(gpiod);
if (rc) {
dev_err(&pdev->dev, "could not set direction for %s %d\n",
name, rc);
return ERR_PTR(rc);
}
return gpiod;
}
static int tegra_uss_io_proxy_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct tegra_uss_io_proxy *proxy;
int rc;
proxy = devm_kzalloc(&pdev->dev, sizeof(*proxy), GFP_KERNEL);
if (unlikely(proxy == NULL))
return -ENOMEM;
proxy->i2s8_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(proxy->i2s8_base)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->i2s8_base),
"failed to get I2S8 mmio\n");
}
proxy->uss_clk = devm_clk_get(&pdev->dev, "i2s8");
if (IS_ERR(proxy->uss_clk)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->uss_clk),
"failed to get I2S8 clock\n");
}
proxy->i2s8_reset = devm_reset_control_get(&pdev->dev, "i2s8");
if (IS_ERR(proxy->i2s8_reset)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->i2s8_reset),
"failed to get I2S8 reset\n");
}
rc = of_property_read_u32(node, "uss-hss-version", &proxy->hss_version);
if (rc != 0) {
/* backward compatibility */
dev_info(&pdev->dev, "uss-hss-version property doesn't exist, version=1\n");
proxy->hss_version = 1;
}
if (proxy->hss_version > 2) {
return dev_err_probe(&pdev->dev, -EINVAL,
"uss_hss_version is not valid.");
}
proxy->uss_reset_gpio = tegra_uss_request_gpio(pdev, "uss-nres", true);
if (IS_ERR(proxy->uss_reset_gpio)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->uss_reset_gpio),
"failed to get uss-nres GPIO\n");
}
proxy->vsup_dia_gpio = tegra_uss_request_gpio(pdev, "uss-vsup-dia",
true);
if (IS_ERR(proxy->vsup_dia_gpio)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->vsup_dia_gpio),
"failed to get vsup-dia GPIO\n");
}
if (proxy->hss_version == 1) {
proxy->vsup_fault_gpio = tegra_uss_request_gpio(pdev,
"uss-vsup-latch",
true);
if (IS_ERR(proxy->vsup_fault_gpio))
return dev_err_probe(&pdev->dev,
PTR_ERR(proxy->vsup_fault_gpio),
"failed to get vsup-latch GPIO\n");
}
if (proxy->hss_version == 2) {
proxy->vsup_fault_gpio = tegra_uss_request_gpio(pdev,
"uss-vsup-fault",
false);
if (IS_ERR(proxy->vsup_fault_gpio))
return dev_err_probe(&pdev->dev,
PTR_ERR(proxy->vsup_fault_gpio),
"failed to get vsup-fault GPIO\n");
}
proxy->g1_gpiods = devm_gpiod_get_array(&pdev->dev, "sensor-group-1",
GPIOD_OUT_LOW);
if (IS_ERR(proxy->g1_gpiods)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->g1_gpiods),
"failed to get sensor-group-1 GPIOs\n");
}
proxy->g2_gpiods = devm_gpiod_get_array(&pdev->dev, "sensor-group-2",
GPIOD_OUT_LOW);
if (IS_ERR(proxy->g2_gpiods)) {
return dev_err_probe(&pdev->dev, PTR_ERR(proxy->g2_gpiods),
"failed to get sensor-group-2 GPIOs\n");
}
platform_set_drvdata(pdev, proxy);
tegra_uss_create_dev_attrs(pdev);
return 0;
}
static void tegra_uss_reset(struct platform_device *pdev)
{
struct tegra_uss_io_proxy *proxy = platform_get_drvdata(pdev);
unsigned long bitmap = 0;
int rc;
gpiod_set_raw_value(proxy->uss_reset_gpio, 0);
rc = reset_control_assert(proxy->i2s8_reset);
if (rc)
dev_err(&pdev->dev, "i2s8 reset assert failed: %d\n", rc);
clk_disable_unprepare(proxy->uss_clk);
rc = gpiod_set_array_value_cansleep(proxy->g1_gpiods->ndescs,
proxy->g1_gpiods->desc,
proxy->g1_gpiods->info,
&bitmap);
if (rc)
dev_err(&pdev->dev, "set group-1 GPIOs failed %d\n", rc);
proxy->g1_bitmap = 0;
rc = gpiod_set_array_value_cansleep(proxy->g2_gpiods->ndescs,
proxy->g2_gpiods->desc,
proxy->g2_gpiods->info,
&bitmap);
if (rc)
dev_err(&pdev->dev, "set group-2 GPIOs failed %d\n", rc);
proxy->g1_bitmap = 0;
}
static int tegra_uss_io_proxy_remove(struct platform_device *pdev)
{
tegra_uss_remove_dev_attrs(pdev);
tegra_uss_reset(pdev);
return 0;
}
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
static void tegra_uss_io_proxy_remove_wrapper(struct platform_device *pdev)
{
tegra_uss_io_proxy_remove(pdev);
}
#else
static int tegra_uss_io_proxy_remove_wrapper(struct platform_device *pdev)
{
return tegra_uss_io_proxy_remove(pdev);
}
#endif
static struct platform_driver tegra_uss_io_proxy_driver = {
.probe = tegra_uss_io_proxy_probe,
.remove = tegra_uss_io_proxy_remove_wrapper,
.driver = {
.name = "tegra-uss-io-proxy",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tegra_uss_io_proxy_of_match),
},
};
module_platform_driver(tegra_uss_io_proxy_driver);
MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra USS IO-PROXY driver");
MODULE_LICENSE("GPL");