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