148 lines · 3.1 KB · C
RawDownload
/* * 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/driver.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 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 = gpiochip_get_data(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 =		container_of(mm_gc, struct 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_data(np, mm_gc, u8_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);	}}