/* * Simple Memory-Mapped GPIOs * * Copyright (c) MontaVista Software, Inc. 2008. * * Author: Anton Vorontsov <avorontsov@ru.mvista.com> * * 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/init.h> #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/types.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/slab.h> #include <asm/prom.h> #include "simple_gpio.h" struct u8_gpio_chip { struct of_mm_gpio_chip mm_gc; spinlock_t lock; /* shadowed data register to clear/set bits safely */ u8 data; }; static struct u8_gpio_chip *to_u8_gpio_chip(struct of_mm_gpio_chip *mm_gc) { return container_of(mm_gc, struct u8_gpio_chip, mm_gc); } static u8 u8_pin2mask(unsigned int pin) { return 1 << (8 - 1 - pin); } static int u8_gpio_get(struct gpio_chip *gc, unsigned int gpio) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); return in_8(mm_gc->regs) & u8_pin2mask(gpio); } static void u8_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc); unsigned long flags; spin_lock_irqsave(&u8_gc->lock, flags); if (val) u8_gc->data |= u8_pin2mask(gpio); else u8_gc->data &= ~u8_pin2mask(gpio); out_8(mm_gc->regs, u8_gc->data); spin_unlock_irqrestore(&u8_gc->lock, flags); } static int u8_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) { return 0; } static int u8_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { u8_gpio_set(gc, gpio, val); return 0; } static void u8_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) { struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc); u8_gc->data = in_8(mm_gc->regs); } static int __init u8_simple_gpiochip_add(struct device_node *np) { int ret; struct u8_gpio_chip *u8_gc; struct of_mm_gpio_chip *mm_gc; struct gpio_chip *gc; u8_gc = kzalloc(sizeof(*u8_gc), GFP_KERNEL); if (!u8_gc) return -ENOMEM; spin_lock_init(&u8_gc->lock); mm_gc = &u8_gc->mm_gc; gc = &mm_gc->gc; mm_gc->save_regs = u8_gpio_save_regs; gc->ngpio = 8; gc->direction_input = u8_gpio_dir_in; gc->direction_output = u8_gpio_dir_out; gc->get = u8_gpio_get; gc->set = u8_gpio_set; ret = of_mm_gpiochip_add(np, mm_gc); if (ret) goto err; return 0; err: kfree(u8_gc); return ret; } void __init simple_gpiochip_init(const char *compatible) { struct device_node *np; for_each_compatible_node(np, NULL, compatible) { int ret; struct resource r; ret = of_address_to_resource(np, 0, &r); if (ret) goto err; switch (resource_size(&r)) { case 1: ret = u8_simple_gpiochip_add(np); if (ret) goto err; break; default: /* * Whenever you need support for GPIO bank width > 1, * please just turn u8_ code into huge macros, and * construct needed uX_ code with it. */ ret = -ENOSYS; goto err; } continue; err: pr_err("%s: registration failed, status %d\n", np->full_name, ret); } }