summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-aspeed/flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-aspeed/flash.c')
-rw-r--r--arch/arm/mach-aspeed/flash.c1403
1 files changed, 1403 insertions, 0 deletions
diff --git a/arch/arm/mach-aspeed/flash.c b/arch/arm/mach-aspeed/flash.c
new file mode 100644
index 0000000000..9c5ead6fd7
--- /dev/null
+++ b/arch/arm/mach-aspeed/flash.c
@@ -0,0 +1,1403 @@
+/*
+ * 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
+ *
+ * History
+ * 01/20/2004 - combined variants of original driver.
+ * 01/22/2004 - Write performance enhancements for parallel chips (Tolunay)
+ * 01/23/2004 - Support for x8/x16 chips (Rune Raknerud)
+ * 01/27/2004 - Little endian support Ed Okerson
+ *
+ * Tested Architectures
+ * Port Width Chip Width # of banks Flash Chip Board
+ * 32 16 1 28F128J3 seranoa/eagle
+ * 64 16 1 28F128J3 seranoa/falcon
+ *
+ */
+
+/* The DEBUG define must be before common to enable debugging */
+/* #define DEBUG */
+
+#include <common.h>
+#include <asm/processor.h>
+#include <asm/byteorder.h>
+#include <environment.h>
+
+#include <asm/arch/ast_scu.h>
+#include <asm/arch/aspeed.h>
+
+
+/*
+ * This file implements a Common Flash Interface (CFI) driver for U-Boot.
+ * The width of the port and the width of the chips are determined at initialization.
+ * These widths are used to calculate the address for access CFI data structures.
+ * It has been tested on an Intel Strataflash implementation and AMD 29F016D.
+ *
+ * References
+ * JEDEC Standard JESD68 - Common Flash Interface (CFI)
+ * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes
+ * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets
+ * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet
+ *
+ * TODO
+ *
+ * Use Primary Extended Query table (PRI) and Alternate Algorithm Query
+ * Table (ALT) to determine if protection is available
+ *
+ * Add support for other command sets Use the PRI and ALT to determine command set
+ * Verify erase and program timeouts.
+ */
+
+#define CFI_MFR_MACRONIX 0x00C2
+#define CFI_MFR_MICRON 0x0020
+#define CFI_MFR_WINBOND 0x00DA
+
+flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* FLASH chips info */
+
+/* Support Flash ID */
+#define STM25P64 0x172020
+#define STM25P128 0x182020
+#define N25Q256 0x19ba20
+#define N25Q512 0x20ba20
+#define S25FL064A 0x160201
+#define S25FL128P 0x182001
+#define S25FL256S 0x190201
+#define W25X16 0x1530ef
+#define W25X64 0x1730ef
+#define W25Q64BV 0x1740ef
+#define W25Q128BV 0x1840ef
+#define W25Q256FV 0x1940ef
+#define MX25L1605D 0x1520C2
+#define MX25L12805D 0x1820C2
+#define MX25L25635E 0x1920C2
+#define MX66L51235F 0x1A20C2
+#define SST25VF016B 0x4125bf
+#define SST25VF064C 0x4b25bf
+#define SST25VF040B 0x8d25bf
+#define AT25DF161 0x02461F
+#define AT25DF321 0x01471F
+
+/* SPI Define */
+#define CS0_CTRL 0x10
+#define CS1_CTRL 0x14
+#define CS2_CTRL 0x18
+
+/* for DMA */
+#define REG_FLASH_INTERRUPT_STATUS 0x08
+#define REG_FLASH_DMA_CONTROL 0x80
+#define REG_FLASH_DMA_FLASH_BASE 0x84
+#define REG_FLASH_DMA_DRAM_BASE 0x88
+#define REG_FLASH_DMA_LENGTH 0x8c
+
+#define FLASH_STATUS_DMA_BUSY 0x0000
+#define FLASH_STATUS_DMA_READY 0x0800
+#define FLASH_STATUS_DMA_CLEAR 0x0800
+
+#define FLASH_DMA_ENABLE 0x01
+
+#define CMD_MASK 0xFFFFFFF8
+
+#define NORMALREAD 0x00
+#define FASTREAD 0x01
+#define NORMALWRITE 0x02
+#define USERMODE 0x03
+
+#define CE_LOW 0x00
+#define CE_HIGH 0x04
+
+/* AST2300 only */
+#define IOMODEx1 0x00000000
+#define IOMODEx2 0x20000000
+#define IOMODEx2_dummy 0x30000000
+#define IOMODEx4 0x40000000
+#define IOMODEx4_dummy 0x50000000
+
+#define DUMMY_COMMAND_OUT 0x00008000
+
+/* specificspi */
+#define SpecificSPI_N25Q512 0x00000001
+
+/*-----------------------------------------------------------------------*/
+static u32 ast_spi_calculate_divisor(u32 max_speed_hz)
+{
+ // [0] ->15 : HCLK , HCLK/16
+ u8 SPI_DIV[16] = {16, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0};
+ u32 i, hclk, spi_cdvr=0;
+
+ hclk = ast_get_ahbclk();
+ for(i=1;i<17;i++) {
+ if(max_speed_hz >= (hclk/i)) {
+ spi_cdvr = SPI_DIV[i-1];
+// printf("hclk = %d , spi_cdvr = %d \n",hclk, spi_cdvr);
+ break;
+ }
+ }
+
+// printf("hclk is %d, divisor is %d, target :%d , cal speed %d\n", hclk, spi_cdvr, max_speed_hz, hclk/i);
+ return spi_cdvr;
+}
+
+/* create an address based on the offset and the port width */
+inline uchar *flash_make_addr(flash_info_t * info, flash_sect_t sect, uint offset)
+{
+#ifdef CONFIG_2SPIFLASH
+ if (info->start[0] >= PHYS_FLASH_2)
+ return ((uchar *) (info->start[sect] + (offset * 1) - (PHYS_FLASH_2 - PHYS_FLASH_2_BASE) ));
+ else
+ return ((uchar *) (info->start[sect] + (offset * 1)));
+#else
+ return ((uchar *) (info->start[sect] + (offset * 1)));
+#endif
+}
+
+static void reset_flash (flash_info_t * info)
+{
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+
+#if 1
+ ulCtrlData = info->iomode | (info->readcmd << 16) | (info->tCK_Read << 8) | (info->dummybyte << 6) | FASTREAD;
+#else
+ ulCtrlData = (info->readcmd << 16) | (info->tCK_Read << 8) | (info->dummybyte << 6) | FASTREAD;
+ if (info->dualport)
+ ulCtrlData |= 0x08;
+#endif
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+
+}
+
+static void enable_write (flash_info_t * info)
+{
+ ulong base;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+ uchar jReg;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ //base = info->start[0];
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ ulCtrlData = (info->tCK_Write << 8);
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x06);
+ udelay(10);
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x05);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while (!(jReg & 0x02));
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+}
+
+static void write_status_register (flash_info_t * info, uchar data)
+{
+ ulong base;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+ uchar jReg;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ //base = info->start[0];
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ enable_write (info);
+
+ ulCtrlData = (info->tCK_Write << 8);
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x01);
+ udelay(10);
+ *(uchar *) (base) = (uchar) (data);
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x05);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while (jReg & 0x01);
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+}
+
+static void enable4b (flash_info_t * info)
+{
+ ulong base;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ //base = info->start[0];
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ ulCtrlData = (info->tCK_Write << 8);
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0xb7);
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+} /* enable4b */
+
+static void enable4b_spansion (flash_info_t * info)
+{
+ ulong base;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+ uchar jReg;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ //base = info->start[0];
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ /* Enable 4B: BAR0 D[7] = 1 */
+ ulCtrlData = (info->tCK_Write << 8);
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x17);
+ udelay(10);
+ *(uchar *) (base) = (uchar) (0x80);
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x16);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while (!(jReg & 0x80));
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+} /* enable4b_spansion */
+
+static void enable4b_numonyx (flash_info_t * info)
+{
+ ulong base;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ //base = info->start[0];
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ /* Enable Write */
+ enable_write (info);
+
+ /* Enable 4B: CMD:0xB7 */
+ ulCtrlData = (info->tCK_Write << 8);
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0xB7);
+ udelay(10);
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+} /* enable4b_numonyx */
+
+/*-----------------------------------------------------------------------
+ */
+static void flash_write_buffer (flash_info_t *info, uchar *src, ulong addr, int len)
+{
+ ulong j, base, offset;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+ uchar jReg;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ base = info->start[0];
+ offset = addr - base;
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ enable_write (info);
+
+ ulCtrlData = (info->tCK_Write << 8);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x02);
+ udelay(10);
+ if (info->address32)
+ {
+ *(uchar *) (base) = (uchar) ((offset & 0xff000000) >> 24);
+ udelay(10);
+ }
+ *(uchar *) (base) = (uchar) ((offset & 0xff0000) >> 16);
+ udelay(10);
+ *(uchar *) (base) = (uchar) ((offset & 0x00ff00) >> 8);
+ udelay(10);
+ *(uchar *) (base) = (uchar) ((offset & 0x0000ff));
+ udelay(10);
+
+ for (j=0; j<len; j++)
+ {
+ *(uchar *) (base) = *(uchar *) (src++);
+ udelay(10);
+ }
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x05);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while ((jReg & 0x01));
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ /* RFSR */
+ if (info->specificspi == SpecificSPI_N25Q512)
+ {
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x70);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while (!(jReg & 0x80));
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ }
+}
+
+/*-----------------------------------------------------------------------
+ *
+ * export functions
+ *
+ */
+
+
+
+#if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE)
+static flash_info_t *flash_get_info(ulong base)
+{
+ int i;
+ flash_info_t * info = 0;
+
+ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i ++) {
+ info = & flash_info[i];
+ if (info->size && info->start[0] <= base &&
+ base <= info->start[0] + info->size - 1)
+ break;
+ }
+
+ return i == CONFIG_SYS_MAX_FLASH_BANKS ? 0 : info;
+}
+#endif
+
+/*-----------------------------------------------------------------------
+ */
+int flash_erase (flash_info_t * info, int s_first, int s_last)
+{
+ int rcode = 0;
+ int prot;
+ flash_sect_t sect;
+
+ ulong base, offset;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+ uchar jReg;
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ if ((s_first < 0) || (s_first > s_last)) {
+ puts ("- no sectors to erase\n");
+ return 1;
+ }
+
+ prot = 0;
+ for (sect = s_first; sect <= s_last; ++sect) {
+ if (info->protect[sect]) {
+ prot++;
+ }
+ }
+ if (prot) {
+ printf ("- Warning: %d protected sectors will not be erased!\n", prot);
+ } else {
+ putc ('\n');
+ }
+
+ ulCtrlData = (info->tCK_Erase << 8);
+ for (sect = s_first; sect <= s_last; sect++) {
+ if (info->protect[sect] == 0) { /* not protected */
+ /* start erasing */
+ enable_write(info);
+
+ base = info->start[0];
+ offset = info->start[sect] - base;
+ base = (ulong) flash_make_addr(info, 0, 0);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0xd8);
+ udelay(10);
+ if (info->address32)
+ {
+ *(uchar *) (base) = (uchar) ((offset & 0xff000000) >> 24);
+ udelay(10);
+ }
+ *(uchar *) (base) = (uchar) ((offset & 0xff0000) >> 16);
+ udelay(10);
+ *(uchar *) (base) = (uchar) ((offset & 0x00ff00) >> 8);
+ udelay(10);
+ *(uchar *) (base) = (uchar) ((offset & 0x0000ff));
+ udelay(10);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x05);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while ((jReg & 0x01));
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+
+ /* RFSR */
+ if (info->specificspi == SpecificSPI_N25Q512)
+ {
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (base) = (uchar) (0x70);
+ udelay(10);
+ do {
+ jReg = *(volatile uchar *) (base);
+ } while (!(jReg & 0x80));
+ ulCtrlData &= CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ }
+
+ putc ('.');
+ }
+ }
+ puts (" done\n");
+
+ reset_flash(info);
+
+ return rcode;
+}
+
+void flash_print_info (flash_info_t * info)
+{
+ int i;
+
+ if (info->flash_id == FLASH_UNKNOWN) {
+ printf ("missing or unknown FLASH type\n");
+ return;
+ }
+
+ printf("%lx : CS#%ld: ", info->start[0] , info->CE);
+ switch (info->flash_id & 0xff) {
+ case CFI_MFR_MACRONIX: printf ("MACRONIX "); break;
+ case CFI_MFR_MICRON: printf ("MICRON "); break;
+ case CFI_MFR_WINBOND: printf ("WINBOND "); break;
+ default: printf ("Unknown Vendor %lx", info->flash_id); break;
+ }
+
+ if (info->size >= (1 << 20)) {
+ i = 20;
+ } else {
+ i = 10;
+ }
+ printf (" Size: %ld %cB in %d Sectors\n",
+ info->size >> i,
+ (i == 20) ? 'M' : 'k',
+ info->sector_count);
+
+ return;
+}
+
+/*-----------------------------------------------------------------------
+ * Copy memory to flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ */
+int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
+{
+ int count;
+ unsigned char pat[] = {'|', '-', '/', '\\'};
+ int patcnt = 0;
+ ulong BufferSize = info->buffersize;
+ /* get lower aligned address */
+ if (addr & (BufferSize - 1))
+ {
+ count = cnt >= BufferSize ? (BufferSize - (addr & 0xff)):cnt;
+ flash_write_buffer (info, src, addr, count);
+ addr+= count;
+ src += count;
+ cnt -= count;
+ }
+
+ /* prog */
+ while (cnt > 0) {
+ count = cnt >= BufferSize ? BufferSize:cnt;
+ flash_write_buffer (info, src, addr, count);
+ addr+= count;
+ src += count;
+ cnt -= count;
+ printf("%c\b", pat[(patcnt++) & 0x03]);
+ }
+
+ reset_flash(info);
+
+ return (0);
+}
+
+static ulong flash_get_size (ulong base, flash_info_t *info)
+{
+ int j;
+ unsigned long sector;
+ int erase_region_size;
+ ulong ulCtrlData, CtrlOffset = CS0_CTRL;
+ ulong ulID;
+ uchar ch[3];
+ ulong reg;
+ ulong WriteClk, EraseClk, ReadClk;
+ ulong vbase;
+
+ info->start[0] = base;
+// printf("base %x \n",base);
+ vbase = (ulong) flash_make_addr(info, 0, 0);
+
+ switch(info->CE) {
+ case 0:
+ CtrlOffset = CS0_CTRL;
+ break;
+ case 1:
+ ast_scu_multi_func_romcs(1);
+ CtrlOffset = CS1_CTRL;
+ break;
+ case 2:
+ ast_scu_multi_func_romcs(2);
+ CtrlOffset = CS2_CTRL;
+ break;
+ }
+
+ /* Get Flash ID */
+ ulCtrlData = *(ulong *) (info->reg_base + CtrlOffset) & CMD_MASK;
+ ulCtrlData |= CE_LOW | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ *(uchar *) (vbase) = (uchar) (0x9F);
+ udelay(10);
+ ch[0] = *(volatile uchar *)(vbase);
+ udelay(10);
+ ch[1] = *(volatile uchar *)(vbase);
+ udelay(10);
+ ch[2] = *(volatile uchar *)(vbase);
+ udelay(10);
+ ulCtrlData = *(ulong *) (info->reg_base + CtrlOffset) & CMD_MASK;
+ ulCtrlData |= CE_HIGH | USERMODE;
+ *(ulong *) (info->reg_base + CtrlOffset) = ulCtrlData;
+ udelay(200);
+ ulID = ((ulong)ch[0]) | ((ulong)ch[1] << 8) | ((ulong)ch[2] << 16) ;
+ info->flash_id = ulID;
+
+// printf("SPI Flash ID: %x \n", ulID);
+
+ /* init default */
+ info->iomode = IOMODEx1;
+ info->address32 = 0;
+ info->quadport = 0;
+ info->specificspi = 0;
+
+ switch (info->flash_id) {
+ case STM25P64:
+ info->sector_count = 128;
+ info->size = 0x800000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 40;
+ EraseClk = 20;
+ ReadClk = 40;
+ break;
+
+ case STM25P128:
+ info->sector_count = 64;
+ info->size = 0x1000000;
+ erase_region_size = 0x40000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+ break;
+
+ case N25Q256:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+#if 1
+ info->sector_count = 512;
+ info->size = 0x2000000;
+ info->address32 = 1;
+#endif
+ break;
+
+ case N25Q512:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ info->specificspi = SpecificSPI_N25Q512;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+#if 1
+ info->sector_count = 1024;
+ info->size = 0x4000000;
+ info->address32 = 1;
+#endif
+ break;
+
+ case W25X16:
+ info->sector_count = 32;
+ info->size = 0x200000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x3b;
+ info->dualport = 1;
+ info->dummybyte = 1;
+ info->iomode = IOMODEx2;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case W25X64:
+ info->sector_count = 128;
+ info->size = 0x800000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x3b;
+ info->dualport = 1;
+ info->dummybyte = 1;
+ info->iomode = IOMODEx2;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case W25Q64BV:
+ info->sector_count = 128;
+ info->size = 0x800000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x3b;
+ info->dualport = 1;
+ info->dummybyte = 1;
+ info->iomode = IOMODEx2;
+ info->buffersize = 256;
+ WriteClk = 80;
+ EraseClk = 40;
+ ReadClk = 80;
+ break;
+
+ case W25Q128BV:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x3b;
+ info->dualport = 1;
+ info->dummybyte = 1;
+ info->iomode = IOMODEx2;
+ info->buffersize = 256;
+ WriteClk = 104;
+ EraseClk = 50;
+ ReadClk = 104;
+ break;
+
+ case W25Q256FV:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+#if 1
+ info->sector_count = 512;
+ info->size = 0x2000000;
+ info->address32 = 1;
+#endif
+ break;
+
+ case S25FL064A:
+ info->sector_count = 128;
+ info->size = 0x800000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case S25FL128P:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 100;
+ EraseClk = 40;
+ ReadClk = 100;
+ break;
+
+ case S25FL256S:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+#if 1
+ info->sector_count = 512;
+ info->size = 0x2000000;
+ info->address32 = 1;
+#endif
+ break;
+
+ case MX25L25635E:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+#if 1
+ info->sector_count = 512;
+ info->size = 0x2000000;
+ info->address32 = 1;
+#if defined(CONFIG_FLASH_SPIx2_Dummy)
+ info->readcmd = 0xbb;
+ info->dummybyte = 1;
+ info->dualport = 1;
+ info->iomode = IOMODEx2_dummy;
+#elif defined(CONFIG_FLASH_SPIx4_Dummy)
+ info->readcmd = 0xeb;
+ info->dummybyte = 3;
+ info->dualport = 0;
+ info->iomode = IOMODEx4_dummy;
+ info->quadport = 1;
+ info->dummydata = 0xaa;
+#endif
+#endif
+ break;
+
+ case MX66L51235F:
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 512;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+#if 1
+ info->sector_count = 1024;
+ info->size = 0x4000000;
+ info->address32 = 1;
+#if defined(CONFIG_FLASH_SPIx2_Dummy)
+ info->readcmd = 0xbb;
+ info->dummybyte = 1;
+ info->dualport = 1;
+ info->iomode = IOMODEx2_dummy;
+#elif defined(CONFIG_FLASH_SPIx4_Dummy)
+ info->readcmd = 0xeb;
+ info->dummybyte = 3;
+ info->dualport = 0;
+ info->iomode = IOMODEx4_dummy;
+ info->quadport = 1;
+ info->dummydata = 0xaa;
+#endif
+#endif
+ break;
+
+ case MX25L12805D:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+
+#if 1
+#if defined(CONFIG_FLASH_SPIx2_Dummy)
+ info->readcmd = 0xbb;
+ info->dummybyte = 1;
+ info->dualport = 1;
+ info->iomode = IOMODEx2_dummy;
+#elif defined(CONFIG_FLASH_SPIx4_Dummy)
+ info->readcmd = 0xeb;
+ info->dummybyte = 3;
+ info->dualport = 0;
+ info->iomode = IOMODEx4_dummy;
+ info->quadport = 1;
+ info->dummydata = 0xaa;
+#endif
+#endif
+ break;
+
+ case MX25L1605D:
+ info->sector_count = 32;
+ info->size = 0x200000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 256;
+ WriteClk = 50;
+ EraseClk = 20;
+ ReadClk = 50;
+ break;
+
+ case SST25VF016B:
+ info->sector_count = 32;
+ info->size = 0x200000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 1;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case SST25VF064C:
+ info->sector_count = 128;
+ info->size = 0x800000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 1;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case SST25VF040B:
+ info->sector_count = 8;
+ info->size = 0x80000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 1;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case AT25DF161:
+ info->sector_count = 32;
+ info->size = 0x200000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 1;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ case AT25DF321:
+ info->sector_count = 32;
+ info->size = 0x400000;
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 1;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ break;
+
+ default: /* use JEDEC ID */
+ printf("Unsupported SPI Flash!! 0x%08lx\n", info->flash_id);
+ erase_region_size = 0x10000;
+ info->readcmd = 0x0b;
+ info->dualport = 0;
+ info->dummybyte = 1;
+ info->buffersize = 1;
+ WriteClk = 50;
+ EraseClk = 25;
+ ReadClk = 50;
+ if ((info->flash_id & 0xFF) == 0x1F) {
+ /* Atmel */
+ switch (info->flash_id & 0x001F00) {
+ case 0x000400:
+ info->sector_count = 8;
+ info->size = 0x80000;
+ break;
+ case 0x000500:
+ info->sector_count = 16;
+ info->size = 0x100000;
+ break;
+ case 0x000600:
+ info->sector_count = 32;
+ info->size = 0x200000;
+ break;
+ case 0x000700:
+ info->sector_count = 64;
+ info->size = 0x400000;
+ break;
+ case 0x000800:
+ info->sector_count = 128;
+ info->size = 0x800000;
+ break;
+ case 0x000900:
+ info->sector_count = 256;
+ info->size = 0x1000000;
+ break;
+ default:
+ printf("Can't support this SPI Flash!! \n");
+ return 0;
+ }
+ } else {
+ /* JDEC */
+ switch (info->flash_id & 0xFF0000)
+ {
+ case 0x120000:
+ info->sector_count = 4;
+ info->size = 0x40000;
+ break;
+ case 0x130000:
+ info->sector_count = 8;
+ info->size = 0x80000;
+ break;
+ case 0x140000:
+ info->sector_count =16;
+ info->size = 0x100000;
+ break;
+ case 0x150000:
+ info->sector_count =32;
+ info->size = 0x200000;
+ break;
+ case 0x160000:
+ info->sector_count =64;
+ info->size = 0x400000;
+ break;
+ case 0x170000:
+ info->sector_count =128;
+ info->size = 0x800000;
+ break;
+ case 0x180000:
+ info->sector_count =256;
+ info->size = 0x1000000;
+ break;
+ case 0x190000:
+ info->sector_count =256;
+ info->size = 0x1000000;
+#if 1
+ info->sector_count = 512;
+ info->size = 0x2000000;
+ info->address32 = 1;
+#if defined(CONFIG_FLASH_SPIx2_Dummy)
+ info->readcmd = 0xbb;
+ info->dummybyte = 1;
+ info->dualport = 1;
+ info->iomode = IOMODEx2_dummy;
+#elif defined(CONFIG_FLASH_SPIx4_Dummy)
+ info->readcmd = 0xeb;
+ info->dummybyte = 3;
+ info->dualport = 0;
+ info->iomode = IOMODEx4_dummy;
+ info->quadport = 1;
+ info->dummydata = 0xaa;
+#endif
+#endif
+ break;
+
+ case 0x200000:
+ info->sector_count =256;
+ info->size = 0x1000000;
+ if ((info->flash_id & 0xFF) == 0x20) /* numonyx */
+ info->specificspi = SpecificSPI_N25Q512;
+#if 1
+ info->sector_count = 1024;
+ info->size = 0x4000000;
+ info->address32 = 1;
+#if defined(CONFIG_FLASH_SPIx2_Dummy)
+ info->readcmd = 0xbb;
+ info->dummybyte = 1;
+ info->dualport = 1;
+ info->iomode = IOMODEx2_dummy;
+#elif defined(CONFIG_FLASH_SPIx4_Dummy)
+ info->readcmd = 0xeb;
+ info->dummybyte = 3;
+ info->dualport = 0;
+ info->iomode = IOMODEx4_dummy;
+ info->quadport = 1;
+ info->dummydata = 0xaa;
+#endif
+#endif
+ break;
+
+ default:
+ printf("Can't support this SPI Flash!! \n");
+ return 0;
+ }
+ } /* JDEC */
+ }
+
+ sector = base;
+ for (j = 0; j < info->sector_count; j++) {
+
+ info->start[j] = sector;
+ sector += erase_region_size;
+ info->protect[j] = 0; /* default: not protected */
+ }
+
+ /* limit Max SPI CLK to 50MHz (Datasheet v1.2) */
+ if (WriteClk > 50) WriteClk = 50;
+ if (EraseClk > 50) EraseClk = 50;
+ if (ReadClk > 50) ReadClk = 50;
+
+ info->tCK_Write = ast_spi_calculate_divisor(WriteClk*1000000);
+ info->tCK_Erase = ast_spi_calculate_divisor(EraseClk*1000000);
+ info->tCK_Read = ast_spi_calculate_divisor(ReadClk*1000000);
+
+ /* unprotect flash */
+ write_status_register(info, 0);
+
+ if (info->quadport)
+ write_status_register(info, 0x40); /* enable QE */
+
+ if (info->address32) {
+#ifndef AST_SOC_G5
+ reg = *((volatile ulong*) 0x1e6e2070); /* set H/W Trappings */
+ reg |= 0x10;
+ *((volatile ulong*) 0x1e6e2070) = reg;
+#endif
+ reg = *((volatile ulong*) (info->reg_base + 0x4)); /* enable 32b control bit*/
+ reg |= (0x01 << info->CE);
+ *((volatile ulong*) (info->reg_base + 0x4)) = reg;
+
+ /* set flash chips to 32bits addressing mode */
+ if ((info->flash_id & 0xFF) == 0x01) /* Spansion */
+ enable4b_spansion(info);
+ else if ((info->flash_id & 0xFF) == 0x20) /* Numonyx */
+ enable4b_numonyx(info);
+ else /* MXIC, Winbond */
+ enable4b(info);
+ }
+
+ reset_flash(info);
+// printf("%08x \n", info->size);
+ return (info->size);
+}
+
+/*-----------------------------------------------------------------------*/
+unsigned long flash_init (void)
+{
+ unsigned long size = 0;
+ int i;
+
+ *((volatile ulong*) AST_FMC_BASE) |= 0x800f0000; /* enable Flash Write */
+
+ /* Init: FMC */
+ /* BANK 0 : FMC CS0 , 1: FMC CS1, */
+ for (i = 0; i < CONFIG_FMC_CS; ++i) {
+ flash_info[i].sysspi = 0;
+ flash_info[i].reg_base = AST_FMC_BASE;
+ flash_info[i].flash_id = FLASH_UNKNOWN;
+ flash_info[i].CE = i;
+ switch(i) {
+ case 0:
+ size += flash_info[i].size = flash_get_size(AST_FMC_CS0_BASE, &flash_info[i]);
+ break;
+ case 1:
+ size += flash_info[i].size = flash_get_size(AST_FMC_CS1_BASE, &flash_info[i]);
+ break;
+ default:
+ printf("TODO ~~~~ \n");
+ break;
+ }
+ if (flash_info[i].flash_id == FLASH_UNKNOWN) {
+ printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n",
+ i, flash_info[i].size, flash_info[i].size << 20);
+ }
+ }
+
+ /* BANK 2:SYSSPI CS0 */
+#ifdef CONFIG_SPI0_CS
+ //pin switch by trap[13:12] -- [0:1] Enable SPI Master
+ ast_scu_spi_master(1); /* enable SPI master */
+ *((volatile ulong*) AST_FMC_SPI0_BASE) |= 0x10000; /* enable Flash Write */
+ flash_info[CONFIG_FMC_CS].sysspi = 1;
+ flash_info[CONFIG_FMC_CS].reg_base = AST_FMC_SPI0_BASE;
+ flash_info[CONFIG_FMC_CS].flash_id = FLASH_UNKNOWN;
+ flash_info[CONFIG_FMC_CS].CE = 0;
+ size += flash_info[CONFIG_FMC_CS].size = flash_get_size(AST_SPI0_CS0_BASE, &flash_info[CONFIG_FMC_CS]);
+ if (flash_info[2].flash_id == FLASH_UNKNOWN) {
+ printf ("## Unknown FLASH on Bank 2 SYS SPI - Size = 0x%08lx = %ld MB\n",
+ flash_info[CONFIG_FMC_CS].size, flash_info[CONFIG_FMC_CS].size << 20);
+ }
+#endif
+
+ /* Monitor protection ON by default */
+#if (CONFIG_MONITOR_BASE >= AST_FMC_CS0_BASE)
+ flash_protect (FLAG_PROTECT_SET,
+ CONFIG_MONITOR_BASE,
+ CONFIG_MONITOR_BASE + monitor_flash_len - 1,
+ flash_get_info(CONFIG_MONITOR_BASE));
+#endif
+
+ /* Environment protection ON by default */
+#ifdef CONFIG_ENV_IS_IN_FLASH
+ flash_protect (FLAG_PROTECT_SET,
+ CONFIG_ENV_ADDR,
+ CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1,
+ flash_get_info(CONFIG_ENV_ADDR));
+#endif
+
+ /* Redundant environment protection ON by default */
+#ifdef CONFIG_ENV_ADDR_REDUND
+ flash_protect (FLAG_PROTECT_SET,
+ CONFIG_ENV_ADDR_REDUND,
+ CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SIZE_REDUND - 1,
+ flash_get_info(CONFIG_ENV_ADDR_REDUND));
+#endif
+
+ return (size);
+}
+
+void memmove_dma(void * dest,const void *src,size_t count)
+{
+ ulong count_align, poll_time, data;
+
+ count_align = (count + 3) & 0xFFFFFFFC; /* 4-bytes align */
+ poll_time = 100; /* set 100 us as default */
+
+ /* force end of burst read */
+ *(volatile ulong *) (AST_FMC_BASE + CS0_CTRL) |= CE_HIGH;
+ *(volatile ulong *) (AST_FMC_BASE + CS0_CTRL) &= ~CE_HIGH;
+
+ *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_CONTROL) = (ulong) (~FLASH_DMA_ENABLE);
+ *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_FLASH_BASE) = (ulong) (src);
+ *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_DRAM_BASE) = (ulong) (dest);
+ *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_LENGTH) = (ulong) (count_align);
+ *(ulong *) (AST_FMC_BASE + REG_FLASH_DMA_CONTROL) = (ulong) (FLASH_DMA_ENABLE);
+
+ /* wait poll */
+ do {
+ udelay(poll_time);
+ data = *(ulong *) (AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS);
+ } while (!(data & FLASH_STATUS_DMA_READY));
+
+ /* clear status */
+ *(ulong *) (AST_FMC_BASE + REG_FLASH_INTERRUPT_STATUS) |= FLASH_STATUS_DMA_CLEAR;
+}
OpenPOWER on IntegriCloud