/* IO interface mux allocator for ETRAX100LX. * Copyright 2004-2007, Axis Communications AB */ /* C.f. ETRAX100LX Designer's Reference chapter 19.9 */ #include #include #include #include #include #include #include #include #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<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);