pinmux.c 8.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * Allocator for I/O pins. All pins are allocated to GPIO at bootup.
 * Unassigned pins and GPIO pins can be allocated to a fixed interface
 * or the I/O processor instead.
 *
 * Copyright (c) 2004-2007 Axis Communications AB.
 */

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <pinmux.h>
#include <hwregs/pinmux_defs.h>

#undef DEBUG

#define PORT_PINS 18
#define PORTS 4

static char pins[PORTS][PORT_PINS];
static DEFINE_SPINLOCK(pinmux_lock);

static void crisv32_pinmux_set(int port);

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
static int __crisv32_pinmux_alloc(int port, int first_pin, int last_pin,
				 enum pin_mode mode)
{
	int i;

	for (i = first_pin; i <= last_pin; i++) {
		if ((pins[port][i] != pinmux_none)
		    && (pins[port][i] != pinmux_gpio)
		    && (pins[port][i] != mode)) {
#ifdef DEBUG
			panic("Pinmux alloc failed!\n");
#endif
			return -EPERM;
		}
	}

	for (i = first_pin; i <= last_pin; i++)
		pins[port][i] = mode;

	crisv32_pinmux_set(port);
}

static int crisv32_pinmux_init(void)
52 53 54 55 56 57 58 59 60 61
{
	static int initialized;

	if (!initialized) {
		reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa);
		initialized = 1;
		REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0);
		pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 =
		    pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes;
		REG_WR(pinmux, regi_pinmux, rw_pa, pa);
62 63 64 65
		__crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio);
		__crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio);
		__crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio);
		__crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio);
66 67 68 69 70
	}

	return 0;
}

71 72
int crisv32_pinmux_alloc(int port, int first_pin, int last_pin,
			 enum pin_mode mode)
73 74
{
	unsigned long flags;
75
	int ret;
76 77 78

	crisv32_pinmux_init();

R
Roel Kluin 已提交
79
	if (port > PORTS || port < 0)
80 81 82 83
		return -EINVAL;

	spin_lock_irqsave(&pinmux_lock, flags);

84
	ret = __crisv32_pinmux_alloc(port, first_pin, last_pin, mode);
85 86 87

	spin_unlock_irqrestore(&pinmux_lock, flags);

88
	return ret;
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
}

int crisv32_pinmux_alloc_fixed(enum fixed_function function)
{
	int ret = -EINVAL;
	char saved[sizeof pins];
	unsigned long flags;

	spin_lock_irqsave(&pinmux_lock, flags);

	/* Save internal data for recovery */
	memcpy(saved, pins, sizeof pins);

	crisv32_pinmux_init();	/* Must be done before we read rw_hwprot */

	reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);

	switch (function) {
	case pinmux_ser1:
108
		ret = __crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed);
109 110 111
		hwprot.ser1 = regk_pinmux_yes;
		break;
	case pinmux_ser2:
112
		ret = __crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed);
113 114 115
		hwprot.ser2 = regk_pinmux_yes;
		break;
	case pinmux_ser3:
116
		ret = __crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed);
117 118 119
		hwprot.ser3 = regk_pinmux_yes;
		break;
	case pinmux_sser0:
120 121
		ret = __crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed);
		ret |= __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
122 123 124
		hwprot.sser0 = regk_pinmux_yes;
		break;
	case pinmux_sser1:
125
		ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
126 127 128
		hwprot.sser1 = regk_pinmux_yes;
		break;
	case pinmux_ata0:
129 130
		ret = __crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed);
		ret |= __crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed);
131 132 133
		hwprot.ata0 = regk_pinmux_yes;
		break;
	case pinmux_ata1:
134 135
		ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
		ret |= __crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed);
136 137 138
		hwprot.ata1 = regk_pinmux_yes;
		break;
	case pinmux_ata2:
139 140
		ret = __crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed);
		ret |= __crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed);
141 142 143
		hwprot.ata2 = regk_pinmux_yes;
		break;
	case pinmux_ata3:
144 145
		ret = __crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed);
		ret |= __crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed);
146 147 148
		hwprot.ata2 = regk_pinmux_yes;
		break;
	case pinmux_ata:
149 150
		ret = __crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed);
		ret |= __crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed);
151 152 153
		hwprot.ata = regk_pinmux_yes;
		break;
	case pinmux_eth1:
154
		ret = __crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed);
155 156 157 158
		hwprot.eth1 = regk_pinmux_yes;
		hwprot.eth1_mgm = regk_pinmux_yes;
		break;
	case pinmux_timer:
159
		ret = __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
		hwprot.timer = regk_pinmux_yes;
		spin_unlock_irqrestore(&pinmux_lock, flags);
		return ret;
	}

	if (!ret)
		REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
	else
		memcpy(pins, saved, sizeof pins);

	spin_unlock_irqrestore(&pinmux_lock, flags);

	return ret;
}

void crisv32_pinmux_set(int port)
{
	int i;
	int gpio_val = 0;
	int iop_val = 0;

	for (i = 0; i < PORT_PINS; i++) {
		if (pins[port][i] == pinmux_gpio)
			gpio_val |= (1 << i);
		else if (pins[port][i] == pinmux_iop)
			iop_val |= (1 << i);
	}

	REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8 * port,
		  gpio_val);
	REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8 * port,
		  iop_val);

#ifdef DEBUG
	crisv32_pinmux_dump();
#endif
}

198
static int __crisv32_pinmux_dealloc(int port, int first_pin, int last_pin)
199 200
{
	int i;
201 202 203 204 205 206 207 208 209 210

	for (i = first_pin; i <= last_pin; i++)
		pins[port][i] = pinmux_none;

	crisv32_pinmux_set(port);
	return 0;
}

int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin)
{
211 212 213 214
	unsigned long flags;

	crisv32_pinmux_init();

R
Roel Kluin 已提交
215
	if (port > PORTS || port < 0)
216 217 218
		return -EINVAL;

	spin_lock_irqsave(&pinmux_lock, flags);
219
	__crisv32_pinmux_dealloc(port, first_pin, last_pin);
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	spin_unlock_irqrestore(&pinmux_lock, flags);

	return 0;
}

int crisv32_pinmux_dealloc_fixed(enum fixed_function function)
{
	int ret = -EINVAL;
	char saved[sizeof pins];
	unsigned long flags;

	spin_lock_irqsave(&pinmux_lock, flags);

	/* Save internal data for recovery */
	memcpy(saved, pins, sizeof pins);

	crisv32_pinmux_init();	/* Must be done before we read rw_hwprot */

	reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);

	switch (function) {
	case pinmux_ser1:
242
		ret = __crisv32_pinmux_dealloc(PORT_C, 4, 7);
243 244 245
		hwprot.ser1 = regk_pinmux_no;
		break;
	case pinmux_ser2:
246
		ret = __crisv32_pinmux_dealloc(PORT_C, 8, 11);
247 248 249
		hwprot.ser2 = regk_pinmux_no;
		break;
	case pinmux_ser3:
250
		ret = __crisv32_pinmux_dealloc(PORT_C, 12, 15);
251 252 253
		hwprot.ser3 = regk_pinmux_no;
		break;
	case pinmux_sser0:
254 255
		ret = __crisv32_pinmux_dealloc(PORT_C, 0, 3);
		ret |= __crisv32_pinmux_dealloc(PORT_C, 16, 16);
256 257 258
		hwprot.sser0 = regk_pinmux_no;
		break;
	case pinmux_sser1:
259
		ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4);
260 261 262
		hwprot.sser1 = regk_pinmux_no;
		break;
	case pinmux_ata0:
263 264
		ret = __crisv32_pinmux_dealloc(PORT_D, 5, 7);
		ret |= __crisv32_pinmux_dealloc(PORT_D, 15, 17);
265 266 267
		hwprot.ata0 = regk_pinmux_no;
		break;
	case pinmux_ata1:
268 269
		ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4);
		ret |= __crisv32_pinmux_dealloc(PORT_E, 17, 17);
270 271 272
		hwprot.ata1 = regk_pinmux_no;
		break;
	case pinmux_ata2:
273 274
		ret = __crisv32_pinmux_dealloc(PORT_C, 11, 15);
		ret |= __crisv32_pinmux_dealloc(PORT_E, 3, 3);
275 276 277
		hwprot.ata2 = regk_pinmux_no;
		break;
	case pinmux_ata3:
278 279
		ret = __crisv32_pinmux_dealloc(PORT_C, 8, 10);
		ret |= __crisv32_pinmux_dealloc(PORT_C, 0, 2);
280 281 282
		hwprot.ata2 = regk_pinmux_no;
		break;
	case pinmux_ata:
283 284
		ret = __crisv32_pinmux_dealloc(PORT_B, 0, 15);
		ret |= __crisv32_pinmux_dealloc(PORT_D, 8, 15);
285 286 287
		hwprot.ata = regk_pinmux_no;
		break;
	case pinmux_eth1:
288
		ret = __crisv32_pinmux_dealloc(PORT_E, 0, 17);
289 290 291 292
		hwprot.eth1 = regk_pinmux_no;
		hwprot.eth1_mgm = regk_pinmux_no;
		break;
	case pinmux_timer:
293
		ret = __crisv32_pinmux_dealloc(PORT_C, 16, 16);
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
		hwprot.timer = regk_pinmux_no;
		spin_unlock_irqrestore(&pinmux_lock, flags);
		return ret;
	}

	if (!ret)
		REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
	else
		memcpy(pins, saved, sizeof pins);

	spin_unlock_irqrestore(&pinmux_lock, flags);

	return ret;
}

309 310
#ifdef DEBUG
static void crisv32_pinmux_dump(void)
311 312 313 314 315 316 317 318 319 320 321
{
	int i, j;

	crisv32_pinmux_init();

	for (i = 0; i < PORTS; i++) {
		printk(KERN_DEBUG "Port %c\n", 'B' + i);
		for (j = 0; j < PORT_PINS; j++)
			printk(KERN_DEBUG "  Pin %d = %d\n", j, pins[i][j]);
	}
}
322
#endif
323
__initcall(crisv32_pinmux_init);