📄 mcp2510.c
字号:
/* * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) * * (c) SAN People (Pty) Ltd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version.*/#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <asm/arch/AT91RM9200_SPI.h>#include <asm/arch/pio.h>#include "mcp2510.h"#include "../spi/at91_spi.h"
#include <asm/uaccess.h>#ifdef CONFIG_DEVFS_FS#include <linux/devfs_fs_kernel.h>#endif#undef DEBUG_MCP2510//#define DEBUG_MCP2510#define CAN_MAJOR 250 /* registered device number */#define NR_CAN_DEVICES 2 /* number of devices on SPI bus */#ifdef CONFIG_DEVFS_FSstatic devfs_handle_t devfs_handle = NULL;static devfs_handle_t devfs_mcp2510[NR_CAN_DEVICES];#endif#define WRITE_COM 1#define READ_COM 2#define RESET_COM 3#define BWM_COM 4#define RTS_COM 5/* ......................................................................... */static struct spi_transfer_list spi_transfer_desc;/* * Perform a SPI transfer to access the CAN device. */static int d_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, char* txnext, int txnext_len, char* rxnext, int rxnext_len){ struct spi_transfer_list* list = (struct spi_transfer_list *)&spi_transfer_desc; list->tx[0] = tx; list->txlen[0] = tx_len; list->rx[0] = rx; list->rxlen[0] = rx_len; list->tx[1] = txnext; list->txlen[1] = txnext_len; list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; list->nr_transfers = nr; return spi_transfer(list);}static int mcp2510_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ void *data; char* databuf; char* command; struct mcp2510_local mcpFrame; int spi_device = MINOR(inode->i_rdev); if (spi_device >= NR_SPI_DEVICES) return -ENODEV; data = (void *) arg; if (copy_from_user(&mcpFrame,data,sizeof(struct mcp2510_local))) { printk("copy mcpFrame from fail\n"); } databuf = kmalloc(mcpFrame.len, GFP_KERNEL); if (!databuf){ printk("malloc memory fail\n"); return 0; } command = kmalloc(2, GFP_KERNEL); if (!command){ printk("malloc memory fail\n"); return 0; } spi_access_bus(spi_device); switch(cmd) { case WRITE_COM: if (copy_from_user(databuf, mcpFrame.pData, mcpFrame.len)) { printk("copy_from_user fail\n"); } command[0] = WRITE_CM; command[1] = mcpFrame.addr;#ifdef DEBUG_MCP2510// printk("WRITE:addr=%x len=%x\n",command[1],mcpFrame.len);#endif d_spi_transfer(2,command,2,command,2,databuf,mcpFrame.len,databuf, mcpFrame.len); break; case READ_COM: command[0] = READ_CM; command[1] = mcpFrame.addr;#ifdef DEBUG_MCP2510// printk("READ:addr=%x len=%x\n",command[1],mcpFrame.len);#endif d_spi_transfer(2,command,2,command,2,databuf,mcpFrame.len,databuf, mcpFrame.len); if (copy_to_user(mcpFrame.pData, databuf, mcpFrame.len)) { printk("copy_to_user fail\n"); } break; case RESET_COM: command[0] = RESET_CM; command[1] = 0; d_spi_transfer(1,command,2,command,2,NULL, 0, NULL, 0); break; case BWM_COM: if (copy_from_user(databuf, mcpFrame.pData, mcpFrame.len)) { printk("copy_from_user fail\n"); } command[0] = BIT_MOD_CM; command[1] = mcpFrame.addr;#ifdef DEBUG_MCP2510 printk("BWM:addr=%x mask=%x data=%x\n",command[1],databuf[0],databuf[1]);#endif d_spi_transfer(2,command,2,command,2,databuf,mcpFrame.len,databuf,mcpFrame.len); break; case RTS_COM: command[0] = mcpFrame.addr; d_spi_transfer(1,command,1,command, 1, NULL, 0, NULL, 0); break; default:#ifdef DEBUG_MCP2510 printk("default device%d\n",spi_device-2);#endif break; } spi_release_bus(spi_device); kfree(databuf); kfree(command); return 0;}/* * Open the SPI device */static int mcp2510_open(struct inode *inode, struct file *file){ unsigned int spi_device = MINOR(inode->i_rdev); if (spi_device >= NR_SPI_DEVICES) return -ENODEV; MOD_INC_USE_COUNT; /* * 'private_data' is actually a pointer, but we overload it with the * value we want to store. */ (unsigned int) file->private_data = spi_device; printk("CAN %i open\n",spi_device-2);//configure power for pio AT91_SYS->PMC_PCER = 1 << AT91C_ID_PIOC; /* enable peripheral clock *///configure input mode AT91_SYS->PIOC_ODR = AT91C_PIO_PC15|AT91C_PIO_PC14; AT91_SYS->PIOC_PER = AT91C_PIO_PC15|AT91C_PIO_PC14;//enable interrupt
AT91_SYS->PIOC_IER = AT91C_PIO_PC15|AT91C_PIO_PC14;
AT91_SYS->AIC_IECR = 0x1 << AT91C_ID_PIOC ;
return 0;}/* * Close the SPI device */static int mcp2510_close(struct inode *inode, struct file *file){ unsigned int spi_device = MINOR(inode->i_rdev); MOD_DEC_USE_COUNT; printk("CAN %i close\n",spi_device-2); return 0;}/* ......................................................................... *//* * Handle interrupts from the SPI controller. */static void can_interrupt(int irq, void *dev_id, struct pt_regs *regs){ unsigned int status;
unsigned int mask = 0x1 << AT91C_ID_PIOC;
//* Disable the interrupt on the interrupt controller
AT91_SYS->AIC_IDCR = mask ;
//* Clear the interrupt on the Interrupt Controller ( if one is pending )
AT91_SYS->AIC_ICCR = mask ;
status =AT91_SYS->PIOC_ISR; if((status&AT91C_PIO_PC14)==AT91C_PIO_PC14){ if(((AT91_SYS->PIOC_PDSR)&AT91C_PIO_PC14?0:1)) { printk("can_interrupt 0 %x\n",status); goto exit; } goto exit; } else if((status&AT91C_PIO_PC15)==AT91C_PIO_PC15){ if(((AT91_SYS->PIOC_PDSR)&AT91C_PIO_PC15?0:1)) { printk("can_interrupt 1 %x\n",status); goto exit; } goto exit; } exit:
//* Enable the interrupt on the interrupt controller
AT91_SYS->AIC_IECR = 0x1 << AT91C_ID_PIOC ;
return;}/* ......................................................................... */static struct file_operations candev_fops = { owner: THIS_MODULE, llseek: no_llseek, read: NULL, write: NULL, ioctl: mcp2510_ioctl, open: mcp2510_open, release: mcp2510_close,};/* ......................................................................... *//* Allocate a single SPI transfer descriptor. We're assuming that if multiple SPI transfers occur at the same time, spi_access_bus() will serialize them. If this is not valid, then either (i) each dataflash 'priv' structure needs it's own transfer descriptor, (ii) we lock this one, or (iii) use another mechanism. */static int __init mcp2510_init(void){ int i; char name[2];#ifdef CONFIG_DEVFS_FS if (devfs_register_chrdev(CAN_MAJOR, "can", &candev_fops)) {#else if (register_chrdev(CAN_MAJOR, "can", &candev_fops)) {#endif printk(KERN_ERR "at91_CANdev: Unable to get major %d for CAN bus\n", CAN_MAJOR); return -EIO; }#ifdef CONFIG_DEVFS_FS devfs_handle = devfs_mk_dir(NULL, "can", NULL); for (i = 0; i < NR_CAN_DEVICES; i++) { sprintf (name, "%d", i); devfs_mcp2510[i] = devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, CAN_MAJOR, 2+i, S_IFCHR | S_IRUSR | S_IWUSR, &candev_fops, NULL); }#endif if (request_irq(AT91C_ID_PIOC, can_interrupt, 0, "can", NULL)) return -EBUSY; printk(KERN_INFO "CAN-Bus driver loaded\n"); return 0;}static void __exit mcp2510_exit(void){#ifdef CONFIG_DEVFS_FS devfs_unregister(devfs_handle); if (devfs_unregister_chrdev(CAN_MAJOR, "can")) {#else if (unregister_chrdev(CAN_MAJOR,"can")) {#endif printk(KERN_ERR "at91_CANdev: Unable to release major %d for CAN-Bus\n", CAN_MAJOR); return; } free_irq(AT91C_ID_PIOC, 0);}EXPORT_NO_SYMBOLS;module_init(mcp2510_init);module_exit(mcp2510_exit);MODULE_LICENSE("GPL")MODULE_AUTHOR("Andrew Victor")MODULE_DESCRIPTION("CAN-Bus driver for Atmel AT91RM9200")
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -