xref: /minix3/minix/drivers/system/gpio/gpio.c (revision 52be5c0afb313c0a55ebc7e73a7d987d503157a0)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * GPIO driver. This driver acts as a file system to allow
3433d6423SLionel Sambuc  * reading and toggling of GPIO's.
4433d6423SLionel Sambuc  */
5433d6423SLionel Sambuc /* kernel headers */
6433d6423SLionel Sambuc #include <minix/driver.h>
7433d6423SLionel Sambuc #include <minix/drvlib.h>
8433d6423SLionel Sambuc #include <minix/vtreefs.h>
9433d6423SLionel Sambuc #include <minix/syslib.h>
10433d6423SLionel Sambuc #include <minix/log.h>
11433d6423SLionel Sambuc #include <minix/mmio.h>
12433d6423SLionel Sambuc #include <minix/gpio.h>
13433d6423SLionel Sambuc #include <minix/padconf.h>
14433d6423SLionel Sambuc #include <minix/type.h>
15433d6423SLionel Sambuc #include <minix/board.h>
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc /* system headers */
18433d6423SLionel Sambuc #include <sys/stat.h>
19433d6423SLionel Sambuc #include <sys/queue.h>
20433d6423SLionel Sambuc #include <sys/queue.h>
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /* usr headers */
23433d6423SLionel Sambuc #include <assert.h>
24433d6423SLionel Sambuc #include <stdio.h>
25433d6423SLionel Sambuc #include <stdlib.h>
26433d6423SLionel Sambuc #include <stdarg.h>
27433d6423SLionel Sambuc #include <signal.h>
28433d6423SLionel Sambuc #include <unistd.h>
29433d6423SLionel Sambuc #include <string.h>
30433d6423SLionel Sambuc 
31433d6423SLionel Sambuc /* local headers */
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc /* used for logging */
34433d6423SLionel Sambuc static struct log log = {
35433d6423SLionel Sambuc 	.name = "gpio",
36433d6423SLionel Sambuc 	.log_level = LEVEL_INFO,
37433d6423SLionel Sambuc 	.log_func = default_log
38433d6423SLionel Sambuc };
39433d6423SLionel Sambuc 
40433d6423SLionel Sambuc #define GPIO_CB_READ 0
41433d6423SLionel Sambuc #define GPIO_CB_INTR_READ 1
42433d6423SLionel Sambuc #define GPIO_CB_ON 2
43433d6423SLionel Sambuc #define GPIO_CB_OFF 3
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc /* The vtreefs library provides callback data when calling
46433d6423SLionel Sambuc  * the read function of inode. gpio_cbdata is used here to
47433d6423SLionel Sambuc  * map between inodes and gpio's. VTreeFS is read-only. to work
48433d6423SLionel Sambuc  * around that issue for a single GPIO we create multiple virtual
49433d6423SLionel Sambuc  * files that can be *read* to read the gpio value and power on
50433d6423SLionel Sambuc  * and off the gpio.
51433d6423SLionel Sambuc  */
52433d6423SLionel Sambuc struct gpio_cbdata
53433d6423SLionel Sambuc {
54433d6423SLionel Sambuc 	struct gpio *gpio;	/* obtained from the driver */
55433d6423SLionel Sambuc 	int type;		/* read=0/on=1/off=2 */
56433d6423SLionel Sambuc 	TAILQ_ENTRY(gpio_cbdata) next;
57433d6423SLionel Sambuc };
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc /* list of inodes used in this driver */
60433d6423SLionel Sambuc /* *INDENT-OFF* */
61433d6423SLionel Sambuc TAILQ_HEAD(gpio_cbdata_head, gpio_cbdata)
62433d6423SLionel Sambuc     gpio_cbdata_list = TAILQ_HEAD_INITIALIZER(gpio_cbdata_list);
63433d6423SLionel Sambuc /* *INDENT-ON* */
64433d6423SLionel Sambuc 
65433d6423SLionel Sambuc /* Sane file stats for a directory */
66433d6423SLionel Sambuc static struct inode_stat default_file_stat = {
67433d6423SLionel Sambuc 	.mode = S_IFREG | 04,
68433d6423SLionel Sambuc 	.uid = 0,
69433d6423SLionel Sambuc 	.gid = 0,
70433d6423SLionel Sambuc 	.size = 0,
71433d6423SLionel Sambuc 	.dev = NO_DEV,
72433d6423SLionel Sambuc };
73433d6423SLionel Sambuc 
745eefd0feSDavid van Moolenbroek /* Buffer size for read requests */
755eefd0feSDavid van Moolenbroek #define DATA_SIZE	26
765eefd0feSDavid van Moolenbroek 
77433d6423SLionel Sambuc int
add_gpio_inode(char * name,int nr,int mode)78433d6423SLionel Sambuc add_gpio_inode(char *name, int nr, int mode)
79433d6423SLionel Sambuc {
80433d6423SLionel Sambuc 	/* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
81433d6423SLionel Sambuc 	 * set values as we don't support writing yet */
82433d6423SLionel Sambuc 	char tmpname[200];
83433d6423SLionel Sambuc 	struct gpio_cbdata *cb;
84433d6423SLionel Sambuc 	struct gpio *gpio;
85433d6423SLionel Sambuc 
86433d6423SLionel Sambuc 	/* claim and configure the gpio */
87433d6423SLionel Sambuc 	if (gpio_claim("gpiofs", nr, &gpio)) {
88433d6423SLionel Sambuc 		log_warn(&log, "Failed to claim GPIO %d\n", nr);
89433d6423SLionel Sambuc 		return EIO;
90433d6423SLionel Sambuc 	}
91433d6423SLionel Sambuc 	assert(gpio != NULL);
92433d6423SLionel Sambuc 
93433d6423SLionel Sambuc 	if (gpio_pin_mode(gpio, mode)) {
94433d6423SLionel Sambuc 		log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr,
95433d6423SLionel Sambuc 		    mode);
96433d6423SLionel Sambuc 		return EIO;
97433d6423SLionel Sambuc 	}
98433d6423SLionel Sambuc 
99433d6423SLionel Sambuc 	/* read value */
100433d6423SLionel Sambuc 	cb = malloc(sizeof(struct gpio_cbdata));
101433d6423SLionel Sambuc 	if (cb == NULL) {
102433d6423SLionel Sambuc 		return ENOMEM;
103433d6423SLionel Sambuc 	}
104433d6423SLionel Sambuc 	memset(cb, 0, sizeof(*cb));
105433d6423SLionel Sambuc 
106433d6423SLionel Sambuc 	cb->type = GPIO_CB_READ;
107433d6423SLionel Sambuc 	cb->gpio = gpio;
108433d6423SLionel Sambuc 
109433d6423SLionel Sambuc 	snprintf(tmpname, 200, "%s", name);
110433d6423SLionel Sambuc 	add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0,
111433d6423SLionel Sambuc 	    (cbdata_t) cb);
112433d6423SLionel Sambuc 	TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc 	if (mode == GPIO_MODE_OUTPUT) {
115433d6423SLionel Sambuc 		/* if we configured the GPIO pin as output mode also create
116433d6423SLionel Sambuc 		 * two additional files to turn on and off the GPIO. */
117433d6423SLionel Sambuc 		/* turn on */
118433d6423SLionel Sambuc 		cb = malloc(sizeof(struct gpio_cbdata));
119433d6423SLionel Sambuc 		if (cb == NULL) {
120433d6423SLionel Sambuc 			return ENOMEM;
121433d6423SLionel Sambuc 		}
122433d6423SLionel Sambuc 		memset(cb, 0, sizeof(*cb));
123433d6423SLionel Sambuc 
124433d6423SLionel Sambuc 		cb->type = GPIO_CB_ON;
125433d6423SLionel Sambuc 		cb->gpio = gpio;
126433d6423SLionel Sambuc 
127433d6423SLionel Sambuc 		snprintf(tmpname, 200, "%sOn", name);
128433d6423SLionel Sambuc 		add_inode(get_root_inode(), tmpname, NO_INDEX,
129433d6423SLionel Sambuc 		    &default_file_stat, 0, (cbdata_t) cb);
130433d6423SLionel Sambuc 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc 		/* turn off */
133433d6423SLionel Sambuc 		cb = malloc(sizeof(struct gpio_cbdata));
134433d6423SLionel Sambuc 		if (cb == NULL) {
135433d6423SLionel Sambuc 			return ENOMEM;
136433d6423SLionel Sambuc 		}
137433d6423SLionel Sambuc 		memset(cb, 0, sizeof(*cb));
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc 		cb->type = GPIO_CB_OFF;
140433d6423SLionel Sambuc 		cb->gpio = gpio;
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 		snprintf(tmpname, 200, "%sOff", name);
143433d6423SLionel Sambuc 		add_inode(get_root_inode(), tmpname, NO_INDEX,
144433d6423SLionel Sambuc 		    &default_file_stat, 0, (cbdata_t) cb);
145433d6423SLionel Sambuc 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
146433d6423SLionel Sambuc 	} else {
147433d6423SLionel Sambuc 		/* read interrupt */
148433d6423SLionel Sambuc 		cb = malloc(sizeof(struct gpio_cbdata));
149433d6423SLionel Sambuc 		if (cb == NULL) {
150433d6423SLionel Sambuc 			return ENOMEM;
151433d6423SLionel Sambuc 		}
152433d6423SLionel Sambuc 		memset(cb, 0, sizeof(*cb));
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc 		cb->type = GPIO_CB_INTR_READ;
155433d6423SLionel Sambuc 		cb->gpio = gpio;
156433d6423SLionel Sambuc 
157433d6423SLionel Sambuc 		snprintf(tmpname, 200, "%sIntr", name);
158433d6423SLionel Sambuc 		add_inode(get_root_inode(), tmpname, NO_INDEX,
159433d6423SLionel Sambuc 		    &default_file_stat, 0, (cbdata_t) cb);
160433d6423SLionel Sambuc 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
161433d6423SLionel Sambuc 	}
162433d6423SLionel Sambuc 	return OK;
163433d6423SLionel Sambuc }
164433d6423SLionel Sambuc 
165433d6423SLionel Sambuc static void
init_hook(void)166433d6423SLionel Sambuc init_hook(void)
167433d6423SLionel Sambuc {
168433d6423SLionel Sambuc 	/* This hook will be called once, after VTreeFS has initialized. */
169433d6423SLionel Sambuc 	if (gpio_init()) {
170433d6423SLionel Sambuc 		log_warn(&log, "Failed to init gpio driver\n");
171433d6423SLionel Sambuc 	}
172433d6423SLionel Sambuc 
173433d6423SLionel Sambuc 	struct machine  machine ;
174433d6423SLionel Sambuc 	sys_getmachine(&machine);
175433d6423SLionel Sambuc 
176433d6423SLionel Sambuc 	if (BOARD_IS_BBXM(machine.board_id)){
177433d6423SLionel Sambuc 		add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT);
178433d6423SLionel Sambuc 		add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT);
179433d6423SLionel Sambuc 		add_gpio_inode("Button", 4, GPIO_MODE_INPUT);
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc 		/* configure GPIO_144 to be exported */
182433d6423SLionel Sambuc 		sys_padconf(CONTROL_PADCONF_UART2_CTS, 0x0000ffff,
183433d6423SLionel Sambuc 		    PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
184433d6423SLionel Sambuc 		    PADCONF_INPUT_ENABLE(1));
185433d6423SLionel Sambuc 		sys_padconf(CONTROL_PADCONF_MMC2_DAT6, 0xffff0000,
186433d6423SLionel Sambuc 		    (PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
187433d6423SLionel Sambuc 			PADCONF_INPUT_ENABLE(1)) << 16);
188433d6423SLionel Sambuc 
189433d6423SLionel Sambuc 		/* Added for demo purposes */
190433d6423SLionel Sambuc 		add_gpio_inode("BigRedButton", 144, GPIO_MODE_INPUT);
191433d6423SLionel Sambuc 		add_gpio_inode("BigRedButtonLed", 139, GPIO_MODE_OUTPUT);
192433d6423SLionel Sambuc 	} else if ( BOARD_IS_BB(machine.board_id)){
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc 		/* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc 		sys_padconf(CONTROL_CONF_MCASP0_FSR, 0xffffffff,
197433d6423SLionel Sambuc 		    (CONTROL_CONF_PUTYPESEL | CONTROL_CONF_MUXMODE(7)));
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 		add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT);
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc 		/* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc 		/* assumes external pull-up resistor (10K) */
204433d6423SLionel Sambuc 		sys_padconf(CONTROL_CONF_SPI0_D0, 0xffffffff, (CONTROL_CONF_RXACTIVE |
205433d6423SLionel Sambuc 		    CONTROL_CONF_PUDEN | CONTROL_CONF_MUXMODE(7)));
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc 		add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT);
208433d6423SLionel Sambuc 
209433d6423SLionel Sambuc 	}
210433d6423SLionel Sambuc }
211433d6423SLionel Sambuc 
2125eefd0feSDavid van Moolenbroek static ssize_t
read_hook(struct inode * inode,char * ptr,size_t len,off_t offset,cbdata_t cbdata)213433d6423SLionel Sambuc     read_hook
2145eefd0feSDavid van Moolenbroek     (struct inode *inode, char *ptr, size_t len, off_t offset, cbdata_t cbdata)
215433d6423SLionel Sambuc {
216433d6423SLionel Sambuc 	/* This hook will be called every time a regular file is read. We use
217433d6423SLionel Sambuc 	 * it to dyanmically generate the contents of our file. */
218433d6423SLionel Sambuc 	int value;
219433d6423SLionel Sambuc 	struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
220433d6423SLionel Sambuc 	assert(gpio_cbdata->gpio != NULL);
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc 	if (gpio_cbdata->type == GPIO_CB_ON
223433d6423SLionel Sambuc 	    || gpio_cbdata->type == GPIO_CB_OFF) {
224433d6423SLionel Sambuc 		/* turn on or off */
225433d6423SLionel Sambuc 		if (gpio_set(gpio_cbdata->gpio,
2265eefd0feSDavid van Moolenbroek 			(gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0))
227433d6423SLionel Sambuc 			return EIO;
2285eefd0feSDavid van Moolenbroek 		return 0;
229433d6423SLionel Sambuc 	}
230433d6423SLionel Sambuc 
231433d6423SLionel Sambuc 	if (gpio_cbdata->type == GPIO_CB_INTR_READ) {
232433d6423SLionel Sambuc 		/* reading interrupt */
2335eefd0feSDavid van Moolenbroek 		if (gpio_intr_read(gpio_cbdata->gpio, &value))
234433d6423SLionel Sambuc 			return EIO;
235433d6423SLionel Sambuc 	} else {
236433d6423SLionel Sambuc 		/* reading */
2375eefd0feSDavid van Moolenbroek 		if (gpio_read(gpio_cbdata->gpio, &value))
238433d6423SLionel Sambuc 			return EIO;
239433d6423SLionel Sambuc 	}
2405eefd0feSDavid van Moolenbroek 	snprintf(ptr, DATA_SIZE, "%d\n", value);
2415eefd0feSDavid van Moolenbroek 	len = strlen(ptr);
242433d6423SLionel Sambuc 
2435eefd0feSDavid van Moolenbroek 	/* If the offset is at or beyond the end of the string, return EOF. */
2445eefd0feSDavid van Moolenbroek 	if (offset >= len)
2455eefd0feSDavid van Moolenbroek 		return 0;
246433d6423SLionel Sambuc 
2475eefd0feSDavid van Moolenbroek 	/* Otherwise, we may have to move the data to the start of ptr. */
2485eefd0feSDavid van Moolenbroek 	if (offset > 0) {
2495eefd0feSDavid van Moolenbroek 		len -= offset;
2505eefd0feSDavid van Moolenbroek 
2515eefd0feSDavid van Moolenbroek 		memmove(ptr, &ptr[offset], len);
252433d6423SLionel Sambuc 	}
253433d6423SLionel Sambuc 
2545eefd0feSDavid van Moolenbroek 	/* Return the resulting length. */
2555eefd0feSDavid van Moolenbroek 	return len;
256433d6423SLionel Sambuc }
257433d6423SLionel Sambuc 
2585eefd0feSDavid van Moolenbroek static void
message_hook(message * m,int __unused ipc_status)2595eefd0feSDavid van Moolenbroek message_hook(message * m, int __unused ipc_status)
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc 	gpio_intr_message(m);
262433d6423SLionel Sambuc }
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc int
main(int argc,char ** argv)265433d6423SLionel Sambuc main(int argc, char **argv)
266433d6423SLionel Sambuc {
267433d6423SLionel Sambuc 
268433d6423SLionel Sambuc 	struct fs_hooks hooks;
269433d6423SLionel Sambuc 	struct inode_stat root_stat;
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc 	/* Set and apply the environment */
272433d6423SLionel Sambuc 	env_setargs(argc, argv);
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc 	/* fill in the hooks */
275433d6423SLionel Sambuc 	memset(&hooks, 0, sizeof(hooks));
276433d6423SLionel Sambuc 	hooks.init_hook = init_hook;
277433d6423SLionel Sambuc 	hooks.read_hook = read_hook;
278433d6423SLionel Sambuc 	hooks.message_hook = message_hook;
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc 	root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
281433d6423SLionel Sambuc 	root_stat.uid = 0;
282433d6423SLionel Sambuc 	root_stat.gid = 0;
283433d6423SLionel Sambuc 	root_stat.size = 0;
284433d6423SLionel Sambuc 	root_stat.dev = NO_DEV;
285433d6423SLionel Sambuc 
286*52be5c0aSDavid van Moolenbroek 	/* run VTreeFS */
287*52be5c0aSDavid van Moolenbroek 	run_vtreefs(&hooks, 30, 0, &root_stat, 0, DATA_SIZE);
288433d6423SLionel Sambuc 
289433d6423SLionel Sambuc 	return EXIT_SUCCESS;
290433d6423SLionel Sambuc }
291