|
|
@ -2,6 +2,7 @@
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/spi/spi.h>
|
|
|
|
#include <linux/spi/spi.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define FRAME_SIZE (24)
|
|
|
|
#define FRAME_SIZE (24)
|
|
|
@ -11,14 +12,15 @@
|
|
|
|
// 10'000'000 Hz = 52 kSPS
|
|
|
|
// 10'000'000 Hz = 52 kSPS
|
|
|
|
#define TRANSFER_HZ (27000000)
|
|
|
|
#define TRANSFER_HZ (27000000)
|
|
|
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// TODO: minimize 45us delay between xfers
|
|
|
|
// - Require max CPU clock speed so 130 kSPS can be achieved
|
|
|
|
|
|
|
|
// - Minimize ~30us delay between transfers
|
|
|
|
// 24-11-23: We're hitting 130 kSPS initially, but are eventually slowed down
|
|
|
|
// - Figure out how to get sample data out
|
|
|
|
// to 87 kSPS. Need to investigate high-priority workers, forcing a high SPI
|
|
|
|
// - Create configuration interface
|
|
|
|
// clock speed, and/or minimizing overall latency.
|
|
|
|
|
|
|
|
|
|
|
|
struct ads1278
|
|
|
|
struct ads1278
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
struct workqueue_struct *wq;
|
|
|
|
unsigned char *rx;
|
|
|
|
unsigned char *rx;
|
|
|
|
unsigned char *tx;
|
|
|
|
unsigned char *tx;
|
|
|
|
dma_addr_t rx_dma_handle;
|
|
|
|
dma_addr_t rx_dma_handle;
|
|
|
@ -29,26 +31,43 @@ struct ads1278
|
|
|
|
int running[2];
|
|
|
|
int running[2];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void ads1278_transfer_callback1(void *context)
|
|
|
|
static struct spi_device *spip = NULL;
|
|
|
|
|
|
|
|
static void ads1278_work_handler(struct work_struct *w)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct spi_device *spi = (struct spi_device *)context;
|
|
|
|
struct ads1278 *ads;
|
|
|
|
struct ads1278 *ads = spi_get_drvdata(spi);
|
|
|
|
|
|
|
|
|
|
|
|
if (!spip)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ads = spi_get_drvdata(spip);
|
|
|
|
|
|
|
|
|
|
|
|
if (!ads->stop_requested)
|
|
|
|
while (!ads->stop_requested) {
|
|
|
|
spi_async(spi, &ads->msg[0]);
|
|
|
|
spi_sync_locked(spip, &ads->msg[0]);
|
|
|
|
else
|
|
|
|
}
|
|
|
|
ads->running[0] = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static DECLARE_WORK(ads1278_work, ads1278_work_handler);
|
|
|
|
|
|
|
|
|
|
|
|
static void ads1278_transfer_callback2(void *context)
|
|
|
|
static void ads1278_transfer_callback1(void *context)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct spi_device *spi = (struct spi_device *)context;
|
|
|
|
// struct spi_device *spi = (struct spi_device *)context;
|
|
|
|
struct ads1278 *ads = spi_get_drvdata(spi);
|
|
|
|
// struct ads1278 *ads = spi_get_drvdata(spi);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (!ads->stop_requested)
|
|
|
|
|
|
|
|
// queue_work(ads->wq, &ads1278_work);
|
|
|
|
|
|
|
|
// //spi_async(spi, &ads->msg[0]);
|
|
|
|
|
|
|
|
// else
|
|
|
|
|
|
|
|
// ads->running[0] = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!ads->stop_requested)
|
|
|
|
static void ads1278_transfer_callback2(void *context)
|
|
|
|
spi_async(spi, &ads->msg[1]);
|
|
|
|
{
|
|
|
|
else
|
|
|
|
// struct spi_device *spi = (struct spi_device *)context;
|
|
|
|
ads->running[1] = 0;
|
|
|
|
// struct ads1278 *ads = spi_get_drvdata(spi);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (!ads->stop_requested)
|
|
|
|
|
|
|
|
// spi_async(spi, &ads->msg[1]);
|
|
|
|
|
|
|
|
// else
|
|
|
|
|
|
|
|
// ads->running[1] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -119,8 +138,16 @@ static int ads1278_begin_transfer(struct spi_device *spi)
|
|
|
|
ads->running[0] = 1;
|
|
|
|
ads->running[0] = 1;
|
|
|
|
ads->running[1] = 1;
|
|
|
|
ads->running[1] = 1;
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO ret = */ spi_async(spi, &ads->msg[0]);
|
|
|
|
// /* TODO ret = */ spi_async(spi, &ads->msg[0]);
|
|
|
|
return spi_async(spi, &ads->msg[1]);
|
|
|
|
// return spi_async(spi, &ads->msg[1]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ads->wq = create_singlethread_workqueue("ads1278");
|
|
|
|
|
|
|
|
if (ads->wq) {
|
|
|
|
|
|
|
|
spip = spi;
|
|
|
|
|
|
|
|
queue_work(ads->wq, &ads1278_work);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ads1278_probe(struct spi_device *spi)
|
|
|
|
static int ads1278_probe(struct spi_device *spi)
|
|
|
@ -149,6 +176,7 @@ static int ads1278_probe(struct spi_device *spi)
|
|
|
|
|
|
|
|
|
|
|
|
dev_info(&spi->dev, "ready to go!");
|
|
|
|
dev_info(&spi->dev, "ready to go!");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
spi_bus_lock(spi->controller);
|
|
|
|
if (ads1278_begin_transfer(spi)) {
|
|
|
|
if (ads1278_begin_transfer(spi)) {
|
|
|
|
dev_err(&spi->dev, "error with initial transfer!");
|
|
|
|
dev_err(&spi->dev, "error with initial transfer!");
|
|
|
|
goto end_free_ads;
|
|
|
|
goto end_free_ads;
|
|
|
@ -184,9 +212,14 @@ static void ads1278_remove(struct spi_device *spi)
|
|
|
|
dma_free_coherent(&spi->dev, TRANSFER_SIZE * 2, ads->tx, ads->tx_dma_handle);
|
|
|
|
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);
|
|
|
|
dma_free_coherent(&spi->dev, TRANSFER_SIZE * 2, ads->rx, ads->rx_dma_handle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flush_scheduled_work();
|
|
|
|
|
|
|
|
if (ads->wq)
|
|
|
|
|
|
|
|
destroy_workqueue(ads->wq);
|
|
|
|
|
|
|
|
|
|
|
|
kfree(ads);
|
|
|
|
kfree(ads);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
spi_bus_unlock(spi->controller);
|
|
|
|
dev_info(&spi->dev, "done.");
|
|
|
|
dev_info(&spi->dev, "done.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|