vr41xx_giu.c

来自「linux 内核源代码」· C语言 代码 · 共 679 行 · 第 1/2 页

C
679
字号
/* *  Driver for NEC VR4100 series General-purpose I/O Unit. * *  Copyright (C) 2002 MontaVista Software Inc. *	Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> *  Copyright (C) 2003-2007  Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> * *  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. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <linux/errno.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/spinlock.h>#include <linux/types.h>#include <asm/io.h>#include <asm/vr41xx/giu.h>#include <asm/vr41xx/irq.h>#include <asm/vr41xx/vr41xx.h>MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>");MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver");MODULE_LICENSE("GPL");static int major;	/* default is dynamic major device number */module_param(major, int, 0);MODULE_PARM_DESC(major, "Major device number");#define GIUIOSELL	0x00#define GIUIOSELH	0x02#define GIUPIODL	0x04#define GIUPIODH	0x06#define GIUINTSTATL	0x08#define GIUINTSTATH	0x0a#define GIUINTENL	0x0c#define GIUINTENH	0x0e#define GIUINTTYPL	0x10#define GIUINTTYPH	0x12#define GIUINTALSELL	0x14#define GIUINTALSELH	0x16#define GIUINTHTSELL	0x18#define GIUINTHTSELH	0x1a#define GIUPODATL	0x1c#define GIUPODATEN	0x1c#define GIUPODATH	0x1e #define PIOEN0		0x0100 #define PIOEN1		0x0200#define GIUPODAT	0x1e#define GIUFEDGEINHL	0x20#define GIUFEDGEINHH	0x22#define GIUREDGEINHL	0x24#define GIUREDGEINHH	0x26#define GIUUSEUPDN	0x1e0#define GIUTERMUPDN	0x1e2#define GPIO_HAS_PULLUPDOWN_IO		0x0001#define GPIO_HAS_OUTPUT_ENABLE		0x0002#define GPIO_HAS_INTERRUPT_EDGE_SELECT	0x0100static spinlock_t giu_lock;static unsigned long giu_flags;static unsigned int giu_nr_pins;static void __iomem *giu_base;#define giu_read(offset)		readw(giu_base + (offset))#define giu_write(offset, value)	writew((value), giu_base + (offset))#define GPIO_PIN_OF_IRQ(irq)	((irq) - GIU_IRQ_BASE)#define GIUINT_HIGH_OFFSET	16#define GIUINT_HIGH_MAX		32static inline uint16_t giu_set(uint16_t offset, uint16_t set){	uint16_t data;	data = giu_read(offset);	data |= set;	giu_write(offset, data);	return data;}static inline uint16_t giu_clear(uint16_t offset, uint16_t clear){	uint16_t data;	data = giu_read(offset);	data &= ~clear;	giu_write(offset, data);	return data;}static void ack_giuint_low(unsigned int irq){	giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq));}static void mask_giuint_low(unsigned int irq){	giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));}static void mask_ack_giuint_low(unsigned int irq){	unsigned int pin;	pin = GPIO_PIN_OF_IRQ(irq);	giu_clear(GIUINTENL, 1 << pin);	giu_write(GIUINTSTATL, 1 << pin);}static void unmask_giuint_low(unsigned int irq){	giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));}static struct irq_chip giuint_low_irq_chip = {	.name		= "GIUINTL",	.ack		= ack_giuint_low,	.mask		= mask_giuint_low,	.mask_ack	= mask_ack_giuint_low,	.unmask		= unmask_giuint_low,};static void ack_giuint_high(unsigned int irq){	giu_write(GIUINTSTATH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));}static void mask_giuint_high(unsigned int irq){	giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));}static void mask_ack_giuint_high(unsigned int irq){	unsigned int pin;	pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET;	giu_clear(GIUINTENH, 1 << pin);	giu_write(GIUINTSTATH, 1 << pin);}static void unmask_giuint_high(unsigned int irq){	giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));}static struct irq_chip giuint_high_irq_chip = {	.name		= "GIUINTH",	.ack		= ack_giuint_high,	.mask		= mask_giuint_high,	.mask_ack	= mask_ack_giuint_high,	.unmask		= unmask_giuint_high,};static int giu_get_irq(unsigned int irq){	uint16_t pendl, pendh, maskl, maskh;	int i;	pendl = giu_read(GIUINTSTATL);	pendh = giu_read(GIUINTSTATH);	maskl = giu_read(GIUINTENL);	maskh = giu_read(GIUINTENH);	maskl &= pendl;	maskh &= pendh;	if (maskl) {		for (i = 0; i < 16; i++) {			if (maskl & (1 << i))				return GIU_IRQ(i);		}	} else if (maskh) {		for (i = 0; i < 16; i++) {			if (maskh & (1 << i))				return GIU_IRQ(i + GIUINT_HIGH_OFFSET);		}	}	printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n",	       maskl, pendl, maskh, pendh);	atomic_inc(&irq_err_count);	return -EINVAL;}void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_t signal){	uint16_t mask;	if (pin < GIUINT_HIGH_OFFSET) {		mask = 1 << pin;		if (trigger != IRQ_TRIGGER_LEVEL) {        		giu_set(GIUINTTYPL, mask);			if (signal == IRQ_SIGNAL_HOLD)				giu_set(GIUINTHTSELL, mask);			else				giu_clear(GIUINTHTSELL, mask);			if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {				switch (trigger) {				case IRQ_TRIGGER_EDGE_FALLING:					giu_set(GIUFEDGEINHL, mask);					giu_clear(GIUREDGEINHL, mask);					break;				case IRQ_TRIGGER_EDGE_RISING:					giu_clear(GIUFEDGEINHL, mask);					giu_set(GIUREDGEINHL, mask);					break;				default:					giu_set(GIUFEDGEINHL, mask);					giu_set(GIUREDGEINHL, mask);					break;				}			}			set_irq_chip_and_handler(GIU_IRQ(pin),			                         &giuint_low_irq_chip,			                         handle_edge_irq);		} else {			giu_clear(GIUINTTYPL, mask);			giu_clear(GIUINTHTSELL, mask);			set_irq_chip_and_handler(GIU_IRQ(pin),			                         &giuint_low_irq_chip,			                         handle_level_irq);		}		giu_write(GIUINTSTATL, mask);	} else if (pin < GIUINT_HIGH_MAX) {		mask = 1 << (pin - GIUINT_HIGH_OFFSET);		if (trigger != IRQ_TRIGGER_LEVEL) {			giu_set(GIUINTTYPH, mask);			if (signal == IRQ_SIGNAL_HOLD)				giu_set(GIUINTHTSELH, mask);			else				giu_clear(GIUINTHTSELH, mask);			if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {				switch (trigger) {				case IRQ_TRIGGER_EDGE_FALLING:					giu_set(GIUFEDGEINHH, mask);					giu_clear(GIUREDGEINHH, mask);					break;				case IRQ_TRIGGER_EDGE_RISING:					giu_clear(GIUFEDGEINHH, mask);					giu_set(GIUREDGEINHH, mask);					break;				default:					giu_set(GIUFEDGEINHH, mask);					giu_set(GIUREDGEINHH, mask);					break;				}			}			set_irq_chip_and_handler(GIU_IRQ(pin),			                         &giuint_high_irq_chip,			                         handle_edge_irq);		} else {			giu_clear(GIUINTTYPH, mask);			giu_clear(GIUINTHTSELH, mask);			set_irq_chip_and_handler(GIU_IRQ(pin),			                         &giuint_high_irq_chip,			                         handle_level_irq);		}		giu_write(GIUINTSTATH, mask);	}}EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger);void vr41xx_set_irq_level(unsigned int pin, irq_level_t level){	uint16_t mask;	if (pin < GIUINT_HIGH_OFFSET) {		mask = 1 << pin;		if (level == IRQ_LEVEL_HIGH)			giu_set(GIUINTALSELL, mask);		else			giu_clear(GIUINTALSELL, mask);		giu_write(GIUINTSTATL, mask);	} else if (pin < GIUINT_HIGH_MAX) {		mask = 1 << (pin - GIUINT_HIGH_OFFSET);		if (level == IRQ_LEVEL_HIGH)			giu_set(GIUINTALSELH, mask);		else			giu_clear(GIUINTALSELH, mask);		giu_write(GIUINTSTATH, mask);	}}EXPORT_SYMBOL_GPL(vr41xx_set_irq_level);gpio_data_t vr41xx_gpio_get_pin(unsigned int pin){	uint16_t reg, mask;	if (pin >= giu_nr_pins)		return GPIO_DATA_INVAL;	if (pin < 16) {		reg = giu_read(GIUPIODL);		mask = (uint16_t)1 << pin;	} else if (pin < 32) {		reg = giu_read(GIUPIODH);		mask = (uint16_t)1 << (pin - 16);	} else if (pin < 48) {		reg = giu_read(GIUPODATL);		mask = (uint16_t)1 << (pin - 32);	} else {		reg = giu_read(GIUPODATH);		mask = (uint16_t)1 << (pin - 48);	}	if (reg & mask)		return GPIO_DATA_HIGH;	return GPIO_DATA_LOW;}EXPORT_SYMBOL_GPL(vr41xx_gpio_get_pin);int vr41xx_gpio_set_pin(unsigned int pin, gpio_data_t data){	uint16_t offset, mask, reg;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?