Initial source upload
parent
d214f74deb
commit
7e06316229
@ -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
|
@ -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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in New Issue