Commit 451de97a authored by Juuso Oikarinen's avatar Juuso Oikarinen Committed by John W. Linville

wl1271: Update memory mapping for firmware revision 6.1.0.0.241

Update the memory regions and memory mapping to support firmware revision
6.1.0.0.241.
Signed-off-by: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 37079a83
...@@ -148,6 +148,8 @@ struct wl1271_partition { ...@@ -148,6 +148,8 @@ struct wl1271_partition {
struct wl1271_partition_set { struct wl1271_partition_set {
struct wl1271_partition mem; struct wl1271_partition mem;
struct wl1271_partition reg; struct wl1271_partition reg;
struct wl1271_partition mem2;
struct wl1271_partition mem3;
}; };
struct wl1271; struct wl1271;
...@@ -302,10 +304,7 @@ struct wl1271 { ...@@ -302,10 +304,7 @@ struct wl1271 {
enum wl1271_state state; enum wl1271_state state;
struct mutex mutex; struct mutex mutex;
int physical_mem_addr; struct wl1271_partition_set part;
int physical_reg_addr;
int virtual_mem_addr;
int virtual_reg_addr;
struct wl1271_chip chip; struct wl1271_chip chip;
......
...@@ -39,6 +39,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { ...@@ -39,6 +39,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
.start = REGISTERS_BASE, .start = REGISTERS_BASE,
.size = 0x00008800 .size = 0x00008800
}, },
.mem2 = {
.start = 0x00000000,
.size = 0x00000000
},
.mem3 = {
.start = 0x00000000,
.size = 0x00000000
},
}, },
[PART_WORK] = { [PART_WORK] = {
...@@ -48,7 +56,15 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { ...@@ -48,7 +56,15 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
}, },
.reg = { .reg = {
.start = REGISTERS_BASE, .start = REGISTERS_BASE,
.size = 0x0000b000 .size = 0x0000a000
},
.mem2 = {
.start = 0x003004f8,
.size = 0x00000004
},
.mem3 = {
.start = 0x00040404,
.size = 0x00000000
}, },
}, },
...@@ -60,6 +76,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { ...@@ -60,6 +76,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
.reg = { .reg = {
.start = DRPW_BASE, .start = DRPW_BASE,
.size = 0x00006000 .size = 0x00006000
},
.mem2 = {
.start = 0x00000000,
.size = 0x00000000
},
.mem3 = {
.start = 0x00000000,
.size = 0x00000000
} }
} }
}; };
...@@ -93,6 +117,7 @@ static void wl1271_boot_fw_version(struct wl1271 *wl) ...@@ -93,6 +117,7 @@ static void wl1271_boot_fw_version(struct wl1271 *wl)
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
size_t fw_data_len, u32 dest) size_t fw_data_len, u32 dest)
{ {
struct wl1271_partition_set partition;
int addr, chunk_num, partition_limit; int addr, chunk_num, partition_limit;
u8 *p, *chunk; u8 *p, *chunk;
...@@ -114,10 +139,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, ...@@ -114,10 +139,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
return -ENOMEM; return -ENOMEM;
} }
wl1271_set_partition(wl, dest, memcpy(&partition, &part_table[PART_DOWN], sizeof(partition));
part_table[PART_DOWN].mem.size, partition.mem.start = dest;
part_table[PART_DOWN].reg.start, wl1271_set_partition(wl, &partition);
part_table[PART_DOWN].reg.size);
/* 10.1 set partition limit and chunk num */ /* 10.1 set partition limit and chunk num */
chunk_num = 0; chunk_num = 0;
...@@ -130,13 +154,8 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, ...@@ -130,13 +154,8 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
addr = dest + chunk_num * CHUNK_SIZE; addr = dest + chunk_num * CHUNK_SIZE;
partition_limit = chunk_num * CHUNK_SIZE + partition_limit = chunk_num * CHUNK_SIZE +
part_table[PART_DOWN].mem.size; part_table[PART_DOWN].mem.size;
partition.mem.start = addr;
/* FIXME: Over 80 chars! */ wl1271_set_partition(wl, &partition);
wl1271_set_partition(wl,
addr,
part_table[PART_DOWN].mem.size,
part_table[PART_DOWN].reg.start,
part_table[PART_DOWN].reg.size);
} }
/* 10.3 upload the chunk */ /* 10.3 upload the chunk */
...@@ -261,11 +280,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) ...@@ -261,11 +280,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
/* FIXME: The driver sets the partition here, but this is not needed, /* FIXME: The driver sets the partition here, but this is not needed,
since it sets to the same one as currently in use */ since it sets to the same one as currently in use */
/* Now we must set the partition correctly */ /* Now we must set the partition correctly */
wl1271_set_partition(wl, wl1271_set_partition(wl, &part_table[PART_WORK]);
part_table[PART_WORK].mem.start,
part_table[PART_WORK].mem.size,
part_table[PART_WORK].reg.start,
part_table[PART_WORK].reg.size);
/* Copy the NVS tables to a new block to ensure alignment */ /* Copy the NVS tables to a new block to ensure alignment */
nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
...@@ -371,11 +386,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) ...@@ -371,11 +386,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
wl->event_box_addr = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); wl->event_box_addr = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
/* set the working partition to its "running" mode offset */ /* set the working partition to its "running" mode offset */
wl1271_set_partition(wl, wl1271_set_partition(wl, &part_table[PART_WORK]);
part_table[PART_WORK].mem.start,
part_table[PART_WORK].mem.size,
part_table[PART_WORK].reg.start,
part_table[PART_WORK].reg.size);
wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
wl->cmd_box_addr, wl->event_box_addr); wl->cmd_box_addr, wl->event_box_addr);
...@@ -469,11 +480,7 @@ int wl1271_boot(struct wl1271 *wl) ...@@ -469,11 +480,7 @@ int wl1271_boot(struct wl1271 *wl)
wl1271_reg_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); wl1271_reg_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
udelay(500); udelay(500);
wl1271_set_partition(wl, wl1271_set_partition(wl, &part_table[PART_DRPW]);
part_table[PART_DRPW].mem.start,
part_table[PART_DRPW].mem.size,
part_table[PART_DRPW].reg.start,
part_table[PART_DRPW].reg.size);
/* Read-modify-write DRPW_SCRATCH_START register (see next state) /* Read-modify-write DRPW_SCRATCH_START register (see next state)
to be used by DRPw FW. The RTRIM value will be added by the FW to be used by DRPw FW. The RTRIM value will be added by the FW
...@@ -488,11 +495,7 @@ int wl1271_boot(struct wl1271 *wl) ...@@ -488,11 +495,7 @@ int wl1271_boot(struct wl1271 *wl)
clk |= (REF_CLOCK << 1) << 4; clk |= (REF_CLOCK << 1) << 4;
wl1271_reg_write32(wl, DRPW_SCRATCH_START, clk); wl1271_reg_write32(wl, DRPW_SCRATCH_START, clk);
wl1271_set_partition(wl, wl1271_set_partition(wl, &part_table[PART_WORK]);
part_table[PART_WORK].mem.start,
part_table[PART_WORK].mem.size,
part_table[PART_WORK].reg.start,
part_table[PART_WORK].reg.size);
/* Disable interrupts */ /* Disable interrupts */
wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
......
...@@ -315,6 +315,7 @@ static int wl1271_setup(struct wl1271 *wl) ...@@ -315,6 +315,7 @@ static int wl1271_setup(struct wl1271 *wl)
static int wl1271_chip_wakeup(struct wl1271 *wl) static int wl1271_chip_wakeup(struct wl1271 *wl)
{ {
struct wl1271_partition_set partition;
int ret = 0; int ret = 0;
wl1271_power_on(wl); wl1271_power_on(wl);
...@@ -324,11 +325,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ...@@ -324,11 +325,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
/* We don't need a real memory partition here, because we only want /* We don't need a real memory partition here, because we only want
* to use the registers at this point. */ * to use the registers at this point. */
wl1271_set_partition(wl, memset(&partition, 0, sizeof(partition));
0x00000000, partition.reg.start = REGISTERS_BASE;
0x00000000, partition.reg.size = REGISTERS_DOWN_SIZE;
REGISTERS_BASE, wl1271_set_partition(wl, &partition);
REGISTERS_DOWN_SIZE);
/* ELP module wake up */ /* ELP module wake up */
wl1271_fw_wakeup(wl); wl1271_fw_wakeup(wl);
......
...@@ -30,17 +30,29 @@ ...@@ -30,17 +30,29 @@
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
#include "wl1271_spi.h" #include "wl1271_spi.h"
static int wl1271_translate_reg_addr(struct wl1271 *wl, int addr) static int wl1271_translate_addr(struct wl1271 *wl, int addr)
{ {
return addr - wl->physical_reg_addr + wl->virtual_reg_addr; /*
} * To translate, first check to which window of addresses the
* particular address belongs. Then subtract the starting address
static int wl1271_translate_mem_addr(struct wl1271 *wl, int addr) * of that window from the address. Then, add offset of the
{ * translated region.
return addr - wl->physical_mem_addr + wl->virtual_mem_addr; *
* The translated regions occur next to each other in physical device
* memory, so just add the sizes of the preceeding address regions to
* get the offset to the new region.
*
* Currently, only the two first regions are addressed, and the
* assumption is that all addresses will fall into either of those
* two.
*/
if ((addr >= wl->part.reg.start) &&
(addr < wl->part.reg.start + wl->part.reg.size))
return addr - wl->part.reg.start + wl->part.mem.size;
else
return addr - wl->part.mem.start;
} }
void wl1271_spi_reset(struct wl1271 *wl) void wl1271_spi_reset(struct wl1271 *wl)
{ {
u8 *cmd; u8 *cmd;
...@@ -123,123 +135,61 @@ void wl1271_spi_init(struct wl1271 *wl) ...@@ -123,123 +135,61 @@ void wl1271_spi_init(struct wl1271 *wl)
/* Set the SPI partitions to access the chip addresses /* Set the SPI partitions to access the chip addresses
* *
* There are two VIRTUAL (SPI) partitions (the memory partition and the * To simplify driver code, a fixed (virtual) memory map is defined for
* registers partition), which are mapped to two different areas of the * register and memory addresses. Because in the chipset, in different stages
* PHYSICAL (hardware) memory. This function also makes other checks to * of operation, those addresses will move around, an address translation
* ensure that the partitions are not overlapping. In the diagram below, the * mechanism is required.
* memory partition comes before the register partition, but the opposite is
* also supported.
* *
* PHYSICAL address * There are four partitions (three memory and one register partition),
* which are mapped to two different areas of the hardware memory.
*
* Virtual address
* space * space
* *
* | | * | |
* ...+----+--> mem_start * ...+----+--> mem.start
* VIRTUAL address ... | | * Physical address ... | |
* space ... | | [PART_0] * space ... | | [PART_0]
* ... | | * ... | |
* 0x00000000 <--+----+... ...+----+--> mem_start + mem_size * 00000000 <--+----+... ...+----+--> mem.start + mem.size
* | | ... | | * | | ... | |
* |MEM | ... | | * |MEM | ... | |
* | | ... | | * | | ... | |
* part_size <--+----+... | | {unused area) * mem.size <--+----+... | | {unused area)
* | | ... | | * | | ... | |
* |REG | ... | | * |REG | ... | |
* part_size | | ... | | * mem.size | | ... | |
* + <--+----+... ...+----+--> reg_start * + <--+----+... ...+----+--> reg.start
* reg_size ... | | * reg.size | | ... | |
* ... | | [PART_1] * |MEM2| ... | | [PART_1]
* ... | | * | | ... | |
* ...+----+--> reg_start + reg_size * ...+----+--> reg.start + reg.size
* | | * | |
* *
*/ */
int wl1271_set_partition(struct wl1271 *wl, int wl1271_set_partition(struct wl1271 *wl,
u32 mem_start, u32 mem_size, struct wl1271_partition_set *p)
u32 reg_start, u32 reg_size)
{ {
struct wl1271_partition *partition; /* copy partition info */
struct spi_transfer t; memcpy(&wl->part, p, sizeof(*p));
struct spi_message m;
size_t len, cmd_len;
u32 *cmd;
int addr;
cmd_len = sizeof(u32) + 2 * sizeof(struct wl1271_partition);
cmd = kzalloc(cmd_len, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
spi_message_init(&m);
memset(&t, 0, sizeof(t));
partition = (struct wl1271_partition *) (cmd + 1);
addr = HW_ACCESS_PART0_SIZE_ADDR;
len = 2 * sizeof(struct wl1271_partition);
*cmd |= WSPI_CMD_WRITE;
*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size); p->mem.start, p->mem.size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size); p->reg.start, p->reg.size);
wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X",
/* Make sure that the two partitions together don't exceed the p->mem2.start, p->mem2.size);
* address range */ wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X",
if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { p->mem3.start, p->mem3.size);
wl1271_debug(DEBUG_SPI, "Total size exceeds maximum virtual"
" address range. Truncating partition[0]."); /* write partition info to the chipset */
mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; wl1271_write32(wl, HW_PART0_START_ADDR, p->mem.start);
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", wl1271_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size);
mem_start, mem_size); wl1271_write32(wl, HW_PART1_START_ADDR, p->reg.start);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", wl1271_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size);
reg_start, reg_size); wl1271_write32(wl, HW_PART2_START_ADDR, p->mem2.start);
} wl1271_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size);
wl1271_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
if ((mem_start < reg_start) &&
((mem_start + mem_size) > reg_start)) {
/* Guarantee that the memory partition doesn't overlap the
* registers partition */
wl1271_debug(DEBUG_SPI, "End of partition[0] is "
"overlapping partition[1]. Adjusted.");
mem_size = reg_start - mem_start;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
} else if ((reg_start < mem_start) &&
((reg_start + reg_size) > mem_start)) {
/* Guarantee that the register partition doesn't overlap the
* memory partition */
wl1271_debug(DEBUG_SPI, "End of partition[1] is"
" overlapping partition[0]. Adjusted.");
reg_size = mem_start - reg_start;
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
}
partition[0].start = mem_start;
partition[0].size = mem_size;
partition[1].start = reg_start;
partition[1].size = reg_size;
wl->physical_mem_addr = mem_start;
wl->physical_reg_addr = reg_start;
wl->virtual_mem_addr = 0;
wl->virtual_reg_addr = mem_size;
t.tx_buf = cmd;
t.len = cmd_len;
spi_message_add_tail(&t, &m);
spi_sync(wl->spi, &m);
kfree(cmd);
return 0; return 0;
} }
...@@ -391,7 +341,7 @@ void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, ...@@ -391,7 +341,7 @@ void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf,
{ {
int physical; int physical;
physical = wl1271_translate_mem_addr(wl, addr); physical = wl1271_translate_addr(wl, addr);
wl1271_spi_read(wl, physical, buf, len, false); wl1271_spi_read(wl, physical, buf, len, false);
} }
...@@ -401,7 +351,7 @@ void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, ...@@ -401,7 +351,7 @@ void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf,
{ {
int physical; int physical;
physical = wl1271_translate_mem_addr(wl, addr); physical = wl1271_translate_addr(wl, addr);
wl1271_spi_write(wl, physical, buf, len, false); wl1271_spi_write(wl, physical, buf, len, false);
} }
...@@ -411,7 +361,7 @@ void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, ...@@ -411,7 +361,7 @@ void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len,
{ {
int physical; int physical;
physical = wl1271_translate_reg_addr(wl, addr); physical = wl1271_translate_addr(wl, addr);
wl1271_spi_read(wl, physical, buf, len, fixed); wl1271_spi_read(wl, physical, buf, len, fixed);
} }
...@@ -421,27 +371,27 @@ void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, ...@@ -421,27 +371,27 @@ void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len,
{ {
int physical; int physical;
physical = wl1271_translate_reg_addr(wl, addr); physical = wl1271_translate_addr(wl, addr);
wl1271_spi_write(wl, physical, buf, len, fixed); wl1271_spi_write(wl, physical, buf, len, fixed);
} }
u32 wl1271_mem_read32(struct wl1271 *wl, int addr) u32 wl1271_mem_read32(struct wl1271 *wl, int addr)
{ {
return wl1271_read32(wl, wl1271_translate_mem_addr(wl, addr)); return wl1271_read32(wl, wl1271_translate_addr(wl, addr));
} }
void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val) void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val)
{ {
wl1271_write32(wl, wl1271_translate_mem_addr(wl, addr), val); wl1271_write32(wl, wl1271_translate_addr(wl, addr), val);
} }
u32 wl1271_reg_read32(struct wl1271 *wl, int addr) u32 wl1271_reg_read32(struct wl1271 *wl, int addr)
{ {
return wl1271_read32(wl, wl1271_translate_reg_addr(wl, addr)); return wl1271_read32(wl, wl1271_translate_addr(wl, addr));
} }
void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val) void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val)
{ {
wl1271_write32(wl, wl1271_translate_reg_addr(wl, addr), val); wl1271_write32(wl, wl1271_translate_addr(wl, addr), val);
} }
...@@ -29,10 +29,14 @@ ...@@ -29,10 +29,14 @@
#define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0
#define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0 #define HW_PARTITION_REGISTERS_ADDR 0x1ffc0
#define HW_ACCESS_PART0_START_ADDR 0x1FFC4 #define HW_PART0_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR)
#define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8 #define HW_PART0_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 4)
#define HW_ACCESS_PART1_START_ADDR 0x1FFCC #define HW_PART1_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 8)
#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12)
#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16)
#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20)
#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
#define HW_ACCESS_REGISTER_SIZE 4 #define HW_ACCESS_REGISTER_SIZE 4
...@@ -92,8 +96,7 @@ void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val); ...@@ -92,8 +96,7 @@ void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val);
void wl1271_spi_reset(struct wl1271 *wl); void wl1271_spi_reset(struct wl1271 *wl);
void wl1271_spi_init(struct wl1271 *wl); void wl1271_spi_init(struct wl1271 *wl);
int wl1271_set_partition(struct wl1271 *wl, int wl1271_set_partition(struct wl1271 *wl,
u32 part_start, u32 part_size, struct wl1271_partition_set *p);
u32 reg_start, u32 reg_size);
static inline u32 wl1271_read32(struct wl1271 *wl, int addr) static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
{ {
......
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