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/vmparam.h minix-sys
|
||||||
./usr/include/evbarm/wchar_limits.h minix-sys
|
./usr/include/evbarm/wchar_limits.h minix-sys
|
||||||
./usr/include/i386 minix-sys obsolete
|
./usr/include/i386 minix-sys obsolete
|
||||||
|
./usr/sbin/gpio minix-sys
|
||||||
./usr/mdec minix-sys
|
./usr/mdec minix-sys
|
||||||
|
|
|
@ -23,7 +23,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.if ${MACHINE_ARCH} == "earm"
|
.if ${MACHINE_ARCH} == "earm"
|
||||||
SUBDIR= mmc log tty
|
SUBDIR= gpio mmc log tty
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.endif # ${MKIMAGEONLY} != "yes"
|
.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"
|
.if ${MACHINE_ARCH} == "earm"
|
||||||
EXTRA+= rc.arm mylogin.sh ttys
|
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_COMMANDS+= cp dd getty ls time sync sleep stty umount
|
||||||
PROG_BIN+= cat rm
|
PROG_BIN+= cat rm
|
||||||
PROTO= proto.arm.small
|
PROTO= proto.arm.small
|
||||||
|
|
|
@ -20,9 +20,13 @@ d--755 0 0
|
||||||
sbin d--755 0 0
|
sbin d--755 0 0
|
||||||
mmc ---755 0 0 mmc
|
mmc ---755 0 0 mmc
|
||||||
mfs ---755 0 0 mfs
|
mfs ---755 0 0 mfs
|
||||||
|
gpio ---755 0 0 gpio
|
||||||
|
|
||||||
$
|
$
|
||||||
mnt d--755 0 0
|
mnt d--755 0 0
|
||||||
$
|
$
|
||||||
|
gpio d--755 0 0
|
||||||
|
$
|
||||||
usr d--755 0 0
|
usr d--755 0 0
|
||||||
bin d--755 0 0
|
bin d--755 0 0
|
||||||
login ---755 0 0 mylogin.sh
|
login ---755 0 0 mylogin.sh
|
||||||
|
|
|
@ -12,5 +12,7 @@ exec </dev/null
|
||||||
#/bin/service -c up /sbin/mmc -dev /dev/c0d0
|
#/bin/service -c up /sbin/mmc -dev /dev/c0d0
|
||||||
#/bin/fsck.mfs -p /dev/c0d0p1
|
#/bin/fsck.mfs -p /dev/c0d0p1
|
||||||
#/bin/mount /dev/c0d0p1 /mnt
|
#/bin/mount /dev/c0d0p1 /mnt
|
||||||
|
#gpio
|
||||||
|
#mount -t gpio none /gpio
|
||||||
|
|
||||||
exit
|
exit
|
||||||
|
|
|
@ -552,6 +552,13 @@ service mmc
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
service gpio
|
||||||
|
{
|
||||||
|
system
|
||||||
|
PRIVCTL # 4
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
service vbox
|
service vbox
|
||||||
{
|
{
|
||||||
system
|
system
|
||||||
|
|
Loading…
Reference in a new issue