#include <plat/dmtimer.h>
#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
+static const char* omap2_spican_defaulttype = "mcp2515";
+
+static struct workqueue_struct *omap2_spican_wq;
+
/*******************************************************************/
/*
* We can't use the standard synchronous wrappers for file I/O; we
*/
static void omap2_spican_spi_complete(void *arg)
{
+ DEBUGMSG("SPICAN sync: complete\n");
complete(arg);
}
-static ssize_t omap2_spican_spi_sync(struct candevice_t *candev, struct spi_message *message)
+ssize_t omap2_spican_spi_sync(struct candevice_t *candev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
- struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ struct omap2_spican_platform_data *pdata;
int status;
+
+ if (candev == NULL)
+ return -ESHUTDOWN;
+ if (message == NULL)
+ panic("SPICAN: trying to send null pointered message");
+ pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
if (pdata == NULL)
return -ESHUTDOWN;
spin_lock_irq(&pdata->spi_lock);
if (pdata->spi == NULL)
status = -ESHUTDOWN;
- else
+ else{
+ DEBUGMSG("SPICAN sync: transfer\n");
status = spi_async(pdata->spi, message);
+ }
spin_unlock_irq(&pdata->spi_lock);
if (status == 0) {
- wait_for_completion(&done);
+ while (!wait_for_completion_interruptible_timeout(&done, msecs_to_jiffies(10))) {
+ DEBUGMSG("SPICAN sync: timeout 10 msecs\n");
+ }
+// do { ;} while (wait_for_completion_interruptible(&done));
status = message->status;
if (status == 0)
status = message->actual_length;
spi_message_add_tail(&t, &m);
status = omap2_spican_spi_sync(candev, &m);
-/* struct spi_transfer *t;
- struct spi_message m;
- int i,status;
+ return status;
+}
+static void hi_async_tasklet_handler(unsigned long data)
+{
+ struct omap2_spican_platform_data *pdata=(struct omap2_spican_platform_data *)data;
+ int n;
+
if (pdata == NULL)
- return -1;
- if(len > SPI_BUF_LEN) panic("long CAN spi transfer: %u",len);
+ return;
- spi_message_init(&m);
+
+ do {
+ n = find_first_bit(pdata->hi_async_done_map,OMAP2_SPICAN_PRE_ASYNC);
+ if (n == OMAP2_SPICAN_PRE_ASYNC)
+ break;
+
+ DEBUGMSG("SPICAN async %d: high priority tasklet\n",n);
+ if (pdata->async[n].complete)
+ pdata->async[n].complete(pdata->async[n].data,pdata->async[n].count);
+
+ clear_bit(n,pdata->hi_async_done_map);
+ clear_bit(n,pdata->async_req_map);
+ } while (n < OMAP2_SPICAN_PRE_ASYNC);
+}
+
+static void lo_async_workqueue_handler(struct work_struct *work)
+{
+ struct omap2_spican_platform_data *pdata = container_of(work,struct omap2_spican_platform_data,lo_async_workqueue);
+ struct omap2_spican_async_t *async;
+ int n;
+
+ if (pdata == NULL)
+ return;
+
+ do {
+ n = find_first_bit(pdata->lo_async_done_map,OMAP2_SPICAN_PRE_ASYNC);
+ if (n >= OMAP2_SPICAN_PRE_ASYNC)
+ break;
+
+ DEBUGMSG("SPICAN async %d: low priority workqueue\n",n);
+ async = pdata->async + n;
+
+ if (async->complete)
+ async->complete(async->data,async->count);
+
+ clear_bit(n,pdata->lo_async_done_map);
+ clear_bit(n,pdata->async_req_map);
+ } while (n < OMAP2_SPICAN_PRE_ASYNC);
+}
- t = can_checked_malloc(len * sizeof(struct spi_transfer));
- for (i=0;i<len;i++){
- (*(t+i)).tx_buf = tx+i;
- (*(t+i)).rx_buf = rx+i;
- (*(t+i)).len = 1;
- (*(t+i)).cs_change = 0;
- (*(t+i)).delay_usecs = 0;
- (*(t+i)).speed_hz = pdata->speed_hz;
- (*(t+i)).bits_per_word = 8;
- spi_message_add_tail(t+i, &m);
+static void omap2_spican_spi_async_complete(void *arg)
+{
+ struct omap2_spican_async_t *as = (struct omap2_spican_async_t *)arg;
+ struct omap2_spican_platform_data *pdata = container_of(as,struct omap2_spican_platform_data,async[as->bitmap_bit]);
+ int i;
+
+// DEBUGMSG("SPICAN async: complete\n");
+ for (i=1;i<as->count;i++)
+ can_filltimestamp(&as->data[i].timestamp);
+ // Serve the fast handlers ASAP
+ if ((as->fasthandler) && (as->complete)){
+// DEBUGMSG("SPICAN async: fasthandler\n");
+/* set_bit(as->bitmap_bit,pdata->hi_async_done_map);
+ tasklet_hi_schedule(&pdata->hi_async_tasklet);*/
+ if (as->complete){
+ as->complete(as->data,as->count);
+ }
+ clear_bit(as->bitmap_bit,pdata->lo_async_done_map);
+ clear_bit(as->bitmap_bit,pdata->async_req_map);
+// DEBUGMSG("SPICAN async: fasthandler done\n");
+ return;
}
- status = omap2_spican_spi_sync(candev, &m);
- can_checked_free(t);*/
- return status;
+
+ DEBUGMSG("SPICAN async %d: slowhandler register\n",as->bitmap_bit);
+ set_bit(as->bitmap_bit,pdata->lo_async_done_map);
+ queue_work(omap2_spican_wq,&pdata->lo_async_workqueue);
}
+ssize_t omap2_spican_spi_async_transfer(struct candevice_t *candev, void (*callback)(void *data), struct can_spi_async_t *data, uint8_t count, uint8_t fasthandler)
+{
+ struct omap2_spican_platform_data *pdata;
+ struct omap2_spican_async_t *async;
+ int status,i;
+ int n;
+
+ if (candev == NULL)
+ return -ESHUTDOWN;
+
+ pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ if (pdata == NULL)
+ return -ESHUTDOWN;
+
+ if (count > OMAP2_SPICAN_MAX_TRANSFERS)
+ return -ENOBUFS;
+
+ do {
+ n = find_first_zero_bit(pdata->async_req_map, OMAP2_SPICAN_PRE_ASYNC);
+ // There's no free transfer slot
+ if (n == OMAP2_SPICAN_PRE_ASYNC)
+ return -ENOBUFS;
+ } while (test_and_set_bit(n,pdata->async_req_map));
+
+ async = pdata->async+n;
+
+ memset(async,0,sizeof(struct omap2_spican_async_t));
+
+ for (i=0;i<count;i++){
+ if(data[i].len > SPI_MESSAGE_LENGTH)
+ panic("too long CAN spi transfer: %u",data[i].len);
+ }
+
+ memcpy(async->data,data,count*sizeof(struct can_spi_async_t));
+ async->count = count;
+ async->bitmap_bit = n;
+ async->complete = callback;
+ async->fasthandler = fasthandler;
+
+ spi_message_init(&async->m);
+
+ for (i=0;i<count;i++){
+ if ((async->data[i].tx_buf == NULL) && (async->data[i].rx_buf == NULL) && async->data[i].len != 0)
+ panic("Trying to send NULL pointered buffers");
+
+ async->t[i].tx_buf = async->data[i].tx_buf;
+ async->t[i].rx_buf = async->data[i].rx_buf;
+ async->t[i].len = async->data[i].len;
+ async->t[i].cs_change = 1;
+ async->t[i].speed_hz = pdata->speed_hz;
+ async->t[i].bits_per_word = 8;
+
+ spi_message_add_tail(&async->t[i], &async->m);
+ }
+
+ async->m.complete = omap2_spican_spi_async_complete;
+ async->m.context = async;
+ spin_lock_irq(&pdata->spi_lock);
+ if (pdata->spi == NULL)
+ status = -ESHUTDOWN;
+ else{
+ DEBUGMSG("SPICAN async %d: transfer\n",n);
+ status = spi_async(pdata->spi, &async->m);
+ }
+ spin_unlock_irq(&pdata->spi_lock);
+
+ if (status){
+ clear_bit(n,pdata->async_req_map);
+ }
-/// ---------------------- EVERYTHING'S DONE AFTER THIS POINT -----------------------------------------------
+ return status;
+}
/**
* omap2_spican_irq_handler: - GPT (general purpose timer) and waiting
int i;
if (!dev_id)
- return IRQ_HANDLED;
-
+ return IRQ_NONE;
+
pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
if (!pdata)
- return IRQ_HANDLED;
+ return IRQ_NONE;
+/* if (((pdata->trigger == OMAP2_SPICAN_TRIG_GPT) && (irq != pdata->timer_irq))
+ || ((pdata->trigger == OMAP2_SPICAN_TRIG_IRQ) && (irq != pdata->irq))
+ )
+ return IRQ_NONE;*/
+ if ((irq != pdata->timer_irq) && (irq != pdata->irq))
+ return IRQ_NONE;
+
+ DEBUGMSG("SPICAN: Got interrupt");
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ if (irq == pdata->timer_irq){
// reset the timer interrupt status
omap_dm_timer_write_status(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
omap_dm_timer_read_status(pdata->timer_ptr); // YES, you really need to do this 'wasteful' read
int omap2_spican_register_chip_data(struct canchip_t *ch,void *data){
struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)data;
- ch->clock = pdata->mcp2515_clk;
+ ch->clock = pdata->clk;
ch->baudrate = pdata->baudrate;
int ret = 0;
/*******************************************************************/
int omap2_spican_spi_acquire_bus(struct candevice_t *candev, short channel, int block){
+ /* Subsystem is always ready to accept messages */
return 1;
}
// SPI specific functions
hwspecops->spi_transfer = omap2_spican_spi_transfer;
+ hwspecops->spi_async_transfer = omap2_spican_spi_async_transfer;
hwspecops->spi_acquire_bus = omap2_spican_spi_acquire_bus;
hwspecops->spi_release_bus = omap2_spican_spi_release_bus;
static int omap2_spican_probe(struct spi_device *spi)
{
- struct omap2_spican_platform_data *pdata = spi->dev.platform_data;
+ struct lincan_spican_platform_data *spidata = spi->dev.platform_data;
+ struct omap2_spican_platform_data *pdata;
struct candevice_t *dev;
int allocated = 0;
-
- static struct omap2_spican_platform_data mypdata;
- static const char* omap2_spican_defaulttype = "mcp2515";
DEBUGMSG("Starting probe for omap2_spican...\n");
// Structure checks and initialization
- if (pdata == NULL){
-/* pdata = (struct omap2_spican_platform_data *)can_checked_malloc(sizeof(struct omap2_spican_platform_data));
- if (pdata)
- memset(pdata,0,sizeof(struct omap2_spican_platform_data));
- else
- return -ENOMEM;
- allocated = 1;*/
- pdata = &mypdata; // No need to free up
-
+ pdata = (struct omap2_spican_platform_data *)can_checked_malloc(sizeof(struct omap2_spican_platform_data));
+ if (pdata)
+ memset(pdata,0,sizeof(struct omap2_spican_platform_data));
+ else
+ return -ENOMEM;
+
+ if (spidata)
+ memcpy(pdata,spidata,sizeof(struct lincan_spican_platform_data));
+ else{
if (!pdata->cs_change) pdata->cs_change = OMAP2_SPICAN_CS_CHANGE;
if (!pdata->delay_usecs) pdata->delay_usecs = OMAP2_SPICAN_DELAY_USECS;
}
- if (!pdata->mcp2515_clk) pdata->mcp2515_clk = OMAP2_SPICAN_MCP_CLK;
+ if (!pdata->clk) pdata->clk = OMAP2_SPICAN_MCP_CLK;
if (!pdata->baudrate) pdata->baudrate = OMAP2_SPICAN_BAUDRATE;
if (!pdata->speed_hz) pdata->speed_hz = OMAP2_SPICAN_SPEED_HZ;
if (!pdata->chiptype) pdata->chiptype = omap2_spican_defaulttype;
-
spin_lock_init(&pdata->spi_lock);
pdata->spi = spi;
pdata->trigger = 0;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&pdata->lo_async_workqueue,
+ lo_async_workqueue_handler,
+ &pdata->lo_async_workqueue);
+ #else
+ INIT_WORK(&pdata->lo_async_workqueue,
+ lo_async_workqueue_handler);
+ #endif
+ tasklet_init(&pdata->hi_async_tasklet,
+ hi_async_tasklet_handler,
+ (unsigned long)pdata);
+
if (spi->irq) {
DEBUGMSG("Interrupt line number %d provided.\n",spi->irq);
pdata->trigger = OMAP2_SPICAN_TRIG_IRQ;
if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ) {
int status = 0;
DEBUGMSG("Requesting interrupt line %d.\n",spi->irq);
- status = set_irq_type(pdata->irq,IRQ_TYPE_EDGE_BOTH);
+ status = set_irq_type(pdata->irq,IRQ_TYPE_EDGE_FALLING);
if(status){
- CANMSG("Setting low level trigger on irq %d failed.\n", spi->irq);
+ CANMSG("Setting falling edge trigger on irq %d failed.\n", spi->irq);
goto release;
}
status = request_irq(pdata->irq, omap2_spican_irq_handler, IRQF_DISABLED , "omap2_spican", dev);
}
}
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == 0){
+// if (pdata->trigger == 0){
+ {
struct clk *gt_fclk;
uint32_t gt_rate;
int status = 0;
gt_rate = clk_get_rate(gt_fclk)/(1<<(prescaler+1));
// set preload, and autoreload
- // we set it to the clock rate in order to get 1 overflow every 10 usecs
- omap_dm_timer_set_load(pdata->timer_ptr, 1, 0xFFFFFFFF - (uint32_t)(gt_rate / 100));
+ // we set it to the clock rate in order to get 1 overflow every 1 msec
+ omap_dm_timer_set_load(pdata->timer_ptr, 1, 0xFFFFFFFF - (uint32_t)(gt_rate / 1000));
// Request for the irq
pdata->timer_irq = omap_dm_timer_get_irq(pdata->timer_ptr);
// setup timer to trigger our IRQ on the overflow event
omap_dm_timer_set_int_enable(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
- pdata->trigger = OMAP2_SPICAN_TRIG_GPT;
+// pdata->trigger = OMAP2_SPICAN_TRIG_GPT;
}
#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
spi_set_drvdata(spi, dev);
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
- // Everything's ready, let's roll!
- DEBUGMSG("Starting OMAP GPT\n");
- omap_dm_timer_start(pdata->timer_ptr);
- }
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ {
+ // Everything's ready, let's roll!
+ DEBUGMSG("Starting OMAP GPT\n");
+ omap_dm_timer_start(pdata->timer_ptr);
+ }
#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
DEBUGMSG("Device omap2_spican successfully configured. Have fun!\n");
return 0;
release:
- if (allocated)
- can_checked_free(pdata);
+ can_checked_free(pdata);
return -ENODEV;
}
free_irq(pdata->irq, candev);
}
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ {
DEBUGMSG("Stopping OMAP GPT\n");
omap_dm_timer_stop(pdata->timer_ptr);
// release the IRQ handler
DEBUGMSG("Removing omap2_spican from device structure...");
// make sure ops on existing fds can abort cleanly
+ deregister_hotplug_dev(candev);
cleanup_hotplug_dev(candev);
spin_lock_irq(&pdata->spi_lock);
// candev->sysdevptr.anydev = NULL;
spin_unlock_irq(&pdata->spi_lock);
+ DEBUGMSG("Kill tasklets.\n");
+ tasklet_kill(&pdata->hi_async_tasklet);
+
+ can_checked_free(pdata);
DEBUGMSG(" done.\n");
return 0;
};
int omap2_spican_init(void){
+ omap2_spican_wq = create_singlethread_workqueue("omap2_spican");
+ if (omap2_spican_wq == NULL)
+ return -1;
return spi_register_driver(&omap2_spican_driver);
}
void omap2_spican_exit(void){
spi_unregister_driver(&omap2_spican_driver);
+ destroy_workqueue(omap2_spican_wq);
}
#ifdef MODULE_ALIAS