For some (unclear to me) reason, superio_enter/superio_exit, although calling request_muxed_region/release_region (respectively), are not sufficient on an multi-processor system to guarantee serialised access, leading to the following errors: [ 1210.586990] Trying to free nonexistent resource <000000000000002e-000000000000002f> [ 1211.227414] Trying to free nonexistent resource <000000000000002e-000000000000002f> [ 1211.867890] Trying to free nonexistent resource <000000000000002e-000000000000002f> [ 1212.508299] Trying to free nonexistent resource <000000000000002e-000000000000002f> [ 1213.148734] Trying to free nonexistent resource <000000000000002e-000000000000002f> [ 1213.789172] Trying to free nonexistent resource <000000000000002e-000000000000002f> [ 1214.429607] Trying to free nonexistent resource <000000000000002e-000000000000002f> Disabling all but one core make the error disappear, and re-enabling it make it reappear. Loaded modules for this SuperIO chip on this system are: 8250_fintek (not actively used) fintek_cir (not actively used) f71882fg (polled every 2 seconds from userland) gpio_f7188x Loaded modules using GPIO functionality: gpio_keys_polled (20ms polling period) leds_gpio (usb-host and 500ms timer triggers) This change replaces frequent superio_enter/superio_exit and accesses via the common GPIO IO region (0x2e..0x2f or 0x4e..0x4f) with mutex locking and accesses via GPIO-dedicated IO region (pre-configured on chip). Code inspired from hwmon/f71882fg . Signed-off-by: Vincent Pelletier <plr.vincent@xxxxxxxxx> --- drivers/gpio/gpio-f7188x.c | 126 ++++++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 46 deletions(-) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index dbda843..2915f8d 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -16,6 +16,8 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/gpio.h> +#include <linux/mutex.h> +#include <linux/acpi.h> #define DRVNAME "gpio-f7188x" @@ -26,11 +28,17 @@ #define SIO_DEVID 0x20 /* Device ID (2 bytes) */ #define SIO_DEVREV 0x22 /* Device revision */ #define SIO_MANID 0x23 /* Fintek ID (2 bytes) */ +#define SIO_ENABLE 0x30 /* Logical device enable */ +#define SIO_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_LD_GPIO 0x06 /* GPIO logical device */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ #define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ +#define REGION_LENGTH 8 +#define ADDR_REG_OFFSET 5 +#define DATA_REG_OFFSET 6 + #define SIO_FINTEK_ID 0x1934 /* Manufacturer ID */ #define SIO_F71869_ID 0x0814 /* F71869 chipset ID */ #define SIO_F71869A_ID 0x1007 /* F71869A chipset ID */ @@ -47,8 +55,9 @@ static const char * const f7188x_names[] = { }; struct f7188x_sio { - int addr; + u16 addr; enum chips type; + struct mutex lock; }; struct f7188x_gpio_bank { @@ -118,6 +127,22 @@ static inline void superio_exit(int base) release_region(base, 2); } +static u8 f7188x_read8(struct f7188x_sio *data, u8 reg) +{ + u8 val; + + outb(reg, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET); + + return val; +} + +static void f7188x_write8(struct f7188x_sio *data, u8 reg, u8 val) +{ + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val, data->addr + DATA_REG_OFFSET); +} + /* * GPIO chip. */ @@ -192,47 +217,35 @@ static struct f7188x_gpio_bank f71889_gpio_bank[] = { static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { - int err; struct f7188x_gpio_bank *bank = container_of(chip, struct f7188x_gpio_bank, chip); struct f7188x_sio *sio = bank->data->sio; u8 dir; - err = superio_enter(sio->addr); - if (err) - return err; - superio_select(sio->addr, SIO_LD_GPIO); - - dir = superio_inb(sio->addr, gpio_dir(bank->regbase)); + mutex_lock(&sio->lock); + dir = f7188x_read8(sio, gpio_dir(bank->regbase)); dir &= ~(1 << offset); - superio_outb(sio->addr, gpio_dir(bank->regbase), dir); - - superio_exit(sio->addr); + f7188x_write8(sio, gpio_dir(bank->regbase), dir); + mutex_unlock(&sio->lock); return 0; } static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset) { - int err; struct f7188x_gpio_bank *bank = container_of(chip, struct f7188x_gpio_bank, chip); struct f7188x_sio *sio = bank->data->sio; u8 dir, data; - err = superio_enter(sio->addr); - if (err) - return err; - superio_select(sio->addr, SIO_LD_GPIO); - - dir = superio_inb(sio->addr, gpio_dir(bank->regbase)); + mutex_lock(&sio->lock); + dir = f7188x_read8(sio, gpio_dir(bank->regbase)); dir = !!(dir & (1 << offset)); if (dir) - data = superio_inb(sio->addr, gpio_data_out(bank->regbase)); + data = f7188x_read8(sio, gpio_data_out(bank->regbase)); else - data = superio_inb(sio->addr, gpio_data_in(bank->regbase)); - - superio_exit(sio->addr); + data = f7188x_read8(sio, gpio_data_in(bank->regbase)); + mutex_unlock(&sio->lock); return !!(data & 1 << offset); } @@ -240,54 +253,42 @@ static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset) static int f7188x_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value) { - int err; struct f7188x_gpio_bank *bank = container_of(chip, struct f7188x_gpio_bank, chip); struct f7188x_sio *sio = bank->data->sio; u8 dir, data_out; - err = superio_enter(sio->addr); - if (err) - return err; - superio_select(sio->addr, SIO_LD_GPIO); - - data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase)); + mutex_lock(&sio->lock); + data_out = f7188x_read8(sio, gpio_data_out(bank->regbase)); if (value) data_out |= (1 << offset); else data_out &= ~(1 << offset); - superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out); + f7188x_write8(sio, gpio_data_out(bank->regbase), data_out); - dir = superio_inb(sio->addr, gpio_dir(bank->regbase)); + dir = f7188x_read8(sio, gpio_dir(bank->regbase)); dir |= (1 << offset); - superio_outb(sio->addr, gpio_dir(bank->regbase), dir); - - superio_exit(sio->addr); + f7188x_write8(sio, gpio_dir(bank->regbase), dir); + mutex_unlock(&sio->lock); return 0; } static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { - int err; struct f7188x_gpio_bank *bank = container_of(chip, struct f7188x_gpio_bank, chip); struct f7188x_sio *sio = bank->data->sio; u8 data_out; - err = superio_enter(sio->addr); - if (err) - return; - superio_select(sio->addr, SIO_LD_GPIO); - - data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase)); + mutex_lock(&sio->lock); + data_out = f7188x_read8(sio, gpio_data_out(bank->regbase)); if (value) data_out |= (1 << offset); else data_out &= ~(1 << offset); - superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out); - - superio_exit(sio->addr); + f7188x_write8(sio, gpio_data_out(bank->regbase), data_out); + mutex_unlock(&sio->lock); } /* @@ -326,6 +327,7 @@ static int f7188x_gpio_probe(struct platform_device *pdev) return -ENODEV; } data->sio = sio; + mutex_init(&sio->lock); platform_set_drvdata(pdev, data); @@ -372,6 +374,7 @@ static int f7188x_gpio_remove(struct platform_device *pdev) static int __init f7188x_find(int addr, struct f7188x_sio *sio) { int err; + u16 logical_device_address; u16 devid; err = superio_enter(addr); @@ -403,12 +406,25 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio) pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid); goto err; } - sio->addr = addr; + superio_select(addr, SIO_LD_GPIO); + + if (!(superio_inb(addr, SIO_ENABLE) & 0x01)) { + pr_warn("Device not activated\n"); + goto err; + } + + logical_device_address = superio_inw(addr, SIO_ADDR); + if (logical_device_address == 0) { + pr_warn("Base address not set\n"); + goto err; + } + logical_device_address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ + sio->addr = logical_device_address; err = 0; pr_info(DRVNAME ": Found %s at %#x, revision %d\n", f7188x_names[sio->type], - (unsigned int) addr, + (unsigned int) logical_device_address, (int) superio_inb(addr, SIO_DEVREV)); err: @@ -421,12 +437,28 @@ static struct platform_device *f7188x_gpio_pdev; static int __init f7188x_gpio_device_add(const struct f7188x_sio *sio) { + struct resource res = { + .start = sio->addr, + .end = sio->addr + REGION_LENGTH - 1, + .flags = IORESOURCE_IO, + }; int err; f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1); if (!f7188x_gpio_pdev) return -ENOMEM; + res.name = f7188x_gpio_pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + goto err; + + err = platform_device_add_resources(f7188x_gpio_pdev, &res, 1); + if (err) { + pr_err("Device resource addition failed\n"); + goto err; + } + err = platform_device_add_data(f7188x_gpio_pdev, sio, sizeof(*sio)); if (err) { @@ -467,6 +499,8 @@ static int __init f7188x_gpio_init(void) int err; struct f7188x_sio sio; + memset(&sio, 0, sizeof(sio)); + if (f7188x_find(0x2e, &sio) && f7188x_find(0x4e, &sio)) return -ENODEV; -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html