#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" // Interval between polls for keyboard (when keys are pressed) and battery/power (always) events #define ALARM_INTERVAL (HZ / 10) // 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 scan () void scan (): // Set all columns to 0 when they become output. GPIO_GPDR (3) &= ~COL_MASK scanning = 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: scanning = true scanning = true if scanning: GPIO_GPDIR (3) &= ~COL_MASK else: GPIO_GPDIR (3) |= COL_MASK class Touchpad: unsigned old_state public: void check_events (): unsigned state = GPIO_GPDR (0) if (state ^ old_state) & (1 << GPIO_TP_LEFT): if state & (1 << GPIO_TP_LEFT): gpio_irq_fall (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) event (TOUCHPAD_EVENT, 0) else: gpio_irq_rise (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) event (TOUCHPAD_EVENT, 0x10000) if (state ^ old_state) & (1 << GPIO_TP_RIGHT): if state & (1 << GPIO_TP_RIGHT): gpio_irq_fall (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) event (TOUCHPAD_EVENT, 1) else: gpio_irq_rise (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) event (TOUCHPAD_EVENT, 0x10001) old_state = state Touchpad (): // Set pins to input with pull-ups. gpio_as_input (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) gpio_as_input (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) GPIO_GPPUR (0) |= (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) // Set up interrupts. gpio_irq_rise (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) gpio_irq_rise (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) 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) |= (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_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_as_output (GPIO_NUM_PORT, GPIO_NUM) gpio_as_output (GPIO_CAPS_PORT, GPIO_CAPS) gpio_as_output (GPIO_SCROLL_PORT, GPIO_SCROLL) GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_SCROLL void set (unsigned state): if state & 4: GPIO_GPDR (GPIO_NUM_PORT) &= ~(1 << GPIO_NUM) else: GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM if state & 2: GPIO_GPDR (GPIO_CAPS_PORT) &= ~(1 << GPIO_CAPS) else: GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS if state & 1: GPIO_GPDR (GPIO_SCROLL_PORT) &= ~(1 << GPIO_SCROLL) else: GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_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_as_gpio (GPIO_PW_O_PORT, GPIO_PW_O) gpio_as_output (GPIO_PW_O_PORT, GPIO_PW_O) GPIO_GPDR (GPIO_PW_O) &= ~(1 << GPIO_PW_O) while true: // Do nothing; wait until the device stops running. void reboot (): wdt_set_count (0xffffffff - 32) wdt_start () while true: // Do nothing; wait until the device reboots. // 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 (): schedule () map_gpio () map_pwm0 () map_wdt () 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, ALARM_INTERVAL) 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, ALARM_INTERVAL) break case IRQ_GPIO0: kdebug ("gpio interrupt") 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 & ((1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT)): tp.check_events () register_interrupt (IRQ_GPIO0) break 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: if msg.data[0]: power.poweroff () else: power.reboot () 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