xref: /minix/minix/drivers/system/gpio/gpio.c (revision 83133719)
1 /*
2  * GPIO driver. This driver acts as a file system to allow
3  * reading and toggling of GPIO's.
4  */
5 /* kernel headers */
6 #include <minix/driver.h>
7 #include <minix/drvlib.h>
8 #include <minix/vtreefs.h>
9 #include <minix/syslib.h>
10 #include <minix/log.h>
11 #include <minix/mmio.h>
12 #include <minix/gpio.h>
13 #include <minix/padconf.h>
14 #include <minix/type.h>
15 #include <minix/board.h>
16 
17 /* system headers */
18 #include <sys/stat.h>
19 #include <sys/queue.h>
20 #include <sys/queue.h>
21 
22 /* usr headers */
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <string.h>
30 
31 /* local headers */
32 
33 /* used for logging */
34 static struct log log = {
35 	.name = "gpio",
36 	.log_level = LEVEL_INFO,
37 	.log_func = default_log
38 };
39 
40 #define GPIO_CB_READ 0
41 #define GPIO_CB_INTR_READ 1
42 #define GPIO_CB_ON 2
43 #define GPIO_CB_OFF 3
44 
45 /* The vtreefs library provides callback data when calling
46  * the read function of inode. gpio_cbdata is used here to
47  * map between inodes and gpio's. VTreeFS is read-only. to work
48  * around that issue for a single GPIO we create multiple virtual
49  * files that can be *read* to read the gpio value and power on
50  * and off the gpio.
51  */
52 struct gpio_cbdata
53 {
54 	struct gpio *gpio;	/* obtained from the driver */
55 	int type;		/* read=0/on=1/off=2 */
56 	TAILQ_ENTRY(gpio_cbdata) next;
57 };
58 
59 /* list of inodes used in this driver */
60 /* *INDENT-OFF* */
61 TAILQ_HEAD(gpio_cbdata_head, gpio_cbdata)
62     gpio_cbdata_list = TAILQ_HEAD_INITIALIZER(gpio_cbdata_list);
63 /* *INDENT-ON* */
64 
65 /* Sane file stats for a directory */
66 static struct inode_stat default_file_stat = {
67 	.mode = S_IFREG | 04,
68 	.uid = 0,
69 	.gid = 0,
70 	.size = 0,
71 	.dev = NO_DEV,
72 };
73 
74 int
75 add_gpio_inode(char *name, int nr, int mode)
76 {
77 	/* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
78 	 * set values as we don't support writing yet */
79 	char tmpname[200];
80 	struct gpio_cbdata *cb;
81 	struct gpio *gpio;
82 
83 	/* claim and configure the gpio */
84 	if (gpio_claim("gpiofs", nr, &gpio)) {
85 		log_warn(&log, "Failed to claim GPIO %d\n", nr);
86 		return EIO;
87 	}
88 	assert(gpio != NULL);
89 
90 	if (gpio_pin_mode(gpio, mode)) {
91 		log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr,
92 		    mode);
93 		return EIO;
94 	}
95 
96 	/* read value */
97 	cb = malloc(sizeof(struct gpio_cbdata));
98 	if (cb == NULL) {
99 		return ENOMEM;
100 	}
101 	memset(cb, 0, sizeof(*cb));
102 
103 	cb->type = GPIO_CB_READ;
104 	cb->gpio = gpio;
105 
106 	snprintf(tmpname, 200, "%s", name);
107 	add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0,
108 	    (cbdata_t) cb);
109 	TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
110 
111 	if (mode == GPIO_MODE_OUTPUT) {
112 		/* if we configured the GPIO pin as output mode also create
113 		 * two additional files to turn on and off the GPIO. */
114 		/* turn on */
115 		cb = malloc(sizeof(struct gpio_cbdata));
116 		if (cb == NULL) {
117 			return ENOMEM;
118 		}
119 		memset(cb, 0, sizeof(*cb));
120 
121 		cb->type = GPIO_CB_ON;
122 		cb->gpio = gpio;
123 
124 		snprintf(tmpname, 200, "%sOn", name);
125 		add_inode(get_root_inode(), tmpname, NO_INDEX,
126 		    &default_file_stat, 0, (cbdata_t) cb);
127 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
128 
129 		/* turn off */
130 		cb = malloc(sizeof(struct gpio_cbdata));
131 		if (cb == NULL) {
132 			return ENOMEM;
133 		}
134 		memset(cb, 0, sizeof(*cb));
135 
136 		cb->type = GPIO_CB_OFF;
137 		cb->gpio = gpio;
138 
139 		snprintf(tmpname, 200, "%sOff", name);
140 		add_inode(get_root_inode(), tmpname, NO_INDEX,
141 		    &default_file_stat, 0, (cbdata_t) cb);
142 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
143 	} else {
144 		/* read interrupt */
145 		cb = malloc(sizeof(struct gpio_cbdata));
146 		if (cb == NULL) {
147 			return ENOMEM;
148 		}
149 		memset(cb, 0, sizeof(*cb));
150 
151 		cb->type = GPIO_CB_INTR_READ;
152 		cb->gpio = gpio;
153 
154 		snprintf(tmpname, 200, "%sIntr", name);
155 		add_inode(get_root_inode(), tmpname, NO_INDEX,
156 		    &default_file_stat, 0, (cbdata_t) cb);
157 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
158 	}
159 	return OK;
160 }
161 
162 static void
163 init_hook(void)
164 {
165 	/* This hook will be called once, after VTreeFS has initialized. */
166 	if (gpio_init()) {
167 		log_warn(&log, "Failed to init gpio driver\n");
168 	}
169 
170 	struct machine  machine ;
171 	sys_getmachine(&machine);
172 
173 	if (BOARD_IS_BBXM(machine.board_id)){
174 		add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT);
175 		add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT);
176 		add_gpio_inode("Button", 4, GPIO_MODE_INPUT);
177 
178 		/* configure GPIO_144 to be exported */
179 		sys_padconf(CONTROL_PADCONF_UART2_CTS, 0x0000ffff,
180 		    PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
181 		    PADCONF_INPUT_ENABLE(1));
182 		sys_padconf(CONTROL_PADCONF_MMC2_DAT6, 0xffff0000,
183 		    (PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
184 			PADCONF_INPUT_ENABLE(1)) << 16);
185 
186 		/* Added for demo purposes */
187 		add_gpio_inode("BigRedButton", 144, GPIO_MODE_INPUT);
188 		add_gpio_inode("BigRedButtonLed", 139, GPIO_MODE_OUTPUT);
189 	} else if ( BOARD_IS_BB(machine.board_id)){
190 
191 		/* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */
192 
193 		sys_padconf(CONTROL_CONF_MCASP0_FSR, 0xffffffff,
194 		    (CONTROL_CONF_PUTYPESEL | CONTROL_CONF_MUXMODE(7)));
195 
196 		add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT);
197 
198 		/* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */
199 
200 		/* assumes external pull-up resistor (10K) */
201 		sys_padconf(CONTROL_CONF_SPI0_D0, 0xffffffff, (CONTROL_CONF_RXACTIVE |
202 		    CONTROL_CONF_PUDEN | CONTROL_CONF_MUXMODE(7)));
203 
204 		add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT);
205 
206 	}
207 }
208 
209 static int
210     read_hook
211     (struct inode *inode, off_t offset, char **ptr, size_t * len,
212     cbdata_t cbdata)
213 {
214 	/* This hook will be called every time a regular file is read. We use
215 	 * it to dyanmically generate the contents of our file. */
216 	static char data[26];
217 	int value;
218 	struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
219 	assert(gpio_cbdata->gpio != NULL);
220 
221 	if (gpio_cbdata->type == GPIO_CB_ON
222 	    || gpio_cbdata->type == GPIO_CB_OFF) {
223 		/* turn on or off */
224 		if (gpio_set(gpio_cbdata->gpio,
225 			(gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0)) {
226 			*len = 0;
227 			return EIO;
228 		}
229 		*len = 0;
230 		return OK;
231 	}
232 
233 	if (gpio_cbdata->type == GPIO_CB_INTR_READ) {
234 		/* reading interrupt */
235 		if (gpio_intr_read(gpio_cbdata->gpio, &value)) {
236 			*len = 0;
237 			return EIO;
238 		}
239 	} else {
240 		/* reading */
241 		if (gpio_read(gpio_cbdata->gpio, &value)) {
242 			*len = 0;
243 			return EIO;
244 		}
245 	}
246 	snprintf(data, 26, "%d\n", value);
247 
248 	/* If the offset is beyond the end of the string, return EOF. */
249 	if (offset > strlen(data)) {
250 		*len = 0;
251 
252 		return OK;
253 	}
254 
255 	/* Otherwise, return a pointer into 'data'. If necessary, bound the
256 	 * returned length to the length of the rest of the string. Note that
257 	 * 'data' has to be static, because it will be used after this
258 	 * function returns. */
259 	*ptr = data + offset;
260 
261 	if (*len > strlen(data) - offset)
262 		*len = strlen(data) - offset;
263 
264 	return OK;
265 }
266 
267 static int
268 message_hook(message * m)
269 {
270 	gpio_intr_message(m);
271 	return OK;
272 }
273 
274 int
275 main(int argc, char **argv)
276 {
277 
278 	struct fs_hooks hooks;
279 	struct inode_stat root_stat;
280 
281 	/* Set and apply the environment */
282 	env_setargs(argc, argv);
283 
284 	/* fill in the hooks */
285 	memset(&hooks, 0, sizeof(hooks));
286 	hooks.init_hook = init_hook;
287 	hooks.read_hook = read_hook;
288 	hooks.message_hook = message_hook;
289 
290 	root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
291 	root_stat.uid = 0;
292 	root_stat.gid = 0;
293 	root_stat.size = 0;
294 	root_stat.dev = NO_DEV;
295 
296 	/* limit the number of indexed entries */
297 	start_vtreefs(&hooks, 30, &root_stat, 0);
298 
299 	return EXIT_SUCCESS;
300 }
301