Files
linux-nv-oot/drivers/spi/spi-aurix-tegra.c
Jon Hunter 233a7eb320 drivers: Fix missing headers for Linux v6.8
For Linux v6.8, commit ef175b29a242 ("of: Stop circularly including
of_device.h and of_platform.h") updated the OF headers included by these
header files. This breaks the build for various drivers and so fix this
by including the headers that are actually needed for each driver.

Bug 4448428

Change-Id: Ia40ab13f865d5631c96855ecc49145848f99c996
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3032442
(cherry picked from commit ab65399274)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3063003
Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2024-01-25 13:55:11 -08:00

423 lines
9.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
#include <nvidia/conftest.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
//#include <soc/tegra/virt/tegra_hv_pm_ctl.h>
#include <linux/version.h>
#include <linux/sched/signal.h>
#define AURIX 0x3
#define TEGRA 0x2
#define SRC(x) ((x))
#define DEST(x) ((x) << 4)
#define FUSA 0x2
#define SHUTDOWN 0x6
#define RESET 0x7
#define SUSPEND 0x8
#define CMD_SHIFT(x) ((x) << 3)
#define AURIX_TEGRA 0x0
#define TEGRA_AURIX 0x1
#define REQ_TYPE(x) ((x) << 7)
#define SPI_DEBUG 1
/*
* Command-Response packet shall follow the structure shown below.
* Packet contains information (e.g. class id, rsp flag) that
* requires 4, 3 or 1 bit. Using Bit Field since we need less than
* 1 byte memory.
*/
struct packet {
u8 e2e_crc;
u8 e2e_alive_counter:4;
u8 data_id_nibble:4;
u8 src_layer_id:4;
u8 dest_layer_id:4;
u8 class_id:3;
u8 cmd_msg_id:4;
u8 rsp_flag:1;
u8 msg_spec_payload[12];
} __attribute__((__packed__));
struct aurix_tegra_spi_data {
struct spi_device *spi;
struct task_struct *thread;
struct packet *transmit;
struct packet *receive;
spinlock_t lock;
bool exited;
u8 buf_len;
u8 command;
};
/***************TBD ** use actual APIs from soc/tegra/virt/tegra_hv_pm_ctl.h ***/
static int tegra_hv_pm_ctl_trigger_sys_suspend(void)
{
return -ENOTSUPP;
}
static int tegra_hv_pm_ctl_trigger_sys_shutdown(void)
{
return -ENOTSUPP;
}
static int tegra_hv_pm_ctl_trigger_sys_reboot(void)
{
return -ENOTSUPP;
}
static void print_message(struct aurix_tegra_spi_data *data)
{
#if SPI_DEBUG == 1
struct spi_device *spi = data->spi;
u8 *msg = (u8*) data->receive;
int len = data->buf_len;
int i;
for (i = 0; i < len; i=i+4)
dev_info(&spi->dev, "%02X %02X %02X %02X",
msg[i], msg[i+1], msg[i+2], msg[i+3]);
#else
#endif
}
/*
* return value: cmd_msg_id (SHUTDOWN, RESET or SUSPEND),
* -1 otherwise (command not valid).
*/
static int read_cmd_message_id(struct aurix_tegra_spi_data *data)
{
int ret;
struct spi_device *spi = data->spi;
struct packet *msg = data->receive;
if (msg->src_layer_id != AURIX) {
dev_err(&spi->dev, "Source is not Aurix\n");
return -1;
}
if (msg->dest_layer_id != TEGRA) {
dev_err(&spi->dev, "Destination is not Tegra\n");
return -1;
}
if (msg->class_id != FUSA) {
dev_err(&spi->dev, "Class id not valid\n");
return -1;
}
if (msg->rsp_flag != AURIX_TEGRA) {
dev_err(&spi->dev, "RSP flag not valid\n");
return -1;
}
switch(msg->cmd_msg_id) {
case SHUTDOWN:
ret = SHUTDOWN;
break;
case RESET:
ret = RESET;
break;
case SUSPEND:
ret = SUSPEND;
break;
default:
ret = -1;
}
return ret;
}
/*
* return value: 0 on success (valid command received),
* negative value otherwise (failure to receive or command is not valid)
*/
static int aurix_tegra_receive(struct aurix_tegra_spi_data *data)
{
int ret;
struct spi_device *spi = data->spi;
struct spi_message msg;
u8 *tx_buf = (u8*) data->transmit;
u8 *rx_buf = (u8*) data->receive;
u8 len = data->buf_len;
struct spi_transfer tr = {
.rx_buf = rx_buf,
.tx_buf = tx_buf,
.len = len,
};
memset(rx_buf, 0, len);
memset(tx_buf, 0xFF, len);
spi_message_init(&msg);
spi_message_add_tail(&tr, &msg);
ret = spi_sync(spi, &msg);
BUG_ON(ret > 0);
if (ret < 0) {
dev_dbg(&spi->dev, "%s: spi_sync() ret val is %d\n",
__func__, ret);
return ret;
}
print_message(data);
ret = read_cmd_message_id(data);
if (ret < 0) {
dev_err(&spi->dev, "%s: Command received not valid\n",
__func__);
return -1;
}
data->command = ret;
return 0;
}
static int trigger_command(struct aurix_tegra_spi_data *data)
{
int err;
struct device *dev = &data->spi->dev;
dev_dbg(&data->spi->dev, "%s: Triggering Command %x\n",
__func__, data->command);
switch(data->command) {
case SHUTDOWN:
err = tegra_hv_pm_ctl_trigger_sys_shutdown();
if (err < 0) {
dev_err(dev, "%s: trigger_sys_shutdown failed\n",
__func__);
return err;
}
break;
case RESET:
err = tegra_hv_pm_ctl_trigger_sys_reboot();
if (err < 0) {
dev_err(dev, "%s: trigger_sys_reboot failed\n",
__func__);
return err;
}
break;
case SUSPEND:
err = tegra_hv_pm_ctl_trigger_sys_suspend();
if (err < 0) {
dev_err(dev, "%s: trigger_sys_suspend failed\n",
__func__);
return err;
}
break;
default:
BUG();
}
return 0;
}
static int aurix_tegra_read_thread(void *data)
{
struct aurix_tegra_spi_data *aurix_data = (struct aurix_tegra_spi_data *) data;
struct device *dev = &aurix_data->spi->dev;
unsigned long flags;
int err = 0;
/*
* Error from aurix_tegra_receive() is raised in one of
* the following cases:
* i) spi_sync() failed
* ii) command received not valid.
* On aurix_tegra_stop_kthread(), SIGINT is sent to kthread
* ONLY if kthread is still running (exited = false). That way,
* kthread unblocks from spi_sync() and exits with error value.
* Thus, checking if kthread_should_stop is not necessary.
*/
err = aurix_tegra_receive(aurix_data);
if (err < 0) {
dev_dbg(dev, "%s: Error receiving\n", __func__);
goto ret;
}
err = trigger_command(aurix_data);
if (err < 0)
goto ret;
ret:
spin_lock_irqsave(&aurix_data->lock, flags);
aurix_data->exited = true;
spin_unlock_irqrestore(&aurix_data->lock, flags);
#if defined(NV_KTHREAD_COMPLETE_AND_EXIT_PRESENT) /* Linux v5.17 */
kthread_complete_and_exit(NULL, err);
#else
complete_and_exit(NULL, err);
#endif
}
static const struct of_device_id aurix_tegra_ids[] = {
{ .compatible = "aurix-tegra-spi", },
{}
};
MODULE_DEVICE_TABLE(of, aurix_tegra_ids);
static int aurix_tegra_spi_probe(struct spi_device *spi)
{
u32 value;
int err = 0;
struct aurix_tegra_spi_data *data;
struct device_node *np = spi->dev.of_node;
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
if (IS_ERR_OR_NULL(data))
return -ENOMEM;
spin_lock_init(&data->lock);
data->exited = false;
err = of_property_read_u32(np, "spi-max-frequency", &value);
if (err < 0) {
dev_err(&spi->dev, "no property for frequency\n");
goto error;
}
spi->max_speed_hz = value;
/*
* Slave can receive and transmit data in Mode 1
*/
spi->mode = SPI_CPHA;
err = spi_setup(spi);
if (err < 0) {
dev_err(&spi->dev, "spi_setup failed!\n");
goto error;
}
data->spi = spi;
data->buf_len = sizeof(struct packet);
data->transmit = devm_kzalloc(&spi->dev, data->buf_len, GFP_KERNEL);
if (IS_ERR_OR_NULL(data->transmit)) {
err = -ENOMEM;
goto error;
}
data->receive = devm_kzalloc(&spi->dev, data->buf_len, GFP_KERNEL);
if (IS_ERR_OR_NULL(data->receive)) {
err = -ENOMEM;
goto error;
}
/*
* Kernel thread handles the communication between Aurix and Tegra.
* It remains idle, until a request from Aurix is sent. Upon receiving,
* it triggers the corresponding command.
*/
data->thread = kthread_run(aurix_tegra_read_thread,
(void*) data, "aurix_tegra_kthread");
if (IS_ERR_OR_NULL(data->thread)) {
err = PTR_ERR(data->thread);
goto error;
}
spi_set_drvdata(spi, data);
return 0;
error:
return err;
}
/*
* Sending SIGINT to kthread unblocks it from spi_sync()
* and forces it to exit. SIGINT should raised ONLY if
* exited status is false, meaning that kthread is still
* running. kthread_stop() is not needed since the kthread
* does not make call to kthread_should_stop()
*/
static int aurix_tegra_stop_kthread(struct device *dev)
{
struct aurix_tegra_spi_data *data = dev_get_drvdata(dev);
int ret = 0;
unsigned long flags;
if (data->thread) {
spin_lock_irqsave(&data->lock, flags);
if (!data->exited)
ret = send_sig(SIGINT, data->thread, 0);
spin_unlock_irqrestore(&data->lock, flags);
if (ret < 0) {
dev_err(dev, "%s: Error sending SIGINT\n", __func__);
return ret;
}
}
return ret;
}
static int aurix_tegra_start_kthread(struct device *dev)
{
struct aurix_tegra_spi_data *data = dev_get_drvdata(dev);
int ret = 0;
unsigned long flags;
data->thread = kthread_run(aurix_tegra_read_thread,
(void*) data, "aurix_tegra_kthread");
if (IS_ERR_OR_NULL(data->thread)) {
ret = PTR_ERR(data->thread);
dev_err(dev, "%s: Error creating thread\n", __func__);
return ret;
}
spin_lock_irqsave(&data->lock, flags);
data->exited = false;
spin_unlock_irqrestore(&data->lock, flags);
return ret;
}
/*
* remove, shutdown, suspend, resume functions
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0)
static int aurix_tegra_spi_remove(struct spi_device *spi)
{
return aurix_tegra_stop_kthread(&spi->dev);
}
#else
static void aurix_tegra_spi_remove(struct spi_device *spi)
{
aurix_tegra_stop_kthread(&spi->dev);
}
#endif
static void aurix_tegra_spi_shutdown(struct spi_device *spi)
{
aurix_tegra_stop_kthread(&spi->dev);
}
static int aurix_tegra_spi_suspend(struct device *dev)
{
return aurix_tegra_stop_kthread(dev);
}
static int aurix_tegra_spi_resume(struct device *dev)
{
return aurix_tegra_start_kthread(dev);
}
static SIMPLE_DEV_PM_OPS(aurix_tegra_pm_ops,
aurix_tegra_spi_suspend,
aurix_tegra_spi_resume);
static struct spi_driver aurix_tegra_spi_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "aurix_tegra_spi",
.of_match_table = of_match_ptr(aurix_tegra_ids),
.pm = &aurix_tegra_pm_ops,
},
.probe = aurix_tegra_spi_probe,
.remove = aurix_tegra_spi_remove,
.shutdown = aurix_tegra_spi_shutdown,
};
module_spi_driver(aurix_tegra_spi_driver);
MODULE_AUTHOR("Theodoros Marinakis <tmarinakis@nvidia.com>");
MODULE_DESCRIPTION("Aurix-Tegra SPI communication driver");
MODULE_LICENSE("GPL v2");