./usr/include/evbarm/vmparam.h minix-sys
./usr/include/evbarm/wchar_limits.h minix-sys
./usr/include/i386 minix-sys obsolete
+./usr/sbin/gpio minix-sys
./usr/mdec minix-sys
.endif
.if ${MACHINE_ARCH} == "earm"
-SUBDIR= mmc log tty
+SUBDIR= gpio mmc log tty
.endif
.endif # ${MKIMAGEONLY} != "yes"
--- /dev/null
+# Makefile for the gpio driver.
+PROG= gpio
+SRCS= gpio.c gpio_omap.c
+
+DPADD+= ${LIBBLOCKDRIVER} ${LIBSYS}
+LDADD+= -lvtreefs -lsys
+
+#
+# This is a system driver.
+CPPFLAGS+= -D_SYSTEM=1
+
+MAN=
+
+BINDIR?= /usr/sbin
+
+.include <minix.service.mk>
--- /dev/null
+General Purpose Input and Output
+
+To make MINIX more usable on embedded hardware we need some way to access the
+GPIO features of the system on chip’s. Generally System on Chips (SoC) designs
+provide some way configure pads to perform basic Input/Output configuration on
+selected ports. These ports are also usually grouped into a bank. The end
+result is that you have a functional general input output block where you need
+to configure some the following functions.
+
+Functional requirements
+
+We envision that the short term usage of the GPIO library will be booth input
+and output handling. Input handling as we want to be able to listen to button
+presses and genrate key events and output handling because we want to be able
+to control leds.
+
+GPIO required functionality
+-Configure pins as input or output.
+-Configure the impedance of the pins.
+-Get or set the values of the pins(possibly in a single call).
+-Configure interrupt levels for input pins.
+
+Configure debouncing of pins.
+
+Additional kernel requirements
+-Manage the GPIO resources (who may access what)
+-Access the GPIO pins from within driver (for the keyboard)
+-Access the GPIO pins from within userland (for toggeling leds)
+
+
+Usage:
+You have to manualy mount the gpio fs using the following command
+
+# mount -t gpio none /gpio
+
--- /dev/null
+/*
+ * GPIO driver. This driver acts as a file system to allow
+ * reading and toggling of GPIO's.
+ */
+/* kernel headers */
+#include <minix/driver.h>
+#include <minix/drvlib.h>
+#include <minix/vtreefs.h>
+
+/* system headers */
+#include <sys/stat.h>
+#include <sys/queue.h>
+
+/* usr headers */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+
+/* local headers */
+#include "log.h"
+#include "mmio.h"
+#include "gpio.h"
+
+/* used for logging */
+static struct log log = {
+ .name = "gpio",
+ .log_level = LEVEL_INFO,
+ .log_func = default_log
+};
+
+#define GPIO_CB_READ 0
+#define GPIO_CB_ON 1
+#define GPIO_CB_OFF 2
+
+/* The vtreefs library provides callback data when calling
+ * the read function of inode. gpio_cbdata is used here to
+ * map between inodes and gpio's. VTreeFS is read-only. to work
+ * around that issue for a single GPIO we create multiple virtual
+ * files that can be *read* to read the gpio value and power on
+ * and off the gpio.
+ */
+struct gpio_cbdata
+{
+ struct gpio *gpio; /* obtained from the driver */
+ int type; /* read=0/on=1/off=2 */
+ TAILQ_ENTRY(gpio_cbdata) next;
+};
+
+/* list of inodes used in this driver */
+TAILQ_HEAD(gpio_cbdata_head, gpio_cbdata)
+ gpio_cbdata_list = TAILQ_HEAD_INITIALIZER(gpio_cbdata_list);
+
+static struct gpio_driver drv;
+
+/* Sane file stats for a directory */
+static struct inode_stat default_file_stat = {
+ .mode = S_IFREG | 04,
+ .uid = 0,
+ .gid = 0,
+ .size = 0,
+ .dev = NO_DEV,
+};
+
+int
+add_gpio_inode(char *name, int nr, int mode)
+{
+ /* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
+ * set values as we don't support writing yet */
+ char tmpname[200];
+ struct gpio_cbdata *cb;
+ struct gpio *gpio;
+
+ /* claim and configure the gpio */
+ if (drv.claim("gpiofs", nr, &gpio)) {
+ log_warn(&log, "Failed to claim GPIO %d\n", nr);
+ return EIO;
+ }
+ assert(gpio != NULL);
+
+ if (drv.pin_mode(gpio, mode)) {
+ log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr,
+ mode);
+ return EIO;
+ }
+
+ /* read value */
+ cb = malloc(sizeof(struct gpio_cbdata));
+ if (cb == NULL) {
+ return ENOMEM;
+ }
+ memset(cb, 0, sizeof(*cb));
+
+ cb->type = GPIO_CB_READ;
+ cb->gpio = gpio;
+
+ snprintf(tmpname, 200, "%s", name);
+ add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0,
+ (cbdata_t) cb);
+ TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
+
+ if (mode == GPIO_MODE_OUTPUT) {
+ /* if we configured the GPIO pin as output mode also create
+ * two additional files to turn on and off the GPIO. */
+ /* turn on */
+ cb = malloc(sizeof(struct gpio_cbdata));
+ if (cb == NULL) {
+ return ENOMEM;
+ }
+ memset(cb, 0, sizeof(*cb));
+
+ cb->type = GPIO_CB_ON;
+ cb->gpio = gpio;
+
+ snprintf(tmpname, 200, "%son", name);
+ add_inode(get_root_inode(), tmpname, NO_INDEX,
+ &default_file_stat, 0, (cbdata_t) cb);
+ TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
+
+ /* turn off */
+ cb = malloc(sizeof(struct gpio_cbdata));
+ if (cb == NULL) {
+ return ENOMEM;
+ }
+ memset(cb, 0, sizeof(*cb));
+
+ cb->type = GPIO_CB_OFF;
+ cb->gpio = gpio;
+
+ snprintf(tmpname, 200, "%soff", name);
+ add_inode(get_root_inode(), tmpname, NO_INDEX,
+ &default_file_stat, 0, (cbdata_t) cb);
+ TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
+ }
+ return OK;
+}
+
+static void
+init_hook(void)
+{
+ /* This hook will be called once, after VTreeFS has initialized. */
+ if (omap_gpio_init(&drv)) {
+ log_warn(&log, "Failed to init gpio driver\n");
+ }
+ add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT);
+ add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT);
+ add_gpio_inode("Button", 4, GPIO_MODE_INPUT);
+
+#if 0
+ add_gpio_inode("input1", 139, GPIO_MODE_INPUT);
+ add_gpio_inode("input2", 144, GPIO_MODE_INPUT);
+#endif
+}
+
+static int
+ read_hook
+ (struct inode *inode, off_t offset, char **ptr, size_t * len,
+ cbdata_t cbdata)
+{
+ /* This hook will be called every time a regular file is read. We use
+ * it to dyanmically generate the contents of our file. */
+ static char data[26];
+ int value;
+ struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
+ assert(gpio_cbdata->gpio != NULL);
+
+ if (gpio_cbdata->type == GPIO_CB_ON) {
+ /* turn on */
+ if (drv.set(gpio_cbdata->gpio, 1)) {
+ *len = 0;
+ return EIO;
+ }
+ *len = 0;
+ return OK;
+ } else if (gpio_cbdata->type == GPIO_CB_OFF) {
+ /* turn off */
+ if (drv.set(gpio_cbdata->gpio, 0)) {
+ *len = 0;
+ return EIO;
+ }
+ *len = 0;
+ return OK;
+ }
+
+ /* reading */
+ if (drv.read(gpio_cbdata->gpio, &value)) {
+ *len = 0;
+ return EIO;
+ }
+ snprintf(data, 26, "%d\n", value);
+
+ /* If the offset is beyond the end of the string, return EOF. */
+ if (offset > strlen(data)) {
+ *len = 0;
+
+ return OK;
+ }
+
+ /* Otherwise, return a pointer into 'data'. If necessary, bound the
+ * returned length to the length of the rest of the string. Note that
+ * 'data' has to be static, because it will be used after this
+ * function returns. */
+ *ptr = data + offset;
+
+ if (*len > strlen(data) - offset)
+ *len = strlen(data) - offset;
+
+ return OK;
+}
+
+int
+main(int argc, char **argv)
+{
+
+ struct fs_hooks hooks;
+ struct inode_stat root_stat;
+
+ /* Set and apply the environment */
+ env_setargs(argc, argv);
+
+ /* fill in the hooks */
+ memset(&hooks, 0, sizeof(hooks));
+ hooks.init_hook = init_hook;
+ hooks.read_hook = read_hook;
+
+ root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
+ root_stat.uid = 0;
+ root_stat.gid = 0;
+ root_stat.size = 0;
+ root_stat.dev = NO_DEV;
+
+ /* limit the number of indexed entries */
+ start_vtreefs(&hooks, 10, &root_stat, 0);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#ifndef __INCLUDE_GPIO_H__
+#define __INCLUDE_GPIO_H__
+
+struct gpio
+{
+ int nr; /* GPIO number */
+ int mode; /* GPIO mode (input=0/output=1) */
+ void *data; /* data pointer (not used in the omap driver) */
+};
+
+#define GPIO_MODE_INPUT 0
+#define GPIO_MODE_OUTPUT 1
+
+struct gpio_driver
+{
+ /* request access to a gpio */
+ int (*claim) (char *owner, int nr, struct gpio ** gpio);
+
+ /* Configure the GPIO for a certain purpose */
+ int (*pin_mode) (struct gpio * gpio, int mode);
+
+ /* Set the value for a GPIO */
+ int (*set) (struct gpio * gpio, int value);
+
+ /* Read the value of the GPIO */
+ int (*read) (struct gpio * gpio, int *value);
+};
+
+int omap_gpio_init(struct gpio_driver *gpio_driver);
+#endif /* __INCLUDE_GPIO_H__ */
--- /dev/null
+/* kernel headers */
+#include <minix/syslib.h>
+#include <minix/drvlib.h>
+
+/* system headers */
+#include <sys/mman.h>
+#include <sys/types.h>
+
+/* usr headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+/* local headers */
+#include "log.h"
+#include "mmio.h"
+#include "gpio.h"
+
+/* used for logging */
+static struct log log = {
+ .name = "gpio_omap",
+ .log_level = LEVEL_INFO,
+ .log_func = default_log
+};
+
+struct omap_gpio_bank
+{
+ const char *name;
+ uint32_t register_address;
+ uint32_t base_address;
+ uint32_t disabled;
+};
+
+static struct omap_gpio_bank omap_gpio_banks[] = {
+ {"GPIO1", 0x48310000, 0, 0},
+ {"GPIO2", 0x49050000, 0, 0},
+ {"GPIO3", 0x49052000, 0, 0},
+ {"GPIO4", 0x49054000, 0, 0},
+ {"GPIO5", 0x49056000, 0, 0},
+ {"GPIO6", 0x49058000, 0, 0},
+ {NULL, 0, 0}
+};
+
+#define GPIO_REVISION 0x00
+#define GPIO_REVISION_MAJOR(X) ((X & 0xF0) >> 4)
+#define GPIO_REVISION_MINOR(X) (X & 0XF)
+
+#define GPIO_DATAOUT 0x3c
+#define GPIO_DATAIN 0x38
+#define GPIO_OE 0x34 /* Output Data Enable */
+#define GPIO_CLEARDATAOUT 0x90
+#define GPIO_SETDATAOUT 0x94
+
+#define LED_USR0 (1 << 21)
+#define LED_USR1 (1 << 22)
+
+struct omap_gpio_bank *
+omap_gpio_bank_get(int gpio_nr)
+{
+ struct omap_gpio_bank *bank;
+ assert(gpio_nr >= 0 && gpio_nr <= 32 * 6);
+ bank = &omap_gpio_banks[gpio_nr / 32];
+ return bank;
+}
+
+int
+omap_gpio_claim(char *owner, int nr, struct gpio **gpio)
+{
+ log_trace(&log, "%s s claiming %d\n", owner, nr);
+
+ if (nr < 0 && nr >= 32 * 6) {
+ log_warn(&log, "%s is claiming unknown GPIO number %d\n", owner,
+ nr);
+ return EINVAL;
+ }
+
+ if ( omap_gpio_bank_get(nr)->disabled == 1) {
+ log_warn(&log, "%s is claiming GPIO %d from disabled bank\n", owner,
+ nr);
+ return EINVAL;
+ }
+
+ struct gpio *tmp = malloc(sizeof(struct gpio));
+ memset(tmp, 0, sizeof(*tmp));
+
+ tmp->nr = nr;
+ *gpio = tmp;
+ return OK;
+}
+
+int
+omap_gpio_pin_mode(struct gpio *gpio, int mode)
+{
+ struct omap_gpio_bank *bank;
+ assert(gpio != NULL);
+ gpio->mode = mode;
+
+ bank = omap_gpio_bank_get(gpio->nr);
+ log_debug(&log,
+ "pin mode bank %s, base address 0x%x -> register address (0x%x,0x%x,0x%x)\n",
+ bank->name, bank->base_address, bank->register_address, GPIO_OE,
+ bank->register_address + GPIO_OE);
+
+ if (mode == GPIO_MODE_OUTPUT) {
+ set32(bank->base_address + GPIO_OE, BIT(gpio->nr % 32), 0);
+ } else {
+ set32(bank->base_address + GPIO_OE, BIT(gpio->nr % 32),
+ 0xffffffff);
+ }
+ return 0;
+}
+
+int
+omap_gpio_set(struct gpio *gpio, int value)
+{
+ struct omap_gpio_bank *bank;
+ assert(gpio != NULL);
+ assert(gpio->nr >= 0 && gpio->nr <= 32 * 6);
+
+ bank = omap_gpio_bank_get(gpio->nr);
+ if (value == 1) {
+ write32(bank->base_address + GPIO_SETDATAOUT,
+ BIT(gpio->nr % 32));
+ } else {
+ write32(bank->base_address + GPIO_CLEARDATAOUT,
+ BIT(gpio->nr % 32));
+ }
+ return OK;
+}
+
+int
+omap_gpio_read(struct gpio *gpio, int *value)
+{
+ struct omap_gpio_bank *bank;
+ assert(gpio != NULL);
+ assert(gpio->nr >= 0 && gpio->nr <= 32 * 6);
+
+ bank = omap_gpio_bank_get(gpio->nr);
+ log_trace(&log, "mode=%d OU/IN 0x%08x 0x%08x\n", gpio->mode,
+ read32(bank->base_address + GPIO_DATAIN),
+ read32(bank->base_address + GPIO_DATAOUT));
+
+ if (gpio->mode == GPIO_MODE_INPUT) {
+ *value =
+ (read32(bank->base_address +
+ GPIO_DATAIN) >> (gpio->nr % 32)) & 0x1;
+ } else {
+ *value =
+ (read32(bank->base_address +
+ GPIO_DATAOUT) >> (gpio->nr % 32)) & 0x1;
+ }
+
+ return OK;
+}
+
+int
+omap_gpio_init(struct gpio_driver *drv)
+{
+ u32_t revision;
+ int i;
+ struct minix_mem_range mr;
+ struct omap_gpio_bank *bank;
+
+ bank = &omap_gpio_banks[0];
+ for (i = 0; omap_gpio_banks[i].name != NULL; i++) {
+ bank = &omap_gpio_banks[i];
+ mr.mr_base = bank->register_address;
+ mr.mr_limit = bank->register_address + 0x400;
+
+ if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != 0) {
+ log_warn(&log,
+ "Unable to request permission to map memory\n");
+ return EPERM; /* fixme */
+ }
+
+ /* Set the base address to use */
+ bank->base_address =
+ (uint32_t) vm_map_phys(SELF,
+ (void *) bank->register_address, 0x400);
+
+ if (bank->base_address == (uint32_t) MAP_FAILED) {
+ log_warn(&log, "Unable to map GPIO memory\n");
+ return EPERM; /* fixme */
+ }
+
+ revision = 0;
+ revision = read32(bank->base_address + GPIO_REVISION);
+ /* test if we can access it */
+ if (GPIO_REVISION_MAJOR(revision) != 2
+ || GPIO_REVISION_MINOR(revision) != 5) {
+ log_warn(&log,
+ "Failed to read the revision of GPIO bank %s.. disabling\n",
+ bank->name);
+ bank->disabled = 1;
+ }
+ bank->disabled = 0;
+ log_trace(&log, "bank %s mapped on 0x%x\n", bank->name,
+ bank->base_address);
+ }
+
+/* the following code need to move to a power management/clock service */
+#define CM_BASE 0x48004000
+#define CM_FCLKEN_WKUP 0xC00
+#define CM_ICLKEN_WKUP 0xC10
+
+ u32_t base;
+ mr.mr_base = CM_BASE;
+ mr.mr_limit = CM_BASE + 0x1000;
+
+ if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != 0) {
+ log_warn(&log, "Unable to request permission to map memory\n");
+ return EPERM;
+ }
+
+ base = (uint32_t) vm_map_phys(SELF, (void *) CM_BASE, 0x1000);
+
+ if (base == (uint32_t) MAP_FAILED) {
+ log_warn(&log, "Unable to map GPIO memory\n");
+ return EPERM;
+ }
+
+ /* enable the interface and functional clock on GPIO bank 1 */
+ set32(base + CM_FCLKEN_WKUP, BIT(3), 0xffffffff);
+ set32(base + CM_ICLKEN_WKUP, BIT(3), 0xffffffff);
+/* end power management/clock service stuff */
+
+
+ drv->claim = omap_gpio_claim;
+ drv->pin_mode = omap_gpio_pin_mode;
+ drv->set = omap_gpio_set;
+ drv->read = omap_gpio_read;
+ return 0;
+}
--- /dev/null
+#ifndef __LOG_H__
+#define __LOG_H__
+/*
+ * Simple logging functions
+ */
+
+/*
+ * LEVEL_NONE do not log anything.
+ * LEVEL_WARN Information that needs to be known.
+ * LEVEL_INFO Basic information like startup messages and occasional events.
+ * LEVEL_DEBUG debug statements about things happening that are less expected.
+ * LEVEL_TRACE Way to much information for anybody.
+ */
+
+#define LEVEL_NONE 0
+#define LEVEL_WARN 1
+#define LEVEL_INFO 2
+#define LEVEL_DEBUG 3
+#define LEVEL_TRACE 4
+
+static const char *level_string[5] = {
+ "none",
+ "warn",
+ "info",
+ "debug",
+ "trace"
+};
+
+/*
+ * struct to be initialized by the user of the logging system.
+ *
+ * name: The name attribute is used in logging statements do differentiate
+ * drivers
+ *
+ * log_level The level attribute describes the requested logging level. a level
+ * of 1 will only print warnings while a level of 4 will print all the trace
+ * information.
+ *
+ * log_func The logging function to use to log, log.h provides default_log
+ * to display information on the kernel output buffer. As a bonus if the
+ * requested log level is debug or trace the method , file and line number will
+ * be printed to the steam.
+ */
+struct log { const char *name; int log_level;
+
+ /* the logging function itself */
+ void (*log_func) (struct log * driver,
+ int level,
+ const char *file,
+ const char *function, int line, const char *fmt, ...);
+
+};
+
+#define __log(driver,log_level, fmt, args...) \
+ ((driver)->log_func(driver,log_level, \
+ __FILE__, __FUNCTION__, __LINE__,\
+ fmt, ## args))
+
+/* Log a warning */
+#define log_warn(driver, fmt, args...) \
+ __log(driver, LEVEL_WARN, fmt, ## args)
+
+/* Log an information message */
+#define log_info(driver, fmt, args...) \
+ __log(driver, LEVEL_INFO, fmt, ## args)
+
+/* log debugging output */
+#define log_debug(driver, fmt, args...) \
+ __log(driver, LEVEL_DEBUG, fmt, ## args)
+
+/* log trace output */
+#define log_trace(driver, fmt, args...) \
+ __log(driver, LEVEL_TRACE, fmt, ## args)
+
+#endif /* __LOG_H__ */
+
+static void
+default_log(struct log *driver,
+ int level,
+ const char *file, const char *function, int line, const char *fmt, ...)
+{
+ va_list args;
+
+ if (level > driver->log_level) {
+ return;
+ }
+ /* If the wanted level is debug also display line/method information */
+ if (driver->log_level >= LEVEL_DEBUG) {
+ fprintf(stderr, "%s(%s):%s+%d(%s):", driver->name,
+ level_string[level], file, line, function);
+ } else {
+ fprintf(stderr, "%s(%s)", driver->name, level_string[level]);
+ }
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+
+#ifdef hacks
+static void
+hexdump(unsigned char *d, unsigned int size)
+{
+ int s;
+ for (s = 0; s < size; s += 4) {
+ fprintf(stdout, "0x%04x 0x%02X%02X%02X%02X %c%c%c%c\n", s,
+ (unsigned int) d[s], (unsigned int) d[s + 1],
+ (unsigned int) d[s + 2], (unsigned int) d[s + 3], d[s],
+ d[s + 1], d[s + 2], d[s + 3]);
+ }
+}
+#endif
--- /dev/null
+#define REG(x)(*((volatile uint32_t *)(x)))
+#define BIT(x)(0x1 << x)
+
+/* Write a uint32_t value to a memory address. */
+static inline void
+write32(uint32_t address, uint32_t value)
+{
+ REG(address) = value;
+}
+
+/* Read an uint32_t from a memory address */
+static inline uint32_t
+read32(uint32_t address)
+{
+
+ return REG(address);
+}
+
+/* Set a 32 bits value depending on a mask */
+static inline void
+set32(uint32_t address, uint32_t mask, uint32_t value)
+{
+ uint32_t val;
+ val = read32(address);
+ /* clear the bits */
+ val &= ~(mask);
+ /* apply the value using the mask */
+ val |= (value & mask);
+ write32(address, val);
+}
.if ${MACHINE_ARCH} == "earm"
EXTRA+= rc.arm mylogin.sh ttys
-PROG_DRIVERS+= mmc tty
+PROG_DRIVERS+= mmc tty gpio
PROG_COMMANDS+= cp dd getty ls time sync sleep stty umount
PROG_BIN+= cat rm
PROTO= proto.arm.small
sbin d--755 0 0
mmc ---755 0 0 mmc
mfs ---755 0 0 mfs
+ gpio ---755 0 0 gpio
+
$
mnt d--755 0 0
$
+ gpio d--755 0 0
+ $
usr d--755 0 0
bin d--755 0 0
login ---755 0 0 mylogin.sh
#/bin/service -c up /sbin/mmc -dev /dev/c0d0
#/bin/fsck.mfs -p /dev/c0d0p1
#/bin/mount /dev/c0d0p1 /mnt
+#gpio
+#mount -t gpio none /gpio
exit
;
};
+service gpio
+{
+ system
+ PRIVCTL # 4
+ ;
+};
+
service vbox
{
system