rockbox/firmware/drivers/ata_mmc.c

309 lines
6.8 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Alan Korr
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdbool.h>
#include "ata.h"
#include "kernel.h"
#include "thread.h"
#include "led.h"
#include "sh7034.h"
#include "system.h"
#include "debug.h"
#include "panic.h"
#include "usb.h"
#include "power.h"
#include "string.h"
#include "hwcompat.h"
#include "adc.h"
/* use file for an MMC-based system, FIXME in makefile */
#ifdef HAVE_MMC
#define SECTOR_SIZE 512
#define Q_SLEEP 0
/* for compatibility */
bool old_recorder = false; /* FIXME: get rid of this cross-dependency */
int ata_spinup_time = 0;
static int sleep_timeout = 5*HZ;
char ata_device = 0; /* device 0 (master) or 1 (slave) */
int ata_io_address = 0; /* 0x300 or 0x200, only valid on recorder */
static unsigned short identify_info[SECTOR_SIZE];
static struct mutex ata_mtx;
static bool sleeping = true;
static char ata_stack[DEFAULT_STACK_SIZE];
static const char ata_thread_name[] = "ata";
static struct event_queue ata_queue;
static bool initialized = false;
static bool delayed_write = false;
static unsigned char delayed_sector[SECTOR_SIZE];
static int delayed_sector_num;
static long last_user_activity = -1;
long last_disk_activity = -1;
int ata_read_sectors(unsigned long start,
int incount,
void* inbuf)
{
int ret = 0;
mutex_lock(&ata_mtx);
last_disk_activity = current_tick;
led(true);
sleeping = false;
/* ToDo: action */
(void)start;
(void)incount;
(void)inbuf;
led(false);
mutex_unlock(&ata_mtx);
/* only flush if reading went ok */
if ( (ret == 0) && delayed_write )
ata_flush();
return ret;
}
int ata_write_sectors(unsigned long start,
int count,
const void* buf)
{
int ret = 0;
if (start == 0)
panicf("Writing on sector 0\n");
mutex_lock(&ata_mtx);
sleeping = false;
last_disk_activity = current_tick;
led(true);
/* ToDo: action */
(void)start;
(void)count;
(void)buf;
led(false);
mutex_unlock(&ata_mtx);
/* only flush if writing went ok */
if ( (ret == 0) && delayed_write )
ata_flush();
return ret;
}
extern void ata_delayed_write(unsigned long sector, const void* buf)
{
memcpy(delayed_sector, buf, SECTOR_SIZE);
delayed_sector_num = sector;
delayed_write = true;
}
extern void ata_flush(void)
{
if ( delayed_write ) {
DEBUGF("ata_flush()\n");
delayed_write = false;
ata_write_sectors(delayed_sector_num, 1, delayed_sector);
}
}
void ata_spindown(int seconds)
{
sleep_timeout = seconds * HZ;
}
bool ata_disk_is_active(void)
{
return !sleeping;
}
static int ata_perform_sleep(void)
{
int ret = 0;
mutex_lock(&ata_mtx);
/* ToDo: is there an equivalent? */
sleeping = true;
mutex_unlock(&ata_mtx);
return ret;
}
int ata_standby(int time)
{
int ret = 0;
mutex_lock(&ata_mtx);
/* ToDo: is there an equivalent? */
(void)time;
mutex_unlock(&ata_mtx);
return ret;
}
int ata_sleep(void)
{
queue_post(&ata_queue, Q_SLEEP, NULL);
return 0;
}
void ata_spin(void)
{
last_user_activity = current_tick;
}
static void ata_thread(void)
{
static long last_sleep = 0;
struct event ev;
while (1) {
while ( queue_empty( &ata_queue ) ) {
if ( sleep_timeout && !sleeping &&
TIME_AFTER( current_tick,
last_user_activity + sleep_timeout ) &&
TIME_AFTER( current_tick,
last_disk_activity + sleep_timeout ) )
{
ata_perform_sleep();
last_sleep = current_tick;
}
sleep(HZ/4);
}
queue_wait(&ata_queue, &ev);
switch ( ev.id ) {
#ifndef USB_NONE
case SYS_USB_CONNECTED:
/* Tell the USB thread that we are safe */
DEBUGF("ata_thread got SYS_USB_CONNECTED\n");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
/* Wait until the USB cable is extracted again */
usb_wait_for_disconnect(&ata_queue);
break;
#endif
case Q_SLEEP:
last_disk_activity = current_tick - sleep_timeout + (HZ/2);
break;
}
}
}
/* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */
int ata_hard_reset(void)
{
int ret = 0;
return ret;
}
int ata_soft_reset(void)
{
int ret = 0;
return ret;
}
void ata_enable(bool on)
{
PBCR1 &= ~0x0CC0; /* PB13 and TxD1 become GPIOs, if not modified below */
if (on)
{
/* serial setup */
PBCR1 |= 0x0880; /* as SCK1, TxD1 */
}
else
{
PBDR |= 0x2000; /* drive PB13 high */
PBIOR |= 0x2000; /* output PB13 */
PBIOR &= ~0x0800; /* high impedance for TxD1 GPIO */
PADR |= 0x0680; /* set all the selects+reset high (=inactive) */
PADR &= ~0x0080; /* assert reset */
sleep(1);
PADR |= 0x0080; /* de-assert reset */
}
}
unsigned short* ata_get_identify(void)
{
return identify_info;
}
int ata_init(void)
{
int rc = 0;
mutex_init(&ata_mtx);
led(false);
/* Port setup */
PADR |= 0x0680; /* set all the selects + reset high (=inactive) */
PAIOR |= 0x1680; /* make outputs for them and the PA12 clock gate */
if(adc_read(ADC_MMC_SWITCH) < 0x200)
{ /* MMC inserted */
PADR &= ~0x1000; /* clear PA12 */
PADR &= ~0x0200; /* chip select external flash */
}
else
{ /* no MMC, use internal memory */
PADR |= 0x1000; /* set PA12 */
PADR &= ~0x0400; /* chip select internal flash */
}
sleeping = false;
ata_enable(true);
if ( !initialized ) {
queue_init(&ata_queue);
last_disk_activity = current_tick;
create_thread(ata_thread, ata_stack,
sizeof(ata_stack), ata_thread_name);
initialized = true;
}
return rc;
}
#endif /* #ifdef HAVE_MMC */