You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ads1278-fs/ads1278_core.c

248 lines
5.6 KiB
C

#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.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
{
struct workqueue_struct *wq;
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 struct spi_device *spip = NULL;
static void ads1278_work_handler(struct work_struct *w)
{
struct ads1278 *ads;
if (!spip)
return;
ads = spi_get_drvdata(spip);
while (!ads->stop_requested) {
spi_sync_locked(spip, &ads->msg[0]);
}
}
static DECLARE_WORK(ads1278_work, ads1278_work_handler);
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)
// queue_work(ads->wq, &ads1278_work);
// //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]);
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)
{
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!");
spi_bus_lock(spi->controller);
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);
flush_scheduled_work();
if (ads->wq)
destroy_workqueue(ads->wq);
kfree(ads);
}
spi_bus_unlock(spi->controller);
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");