From 283b97955dd2d72edbd67b6a71179e0a6e67c734 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Sat, 25 Jul 2009 22:00:32 +0200 Subject: [PATCH] fixes and improvements --- boot-programs/gpio.ccp | 201 +++++++++++++++++++++-------------------- boot-programs/init.ccp | 22 +++-- iris.h | 17 +++- mips/jz4730.hhp | 64 ++++++++++++- 4 files changed, 194 insertions(+), 110 deletions(-) diff --git a/boot-programs/gpio.ccp b/boot-programs/gpio.ccp index 6a8c872..e6b96c7 100644 --- a/boot-programs/gpio.ccp +++ b/boot-programs/gpio.ccp @@ -21,7 +21,7 @@ #include "arch.hh" // Interval between polls for keyboard (when keys are pressed) and battery/power (always) events -#define ALARM_INTERVAL (HZ / 10) +#define ALARM_INTERVAL (HZ / 1) // GPIO pins for the devices (port.pin) @@ -89,75 +89,89 @@ enum battery_type: BATTERY_CHARGED class Keyboard: - enum { NUM_COLS = 17 } - enum { COL_MASK = 0x2000ffff } - enum { ROW_MASK = 0x000000ff } - - unsigned keys[NUM_COLS] - bool scanning - + unsigned keys[GPIO_KBD_NUM_COLS] + void parse (unsigned col, unsigned data): + for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row: + if (data ^ keys[col]) & (1 << row): + unsigned code = (col << 3) | row + if data & (1 << row): + code |= 0x10000 + event (KEYBOARD_EVENT, code) + keys[col] = data public: - bool is_scanning (): - return scanning + void scan (): + kdebug ("keyboard scan\n") + // 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 + unsigned data + for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col: + // clear pin + GPIO_GPDR (GPIO_KBD_COL_PORT) = dat + // output + GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col]) + // Generate events of previous column. Do that now, so there is a short delay for the data to stabilize. + if col != 0: + parse (col - 1, data) + else: + // Add a short delay for stabilization. + parse (0, keys[0]) + data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK + // set pin + GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col]) + // input + GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir + parse (GPIO_KBD_NUM_COLS - 1, data) + // 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 + // clear pending interrupts. + for unsigned i = 0; i < 8; ++i: + GPIO_GPFR (GPIO_KBD_ROW_PORT) |= 1 << i + // Reenable interrupts. + GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK Keyboard (): - // Set all columns to input and disable the pull-ups. - GPIO_GPDIR (3) &= ~COL_MASK - GPIO_GPPUR (3) &= ~COL_MASK + // 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 // 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: + GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK + GPIO_GPDIR (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK + // Detect interrupts on falling edge. + for unsigned i = 0; i < GPIO_KBD_NUM_ROWS; ++i: + gpio_irq_fall (GPIO_KBD_ROW_PORT, i) + // Initialize matrix. + for unsigned i = 0; i < GPIO_KBD_NUM_COLS; ++i: keys[i] = 0xff + // Perform initial scan to get real values into matrix and set up the rest. 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) + unsigned state = GPIO_GPDR (GPIO_TP_LEFT_PORT) + if state & (1 << GPIO_TP_LEFT): + gpio_irq_fall (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) + if (state ^ old_state) & (1 << GPIO_TP_LEFT): event (TOUCHPAD_EVENT, 0) - else: - gpio_irq_rise (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) + else: + gpio_irq_rise (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) + if (state ^ old_state) & (1 << 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) + if state & (1 << GPIO_TP_RIGHT): + gpio_irq_fall (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) + if (state ^ old_state) & (1 << GPIO_TP_RIGHT): event (TOUCHPAD_EVENT, 1) - else: - gpio_irq_rise (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) + else: + gpio_irq_rise (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) + if (state ^ old_state) & (1 << GPIO_TP_RIGHT): event (TOUCHPAD_EVENT, 0x10001) old_state = state Touchpad (): @@ -165,20 +179,13 @@ class Touchpad: 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. + // See if they are already pressed. Also set up interrupts. 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) @@ -203,13 +210,11 @@ class Lockleds: 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 (): + #if 0 // Switch off keyboard interrupts, because this may interfere with them. GPIO_GPIER (0) &= ~0xff GPIO_GPDIR (3) &= ~(PWR_IN | BATTERY) @@ -237,16 +242,16 @@ class Power: GPIO_GPDIR (3) &= ~(PWR_IN | BATTERY) //udelay (100) GPIO_GPIER (3) |= 0xff + #endif Power (): - GPIO_GPDR (2) |= PWR_OUT - GPIO_GPDIR (2) |= PWR_OUT was_present = true - old_state = BATTERY + //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) + // TODO: doesn't work. + i2c_open () + i2c_write_page (I2C_DEV_MCU, I2C_MCU_SHUTDOWN, "\1", 1) + i2c_close () while true: // Do nothing; wait until the device stops running. void reboot (): @@ -257,29 +262,27 @@ class Power: // 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 + GPIO_GPDIR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_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 + 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 else: - PWM_DUT (0) = 0 - PWM_CTR (0) = 0x3f - GPIO_GPDR (2) &= ~PWM_ENABLE + PWM_CTR (0) = 0x00 + GPIO_GPDR (GPIO_PWM_ENABLE_PORT) &= ~(1 << GPIO_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 () + map_i2c () Keyboard kbd Touchpad tp @@ -287,6 +290,8 @@ int main (): Power power Pwm pwm + // 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 register_interrupt (IRQ_GPIO0) Capability cap_kbd = receiver_create_capability (__my_receiver, CAP_KEYBOARD) @@ -302,25 +307,27 @@ int main (): receiver_set_alarm (__my_receiver, ALARM_INTERVAL) while true: + schedule () Message msg wait (&msg) switch msg.protected_data: case ~0: // Alarm. - if kbd.is_scanning (): - kbd.scan () - //power.poll () + kdebug ("alarm\n") + // Periodically scan several devices. + 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 () + kdebug ("gpio interrupt\n") + //unsigned irq = GPIO_GPFR (0) + // Ack all. + GPIO_GPFR (0) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) | GPIO_KBD_ROW_MASK + // Always scan keyboard and touchpad on any interrupt. + kbd.scan () + tp.check_events () + // Reregister the interrupt. register_interrupt (IRQ_GPIO0) break case CAP_KEYBOARD: diff --git a/boot-programs/init.ccp b/boot-programs/init.ccp index a619f2b..d93127b 100644 --- a/boot-programs/init.ccp +++ b/boot-programs/init.ccp @@ -39,7 +39,7 @@ static void setup (): wait (&msg) switch msg.data[0]: case INIT_SET_GPIO_0: - kdebug ("gpio 0") + kdebug ("gpio 0\n") kbd = msg.cap[0] tp = msg.cap[1] poweroff = msg.cap[2] @@ -47,14 +47,14 @@ static void setup (): ++state break case INIT_SET_GPIO_1: - kdebug ("gpio 1") + kdebug ("gpio 1\n") battery = msg.cap[0] lockleds = msg.cap[1] pwm = msg.cap[2] ++state break case INIT_SET_LCD: - kdebug ("lcd") + kdebug ("lcd\n") lcd = msg.cap[0] ++state break @@ -67,10 +67,11 @@ static void setup (): invoke_01 (pwm, 1) int main (): + // Set up lcd first schedule () - kdebug ("start init") + kdebug ("start init\n") setup () - kdebug ("run init") + kdebug ("run init\n") while true: Message msg wait (&msg) @@ -81,9 +82,14 @@ int main (): kdebug_char ('\n') break case TP: - if msg.data[0] == 0: - // Press left button. - invoke_00 (poweroff) + unsigned leds = 0 + if msg.data[0] & 1: + leds |= 0x1 + else: + leds |= 0x4 + if !(msg.data[0] & 0x10000): + leds |= 0x2 + invoke_01 (lockleds, leds) break case POWERBUTTON: kdebug ("powerbutton event\n") diff --git a/iris.h b/iris.h index 34f54bd..37a1d9f 100644 --- a/iris.h +++ b/iris.h @@ -30,7 +30,7 @@ extern "C" { #endif // Number of clock interrupts per second. -#define HZ 10 +#define HZ 20 #define PAGE_BITS (12) #define PAGE_SIZE (1 << PAGE_BITS) @@ -441,6 +441,20 @@ static void invoke_41 (Capability t, Capability c0, Capability c1, Capability c2 invoke (t, &msg); } +static void call_00 (Capability c) +{ + Message msg; + msg.cap[0] = c; + msg.data[0] = 0; + msg.data[1] = 0; + msg.data[2] = 0; + msg.data[3] = 0; + msg.cap[1] = 0; + msg.cap[2] = 0; + msg.cap[3] = 0; + call (__my_call, &msg); +} + static Capability call_c01 (Capability c, unsigned d) { Message msg; @@ -760,7 +774,6 @@ static unsigned memory_limit (Capability memory, unsigned limit) static void drop (Capability cap) { - return; invoke_11 (__my_memory, cap, CAP_MEMORY_DROP); } diff --git a/mips/jz4730.hhp b/mips/jz4730.hhp index e8b54de..34e1e36 100644 --- a/mips/jz4730.hhp +++ b/mips/jz4730.hhp @@ -1399,8 +1399,15 @@ static void __map_io (unsigned physical, unsigned mapping): #define I2C_SR_DRF (1 << 1) #define I2C_SR_ACKF (1 << 0) +#define I2C_WRITE 0 +#define I2C_READ 1 - +/* I2C devices */ +#define I2C_DEV_MCU 0x48 +/* I2C device registers */ +#define I2C_MCU_SHUTDOWN 0xd8 +#define I2C_MCU_BAT_STATUS 0xdb +#define I2C_MCU_BAT_CHARGE 0xd9 /************************************************************************* * UDC usb device controller (unused in trendtac) @@ -2821,8 +2828,8 @@ static __inline__ unsigned msc_calc_slow_clk_divisor (bool is_sd): #define GPIO_PW_I_PORT 3 #define GPIO_PW_I 1 -#define GPIO_PW_O_PORT 2 -#define GPIO_PW_O 2 +#define GPIO_MCU_PORT 2 +#define GPIO_MCU 1 #define GPIO_LED_EN_PORT 2 #define GPIO_LED_EN 28 #define GPIO_DISP_OFF_N_PORT 2 @@ -2847,6 +2854,15 @@ static __inline__ unsigned msc_calc_slow_clk_divisor (bool is_sd): #define GPIO_TP_LEFT 16 #define GPIO_TP_RIGHT_PORT 0 #define GPIO_TP_RIGHT 13 +#define GPIO_PWM_ENABLE_PORT 2 +#define GPIO_PWM_ENABLE 30 +#define GPIO_KBD_NUM_ROWS 8 +#define GPIO_KBD_NUM_COLS 17 +#define GPIO_KBD_COL_PORT 3 +#define GPIO_KBD_COL_MASK 0x2000ffff +#define GPIO_KBD_ROW_PORT 0 +#define GPIO_KBD_ROW_MASK 0x000000ff +#define GPIO_KBD_ROW_HALF 0x00005555 #define GPIO_HALF(x) (((x) & 0xf) << 1) @@ -3012,6 +3028,48 @@ static __inline__ void gpio_as_cim (): #define i2c_read() ( I2C_DR ) #define i2c_write(val) ( I2C_DR = (val) ) +#ifndef __KERNEL +static __inline__ void i2c_open (): + i2c_set_clk (JZ_EXTAL, 10000) + i2c_enable () + +// Note that this kills messages from the queue. +static __inline__ void i2c_close (): + Message msg + receiver_set_alarm (__my_receiver, 3 * HZ / 10) + call_00 (0) + i2c_disable () + +static __inline__ bool i2c_send (unsigned data): + unsigned timeout = 10000 + i2c_write (data) + i2c_set_drf () + while i2c_check_drf () != 0: + while !i2c_transmit_ended (): + while !i2c_received_ack (): + if !--timeout: + return false + return true + +static __inline__ unsigned i2c_write_page (unsigned dev, unsigned addr, char const *data, unsigned count): + unsigned timeout = 5 + i2c_send_start () + if !i2c_send ((dev << 1) | I2C_WRITE): + i2c_send_stop () + return 0 + if !i2c_send (addr): + i2c_send_stop () + return 0 + if count > 8: + count = 8 + unsigned i + for i = 0; i < count; ++i: + if !i2c_send (*data++): + break + i2c_send_stop () + return i +#endif + /*************************************************************************** * UDC ***************************************************************************/