xref: /minix3/minix/drivers/system/gpio/gpio.c (revision 52be5c0afb313c0a55ebc7e73a7d987d503157a0)
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 /* Buffer size for read requests */
75 #define DATA_SIZE	26
76 
77 int
add_gpio_inode(char * name,int nr,int mode)78 add_gpio_inode(char *name, int nr, int mode)
79 {
80 	/* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
81 	 * set values as we don't support writing yet */
82 	char tmpname[200];
83 	struct gpio_cbdata *cb;
84 	struct gpio *gpio;
85 
86 	/* claim and configure the gpio */
87 	if (gpio_claim("gpiofs", nr, &gpio)) {
88 		log_warn(&log, "Failed to claim GPIO %d\n", nr);
89 		return EIO;
90 	}
91 	assert(gpio != NULL);
92 
93 	if (gpio_pin_mode(gpio, mode)) {
94 		log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr,
95 		    mode);
96 		return EIO;
97 	}
98 
99 	/* read value */
100 	cb = malloc(sizeof(struct gpio_cbdata));
101 	if (cb == NULL) {
102 		return ENOMEM;
103 	}
104 	memset(cb, 0, sizeof(*cb));
105 
106 	cb->type = GPIO_CB_READ;
107 	cb->gpio = gpio;
108 
109 	snprintf(tmpname, 200, "%s", name);
110 	add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0,
111 	    (cbdata_t) cb);
112 	TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
113 
114 	if (mode == GPIO_MODE_OUTPUT) {
115 		/* if we configured the GPIO pin as output mode also create
116 		 * two additional files to turn on and off the GPIO. */
117 		/* turn on */
118 		cb = malloc(sizeof(struct gpio_cbdata));
119 		if (cb == NULL) {
120 			return ENOMEM;
121 		}
122 		memset(cb, 0, sizeof(*cb));
123 
124 		cb->type = GPIO_CB_ON;
125 		cb->gpio = gpio;
126 
127 		snprintf(tmpname, 200, "%sOn", name);
128 		add_inode(get_root_inode(), tmpname, NO_INDEX,
129 		    &default_file_stat, 0, (cbdata_t) cb);
130 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
131 
132 		/* turn off */
133 		cb = malloc(sizeof(struct gpio_cbdata));
134 		if (cb == NULL) {
135 			return ENOMEM;
136 		}
137 		memset(cb, 0, sizeof(*cb));
138 
139 		cb->type = GPIO_CB_OFF;
140 		cb->gpio = gpio;
141 
142 		snprintf(tmpname, 200, "%sOff", name);
143 		add_inode(get_root_inode(), tmpname, NO_INDEX,
144 		    &default_file_stat, 0, (cbdata_t) cb);
145 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
146 	} else {
147 		/* read interrupt */
148 		cb = malloc(sizeof(struct gpio_cbdata));
149 		if (cb == NULL) {
150 			return ENOMEM;
151 		}
152 		memset(cb, 0, sizeof(*cb));
153 
154 		cb->type = GPIO_CB_INTR_READ;
155 		cb->gpio = gpio;
156 
157 		snprintf(tmpname, 200, "%sIntr", name);
158 		add_inode(get_root_inode(), tmpname, NO_INDEX,
159 		    &default_file_stat, 0, (cbdata_t) cb);
160 		TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
161 	}
162 	return OK;
163 }
164 
165 static void
init_hook(void)166 init_hook(void)
167 {
168 	/* This hook will be called once, after VTreeFS has initialized. */
169 	if (gpio_init()) {
170 		log_warn(&log, "Failed to init gpio driver\n");
171 	}
172 
173 	struct machine  machine ;
174 	sys_getmachine(&machine);
175 
176 	if (BOARD_IS_BBXM(machine.board_id)){
177 		add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT);
178 		add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT);
179 		add_gpio_inode("Button", 4, GPIO_MODE_INPUT);
180 
181 		/* configure GPIO_144 to be exported */
182 		sys_padconf(CONTROL_PADCONF_UART2_CTS, 0x0000ffff,
183 		    PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
184 		    PADCONF_INPUT_ENABLE(1));
185 		sys_padconf(CONTROL_PADCONF_MMC2_DAT6, 0xffff0000,
186 		    (PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
187 			PADCONF_INPUT_ENABLE(1)) << 16);
188 
189 		/* Added for demo purposes */
190 		add_gpio_inode("BigRedButton", 144, GPIO_MODE_INPUT);
191 		add_gpio_inode("BigRedButtonLed", 139, GPIO_MODE_OUTPUT);
192 	} else if ( BOARD_IS_BB(machine.board_id)){
193 
194 		/* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */
195 
196 		sys_padconf(CONTROL_CONF_MCASP0_FSR, 0xffffffff,
197 		    (CONTROL_CONF_PUTYPESEL | CONTROL_CONF_MUXMODE(7)));
198 
199 		add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT);
200 
201 		/* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */
202 
203 		/* assumes external pull-up resistor (10K) */
204 		sys_padconf(CONTROL_CONF_SPI0_D0, 0xffffffff, (CONTROL_CONF_RXACTIVE |
205 		    CONTROL_CONF_PUDEN | CONTROL_CONF_MUXMODE(7)));
206 
207 		add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT);
208 
209 	}
210 }
211 
212 static ssize_t
read_hook(struct inode * inode,char * ptr,size_t len,off_t offset,cbdata_t cbdata)213     read_hook
214     (struct inode *inode, char *ptr, size_t len, off_t offset, cbdata_t cbdata)
215 {
216 	/* This hook will be called every time a regular file is read. We use
217 	 * it to dyanmically generate the contents of our file. */
218 	int value;
219 	struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
220 	assert(gpio_cbdata->gpio != NULL);
221 
222 	if (gpio_cbdata->type == GPIO_CB_ON
223 	    || gpio_cbdata->type == GPIO_CB_OFF) {
224 		/* turn on or off */
225 		if (gpio_set(gpio_cbdata->gpio,
226 			(gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0))
227 			return EIO;
228 		return 0;
229 	}
230 
231 	if (gpio_cbdata->type == GPIO_CB_INTR_READ) {
232 		/* reading interrupt */
233 		if (gpio_intr_read(gpio_cbdata->gpio, &value))
234 			return EIO;
235 	} else {
236 		/* reading */
237 		if (gpio_read(gpio_cbdata->gpio, &value))
238 			return EIO;
239 	}
240 	snprintf(ptr, DATA_SIZE, "%d\n", value);
241 	len = strlen(ptr);
242 
243 	/* If the offset is at or beyond the end of the string, return EOF. */
244 	if (offset >= len)
245 		return 0;
246 
247 	/* Otherwise, we may have to move the data to the start of ptr. */
248 	if (offset > 0) {
249 		len -= offset;
250 
251 		memmove(ptr, &ptr[offset], len);
252 	}
253 
254 	/* Return the resulting length. */
255 	return len;
256 }
257 
258 static void
message_hook(message * m,int __unused ipc_status)259 message_hook(message * m, int __unused ipc_status)
260 {
261 	gpio_intr_message(m);
262 }
263 
264 int
main(int argc,char ** argv)265 main(int argc, char **argv)
266 {
267 
268 	struct fs_hooks hooks;
269 	struct inode_stat root_stat;
270 
271 	/* Set and apply the environment */
272 	env_setargs(argc, argv);
273 
274 	/* fill in the hooks */
275 	memset(&hooks, 0, sizeof(hooks));
276 	hooks.init_hook = init_hook;
277 	hooks.read_hook = read_hook;
278 	hooks.message_hook = message_hook;
279 
280 	root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
281 	root_stat.uid = 0;
282 	root_stat.gid = 0;
283 	root_stat.size = 0;
284 	root_stat.dev = NO_DEV;
285 
286 	/* run VTreeFS */
287 	run_vtreefs(&hooks, 30, 0, &root_stat, 0, DATA_SIZE);
288 
289 	return EXIT_SUCCESS;
290 }
291