diff options
Diffstat (limited to 'drivers/gpio/gpio-loongson.c')
-rw-r--r-- | drivers/gpio/gpio-loongson.c | 116 |
1 files changed, 70 insertions, 46 deletions
diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c index 92c4fe7b2677..16cfbe9e72fe 100644 --- a/drivers/gpio/gpio-loongson.c +++ b/drivers/gpio/gpio-loongson.c @@ -17,9 +17,11 @@ #include <linux/module.h> #include <linux/spinlock.h> #include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/bitops.h> #include <asm/types.h> #include <loongson.h> -#include <linux/gpio.h> #define STLS2F_N_GPIO 4 #define STLS3A_N_GPIO 16 @@ -30,86 +32,108 @@ #define LOONGSON_N_GPIO STLS2F_N_GPIO #endif +/* + * Offset into the register where we read lines, we write them from offset 0. + * This offset is the only thing that stand between us and using + * GPIO_GENERIC. + */ #define LOONGSON_GPIO_IN_OFFSET 16 static DEFINE_SPINLOCK(gpio_lock); -static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) { - u32 temp; - u32 mask; + u32 val; spin_lock(&gpio_lock); - mask = 1 << gpio; - temp = LOONGSON_GPIOIE; - temp |= mask; - LOONGSON_GPIOIE = temp; + val = LOONGSON_GPIODATA; spin_unlock(&gpio_lock); - return 0; + return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); } -static int loongson_gpio_direction_output(struct gpio_chip *chip, - unsigned gpio, int level) +static void loongson_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int value) +{ + u32 val; + + spin_lock(&gpio_lock); + val = LOONGSON_GPIODATA; + if (value) + val |= BIT(gpio); + else + val &= ~BIT(gpio); + LOONGSON_GPIODATA = val; + spin_unlock(&gpio_lock); +} + +static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { u32 temp; - u32 mask; - gpio_set_value(gpio, level); spin_lock(&gpio_lock); - mask = 1 << gpio; temp = LOONGSON_GPIOIE; - temp &= (~mask); + temp |= BIT(gpio); LOONGSON_GPIOIE = temp; spin_unlock(&gpio_lock); return 0; } -static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static int loongson_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int level) { - u32 val; - u32 mask; + u32 temp; - mask = 1 << (gpio + LOONGSON_GPIO_IN_OFFSET); + loongson_gpio_set_value(chip, gpio, level); spin_lock(&gpio_lock); - val = LOONGSON_GPIODATA; + temp = LOONGSON_GPIOIE; + temp &= ~BIT(gpio); + LOONGSON_GPIOIE = temp; spin_unlock(&gpio_lock); - return (val & mask) != 0; + return 0; } -static void loongson_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int loongson_gpio_probe(struct platform_device *pdev) { - u32 val; - u32 mask; - - mask = 1 << gpio; - - spin_lock(&gpio_lock); - val = LOONGSON_GPIODATA; - if (value) - val |= mask; - else - val &= (~mask); - LOONGSON_GPIODATA = val; - spin_unlock(&gpio_lock); + struct gpio_chip *gc; + struct device *dev = &pdev->dev; + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->label = "loongson-gpio-chip"; + gc->base = 0; + gc->ngpio = LOONGSON_N_GPIO; + gc->get = loongson_gpio_get_value; + gc->set = loongson_gpio_set_value; + gc->direction_input = loongson_gpio_direction_input; + gc->direction_output = loongson_gpio_direction_output; + + return gpiochip_add_data(gc, NULL); } -static struct gpio_chip loongson_chip = { - .label = "Loongson-gpio-chip", - .direction_input = loongson_gpio_direction_input, - .get = loongson_gpio_get_value, - .direction_output = loongson_gpio_direction_output, - .set = loongson_gpio_set_value, - .base = 0, - .ngpio = LOONGSON_N_GPIO, - .can_sleep = false, +static struct platform_driver loongson_gpio_driver = { + .driver = { + .name = "loongson-gpio", + }, + .probe = loongson_gpio_probe, }; static int __init loongson_gpio_setup(void) { - return gpiochip_add_data(&loongson_chip, NULL); + struct platform_device *pdev; + int ret; + + ret = platform_driver_register(&loongson_gpio_driver); + if (ret) { + pr_err("error registering loongson GPIO driver\n"); + return ret; + } + + pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0); + return PTR_ERR_OR_ZERO(pdev); } postcore_initcall(loongson_gpio_setup); |