Loading...
Note: File does not exist in v3.1.
1// SPDX-License-Identifier: (GPL-2.0)
2/*
3 * Microchip PolarFire SoC (MPFS) GPIO controller driver
4 *
5 * Copyright (c) 2018-2024 Microchip Technology Inc. and its subsidiaries
6 */
7
8#include <linux/clk.h>
9#include <linux/device.h>
10#include <linux/errno.h>
11#include <linux/gpio/driver.h>
12#include <linux/init.h>
13#include <linux/mod_devicetable.h>
14#include <linux/platform_device.h>
15#include <linux/property.h>
16#include <linux/regmap.h>
17#include <linux/spinlock.h>
18
19#define MPFS_GPIO_CTRL(i) (0x4 * (i))
20#define MPFS_MAX_NUM_GPIO 32
21#define MPFS_GPIO_EN_INT 3
22#define MPFS_GPIO_EN_OUT_BUF BIT(2)
23#define MPFS_GPIO_EN_IN BIT(1)
24#define MPFS_GPIO_EN_OUT BIT(0)
25#define MPFS_GPIO_DIR_MASK GENMASK(2, 0)
26
27#define MPFS_GPIO_TYPE_INT_EDGE_BOTH 0x80
28#define MPFS_GPIO_TYPE_INT_EDGE_NEG 0x60
29#define MPFS_GPIO_TYPE_INT_EDGE_POS 0x40
30#define MPFS_GPIO_TYPE_INT_LEVEL_LOW 0x20
31#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH 0x00
32#define MPFS_GPIO_TYPE_INT_MASK GENMASK(7, 5)
33#define MPFS_IRQ_REG 0x80
34
35#define MPFS_INP_REG 0x84
36#define COREGPIO_INP_REG 0x90
37#define MPFS_OUTP_REG 0x88
38#define COREGPIO_OUTP_REG 0xA0
39
40struct mpfs_gpio_reg_offsets {
41 u8 inp;
42 u8 outp;
43};
44
45struct mpfs_gpio_chip {
46 struct regmap *regs;
47 const struct mpfs_gpio_reg_offsets *offsets;
48 struct gpio_chip gc;
49};
50
51static const struct regmap_config mpfs_gpio_regmap_config = {
52 .reg_bits = 32,
53 .reg_stride = 4,
54 .val_bits = 32,
55};
56
57static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index)
58{
59 struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
60
61 regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
62 MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
63
64 return 0;
65}
66
67static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value)
68{
69 struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
70
71 regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
72 MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
73 regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index),
74 value << gpio_index);
75
76 return 0;
77}
78
79static int mpfs_gpio_get_direction(struct gpio_chip *gc,
80 unsigned int gpio_index)
81{
82 struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
83 unsigned int gpio_cfg;
84
85 regmap_read(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), &gpio_cfg);
86 if (gpio_cfg & MPFS_GPIO_EN_IN)
87 return GPIO_LINE_DIRECTION_IN;
88
89 return GPIO_LINE_DIRECTION_OUT;
90}
91
92static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index)
93{
94 struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
95
96 if (mpfs_gpio_get_direction(gc, gpio_index) == GPIO_LINE_DIRECTION_OUT)
97 return regmap_test_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index));
98 else
99 return regmap_test_bits(mpfs_gpio->regs, mpfs_gpio->offsets->inp, BIT(gpio_index));
100}
101
102static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value)
103{
104 struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
105
106 mpfs_gpio_get(gc, gpio_index);
107
108 regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index),
109 value << gpio_index);
110
111 mpfs_gpio_get(gc, gpio_index);
112}
113
114static int mpfs_gpio_probe(struct platform_device *pdev)
115{
116 struct device *dev = &pdev->dev;
117 struct mpfs_gpio_chip *mpfs_gpio;
118 struct clk *clk;
119 void __iomem *base;
120 int ngpios;
121
122 mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL);
123 if (!mpfs_gpio)
124 return -ENOMEM;
125
126 mpfs_gpio->offsets = device_get_match_data(&pdev->dev);
127
128 base = devm_platform_ioremap_resource(pdev, 0);
129 if (IS_ERR(base))
130 return dev_err_probe(dev, PTR_ERR(base), "failed to ioremap memory resource\n");
131
132 mpfs_gpio->regs = devm_regmap_init_mmio(dev, base, &mpfs_gpio_regmap_config);
133 if (IS_ERR(mpfs_gpio->regs))
134 return dev_err_probe(dev, PTR_ERR(mpfs_gpio->regs),
135 "failed to initialise regmap\n");
136
137 clk = devm_clk_get_enabled(dev, NULL);
138 if (IS_ERR(clk))
139 return dev_err_probe(dev, PTR_ERR(clk), "failed to get and enable clock\n");
140
141 ngpios = MPFS_MAX_NUM_GPIO;
142 device_property_read_u32(dev, "ngpios", &ngpios);
143 if (ngpios > MPFS_MAX_NUM_GPIO)
144 ngpios = MPFS_MAX_NUM_GPIO;
145
146 mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
147 mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
148 mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
149 mpfs_gpio->gc.get = mpfs_gpio_get;
150 mpfs_gpio->gc.set = mpfs_gpio_set;
151 mpfs_gpio->gc.base = -1;
152 mpfs_gpio->gc.ngpio = ngpios;
153 mpfs_gpio->gc.label = dev_name(dev);
154 mpfs_gpio->gc.parent = dev;
155 mpfs_gpio->gc.owner = THIS_MODULE;
156
157 return devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio);
158}
159
160static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = {
161 .inp = MPFS_INP_REG,
162 .outp = MPFS_OUTP_REG,
163};
164
165static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = {
166 .inp = COREGPIO_INP_REG,
167 .outp = COREGPIO_OUTP_REG,
168};
169
170static const struct of_device_id mpfs_gpio_of_ids[] = {
171 {
172 .compatible = "microchip,mpfs-gpio",
173 .data = &mpfs_reg_offsets,
174 }, {
175 .compatible = "microchip,coregpio-rtl-v3",
176 .data = &coregpio_reg_offsets,
177 },
178 { /* end of list */ }
179};
180
181static struct platform_driver mpfs_gpio_driver = {
182 .probe = mpfs_gpio_probe,
183 .driver = {
184 .name = "microchip,mpfs-gpio",
185 .of_match_table = mpfs_gpio_of_ids,
186 },
187};
188builtin_platform_driver(mpfs_gpio_driver);