Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/io.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/sched.h>
- #include <linux/interrupt.h>
- #include <linux/i2c-dev.h>
- #include <asm/uaccess.h>
- #include <mach/irqs.h>
- // base addresses of the 3 I2C units
- #define BSC0_BASE 0x20205000
- #define BSC1_BASE 0x20804000
- #define BSC2_BASE 0x20805000
- // registers for each unit
- #define BSC_C 0
- #define BSC_S 1
- #define BSC_DLEN 2
- #define BSC_A 3
- #define BSC_FIFO 4
- #define BSC_DIV 5
- #define BSC_DEL 6
- #define BSC_CLKT 6
- #define BSC_C_I2CEN (1 << 15)
- #define BSC_C_INTR (1 << 10)
- #define BSC_C_INTT (1 << 9)
- #define BSC_C_INTD (1 << 8)
- #define BSC_C_ST (1 << 7)
- #define BSC_C_CLEAR ((1 << 4) | (1 << 5))
- #define BSC_C_READ (1 << 0)
- #define BSC_S_CLKT (1 << 9)
- #define BSC_S_ERR (1 << 8)
- #define BSC_S_RXF (1 << 7)
- #define BSC_S_TXE (1 << 6)
- #define BSC_S_RXD (1 << 5)
- #define BSC_S_TXD (1 << 4)
- #define BSC_S_RXR (1 << 3)
- #define BSC_S_TXW (1 << 2)
- #define BSC_S_DONE (1 << 1)
- #define BSC_S_TA (1 << 0)
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Frank Buss");
- MODULE_DESCRIPTION("I2C driver");
- MODULE_SUPPORTED_DEVICE("I2C");
- const char *g_device_name = "i2c";
- static int g_major;
- static struct class *g_class_i2c;
- static struct device *g_dev_i2c;
- static u32 *g_base0 = NULL;
- static u32 g_slaveAddress = 0;
- // transfer timeout time in milliseconds
- #define TIMEOUT_MS 500
- // in jiffies
- #define I2C_TIMEOUT_JIFFIES HZ*TIMEOUT_MS/1000
- // maximum number of bytes for sending and receiving one message
- #define MAX_BYTE_COUNT 256
- // IRQ locking and signaling
- static DECLARE_WAIT_QUEUE_HEAD(g_irqQueue);
- static int g_doneFlag = 0;
- // send/receive buffer
- #define MAX_DATA_SIZE 256
- static uint8_t g_data[MAX_DATA_SIZE];
- static int g_len;
- static int g_index;
- // set alternate function 0 on GPIO 0 and GPIO 1 for SDA0/SCL0
- static void set_i2c_function(void)
- {
- // TODO: this is a really bad hack, maybe should call an exported function from bcm2708_gpio.c
- #define GPIO_FSEL_ALT0 4
- u32* gpio = (u32*) ioremap(0x20200000, SZ_16K);
- gpio[0] = (gpio[0] & ~0x3f) | GPIO_FSEL_ALT0 | (GPIO_FSEL_ALT0 << 3);
- iounmap((void*) gpio);
- }
- static int i2c_open(struct inode *inode, struct file *filp)
- {
- // TODO: maybe exclusive access
- return 0;
- }
- static int i2c_release(struct inode *inode, struct file *filp)
- {
- return 0;
- }
- static irqreturn_t i2c_interrupt(int irq, void *dev_id)
- {
- unsigned long flags;
- local_irq_save(flags);
- // send next bytes to FIFO
- while (g_base0[BSC_S] & BSC_S_TXW) {
- if (g_index == g_len) break;
- g_base0[BSC_FIFO] = g_data[g_index++];
- }
- // receive next bytes from FIFO
- while (g_base0[BSC_S] & BSC_S_RXD) {
- if (g_index == g_len) break;
- g_data[g_index++] = g_base0[BSC_FIFO];
- }
- // transfer finished
- if (g_base0[BSC_S] & BSC_S_DONE) {
- g_base0[BSC_S] = BSC_S_DONE;
- g_doneFlag = 1;
- wake_up_interruptible(&g_irqQueue);
- }
- local_irq_restore(flags);
- return IRQ_HANDLED;
- }
- static ssize_t i2c_read(struct file *filp, char *buffer, size_t len, loff_t *offset)
- {
- unsigned long flags;
- // test parameters
- if (len > MAX_DATA_SIZE) {
- return -EINVAL;
- }
- // disable interrupts
- local_irq_save(flags);
- // disable BSC
- g_base0[BSC_C] = 0;
- // init interrupt data
- g_len = len;
- g_index = 0;
- g_doneFlag = 0;
- // set slave address
- g_base0[BSC_A] = g_slaveAddress;
- // set len and start transfer
- g_base0[BSC_DLEN] = len;
- // enable interrupts
- local_irq_restore(flags);
- // start transfer, interrupt on RXR and DONE
- g_base0[BSC_C] = BSC_C_READ | BSC_C_I2CEN | BSC_C_CLEAR | BSC_C_ST | BSC_C_INTR | BSC_C_INTD;
- // non-blocking wait for transfer end
- if (wait_event_interruptible_timeout(g_irqQueue, g_doneFlag, I2C_TIMEOUT_JIFFIES) == 0) {
- printk(KERN_INFO "read timeout\n");
- return -EFAULT;
- }
- // read rest from FIFO, if there is data
- while (g_base0[BSC_S] & BSC_S_RXD) {
- if (g_index == g_len) break;
- g_data[g_index++] = g_base0[BSC_FIFO];
- }
- // reset flags
- g_base0[BSC_S] = BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE;
- // disable BSC
- g_base0[BSC_C] = 0;
- // copy to user space
- if (copy_to_user(buffer, g_data, len)) return -EFAULT;
- return len;
- }
- static ssize_t i2c_write(struct file *filp, const char *buffer, size_t len, loff_t *off)
- {
- unsigned long flags;
- // test parameters
- if (len > MAX_DATA_SIZE) {
- return -EINVAL;
- }
- // copy to kernel space
- if (copy_from_user(g_data, buffer, len)) return -EFAULT;
- // disable interrupts
- local_irq_save(flags);
- // disable BSC
- g_base0[BSC_C] = 0;
- // init interrupt data
- g_len = len;
- g_index = 0;
- g_doneFlag = 0;
- // set slave address
- g_base0[BSC_A] = g_slaveAddress;
- // set len and start transfer
- g_base0[BSC_DLEN] = len;
- // enable interrupts
- local_irq_restore(flags);
- // start transfer, interrupt on TXW and DONE
- g_base0[BSC_C] = BSC_C_I2CEN | BSC_C_CLEAR | BSC_C_ST | BSC_C_INTT | BSC_C_INTD;
- // non-blocking wait for transfer end
- if (wait_event_interruptible_timeout(g_irqQueue, g_doneFlag, I2C_TIMEOUT_JIFFIES) == 0) {
- printk(KERN_INFO "write timeout\n");
- return -EFAULT;
- }
- // reset flags
- g_base0[BSC_S] = BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE;
- // disable BSC
- g_base0[BSC_C] = 0;
- return len;
- }
- static long i2c_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- switch (cmd) {
- case I2C_SLAVE:
- g_slaveAddress = arg;
- return 0;
- default:
- return -ENOIOCTLCMD;
- }
- }
- struct file_operations i2c_fops = {
- owner: THIS_MODULE,
- open: i2c_open,
- release: i2c_release,
- read: i2c_read,
- write: i2c_write,
- unlocked_ioctl: i2c_ioctl,
- };
- static int __init i2c_init(void)
- {
- int ret;
- // init irq
- init_waitqueue_head(&g_irqQueue);
- ret = request_irq(IRQ_I2C, i2c_interrupt, IRQF_DISABLED, "i2c", NULL);
- if (ret) {
- printk(KERN_INFO "request_irq failed: %i\n", ret);
- return ret;
- }
- // init hardware
- set_i2c_function();
- g_base0 = (u32*) ioremap(BSC0_BASE, SZ_16K);
- if (!g_base0) {
- printk(KERN_ERR "ioremap failed\n");
- return -EINVAL;
- }
- // register character device
- g_major = register_chrdev(0, g_device_name, &i2c_fops);
- if (g_major < 0) {
- printk(KERN_ERR "register_chrdev failed with %d\n", g_major);
- return g_major;
- }
- g_class_i2c = class_create(THIS_MODULE, g_device_name);
- if (IS_ERR(g_class_i2c)) return PTR_ERR(g_class_i2c);
- g_dev_i2c = device_create(g_class_i2c, NULL, MKDEV(g_major, 0), NULL, g_device_name);
- if (IS_ERR(g_dev_i2c)) return PTR_ERR(g_dev_i2c);
- printk(KERN_INFO "initialized\n");
- return 0;
- }
- static void __exit i2c_exit(void)
- {
- // unregister character device
- device_destroy(g_class_i2c, MKDEV(g_major, 0));
- class_destroy(g_class_i2c);
- unregister_chrdev(g_major, g_device_name);
- // unmap register area
- if (g_base0) {
- iounmap((void*) g_base0);
- g_base0 = NULL;
- }
- // release interrupt
- free_irq(IRQ_I2C, NULL);
- printk(KERN_INFO "module unloaded\n");
- }
- module_init(i2c_init);
- module_exit(i2c_exit);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement