> 1/3 adds superio_locks, into newly created drivers/isa > Its a bit chatty, which I presume is ok for now.. > the number of reservations is settable via modparam: max_locks > signoff later.. diff -ruNp -X dontdiff -X exclude-diffs 6locks-1/drivers/isa/Kconfig 6locks-2/drivers/isa/Kconfig --- 6locks-1/drivers/isa/Kconfig 1969-12-31 17:00:00.000000000 -0700 +++ 6locks-2/drivers/isa/Kconfig 2006-09-13 09:54:18.000000000 -0600 @@ -0,0 +1,7 @@ + +config SUPERIO_LOCKS + tristate "Super-IO port sharing" + help + this module provides locks for use by drivers which need to + share access to a multi-function device via its superio port, + and which register that port. diff -ruNp -X dontdiff -X exclude-diffs 6locks-1/drivers/isa/Makefile 6locks-2/drivers/isa/Makefile --- 6locks-1/drivers/isa/Makefile 1969-12-31 17:00:00.000000000 -0700 +++ 6locks-2/drivers/isa/Makefile 2006-09-13 09:54:18.000000000 -0600 @@ -0,0 +1 @@ +obj-$(CONFIG_SUPERIO_LOCKS) += superio_locks.o diff -ruNp -X dontdiff -X exclude-diffs 6locks-1/drivers/isa/superio_locks.c 6locks-2/drivers/isa/superio_locks.c --- 6locks-1/drivers/isa/superio_locks.c 1969-12-31 17:00:00.000000000 -0700 +++ 6locks-2/drivers/isa/superio_locks.c 2006-09-13 14:56:32.000000000 -0600 @@ -0,0 +1,169 @@ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <asm/io.h> +#include <linux/superio-locks.h> + +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/i2c-isa.h> +#include <linux/err.h> + +MODULE_AUTHOR("Jim Cromie <jim.cromie at gmail.com"); +MODULE_LICENSE("GPL"); + +/** + module provides a means for modules to register their use of a + Super-IO port, and provides an access-lock for the registering + modules to use to coordinate with each other. Consider it a + parking-attendant's key-board. Design is perhaps ISA centric, + maybe formalize this, with (platform|isa)_driver. +*/ + +static int max_locks = 3; /* 1 is enough for 90% uses */ +module_param(max_locks, int, 0); +MODULE_PARM_DESC(max_locks, + " Number of sio-lock clients to serve (default=3)"); + +static struct superio *sio_locks; +static int num_locks; +static struct mutex reservation_lock; + + +/* TB-Replaced by something better: platform_driver ? */ +#define dprintk(...) printk(KERN_NOTICE "superio: " __VA_ARGS__) + +/* superio_get() checks whether the expected SuperIO device is + present at a specific cmd-addr. Use in loop to scan. +*/ + +struct superio* superio_get(u16 cmd_addr, u8 dev_id_addr, + u8 want_devid) +{ + int slot, rc, mydevid; + + mutex_lock(&reservation_lock); + + /* share any already allocated lock for this cmd_addr, device-id */ + for (slot = 0; slot < max_locks; slot++) { + if (sio_locks[slot].users + && cmd_addr == sio_locks[slot].sioaddr + && want_devid == sio_locks[slot].devid) { + + if (sio_locks[slot].users == 255) { + dprintk("too many drivers sharing port %x\n", cmd_addr); + mutex_unlock(&reservation_lock); + return 0; + } + sio_locks[slot].users++; + dprintk("sharing port:%x dev:%x users:%d\n", + cmd_addr, want_devid, sio_locks[slot].users); + mutex_unlock(&reservation_lock); + return &sio_locks[slot]; + } + } + /* read the device-id-address */ + outb(dev_id_addr, cmd_addr); + mydevid = inb(cmd_addr+1); + + /* but 1st, check that the cmd register remembers the val just written */ + rc = inb(cmd_addr); + if (rc != dev_id_addr) { + dprintk("superio_cmdaddr %x absent %d\n", cmd_addr, rc); + mutex_unlock(&reservation_lock); + return NULL; + } + /* test for the desired device id value */ + if (mydevid != want_devid) { + mutex_unlock(&reservation_lock); + return NULL; + } + /* find 1st unused slot */ + for (slot = 0; slot < max_locks; slot++) + if (!sio_locks[slot].users) + break; + + if (slot >= max_locks) { + printk(KERN_ERR "No superio-locks left. increase max_locks\n"); + mutex_unlock(&reservation_lock); + return NULL; + } + dprintk("allocating slot %d, addr %x for device %x\n", + slot, cmd_addr, want_devid); + + sio_locks[slot].sioaddr = cmd_addr; + sio_locks[slot].devid = want_devid; + sio_locks[slot].users = 1; + num_locks++; + + mutex_unlock(&reservation_lock); + return &sio_locks[slot]; +} +EXPORT_SYMBOL_GPL(superio_get); + +/* array args must be null terminated */ +struct superio* superio_find(u16 cmd_addrs[], u8 devid_addr, + u8 want_devids[]) +{ + int i, j; + struct superio* gate; + + for (i = 0; cmd_addrs[i]; i++) { + for (j = 0; want_devids[j]; j++) { + gate = superio_get(cmd_addrs[i], devid_addr, + want_devids[j]); + if (gate) { + dprintk("found devid:%x port:%x\n", + want_devids[j], cmd_addrs[i]); + return gate; + } else + dprintk("no devid:%x at port:%x\n", + want_devids[j], cmd_addrs[i]); + } + } + return NULL; +} +EXPORT_SYMBOL_GPL(superio_find); + +void superio_release(struct superio* const gate) +{ + if (gate < &sio_locks[0] || gate >= &sio_locks[max_locks]) { + printk(KERN_ERR + " superio: attempt to release corrupted superio-lock" + " %p vs %p\n", gate, &sio_locks); + return; + } + if (!(--gate->users)) + dprintk("releasing last user of superio-port %x\n", gate->sioaddr); + return; +} +EXPORT_SYMBOL_GPL(superio_release); + +static int superio_locks_init_module(void) +{ + int i; + + dprintk("initializing with %d reservation slots\n", max_locks); + sio_locks = kzalloc(max_locks*sizeof(struct superio), GFP_KERNEL); + if (!sio_locks) { + printk(KERN_ERR "superio: no memory\n"); + return -ENOMEM; + } + for (i = 0; i < max_locks; i++) + mutex_init(&sio_locks[i].lock); + + mutex_init(&reservation_lock); + return 0; +} + +static void superio_locks_cleanup_module(void) +{ + dprintk("releasing %d superio reservation slots\n", max_locks); + kfree(sio_locks); +} + +module_init(superio_locks_init_module); +module_exit(superio_locks_cleanup_module); diff -ruNp -X dontdiff -X exclude-diffs 6locks-1/drivers/Kconfig 6locks-2/drivers/Kconfig --- 6locks-1/drivers/Kconfig 2006-09-07 16:11:30.000000000 -0600 +++ 6locks-2/drivers/Kconfig 2006-09-13 09:54:18.000000000 -0600 @@ -74,4 +74,6 @@ source "drivers/rtc/Kconfig" source "drivers/dma/Kconfig" +source "drivers/isa/Kconfig" + endmenu diff -ruNp -X dontdiff -X exclude-diffs 6locks-1/drivers/Makefile 6locks-2/drivers/Makefile --- 6locks-1/drivers/Makefile 2006-09-07 16:11:30.000000000 -0600 +++ 6locks-2/drivers/Makefile 2006-09-13 09:54:18.000000000 -0600 @@ -76,3 +76,4 @@ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ obj-$(CONFIG_GENERIC_TIME) += clocksource/ obj-$(CONFIG_DMA_ENGINE) += dma/ +obj-$(CONFIG_ISA) += isa/ diff -ruNp -X dontdiff -X exclude-diffs 6locks-1/include/linux/superio-locks.h 6locks-2/include/linux/superio-locks.h --- 6locks-1/include/linux/superio-locks.h 1969-12-31 17:00:00.000000000 -0700 +++ 6locks-2/include/linux/superio-locks.h 2006-09-13 14:21:08.000000000 -0600 @@ -0,0 +1,55 @@ +#include <linux/mutex.h> +#include <asm/io.h> + +/* Super-IO ports are found in low-pin-count hardware (typically ISA, + any others ?). They usually provide access to many functional + units, so many drivers must share the superio port. This struct + provides a lock that allows the drivers to coordinate access to that + port. +*/ +struct superio { + struct mutex lock; /* lock shared amongst user drivers */ + u16 sioaddr; /* port's tested cmd-address */ + u8 devid; /* devid found by the registering driver */ + u8 users; /* I cant imagine >256 user drivers */ +}; + +/* array args must be null terminated */ +struct superio* superio_find(u16 sioaddrs[], u8 devid_addr, u8 devid_vals[]); +struct superio* superio_get(u16 sioaddr, u8 devid_addr, u8 devid_val); +void superio_release(struct superio* const gate); + +/* these locking ops do not address the idling & activation of some + superio devices, which will, once 'locked', ignore accesses until + the 'unlock' sequence is done 1st. Unfortunately these sequences + vary by device, and in any case don't protect 2 drivers from + stepping on each other's operations. + + Callbacks are a possible approach, but every driver using a device + would have to provide them, and only the 1st loaded module would + actually succeed in registering them. Furthermore, if any driver + accessing a port uses the idle/activate sequences, they all must. + On the whole, this is complexity w/o benefit. +*/ +static inline void superio_enter(struct superio * const sio_port) +{ + mutex_lock(&sio_port->lock); +} + +static inline void superio_exit(struct superio * const sio_port) +{ + mutex_unlock(&sio_port->lock); +} + +static inline void superio_outb(struct superio * const sio_port, u8 reg, u8 val) +{ + outb(reg, sio_port->sioaddr); + outb(val, sio_port->sioaddr+1); +} + +static inline int superio_inb(struct superio * const sio_port, u8 reg) +{ + outb(reg, sio_port->sioaddr); + return inb(sio_port->sioaddr+1); +} +