xref: /openbmc/linux/drivers/gpio/gpio-loongson1.c (revision 9bc167e2)
1a9b4678aSKeguang Zhang // SPDX-License-Identifier: GPL-2.0-only
2bd37c999SKelvin Cheung /*
3bd37c999SKelvin Cheung  * GPIO Driver for Loongson 1 SoC
4bd37c999SKelvin Cheung  *
5a9b4678aSKeguang Zhang  * Copyright (C) 2015-2023 Keguang Zhang <keguang.zhang@gmail.com>
6bd37c999SKelvin Cheung  */
7bd37c999SKelvin Cheung 
85f604506SPaul Gortmaker #include <linux/module.h>
9bd37c999SKelvin Cheung #include <linux/gpio/driver.h>
10bd37c999SKelvin Cheung #include <linux/platform_device.h>
11fe29416bSLinus Walleij #include <linux/bitops.h>
12bd37c999SKelvin Cheung 
13bd37c999SKelvin Cheung /* Loongson 1 GPIO Register Definitions */
14bd37c999SKelvin Cheung #define GPIO_CFG		0x0
15bd37c999SKelvin Cheung #define GPIO_DIR		0x10
16bd37c999SKelvin Cheung #define GPIO_DATA		0x20
17bd37c999SKelvin Cheung #define GPIO_OUTPUT		0x30
18bd37c999SKelvin Cheung 
1931b6ec67SKeguang Zhang struct ls1x_gpio_chip {
2031b6ec67SKeguang Zhang 	struct gpio_chip gc;
2131b6ec67SKeguang Zhang 	void __iomem *reg_base;
2231b6ec67SKeguang Zhang };
23bd37c999SKelvin Cheung 
ls1x_gpio_request(struct gpio_chip * gc,unsigned int offset)24bd37c999SKelvin Cheung static int ls1x_gpio_request(struct gpio_chip *gc, unsigned int offset)
25bd37c999SKelvin Cheung {
2631b6ec67SKeguang Zhang 	struct ls1x_gpio_chip *ls1x_gc = gpiochip_get_data(gc);
27bd37c999SKelvin Cheung 	unsigned long flags;
28bd37c999SKelvin Cheung 
293c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
3031b6ec67SKeguang Zhang 	__raw_writel(__raw_readl(ls1x_gc->reg_base + GPIO_CFG) | BIT(offset),
3131b6ec67SKeguang Zhang 		     ls1x_gc->reg_base + GPIO_CFG);
323c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
33bd37c999SKelvin Cheung 
34bd37c999SKelvin Cheung 	return 0;
35bd37c999SKelvin Cheung }
36bd37c999SKelvin Cheung 
ls1x_gpio_free(struct gpio_chip * gc,unsigned int offset)37bd37c999SKelvin Cheung static void ls1x_gpio_free(struct gpio_chip *gc, unsigned int offset)
38bd37c999SKelvin Cheung {
3931b6ec67SKeguang Zhang 	struct ls1x_gpio_chip *ls1x_gc = gpiochip_get_data(gc);
40bd37c999SKelvin Cheung 	unsigned long flags;
41bd37c999SKelvin Cheung 
423c938cc5SSchspa Shi 	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
4331b6ec67SKeguang Zhang 	__raw_writel(__raw_readl(ls1x_gc->reg_base + GPIO_CFG) & ~BIT(offset),
4431b6ec67SKeguang Zhang 		     ls1x_gc->reg_base + GPIO_CFG);
453c938cc5SSchspa Shi 	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
46bd37c999SKelvin Cheung }
47bd37c999SKelvin Cheung 
ls1x_gpio_probe(struct platform_device * pdev)48bd37c999SKelvin Cheung static int ls1x_gpio_probe(struct platform_device *pdev)
49bd37c999SKelvin Cheung {
50bd37c999SKelvin Cheung 	struct device *dev = &pdev->dev;
5131b6ec67SKeguang Zhang 	struct ls1x_gpio_chip *ls1x_gc;
52bd37c999SKelvin Cheung 	int ret;
53bd37c999SKelvin Cheung 
5431b6ec67SKeguang Zhang 	ls1x_gc = devm_kzalloc(dev, sizeof(*ls1x_gc), GFP_KERNEL);
5531b6ec67SKeguang Zhang 	if (!ls1x_gc)
56bd37c999SKelvin Cheung 		return -ENOMEM;
57bd37c999SKelvin Cheung 
5831b6ec67SKeguang Zhang 	ls1x_gc->reg_base = devm_platform_ioremap_resource(pdev, 0);
5931b6ec67SKeguang Zhang 	if (IS_ERR(ls1x_gc->reg_base))
6031b6ec67SKeguang Zhang 		return PTR_ERR(ls1x_gc->reg_base);
61bd37c999SKelvin Cheung 
6231b6ec67SKeguang Zhang 	ret = bgpio_init(&ls1x_gc->gc, dev, 4, ls1x_gc->reg_base + GPIO_DATA,
6331b6ec67SKeguang Zhang 			 ls1x_gc->reg_base + GPIO_OUTPUT, NULL,
6431b6ec67SKeguang Zhang 			 NULL, ls1x_gc->reg_base + GPIO_DIR, 0);
65bd37c999SKelvin Cheung 	if (ret)
66bd37c999SKelvin Cheung 		goto err;
67bd37c999SKelvin Cheung 
6831b6ec67SKeguang Zhang 	ls1x_gc->gc.owner = THIS_MODULE;
6931b6ec67SKeguang Zhang 	ls1x_gc->gc.request = ls1x_gpio_request;
7031b6ec67SKeguang Zhang 	ls1x_gc->gc.free = ls1x_gpio_free;
71*9bc167e2SKeguang Zhang 	/*
72*9bc167e2SKeguang Zhang 	 * Clear ngpio to let gpiolib get the correct number
73*9bc167e2SKeguang Zhang 	 * by reading ngpios property
74*9bc167e2SKeguang Zhang 	 */
75*9bc167e2SKeguang Zhang 	ls1x_gc->gc.ngpio = 0;
76bd37c999SKelvin Cheung 
7731b6ec67SKeguang Zhang 	ret = devm_gpiochip_add_data(dev, &ls1x_gc->gc, ls1x_gc);
78bd37c999SKelvin Cheung 	if (ret)
79bd37c999SKelvin Cheung 		goto err;
80bd37c999SKelvin Cheung 
8131b6ec67SKeguang Zhang 	platform_set_drvdata(pdev, ls1x_gc);
82*9bc167e2SKeguang Zhang 
83*9bc167e2SKeguang Zhang 	dev_info(dev, "GPIO controller registered with %d pins\n",
84*9bc167e2SKeguang Zhang 		 ls1x_gc->gc.ngpio);
85bd37c999SKelvin Cheung 
86bd37c999SKelvin Cheung 	return 0;
87bd37c999SKelvin Cheung err:
88*9bc167e2SKeguang Zhang 	dev_err(dev, "failed to register GPIO controller\n");
89bd37c999SKelvin Cheung 	return ret;
90bd37c999SKelvin Cheung }
91bd37c999SKelvin Cheung 
92*9bc167e2SKeguang Zhang static const struct of_device_id ls1x_gpio_dt_ids[] = {
93*9bc167e2SKeguang Zhang 	{ .compatible = "loongson,ls1x-gpio" },
94*9bc167e2SKeguang Zhang 	{ /* sentinel */ }
95*9bc167e2SKeguang Zhang };
96*9bc167e2SKeguang Zhang MODULE_DEVICE_TABLE(of, ls1x_gpio_dt_ids);
97*9bc167e2SKeguang Zhang 
98bd37c999SKelvin Cheung static struct platform_driver ls1x_gpio_driver = {
99bd37c999SKelvin Cheung 	.probe	= ls1x_gpio_probe,
100bd37c999SKelvin Cheung 	.driver	= {
101bd37c999SKelvin Cheung 		.name	= "ls1x-gpio",
102*9bc167e2SKeguang Zhang 		.of_match_table = ls1x_gpio_dt_ids,
103bd37c999SKelvin Cheung 	},
104bd37c999SKelvin Cheung };
105bd37c999SKelvin Cheung 
106bd37c999SKelvin Cheung module_platform_driver(ls1x_gpio_driver);
107bd37c999SKelvin Cheung 
108a9b4678aSKeguang Zhang MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
109bd37c999SKelvin Cheung MODULE_DESCRIPTION("Loongson1 GPIO driver");
110bd37c999SKelvin Cheung MODULE_LICENSE("GPL");
111