1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2025-04-21 12:27:27 +03:00

new directory organization

This commit is contained in:
Bas Wijnen
2013-05-14 18:30:50 -04:00
parent b06e011a07
commit fa021a80f0
62 changed files with 133 additions and 2572 deletions
+136
View File
@@ -0,0 +1,136 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// source/boot.ccp: Boot into another kernel.
// Copyright 2010 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 <iris.hh>
#include <devices.hh>
extern "C":
extern unsigned pre_code, pre_end, post_code, post_end
struct args_t:
unsigned load
unsigned size
unsigned entry
unsigned source
Iris::Num start ():
unsigned *loader_addr = (unsigned *)0x15000
unsigned *page_addr = (unsigned *)0x16000
unsigned *tmp_addr = (unsigned *)0x17000
Iris::Boot cap = Iris::my_receiver.create_capability (0)
Iris::my_parent.provide_capability <Iris::Boot> (cap.copy ())
Iris::free_cap (cap)
Iris::my_parent.init_done ()
while true:
Iris::wait ()
switch Iris::recv.data[0].l:
case Iris::Boot::BOOT:
Iris::Block code = Iris::get_arg ()
unsigned load = Iris::recv.data[1].l
unsigned entry = Iris::recv.data[1].h
Iris::Num lsize = code.get_size ()
if lsize.h != 0:
Iris::panic (lsize.h, "string to boot is too large to be loaded")
unsigned size = lsize.l
unsigned pages = ((size + ~PAGE_MASK) >> PAGE_BITS) + 1
unsigned phys = Iris::my_memory.alloc_range (pages)
if phys & ~PAGE_MASK:
Iris::panic (size, "unable to allocate space for string to load")
unsigned target, offset
if phys < (load & ~0xa0000000):
Iris::debug ("pre-loading\n")
Iris::Page pre = Iris::my_memory.create_page ()
pre.alloc_physical (phys, true, true)
Iris::my_memory.map (pre, (unsigned)loader_addr)
for unsigned i = 0; i < &pre_end - &pre_code; ++i:
loader_addr[i] = (&pre_code)[i]
target = phys
offset = 1
else:
Iris::debug ("post-loading\n")
Iris::Page post = Iris::my_memory.create_page ()
post.alloc_physical (phys + ((pages - 1) << PAGE_BITS), true, true)
Iris::my_memory.map (post, (unsigned)loader_addr)
for unsigned i = 0; i < &post_end - &post_code; ++i:
loader_addr[i] = (&post_code)[i]
target = phys + ((pages - 1) << PAGE_BITS)
offset = 0
Iris::Page tmp = Iris::my_memory.create_page ()
tmp.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
Iris::my_memory.map (tmp, (unsigned)tmp_addr)
for unsigned i = 0; i < pages - 1; ++i:
Iris::Page page = Iris::my_memory.create_page ()
page.alloc_physical (phys + ((i + offset) << PAGE_BITS), true, true)
Iris::my_memory.map (page, (unsigned)page_addr)
code.get_block (i << PAGE_BITS, PAGE_SIZE, 0, tmp)
for unsigned t = 0; t < PAGE_SIZE / 4; ++t:
page_addr[t] = tmp_addr[t]
args_t *args = (args_t *)((unsigned)loader_addr + PAGE_SIZE - sizeof (args_t))
unsigned phys_args = target + PAGE_SIZE - sizeof (args_t)
args->load = load
args->entry = entry
args->size = size
args->source = phys + (offset << PAGE_BITS) | 0x80000000
Iris::debug ("booting into: %x+%x->%x@%x (%x, %x)\n", args->source, args->size, args->load, args->entry, args, phys_args)
// Everything is set up; jump to the loader.
Iris::boot (target | 0x80000000, phys_args | 0x80000000)
Iris::panic (0, "Iris::boot should not return, but it does")
default:
Iris::panic (Iris::recv.data[0].l, "invalid commend received on boot capability")
asm volatile ("\t.set noreorder\n"
"\t.globl pre_code, pre_end, post_code, post_end\n"
"\t.text\n"
"pre_code:\n"
"\tlw $t0, 0($a0)\n" // t0 is the load address
"\tlw $t1, 4($a0)\n" // t1 is the size
"\tlw $t9, 8($a0)\n" // t9 is the entry point
"\tlw $t2, 12($a0)\n" // t2 is the source of the loaded image
"\tadd $t0, $t0, $t1\n" // t0 is the end of the load region
"\tadd $t2, $t2, $t1\n" // t2 is the end of the source
"1:\n"
"\tlw $t3, -4($t2)\n"
"\tsw $t3, -4($t0)\n"
"\taddiu $t2, $t2, -4\n"
"\taddiu $t1, $t1, -4\n"
"\tbnez $t1, 1b\n"
"\taddiu $t0, $t0, -4\n"
// Done copying
"\tjr $t9\n"
"\tnop\n"
"pre_end:\n"
"\n"
"post_code:\n"
"\tlw $t0, 0($a0)\n" // t0 is the load address
"\tlw $t1, 4($a0)\n" // t1 is the size
"\tlw $t9, 8($a0)\n" // t9 is the entry point
"\tlw $t2, 12($a0)\n" // t2 is the source of the loaded image
"1:\n"
"\tlw $t3, 0($t2)\n"
"\tsw $t3, 0($t0)\n"
"\taddiu $t2, $t2, 4\n"
"\taddiu $t1, $t1, -4\n"
"\tbnez $t1, 1b\n"
"\taddiu $t0, $t0, 4\n"
// Done copying
"\tjr $t9\n"
"\tnop\n"
"post_end:\n"
"\n"
"\t.set reorder\n")
+97
View File
@@ -0,0 +1,97 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// boot-programs/buzzer.ccp: Piezo buzzer driver.
// 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"
class DevBuzzer:
static unsigned const pwm = 4
Iris::Cap event
bool is_beeping
public:
DevBuzzer ():
is_beeping = false
tcu_stop_counter (pwm)
tcu_select_extalclk (pwm)
tcu_select_clk_div64 (pwm)
tcu_enable_pwm_output (pwm)
void stop ():
if !is_beeping:
return
tcu_stop_counter (pwm)
event.invoke ()
Iris::free_cap (event)
is_beeping = false
void beep (unsigned freq, unsigned ms, Iris::Cap cb):
stop ()
event = cb
unsigned full = JZ_EXTAL / 64 / freq
tcu_set_full_data (pwm, full)
tcu_set_half_data (pwm, full / 2)
tcu_set_count (pwm, 0)
tcu_start_counter (pwm)
Iris::my_receiver.set_alarm (ms * HZ / 1000)
is_beeping = true
enum codes:
BUZZER = 32
Iris::Num start ():
map_tcu ()
DevBuzzer buzzer
Iris::Buzzer dev = Iris::my_receiver.create_capability (BUZZER)
Iris::my_parent.provide_capability <Iris::Buzzer> (dev.copy ())
Iris::free_cap (dev)
Iris::my_parent.init_done ()
while true:
Iris::wait ()
if Iris::recv.protected_data.h == ~0:
// Alarm.
buzzer.stop ()
continue
switch Iris::recv.protected_data.l:
case BUZZER:
// Buzzer device user request.
switch Iris::recv.data[0].l:
case Iris::Device::RESET:
buzzer.stop ()
Iris::recv.reply.invoke ()
break
case Iris::Buzzer::BEEP:
// Volume is not used by this buzzer.
Iris::Cap arg = Iris::get_arg ()
Iris::Cap reply = Iris::get_reply ()
buzzer.beep (Iris::recv.data[1].l, Iris::recv.data[1].h, arg)
reply.invoke ()
Iris::free_cap (reply)
break
case Iris::Buzzer::STOP:
buzzer.stop ()
Iris::recv.reply.invoke ()
break
default:
kdebug ("Buzzer: other\n")
break
break
default:
kdebug ("Buzzer: unknown num: ")
kdebug_num (Iris::recv.protected_data.l)
kdebug ("\n")
+299
View File
@@ -0,0 +1,299 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// boot-programs/nanonote-gpio.ccp: gpio devices on the nanonote.
// 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"
#include "keys.hh"
#define QI
#define SCAN_INTERVAL HZ / 50
class DevKbd:
static unsigned const NUM_COLS = 8
static unsigned const NUM_ROWS = 8
static unsigned const COLS_PORT = 2
static unsigned const ROWS_PORT = 3
static unsigned const ALL_COLS = 0x0003fc00
static unsigned const ALL_ROWS = 0x05fc0000
static unsigned const COLS[NUM_COLS]
static unsigned const ROWS[NUM_ROWS]
static unsigned const encode[NUM_ROWS][NUM_COLS]
static unsigned const NUM_KEYS = 58
static unsigned const keys[NUM_KEYS]
unsigned state[NUM_COLS]
Iris::Cap event
bool is_active
bool is_scanning
public:
unsigned size ():
return NUM_KEYS
void send_keys (unsigned first, Iris::Cap target):
unsigned d[4]
unsigned i
for i = 0; first + i < NUM_KEYS && i < 4; ++i:
d[i] = keys[first + i]
for ; i < 4; ++i:
d[i] = ~0
target.invoke (Iris::Num (d[0], d[1]), Iris::Num (d[2], d[3]))
bool scanning ():
return is_scanning
void inactive ():
if is_active:
Iris::free_cap (event)
is_active = false
void check (unsigned col, unsigned rowdata):
for unsigned r = 0; r < NUM_ROWS; ++r:
if !((rowdata ^ state[col]) & (1 << ROWS[r])):
continue
unsigned code = encode[r][col]
if rowdata & (1 << ROWS[r]):
code |= Iris::Keyboard::RELEASE
if is_active:
event.invoke (code)
state[col] = rowdata
void delay ():
for unsigned i = 0; i < 10000; ++i:
gpio_set (0, 0)
void scan ():
unsigned r
gpio_mask_irq (ROWS_PORT, ALL_ROWS)
is_scanning = false
if !is_active:
return
for unsigned c = 0; c < NUM_COLS; ++c:
gpio_as_input (COLS_PORT, ALL_COLS & ~(1 << COLS[c]))
gpio_as_output (COLS_PORT, 1 << COLS[c])
if c > 0:
check (c - 1, r)
delay ()
else:
check (0, state[0])
delay ()
r = gpio_get_port (ROWS_PORT) & ALL_ROWS
if r != ALL_ROWS:
is_scanning = true
gpio_as_output (COLS_PORT, ALL_COLS)
check (NUM_COLS - 1, r)
delay ()
r = gpio_get_port (ROWS_PORT) & ALL_ROWS
unsigned high = 0, low = 0
for unsigned i = 0; i < NUM_ROWS; ++i:
if r & (1 << ROWS[i]):
low |= 1 << ROWS[i]
else:
high |= 1 << ROWS[i]
gpio_as_interrupt (ROWS_PORT, high, true, true)
gpio_as_interrupt (ROWS_PORT, low, false, true)
gpio_unmask_irq (ROWS_PORT, ALL_ROWS)
void active (Iris::Cap cb):
inactive ()
event = cb
is_active = true
for unsigned c = 0; c < NUM_COLS; ++c:
state[c] = ALL_ROWS
scan ()
DevKbd ():
is_active = false
gpio_as_gpio (COLS_PORT, ALL_COLS)
gpio_as_gpio (ROWS_PORT, ALL_ROWS)
gpio_clear (COLS_PORT, ALL_COLS)
gpio_as_output (COLS_PORT, ALL_COLS)
gpio_as_input (ROWS_PORT, ALL_ROWS)
gpio_enable_pull (ROWS_PORT, ALL_ROWS)
for unsigned i = 0; i < NUM_COLS; ++i:
state[i] = ALL_ROWS
scan ()
unsigned const DevKbd::COLS[NUM_COLS] = { 10, 11, 12, 13, 14, 15, 16, 17 }
unsigned const DevKbd::ROWS[NUM_ROWS] = { 18, 19, 20, 21, 22, 23, 24, 26 }
unsigned const DevKbd::encode[NUM_ROWS][NUM_COLS] = {
#ifdef QI
{ Key::F1, Key::F2, Key::F3, Key::F4, Key::F5, Key::F6, Key::F7, ~0 },
{ Key::Q, Key::W, Key::E, Key::R, Key::T, Key::Y, Key::U, Key::I },
{ Key::A, Key::S, Key::D, Key::F, Key::G, Key::H, Key::J, Key::K },
{ Key::ESCAPE, Key::Z, Key::X, Key::C, Key::V, Key::B, Key::N, Key::M },
{ Key::TAB, Key::CAPS_LOCK, Key::BACKSLASH, Key::QUOTE, Key::COMMA, Key::PERIOD, Key::SLASH, Key::UP },
{ Key::O, Key::L, Key::EQUALS, Key::SPECIAL + 0, Key::SPACE, Key::RIGHT_LOGO, Key::RIGHT_CONTROL, Key::LEFT },
{ Key::F8, Key::P, Key::BACKSPACE, Key::ENTER, Key::VOLUME_UP, Key::VOLUME_DOWN, Key::DOWN, Key::RIGHT },
{ Key::LEFT_SHIFT, Key::LEFT_ALT, Key::FN, ~0, ~0, ~0, ~0, ~0 }
#else
{ Key::ESCAPE, Key::TAB, Key::F1, Key::F2, Key::F3, Key::F4, Key::SPECIAL + 0, ~0 },
{ Key::N1, Key::N2, Key::N3, Key::N4, Key::N5, Key::N6, Key::N7, Key::N8 },
{ Key::Q, Key::W, Key::E, Key::R, Key::T, Key::Y, Key::U, Key::I },
{ Key::A, Key::S, Key::D, Key::F, Key::G, Key::H, Key::J, Key::K },
{ Key::Z, Key::X, Key::C, Key::V, Key::B, Key::N, Key::M, Key::UP },
{ Key::N9, Key::O, Key::L, Key::LEFT_ALT, Key::CAPS_LOCK, Key::SPACE, Key::EQUALS, Key::LEFT },
{ Key::BACKSPACE, Key::N0, Key::P, Key::ENTER, Key::VOLUME_UP, Key::VOLUME_DOWN, Key::DOWN, Key::RIGHT },
{ Key::FN, Key::LEFT_SHIFT, Key::LEFT_CONTROL, ~0, ~0, ~0, ~0, ~0 }
#endif
}
unsigned const DevKbd::keys[NUM_KEYS] = {
#ifdef QI
Key::F1, Key::F2, Key::F3, Key::F4, Key::F5, Key::F6, Key::F7,
Key::Q, Key::W, Key::E, Key::R, Key::T, Key::Y, Key::U, Key::I,
Key::A, Key::S, Key::D, Key::F, Key::G, Key::H, Key::J, Key::K,
Key::ESCAPE, Key::Z, Key::X, Key::C, Key::V, Key::B, Key::N, Key::M,
Key::TAB, Key::CAPS_LOCK, Key::BACKSLASH, Key::QUOTE, Key::COMMA, Key::PERIOD, Key::SLASH, Key::UP,
Key::O, Key::L, Key::EQUALS, Key::SPECIAL + 0, Key::SPACE, Key::RIGHT_LOGO, Key::RIGHT_CONTROL, Key::LEFT,
Key::F8, Key::P, Key::BACKSPACE, Key::ENTER, Key::VOLUME_UP, Key::VOLUME_DOWN, Key::DOWN, Key::RIGHT,
Key::LEFT_SHIFT, Key::LEFT_ALT, Key::FN
#else
Key::ESCAPE, Key::TAB, Key::F1, Key::F2, Key::F3, Key::F4, Key::SPECIAL + 0,
Key::N1, Key::N2, Key::N3, Key::N4, Key::N5, Key::N6, Key::N7, Key::N8,
Key::Q, Key::W, Key::E, Key::R, Key::T, Key::Y, Key::U, Key::I,
Key::A, Key::S, Key::D, Key::F, Key::G, Key::H, Key::J, Key::K,
Key::Z, Key::X, Key::C, Key::V, Key::B, Key::N, Key::M, Key::UP,
Key::N9, Key::O, Key::L, Key::LEFT_ALT, Key::CAPS_LOCK, Key::SPACE, Key::EQUALS, Key::LEFT,
Key::BACKSPACE, Key::N0, Key::P, Key::ENTER, Key::VOLUME_UP, Key::VOLUME_DOWN, Key::DOWN, Key::RIGHT,
Key::FN, Key::LEFT_SHIFT, Key::LEFT_CONTROL
#endif
}
class Event:
bool state, started
unsigned pin
Iris::Cap cb
public:
void scan ():
if !started:
return
gpio_mask_irq (3, 1 << pin)
bool s = gpio_get_port (3) & (1 << pin)
if s != state:
state = s
cb.invoke (state ? Iris::Keyboard::RELEASE : 0)
gpio_as_interrupt (3, 1 << pin, !state, true)
gpio_unmask_irq (3, 1 << pin)
Event (unsigned p):
pin = p
gpio_as_gpio (3, pin)
state = true
started = false
void set_cb (Iris::Cap c):
if started:
Iris::free_cap (cb)
else:
started = true
cb = c
state = true
scan ()
enum codes:
KBD_DEV = 1
PWR
SDMMC
Iris::Num start ():
map_gpio ()
DevKbd kbd
Event pwr (29)
Event sdmmc (0)
Iris::Keyboard dev = Iris::my_receiver.create_capability (KBD_DEV)
Iris::Keyboard pw = Iris::my_receiver.create_capability (PWR)
Iris::Event sm = Iris::my_receiver.create_capability (SDMMC)
Iris::my_parent.provide_capability <Iris::Keyboard> (dev.copy (), 0)
Iris::my_parent.provide_capability <Iris::Keyboard> (pw.copy (), 1)
Iris::my_parent.provide_capability <Iris::Event> (sm.copy ())
Iris::free_cap (dev)
Iris::free_cap (pw)
Iris::free_cap (sm)
Iris::my_parent.init_done ()
if kbd.scanning ():
Iris::my_receiver.set_alarm (SCAN_INTERVAL)
Iris::register_interrupt (IRQ_GPIO3)
while true:
Iris::wait ()
if Iris::recv.protected_data.h == ~0:
// Alarm.
kbd.scan ()
if kbd.scanning ():
Iris::my_receiver.set_alarm (SCAN_INTERVAL)
continue
switch Iris::recv.protected_data.l:
case 0:
// Interrupt.
pwr.scan ()
kbd.scan ()
sdmmc.scan ()
if kbd.scanning ():
Iris::my_receiver.set_alarm (SCAN_INTERVAL)
Iris::register_interrupt (IRQ_GPIO3)
break
case KBD_DEV:
// Keyboard device user request.
switch Iris::recv.data[0].l:
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Event::SET_CB:
Iris::Cap reply = Iris::get_reply ()
kbd.active (Iris::get_arg ())
reply.invoke ()
Iris::free_cap (reply)
break
case Iris::Keyboard::GET_NUM_KEYS:
Iris::recv.reply.invoke (kbd.size ())
break
case Iris::Keyboard::GET_KEYS:
kbd.send_keys (Iris::recv.data[0].l, Iris::recv.reply)
break
default:
kdebug ("keyboard other\n")
break
break
case PWR:
switch Iris::recv.data[0].l:
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Event::SET_CB:
Iris::Cap reply = Iris::get_reply ()
pwr.set_cb (Iris::get_arg ())
reply.invoke ()
Iris::free_cap (reply)
break
default:
kdebug ("Power button invalid request\n")
kdebug_num (Iris::recv.data[0].l)
kdebug ("\n")
break
break
case SDMMC:
switch Iris::recv.data[0].l:
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Event::SET_CB:
Iris::Cap reply = Iris::get_reply ()
sdmmc.set_cb (Iris::get_arg ())
reply.invoke ()
Iris::free_cap (reply)
break
default:
kdebug ("SdMmc gpio invalid request\n")
kdebug_num (Iris::recv.data[0].l)
kdebug ("\n")
break
break
default:
kdebug ("keyboard unknown num: ")
kdebug_num (Iris::recv.protected_data.h)
kdebug ("\n")
+470
View File
@@ -0,0 +1,470 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// boot-programs/lcd.ccp: Display driver.
// 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"
__asm__ volatile (".section .rodata\n.globl charset\ncharset:\n.incbin \"source/data/charset.data\"\n.section .text")
extern unsigned char const charset[127-32][8][6]
#define assert(x) do { if (!(x)) { kdebug ("assertion failed " #x); while (true) {} } } while (0)
#if defined (TRENDTAC)
static unsigned h = 800, v = 480, fps = 60, Bpp = 2
#define LOG_X_BASE 1
#define LOG_Y_BASE 1
#elif defined (NANONOTE)
static unsigned h = 320, v = 240, fps = 60, Bpp = 4
#define LOG_X_BASE 0
#define LOG_Y_BASE 0
#else
#error unknown board
#endif
#define frame_size (v * h * Bpp)
static unsigned physical_descriptor
/**/struct Descriptor {
unsigned next;
unsigned frame;
unsigned id;
unsigned cmd;
} __attribute__ ((packed))
#if defined (NANONOTE)
#define SP_PORT 2
#define SPEN 21
#define SPDA 22
#define SPCK 23
static void udelay (unsigned num):
for unsigned i = 0; i < num * (JZ_EXTAL / 1000000); ++i:
// set 0 does nothing, but costs at least one clock pulse.
gpio_set (SP_PORT, 0)
// Register names. The registers are not named in the datasheet, and in some cases the name only describes a part of the bits in it.
// The registers with bit 6 set have bit 7 set instead, because of the transfer method.
enum spi_reg:
AC = 0x00
DC = 0x01
BRIGHTNESS = 0x03
FORMAT = 0x04
BACKLIGHT1 = 0x05
VBLANK = 0x06
HBLANK = 0x07
BACKLIGHT2 = 0x08
SYNC = 0x0b
POLARITY = 0x0c
CONTRAST_RGB = 0x0d
SUB_CONTRAST_R = 0x0e
SUB_BRIGHTNESS_R= 0x0f
SUB_CONTRAST_B = 0x10
SUB_BRIGHTNESS_B= 0x11
TRIM = 0x12
COLOR = 0x13
GAMMA = 0x16
GAMMA1 = 0x17
GAMMA2 = 0x18
GAMMA3 = 0x19
GAMMA4 = 0x1a
INVERSION = 0xa5
HLEVEL = 0xa6
LLEVEL = 0xa7
FB = 0xb1
static void write_reg (unsigned reg, unsigned val):
unsigned value = (reg << 8) | (val & 0xff)
gpio_clear (SP_PORT, 1 << SPEN)
udelay (1)
for unsigned i = 0; i < 16; ++i, value <<= 1:
gpio_clear (SP_PORT, 1 << SPCK)
if value & 0x8000:
gpio_set (SP_PORT, 1 << SPDA)
else:
gpio_clear (SP_PORT, 1 << SPDA)
udelay (4)
gpio_set (SP_PORT, 1 << SPCK)
udelay (4)
gpio_set (SP_PORT, 1 << SPEN)
udelay(4)
#endif
static void reset ():
#if defined (TRENDTAC)
// Note that the sync pulse is part of the pre-display region.
// Vertical timings.
unsigned vsync = 20, vpre = 20, vpost = 0
// Horizontal timings.
unsigned hsync = 80, hpre = 80, hpost = 0
// One clock pulse per pixel.
unsigned cpp = 1
// Bits per pixel.
unsigned bpp = LCD_CTRL_BPP_16
// Configuration.
#define MODE_TFT_GEN 0
#define VSYNC_N (1 << 8)
unsigned cfg = MODE_TFT_GEN | VSYNC_N
#elif defined (NANONOTE)
// Note that the sync pulse is part of the pre-display region.
// Horizontal timings.
unsigned hsync = 1, hpre = 70, hpost = 273
// Vertical timings.
unsigned vsync = 1, vpre = 21, vpost = 2
// 3 clocks per pixel.
unsigned cpp = 3
// Bits per pixel.
unsigned bpp = LCD_CTRL_BPP_18_24
// Configuration.
unsigned cfg = LCD_CFG_MODE_SERIAL_TFT | LCD_CFG_HSP | LCD_CFG_VSP | LCD_CFG_PCP
// Set up SPI pins.
gpio_as_output (SP_PORT, (1 << SPEN) | (1 << SPCK) | (1 << SPDA))
gpio_set (SP_PORT, (1 << SPEN) | (1 << SPCK))
#else
#error unknown board
#endif
// Note that the sync pulse is part of the pre-display region.
unsigned vpe = vsync, vds = vpre, vde = vds + v, vt = vde + vpost
unsigned hpe = hsync, hds = hpre, hde = hds + h, ht = hde + hpost
cpm_stop_lcd ()
unsigned pixclock = fps * ht * vt
#if defined (TRENDTAC)
unsigned pllout = cpm_get_pllout ()
CPM_CFCR2 = pllout / pixclock - 1
unsigned val = pllout / (pixclock * 4) - 1
assert (pllout / (val + 1) <= 150000000)
assert (val <= 0xf)
cpm_set_lcdclk_div (val)
CPM_CFCR |= CPM_CFCR_UPE
#elif defined (NANONOTE)
unsigned val = cpm_get_pllout2 () / pixclock - 1
//assert (val < 0x400)
//cpm_set_pixdiv (val)
//cpm_set_pixdiv (12)
val = cpm_get_pllout2 () / (pixclock * 3) - 1
assert (val < 0x20)
//cpm_set_ldiv (val)
// Update dividers.
//CPM_CPCCR |= CPM_CPCCR_CE
#else
#error "Unknown board"
#endif
cpm_start_lcd ()
#ifdef NANONOTE
// Reset the controller.
write_reg (BACKLIGHT1, 0x1e)
// Enable display.
write_reg (BACKLIGHT1, 0x5e)
// Set data to rgbrgbrgb input, with a delta color filter.
write_reg (COLOR, 0x01)
#endif
LCD_CTRL = (bpp << LCD_CTRL_BPP_BIT) | LCD_CTRL_BST_16 | LCD_CTRL_EOFM
LCD_CFG = cfg
LCD_HSYNC = hpe
LCD_VSYNC = vpe
LCD_VAT = (ht << 16) | vt
LCD_DAH = (hds << 16) | hde
LCD_DAV = (vds << 16) | vde
LCD_DA0 = physical_descriptor
LCD_STATE = 0
LCD_CTRL = (bpp << LCD_CTRL_BPP_BIT) | LCD_CTRL_BST_16 | LCD_CTRL_EOFM | LCD_CTRL_ENA
static void putchar (unsigned x, unsigned y, unsigned ch):
if ch < 32 || ch > 126:
ch = 127
ch -= 32
for unsigned k = 0; k < 6; ++k:
for unsigned r = 0; r < 8; ++r:
LCD_FRAMEBUFFER_BASE[(y * 8 + r) * h + x * 6 + k] = charset[ch][r][k] * 0x00010101
static unsigned log_x = LOG_X_BASE, log_y = LOG_Y_BASE
static void inc_logx ():
if ++log_x >= h / 6:
log_x = LOG_X_BASE
if ++log_y >= v / 8:
log_y = LOG_Y_BASE
static void log_char (unsigned ch):
switch ch:
case '\n':
while log_x < h / 6:
putchar (log_x++, log_y, ' ')
inc_logx ()
break
default:
putchar (log_x, log_y, ch)
inc_logx ()
static void log_str (char const *str):
while *str:
log_char (*str++)
static void log_num (Iris::Num n):
char const *encode = "0123456789abcdef"
log_char ('[')
for unsigned i = 0; i < 8; ++i:
log_char (encode[(n.h >> (4 * (7 - i))) & 0xf])
log_char (' ')
for unsigned i = 0; i < 8; ++i:
log_char (encode[(n.l >> (4 * (7 - i))) & 0xf])
log_char (']')
static void log_msg ():
log_str ("prot:")
log_num (Iris::recv.protected_data)
log_str ("data:")
for unsigned i = 0; i < 2; ++i:
log_num (Iris::recv.data[i])
log_char ('\n')
enum captype:
LOG = 1
BACKLIGHT
LCD
static unsigned spot (unsigned x, unsigned y, unsigned cx, unsigned cy):
unsigned dx2 = (x - cx) * (x - cx)
unsigned dy2 = (y - cy) * (y - cy)
unsigned d2 = dx2 + dy2
unsigned l = 120
if d2 >= l * l:
return 0
return ((l * l - d2 - 1) << 8) / (l * l)
static unsigned pages
static Descriptor descriptor __attribute__ ((aligned (16)))
static bool is_on
static unsigned create (Iris::Memory mem):
unsigned physical = mem.alloc_range (pages)
unsigned address = 0x15000
if physical & ~PAGE_MASK:
Iris::panic (0, "can't allocate framebuffer")
assert (physical & PAGE_MASK && ~physical)
for unsigned i = 0; i < pages; ++i:
Iris::Page p = mem.create_page ()
p.alloc_physical (physical + (i << PAGE_BITS), false, true)
if address != ~0:
mem.map (p, address + (i << PAGE_BITS))
Iris::free_cap (p)
return physical
static void destroy (unsigned physical, Iris::Memory mem):
unsigned address = 0x15000
if physical == ~0:
Iris::panic (0, "unable to destroy framebuffer with wrong cap0")
if descriptor.frame == physical && is_on:
lcd_clr_ena ()
#ifdef NANONOTE
write_reg (BACKLIGHT1, 0x5e)
#endif
if address != ~0:
for unsigned i = 0; i < pages; ++i:
Iris::Page p = mem.mapping ((void *)(address + (i << PAGE_BITS)))
mem.destroy (p)
Iris::free_cap (p)
static void use (unsigned physical):
if physical == ~0:
Iris::panic (0, "unable to use framebuffer with wrong cap0")
bool was_unused = descriptor.frame == 0
descriptor.frame = physical
unsigned dptr = (unsigned)&descriptor
__asm__ volatile ("lw $a0, %0\ncache 0x15, 0($a0)" :: "m"(dptr) : "memory", "a0")
if was_unused && is_on:
lcd_set_ena ()
#ifdef NANONOTE:
write_reg (BACKLIGHT1, 0x5f)
#endif
static void unuse (unsigned physical):
if physical == ~0:
Iris::panic (0, "unable to unuse framebuffer with wrong cap0")
if descriptor.frame == physical:
lcd_clr_ena ()
#ifdef NANONOTE
write_reg (BACKLIGHT1, 0x5e)
#endif
descriptor.frame = 0
Iris::Num start ():
Iris::schedule ()
map_lcd ()
map_cpm ()
#ifdef NANONOTE
map_gpio ()
#endif
pages = (frame_size + ~PAGE_MASK) >> PAGE_BITS
unsigned physical = 0
Iris::Page p = Iris::my_memory.mapping (&descriptor)
unsigned paddr = p.physical_address ()
physical_descriptor = paddr + ((unsigned)&descriptor & ~PAGE_MASK)
Iris::free_cap (p)
descriptor.next = physical_descriptor
descriptor.frame = physical
descriptor.id = 0xdeadbeef
descriptor.cmd = LCD_CMD_EOFINT | ((frame_size / 4) << LCD_CMD_LEN_BIT)
unsigned dptr = (unsigned)&descriptor
__asm__ volatile ("lw $a0, %0\ncache 0x15, 0($a0)" :: "m"(dptr) : "memory", "a0")
reset ()
#if defined (TRENDTAC)
Iris::Cap logcap = Iris::my_receiver.create_capability (LOG)
__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory")
#endif
// Register the backlight device.
Iris::Setting backlight = Iris::my_receiver.create_capability (BACKLIGHT)
Iris::my_parent.provide_capability <Iris::Setting> (backlight.copy ())
Iris::free_cap (backlight)
// Register the display device.
Iris::Display display = Iris::my_receiver.create_capability (LCD)
Iris::my_parent.provide_capability <Iris::Display> (display.copy ())
Iris::free_cap (display)
Iris::my_parent.init_done ()
Iris::Cap eof_cb
bool have_eof = false
is_on = true
while true:
Iris::wait ()
//log_msg ()
switch Iris::recv.protected_data.l:
case 0:
have_eof = false
eof_cb.invoke ()
Iris::free_cap (eof_cb)
break
#if defined (TRENDTAC)
case LOG:
log_char (Iris::recv.data[0].l)
break
#endif
case BACKLIGHT:
switch Iris::recv.data[0].l:
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Setting::SET:
// TODO
unsigned state = Iris::recv.data[1].l
if !state:
#if defined (NANONOTE)
if is_on:
write_reg (BACKLIGHT1, 0x5e)
is_on = false
#else
#endif
else:
#if defined (NANONOTE)
if !is_on:
write_reg (BACKLIGHT1, 0x5f)
is_on = true
#else
#endif
Iris::recv.reply.invoke ()
break
case Iris::Setting::GET_RANGE:
Iris::recv.reply.invoke (~0)
break
default:
Iris::panic (0, "invalid operation for backlight")
break
break
case LCD:
switch Iris::recv.data[0].l:
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Display::SET_EOF_CB:
Iris::Cap reply = Iris::get_reply ()
Iris::Cap arg = Iris::get_arg ()
if have_eof:
Iris::free_cap (eof_cb)
Iris::panic (0, "replacing eof_cb")
else:
lcd_clr_eof ()
Iris::register_interrupt (IRQ_LCD)
have_eof = true
eof_cb = arg
reply.invoke ()
Iris::free_cap (reply)
break
case Iris::Display::MAP_FB:
unsigned addr = Iris::recv.data[1].l
unsigned use = Iris::recv.data[0].h
Iris::Cap reply = Iris::get_reply ()
Iris::Memory mem = Iris::get_arg ()
unsigned physical = mem.alloc_range (pages)
assert (physical & PAGE_MASK && ~physical)
Iris::Caps ret = mem.create_caps (pages / 63 + 1)
unsigned slot = ret.use ()
for unsigned c = 0; c < pages / 63 + 1; ++c:
Iris::Caps caps (Iris::Cap (slot, c))
unsigned num = pages - 63 * c >= 63 ? 63 : pages - 63 * c
Iris::set_recv_arg (caps)
mem.create_caps (num)
unsigned slot2 = caps.use ()
for unsigned i = 0; i < num; ++i:
Iris::Page p = Iris::Cap (slot2, i)
Iris::set_recv_arg (p)
mem.create_page ()
p.alloc_physical (physical + ((63 * c + i) << PAGE_BITS), false, true)
mem.map (p, addr + ((63 * c + i) << PAGE_BITS))
Iris::free_slot (slot2)
Iris::free_slot (slot)
reply.invoke (0, 0, ret.copy ())
Iris::free_cap (ret)
Iris::free_cap (mem)
Iris::free_cap (reply)
if !use:
break
bool was_unused = descriptor.frame == 0
descriptor.frame = physical
unsigned dptr = (unsigned)&descriptor
__asm__ volatile ("lw $a0, %0\ncache 0x15, 0($a0)" :: "m"(dptr) : "memory", "a0")
if was_unused && is_on:
lcd_set_ena ()
#ifdef NANONOTE:
write_reg (BACKLIGHT1, 0x5f)
#endif
break
case Iris::Display::UNMAP_FB:
Iris::panic (0, "unmap_fb isn't implemented yet")
case Iris::Display::GET_INFO:
Iris::panic (0, "get_info isn't implemented yet.")
default:
Iris::panic (Iris::recv.data[0].l, "invalid operation for lcd")
break
default:
Iris::panic (0, "invalid operation type for lcd")
break
+180
View File
@@ -0,0 +1,180 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// boot-programs/nand.ccp: NAND driver.
// 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"
#define debug Iris::debug
#define DELAY Iris::schedule
#include "nand.hh"
#undef debug
static unsigned *cache
static bool dirty
static unsigned current_block
static void sync ():
//Iris::debug ("erasing %x\n", current_block << block_bits)
erase (current_block << block_bits)
for unsigned p = 0; p < 1 << (block_bits - page_bits); ++p:
write ((current_block << block_bits) + (p << page_bits), (char *)&cache[p << (page_bits - 2)])
static void read_block (unsigned b):
if b == current_block:
return
if current_block != ~0 && dirty:
sync ()
current_block = b
//kdebug ("setting current block to ")
//kdebug_num (b)
//kdebug ("\n")
for unsigned p = 0; p < 1 << (block_bits - 9); ++p:
read ((b << block_bits) + (p << 9), (char *)&cache[p << (9 - 2)])
dirty = false
Iris::Num start ():
map_emc ()
map_gpio ()
// Arbitrary addresses where the pages are mapped.
command = (volatile char *)0x15000
address = (volatile char *)0x16000
data = (volatile char *)0x17000
Iris::Page data_page = Iris::my_memory.create_page ()
Iris::Page command_page = Iris::my_memory.create_page ()
Iris::Page address_page = Iris::my_memory.create_page ()
unsigned base = 0x18000000
data_page.alloc_physical (base, false, false)
command_page.alloc_physical (base + 0x8000, false, false)
address_page.alloc_physical (base + 0x10000, false, false)
Iris::my_memory.map (data_page, (unsigned)data)
Iris::my_memory.map (command_page, (unsigned)command)
Iris::my_memory.map (address_page, (unsigned)address)
Iris::free_cap (data_page)
Iris::free_cap (command_page)
Iris::free_cap (address_page)
reset ()
current_block = ~0
dirty = false
cache = (unsigned *)0x40000000
for unsigned p = 0; p < 1 << (block_bits - PAGE_BITS); ++p:
Iris::Page page = Iris::my_memory.create_page ()
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
Iris::my_memory.map (page, (unsigned)cache + (p << PAGE_BITS))
Iris::free_cap (page)
Iris::Page tmp = Iris::my_memory.create_page ()
tmp.set_flags (Iris::Page::PAYING)
unsigned *tmp_addr = (unsigned *)0x3ffff000
Iris::my_memory.map (tmp, (unsigned)tmp_addr)
Iris::Cap cap = Iris::my_receiver.create_capability (0)
Iris::my_parent.provide_capability <Iris::WBlock> (cap.copy ())
Iris::free_cap (cap)
Iris::my_parent.init_done ()
while true:
Iris::wait ()
switch Iris::recv.data[0].l:
case Iris::Block::GET_SIZE:
if size_bits > 32:
Iris::recv.reply.invoke (Iris::Num (0, 1 << (size_bits - 32)))
else:
Iris::recv.reply.invoke (1 << size_bits)
break
case Iris::Block::GET_ALIGN_BITS:
Iris::recv.reply.invoke (9)
break
case Iris::Block::GET_BLOCK:
unsigned row = Iris::recv.data[1].value () >> 9
unsigned block = row >> (block_bits - 9)
row &= (1 << (block_bits - 9)) - 1
unsigned offset = Iris::recv.data[0].h & 0xe00
unsigned size = Iris::recv.data[0].h >> 16
if size + offset >= 0x1000:
size = 0x1000 - offset
#if 0
kdebug ("get ")
kdebug_num (block)
kdebug (":")
kdebug_num (row)
kdebug ("+")
kdebug_num (size)
kdebug ("@")
kdebug_num (offset)
kdebug ("\n")
#endif
read_block (block)
Iris::Cap reply = Iris::get_reply ()
Iris::Page page = Iris::get_arg ()
page.share (tmp)
tmp.set_flags (Iris::Page::FRAME)
for unsigned t = 0; t < size >> 2; ++t:
tmp_addr[(offset >> 2) + t] = cache[t + (row << (9 - 2))]
tmp.set_flags (0, Iris::Page::FRAME)
reply.invoke ()
Iris::free_cap (reply)
Iris::free_cap (page)
break
case Iris::WBlock::SET_BLOCK:
unsigned row = Iris::recv.data[1].value () >> 9
unsigned block = row >> (block_bits - 9)
row &= (1 << (block_bits - 9)) - 1
unsigned offset = Iris::recv.data[0].h & 0xe00
unsigned size = Iris::recv.data[0].h >> 16
if size + offset >= 0x1000:
size = 0x1000 - offset
#if 0
kdebug ("set ")
kdebug_num (block)
kdebug (":")
kdebug_num (row)
kdebug ("+")
kdebug_num (size)
kdebug ("@")
kdebug_num (offset)
kdebug ("\n")
#endif
//kdebug_num (current_block)
//kdebug ("/")
read_block (block)
Iris::Cap reply = Iris::get_reply ()
Iris::Page page = Iris::get_arg ()
page.share (tmp)
tmp.set_flags (Iris::Page::FRAME)
for unsigned t = 0; t < size; t += 4:
cache[(offset + t + (row << 9)) >> 2] = tmp_addr[(offset + t) >> 2]
tmp.set_flags (0, Iris::Page::FRAME)
reply.invoke ()
Iris::free_cap (reply)
Iris::free_cap (page)
dirty = true
//kdebug_num (current_block)
//kdebug ("!")
break
case Iris::WBlock::TRUNCATE:
default:
Iris::recv.reply.invoke (Iris::ERR_INVALID_OPERATION)
break
+153
View File
@@ -0,0 +1,153 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// source/rtc.ccp: real-time clock driver.
// Copyright 2010 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"
static Iris::Font font
static void ready ():
while !rtc_write_ready ():
Iris::schedule ()
static unsigned get_second ():
ready ()
unsigned ret = rtc_get_second ()
unsigned r2
while true:
ready ()
r2 = rtc_get_second ()
if ret == r2:
return ret
kdebug ("ret != r2\n")
ret = r2
Iris::Num start ():
map_cpm ()
map_rtc ()
cpm_start_rtc ()
ready ()
rtc_enabled ()
if rtc_get_scratch_pattern () != 0xbeefface:
ready ()
rtc_set_nc1Hz_val (RTC_CLOCK)
ready ()
rtc_set_adjc_val (0)
ready ()
rtc_set_second (0)
ready ()
rtc_set_alarm_second (0)
ready ()
rtc_set_scratch_pattern (0xbeefface)
ready ()
rtc_disable_alarm ()
ready ()
rtc_clear_alarm_flag ()
ready ()
rtc_enable_alarm_irq ()
ready ()
rtc_disable_1Hz_irq ()
ready ()
rtc_set_hwfcr_val (0x1e0)
ready ()
rtc_set_hrcr_val (0xfe0)
ready ()
rtc_disable_alarm_wakeup ()
Iris::RTC self = Iris::my_receiver.create_capability (1)
Iris::my_parent.provide_capability <Iris::RTC> (self.copy ())
Iris::free_cap (self)
Iris::my_parent.init_done ()
bool have_interrupt = false
Iris::Cap interrupt
while true:
Iris::wait ()
if Iris::recv.protected_data.l == 0:
// Interrupt.
ready ()
rtc_disable_alarm ()
ready ()
rtc_disable_alarm_wakeup ()
if have_interrupt:
interrupt.invoke (0)
Iris::free_cap (interrupt)
have_interrupt = false
continue
switch Iris::recv.data[0].l:
case Iris::RTC::SETUP:
ready ()
rtc_set_nc1Hz_val (Iris::recv.data[1].l)
ready ()
rtc_set_adjc_val (Iris::recv.data[1].h)
Iris::recv.reply.invoke ()
break
case Iris::RTC::GET_TIME:
ready ()
Iris::recv.reply.invoke (rtc_get_second ())
Iris::recv.reply.invoke (0)
break
case Iris::RTC::SET_TIME:
ready ()
rtc_set_second (Iris::recv.data[1].l)
Iris::recv.reply.invoke ()
break
case Iris::RTC::GET_ALARM:
ready ()
Iris::recv.reply.invoke (rtc_get_alarm_second (), rtc_alarm_is_enabled ())
Iris::recv.reply.invoke (0)
break
case Iris::RTC::UNSET_ALARM:
Iris::Cap reply = Iris::get_reply ()
if have_interrupt:
have_interrupt = false
interrupt.invoke (~0)
Iris::free_cap (interrupt)
Iris::unregister_interrupt (IRQ_RTC)
ready ()
rtc_disable_alarm ()
ready ()
rtc_disable_alarm_wakeup ()
reply.invoke ()
Iris::free_cap (reply)
case Iris::RTC::SET_ALARM:
Iris::Cap reply = Iris::get_reply ()
Iris::Cap arg = Iris::get_arg ()
unsigned alarm = Iris::recv.data[1].l
if have_interrupt:
Iris::debug ("not registering irq\n")
interrupt.invoke (~0)
Iris::free_cap (interrupt)
else:
Iris::debug ("registering irq\n")
Iris::register_interrupt (IRQ_RTC)
interrupt = arg
have_interrupt = true
ready ()
rtc_set_alarm_second (alarm)
ready ()
rtc_clear_alarm_flag ()
ready ()
rtc_enable_alarm ()
ready ()
rtc_enable_alarm_wakeup ()
reply.invoke ()
Iris::free_cap (reply)
break
default:
Iris::panic (Iris::recv.data[0].l, "invalid rtc request")
return 0
+590
View File
@@ -0,0 +1,590 @@
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
// source/sd+mmc.ccp: sd+mmc driver.
// 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"
class Mmc:
public:
enum Response_type:
NONE = MSC_CMDAT_RESPONSE_NONE
RD_DATA = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_DATA_EN | MSC_CMDAT_READ
WR_DATA = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE
R1 = MSC_CMDAT_RESPONSE_R1
R1B = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_BUSY
R2 = MSC_CMDAT_RESPONSE_R2
R3 = MSC_CMDAT_RESPONSE_R3
R4 = MSC_CMDAT_RESPONSE_R4
R5 = MSC_CMDAT_RESPONSE_R5
R6 = MSC_CMDAT_RESPONSE_R6
R7 = MSC_CMDAT_RESPONSE_R7
static unsigned const POWER_PORT = 3
static unsigned const POWER_PIN = 2
struct CID:
unsigned mid
char oid[2]
char pnm[5]
unsigned prv
unsigned psn
unsigned year
unsigned month
struct CSD:
unsigned c_size
unsigned c_size_mult
unsigned read_bl_len, write_bl_len
bool copy
bool perm_write_protect
bool tmp_write_protect
bool send (unsigned cmd, unsigned arg, Response_type response_type, unsigned *response = NULL)
void check_sd ()
void check_sdmem ()
void check_mmc ()
public:
void reset ()
void detect ()
void release ()
void interrupt ()
CID const &get_cid ():
return cid
unsigned get_num_blocks ():
return num_blocks
unsigned get_read_block_size ():
return read_block_size
unsigned get_block_bits ():
return hc ? 9 : csd.read_bl_len > csd.write_bl_len ? csd.read_bl_len : csd.write_bl_len
void write_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset)
void read_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset)
void wait_write ()
void add_cb (Iris::Listitem item):
cb_list.add_item (item)
private:
void set_block (unsigned block)
unsigned current_block_num
bool dirty
unsigned *current_block
unsigned rca
bool have_sdmem, have_io
bool hc
CID cid
CSD csd
unsigned num_blocks, read_block_size
Iris::Page buffer_page
Iris::List cb_list
static unsigned const buffer = 0x15000
bool Mmc::send (unsigned cmd, unsigned arg, Response_type response_type, unsigned *response):
MSC_CMD = cmd
MSC_ARG = arg
MSC_CMDAT = response_type
MSC_IMASK = ~MSC_IMASK_END_CMD_RES
Iris::register_interrupt (IRQ_MSC)
msc_start_op ()
Iris::wait_for_interrupt ()
MSC_IMASK = ~0
//kdebug ("cmd: ")
//kdebug_num (cmd)
unsigned stat = MSC_STAT
//kdebug (", stat: ")
//kdebug_num (stat)
//kdebug ("\n")
if stat & MSC_STAT_CRC_RES_ERR:
Iris::panic (0, "crc error in mmc response")
return false
if stat & MSC_STAT_TIME_OUT_RES:
kdebug ("time out waiting for mmc response\n")
MSC_IREG = MSC_IREG_END_CMD_RES
return false
if response_type == R2:
unsigned d = MSC_RES
if d >> 8 != 0x3f:
Iris::panic (d, "invalid r2 response")
if cmd == 3:
// Read out result.
cid.mid = d & 0xff
d = MSC_RES
cid.oid[0] = d >> 8
cid.oid[1] = d & 0xff
d = MSC_RES
cid.pnm[0] = d >> 8
cid.pnm[1] = d & 0xff
d = MSC_RES
cid.pnm[2] = d >> 8
cid.pnm[3] = d & 0xff
d = MSC_RES
cid.pnm[4] = d >> 8
cid.prv = d & 0xff
d = MSC_RES
cid.psn = d << 16
d = MSC_RES
cid.psn |= d
d = MSC_RES
cid.year = 2000 + (d >> 4 & 0xff)
cid.month = d & 0xf
#if 1
Iris::debug ("CID: mid=%x, oid=%x %x, pnm=%x %x %x %x %x, prv=%x, psn=%x, year=%x, month=%x\n", cid.mid, cid.oid[0], cid.oid[1], cid.pnm[0], cid.pnm[1], cid.pnm[2], cid.pnm[3], cid.pnm[4], cid.prv, cid.psn, cid.year, cid.month)
#endif
else:
// Header (8) 1.0 1.0
// Read out csd.
// Ignore csd_structure. 2 (+ 6) 1.0 2.0 ***
d = MSC_RES
// Ignore taac and nsac. 8 + 8 2.0 4.0 ***
d = MSC_RES
// Ignore tran_speed, ccc. 8 + 8/12 2.0 6.0 ***
d = MSC_RES
// Ignore rest of ccc. 4/12 0.4 6.4
// 4 0.4 7.0
csd.read_bl_len = (d >> 8) & 0xf
// Ignore read_bl_partial, write_blk_misalign, read_blk_misalign, dsr_imp. 1 + 1 + 1 + 1 (+ 2) 0.6 7.6
// 2/12 0.2 8.0 ***
csd.c_size = (d & 0x0003) << 10
d = MSC_RES
// 10/12 1.2 9.2
csd.c_size |= d >> 6
// Ignore vdd_r_cur_min, vdd_r_cur_max. 3 + 3 0.6 10.0 ***
d = MSC_RES
// Ignore vdd_w_cur_min, vdd_w_cur_max. 3 + 3 0.6 10.6
// 3 0.3 11.1
csd.c_size_mult = (d >> 7) & 0x7
// Ignore erase_blk_enable, sector_size. 1 + 6/7 0.7 12.0 ***
d = MSC_RES
// Ignore rest of sector_size, wp_grp_size, wp_grp_enable, r2w_factor. 1/7 + 7 + 1 (+ 2) + 3 1.6 13.6
// 2/4 0.4 14.0 ***
csd.write_bl_len = (d << 2) & 0xc
d = MSC_RES
// 2/4 0.2 14.2
csd.write_bl_len |= (d >> 14) & 0x3
// Ignore write_bl_partial, file_format_grp. 1 (+ 5) + 1 0.7 15.1
// 1 0.1 15.2
csd.copy = d & 0x40
// 1 0.1 15.3
csd.perm_write_protect = d & 0x20
// 1 0.1 15.4
csd.tmp_write_protect = d & 0x10
// Ignore file_format. 2 (+ 2) 0.4 16.0 ***
read_block_size = hc ? 512 : 1 << csd.read_bl_len
num_blocks = (csd.c_size + 1) << (csd.c_size_mult + 2)
if hc:
if csd.read_bl_len < 9:
num_blocks >>= 9 - csd.read_bl_len
else:
num_blocks <<= csd.read_bl_len - 9
#if 1
Iris::debug ("CSD: size=%x<<%x, r/w len=%x/%x, %s, %s, %s\n", csd.c_size, csd.c_size_mult, csd.read_bl_len, csd.write_bl_len, csd.copy ? "copy" : "no copy", csd.perm_write_protect ? "fixed write protect" : "no fixed write protect", csd.tmp_write_protect ? "write protect" : "no write protect")
#endif
unsigned c_size
unsigned c_size_mult
unsigned read_bl_len, write_bl_len
bool copy
bool perm_write_protect
bool tmp_write_protect
else if response_type != NONE:
unsigned r = MSC_RES
if response_type == R3:
if r >> 8 != 0x3f:
Iris::panic (r, "r3 response was not 3f")
else if r >> 8 != cmd:
kdebug ("stat: ")
kdebug_num (MSC_STAT)
kdebug ("; response: ")
kdebug_num (r)
kdebug ("; cmd: ")
kdebug_num (cmd)
Iris::panic (r, "response doesn't match command")
r <<= 24
r |= MSC_RES << 8
r |= MSC_RES & 0xff
if response:
*response = r
else:
//kdebug ("extra response fifo read: ")
//for unsigned i = 0; i < 9; ++i:
//kdebug (" ")
//kdebug_num (MSC_RES, 4)
//kdebug ("\n")
MSC_IREG = MSC_IREG_END_CMD_RES
return true
void Mmc::reset ():
current_block_num = ~0
dirty = false
cb_list = Iris::my_memory.create_list ()
current_block = (unsigned *)(buffer + PAGE_SIZE)
// Create a buffer to use for data transfer.
buffer_page = Iris::my_memory.create_page ()
Iris::my_memory.map (buffer_page, buffer)
// Reset all state, by faking a release event.
release ()
// Enable 25 MHz clock to msc.
CPM_MSCCDR = 13
cpm_start_msc ()
// Enable msc pins.
gpio_as_msc ()
// Disable power to card.
gpio_as_gpio (POWER_PORT, 1 << POWER_PIN)
gpio_as_output (POWER_PORT, 1 << POWER_PIN)
gpio_disable_pull (POWER_PORT, 1 << POWER_PIN)
gpio_set (POWER_PORT, 1 << POWER_PIN)
// Stop the clock.
MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP
while MSC_STAT & MSC_STAT_CLK_EN:
//kdebug (",")
Iris::sleep (1)
// Reset controller and inserted devices.
MSC_STRPCL = MSC_STRPCL_RESET
while MSC_STAT & MSC_STAT_IS_RESETTING:
//kdebug (":")
Iris::sleep (1)
// Initialize registers.
MSC_CLKRT = MSC_CLKRT_CLK_RATE_DIV_1
MSC_RESTO = 64
MSC_RDTO = ~0
MSC_BLKLEN = 0x200
MSC_NOB = 0
MSC_IREG = ~0
MSC_IMASK = ~(MSC_IMASK_END_CMD_RES | MSC_IMASK_RXFIFO_RD_REQ)
MSC_ARG = 0
// Start the clock.
MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START
// Set cards, if any, to idle.
send (0, 0, NONE)
// Reset SDIO device, if any. Don't do this, because it breaks for some reason.
//send (52, 0x88000c08, R5)
void Mmc::check_mmc ():
//kdebug ("checking mmc\n")
// 1. SEND CMD1 (SEND_OP_CMD) TO VALIDATE VOLTAGE (THE GENERAL OCR VALUE IS 0X00FF88000).
// 2. IF THE RESPONSE IS CORRECT, THEN CONTINUE, ELSE GOTO 9.
// 3. IF THE INITIALIZATION HAS FINISHED, GO TO 5. (THE RESPONSE IS THE OCR REGISTER AND IT INCLUDES A STATUS INFORMATION BIT (BIT [31]). THIS STATUS BIT IS SET IF THE CARD POWER UP PROCEDURE HAS BEEN FINISHED. AS LONG AS THE CARD IS BUSY, THE CORRESPONDING BIT[31] IS SET TO LOW.)
// 4. Send CMD1 (SEND_OP_CMD) to validate voltage, and then go to 3.
// 5. Send CMD2 (ALL_SEND_CID) to get the card CID.
// 6. If the response timeout occurs, goto 9.
// 7. Send CMD3 (SET_RELATIVE_ADDR) to assign the card a RCA.
void Mmc::check_sdmem ():
kdebug ("checking sdmem\n")
// 2. Send CMD55. Here the default RCA 0x0000 is used for CMD55.
// 3. If the response is correct (CMD55 has response), then continue, else go to check MMC.
unsigned code
hc = false
if send (8, 0x1aa, R7, &code) && (code & 0xff) == 0xaa:
kdebug ("hc\n")
hc = true
if !send (55, 0, R1, &code):
check_mmc ()
return
// 4. Send ACMD41 (SD_SEND_OP_CMD) to validate voltage (the general OCR value is 0x00FF8000).
if !send (41, hc ? 0x40800000 : 0x00800000, R3, &code):
check_mmc ()
return
// 5. If the initialization has finished, go to 7. (The response is the OCR register and it includes a status information bit (bit [31]). This status bit is set if the card power up procedure has been finished. As long as the card is busy, the corresponding bit[31] is set to LOW.)
// 6. Send CMD55 and ACMD41 to validate voltage, and then go to 5.
unsigned retries = 100
while !(code & (1 << 31)) && --retries:
if !send (55, 0, R1, &code):
return
if !send (41, hc ? 0x40800000 : 0x00800000, R3, &code):
return
Iris::sleep (1)
if !(code & (1 << 31)):
Iris::panic (code, "card fails to finish setting up")
// 7. Send CMD2 (ALL_SEND_CID) to get the card CID.
if !send (2, 0, R2):
Iris::panic (0, "card failed to send CID")
// 8. Send CMD3 (SET_RELATIVE_ADDR) to let card publish a RCA. The RCA is returned from the response.
// 9. If do not accept the new RCA, go to 8, else record the new RCA.
rca = 0
while !rca:
if !send (3, 0, R6, &rca):
Iris::panic (0, "card failed to provide rca")
rca &= 0xffff0000
kdebug ("received rca ")
kdebug_num (rca >> 16, 4)
kdebug ("\n")
have_sdmem = true
void Mmc::check_sd ():
//kdebug ("checking sdio\n")
if !send (0, 0, NONE):
Iris::panic (0, "unable to reset cards?")
// 2. Send CMD5 (IO_SEND_OP_CMD) to validate voltage.
// 3. If the response is correct and the number of IO functions > 0, then continue, else go to check SDMEM.
unsigned code
if !send (5, 1 << 20, R4, &code) || !(code & (7 << 28)):
check_sdmem ()
return
// 4. If C-bit in the response is ready (the initialization has finished), go to 6.
// 5. Send CMD5 (IO_SEND_OP_CMD) to validate voltage, then go to 4.
while !(code & (1 << 31)):
if !send (5, 1 << 20, R4, &code):
Iris::panic (0, "invalid response to cmd 5")
// 6. If memory-present-bit in the response is true, then it is a combo card (SDIO + Memory), else it is only a SDIO card.
// 7. If it is a combo card, go to check SDMEM to initialize the memory part.
have_io = true
if code & (1 << 27):
check_sdmem ()
return
// 8. Send CMD3 (SET_RELATIVE_ADDR) to let the card publish a RCA. The RCA is returned from the response.
// 9. If do not accept the new RCA, go to 8, else record the new RCA.
rca = 0
while rca == 0:
if !send (3, 0, R6, &rca):
Iris::panic (0, "unable to set rca")
rca &= 0xffff0000
check_mmc ()
void Mmc::detect ():
kdebug ("mmc detect\n")
gpio_clear (POWER_PORT, 1 << POWER_PIN)
check_sd ()
check_mmc ()
if have_sdmem:
if !send (9, rca, R2):
Iris::panic (0, "unable to request csd")
if !send (7, rca, R1B):
Iris::panic (0, "unable to select sdmem")
kdebug ("found device; size = ")
kdebug_num (num_blocks)
kdebug (" * ")
kdebug_num (read_block_size)
kdebug (" = ")
kdebug_num (num_blocks * read_block_size)
kdebug ("\n")
// Set up buffer memory.
for unsigned i = 0; i < 1 << csd.write_bl_len; i += PAGE_SIZE:
Iris::Page p = Iris::my_memory.create_page ()
p.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
Iris::my_memory.map (p, (unsigned)current_block + i)
Iris::free_cap (p)
Iris::Listitem item = cb_list.get_next ()
while item.code != Iris::Cap ().code:
Iris::Cap c = cb_list.get_cap (item)
c.invoke (0, ~0)
Iris::free_cap (c)
Iris::Listitem nextitem = cb_list.get_next (item);
Iris::free_cap (item)
item = nextitem
void Mmc::release ():
kdebug ("mmc release\n")
gpio_set (POWER_PORT, 1 << POWER_PIN)
have_sdmem = false
have_io = false
read_block_size = 0
if num_blocks != 0:
for unsigned i = 0; i < 1 << csd.write_bl_len; i += PAGE_SIZE:
Iris::Page p = Iris::my_memory.mapping ((void *)((unsigned)current_block + i))
Iris::my_memory.destroy (p)
Iris::free_cap (p)
if dirty:
Iris::debug ("Warning: sd/mmc card removed before data was written to it")
current_block_num = ~0
dirty = false
num_blocks = 0
Iris::Listitem item = cb_list.get_next ()
while item.code != Iris::Cap ().code:
Iris::Cap c = cb_list.get_cap (item)
c.invoke (0, ~0)
Iris::free_cap (c)
Iris::Listitem nextitem = cb_list.get_next (item);
Iris::free_cap (item)
item = nextitem
void Mmc::interrupt ():
kdebug ("mmc interrupt\n")
void Mmc::set_block (unsigned block):
if current_block_num == block:
return
if dirty && current_block_num != ~0:
MSC_NOB = 1
MSC_BLKLEN = 1 << csd.write_bl_len
if !send (24, (current_block_num << csd.write_bl_len), WR_DATA):
Iris::panic (0, "unable to send data")
MSC_IMASK = ~MSC_IMASK_TXFIFO_WR_REQ
for unsigned a = 0; a < 1 << csd.write_bl_len; a += 4:
while MSC_STAT & MSC_STAT_DATA_FIFO_FULL:
Iris::register_interrupt (IRQ_MSC)
Iris::wait_for_interrupt ()
MSC_TXFIFO = current_block[a >> 2]
MSC_IMASK = ~0
MSC_IREG = MSC_IREG_DATA_TRAN_DONE
//kdebug ("done writing page\n")
current_block_num = block
dirty = false
MSC_NOB = 1
MSC_BLKLEN = 1 << 9
for unsigned a = 0; a < 1 << csd.write_bl_len; a += 1 << 9:
if !send (17, (block << csd.write_bl_len) + a, RD_DATA):
Iris::panic (0, "unable to request data")
MSC_IMASK = ~MSC_IMASK_RXFIFO_RD_REQ
for unsigned aa = 0; aa < 1 << 9; aa += 4:
while MSC_STAT & MSC_STAT_DATA_FIFO_EMPTY:
Iris::register_interrupt (IRQ_MSC)
Iris::wait_for_interrupt ()
current_block[(a + aa) >> 2] = MSC_RXFIFO
MSC_IMASK = ~0
MSC_IREG = MSC_IREG_DATA_TRAN_DONE
//kdebug ("done filling page\n")
void Mmc::read_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset):
if address.value () >> (csd.write_bl_len + 32):
Iris::panic (address.h, "page too high: not supported")
unsigned block = address.value () >> csd.write_bl_len
unsigned start_pos = address.l & (1 << csd.write_bl_len) - 1
set_block (block)
unsigned blockmask = ~((1 << 9) - 1)
size &= blockmask
offset &= ~PAGE_MASK & ~3
if size + offset > PAGE_SIZE:
size = PAGE_SIZE - offset
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
page.share (buffer_page)
buffer_page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
page.set_flags (0, Iris::Page::PAYING)
for unsigned i = 0; i < size; i += 4:
((unsigned *)buffer)[(offset + i) >> 2] = current_block[(start_pos + i) >> 2]
void Mmc::write_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset):
if address.value () >> (csd.write_bl_len + 32):
Iris::panic (address.h, "page too high: not supported")
unsigned block = address.value () >> csd.write_bl_len
unsigned start_pos = address.l & (1 << csd.write_bl_len) - 1
set_block (block)
unsigned blockmask = ~((1 << 9) - 1)
size &= blockmask
offset &= ~PAGE_MASK & ~3
if size + offset > PAGE_SIZE:
size = PAGE_SIZE - offset
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
page.share (buffer_page)
buffer_page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
page.set_flags (0, Iris::Page::PAYING)
for unsigned i = 0; i < size; i += 4:
current_block[(start_pos + i) >> 2] = ((unsigned *)buffer)[(offset + i) >> 2]
dirty = true
void Mmc::wait_write ():
MSC_IMASK = ~MSC_IMASK_PRG_DONE
while !MSC_STAT & MSC_STAT_PRG_DONE:
Iris::register_interrupt (IRQ_MSC)
Iris::wait_for_interrupt ()
MSC_IREG = MSC_IREG_PRG_DONE
static Mmc mmc
enum types:
DETECT = 1
REQUEST
Iris::Num start ():
map_msc ()
map_gpio ()
map_cpm ()
mmc.reset ()
Iris::Event detect = Iris::my_parent.get_capability <Iris::Event> ()
Iris::Cap cap = Iris::my_receiver.create_capability (DETECT)
detect.set_cb (cap.copy ())
cap.invoke (~0)
Iris::free_cap (cap)
// Get a message from the queue. This is either the "there is no card" message, or the message we just sent.
Iris::wait ()
if Iris::recv.data[0].l != ~0:
// If it was "there is no card", the message we sent is still in the queue.
Iris::wait ()
else:
// Otherwise, there is a card.
mmc.detect ()
cap = Iris::my_receiver.create_capability (REQUEST)
Iris::my_parent.provide_capability <Iris::WBlock> (cap.copy ())
Iris::free_cap (cap)
Iris::my_parent.init_done ()
while true:
Iris::wait ()
switch Iris::recv.protected_data.l:
case 0:
mmc.interrupt ()
break
case DETECT:
if Iris::recv.data[0].l:
mmc.detect ()
else:
mmc.release ()
break
case REQUEST:
//kdebug ("sd+mmc request ")
//kdebug_num (Iris::recv.data[0].l)
//kdebug ("\n")
switch Iris::recv.data[0].l:
case Iris::Block::GET_SIZE:
Iris::debug ("get size\n")
unsigned long long size = mmc.get_num_blocks () * mmc.get_read_block_size ()
Iris::recv.reply.invoke (size)
break
case Iris::Block::GET_ALIGN_BITS:
Iris::debug ("get align bits\n")
Iris::recv.reply.invoke (9)
break
case Iris::Block::GET_BLOCK:
//Iris::debug ("get block\n")
Iris::Cap reply = Iris::get_reply ()
Iris::Page page = Iris::get_arg ()
mmc.read_page (page, Iris::recv.data[1], Iris::recv.data[0].h >> 16, Iris::recv.data[0].h & 0xffff)
reply.invoke ()
Iris::free_cap (page)
Iris::free_cap (reply)
break
case Iris::WBlock::SET_BLOCK:
Iris::debug ("set block\n")
Iris::Cap reply = Iris::get_reply ()
Iris::Page page = Iris::get_arg ()
mmc.write_page (page, Iris::recv.data[1], Iris::recv.data[0].h >> 16, Iris::recv.data[0].h & 0xffff)
reply.invoke ()
Iris::free_cap (page)
Iris::free_cap (reply)
mmc.wait_write ()
break
case Iris::Block::SET_CHANGE_CB:
Iris::debug ("set change cb\n")
Iris::Listitem item = Iris::get_arg ()
Iris::Cap reply = Iris::get_reply ()
mmc.add_cb (item)
reply.invoke ()
Iris::free_cap (item)
Iris::free_cap (reply)
break
case Iris::WBlock::TRUNCATE:
Iris::debug ("truncate\n")
// Fall through: don't support resizing.
default:
Iris::panic (0, "unexpected event for sd+mmc")
break
default:
Iris::panic (0, "unexpected request source for sd+mmc")
+664
View File
@@ -0,0 +1,664 @@
#pypp 0
// vim: set filetype=cpp : //
// Iris: micro-kernel for a capability-based operating system.
// boot-programs/udc.ccp: USB device controller driver.
// 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 "iris.hh"
#include "devices.hh"
#define ARCH
#include "arch.hh"
class Udc:
typedef unsigned char u8
typedef unsigned short u16
typedef unsigned int u32
typedef u8 string
// The ugly stuff is because pypp doesn't support __attribute__.
/**/struct Setup {
u8 request_type;
u8 request;
u16 value;
u16 index;
u16 length;
} __attribute__ ((packed))
/**/struct Device {
static u8 const Type = 1;
u8 length;
u8 type;
u16 usb_version;
u8 dev_class;
u8 subclass;
u8 protocol;
u8 max_packet_size0;
u16 vendor;
u16 product;
u16 dev_version;
string s_manufacturer;
string s_product;
string s_serial;
u8 num_configurations;
} __attribute__ ((packed))
/**/struct Configuration {
static u8 const Type = 2;
u8 length;
u8 type;
u16 total_length;
u8 num_interfaces;
u8 configuration_value;
u8 configuration;
u8 attributes;
u8 max_power;
} __attribute__ ((packed))
/**/struct Interface {
static u8 const Type = 4;
u8 length;
u8 type;
u8 interface;
u8 alternate;
u8 num_endpoints;
u8 iface_class;
u8 subclass;
u8 protocol;
string name;
} __attribute__ ((packed))
/**/struct Endpoint {
static u8 const Type = 5;
u8 length;
u8 type;
u8 address;
u8 attributes;
u16 max_packet_size;
u8 interval;
} __attribute__ ((packed))
/**/struct Device_Qualifier {
static u8 const Type = 6;
u8 length;
u8 type;
u16 version;
u8 dev_class;
u8 subclass;
u8 protocol;
u8 max_packet_size0;
u8 num_configurations;
u8 reserved;
} __attribute__ ((packed))
/**/struct Langs {
static u8 const Type = 3;
u8 length;
u8 type;
u8 lang;
} __attribute__ ((packed))
template <unsigned size> struct String {
static u8 const Type = 3;
u8 length;
u8 type;
u16 data[size];
} __attribute__ ((packed))
static unsigned const max_packet_size0 = 64
static unsigned const max_packet_size_bulk = 64
enum Requests:
GET_STATUS = 0
CLEAR_FEATURE = 1
SET_FEATURE = 3
SET_ADDRESS = 5
GET_DESCRIPTOR = 6
SET_DESCRIPTOR = 7
GET_CONFIGURATION = 8
SET_CONFIGURATION = 9
GET_INTERFACE = 10
SET_INTERFACE = 11
SYNCH_FRAME = 12
enum Request_types:
STANDARD_TO_DEVICE = 0
VENDOR_TO_DEVICE = 0x40
STANDARD_FROM_DEVICE = 0x80
VENDOR_FROM_DEVICE = 0xc0
enum Endpoint_types:
CONTROL = 0
ISOCHRONOUS = 1
BULK = 2
INTERRUPT = 3
/**/struct my_config {
Configuration config;
Interface interface;
Endpoint endpoint[2];
} __attribute__ ((packed))
static Device device_descriptor
//static Device_Qualifier device_qualifier_descriptor
static my_config config_descriptor; //, other_config_descriptor
static String <1> s_langs
static String <6> s_manufacturer
static String <16> s_product
enum State:
IDLE
TX
RX
State state
char configuration
unsigned size
unsigned rx_request
char const *ptr
unsigned cmd_code, cmd_arg
bool rebooting
bool vendor (Setup *s, unsigned cmd)
bool get_descriptor (unsigned type, unsigned idx, unsigned len)
bool handle_setup (Setup *s, unsigned cmd)
void irq_usb (unsigned cmd)
void irq_in (unsigned cmd)
void irq_out (unsigned cmd)
char log_buffer[1000]
unsigned log_buffer_size
unsigned log_buffer_start
Iris::Cap caller, caller_arg
bool have_caller
unsigned *page
unsigned *p
Iris::Page buffer_page
public:
void init ()
void log (unsigned c)
void interrupt (unsigned cmd)
void send (unsigned code, unsigned narg, Iris::Cap reply, Iris::Cap arg)
Udc::Device Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
Udc::my_config Udc::config_descriptor = {
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
}
}
Udc::String <1> Udc::s_langs = { sizeof (String <1>), String <1>::Type, { 0x0409 } }
Udc::String <6> Udc::s_manufacturer = { sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
Udc::String <16> Udc::s_product = { sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
//Udc::Device_Qualifier const Udc::device_qualifier_descriptor = { sizeof (Device_Qualifier), Device_Qualifier::Type, 0x200, 0, 0, 0, max_packet_size0, 1, 0 }
//Udc::my_config const Udc::other_config_descriptor = {
// (Configuration){ sizeof (Configuration), 7, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
// (Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
// }
// }
void Udc::init ():
// Initialize the globals. My method of compiling doesn't do that properly.
device_descriptor = (Device){ sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
config_descriptor = (my_config){
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
}
}
s_langs = (String <1>){ sizeof (String <1>), String <1>::Type, { 0x0409 } }
s_manufacturer = (String <6>){ sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
s_product = (String <16>){ sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
have_caller = false
log_buffer_size = 0
log_buffer_start = 0
cmd_code = ~0
cmd_arg = 0
// Use the space which is reserved for the framebuffer, because it will not be in use.
// Normally a normal new page should be allocated here, but new isn't implemented at this point.
page = (unsigned *)LCD_FRAMEBUFFER_BASE
p = page
buffer_page = Iris::my_memory.create_page ()
buffer_page.set_flags (Iris::Page::FRAME | Iris::Page::PAYING)
Iris::my_memory.map (buffer_page, (unsigned)page)
// Disconnect from the bus and don't try to get high-speed.
UDC_POWER = 0
UDC_TESTMODE = 0
UDC_INDEX = 0
state = IDLE
configuration = 0
size = 0
// exit suspend mode by reading the interrupt register.
cpm_start_udc ()
// reset all pending endpoint interrupts.
unsigned i = UDC_INTRUSB
i = UDC_INTRIN
i = UDC_INTROUT
// enable interrupt on bus reset.
UDC_INTRUSBE = UDC_INTR_RESET
// enable interrupts on endpoint 0.
UDC_INTRINE = 1 << 0
// and on out endpoint 1.
UDC_INTROUTE = 1 << 1
// Wait a while.
for unsigned w = 0; w < 10000; ++w:
Iris::schedule ()
// Connect to the host.
UDC_POWER = UDC_POWER_SOFTCONN
bool Udc::vendor (Setup *s, unsigned cmd):
if !(s->request_type & 0x80):
kdebug ("data to device without size\n")
Iris::panic (0)
return true
if s->request == 10:
static unsigned b[2]
ptr = (char *)b
size = s->length < 8 ? s->length : 8
if cmd_code != ~0:
b[0] = cmd_code
b[1] = cmd_arg
if cmd_code == Iris::Directory::LOCK_RO || cmd_code == Iris::Directory::UNLOCK_RO:
caller.invoke ()
Iris::free_cap (caller)
Iris::free_cap (caller_arg)
have_caller = false
//kdebug ("(un)lock response\n")
cmd_code = ~0
else:
if log_buffer_start == log_buffer_size:
b[0] = ~1
b[1] = 0
else:
b[0] = ~0
b[1] = (log_buffer[log_buffer_start++] & 0xff) * 0x01010101
if log_buffer_start == log_buffer_size:
log_buffer_start = 0
log_buffer_size = 0
else:
static char const *name = "Reboot"
ptr = name
size = s->length < 6 ? s->length : 6
rebooting = true
state = TX
return true
void Udc::send (unsigned code, unsigned narg, Iris::Cap reply, Iris::Cap arg):
if cmd_code != ~0:
kdebug ("new code sent while old one wasn't finished.\n")
Iris::panic (0)
cmd_code = code
cmd_arg = narg
caller = reply
caller_arg = arg
have_caller = true
bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
switch type:
case Configuration::Type:
if idx != 0:
return false
ptr = reinterpret_cast <char const *> (&config_descriptor)
size = (len < sizeof (config_descriptor) ? len : sizeof (config_descriptor))
break
case Device::Type:
if idx != 0:
return false
ptr = reinterpret_cast <char const *> (&device_descriptor)
size = (len < sizeof (device_descriptor) ? len : sizeof (device_descriptor))
break
case Device_Qualifier::Type:
//if idx != 0:
// return false
//ptr = reinterpret_cast <char const *> (&device_qualifier_descriptor)
//size = (len < sizeof (device_qualifier_descriptor) ? len : sizeof (device_qualifier_descriptor))
//break
return false
// The 6 is an arbitrary number, except that String <6> is instantiated already.
case String <6>::Type:
switch idx:
case 0:
ptr = reinterpret_cast <char const *> (&s_langs)
size = (len < sizeof (s_langs) ? len : sizeof (s_langs))
break
case 1:
ptr = reinterpret_cast <char const *> (&s_manufacturer)
size = (len < sizeof (s_manufacturer) ? len : sizeof (s_manufacturer))
break
case 2:
ptr = reinterpret_cast <char const *> (&s_product)
size = (len < sizeof (s_product) ? len : sizeof (s_product))
break
default:
return false
break
default:
return false
state = TX
return true
bool Udc::handle_setup (Setup *s, unsigned cmd):
switch s->request_type:
case STANDARD_TO_DEVICE:
switch s->request:
case SET_ADDRESS:
UDC_FADDR = s->value
break
case SET_CONFIGURATION:
if s->value >= 2:
return false
configuration = s->value
break
case SET_INTERFACE:
if s->value != 0:
return false
break
default:
return false
UDC_OUTMAXP = 64
UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF
UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF
break
case STANDARD_FROM_DEVICE:
switch s->request:
case GET_STATUS:
ptr = "\0\0"
size = (s->length < 2 ? s->length : 2)
state = TX
break
case GET_DESCRIPTOR:
return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length)
case GET_CONFIGURATION:
ptr = &configuration
size = (s->length < 1 ? s->length : 1)
state = TX
break
case GET_INTERFACE:
ptr = "\0"
size = (s->length < 1 ? s->length : 1)
state = TX
break
default:
return false
break
case VENDOR_TO_DEVICE:
case VENDOR_FROM_DEVICE:
return vendor (s, cmd)
default:
return false
return true
void Udc::irq_usb (unsigned cmd):
// Reset.
//Iris::debug ("usb reset\n")
state = IDLE
void Udc::irq_in (unsigned cmd):
// Interrupt on endpoint 0.
unsigned csr = UDC_CSR0
if csr & UDC_CSR0_SENTSTALL:
csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL)
state = IDLE
if csr & UDC_CSR0_SETUPEND:
csr |= UDC_CSR0_SVDSETUPEND
state = IDLE
switch state:
case IDLE:
if rebooting:
Iris::reboot ()
if !(csr & UDC_CSR0_OUTPKTRDY):
return
union { unsigned d[2]; Setup s; } packet
packet.d[0] = UDC_FIFO (0)
packet.d[1] = UDC_FIFO (0)
if !(packet.s.request_type & 0x80) && packet.s.length > 0:
// More data will follow; delay handling of packet.
state = RX
UDC_CSR0 = csr | UDC_CSR0_SVDOUTPKTRDY
rx_request = packet.s.request
return
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
if !handle_setup (&packet.s, cmd):
csr |= UDC_CSR0_SENDSTALL
break
if size == 0:
return
// Fall through.
case TX:
unsigned i
for i = 0; (size & ~3) > 0 && i < max_packet_size0; i += 4, size -= 4:
UDC_FIFO (0) = *(unsigned *)ptr
ptr += 4
for ; size > 0 && i < max_packet_size0; ++i, --size:
UDC_FIFO8 (0) = *ptr++
if i == max_packet_size0:
csr |= UDC_CSR0_INPKTRDY
else:
state = IDLE
csr |= UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
break
case RX:
// The protocol that is used doesn't allow large packets, so being here always means the entire packet is received.
switch rx_request & 0xff:
case Iris::Directory::GET_SIZE & 0xff:
if !have_caller:
kdebug ("received dir size from server without a caller waiting\n")
Iris::panic (0)
unsigned size_l = UDC_FIFO (0)
unsigned size_h = UDC_FIFO (0)
caller.invoke (Iris::Num (size_l, size_h))
Iris::free_cap (caller)
Iris::free_cap (caller_arg)
have_caller = false
//kdebug ("get_size response\n")
break
case Iris::Directory::GET_NAME & 0xff:
if !have_caller:
kdebug ("received filename from server without a caller waiting\n")
Iris::panic (0)
unsigned n[4]
for unsigned i = 0; i < 4; ++i:
n[i] = UDC_FIFO (0)
caller.invoke (Iris::Num (n[0], n[1]), Iris::Num (n[2], n[3]))
Iris::free_cap (caller)
Iris::free_cap (caller_arg)
//kdebug ("get_name response\n")
have_caller = false
break
case Iris::String::GET_SIZE & 0xff:
if !have_caller:
kdebug ("received string size from server without a caller waiting\n")
Iris::panic (0)
unsigned size_l = UDC_FIFO (0)
unsigned size_h = UDC_FIFO (0)
caller.invoke (Iris::Num (size_l, size_h))
Iris::free_cap (caller)
Iris::free_cap (caller_arg)
have_caller = false
//kdebug ("get_filesize response\n")
break
case Iris::String::GET_CHARS & 0xff:
if !have_caller:
kdebug ("received string char data from server without a caller waiting\n")
Iris::panic (0)
unsigned n[4]
for unsigned i = 0; i < 4; ++i:
n[i] = UDC_FIFO (0)
caller.invoke (Iris::Num (n[0], n[1]), Iris::Num (n[2], n[3]))
Iris::free_cap (caller)
Iris::free_cap (caller_arg)
have_caller = false
//kdebug ("get_chars response\n")
break
default:
kdebug ("invalid vendor request: ")
kdebug_num (rx_request)
kdebug ("\n")
Iris::panic (0)
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
state = IDLE
break
UDC_CSR0 = csr
void Udc::irq_out (unsigned cmd):
// Interrupt on OUT endpoint 1.
UDC_INDEX = 1
if !have_caller:
kdebug ("received bulk data from server without a caller waiting\n")
Iris::panic (0)
unsigned size = UDC_OUTCOUNT
unsigned csr = UDC_OUTCSR
//Iris::debug ("handling bulk interrupt for %x with %d bytes.\n", csr, size)
csr &= ~UDC_OUTCSR_OUTPKTRDY
for unsigned i = 0; i < size; i += 4:
*p++ = UDC_FIFO (1)
if p - page == PAGE_SIZE >> 2:
buffer_page.share (caller_arg, Iris::Page::FORGET)
buffer_page.set_flags (Iris::Page::FRAME)
caller.invoke ()
Iris::free_cap (caller)
Iris::free_cap (caller_arg)
have_caller = false
//kdebug ("bulk response\n")
p = page
UDC_OUTCSR = csr
UDC_INDEX = 0
void Udc::interrupt (unsigned cmd):
while true:
unsigned usb = UDC_INTRUSB
unsigned in = UDC_INTRIN
unsigned out = UDC_INTROUT
if !(usb & 4) && !(in & 1) && !(out & 2):
break
//Iris::debug ("interrupt: %d/%d/%d\n", usb, in, out)
if usb & 4:
irq_usb (cmd)
if in & 1:
irq_in (cmd)
if out & 2:
irq_out (cmd)
void Udc::log (unsigned c):
if log_buffer_size >= sizeof (log_buffer):
return
log_buffer[log_buffer_size++] = c
enum pdata:
LOG = 1
DIRECTORY
FILE
NAME
Iris::Num start ():
map_udc ()
map_gpio ()
map_cpm ()
Udc udc
//Iris::Cap logcap = Iris::my_receiver.create_capability (LOG)
//__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1")
Iris::Directory dir = Iris::my_receiver.create_capability (DIRECTORY)
Iris::my_parent.provide_capability <Iris::Directory> (dir.copy ())
Iris::free_cap (dir)
udc.init ()
Iris::register_interrupt (IRQ_UDC)
// Don't call init_done, because this can be used as bootthread and without a parent this call will not be answered.
//Iris::my_parent.init_done ()
unsigned state = 0
while true:
Iris::wait ()
Iris::Cap reply = Iris::get_reply ()
Iris::Cap arg = Iris::get_arg ()
//Iris::debug ("udc event, protected: %x\n", Iris::recv.protected_data.l)
switch Iris::recv.protected_data.l:
case 0:
udc.interrupt (state)
Iris::register_interrupt (IRQ_UDC)
break
case LOG:
udc.log (Iris::recv.data[0].l)
break
case DIRECTORY:
//Iris::debug ("dir request %d\n", Iris::recv.data[0].l)
switch Iris::recv.data[0].l:
case Iris::Directory::GET_NAME:
Iris::Cap name = Iris::my_receiver.create_capability (Iris::Num (NAME, Iris::recv.data[1].l))
reply.invoke (0, 0, name.copy ())
Iris::free_cap (name)
Iris::free_cap (reply)
Iris::free_cap (arg)
continue
case Iris::Directory::GET_SIZE:
case Iris::Directory::LOCK_RO:
case Iris::Directory::UNLOCK_RO:
state = Iris::recv.data[0].l
if Iris::recv.data[1].h != 0:
Iris::panic (0, "index out of supported range")
udc.send (Iris::recv.data[0].l, Iris::recv.data[1].l, reply, arg)
continue
case Iris::Directory::GET_FILE_RO:
if Iris::recv.data[1].h != 0:
kdebug ("index out of supported range\n")
Iris::panic (0)
//kdebug ("sending file\n")
Iris::Cap file = Iris::my_receiver.create_capability (Iris::Num (FILE, Iris::recv.data[1].l))
reply.invoke (0, 0, file.copy ())
Iris::free_cap (file)
Iris::free_cap (reply)
Iris::free_cap (arg)
continue
case Iris::Directory::GET_FILE_INFO:
default:
reply.invoke (Iris::ERR_INVALID_OPERATION)
Iris::free_cap (reply)
Iris::free_cap (arg)
continue
break
case FILE:
//Iris::debug ("file request %d\n", Iris::recv.data[0].l)
switch Iris::recv.data[0].l:
case Iris::Block::GET_BLOCK:
if Iris::recv.data[0].h != PAGE_SIZE << 16:
Iris::panic (0, "unsupported get_block arguments for boot usb device driver")
// Fall through.
case Iris::Block::GET_SIZE:
udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg)
continue
default:
reply.invoke (Iris::ERR_INVALID_OPERATION)
Iris::free_cap (reply)
Iris::free_cap (arg)
continue
break
case NAME:
//Iris::debug ("name request %d\n", Iris::recv.data[0].l)
switch Iris::recv.data[0].l:
case Iris::String::GET_SIZE:
reply.invoke (16)
Iris::free_cap (reply)
Iris::free_cap (arg)
continue
case Iris::String::GET_CHARS:
state = Iris::recv.data[0].l
udc.send (Iris::Directory::GET_NAME, Iris::recv.protected_data.h, reply, arg)
continue
default:
reply.invoke (Iris::ERR_INVALID_OPERATION)
Iris::free_cap (reply)
Iris::free_cap (arg)
continue
default:
kdebug ("other request:")
kdebug_num (Iris::recv.protected_data.l)
kdebug ("\n")
udc.log ('~')
char digit[] = "0123456789abcdef"
for unsigned i = 0; i < 8; ++i:
udc.log (digit[(Iris::recv.protected_data.l >> (4 * (7 - i))) & 0xf])
udc.log ('\n')
break
reply.invoke ()
Iris::free_cap (reply)
Iris::free_cap (arg)