From d05151fdef59ff586350ccd0b0f24e06d3709db3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Herthel?= <rene.herthel@haw-hamburg.de>
Date: Mon, 13 Apr 2015 14:25:42 +0200
Subject: [PATCH] cpu/mega2560: initial import of a gpio driver

---
 boards/arduino-mega2560/Makefile.features     |   1 +
 boards/arduino-mega2560/include/periph_conf.h |  26 --
 cpu/atmega2560/include/periph_cpu.h           |  27 +-
 cpu/atmega2560/periph/Makefile                |   1 +
 cpu/atmega2560/periph/gpio.c                  | 259 ++++++++++++++++++
 5 files changed, 285 insertions(+), 29 deletions(-)
 create mode 100644 cpu/atmega2560/periph/gpio.c

diff --git a/boards/arduino-mega2560/Makefile.features b/boards/arduino-mega2560/Makefile.features
index 675243084d..098dfa3e53 100644
--- a/boards/arduino-mega2560/Makefile.features
+++ b/boards/arduino-mega2560/Makefile.features
@@ -1,2 +1,3 @@
 FEATURES_PROVIDED += periph_uart
+FEATURES_PROVIDED += periph_gpio
 FEATURES_MCU_GROUP = avr8
diff --git a/boards/arduino-mega2560/include/periph_conf.h b/boards/arduino-mega2560/include/periph_conf.h
index 22549013bc..19f1d3dc1f 100644
--- a/boards/arduino-mega2560/include/periph_conf.h
+++ b/boards/arduino-mega2560/include/periph_conf.h
@@ -227,12 +227,6 @@ extern "C" {
 #define UART3_RECEIVED_DATA (UART3_CTRL_STAT_A & (1 << UART3_RX_COMPLETE))
 #define UART3_DTREG_EMPTY   (UART3_CTRL_STAT_A & (1 << UART3_DATA_EMPTY))
 
-
-
-/* TODO: add defines for device agnostic implementation */
-/** @} */
-
-
 /**
  * @brief ADC configuration
  */
@@ -261,26 +255,6 @@ extern "C" {
 #define I2C_0_EN            0
 #define I2C_0_EN            0
 
-/**
- * @brief GPIO configuration
- */
-#define GPIO_0_EN           0
-#define GPIO_1_EN           0
-#define GPIO_2_EN           0
-#define GPIO_3_EN           0
-#define GPIO_4_EN           0
-#define GPIO_5_EN           0
-#define GPIO_6_EN           0
-#define GPIO_7_EN           0
-#define GPIO_8_EN           0
-#define GPIO_9_EN           0
-#define GPIO_10_EN          0
-#define GPIO_11_EN          0
-#define GPIO_12_EN          0
-#define GPIO_13_EN          0
-#define GPIO_14_EN          0
-#define GPIO_15_EN          0
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/atmega2560/include/periph_cpu.h b/cpu/atmega2560/include/periph_cpu.h
index f4b1548294..7d38894e90 100644
--- a/cpu/atmega2560/include/periph_cpu.h
+++ b/cpu/atmega2560/include/periph_cpu.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Freie Universität Berlin
+ * Copyright (C) 2015 HAW Hamburg
  *
  * This file is subject to the terms and conditions of the GNU Lesser
  * General Public License v2.1. See the file LICENSE in the top level
@@ -13,19 +13,40 @@
  * @file
  * @brief           CPU specific definitions for internal peripheral handling
  *
- * @author          Hauke Petersen <hauke.peterse@fu-berlin.de>
+ * @author          René Herthel <rene-herthel@outlook.de>
  */
 
 #ifndef PERIPH_CPU_H_
 #define PERIPH_CPU_H_
 
 #include "periph/dev_enums.h"
+#include <avr/io.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* nothing defined here so far... */
+/**
+ * @brief   Define a CPU specific GPIO pin generator macro
+ */
+#define GPIO(x, y)          ((x << 4) | y)
+
+/**
+ * @brief   Available ports on the ATmega2560 family
+ */
+enum {
+    PORT_A = 0,       /**< port A */
+    PORT_B = 1,       /**< port B */
+    PORT_C = 2,       /**< port C */
+    PORT_D = 3,       /**< port D */
+    PORT_E = 4,       /**< port E */
+    PORT_F = 5,       /**< port F */
+    PORT_G = 6,       /**< port G */
+    PORT_H = 7,       /**< port H */
+    PORT_J = 8,       /**< port J */
+    PORT_K = 9,       /**< port K */
+    PORT_L = 10       /**< port L */
+};
 
 #ifdef __cplusplus
 }
diff --git a/cpu/atmega2560/periph/Makefile b/cpu/atmega2560/periph/Makefile
index 87a55ed684..6d1887b640 100644
--- a/cpu/atmega2560/periph/Makefile
+++ b/cpu/atmega2560/periph/Makefile
@@ -1,2 +1,3 @@
 MODULE = periph
+
 include $(RIOTBASE)/Makefile.base
diff --git a/cpu/atmega2560/periph/gpio.c b/cpu/atmega2560/periph/gpio.c
new file mode 100644
index 0000000000..275e1dc7eb
--- /dev/null
+++ b/cpu/atmega2560/periph/gpio.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2015 HAW Hamburg
+
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     driver_periph
+ * @{
+ *
+ * @file
+ * @brief       Low-level GPIO driver implementation for ATmega2560
+ *
+ * @author      René Herthel <rene-herthel@outlook.de>
+ *
+ * @}
+ */
+
+
+#include <stdio.h>
+
+#include <avr/interrupt.h>
+
+#include "cpu.h"
+#include "periph/gpio.h"
+#include "periph_conf.h"
+
+#define GPIO_BASE_PORT_A        (0x20)
+#define GPIO_OFFSET_PORT_H      (0xCB)
+#define GPIO_OFFSET_PIN_PORT    (0x02)
+#define GPIO_OFFSET_PIN_PIN     (0x03)
+#define GPIO_EXT_INT_NUMOF      (7U)
+
+typedef struct {
+    gpio_cb_t cb;
+    void *arg;
+} gpio_state_t;
+
+static gpio_state_t config[GPIO_EXT_INT_NUMOF];
+
+/**
+ * @brief     Extract the pin number of the given pin
+ */
+static inline uint8_t _pin_num(gpio_t pin)
+{
+    return (pin & 0x0f);
+}
+
+/**
+ * @brief     Extract the port number of the given pin
+ */
+static inline uint8_t _port_num(gpio_t pin)
+{
+    return (pin >> 4) & 0x0f;
+}
+
+/**
+ * @brief     Generate the PORTx address of the give pin.
+ */
+static inline uint16_t _port_addr(gpio_t pin)
+{
+    uint8_t port_num = _port_num(pin);
+    uint16_t port_addr = port_num * GPIO_OFFSET_PIN_PIN;
+
+    port_addr += GPIO_BASE_PORT_A;
+    port_addr += GPIO_OFFSET_PIN_PORT;
+
+    if (port_num > PORT_G) {
+        port_addr += GPIO_OFFSET_PORT_H;
+    }
+
+    return port_addr;
+}
+
+/**
+ * @brief     Generate the DDRx address of the given pin
+ */
+static inline uint8_t _ddr_addr(gpio_t pin)
+{
+    return (_port_addr(pin) - 0x01);
+}
+
+/**
+ * @brief     Generate the PINx address of the given pin.
+ */
+static inline uint8_t _pin_addr(gpio_t pin)
+{
+    return (_port_addr(pin) - 0x02);
+}
+
+int gpio_init(gpio_t pin, gpio_dir_t dir, gpio_pp_t pullup)
+{
+    int res;
+
+    if (dir == GPIO_DIR_OUT) {
+        _SFR_MEM8(_ddr_addr(pin)) |= (1 << _pin_num(pin));
+        res = bit_is_set(_SFR_MEM8(_ddr_addr(pin)), _pin_num(pin));
+    }
+    else {
+        _SFR_MEM8(_ddr_addr(pin)) &= ~(1 << _pin_num(pin));
+        res = bit_is_clear(_SFR_MEM8(_ddr_addr(pin)), _pin_num(pin));
+    }
+
+    return (res == 0) ? -1 : 0;
+}
+
+int gpio_init_int(gpio_t pin, gpio_pp_t pullup, gpio_flank_t flank,
+                  gpio_cb_t cb, void *arg)
+{
+    uint8_t pin_num = _pin_num(pin);
+
+    if ((_port_num(pin) == PORT_D && pin_num > 3)
+         || (_port_num(pin) == PORT_E && pin_num < 4)) {
+        return -1;
+    }
+
+    if (gpio_init(pin, GPIO_DIR_IN, pullup) < 0) {
+        return -1;
+    }
+
+    gpio_set(pin);
+
+    /* clear global interrupt flag */
+    cli();
+
+    EIMSK |= (1 << pin_num);
+
+    /* configure the flank */
+    switch (flank) {
+        case GPIO_RISING:
+            if (pin_num < 4) {
+                EICRA |= (3 << pin_num * 2);
+            }
+            else {
+                EICRB |= (3 << (pin_num * 2) % 4);
+            }
+            break;
+        case GPIO_FALLING:
+            if (pin_num < 4) {
+                EICRA |= (2 << pin_num * 2);
+            }
+            else {
+                EICRB |= (2 << (pin_num * 2) % 4);
+            }
+            break;
+        case GPIO_BOTH:
+            if (pin_num < 4) {
+                EICRA |= (1 << pin_num * 2);
+            }
+            else {
+                EICRB |= (1 << (pin_num * 2) % 4);
+            }
+            break;
+        default:
+            return -1;
+    };
+
+    /* set callback */
+    config[pin_num].cb = cb;
+    config[pin_num].arg = arg;
+
+    /* set global interrupt flag */
+    sei();
+
+    return 0;
+}
+
+void gpio_irq_enable(gpio_t pin)
+{
+    EIMSK |= (1 << _pin_num(pin));
+}
+
+void gpio_irq_disable(gpio_t pin)
+{
+    EIMSK &= ~(1 << _pin_num(pin));
+}
+
+int gpio_read(gpio_t pin)
+{
+    return (_SFR_MEM8(_pin_addr(pin)) & (1 << _pin_num(pin)));
+}
+
+void gpio_set(gpio_t pin)
+{
+    _SFR_MEM8(_port_addr(pin)) |= (1 << _pin_num(pin));
+}
+
+void gpio_clear(gpio_t pin)
+{
+    _SFR_MEM8(_port_addr(pin)) &= ~(1 << _pin_num(pin));
+}
+
+void gpio_toggle(gpio_t pin)
+{
+    if (gpio_read(pin)) {
+        gpio_clear(pin);
+    }
+    else {
+        gpio_set(pin);
+    }
+}
+
+void gpio_write(gpio_t pin, int value)
+{
+    if (value) {
+        gpio_set(pin);
+    }
+    else {
+        gpio_clear(pin);
+    }
+}
+
+static inline void irq_handler(uint8_t pin_num)
+{
+    config[pin_num].cb(config[pin_num].arg);
+}
+
+ISR(INT0_vect, ISR_BLOCK)
+{
+    irq_handler(0); /**< predefined interrupt pin */
+}
+
+ISR(INT1_vect, ISR_BLOCK)
+{
+    irq_handler(1); /**< predefined interrupt pin */
+}
+
+ISR(INT2_vect, ISR_BLOCK)
+{
+    irq_handler(2); /**< predefined interrupt pin */
+}
+
+ISR(INT3_vect, ISR_BLOCK)
+{
+    irq_handler(3); /**< predefined interrupt pin */
+}
+
+ISR(INT4_vect, ISR_BLOCK)
+{
+    irq_handler(4); /**< predefined interrupt pin */
+}
+
+ISR(INT5_vect, ISR_BLOCK)
+{
+    irq_handler(5); /**< predefined interrupt pin */
+}
+
+ISR(INT6_vect, ISR_BLOCK)
+{
+    irq_handler(6); /**< predefined interrupt pin */
+}
+
+ISR(INT7_vect, ISR_BLOCK)
+{
+    irq_handler(7); /**< predefined interrupt pin */
+}
-- 
GitLab