# HG changeset patch # User John Tsiombikas # Date 1457068855 0 # Node ID fbab2b3e28da89e0242f62ad99860b21e8b3bfc7 gpio module first test diff -r 000000000000 -r fbab2b3e28da Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Mar 04 05:20:55 2016 +0000 @@ -0,0 +1,9 @@ +obj-m := gpio.o + +.PHONY: all +all: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules + +.PHONY: clean +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean diff -r 000000000000 -r fbab2b3e28da gpio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpio.c Fri Mar 04 05:20:55 2016 +0000 @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include + +/* raspberry pi 2 */ +#define PERI_ADDR 0x3f000000 +#define PERI_SIZE 0x1000000 + +#define REG_GPIO_BASE 0x200000 +#define REG_GPFSEL_BASE 0x200000 +#define REG_GPSET_BASE 0x20001c +#define REG_GPCLR_BASE 0x200028 +#define REG_GPLEV_BASE 0x200034 +#define REG_GPPUD 0x200094 +#define REG_GPPUDCLK_BASE 0x200098 + +#define FSEL_BITS 3 +#define FSEL_INPUT 0 +#define FSEL_OUTPUT 1 + +#define FSEL_SHIFT(i) (((i) % 10) * 3) +#define FSEL_MASK(i) (3 << FSEL_SHIFT(i)) + +#define REG(r) (volatile uint32_t*)((unsigned char*)iomem + (r)) + +static int gpio_init(void); +static void gpio_exit(void); +static int gpio_open(struct inode *inode, struct file *file); +static int gpio_close(struct inode *inode, struct file *file); +static ssize_t gpio_write(struct file *file, const char *buf, size_t sz, loff_t *offset); + +static inline void set_pin_func(int pin, int func); +static inline void set_pin_state(int pin, int s); +static inline int get_pin_state(int pin); +static inline void set_pullup(int pin, int en); +static inline void set_pulldown(int pin, int en); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Tsiombikas"); +MODULE_DESCRIPTION("gpio module"); + +module_init(gpio_init); +module_exit(gpio_exit); + +static int major; +static void *iomem; + +static struct file_operations ops = { + .open = gpio_open, + .release = gpio_close, + .write = gpio_write +}; + +static int gpio_init(void) +{ + if((major = register_chrdev(0, "gpio", &ops)) < 0) { + printk("failed to register character device gpio (err: %d)\n", major); + return -1; + } + printk("gpio module registered (device: %d)\n", major); + return 0; +} + +static void gpio_exit(void) +{ + unregister_chrdev(major, "gpio"); + printk("gpio exit\n"); +} + +static int gpio_open(struct inode *inode, struct file *file) +{ + printk("gpio_open called\n"); + if(iomem) { + return -EBUSY; + } + + if(!(iomem = ioremap(PERI_ADDR, PERI_SIZE))) { + printk("gpio: failed to map peripherals\n"); + return -EBUSY; + } + set_pin_func(4, FSEL_OUTPUT); + set_pin_state(4, 0); + + try_module_get(THIS_MODULE); + return 0; +} + +static int gpio_close(struct inode *inode, struct file *file) +{ + if(!iomem) { + return -EBADF; + } + + iounmap(iomem); + iomem = 0; + + module_put(THIS_MODULE); + return 0; +} + +static ssize_t gpio_write(struct file *file, const char *buf, size_t sz, loff_t *offset) +{ + unsigned char val; + + printk("gpio_write(%d) called\n", sz); + + if(sz < 1) { + return 0; + } + if(copy_from_user(&val, (unsigned char*)buf + sz - 1, 1) != 0) { + return -EFAULT; + } + + set_pin_state(4, val ? 1 : 0); + return sz; +} + + +static inline void set_pin_func(int pin, int func) +{ + int reg_addr, val; + reg_addr = REG_GPFSEL_BASE + (pin / 10) * 4; + val = *REG(reg_addr); + *REG(reg_addr) = (val & FSEL_MASK(pin)) | (func << FSEL_SHIFT(pin)); +} + +static inline void set_pin_state(int pin, int s) +{ + int reg_addr; + int regidx = pin / 32; + int bit = pin % 32; + + if(s) { + reg_addr = REG_GPSET_BASE + regidx * 4; + *REG(reg_addr) |= 1 << bit; + } else { + reg_addr = REG_GPCLR_BASE + regidx * 4; + *REG(reg_addr) |= 1 << bit; + } +} + +static inline int get_pin_state(int pin) +{ + int reg_addr; + int regidx = pin / 32; + int bit = pin % 32; + + reg_addr = REG_GPLEV_BASE + regidx * 4; + return (*REG(reg_addr) >> bit) & 1; +} + +#define delay_cycles(x) \ + __asm__ __volatile__ ( \ + "1:\n\t" \ + "subs %0, %0, #1\n\t" \ + "bne 1b\n\t" \ + :: "r"(x)) + +static inline void set_pull_updown(int pin, int updown) +{ + int regidx = pin / 32; + int bit = pin % 32; + int clk_addr = REG_GPPUDCLK_BASE + regidx * 4; + + *REG(REG_GPPUD) = updown; + delay_cycles(150); + *REG(clk_addr) = bit; + delay_cycles(150); + *REG(REG_GPPUD) = 0; + *REG(clk_addr) = 0; +} + +static inline void set_pullup(int pin, int en) +{ + set_pull_updown(pin, en ? 2 : 0); +} + +static inline void set_pulldown(int pin, int en) +{ + set_pull_updown(pin, en ? 1 : 0); +}