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 int loongson_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, 52 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 return 0; 66 } 67 68 static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 69 { 70 u32 temp; 71 72 spin_lock(&gpio_lock); 73 temp = LOONGSON_GPIOIE; 74 temp |= BIT(gpio); 75 LOONGSON_GPIOIE = temp; 76 spin_unlock(&gpio_lock); 77 78 return 0; 79 } 80 81 static int loongson_gpio_direction_output(struct gpio_chip *chip, 82 unsigned gpio, int level) 83 { 84 u32 temp; 85 86 loongson_gpio_set_value(chip, gpio, level); 87 spin_lock(&gpio_lock); 88 temp = LOONGSON_GPIOIE; 89 temp &= ~BIT(gpio); 90 LOONGSON_GPIOIE = temp; 91 spin_unlock(&gpio_lock); 92 93 return 0; 94 } 95 96 static int loongson_gpio_probe(struct platform_device *pdev) 97 { 98 struct gpio_chip *gc; 99 struct device *dev = &pdev->dev; 100 101 gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); 102 if (!gc) 103 return -ENOMEM; 104 105 gc->label = "loongson-gpio-chip"; 106 gc->base = 0; 107 gc->ngpio = LOONGSON_N_GPIO; 108 gc->get = loongson_gpio_get_value; 109 gc->set_rv = loongson_gpio_set_value; 110 gc->direction_input = loongson_gpio_direction_input; 111 gc->direction_output = loongson_gpio_direction_output; 112 113 return gpiochip_add_data(gc, NULL); 114 } 115 116 static struct platform_driver loongson_gpio_driver = { 117 .driver = { 118 .name = "loongson-gpio", 119 }, 120 .probe = loongson_gpio_probe, 121 }; 122 123 static int __init loongson_gpio_setup(void) 124 { 125 struct platform_device *pdev; 126 int ret; 127 128 ret = platform_driver_register(&loongson_gpio_driver); 129 if (ret) { 130 pr_err("error registering loongson GPIO driver\n"); 131 return ret; 132 } 133 134 pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0); 135 return PTR_ERR_OR_ZERO(pdev); 136 } 137 postcore_initcall(loongson_gpio_setup); 138