Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1220e2e authored by Jesper Nilsson's avatar Jesper Nilsson
Browse files

CRIS v10: Update kernel/io_interface_mux.c

Fixed a bug where two interfaces using pins in the same pin group could
not be allocated at the same time even if there where no pin collisions.

Change all restore and returns into goto exit pattern.

Also, remove useless CVS id and correct chapter reference for ETRAX100LX
Designer's Reference in comment.
parent 5f526d14
Loading
Loading
Loading
Loading
+408 −107
Original line number Diff line number Diff line
/* IO interface mux allocator for ETRAX100LX.
 * Copyright 2004, Axis Communications AB
 * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $
 * Copyright 2004-2007, Axis Communications AB
 */


/* C.f. ETRAX100LX Designer's Reference 20.9 */
/* C.f. ETRAX100LX Designer's Reference chapter 19.9 */

#include <linux/kernel.h>
#include <linux/slab.h>
@@ -45,17 +44,39 @@ struct watcher
struct if_group
{
	enum io_if_group        group;
	unsigned char           used;
	enum cris_io_interface  owner;
	/* name	- the name of the group 'A' to 'F' */
	char                   *name;
	/* used	- a bit mask of all pins in the group in the order listed
	 * in the tables in 19.9.1 to 19.9.6.  Note that no
	 * distinction is made between in, out and in/out pins. */
	unsigned int            used;
};


struct interface
{
	enum cris_io_interface   ioif;
	/* name - the name of the interface */
	char                    *name;
	/* groups - OR'ed together io_if_group flags describing what pin groups
	 * the interface uses pins in. */
	unsigned char            groups;
	/* used - set when the interface is allocated. */
	unsigned char            used;
	char                    *owner;
	/* group_a through group_f - bit masks describing what pins in the
	 * pin groups the interface uses. */
	unsigned int             group_a;
	unsigned int             group_b;
	unsigned int             group_c;
	unsigned int             group_d;
	unsigned int             group_e;
	unsigned int             group_f;

	/* gpio_g_in, gpio_g_out, gpio_b - bit masks telling what pins in the
	 * GPIO ports the interface uses.  This could be reconstucted using
	 * the group_X masks and a table of what pins the GPIO ports use,
	 * but that would be messy. */
	unsigned int             gpio_g_in;
	unsigned int             gpio_g_out;
	unsigned char            gpio_b;
@@ -64,26 +85,32 @@ struct interface
static struct if_group if_groups[6] = {
	{
		.group = group_a,
		.name = "A",
		.used = 0,
	},
	{
		.group = group_b,
		.name = "B",
		.used = 0,
	},
	{
		.group = group_c,
		.name = "C",
		.used = 0,
	},
	{
		.group = group_d,
		.name = "D",
		.used = 0,
	},
	{
		.group = group_e,
		.name = "E",
		.used = 0,
	},
	{
		.group = group_f,
		.name = "F",
		.used = 0,
	}
};
@@ -94,14 +121,32 @@ static struct interface interfaces[] = {
	/* Begin Non-multiplexed interfaces */
	{
		.ioif = if_eth,
		.name = "ethernet",
		.groups = 0,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in = 0,
		.gpio_g_out = 0,
		.gpio_b = 0
	},
	{
		.ioif = if_serial_0,
		.name = "serial_0",
		.groups = 0,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in = 0,
		.gpio_g_out = 0,
		.gpio_b = 0
@@ -109,172 +154,385 @@ static struct interface interfaces[] = {
	/* End Non-multiplexed interfaces */
	{
		.ioif = if_serial_1,
		.name = "serial_1",
		.groups = group_e,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0x0f,
		.group_f = 0,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0x00
	},
	{
		.ioif = if_serial_2,
		.name = "serial_2",
		.groups = group_b,

		.group_a = 0,
		.group_b = 0x0f,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x000000c0,
		.gpio_g_out = 0x000000c0,
		.gpio_b = 0x00
	},
	{
		.ioif = if_serial_3,
		.name = "serial_3",
		.groups = group_c,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0x0f,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0xc0000000,
		.gpio_g_out = 0xc0000000,
		.gpio_b = 0x00
	},
	{
		.ioif = if_sync_serial_1,
		.groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3
					       can be used simultaneously */
		.name = "sync_serial_1",
		.groups = group_e | group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0x0f,
		.group_f = 0x10,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0x10
	},
	{
		.ioif = if_sync_serial_3,
		.name = "sync_serial_3",
		.groups = group_c | group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0x0f,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0x80,

		.gpio_g_in =  0xc0000000,
		.gpio_g_out = 0xc0000000,
		.gpio_b = 0x80
	},
	{
		.ioif = if_shared_ram,
		.name = "shared_ram",
		.groups = group_a,

		.group_a = 0x7f8ff,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x0000ff3e,
		.gpio_g_out = 0x0000ff38,
		.gpio_b = 0x00
	},
	{
		.ioif = if_shared_ram_w,
		.name = "shared_ram_w",
		.groups = group_a | group_d,

		.group_a = 0x7f8ff,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0xff,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x00ffff3e,
		.gpio_g_out = 0x00ffff38,
		.gpio_b = 0x00
	},
	{
		.ioif = if_par_0,
		.name = "par_0",
		.groups = group_a,

		.group_a = 0x7fbff,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x0000ff3e,
		.gpio_g_out = 0x0000ff3e,
		.gpio_b = 0x00
	},
	{
		.ioif = if_par_1,
		.name = "par_1",
		.groups = group_d,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0x7feff,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x3eff0000,
		.gpio_g_out = 0x3eff0000,
		.gpio_b = 0x00
	},
	{
		.ioif = if_par_w,
		.name = "par_w",
		.groups = group_a | group_d,

		.group_a = 0x7fbff,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0xff,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x00ffff3e,
		.gpio_g_out = 0x00ffff3e,
		.gpio_b = 0x00
	},
	{
		.ioif = if_scsi8_0,
		.groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1
							  can be used simultaneously */
		.name = "scsi8_0",
		.groups = group_a | group_b | group_f,

		.group_a = 0x7ffff,
		.group_b = 0x0f,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0x10,

		.gpio_g_in =  0x0000ffff,
		.gpio_g_out = 0x0000ffff,
		.gpio_b = 0x10
	},
	{
		.ioif = if_scsi8_1,
		.groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1
							  can be used simultaneously */
		.name = "scsi8_1",
		.groups = group_c | group_d | group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0x0f,
		.group_d = 0x7ffff,
		.group_e = 0,
		.group_f = 0x80,

		.gpio_g_in =  0xffff0000,
		.gpio_g_out = 0xffff0000,
		.gpio_b = 0x80
	},
	{
		.ioif = if_scsi_w,
		.name = "scsi_w",
		.groups = group_a | group_b | group_d | group_f,

		.group_a = 0x7ffff,
		.group_b = 0x0f,
		.group_c = 0,
		.group_d = 0x601ff,
		.group_e = 0,
		.group_f = 0x90,

		.gpio_g_in =  0x01ffffff,
		.gpio_g_out = 0x07ffffff,
		.gpio_b = 0x80
	},
	{
		.ioif = if_ata,
		.name = "ata",
		.groups = group_a | group_b | group_c | group_d,

		.group_a = 0x7ffff,
		.group_b = 0x0f,
		.group_c = 0x0f,
		.group_d = 0x7cfff,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0xf9ffffff,
		.gpio_g_out = 0xffffffff,
		.gpio_b = 0x80
	},
	{
		.ioif = if_csp,
		.groups = group_f, /* if_csp and if_i2c can be used simultaneously */
		.name = "csp",
		.groups = group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0xfc,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0xfc
	},
	{
		.ioif = if_i2c,
		.groups = group_f, /* if_csp and if_i2c can be used simultaneously */
		.name = "i2c",
		.groups = group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0x03,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0x03
	},
	{
		.ioif = if_usb_1,
		.name = "usb_1",
		.groups = group_e | group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0x0f,
		.group_f = 0x2c,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0x2c
	},
	{
		.ioif = if_usb_2,
		.name = "usb_2",
		.groups = group_d,
		.gpio_g_in =  0x0e000000,
		.gpio_g_out = 0x3c000000,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0x33e00,
		.group_f = 0,

		.gpio_g_in =  0x3e000000,
		.gpio_g_out = 0x0c000000,
		.gpio_b = 0x00
	},
	/* GPIO pins */
	{
		.ioif = if_gpio_grp_a,
		.name = "gpio_a",
		.groups = group_a,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x0000ff3f,
		.gpio_g_out = 0x0000ff3f,
		.gpio_b = 0x00
	},
	{
		.ioif = if_gpio_grp_b,
		.name = "gpio_b",
		.groups = group_b,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x000000c0,
		.gpio_g_out = 0x000000c0,
		.gpio_b = 0x00
	},
	{
		.ioif = if_gpio_grp_c,
		.name = "gpio_c",
		.groups = group_c,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0xc0000000,
		.gpio_g_out = 0xc0000000,
		.gpio_b = 0x00
	},
	{
		.ioif = if_gpio_grp_d,
		.name = "gpio_d",
		.groups = group_d,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x3fff0000,
		.gpio_g_out = 0x3fff0000,
		.gpio_b = 0x00
	},
	{
		.ioif = if_gpio_grp_e,
		.name = "gpio_e",
		.groups = group_e,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0x00
	},
	{
		.ioif = if_gpio_grp_f,
		.name = "gpio_f",
		.groups = group_f,

		.group_a = 0,
		.group_b = 0,
		.group_c = 0,
		.group_d = 0,
		.group_e = 0,
		.group_f = 0,

		.gpio_g_in =  0x00000000,
		.gpio_g_out = 0x00000000,
		.gpio_b = 0xff
@@ -284,11 +542,13 @@ static struct interface interfaces[] = {

static struct watcher *watchers = NULL;

/* The pins that are free to use in the GPIO ports. */
static unsigned int gpio_in_pins =  0xffffffff;
static unsigned int gpio_out_pins = 0xffffffff;
static unsigned char gpio_pb_pins = 0xff;
static unsigned char gpio_pa_pins = 0xff;

/* Identifiers for the owners of the GPIO pins. */
static enum cris_io_interface gpio_pa_owners[8];
static enum cris_io_interface gpio_pb_owners[8];
static enum cris_io_interface gpio_pg_owners[32];
@@ -338,13 +598,15 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id
	struct if_group *grp;
	unsigned char group_set;
	unsigned long flags;
	int res = 0;

	(void)cris_io_interface_init();

	DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id));

	if ((ioif >= if_max_interfaces) || (ioif < 0)) {
		printk(KERN_CRIT "cris_request_io_interface: Bad interface %u submitted for %s\n",
		printk(KERN_CRIT "cris_request_io_interface: Bad interface "
			"%u submitted for %s\n",
		       ioif,
		       device_id);
		return -EINVAL;
@@ -353,59 +615,69 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id
	local_irq_save(flags);

	if (interfaces[ioif].used) {
		local_irq_restore(flags);
		printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n",
		printk(KERN_CRIT "cris_io_interface: Cannot allocate interface "
			"%s for %s, in use by %s\n",
		       interfaces[ioif].name,
		       device_id,
		       interfaces[ioif].owner);
		return -EBUSY;
		res = -EBUSY;
		goto exit;
	}

	/* Check that all required groups are free before allocating, */
	/* Check that all required pins in the used groups are free
	 * before allocating. */
	group_set = interfaces[ioif].groups;
	while (NULL != (grp = get_group(group_set))) {
		if (grp->used) {
			if (grp->group == group_f) {
				if ((if_sync_serial_1 ==  ioif) ||
				    (if_sync_serial_3 ==  ioif)) {
					if ((grp->owner != if_sync_serial_1) &&
					    (grp->owner != if_sync_serial_3)) {
						local_irq_restore(flags);
						return -EBUSY;
					}
				} else if ((if_scsi8_0 == ioif) ||
					   (if_scsi8_1 == ioif)) {
					if ((grp->owner != if_scsi8_0) &&
					    (grp->owner != if_scsi8_1)) {
						local_irq_restore(flags);
						return -EBUSY;
					}
				}
			} else {
				local_irq_restore(flags);
				return -EBUSY;
		unsigned int if_group_use = 0;

		switch (grp->group) {
		case group_a:
			if_group_use = interfaces[ioif].group_a;
			break;
		case group_b:
			if_group_use = interfaces[ioif].group_b;
			break;
		case group_c:
			if_group_use = interfaces[ioif].group_c;
			break;
		case group_d:
			if_group_use = interfaces[ioif].group_d;
			break;
		case group_e:
			if_group_use = interfaces[ioif].group_e;
			break;
		case group_f:
			if_group_use = interfaces[ioif].group_f;
			break;
		default:
			BUG_ON(1);
		}

		if (if_group_use & grp->used) {
			printk(KERN_INFO "cris_request_io_interface: group "
				"%s needed by %s not available\n",
				grp->name, interfaces[ioif].name);
			res = -EBUSY;
			goto exit;
		}

		group_set = clear_group_from_set(group_set, grp);
	}

	/* Are the required GPIO pins available too? */
	if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) ||
	    ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) ||
	    ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) {
		local_irq_restore(flags);
		printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n",
		       ioif);
		return -EBUSY;
	}

	/* All needed I/O pins and pin groups are free, allocate. */
	group_set = interfaces[ioif].groups;
	while (NULL != (grp = get_group(group_set))) {
		grp->used = 1;
		grp->owner = ioif;
		group_set = clear_group_from_set(group_set, grp);
	if (((interfaces[ioif].gpio_g_in & gpio_in_pins) !=
			interfaces[ioif].gpio_g_in) ||
		((interfaces[ioif].gpio_g_out & gpio_out_pins) !=
			interfaces[ioif].gpio_g_out) ||
		((interfaces[ioif].gpio_b & gpio_pb_pins) !=
			interfaces[ioif].gpio_b)) {
		printk(KERN_CRIT "cris_request_io_interface: Could not get "
			"required pins for interface %u\n", ioif);
		res = -EBUSY;
		goto exit;
	}

	/* Check which registers need to be reconfigured. */
	gens = genconfig_shadow;
	gens_ii = gen_config_ii_shadow;

@@ -495,9 +767,43 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id
		set_gen_config = 0;
		break;
	default:
		panic("cris_request_io_interface: Bad interface %u submitted for %s\n",
		      ioif,
		      device_id);
		printk(KERN_INFO "cris_request_io_interface: Bad interface "
			"%u submitted for %s\n",
			ioif, device_id);
		res = -EBUSY;
		goto exit;
	}

	/* All needed I/O pins and pin groups are free, allocate. */
	group_set = interfaces[ioif].groups;
	while (NULL != (grp = get_group(group_set))) {
		unsigned int if_group_use = 0;

		switch (grp->group) {
		case group_a:
			if_group_use = interfaces[ioif].group_a;
			break;
		case group_b:
			if_group_use = interfaces[ioif].group_b;
			break;
		case group_c:
			if_group_use = interfaces[ioif].group_c;
			break;
		case group_d:
			if_group_use = interfaces[ioif].group_d;
			break;
		case group_e:
			if_group_use = interfaces[ioif].group_e;
			break;
		case group_f:
			if_group_use = interfaces[ioif].group_f;
			break;
		default:
			BUG_ON(1);
		}
		grp->used |= if_group_use;

		group_set = clear_group_from_set(group_set, grp);
	}

	interfaces[ioif].used = 1;
@@ -516,9 +822,11 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id
		*R_GEN_CONFIG_II = gen_config_ii_shadow;
	}

	DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
	DBG(printk(KERN_DEBUG "GPIO pins: available before: "
		"g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
		gpio_in_pins, gpio_out_pins, gpio_pb_pins));
	DBG(printk("grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
	DBG(printk(KERN_DEBUG
		"grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
		interfaces[ioif].gpio_g_in,
		interfaces[ioif].gpio_g_out,
		interfaces[ioif].gpio_b));
@@ -527,14 +835,15 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id
	gpio_out_pins &= ~interfaces[ioif].gpio_g_out;
	gpio_pb_pins &= ~interfaces[ioif].gpio_b;

	DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
	DBG(printk(KERN_DEBUG "GPIO pins: available after: "
		"g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
		gpio_in_pins, gpio_out_pins, gpio_pb_pins));

exit:
	local_irq_restore(flags);

	if (res == 0)
		notify_watchers();

	return 0;
	return res;
}


@@ -560,43 +869,35 @@ void cris_free_io_interface(enum cris_io_interface ioif)
	}
	group_set = interfaces[ioif].groups;
	while (NULL != (grp = get_group(group_set))) {
		if (grp->group == group_f) {
			switch (ioif)
			{
			case if_sync_serial_1:
				if ((grp->owner == if_sync_serial_1) &&
				    interfaces[if_sync_serial_3].used) {
					grp->owner = if_sync_serial_3;
				} else
					grp->used = 0;
		unsigned int if_group_use = 0;

		switch (grp->group) {
		case group_a:
			if_group_use = interfaces[ioif].group_a;
			break;
			case if_sync_serial_3:
				if ((grp->owner == if_sync_serial_3) &&
				    interfaces[if_sync_serial_1].used) {
					grp->owner = if_sync_serial_1;
				} else
					grp->used = 0;
		case group_b:
			if_group_use = interfaces[ioif].group_b;
			break;
			case if_scsi8_0:
				if ((grp->owner == if_scsi8_0) &&
				    interfaces[if_scsi8_1].used) {
					grp->owner = if_scsi8_1;
				} else
					grp->used = 0;
		case group_c:
			if_group_use = interfaces[ioif].group_c;
			break;
			case if_scsi8_1:
				if ((grp->owner == if_scsi8_1) &&
				    interfaces[if_scsi8_0].used) {
					grp->owner = if_scsi8_0;
				} else
					grp->used = 0;
		case group_d:
			if_group_use = interfaces[ioif].group_d;
			break;
		case group_e:
			if_group_use = interfaces[ioif].group_e;
			break;
		case group_f:
			if_group_use = interfaces[ioif].group_f;
			break;
		default:
				grp->used = 0;
			}
		} else {
			grp->used = 0;
			BUG_ON(1);
		}

		if ((grp->used & if_group_use) != if_group_use)
			BUG_ON(1);
		grp->used = grp->used & ~if_group_use;

		group_set = clear_group_from_set(group_set, grp);
	}
	interfaces[ioif].used = 0;