]> code.bitgloo.com Git - clyne/ads1278-fs.git/commitdiff
Initial source upload
authorClyne <clyne@bitgloo.com>
Sat, 23 Nov 2024 15:06:58 +0000 (10:06 -0500)
committerClyne <clyne@bitgloo.com>
Sat, 23 Nov 2024 15:06:58 +0000 (10:06 -0500)
Makefile [new file with mode: 0644]
ads1278-fs.dts [new file with mode: 0644]
ads1278_core.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c850aaa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+KERNELDIR := /usr/src/linux-headers-6.1.21-v8+
+MODULE_NAME=ads1278
+
+SRC := ads1278_core.c
+
+ifneq ($(KERNELRELEASE),)
+    $(MODULE_NAME)-objs = $(SRC:.c=.o)
+    obj-m := $(MODULE_NAME).o
+else
+    PWD := $(shell pwd)
+
+default:
+ifeq ($(strip $(KERNELDIR)),)
+       $(error "KERNELDIR is undefined!")
+else
+       $(MAKE) -C $(KERNELDIR) scripts
+       $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
+endif
+
+clean:
+       rm -rf *~ *.ko *.o *.mod.c modules.order Module.symvers .${MODULE_NAME}* .tmp_versions
+
+endif
diff --git a/ads1278-fs.dts b/ads1278-fs.dts
new file mode 100644 (file)
index 0000000..abdab95
--- /dev/null
@@ -0,0 +1,43 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+       compatible = "brcm,bcm2835";
+
+       fragment@0 {
+               target = <&spi0>;
+               __overlay__ {
+                       status = "okay";
+               };
+       };
+
+       fragment@1 {
+               target = <&spidev0>;
+               __overlay__ {
+                       status = "disabled";
+               };
+       };
+
+       fragment@2 {
+               target = <&spidev1>;
+               __overlay__ {
+                       status = "disabled";
+               };
+       };
+
+       fragment@3 {
+               target = <&spi0>;
+               __overlay__ {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       ads1278: ads1278@0 {
+                               compatible = "ti,ads1278-fs";
+                               status = "okay";
+                               reg = <0>;
+                               pinctrl-names = "default";
+                       };
+               };
+       };
+};
+
diff --git a/ads1278_core.c b/ads1278_core.c
new file mode 100644 (file)
index 0000000..e47f0c7
--- /dev/null
@@ -0,0 +1,214 @@
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#define FRAME_SIZE     (24)
+#define TRANSFER_SIZE  (FRAME_SIZE * 10)
+// 27'648'000 Hz = 144 kSPS
+// 25'000'000 Hz = 130 kSPS
+// 10'000'000 Hz =  52 kSPS
+#define TRANSFER_HZ    (27000000)
+
+// TODO: minimize 45us delay between xfers
+
+// 24-11-23: We're hitting 130 kSPS initially, but are eventually slowed down
+// to 87 kSPS. Need to investigate high-priority workers, forcing a high SPI
+// clock speed, and/or minimizing overall latency.
+
+struct ads1278
+{
+       unsigned char *rx;
+       unsigned char *tx;
+       dma_addr_t rx_dma_handle;
+       dma_addr_t tx_dma_handle;
+       struct spi_message msg[2];
+       struct spi_transfer tr[2];
+       int stop_requested;
+       int running[2];
+};
+
+static void ads1278_transfer_callback1(void *context)
+{
+       struct spi_device *spi = (struct spi_device *)context;
+        struct ads1278 *ads = spi_get_drvdata(spi);
+
+       if (!ads->stop_requested)
+               spi_async(spi, &ads->msg[0]);
+       else
+               ads->running[0] = 0;
+}
+
+static void ads1278_transfer_callback2(void *context)
+{
+       struct spi_device *spi = (struct spi_device *)context;
+        struct ads1278 *ads = spi_get_drvdata(spi);
+
+       if (!ads->stop_requested)
+               spi_async(spi, &ads->msg[1]);
+       else
+               ads->running[1] = 0;
+}
+
+
+static int ads1278_build_transfers(struct spi_device *spi)
+{
+        struct ads1278 *ads = spi_get_drvdata(spi);
+       int i;
+
+       dma_set_coherent_mask(&spi->dev, 0xFFFFFFFF);
+
+       ads->tx = (unsigned char *)dma_alloc_coherent(&spi->dev, TRANSFER_SIZE * 2,
+               &ads->tx_dma_handle, GFP_KERNEL);
+       if (!ads->tx)
+               return -1;
+
+       ads->rx = (unsigned char *)dma_alloc_coherent(&spi->dev, TRANSFER_SIZE * 2,
+               &ads->rx_dma_handle, GFP_KERNEL);
+       if (!ads->rx)
+               return -1;
+
+       memset(ads->tr, 0, sizeof(ads->tr));
+       memset(ads->tx, 0, TRANSFER_SIZE * 2);
+       memset(ads->rx, 0, TRANSFER_SIZE * 2);
+
+       for (i = 0; i < TRANSFER_SIZE * 2; i += FRAME_SIZE) {
+               ads->tx[i] = 0xC0;
+       }
+
+       ads->tr[0].tx_buf = ads->tx;
+       ads->tr[0].rx_buf = ads->rx;
+       ads->tr[0].len = TRANSFER_SIZE;
+       ads->tr[0].cs_off = 1;
+       ads->tr[0].bits_per_word = 8;
+       ads->tr[0].speed_hz = TRANSFER_HZ;
+
+       ads->tr[1].tx_buf = ads->tx + TRANSFER_SIZE;
+       ads->tr[1].rx_buf = ads->rx + TRANSFER_SIZE;
+       ads->tr[1].len = TRANSFER_SIZE;
+       ads->tr[1].cs_off = 1;
+       ads->tr[1].bits_per_word = 8;
+       ads->tr[1].speed_hz = TRANSFER_HZ;
+
+       return 0;
+}
+
+static int ads1278_begin_transfer(struct spi_device *spi)
+{
+        struct ads1278 *ads = spi_get_drvdata(spi);
+
+       if (!ads)
+               return -1;
+
+       if (ads1278_build_transfers(spi)) {
+               dev_err(&spi->dev, "error with building transfer!");
+               return -1;
+       }
+
+       spi_message_init_with_transfers(&ads->msg[0], &ads->tr[0], 1);
+       spi_message_init_with_transfers(&ads->msg[1], &ads->tr[1], 1);
+
+       ads->msg[0].complete = ads1278_transfer_callback1;
+       ads->msg[1].complete = ads1278_transfer_callback2;
+       ads->msg[0].context = spi;
+       ads->msg[1].context = spi;
+       //spi_optimize_message(spi, msg);
+
+       ads->stop_requested = 0;
+       ads->running[0] = 1;
+       ads->running[1] = 1;
+
+       /* TODO ret = */ spi_async(spi, &ads->msg[0]);
+       return spi_async(spi, &ads->msg[1]);
+}
+
+static int ads1278_probe(struct spi_device *spi)
+{
+        struct ads1278 *ads;
+
+       dev_info(&spi->dev, "probing...");
+
+        ads = kzalloc(sizeof(*ads), GFP_KERNEL);
+        if (!ads)
+                return -ENOMEM;
+        spi_set_drvdata(spi, ads);
+
+       spi->max_speed_hz = TRANSFER_HZ;
+       spi->mode = 1;
+       spi->bits_per_word = 8;
+       spi->rt = 1;
+       spi->word_delay.value = 0;
+       spi->cs_setup.value = 0;
+       spi->cs_hold.value = 0;
+       spi->cs_inactive.value = 0;
+       if (spi_setup(spi)) {
+               dev_err(&spi->dev, "error with spi setup!");
+               goto end_free_ads;
+       }
+
+       dev_info(&spi->dev, "ready to go!");
+
+       if (ads1278_begin_transfer(spi)) {
+               dev_err(&spi->dev, "error with initial transfer!");
+               goto end_free_ads;
+       }
+
+        return 0;
+
+end_free_ads:
+       kfree(ads);
+
+       return -1;
+}
+
+static void ads1278_remove(struct spi_device *spi)
+{
+        struct ads1278 *ads = spi_get_drvdata(spi);
+       int i;
+
+       dev_info(&spi->dev, "cleaning up...");
+
+       if (ads) {
+               ads->stop_requested = 1;
+
+               for (i = 0; i < 100; i++) {
+                       if (ads->running[0] <= 0 && ads->running[1] <= 0)
+                               break;
+
+                       mdelay(10);
+               }
+
+               //spi_unoptimize_message(&ads->msg);
+
+               dma_free_coherent(&spi->dev, TRANSFER_SIZE * 2, ads->tx, ads->tx_dma_handle);
+               dma_free_coherent(&spi->dev, TRANSFER_SIZE * 2, ads->rx, ads->rx_dma_handle);
+
+               kfree(ads);
+       }
+
+       dev_info(&spi->dev, "done.");
+}
+
+static struct of_device_id ads1278_id_table[] = {
+       { .compatible = "ti,ads1278-fs", },
+       {}
+};
+MODULE_DEVICE_TABLE(spi, ads1278_id_table);
+
+static struct spi_driver ads1278_spi_driver =
+{
+        .driver = {
+                .name           = "ti,ads1278-fs",
+               .of_match_table = ads1278_id_table,
+        },
+
+        .probe          = ads1278_probe,
+       .remove         = ads1278_remove,
+};
+module_spi_driver(ads1278_spi_driver);
+
+MODULE_DESCRIPTION("SPI/Frame-sync protocol driver for ADS1278");
+MODULE_AUTHOR("Clyne Sullivan <clyne@bitgloo.com");
+MODULE_LICENSE("GPL");
+