gpio:Initial GPIO driver.(ARM)
Small GPIO driver that exports a few pins using a virtual file system. Currently the two user leds and the user button are exported. Change-Id: I001d017ae27cd17b635587873f7da981054da459
This commit is contained in:
parent
5139afee6b
commit
e641d42a37
13 changed files with 714 additions and 2 deletions
|
@ -90,4 +90,5 @@
|
|||
./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
|
||||
|
|
|
@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
|
|||
.endif
|
||||
|
||||
.if ${MACHINE_ARCH} == "earm"
|
||||
SUBDIR= mmc log tty
|
||||
SUBDIR= gpio mmc log tty
|
||||
.endif
|
||||
|
||||
.endif # ${MKIMAGEONLY} != "yes"
|
||||
|
|
16
drivers/gpio/Makefile
Normal file
16
drivers/gpio/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
# 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>
|
35
drivers/gpio/README.txt
Normal file
35
drivers/gpio/README.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
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
|
||||
|
239
drivers/gpio/gpio.c
Normal file
239
drivers/gpio/gpio.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
30
drivers/gpio/gpio.h
Normal file
30
drivers/gpio/gpio.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#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__ */
|
236
drivers/gpio/gpio_omap.c
Normal file
236
drivers/gpio/gpio_omap.c
Normal file
|
@ -0,0 +1,236 @@
|
|||
/* 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;
|
||||
}
|
112
drivers/gpio/log.h
Normal file
112
drivers/gpio/log.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
#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
|
30
drivers/gpio/mmio.h
Normal file
30
drivers/gpio/mmio.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#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);
|
||||
}
|
|
@ -57,7 +57,7 @@ PROG_DRIVERS+= acpi
|
|||
|
||||
.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
|
||||
|
|
|
@ -20,9 +20,13 @@ d--755 0 0
|
|||
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
|
||||
|
|
|
@ -12,5 +12,7 @@ exec </dev/null
|
|||
#/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
|
||||
|
|
|
@ -552,6 +552,13 @@ service mmc
|
|||
;
|
||||
};
|
||||
|
||||
service gpio
|
||||
{
|
||||
system
|
||||
PRIVCTL # 4
|
||||
;
|
||||
};
|
||||
|
||||
service vbox
|
||||
{
|
||||
system
|
||||
|
|
Loading…
Reference in a new issue