| /* | 
 |  * (C) Copyright 2009 mGine co. | 
 |  * unsik Kim <donari75@gmail.com> | 
 |  * | 
 |  * See file CREDITS for list of people who contributed to this | 
 |  * project. | 
 |  * | 
 |  * 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 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, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
 |  * MA 02111-1307 USA | 
 |  */ | 
 |  | 
 | #include <common.h> | 
 | #include <malloc.h> | 
 | #include <part.h> | 
 | #include <ata.h> | 
 | #include <asm/io.h> | 
 | #include "mg_disk_prv.h" | 
 |  | 
 | #ifndef CONFIG_MG_DISK_RES | 
 | #define CONFIG_MG_DISK_RES	0 | 
 | #endif | 
 |  | 
 | #define MG_RES_SEC ((CONFIG_MG_DISK_RES) << 1) | 
 |  | 
 | static struct mg_host host; | 
 |  | 
 | static inline u32 mg_base(void) | 
 | { | 
 | 	return host.drv_data->base; | 
 | } | 
 |  | 
 | static block_dev_desc_t mg_disk_dev = { | 
 | 	.if_type = IF_TYPE_ATAPI, | 
 | 	.part_type = PART_TYPE_UNKNOWN, | 
 | 	.type = DEV_TYPE_HARDDISK, | 
 | 	.blksz = MG_SECTOR_SIZE, | 
 | 	.priv = &host }; | 
 |  | 
 | static void mg_dump_status (const char *msg, unsigned int stat, unsigned err) | 
 | { | 
 | 	char *name = MG_DEV_NAME; | 
 |  | 
 | 	printf("%s: %s: status=0x%02x { ", name, msg, stat & 0xff); | 
 | 	if (stat & MG_REG_STATUS_BIT_BUSY) | 
 | 		printf("Busy "); | 
 | 	if (stat & MG_REG_STATUS_BIT_READY) | 
 | 		printf("DriveReady "); | 
 | 	if (stat & MG_REG_STATUS_BIT_WRITE_FAULT) | 
 | 		printf("WriteFault "); | 
 | 	if (stat & MG_REG_STATUS_BIT_SEEK_DONE) | 
 | 		printf("SeekComplete "); | 
 | 	if (stat & MG_REG_STATUS_BIT_DATA_REQ) | 
 | 		printf("DataRequest "); | 
 | 	if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR) | 
 | 		printf("CorrectedError "); | 
 | 	if (stat & MG_REG_STATUS_BIT_ERROR) | 
 | 		printf("Error "); | 
 | 	printf("}\n"); | 
 |  | 
 | 	if ((stat & MG_REG_STATUS_BIT_ERROR)) { | 
 | 		printf("%s: %s: error=0x%02x { ", name, msg, err & 0xff); | 
 | 		if (err & MG_REG_ERR_BBK) | 
 | 			printf("BadSector "); | 
 | 		if (err & MG_REG_ERR_UNC) | 
 | 			printf("UncorrectableError "); | 
 | 		if (err & MG_REG_ERR_IDNF) | 
 | 			printf("SectorIdNotFound "); | 
 | 		if (err & MG_REG_ERR_ABRT) | 
 | 			printf("DriveStatusError "); | 
 | 		if (err & MG_REG_ERR_AMNF) | 
 | 			printf("AddrMarkNotFound "); | 
 | 		printf("}\n"); | 
 | 	} | 
 | } | 
 |  | 
 | static unsigned int mg_wait (u32 expect, u32 msec) | 
 | { | 
 | 	u8 status; | 
 | 	u32 from, cur, err; | 
 |  | 
 | 	err = MG_ERR_NONE; | 
 | 	reset_timer(); | 
 | 	from = get_timer(0); | 
 |  | 
 | 	status = readb(mg_base() + MG_REG_STATUS); | 
 | 	do { | 
 | 		cur = get_timer(from); | 
 | 		if (status & MG_REG_STATUS_BIT_BUSY) { | 
 | 			if (expect == MG_REG_STATUS_BIT_BUSY) | 
 | 				break; | 
 | 		} else { | 
 | 			/* Check the error condition! */ | 
 | 			if (status & MG_REG_STATUS_BIT_ERROR) { | 
 | 				err = readb(mg_base() + MG_REG_ERROR); | 
 | 				mg_dump_status("mg_wait", status, err); | 
 | 				break; | 
 | 			} | 
 |  | 
 | 			if (expect == MG_STAT_READY) | 
 | 				if (MG_READY_OK(status)) | 
 | 					break; | 
 |  | 
 | 			if (expect == MG_REG_STATUS_BIT_DATA_REQ) | 
 | 				if (status & MG_REG_STATUS_BIT_DATA_REQ) | 
 | 					break; | 
 | 		} | 
 | 		status = readb(mg_base() + MG_REG_STATUS); | 
 | 	} while (cur < msec); | 
 |  | 
 | 	if (cur >= msec) | 
 | 		err = MG_ERR_TIMEOUT; | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static int mg_get_disk_id (void) | 
 | { | 
 | 	u16 id[(MG_SECTOR_SIZE / sizeof(u16))]; | 
 | 	hd_driveid_t *iop = (hd_driveid_t *)id; | 
 | 	u32 i, err, res; | 
 |  | 
 | 	writeb(MG_CMD_ID, mg_base() + MG_REG_COMMAND); | 
 | 	err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	for(i = 0; i < (MG_SECTOR_SIZE / sizeof(u16)); i++) | 
 | 		id[i] = readw(mg_base() + MG_BUFF_OFFSET + i * 2); | 
 |  | 
 | 	writeb(MG_CMD_RD_CONF, mg_base() + MG_REG_COMMAND); | 
 | 	err = mg_wait(MG_STAT_READY, 3000); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	ata_swap_buf_le16(id, MG_SECTOR_SIZE / sizeof(u16)); | 
 |  | 
 | 	if((iop->field_valid & 1) == 0) | 
 | 		return MG_ERR_TRANSLATION; | 
 |  | 
 | 	ata_id_c_string(id, (unsigned char *)mg_disk_dev.revision, | 
 | 			ATA_ID_FW_REV, sizeof(mg_disk_dev.revision)); | 
 | 	ata_id_c_string(id, (unsigned char *)mg_disk_dev.vendor, | 
 | 			ATA_ID_PROD, sizeof(mg_disk_dev.vendor)); | 
 | 	ata_id_c_string(id, (unsigned char *)mg_disk_dev.product, | 
 | 			ATA_ID_SERNO, sizeof(mg_disk_dev.product)); | 
 |  | 
 | #ifdef __BIG_ENDIAN | 
 | 	iop->lba_capacity = (iop->lba_capacity << 16) | | 
 | 		(iop->lba_capacity >> 16); | 
 | #endif /* __BIG_ENDIAN */ | 
 |  | 
 | 	if (MG_RES_SEC) { | 
 | 		MG_DBG("MG_RES_SEC=%d\n", MG_RES_SEC); | 
 | 		iop->cyls = (iop->lba_capacity - MG_RES_SEC) / | 
 | 			iop->sectors / iop->heads; | 
 | 		res = iop->lba_capacity - | 
 | 			iop->cyls * iop->heads * iop->sectors; | 
 | 		iop->lba_capacity -= res; | 
 | 		printf("mg_disk: %d sectors reserved\n", res); | 
 | 	} | 
 |  | 
 | 	mg_disk_dev.lba = iop->lba_capacity; | 
 | 	return MG_ERR_NONE; | 
 | } | 
 |  | 
 | static int mg_disk_reset (void) | 
 | { | 
 | 	struct mg_drv_data *prv_data = host.drv_data; | 
 | 	s32 err; | 
 | 	u8 init_status; | 
 |  | 
 | 	/* hdd rst low */ | 
 | 	prv_data->mg_hdrst_pin(0); | 
 | 	err = mg_wait(MG_REG_STATUS_BIT_BUSY, 300); | 
 | 	if(err) | 
 | 		return err; | 
 |  | 
 | 	/* hdd rst high */ | 
 | 	prv_data->mg_hdrst_pin(1); | 
 | 	err = mg_wait(MG_STAT_READY, 3000); | 
 | 	if(err) | 
 | 		return err; | 
 |  | 
 | 	/* soft reset on */ | 
 | 	writeb(MG_REG_CTRL_RESET | MG_REG_CTRL_INTR_DISABLE, | 
 | 		mg_base() + MG_REG_DRV_CTRL); | 
 | 	err = mg_wait(MG_REG_STATUS_BIT_BUSY, 3000); | 
 | 	if(err) | 
 | 		return err; | 
 |  | 
 | 	/* soft reset off */ | 
 | 	writeb(MG_REG_CTRL_INTR_DISABLE, mg_base() + MG_REG_DRV_CTRL); | 
 | 	err = mg_wait(MG_STAT_READY, 3000); | 
 | 	if(err) | 
 | 		return err; | 
 |  | 
 | 	init_status = readb(mg_base() + MG_REG_STATUS) & 0xf; | 
 |  | 
 | 	if (init_status == 0xf) | 
 | 		return MG_ERR_INIT_STAT; | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 |  | 
 | static unsigned int mg_out(unsigned int sect_num, | 
 | 			unsigned int sect_cnt, | 
 | 			unsigned int cmd) | 
 | { | 
 | 	u32 err = MG_ERR_NONE; | 
 |  | 
 | 	err = mg_wait(MG_STAT_READY, 3000); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	writeb((u8)sect_cnt, mg_base() + MG_REG_SECT_CNT); | 
 | 	writeb((u8)sect_num, mg_base() + MG_REG_SECT_NUM); | 
 | 	writeb((u8)(sect_num >> 8), mg_base() + MG_REG_CYL_LOW); | 
 | 	writeb((u8)(sect_num >> 16), mg_base() + MG_REG_CYL_HIGH); | 
 | 	writeb((u8)((sect_num >> 24) | MG_REG_HEAD_LBA_MODE), | 
 | 		mg_base() + MG_REG_DRV_HEAD); | 
 | 	writeb(cmd, mg_base() + MG_REG_COMMAND); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static unsigned int mg_do_read_sects(void *buff, u32 sect_num, u32 sect_cnt) | 
 | { | 
 | 	u32 i, j, err; | 
 | 	u8 *buff_ptr = buff; | 
 | 	union mg_uniwb uniwb; | 
 |  | 
 | 	err = mg_out(sect_num, sect_cnt, MG_CMD_RD); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	for (i = 0; i < sect_cnt; i++) { | 
 | 		err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000); | 
 | 		if (err) | 
 | 			return err; | 
 |  | 
 | 		if ((u32)buff_ptr & 1) { | 
 | 			for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) { | 
 | 				uniwb.w = readw(mg_base() + MG_BUFF_OFFSET | 
 | 						+ (j << 1)); | 
 | 				*buff_ptr++ = uniwb.b[0]; | 
 | 				*buff_ptr++ = uniwb.b[1]; | 
 | 			} | 
 | 		} else { | 
 | 			for(j = 0; j < MG_SECTOR_SIZE >> 1; j++) { | 
 | 				*(u16 *)buff_ptr = readw(mg_base() + | 
 | 						MG_BUFF_OFFSET + (j << 1)); | 
 | 				buff_ptr += 2; | 
 | 			} | 
 | 		} | 
 | 		writeb(MG_CMD_RD_CONF, mg_base() + MG_REG_COMMAND); | 
 |  | 
 | 		MG_DBG("%u (0x%8.8x) sector read", sect_num + i, | 
 | 			(sect_num + i) * MG_SECTOR_SIZE); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | unsigned int mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt) | 
 | { | 
 | 	u32 quotient, residue, i, err; | 
 | 	u8 *buff_ptr = buff; | 
 |  | 
 | 	quotient = sect_cnt >> 8; | 
 | 	residue = sect_cnt % 256; | 
 |  | 
 | 	for (i = 0; i < quotient; i++) { | 
 | 		MG_DBG("sect num : %u buff : 0x%8.8x", sect_num, (u32)buff_ptr); | 
 | 		err = mg_do_read_sects(buff_ptr, sect_num, 256); | 
 | 		if (err) | 
 | 			return err; | 
 | 		sect_num += 256; | 
 | 		buff_ptr += 256 * MG_SECTOR_SIZE; | 
 | 	} | 
 |  | 
 | 	if (residue) { | 
 | 		MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr); | 
 | 		err = mg_do_read_sects(buff_ptr, sect_num, residue); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | unsigned long mg_block_read (int dev, unsigned long start, | 
 | 		lbaint_t blkcnt, void *buffer) | 
 | { | 
 | 	start += MG_RES_SEC; | 
 | 	if (! mg_disk_read_sects(buffer, start, blkcnt)) | 
 | 		return blkcnt; | 
 | 	else | 
 | 		return 0; | 
 | } | 
 |  | 
 | unsigned int mg_disk_read (u32 addr, u8 *buff, u32 len) | 
 | { | 
 | 	u8 *sect_buff, *buff_ptr = buff; | 
 | 	u32 cur_addr, next_sec_addr, end_addr, cnt, sect_num; | 
 | 	u32 err = MG_ERR_NONE; | 
 |  | 
 | 	/* TODO : sanity chk */ | 
 | 	cnt = 0; | 
 | 	cur_addr = addr; | 
 | 	end_addr = addr + len; | 
 |  | 
 | 	sect_buff = malloc(MG_SECTOR_SIZE); | 
 |  | 
 | 	if (cur_addr & MG_SECTOR_SIZE_MASK) { | 
 | 		next_sec_addr = (cur_addr + MG_SECTOR_SIZE) & | 
 | 				~MG_SECTOR_SIZE_MASK; | 
 | 		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT; | 
 | 		err = mg_disk_read_sects(sect_buff, sect_num, 1); | 
 | 		if (err) | 
 | 			goto mg_read_exit; | 
 |  | 
 | 		if (end_addr < next_sec_addr) { | 
 | 			memcpy(buff_ptr, | 
 | 				sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK), | 
 | 				end_addr - cur_addr); | 
 | 			MG_DBG("copies %u byte from sector offset 0x%8.8x", | 
 | 				end_addr - cur_addr, cur_addr); | 
 | 			cur_addr = end_addr; | 
 | 		} else { | 
 | 			memcpy(buff_ptr, | 
 | 				sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK), | 
 | 				next_sec_addr - cur_addr); | 
 | 			MG_DBG("copies %u byte from sector offset 0x%8.8x", | 
 | 				next_sec_addr - cur_addr, cur_addr); | 
 | 			buff_ptr += (next_sec_addr - cur_addr); | 
 | 			cur_addr = next_sec_addr; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (cur_addr < end_addr) { | 
 | 		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT; | 
 | 		cnt = ((end_addr & ~MG_SECTOR_SIZE_MASK) - cur_addr) >> | 
 | 			MG_SECTOR_SIZE_SHIFT; | 
 |  | 
 | 		if (cnt) | 
 | 			err = mg_disk_read_sects(buff_ptr, sect_num, cnt); | 
 | 		if (err) | 
 | 			goto mg_read_exit; | 
 |  | 
 | 		buff_ptr += cnt * MG_SECTOR_SIZE; | 
 | 		cur_addr += cnt * MG_SECTOR_SIZE; | 
 |  | 
 | 		if (cur_addr < end_addr) { | 
 | 			sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT; | 
 | 			err = mg_disk_read_sects(sect_buff, sect_num, 1); | 
 | 			if (err) | 
 | 				goto mg_read_exit; | 
 | 			memcpy(buff_ptr, sect_buff, end_addr - cur_addr); | 
 | 			MG_DBG("copies %u byte", end_addr - cur_addr); | 
 | 		} | 
 | 	} | 
 |  | 
 | mg_read_exit: | 
 | 	free(sect_buff); | 
 |  | 
 | 	return err; | 
 | } | 
 | static int mg_do_write_sects(void *buff, u32 sect_num, u32 sect_cnt) | 
 | { | 
 | 	u32 i, j, err; | 
 | 	u8 *buff_ptr = buff; | 
 | 	union mg_uniwb uniwb; | 
 |  | 
 | 	err = mg_out(sect_num, sect_cnt, MG_CMD_WR); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	for (i = 0; i < sect_cnt; i++) { | 
 | 		err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000); | 
 | 		if (err) | 
 | 			return err; | 
 |  | 
 | 		if ((u32)buff_ptr & 1) { | 
 | 			uniwb.b[0] = *buff_ptr++; | 
 | 			uniwb.b[1] = *buff_ptr++; | 
 | 			writew(uniwb.w, mg_base() + MG_BUFF_OFFSET + (j << 1)); | 
 | 		} else { | 
 | 			for(j = 0; j < MG_SECTOR_SIZE >> 1; j++) { | 
 | 				writew(*(u16 *)buff_ptr, | 
 | 						mg_base() + MG_BUFF_OFFSET + | 
 | 						(j << 1)); | 
 | 				buff_ptr += 2; | 
 | 			} | 
 | 		} | 
 | 		writeb(MG_CMD_WR_CONF, mg_base() + MG_REG_COMMAND); | 
 |  | 
 | 		MG_DBG("%u (0x%8.8x) sector write", | 
 | 			sect_num + i, (sect_num + i) * MG_SECTOR_SIZE); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | unsigned int mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt) | 
 | { | 
 | 	u32 quotient, residue, i; | 
 | 	u32 err = MG_ERR_NONE; | 
 | 	u8 *buff_ptr = buff; | 
 |  | 
 | 	quotient = sect_cnt >> 8; | 
 | 	residue = sect_cnt % 256; | 
 |  | 
 | 	for (i = 0; i < quotient; i++) { | 
 | 		MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr); | 
 | 		err = mg_do_write_sects(buff_ptr, sect_num, 256); | 
 | 		if (err) | 
 | 			return err; | 
 | 		sect_num += 256; | 
 | 		buff_ptr += 256 * MG_SECTOR_SIZE; | 
 | 	} | 
 |  | 
 | 	if (residue) { | 
 | 		MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr); | 
 | 		err = mg_do_write_sects(buff_ptr, sect_num, residue); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | unsigned long mg_block_write (int dev, unsigned long start, | 
 | 		lbaint_t blkcnt, const void *buffer) | 
 | { | 
 | 	start += MG_RES_SEC; | 
 | 	if (!mg_disk_write_sects((void *)buffer, start, blkcnt)) | 
 | 		return blkcnt; | 
 | 	else | 
 | 		return 0; | 
 | } | 
 |  | 
 | unsigned int mg_disk_write(u32 addr, u8 *buff, u32 len) | 
 | { | 
 | 	u8 *sect_buff, *buff_ptr = buff; | 
 | 	u32 cur_addr, next_sec_addr, end_addr, cnt, sect_num; | 
 | 	u32 err = MG_ERR_NONE; | 
 |  | 
 | 	/* TODO : sanity chk */ | 
 | 	cnt = 0; | 
 | 	cur_addr = addr; | 
 | 	end_addr = addr + len; | 
 |  | 
 | 	sect_buff = malloc(MG_SECTOR_SIZE); | 
 |  | 
 | 	if (cur_addr & MG_SECTOR_SIZE_MASK) { | 
 |  | 
 | 		next_sec_addr = (cur_addr + MG_SECTOR_SIZE) & | 
 | 				~MG_SECTOR_SIZE_MASK; | 
 | 		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT; | 
 | 		err = mg_disk_read_sects(sect_buff, sect_num, 1); | 
 | 		if (err) | 
 | 			goto mg_write_exit; | 
 |  | 
 | 		if (end_addr < next_sec_addr) { | 
 | 			memcpy(sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK), | 
 | 				buff_ptr, end_addr - cur_addr); | 
 | 			MG_DBG("copies %u byte to sector offset 0x%8.8x", | 
 | 				end_addr - cur_addr, cur_addr); | 
 | 			cur_addr = end_addr; | 
 | 		} else { | 
 | 			memcpy(sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK), | 
 | 				buff_ptr, next_sec_addr - cur_addr); | 
 | 			MG_DBG("copies %u byte to sector offset 0x%8.8x", | 
 | 				next_sec_addr - cur_addr, cur_addr); | 
 | 			buff_ptr += (next_sec_addr - cur_addr); | 
 | 			cur_addr = next_sec_addr; | 
 | 		} | 
 |  | 
 | 		err = mg_disk_write_sects(sect_buff, sect_num, 1); | 
 | 		if (err) | 
 | 			goto mg_write_exit; | 
 | 	} | 
 |  | 
 | 	if (cur_addr < end_addr) { | 
 |  | 
 | 		sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT; | 
 | 		cnt = ((end_addr & ~MG_SECTOR_SIZE_MASK) - cur_addr) >> | 
 | 			MG_SECTOR_SIZE_SHIFT; | 
 |  | 
 | 		if (cnt) | 
 | 			err = mg_disk_write_sects(buff_ptr, sect_num, cnt); | 
 | 		if (err) | 
 | 			goto mg_write_exit; | 
 |  | 
 | 		buff_ptr += cnt * MG_SECTOR_SIZE; | 
 | 		cur_addr += cnt * MG_SECTOR_SIZE; | 
 |  | 
 | 		if (cur_addr < end_addr) { | 
 | 			sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT; | 
 | 			err = mg_disk_read_sects(sect_buff, sect_num, 1); | 
 | 			if (err) | 
 | 				goto mg_write_exit; | 
 | 			memcpy(sect_buff, buff_ptr, end_addr - cur_addr); | 
 | 			MG_DBG("copies %u byte", end_addr - cur_addr); | 
 | 			err = mg_disk_write_sects(sect_buff, sect_num, 1); | 
 | 		} | 
 |  | 
 | 	} | 
 |  | 
 | mg_write_exit: | 
 | 	free(sect_buff); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | block_dev_desc_t *mg_disk_get_dev(int dev) | 
 | { | 
 | 	return ((block_dev_desc_t *) & mg_disk_dev); | 
 | } | 
 |  | 
 | /* must override this function */ | 
 | struct mg_drv_data * __attribute__((weak)) mg_get_drv_data (void) | 
 | { | 
 | 	puts ("### WARNING ### port mg_get_drv_data function\n"); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | unsigned int mg_disk_init (void) | 
 | { | 
 | 	struct mg_drv_data *prv_data; | 
 | 	u32 err = MG_ERR_NONE; | 
 |  | 
 | 	prv_data = mg_get_drv_data(); | 
 | 	if (! prv_data) { | 
 | 		printf("%s:%d fail (no driver_data)\n", __func__, __LINE__); | 
 | 		err = MG_ERR_NO_DRV_DATA; | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	((struct mg_host *)mg_disk_dev.priv)->drv_data = prv_data; | 
 |  | 
 | 	/* init ctrl pin */ | 
 | 	if (prv_data->mg_ctrl_pin_init) | 
 | 		prv_data->mg_ctrl_pin_init(); | 
 |  | 
 | 	if (! prv_data->mg_hdrst_pin) { | 
 | 		err = MG_ERR_CTRL_RST; | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	/* disk reset */ | 
 | 	err = mg_disk_reset(); | 
 | 	if (err) { | 
 | 		printf("%s:%d fail (err code : %d)\n", __func__, __LINE__, err); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	/* get disk id */ | 
 | 	err = mg_get_disk_id(); | 
 | 	if (err) { | 
 | 		printf("%s:%d fail (err code : %d)\n", __func__, __LINE__, err); | 
 | 		return err; | 
 | 	} | 
 |  | 
 | 	mg_disk_dev.block_read = mg_block_read; | 
 | 	mg_disk_dev.block_write = mg_block_write; | 
 |  | 
 | 	init_part(&mg_disk_dev); | 
 |  | 
 | 	dev_print(&mg_disk_dev); | 
 |  | 
 | 	return err; | 
 | } |