Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@primiano
Created July 13, 2015 22:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save primiano/f807583afb05e39d2d3a to your computer and use it in GitHub Desktop.
Save primiano/f807583afb05e39d2d3a to your computer and use it in GitHub Desktop.
// Working example of 25 MHz SPI on Intel Edison using DMA
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#define BUF_SIZE 4096
#define BITS_PER_WORD 32
static volatile int tasklet_enabled = 0;
static struct spi_device* spi_dev = NULL;
static void* buffer;
static struct spi_message msg;
static struct spi_transfer xfer = {
.bits_per_word = BITS_PER_WORD,
.len = BUF_SIZE,
};
static void spitx_tasklet(unsigned long);
DECLARE_TASKLET(spitx, spitx_tasklet, 0);
static void spi_async_callback(void* unused ) {
tasklet_schedule(&spitx);
}
static void spitx_tasklet(unsigned long ignored) {
int res;
xfer.tx_buf = buffer;
memset((char*)buffer, 0xA0, BUF_SIZE);
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
msg.complete = spi_async_callback;
res = spi_async(spi_dev, &msg);
if (res) {
printk(KERN_ERR "spi_async_locked FAILED %d\n", res);
}
}
static int test_spi_probe(struct spi_device *spi) {
spi->max_speed_hz = 25000000;
spi->bits_per_word = BITS_PER_WORD;
spi_setup(spi);
spi_dev = spi;
printk(KERN_INFO "SPI probed, starting pump thread.\n");
tasklet_enabled = 1;
tasklet_schedule(&spitx);
return 0;
}
static void terminate_tasklet() {
if (!tasklet_enabled)
return;
tasklet_enabled = 0;
tasklet_kill(&spitx);
}
static int test_spi_remove(struct spi_device *spi) {
spi_dev = NULL;
terminate_tasklet();
return 0;
}
static const struct spi_device_id idtable[] = {
{"ads7955", 0},
{}
};
static struct spi_driver test_spi_driver = {
.driver = {
.name = "ads7955",
.owner = THIS_MODULE,
},
.probe = test_spi_probe,
.remove = test_spi_remove,
.id_table = idtable,
};
static int __init test_spi_init(void)
{
int res;
buffer = kmalloc(BUF_SIZE, GFP_DMA | GFP_KERNEL);
memset((char*)buffer, 0xA0, BUF_SIZE);
res = spi_register_driver(&test_spi_driver);
if (res)
return res;
printk(KERN_INFO "test spi intialized\n");
return 0;
}
static void __exit test_spi_cleanup(void)
{
terminate_tasklet();
printk(KERN_INFO "Cleaning up module.\n");
kfree(buffer);
return spi_unregister_driver(&test_spi_driver);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Primiano Tucci");
MODULE_DESCRIPTION("Test");
module_init(test_spi_init);
module_exit(test_spi_cleanup);
@thar
Copy link

thar commented Oct 7, 2015

Hi,

I like the fix you've made for the SPI and the DMA.
I would like to test it.
I've changed the yocto kernel recipes to use your repo and added a new recipe for this driver, but I can't get it to work.
The project compiles, and I'm able to flash the edison with the new kernel.
I've made two tests. The first one gives me the following output when running the modprobe:
Error: Driver 'ads7955' is already registered, aborting...
modprobe: ERROR: could not insert 'intel_edison_spi_working': Device or resource busy

But I've not seen the ads7955 module loaded with the lsmod command.

So I've compiled again without the ads7955 module configured in the kernel and the result is that the module loads, the initialized message gets printed, but the probe fucntion message is not printed.

Can you help me? I'm a newbie with the kernel modifications, so I'm not sure if I'm missing something.

@drcrallen
Copy link

If I'm reading documentation correctly, the way to handle this is to have a magic spi_device_id which allows https://github.com/primiano/edison-kernel/blob/master/drivers/spi/spi.c#L71 to evaluate to true. But for the life of me I can't find the right set of key values to use.

I even tried other labels from https://github.com/primiano/edison-kernel/blob/master/arch/x86/platform/intel-mid/board.c#L110 but none of them seem to get the probe to fire.

I even tried getting the different masters by looping through spi_busnum_to_master and getting the master->dev.bus to look for devices... but that doesn't seem to work because the dev.bus pointer for every master is NULL.

At this point I'm pretty stuck on making a module that can do SPI stuff with this kernel patchset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment