1 /* 2 * Loongson-2F/3A/3B GPIO Support 3 * 4 * Copyright (c) 2008 Richard Liu, STMicroelectronics <richard.liu@st.com> 5 * Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com> 6 * Copyright (c) 2013 Hongbing Hu <huhb@lemote.com> 7 * Copyright (c) 2014 Huacai Chen <chenhc@lemote.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 */ 14 15 #include <linux/kernel.h> 16 #include <linux/init.h> 17 #include <linux/module.h> 18 #include <linux/spinlock.h> 19 #include <linux/err.h> 20 #include <linux/gpio/driver.h> 21 #include <linux/platform_device.h> 22 #include <linux/bitops.h> 23 #include <asm/types.h> 24 #include <loongson.h> 25 26 #define STLS2F_N_GPIO 4 27 #define STLS3A_N_GPIO 16 28 29 #ifdef CONFIG_CPU_LOONGSON3 30 #define LOONGSON_N_GPIO STLS3A_N_GPIO 31 #else 32 #define LOONGSON_N_GPIO STLS2F_N_GPIO 33 #endif 34 35 /* 36 * Offset into the register where we read lines, we write them from offset 0. 37 * This offset is the only thing that stand between us and using 38 * GPIO_GENERIC. 39 */ 40 #define LOONGSON_GPIO_IN_OFFSET 16 41 42 static DEFINE_SPINLOCK(gpio_lock); 43 44 static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) 45 { 46 u32 val; 47 48 spin_lock(&gpio_lock); 49 val = LOONGSON_GPIODATA; 50 spin_unlock(&gpio_lock); 51 52 return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); 53 } 54 55 static void loongson_gpio_set_value(struct gpio_chip *chip, 56 unsigned gpio, int value) 57 { 58 u32 val; 59 60 spin_lock(&gpio_lock); 61 val = LOONGSON_GPIODATA; 62 if (value) 63 val |= BIT(gpio); 64 else 65 val &= ~BIT(gpio); 66 LOONGSON_GPIODATA = val; 67 spin_unlock(&gpio_lock); 68 } 69 70 static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 71 { 72 u32 temp; 73 74 spin_lock(&gpio_lock); 75 temp = LOONGSON_GPIOIE; 76 temp |= BIT(gpio); 77 LOONGSON_GPIOIE = temp; 78 spin_unlock(&gpio_lock); 79 80 return 0; 81 } 82 83 static int loongson_gpio_direction_output(struct gpio_chip *chip, 84 unsigned gpio, int level) 85 { 86 u32 temp; 87 88 loongson_gpio_set_value(chip, gpio, level); 89 spin_lock(&gpio_lock); 90 temp = LOONGSON_GPIOIE; 91 temp &= ~BIT(gpio); 92 LOONGSON_GPIOIE = temp; 93 spin_unlock(&gpio_lock); 94 95 return 0; 96 } 97 98 static int loongson_gpio_probe(struct platform_device *pdev) 99 { 100 struct gpio_chip *gc; 101 struct device *dev = &pdev->dev; 102 103 gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); 104 if (!gc) 105 return -ENOMEM; 106 107 gc->label = "loongson-gpio-chip"; 108 gc->base = 0; 109 gc->ngpio = LOONGSON_N_GPIO; 110 gc->get = loongson_gpio_get_value; 111 gc->set = loongson_gpio_set_value; 112 gc->direction_input = loongson_gpio_direction_input; 113 gc->direction_output = loongson_gpio_direction_output; 114 115 return gpiochip_add_data(gc, NULL); 116 } 117 118 static struct platform_driver loongson_gpio_driver = { 119 .driver = { 120 .name = "loongson-gpio", 121 }, 122 .probe = loongson_gpio_probe, 123 }; 124 125 static int __init loongson_gpio_setup(void) 126 { 127 struct platform_device *pdev; 128 int ret; 129 130 ret = platform_driver_register(&loongson_gpio_driver); 131 if (ret) { 132 pr_err("error registering loongson GPIO driver\n"); 133 return ret; 134 } 135 136 pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0); 137 return PTR_ERR_OR_ZERO(pdev); 138 } 139 postcore_initcall(loongson_gpio_setup); 140