Exemple #1
0
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
	struct nand_chip *chip = mtd->priv;
	struct s3c2410_nand *nand = s3c2410_get_base_nand();

	debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);

	if (ctrl & NAND_CTRL_CHANGE) {
		ulong IO_ADDR_W = (ulong)nand;

		if (!(ctrl & NAND_CLE))
			IO_ADDR_W |= S3C2410_ADDR_NCLE;
		if (!(ctrl & NAND_ALE))
			IO_ADDR_W |= S3C2410_ADDR_NALE;

		chip->IO_ADDR_W = (void *)IO_ADDR_W;

		if (ctrl & NAND_NCE)
			writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
			       &nand->nfconf);
		else
			writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE,
			       &nand->nfconf);
	}

	if (cmd != NAND_CMD_NONE)
		writeb(cmd, chip->IO_ADDR_W);
}
Exemple #2
0
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
				      u_char *ecc_code)
{
	struct s3c2410_nand *nand = s3c2410_get_base_nand();
	ecc_code[0] = readb(&nand->nfecc);
	ecc_code[1] = readb(&nand->nfecc + 1);
	ecc_code[2] = readb(&nand->nfecc + 2);
	debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
	       mtd , ecc_code[0], ecc_code[1], ecc_code[2]);

	return 0;
}
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
	int i, dev, ret = 0;
	ulong addr, off;
	size_t size;
	char *cmd, *s;
	nand_info_t *nand;
#ifdef CONFIG_SYS_NAND_QUIET
	int quiet = CONFIG_SYS_NAND_QUIET;
#else
	int quiet = 0;
#endif
	const char *quiet_str = getenv("quiet");

	/* at least two arguments please */
	if (argc < 2)
		goto usage;

	if (quiet_str)
		quiet = simple_strtoul(quiet_str, NULL, 0) != 0;

	cmd = argv[1];

	if (strcmp(cmd, "info") == 0) {

		putc('\n');
		for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
			if (nand_info[i].name)
				nand_print_info(i);
		}
		return 0;
	}

	if (strcmp(cmd, "device") == 0) {

		if (argc < 3) {
			putc('\n');
			if ((nand_curr_device < 0) ||
			    (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE))
				puts("no devices available\n");
			else
				nand_print_info(nand_curr_device);
			return 0;
		}
		dev = (int)simple_strtoul(argv[2], NULL, 10);
		if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) {
			puts("No such device\n");
			return 1;
		}
		printf("Device %d: %s", dev, nand_info[dev].name);
		puts("... is now current device\n");
		nand_curr_device = dev;

#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
		/*
		 * Select the chip in the board/cpu specific driver
		 */
		board_nand_select_device(nand_info[dev].priv, dev);
#endif

		return 0;
	}

	if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
	    strncmp(cmd, "dump", 4) != 0 &&
	    strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 &&
	    strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 &&
	    strcmp(cmd, "biterr") != 0 &&
	    strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 )
		goto usage;

	/* the following commands operate on the current device */
	if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
	    !nand_info[nand_curr_device].name) {
		puts("\nno devices available\n");
		return 1;
	}
	nand = &nand_info[nand_curr_device];

	if (strcmp(cmd, "bad") == 0) {
		printf("\nDevice %d bad blocks:\n", nand_curr_device);
		for (off = 0; off < nand->size; off += nand->erasesize)
			if (nand_block_isbad(nand, off))
				printf("  %08lx\n", off);
		return 0;
	}

	/*
	 * Syntax is:
	 *   0    1     2       3    4
	 *   nand erase [clean] [off size]
	 */
	if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) {
		nand_erase_options_t opts;
		/* "clean" at index 2 means request to write cleanmarker */
		int clean = argc > 2 && !strcmp("clean", argv[2]);
		int o = clean ? 3 : 2;
		int scrub = !strcmp(cmd, "scrub");

		printf("\nNAND %s: ", scrub ? "scrub" : "erase");
		/* skip first two or three arguments, look for offset and size */
		if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)
			return 1;

		memset(&opts, 0, sizeof(opts));
		opts.offset = off;
		opts.length = size;
		opts.jffs2  = clean;
		opts.quiet  = quiet;

		if (scrub) {
			puts("Warning: "
			     "scrub option will erase all factory set "
			     "bad blocks!\n"
			     "         "
			     "There is no reliable way to recover them.\n"
			     "         "
			     "Use this command only for testing purposes "
			     "if you\n"
			     "         "
			     "are sure of what you are doing!\n"
			     "\nReally scrub this NAND flash? <y/N>\n");

			if (getc() == 'y' && getc() == '\r') {
				opts.scrub = 1;
			} else {
				puts("scrub aborted\n");
				return -1;
			}
		}
		ret = nand_erase_opts(nand, &opts);
		printf("%s\n", ret ? "ERROR" : "OK");

		return ret == 0 ? 0 : 1;
	}

	if (strncmp(cmd, "dump", 4) == 0) {
		if (argc < 3)
			goto usage;

		s = strchr(cmd, '.');
		off = (int)simple_strtoul(argv[2], NULL, 16);

		if (s != NULL && strcmp(s, ".oob") == 0)
			ret = nand_dump(nand, off, 1);
		else
			ret = nand_dump(nand, off, 0);

		return ret == 0 ? 1 : 0;

	}

	if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
		int read;

		if (argc < 4)
			goto usage;

		addr = (ulong)simple_strtoul(argv[2], NULL, 16);

		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
		printf("\nNAND %s: ", read ? "read" : "write");
		if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
			return 1;

		s = strchr(cmd, '.');
		if (!s || !strcmp(s, ".jffs2") ||
		    !strcmp(s, ".e") || !strcmp(s, ".i")) {
			if (read)
				ret = nand_read_skip_bad(nand, off, &size,
							 (u_char *)addr);
			else
				ret = nand_write_skip_bad(nand, off, &size,
							  (u_char *)addr);
		} 
//添加yaffs2相关操作,注意该处又关联到nand_write_skip_bad函数
#if defined(CONFIG_MTD_NAND_YAFFS2)
        else if (s != NULL && (!strcmp(s, ".yaffs2")))
        {
			debugX(1, "write yaffs2\n");
            nand->rw_oob = 1;
            nand->skipfirstblk = 1;
            ret = nand_write_skip_bad(nand,off,&size,(u_char *)addr);
            nand->skipfirstblk = 0;
            nand->rw_oob = 0;
        }
#endif
		else if (!strcmp(s, ".oob")) {
			/* out-of-band data */
			mtd_oob_ops_t ops = {
				.oobbuf = (u8 *)addr,
				.ooblen = size,
				.mode = MTD_OOB_RAW
			};

			if (read)
				ret = nand->read_oob(nand, off, &ops);
			else
				ret = nand->write_oob(nand, off, &ops);
		} else {
uint8 ValveContron_Delu(MeterFileType *p_mf, uint8 functype, uint8 *p_DataIn, uint8 *p_databuf, uint8 *p_datalenback)
{
    uint8 Err = 0;
    uint8 lu8data[100] = {0};
    uint8 lu8databuf[20] = {0xee};
    uint8 lu8datalenback = 0;

    CJ188_Format  lMeterData;

    DELU_Protocol	ProtocoalInfo;

    ProtocoalInfo.PreSmybolNum  = 0x04;
    ProtocoalInfo.MeterType 	= 0x20;
    memcpy(ProtocoalInfo.MeterAddr, p_mf->ValveAddr, 7);
    ProtocoalInfo.ControlCode 	= 0x04;
    ProtocoalInfo.SER = 0;


    switch(functype)
    {
    case ReadVALVE_All:  	//对德鲁协议来说,包括读室内温度和阀门状态2方面。
    {
        ProtocoalInfo.Length		= 0x0B;
        ProtocoalInfo.DataIdentifier = 0x20A0;
        memcpy(ProtocoalInfo.DataBuf, p_mf->ControlPanelAddr, 7);
        ProtocoalInfo.DataBuf[7]	= 0x11;

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            memcpy(lu8data, ProtocoalInfo.DataBuf, (ProtocoalInfo.Length - 3));

            if(lu8data[0] == 0xff)
            {
                memset(lu8databuf, 0xee, 3);
            }
            else
            {
                memcpy(lu8databuf, lu8data, 3);
            }

            lu8datalenback += 3;

        }
        else
        {
            memset(lu8databuf, 0xee, 3);								//温度3个字节
            lu8datalenback += 3;
        }


        //读取阀门状态!!
        ProtocoalInfo.PreSmybolNum  = 0x04;   //经过上面读室内温度,ProtocoalInfo中有一些量改变了,所以重赋值。
        ProtocoalInfo.MeterType 	= 0x20;
        memcpy(ProtocoalInfo.MeterAddr, p_mf->ValveAddr, 7);
        ProtocoalInfo.ControlCode 	= 0x04;
        ProtocoalInfo.SER = 0;
        ProtocoalInfo.Length		= 0x04;
        ProtocoalInfo.DataIdentifier = 0x24A0;
        ProtocoalInfo.DataBuf[0]	= 0x11;

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            uint16  ValveState = 0;
            ValveState = *(uint16 *)ProtocoalInfo.DataBuf;
            if(ValveState == 0x0800 || ValveState == 0x9900)
            {
                ValveState = 0x0099;
            }/*全关*/
            else if(ValveState == 0x0400 || ValveState == 0x8800)
            {
                ValveState = 0x0088;
            }
            else if(ValveState == 0x0200 || ValveState == 0x7700)
            {
                ValveState = 0x0077;
            }
            else if(ValveState == 0x0100 || ValveState == 0x6600)
            {
                ValveState = 0x0066;
            }
            else if(ValveState == 0x0000 || ValveState == 0x5500)
            {
                ValveState = 0x0055;
            }/*全开*/
            else
            {
                ValveState = 0x0099;
            }

            lu8databuf[3] = (uint8)ValveState;  //阀门开度字节

            lu8databuf[4] = 0;//按照格式补齐。
            lu8databuf[5] = 0;

            lu8datalenback += 3;

        }
        else
        {
            memset(&lu8databuf[3], 0xee, 3);								//温度3个字节
            lu8datalenback += 3;
        }

        memcpy(p_databuf, lu8databuf, lu8datalenback);
        *p_datalenback = lu8datalenback;

        break;
    }

    case READROOM_TEMP:
    {
        ProtocoalInfo.Length		= 0x0B;
        ProtocoalInfo.DataIdentifier = 0x20A0;
        memcpy(ProtocoalInfo.DataBuf, p_mf->ControlPanelAddr, 7);
        ProtocoalInfo.DataBuf[7]	= 0x11;

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            memcpy(lu8data, ProtocoalInfo.DataBuf, (ProtocoalInfo.Length - 3));

            if(lu8data[0] == 0xff)
            {
                memset(lu8databuf, 0xee, 3);
            }
            else
            {
                lu8databuf[0] = lu8data[2];   //因协议格式,调换顺序。
                lu8databuf[1] = lu8data[1];
                lu8databuf[2] = lu8data[0];

            }

            lu8datalenback += 3;

        }
        else
        {
            memset(lu8databuf, 0xee, 3);								//温度3个字节
            lu8datalenback += 3;
        }

        memcpy(p_databuf, lu8databuf, lu8datalenback);
        *p_datalenback = lu8datalenback;

        break;
    }

    case SETHEAT_DISPLAY:
    {

        break;
    }

    case SETHEAT_VALUE:
    {
        ProtocoalInfo.Length		= 0x2E;
        ProtocoalInfo.DataIdentifier = 0x25A0;
        memcpy(ProtocoalInfo.DataBuf, p_DataIn, sizeof(lMeterData));
        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            debug_info(gDebugModule[TASKDOWN_MODULE], "%s %d Send HeatMeter data  to valve ok", __FUNCTION__, __LINE__);
        }
        else
        {
            debugX(LOG_LEVEL_ERROR, "%s %d Send HeatMeter data  to valve  failed!\r\n", __FUNCTION__, __LINE__);
        }


        break;
    }

    case SETROOM_TEMP:
    {
        ProtocoalInfo.Length			= 0x0D;
        ProtocoalInfo.DataIdentifier	= 0x23A0;
        memcpy(ProtocoalInfo.DataBuf, p_mf->ControlPanelAddr, 7);
        //memcpy(&ProtocoalInfo.DataBuf[7], p_DataIn, 3);
        ProtocoalInfo.DataBuf[7] = *(p_DataIn + 2);   //按照德鲁阀控协议调整顺序。
        ProtocoalInfo.DataBuf[8] = *(p_DataIn + 1);
        ProtocoalInfo.DataBuf[9] = *(p_DataIn + 0);

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            memcpy(lu8data, ProtocoalInfo.DataBuf, (ProtocoalInfo.Length - 3));
            lu8datalenback = ProtocoalInfo.Length - 3;

        }

        memcpy(p_databuf, lu8data, lu8datalenback);
        *p_datalenback = lu8datalenback;

        break;
    }

    case SETTEMP_RANGE:
    {
        ProtocoalInfo.Length			= 0x10;
        ProtocoalInfo.DataIdentifier	= 0x21A0;
        memcpy(ProtocoalInfo.DataBuf, p_mf->ControlPanelAddr, 7);
        //memcpy(&ProtocoalInfo.DataBuf[7], p_DataIn, 6);
        ProtocoalInfo.DataBuf[7] = *(p_DataIn + 2);   //按照德鲁阀控协议调整顺序。
        ProtocoalInfo.DataBuf[8] = *(p_DataIn + 1);
        ProtocoalInfo.DataBuf[9] = *(p_DataIn + 0);
        ProtocoalInfo.DataBuf[10] = *(p_DataIn + 5);   //按照德鲁阀控协议调整顺序。
        ProtocoalInfo.DataBuf[11] = *(p_DataIn + 4);
        ProtocoalInfo.DataBuf[12] = *(p_DataIn + 3);

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            memcpy(lu8data, ProtocoalInfo.DataBuf, (ProtocoalInfo.Length - 3));
            lu8datalenback = ProtocoalInfo.Length - 3;

        }

        memcpy(p_databuf, lu8data, lu8datalenback);
        *p_datalenback = lu8datalenback;



        break;
    }

    case SETVALVE_STATUS:
    {
        ProtocoalInfo.Length		= 0x04;
        ProtocoalInfo.DataIdentifier = 0x17A0;
        ProtocoalInfo.DataBuf[0]	= *(p_DataIn + 0);

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            memcpy(lu8data, ProtocoalInfo.DataBuf, (ProtocoalInfo.Length - 3));
            lu8datalenback = ProtocoalInfo.Length - 3;

        }

        memcpy(p_databuf, lu8data, lu8datalenback);
        *p_datalenback = lu8datalenback;



        break;
    }

    case SETVALVE_CONTROLTYPE:
    {
        ProtocoalInfo.Length			= 0x0B;
        ProtocoalInfo.DataIdentifier	= 0x22A0;
        memcpy(ProtocoalInfo.DataBuf, p_mf->ControlPanelAddr, 7);
        if(*(p_DataIn + 0) == 0x09)
        {
            ProtocoalInfo.DataBuf[7]		= 0x55;
        }
        if(*(p_DataIn + 0) == 0x0B)
        {
            ProtocoalInfo.DataBuf[7]		= 0x66;
        }
        if(*(p_DataIn + 0) == 0x0C)
        {
            ProtocoalInfo.DataBuf[7]		= 0x77;
        }
        if(*(p_DataIn + 0) == 0x0D)
        {
            ProtocoalInfo.DataBuf[7]		= 0x88;
        }

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            memcpy(lu8data, ProtocoalInfo.DataBuf, (ProtocoalInfo.Length - 3));
            lu8datalenback = ProtocoalInfo.Length - 3;

        }

        memcpy(p_databuf, lu8data, lu8datalenback);
        *p_datalenback = lu8datalenback;


        break;
    }

    case READVALVE_STATUS:
    {
        //读取阀门状态!!
        ProtocoalInfo.Length		= 0x04;
        ProtocoalInfo.DataIdentifier = 0x24A0;
        ProtocoalInfo.DataBuf[0]	= 0x11;

        Err = METER_DataItem(&ProtocoalInfo);
        if(Err == NO_ERR)
        {
            uint16  ValveState = 0;
            ValveState = *(uint16 *)ProtocoalInfo.DataBuf;
            if(ValveState == 0x0800 || ValveState == 0x9900)
            {
                ValveState = 0x0099;
            }/*全关*/
            else if(ValveState == 0x0400 || ValveState == 0x8800)
            {
                ValveState = 0x0088;
            }
            else if(ValveState == 0x0200 || ValveState == 0x7700)
            {
                ValveState = 0x0077;
            }
            else if(ValveState == 0x0100 || ValveState == 0x6600)
            {
                ValveState = 0x0066;
            }
            else if(ValveState == 0x0000 || ValveState == 0x5500)
            {
                ValveState = 0x0055;
            }/*全开*/
            else
            {
                ValveState = 0x0099;
            }

            lu8databuf[0] = (uint8)ValveState;  //阀门开度字节
            lu8datalenback += 1;

        }

        memcpy(p_databuf, lu8databuf, lu8datalenback);
        *p_datalenback = lu8datalenback;

        break;
    }


    default:
        break;
    }



    return Err;
}
Exemple #5
0
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
	struct s3c2410_nand *nand = s3c2410_get_base_nand();
	debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
	writel(readl(&nand->nfconf) | S3C2410_NFCONF_INITECC, &nand->nfconf);
}
Exemple #6
0
static int s3c2410_dev_ready(struct mtd_info *mtd)
{
	struct s3c2410_nand *nand = s3c2410_get_base_nand();
	debugX(1, "dev_ready\n");
	return readl(&nand->nfstat) & 0x01;
}
Exemple #7
0
int board_nand_init(struct nand_chip *nand)
{
	u_int32_t cfg;
	u_int8_t tacls, twrph0, twrph1;
	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
	struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();

	debugX(1, "board_nand_init()\n");

	writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);

	/* initialize hardware */
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
	tacls  = CONFIG_S3C24XX_TACLS;
	twrph0 = CONFIG_S3C24XX_TWRPH0;
	twrph1 =  CONFIG_S3C24XX_TWRPH1;
#else
	tacls = 4;
	twrph0 = 8;
	twrph1 = 8;
#endif

	cfg = S3C2410_NFCONF_EN;
	cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
	cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
	cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
	writel(cfg, &nand_reg->nfconf);

	/* initialize nand_chip data structure */
	nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
	nand->IO_ADDR_W = (void *)&nand_reg->nfdata;

	nand->select_chip = NULL;

	/* read_buf and write_buf are default */
	/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
	nand->read_buf = nand_read_buf;
#endif

	/* hwcontrol always must be implemented */
	nand->cmd_ctrl = s3c2410_hwcontrol;

	nand->dev_ready = s3c2410_dev_ready;

#ifdef CONFIG_S3C2410_NAND_HWECC
	nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
	nand->ecc.calculate = s3c2410_nand_calculate_ecc;
	nand->ecc.correct = s3c2410_nand_correct_data;
	nand->ecc.mode = NAND_ECC_HW;
	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
	nand->ecc.mode = NAND_ECC_SOFT;
#endif

#ifdef CONFIG_S3C2410_NAND_BBT
	nand->options = NAND_USE_FLASH_BBT;
#else
	nand->options = 0;
#endif

	debugX(1, "end of nand_init\n");

	return 0;
}
static void s3c2410_udc_epn(int ep)
{
	struct usb_endpoint_instance *endpoint;
	struct urb *urb;
	u32 ep_csr1;

	if (ep >= S3C2410_UDC_NUM_ENDPOINTS)
		return;

	endpoint = &udc_device->bus->endpoint_array[ep];

	S3C2410_UDC_SETIX(ep);

	if (endpoint->endpoint_address & USB_DIR_IN) {
		/* IN transfer (device to host) */
		ep_csr1 = inl(S3C2410_UDC_IN_CSR1_REG);
		debug("for ep=%u, IN_CSR1=0x%x ", ep, ep_csr1);

		urb = endpoint->tx_urb;
		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
			/* Stall handshake */
			debug("stall\n");
			outl(0x00, S3C2410_UDC_IN_CSR1_REG);
			return;
		}
		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && urb &&
		      urb->actual_length) {

			debug("completing previously send data ");
			usbd_tx_complete(endpoint);

			/* push pending data into FIFO */
			if ((endpoint->last == endpoint->tx_packetSize) &&
			    (urb->actual_length - endpoint->sent - endpoint->last == 0)) {
				endpoint->sent += endpoint->last;
				/* Write 0 bytes of data (ZLP) */
				debug("ZLP ");
				outl(ep_csr1|S3C2410_UDC_ICSR1_PKTRDY, S3C2410_UDC_IN_CSR1_REG);
			} else {
				/* write actual data to fifo */
				debug_urb_buffer("TX_DATA", endpoint);
				s3c2410_write_noniso_tx_fifo(endpoint);
				outl(ep_csr1|S3C2410_UDC_ICSR1_PKTRDY, S3C2410_UDC_IN_CSR1_REG);
			}
		}
		debug("\n");
	} else {
		/* OUT transfer (host to device) */
		ep_csr1 = inl(S3C2410_UDC_OUT_CSR1_REG);
		debug("for ep=%u, OUT_CSR1=0x%x ", ep, ep_csr1);

		urb = endpoint->rcv_urb;
		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
			/* Stall handshake */
			debugX("SENT STALL\n");
			outl(0x00, S3C2410_UDC_IN_CSR1_REG);
			return;
		}
		if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && urb) {
			/* Read pending data from fifo */
			u32 fifo_count = fifo_count_out();
			int is_last = 0;
			u32 i, urb_avail = urb->buffer_length - urb->actual_length;
			u8 *cp = urb->buffer + urb->actual_length;

			if (fifo_count < endpoint->rcv_packetSize)
				is_last = 1;

			debug("fifo_count=%u is_last=%d, urb_avail=%u\n",
				fifo_count, is_last, urb_avail);

			if (fifo_count < urb_avail)
				urb_avail = fifo_count;

			for (i = 0; i < urb_avail; i++)
				*cp++ = inb(ep_fifo_reg[ep]);

			/* if (is_last) */
			/*
			 * Once the MCU reads the packet from FIFO,
			 * this bit should be cleared
			 */
#ifndef AUTO_CLEAR 
			outl(ep_csr1 & ~S3C2410_UDC_OCSR1_PKTRDY,
				     S3C2410_UDC_OUT_CSR1_REG);
#endif

			usbd_rcv_complete(endpoint, urb_avail, 0);
			/* FIXME is there a better way to notify that,
			 * we have got data */
			usbd_device_event_irq(udc_device,
						DEVICE_FUNCTION_PRIVATE, 0);
		}
	}

	urb = endpoint->rcv_urb;
}