#include #include #include #include #include #include #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