| /* | 
 |  * atmel_df_pow2.c - convert Atmel Dataflashes to Power of 2 mode | 
 |  * | 
 |  * Copyright 2009 Analog Devices Inc. | 
 |  * | 
 |  * Licensed under the 2-clause BSD. | 
 |  */ | 
 |  | 
 | #include <common.h> | 
 | #include <exports.h> | 
 |  | 
 | #define CMD_ID    0x9f | 
 | #define CMD_STAT  0xd7 | 
 | #define CMD_CFG   0x3d | 
 |  | 
 | static int flash_cmd(struct spi_slave *slave, uchar cmd, uchar *buf, int len) | 
 | { | 
 | 	buf[0] = cmd; | 
 | 	return spi_xfer(slave, 8 * len, buf, buf, SPI_XFER_BEGIN | SPI_XFER_END); | 
 | } | 
 |  | 
 | static int flash_status(struct spi_slave *slave) | 
 | { | 
 | 	uchar buf[2]; | 
 | 	if (flash_cmd(slave, CMD_STAT, buf, sizeof(buf))) | 
 | 		return -1; | 
 | 	return buf[1]; | 
 | } | 
 |  | 
 | static int flash_set_pow2(struct spi_slave *slave) | 
 | { | 
 | 	int ret; | 
 | 	uchar buf[4]; | 
 |  | 
 | 	buf[1] = 0x2a; | 
 | 	buf[2] = 0x80; | 
 | 	buf[3] = 0xa6; | 
 |  | 
 | 	ret = flash_cmd(slave, CMD_CFG, buf, sizeof(buf)); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	/* wait Tp, or 6 msec */ | 
 | 	udelay(6000); | 
 |  | 
 | 	ret = flash_status(slave); | 
 | 	if (ret == -1) | 
 | 		return 1; | 
 |  | 
 | 	return ret & 0x1 ? 0 : 1; | 
 | } | 
 |  | 
 | static int flash_check(struct spi_slave *slave) | 
 | { | 
 | 	int ret; | 
 | 	uchar buf[4]; | 
 |  | 
 | 	ret = flash_cmd(slave, CMD_ID, buf, sizeof(buf)); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	if (buf[1] != 0x1F) { | 
 | 		printf("atmel flash not found (id[0] = %#x)\n", buf[1]); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	if ((buf[2] >> 5) != 0x1) { | 
 | 		printf("AT45 flash not found (id[0] = %#x)\n", buf[2]); | 
 | 		return 2; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static char *getline(void) | 
 | { | 
 | 	static char buffer[100]; | 
 | 	char c; | 
 | 	size_t i; | 
 |  | 
 | 	i = 0; | 
 | 	while (1) { | 
 | 		buffer[i] = '\0'; | 
 |  | 
 | 		c = getc(); | 
 |  | 
 | 		switch (c) { | 
 | 		case '\r':	/* Enter/Return key */ | 
 | 		case '\n': | 
 | 			puts("\n"); | 
 | 			return buffer; | 
 |  | 
 | 		case 0x03:	/* ^C - break */ | 
 | 			return NULL; | 
 |  | 
 | 		case 0x5F: | 
 | 		case 0x08:	/* ^H  - backspace */ | 
 | 		case 0x7F:	/* DEL - backspace */ | 
 | 			if (i) { | 
 | 				puts("\b \b"); | 
 | 				i--; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			/* Ignore control characters */ | 
 | 			if (c < 0x20) | 
 | 				break; | 
 | 			/* Queue up all other characters */ | 
 | 			buffer[i++] = c; | 
 | 			printf("%c", c); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | int atmel_df_pow2(int argc, char * const argv[]) | 
 | { | 
 | 	/* Print the ABI version */ | 
 | 	app_startup(argv); | 
 | 	if (XF_VERSION != get_version()) { | 
 | 		printf("Expects ABI version %d\n", XF_VERSION); | 
 | 		printf("Actual U-Boot ABI version %lu\n", get_version()); | 
 | 		printf("Can't run\n\n"); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	spi_init(); | 
 |  | 
 | 	while (1) { | 
 | 		struct spi_slave *slave; | 
 | 		char *line, *p; | 
 | 		int bus, cs, status; | 
 |  | 
 | 		puts("\nenter the [BUS:]CS of the SPI flash: "); | 
 | 		line = getline(); | 
 |  | 
 | 		/* CTRL+C */ | 
 | 		if (!line) | 
 | 			return 0; | 
 | 		if (line[0] == '\0') | 
 | 			continue; | 
 |  | 
 | 		bus = cs = simple_strtoul(line, &p, 10); | 
 | 		if (*p) { | 
 | 			if (*p == ':') { | 
 | 				++p; | 
 | 				cs = simple_strtoul(p, &p, 10); | 
 | 			} | 
 | 			if (*p) { | 
 | 				puts("invalid format, please try again\n"); | 
 | 				continue; | 
 | 			} | 
 | 		} else | 
 | 			bus = 0; | 
 |  | 
 | 		printf("\ngoing to work with dataflash at %i:%i\n", bus, cs); | 
 |  | 
 | 		/* use a low speed -- it'll work with all devices, and | 
 | 		 * speed here doesn't really matter. | 
 | 		 */ | 
 | 		slave = spi_setup_slave(bus, cs, 1000, SPI_MODE_3); | 
 | 		if (!slave) { | 
 | 			puts("unable to setup slave\n"); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (spi_claim_bus(slave)) { | 
 | 			spi_free_slave(slave); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (flash_check(slave)) { | 
 | 			puts("no flash found\n"); | 
 | 			goto done; | 
 | 		} | 
 |  | 
 | 		status = flash_status(slave); | 
 | 		if (status == -1) { | 
 | 			puts("unable to read status register\n"); | 
 | 			goto done; | 
 | 		} | 
 | 		if (status & 0x1) { | 
 | 			puts("flash is already in power-of-2 mode!\n"); | 
 | 			goto done; | 
 | 		} | 
 |  | 
 | 		puts("are you sure you wish to set power-of-2 mode?\n"); | 
 | 		puts("this operation is permanent and irreversible\n"); | 
 | 		printf("enter YES to continue: "); | 
 | 		line = getline(); | 
 | 		if (!line || strcmp(line, "YES")) | 
 | 			goto done; | 
 |  | 
 | 		if (flash_set_pow2(slave)) { | 
 | 			puts("setting pow2 mode failed\n"); | 
 | 			goto done; | 
 | 		} | 
 |  | 
 | 		puts( | 
 | 			"Configuration should be updated now.  You will have to\n" | 
 | 			"power cycle the part in order to finish the conversion.\n" | 
 | 		); | 
 |  | 
 |  done: | 
 | 		spi_release_bus(slave); | 
 | 		spi_free_slave(slave); | 
 | 	} | 
 | } |