Skip to content

Commit d32d9ce

Browse files
committed
pinctrl: Add support for firmware-controlled GPIOs
Several Raspberry Pi models have an I2C-attached GPIO expander, controlled by the firmware. Add a driver for that uses the vcmailbox API to query and control those GPIOs. Signed-off-by: Phil Elwell <[email protected]>
1 parent 590c061 commit d32d9ce

File tree

3 files changed

+326
-1
lines changed

3 files changed

+326
-1
lines changed

pinctrl/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ project(pinctrl)
88

99
add_compile_definitions(LIBRARY_BUILD=1)
1010

11-
add_library(gpiolib gpiolib.c util.c library_gpiochips.c gpiochip_bcm2835.c gpiochip_bcm2712.c gpiochip_rp1.c)
11+
add_library(gpiolib gpiolib.c util.c library_gpiochips.c gpiochip_bcm2835.c gpiochip_bcm2712.c gpiochip_rp1.c gpiochip_firmware.c)
1212
target_sources(gpiolib PUBLIC gpiolib.h)
1313
set_target_properties(gpiolib PROPERTIES PUBLIC_HEADER gpiolib.h)
1414
set_target_properties(gpiolib PROPERTIES SOVERSION 0)

pinctrl/gpiochip_firmware.c

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
#include <ctype.h>
2+
#include <errno.h>
3+
#include <fcntl.h>
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <sys/ioctl.h>
8+
#include <unistd.h>
9+
10+
#include "gpiochip.h"
11+
#include "util.h"
12+
13+
#define DEVICE_FILE_NAME "/dev/vcio"
14+
#define MAJOR_NUM 100
15+
16+
#define NUM_GPIOS 8
17+
#define RPI_EXP_GPIO_BASE 128
18+
19+
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
20+
21+
#define RPI_FIRMWARE_STATUS_REQUEST 0
22+
#define RPI_FIRMWARE_STATUS_SUCCESS 0x80000000
23+
#define RPI_FIRMWARE_STATUS_ERROR 0x80000001
24+
25+
#define RPI_FIRMWARE_PROPERTY_END 0
26+
#define RPI_FIRMWARE_GET_GPIO_STATE 0x00030041
27+
#define RPI_FIRMWARE_SET_GPIO_STATE 0x00038041
28+
#define RPI_FIRMWARE_GET_GPIO_CONFIG 0x00030043
29+
#define RPI_FIRMWARE_SET_GPIO_CONFIG 0x00038043
30+
31+
struct firmware_inst
32+
{
33+
unsigned num_gpios;
34+
int mbox_fd;
35+
};
36+
37+
struct gpio_config
38+
{
39+
uint32_t direction;
40+
uint32_t polarity;
41+
uint32_t term_en;
42+
uint32_t term_pull_up;
43+
uint32_t drive;
44+
};
45+
46+
struct gpio_get_set_config
47+
{
48+
uint32_t gpio;
49+
struct gpio_config config;
50+
};
51+
52+
struct gpio_get_set_state
53+
{
54+
uint32_t gpio;
55+
uint32_t state;
56+
};
57+
58+
static struct firmware_inst firmware_instance;
59+
60+
static int firmware_property(struct firmware_inst *inst, uint32_t tag, void *tag_data, int tag_size)
61+
{
62+
uint32_t buf[16];
63+
int words = 5 + (tag_size + 3) / 4 + 1;
64+
int err;
65+
66+
if (words > (int)ARRAY_SIZE(buf))
67+
return -1;
68+
if (!inst->mbox_fd)
69+
inst->mbox_fd = open(DEVICE_FILE_NAME, 0);
70+
if (inst->mbox_fd < 0)
71+
return -1;
72+
73+
buf[0] = words * sizeof(buf[0]);
74+
buf[1] = RPI_FIRMWARE_STATUS_REQUEST; // process request
75+
buf[2] = tag;
76+
buf[3] = tag_size;
77+
buf[4] = tag_size; // set to response length
78+
memcpy(&buf[5], tag_data, tag_size);
79+
buf[words - 1] = RPI_FIRMWARE_PROPERTY_END;
80+
err = ioctl(inst->mbox_fd, IOCTL_MBOX_PROPERTY, buf);
81+
if (!err)
82+
{
83+
if (buf[4] & RPI_FIRMWARE_STATUS_SUCCESS)
84+
memcpy(tag_data, &buf[5], buf[4] & ~RPI_FIRMWARE_STATUS_SUCCESS);
85+
else
86+
err = -EREMOTEIO;
87+
}
88+
return err;
89+
}
90+
91+
static int firmware_get_gpio_state(struct firmware_inst *inst, unsigned gpio)
92+
{
93+
struct gpio_get_set_state prop;
94+
prop.gpio = RPI_EXP_GPIO_BASE + gpio;
95+
if (firmware_property(inst, RPI_FIRMWARE_GET_GPIO_STATE, &prop, sizeof(prop)))
96+
return -1;
97+
return prop.state;
98+
}
99+
100+
static int firmware_set_gpio_state(struct firmware_inst *inst, unsigned gpio, unsigned state)
101+
{
102+
struct gpio_get_set_state prop;
103+
prop.gpio = RPI_EXP_GPIO_BASE + gpio;
104+
prop.state = state;
105+
return firmware_property(inst, RPI_FIRMWARE_SET_GPIO_STATE, &prop, sizeof(prop));
106+
}
107+
108+
static int firmware_get_gpio_config(struct firmware_inst *inst, int gpio, struct gpio_config *config)
109+
{
110+
struct gpio_get_set_config prop;
111+
prop.gpio = RPI_EXP_GPIO_BASE + gpio;
112+
prop.config.drive = ~0;
113+
if (firmware_property(inst, RPI_FIRMWARE_GET_GPIO_CONFIG, &prop, sizeof(prop)) < 0)
114+
return -1;
115+
if (prop.config.drive == ~0u)
116+
prop.config.drive = firmware_get_gpio_state(inst, gpio);
117+
*config = prop.config;
118+
return 0;
119+
}
120+
121+
static int firmware_set_gpio_config(struct firmware_inst *inst, int gpio, const struct gpio_config *config)
122+
{
123+
struct gpio_get_set_config prop;
124+
prop.gpio = RPI_EXP_GPIO_BASE + gpio;
125+
prop.config = *config;
126+
return firmware_property(inst, RPI_FIRMWARE_SET_GPIO_CONFIG, &prop, sizeof(prop));
127+
}
128+
129+
static GPIO_DIR_T firmware_gpio_get_dir(void *priv, unsigned gpio)
130+
{
131+
struct firmware_inst *inst = priv;
132+
struct gpio_config config;
133+
if (gpio < inst->num_gpios &&
134+
!firmware_get_gpio_config(inst, gpio, &config))
135+
return (config.direction == 1) ? DIR_OUTPUT : DIR_INPUT;
136+
return DIR_MAX;
137+
}
138+
139+
static void firmware_gpio_set_dir(void *priv, unsigned gpio, GPIO_DIR_T dir)
140+
{
141+
struct firmware_inst *inst = priv;
142+
struct gpio_config config;
143+
144+
if (gpio >= inst->num_gpios)
145+
return;
146+
147+
if (!firmware_get_gpio_config(inst, gpio, &config))
148+
{
149+
if (dir != config.direction)
150+
{
151+
config.direction = dir;
152+
firmware_set_gpio_config(inst, gpio, &config);
153+
}
154+
}
155+
}
156+
157+
static GPIO_FSEL_T firmware_gpio_get_fsel(void *priv, unsigned gpio)
158+
{
159+
GPIO_DIR_T dir = firmware_gpio_get_dir(priv, gpio);
160+
if (dir == DIR_INPUT)
161+
return GPIO_FSEL_INPUT;
162+
else if (dir == DIR_OUTPUT)
163+
return GPIO_FSEL_OUTPUT;
164+
else
165+
return GPIO_FSEL_MAX;
166+
}
167+
168+
static void firmware_gpio_set_fsel(void *priv, unsigned gpio, const GPIO_FSEL_T func)
169+
{
170+
GPIO_DIR_T dir;
171+
172+
switch (func)
173+
{
174+
case GPIO_FSEL_INPUT: dir = DIR_INPUT; break;
175+
case GPIO_FSEL_OUTPUT: dir = DIR_OUTPUT; break;
176+
default:
177+
return;
178+
}
179+
180+
firmware_gpio_set_dir(priv, gpio, dir);
181+
}
182+
183+
static int firmware_gpio_get_level(void *priv, unsigned gpio)
184+
{
185+
struct firmware_inst *inst = priv;
186+
187+
if (gpio >= inst->num_gpios)
188+
return -1;
189+
190+
return firmware_get_gpio_state(inst, gpio);
191+
}
192+
193+
GPIO_DRIVE_T firmware_gpio_get_drive(void *priv, unsigned gpio)
194+
{
195+
struct firmware_inst *inst = priv;
196+
struct gpio_config config;
197+
if (!firmware_get_gpio_config(inst, gpio, &config))
198+
{
199+
if (config.direction == 1)
200+
return config.drive ? DRIVE_HIGH : DRIVE_LOW;
201+
}
202+
return DRIVE_MAX;
203+
}
204+
205+
static void firmware_gpio_set_drive(void *priv, unsigned gpio, GPIO_DRIVE_T drv)
206+
{
207+
struct firmware_inst *inst = priv;
208+
209+
if (gpio >= inst->num_gpios)
210+
return;
211+
212+
firmware_set_gpio_state(inst, gpio, drv == DRIVE_HIGH);
213+
}
214+
215+
static GPIO_PULL_T firmware_gpio_get_pull(void *priv, unsigned gpio)
216+
{
217+
struct firmware_inst *inst = priv;
218+
struct gpio_config config;
219+
if (!firmware_get_gpio_config(inst, gpio, &config))
220+
{
221+
if (!config.term_en)
222+
return PULL_NONE;
223+
return config.term_pull_up ? PULL_UP : PULL_DOWN;
224+
}
225+
return PULL_MAX;
226+
}
227+
228+
static void firmware_gpio_set_pull(void *priv, unsigned gpio, GPIO_PULL_T pull)
229+
{
230+
struct firmware_inst *inst = priv;
231+
struct gpio_config config;
232+
uint32_t term_en, term_pull_up;
233+
234+
switch (pull)
235+
{
236+
case PULL_NONE:
237+
term_en = 0;
238+
term_pull_up = 0;
239+
break;
240+
case PULL_DOWN:
241+
term_en = 1;
242+
term_pull_up = 0;
243+
break;
244+
case PULL_UP:
245+
term_en = 1;
246+
term_pull_up = 1;
247+
break;
248+
default:
249+
return;
250+
}
251+
252+
if (!firmware_get_gpio_config(inst, gpio, &config))
253+
{
254+
if (term_en != config.term_en ||
255+
term_pull_up != config.term_pull_up)
256+
{
257+
config.term_en = term_en;
258+
config.term_pull_up = term_pull_up;
259+
firmware_set_gpio_config(inst, gpio, &config);
260+
}
261+
}
262+
}
263+
264+
static const char *firmware_gpio_get_name(void *priv, unsigned gpio)
265+
{
266+
struct firmware_inst *inst = priv;
267+
static char name_buf[16];
268+
if (gpio >= inst->num_gpios)
269+
return NULL;
270+
sprintf(name_buf, "FWGPIO%d", gpio);
271+
return name_buf;
272+
}
273+
274+
static const char *firmware_gpio_get_fsel_name(void *priv, unsigned gpio, GPIO_FSEL_T fsel)
275+
{
276+
UNUSED(priv);
277+
UNUSED(gpio);
278+
switch (fsel)
279+
{
280+
case GPIO_FSEL_INPUT:
281+
return "input";
282+
case GPIO_FSEL_OUTPUT:
283+
return "output";
284+
default:
285+
return NULL;
286+
}
287+
}
288+
289+
static void *firmware_gpio_create_instance(const GPIO_CHIP_T *chip,
290+
const char *dtnode)
291+
{
292+
UNUSED(chip);
293+
UNUSED(dtnode);
294+
firmware_instance.num_gpios = NUM_GPIOS;
295+
firmware_instance.mbox_fd = 0;
296+
return &firmware_instance;
297+
}
298+
299+
static int firmware_gpio_count(void *priv)
300+
{
301+
struct firmware_inst *inst = priv;
302+
return inst->num_gpios;
303+
}
304+
305+
static const GPIO_CHIP_INTERFACE_T firmware_gpio_interface =
306+
{
307+
.gpio_create_instance = firmware_gpio_create_instance,
308+
.gpio_count = firmware_gpio_count,
309+
.gpio_get_fsel = firmware_gpio_get_fsel,
310+
.gpio_set_fsel = firmware_gpio_set_fsel,
311+
.gpio_set_drive = firmware_gpio_set_drive,
312+
.gpio_set_dir = firmware_gpio_set_dir,
313+
.gpio_get_dir = firmware_gpio_get_dir,
314+
.gpio_get_level = firmware_gpio_get_level,
315+
.gpio_get_drive = firmware_gpio_get_drive,
316+
.gpio_get_pull = firmware_gpio_get_pull,
317+
.gpio_set_pull = firmware_gpio_set_pull,
318+
.gpio_get_name = firmware_gpio_get_name,
319+
.gpio_get_fsel_name = firmware_gpio_get_fsel_name,
320+
};
321+
322+
DECLARE_GPIO_CHIP(firmware, "raspberrypi,firmware-gpio", &firmware_gpio_interface,
323+
0, 0);

pinctrl/library_gpiochips.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ EXTERN_GPIO_CHIP(bcm2712d0);
1414
EXTERN_GPIO_CHIP(bcm2712d0_aon);
1515
EXTERN_GPIO_CHIP(brcmstb);
1616
EXTERN_GPIO_CHIP(rp1);
17+
EXTERN_GPIO_CHIP(firmware);
1718

1819
const GPIO_CHIP_T *const library_gpiochips[] =
1920
{
@@ -27,6 +28,7 @@ const GPIO_CHIP_T *const library_gpiochips[] =
2728
&GPIO_CHIP(bcm2712d0_aon),
2829
&GPIO_CHIP(brcmstb),
2930
&GPIO_CHIP(rp1),
31+
&GPIO_CHIP(firmware),
3032
};
3133

3234
const int library_gpiochips_count = ARRAY_SIZE(library_gpiochips);

0 commit comments

Comments
 (0)