2012-12-04 15:52:15 +02:00
|
|
|
/*
|
|
|
|
* fw/mmc.c - MMC card access
|
|
|
|
*
|
|
|
|
* Written 2012 by Werner Almesberger
|
|
|
|
* Copyright 2012 Werner Almesberger
|
|
|
|
*
|
|
|
|
* 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Heavily influenced by the MMC driver in the Mass Storage for V-USB project
|
|
|
|
* by "lleeloo":
|
|
|
|
* http://we.easyelectronics.ru/AVR/usb-fleshka-na-atmega8-i-v-usb-ot-idei-k-gotovomu-ustroystvu.html
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
2012-12-05 16:34:31 +02:00
|
|
|
#include "delay.h"
|
2012-12-04 15:52:15 +02:00
|
|
|
#include "mmc-hw.h"
|
|
|
|
#include "mmc.h"
|
|
|
|
|
|
|
|
|
2012-12-05 18:22:36 +02:00
|
|
|
#ifdef AVR
|
|
|
|
|
|
|
|
#define DEBUG(fmt, ...)
|
|
|
|
|
|
|
|
#else /* AVR */
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#define DEBUG(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__)
|
|
|
|
|
|
|
|
#endif /* !AVR */
|
|
|
|
|
|
|
|
|
2012-12-04 15:52:15 +02:00
|
|
|
/* Command indices */
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MMC_GO_IDLE_STATE = 0,
|
|
|
|
MMC_SEND_OP_COND = 1,
|
|
|
|
MMC_SET_BLOCKLEN = 16,
|
|
|
|
MMC_READ_SINGLE_BLOCK = 17,
|
|
|
|
MMC_WRITE_BLOCK = 24,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Start block tokens */
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MMC_START_SINGLE_BLOCK = 0xfe,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void mmc_begin(void)
|
|
|
|
{
|
|
|
|
mmc_select();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void mmc_end(void)
|
|
|
|
{
|
|
|
|
mmc_send(0xff);
|
|
|
|
mmc_send(0xff);
|
|
|
|
mmc_deselect();
|
|
|
|
mmc_send(0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mmc_command(uint8_t cmd, uint32_t arg)
|
|
|
|
{
|
|
|
|
mmc_send(0x40 | cmd); /* start (0)+transmission(1)+cmd */
|
|
|
|
mmc_send(arg >> 24);
|
|
|
|
mmc_send(arg >> 16);
|
|
|
|
mmc_send(arg >> 8);
|
|
|
|
mmc_send(arg);
|
|
|
|
mmc_send(0x95); /* crc7+end(1) */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint8_t mmc_r1(void)
|
|
|
|
{
|
|
|
|
uint8_t v, tries = 0xff;
|
|
|
|
|
2012-12-05 15:59:54 +02:00
|
|
|
do v = mmc_recv();
|
2012-12-04 15:52:15 +02:00
|
|
|
while ((v & 0x80) && --tries);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-05 16:01:36 +02:00
|
|
|
static bool mmc_wait(void)
|
2012-12-04 15:52:15 +02:00
|
|
|
{
|
|
|
|
uint8_t v, tries = 0xff;
|
|
|
|
|
|
|
|
do {
|
2012-12-05 15:59:54 +02:00
|
|
|
v = mmc_recv();
|
2012-12-04 15:52:15 +02:00
|
|
|
if (v == MMC_START_SINGLE_BLOCK)
|
2012-12-05 16:01:36 +02:00
|
|
|
return 1;
|
2012-12-05 16:34:31 +02:00
|
|
|
_delay_ms(1);
|
2012-12-04 15:52:15 +02:00
|
|
|
}
|
|
|
|
while (--tries);
|
2012-12-05 16:01:36 +02:00
|
|
|
return 0;
|
2012-12-04 15:52:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-05 18:23:54 +02:00
|
|
|
bool mmc_begin_read(uint32_t addr)
|
2012-12-04 15:52:15 +02:00
|
|
|
{
|
2012-12-05 18:22:36 +02:00
|
|
|
uint8_t res;
|
|
|
|
|
2012-12-04 15:52:15 +02:00
|
|
|
mmc_begin();
|
2012-12-05 18:23:54 +02:00
|
|
|
mmc_command(MMC_READ_SINGLE_BLOCK, addr);
|
2012-12-05 18:22:36 +02:00
|
|
|
res = mmc_r1();
|
|
|
|
DEBUG("mmc_begin_read: r1 = 0x%02x", res);
|
|
|
|
if (res) {
|
2012-12-04 15:52:15 +02:00
|
|
|
mmc_end();
|
|
|
|
return 0;
|
|
|
|
} else {
|
2012-12-05 16:01:36 +02:00
|
|
|
return mmc_wait();
|
2012-12-04 15:52:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t mmc_read(void)
|
|
|
|
{
|
|
|
|
return mmc_recv();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool mmc_end_read(void)
|
|
|
|
{
|
|
|
|
mmc_end();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-05 18:23:54 +02:00
|
|
|
bool mmc_begin_write(uint32_t addr)
|
2012-12-04 15:52:15 +02:00
|
|
|
{
|
2012-12-05 18:22:36 +02:00
|
|
|
uint8_t res;
|
|
|
|
|
2012-12-04 15:52:15 +02:00
|
|
|
mmc_begin();
|
2012-12-05 18:23:54 +02:00
|
|
|
mmc_command(MMC_WRITE_BLOCK, addr);
|
2012-12-05 18:22:36 +02:00
|
|
|
res = mmc_r1();
|
|
|
|
DEBUG("mmc_begin_write: r1 = 0x%02x", res);
|
|
|
|
if (res) {
|
2012-12-04 15:52:15 +02:00
|
|
|
mmc_end();
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
mmc_send(0xff);
|
|
|
|
mmc_send(0xff);
|
|
|
|
mmc_send(MMC_START_SINGLE_BLOCK);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void mmc_write(uint8_t data)
|
|
|
|
{
|
|
|
|
mmc_send(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool mmc_end_write(void)
|
|
|
|
{
|
|
|
|
mmc_send(0xff);
|
|
|
|
mmc_send(0xff);
|
|
|
|
mmc_recv();
|
|
|
|
while (!mmc_recv());
|
|
|
|
mmc_end();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void mmc_off(void)
|
|
|
|
{
|
|
|
|
mmc_deactivate();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void send_clock(void)
|
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
for (i = 0; i != 10; i++)
|
|
|
|
mmc_send(0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool mmc_init(void)
|
|
|
|
{
|
|
|
|
uint16_t tries = 0xff;
|
|
|
|
uint8_t res;
|
|
|
|
|
|
|
|
mmc_activate();
|
|
|
|
send_clock();
|
|
|
|
mmc_begin();
|
|
|
|
|
|
|
|
do {
|
|
|
|
mmc_command(MMC_GO_IDLE_STATE, 0);
|
|
|
|
if (mmc_r1() == 0x01) /* in idle state */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
while (--tries);
|
|
|
|
mmc_end();
|
|
|
|
if (!tries)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
send_clock();
|
|
|
|
|
|
|
|
tries = 0x7ff;
|
|
|
|
do {
|
|
|
|
mmc_select();
|
|
|
|
mmc_command(MMC_SEND_OP_COND, 0);
|
|
|
|
res = mmc_r1();
|
|
|
|
mmc_deselect();
|
|
|
|
send_clock();
|
|
|
|
}
|
|
|
|
while (res && --tries);
|
|
|
|
|
|
|
|
mmc_end();
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
mmc_deactivate();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mmc_begin();
|
|
|
|
mmc_command(MMC_SET_BLOCKLEN, MMC_BLOCK);
|
|
|
|
mmc_r1();
|
|
|
|
mmc_end();
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|