// SPDX-License-Identifier: GPL-2.0 /* * Google LWIS SPI Device Driver * * Copyright (c) 2023 Google, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME "-spi-dev: " fmt #include "lwis_device_spi.h" #include #include #include #include #include #include #include #include #include #include #include #include "lwis_device.h" #include "lwis_spi.h" #include "lwis_util.h" #include "lwis_trace.h" #ifdef CONFIG_OF #include "lwis_dt.h" #endif #define LWIS_DRIVER_NAME "lwis-spi-device" static int lwis_spi_device_enable(struct lwis_device *lwis_dev); static int lwis_spi_device_disable(struct lwis_device *lwis_dev); static int lwis_spi_register_io(struct lwis_device *lwis_dev, struct lwis_io_entry *entry, int access_size); static struct lwis_device_subclass_operations spi_vops = { .register_io = lwis_spi_register_io, .register_io_barrier = NULL, .device_enable = lwis_spi_device_enable, .device_disable = lwis_spi_device_disable, .event_enable = NULL, .event_flags_updated = NULL, .close = NULL, }; static struct lwis_event_subscribe_operations spi_subscribe_ops = { .subscribe_event = NULL, .unsubscribe_event = NULL, .notify_event_subscriber = NULL, .release = NULL, }; static int lwis_spi_device_enable(struct lwis_device *lwis_dev) { return 0; } static int lwis_spi_device_disable(struct lwis_device *lwis_dev) { return 0; } static int lwis_spi_register_io(struct lwis_device *lwis_dev, struct lwis_io_entry *entry, int access_size) { struct lwis_spi_device *spi_dev; spi_dev = container_of(lwis_dev, struct lwis_spi_device, base_dev); /* Running in interrupt context is not supported as spi driver might sleep */ if (in_interrupt()) { return -EAGAIN; } lwis_save_register_io_info(lwis_dev, entry, access_size); return lwis_spi_io_entry_rw(spi_dev, entry); } static int lwis_spi_device_setup(struct lwis_spi_device *spi_dev) { int ret; #ifdef CONFIG_OF /* Parse device tree for device configurations */ ret = lwis_spi_device_parse_dt(spi_dev); if (ret) { dev_err(spi_dev->base_dev.dev, "Failed to parse device tree\n"); return ret; } #else /* Non-device-tree init: Save for future implementation */ return -ENOSYS; #endif ret = spi_setup(spi_dev->spi); if (ret) { dev_err(spi_dev->base_dev.dev, "spi_setup fail (%d)\n", ret); return ret; } return 0; } static int lwis_spi_device_probe(struct spi_device *spi) { int ret = 0; struct lwis_spi_device *spi_dev; struct device *dev = &spi->dev; /* Allocate SPI device specific data construct */ spi_dev = devm_kzalloc(dev, sizeof(*spi_dev), GFP_KERNEL); if (!spi_dev) { dev_err(dev, "Failed to allocate spi device structure\n"); return -ENOMEM; } spi_dev->base_dev.type = DEVICE_TYPE_SPI; spi_dev->base_dev.vops = spi_vops; spi_dev->base_dev.subscribe_ops = spi_subscribe_ops; spi_dev->base_dev.plat_dev = NULL; spi_dev->base_dev.k_dev = &spi->dev; spi_dev->spi = spi; /* Initialize spin lock */ spin_lock_init(&spi_dev->spi_lock); /* Call the base device probe function */ ret = lwis_base_probe(&spi_dev->base_dev); if (ret) { dev_err(dev, "Error in lwis base probe\n"); goto error_probe; } spi_set_drvdata(spi, &spi_dev->base_dev); /* Call SPI device specific setup function */ ret = lwis_spi_device_setup(spi_dev); if (ret) { dev_err(spi_dev->base_dev.dev, "Error in spi device initialization\n"); lwis_base_unprobe(&spi_dev->base_dev); goto error_probe; } /* Create associated kworker threads */ ret = lwis_create_kthread_workers(&spi_dev->base_dev); if (ret) { dev_err(spi_dev->base_dev.dev, "Failed to create lwis_spi_kthread"); lwis_base_unprobe(&spi_dev->base_dev); goto error_probe; } if (spi_dev->base_dev.transaction_thread_priority != 0) { ret = lwis_set_kthread_priority(&spi_dev->base_dev, spi_dev->base_dev.transaction_worker_thread, spi_dev->base_dev.transaction_thread_priority); if (ret) { dev_err(spi_dev->base_dev.dev, "Failed to set LWIS SPI transaction kthread priority (%d)", ret); lwis_base_unprobe(&spi_dev->base_dev); goto error_probe; } } dev_info(spi_dev->base_dev.dev, "SPI Device Probe: Success\n"); return 0; error_probe: return ret; } #ifdef CONFIG_PM static int lwis_spi_device_suspend(struct device *dev) { struct lwis_device *lwis_dev = dev_get_drvdata(dev); if (lwis_dev->pm_hibernation == 0) { return 0; } if (lwis_dev->enabled != 0) { dev_warn(lwis_dev->dev, "Can't suspend because %s is in use!\n", lwis_dev->name); return -EBUSY; } return 0; } static int lwis_spi_device_resume(struct device *dev) { return 0; } static SIMPLE_DEV_PM_OPS(lwis_spi_device_ops, lwis_spi_device_suspend, lwis_spi_device_resume); #endif #ifdef CONFIG_OF static const struct of_device_id lwis_id_match[] = { { .compatible = LWIS_SPI_DEVICE_COMPAT }, {}, }; // MODULE_DEVICE_TABLE(of, lwis_id_match); static struct spi_driver lwis_driver = { .probe = lwis_spi_device_probe, .driver = { .name = LWIS_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = lwis_id_match, .pm = &lwis_spi_device_ops, }, }; #else /* CONFIG_OF not defined */ static struct spi_device_id lwis_driver_id[] = { { .name = LWIS_DRIVER_NAME, .driver_data = 0, }, {}, }; MODULE_DEVICE_TABLE(spi, lwis_driver_id); static struct spi_driver lwis_driver = { .probe = lwis_spi_device_probe, .id_table = lwis_driver_id, .driver = { .name = LWIS_DRIVER_NAME, .owner = THIS_MODULE, } }; #endif /* CONFIG_OF */ /* * lwis_spi_device_init: Init function that will be called by the kernel * initialization routines. */ int __init lwis_spi_device_init(void) { int ret = 0; pr_info("SPI device initialization\n"); ret = spi_register_driver(&lwis_driver); if (ret) { pr_err("spi_register_driver failed: %d\n", ret); } return ret; } int lwis_spi_device_deinit(void) { spi_unregister_driver(&lwis_driver); return 0; }