diff -rupN a/arch/powerpc/sysdev/simple_gpio.c b/arch/powerpc/sysdev/simple_gpio.c --- a/arch/powerpc/sysdev/simple_gpio.c 2014-01-07 14:38:23.383158544 +0100 +++ b/arch/powerpc/sysdev/simple_gpio.c 2014-01-07 14:38:44.283157870 +0100 @@ -117,6 +117,98 @@ err: return ret; } +struct u16_gpio_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* shadowed data register to clear/set bits safely */ + u16 data; +}; + +static struct u16_gpio_chip *to_u16_gpio_chip(struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct u16_gpio_chip, mm_gc); +} + +static u16 u16_pin2mask(unsigned int pin) +{ + return 1 << (16 - 1 - pin); +} + +static int u16_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return in_be16(mm_gc->regs) & u16_pin2mask(gpio); +} + +static void u16_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct u16_gpio_chip *u16_gc = to_u16_gpio_chip(mm_gc); + unsigned long flags; + + spin_lock_irqsave(&u16_gc->lock, flags); + + if (val) + u16_gc->data |= u16_pin2mask(gpio); + else + u16_gc->data &= ~u16_pin2mask(gpio); + + out_be16(mm_gc->regs, u16_gc->data); + + spin_unlock_irqrestore(&u16_gc->lock, flags); +} + +static int u16_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return 0; +} + +static int u16_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + u16_gpio_set(gc, gpio, val); + return 0; +} + +static void u16_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct u16_gpio_chip *u16_gc = to_u16_gpio_chip(mm_gc); + + u16_gc->data = in_be16(mm_gc->regs); +} + +static int __init u16_simple_gpiochip_add(struct device_node *np) +{ + int ret; + struct u16_gpio_chip *u16_gc; + struct of_mm_gpio_chip *mm_gc; + struct gpio_chip *gc; + + u16_gc = kzalloc(sizeof(*u16_gc), GFP_KERNEL); + if (!u16_gc) + return -ENOMEM; + + spin_lock_init(&u16_gc->lock); + + mm_gc = &u16_gc->mm_gc; + gc = &mm_gc->gc; + + mm_gc->save_regs = u16_gpio_save_regs; + gc->ngpio = 16; + gc->direction_input = u16_gpio_dir_in; + gc->direction_output = u16_gpio_dir_out; + gc->get = u16_gpio_get; + gc->set = u16_gpio_set; + + ret = of_mm_gpiochip_add(np, mm_gc); + if (ret) + goto err; + return 0; +err: + kfree(u16_gc); + return ret; +} void __init simple_gpiochip_init(const char *compatible) { struct device_node *np; @@ -135,6 +227,11 @@ void __init simple_gpiochip_init(const c if (ret) goto err; break; + case 2: + ret = u16_simple_gpiochip_add(np); + if (ret) + goto err; + break; default: /* * Whenever you need support for GPIO bank width > 1,