blob: ad64cd1c861aa81d97d69bd4fa205c4fc2234d1b [file] [log] [blame]
/* IO interface mux allocator for ETRAX100LX.
* Copyright 2004-2007, Axis Communications AB
*/
/* C.f. ETRAX100LX Designer's Reference chapter 19.9 */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <arch/svinto.h>
#include <asm/io.h>
#include <arch/io_interface_mux.h>
#include <arch/system.h>
#define DBG(s)
/* Macro to access ETRAX 100 registers */
#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
IO_STATE_(reg##_, field##_, _##val)
enum io_if_group {
group_a = (1<<0),
group_b = (1<<1),
group_c = (1<<2),
group_d = (1<<3),
group_e = (1<<4),
group_f = (1<<5)
};
struct watcher
{
void (*notify)(const unsigned int gpio_in_available,
const unsigned int gpio_out_available,
const unsigned char pa_available,
const unsigned char pb_available);
struct watcher *next;
};
struct if_group
{
enum io_if_group group;
/* 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;
};
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,
}
};
/* The order in the array must match the order of enum
* cris_io_interface in io_interface_mux.h */
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
},
/* 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,
.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,
.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,
.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,
.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,
.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,
.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
}
/* Array end */
};
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];
static int cris_io_interface_init(void);
static unsigned char clear_group_from_set(const unsigned char groups, struct if_group *group)
{
return (groups & ~group->group);
}
static struct if_group *get_group(const unsigned char groups)
{
int i;
for (i = 0; i < ARRAY_SIZE(if_groups); i++) {
if (groups & if_groups[i].group) {
return &if_groups[i];
}
}
return NULL;
}
static void notify_watchers(void)
{
struct watcher *w = watchers;
DBG(printk("io_interface_mux: notifying watchers\n"));
while (NULL != w) {
w->notify((const unsigned int)gpio_in_pins,
(const unsigned int)gpio_out_pins,
(const unsigned char)gpio_pa_pins,
(const unsigned char)gpio_pb_pins);
w = w->next;
}
}
int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id)
{
int set_gen_config = 0;
int set_gen_config_ii = 0;
unsigned long int gens;
unsigned long int gens_ii;
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",
ioif,
device_id);
return -EINVAL;
}
local_irq_save(flags);
if (interfaces[ioif].used) {
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);
res = -EBUSY;
goto exit;
}
/* 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))) {
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)) {
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;
set_gen_config = 1;
switch (ioif)
{
/* Begin Non-multiplexed interfaces */
case if_eth:
/* fall through */
case if_serial_0:
set_gen_config = 0;
break;
/* End Non-multiplexed interfaces */
case if_serial_1:
set_gen_config_ii = 1;
SETS(gens_ii, R_GEN_CONFIG_II, sermode1, async);
break;
case if_serial_2:
SETS(gens, R_GEN_CONFIG, ser2, select);
break;
case if_serial_3:
SETS(gens, R_GEN_CONFIG, ser3, select);
set_gen_config_ii = 1;
SETS(gens_ii, R_GEN_CONFIG_II, sermode3, async);
break;
case if_sync_serial_1:
set_gen_config_ii = 1;
SETS(gens_ii, R_GEN_CONFIG_II, sermode1, sync);
break;
case if_sync_serial_3:
SETS(gens, R_GEN_CONFIG, ser3, select);
set_gen_config_ii = 1;
SETS(gens_ii, R_GEN_CONFIG_II, sermode3, sync);
break;
case if_shared_ram:
SETS(gens, R_GEN_CONFIG, mio, select);
break;
case if_shared_ram_w:
SETS(gens, R_GEN_CONFIG, mio_w, select);
break;
case if_par_0:
SETS(gens, R_GEN_CONFIG, par0, select);
break;
case if_par_1:
SETS(gens, R_GEN_CONFIG, par1, select);
break;
case if_par_w:
SETS(gens, R_GEN_CONFIG, par0, select);
SETS(gens, R_GEN_CONFIG, par_w, select);
break;
case if_scsi8_0:
SETS(gens, R_GEN_CONFIG, scsi0, select);
break;
case if_scsi8_1:
SETS(gens, R_GEN_CONFIG, scsi1, select);
break;
case if_scsi_w:
SETS(gens, R_GEN_CONFIG, scsi0, select);
SETS(gens, R_GEN_CONFIG, scsi0w, select);
break;
case if_ata:
SETS(gens, R_GEN_CONFIG, ata, select);
break;
case if_csp:
/* fall through */
case if_i2c:
set_gen_config = 0;
break;
case if_usb_1:
SETS(gens, R_GEN_CONFIG, usb1, select);
break;
case if_usb_2:
SETS(gens, R_GEN_CONFIG, usb2, select);
break;
case if_gpio_grp_a:
/* GPIO groups are only accounted, don't do configuration changes. */
/* fall through */
case if_gpio_grp_b:
/* fall through */
case if_gpio_grp_c:
/* fall through */
case if_gpio_grp_d:
/* fall through */
case if_gpio_grp_e:
/* fall through */
case if_gpio_grp_f:
set_gen_config = 0;
break;
default:
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;
interfaces[ioif].owner = (char*)device_id;
if (set_gen_config) {
volatile int i;
genconfig_shadow = gens;
*R_GEN_CONFIG = genconfig_shadow;
/* Wait 12 cycles before doing any DMA command */
for(i = 6; i > 0; i--)
nop();
}
if (set_gen_config_ii) {
gen_config_ii_shadow = gens_ii;
*R_GEN_CONFIG_II = gen_config_ii_shadow;
}
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(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));
gpio_in_pins &= ~interfaces[ioif].gpio_g_in;
gpio_out_pins &= ~interfaces[ioif].gpio_g_out;
gpio_pb_pins &= ~interfaces[ioif].gpio_b;
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 res;
}
void cris_free_io_interface(enum cris_io_interface ioif)
{
struct if_group *grp;
unsigned char group_set;
unsigned long flags;
(void)cris_io_interface_init();
if ((ioif >= if_max_interfaces) || (ioif < 0)) {
printk(KERN_CRIT "cris_free_io_interface: Bad interface %u\n",
ioif);
return;
}
local_irq_save(flags);
if (!interfaces[ioif].used) {
printk(KERN_CRIT "cris_free_io_interface: Freeing free interface %u\n",
ioif);
local_irq_restore(flags);
return;
}
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);
}
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;
interfaces[ioif].owner = NULL;
DBG(printk("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("freeing 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));
gpio_in_pins |= interfaces[ioif].gpio_g_in;
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",
gpio_in_pins, gpio_out_pins, gpio_pb_pins));
local_irq_restore(flags);
notify_watchers();
}
/* Create a bitmask from bit 0 (inclusive) to bit stop_bit
(non-inclusive). stop_bit == 0 returns 0x0 */
static inline unsigned int create_mask(const unsigned stop_bit)
{
/* Avoid overflow */
if (stop_bit >= 32) {
return 0xffffffff;
}
return (1<<stop_bit)-1;
}
/* port can be 'a', 'b' or 'g' */
int cris_io_interface_allocate_pins(const enum cris_io_interface ioif,
const char port,
const unsigned start_bit,
const unsigned stop_bit)
{
unsigned int i;
unsigned int mask = 0;
unsigned int tmp_mask;
unsigned long int flags;
enum cris_io_interface *owners;
(void)cris_io_interface_init();
DBG(printk("cris_io_interface_allocate_pins: if=%d port=%c start=%u stop=%u\n",
ioif, port, start_bit, stop_bit));
if (!((start_bit <= stop_bit) &&
((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
((port == 'g') && (stop_bit < 32))))) {
return -EINVAL;
}
mask = create_mask(stop_bit + 1);
tmp_mask = create_mask(start_bit);
mask &= ~tmp_mask;
DBG(printk("cris_io_interface_allocate_pins: port=%c start=%u stop=%u mask=0x%08x\n",
port, start_bit, stop_bit, mask));
local_irq_save(flags);
switch (port) {
case 'a':
if ((gpio_pa_pins & mask) != mask) {
local_irq_restore(flags);
return -EBUSY;
}
owners = gpio_pa_owners;
gpio_pa_pins &= ~mask;
break;
case 'b':
if ((gpio_pb_pins & mask) != mask) {
local_irq_restore(flags);
return -EBUSY;
}
owners = gpio_pb_owners;
gpio_pb_pins &= ~mask;
break;
case 'g':
if (((gpio_in_pins & mask) != mask) ||
((gpio_out_pins & mask) != mask)) {
local_irq_restore(flags);
return -EBUSY;
}
owners = gpio_pg_owners;
gpio_in_pins &= ~mask;
gpio_out_pins &= ~mask;
break;
default:
local_irq_restore(flags);
return -EINVAL;
}
for (i = start_bit; i <= stop_bit; i++) {
owners[i] = ioif;
}
local_irq_restore(flags);
notify_watchers();
return 0;
}
/* port can be 'a', 'b' or 'g' */
int cris_io_interface_free_pins(const enum cris_io_interface ioif,
const char port,
const unsigned start_bit,
const unsigned stop_bit)
{
unsigned int i;
unsigned int mask = 0;
unsigned int tmp_mask;
unsigned long int flags;
enum cris_io_interface *owners;
(void)cris_io_interface_init();
if (!((start_bit <= stop_bit) &&
((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
((port == 'g') && (stop_bit < 32))))) {
return -EINVAL;
}
mask = create_mask(stop_bit + 1);
tmp_mask = create_mask(start_bit);
mask &= ~tmp_mask;
DBG(printk("cris_io_interface_free_pins: port=%c start=%u stop=%u mask=0x%08x\n",
port, start_bit, stop_bit, mask));
local_irq_save(flags);
switch (port) {
case 'a':
if ((~gpio_pa_pins & mask) != mask) {
local_irq_restore(flags);
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
}
owners = gpio_pa_owners;
break;
case 'b':
if ((~gpio_pb_pins & mask) != mask) {
local_irq_restore(flags);
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
}
owners = gpio_pb_owners;
break;
case 'g':
if (((~gpio_in_pins & mask) != mask) ||
((~gpio_out_pins & mask) != mask)) {
local_irq_restore(flags);
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
}
owners = gpio_pg_owners;
break;
default:
owners = NULL; /* Cannot happen. Shut up, gcc! */
}
for (i = start_bit; i <= stop_bit; i++) {
if (owners[i] != ioif) {
printk(KERN_CRIT "cris_io_interface_free_pins: Freeing unowned pins");
}
}
/* All was ok, change data. */
switch (port) {
case 'a':
gpio_pa_pins |= mask;
break;
case 'b':
gpio_pb_pins |= mask;
break;
case 'g':
gpio_in_pins |= mask;
gpio_out_pins |= mask;
break;
}
for (i = start_bit; i <= stop_bit; i++) {
owners[i] = if_unclaimed;
}
local_irq_restore(flags);
notify_watchers();
return 0;
}
int cris_io_interface_register_watcher(void (*notify)(const unsigned int gpio_in_available,
const unsigned int gpio_out_available,
const unsigned char pa_available,
const unsigned char pb_available))
{
struct watcher *w;
(void)cris_io_interface_init();
if (NULL == notify) {
return -EINVAL;
}
w = kmalloc(sizeof(*w), GFP_KERNEL);
if (!w) {
return -ENOMEM;
}
w->notify = notify;
w->next = watchers;
watchers = w;
w->notify((const unsigned int)gpio_in_pins,
(const unsigned int)gpio_out_pins,
(const unsigned char)gpio_pa_pins,
(const unsigned char)gpio_pb_pins);
return 0;
}
void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available,
const unsigned int gpio_out_available,
const unsigned char pa_available,
const unsigned char pb_available))
{
struct watcher *w = watchers, *prev = NULL;
(void)cris_io_interface_init();
while ((NULL != w) && (w->notify != notify)){
prev = w;
w = w->next;
}
if (NULL != w) {
if (NULL != prev) {
prev->next = w->next;
} else {
watchers = w->next;
}
kfree(w);
return;
}
printk(KERN_WARNING "cris_io_interface_delete_watcher: Deleting unknown watcher 0x%p\n", notify);
}
static int cris_io_interface_init(void)
{
static int first = 1;
int i;
if (!first) {
return 0;
}
first = 0;
for (i = 0; i<8; i++) {
gpio_pa_owners[i] = if_unclaimed;
gpio_pb_owners[i] = if_unclaimed;
gpio_pg_owners[i] = if_unclaimed;
}
for (; i<32; i++) {
gpio_pg_owners[i] = if_unclaimed;
}
return 0;
}
module_init(cris_io_interface_init);
EXPORT_SYMBOL(cris_request_io_interface);
EXPORT_SYMBOL(cris_free_io_interface);
EXPORT_SYMBOL(cris_io_interface_allocate_pins);
EXPORT_SYMBOL(cris_io_interface_free_pins);
EXPORT_SYMBOL(cris_io_interface_register_watcher);
EXPORT_SYMBOL(cris_io_interface_delete_watcher);