]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
f5f69594 MV |
2 | /* |
3 | * Copyright (C) 2017 Marek Vasut <[email protected]> | |
f5f69594 MV |
4 | */ |
5 | ||
6 | #include <common.h> | |
7 | #include <clk.h> | |
8 | #include <dm.h> | |
336d4615 | 9 | #include <malloc.h> |
401d1c4f | 10 | #include <asm/global_data.h> |
336d4615 | 11 | #include <dm/device_compat.h> |
fbf26bea | 12 | #include <dm/pinctrl.h> |
f5f69594 MV |
13 | #include <errno.h> |
14 | #include <asm/gpio.h> | |
15 | #include <asm/io.h> | |
cd93d625 | 16 | #include <linux/bitops.h> |
52c8034b | 17 | #include "../pinctrl/renesas/sh_pfc.h" |
f5f69594 MV |
18 | |
19 | #define GPIO_IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ | |
20 | #define GPIO_INOUTSEL 0x04 /* General Input/Output Switching Register */ | |
21 | #define GPIO_OUTDT 0x08 /* General Output Register */ | |
22 | #define GPIO_INDT 0x0c /* General Input Register */ | |
23 | #define GPIO_INTDT 0x10 /* Interrupt Display Register */ | |
24 | #define GPIO_INTCLR 0x14 /* Interrupt Clear Register */ | |
25 | #define GPIO_INTMSK 0x18 /* Interrupt Mask Register */ | |
26 | #define GPIO_MSKCLR 0x1c /* Interrupt Mask Clear Register */ | |
27 | #define GPIO_POSNEG 0x20 /* Positive/Negative Logic Select Register */ | |
28 | #define GPIO_EDGLEVEL 0x24 /* Edge/level Select Register */ | |
29 | #define GPIO_FILONOFF 0x28 /* Chattering Prevention On/Off Register */ | |
30 | #define GPIO_BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ | |
31 | ||
32 | #define RCAR_MAX_GPIO_PER_BANK 32 | |
33 | ||
34 | DECLARE_GLOBAL_DATA_PTR; | |
35 | ||
36 | struct rcar_gpio_priv { | |
52c8034b MV |
37 | void __iomem *regs; |
38 | int pfc_offset; | |
f5f69594 MV |
39 | }; |
40 | ||
41 | static int rcar_gpio_get_value(struct udevice *dev, unsigned offset) | |
42 | { | |
43 | struct rcar_gpio_priv *priv = dev_get_priv(dev); | |
44 | const u32 bit = BIT(offset); | |
45 | ||
46 | /* | |
47 | * Testing on r8a7790 shows that INDT does not show correct pin state | |
48 | * when configured as output, so use OUTDT in case of output pins. | |
49 | */ | |
50 | if (readl(priv->regs + GPIO_INOUTSEL) & bit) | |
51 | return !!(readl(priv->regs + GPIO_OUTDT) & bit); | |
52 | else | |
53 | return !!(readl(priv->regs + GPIO_INDT) & bit); | |
54 | } | |
55 | ||
56 | static int rcar_gpio_set_value(struct udevice *dev, unsigned offset, | |
57 | int value) | |
58 | { | |
59 | struct rcar_gpio_priv *priv = dev_get_priv(dev); | |
60 | ||
61 | if (value) | |
62 | setbits_le32(priv->regs + GPIO_OUTDT, BIT(offset)); | |
63 | else | |
64 | clrbits_le32(priv->regs + GPIO_OUTDT, BIT(offset)); | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | static void rcar_gpio_set_direction(void __iomem *regs, unsigned offset, | |
70 | bool output) | |
71 | { | |
72 | /* | |
73 | * follow steps in the GPIO documentation for | |
74 | * "Setting General Output Mode" and | |
75 | * "Setting General Input Mode" | |
76 | */ | |
77 | ||
78 | /* Configure postive logic in POSNEG */ | |
79 | clrbits_le32(regs + GPIO_POSNEG, BIT(offset)); | |
80 | ||
81 | /* Select "General Input/Output Mode" in IOINTSEL */ | |
82 | clrbits_le32(regs + GPIO_IOINTSEL, BIT(offset)); | |
83 | ||
84 | /* Select Input Mode or Output Mode in INOUTSEL */ | |
85 | if (output) | |
86 | setbits_le32(regs + GPIO_INOUTSEL, BIT(offset)); | |
87 | else | |
88 | clrbits_le32(regs + GPIO_INOUTSEL, BIT(offset)); | |
89 | } | |
90 | ||
91 | static int rcar_gpio_direction_input(struct udevice *dev, unsigned offset) | |
92 | { | |
93 | struct rcar_gpio_priv *priv = dev_get_priv(dev); | |
94 | ||
95 | rcar_gpio_set_direction(priv->regs, offset, false); | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | static int rcar_gpio_direction_output(struct udevice *dev, unsigned offset, | |
101 | int value) | |
102 | { | |
103 | struct rcar_gpio_priv *priv = dev_get_priv(dev); | |
104 | ||
105 | /* write GPIO value to output before selecting output mode of pin */ | |
106 | rcar_gpio_set_value(dev, offset, value); | |
107 | rcar_gpio_set_direction(priv->regs, offset, true); | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | static int rcar_gpio_get_function(struct udevice *dev, unsigned offset) | |
113 | { | |
114 | struct rcar_gpio_priv *priv = dev_get_priv(dev); | |
115 | ||
116 | if (readl(priv->regs + GPIO_INOUTSEL) & BIT(offset)) | |
117 | return GPIOF_OUTPUT; | |
118 | else | |
119 | return GPIOF_INPUT; | |
120 | } | |
121 | ||
52c8034b MV |
122 | static int rcar_gpio_request(struct udevice *dev, unsigned offset, |
123 | const char *label) | |
124 | { | |
fbf26bea MV |
125 | return pinctrl_gpio_request(dev, offset); |
126 | } | |
52c8034b | 127 | |
fbf26bea MV |
128 | static int rcar_gpio_free(struct udevice *dev, unsigned offset) |
129 | { | |
130 | return pinctrl_gpio_free(dev, offset); | |
52c8034b MV |
131 | } |
132 | ||
f5f69594 | 133 | static const struct dm_gpio_ops rcar_gpio_ops = { |
52c8034b | 134 | .request = rcar_gpio_request, |
093152f2 | 135 | .rfree = rcar_gpio_free, |
f5f69594 MV |
136 | .direction_input = rcar_gpio_direction_input, |
137 | .direction_output = rcar_gpio_direction_output, | |
138 | .get_value = rcar_gpio_get_value, | |
139 | .set_value = rcar_gpio_set_value, | |
140 | .get_function = rcar_gpio_get_function, | |
141 | }; | |
142 | ||
143 | static int rcar_gpio_probe(struct udevice *dev) | |
144 | { | |
145 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
146 | struct rcar_gpio_priv *priv = dev_get_priv(dev); | |
147 | struct fdtdec_phandle_args args; | |
148 | struct clk clk; | |
149 | int node = dev_of_offset(dev); | |
150 | int ret; | |
151 | ||
8613c8d8 | 152 | priv->regs = dev_read_addr_ptr(dev); |
f5f69594 MV |
153 | uc_priv->bank_name = dev->name; |
154 | ||
155 | ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges", | |
156 | NULL, 3, 0, &args); | |
52c8034b | 157 | priv->pfc_offset = ret == 0 ? args.args[1] : -1; |
f5f69594 MV |
158 | uc_priv->gpio_count = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; |
159 | ||
160 | ret = clk_get_by_index(dev, 0, &clk); | |
161 | if (ret < 0) { | |
162 | dev_err(dev, "Failed to get GPIO bank clock\n"); | |
163 | return ret; | |
164 | } | |
165 | ||
166 | ret = clk_enable(&clk); | |
167 | clk_free(&clk); | |
168 | if (ret) { | |
169 | dev_err(dev, "Failed to enable GPIO bank clock\n"); | |
170 | return ret; | |
171 | } | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static const struct udevice_id rcar_gpio_ids[] = { | |
177 | { .compatible = "renesas,gpio-r8a7795" }, | |
178 | { .compatible = "renesas,gpio-r8a7796" }, | |
76ed8f05 | 179 | { .compatible = "renesas,gpio-r8a77965" }, |
0f2f0d89 | 180 | { .compatible = "renesas,gpio-r8a77970" }, |
60ae40c2 | 181 | { .compatible = "renesas,gpio-r8a77990" }, |
f122c13b | 182 | { .compatible = "renesas,gpio-r8a77995" }, |
8b05436f | 183 | { .compatible = "renesas,rcar-gen2-gpio" }, |
e3ab4248 | 184 | { .compatible = "renesas,rcar-gen3-gpio" }, |
f5f69594 MV |
185 | { /* sentinel */ } |
186 | }; | |
187 | ||
188 | U_BOOT_DRIVER(rcar_gpio) = { | |
189 | .name = "rcar-gpio", | |
190 | .id = UCLASS_GPIO, | |
191 | .of_match = rcar_gpio_ids, | |
192 | .ops = &rcar_gpio_ops, | |
41575d8e | 193 | .priv_auto = sizeof(struct rcar_gpio_priv), |
f5f69594 MV |
194 | .probe = rcar_gpio_probe, |
195 | }; |