gpio-loongson.c (3031B)
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 38static DEFINE_SPINLOCK(gpio_lock); 39 40static 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 51static 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 66static 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 79static 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 94static 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 114static struct platform_driver loongson_gpio_driver = { 115 .driver = { 116 .name = "loongson-gpio", 117 }, 118 .probe = loongson_gpio_probe, 119}; 120 121static 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} 135postcore_initcall(loongson_gpio_setup);