]> rtime.felk.cvut.cz Git - lincan.git/blob - lincan/src/omap2_spican.c
Spican1 support added
[lincan.git] / lincan / src / omap2_spican.c
1 /* omap2_spican.c
2  * Linux CAN-bus device driver.
3  * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
4  * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
5  * email:pisa@cmp.felk.cvut.cz
6  * This software is released under the GPL-License.
7  * Version lincan-0.3  17 Jun 2004
8  */ 
9
10 #include <linux/err.h>
11 #include <linux/errno.h>
12 #include <linux/mutex.h>
13 #include <linux/irq.h>
14 #include <linux/interrupt.h>
15 #include <linux/types.h>
16 #include <linux/proc_fs.h>
17 #include <linux/clk.h>          
18 #include <linux/spi/spi.h>
19
20 #include "../include/can.h"
21 #include "../include/can_sysdep.h"
22 #include "../include/main.h"
23 #include "../include/setup.h"
24 #include "../include/omap2_spican.h"
25 #include "../include/mcp2515.h"
26
27 #ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
28         #include <asm/io.h>                     
29         #include <asm/uaccess.h>
30         #include <plat/dmtimer.h>       
31 #endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
32
33 /*******************************************************************/
34 /*
35  * We can't use the standard synchronous wrappers for file I/O; we
36  * need to protect against async removal of the underlying spi_device.
37  */
38 static void omap2_spican_spi_complete(void *arg)
39 {
40         complete(arg);
41 }
42
43 static ssize_t omap2_spican_spi_sync(struct candevice_t *candev, struct spi_message *message)
44 {
45         DECLARE_COMPLETION_ONSTACK(done);
46         struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
47         int status;
48
49         if (pdata == NULL)
50                 return -ESHUTDOWN;
51
52         message->complete = omap2_spican_spi_complete;
53         message->context = &done;
54         spin_lock_irq(&pdata->spi_lock);
55         if (pdata->spi == NULL)
56                 status = -ESHUTDOWN;
57         else
58                 status = spi_async(pdata->spi, message);
59         spin_unlock_irq(&pdata->spi_lock);
60
61         if (status == 0) {
62                 wait_for_completion(&done);
63                 status = message->status;
64                 if (status == 0)
65                         status = message->actual_length;
66         }
67         return status;
68 }
69
70 static ssize_t omap2_spican_spi_transfer(struct candevice_t *candev,void *tx, void *rx, uint16_t len)
71 {
72         struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
73         struct spi_transfer     t = {
74                 .tx_buf         = tx,
75                 .rx_buf         = rx,
76                 .len            = len,
77                 .cs_change = 1,
78                 .speed_hz = pdata->speed_hz,
79                 .bits_per_word = 8,
80         };
81         struct spi_message      m;
82         int i,status;
83
84         if (pdata == NULL)
85                 return -1;
86         if(len > SPI_BUF_LEN) panic("long CAN spi transfer: %u",len);
87
88         spi_message_init(&m);
89
90         spi_message_add_tail(&t, &m);
91         status = omap2_spican_spi_sync(candev, &m);
92 /*      struct spi_transfer     *t;
93         struct spi_message      m;
94         int i,status;
95
96         if (pdata == NULL)
97                 return -1;
98         if(len > SPI_BUF_LEN) panic("long CAN spi transfer: %u",len);
99
100         spi_message_init(&m);
101
102         t = can_checked_malloc(len * sizeof(struct spi_transfer));
103         for (i=0;i<len;i++){
104                 (*(t+i)).tx_buf         = tx+i;
105                 (*(t+i)).rx_buf         = rx+i;
106                 (*(t+i)).len            = 1;
107                 (*(t+i)).cs_change = 0;
108                 (*(t+i)).delay_usecs = 0;
109                 (*(t+i)).speed_hz = pdata->speed_hz;
110                 (*(t+i)).bits_per_word = 8;
111                 spi_message_add_tail(t+i, &m);
112         }
113         status = omap2_spican_spi_sync(candev, &m);
114         can_checked_free(t);*/
115         return status;
116 }
117
118
119 /// ---------------------- EVERYTHING'S DONE AFTER THIS POINT -----------------------------------------------
120
121 /**
122  * omap2_spican_irq_handler: - GPT (general purpose timer) and waiting
123  * thread interrupt handler
124  * @irq: the interrupt line number
125  * @dev_id: pointer to can device data
126  * 
127  * Return Value: Interrupt handled status is returned
128  * File: src/omap2_spican.c
129  */
130 static irqreturn_t omap2_spican_irq_handler(int irq, void *dev_id)
131 {
132         struct candevice_t *candev = dev_id;
133         struct omap2_spican_platform_data *pdata;
134         unsigned long flags;
135         int i;
136         
137         if (!dev_id)
138                 return IRQ_HANDLED;
139
140         pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
141         if (!pdata)
142                 return IRQ_HANDLED;
143         
144         #ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
145         if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
146                 // reset the timer interrupt status
147                 omap_dm_timer_write_status(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
148                 omap_dm_timer_read_status(pdata->timer_ptr); // YES, you really need to do this 'wasteful' read
149         }
150         #endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
151  
152         for (i=0;i<candev->nr_all_chips;i++){
153                 if (candev->chip[i]->chipspecops->irq_handler)
154                         candev->chip[i]->chipspecops->irq_handler(irq, candev->chip[i]);
155         }
156
157         return IRQ_HANDLED;
158         
159 }
160  
161 /**
162  * omap2_spican_request_io: - reserve io or memory range for can board
163  * @candev: pointer to candevice/board which asks for io. Field @io_addr
164  *      of @candev is used in most cases to define start of the range
165  *
166  * The function omap2_spican_request_io() is used to reserve the io-memory. If your
167  * hardware uses a dedicated memory range as hardware control registers you
168  * will have to add the code to reserve this memory as well. 
169  * %IO_RANGE is the io-memory range that gets reserved, please adjust according
170  * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
171  * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
172  * Return Value: The function returns zero on success or %-ENODEV on failure
173  * File: src/omap2_spican.c
174  */
175 int omap2_spican_request_io(struct candevice_t *candev)
176 {
177         return 0;
178 }
179
180 /**
181  * omap2_spican_release_io - free reserved io memory range
182  * @candev: pointer to candevice/board which releases io
183  *
184  * The function omap2_spican_release_io() is used to free reserved io-memory.
185  * In case you have reserved more io memory, don't forget to free it here.
186  * IO_RANGE is the io-memory range that gets released, please adjust according
187  * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
188  * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
189  * Return Value: The function always returns zero
190  * File: src/omap2_spican.c
191  */
192 int omap2_spican_release_io(struct candevice_t *candev)
193 {
194         return 0;
195 }
196
197 /**
198  * omap2_spican_reset - hardware reset routine
199  * @candev: Pointer to candevice/board structure
200  *
201  * The function omap2_spican_reset() is used to give a hardware reset. This is 
202  * rather hardware specific so I haven't included example code. Don't forget to 
203  * check the reset status of the chip before returning.
204  * Return Value: The function returns zero on success or %-ENODEV on failure
205  * File: src/omap2_spican.c
206  */
207 int omap2_spican_reset(struct candevice_t *candev)
208 {
209         int i;
210         DEBUGMSG("Resetting all underlying chips\n");
211         for(i=0;i<OMAP2_SPICAN_NCHIPS;i++)
212                 (candev->chip[i]->chipspecops->reset_chip)(candev->chip[i]);
213
214         return 0;
215 }
216
217 /**
218  * omap2_spican_init_hw_data - Initialize hardware cards
219  * @candev: Pointer to candevice/board structure
220  *
221  * The function omap2_spican_init_hw_data() is used to initialize the hardware
222  * structure containing information about the installed CAN-board.
223  * %RESET_ADDR represents the io-address of the hardware reset register.
224  * %NR_82527 represents the number of Intel 82527 chips on the board.
225  * %NR_SJA1000 represents the number of Philips sja1000 chips on the board.
226  * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
227  * the hardware uses programmable interrupts.
228  * Return Value: The function always returns zero
229  * File: src/omap2_spican.c
230  */
231 int omap2_spican_init_hw_data(struct candevice_t *candev) 
232 {
233         candev->dev_base_addr = 0;
234         candev->nr_82527_chips = 0;
235         candev->nr_sja1000_chips = 0;
236         candev->nr_mcp2515_chips = OMAP2_SPICAN_NCHIPS;
237         candev->nr_all_chips = candev->nr_82527_chips + 
238                 candev->nr_sja1000_chips + 
239                 candev->nr_mcp2515_chips;
240         candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
241
242         return 0;
243 }
244
245 /**
246  * omap2_spican_init_chip_data - Initialize chips
247  * @candev: Pointer to candevice/board structure
248  * @chipnr: Number of the CAN chip on the hardware card
249  *
250  * The function omap2_spican_init_chip_data() is used to initialize the hardware
251  * structure containing information about the CAN chips.
252  * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
253  * "sja1000".
254  * The @chip_base_addr entry represents the start of the 'official' memory map
255  * of the installed chip. It's likely that this is the same as the @io_addr
256  * argument supplied at module loading time.
257  * The @clock entry holds the chip clock value in Hz.
258  * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
259  * register. Options defined in the %sja1000.h file:
260  * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN
261  * The entry @sja_ocr_reg holds hardware specific options for the Output Control
262  * register. Options defined in the %sja1000.h file:
263  * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK,
264  * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ.
265  * The entry @int_clk_reg holds hardware specific options for the Clock Out
266  * register. Options defined in the %i82527.h file:
267  * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
268  * The entry @int_bus_reg holds hardware specific options for the Bus 
269  * Configuration register. Options defined in the %i82527.h file:
270  * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
271  * The entry @int_cpu_reg holds hardware specific options for the cpu interface
272  * register. Options defined in the %i82527.h file:
273  * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST.
274  * Return Value: The function always returns zero
275  * File: src/omap2_spican.c
276  */
277 int omap2_spican_init_chip_data(struct candevice_t *candev, int chipnr)
278 {
279         if(chipnr >= OMAP2_SPICAN_NCHIPS || chipnr < 0) {
280                 CANMSG("Error: chipnr=%d\n",chipnr);
281                 return -ENODEV;
282         }
283         
284         mcp2515_fill_chipspecops(candev->chip[chipnr]);
285 /*      candev->chip[chipnr]->chip_base_addr = 0;
286         candev->chip[chipnr]->spi_channel = 2;*/
287         candev->chip[chipnr]->chip_data = can_checked_malloc(sizeof(MCP2515_PRIV));
288         if(candev->chip[chipnr]->chip_data == NULL) return -ENOMEM;
289         memset(candev->chip[chipnr]->chip_data,0,sizeof(MCP2515_PRIV));
290
291         return 0;
292 }
293
294 int omap2_spican_register_chip_data(struct canchip_t *ch,void *data){
295         struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)data;
296         ch->clock = pdata->mcp2515_clk;
297         ch->baudrate = pdata->baudrate;
298         int ret = 0;
299         
300         ch->flags |= CHIP_IRQ_CUSTOM;
301 //      if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ){
302 //              ch->chip_irq = pdata->irq;
303 //      }
304
305         ret = ch->chipspecops->chip_config(ch);
306         if (ret){
307                 CANMSG("Error configuring chip.\n");
308         }
309         else
310                 ch->flags |= CHIP_CONFIGURED; 
311         return ret;
312 }
313
314 /**
315  * omap2_spican_init_obj_data - Initialize message buffers
316  * @chip: Pointer to chip specific structure
317  * @objnr: Number of the message buffer
318  *
319  * The function omap2_spican_init_obj_data() is used to initialize the hardware
320  * structure containing information about the different message objects on the
321  * CAN chip. In case of the sja1000 there's only one message object but on the
322  * i82527 chip there are 15.
323  * The code below is for a i82527 chip and initializes the object base addresses
324  * The entry @obj_base_addr represents the first memory address of the message 
325  * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
326  * base address.
327  * Unless the hardware uses a segmented memory map, flags can be set zero.
328  * Return Value: The function always returns zero
329  * File: src/omap2_spican.c
330  */
331 int omap2_spican_init_obj_data(struct canchip_t *chip, int objnr)
332 {
333         chip->msgobj[objnr]->obj_base_addr=0;
334         chip->msgobj[objnr]->obj_flags=0;
335         
336         return 0;
337 }
338
339 /**
340  * omap2_spican_write_register - Low level write register routine
341  * @data: data to be written
342  * @address: memory address to write to
343  *
344  * The function omap2_spican_write_register() is used to write to hardware registers
345  * on the CAN chip. You should only have to edit this function if your hardware
346  * uses some specific write process.
347  * Return Value: The function does not return a value
348  * File: src/omap2_spican.c
349  */
350 void omap2_spican_write_register(unsigned data, can_ioptr_t address)
351 {
352         panic("omap2_spican_write_register");
353 }
354
355 /**
356  * omap2_spican_read_register - Low level read register routine
357  * @address: memory address to read from
358  *
359  * The function omap2_spican_read_register() is used to read from hardware registers
360  * on the CAN chip. You should only have to edit this function if your hardware
361  * uses some specific read process.
362  * Return Value: The function returns the value stored in @address
363  * File: src/omap2_spican.c
364  */
365 unsigned omap2_spican_read_register(can_ioptr_t address)
366 {
367         panic("omap2_spican_read_register");
368         return 0;
369 }
370
371 /**
372  * omap2_spican_program_irq - program interrupts
373  * @candev: Pointer to candevice/board structure
374  *
375  * The function omap2_spican_program_irq() is used for hardware that uses 
376  * programmable interrupts. If your hardware doesn't use programmable interrupts
377  * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and 
378  * leave this function unedited. Again this function is hardware specific so 
379  * there's no example code.
380  * Return value: The function returns zero on success or %-ENODEV on failure
381  * File: src/omap2_spican.c
382  */
383 int omap2_spican_program_irq(struct candevice_t *candev)
384 {
385         return 0;
386 }
387
388 /*******************************************************************/
389 int omap2_spican_spi_acquire_bus(struct candevice_t *candev, short channel, int block){
390         return 1;
391 }
392
393 /*******************************************************************/
394 void omap2_spican_spi_release_bus(struct candevice_t *candev, short channel){
395         return;
396 }
397
398 int omap2_spican_register(struct hwspecops_t *hwspecops)
399 {
400         hwspecops->request_io = omap2_spican_request_io;
401         hwspecops->release_io = omap2_spican_release_io;
402         hwspecops->reset = omap2_spican_reset;
403         hwspecops->init_hw_data = omap2_spican_init_hw_data;
404         hwspecops->init_chip_data = omap2_spican_init_chip_data;
405         hwspecops->init_obj_data = omap2_spican_init_obj_data;
406         hwspecops->write_register = omap2_spican_write_register;
407         hwspecops->read_register = omap2_spican_read_register;
408         hwspecops->program_irq = omap2_spican_program_irq;
409
410         // SPI specific functions
411         hwspecops->spi_transfer = omap2_spican_spi_transfer;
412         hwspecops->spi_acquire_bus = omap2_spican_spi_acquire_bus;
413         hwspecops->spi_release_bus = omap2_spican_spi_release_bus;
414
415         return 0;
416 }
417
418 /*******************************************************************/
419
420 static int omap2_spican_probe(struct spi_device *spi)
421 {
422         struct omap2_spican_platform_data       *pdata = spi->dev.platform_data;
423         struct candevice_t *dev;
424         int allocated = 0;
425
426         static struct omap2_spican_platform_data mypdata;
427         static const char* omap2_spican_defaulttype = "mcp2515";
428         
429         DEBUGMSG("Starting probe for omap2_spican...\n");
430         
431         // Structure checks and initialization
432         if (pdata == NULL){
433 /*              pdata = (struct omap2_spican_platform_data *)can_checked_malloc(sizeof(struct omap2_spican_platform_data));
434                 if (pdata)
435                         memset(pdata,0,sizeof(struct omap2_spican_platform_data));
436                 else
437                         return -ENOMEM;
438                 allocated = 1;*/
439                 pdata = &mypdata; // No need to free up
440                 
441                 if (!pdata->cs_change) pdata->cs_change = OMAP2_SPICAN_CS_CHANGE;
442                 if (!pdata->delay_usecs) pdata->delay_usecs = OMAP2_SPICAN_DELAY_USECS;
443         }
444         if (!pdata->mcp2515_clk) pdata->mcp2515_clk = OMAP2_SPICAN_MCP_CLK;
445         if (!pdata->baudrate) pdata->baudrate = OMAP2_SPICAN_BAUDRATE;
446         if (!pdata->speed_hz) pdata->speed_hz = OMAP2_SPICAN_SPEED_HZ;
447         if (!pdata->chiptype) pdata->chiptype = omap2_spican_defaulttype;
448
449         
450         spin_lock_init(&pdata->spi_lock);
451         pdata->spi = spi;
452         pdata->trigger = 0;
453         if (spi->irq) {
454                 DEBUGMSG("Interrupt line number %d provided.\n",spi->irq);
455                 pdata->trigger = OMAP2_SPICAN_TRIG_IRQ;
456                 pdata->irq = spi->irq;
457         }
458         else
459                 DEBUGMSG("no IRQ given, trying to find timers. Performance loss will be observed.\n");
460
461         /* Register device data in LinCAN - 1 chip, 2 msgobjs */
462         dev = register_hotplug_dev("omap2_spican",omap2_spican_register_chip_data,pdata);
463         if (!dev)
464                 goto release;
465
466         if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ) {
467                 int status = 0;
468                 DEBUGMSG("Requesting interrupt line %d.\n",spi->irq);
469                 status = set_irq_type(pdata->irq,IRQ_TYPE_EDGE_BOTH);
470                 if(status){
471                         CANMSG("Setting low level trigger on irq %d failed.\n", spi->irq);
472                         goto release;
473                 }
474                 status = request_irq(pdata->irq, omap2_spican_irq_handler, IRQF_DISABLED , "omap2_spican", dev);
475                 if(status){
476                         CANMSG("request_irq failed on irq %d\n", spi->irq);
477                         goto release;
478                 }
479         }
480 #ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
481         if (pdata->trigger == 0){
482                 struct clk *gt_fclk;
483                 uint32_t gt_rate;
484                 int status = 0;
485                 int prescaler = 0;
486
487                 DEBUGMSG("Setting up OMAP GPT\n");
488                 // Get the timer
489                 pdata->timer_ptr = omap_dm_timer_request();
490                 if(pdata->timer_ptr == NULL){
491                         CANMSG("No more gp timers available, bailing out\n");
492                         goto release;
493                 }
494                 
495                 // Initialize timer
496                 omap_dm_timer_set_source(pdata->timer_ptr, OMAP_TIMER_SRC_SYS_CLK); // We use the system clock 38.4 MHz
497                 
498                 omap_dm_timer_set_prescaler(pdata->timer_ptr, prescaler);               // Set prescaler to 2^(n+1)
499  
500                 // get clock rate in Hz
501                 gt_fclk = omap_dm_timer_get_fclk(pdata->timer_ptr);
502                 gt_rate = clk_get_rate(gt_fclk)/(1<<(prescaler+1));
503          
504                 // set preload, and autoreload
505                 // we set it to the clock rate in order to get 1 overflow every 10 usecs
506                 omap_dm_timer_set_load(pdata->timer_ptr, 1, 0xFFFFFFFF - (uint32_t)(gt_rate / 100));
507
508                 // Request for the irq
509                 pdata->timer_irq = omap_dm_timer_get_irq(pdata->timer_ptr);
510                 DEBUGMSG("Requesting irq %d for OMAP GPT\n", pdata->timer_irq);
511                 status = request_irq(pdata->timer_irq, omap2_spican_irq_handler, IRQF_DISABLED | IRQF_TIMER , "omap2_spican", dev);
512                 if(status){
513                         CANMSG("request_irq failed (on irq %d), bailing out\n", pdata->timer_irq);
514                   omap_dm_timer_free(pdata->timer_ptr);
515                         goto release;
516                 }
517
518                 // setup timer to trigger our IRQ on the overflow event
519                 omap_dm_timer_set_int_enable(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
520                 pdata->trigger = OMAP2_SPICAN_TRIG_GPT;
521         }
522 #endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
523
524         spi_set_drvdata(spi, dev);
525
526 #ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
527                         if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
528                                 // Everything's ready, let's roll!
529                                 DEBUGMSG("Starting OMAP GPT\n");
530                                 omap_dm_timer_start(pdata->timer_ptr);
531                         }
532 #endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
533         DEBUGMSG("Device omap2_spican successfully configured. Have fun!\n");
534         return 0;
535         
536 release:
537         if (allocated)
538                 can_checked_free(pdata);
539         return -ENODEV;
540 }
541
542 static int omap2_spican_remove(struct spi_device *spi)
543 {
544         struct candevice_t *candev = spi_get_drvdata(spi);
545         struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
546
547         if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ){
548                 // release the IRQ handler
549                 free_irq(pdata->irq, candev);
550         }
551 #ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
552         if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
553                 DEBUGMSG("Stopping OMAP GPT\n");
554                 omap_dm_timer_stop(pdata->timer_ptr);
555                 // release the IRQ handler
556                 free_irq(pdata->timer_irq, candev);
557           // release the timer
558           omap_dm_timer_free(pdata->timer_ptr);
559         }
560 #endif
561
562         DEBUGMSG("Removing omap2_spican from device structure...");
563         //      make sure ops on existing fds can abort cleanly
564         cleanup_hotplug_dev(candev);
565
566         spin_lock_irq(&pdata->spi_lock);
567         pdata->spi = NULL;
568         spi_set_drvdata(spi, NULL);
569 //      candev->sysdevptr.anydev = NULL;
570         spin_unlock_irq(&pdata->spi_lock);
571
572         DEBUGMSG(" done.\n");
573
574         return 0;
575 }
576
577 static struct spi_driver omap2_spican_driver = {
578         .driver = {
579                 .name   = "omap2_spican",
580                 .owner  = THIS_MODULE,
581         },
582         .probe          = omap2_spican_probe,
583         .remove         = omap2_spican_remove,
584 /*      .suspend        = omap2_spican_suspend,
585         .resume         = omap2_spican_resume,*/
586 };
587
588 int omap2_spican_init(void){
589         return spi_register_driver(&omap2_spican_driver);
590 }
591
592 void omap2_spican_exit(void){
593         spi_unregister_driver(&omap2_spican_driver);
594 }
595
596 #ifdef MODULE_ALIAS
597 MODULE_ALIAS("spi:omap2_spican");
598 #endif