raspi_gpio_driver

annotate gpio.c @ 0:fbab2b3e28da

gpio module first test
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 04 Mar 2016 05:20:55 +0000
parents
children acfcd119e532
rev   line source
nuclear@0 1 #include <linux/module.h>
nuclear@0 2 #include <linux/kernel.h>
nuclear@0 3 #include <linux/io.h>
nuclear@0 4 #include <linux/fs.h>
nuclear@0 5 #include <asm/uaccess.h>
nuclear@0 6
nuclear@0 7 /* raspberry pi 2 */
nuclear@0 8 #define PERI_ADDR 0x3f000000
nuclear@0 9 #define PERI_SIZE 0x1000000
nuclear@0 10
nuclear@0 11 #define REG_GPIO_BASE 0x200000
nuclear@0 12 #define REG_GPFSEL_BASE 0x200000
nuclear@0 13 #define REG_GPSET_BASE 0x20001c
nuclear@0 14 #define REG_GPCLR_BASE 0x200028
nuclear@0 15 #define REG_GPLEV_BASE 0x200034
nuclear@0 16 #define REG_GPPUD 0x200094
nuclear@0 17 #define REG_GPPUDCLK_BASE 0x200098
nuclear@0 18
nuclear@0 19 #define FSEL_BITS 3
nuclear@0 20 #define FSEL_INPUT 0
nuclear@0 21 #define FSEL_OUTPUT 1
nuclear@0 22
nuclear@0 23 #define FSEL_SHIFT(i) (((i) % 10) * 3)
nuclear@0 24 #define FSEL_MASK(i) (3 << FSEL_SHIFT(i))
nuclear@0 25
nuclear@0 26 #define REG(r) (volatile uint32_t*)((unsigned char*)iomem + (r))
nuclear@0 27
nuclear@0 28 static int gpio_init(void);
nuclear@0 29 static void gpio_exit(void);
nuclear@0 30 static int gpio_open(struct inode *inode, struct file *file);
nuclear@0 31 static int gpio_close(struct inode *inode, struct file *file);
nuclear@0 32 static ssize_t gpio_write(struct file *file, const char *buf, size_t sz, loff_t *offset);
nuclear@0 33
nuclear@0 34 static inline void set_pin_func(int pin, int func);
nuclear@0 35 static inline void set_pin_state(int pin, int s);
nuclear@0 36 static inline int get_pin_state(int pin);
nuclear@0 37 static inline void set_pullup(int pin, int en);
nuclear@0 38 static inline void set_pulldown(int pin, int en);
nuclear@0 39
nuclear@0 40 MODULE_LICENSE("GPL");
nuclear@0 41 MODULE_AUTHOR("John Tsiombikas");
nuclear@0 42 MODULE_DESCRIPTION("gpio module");
nuclear@0 43
nuclear@0 44 module_init(gpio_init);
nuclear@0 45 module_exit(gpio_exit);
nuclear@0 46
nuclear@0 47 static int major;
nuclear@0 48 static void *iomem;
nuclear@0 49
nuclear@0 50 static struct file_operations ops = {
nuclear@0 51 .open = gpio_open,
nuclear@0 52 .release = gpio_close,
nuclear@0 53 .write = gpio_write
nuclear@0 54 };
nuclear@0 55
nuclear@0 56 static int gpio_init(void)
nuclear@0 57 {
nuclear@0 58 if((major = register_chrdev(0, "gpio", &ops)) < 0) {
nuclear@0 59 printk("failed to register character device gpio (err: %d)\n", major);
nuclear@0 60 return -1;
nuclear@0 61 }
nuclear@0 62 printk("gpio module registered (device: %d)\n", major);
nuclear@0 63 return 0;
nuclear@0 64 }
nuclear@0 65
nuclear@0 66 static void gpio_exit(void)
nuclear@0 67 {
nuclear@0 68 unregister_chrdev(major, "gpio");
nuclear@0 69 printk("gpio exit\n");
nuclear@0 70 }
nuclear@0 71
nuclear@0 72 static int gpio_open(struct inode *inode, struct file *file)
nuclear@0 73 {
nuclear@0 74 printk("gpio_open called\n");
nuclear@0 75 if(iomem) {
nuclear@0 76 return -EBUSY;
nuclear@0 77 }
nuclear@0 78
nuclear@0 79 if(!(iomem = ioremap(PERI_ADDR, PERI_SIZE))) {
nuclear@0 80 printk("gpio: failed to map peripherals\n");
nuclear@0 81 return -EBUSY;
nuclear@0 82 }
nuclear@0 83 set_pin_func(4, FSEL_OUTPUT);
nuclear@0 84 set_pin_state(4, 0);
nuclear@0 85
nuclear@0 86 try_module_get(THIS_MODULE);
nuclear@0 87 return 0;
nuclear@0 88 }
nuclear@0 89
nuclear@0 90 static int gpio_close(struct inode *inode, struct file *file)
nuclear@0 91 {
nuclear@0 92 if(!iomem) {
nuclear@0 93 return -EBADF;
nuclear@0 94 }
nuclear@0 95
nuclear@0 96 iounmap(iomem);
nuclear@0 97 iomem = 0;
nuclear@0 98
nuclear@0 99 module_put(THIS_MODULE);
nuclear@0 100 return 0;
nuclear@0 101 }
nuclear@0 102
nuclear@0 103 static ssize_t gpio_write(struct file *file, const char *buf, size_t sz, loff_t *offset)
nuclear@0 104 {
nuclear@0 105 unsigned char val;
nuclear@0 106
nuclear@0 107 printk("gpio_write(%d) called\n", sz);
nuclear@0 108
nuclear@0 109 if(sz < 1) {
nuclear@0 110 return 0;
nuclear@0 111 }
nuclear@0 112 if(copy_from_user(&val, (unsigned char*)buf + sz - 1, 1) != 0) {
nuclear@0 113 return -EFAULT;
nuclear@0 114 }
nuclear@0 115
nuclear@0 116 set_pin_state(4, val ? 1 : 0);
nuclear@0 117 return sz;
nuclear@0 118 }
nuclear@0 119
nuclear@0 120
nuclear@0 121 static inline void set_pin_func(int pin, int func)
nuclear@0 122 {
nuclear@0 123 int reg_addr, val;
nuclear@0 124 reg_addr = REG_GPFSEL_BASE + (pin / 10) * 4;
nuclear@0 125 val = *REG(reg_addr);
nuclear@0 126 *REG(reg_addr) = (val & FSEL_MASK(pin)) | (func << FSEL_SHIFT(pin));
nuclear@0 127 }
nuclear@0 128
nuclear@0 129 static inline void set_pin_state(int pin, int s)
nuclear@0 130 {
nuclear@0 131 int reg_addr;
nuclear@0 132 int regidx = pin / 32;
nuclear@0 133 int bit = pin % 32;
nuclear@0 134
nuclear@0 135 if(s) {
nuclear@0 136 reg_addr = REG_GPSET_BASE + regidx * 4;
nuclear@0 137 *REG(reg_addr) |= 1 << bit;
nuclear@0 138 } else {
nuclear@0 139 reg_addr = REG_GPCLR_BASE + regidx * 4;
nuclear@0 140 *REG(reg_addr) |= 1 << bit;
nuclear@0 141 }
nuclear@0 142 }
nuclear@0 143
nuclear@0 144 static inline int get_pin_state(int pin)
nuclear@0 145 {
nuclear@0 146 int reg_addr;
nuclear@0 147 int regidx = pin / 32;
nuclear@0 148 int bit = pin % 32;
nuclear@0 149
nuclear@0 150 reg_addr = REG_GPLEV_BASE + regidx * 4;
nuclear@0 151 return (*REG(reg_addr) >> bit) & 1;
nuclear@0 152 }
nuclear@0 153
nuclear@0 154 #define delay_cycles(x) \
nuclear@0 155 __asm__ __volatile__ ( \
nuclear@0 156 "1:\n\t" \
nuclear@0 157 "subs %0, %0, #1\n\t" \
nuclear@0 158 "bne 1b\n\t" \
nuclear@0 159 :: "r"(x))
nuclear@0 160
nuclear@0 161 static inline void set_pull_updown(int pin, int updown)
nuclear@0 162 {
nuclear@0 163 int regidx = pin / 32;
nuclear@0 164 int bit = pin % 32;
nuclear@0 165 int clk_addr = REG_GPPUDCLK_BASE + regidx * 4;
nuclear@0 166
nuclear@0 167 *REG(REG_GPPUD) = updown;
nuclear@0 168 delay_cycles(150);
nuclear@0 169 *REG(clk_addr) = bit;
nuclear@0 170 delay_cycles(150);
nuclear@0 171 *REG(REG_GPPUD) = 0;
nuclear@0 172 *REG(clk_addr) = 0;
nuclear@0 173 }
nuclear@0 174
nuclear@0 175 static inline void set_pullup(int pin, int en)
nuclear@0 176 {
nuclear@0 177 set_pull_updown(pin, en ? 2 : 0);
nuclear@0 178 }
nuclear@0 179
nuclear@0 180 static inline void set_pulldown(int pin, int en)
nuclear@0 181 {
nuclear@0 182 set_pull_updown(pin, en ? 1 : 0);
nuclear@0 183 }