2 * GPIO driver. This driver acts as a file system to allow
3 * reading and toggling of GPIO's.
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>
19 #include <sys/queue.h>
20 #include <sys/queue.h>
33 /* used for logging */
34 static struct log log
= {
36 .log_level
= LEVEL_INFO
,
37 .log_func
= default_log
40 #define GPIO_CB_READ 0
41 #define GPIO_CB_INTR_READ 1
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
54 struct gpio
*gpio
; /* obtained from the driver */
55 int type
; /* read=0/on=1/off=2 */
56 TAILQ_ENTRY(gpio_cbdata
) next
;
59 /* list of inodes used in this driver */
61 TAILQ_HEAD(gpio_cbdata_head
, gpio_cbdata
)
62 gpio_cbdata_list
= TAILQ_HEAD_INITIALIZER(gpio_cbdata_list
);
65 /* Sane file stats for a directory */
66 static struct inode_stat default_file_stat
= {
74 /* Buffer size for read requests */
78 add_gpio_inode(char *name
, int nr
, int mode
)
80 /* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
81 * set values as we don't support writing yet */
83 struct gpio_cbdata
*cb
;
86 /* claim and configure the gpio */
87 if (gpio_claim("gpiofs", nr
, &gpio
)) {
88 log_warn(&log
, "Failed to claim GPIO %d\n", nr
);
93 if (gpio_pin_mode(gpio
, mode
)) {
94 log_warn(&log
, "Failed to switch GPIO %d to mode %d\n", nr
,
100 cb
= malloc(sizeof(struct gpio_cbdata
));
104 memset(cb
, 0, sizeof(*cb
));
106 cb
->type
= GPIO_CB_READ
;
109 snprintf(tmpname
, 200, "%s", name
);
110 add_inode(get_root_inode(), tmpname
, NO_INDEX
, &default_file_stat
, 0,
112 TAILQ_INSERT_HEAD(&gpio_cbdata_list
, cb
, next
);
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. */
118 cb
= malloc(sizeof(struct gpio_cbdata
));
122 memset(cb
, 0, sizeof(*cb
));
124 cb
->type
= GPIO_CB_ON
;
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
);
133 cb
= malloc(sizeof(struct gpio_cbdata
));
137 memset(cb
, 0, sizeof(*cb
));
139 cb
->type
= GPIO_CB_OFF
;
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
);
148 cb
= malloc(sizeof(struct gpio_cbdata
));
152 memset(cb
, 0, sizeof(*cb
));
154 cb
->type
= GPIO_CB_INTR_READ
;
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
);
168 /* This hook will be called once, after VTreeFS has initialized. */
170 log_warn(&log
, "Failed to init gpio driver\n");
173 struct machine machine
;
174 sys_getmachine(&machine
);
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
);
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);
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
)){
194 /* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */
196 sys_padconf(CONTROL_CONF_MCASP0_FSR
, 0xffffffff,
197 (CONTROL_CONF_PUTYPESEL
| CONTROL_CONF_MUXMODE(7)));
199 add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT
);
201 /* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */
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)));
207 add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT
);
214 (struct inode
*inode
, char *ptr
, size_t len
, off_t offset
, cbdata_t cbdata
)
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. */
219 struct gpio_cbdata
*gpio_cbdata
= (struct gpio_cbdata
*) cbdata
;
220 assert(gpio_cbdata
->gpio
!= NULL
);
222 if (gpio_cbdata
->type
== GPIO_CB_ON
223 || gpio_cbdata
->type
== GPIO_CB_OFF
) {
225 if (gpio_set(gpio_cbdata
->gpio
,
226 (gpio_cbdata
->type
== GPIO_CB_ON
) ? 1 : 0))
231 if (gpio_cbdata
->type
== GPIO_CB_INTR_READ
) {
232 /* reading interrupt */
233 if (gpio_intr_read(gpio_cbdata
->gpio
, &value
))
237 if (gpio_read(gpio_cbdata
->gpio
, &value
))
240 snprintf(ptr
, DATA_SIZE
, "%d\n", value
);
243 /* If the offset is at or beyond the end of the string, return EOF. */
247 /* Otherwise, we may have to move the data to the start of ptr. */
251 memmove(ptr
, &ptr
[offset
], len
);
254 /* Return the resulting length. */
259 message_hook(message
* m
, int __unused ipc_status
)
261 gpio_intr_message(m
);
265 main(int argc
, char **argv
)
268 struct fs_hooks hooks
;
269 struct inode_stat root_stat
;
271 /* Set and apply the environment */
272 env_setargs(argc
, argv
);
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
;
280 root_stat
.mode
= S_IFDIR
| S_IRUSR
| S_IRGRP
| S_IROTH
;
284 root_stat
.dev
= NO_DEV
;
287 run_vtreefs(&hooks
, 30, 0, &root_stat
, 0, DATA_SIZE
);