Skip to content
Snippets Groups Projects
cfi_flash.c 32.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    #ifdef DEBUG
    		flash_printqry (info, 0);
    #endif
    		switch (info->vendor) {
    
    		case CFI_CMDSET_INTEL_STANDARD:
    		case CFI_CMDSET_INTEL_EXTENDED:
    		default:
    			info->cmd_reset = FLASH_CMD_RESET;
    			break;
    		case CFI_CMDSET_AMD_STANDARD:
    		case CFI_CMDSET_AMD_EXTENDED:
    			info->cmd_reset = AMD_CMD_RESET;
    			break;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		debug ("manufacturer is %d\n", info->vendor);
    
    		size_ratio = info->portwidth / info->chipwidth;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* if the chip is x8/x16 reduce the ratio by half */
    		if ((info->interface == FLASH_CFI_X8X16)
    		    && (info->chipwidth == FLASH_CFI_BY8)) {
    			size_ratio >>= 1;
    		}
    
    		num_erase_regions = flash_read_uchar (info, FLASH_OFFSET_NUM_ERASE_REGIONS);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		debug ("size_ratio %d port %d bits chip %d bits\n",
    		       size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH,
    		       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
    		debug ("found %d erase regions\n", num_erase_regions);
    
    		sect_cnt = 0;
    		sector = base;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		for (i = 0; i < num_erase_regions; i++) {
    			if (i > NUM_ERASE_REGIONS) {
    
    				printf ("%d erase regions found, only %d used\n",
    					num_erase_regions, NUM_ERASE_REGIONS);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			tmp = flash_read_long (info, 0,
    					       FLASH_OFFSET_ERASE_REGIONS +
    					       i * 4);
    			erase_region_size =
    				(tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			erase_region_count = (tmp & 0xffff) + 1;
    
    			debug ("erase_region_count = %d erase_region_size = %d\n",
    
    				erase_region_count, erase_region_size);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			for (j = 0; j < erase_region_count; j++) {
    
    				info->start[sect_cnt] = sector;
    				sector += (erase_region_size * size_ratio);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				info->protect[sect_cnt] =
    					flash_isset (info, sect_cnt,
    						     FLASH_OFFSET_PROTECT,
    						     FLASH_STATUS_PROTECT);
    
    				sect_cnt++;
    			}
    		}
    
    		info->sector_count = sect_cnt;
    		/* multiply the size by the number of chips */
    
    		info->size = (1 << flash_read_uchar (info, FLASH_OFFSET_SIZE)) * size_ratio;
    		info->buffer_size = (1 << flash_read_ushort (info, 0, FLASH_OFFSET_BUFFER_SIZE));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_ETOUT);
    
    		info->erase_blk_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_EMAX_TOUT)));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_WBTOUT);
    
    		info->buffer_write_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_WBMAX_TOUT)));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_WTOUT);
    
    		info->write_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_WMAX_TOUT))) / 1000;
    
    		info->flash_id = FLASH_MAN_CFI;
    
    		if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) {
    			info->portwidth >>= 1;	/* XXX - Need to test on x8/x16 in parallel. */
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	flash_write_cmd (info, 0, 0, FLASH_CMD_RESET);
    	return (info->size);
    
    }
    
    
    /*-----------------------------------------------------------------------
     */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    static int flash_write_cfiword (flash_info_t * info, ulong dest,
    				cfiword_t cword)
    
    {
    
    	cfiptr_t ctladdr;
    	cfiptr_t cptr;
    	int flag;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	ctladdr.cp = flash_make_addr (info, 0, 0);
    	cptr.cp = (uchar *) dest;
    
    
    
    	/* Check if Flash is (sufficiently) erased */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	switch (info->portwidth) {
    
    	case FLASH_CFI_8BIT:
    		flag = ((cptr.cp[0] & cword.c) == cword.c);
    		break;
    	case FLASH_CFI_16BIT:
    		flag = ((cptr.wp[0] & cword.w) == cword.w);
    		break;
    	case FLASH_CFI_32BIT:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		flag = ((cptr.lp[0] & cword.l) == cword.l);
    
    		break;
    	case FLASH_CFI_64BIT:
    		flag = ((cptr.lp[0] & cword.ll) == cword.ll);
    		break;
    	default:
    		return 2;
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if (!flag)
    
    		return 2;
    
    	/* Disable interrupts which might cause a timeout here */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	flag = disable_interrupts ();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	switch (info->vendor) {
    
    	case CFI_CMDSET_INTEL_EXTENDED:
    	case CFI_CMDSET_INTEL_STANDARD:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
    		flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
    
    		break;
    	case CFI_CMDSET_AMD_EXTENDED:
    	case CFI_CMDSET_AMD_STANDARD:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		flash_unlock_seq (info, 0);
    
    		flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	switch (info->portwidth) {
    
    	case FLASH_CFI_8BIT:
    		cptr.cp[0] = cword.c;
    		break;
    	case FLASH_CFI_16BIT:
    		cptr.wp[0] = cword.w;
    		break;
    	case FLASH_CFI_32BIT:
    		cptr.lp[0] = cword.l;
    		break;
    	case FLASH_CFI_64BIT:
    		cptr.llp[0] = cword.ll;
    		break;
    	}
    
    	/* re-enable interrupts if necessary */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if (flag)
    		enable_interrupts ();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return flash_full_status_check (info, 0, info->write_tout, "write");
    
    }
    
    #ifdef CFG_FLASH_USE_BUFFER_WRITE
    
    /* loop through the sectors from the highest address
     * when the passed address is greater or equal to the sector address
     * we have a match
     */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    static flash_sect_t find_sector (flash_info_t * info, ulong addr)
    
    {
    	flash_sect_t sector;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	for (sector = info->sector_count - 1; sector >= 0; sector--) {
    		if (addr >= info->start[sector])
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
    				  int len)
    
    {
    	flash_sect_t sector;
    	int cnt;
    	int retcode;
    	volatile cfiptr_t src;
    	volatile cfiptr_t dst;
    
    	/* buffered writes in the AMD chip set is not supported yet */
    	if((info->vendor ==  CFI_CMDSET_AMD_STANDARD) ||
    		(info->vendor == CFI_CMDSET_AMD_EXTENDED))
    		return ERR_INVAL;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	dst.cp = (uchar *) dest;
    	sector = find_sector (info, dest);
    	flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
    	flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER);
    	if ((retcode =
    	     flash_status_check (info, sector, info->buffer_write_tout,
    				 "write to buffer")) == ERR_OK) {
    		/* reduce the number of loops by the width of the port	*/
    		switch (info->portwidth) {
    
    		case FLASH_CFI_8BIT:
    			cnt = len;
    			break;
    		case FLASH_CFI_16BIT:
    			cnt = len >> 1;
    			break;
    		case FLASH_CFI_32BIT:
    			cnt = len >> 2;
    			break;
    		case FLASH_CFI_64BIT:
    			cnt = len >> 3;
    			break;
    		default:
    			return ERR_INVAL;
    			break;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		flash_write_cmd (info, sector, 0, (uchar) cnt - 1);
    		while (cnt-- > 0) {
    			switch (info->portwidth) {
    
    			case FLASH_CFI_8BIT:
    				*dst.cp++ = *src.cp++;
    				break;
    			case FLASH_CFI_16BIT:
    				*dst.wp++ = *src.wp++;
    				break;
    			case FLASH_CFI_32BIT:
    				*dst.lp++ = *src.lp++;
    				break;
    			case FLASH_CFI_64BIT:
    				*dst.llp++ = *src.llp++;
    				break;
    			default:
    				return ERR_INVAL;
    				break;
    			}
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		flash_write_cmd (info, sector, 0,
    				 FLASH_CMD_WRITE_BUFFER_CONFIRM);
    		retcode =
    			flash_full_status_check (info, sector,
    						 info->buffer_write_tout,
    						 "buffer write");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS);
    
    	return retcode;
    }
    
    #endif /* CFG_FLASH_USE_BUFFER_WRITE */
    
    #endif /* CFG_FLASH_CFI */