// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved. #include #include #include #include #include //#include #include #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 defined(NV_SPI_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 5.18 */ 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 "); MODULE_DESCRIPTION("Aurix-Tegra SPI communication driver"); MODULE_LICENSE("GPL v2");