blob: 796515b3f1be466e3d3f7eabc1a899a4226d1b72 [file] [log] [blame]
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/device.h>
#include <linux/fs.h>
struct spistest_core;
struct wrapper_spi_info {
struct spi_message* mesg;
struct spi_device* spi;
int mesg_position;
int nb_send;
int target_send;
unsigned char offset;
int fail_at;
struct spistest_core* core;
};
struct spistest_core {
int bus_num;
struct spi_message *mesgs;
struct spi_transfer *xfers;
struct wrapper_spi_info *infos;
int nb_send;
int nb_fail;
int total_messages_received;
struct completion done;
};
static unsigned int nb_messages = 1;
module_param(nb_messages, uint, 0644);
static unsigned int message_size = 256;
module_param(message_size, uint, 0644);
static unsigned int total_len = 256;
module_param(total_len, uint, 0644);
static unsigned int verbose = 1;
module_param(verbose, uint, 0644);
static unsigned int offset = 0x2A;
module_param(offset, uint, 0644);
static struct spistest_core cores[2];
#define spistest_print(level, core, format, arg...) \
printk(level "[SPISTEST LOOPBACK][SLV %d] " format, core->bus_num, ##arg)
#define spistest_info(core, format, arg...) \
spistest_print(KERN_INFO, core, format, ##arg)
#define spistest_err(core, format, arg...) \
spistest_print(KERN_ERR, core, format, ##arg)
static struct spistest_core* get_spistest_core(int bus_num)
{
/* that's hackish but...
* In some case, there are two SPI slave receiving the same data (because
* they are wired to the same pads, yet another hack), so we want this
* driver to support up to two cores. For the moment, slaves are on bus
* 1 and 3.
*/
switch(bus_num) {
case 1:
cores[0].bus_num = 1;
return &cores[0];
case 3:
cores[1].bus_num = 3;
return &cores[1];
default:
return NULL;
}
}
static int spistest_allocate(struct spistest_core *core)
{
int err = -ENOMEM;
int i;
core->infos = kzalloc(nb_messages * sizeof(struct spi_message), GFP_KERNEL);
if (! core->infos) {
goto exit;
}
core->mesgs = kzalloc(nb_messages * sizeof(struct spi_message), GFP_KERNEL);
if (! core->mesgs) {
goto free_infos;
}
core->xfers = kzalloc(nb_messages * sizeof(struct spi_transfer), GFP_KERNEL);
if (! core->xfers) {
goto free_mesgs;
}
for(i = 0; i < nb_messages; i++) {
spi_message_init(&core->mesgs[i]);
memset(&core->xfers[i], 0, sizeof(struct spi_transfer));
core->xfers[i].len = message_size;
core->xfers[i].rx_buf = kzalloc(message_size, GFP_KERNEL);
if (! core->xfers[i].rx_buf ) {
goto free_buffers;
}
spi_message_add_tail(&core->xfers[i], &core->mesgs[i]);
}
return 0;
free_buffers:
for(i = 0; i < nb_messages; i++) {
if (core->xfers[i].rx_buf)
kfree(core->xfers[i].rx_buf);
}
free_mesgs:
kfree(core->mesgs);
free_infos:
kfree(core->infos);
exit:
return err;
}
static int spistest_check(struct spistest_core *core,
u8* buf, int len, unsigned char offset)
{
int i, j, err = -1;
for(i = 0; i < len; i++) {
if (buf[i] != offset) {
spistest_err(core, "-FAIL- Unexpected value at offset %d."
"Read 0x%.2X, expected 0x%.2X\n",
i, buf[i], offset);
for (j = 0; j < 10; j++) {
if (i + j < len)
spistest_err(core, "-FAIL- buf[%d] = 0x%X\n",
i + j, buf[i + j]);
}
return i;
}
offset++;
}
return err;
}
static void spistest_receive(void* context)
{
int err;
struct wrapper_spi_info* info = context;
struct spistest_core* core = info->core;
struct spi_message* mesg = info->mesg;
struct spi_transfer* xfer = list_entry(
mesg->transfers.next,
struct spi_transfer,
transfer_list);
if (mesg->status) {
spistest_err(core, "-FAIL- Msg %d (round %d) received with error code %d\n",
info->mesg_position, info->nb_send, mesg->status);
core->nb_fail++;
spistest_check(core, xfer->rx_buf, message_size, info->offset);
goto completion;
}
/* check buffer */
err = spistest_check(core, xfer->rx_buf, message_size, info->offset);
if (err != -1) {
spistest_err(core, "-FAIL- Msg %d (round %d) is invalid\n",
info->mesg_position, info->nb_send);
info->fail_at = info->nb_send;
core->nb_fail++;
goto completion;
}
if (verbose)
spistest_info(core, "-PASS- Msg %d (round %d) validated\n",
info->mesg_position, info->nb_send);
if (core->nb_fail)
/* If this message was successful but a previous one failed,
* do not resend this message to end the test asap */
core->nb_fail++;
else {
info->nb_send++;
if (info->nb_send < info->target_send) {
spi_async(info->spi, mesg);
}
}
completion:
core->total_messages_received++;
if (core->total_messages_received == core->nb_send ||
core->nb_fail == nb_messages)
complete(&core->done);
}
static int __devinit spistest_probe(struct spi_device *spi)
{
int err = 0, i;
int send_per_message;
int remaining_send;
struct spistest_core *core;
core = get_spistest_core(spi->master->bus_num);
if ( ! core ) {
printk(KERN_ERR "SPI bus number must be only 1 or 3\n");
return -EINVAL;
}
core->total_messages_received = 0;
core->nb_send = total_len / message_size;
if (total_len % message_size) {
spistest_err(core, "total_len must be a multiple of message_size\n");
return -EINVAL;
}
send_per_message = core->nb_send / nb_messages;
remaining_send = core->nb_send % nb_messages;
init_completion(&core->done);
err = spistest_allocate(core);
if (err) {
goto exit;
}
/*
* Send each message once, the rest is done in completion function
*/
for(i = 0; i < nb_messages; i++) {
core->infos[i].mesg = &core->mesgs[i];
core->infos[i].spi = spi;
core->infos[i].nb_send = 0;
core->infos[i].target_send = send_per_message;
if (remaining_send) {
core->infos[i].target_send++;
remaining_send--;
}
core->infos[i].offset = (offset + (i * message_size)) % 256;
core->infos[i].fail_at = 0;
core->infos[i].mesg_position = i;
core->infos[i].core = core;
core->mesgs[i].complete = spistest_receive;
core->mesgs[i].context = &core->infos[i];
err = spi_async(spi, &core->mesgs[i]);
if (err) {
spistest_err(core, "-FAIL- Can't send a message (%d)\n",
err);
}
else {
spistest_info(core,
"Sending msg %d [%p], size %d (total %d)\n",
i, core->xfers[i].rx_buf, message_size, total_len);
}
}
exit:
return err;
}
static int __devexit spistest_remove(struct spi_device *spi)
{
int i;
struct spistest_core *core;
core = get_spistest_core(spi->master->bus_num);
wait_for_completion(&core->done);
if (! core->nb_fail) {
bool success = true;
for(i = 0; i < nb_messages; i++) {
if (core->infos[i].fail_at != 0)
success = false;
}
spistest_info(core, "%s message pool %d msg. %d msgs sent\n",
success ? "-PASS-" : "-FAIL-",
nb_messages, core->nb_send);
}
else {
spistest_err(core, "-FAIL- Could not send all messages\n");
for(i = 0; i < nb_messages; i++) {
spistest_err(core, "\tMsg %d sent %d time(s) successfully\n",
i, core->infos[i].nb_send);
}
}
for(i = 0; i < nb_messages; i++) {
kfree(core->xfers[i].rx_buf);
}
kfree(core->mesgs);
kfree(core->xfers);
kfree(core->infos);
return 0;
}
static struct spi_driver spistest_driver = {
.driver = {
.name = "spistest",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = spistest_probe,
.remove = __devexit_p(spistest_remove),
};
module_spi_driver(spistest_driver);
MODULE_DESCRIPTION( "spistest loopback module" );
MODULE_LICENSE( "GPL" );