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 74*5eefd0feSDavid van Moolenbroek /* Buffer size for read requests */ 75*5eefd0feSDavid van Moolenbroek #define DATA_SIZE 26 76*5eefd0feSDavid van Moolenbroek 77433d6423SLionel Sambuc int 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 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 212*5eefd0feSDavid van Moolenbroek static ssize_t 213433d6423SLionel Sambuc read_hook 214*5eefd0feSDavid 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, 226*5eefd0feSDavid van Moolenbroek (gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0)) 227433d6423SLionel Sambuc return EIO; 228*5eefd0feSDavid van Moolenbroek return 0; 229433d6423SLionel Sambuc } 230433d6423SLionel Sambuc 231433d6423SLionel Sambuc if (gpio_cbdata->type == GPIO_CB_INTR_READ) { 232433d6423SLionel Sambuc /* reading interrupt */ 233*5eefd0feSDavid van Moolenbroek if (gpio_intr_read(gpio_cbdata->gpio, &value)) 234433d6423SLionel Sambuc return EIO; 235433d6423SLionel Sambuc } else { 236433d6423SLionel Sambuc /* reading */ 237*5eefd0feSDavid van Moolenbroek if (gpio_read(gpio_cbdata->gpio, &value)) 238433d6423SLionel Sambuc return EIO; 239433d6423SLionel Sambuc } 240*5eefd0feSDavid van Moolenbroek snprintf(ptr, DATA_SIZE, "%d\n", value); 241*5eefd0feSDavid van Moolenbroek len = strlen(ptr); 242433d6423SLionel Sambuc 243*5eefd0feSDavid van Moolenbroek /* If the offset is at or beyond the end of the string, return EOF. */ 244*5eefd0feSDavid van Moolenbroek if (offset >= len) 245*5eefd0feSDavid van Moolenbroek return 0; 246433d6423SLionel Sambuc 247*5eefd0feSDavid van Moolenbroek /* Otherwise, we may have to move the data to the start of ptr. */ 248*5eefd0feSDavid van Moolenbroek if (offset > 0) { 249*5eefd0feSDavid van Moolenbroek len -= offset; 250*5eefd0feSDavid van Moolenbroek 251*5eefd0feSDavid van Moolenbroek memmove(ptr, &ptr[offset], len); 252433d6423SLionel Sambuc } 253433d6423SLionel Sambuc 254*5eefd0feSDavid van Moolenbroek /* Return the resulting length. */ 255*5eefd0feSDavid van Moolenbroek return len; 256433d6423SLionel Sambuc } 257433d6423SLionel Sambuc 258*5eefd0feSDavid van Moolenbroek static void 259*5eefd0feSDavid 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 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 286433d6423SLionel Sambuc /* limit the number of indexed entries */ 287*5eefd0feSDavid van Moolenbroek start_vtreefs(&hooks, 30, &root_stat, 0, DATA_SIZE); 288433d6423SLionel Sambuc 289433d6423SLionel Sambuc return EXIT_SUCCESS; 290433d6423SLionel Sambuc } 291