1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-07-05 01:22:00 +03:00
iris/boot-programs/gpio.ccp

324 lines
11 KiB
Plaintext
Raw Normal View History

2009-07-20 03:09:12 +03:00
#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 <wijnen@debian.org>
//
// 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 <http://www.gnu.org/licenses/>.
#include "devices.hh"
#define ARCH
#include "arch.hh"
// Interval between polls for keyboard events (when keys are pressed)
#define ALARM_INTERVAL (HZ / 50)
2009-07-25 01:54:12 +03:00
2009-07-20 03:09:12 +03:00
// 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.
2009-07-27 21:03:58 +03:00
// interrupts: yes, with all columns set to output 0, the first key press can be detected as an interrupt; some other events also trigger interrupts.
2009-07-20 03:09:12 +03:00
// 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.
2009-07-21 13:17:52 +03:00
// 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
NUM_EVENTS
2009-09-06 12:04:09 +03:00
static Kernel::Cap events[NUM_EVENTS]
2009-07-21 13:17:52 +03:00
static void event (event_type type, unsigned data):
2009-09-06 12:04:09 +03:00
events[type].invoke (data)
2009-07-21 13:17:52 +03:00
2009-09-06 12:04:09 +03:00
static void set_cb (event_type type):
Kernel::free_cap (events[type])
events[type] = Kernel::get_arg ()
2009-07-21 13:17:52 +03:00
class DevKeyboard:
static unsigned const encode[GPIO_KBD_NUM_COLS][GPIO_KBD_NUM_ROWS]
2009-07-25 23:00:32 +03:00
unsigned keys[GPIO_KBD_NUM_COLS]
2009-07-27 21:03:58 +03:00
bool scanning
2009-07-25 23:00:32 +03:00
void parse (unsigned col, unsigned data):
for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row:
if (data ^ keys[col]) & (1 << row):
unsigned code = encode[col][row]
2009-07-25 23:00:32 +03:00
if data & (1 << row):
code |= Keyboard::RELEASE
2009-07-25 23:00:32 +03:00
event (KEYBOARD_EVENT, code)
keys[col] = data
2009-07-27 21:03:58 +03:00
// If any keys are pressed, scanning is required.
if data != GPIO_KBD_ROW_MASK:
scanning = true
2009-07-20 03:09:12 +03:00
public:
2009-07-27 21:03:58 +03:00
bool is_scanning ():
return scanning
2009-07-25 23:00:32 +03:00
void scan ():
// Disable interrupts during scan.
GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
// All columns are input.
GPIO_GPDIR (GPIO_KBD_COL_PORT) &= ~GPIO_KBD_COL_MASK
int const cols[GPIO_KBD_NUM_COLS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 }
unsigned dir = GPIO_GPDIR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK
unsigned dat = GPIO_GPDR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK
2009-07-27 21:03:58 +03:00
// Set scanning to false before first parse.
scanning = false
// Clear all pins. This is only required once, because it's done at the end of the loop as well.
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
2009-07-25 23:00:32 +03:00
for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col:
// Set pin to output.
2009-07-25 23:00:32 +03:00
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
// Delay for stabalization.
for unsigned i = 0; i < 100; ++i:
2009-07-27 21:03:58 +03:00
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
// Read the result.
2009-07-27 21:03:58 +03:00
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK
// Generate events.
2009-07-27 21:03:58 +03:00
parse (col, data)
// Set pin to get rid of capacitance effects.
2009-07-25 23:00:32 +03:00
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col])
// Delay to make the above trick work.
for unsigned i = 0; i < 100; ++i:
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
// Pin is set to input at the start of the loop.
2009-07-25 23:00:32 +03:00
// set all to 0.
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
// set all to output.
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK
// Delay for stabalization.
for unsigned i = 0; i < 100; ++i:
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK
// Set interrupts.
2009-07-27 21:03:58 +03:00
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT)
2009-07-25 23:00:32 +03:00
for unsigned i = 0; i < 8; ++i:
2009-07-27 21:03:58 +03:00
if data & (1 << i):
gpio_irq_low (GPIO_KBD_ROW_PORT, i)
2009-07-27 21:03:58 +03:00
else:
gpio_irq_high (GPIO_KBD_ROW_PORT, i)
2009-07-25 23:00:32 +03:00
// Reenable interrupts.
GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
DevKeyboard ():
2009-07-25 23:00:32 +03:00
// Set all columns to output without pull-ups when set as input.
GPIO_GPPUR (GPIO_KBD_COL_PORT) &= ~GPIO_KBD_COL_MASK
GPIO_GPDIR (GPIO_KBD_COL_PORT) |= GPIO_KBD_COL_MASK
2009-07-20 03:09:12 +03:00
// Set all rows to input and enable the pull-ups.
2009-07-25 23:00:32 +03:00
GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
GPIO_GPDIR (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
// Initialize things in the same way as when a new callback is set up.
send_initial ()
void send_initial ():
for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col:
keys[col] = 0xff
2009-07-25 01:54:12 +03:00
scan ()
enum Keys:
N0, N1, N2, N3, N4, N5, N6, N7, N8, N9
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10
SPACE, TAB, ENTER, LBRACE, RBRACE, COMMA, PERIOD, MINUS, EQUAL, SLASH, BACKSLASH, SEMICOLON, EXTRA, BACKQUOTE, QUOTE
UP, DOWN, LEFT, RIGHT
ESC, INSERT, DELETE, BACKSPACE, PAUSE, FN, ZZZ, MENU, SYSRQ
LSHIFT, RSHIFT, CTRL, ALT
CAPS, NUM
NONE = ~Keyboard::RELEASE
unsigned const DevKeyboard::encode[GPIO_KBD_NUM_COLS][GPIO_KBD_NUM_ROWS] = {
{ PAUSE, NONE, NONE, NONE, NONE, NONE, CTRL, F5 },
{ Q, TAB, A, ESC, Z, NONE, BACKQUOTE, N1 },
{ W, CAPS, S, EXTRA, X, NONE, NONE, N2 },
{ E, F3, D, F4, C, NONE, NONE, N3 },
{ R, T, F, G, V, B, N5, N4 },
{ U, Y, J, H, M, N, N6, N7 },
{ I, RBRACE, K, F6, COMMA, NONE, EQUAL, N8 },
{ O, F7, L, NONE, PERIOD, MENU, F8, N9 },
{ NONE, NONE, NONE, SPACE, NUM, NONE, DELETE, NONE },
{ NONE, BACKSPACE, NONE, NONE, ENTER, NONE, F9, NONE },
{ NONE, NONE, NONE, ALT, NONE, NONE, NONE, SYSRQ },
{ P, LBRACE, SEMICOLON, QUOTE, BACKSLASH, SLASH, MINUS, N0 },
{ NONE, ZZZ, NONE, NONE, NONE, NONE, NONE, F10 },
{ NONE, NONE, NONE, NONE, NONE, NONE, F2, NONE },
{ NONE, NONE, NONE, NONE, NONE, NONE, INSERT, NONE },
{ NONE, NONE, UP, DOWN, LEFT, RIGHT, NONE, NONE },
{ NONE, LSHIFT, RSHIFT, NONE, NONE, NONE, F1, FN }}
2009-07-20 03:09:12 +03:00
class Touchpad:
unsigned old_state
2009-07-21 13:17:52 +03:00
public:
void check_events ():
2009-07-25 23:00:32 +03:00
unsigned state = GPIO_GPDR (GPIO_TP_LEFT_PORT)
if state & (1 << GPIO_TP_LEFT):
gpio_irq_low (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
2009-07-25 23:00:32 +03:00
if (state ^ old_state) & (1 << GPIO_TP_LEFT):
2009-07-21 13:17:52 +03:00
event (TOUCHPAD_EVENT, 0)
2009-07-25 23:00:32 +03:00
else:
gpio_irq_high (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
2009-07-25 23:00:32 +03:00
if (state ^ old_state) & (1 << GPIO_TP_LEFT):
2009-08-24 22:02:35 +03:00
event (TOUCHPAD_EVENT, 0 | Keyboard::RELEASE)
2009-07-25 23:00:32 +03:00
if state & (1 << GPIO_TP_RIGHT):
gpio_irq_low (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT)
2009-07-25 23:00:32 +03:00
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
2009-07-21 13:17:52 +03:00
event (TOUCHPAD_EVENT, 1)
2009-07-25 23:00:32 +03:00
else:
gpio_irq_high (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT)
2009-07-25 23:00:32 +03:00
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
2009-08-24 22:02:35 +03:00
event (TOUCHPAD_EVENT, 1 | Keyboard::RELEASE)
2009-07-20 03:09:12 +03:00
old_state = state
2009-07-27 21:03:58 +03:00
// Ack interrupts.
//GPIO_GPFR (GPIO_TP_LEFT_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT)
2009-07-20 03:09:12 +03:00
Touchpad ():
// Set pins to input with pull-ups.
2009-07-25 01:54:12 +03:00
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)
// See if they are already pressed. Also set up interrupts. This is done like when a new callback is registered.
send_initial ()
void send_initial ():
2009-07-20 03:09:12 +03:00
old_state = 0
2009-07-21 13:17:52 +03:00
check_events ()
2009-07-20 03:09:12 +03:00
class Lockleds:
// Note that num lock is in port 2. The others are in port 0.
public:
Lockleds ():
2009-07-25 01:54:12 +03:00
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
2009-07-21 13:17:52 +03:00
void set (unsigned state):
if state & 4:
2009-07-25 01:54:12 +03:00
GPIO_GPDR (GPIO_NUM_PORT) &= ~(1 << GPIO_NUM)
2009-07-20 03:09:12 +03:00
else:
2009-07-25 01:54:12 +03:00
GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM
2009-07-21 13:17:52 +03:00
if state & 2:
2009-07-25 01:54:12 +03:00
GPIO_GPDR (GPIO_CAPS_PORT) &= ~(1 << GPIO_CAPS)
2009-07-20 03:09:12 +03:00
else:
2009-07-25 01:54:12 +03:00
GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS
2009-07-21 13:17:52 +03:00
if state & 1:
2009-07-25 01:54:12 +03:00
GPIO_GPDR (GPIO_SCROLL_PORT) &= ~(1 << GPIO_SCROLL)
2009-07-20 03:09:12 +03:00
else:
2009-07-25 01:54:12 +03:00
GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_SCROLL
2009-07-20 03:09:12 +03:00
2009-07-21 13:17:52 +03:00
// Not really a gpio device, but it's so small, and uses gpio, so I include it here to avoid ipc.
class Pwm:
public:
Pwm ():
2009-07-25 23:00:32 +03:00
GPIO_GPDIR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_PWM_ENABLE
2009-07-21 13:17:52 +03:00
PWM_PER (0) = 300
2009-07-25 23:00:32 +03:00
void set_backlight (unsigned level):
if level > 300:
level = 300
PWM_DUT (0) = level
if level:
PWM_CTR (0) = 0x80
GPIO_GPDR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_PWM_ENABLE
2009-07-21 13:17:52 +03:00
else:
2009-07-25 23:00:32 +03:00
PWM_CTR (0) = 0x00
GPIO_GPDR (GPIO_PWM_ENABLE_PORT) &= ~(1 << GPIO_PWM_ENABLE)
2009-07-21 13:17:52 +03:00
// TODO: make it really work as a pwm instead of a switch; check if pwm1 is connected to anything.
2009-10-31 10:32:23 +02:00
enum codes:
KEYBOARD = 32
TOUCHPAD
LOCKLEDS
PWM
2009-09-06 12:04:09 +03:00
Kernel::Num start ():
2009-08-24 22:02:35 +03:00
Kernel::schedule ()
2009-07-20 03:09:12 +03:00
map_gpio ()
2009-07-21 13:17:52 +03:00
map_pwm0 ()
2009-07-20 03:09:12 +03:00
2009-09-06 12:04:09 +03:00
for unsigned i = 0; i < NUM_EVENTS; ++i:
events[i] = Kernel::alloc_cap ()
2009-08-19 02:00:05 +03:00
DevKeyboard kbd
2009-07-20 03:09:12 +03:00
Touchpad tp
2009-07-25 01:54:12 +03:00
Lockleds leds
2009-07-21 13:17:52 +03:00
Pwm pwm
2009-10-31 10:32:23 +02:00
Kernel::Cap c = Kernel::my_receiver.create_capability (KEYBOARD)
Kernel::my_parent.provide_device <Keyboard> (c.copy (), 0)
Kernel::free_cap (c)
c = Kernel::my_receiver.create_capability (TOUCHPAD)
Kernel::my_parent.provide_device <Keyboard> (c.copy (), 1)
Kernel::free_cap (c)
2009-07-20 03:09:12 +03:00
if kbd.is_scanning ():
2009-09-06 12:04:09 +03:00
Kernel::my_receiver.set_alarm (ALARM_INTERVAL)
2009-08-24 22:02:35 +03:00
// Enable interrupts. All are in port 0.
GPIO_GPIER (GPIO_KBD_ROW_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) | GPIO_KBD_ROW_MASK
Kernel::register_interrupt (IRQ_GPIO0)
2009-07-20 03:09:12 +03:00
while true:
Kernel::schedule ()
2009-09-06 12:04:09 +03:00
Kernel::wait ()
switch Kernel::recv.protected_data.l:
2009-07-21 13:17:52 +03:00
case ~0:
// Alarm.
kbd.scan ()
2009-07-27 21:03:58 +03:00
if kbd.is_scanning ():
2009-09-06 12:04:09 +03:00
Kernel::my_receiver.set_alarm (ALARM_INTERVAL)
2009-07-21 13:17:52 +03:00
break
2009-07-20 03:09:12 +03:00
case IRQ_GPIO0:
2009-07-25 23:00:32 +03:00
// Always scan keyboard and touchpad on any interrupt.
kbd.scan ()
tp.check_events ()
// Reregister the interrupt.
Kernel::register_interrupt (IRQ_GPIO0)
2009-07-25 01:54:12 +03:00
break
2009-10-31 10:32:23 +02:00
case KEYBOARD:
2009-09-06 12:04:09 +03:00
set_cb (KEYBOARD_EVENT)
Kernel::recv.reply.invoke ()
kbd.send_initial ()
2009-09-06 12:04:09 +03:00
event (KEYBOARD_EVENT, ~0)
2009-07-21 13:17:52 +03:00
break
2009-10-31 10:32:23 +02:00
case TOUCHPAD:
2009-09-06 12:04:09 +03:00
set_cb (TOUCHPAD_EVENT)
Kernel::recv.reply.invoke ()
tp.send_initial ()
2009-09-06 12:04:09 +03:00
event (TOUCHPAD_EVENT, ~0)
2009-07-21 13:17:52 +03:00
break
2009-10-31 10:32:23 +02:00
case LOCKLEDS:
2009-09-06 12:04:09 +03:00
leds.set (Kernel::recv.data[0].l)
Kernel::recv.reply.invoke ()
2009-07-21 13:17:52 +03:00
break
2009-10-31 10:32:23 +02:00
case PWM:
2009-09-06 12:04:09 +03:00
pwm.set_backlight (Kernel::recv.data[0].l)
Kernel::recv.reply.invoke ()
break
default:
kdebug ("invalid gpio operation ")
kdebug_num (Kernel::recv.protected_data.l)
kdebug ("\n")
2009-07-21 13:17:52 +03:00
break