18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Loongson-2F/3A/3B GPIO Support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Richard Liu, STMicroelectronics <richard.liu@st.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com> 78c2ecf20Sopenharmony_ci * Copyright (c) 2013 Hongbing Hu <huhb@lemote.com> 88c2ecf20Sopenharmony_ci * Copyright (c) 2014 Huacai Chen <chenhc@lemote.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/bitops.h> 198c2ecf20Sopenharmony_ci#include <asm/types.h> 208c2ecf20Sopenharmony_ci#include <loongson.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define STLS2F_N_GPIO 4 238c2ecf20Sopenharmony_ci#define STLS3A_N_GPIO 16 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_LOONGSON2EF 268c2ecf20Sopenharmony_ci#define LOONGSON_N_GPIO STLS2F_N_GPIO 278c2ecf20Sopenharmony_ci#else 288c2ecf20Sopenharmony_ci#define LOONGSON_N_GPIO STLS3A_N_GPIO 298c2ecf20Sopenharmony_ci#endif 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * Offset into the register where we read lines, we write them from offset 0. 338c2ecf20Sopenharmony_ci * This offset is the only thing that stand between us and using 348c2ecf20Sopenharmony_ci * GPIO_GENERIC. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#define LOONGSON_GPIO_IN_OFFSET 16 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(gpio_lock); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci u32 val; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci spin_lock(&gpio_lock); 458c2ecf20Sopenharmony_ci val = LOONGSON_GPIODATA; 468c2ecf20Sopenharmony_ci spin_unlock(&gpio_lock); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void loongson_gpio_set_value(struct gpio_chip *chip, 528c2ecf20Sopenharmony_ci unsigned gpio, int value) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u32 val; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci spin_lock(&gpio_lock); 578c2ecf20Sopenharmony_ci val = LOONGSON_GPIODATA; 588c2ecf20Sopenharmony_ci if (value) 598c2ecf20Sopenharmony_ci val |= BIT(gpio); 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci val &= ~BIT(gpio); 628c2ecf20Sopenharmony_ci LOONGSON_GPIODATA = val; 638c2ecf20Sopenharmony_ci spin_unlock(&gpio_lock); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u32 temp; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci spin_lock(&gpio_lock); 718c2ecf20Sopenharmony_ci temp = LOONGSON_GPIOIE; 728c2ecf20Sopenharmony_ci temp |= BIT(gpio); 738c2ecf20Sopenharmony_ci LOONGSON_GPIOIE = temp; 748c2ecf20Sopenharmony_ci spin_unlock(&gpio_lock); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int loongson_gpio_direction_output(struct gpio_chip *chip, 808c2ecf20Sopenharmony_ci unsigned gpio, int level) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u32 temp; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci loongson_gpio_set_value(chip, gpio, level); 858c2ecf20Sopenharmony_ci spin_lock(&gpio_lock); 868c2ecf20Sopenharmony_ci temp = LOONGSON_GPIOIE; 878c2ecf20Sopenharmony_ci temp &= ~BIT(gpio); 888c2ecf20Sopenharmony_ci LOONGSON_GPIOIE = temp; 898c2ecf20Sopenharmony_ci spin_unlock(&gpio_lock); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int loongson_gpio_probe(struct platform_device *pdev) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct gpio_chip *gc; 978c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!gc) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci gc->label = "loongson-gpio-chip"; 1048c2ecf20Sopenharmony_ci gc->base = 0; 1058c2ecf20Sopenharmony_ci gc->ngpio = LOONGSON_N_GPIO; 1068c2ecf20Sopenharmony_ci gc->get = loongson_gpio_get_value; 1078c2ecf20Sopenharmony_ci gc->set = loongson_gpio_set_value; 1088c2ecf20Sopenharmony_ci gc->direction_input = loongson_gpio_direction_input; 1098c2ecf20Sopenharmony_ci gc->direction_output = loongson_gpio_direction_output; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return gpiochip_add_data(gc, NULL); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic struct platform_driver loongson_gpio_driver = { 1158c2ecf20Sopenharmony_ci .driver = { 1168c2ecf20Sopenharmony_ci .name = "loongson-gpio", 1178c2ecf20Sopenharmony_ci }, 1188c2ecf20Sopenharmony_ci .probe = loongson_gpio_probe, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int __init loongson_gpio_setup(void) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct platform_device *pdev; 1248c2ecf20Sopenharmony_ci int ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = platform_driver_register(&loongson_gpio_driver); 1278c2ecf20Sopenharmony_ci if (ret) { 1288c2ecf20Sopenharmony_ci pr_err("error registering loongson GPIO driver\n"); 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0); 1338c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(pdev); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_cipostcore_initcall(loongson_gpio_setup); 136