18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file is part of wl12xx
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "wl1251.h"
98c2ecf20Sopenharmony_ci#include "reg.h"
108c2ecf20Sopenharmony_ci#include "io.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/* FIXME: this is static data nowadays and the table can be removed */
138c2ecf20Sopenharmony_cistatic enum wl12xx_acx_int_reg wl1251_io_reg_table[ACX_REG_TABLE_LEN] = {
148c2ecf20Sopenharmony_ci	[ACX_REG_INTERRUPT_TRIG]     = (REGISTERS_BASE + 0x0474),
158c2ecf20Sopenharmony_ci	[ACX_REG_INTERRUPT_TRIG_H]   = (REGISTERS_BASE + 0x0478),
168c2ecf20Sopenharmony_ci	[ACX_REG_INTERRUPT_MASK]     = (REGISTERS_BASE + 0x0494),
178c2ecf20Sopenharmony_ci	[ACX_REG_HINT_MASK_SET]      = (REGISTERS_BASE + 0x0498),
188c2ecf20Sopenharmony_ci	[ACX_REG_HINT_MASK_CLR]      = (REGISTERS_BASE + 0x049C),
198c2ecf20Sopenharmony_ci	[ACX_REG_INTERRUPT_NO_CLEAR] = (REGISTERS_BASE + 0x04B0),
208c2ecf20Sopenharmony_ci	[ACX_REG_INTERRUPT_CLEAR]    = (REGISTERS_BASE + 0x04A4),
218c2ecf20Sopenharmony_ci	[ACX_REG_INTERRUPT_ACK]      = (REGISTERS_BASE + 0x04A8),
228c2ecf20Sopenharmony_ci	[ACX_REG_SLV_SOFT_RESET]     = (REGISTERS_BASE + 0x0000),
238c2ecf20Sopenharmony_ci	[ACX_REG_EE_START]           = (REGISTERS_BASE + 0x080C),
248c2ecf20Sopenharmony_ci	[ACX_REG_ECPU_CONTROL]       = (REGISTERS_BASE + 0x0804)
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int wl1251_translate_reg_addr(struct wl1251 *wl, int addr)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	/* If the address is lower than REGISTERS_BASE, it means that this is
308c2ecf20Sopenharmony_ci	 * a chip-specific register address, so look it up in the registers
318c2ecf20Sopenharmony_ci	 * table */
328c2ecf20Sopenharmony_ci	if (addr < REGISTERS_BASE) {
338c2ecf20Sopenharmony_ci		/* Make sure we don't go over the table */
348c2ecf20Sopenharmony_ci		if (addr >= ACX_REG_TABLE_LEN) {
358c2ecf20Sopenharmony_ci			wl1251_error("address out of range (%d)", addr);
368c2ecf20Sopenharmony_ci			return -EINVAL;
378c2ecf20Sopenharmony_ci		}
388c2ecf20Sopenharmony_ci		addr = wl1251_io_reg_table[addr];
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return addr - wl->physical_reg_addr + wl->virtual_reg_addr;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int wl1251_translate_mem_addr(struct wl1251 *wl, int addr)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	return addr - wl->physical_mem_addr + wl->virtual_mem_addr;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_civoid wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int physical;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	physical = wl1251_translate_mem_addr(wl, addr);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	wl->if_ops->read(wl, physical, buf, len);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_civoid wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	int physical;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	physical = wl1251_translate_mem_addr(wl, addr);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	wl->if_ops->write(wl, physical, buf, len);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ciu32 wl1251_mem_read32(struct wl1251 *wl, int addr)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return wl1251_read32(wl, wl1251_translate_mem_addr(wl, addr));
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_civoid wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	wl1251_write32(wl, wl1251_translate_mem_addr(wl, addr), val);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ciu32 wl1251_reg_read32(struct wl1251 *wl, int addr)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return wl1251_read32(wl, wl1251_translate_reg_addr(wl, addr));
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_civoid wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	wl1251_write32(wl, wl1251_translate_reg_addr(wl, addr), val);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Set the partitions to access the chip addresses.
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci * There are two VIRTUAL partitions (the memory partition and the
908c2ecf20Sopenharmony_ci * registers partition), which are mapped to two different areas of the
918c2ecf20Sopenharmony_ci * PHYSICAL (hardware) memory.  This function also makes other checks to
928c2ecf20Sopenharmony_ci * ensure that the partitions are not overlapping.  In the diagram below, the
938c2ecf20Sopenharmony_ci * memory partition comes before the register partition, but the opposite is
948c2ecf20Sopenharmony_ci * also supported.
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci *                               PHYSICAL address
978c2ecf20Sopenharmony_ci *                                     space
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci *                                    |    |
1008c2ecf20Sopenharmony_ci *                                 ...+----+--> mem_start
1018c2ecf20Sopenharmony_ci *          VIRTUAL address     ...   |    |
1028c2ecf20Sopenharmony_ci *               space       ...      |    | [PART_0]
1038c2ecf20Sopenharmony_ci *                        ...         |    |
1048c2ecf20Sopenharmony_ci * 0x00000000 <--+----+...         ...+----+--> mem_start + mem_size
1058c2ecf20Sopenharmony_ci *               |    |         ...   |    |
1068c2ecf20Sopenharmony_ci *               |MEM |      ...      |    |
1078c2ecf20Sopenharmony_ci *               |    |   ...         |    |
1088c2ecf20Sopenharmony_ci *  part_size <--+----+...            |    | {unused area)
1098c2ecf20Sopenharmony_ci *               |    |   ...         |    |
1108c2ecf20Sopenharmony_ci *               |REG |      ...      |    |
1118c2ecf20Sopenharmony_ci *  part_size    |    |         ...   |    |
1128c2ecf20Sopenharmony_ci *      +     <--+----+...         ...+----+--> reg_start
1138c2ecf20Sopenharmony_ci *  reg_size              ...         |    |
1148c2ecf20Sopenharmony_ci *                           ...      |    | [PART_1]
1158c2ecf20Sopenharmony_ci *                              ...   |    |
1168c2ecf20Sopenharmony_ci *                                 ...+----+--> reg_start + reg_size
1178c2ecf20Sopenharmony_ci *                                    |    |
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_civoid wl1251_set_partition(struct wl1251 *wl,
1218c2ecf20Sopenharmony_ci			  u32 mem_start, u32 mem_size,
1228c2ecf20Sopenharmony_ci			  u32 reg_start, u32 reg_size)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct wl1251_partition partition[2];
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
1278c2ecf20Sopenharmony_ci		     mem_start, mem_size);
1288c2ecf20Sopenharmony_ci	wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
1298c2ecf20Sopenharmony_ci		     reg_start, reg_size);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Make sure that the two partitions together don't exceed the
1328c2ecf20Sopenharmony_ci	 * address range */
1338c2ecf20Sopenharmony_ci	if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) {
1348c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "Total size exceeds maximum virtual"
1358c2ecf20Sopenharmony_ci			     " address range.  Truncating partition[0].");
1368c2ecf20Sopenharmony_ci		mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size;
1378c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
1388c2ecf20Sopenharmony_ci			     mem_start, mem_size);
1398c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
1408c2ecf20Sopenharmony_ci			     reg_start, reg_size);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if ((mem_start < reg_start) &&
1448c2ecf20Sopenharmony_ci	    ((mem_start + mem_size) > reg_start)) {
1458c2ecf20Sopenharmony_ci		/* Guarantee that the memory partition doesn't overlap the
1468c2ecf20Sopenharmony_ci		 * registers partition */
1478c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "End of partition[0] is "
1488c2ecf20Sopenharmony_ci			     "overlapping partition[1].  Adjusted.");
1498c2ecf20Sopenharmony_ci		mem_size = reg_start - mem_start;
1508c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
1518c2ecf20Sopenharmony_ci			     mem_start, mem_size);
1528c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
1538c2ecf20Sopenharmony_ci			     reg_start, reg_size);
1548c2ecf20Sopenharmony_ci	} else if ((reg_start < mem_start) &&
1558c2ecf20Sopenharmony_ci		   ((reg_start + reg_size) > mem_start)) {
1568c2ecf20Sopenharmony_ci		/* Guarantee that the register partition doesn't overlap the
1578c2ecf20Sopenharmony_ci		 * memory partition */
1588c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "End of partition[1] is"
1598c2ecf20Sopenharmony_ci			     " overlapping partition[0].  Adjusted.");
1608c2ecf20Sopenharmony_ci		reg_size = mem_start - reg_start;
1618c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
1628c2ecf20Sopenharmony_ci			     mem_start, mem_size);
1638c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
1648c2ecf20Sopenharmony_ci			     reg_start, reg_size);
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	partition[0].start = mem_start;
1688c2ecf20Sopenharmony_ci	partition[0].size  = mem_size;
1698c2ecf20Sopenharmony_ci	partition[1].start = reg_start;
1708c2ecf20Sopenharmony_ci	partition[1].size  = reg_size;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	wl->physical_mem_addr = mem_start;
1738c2ecf20Sopenharmony_ci	wl->physical_reg_addr = reg_start;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	wl->virtual_mem_addr = 0;
1768c2ecf20Sopenharmony_ci	wl->virtual_reg_addr = mem_size;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	wl->if_ops->write(wl, HW_ACCESS_PART0_SIZE_ADDR, partition,
1798c2ecf20Sopenharmony_ci		sizeof(partition));
1808c2ecf20Sopenharmony_ci}
181