rockbox/firmware/drivers/ata_flash.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

501 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 Tomasz Malesinski
*
* 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "ata.h"
#include <stdbool.h>
#include <string.h>
#if CONFIG_CPU == PNX0101
#include "pnx0101.h"
#endif
/*
#include "kernel.h"
#include "thread.h"
#include "led.h"
#include "cpu.h"
#include "system.h"
#include "debug.h"
#include "panic.h"
#include "usb.h"
#include "power.h"
#include "string.h"
*/
#define SECTOR_SIZE (512)
static unsigned short identify_info[SECTOR_SIZE];
int ata_spinup_time = 0;
long last_disk_activity = -1;
#if CONFIG_FLASH == FLASH_IFP7XX
static unsigned char flash_ce[4] = {0x20, 0x02, 0x10, 0x08};
#define FLASH_IO_BASE 0x28000000
#define FLASH_REG_DATA (*((volatile unsigned char*)(FLASH_IO_BASE)))
#define FLASH_REG_CMD (*((volatile unsigned char*)(FLASH_IO_BASE + 4)))
#define FLASH_REG_ADDR (*((volatile unsigned char*)(FLASH_IO_BASE + 8)))
#define SEGMENT_SIZE 1000
#define MAX_N_SEGMENTS 8
#endif
#define FLASH_MODEL_NONE 0
#define FLASH_MODEL_256 1
#define FLASH_MODEL_512 2
struct flash_disk
{
unsigned short block_map[MAX_N_SEGMENTS][SEGMENT_SIZE];
short cur_block;
int cur_phblock_start;
int n_chips;
unsigned char chip_no[4];
unsigned char model;
};
static struct flash_disk flash_disk;
void flash_select_chip(int no, int sel)
{
#if CONFIG_FLASH == FLASH_IFP7XX
if (sel)
GPIO5_CLR = flash_ce[no];
else
GPIO5_SET = flash_ce[no];
#endif
}
static inline unsigned char flash_read_data(void)
{
return FLASH_REG_DATA;
}
static inline void flash_write_data(unsigned char data)
{
FLASH_REG_DATA = data;
}
/* TODO: these two doesn't work when inlined, probably some
delay is required */
static void flash_write_cmd(unsigned char cmd)
{
FLASH_REG_CMD = cmd;
}
static void flash_write_addr(unsigned char addr)
{
FLASH_REG_ADDR = addr;
}
static void flash_wait_ready(void)
{
int i;
for (i = 0; i < 5; i++)
while ((GPIO6_READ & 8) == 0);
}
static unsigned char model_n_sectors_order[] = {0, 19, 20};
int flash_map_sector(int sector, int* chip, int* chip_sector)
{
int ord, c;
if (flash_disk.model == FLASH_MODEL_NONE)
return -1;
ord = model_n_sectors_order[flash_disk.model];
c = sector >> ord;
*chip_sector = sector & ((1 << ord) - 1);
if (c >= flash_disk.n_chips)
return -1;
*chip = flash_disk.chip_no[c];
return 0;
}
int flash_read_id(int no) {
int id;
flash_select_chip(no, 1);
flash_write_cmd(0x90);
flash_write_addr(0);
flash_read_data();
id = flash_read_data();
flash_select_chip(no, 0);
return id;
}
int flash_read_sector(int sector, unsigned char* buf,
unsigned char* oob)
{
unsigned long *bufl = (unsigned long *)buf;
int chip, chip_sector;
int i;
if (flash_map_sector(sector, &chip, &chip_sector) < 0)
return -1;
flash_select_chip(chip, 1);
flash_write_cmd(0x00);
flash_write_addr(0);
flash_write_addr((chip_sector << 1) & 7);
flash_write_addr((chip_sector >> 2) & 0xff);
flash_write_addr((chip_sector >> 10) & 0xff);
flash_write_addr((chip_sector >> 18) & 0xff);
flash_write_cmd(0x30);
flash_wait_ready();
if ((unsigned long)buf & 3)
{
for (i = 0; i < 512; i++)
buf[i] = flash_read_data();
}
else
{
for (i = 0; i < 512 / 4; i++) {
unsigned long v;
#ifdef ROCKBOX_LITTLE_ENDIAN
v = flash_read_data();
v |= (unsigned long)flash_read_data() << 8;
v |= (unsigned long)flash_read_data() << 16;
v |= (unsigned long)flash_read_data() << 24;
#else
v = (unsigned long)flash_read_data() << 24;
v |= (unsigned long)flash_read_data() << 16;
v |= (unsigned long)flash_read_data() << 8;
v |= flash_read_data();
#endif
bufl[i] = v;
}
}
flash_write_cmd(0x05);
flash_write_addr((chip_sector & 3) * 0x10);
flash_write_addr(8);
flash_write_cmd(0xe0);
for (i = 0; i < 16; i++)
oob[i] = flash_read_data();
flash_select_chip(chip, 0);
return 0;
}
int flash_read_sector_oob(int sector, unsigned char* oob)
{
int chip, chip_sector;
int i;
if (flash_map_sector(sector, &chip, &chip_sector) < 0)
return -1;
flash_select_chip(chip, 1);
flash_write_cmd(0x00);
flash_write_addr((chip_sector & 3) * 0x10);
flash_write_addr(8);
flash_write_addr((chip_sector >> 2) & 0xff);
flash_write_addr((chip_sector >> 10) & 0xff);
flash_write_addr((chip_sector >> 18) & 0xff);
flash_write_cmd(0x30);
flash_wait_ready();
for (i = 0; i < 16; i++)
oob[i] = flash_read_data();
flash_select_chip(chip, 0);
return 0;
}
static unsigned char model_n_segments[] = {0, 2, 4};
static inline int flash_get_n_segments(void)
{
return model_n_segments[flash_disk.model] * flash_disk.n_chips;
}
static inline int flash_get_n_phblocks(void)
{
return 1024;
}
static int model_n_sectors_in_block[] = {0, 256, 256};
static int flash_get_n_sectors_in_block(void)
{
return model_n_sectors_in_block[flash_disk.model];
}
static int flash_phblock_to_sector(int segment, int block)
{
return (segment * flash_get_n_phblocks() + block)
* flash_get_n_sectors_in_block();
}
static int flash_is_bad_block(unsigned char* oob)
{
/* TODO: should we check two pages? (see datasheet) */
return oob[0] != 0xff;
}
static int count_1(int n) {
int r = 0;
while (n != 0) {
r += (n & 1);
n >>= 1;
}
return r;
}
static int flash_get_logical_block_no(unsigned char* oob)
{
int no1, no2;
no1 = oob[6] + (oob[7] << 8);
no2 = oob[11] + (oob[12] << 8);
if (no1 == no2 && (no1 & 0xf000) == 0x1000)
return (no1 & 0xfff) >> 1;
if (count_1(no1 ^ no2) > 1)
return -1;
if ((no1 & 0xf000) == 0x1000
&& (count_1(no1) & 1) == 0)
return (no1 & 0xfff) >> 1;
if ((no2 & 0xf000) == 0x1000
&& (count_1(no2) & 1) == 0)
return (no2 & 0xfff) >> 1;
return -1;
}
int flash_disk_scan(void)
{
int n_segments, n_phblocks;
unsigned char oob[16];
int s, b;
/* TODO: checking for double blocks */
n_segments = flash_get_n_segments();
n_phblocks = flash_get_n_phblocks();
flash_disk.cur_block = -1;
flash_disk.cur_phblock_start = -1;
for (s = 0; s < n_segments; s++)
{
for (b = 0; b < n_phblocks; b++)
{
int r;
r = flash_read_sector_oob(flash_phblock_to_sector(s, b),
oob);
if (r >= 0 && !flash_is_bad_block(oob))
{
int lb;
lb = flash_get_logical_block_no(oob);
if (lb >= 0 && lb < SEGMENT_SIZE)
flash_disk.block_map[s][lb] = b;
}
}
}
return 0;
}
int flash_disk_find_block(int block)
{
int seg, bmod, phb;
unsigned char oob[16];
int r;
if (block >= SEGMENT_SIZE * flash_get_n_segments())
return -1;
if (block == flash_disk.cur_block)
return flash_disk.cur_phblock_start;
seg = block / SEGMENT_SIZE;
bmod = block % SEGMENT_SIZE;
phb = flash_disk.block_map[seg][bmod];
r = flash_read_sector_oob(flash_phblock_to_sector(seg, phb), oob);
if (r < 0)
return -1;
if (flash_is_bad_block(oob))
return -1;
if (flash_get_logical_block_no(oob) != bmod)
return -1;
flash_disk.cur_block = block;
flash_disk.cur_phblock_start = flash_phblock_to_sector(seg, phb);
return flash_disk.cur_phblock_start;
}
int flash_disk_read_sectors(unsigned long start,
int count,
void* buf)
{
int block, secmod, done;
int phb;
char oob[16];
block = start / flash_get_n_sectors_in_block();
secmod = start % flash_get_n_sectors_in_block();
phb = flash_disk_find_block(block);
done = 0;
while (count > 0 && secmod < flash_get_n_sectors_in_block())
{
if (phb >= 0)
flash_read_sector(phb + secmod, buf, oob);
else
memset(buf, 0, SECTOR_SIZE);
buf += SECTOR_SIZE;
count--;
secmod++;
done++;
}
return done;
}
int ata_read_sectors(IF_MV2(int drive,)
unsigned long start,
int incount,
void* inbuf)
{
while (incount > 0)
{
int done = flash_disk_read_sectors(start, incount, inbuf);
if (done < 0)
return -1;
start += done;
incount -= done;
inbuf += SECTOR_SIZE * done;
}
return 0;
}
int ata_write_sectors(IF_MV2(int drive,)
unsigned long start,
int count,
const void* buf)
{
(void)start;
(void)count;
(void)buf;
return -1;
}
/* schedule a single sector write, executed with the the next spinup
(volume 0 only, used for config sector) */
extern void ata_delayed_write(unsigned long sector, const void* buf)
{
(void)sector;
(void)buf;
}
/* write the delayed sector to volume 0 */
extern void ata_flush(void)
{
}
void ata_spindown(int seconds)
{
(void)seconds;
}
bool ata_disk_is_active(void)
{
return 0;
}
void ata_sleep(void)
{
}
void ata_spin(void)
{
}
/* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */
int ata_hard_reset(void)
{
return 0;
}
int ata_soft_reset(void)
{
return 0;
}
void ata_enable(bool on)
{
(void)on;
}
unsigned short* ata_get_identify(void)
{
return identify_info;
}
int ata_init(void)
{
int i, id, id2;
id = flash_read_id(0);
switch (id)
{
case 0xda:
flash_disk.model = FLASH_MODEL_256;
break;
case 0xdc:
flash_disk.model = FLASH_MODEL_512;
break;
default:
flash_disk.model = FLASH_MODEL_NONE;
return -1;
}
flash_disk.n_chips = 1;
flash_disk.chip_no[0] = 0;
for (i = 1; i < 4; i++)
{
id2 = flash_read_id(i);
if (id2 == id)
flash_disk.chip_no[flash_disk.n_chips++] = i;
}
if (flash_disk_scan() < 0)
return -2;
return 0;
}