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

add partial nand and sd/mmc drivers

This commit is contained in:
Bas Wijnen
2010-05-15 18:42:17 +02:00
parent 8c7cac36e6
commit 6bf41032d8
13 changed files with 584 additions and 132 deletions

View File

@@ -12,11 +12,12 @@ enum Outs:
ALARM
NUM_OUTS
static UI <NUM_INS, NUM_OUTS> ui
static UI <NUM_INS, NUM_OUTS>::in <unsigned> total_time
static UI <NUM_INS, NUM_OUTS>::in_event do_start
static UI <NUM_INS, NUM_OUTS>::out <unsigned> current_time
static UI <NUM_INS, NUM_OUTS>::out_event do_alarm
typedef UI <NUM_INS, NUM_OUTS> ui_t
static ui_t ui
static ui_t::in <unsigned> total_time
static ui_t::in_event do_start
static ui_t::out <unsigned> current_time
static ui_t::out_event do_alarm
static bool ticking
@@ -56,7 +57,8 @@ Iris::Num start ():
switch Iris::recv.protected_data.l:
case ~0:
// alarm.
current_time = current_time - 1
if current_time:
current_time = current_time - 1
if !current_time:
do_alarm ()
ticking = false

View File

@@ -137,7 +137,7 @@ extern "C":
Iris::recv.reply = Iris::alloc_cap ()
Iris::recv.arg = Iris::alloc_cap ()
Iris::Num ret = start ()
Iris::my_parent.invoke (~0, ret)
Iris::my_parent.exit (ret)
Iris::my_memory.destroy (Iris::my_thread)
// The program no longer exists. If it somehow does, generate an address fault.
while true:

View File

@@ -25,6 +25,7 @@ enum ins:
static Iris::Display display
static Iris::Buzzer buzzer
static unsigned *framebuffer
static unsigned alarming
enum PD:
UI
@@ -151,38 +152,57 @@ static void draw_time (bool upper, unsigned time):
draw_num (upper, 3, time / 10)
draw_num (upper, 4, time % 10)
static void beep ():
buzzer.beep (4 * 440, 1000, ~0)
static void do_alarm ():
static unsigned tune[] = { 4, 6, 6, 5, 7, 7 }
if ++alarming > sizeof (tune) / sizeof (*tune):
alarming = 1
buzzer.beep (tune[alarming - 1] * 220, 300, ~0)
Iris::my_receiver.set_alarm (HZ / 3)
Iris::Num start ():
Iris::my_parent.init_done ()
display = Iris::my_parent.get_capability <Iris::Display> ()
Iris::Setting bright = Iris::my_parent.get_capability <Iris::Setting> ()
Iris::Keyboard keyboard = Iris::my_parent.get_capability <Iris::Keyboard> ()
buzzer = Iris::my_parent.get_capability <Iris::Buzzer> ()
Iris::UI app = Iris::my_parent.get_capability <Iris::UI> ()
Iris::Cap cb = Iris::my_receiver.create_capability (UI)
framebuffer = (unsigned *)0x15000
Iris::Caps fb = display.map_fb ((unsigned)framebuffer)
bright.set (bright.get_range ())
unsigned screen_max = bright.get_range ()
bright.set (0)
bool screen_on = false
Iris::Cap cb = Iris::my_receiver.create_capability (UI)
app.get_state (cb.copy ())
Iris::free_cap (cb)
cb = Iris::my_receiver.create_capability (KBD)
keyboard.set_cb (cb.copy ())
Iris::free_cap (cb)
draw_num (false, 2, 10)
draw_num (true, 2, 10)
unsigned total_time = 0
alarming = 0
while true:
Iris::wait ()
switch Iris::recv.protected_data.l:
case ~0:
// Alarm.
if alarming:
do_alarm ()
break
case UI:
switch Iris::recv.data[0].l:
case CURRENT_TIME:
draw_time (false, Iris::recv.data[1].l)
break
case ALARM:
beep ()
do_alarm ()
break
case TOTAL_TIME | Iris::UI::INPUT:
total_time = Iris::recv.data[1].l
@@ -194,6 +214,7 @@ Iris::Num start ():
case KBD:
if Iris::recv.data[0].l & Iris::Keyboard::RELEASE:
break
alarming = 0
switch Iris::recv.data[0].l:
case Key::VOLUME_UP:
total_time += 60
@@ -236,3 +257,11 @@ Iris::Num start ():
break
case Key::ENTER:
app.event (START)
break
case Key::BACKSPACE:
screen_on = !screen_on
if screen_on:
bright.set (screen_max)
else:
bright.set (0)
break

View File

@@ -269,6 +269,7 @@ static Type types[] = {
{ "String", 6, Iris::String::ID },
{ "WString", 7, Iris::WString::ID },
{ "Device", 6, Iris::Device::ID },
{ "Event", 5, Iris::Event::ID },
{ "Parent", 6, Iris::Parent::ID },
{ "Keyboard", 8, Iris::Keyboard::ID },
{ "Buzzer", 6, Iris::Buzzer::ID },

199
source/nand.ccp Normal file
View File

@@ -0,0 +1,199 @@
#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"
// The following defines are taken from mtd/nand.h in the Linux source.
// Standard NAND flash commands
#define CMD_READ0 0
#define CMD_READ1 1
#define CMD_RNDOUT 5
#define CMD_PAGEPROG 0x10
#define CMD_READOOB 0x50
#define CMD_ERASE1 0x60
#define CMD_STATUS 0x70
#define CMD_STATUS_MULTI 0x71
#define CMD_SEQIN 0x80
#define CMD_RNDIN 0x85
#define CMD_READID 0x90
#define CMD_ERASE2 0xd0
#define CMD_RESET 0xff
// Extended commands for large page devices
#define CMD_READSTART 0x30
#define CMD_RNDOUTSTART 0xE0
#define CMD_CACHEDPROG 0x15
// Extended commands for AG-AND device
// Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
// there is no way to distinguish that from NAND_CMD_READ0
// until the remaining sequence of commands has been completed
// so add a high order bit and mask it off in the command.
#define CMD_DEPLETE1 0x100
#define CMD_DEPLETE2 0x38
#define CMD_STATUS_MULTI 0x71
#define CMD_STATUS_ERROR 0x72
// multi-bank error status (banks 0-3)
#define CMD_STATUS_ERROR0 0x73
#define CMD_STATUS_ERROR1 0x74
#define CMD_STATUS_ERROR2 0x75
#define CMD_STATUS_ERROR3 0x76
#define CMD_STATUS_RESET 0x7f
#define CMD_STATUS_CLEAR 0xff
#define CMD_NONE -1
// Status bits
#define STATUS_FAIL 0x01
#define STATUS_FAIL_N1 0x02
#define STATUS_TRUE_READY 0x20
#define STATUS_READY 0x40
#define STATUS_WP 0x80
static volatile char *command
static volatile char *address
static volatile char *data
static unsigned page_bits
static unsigned redundant_bits
static unsigned block_bits
static unsigned word_size
static void unbusy ():
while !(gpio_get_port (2) & (1 << 30)):
Iris::schedule ()
static void reset ():
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)
// Set up.
gpio_as_nand ()
EMC_NFCSR = EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1
// Reset nand.
*command = CMD_RESET
unbusy ()
*command = CMD_READID
*address = 0
unsigned d = *data
//unsigned maker = d
d = *data
//unsigned device = d
d = *data
//unsigned internal_chip_number = 1 << (d & 0x3)
//unsigned cell_type = 2 << ((d >> 2) & 0x3)
//unsigned simultaneously_programmed_pages = 1 << ((d >> 4) & 0x3)
//bool can_interleave_program_between_chips = d & 0x40
//bool can_cache_program = d & 0x80
d = *data
page_bits = 10 + (d & 3)
kdebug ("page bits: ")
kdebug_num (page_bits)
kdebug ("\n")
redundant_bits = (d & 4 ? 4 : 3)
block_bits = 64 + ((d >> 4) & 3)
word_size = (d & 0x40 ? 16 : 8)
//unsigned serial_access_minimum = (d & 0x80 ? 25 : 50)
d = *data
//unsigned num_planes = 1 << ((d >> 2) & 3)
//unsigned plane_bits = 26 + ((d >> 4) & 7)
static void read (unsigned a, char *buffer):
unsigned column = a & ((1 << page_bits) - 1)
unsigned row = a >> page_bits
kdebug ("reading: ")
kdebug_num (a)
kdebug ("/")
kdebug_num (row)
kdebug ("/")
kdebug_num (column)
kdebug (": ")
*command = CMD_READ0
*address = column
*address = column >> 8
*address = row
*address = row >> 8
*address = row >> 16
*command = CMD_READSTART
EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_RS | EMC_NFECR_RS_DECODING | EMC_NFECR_ERST
// Wait for nand to be ready.
unbusy ()
for unsigned t = 0; t < 0x200; ++t:
buffer[t] = *data
char error[9]
unsigned errcol = (1 << page_bits) + (column >> 5)
*command = CMD_RNDOUT
*address = errcol
*address = errcol >> 8
*command = CMD_RNDOUTSTART
for unsigned t = 0; t < 9; ++t:
error[t] = *data
EMC_NFPAR (0) = ((unsigned *)error)[0]
EMC_NFPAR (1) = ((unsigned *)error)[1]
EMC_NFPAR (2) = error[9]
EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_RS | EMC_NFECR_RS_DECODING | EMC_NFECR_PRDY
while !(EMC_NFINTS & EMC_NFINTS_DECF):
Iris::schedule ()
// delay...
//Iris::schedule ()
unsigned errs = (EMC_NFINTS & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT
Iris::Num start ():
for unsigned i = 0; i < 30; ++i:
Iris::schedule ()
map_emc ()
map_gpio ()
command = (volatile char *)0x15000
address = (volatile char *)0x16000
data = (volatile char *)0x17000
reset ()
char buffer[0x200]
// Send nand contents to serial port.
for unsigned a = 0; a < 0x4000; a += 0x200:
read (a, buffer)
//for unsigned s = 0; s < 0x10; ++s:
for unsigned t = 0; t < 0x10; ++t:
kdebug (" ")
kdebug_num (buffer[0 * 0x20 + t], 2)
kdebug ("\n")
//kdebug ("\n")
// Exit.
return 0

View File

@@ -165,22 +165,24 @@ unsigned const DevKbd::keys[NUM_KEYS] = {
#endif
}
class PowerButton:
class Event:
bool state, started
unsigned pin
Iris::Cap cb
public:
void scan ():
if !started:
return
gpio_mask_irq (3, 1 << 29)
bool s = gpio_get_port (3) & (1 << 29)
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 << 29, !state, true)
gpio_unmask_irq (3, 1 << 29)
PowerButton ():
gpio_as_gpio (3, 29)
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):
@@ -195,19 +197,24 @@ class PowerButton:
enum codes:
KBD_DEV = 32
PWR
SDMMC
Iris::Num start ():
map_gpio ()
DevKbd kbd
PowerButton pwr
Event pwr (29)
Event sdmmc (0)
Iris::Device dev = Iris::my_receiver.create_capability (KBD_DEV)
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)
@@ -225,6 +232,7 @@ Iris::Num start ():
// Interrupt.
pwr.scan ()
kbd.scan ()
sdmmc.scan ()
if kbd.scanning ():
Iris::my_receiver.set_alarm (SCAN_INTERVAL)
Iris::register_interrupt (IRQ_GPIO3)
@@ -235,7 +243,7 @@ Iris::Num start ():
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Keyboard::SET_CB:
case Iris::Event::SET_CB:
Iris::Cap reply = Iris::get_reply ()
kbd.active (Iris::get_arg ())
reply.invoke ()
@@ -256,7 +264,7 @@ Iris::Num start ():
case Iris::Device::RESET:
Iris::recv.reply.invoke ()
break
case Iris::Keyboard::SET_CB:
case Iris::Event::SET_CB:
Iris::Cap reply = Iris::get_reply ()
pwr.set_cb (Iris::get_arg ())
reply.invoke ()
@@ -268,6 +276,23 @@ Iris::Num start ():
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)

184
source/sd+mmc.ccp Normal file
View File

@@ -0,0 +1,184 @@
#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:
static unsigned const PORT = 3
static unsigned const PIN = 2
bool check_sdio ()
void check_sdmem ()
void check_mmc ()
public:
void reset ()
void detect ()
void release ()
void interrupt ()
void Mmc::reset ():
// Enable slow clock to msc.
CPM_MSCCDR = ~0
cpm_start_msc ()
// Enable msc pins.
gpio_as_msc ()
// Disable power to card.
gpio_as_gpio (PORT, 1 << PIN)
gpio_as_output (PORT, 1 << PIN)
gpio_disable_pull (PORT, 1 << PIN)
gpio_set (PORT, 1 << PIN)
// Stop the clock.
MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP
while MSC_STAT & MSC_STAT_CLK_EN:
Iris::schedule ()
// Initialize registers.
MSC_CLKRT = MSC_CLKRT_CLK_RATE_DIV_128
MSC_RESTO = 64
MSC_RDTO = ~0
MSC_BLKLEN = 0x200
MSC_NOB = 0
MSC_IMASK = ~0
MSC_ARG = 0
// Reset controller and inserted devices.
MSC_STRPCL = MSC_STRPCL_RESET
while MSC_STAT & MSC_STAT_IS_RESETTING:
Iris::schedule ()
// Start the clock.
MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START
// Set cards, if any, to idle.
MSC_CMD = 0
MSC_CMDAT = MSC_CMDAT_RESPONSE_NONE
msc_start_op ()
while !msc_ireg_end_cmd_res ():
Iris::schedule ()
msc_ireg_clear_end_cmd_res ()
// Reset SDIO device, if any.
MSC_CMD = 52
MSC_ARG = 0x88000C08
MSC_CMDAT = MSC_CMDAT_RESPONSE_R5
msc_start_op ()
while !msc_ireg_end_cmd_res ():
Iris::schedule ()
msc_ireg_clear_end_cmd_res ()
bool Mmc::check_sdio ():
// 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.
// 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.
// 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.
// 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.
// 10. Go to check MMC, because we can assure that there is no SDMEM card.
return false
void Mmc::check_sdmem ():
// 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.
// 4. Send ACMD41 (SD_SEND_OP_CMD) to validate voltage (the general OCR value is 0x00FF8000).
// 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.
// 7. Send CMD2 (ALL_SEND_CID) to get the card 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.
// 10. Go to check MMC.
void Mmc::check_mmc ():
// 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.
// 8. If there are other MMC cards, then go to 5.
void Mmc::detect ():
kdebug ("mmc detect\n")
gpio_clear (PORT, 1 << PIN)
if check_sdio ():
check_sdmem ()
check_mmc ()
void Mmc::release ():
kdebug ("mmc release\n")
gpio_set (PORT, 1 << PIN)
void Mmc::interrupt ():
kdebug ("mmc interrupt\n")
enum types:
DETECT
REQUEST
Iris::Num start ():
map_msc ()
map_gpio ()
map_cpm ()
Mmc mmc
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::WString> (cap.copy ())
Iris::free_cap (cap)
Iris::my_parent.init_done ()
while true:
Iris::wait ()
switch Iris::recv.protected_data.l:
case DETECT:
if Iris::recv.data[0].l:
mmc.detect ()
else:
mmc.release ()
break
case IRQ_MSC:
mmc.interrupt ()
break
case REQUEST:
kdebug ("sd+mmc request\n")
break
default:
Iris::panic (0, "unexpected request source for sd+mmc")