gpio: RaspberryPI GPIO driver

Fixes #1654
This commit is contained in:
Reinier Millo Sánchez 2015-08-24 16:29:15 -04:00 committed by Christian Helmuth
parent 433f859cb9
commit 98da445269
11 changed files with 614 additions and 1 deletions

View File

@ -49,6 +49,9 @@ struct Genode::Board_base
IRQ_CONTROLLER_BASE = 0x2000b200,
IRQ_CONTROLLER_SIZE = 0x100,
GPIO_CONTROLLER_BASE = 0x20200000,
GPIO_CONTROLLER_SIZE = 0x1000,
USB_DWC_OTG_BASE = 0x20980000,
USB_DWC_OTG_SIZE = 0x20000,

View File

@ -5,7 +5,7 @@
#
# denote wich specs are also fullfilled by this spec
SPECS += arm_v6 usb framebuffer
SPECS += arm_v6 usb framebuffer gpio
# add repository relative include paths
REP_INC_DIR += include/platform/rpi

View File

@ -0,0 +1,2 @@
INC_DIR += $(REP_DIR)/src/drivers/gpio/rpi/foc
include $(REP_DIR)/lib/mk/rpi/gpio.inc

View File

@ -0,0 +1,2 @@
INC_DIR += $(REP_DIR)/src/drivers/gpio/rpi/hw
include $(REP_DIR)/lib/mk/rpi/gpio.inc

View File

@ -0,0 +1,5 @@
SRC_CC += main.cc
LIBS += base config server
INC_DIR += $(REP_DIR)/src/drivers/gpio/rpi
vpath % $(REP_DIR)/src/drivers/gpio/rpi

View File

@ -0,0 +1,207 @@
/*
* \brief Gpio driver for the RaspberryPI
* \author Reinier Millo Sánchez <rmillo@uclv.cu>
* \author Alexy Gallardo Segura <alexy@uclv.cu>
* \author Humberto Lopéz Leon <humberto@uclv.cu>
* \date 2015-07-23
*/
/*
* Copyright (C) 2012 Ksys Labs LLC
* Copyright (C) 2012-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DRIVER_H_
#define _DRIVER_H_
/* Genode includes */
#include <base/printf.h>
#include <drivers/board_base.h>
#include <gpio/driver.h>
#include <irq_session/connection.h>
/* local includes */
#include <irq.h>
#include <gpio.h>
static int verbose = 1;
namespace Gpio { class Rpi_driver; }
class Gpio::Rpi_driver : public Driver
{
private:
enum { MAX_PINS = 54 };
Server::Entrypoint &_ep;
Reg _reg;
Genode::Irq_connection _irq;
Genode::Signal_rpc_member<Rpi_driver> _dispatcher;
Genode::Signal_context_capability _sig_cap[MAX_PINS];
bool _irq_enabled[MAX_PINS];
bool _async;
void _handle(unsigned)
{
_reg.for_each_gpio_status([&] (unsigned i, bool s) {
if (!s || !_irq_enabled[i] || !_sig_cap[i].valid()) { return; }
Genode::Signal_transmitter(_sig_cap[i]).submit();
});
}
Rpi_driver(Server::Entrypoint &ep)
:
_ep(ep),
_reg(Genode::Board_base::GPIO_CONTROLLER_BASE,
0, Genode::Board_base::GPIO_CONTROLLER_SIZE),
_irq(IRQ),
_dispatcher(ep,*this,&Rpi_driver::_handle),
_async(false)
{
_irq.sigh(_dispatcher);
_irq.ack_irq();
}
void _invalid_gpio(unsigned gpio) {
PERR("invalid GPIO pin number %u", gpio); }
public:
void set_async_events(bool async) { _async = async; }
void set_func(unsigned gpio, Reg::Function function)
{
if (verbose) PDBG("gpio=%d function=%d", gpio, function);
_reg.set_gpio_function(gpio, function);
}
static Rpi_driver& factory(Server::Entrypoint &ep);
/******************************
** Driver interface **
******************************/
bool gpio_valid(unsigned gpio) { return gpio < MAX_PINS; }
void direction(unsigned gpio, bool input)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d input=%d", gpio, input);
Reg::Function f = input ? Reg::FSEL_INPUT : Reg::FSEL_OUTPUT;
_reg.set_gpio_function(gpio, f);
}
void write(unsigned gpio, bool level)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d level=%d", gpio, level);
if (_reg.get_gpio_function(gpio)!=Reg::FSEL_OUTPUT)
PWRN("GPIO pin (%d) is not configured for output.", gpio);
if (level)
_reg.set_gpio_level(gpio);
else
_reg.clear_gpio_level(gpio);
}
bool read(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return 0; }
if(_reg.get_gpio_function(gpio) != Reg::FSEL_INPUT)
PWRN("GPIO pin (%d) is not configured for input.", gpio);
return _reg.get_gpio_level(gpio);
}
void debounce_enable(unsigned, bool) { PWRN("Not supported!"); }
void debounce_time(unsigned, unsigned long) { PWRN("Not supported!"); }
void falling_detect(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
if(_async)
_reg.set_gpio_async_falling_detect(gpio);
else
_reg.set_gpio_falling_detect(gpio);
}
void rising_detect(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
if(_async)
_reg.set_gpio_async_rising_detect(gpio);
else
_reg.set_gpio_rising_detect(gpio);
}
void high_detect(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
_reg.set_gpio_high_detect(gpio);
}
void low_detect(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
_reg.set_gpio_low_detect(gpio);
}
void irq_enable(unsigned gpio, bool enable)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d enable=%d", gpio, enable);
_irq_enabled[gpio] = enable;
}
void ack_irq(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
_reg.clear_event(gpio);
_irq.ack_irq();
}
void register_signal(unsigned gpio,
Genode::Signal_context_capability cap)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
_sig_cap[gpio] = cap;
}
void unregister_signal(unsigned gpio)
{
if (!gpio_valid(gpio)) { _invalid_gpio(gpio); return; }
if (verbose) PDBG("gpio=%d", gpio);
Genode::Signal_context_capability cap;
_sig_cap[gpio] = cap;
}
};
Gpio::Rpi_driver& Gpio::Rpi_driver::factory(Server::Entrypoint &ep)
{
static Rpi_driver driver(ep);
return driver;
}
#endif /* _DRIVER_H_ */

View File

@ -0,0 +1,25 @@
/*
* \brief GPIO interrupt number
* \author Reinier Millo Sánchez <rmillo@uclv.cu>
* \date 2015-07-27
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _IRQ_H_
#define _IRQ_H_
/* Genode includes */
#include <drivers/board_base.h>
namespace Gpio
{
enum { IRQ = 49 };
}
#endif /* _IRQ_H_ */

View File

@ -0,0 +1,238 @@
/*
* \brief Gpio driver for the RaspberryPI
* \author Reinier Millo Sánchez <rmillo@uclv.cu>
* \author Alexy Gallardo Segura <alexy@uclv.cu>
* \author Humberto Lopéz Leon <humberto@uclv.cu>
* \date 2015-07-23
*/
/*
* Copyright (C) 2012 Ksys Labs LLC
* Copyright (C) 2012-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _GPIO_H_
#define _GPIO_H_
/* Genode includes */
#include <base/printf.h>
#include <os/attached_io_mem_dataspace.h>
#include <util/mmio.h>
#include <timer_session/connection.h>
namespace Gpio {
using namespace Genode;
class Reg;
}
class Gpio::Reg : Attached_io_mem_dataspace, Mmio
{
private:
/**
* GPIO Function Select Registers
*/
struct Gpfsel : Register_array <0x00,32,6,32> {
struct Sel0 : Bitfield <0,3> {};
struct Sel1 : Bitfield <3,3> {};
struct Sel2 : Bitfield <6,3> {};
struct Sel3 : Bitfield <9,3> {};
struct Sel4 : Bitfield <12,3> {};
struct Sel5 : Bitfield <15,3> {};
struct Sel6 : Bitfield <18,3> {};
struct Sel7 : Bitfield <21,3> {};
struct Sel8 : Bitfield <24,3> {};
struct Sel9 : Bitfield <27,3> {};
};
/**
* GPIO Pin Output Set Registers
*/
struct Gpset : Register_array <0x1c,32,64,1> {};
/**
* GPIO Pin Output Clear Registers
*/
struct Gpclr : Register_array <0x28,32,64,1> {};
/**
* GPIO Pin Level Registers
*/
struct Gplev : Register_array <0x34,32,64,1> {};
/**
* GPIO Pin Event Detect Status Registers
*/
struct Gppeds : Register_array <0x40,32,64,1> {};
struct Gppeds_raw : Register <0x40,64> {};
/**
* GPIO Pin Rising Edge Detect Enable Registers
*/
struct Gpren : Register_array <0x4c,32,64,1> {};
/**
* GPIO Pin Falling Edge Detect Enable Registers
*/
struct Gpfen : Register_array <0x58,32,64,1> {};
/**
* GPIO Pin High Detect Enable Registers
*/
struct Gphen : Register_array <0x64,32,64,1> {};
/**
* GPIO Pin Low Detect Enable Registers
*/
struct Gplen : Register_array <0x70,32,64,1> {};
/**
* GPIO Pin Aync. Rising Edge Detect Registers
*/
struct Gparen : Register_array <0x7c,32,64,1> {};
/**
* GPIO Pin Async. Falling Edge Detect Registers
*/
struct Gpafen : Register_array <0x88,32,64,1> {};
/**
* GPIO Pin Pull-up/down Enable Registers
*/
struct Gppud : Register <0x94,32> {};
/**
* GPIO Pin Pull-up/down Enable Clock Registers
*/
struct Gppudclk : Register_array <0x98,32,64,1> {};
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
/**
* Implementation of 'Delayer' interface
*/
void usleep(unsigned us) { Timer::Connection::usleep(us); }
} _delayer;
template <typename T0, typename T1, typename T2,
typename T3, typename T4, typename T5>
void _set_gpio_det(unsigned gpio)
{
write<T0>(0, gpio);
write<T1>(0, gpio);
write<T2>(0, gpio);
write<T3>(0, gpio);
write<T4>(0, gpio);
write<T5>(1, gpio);
}
public:
Reg(addr_t base, off_t offset, size_t size)
:
Attached_io_mem_dataspace(base, size),
Mmio((addr_t)local_addr<Reg>() + offset)
{ }
enum Function {
FSEL_INPUT = 0,
FSEL_OUTPUT = 1,
FSEL_ALT0 = 4,
FSEL_ALT1 = 5,
FSEL_ALT2 = 6,
FSEL_ALT3 = 7,
FSEL_ALT4 = 3,
FSEL_ALT5 = 2,
};
void set_gpio_function(unsigned gpio, Function function)
{
/*
* Set a pull-up internal resistor in the input pin to avoid
* electromagnetic radiation or static noise
*/
if (function == FSEL_INPUT) {
write<Gppud>(1);
_delayer.usleep(1);
write<Gppudclk>(1, gpio);
_delayer.usleep(1);
write<Gppud>(0);
write<Gppudclk>(0, gpio);
}
/* set the pin function */
unsigned sel_id = gpio % 10;
unsigned reg_id = gpio / 10;
switch(sel_id){
case 0: write<Gpfsel::Sel0>(function, reg_id); break;
case 1: write<Gpfsel::Sel1>(function, reg_id); break;
case 2: write<Gpfsel::Sel2>(function, reg_id); break;
case 3: write<Gpfsel::Sel3>(function, reg_id); break;
case 4: write<Gpfsel::Sel4>(function, reg_id); break;
case 5: write<Gpfsel::Sel5>(function, reg_id); break;
case 6: write<Gpfsel::Sel6>(function, reg_id); break;
case 7: write<Gpfsel::Sel7>(function, reg_id); break;
case 8: write<Gpfsel::Sel8>(function, reg_id); break;
case 9: write<Gpfsel::Sel9>(function, reg_id); break;
default:;
}
}
unsigned get_gpio_function(unsigned gpio)
{
unsigned sel_id = gpio % 10;
unsigned reg_id = gpio / 10;
switch(sel_id){
case 0: return read<Gpfsel::Sel0>(reg_id);
case 1: return read<Gpfsel::Sel1>(reg_id);
case 2: return read<Gpfsel::Sel2>(reg_id);
case 3: return read<Gpfsel::Sel3>(reg_id);
case 4: return read<Gpfsel::Sel4>(reg_id);
case 5: return read<Gpfsel::Sel5>(reg_id);
case 6: return read<Gpfsel::Sel6>(reg_id);
case 7: return read<Gpfsel::Sel7>(reg_id);
case 8: return read<Gpfsel::Sel8>(reg_id);
case 9: return read<Gpfsel::Sel9>(reg_id);
default: return 0;
}
}
int get_gpio_level(unsigned gpio) { return read<Gppudclk>(gpio); }
void set_gpio_level(unsigned gpio) { write<Gpset>(1, gpio); }
void clear_gpio_level(unsigned gpio) { write<Gpclr>(1, gpio); }
void set_gpio_falling_detect(unsigned gpio) {
_set_gpio_det<Gpren, Gphen, Gplen, Gparen, Gpafen, Gpfen>(gpio); }
void set_gpio_rising_detect(unsigned gpio) {
_set_gpio_det<Gphen, Gplen, Gparen, Gpafen, Gpfen, Gpren>(gpio); }
void set_gpio_high_detect(unsigned gpio) {
_set_gpio_det<Gpren, Gplen, Gparen, Gpafen, Gpfen, Gphen>(gpio); }
void set_gpio_low_detect(unsigned gpio) {
_set_gpio_det<Gpren, Gphen, Gparen, Gpafen, Gpfen, Gplen>(gpio); }
void set_gpio_async_falling_detect(unsigned gpio) {
_set_gpio_det<Gpren, Gphen, Gplen, Gparen, Gpfen, Gpafen>(gpio); }
void set_gpio_async_rising_detect(unsigned gpio) {
_set_gpio_det<Gpren, Gphen, Gplen, Gpafen, Gpfen, Gparen>(gpio); }
template <typename F>
void for_each_gpio_status(F f)
{
Gppeds_raw::access_t const gppeds = read<Gppeds_raw>();
for(unsigned i = 0; i < Gppeds_raw::ACCESS_WIDTH; i++) {
f(i, gppeds & (1 << i)); }
}
void clear_event(unsigned gpio) { write<Gppeds>(1, gpio); }
};
#endif /* _GPIO_H_ */

View File

@ -0,0 +1,25 @@
/*
* \brief GPIO interrupt number
* \author Reinier Millo Sánchez <rmillo@uclv.cu>
* \date 2015-07-27
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _IRQ_H_
#define _IRQ_H_
/* Genode includes */
#include <drivers/board_base.h>
namespace Gpio
{
enum { IRQ = Genode::Board_base::GPU_IRQ_BASE + 49 };
}
#endif /* _IRQ_H_ */

View File

@ -0,0 +1,103 @@
/*
* \brief Gpio driver for the RaspberryPI
* \author Reinier Millo Sánchez <rmillo@uclv.cu>
* \author Alexy Gallardo Segura <alexy@uclv.cu>
* \author Humberto Lopéz Leon <humberto@uclv.cu>
* \date 2015-07-23
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <gpio/component.h>
#include <gpio/config.h>
#include <os/server.h>
/* local includes */
#include "driver.h"
struct Main
{
Server::Entrypoint &ep;
Genode::Sliced_heap sliced_heap;
Gpio::Rpi_driver &driver;
Gpio::Root root;
Main(Server::Entrypoint &ep)
:
ep(ep),
sliced_heap(Genode::env()->ram_session(), Genode::env()->rm_session()),
driver(Gpio::Rpi_driver::factory(ep)),
root(&ep.rpc_ep(), &sliced_heap, driver)
{
using namespace Genode;
printf("--- RaspberryPI gpio driver ---\n");
/*
* Check configuration for async events detect
*/
unsigned int async = 0;
try {
config()->xml_node().attribute("async_events").value(&async);
} catch (...) { }
driver.set_async_events(async>0);
/*
* Check for common GPIO configuration
*/
Gpio::process_config(driver);
/*
* Check configuration for specific function
*/
try {
Xml_node gpio_node = config()->xml_node().sub_node("gpio");
for (;; gpio_node = gpio_node.next("gpio")) {
unsigned num = 0;
unsigned function = 0;
try {
gpio_node.attribute("num").value(&num);
gpio_node.attribute("function").value(&function);
switch(function){
case 0: driver.set_func(num, Gpio::Reg::FSEL_ALT0); break;
case 1: driver.set_func(num, Gpio::Reg::FSEL_ALT1); break;
case 2: driver.set_func(num, Gpio::Reg::FSEL_ALT2); break;
case 3: driver.set_func(num, Gpio::Reg::FSEL_ALT3); break;
case 4: driver.set_func(num, Gpio::Reg::FSEL_ALT4); break;
case 5: driver.set_func(num, Gpio::Reg::FSEL_ALT5); break;
default: PWRN("Wrong pin function. Ignore node.");
}
} catch(Xml_node::Nonexistent_attribute) {
PWRN("Missing attribute. Ignore node.");
}
if (gpio_node.is_last("gpio")) break;
}
} catch (Xml_node::Nonexistent_sub_node) { PWRN("No GPIO config"); }
/*
* Announce service
*/
env()->parent()->announce(ep.manage(root));
}
};
/************
** Server **
************/
namespace Server {
char const *name() { return "gpio_drv_ep"; }
size_t stack_size() { return 1024*sizeof(long); }
void construct(Entrypoint &ep) { static Main server(ep); }
}

View File

@ -0,0 +1,3 @@
TARGET = gpio_drv
REQUIRES = rpi
LIBS += gpio