gpio.c 4.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * arch/arm/mach-ns9xxx/gpio.c
 *
 * Copyright (C) 2006 by Digi International Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */
#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/module.h>

#include <asm/arch-ns9xxx/gpio.h>
#include <asm/arch-ns9xxx/processor.h>
#include <asm/arch-ns9xxx/regs-bbu.h>
19
#include <asm/io.h>
20 21 22 23 24 25 26 27 28 29 30 31 32 33
#include <asm/bug.h>
#include <asm/types.h>
#include <asm/bitops.h>

#if defined(CONFIG_PROCESSOR_NS9360)
#define GPIO_MAX 72
#elif defined(CONFIG_PROCESSOR_NS9750)
#define GPIO_MAX 49
#endif

/* protects BBU_GCONFx and BBU_GCTRLx */
static spinlock_t gpio_lock = __SPIN_LOCK_UNLOCKED(gpio_lock);

/* only access gpiores with atomic ops */
34
static DECLARE_BITMAP(gpiores, GPIO_MAX + 1);
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

static inline int ns9xxx_valid_gpio(unsigned gpio)
{
#if defined(CONFIG_PROCESSOR_NS9360)
	if (processor_is_ns9360())
		return gpio <= 72;
	else
#endif
#if defined(CONFIG_PROCESSOR_NS9750)
	if (processor_is_ns9750())
		return gpio <= 49;
	else
#endif
		BUG();
}

51
static inline void __iomem *ns9xxx_gpio_get_gconfaddr(unsigned gpio)
52 53
{
	if (gpio < 56)
54
		return BBU_GCONFb1(gpio / 8);
55 56 57 58 59
	else
		/*
		 * this could be optimised away on
		 * ns9750 only builds, but it isn't ...
		 */
60
		return BBU_GCONFb2((gpio - 56) / 8);
61 62
}

63
static inline void __iomem *ns9xxx_gpio_get_gctrladdr(unsigned gpio)
64 65
{
	if (gpio < 32)
66
		return BBU_GCTRL1;
67
	else if (gpio < 64)
68
		return BBU_GCTRL2;
69 70
	else
		/* this could be optimised away on ns9750 only builds */
71
		return BBU_GCTRL3;
72 73
}

74
static inline void __iomem *ns9xxx_gpio_get_gstataddr(unsigned gpio)
75 76
{
	if (gpio < 32)
77
		return BBU_GSTAT1;
78
	else if (gpio < 64)
79
		return BBU_GSTAT2;
80 81
	else
		/* this could be optimised away on ns9750 only builds */
82
		return BBU_GSTAT3;
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
}

int gpio_request(unsigned gpio, const char *label)
{
	if (likely(ns9xxx_valid_gpio(gpio)))
		return test_and_set_bit(gpio, gpiores) ? -EBUSY : 0;
	else
		return -EINVAL;
}
EXPORT_SYMBOL(gpio_request);

void gpio_free(unsigned gpio)
{
	clear_bit(gpio, gpiores);
	return;
}
EXPORT_SYMBOL(gpio_free);

/*
 * each gpio can serve for 4 different purposes [0..3].  These are called
 * "functions" and passed in the parameter func.  Functions 0-2 are always some
 * special things, function 3 is GPIO.  If func == 3 dir specifies input or
 * output, and with inv you can enable an inverter (independent of func).
 */
static int __ns9xxx_gpio_configure(unsigned gpio, int dir, int inv, int func)
{
109
	void __iomem *conf = ns9xxx_gpio_get_gconfaddr(gpio);
110 111 112 113 114
	u32 confval;
	unsigned long flags;

	spin_lock_irqsave(&gpio_lock, flags);

115
	confval = __raw_readl(conf);
116 117 118
	REGSETIM_IDX(confval, BBU_GCONFx, DIR, gpio & 7, dir);
	REGSETIM_IDX(confval, BBU_GCONFx, INV, gpio & 7, inv);
	REGSETIM_IDX(confval, BBU_GCONFx, FUNC, gpio & 7, func);
119
	__raw_writel(confval, conf);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

	spin_unlock_irqrestore(&gpio_lock, flags);

	return 0;
}

int ns9xxx_gpio_configure(unsigned gpio, int inv, int func)
{
	if (likely(ns9xxx_valid_gpio(gpio))) {
		if (func == 3) {
			printk(KERN_WARNING "use gpio_direction_input "
					"or gpio_direction_output\n");
			return -EINVAL;
		} else
			return __ns9xxx_gpio_configure(gpio, 0, inv, func);
	} else
		return -EINVAL;
}
EXPORT_SYMBOL(ns9xxx_gpio_configure);

int gpio_direction_input(unsigned gpio)
{
	if (likely(ns9xxx_valid_gpio(gpio))) {
		return __ns9xxx_gpio_configure(gpio, 0, 0, 3);
	} else
		return -EINVAL;
}
EXPORT_SYMBOL(gpio_direction_input);

int gpio_direction_output(unsigned gpio, int value)
{
	if (likely(ns9xxx_valid_gpio(gpio))) {
		gpio_set_value(gpio, value);

		return __ns9xxx_gpio_configure(gpio, 1, 0, 3);
	} else
		return -EINVAL;
}
EXPORT_SYMBOL(gpio_direction_output);

int gpio_get_value(unsigned gpio)
{
162
	void __iomem *stat = ns9xxx_gpio_get_gstataddr(gpio);
163 164
	int ret;

165
	ret = 1 & (__raw_readl(stat) >> (gpio & 31));
166 167 168 169 170 171 172

	return ret;
}
EXPORT_SYMBOL(gpio_get_value);

void gpio_set_value(unsigned gpio, int value)
{
173 174
	void __iomem *ctrl = ns9xxx_gpio_get_gctrladdr(gpio);
	u32 ctrlval;
175 176 177 178
	unsigned long flags;

	spin_lock_irqsave(&gpio_lock, flags);

179 180
	ctrlval = __raw_readl(ctrl);

181
	if (value)
182
		ctrlval |= 1 << (gpio & 31);
183
	else
184 185 186
		ctrlval &= ~(1 << (gpio & 31));

	__raw_writel(ctrlval, ctrl);
187 188 189 190

	spin_unlock_irqrestore(&gpio_lock, flags);
}
EXPORT_SYMBOL(gpio_set_value);