Commit 31baa1cd authored by Jonathan Currier's avatar Jonathan Currier
Browse files

WIP: spi-nor/rcs-tercel: stell testing, but reads seem to work, and writes...

WIP: spi-nor/rcs-tercel: stell testing, but reads seem to work, and writes almost work (but dont actually)

The write/read cycle reveals that each 4-byte block is being byte swapped somewhere along the line.
parent 9e7dce44
......@@ -220,13 +220,46 @@ rtc@c3ff8000 {
spi@c3ff7000 {
compatible = "rcs,tercel";
//clocks = <0x02 0x19>;
status = "okay";
//#address-cells = <0x01>;
//interrupts = <0x13>;
//#size-cells = <0x00>;
#address-cells = <0x01>;
#size-cells = <0x00>;
reg = <0xc3ff7000 0x80
0xc4000000 0x04000000>;
flash@0 {
//m25p,fast-read;
compatible = "jedec,spi-nor";
status = "okay";
label = "bmc";
reg = <0x00>;
spi-max-frequency = <0x17d7840>;
partitions {
compatible = "fixed-partitions";
#address-cells = <0x01>;
#size-cells = <0x01>;
boothdr@0 {
label = "boothdr";
reg = <0x0 0x1000>;
};
kernel@400 {
label = "kernel";
reg = <0x1000 0x1ff000>;
};
dev-data@200000 {
label = "dev-data";
reg = <0x200000 0x200000>;
};
rwfs@400000 {
label = "rffs";
reg = <0x400000 0x400000>;
};
};
};
};
i2c0@c3ffa000 {
......
......@@ -10,15 +10,33 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
// Device ID string (8 bytes)
// version (4 bytes)
// Base clock frequency (4 bytes)
// PHY configuration register 1 (4 bytes): {cs_extra_idle_cycles, 2'b0, qspi_write_quad_io_en, qspi_read_quad_io_en, fast_read_mode, four_byte_address_mode, phy_io_type, dummy_cycle_count, spi_clock_divisor}
// Flash configuration register 1 (4 bytes): {4BA QSPI read command, 3BA QSPI read command, 4BA SPI read command, 3BA SPI read command}
// Flash configuration register 2 (4 bytes): {4BA QSPI fast read command, 3BA QSPI fast read command, 4BA SPI fast read command, 3BA SPI fast read command}
// Flash configuration register 3 (4 bytes): {4BA QSPI program command, 3BA QSPI program command, 4BA SPI program command, 3BA SPI program command}
// Flash configuration register 4 (4 bytes): {spi_cs_active_hold_cycles}
// Flash configuration register 5 (4 bytes): {30'b0, allow_multicycle_writes, allow_multicycle_reads}
// Control register 1 (4 bytes): {31'b0, user_command_mode}
// Data register 1 (4 bytes): {24'b0, user_command_mode_read_during_write}
// PHY configuration register 1 (4 bytes):
// {cs_extra_idle_cycles, 2'b0, qspi_write_quad_io_en,
// qspi_read_quad_io_en, fast_read_mode, four_byte_address_mode,
// phy_io_type[1:0], dummy_cycle_count[7:0], spi_clock_divisor[7:0]}
// Flash configuration register 1 (4 bytes):
// {4BA QSPI read command, 3BA QSPI read command, 4BA SPI read command, 3BA SPI read command}
// Flash configuration register 2 (4 bytes):
// {4BA QSPI fast read command, 3BA QSPI fast read command, 4BA SPI fast read command, 3BA SPI fast read command}
// Flash configuration register 3 (4 bytes):
// {4BA QSPI program command, 3BA QSPI program command, 4BA SPI program command, 3BA SPI program command}
// Flash configuration register 4 (4 bytes):
// {spi_cs_active_hold_cycles}
// Flash configuration register 5 (4 bytes):
// {30'b0, allow_multicycle_writes, allow_multicycle_reads}
// Control register 1 (4 bytes):
// {31'b0, user_command_mode}
// Data register 1 (4 bytes):
// {24'b0, user_command_mode_read_during_write}
#define TERCEL_MULTICYLE_READ 0x1
#define TERCEL_MULTICYLE_WRITE 0x2
#define TERCEL_PHY_IO_MASK 0x30000
#define TERCEL_PHY_IO_TYPE_SINGLE 0
#define TERCEL_PHY_IO_TYPE_QUAD 0x20000
struct tercel_hw_regs {
uint32_t device_id[2];
uint32_t version;
......@@ -33,28 +51,266 @@ struct tercel_hw_regs {
uint32_t data;
};
struct rcs_tercel_state;
struct rcs_tercel_chip_state {
struct rcs_tercel_state *controller;
struct tercel_hw_regs __iomem *ctlbase;
void __iomem *windowbase;
int cs;
struct spi_nor nor;
};
struct rcs_tercel_state {
struct tercel_hw_regs __iomem *ctlbase;
void __iomem *windowbase;
uint32_t window_size;
struct spi_nor nor;
struct platform_device *pdev;
struct mutex mutex;
int ncs;
/* must be last entry if converted to variable struct*/
struct rcs_tercel_chip_state chips[1];
};
#define TERCEL_USER_MODE 0x1
/* Caller is expect to hold the controller mutex lock */
static inline int tercel_um_raw_read_reg(struct rcs_tercel_chip_state *chip,
uint8_t opcode, uint8_t *buf, size_t len)
{
uint32_t tmpcfg = readl(&chip->ctlbase->ctr);
writel(tmpcfg | TERCEL_USER_MODE, &chip->ctlbase->ctr);
writeb(opcode, chip->windowbase);
for (;len; len--, buf++) {
*buf = readb(chip->windowbase);
}
writel(tmpcfg, &chip->ctlbase->ctr);
}
static int rcs_tercel_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len)
{
struct rcs_tercel_chip_state *chip = nor->priv;
mutex_lock(&chip->controller->mutex);
#if 1
dev_info(chip->nor.dev, "%s called for op %02x, with %08x bytes\n",
__func__, opcode, len);
#endif
#if 0
uint32_t tmpcfg = readl(&chip->ctlbase->ctr);
writel(tmpcfg | TERCEL_USER_MODE, &chip->ctlbase->ctr);
writeb(opcode, chip->windowbase);
for (;len; len--, buf++) {
*buf = readb(chip->windowbase);
}
writel(tmpcfg, &chip->ctlbase->ctr);
#else
tercel_um_raw_read_reg(chip, opcode, buf, len);
#endif
mutex_unlock(&chip->controller->mutex);
return 0;
}
static int rcs_tercel_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf,
size_t len)
{
struct rcs_tercel_chip_state *chip = nor->priv;
mutex_lock(&chip->controller->mutex);
uint32_t tmpcfg = readl(&chip->ctlbase->ctr);
writel(tmpcfg | TERCEL_USER_MODE, &chip->ctlbase->ctr);
writeb(opcode, chip->windowbase);
for (;len; len--, buf++) {
writeb(*buf, chip->windowbase);
}
writel(tmpcfg, &chip->ctlbase->ctr);
mutex_unlock(&chip->controller->mutex);
return 0;
}
static ssize_t rcs_tercel_read(struct spi_nor *nor, loff_t from, size_t len, u8 *buf)
{
struct rcs_tercel_chip_state *chip = nor->priv;
/* we probably should set up a reader/writer interface, but it probably
* doesnt mater */
mutex_lock(&chip->controller->mutex);
memcpy_fromio(buf, chip->windowbase + from, len);
mutex_unlock(&chip->controller->mutex);
return len;
}
static ssize_t rcs_tercel_write(struct spi_nor *nor, loff_t to, size_t len,
const u8 *buf)
{
struct rcs_tercel_chip_state *chip = nor->priv;
/* we probably should set up a reader/writer interface, but it probably
* doesnt mater */
mutex_lock(&chip->controller->mutex);
uint32_t offset = to;
size_t remaining = len;
uint8_t flash_status = 0;
//memcpy_toio(chip->windowbase + to, buf, len);
for (;remaining >= 0x200; remaining -= 0x200, offset += 0x200, buf += 0x200)
{
// TODO: this op code is totally wrong, it probably depends on the part.
tercel_um_raw_read_reg(chip, 0x05, &flash_status, sizeof(flash_status));
while (flash_status & 1)
tercel_um_raw_read_reg(chip, 0x05, &flash_status, sizeof(flash_status));
memcpy_toio(chip->windowbase + offset, buf, 0x200);
}
tercel_um_raw_read_reg(chip, 0x05, &flash_status, sizeof(flash_status));
while (flash_status & 1)
tercel_um_raw_read_reg(chip, 0x05, &flash_status, sizeof(flash_status));
if (remaining)
memcpy_toio(chip->windowbase + offset, buf, remaining);
mutex_unlock(&chip->controller->mutex);
return len;
}
#if 0
static int rcs_tercel_erase(struct spi_nor *nor, loff_t offs)
{
return -EINVAL;
}
#endif
static const struct spi_nor_controller_ops rcs_tercel_flash_controller_ops = {
#if 0
.prepare = rcs_tercel_prepare,
.unprepare = rcs_tercel_unprepare,
.erase = rcs_tercel_erase,
#endif
.read_read = rcs_tercel_read_reg,
.read_reg = rcs_tercel_read_reg,
.write_reg = rcs_tercel_write_reg,
.read = rcs_tercel_read,
.write = rcs_tercel_write,
.erase = rcs_tercel_erase,
#endif
};
/* Initial versions of tercel only support 1 (implicit) chip select line
* however call it 'chips' incase there's more added in a later verison
*/
static int rcs_tercel_setup_chips(struct rcs_tercel_state *controller)
{
// TODO: the tercel supports more caps than this
const struct spi_nor_hwcaps hwcaps = {
.mask = SNOR_HWCAPS_READ |
SNOR_HWCAPS_READ_FAST |
SNOR_HWCAPS_PP,
};
struct device *dev = &controller->pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child = NULL;
unsigned int cs;
int ret = -ENODEV;
for_each_available_child_of_node(np, child) {
struct rcs_tercel_chip_state *chip;
/* Other types not supported */
if (!of_device_is_compatible(child, "jedec,spi-nor"))
continue;
ret = of_property_read_u32(child, "reg", &cs);
if (ret) {
dev_err(dev, "Couldn't read chip select.\n");
break;
}
if (cs >= controller->ncs) {
dev_err(dev, "Chip select %d out of range.\n", cs);
ret = -ERANGE;
break;
}
chip = &controller->chips[cs];
if (chip->controller) {
dev_err(dev, "Chip select %d already in use by %s\n",
cs, dev_name(chip->nor.dev));
ret = -EBUSY;
break;
}
chip->controller = controller;
chip->ctlbase = controller->ctlbase;
chip->windowbase = controller->windowbase;
chip->nor.priv = chip;
chip->nor.dev = dev;
spi_nor_set_flash_node(&chip->nor, child);
chip->nor.controller_ops = &rcs_tercel_flash_controller_ops;
// May need some prep here.
ret = spi_nor_scan(&chip->nor, NULL, &hwcaps);
if (ret)
break;
{
/* Set to Single SPI mode */
uint32_t tmp = readl(&chip->ctlbase->phy_cfg);
tmp &= ~TERCEL_PHY_IO_MASK;
writel(tmp, &chip->ctlbase->phy_cfg);
dev_info(dev, "Chip configure with addr width: %d, readop: %02x, "
"read dummy: %02x, program op %02x\n",
chip->nor.addr_width, chip->nor.read_opcode,
chip->nor.read_dummy, chip->nor.program_opcode);
#if 1
/* enable multi cycle reads and writes */
tmp = readl(&chip->ctlbase->flash_cfg4);
tmp |= (TERCEL_MULTICYLE_WRITE | TERCEL_MULTICYLE_READ);
writel(tmp, &chip->ctlbase->flash_cfg4);
#endif
}
#if 0
[ 52.823331] rcs-tercel c3ff7000.spi: Chip configure with addr width: 3, readop: 03, read dummy: 00, program op 02
// {4BA QSPI program command, 3BA QSPI program command, 4BA SPI program command, 3BA SPI program command}
// Device OD
0xc3ff7000: 0x7C525054
0xc3ff7004: 0x5350494D
// Version
0xc3ff7008: 0x00010000
// Clk
0xc3ff700c: 0x02FAF080
// PhyCFG
0xc3ff7010: 0x00000A10
// Flash CFG
0xc3ff7014: 0x13031303
0xc3ff7018: 0xECEB0C0B
// Program CMDs
0xc3ff701c: 0x34321202
0xc3ff7020: 0x00000000
0xc3ff7024: 0x00000000
// CTR
0xc3ff7028: 0x00000000
// Data
0xc3ff702c: 0x00000000
#endif
ret = mtd_device_register(&chip->nor.mtd, NULL, 0);
if (ret)
break;
}
/* TODO missing error handling */
return ret;
}
static const struct of_device_id rcs_tercel_matches[] = {
{ . compatible = "rcs,tercel", },
{ }
......@@ -76,6 +332,7 @@ static int rcs_check_dev(struct rcs_tercel_state *controller)
dev_id, regs.version, regs.base_clock);
return 0;
}
static int rcs_tercel_probe(struct platform_device *pdev)
{
struct rcs_tercel_state *controller = NULL;
......@@ -92,6 +349,7 @@ static int rcs_tercel_probe(struct platform_device *pdev)
return -ENOMEM;
controller->pdev = pdev;
controller->ncs = 1;
platform_set_drvdata(pdev, controller);
......@@ -117,6 +375,9 @@ static int rcs_tercel_probe(struct platform_device *pdev)
rcs_check_dev(controller);
mutex_init(&controller->mutex);
rcs_tercel_setup_chips(controller);
return 0;
err_window_alloc:
err_ctl_alloc:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment