| /* |
| * MTD SPI driver for ST M25Pxx flash chips |
| * |
| * Author: Mike Lavender, mike@steroidmicros.com |
| * |
| * Copyright (c) 2005, Intec Automation Inc. |
| * Copyright (c) 2012, CradlePoint Technology, Inc. |
| * |
| * Some parts are based on lart.c by Abraham Van Der Merwe |
| * |
| * Cleaned up and generalized based on mtd_dataflash.c |
| * |
| * Ralink SPI updates and adjustments by CradlePoint |
| * |
| * This code is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| */ |
| #include <linux/module.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/sched.h> |
| #include <linux/slab.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/map.h> |
| #include <linux/mtd/partitions.h> |
| #include <linux/mtd/physmap.h> |
| #include <linux/reboot.h> |
| #include <linux/semaphore.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| |
| #include "../maps/ralink-flash.h" |
| |
| #if defined (CONFIG_RALINK_RT6855A) |
| #include "bbu_spiflash.h" |
| #else |
| #include "ralink_spi.h" |
| #endif |
| |
| /* Choose the SPI flash mode */ |
| #if defined (CONFIG_RALINK_RT6855A) |
| #define BBU_MODE // BBU SPI flash controller |
| #define MORE_BUF_MODE |
| #else |
| #define USER_MODE // SPI flash user mode support by default |
| //#define COMMAND_MODE // SPI flash command mode support |
| #endif |
| |
| #if !defined USER_MODE && !defined COMMAND_MODE && !defined BBU_MODE |
| #error "Please choose the correct mode of SPI flash controller" |
| #endif |
| |
| /****************************************************************************** |
| * SPI FLASH elementray definition and function |
| ******************************************************************************/ |
| |
| #define FLASH_PAGESIZE 256 |
| #define MX_4B_MODE /* MXIC 4 Byte Mode */ |
| |
| /* Flash opcodes. */ |
| #define OPCODE_WREN 6 /* Write enable */ |
| #define OPCODE_WRDI 4 /* Write disable */ |
| #define OPCODE_RDSR 5 /* Read status register */ |
| #define OPCODE_WRSR 1 /* Write status register */ |
| #define OPCODE_READ 3 /* Read data bytes */ |
| #define OPCODE_PP 2 /* Page program */ |
| #define OPCODE_SE 0xD8 /* Sector erase */ |
| #define OPCODE_RES 0xAB /* Read Electronic Signature */ |
| #define OPCODE_RDID 0x9F /* Read JEDEC ID */ |
| |
| #define OPCODE_FAST_READ 0x0B /* Fast Read */ |
| #define OPCODE_DOR 0x3B /* Dual Output Read */ |
| #define OPCODE_QOR 0x6B /* Quad Output Read */ |
| #define OPCODE_DIOR 0xBB /* Dual IO High Performance Read */ |
| #define OPCODE_QIOR 0xEB /* Quad IO High Performance Read */ |
| #define OPCODE_READ_ID 0x90 /* Read Manufacturer and Device ID */ |
| |
| #define OPCODE_P4E 0x20 /* 4KB Parameter Sectore Erase */ |
| #define OPCODE_P8E 0x40 /* 8KB Parameter Sectore Erase */ |
| #define OPCODE_BE 0x60 /* Bulk Erase */ |
| #define OPCODE_BE1 0xC7 /* Bulk Erase */ |
| #define OPCODE_QPP 0x32 /* Quad Page Programing */ |
| |
| /* Status Register bits. */ |
| #define SR_WIP 1 /* Write in progress */ |
| #define SR_WEL 2 /* Write enable latch */ |
| #define SR_BP0 4 /* Block protect 0 */ |
| #define SR_BP1 8 /* Block protect 1 */ |
| #define SR_BP2 0x10 /* Block protect 2 */ |
| #define SR_EPE 0x20 /* Erase/Program error */ |
| #define SR_SRWD 0x80 /* SR write protect */ |
| |
| /* Security Register bits. */ |
| #define READ_SCUR 0x2b /* Read Security register */ |
| #define SECR_OTP 0x01 |
| #define SECR_LDSO 0x02 |
| #define SECR_4BYTE 0x04 |
| #define SECR_CP 0x10 |
| #define SECR_PFAIL 0x20 |
| #define SECR_EFAIL 0x40 |
| #define SECR_WPSEL 0x80 |
| |
| |
| /* |
| The MTD partition map and number of partitions. |
| */ |
| static struct mtd_partition *rt2880_partitions; |
| static int nr_parts; |
| |
| //#define SPI_DEBUG |
| #if !defined (SPI_DEBUG) |
| |
| #define ra_inl(addr) (*(volatile unsigned int *)(addr)) |
| #define ra_outl(addr, value) (*(volatile unsigned int *)(addr) = (value)) |
| #define ra_dbg(args...) do {} while(0) |
| //#define ra_dbg(args...) do { printk(args); } while(0) |
| |
| #else |
| |
| #define ra_dbg(args...) do { printk(args); } while(0) |
| #define _ra_inl(addr) (*(volatile unsigned int *)(addr)) |
| #define _ra_outl(addr, value) (*(volatile unsigned int *)(addr) = (value)) |
| |
| u32 ra_inl(u32 addr) |
| { |
| u32 retval = _ra_inl(addr); |
| |
| printk("%s(%x) => %x \n", __func__, addr, retval); |
| |
| return retval; |
| } |
| |
| u32 ra_outl(u32 addr, u32 val) |
| { |
| _ra_outl(addr, val); |
| |
| printk("%s(%x, %x) \n", __func__, addr, val); |
| |
| return val; |
| } |
| |
| #endif // SPI_DEBUG // |
| |
| #define ra_aor(addr, a_mask, o_value) ra_outl(addr, (ra_inl(addr) & (a_mask)) | (o_value)) |
| #define ra_and(addr, a_mask) ra_aor(addr, a_mask, 0) |
| #define ra_or(addr, o_value) ra_aor(addr, -1, o_value) |
| |
| #define SPIC_READ_BYTES (1<<0) |
| #define SPIC_WRITE_BYTES (1<<1) |
| |
| #if 1 |
| void usleep(unsigned int usecs) |
| { |
| unsigned long timeout = usecs_to_jiffies(usecs); |
| |
| while (timeout) |
| timeout = schedule_timeout_interruptible(timeout); |
| } |
| #endif |
| |
| |
| #if defined USER_MODE || defined COMMAND_MODE |
| static unsigned int spi_wait_nsec = 0; |
| static int spic_busy_wait(void) |
| { |
| do { |
| if ((ra_inl(RT2880_SPISTAT_REG) & 0x01) == 0) |
| return 0; |
| } while (spi_wait_nsec >> 1); |
| |
| printk("%s: fail \n", __func__); |
| return -1; |
| } |
| #elif defined BBU_MODE |
| static int bbu_spic_busy_wait(void) |
| { |
| int n = 100000; |
| do { |
| if ((ra_inl(SPI_REG_CTL) & SPI_CTL_BUSY) == 0) |
| return 0; |
| udelay(1); |
| } while (--n > 0); |
| |
| printk("%s: fail \n", __func__); |
| return -1; |
| } |
| #endif // USER_MODE || COMMAND_MODE // |
| |
| |
| #ifdef USER_MODE |
| /* |
| * @cmd: command and address |
| * @n_cmd: size of command, in bytes |
| * @buf: buffer into which data will be read/written |
| * @n_buf: size of buffer, in bytes |
| * @flag: tag as READ/WRITE |
| * |
| * @return: if write_onlu, -1 means write fail, or return writing counter. |
| * @return: if read, -1 means read fail, or return reading counter. |
| */ |
| static int spic_transfer(const u8 *cmd, int n_cmd, u8 *buf, int n_buf, int flag) |
| { |
| int retval = -1; |
| ra_dbg("cmd(%x): %x %x %x %x , buf:%x len:%x, flag:%s \n", |
| n_cmd, cmd[0], cmd[1], cmd[2], cmd[3], |
| (buf)? (*buf) : 0, n_buf, |
| (flag == SPIC_READ_BYTES)? "read" : "write"); |
| |
| // assert CS and we are already CLK normal high |
| ra_and(RT2880_SPICTL_REG, ~(SPICTL_SPIENA_HIGH)); |
| |
| // write command |
| for (retval = 0; retval < n_cmd; retval++) { |
| ra_outl(RT2880_SPIDATA_REG, cmd[retval]); |
| ra_or(RT2880_SPICTL_REG, SPICTL_STARTWR); |
| if (spic_busy_wait()) { |
| retval = -1; |
| goto end_trans; |
| } |
| } |
| |
| // read / write data |
| if (flag & SPIC_READ_BYTES) { |
| for (retval = 0; retval < n_buf; retval++) { |
| ra_or(RT2880_SPICTL_REG, SPICTL_STARTRD); |
| if (spic_busy_wait()) |
| goto end_trans; |
| buf[retval] = (u8) ra_inl(RT2880_SPIDATA_REG); |
| } |
| |
| } |
| else if (flag & SPIC_WRITE_BYTES) { |
| for (retval = 0; retval < n_buf; retval++) { |
| ra_outl(RT2880_SPIDATA_REG, buf[retval]); |
| ra_or(RT2880_SPICTL_REG, SPICTL_STARTWR); |
| if (spic_busy_wait()) |
| goto end_trans; |
| } |
| } |
| |
| end_trans: |
| // de-assert CS and |
| ra_or (RT2880_SPICTL_REG, (SPICTL_SPIENA_HIGH)); |
| |
| return retval; |
| } |
| |
| static int spic_read(const u8 *cmd, size_t n_cmd, u8 *rxbuf, size_t n_rx) |
| { |
| return spic_transfer(cmd, n_cmd, rxbuf, n_rx, SPIC_READ_BYTES); |
| } |
| |
| static int spic_write(const u8 *cmd, size_t n_cmd, const u8 *txbuf, size_t n_tx) |
| { |
| return spic_transfer(cmd, n_cmd, (u8 *)txbuf, n_tx, SPIC_WRITE_BYTES); |
| } |
| #endif // USER_MODE // |
| |
| void spic_init(void) |
| { |
| #if defined USER_MODE || defined COMMAND_MODE |
| |
| // use normal(SPI) mode instead of GPIO mode |
| //ra_and(RALINK_REG_GPIOMODE, ~(1 << 1)); |
| |
| // reset spi block |
| ra_or(RT2880_RSTCTRL_REG, RSTCTRL_SPI_RESET); |
| udelay(1); |
| ra_and(RT2880_RSTCTRL_REG, ~RSTCTRL_SPI_RESET); |
| |
| // FIXME, clk_div should depend on spi-flash. |
| // mode 0 (SPICLKPOL = 0) & (RXCLKEDGE_FALLING = 0) |
| // mode 3 (SPICLKPOL = 1) & (RXCLKEDGE_FALLING = 0) |
| ra_outl(RT2880_SPICFG_REG, SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING | CFG_CLK_DIV | SPICFG_SPICLKPOL ); |
| |
| // set idle state |
| ra_outl(RT2880_SPICTL_REG, SPICTL_HIZSDO | SPICTL_SPIENA_HIGH); |
| |
| spi_wait_nsec = (8 * 1000 / (128 / CFG_CLK_DIV) ) >> 1 ; |
| //printk("spi_wait_nsec: %x \n", spi_wait_nsec); |
| #elif defined BBU_MODE |
| // enable SMC bank 0 alias addressing |
| ra_or(RALINK_SYSCTL_BASE + 0x38, 0x80000000); |
| #endif |
| } |
| |
| |
| struct chip_info { |
| char *name; |
| u8 id; |
| u32 jedec_id; |
| unsigned long sector_size; |
| unsigned int n_sectors; |
| char addr4b; |
| }; |
| |
| static struct chip_info chips_data [] = { |
| /* REVISIT: fill in JEDEC ids, for parts that have them */ |
| { "AT25DF321", 0x1f, 0x47000000, 64 * 1024, 64, 0 }, |
| { "AT26DF161", 0x1f, 0x46000000, 64 * 1024, 32, 0 }, |
| { "FL016AIF", 0x01, 0x02140000, 64 * 1024, 32, 0 }, |
| { "FL064AIF", 0x01, 0x02160000, 64 * 1024, 128, 0 }, |
| { "MX25L1605D", 0xc2, 0x2015c220, 64 * 1024, 32, 0 }, |
| { "MX25L3205D", 0xc2, 0x2016c220, 64 * 1024, 64, 0 }, |
| { "MX25L6405D", 0xc2, 0x2017c220, 64 * 1024, 128, 0 }, |
| { "MX25L12805D", 0xc2, 0x2018c220, 64 * 1024, 256, 0 }, |
| #ifdef MX_4B_MODE |
| { "MX25L25635E", 0xc2, 0x2019c220, 64 * 1024, 512, 1 }, |
| #endif |
| { "S25FL128P", 0x01, 0x20180301, 64 * 1024, 256, 0 }, |
| { "S25FL129P", 0x01, 0x20184D01, 64 * 1024, 256, 0 }, |
| { "S25FL032P", 0x01, 0x02154D00, 64 * 1024, 64, 0 }, |
| { "S25FL064P", 0x01, 0x02164D00, 64 * 1024, 128, 0 }, |
| { "EN25F16", 0x1c, 0x31151c31, 64 * 1024, 32, 0 }, |
| { "EN25F32", 0x1c, 0x31161c31, 64 * 1024, 64, 0 }, |
| { "EN25Q32", 0x1c, 0x30161c30, 64 * 1024, 64, 0 }, //EN25Q32B |
| { "EN25F64", 0x1c, 0x20171c20, 64 * 1024, 128, 0 }, //EN25P64 |
| { "EN25Q64", 0x1c, 0x30171c30, 64 * 1024, 128, 0 }, |
| { "W25Q32BV", 0xef, 0x30160000, 64 * 1024, 64, 0 }, |
| { "W25Q32BV", 0xef, 0x40160000, 64 * 1024, 64, 0 }, |
| { "W25Q64BV", 0xef, 0x40170000, 64 * 1024, 128, 0 }, //S25FL064K |
| { "W25Q128BV", 0xef, 0x40180000, 64 * 1024, 256, 0 }, |
| }; |
| |
| |
| struct flash_info { |
| struct semaphore lock; |
| struct mtd_info mtd; |
| struct chip_info *chip; |
| u8 command[5]; |
| }; |
| |
| struct flash_info *flash = NULL; |
| |
| |
| #ifdef BBU_MODE |
| #ifdef MORE_BUF_MODE |
| static int bbu_mb_spic_trans(const u8 code, const u32 addr, u8 *buf, const size_t n_tx, const size_t n_rx, int flag) |
| { |
| u32 reg; |
| int i, q, r; |
| int rc = -1; |
| |
| if (flag != SPIC_READ_BYTES && flag != SPIC_WRITE_BYTES) { |
| printk("we currently support more-byte-mode for reading and writing data only\n"); |
| return -1; |
| } |
| |
| /* step 0. enable more byte mode */ |
| ra_or(SPI_REG_MASTER, (1 << 2)); |
| |
| bbu_spic_busy_wait(); |
| |
| /* step 1. set opcode & address, and fix cmd bit count to 32 (or 40) */ |
| #ifdef MX_4B_MODE |
| if (flash && flash->chip->addr4b) { |
| ra_and(SPI_REG_CTL, ~SPI_CTL_ADDREXT_MASK); |
| ra_or(SPI_REG_CTL, (code << 24) & SPI_CTL_ADDREXT_MASK); |
| ra_outl(SPI_REG_OPCODE, addr); |
| } |
| else |
| #endif |
| { |
| ra_outl(SPI_REG_OPCODE, (code << 24) & 0xff000000); |
| ra_or(SPI_REG_OPCODE, (addr & 0xffffff)); |
| } |
| ra_and(SPI_REG_MOREBUF, ~SPI_MBCTL_CMD_MASK); |
| #ifdef MX_4B_MODE |
| if (flash && flash->chip->addr4b) |
| ra_or(SPI_REG_MOREBUF, (40 << 24)); |
| else |
| #endif |
| ra_or(SPI_REG_MOREBUF, (32 << 24)); |
| |
| /* step 2. write DI/DO data #0 ~ #7 */ |
| if (flag & SPIC_WRITE_BYTES) { |
| if (buf == NULL) { |
| printk("%s: write null buf\n", __func__); |
| goto RET_MB_TRANS; |
| } |
| for (i = 0; i < n_tx; i++) { |
| q = i / 4; |
| r = i % 4; |
| if (r == 0) |
| ra_outl(SPI_REG_DATA(q), 0); |
| ra_or(SPI_REG_DATA(q), (*(buf + i) << (r * 8))); |
| } |
| } |
| |
| /* step 3. set rx (miso_bit_cnt) and tx (mosi_bit_cnt) bit count */ |
| ra_and(SPI_REG_MOREBUF, ~SPI_MBCTL_TX_RX_CNT_MASK); |
| ra_or(SPI_REG_MOREBUF, (n_rx << 3 << 12)); |
| ra_or(SPI_REG_MOREBUF, n_tx << 3); |
| |
| /* step 4. kick */ |
| ra_or(SPI_REG_CTL, SPI_CTL_START); |
| |
| /* step 5. wait spi_master_busy */ |
| bbu_spic_busy_wait(); |
| if (flag & SPIC_WRITE_BYTES) { |
| rc = 0; |
| goto RET_MB_TRANS; |
| } |
| |
| /* step 6. read DI/DO data #0 */ |
| if (flag & SPIC_READ_BYTES) { |
| if (buf == NULL) { |
| printk("%s: read null buf\n", __func__); |
| return -1; |
| } |
| for (i = 0; i < n_rx; i++) { |
| q = i / 4; |
| r = i % 4; |
| reg = ra_inl(SPI_REG_DATA(q)); |
| *(buf + i) = (u8)(reg >> (r * 8)); |
| } |
| } |
| |
| rc = 0; |
| RET_MB_TRANS: |
| /* step #. disable more byte mode */ |
| ra_and(SPI_REG_MASTER, ~(1 << 2)); |
| return rc; |
| } |
| #endif // MORE_BUF_MODE // |
| |
| static int bbu_spic_trans(const u8 code, const u32 addr, u8 *buf, const size_t n_tx, const size_t n_rx, int flag) |
| { |
| u32 reg; |
| |
| bbu_spic_busy_wait(); |
| |
| /* step 1. set opcode & address */ |
| #ifdef MX_4B_MODE |
| if (flash && flash->chip->addr4b) { |
| ra_and(SPI_REG_CTL, ~SPI_CTL_ADDREXT_MASK); |
| ra_or(SPI_REG_CTL, addr & SPI_CTL_ADDREXT_MASK); |
| } |
| #endif |
| ra_outl(SPI_REG_OPCODE, ((addr & 0xffffff) << 8)); |
| ra_or(SPI_REG_OPCODE, code); |
| |
| /* step 2. write DI/DO data #0 */ |
| if (flag & SPIC_WRITE_BYTES) { |
| if (buf == NULL) { |
| printk("%s: write null buf\n", __func__); |
| return -1; |
| } |
| ra_outl(SPI_REG_DATA0, 0); |
| switch (n_tx) { |
| case 8: |
| ra_or(SPI_REG_DATA0, (*(buf+3) << 24)); |
| case 7: |
| ra_or(SPI_REG_DATA0, (*(buf+2) << 16)); |
| case 6: |
| ra_or(SPI_REG_DATA0, (*(buf+1) << 8)); |
| case 5: |
| case 2: |
| ra_or(SPI_REG_DATA0, *buf); |
| break; |
| default: |
| printk("%s: fixme, write of length %d\n", __func__, n_tx); |
| return -1; |
| } |
| } |
| |
| /* step 3. set mosi_byte_cnt */ |
| ra_and(SPI_REG_CTL, ~SPI_CTL_TX_RX_CNT_MASK); |
| ra_or(SPI_REG_CTL, (n_rx << 4)); |
| #ifdef MX_4B_MODE |
| if (flash && flash->chip->addr4b && n_tx >= 4) |
| ra_or(SPI_REG_CTL, (n_tx + 1)); |
| else |
| #endif |
| ra_or(SPI_REG_CTL, n_tx); |
| |
| /* step 4. kick */ |
| ra_or(SPI_REG_CTL, SPI_CTL_START); |
| |
| /* step 5. wait spi_master_busy */ |
| bbu_spic_busy_wait(); |
| if (flag & SPIC_WRITE_BYTES) |
| return 0; |
| |
| /* step 6. read DI/DO data #0 */ |
| if (flag & SPIC_READ_BYTES) { |
| if (buf == NULL) { |
| printk("%s: read null buf\n", __func__); |
| return -1; |
| } |
| reg = ra_inl(SPI_REG_DATA0); |
| switch (n_rx) { |
| case 4: |
| *(buf+3) = (u8)(reg >> 24); |
| case 3: |
| *(buf+2) = (u8)(reg >> 16); |
| case 2: |
| *(buf+1) = (u8)(reg >> 8); |
| case 1: |
| *buf = (u8)reg; |
| break; |
| default: |
| printk("%s: fixme, read of length %d\n", __func__, n_rx); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| #endif // BBU_MODE // |
| |
| /* |
| * read SPI flash device ID |
| */ |
| static int raspi_read_devid(u8 *rxbuf, int n_rx) |
| { |
| u8 code = OPCODE_RDID; |
| int retval; |
| |
| #ifdef USER_MODE |
| retval = spic_read(&code, 1, rxbuf, n_rx); |
| #elif defined BBU_MODE |
| retval = bbu_spic_trans(code, 0, rxbuf, 1, 3, SPIC_READ_BYTES); |
| if (!retval) |
| retval = n_rx; |
| #endif |
| if (retval != n_rx) { |
| printk("%s: ret: %x\n", __func__, retval); |
| return retval; |
| } |
| return retval; |
| } |
| |
| /* |
| * Read the status register, returning its value in the location |
| */ |
| static int raspi_read_sr(u8 *val) |
| { |
| ssize_t retval; |
| u8 code = OPCODE_RDSR; |
| |
| #ifdef USER_MODE |
| retval = spic_read(&code, 1, val, 1); |
| #elif defined BBU_MODE |
| retval = bbu_spic_trans(code, 0, val, 1, 1, SPIC_READ_BYTES); |
| return retval; |
| #endif |
| if (retval != 1) { |
| printk("%s: ret: %x\n", __func__, retval); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| /* |
| * write status register |
| */ |
| static int raspi_write_sr(u8 *val) |
| { |
| ssize_t retval; |
| u8 code = OPCODE_WRSR; |
| |
| #ifdef USER_MODE |
| retval = spic_write(&code, 1, val, 1); |
| #elif defined BBU_MODE |
| retval = bbu_spic_trans(code, 0, val, 2, 0, SPIC_WRITE_BYTES); |
| #endif |
| if (retval != 1) { |
| printk("%s: ret: %x\n", __func__, retval); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| #ifdef MX_4B_MODE |
| static int raspi_4byte_mode(int enable) |
| { |
| ssize_t retval; |
| u8 code; |
| |
| code = enable? 0xB7 : 0xE9; /* B7: enter 4B, E9: exit 4B */ |
| |
| #ifdef USER_MODE |
| if (enable) |
| ra_or(RT2880_SPICFG_REG, SPICFG_ADDRMODE); |
| else |
| ra_and(RT2880_SPICFG_REG, ~(SPICFG_ADDRMODE)); |
| retval = spic_read(&code, 1, 0, 0); |
| #elif defined BBU_MODE |
| if (enable) { |
| ra_or(SPI_REG_CTL, 0x3 << 19); |
| ra_or(SPI_REG_Q_CTL, 0x3 << 8); |
| } |
| else { |
| ra_and(SPI_REG_CTL, ~SPI_CTL_SIZE_MASK); |
| ra_or(SPI_REG_CTL, 0x2 << 19); |
| ra_and(SPI_REG_Q_CTL, ~(0x3 << 8)); |
| ra_or(SPI_REG_Q_CTL, 0x2 << 8); |
| } |
| retval = bbu_spic_trans(code, 0, NULL, 1, 0, 0); |
| #endif |
| if (retval != 0) { |
| printk("%s: ret: %x\n", __func__, retval); |
| return -1; |
| } |
| return 0; |
| } |
| #endif // MX_4B_MODE // |
| |
| /* |
| * Set write enable latch with Write Enable command. |
| * Returns negative if error occurred. |
| */ |
| static inline int raspi_write_enable(void) |
| { |
| u8 code = OPCODE_WREN; |
| |
| #ifdef USER_MODE |
| return spic_write(&code, 1, NULL, 0); |
| #elif defined BBU_MODE |
| return bbu_spic_trans(code, 0, NULL, 1, 0, 0); |
| #endif |
| } |
| |
| /* |
| * Set all sectors (global) unprotected if they are protected. |
| * Returns negative if error occurred. |
| */ |
| static inline int raspi_unprotect(void) |
| { |
| u8 sr = 0; |
| |
| if (raspi_read_sr(&sr) < 0) { |
| printk("%s: read_sr fail: %x\n", __func__, sr); |
| return -1; |
| } |
| |
| if ((sr & (SR_BP0 | SR_BP1 | SR_BP2)) != 0) { |
| sr = 0; |
| raspi_write_sr(&sr); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Service routine to read status register until ready, or timeout occurs. |
| * Returns non-zero if error. |
| */ |
| static int raspi_wait_ready(int sleep_ms) |
| { |
| int count; |
| int sr = 0; |
| |
| /*int timeout = sleep_ms * HZ / 1000; |
| while (timeout) |
| timeout = schedule_timeout (timeout);*/ |
| |
| /* one chip guarantees max 5 msec wait here after page writes, |
| * but potentially three seconds (!) after page erase. |
| */ |
| for (count = 0; count < ((sleep_ms+1) *1000 * 500); count++) { |
| if ((raspi_read_sr((u8 *)&sr)) < 0) |
| break; |
| else if (!(sr & (SR_WIP | SR_EPE))) { |
| return 0; |
| } |
| |
| udelay(1); |
| /* REVISIT sometimes sleeping would be best */ |
| } |
| |
| printk("%s: read_sr fail: %x\n", __func__, sr); |
| return -EIO; |
| } |
| |
| static int raspi_wait_sleep_ready(int sleep_ms) |
| { |
| int count; |
| int sr = 0; |
| |
| /*int timeout = sleep_ms * HZ / 1000; |
| while (timeout) |
| timeout = schedule_timeout (timeout);*/ |
| |
| /* one chip guarantees max 5 msec wait here after page writes, |
| * but potentially three seconds (!) after page erase. |
| */ |
| for (count = 0; count < ((sleep_ms+1) *1000); count++) { |
| if ((raspi_read_sr((u8 *)&sr)) < 0) |
| break; |
| else if (!(sr & (SR_WIP | SR_EPE))) { |
| return 0; |
| } |
| |
| usleep(1); |
| /* REVISIT sometimes sleeping would be best */ |
| } |
| |
| printk("%s: read_sr fail: %x\n", __func__, sr); |
| return -EIO; |
| } |
| |
| /* |
| * Erase one sector of flash memory at offset ``offset'' which is any |
| * address within the sector which should be erased. |
| * |
| * Returns 0 if successful, non-zero otherwise. |
| */ |
| static int raspi_erase_sector(u32 offset) |
| { |
| |
| u8 opcode = OPCODE_SE; |
| |
| // Here's an ugly magic number. For the partition at this address, |
| // I don't want to erase the entire partition. I only want to |
| // erase the first 4K block because there is important |
| // manufacturing data at a later block. I may play with erasesize |
| // at some time, but that requires app-layer changes also. |
| if (offset == 0x30000) { |
| opcode = OPCODE_P4E; |
| } |
| |
| /* Wait until finished previous write command. */ |
| if (raspi_wait_ready(3)) |
| return -EIO; |
| |
| /* Send write enable, then erase commands. */ |
| raspi_write_enable(); |
| raspi_unprotect(); |
| |
| #ifdef USER_MODE |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) { |
| raspi_4byte_mode(1); |
| flash->command[0] = opcode; |
| flash->command[1] = offset >> 24; |
| flash->command[2] = offset >> 16; |
| flash->command[3] = offset >> 8; |
| flash->command[4] = offset; |
| spic_write(flash->command, 5, 0 , 0); |
| raspi_wait_sleep_ready(950); |
| raspi_4byte_mode(0); |
| return 0; |
| } |
| #endif // MX_4B_MODE // |
| |
| /* Set up command buffer. */ |
| flash->command[0] = opcode; |
| flash->command[1] = offset >> 16; |
| flash->command[2] = offset >> 8; |
| flash->command[3] = offset; |
| |
| spic_write(flash->command, 4, 0 , 0); |
| raspi_wait_sleep_ready(950); |
| #elif defined BBU_MODE |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| raspi_4byte_mode(1); |
| #endif |
| bbu_spic_trans(STM_OP_SECTOR_ERASE, offset, NULL, 4, 0, 0); |
| //raspi_wait_ready(950); |
| raspi_wait_sleep_ready(950); |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| raspi_4byte_mode(0); |
| #endif |
| #endif |
| return 0; |
| } |
| |
| /* |
| * SPI device driver setup and teardown |
| */ |
| struct chip_info *chip_prob(void) |
| { |
| struct chip_info *info, *match; |
| u8 buf[5]; |
| u32 jedec, weight; |
| int i; |
| |
| raspi_read_devid(buf, 5); |
| jedec = (u32)((u32)(buf[1] << 24) | ((u32)buf[2] << 16) | ((u32)buf[3] <<8) | (u32)buf[4]); |
| |
| #ifdef BBU_MODE |
| printk("flash manufacture id: %x, device id %x %x\n", buf[0], buf[1], buf[2]); |
| #else |
| printk("device id : %x %x %x %x %x (%x)\n", buf[0], buf[1], buf[2], buf[3], buf[4], jedec); |
| #endif |
| |
| // FIXME, assign default as AT25D |
| weight = 0xffffffff; |
| match = &chips_data[0]; |
| for (i = 0; i < ARRAY_SIZE(chips_data); i++) { |
| info = &chips_data[i]; |
| if (info->id == buf[0]) { |
| #ifdef BBU_MODE |
| if ((u8)(info->jedec_id >> 24 & 0xff) == buf[1] && |
| (u8)(info->jedec_id >> 16 & 0xff) == buf[2]) |
| #else |
| if (info->jedec_id == jedec) |
| #endif |
| return info; |
| |
| if (weight > (info->jedec_id ^ jedec)) { |
| weight = info->jedec_id ^ jedec; |
| match = info; |
| } |
| } |
| } |
| printk("Warning: un-recognized chip ID, please update SPI driver!\n"); |
| |
| return match; |
| } |
| |
| int raspi_set_lock (struct mtd_info *mtd, loff_t to, size_t len, int set) |
| { |
| u32 page_offset, page_size; |
| int retval; |
| |
| /* */ |
| while (len > 0) { |
| /* FIXME: 4b mode ? */ |
| /* write the next page to flash */ |
| flash->command[0] = (set == 0)? 0x39 : 0x36; |
| flash->command[1] = to >> 16; |
| flash->command[2] = to >> 8; |
| flash->command[3] = to; |
| |
| raspi_wait_ready(1); |
| |
| raspi_write_enable(); |
| |
| #ifdef USER_MODE |
| retval = spic_write(flash->command, 4, 0, 0); |
| #elif defined BBU_MODE |
| retval = 0; |
| //FIXME |
| #endif |
| |
| if (retval < 0) { |
| return -EIO; |
| } |
| |
| page_offset = (to & (mtd->erasesize-1)); |
| page_size = mtd->erasesize - page_offset; |
| |
| len -= mtd->erasesize; |
| to += mtd->erasesize; |
| } |
| |
| return 0; |
| |
| } |
| |
| |
| /* |
| * MTD implementation |
| */ |
| |
| /* |
| * Erase an address range on the flash chip. The address range may extend |
| * one or more erase sectors. Return an error is there is a problem erasing. |
| */ |
| static int ramtd_erase(struct mtd_info *mtd, struct erase_info *instr) |
| { |
| u32 addr,len; |
| |
| //printk("%s: addr:%x len:%x\n", __func__, instr->addr, instr->len); |
| /* sanity checks */ |
| if (instr->addr + instr->len > flash->mtd.size) |
| return -EINVAL; |
| |
| #if 0 //FIXME - undefined reference to `__umoddi3' |
| if ((instr->addr % mtd->erasesize) != 0 |
| || (instr->len % mtd->erasesize) != 0) { |
| return -EINVAL; |
| } |
| #endif |
| |
| addr = instr->addr; |
| len = instr->len; |
| |
| down(&flash->lock); |
| |
| /* now erase those sectors */ |
| while (len > 0) { |
| if (raspi_erase_sector(addr)) { |
| instr->state = MTD_ERASE_FAILED; |
| up(&flash->lock); |
| return -EIO; |
| } |
| |
| addr += mtd->erasesize; |
| len -= mtd->erasesize; |
| } |
| |
| up(&flash->lock); |
| |
| instr->state = MTD_ERASE_DONE; |
| mtd_erase_callback(instr); |
| |
| return 0; |
| } |
| |
| /* |
| * Read an address range from the flash chip. The address range |
| * may be any size provided it is within the physical boundaries. |
| */ |
| static int ramtd_read(struct mtd_info *mtd, loff_t from, size_t len, |
| size_t *retlen, u_char *buf) |
| { |
| size_t rdlen = 0; |
| |
| ra_dbg("%s: from:%x len:%x \n", __func__, (unsigned int)from, len); |
| |
| /* sanity checks */ |
| if ((len == 0) || (flash == NULL)) |
| return 0; |
| |
| if (from + len > flash->mtd.size) |
| return -EINVAL; |
| |
| /* Byte count starts at zero. */ |
| if (retlen) |
| *retlen = 0; |
| |
| down(&flash->lock); |
| |
| /* Wait till previous write/erase is done. */ |
| if (raspi_wait_ready(1)) { |
| /* REVISIT status return?? */ |
| up(&flash->lock); |
| return -EIO; |
| } |
| |
| #ifdef USER_MODE |
| |
| /* Set up the write data buffer. */ |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) { |
| raspi_4byte_mode(1); |
| flash->command[0] = OPCODE_READ; |
| flash->command[1] = from >> 24; |
| flash->command[2] = from >> 16; |
| flash->command[3] = from >> 8; |
| flash->command[4] = from; |
| rdlen = spic_read(flash->command, 5, buf, len); |
| raspi_4byte_mode(0); |
| } |
| else |
| #endif |
| { |
| flash->command[0] = OPCODE_FAST_READ; |
| flash->command[1] = from >> 16; |
| flash->command[2] = from >> 8; |
| flash->command[3] = from; |
| flash->command[4] = 0; |
| rdlen = spic_read(flash->command, 5, buf, len); |
| } |
| #elif defined BBU_MODE |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| raspi_4byte_mode(1); |
| #endif |
| do { |
| int rc, more; |
| #ifdef MORE_BUF_MODE |
| more = 32; |
| #else |
| more = 4; |
| #endif |
| if (len - rdlen <= more) { |
| #ifdef MORE_BUF_MODE |
| rc = bbu_mb_spic_trans(STM_OP_RD_DATA, from, (buf+rdlen), 0, (len-rdlen), SPIC_READ_BYTES); |
| #else |
| rc = bbu_spic_trans(STM_OP_RD_DATA, from, (buf+rdlen), 4, (len-rdlen), SPIC_READ_BYTES); |
| #endif |
| if (rc != 0) { |
| printk("%s: failed\n", __func__); |
| break; |
| } |
| rdlen = len; |
| } |
| else { |
| #ifdef MORE_BUF_MODE |
| rc = bbu_mb_spic_trans(STM_OP_RD_DATA, from, (buf+rdlen), 0, more, SPIC_READ_BYTES); |
| #else |
| rc = bbu_spic_trans(STM_OP_RD_DATA, from, (buf+rdlen), 4, more, SPIC_READ_BYTES); |
| #endif |
| if (rc != 0) { |
| printk("%s: failed\n", __func__); |
| break; |
| } |
| rdlen += more; |
| from += more; |
| } |
| } while (rdlen < len); |
| #endif |
| |
| up(&flash->lock); |
| |
| if (retlen) |
| *retlen = rdlen; |
| |
| if (rdlen != len) |
| return -EIO; |
| |
| return 0; |
| } |
| |
| inline int ramtd_lock (struct mtd_info *mtd, loff_t to, uint64_t len) |
| { |
| //return raspi_set_lock(mtd, to, len, 1); |
| return 0; |
| } |
| |
| inline int ramtd_unlock (struct mtd_info *mtd, loff_t to, uint64_t len) |
| { |
| //return raspi_set_lock(mtd, to, len, 0); |
| return 0; |
| } |
| |
| |
| /* |
| * Write an address range to the flash chip. Data must be written in |
| * FLASH_PAGESIZE chunks. The address range may be any size provided |
| * it is within the physical boundaries. |
| */ |
| static int ramtd_write(struct mtd_info *mtd, loff_t to, size_t len, |
| size_t *retlen, const u_char *buf) |
| { |
| u32 page_offset, page_size; |
| int rc = 0; |
| #if defined BBU_MODE |
| int wrto, wrlen, more; |
| char *wrbuf; |
| #endif |
| int count = 0; |
| |
| ra_dbg("%s: to:%llx len:%x \n", __func__, to, len); |
| if (retlen) |
| *retlen = 0; |
| |
| /* sanity checks */ |
| if (len == 0) |
| return 0; |
| if (to + len > flash->mtd.size) |
| return -EINVAL; |
| |
| down(&flash->lock); |
| |
| /* Wait until finished previous write command. */ |
| if (raspi_wait_ready(2)) { |
| up(&flash->lock); |
| return -1; |
| } |
| |
| #ifdef USER_MODE |
| /* Set up the opcode in the write buffer. */ |
| flash->command[0] = OPCODE_PP; |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) { |
| flash->command[1] = to >> 24; |
| flash->command[2] = to >> 16; |
| flash->command[3] = to >> 8; |
| flash->command[4] = to; |
| } |
| else |
| #endif |
| { |
| flash->command[1] = to >> 16; |
| flash->command[2] = to >> 8; |
| flash->command[3] = to; |
| } |
| #endif |
| |
| /* what page do we start with? */ |
| page_offset = to % FLASH_PAGESIZE; |
| |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| raspi_4byte_mode(1); |
| #endif |
| |
| /* write everything in PAGESIZE chunks */ |
| while (len > 0) { |
| page_size = min_t(size_t, len, FLASH_PAGESIZE-page_offset); |
| page_offset = 0; |
| |
| /* write the next page to flash */ |
| #ifdef USER_MODE |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) { |
| flash->command[1] = to >> 24; |
| flash->command[2] = to >> 16; |
| flash->command[3] = to >> 8; |
| flash->command[4] = to; |
| } |
| else |
| #endif |
| { |
| flash->command[1] = to >> 16; |
| flash->command[2] = to >> 8; |
| flash->command[3] = to; |
| } |
| #endif |
| |
| raspi_wait_ready(3); |
| raspi_write_enable(); |
| raspi_unprotect(); |
| |
| #ifdef USER_MODE |
| |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| rc = spic_write(flash->command, 5, buf, page_size); |
| else |
| #endif |
| rc = spic_write(flash->command, 4, buf, page_size); |
| |
| #elif defined BBU_MODE |
| wrto = to; |
| wrlen = page_size; |
| wrbuf = buf; |
| rc = wrlen; |
| do { |
| #ifdef MORE_BUF_MODE |
| more = 32; |
| #else |
| more = 4; |
| #endif |
| if (wrlen <= more) { |
| #ifdef MORE_BUF_MODE |
| bbu_mb_spic_trans(STM_OP_PAGE_PGRM, wrto, wrbuf, wrlen, 0, SPIC_WRITE_BYTES); |
| #else |
| bbu_spic_trans(STM_OP_PAGE_PGRM, wrto, wrbuf, wrlen+4, 0, SPIC_WRITE_BYTES); |
| #endif |
| wrlen = 0; |
| } |
| else { |
| #ifdef MORE_BUF_MODE |
| bbu_mb_spic_trans(STM_OP_PAGE_PGRM, wrto, wrbuf, more, 0, SPIC_WRITE_BYTES); |
| #else |
| bbu_spic_trans(STM_OP_PAGE_PGRM, wrto, wrbuf, more+4, 0, SPIC_WRITE_BYTES); |
| #endif |
| wrto += more; |
| wrlen -= more; |
| wrbuf += more; |
| } |
| if (wrlen > 0) { |
| raspi_wait_ready(3); |
| raspi_write_enable(); |
| } |
| } while (wrlen > 0); |
| #endif |
| //printk("%s : to:%llx page_size:%x ret:%x\n", __func__, to, page_size, rc); |
| |
| if (rc > 0) { |
| if (retlen) |
| *retlen += rc; |
| if (rc < page_size) { |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| raspi_4byte_mode(0); |
| #endif |
| up(&flash->lock); |
| printk("%s: rc:%x return:%x page_size:%x \n", |
| __func__, rc, rc, page_size); |
| return -EIO; |
| } |
| } |
| |
| len -= page_size; |
| to += page_size; |
| buf += page_size; |
| count++; |
| if ((count & 0xf) == 0) |
| raspi_wait_sleep_ready(1); |
| |
| } |
| |
| #ifdef MX_4B_MODE |
| if (flash->chip->addr4b) |
| raspi_4byte_mode(0); |
| #endif |
| |
| up(&flash->lock); |
| |
| return 0; |
| } |
| |
| /* |
| * board specific setup should have ensured the SPI clock used here |
| * matches what the READ command supports, at least until this driver |
| * understands FAST_READ (for clocks over 25 MHz). |
| */ |
| static struct mtd_info *raspi_probe(struct map_info *map) |
| { |
| unsigned i; |
| struct chip_info *chip; |
| |
| if (flash == NULL) { |
| return NULL; |
| } |
| |
| chip = flash->chip; |
| |
| //printk("%s\n", __FUNCTION__); |
| |
| printk("%s(%02x %04x) (%lld Kbytes)\n", |
| chip->name, chip->id, chip->jedec_id, flash->mtd.size / 1024); |
| |
| printk("mtd .name = %s, .size = 0x%.8llx (%lluM) " |
| ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n", |
| flash->mtd.name, |
| flash->mtd.size, flash->mtd.size / (1024*1024), |
| flash->mtd.erasesize, flash->mtd.erasesize / 1024, |
| flash->mtd.numeraseregions); |
| |
| if (flash->mtd.numeraseregions) |
| for (i = 0; i < flash->mtd.numeraseregions; i++) |
| printk("mtd.eraseregions[%d] = { .offset = 0x%.8llx, " |
| ".erasesize = 0x%.8x (%uK), " |
| ".numblocks = %d }\n", |
| i, flash->mtd.eraseregions[i].offset, |
| flash->mtd.eraseregions[i].erasesize, |
| flash->mtd.eraseregions[i].erasesize / 1024, |
| flash->mtd.eraseregions[i].numblocks); |
| |
| mtd_device_register(&flash->mtd, rt2880_partitions, nr_parts); |
| |
| return &flash->mtd; |
| } |
| |
| |
| static void raspi_destroy(struct mtd_info *mtd) |
| { |
| int status; |
| |
| /* Clean up MTD stuff. */ |
| status = mtd_device_unregister(&flash->mtd); |
| if (status == 0) { |
| kfree(flash); |
| } |
| } |
| |
| static struct mtd_chip_driver raspi_chipdrv = { |
| .probe = raspi_probe, |
| .destroy = raspi_destroy, |
| .name = "raspi_probe", |
| .module = THIS_MODULE |
| }; |
| |
| /* |
| * Set the Intel flash back to read mode since some old boot |
| * loaders don't. |
| */ |
| static int raspi_reboot_notifier(struct notifier_block *nb, unsigned long val, void *v) |
| { |
| |
| #ifdef MX_4B_MODE |
| int retval; |
| u8 code, scur; |
| int timeout = 10; |
| struct mtd_info mtd; |
| u_char buf[256]; |
| int len; |
| |
| raspi_4byte_mode(0); |
| |
| /* |
| This is an odd-looking hack. On some hardware (early rev |
| Pearl for example) if the last flash activity was an |
| erase/write cycle, the router will not reboot correctly |
| without a power cycle. This read makes the system happier. |
| */ |
| mtd.size = 0x10000; |
| ramtd_read(&mtd, 0x30000, 0x100, &len, &buf[0]); |
| |
| scur = SECR_4BYTE; |
| while ((scur & SECR_4BYTE) && timeout) { |
| timeout--; |
| code = READ_SCUR; |
| retval = spic_transfer(&code, 1, &scur, 1, SPIC_READ_BYTES); |
| if (retval != 0) { |
| printk("read scur failed\n"); |
| scur = SECR_4BYTE; |
| } else { |
| //printf("scur 0x%x\n", scur); |
| if (scur & SECR_4BYTE) { |
| //printf("Still in 4 byte mode\n"); |
| code = 0xE9; /* EX4B, exit 4-byte mode */ |
| retval = spic_transfer(&code, 1, NULL, 0, SPIC_READ_BYTES); |
| if (retval != 0) { |
| printk("%s: ret: %x\n", __func__, retval); |
| return(NOTIFY_OK); |
| } |
| usleep(500); |
| } |
| } |
| } |
| |
| #endif |
| |
| return(NOTIFY_OK); |
| } |
| |
| |
| static struct notifier_block raspi_notifier_block = { |
| raspi_reboot_notifier, NULL, 0 |
| }; |
| |
| static int __init raspi_init(void) |
| { |
| #if defined (CONFIG_RT6855A_FPGA) |
| // only for FPGA: restore to registers since PCI initiation changes them. |
| ra_outl(0xbfbc0028, 0x68880); |
| ra_outl(0xbfbc0004, 0xe9); |
| ra_outl(0xbfbc0008, 0xffffffff); |
| ra_outl(0xbfbc0000, 0x160001); |
| #endif |
| |
| register_mtd_chip_driver(&raspi_chipdrv); |
| |
| raspi_chipdrv.probe(NULL); |
| |
| register_reboot_notifier(&raspi_notifier_block); |
| |
| return 0; |
| } |
| |
| static void __exit raspi_exit(void) |
| { |
| unregister_mtd_chip_driver(&raspi_chipdrv); |
| unregister_reboot_notifier(&raspi_notifier_block); |
| } |
| |
| |
| module_init(raspi_init); |
| module_exit(raspi_exit); |
| |
| /* |
| Create dynamic MTD mappings. There are hints in |
| the flash image for kernel and filesystem sizes. |
| Use those for the mapping, and the Upgrade |
| partition gets all free flash. |
| */ |
| void __init adjust_dynamic_maps(struct mtd_partition *parts, |
| int number_partitions) |
| { |
| int i; |
| u32 size; |
| u32 aligned_size = 0; |
| u32 magic; |
| size_t retlen; |
| u32 address; |
| |
| int kernel = 0, filesystem = 0, upgrade = 0; |
| |
| // Fake needed for the MTD Read function calls |
| struct mtd_info mtd; |
| |
| #define KERNEL_MAGIC 0x27051956 |
| #define FS_MAGIC 0x27051958 |
| |
| //printk("%s\n", __FUNCTION__); |
| |
| #if 0 |
| printk("parts 0x%p, number_partitions %d\n", parts, number_partitions); |
| if (0 == parts) return; |
| for (i = 0; i < number_partitions; i++) { |
| printk("name %s, size 0x%llx, offset 0x%llx, flags 0x%x \n", |
| parts[i].name, parts[i].size, parts[i].offset, parts[i].mask_flags); |
| } |
| #endif |
| |
| /* Let's determine partitions based on names */ |
| for (i = 0; i < number_partitions; i++) { |
| if (strncmp(parts[i].name, "Kernel", strlen("Kernel")) == 0) { |
| kernel = i; |
| } |
| if (strncmp(parts[i].name, "RootFS", strlen("RootFS")) == 0) { |
| filesystem = i; |
| } |
| if (strncmp(parts[i].name, "Upgrade", strlen("Upgrade")) == 0) { |
| upgrade = i; |
| } |
| } |
| |
| // SPI flash needs a read function to get a value |
| |
| // Find/set kernel size |
| address = MTD_BOOT_PART_SIZE + MTD_CONFIG_PART_SIZE + MTD_FACTORY_PART_SIZE; |
| ramtd_read(&mtd, address, sizeof(u32), &retlen, (char *)&magic); |
| //printk("0x%x, %x\n", address, ntohl(magic)); |
| |
| if ((ntohl(magic) == KERNEL_MAGIC) && kernel) { |
| ramtd_read(&mtd, address + 12, sizeof(u32), &retlen, (char *)&size); |
| size = ntohl(size); |
| //printk("%x\n", size); |
| aligned_size = (size + 0x20000) & ~(0x20000 - 1); |
| //printk("%x\n", aligned_size); |
| parts[kernel].size = aligned_size; |
| } |
| |
| // Find/Set Filesystem size |
| address = address + aligned_size - 16; |
| ramtd_read(&mtd, address, sizeof(u32), &retlen, (char *)&magic); |
| //printk("0x%x %x\n", address, ntohl(magic)); |
| |
| if ((ntohl(magic) == FS_MAGIC) && filesystem) { |
| ramtd_read(&mtd, address + 8, sizeof(u32), &retlen, (char *)&size); |
| //printk("%x\n", size); |
| //size = ntohl(size); // size is aligned correctly |
| aligned_size = (size + 0x20000) & ~(0x20000 - 1); |
| parts[filesystem].size = aligned_size; |
| } |
| |
| // Upgrade partition has all the rest of the flash |
| #ifdef CONFIG_MTD_TESTS_MODULE |
| // If I'm using MTD tests, then I want a smaller |
| // partition to play in. |
| if (upgrade && (parts[upgrade].size == 0)) |
| #endif |
| { |
| if (upgrade) { |
| parts[upgrade].size = MTD_PART_SIZE; |
| for (i = 0; i < number_partitions; i++) { |
| if (i != upgrade) |
| parts[upgrade].size -= parts[i].size; |
| } |
| } |
| } |
| |
| #if 0 |
| if (0 == parts) return; |
| for (i = 0; i < number_partitions; i++) { |
| printk("name %s, size 0x%x, offset 0x%x, flags 0x%x \n", |
| parts[i].name, parts[i].size, parts[i].offset, parts[i].mask_flags); |
| } |
| #endif |
| |
| } |
| |
| static int physmap_flash_probe(struct platform_device *dev) |
| { |
| struct physmap_flash_data *physmap_data; |
| struct chip_info *chip; |
| |
| //printk("%s init\n", __FUNCTION__); |
| |
| physmap_data = dev->dev.platform_data; |
| |
| if (physmap_data == NULL) |
| return -ENODEV; |
| |
| // We do some chip driver setup here, rather than in |
| // raspi_probe. We need to be able to read some |
| // data from the flash part. |
| |
| spic_init(); |
| |
| chip = chip_prob(); |
| |
| flash = kzalloc(sizeof *flash, GFP_KERNEL); |
| if (!flash) |
| return -ENODEV; |
| |
| sema_init(&flash->lock, 1); |
| |
| flash->chip = chip; |
| flash->mtd.name = "raspi"; |
| |
| flash->mtd.type = MTD_NORFLASH; |
| flash->mtd.writesize = 1; |
| flash->mtd.flags = MTD_CAP_NORFLASH; |
| flash->mtd.size = chip->sector_size * chip->n_sectors; |
| flash->mtd.erasesize = chip->sector_size; |
| flash->mtd._erase = ramtd_erase; |
| flash->mtd._read = ramtd_read; |
| flash->mtd._write = ramtd_write; |
| flash->mtd._lock = ramtd_lock; |
| flash->mtd._unlock = ramtd_unlock; |
| |
| rt2880_partitions = physmap_data->parts; |
| nr_parts = physmap_data->nr_parts; |
| adjust_dynamic_maps(physmap_data->parts, physmap_data->nr_parts); |
| |
| return 0; |
| } |
| |
| static const struct of_device_id ralink_mtd_match[] = { |
| { .compatible = "ralink,nor" }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, ralink_mtd_match); |
| |
| static struct platform_driver physmap_flash_driver = { |
| .probe = physmap_flash_probe, |
| .driver = { |
| .name = "ralink-flash", |
| .owner = THIS_MODULE, |
| .of_match_table = ralink_mtd_match, |
| }, |
| }; |
| |
| static int __init ralink_physmap_init(void) |
| { |
| int err; |
| |
| //printk("*SPI* %s\n", __FUNCTION__); |
| |
| err = platform_driver_register(&physmap_flash_driver); |
| return err; |
| } |
| |
| static void __exit ralink_physmap_exit(void) |
| { |
| platform_driver_unregister(&physmap_flash_driver); |
| } |
| |
| module_init(ralink_physmap_init); |
| module_exit(ralink_physmap_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Steven Liu"); |
| MODULE_DESCRIPTION("MTD SPI driver for Ralink flash chips"); |
| |
| int ra_mtd_write_nm(char *name, loff_t to, size_t len, const u_char *buf) |
| { |
| int ret = -1; |
| size_t rdlen, wrlen; |
| struct mtd_info *mtd; |
| struct erase_info ei; |
| u_char *bak = NULL; |
| |
| mtd = get_mtd_device_nm(name); |
| if (IS_ERR(mtd)) |
| return (int)mtd; |
| if (len > mtd->erasesize) { |
| put_mtd_device(mtd); |
| return -E2BIG; |
| } |
| |
| bak = kmalloc(mtd->erasesize, GFP_KERNEL); |
| if (bak == NULL) { |
| put_mtd_device(mtd); |
| return -ENOMEM; |
| } |
| |
| ret = mtd_read(mtd, 0, mtd->erasesize, &rdlen, bak); |
| if (ret != 0) { |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| if (rdlen != mtd->erasesize) |
| printk("warning: ra_mtd_write: rdlen is not equal to erasesize\n"); |
| |
| memcpy(bak + to, buf, len); |
| |
| ei.mtd = mtd; |
| ei.callback = NULL; |
| ei.addr = 0; |
| ei.len = mtd->erasesize; |
| ei.priv = 0; |
| ret = mtd_erase(mtd, &ei); |
| if (ret != 0) { |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| |
| ret = mtd_write(mtd, 0, mtd->erasesize, &wrlen, bak); |
| |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| |
| int ra_mtd_read_nm(char *name, loff_t from, size_t len, u_char *buf) |
| { |
| int ret; |
| size_t rdlen; |
| struct mtd_info *mtd; |
| |
| mtd = get_mtd_device_nm(name); |
| if (IS_ERR(mtd)) |
| return (int)mtd; |
| |
| ret = mtd_read(mtd, from, len, &rdlen, buf); |
| if (rdlen != len) |
| printk("warning: ra_mtd_read_nm: rdlen is not equal to len\n"); |
| |
| put_mtd_device(mtd); |
| return ret; |
| } |
| |
| EXPORT_SYMBOL(ra_mtd_write_nm); |
| EXPORT_SYMBOL(ra_mtd_read_nm); |