From aee2b6dbdd7e813a3ddc9471eb81b359dd163c24 Mon Sep 17 00:00:00 2001 From: Prabhu Jayakumar Date: Tue, 3 May 2016 18:32:47 +0530 Subject: [PATCH] MIPS: qca956x: enable the flash driver This change enables the flash driver on u-boot for the AP152 target. Change-Id: I9778adf4a4d347fdcce16fbfede87f765302595c Signed-off-by: Prabhu Jayakumar --- board/qca/mips32/common/ath_flash.c | 436 ++++++++++++++++++++++++++++ board/qca/mips32/common/ath_flash.h | 53 ++++ board/qca/mips32/qca956x/Makefile | 3 +- board/qca/mips32/qca956x/flash.c | 46 +++ include/configs/qca956x.h | 9 +- include/flash.h | 1 + 6 files changed, 544 insertions(+), 4 deletions(-) create mode 100644 board/qca/mips32/common/ath_flash.c create mode 100644 board/qca/mips32/common/ath_flash.h create mode 100644 board/qca/mips32/qca956x/flash.c diff --git a/board/qca/mips32/common/ath_flash.c b/board/qca/mips32/common/ath_flash.c new file mode 100644 index 0000000000..48bf5a45bf --- /dev/null +++ b/board/qca/mips32/common/ath_flash.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include "ath_flash.h" + +#if !defined(ATH_DUAL_FLASH) +# define ath_spi_flash_print_info flash_print_info +#endif + +#define ATH_16M_FLASH_SIZE 0x1000000 +#define ATH_GET_EXT_3BS(addr) ((addr) % ATH_16M_FLASH_SIZE) +#define ATH_GET_EXT_4B(addr) ((addr) >> 24) + + +/* + * globals + */ +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + +/* + * statics + */ +static void ath_spi_write_enable(void); +static void ath_spi_poll(void); +#if !defined(ATH_SST_FLASH) +static void ath_spi_write_page(uint32_t addr, uint8_t * data, int len); +#endif +static void ath_spi_sector_erase(uint32_t addr); + +#if defined(ATH_DUAL_NOR) +static void ath_spi_enter_ext_addr(u8 addr) +{ + ath_spi_write_enable(); + ath_reg_wr_nf(ATH_SPI_WRITE, ATH_SPI_CS_DIS); + ath_spi_bit_banger(ATH_SPI_CMD_WR_EXT); + ath_spi_bit_banger(addr); + ath_spi_go(); + ath_spi_poll(); +} + +static void ath_spi_exit_ext_addr(int ext) +{ + if (ext) + ath_spi_enter_ext_addr(0); +} +#else +#define ath_spi_enter_ext_addr(addr) +#define ath_spi_exit_ext_addr(ext) +#endif + +static void +ath_spi_read_id(void) +{ + u32 rd; + + ath_reg_wr_nf(ATH_SPI_WRITE, ATH_SPI_CS_DIS); + ath_spi_bit_banger(ATH_SPI_CMD_RDID); + ath_spi_delay_8(); + ath_spi_delay_8(); + ath_spi_delay_8(); + ath_spi_go(); + + rd = ath_reg_rd(ATH_SPI_RD_STATUS); + + printf("Flash Manuf Id 0x%x, DeviceId0 0x%x, DeviceId1 0x%x\n", + (rd >> 16) & 0xff, (rd >> 8) & 0xff, (rd >> 0) & 0xff); +} + + +#ifdef ATH_SST_FLASH +void ath_spi_flash_unblock(void) +{ + ath_spi_write_enable(); + ath_spi_bit_banger(ATH_SPI_CMD_WRITE_SR); + ath_spi_bit_banger(0x0); + ath_spi_go(); + ath_spi_poll(); +} +#endif + +#if defined(CONFIG_ATH_SPI_CS1_GPIO) + +#define ATH_SPI_CS0_GPIO 5 + +static void ath_gpio_config_output(int gpio) +{ +#if defined(CONFIG_MACH_AR934x) || \ + defined(CONFIG_MACH_QCA955x) || \ + defined(CONFIG_MACH_QCA953x) || \ + defined(CONFIG_MACH_QCA956x) + ath_reg_rmw_clear(ATH_GPIO_OE, (1 << gpio)); +#else + ath_reg_rmw_set(ATH_GPIO_OE, (1 << gpio)); +#endif +} + +static void ath_gpio_set_fn(int gpio, int fn) +{ +#define gpio_fn_reg(x) ((x) / 4) +#define gpio_lsb(x) (((x) % 4) * 8) +#define gpio_msb(x) (gpio_lsb(x) + 7) +#define gpio_mask(x) (0xffu << gpio_lsb(x)) +#define gpio_set(x, f) (((f) & 0xffu) << gpio_lsb(x)) + + uint32_t *reg = ((uint32_t *)GPIO_OUT_FUNCTION0_ADDRESS) + + gpio_fn_reg(gpio); + + ath_reg_wr(reg, (ath_reg_rd(reg) & ~gpio_mask(gpio)) | gpio_set(gpio, fn)); +} + +int ath_spi_flash_get_fn_cs0(void) +{ +#if CONFIG_MACH_QCA934x + return 0x09; +#elif (CONFIG_MACH_QCA953x || CONFIG_MACH_QCA955x) + return 0x09; +#endif + return -1; +} + +int ath_spi_flash_get_fn_cs1(void) +{ +#if CONFIG_MACH_QCA934x + return 0x07; +#elif (CONFIG_MACH_QCA953x || CONFIG_MACH_QCA955x) + return 0x0a; +#endif + return -1; +} + +void ath_spi_flash_enable_cs1(void) +{ + int fn = ath_spi_flash_get_fn_cs1(); + + if (fn < 0) { + printf("Error, enable SPI_CS_1 failed\n"); + return; + } + + ath_gpio_set_fn(CONFIG_ATH_SPI_CS1_GPIO, + ath_spi_flash_get_fn_cs1()); + ath_gpio_config_output(CONFIG_ATH_SPI_CS1_GPIO); +} +#else +#define ath_spi_flash_enable_cs1(...) +#endif + +int flash_select(int chip) +{ +#if defined(CONFIG_ATH_SPI_CS1_GPIO) + int fn_cs0, fn_cs1; + + fn_cs0 = ath_spi_flash_get_fn_cs0(); + fn_cs1 = ath_spi_flash_get_fn_cs1(); + + if (fn_cs0 < 0 || fn_cs1 < 0) { + printf("Error, flash select failed\n"); + return -1; + } +#endif + + switch (chip) { + case 0: +#if defined(CONFIG_ATH_SPI_CS1_GPIO) + ath_gpio_set_fn(CONFIG_ATH_SPI_CS1_GPIO, fn_cs1); + ath_gpio_set_fn(ATH_SPI_CS0_GPIO, fn_cs0); +#elif defined(ATH_DUAL_NOR) + ath_reg_rmw_set(ATH_SPI_FS, 1); + ath_spi_exit_ext_addr(1); + ath_reg_rmw_clear(ATH_SPI_FS, 1); +#endif + break; + + case 1: +#if defined(CONFIG_ATH_SPI_CS1_GPIO) + ath_gpio_set_fn(ATH_SPI_CS0_GPIO, 0); + ath_gpio_set_fn(CONFIG_ATH_SPI_CS1_GPIO, fn_cs0); +#elif defined(ATH_DUAL_NOR) + ath_reg_rmw_set(ATH_SPI_FS, 1); + ath_spi_enter_ext_addr(1); + ath_reg_rmw_clear(ATH_SPI_FS, 1); +#endif + break; + + default: + printf("Error, please specify correct flash number 0/1\n"); + return -1; + } + + return 0; +} + +unsigned long flash_init(void) +{ +#if !(defined(CONFIG_WASP_SUPPORT) || defined(CONFIG_MACH_QCA955x) || defined(CONFIG_MACH_QCA956x)) +#ifdef ATH_SST_FLASH + ath_reg_wr_nf(ATH_SPI_CLOCK, 0x3); + ath_spi_flash_unblock(); + ath_reg_wr(ATH_SPI_FS, 0); +#else + ath_reg_wr_nf(ATH_SPI_CLOCK, 0x43); +#endif +#endif + +#if defined(CONFIG_MACH_QCA953x) /* Added for HB-SMIC */ +#ifdef ATH_SST_FLASH + ath_reg_wr_nf(ATH_SPI_CLOCK, 0x4); + ath_spi_flash_unblock(); + ath_reg_wr(ATH_SPI_FS, 0); +#else + ath_reg_wr_nf(ATH_SPI_CLOCK, 0x44); +#endif +#endif + ath_reg_rmw_set(ATH_SPI_FS, 1); + ath_spi_read_id(); + ath_spi_exit_ext_addr(1); + ath_reg_rmw_clear(ATH_SPI_FS, 1); + + ath_spi_flash_enable_cs1(); + /* + * hook into board specific code to fill flash_info + */ + return (flash_get_geom(&flash_info[0])); +} + +void +ath_spi_flash_print_info(flash_info_t *info) +{ + printf("The hell do you want flinfo for??\n"); +} + +int +flash_erase(flash_info_t *info, int s_first, int s_last) +{ + u32 addr; + int ext = 0; + int i, sector_size = info->size / info->sector_count; + + printf("\nFirst %#x last %#x sector size %#x\n", + s_first, s_last, sector_size); + + addr = s_first * sector_size; + if (addr >= ATH_16M_FLASH_SIZE) { + ext = 1; + ath_spi_enter_ext_addr(ATH_GET_EXT_4B(addr)); + } else if (s_last * sector_size >= ATH_16M_FLASH_SIZE) { + printf("Erase failed, cross 16M is forbidden\n"); + return -1; + } + + for (i = s_first; i <= s_last; i++) { + addr = i * sector_size; + + if (ext) + addr = ATH_GET_EXT_3BS(addr); + + printf("\b\b\b\b%4d", i); + ath_spi_sector_erase(addr); + } + + ath_spi_exit_ext_addr(ext); + + ath_spi_done(); + printf("\n"); + + return 0; +} + +/* + * Write a buffer from memory to flash: + * 0. Assumption: Caller has already erased the appropriate sectors. + * 1. call page programming for every 256 bytes + */ +#ifdef ATH_SST_FLASH +void +ath_spi_flash_chip_erase(void) +{ + ath_spi_write_enable(); + ath_spi_bit_banger(ATH_SPI_CMD_CHIP_ERASE); + ath_spi_go(); + ath_spi_poll(); +} + +int +write_buff(flash_info_t *info, uchar *src, ulong dst, ulong len) +{ + uint32_t val; + + dst = dst - CFG_FLASH_BASE; + printf("write len: %lu dst: 0x%x src: %p\n", len, dst, src); + + for (; len; len--, dst++, src++) { + ath_spi_write_enable(); // dont move this above 'for' + ath_spi_bit_banger(ATH_SPI_CMD_PAGE_PROG); + ath_spi_send_addr(dst); + + val = *src & 0xff; + ath_spi_bit_banger(val); + + ath_spi_go(); + ath_spi_poll(); + } + /* + * Disable the Function Select + * Without this we can't read from the chip again + */ + ath_reg_wr(ATH_SPI_FS, 0); + + if (len) { + // how to differentiate errors ?? + return ERR_PROG_ERROR; + } else { + return ERR_OK; + } +} +#else +int +write_buff(flash_info_t *info, uchar *source, ulong addr, ulong len) +{ + int total = 0, len_this_lp, bytes_this_page; + ulong dst; + uchar *src; + int ext = 0; + + printf("write addr: %x\n", addr); + addr = addr - CFG_FLASH_BASE; + + if (addr >= ATH_16M_FLASH_SIZE) { + ext = 1; + ath_spi_enter_ext_addr(ATH_GET_EXT_4B(addr)); + addr = ATH_GET_EXT_3BS(addr); + } else if (addr + len >= ATH_16M_FLASH_SIZE) { + printf("Write failed, cross 16M is forbidden\n"); + return -1; + } + + while (total < len) { + src = source + total; + dst = addr + total; + bytes_this_page = + ATH_SPI_PAGE_SIZE - (addr % ATH_SPI_PAGE_SIZE); + len_this_lp = + ((len - total) > + bytes_this_page) ? bytes_this_page : (len - total); + ath_spi_write_page(dst, src, len_this_lp); + total += len_this_lp; + } + + ath_spi_exit_ext_addr(ext); + + ath_spi_done(); + + return 0; +} +#endif + +static void +ath_spi_write_enable() +{ + ath_reg_wr_nf(ATH_SPI_FS, 1); + ath_reg_wr_nf(ATH_SPI_WRITE, ATH_SPI_CS_DIS); + ath_spi_bit_banger(ATH_SPI_CMD_WREN); + ath_spi_go(); +} + +static void +ath_spi_poll() +{ + int rd; + + do { + ath_reg_wr_nf(ATH_SPI_WRITE, ATH_SPI_CS_DIS); + ath_spi_bit_banger(ATH_SPI_CMD_RD_STATUS); + ath_spi_delay_8(); + rd = (ath_reg_rd(ATH_SPI_RD_STATUS) & 1); + } while (rd); +} + +#if !defined(ATH_SST_FLASH) +static void +ath_spi_write_page(uint32_t addr, uint8_t *data, int len) +{ + int i; + uint8_t ch; + + display(0x77); + ath_spi_write_enable(); + ath_spi_bit_banger(ATH_SPI_CMD_PAGE_PROG); + ath_spi_send_addr(addr); + + for (i = 0; i < len; i++) { + ch = *(data + i); + ath_spi_bit_banger(ch); + } + + ath_spi_go(); + display(0x66); + ath_spi_poll(); + display(0x6d); +} +#endif + +static void +ath_spi_sector_erase(uint32_t addr) +{ + ath_spi_write_enable(); + ath_spi_bit_banger(ATH_SPI_CMD_SECTOR_ERASE); + ath_spi_send_addr(addr); + ath_spi_go(); + display(0x7d); + ath_spi_poll(); +} + +#ifdef ATH_DUAL_FLASH +void flash_print_info(flash_info_t *info) +{ + ath_spi_flash_print_info(NULL); + ath_nand_flash_print_info(NULL); +} +#endif diff --git a/board/qca/mips32/common/ath_flash.h b/board/qca/mips32/common/ath_flash.h new file mode 100644 index 0000000000..6613fe1ce3 --- /dev/null +++ b/board/qca/mips32/common/ath_flash.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _ATH_FLASH_H +#define _ATH_FLASH_H + +#define display(_x) + +/* + * primitives + */ + +#define ath_be_msb(_val, _i) (((_val) & (1 << (7 - _i))) >> (7 - _i)) + +#define ath_spi_bit_banger(_byte) do { \ + int i; \ + for(i = 0; i < 8; i++) { \ + ath_reg_wr_nf(ATH_SPI_WRITE, \ + ATH_SPI_CE_LOW | ath_be_msb(_byte, i)); \ + ath_reg_wr_nf(ATH_SPI_WRITE, \ + ATH_SPI_CE_HIGH | ath_be_msb(_byte, i)); \ + } \ +} while (0) + +#define ath_spi_go() do { \ + ath_reg_wr_nf(ATH_SPI_WRITE, ATH_SPI_CE_LOW); \ + ath_reg_wr_nf(ATH_SPI_WRITE, ATH_SPI_CS_DIS); \ +} while (0) + + +#define ath_spi_send_addr(__a) do { \ + ath_spi_bit_banger(((__a & 0xff0000) >> 16)); \ + ath_spi_bit_banger(((__a & 0x00ff00) >> 8)); \ + ath_spi_bit_banger(__a & 0x0000ff); \ +} while (0) + +#define ath_spi_delay_8() ath_spi_bit_banger(0) +#define ath_spi_done() ath_reg_wr_nf(ATH_SPI_FS, 0) + +extern unsigned long flash_get_geom (flash_info_t *flash_info); + +#endif /* _ATH_FLASH_H */ diff --git a/board/qca/mips32/qca956x/Makefile b/board/qca/mips32/qca956x/Makefile index 889389a0f9..c47f88ae4f 100644 --- a/board/qca/mips32/qca956x/Makefile +++ b/board/qca/mips32/qca956x/Makefile @@ -1,3 +1,4 @@ obj-y := board956x.o extra.o ../common/init-956x.o \ ../common/956x.o ../common/tap-956x.o \ - ../common/ath_serial.o ../common/qca-mach-common.o + ../common/ath_serial.o ../common/qca-mach-common.o \ + flash.o ../common/ath_flash.o diff --git a/board/qca/mips32/qca956x/flash.c b/board/qca/mips32/qca956x/flash.c new file mode 100644 index 0000000000..291a785a22 --- /dev/null +++ b/board/qca/mips32/qca956x/flash.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include +#include +#include +#include + +#ifndef CONFIG_ATH_NAND_BR +/* + * sets up flash_info and returns size of FLASH (bytes) + */ +unsigned long +flash_get_geom (flash_info_t *flash_info) +{ + int i; + + /* XXX this is hardcoded until we figure out how to read flash id */ + + flash_info->flash_id = FLASH_M25P64; + flash_info->size = CFG_FLASH_SIZE; /* bytes */ + flash_info->sector_count = flash_info->size / CFG_FLASH_SECTOR_SIZE; + + for (i = 0; i < flash_info->sector_count; i++) { + flash_info->start[i] = CFG_FLASH_BASE + + (i * CFG_FLASH_SECTOR_SIZE); + flash_info->protect[i] = 0; + } + + printf ("flash size %dMB, sector count = %d\n", + FLASH_SIZE, flash_info->sector_count); + + return (flash_info->size); +} +#endif /* CONFIG_ATH_NAND_BR */ diff --git a/include/configs/qca956x.h b/include/configs/qca956x.h index 606dfbf9d5..67c4083c9e 100644 --- a/include/configs/qca956x.h +++ b/include/configs/qca956x.h @@ -142,9 +142,7 @@ #define CFG_FLASH_SECTOR_SIZE_F CFG_FLASH_SECTOR_SIZE #endif -#define CONFIG_ENV_IS_NOWHERE 1 -#define CONFIG_SYS_NO_FLASH 1 -//#define CONFIG_ENV_IS_IN_FLASH 1 +#define CONFIG_ENV_IS_IN_FLASH 1 #define CONFIG_ENV_SIZE CFG_FLASH_SECTOR_SIZE //CFG_ENV_SIZE #define CONFIG_ENV_ADDR 0x9f040000 @@ -165,6 +163,11 @@ #define __CONFIG_BOARD_NAME ap152 #define CONFIG_BOARD_NAME "ap152" +#if defined(CONFIG_CUS249) || defined(CONFIG_TB753) +#else +#define CONFIG_USB 1 +#endif + /* ** Parameters defining the location of the calibration/initialization ** information for the two Merlin devices. diff --git a/include/flash.h b/include/flash.h index f53ace7889..cddcdbe8b6 100644 --- a/include/flash.h +++ b/include/flash.h @@ -468,6 +468,7 @@ extern flash_info_t *flash_get_info(ulong base); #define FLASH_STM32F4 0x00F2 /* STM32F4 Embedded Flash */ #define FLASH_STM32F1 0x00F3 /* STM32F1 Embedded Flash */ +#define FLASH_M25P64 0x00F2 #define FLASH_UNKNOWN 0xFFFF /* unknown flash type */