From 98da445269bf4cb4a79b6fe1e343ad36ad4591c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinier=20Millo=20S=C3=A1nchez?= Date: Mon, 24 Aug 2015 16:29:15 -0400 Subject: [PATCH] gpio: RaspberryPI GPIO driver Fixes #1654 --- .../include/platform/rpi/drivers/board_base.h | 3 + repos/base/mk/spec-platform_rpi.mk | 2 +- repos/os/lib/mk/foc_rpi/gpio.mk | 2 + repos/os/lib/mk/hw_rpi/gpio.mk | 2 + repos/os/lib/mk/rpi/gpio.inc | 5 + repos/os/src/drivers/gpio/rpi/driver.h | 207 +++++++++++++++ repos/os/src/drivers/gpio/rpi/foc/irq.h | 25 ++ repos/os/src/drivers/gpio/rpi/gpio.h | 238 ++++++++++++++++++ repos/os/src/drivers/gpio/rpi/hw/irq.h | 25 ++ repos/os/src/drivers/gpio/rpi/main.cc | 103 ++++++++ repos/os/src/drivers/gpio/rpi/target.mk | 3 + 11 files changed, 614 insertions(+), 1 deletion(-) create mode 100644 repos/os/lib/mk/foc_rpi/gpio.mk create mode 100644 repos/os/lib/mk/hw_rpi/gpio.mk create mode 100644 repos/os/lib/mk/rpi/gpio.inc create mode 100644 repos/os/src/drivers/gpio/rpi/driver.h create mode 100644 repos/os/src/drivers/gpio/rpi/foc/irq.h create mode 100644 repos/os/src/drivers/gpio/rpi/gpio.h create mode 100644 repos/os/src/drivers/gpio/rpi/hw/irq.h create mode 100644 repos/os/src/drivers/gpio/rpi/main.cc create mode 100644 repos/os/src/drivers/gpio/rpi/target.mk diff --git a/repos/base/include/platform/rpi/drivers/board_base.h b/repos/base/include/platform/rpi/drivers/board_base.h index cca7f0687..70f6fee01 100644 --- a/repos/base/include/platform/rpi/drivers/board_base.h +++ b/repos/base/include/platform/rpi/drivers/board_base.h @@ -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, diff --git a/repos/base/mk/spec-platform_rpi.mk b/repos/base/mk/spec-platform_rpi.mk index 06c828214..f2f67e106 100644 --- a/repos/base/mk/spec-platform_rpi.mk +++ b/repos/base/mk/spec-platform_rpi.mk @@ -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 diff --git a/repos/os/lib/mk/foc_rpi/gpio.mk b/repos/os/lib/mk/foc_rpi/gpio.mk new file mode 100644 index 000000000..d9f12b813 --- /dev/null +++ b/repos/os/lib/mk/foc_rpi/gpio.mk @@ -0,0 +1,2 @@ +INC_DIR += $(REP_DIR)/src/drivers/gpio/rpi/foc +include $(REP_DIR)/lib/mk/rpi/gpio.inc diff --git a/repos/os/lib/mk/hw_rpi/gpio.mk b/repos/os/lib/mk/hw_rpi/gpio.mk new file mode 100644 index 000000000..99a2e4886 --- /dev/null +++ b/repos/os/lib/mk/hw_rpi/gpio.mk @@ -0,0 +1,2 @@ +INC_DIR += $(REP_DIR)/src/drivers/gpio/rpi/hw +include $(REP_DIR)/lib/mk/rpi/gpio.inc diff --git a/repos/os/lib/mk/rpi/gpio.inc b/repos/os/lib/mk/rpi/gpio.inc new file mode 100644 index 000000000..d643be540 --- /dev/null +++ b/repos/os/lib/mk/rpi/gpio.inc @@ -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 diff --git a/repos/os/src/drivers/gpio/rpi/driver.h b/repos/os/src/drivers/gpio/rpi/driver.h new file mode 100644 index 000000000..50d85ad1e --- /dev/null +++ b/repos/os/src/drivers/gpio/rpi/driver.h @@ -0,0 +1,207 @@ +/* + * \brief Gpio driver for the RaspberryPI + * \author Reinier Millo Sánchez + * \author Alexy Gallardo Segura + * \author Humberto Lopéz Leon + * \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 +#include +#include +#include + +/* local includes */ +#include +#include + +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 _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_ */ diff --git a/repos/os/src/drivers/gpio/rpi/foc/irq.h b/repos/os/src/drivers/gpio/rpi/foc/irq.h new file mode 100644 index 000000000..8c8af8ef8 --- /dev/null +++ b/repos/os/src/drivers/gpio/rpi/foc/irq.h @@ -0,0 +1,25 @@ +/* + * \brief GPIO interrupt number + * \author Reinier Millo Sánchez + * \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 + +namespace Gpio +{ + enum { IRQ = 49 }; +} + +#endif /* _IRQ_H_ */ diff --git a/repos/os/src/drivers/gpio/rpi/gpio.h b/repos/os/src/drivers/gpio/rpi/gpio.h new file mode 100644 index 000000000..7401b0bb2 --- /dev/null +++ b/repos/os/src/drivers/gpio/rpi/gpio.h @@ -0,0 +1,238 @@ +/* + * \brief Gpio driver for the RaspberryPI + * \author Reinier Millo Sánchez + * \author Alexy Gallardo Segura + * \author Humberto Lopéz Leon + * \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 +#include +#include +#include + +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 + void _set_gpio_det(unsigned gpio) + { + write(0, gpio); + write(0, gpio); + write(0, gpio); + write(0, gpio); + write(0, gpio); + write(1, gpio); + } + + public: + + Reg(addr_t base, off_t offset, size_t size) + : + Attached_io_mem_dataspace(base, size), + Mmio((addr_t)local_addr() + 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(1); + _delayer.usleep(1); + write(1, gpio); + _delayer.usleep(1); + write(0); + write(0, gpio); + } + /* set the pin function */ + unsigned sel_id = gpio % 10; + unsigned reg_id = gpio / 10; + switch(sel_id){ + case 0: write(function, reg_id); break; + case 1: write(function, reg_id); break; + case 2: write(function, reg_id); break; + case 3: write(function, reg_id); break; + case 4: write(function, reg_id); break; + case 5: write(function, reg_id); break; + case 6: write(function, reg_id); break; + case 7: write(function, reg_id); break; + case 8: write(function, reg_id); break; + case 9: write(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(reg_id); + case 1: return read(reg_id); + case 2: return read(reg_id); + case 3: return read(reg_id); + case 4: return read(reg_id); + case 5: return read(reg_id); + case 6: return read(reg_id); + case 7: return read(reg_id); + case 8: return read(reg_id); + case 9: return read(reg_id); + default: return 0; + } + } + + int get_gpio_level(unsigned gpio) { return read(gpio); } + void set_gpio_level(unsigned gpio) { write(1, gpio); } + void clear_gpio_level(unsigned gpio) { write(1, gpio); } + + void set_gpio_falling_detect(unsigned gpio) { + _set_gpio_det(gpio); } + + void set_gpio_rising_detect(unsigned gpio) { + _set_gpio_det(gpio); } + + void set_gpio_high_detect(unsigned gpio) { + _set_gpio_det(gpio); } + + void set_gpio_low_detect(unsigned gpio) { + _set_gpio_det(gpio); } + + void set_gpio_async_falling_detect(unsigned gpio) { + _set_gpio_det(gpio); } + + void set_gpio_async_rising_detect(unsigned gpio) { + _set_gpio_det(gpio); } + + template + void for_each_gpio_status(F f) + { + Gppeds_raw::access_t const gppeds = read(); + for(unsigned i = 0; i < Gppeds_raw::ACCESS_WIDTH; i++) { + f(i, gppeds & (1 << i)); } + } + + void clear_event(unsigned gpio) { write(1, gpio); } +}; + +#endif /* _GPIO_H_ */ diff --git a/repos/os/src/drivers/gpio/rpi/hw/irq.h b/repos/os/src/drivers/gpio/rpi/hw/irq.h new file mode 100644 index 000000000..a86888708 --- /dev/null +++ b/repos/os/src/drivers/gpio/rpi/hw/irq.h @@ -0,0 +1,25 @@ +/* + * \brief GPIO interrupt number + * \author Reinier Millo Sánchez + * \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 + +namespace Gpio +{ + enum { IRQ = Genode::Board_base::GPU_IRQ_BASE + 49 }; +} + +#endif /* _IRQ_H_ */ diff --git a/repos/os/src/drivers/gpio/rpi/main.cc b/repos/os/src/drivers/gpio/rpi/main.cc new file mode 100644 index 000000000..76187650a --- /dev/null +++ b/repos/os/src/drivers/gpio/rpi/main.cc @@ -0,0 +1,103 @@ +/* + * \brief Gpio driver for the RaspberryPI + * \author Reinier Millo Sánchez + * \author Alexy Gallardo Segura + * \author Humberto Lopéz Leon + * \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 +#include +#include +#include +#include +#include + +/* 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); } +} diff --git a/repos/os/src/drivers/gpio/rpi/target.mk b/repos/os/src/drivers/gpio/rpi/target.mk new file mode 100644 index 000000000..aa3c71ced --- /dev/null +++ b/repos/os/src/drivers/gpio/rpi/target.mk @@ -0,0 +1,3 @@ +TARGET = gpio_drv +REQUIRES = rpi +LIBS += gpio