cregit-Linux how code gets into the kernel

Release 4.14 drivers/gpio/gpio-vr41xx.c

Directory: drivers/gpio
/*
 *  Driver for NEC VR4100 series General-purpose I/O Unit.
 *
 *  Copyright (C) 2002 MontaVista Software Inc.
 *      Author: Yoichi Yuasa <source@mvista.com>
 *  Copyright (C) 2003-2009  Yoichi Yuasa <yuasa@linux-mips.org>
 *
 *  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/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.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/vr41xx/giu.h>
#include <asm/vr41xx/irq.h>
#include <asm/vr41xx/vr41xx.h>

MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver");
MODULE_LICENSE("GPL");


#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	0x0100

enum {
	
GPIO_INPUT,
	
GPIO_OUTPUT,
};

static DEFINE_SPINLOCK(giu_lock);

static unsigned long giu_flags;


static void __iomem *giu_base;

static struct gpio_chip vr41xx_gpio_chip;


#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		32


static inline u16 giu_set(u16 offset, u16 set) { u16 data; data = giu_read(offset); data |= set; giu_write(offset, data); return data; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa37100.00%1100.00%
Total37100.00%1100.00%


static inline u16 giu_clear(u16 offset, u16 clear) { u16 data; data = giu_read(offset); data &= ~clear; giu_write(offset, data); return data; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa38100.00%1100.00%
Total38100.00%1100.00%


static void ack_giuint_low(struct irq_data *d) { giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq)); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa1976.00%150.00%
Lennert Buytenhek624.00%150.00%
Total25100.00%2100.00%


static void mask_giuint_low(struct irq_data *d) { giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa1976.00%150.00%
Lennert Buytenhek624.00%150.00%
Total25100.00%2100.00%


static void mask_ack_giuint_low(struct irq_data *d) { unsigned int pin; pin = GPIO_PIN_OF_IRQ(d->irq); giu_clear(GIUINTENL, 1 << pin); giu_write(GIUINTSTATL, 1 << pin); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa3685.71%150.00%
Lennert Buytenhek614.29%150.00%
Total42100.00%2100.00%


static void unmask_giuint_low(struct irq_data *d) { giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa1976.00%150.00%
Lennert Buytenhek624.00%150.00%
Total25100.00%2100.00%


static unsigned int startup_giuint(struct irq_data *data) { if (gpiochip_lock_as_irq(&vr41xx_gpio_chip, data->hwirq)) dev_err(vr41xx_gpio_chip.parent, "unable to lock HW IRQ %lu for IRQ\n", data->hwirq); /* Satisfy the .enable semantics by unmasking the line */ unmask_giuint_low(data); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij4597.83%266.67%
Alexandre Courbot12.17%133.33%
Total46100.00%3100.00%


static void shutdown_giuint(struct irq_data *data) { mask_giuint_low(data); gpiochip_unlock_as_irq(&vr41xx_gpio_chip, data->hwirq); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij2596.15%150.00%
Alexandre Courbot13.85%150.00%
Total26100.00%2100.00%

static struct irq_chip giuint_low_irq_chip = { .name = "GIUINTL", .irq_ack = ack_giuint_low, .irq_mask = mask_giuint_low, .irq_mask_ack = mask_ack_giuint_low, .irq_unmask = unmask_giuint_low, .irq_startup = startup_giuint, .irq_shutdown = shutdown_giuint, };
static void ack_giuint_high(struct irq_data *d) { giu_write(GIUINTSTATH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa2379.31%150.00%
Lennert Buytenhek620.69%150.00%
Total29100.00%2100.00%


static void mask_giuint_high(struct irq_data *d) { giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa2379.31%150.00%
Lennert Buytenhek620.69%150.00%
Total29100.00%2100.00%


static void mask_ack_giuint_high(struct irq_data *d) { unsigned int pin; pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET; giu_clear(GIUINTENH, 1 << pin); giu_write(GIUINTSTATH, 1 << pin); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa3886.36%150.00%
Lennert Buytenhek613.64%150.00%
Total44100.00%2100.00%


static void unmask_giuint_high(struct irq_data *d) { giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa2379.31%150.00%
Lennert Buytenhek620.69%150.00%
Total29100.00%2100.00%

static struct irq_chip giuint_high_irq_chip = { .name = "GIUINTH", .irq_ack = ack_giuint_high, .irq_mask = mask_giuint_high, .irq_mask_ack = mask_ack_giuint_high, .irq_unmask = unmask_giuint_high, };
static int giu_get_irq(unsigned int irq) { u16 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; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa159100.00%1100.00%
Total159100.00%1100.00%


void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_t signal) { u16 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; } } irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_low_irq_chip, handle_edge_irq); } else { giu_clear(GIUINTTYPL, mask); giu_clear(GIUINTHTSELL, mask); irq_set_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; } } irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_high_irq_chip, handle_edge_irq); } else { giu_clear(GIUINTTYPH, mask); giu_clear(GIUINTHTSELH, mask); irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_high_irq_chip, handle_level_irq); } giu_write(GIUINTSTATH, mask); } }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa35198.87%150.00%
Thomas Gleixner41.13%150.00%
Total355100.00%2100.00%

EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger);
void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) { u16 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); } }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa104100.00%1100.00%
Total104100.00%1100.00%

EXPORT_SYMBOL_GPL(vr41xx_set_irq_level);
static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) { u16 offset, mask, reg; unsigned long flags; if (pin >= chip->ngpio) return -EINVAL; if (pin < 16) { offset = GIUIOSELL; mask = 1 << pin; } else if (pin < 32) { offset = GIUIOSELH; mask = 1 << (pin - 16); } else { if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { offset = GIUPODATEN; mask = 1 << (pin - 32); } else { switch (pin) { case 48: offset = GIUPODATH; mask = PIOEN0; break; case 49: offset = GIUPODATH; mask = PIOEN1; break; default: return -EINVAL; } } } spin_lock_irqsave(&giu_lock, flags); reg = giu_read(offset); if (dir == GPIO_OUTPUT) reg |= mask; else reg &= ~mask; giu_write(offset, reg); spin_unlock_irqrestore(&giu_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa193100.00%1100.00%
Total193100.00%1100.00%


int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull) { u16 reg, mask; unsigned long flags; if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO) return -EPERM; if (pin >= 15) return -EINVAL; mask = 1 << pin; spin_lock_irqsave(&giu_lock, flags); if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) { reg = giu_read(GIUTERMUPDN); if (pull == GPIO_PULL_UP) reg |= mask; else reg &= ~mask; giu_write(GIUTERMUPDN, reg); reg = giu_read(GIUUSEUPDN); reg |= mask; giu_write(GIUUSEUPDN, reg); } else { reg = giu_read(GIUUSEUPDN); reg &= ~mask; giu_write(GIUUSEUPDN, reg); } spin_unlock_irqrestore(&giu_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa152100.00%1100.00%
Total152100.00%1100.00%

EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown);
static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) { u16 reg, mask; if (pin >= chip->ngpio) return -EINVAL; if (pin < 16) { reg = giu_read(GIUPIODL); mask = 1 << pin; } else if (pin < 32) { reg = giu_read(GIUPIODH); mask = 1 << (pin - 16); } else if (pin < 48) { reg = giu_read(GIUPODATL); mask = 1 << (pin - 32); } else { reg = giu_read(GIUPODATH); mask = 1 << (pin - 48); } if (reg & mask) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa136100.00%1100.00%
Total136100.00%1100.00%


static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin, int value) { u16 offset, mask, reg; unsigned long flags; if (pin >= chip->ngpio) return; if (pin < 16) { offset = GIUPIODL; mask = 1 << pin; } else if (pin < 32) { offset = GIUPIODH; mask = 1 << (pin - 16); } else if (pin < 48) { offset = GIUPODATL; mask = 1 << (pin - 32); } else { offset = GIUPODATH; mask = 1 << (pin - 48); } spin_lock_irqsave(&giu_lock, flags); reg = giu_read(offset); if (value) reg |= mask; else reg &= ~mask; giu_write(offset, reg); spin_unlock_irqrestore(&giu_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa162100.00%1100.00%
Total162100.00%1100.00%


static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { return giu_set_direction(chip, offset, GPIO_INPUT); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa24100.00%1100.00%
Total24100.00%1100.00%


static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { vr41xx_gpio_set(chip, offset, value); return giu_set_direction(chip, offset, GPIO_OUTPUT); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa36100.00%1100.00%
Total36100.00%1100.00%


static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { if (offset >= chip->ngpio) return -EINVAL; return GIU_IRQ_BASE + offset; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa31100.00%1100.00%
Total31100.00%1100.00%

static struct gpio_chip vr41xx_gpio_chip = { .label = "vr41xx", .owner = THIS_MODULE, .direction_input = vr41xx_gpio_direction_input, .get = vr41xx_gpio_get, .direction_output = vr41xx_gpio_direction_output, .set = vr41xx_gpio_set, .to_irq = vr41xx_gpio_to_irq, };
static int giu_probe(struct platform_device *pdev) { struct resource *res; unsigned int trigger, i, pin; struct irq_chip *chip; int irq, ret; switch (pdev->id) { case GPIO_50PINS_PULLUPDOWN: giu_flags = GPIO_HAS_PULLUPDOWN_IO; vr41xx_gpio_chip.ngpio = 50; break; case GPIO_36PINS: vr41xx_gpio_chip.ngpio = 36; break; case GPIO_48PINS_EDGE_SELECT: giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; vr41xx_gpio_chip.ngpio = 48; break; default: dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id); return -ENODEV; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -EBUSY; giu_base = ioremap(res->start, resource_size(res)); if (!giu_base) return -ENOMEM; vr41xx_gpio_chip.parent = &pdev->dev; ret = gpiochip_add_data(&vr41xx_gpio_chip, NULL); if (!ret) { iounmap(giu_base); return -ENODEV; } giu_write(GIUINTENL, 0); giu_write(GIUINTENH, 0); trigger = giu_read(GIUINTTYPH) << 16; trigger |= giu_read(GIUINTTYPL); for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { pin = GPIO_PIN_OF_IRQ(i); if (pin < GIUINT_HIGH_OFFSET) chip = &giuint_low_irq_chip; else chip = &giuint_high_irq_chip; if (trigger & (1 << pin)) irq_set_chip_and_handler(i, chip, handle_edge_irq); else irq_set_chip_and_handler(i, chip, handle_level_irq); } irq = platform_get_irq(pdev, 0); if (irq < 0 || irq >= nr_irqs) return -EBUSY; return cascade_irq(irq, giu_get_irq); }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa27991.18%116.67%
Linus Walleij227.19%350.00%
Joe Perches30.98%116.67%
Thomas Gleixner20.65%116.67%
Total306100.00%6100.00%


static int giu_remove(struct platform_device *pdev) { if (giu_base) { iounmap(giu_base); giu_base = NULL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa29100.00%1100.00%
Total29100.00%1100.00%

static struct platform_driver giu_device_driver = { .probe = giu_probe, .remove = giu_remove, .driver = { .name = "GIU", }, }; module_platform_driver(giu_device_driver);

Overall Contributors

PersonTokensPropCommitsCommitProp
Yoichi Yuasa230692.91%218.18%
Linus Walleij1074.31%436.36%
Lennert Buytenhek562.26%19.09%
Thomas Gleixner60.24%19.09%
Joe Perches30.12%19.09%
Alexandre Courbot20.08%19.09%
Mark Brown20.08%19.09%
Total2482100.00%11100.00%
Directory: drivers/gpio
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.