nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: /* raspberry pi 2 */ nuclear@0: #define PERI_ADDR 0x3f000000 nuclear@0: #define PERI_SIZE 0x1000000 nuclear@0: nuclear@0: #define REG_GPIO_BASE 0x200000 nuclear@0: #define REG_GPFSEL_BASE 0x200000 nuclear@0: #define REG_GPSET_BASE 0x20001c nuclear@0: #define REG_GPCLR_BASE 0x200028 nuclear@0: #define REG_GPLEV_BASE 0x200034 nuclear@0: #define REG_GPPUD 0x200094 nuclear@0: #define REG_GPPUDCLK_BASE 0x200098 nuclear@0: nuclear@0: #define FSEL_BITS 3 nuclear@0: #define FSEL_INPUT 0 nuclear@0: #define FSEL_OUTPUT 1 nuclear@0: nuclear@0: #define FSEL_SHIFT(i) (((i) % 10) * 3) nuclear@0: #define FSEL_MASK(i) (3 << FSEL_SHIFT(i)) nuclear@0: nuclear@0: #define REG(r) (volatile uint32_t*)((unsigned char*)iomem + (r)) nuclear@0: nuclear@0: static int gpio_init(void); nuclear@0: static void gpio_exit(void); nuclear@0: static int gpio_open(struct inode *inode, struct file *file); nuclear@0: static int gpio_close(struct inode *inode, struct file *file); nuclear@0: static ssize_t gpio_write(struct file *file, const char *buf, size_t sz, loff_t *offset); nuclear@0: nuclear@0: static inline void set_pin_func(int pin, int func); nuclear@0: static inline void set_pin_state(int pin, int s); nuclear@0: static inline int get_pin_state(int pin); nuclear@0: static inline void set_pullup(int pin, int en); nuclear@0: static inline void set_pulldown(int pin, int en); nuclear@0: nuclear@0: MODULE_LICENSE("GPL"); nuclear@0: MODULE_AUTHOR("John Tsiombikas"); nuclear@0: MODULE_DESCRIPTION("gpio module"); nuclear@0: nuclear@0: module_init(gpio_init); nuclear@0: module_exit(gpio_exit); nuclear@0: nuclear@0: static int major; nuclear@0: static void *iomem; nuclear@0: nuclear@0: static struct file_operations ops = { nuclear@0: .open = gpio_open, nuclear@0: .release = gpio_close, nuclear@0: .write = gpio_write nuclear@0: }; nuclear@0: nuclear@0: static int gpio_init(void) nuclear@0: { nuclear@0: if((major = register_chrdev(0, "gpio", &ops)) < 0) { nuclear@0: printk("failed to register character device gpio (err: %d)\n", major); nuclear@0: return -1; nuclear@0: } nuclear@0: printk("gpio module registered (device: %d)\n", major); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static void gpio_exit(void) nuclear@0: { nuclear@0: unregister_chrdev(major, "gpio"); nuclear@0: printk("gpio exit\n"); nuclear@0: } nuclear@0: nuclear@0: static int gpio_open(struct inode *inode, struct file *file) nuclear@0: { nuclear@0: printk("gpio_open called\n"); nuclear@0: if(iomem) { nuclear@0: return -EBUSY; nuclear@0: } nuclear@0: nuclear@0: if(!(iomem = ioremap(PERI_ADDR, PERI_SIZE))) { nuclear@0: printk("gpio: failed to map peripherals\n"); nuclear@0: return -EBUSY; nuclear@0: } nuclear@0: set_pin_func(4, FSEL_OUTPUT); nuclear@0: set_pin_state(4, 0); nuclear@0: nuclear@0: try_module_get(THIS_MODULE); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static int gpio_close(struct inode *inode, struct file *file) nuclear@0: { nuclear@0: if(!iomem) { nuclear@0: return -EBADF; nuclear@0: } nuclear@0: nuclear@0: iounmap(iomem); nuclear@0: iomem = 0; nuclear@0: nuclear@0: module_put(THIS_MODULE); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static ssize_t gpio_write(struct file *file, const char *buf, size_t sz, loff_t *offset) nuclear@0: { nuclear@0: unsigned char val; nuclear@0: nuclear@0: printk("gpio_write(%d) called\n", sz); nuclear@0: nuclear@0: if(sz < 1) { nuclear@0: return 0; nuclear@0: } nuclear@0: if(copy_from_user(&val, (unsigned char*)buf + sz - 1, 1) != 0) { nuclear@0: return -EFAULT; nuclear@0: } nuclear@0: nuclear@0: set_pin_state(4, val ? 1 : 0); nuclear@0: return sz; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: static inline void set_pin_func(int pin, int func) nuclear@0: { nuclear@0: int reg_addr, val; nuclear@0: reg_addr = REG_GPFSEL_BASE + (pin / 10) * 4; nuclear@0: val = *REG(reg_addr); nuclear@0: *REG(reg_addr) = (val & FSEL_MASK(pin)) | (func << FSEL_SHIFT(pin)); nuclear@0: } nuclear@0: nuclear@0: static inline void set_pin_state(int pin, int s) nuclear@0: { nuclear@0: int reg_addr; nuclear@0: int regidx = pin / 32; nuclear@0: int bit = pin % 32; nuclear@0: nuclear@0: if(s) { nuclear@0: reg_addr = REG_GPSET_BASE + regidx * 4; nuclear@0: *REG(reg_addr) |= 1 << bit; nuclear@0: } else { nuclear@0: reg_addr = REG_GPCLR_BASE + regidx * 4; nuclear@0: *REG(reg_addr) |= 1 << bit; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static inline int get_pin_state(int pin) nuclear@0: { nuclear@0: int reg_addr; nuclear@0: int regidx = pin / 32; nuclear@0: int bit = pin % 32; nuclear@0: nuclear@0: reg_addr = REG_GPLEV_BASE + regidx * 4; nuclear@0: return (*REG(reg_addr) >> bit) & 1; nuclear@0: } nuclear@0: nuclear@0: #define delay_cycles(x) \ nuclear@0: __asm__ __volatile__ ( \ nuclear@0: "1:\n\t" \ nuclear@0: "subs %0, %0, #1\n\t" \ nuclear@0: "bne 1b\n\t" \ nuclear@0: :: "r"(x)) nuclear@0: nuclear@0: static inline void set_pull_updown(int pin, int updown) nuclear@0: { nuclear@0: int regidx = pin / 32; nuclear@0: int bit = pin % 32; nuclear@0: int clk_addr = REG_GPPUDCLK_BASE + regidx * 4; nuclear@0: nuclear@0: *REG(REG_GPPUD) = updown; nuclear@0: delay_cycles(150); nuclear@0: *REG(clk_addr) = bit; nuclear@0: delay_cycles(150); nuclear@0: *REG(REG_GPPUD) = 0; nuclear@0: *REG(clk_addr) = 0; nuclear@0: } nuclear@0: nuclear@0: static inline void set_pullup(int pin, int en) nuclear@0: { nuclear@0: set_pull_updown(pin, en ? 2 : 0); nuclear@0: } nuclear@0: nuclear@0: static inline void set_pulldown(int pin, int en) nuclear@0: { nuclear@0: set_pull_updown(pin, en ? 1 : 0); nuclear@0: }