#pypp 0 // Iris: micro-kernel for a capability-based operating system. // boot-programs/gpio.ccp: GPIO driver, controlling all devices without special hardware. // Copyright 2009 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "devices.hh" #define ARCH #include "arch.hh" // GPIO pins for the devices (port.pin) // keyboard // Cols = 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14, 3.15, 3.29 // Rows = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7 // For some reason, it only works if the rows are input and the columns are output. // interrupts: yes, with all columns set to output 0, the first key press can be detected as an interrupt. // touchpad buttons // Left: 0.16 // Right: 0.13 // interrupts: yes, for any change. // Lock leds // Num lock: 2.22 // Caps lock: 0.27 // Scroll lock: 0.9 // interrupts: no, output only. // Power output (setting to 0 switches off the machine): 2.2 // interrupts: no, output only. // Power button: 3.1 (note that this is also a keyboard column.) // Battery presence and charge detection: 3.29 (note that this is also a keyboard column.) // interrupts: no; it would be possible, but setting these to input makes it impossible to detect certain key presses as interrupts. // interrupt summary // Port 0: pin 0, 1, 2, 3, 4, 5, 6, 7: keyboard; 13, 16: touchpad // Port 1: None. // Port 2: None. // Port 3: None. enum event_type: KEYBOARD_EVENT TOUCHPAD_EVENT POWERBUTTON_EVENT BATTERY_EVENT NUM_EVENTS enum cap_type: CAP_KEYBOARD = 32 CAP_TOUCHPAD CAP_POWEROFF CAP_POWERBUTTON CAP_BATTERY CAP_LOCKLEDS CAP_PWM static Capability cbs[NUM_EVENTS] static void event (event_type type, unsigned data): if !cbs[type]: return invoke_01 (cbs[type], data) static void set_cb (event_type type, Capability cb): if cbs[type]: drop (cbs[type]) cbs[type] = cb enum battery_type: BATTERY_ABSENT BATTERY_CHARGING BATTERY_CHARGED class Keyboard: enum { NUM_COLS = 17 } enum { COL_MASK = 0x2000ffff } enum { ROW_MASK = 0x000000ff } unsigned keys[NUM_COLS] bool scanning public: bool is_scanning (): return scanning Keyboard (): // Set all columns to input and disable the pull-ups. GPIO_GPDIR (3) &= ~COL_MASK GPIO_GPPUR (3) &= ~COL_MASK // Set all rows to input and enable the pull-ups. GPIO_GPDIR (0) &= ~ROW_MASK GPIO_GPPUR (0) |= ROW_MASK // Enable interrupts on falling edge. GPIO_GPIDLR (0) = (GPIO_GPIDLR (0) & 0xffff) | (GPIO_IRQ_FALLEDG * 0xaaaa) GPIO_GPIER (0) |= 0xff scanning = false for unsigned i = 0; i < NUM_COLS; ++i: keys[i] = 0xff void scan (): // Set all columns to 0 when the become output. GPIO_GPDR (3) &= ~COL_MASK bool key_pressed = false int const cols[NUM_COLS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 } for unsigned col = 0; col < NUM_COLS; ++col: GPIO_GPDIR (3) = (GPIO_GPDIR (3) & ~COL_MASK) | (1 << cols[col]) udelay (100) unsigned data = GPIO_GPDR (0) & ROW_MASK // Generate events. for unsigned row = 0; row < 8; ++row: if (data ^ keys[col]) & (1 << row): unsigned code = (row << 8) | col if data & (1 << row): code |= 0x10000 event (KEYBOARD_EVENT, code) keys[col] = data if data != ROW_MASK: key_pressed = true if key_pressed: scanning = true class Touchpad: unsigned old_state public: enum buttons: LEFT = 1 << 16 RIGHT = 1 << 13 void check_events (): unsigned state = GPIO_GPDR (0) if (state ^ old_state) & LEFT: if state & LEFT: GPIO_GPIDUR (0) = (GPIO_GPIDUR (0) & (3 << (2 * 0))) | (GPIO_IRQ_FALLEDG << (2 * 0)) event (TOUCHPAD_EVENT, 0) else: GPIO_GPIDUR (0) = (GPIO_GPIDUR (0) & (3 << (2 * 0))) | (GPIO_IRQ_RAISEDG << (2 * 0)) event (TOUCHPAD_EVENT, 0x10000) if (state ^ old_state) & RIGHT: if state & RIGHT: GPIO_GPIDLR (0) = (GPIO_GPIDLR (0) & (3 << (2 * 13))) | (GPIO_IRQ_FALLEDG << (2 * 13)) event (TOUCHPAD_EVENT, 1) else: GPIO_GPIDLR (0) = (GPIO_GPIDLR (0) & (3 << (2 * 13))) | (GPIO_IRQ_RAISEDG << (2 * 13)) event (TOUCHPAD_EVENT, 0x10001) old_state = state Touchpad (): // Set pins to input with pull-ups. GPIO_GPDIR (0) &= ~(LEFT | RIGHT) GPIO_GPPUR (0) |= LEFT | RIGHT // Enable interrupts. GPIO_GPIDUR (0) = (GPIO_GPIDUR (0) & (3 << (2 * 0))) | (GPIO_IRQ_FALLEDG << (2 * 0)) GPIO_GPIDLR (0) = (GPIO_GPIDLR (0) & (3 << (2 * 13))) | (GPIO_IRQ_FALLEDG << (2 * 13)) old_state = 0 // See if they are already pressed. If so, the interrupt detection is changed. check_events () // Now enable the interrupts. GPIO_GPIER (0) |= LEFT | RIGHT class Lockleds: // Note that num lock is in port 2. The others are in port 0. enum { NUM = 1 << 22 } enum { CAPS = 1 << 27 } enum { SCROLL = 1 << 9 } public: Lockleds (): GPIO_GPDR (2) &= ~NUM GPIO_GPDR (0) &= ~(SCROLL | CAPS) GPIO_GPDIR (2) |= NUM GPIO_GPDIR (0) |= CAPS | SCROLL void set (unsigned state): if state & 4: GPIO_GPDR (2) &= ~NUM else: GPIO_GPDR (2) |= NUM if state & 2: GPIO_GPDR (0) &= ~CAPS else: GPIO_GPDR (0) |= CAPS if state & 1: GPIO_GPDR (0) &= ~SCROLL else: GPIO_GPDR (0) |= SCROLL class Power: // Power out is in port 2, the rest in port 3. enum { PWR_OUT = 1 << 2 } enum { PWR_IN = 1 << 1 } enum { BATTERY = 1 << 29 } unsigned old_state bool was_present public: void poll (): // Switch off keyboard interrupts, because this may interfere with them. GPIO_GPIER (0) &= ~0xff GPIO_GPDIR (3) &= ~(PWR_IN | BATTERY) GPIO_GPPUR (3) &= ~(PWR_IN | BATTERY) udelay (100) unsigned state = GPIO_GPDR (3) if (state ^ old_state) & PWR_IN: event (POWERBUTTON_EVENT, state & PWR_IN ? 0 : 0x10000) if (state ^ old_state) & BATTERY: if !(state & BATTERY): GPIO_GPPUR (3) |= BATTERY udelay (100) if GPIO_GPDR (3) & BATTERY: if !was_present: event (BATTERY_EVENT, BATTERY_CHARGED) was_present = true else: if was_present: event (BATTERY_EVENT, BATTERY_ABSENT) was_present = false else: event (BATTERY_EVENT, BATTERY_CHARGING) old_state = state GPIO_GPPUR (3) &= ~BATTERY GPIO_GPDIR (3) &= ~(PWR_IN | BATTERY) udelay (100) GPIO_GPIER (3) |= 0xff Power (): GPIO_GPDR (2) |= PWR_OUT GPIO_GPDIR (2) |= PWR_OUT was_present = true old_state = BATTERY poll () void poweroff (): GPIO_GPDR (2) &= ~PWR_OUT GPIO_GPDIR (2) |= PWR_OUT while true: // Do nothing; wait until the device stops running. // Not really a gpio device, but it's so small, and uses gpio, so I include it here to avoid ipc. class Pwm: // Pin definitions, all in port 2. enum { PWM_ENABLE = 1 << 30 } public: Pwm (): GPIO_GPDIR (2) |= PWM_ENABLE PWM_PER (0) = 300 void set_backlight (bool state): if state: PWM_DUT (0) = 300 PWM_CTR (0) = 0xbf GPIO_GPDR (2) |= PWM_ENABLE else: PWM_DUT (0) = 0 PWM_CTR (0) = 0x3f GPIO_GPDR (2) &= ~PWM_ENABLE // TODO: make it really work as a pwm instead of a switch; check if pwm1 is connected to anything. int main (): for unsigned i = 0; i < 10; ++i: schedule () *(unsigned *)~3 = 0 map_gpio () map_pwm0 () Keyboard kbd Touchpad tp //Lockleds leds Power power Pwm pwm register_interrupt (IRQ_GPIO0) Capability cap_kbd = receiver_create_capability (__my_receiver, CAP_KEYBOARD) Capability cap_tp = receiver_create_capability (__my_receiver, CAP_TOUCHPAD) Capability cap_poweroff = receiver_create_capability (__my_receiver, CAP_POWEROFF) Capability cap_powerbutton = receiver_create_capability (__my_receiver, CAP_POWERBUTTON) Capability cap_battery = receiver_create_capability (__my_receiver, CAP_BATTERY) Capability cap_lockleds = receiver_create_capability (__my_receiver, CAP_LOCKLEDS) Capability cap_pwm = receiver_create_capability (__my_receiver, CAP_PWM) invoke_41 (__my_parent, cap_copy (cap_kbd), cap_copy (cap_tp), cap_copy (cap_poweroff), cap_copy (cap_powerbutton), INIT_SET_GPIO_0) invoke_31 (__my_parent, cap_copy (cap_battery), cap_copy (cap_lockleds), cap_copy (cap_pwm), INIT_SET_GPIO_1) receiver_set_alarm (__my_receiver, HZ / 5) while true: Message msg wait (&msg) switch msg.protected_data: case ~0: // Alarm. if kbd.is_scanning (): kbd.scan () power.poll () receiver_set_alarm (__my_receiver, HZ / 5) break case IRQ_GPIO0: unsigned irq = GPIO_GPFR (0) // Ack all. This works because they are all edge triggered. GPIO_GPFR (0) = irq if irq & 0xff: kbd.scan () if irq & (Touchpad::LEFT | Touchpad::RIGHT): tp.check_events () case CAP_KEYBOARD: set_cb (KEYBOARD_EVENT, msg.cap[0]) break case CAP_TOUCHPAD: set_cb (TOUCHPAD_EVENT, msg.cap[0]) break case CAP_POWEROFF: power.poweroff () break case CAP_POWERBUTTON: set_cb (POWERBUTTON_EVENT, msg.cap[0]) break case CAP_BATTERY: set_cb (BATTERY_EVENT, msg.cap[0]) break case CAP_LOCKLEDS: //leds.set (msg.data[0]) break case CAP_PWM: pwm.set_backlight (msg.data[0]) break