lineage_kernel_xcoverpro/drivers/misc/samsung/scsc/platform_mif.c

2514 lines
83 KiB
C
Executable File

/****************************************************************************
*
* Copyright (c) 2014 - 2018 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
/* Implements interface */
#include "platform_mif.h"
/* Interfaces it Uses */
#include <linux/version.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/pm_qos.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include <linux/iommu.h>
#include <linux/slab.h>
#include <linux/io.h>
#ifndef CONFIG_SOC_EXYNOS7570
#include <linux/smc.h>
#endif
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#endif
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <scsc/scsc_logring.h>
#ifdef CONFIG_SOC_EXYNOS7570
#include "mif_reg.h"
#elif defined(CONFIG_SOC_EXYNOS7872)
#include "mif_reg_S5E7872.h"
#elif defined(CONFIG_SOC_EXYNOS7885)
#include "mif_reg_S5E7885.h"
#elif defined(CONFIG_SOC_EXYNOS9610)
#include "mif_reg_S5E9610.h"
#endif
#include "platform_mif_module.h"
#ifdef CONFIG_ARCH_EXYNOS
#include <linux/soc/samsung/exynos-soc.h>
#endif
#ifdef CONFIG_SCSC_SMAPPER
#include <linux/dma-mapping.h>
#include "mif_reg_smapper.h"
#endif
#ifdef CONFIG_SCSC_QOS
#include <linux/pm_qos.h>
#endif
#if !defined(CONFIG_SOC_EXYNOS7872) && !defined(CONFIG_SOC_EXYNOS7570) \
&& !defined(CONFIG_SOC_EXYNOS7885) && !defined(CONFIG_SOC_EXYNOS9610) && !defined(CONFIG_SOC_EXYNOS9630)
#error Target processor CONFIG_SOC_EXYNOS7570 or CONFIG_SOC_EXYNOS7872 or CONFIG_SOC_EXYNOS7885 or CONFIG_SOC_EXYNOS9610 not selected
#endif
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
/* TODO: this will be put in a header */
extern int exynos_acpm_set_flag(void);
#endif
#if IS_ENABLED(CONFIG_SCSC_LOG_COLLECTION)
#include <scsc/scsc_log_collector.h>
#endif
/* Time to wait for CFG_REQ IRQ on 9610 */
#define WLBT_BOOT_TIMEOUT (HZ)
#ifdef CONFIG_OF_RESERVED_MEM
#include <linux/of_reserved_mem.h>
#endif
static unsigned long sharedmem_base;
static size_t sharedmem_size;
#ifdef CONFIG_SCSC_CHV_SUPPORT
static bool chv_disable_irq;
module_param(chv_disable_irq, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(chv_disable_irq, "Do not register for irq");
#endif
#ifdef CONFIG_SCSC_GPR4_CON_DEBUG
static u32 reg_bkp;
static bool reg_update;
static void __iomem *gpio_base;
static bool gpr4_debug;
module_param(gpr4_debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(gpr4_debug, "GPR4 PIO muxes switching to the Maxwell. Default = N. Effective on Maxwell power on");
#endif
static bool enable_platform_mif_arm_reset = true;
module_param(enable_platform_mif_arm_reset, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(enable_platform_mif_arm_reset, "Enables WIFIBT ARM cores reset");
#ifdef CONFIG_SCSC_QOS
struct qos_table {
unsigned int freq_mif;
unsigned int freq_int;
unsigned int freq_cl0;
unsigned int freq_cl1;
};
#endif
struct platform_mif {
struct scsc_mif_abs interface;
struct scsc_mbox_s *mbox;
struct platform_device *pdev;
struct device *dev;
struct {
int irq_num;
int flags;
atomic_t irq_disabled_cnt;
} wlbt_irq[PLATFORM_MIF_NUM_IRQS];
/* MIF registers preserved during suspend */
struct {
u32 irq_bit_mask;
} mif_preserve;
/* register MBOX memory space */
size_t reg_start;
size_t reg_size;
void __iomem *base;
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
/* register MBOX memory space for M4 */
size_t reg_start_m4;
size_t reg_size_m4;
void __iomem *base_m4;
#endif
/* register CMU memory space */
struct regmap *cmu_base;
#ifdef CONFIG_SCSC_CLK20MHZ
u32 usbpll_delay;
#endif
void __iomem *con0_base;
/* pmu syscon regmap */
struct regmap *pmureg;
#if defined(CONFIG_SOC_EXYNOS9610)
struct regmap *baaw_p_wlbt;
struct regmap *dbus_baaw;
struct regmap *pbus_baaw;
struct regmap *wlbt_remap;
struct regmap *boot_cfg;
/* Signalled when CFG_REQ IRQ handled */
struct completion cfg_ack;
/* State of CFG_REQ handler */
enum wlbt_boot_state {
WLBT_BOOT_IN_RESET = 0,
WLBT_BOOT_WAIT_CFG_REQ,
WLBT_BOOT_CFG_DONE,
WLBT_BOOT_CFG_ERROR
} boot_state;
#endif
#ifdef CONFIG_SCSC_SMAPPER
/* SMAPPER */
void __iomem *smapper_base;
u8 smapper_banks;
struct {
u8 bank;
u32 ws;
bool large;
struct scsc_mif_smapper_info bank_info;
} *smapper;
#endif
/* Shared memory space - reserved memory */
unsigned long mem_start;
size_t mem_size;
void __iomem *mem;
/* Callback function and dev pointer mif_intr manager handler */
void (*r4_handler)(int irq, void *data);
void *irq_dev;
/* spinlock to serialize driver access */
spinlock_t mif_spinlock;
void (*reset_request_handler)(int irq, void *data);
void *irq_reset_request_dev;
#ifdef CONFIG_SCSC_QOS
/* QoS table */
struct qos_table *qos;
bool qos_enabled;
#endif
/* Suspend/resume handlers */
int (*suspend_handler)(struct scsc_mif_abs *abs, void *data);
void (*resume_handler)(struct scsc_mif_abs *abs, void *data);
void *suspendresume_data;
};
#define platform_mif_from_mif_abs(MIF_ABS_PTR) container_of(MIF_ABS_PTR, struct platform_mif, interface)
#ifdef CONFIG_SCSC_CLK20MHZ
static void __platform_mif_usbpll_claim(struct platform_mif *platform, bool wlbt);
#endif
inline void platform_mif_reg_write(struct platform_mif *platform, u16 offset, u32 value)
{
writel(value, platform->base + offset);
}
inline u32 platform_mif_reg_read(struct platform_mif *platform, u16 offset)
{
return readl(platform->base + offset);
}
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
inline void platform_mif_reg_write_m4(struct platform_mif *platform, u16 offset, u32 value)
{
writel(value, platform->base_m4 + offset);
}
inline u32 platform_mif_reg_read_m4(struct platform_mif *platform, u16 offset)
{
return readl(platform->base_m4 + offset);
}
#endif
#ifdef CONFIG_SCSC_SMAPPER
inline void platform_mif_reg_write_smapper(struct platform_mif *platform, u16 offset, u32 value)
{
writel(value, platform->smapper_base + offset);
}
inline u32 platform_mif_reg_read_smapper(struct platform_mif *platform, u16 offset)
{
return readl(platform->smapper_base + offset);
}
#define PLATFORM_MIF_SHIFT_SMAPPER_ADDR 11 /* From 36 bits addres to 25 bits */
#define PLATFORM_MIF_SHIFT_SMAPPER_END 4 /* End address aligment */
/* Platform is responsible to give the phys mapping of the SMAPPER maps */
static int platform_mif_smapper_get_mapping(struct scsc_mif_abs *interface, u8 *phy_map, u16 *align)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u8 i;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Mapping %d banks\n", platform->smapper_banks);
if (!platform->smapper_banks)
return -EINVAL;
for (i = 0; i < platform->smapper_banks; i++) {
if (platform->smapper[i].large)
phy_map[i] = SCSC_MIF_ABS_LARGE_BANK;
else
phy_map[i] = SCSC_MIF_ABS_SMALL_BANK;
}
if (align)
*align = 1 << PLATFORM_MIF_SHIFT_SMAPPER_ADDR;
return 0;
}
static int platform_mif_smapper_get_bank_info(struct scsc_mif_abs *interface, u8 bank, struct scsc_mif_smapper_info *bank_info)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
if (!platform->smapper_banks)
return -EINVAL;
bank_info->num_entries = platform->smapper[bank].bank_info.num_entries;
bank_info->mem_range_bytes = platform->smapper[bank].bank_info.mem_range_bytes;
return 0;
}
static u8 platform_mif_smapper_granularity_to_bits(u32 granularity)
{
if (granularity <= 2 * 1024)
return 0;
if (granularity <= 4 * 1024)
return 1;
if (granularity <= 8 * 1024)
return 2;
if (granularity <= 16 * 1024)
return 3;
if (granularity <= 32 * 1024)
return 4;
if (granularity <= 64 * 1024)
return 5;
if (granularity <= 128 * 1024)
return 6;
return 7;
}
static u32 platform_mif_smapper_get_bank_base_address(struct scsc_mif_abs *interface, u8 bank)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
if (!platform->smapper)
return 0;
return platform->smapper[bank].ws;
}
/* Configure smapper according the memory map and range */
static void platform_mif_smapper_configure(struct scsc_mif_abs *interface, u32 granularity)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u8 i;
u8 gran;
u8 nb = platform->smapper_banks;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Configure SMAPPER with granularity %d\n", granularity);
gran = platform_mif_smapper_granularity_to_bits(granularity);
platform_mif_reg_write_smapper(platform, SMAPPER_QCH_DISABLE, 1);
platform_mif_reg_write_smapper(platform, ORIGIN_ADDR_AR, 0);
platform_mif_reg_write_smapper(platform, ORIGIN_ADDR_AW, 0);
/* Program SMAPPER memmap */
for (i = 0; i < nb; i++) {
/* Set ADDR_MAP_EN to 1'b0*/
platform_mif_reg_write_smapper(platform, ADDR_MAP_EN(i), 0);
/* Set START_ADDR */
platform_mif_reg_write_smapper(platform, START_ADDR(i), platform->smapper[i].ws);
/* Set ADDR_GRANULARITY - FIXED AT 4KB */
platform_mif_reg_write_smapper(platform, ADDR_GRANULARITY(i), gran);
/* WLAN_ADDR_MAP operation is started */
}
/* Set access window control (MSB 32bits Start/End address) */
/* Remapped address should be ranged from AW_START_ADDR to AW_EN_ADDR */
platform_mif_reg_write_smapper(platform, AW_START_ADDR, 0);
platform_mif_reg_write_smapper(platform, AW_END_ADDR, dma_get_mask(platform->dev) >> PLATFORM_MIF_SHIFT_SMAPPER_END);
smp_mb();
}
/* Caller is responsible of validating the phys address (alignment) */
static int platform_mif_smapper_write_sram(struct scsc_mif_abs *interface, u8 bank, u8 num_entries, u8 first_entry, dma_addr_t *addr)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u8 i;
u32 rb;
if (!platform->smapper_banks)
return -EINVAL;
if (!platform->smapper_base) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "SMAPPER not enabled\n");
return -EINVAL;
}
/* Set ADDR_MAP_EN to 1'b0*/
platform_mif_reg_write_smapper(platform, ADDR_MAP_EN(bank), 0);
/* Write mapping table to SRAM. Each entry consists of 25 bits MSB address to remap */
for (i = 0; i < num_entries; i++) {
if (!addr[i]) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "ADDR IS NULL at bank %d entry %d/%d\n", bank, first_entry + i, num_entries);
return -EINVAL;
}
/* Set SRAM_WRITE_CTRL to 1'b1*/
platform_mif_reg_write_smapper(platform, SRAM_WRITE_CTRL(bank), 1);
platform_mif_reg_write_smapper(platform, SRAM_BANK_INDEX(bank, first_entry + i), addr[i] >> PLATFORM_MIF_SHIFT_SMAPPER_ADDR);
/* check incorrect writings */
platform_mif_reg_write_smapper(platform, SRAM_WRITE_CTRL(bank), 0);
rb = platform_mif_reg_read_smapper(platform, SRAM_BANK_INDEX(bank, first_entry + i));
if (rb != addr[i] >> PLATFORM_MIF_SHIFT_SMAPPER_ADDR) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "incorrect mapping detected rb 0x%x, addr 0x%x\n", rb, (u32)addr[i] >> PLATFORM_MIF_SHIFT_SMAPPER_ADDR);
return -EFAULT;
}
}
platform_mif_reg_write_smapper(platform, ADDR_MAP_EN(bank), 1);
smp_mb();
return 0;
}
static int platform_mif_parse_smapper(struct platform_mif *platform, struct device_node *np, u8 num_banks)
{
/* SMAPPER parsing */
struct device_node *np_banks;
char node_name[50];
u32 val[2];
u8 i;
u32 bank = 0, ws = 0, wsz = 0, ent = 0, large = 0;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "banks found: %d szof %zu\n", num_banks, sizeof(*platform->smapper));
platform->smapper = kmalloc_array(num_banks, sizeof(*platform->smapper), GFP_KERNEL);
if (!platform->smapper)
return -ENOMEM;
for (i = 0; i < num_banks; i++) {
snprintf(node_name, sizeof(node_name), "smapper_bank_%d", i);
np_banks = of_find_node_by_name(np, node_name);
if (!np_banks) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "%s: could not find smapper_bank\n",
node_name);
kfree(platform->smapper);
platform->smapper = NULL;
return -ENOENT;
}
of_property_read_u32(np_banks, "bank_num", &bank);
of_property_read_u32(np_banks, "fw_window_start", &ws);
of_property_read_u32(np_banks, "fw_window_size", &wsz);
of_property_read_u32(np_banks, "num_entries", &ent);
of_property_read_u32(np_banks, "is_large", &large);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"bank %d fw_w_start 0x%x fw_w_sz 0x%x entries %d is_large %d\n",
bank, ws, wsz, ent, large);
platform->smapper[i].bank = (u8)bank;
platform->smapper[i].ws = ws;
platform->smapper[i].large = (bool)large;
platform->smapper[i].bank_info.num_entries = ent;
platform->smapper[i].bank_info.mem_range_bytes = wsz;
}
/* Update the number of banks before returning */
platform->smapper_banks = num_banks;
of_property_read_u32_array(np, "smapper_reg", val, 2);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"smapper reg address 0x%x size 0x%x\n", val[0], val[1]);
platform->smapper_base =
devm_ioremap_nocache(platform->dev, val[0], val[1]);
if (!platform->smapper_base) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Error mapping smapper register region\n");
kfree(platform->smapper);
platform->smapper = NULL;
return -ENOENT;
}
return 0;
}
#endif
#ifdef CONFIG_SCSC_QOS
static int platform_mif_parse_qos(struct platform_mif *platform, struct device_node *np)
{
int len, i;
platform->qos_enabled = false;
len = of_property_count_u32_elems(np, "qos_table");
if (!(len == 12)) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"No qos table for wlbt, or incorrect size\n");
return -ENOENT;
}
platform->qos = devm_kzalloc(platform->dev, sizeof(struct qos_table) * len / 4, GFP_KERNEL);
if (!platform->qos)
return -ENOMEM;
of_property_read_u32_array(np, "qos_table", (unsigned int *)platform->qos, len);
for (i = 0; i < len / 4; i++) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "QoS Table[%d] mif : %u int : %u cl0 : %u cl1: %u\n", i,
platform->qos[i].freq_mif,
platform->qos[i].freq_int,
platform->qos[i].freq_cl0,
platform->qos[i].freq_cl1);
}
platform->qos_enabled = true;
return 0;
}
struct qos_table platform_mif_pm_qos_get_table(struct platform_mif *platform, enum scsc_qos_config config)
{
struct qos_table table;
switch (config) {
case SCSC_QOS_MIN:
table.freq_mif = platform->qos[0].freq_mif;
table.freq_int = platform->qos[0].freq_int;
table.freq_cl0 = platform->qos[0].freq_cl0;
table.freq_cl1 = platform->qos[0].freq_cl1;
break;
case SCSC_QOS_MED:
table.freq_mif = platform->qos[1].freq_mif;
table.freq_int = platform->qos[1].freq_int;
table.freq_cl0 = platform->qos[1].freq_cl0;
table.freq_cl1 = platform->qos[1].freq_cl1;
break;
case SCSC_QOS_MAX:
table.freq_mif = platform->qos[2].freq_mif;
table.freq_int = platform->qos[2].freq_int;
table.freq_cl0 = platform->qos[2].freq_cl0;
table.freq_cl1 = platform->qos[2].freq_cl1;
break;
default:
table.freq_mif = 0;
table.freq_int = 0;
table.freq_cl0 = 0;
table.freq_cl1 = 0;
}
return table;
}
static int platform_mif_pm_qos_add_request(struct scsc_mif_abs *interface, struct scsc_mifqos_request *qos_req, enum scsc_qos_config config)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
struct qos_table table;
if (!platform)
return -ENODEV;
if (!platform->qos_enabled) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS not configured\n");
return -EOPNOTSUPP;
}
table = platform_mif_pm_qos_get_table(platform, config);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"PM QoS add request: %u. MIF %u INT %u CL0 %u CL1 %u\n", config, table.freq_mif, table.freq_int, table.freq_cl0, table.freq_cl1);
pm_qos_add_request(&qos_req->pm_qos_req_mif, PM_QOS_BUS_THROUGHPUT, table.freq_mif);
pm_qos_add_request(&qos_req->pm_qos_req_int, PM_QOS_DEVICE_THROUGHPUT, table.freq_int);
pm_qos_add_request(&qos_req->pm_qos_req_cl0, PM_QOS_CLUSTER0_FREQ_MIN, table.freq_cl0);
pm_qos_add_request(&qos_req->pm_qos_req_cl1, PM_QOS_CLUSTER1_FREQ_MIN, table.freq_cl1);
return 0;
}
static int platform_mif_pm_qos_update_request(struct scsc_mif_abs *interface, struct scsc_mifqos_request *qos_req, enum scsc_qos_config config)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
struct qos_table table;
if (!platform)
return -ENODEV;
if (!platform->qos_enabled) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS not configured\n");
return -EOPNOTSUPP;
}
table = platform_mif_pm_qos_get_table(platform, config);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"PM QoS update request: %u. MIF %u INT %u CL0 %u CL1 %u\n", config, table.freq_mif, table.freq_int, table.freq_cl0, table.freq_cl1);
pm_qos_update_request(&qos_req->pm_qos_req_mif, table.freq_mif);
pm_qos_update_request(&qos_req->pm_qos_req_int, table.freq_int);
pm_qos_update_request(&qos_req->pm_qos_req_cl0, table.freq_cl0);
pm_qos_update_request(&qos_req->pm_qos_req_cl1, table.freq_cl1);
return 0;
}
static int platform_mif_pm_qos_remove_request(struct scsc_mif_abs *interface, struct scsc_mifqos_request *qos_req)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
if (!platform)
return -ENODEV;
if (!platform->qos_enabled) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS not configured\n");
return -EOPNOTSUPP;
}
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "PM QoS remove request\n");
pm_qos_remove_request(&qos_req->pm_qos_req_mif);
pm_qos_remove_request(&qos_req->pm_qos_req_int);
pm_qos_remove_request(&qos_req->pm_qos_req_cl0);
pm_qos_remove_request(&qos_req->pm_qos_req_cl1);
return 0;
}
#endif
static void platform_mif_irq_default_handler(int irq, void *data)
{
/* Avoid unused parameter error */
(void)irq;
(void)data;
/* int handler not registered */
SCSC_TAG_INFO_DEV(PLAT_MIF, NULL, "INT handler not registered\n");
}
static void platform_mif_irq_reset_request_default_handler(int irq, void *data)
{
/* Avoid unused parameter error */
(void)irq;
(void)data;
/* int handler not registered */
SCSC_TAG_INFO_DEV(PLAT_MIF, NULL, "INT reset_request handler not registered\n");
}
irqreturn_t platform_mif_isr(int irq, void *data)
{
struct platform_mif *platform = (struct platform_mif *)data;
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "INT %pS\n", platform->r4_handler);
if (platform->r4_handler != platform_mif_irq_default_handler)
platform->r4_handler(irq, platform->irq_dev);
else
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "MIF Interrupt Handler not registered\n");
return IRQ_HANDLED;
}
#ifdef CONFIG_SCSC_ENABLE_ALIVE_IRQ
irqreturn_t platform_alive_isr(int irq, void *data)
{
struct platform_mif *platform = (struct platform_mif *)data;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INT received\n");
return IRQ_HANDLED;
}
#endif
irqreturn_t platform_wdog_isr(int irq, void *data)
{
int ret = 0;
struct platform_mif *platform = (struct platform_mif *)data;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INT received\n");
if (platform->reset_request_handler != platform_mif_irq_reset_request_default_handler) {
disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num);
platform->reset_request_handler(irq, platform->irq_reset_request_dev);
} else {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WDOG Interrupt reset_request_handler not registered\n");
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Disabling unhandled WDOG IRQ.\n");
disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num);
atomic_inc(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt);
}
#ifdef CONFIG_SOC_EXYNOS9610
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS,
WLBT_RESET_REQ_CLR, WLBT_RESET_REQ_CLR);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Clearing WLBT_RESET_REQ\n");
if (ret < 0)
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to Set WLBT_CTRL_NS[WLBT_RESET_REQ_CLR]: %d\n", ret);
#else
ret = regmap_update_bits(platform->pmureg, WIFI_CTRL_NS,
WIFI_RESET_REQ_CLR, WIFI_RESET_REQ_CLR);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Clearing WIFI_RESET_REQ\n");
if (ret < 0)
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to Set WIFI_CTRL_NS[WIFI_RESET_REQ_CLR]: %d\n", ret);
#endif
return IRQ_HANDLED;
}
#ifdef CONFIG_SOC_EXYNOS9610
/*
* Attached array contains the replacement PMU boot code which should
* be programmed using the CBUS during the config phase.
*/
uint32_t ka_patch[] = {
/* Low temp fix 28/1
* Maxwell142 PMU+PROC combined boot ROM
* IP Version: 0xA3
* Major Version: 0xF, Minor Version: 0xF
* PMU ROM version: 0x4
* PROC ROM version: 0x0
*/
0x90750002,
0x11a4c218,
0x75671191,
0x9075e090,
0x54b3e5e7,
0x30f76001,
0xb14315a2,
0xb4b2e503,
0xb153fb03,
0xa90185fc,
0xacf50774,
0x75fdadb5,
0xb47508a0,
0x54b3e501,
0x75fa7001,
0x907500b4,
0x78cb8018,
0x80837982,
0x07a075c5,
0xb0783779,
0xb40754e6,
0x0b800207,
0xc404f6d9,
0xaf7590f5,
0x75038000,
0x53229090,
0xce53eff7,
0xd90479fe,
0xfdce53fe,
0xfed90c79,
0x75fbce53,
0x91530b92,
0xf7ce53fd,
0x5308f943,
0xf922fef9,
0xfbd8fed9,
0x019e7522,
0x75cfc175,
0xc375a4c2,
0x47c4754a,
0x75a4c575,
0xc7756dc6,
0x03d27540,
0x7510d375,
0xca7500c9,
0x00cb75d0,
0x7500cc75,
0x9b75009a,
0x009c75c0,
0x78009d75,
0x12827402,
0xc6438b80,
0x74097802,
0x8b8012e7,
0x75d09075,
0x9e750291,
0x01a97502,
0x00000022,
};
irqreturn_t platform_cfg_req_isr(int irq, void *data)
{
struct platform_mif *platform = (struct platform_mif *)data;
const u64 EXYNOS_WLBT = 0x1;
u64 ret64 = 0;
s32 ret = 0;
unsigned int ka_addr = 0x1000;
uint32_t *ka_patch_addr = ka_patch;
u32 id;
#define CHECK(x) do { \
int retval = (x); \
if (retval < 0) \
goto cfg_error; \
} while (0)
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INT received\n");
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "disable_irq\n");
/* mask the irq */
disable_irq_nosync(platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num);
/* Was the CFG_REQ irq received from WLBT before we expected it?
* Typically this indicates an issue returning WLBT HW to reset.
*/
if (platform->boot_state != WLBT_BOOT_WAIT_CFG_REQ) {
u32 val;
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Spurious CFG_REQ IRQ from WLBT!\n");
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
SCSC_TAG_INFO(PLAT_MIF, "CENTRAL_SEQ_WLBT_STATUS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_CTRL_NS, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_CTRL_NS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_CTRL_S, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_CTRL_S 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_DEBUG, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val);
return IRQ_HANDLED;
}
/* CBUS should be ready before we get CFG_REQ, but we suspect
* CBUS is not ready yet. add some delay to see if that helps
*/
udelay(100);
/* Set TZPC to non-secure mode */
ret64 = exynos_smc(SMC_CMD_CONN_IF, (EXYNOS_WLBT << 32) | EXYNOS_SET_CONN_TZPC, 0, 0);
if (ret64)
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to set TZPC to non-secure mode: %llu\n", ret64);
else
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"SMC_CMD_CONN_IF run successfully : %llu\n", ret64);
/* WLBT_REMAP */
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP begin\n");
CHECK(regmap_write(platform->wlbt_remap, 0x0, WLBT_DBUS_BAAW_0_START >> 12));
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "WLBT_REMAP end\n");
/* CHIP_VERSION_ID - overload with EMA settings */
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "CHIP_VERSION_ID begin\n");
regmap_read(platform->wlbt_remap, 0x10, &id);
id &= ~CHIP_VERSION_ID_EMA_MASK;
id |= CHIP_VERSION_ID_EMA_VALUE;
CHECK(regmap_write(platform->wlbt_remap, 0x10, id));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "CHIP_VERSION_ID 0x%x end\n", id);
/* DBUS_BAAW regions */
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "DBUS_BAAW begin\n");
CHECK(regmap_write(platform->dbus_baaw, 0x0, WLBT_DBUS_BAAW_0_START >> 12));
CHECK(regmap_write(platform->dbus_baaw, 0x4, WLBT_DBUS_BAAW_0_END >> 12));
CHECK(regmap_write(platform->dbus_baaw, 0x8, platform->mem_start >> 12));
CHECK(regmap_write(platform->dbus_baaw, 0xC, WLBT_BAAW_ACCESS_CTRL));
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "DBUS_BAAW end\n");
/* PBUS_BAAW regions */
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "PBUS_BAAW begin\n");
CHECK(regmap_write(platform->pbus_baaw, 0x0, WLBT_PBUS_BAAW_0_START >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x4, WLBT_PBUS_BAAW_0_END >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x8, WLBT_PBUS_MBOX_CP2WLBT_BASE >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0xC, WLBT_BAAW_ACCESS_CTRL));
CHECK(regmap_write(platform->pbus_baaw, 0x10, WLBT_PBUS_BAAW_1_START >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x14, WLBT_PBUS_BAAW_1_END >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x18, WLBT_PBUS_MBOX_SHUB2WLBT_BASE >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x1C, WLBT_BAAW_ACCESS_CTRL));
CHECK(regmap_write(platform->pbus_baaw, 0x20, WLBT_PBUS_BAAW_2_START >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x24, WLBT_PBUS_BAAW_2_END >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x28, WLBT_PBUS_USI_CMG00_BASE >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x2C, WLBT_BAAW_ACCESS_CTRL));
CHECK(regmap_write(platform->pbus_baaw, 0x30, WLBT_PBUS_BAAW_3_START >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x34, WLBT_PBUS_BAAW_3_END >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x38, WLBT_PBUS_SYSREG_CMGP2WLBT_BASE >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x3C, WLBT_BAAW_ACCESS_CTRL));
CHECK(regmap_write(platform->pbus_baaw, 0x40, WLBT_PBUS_BAAW_4_START >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x44, WLBT_PBUS_BAAW_4_END >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x48, WLBT_PBUS_GPIO_CMGP_BASE >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x4C, WLBT_BAAW_ACCESS_CTRL));
CHECK(regmap_write(platform->pbus_baaw, 0x50, WLBT_PBUS_BAAW_5_START >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x54, WLBT_PBUS_BAAW_5_END >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x58, WLBT_PBUS_SHUB_BASE >> 12));
CHECK(regmap_write(platform->pbus_baaw, 0x5C, WLBT_BAAW_ACCESS_CTRL));
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "PBUS_BAAW end\n");
/* PMU boot bug workaround */
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "BOOT_WLBT begin\n");
CHECK(regmap_write(platform->boot_cfg, 0x0, 0x1));
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "BOOT_WLBT done\n");
while (ka_patch_addr < (ka_patch + ARRAY_SIZE(ka_patch))) {
CHECK(regmap_write(platform->boot_cfg, ka_addr, *ka_patch_addr));
ka_addr += sizeof(ka_patch[0]);
ka_patch_addr++;
}
/* Notify PMU of configuration done */
CHECK(regmap_write(platform->boot_cfg, 0x0, 0x0));
/* BOOT_CFG_ACK */
CHECK(regmap_write(platform->boot_cfg, 0x4, 0x1));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "BOOT_CFG_ACK done\n");
/* Delay to allow HW to clear CFG_REQ and hence de-assert IRQ, which
* it does in response to CFG_ACK
*/
udelay(100);
/* Release ownership of MASK_PWR_REQ */
/* See sequence in 9.6.6 */
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS,
MASK_PWR_REQ, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to clear WLBT_CTRL_NS[MASK_PWR_REQ]: %d\n", ret);
goto cfg_error;
}
/* Mark as CFQ_REQ handled, so boot may continue */
platform->boot_state = WLBT_BOOT_CFG_DONE;
/* Signal triggering function that the IRQ arrived and CFG was done */
complete(&platform->cfg_ack);
return IRQ_HANDLED;
cfg_error:
platform->boot_state = WLBT_BOOT_CFG_ERROR;
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "ERROR: WLBT Config failed. WLBT will not work\n");
complete(&platform->cfg_ack);
return IRQ_HANDLED;
}
#endif
static void platform_mif_unregister_irq(struct platform_mif *platform)
{
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unregistering IRQs\n");
devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_MBOX].irq_num, platform);
devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num, platform);
/* Reset irq_disabled_cnt for WDOG IRQ since the IRQ itself is here unregistered and disabled */
atomic_set(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt, 0);
#ifdef CONFIG_SCSC_ENABLE_ALIVE_IRQ
/* if ALIVE irq is required */
devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_ALIVE].irq_num, platform);
#endif
#ifdef CONFIG_SOC_EXYNOS9610
devm_free_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num, platform);
#endif
}
static int platform_mif_register_irq(struct platform_mif *platform)
{
int err;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering IRQs\n");
/* Register MBOX irq */
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering MBOX irq: %d flag 0x%x\n",
platform->wlbt_irq[PLATFORM_MIF_MBOX].irq_num, platform->wlbt_irq[PLATFORM_MIF_MBOX].flags);
err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_MBOX].irq_num, platform_mif_isr,
platform->wlbt_irq[PLATFORM_MIF_MBOX].flags, DRV_NAME, platform);
if (IS_ERR_VALUE((unsigned long)err)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to register MBOX handler: %d. Aborting.\n", err);
err = -ENODEV;
return err;
}
/* Register WDOG irq */
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering WDOG irq: %d flag 0x%x\n",
platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num, platform->wlbt_irq[PLATFORM_MIF_WDOG].flags);
err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num, platform_wdog_isr,
platform->wlbt_irq[PLATFORM_MIF_WDOG].flags, DRV_NAME, platform);
if (IS_ERR_VALUE((unsigned long)err)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to register WDOG handler: %d. Aborting.\n", err);
err = -ENODEV;
return err;
}
#ifdef CONFIG_SCSC_ENABLE_ALIVE_IRQ
/* Register ALIVE irq */
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering ALIVE irq: %d flag 0x%x\n",
platform->wlbt_irq[PLATFORM_MIF_ALIVE].irq_num, platform->wlbt_irq[PLATFORM_MIF_ALIVE].flags);
err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_ALIVE].irq_num, platform_alive_isr,
platform->wlbt_irq[PLATFORM_MIF_ALIVE].flags, DRV_NAME, platform);
if (IS_ERR_VALUE(err)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to register ALIVE handler: %d. Aborting.\n", err);
err = -ENODEV;
return err;
}
#endif
#ifdef CONFIG_SOC_EXYNOS9610
/* Mark as WLBT in reset before enabling IRQ to guard against spurious IRQ */
platform->boot_state = WLBT_BOOT_IN_RESET;
smp_wmb(); /* commit before irq */
/* Register WB2AP_CFG_REQ irq */
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering CFG_REQ irq: %d flag 0x%x\n",
platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num, platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].flags);
err = devm_request_irq(platform->dev, platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].irq_num, platform_cfg_req_isr,
platform->wlbt_irq[PLATFORM_MIF_CFG_REQ].flags, DRV_NAME, platform);
if (IS_ERR_VALUE((unsigned long)err)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to register CFG_REQ handler: %d. Aborting.\n", err);
err = -ENODEV;
return err;
}
#endif
return 0;
}
static void platform_mif_destroy(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
platform_mif_unregister_irq(platform);
}
static char *platform_mif_get_uid(struct scsc_mif_abs *interface)
{
/* Avoid unused parameter error */
(void)interface;
return "0";
}
/* WLBT Power domain */
static int platform_mif_power(struct scsc_mif_abs *interface, bool power)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val = 0;
s32 ret = 0;
#ifdef CONFIG_SOC_EXYNOS9610
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "power %d\n", power);
if (power)
val = MASK_PWR_REQ;
/* See sequence in 9.6.6 */
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS,
MASK_PWR_REQ, val);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WLBT_CTRL_NS[MASK_PWR_REQ]: %d\n", ret);
return ret;
}
#else
/* See sequence in 8.6.6 */
/* WIFI power on/off control If WIFI_PWRON = 1
* and WIFI_START=1, WIFI enters to UP state.
* This bit is 0 as default value because WIFI
* should be reset at AP boot mode after Power-on Reset.
*/
if (power)
val = WIFI_PWRON;
ret = regmap_update_bits(platform->pmureg, WIFI_CTRL_NS,
WIFI_PWRON, val);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WIFI_CTRL_NS[WIFI_PWRON]: %d\n", ret);
return ret;
}
#endif
return 0;
}
/* WLBT RESET */
static int platform_mif_hold_reset(struct scsc_mif_abs *interface, bool reset)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val = 0;
s32 ret = 0;
#ifdef CONFIG_SOC_EXYNOS9610
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "reset %d\n", reset);
if (reset)
val = WLBT_RESET_SET;
/* See sequence in 9.6.6 */
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS,
WLBT_RESET_SET, val);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WLBT_CTRL_NS[WLBT_RESET_SET]: %d\n", ret);
return ret;
}
#else
if (reset)
val = WIFI_RESET_SET;
/* See sequence in 8.6.6 */
ret = regmap_update_bits(platform->pmureg, WIFI_CTRL_NS,
WIFI_RESET_SET, val);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WIFI_CTRL_NS[WIFI_RESET_SET]: %d\n", ret);
return ret;
}
#endif
return 0;
}
/* WLBT START */
static int platform_mif_start(struct scsc_mif_abs *interface, bool start)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val = 0;
s32 ret = 0;
#ifdef CONFIG_SOC_EXYNOS9610
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "start %d\n", start);
if (start)
val = WLBT_START;
/* See sequence in 9.6.6 */
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_S,
WLBT_START, val);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WLBT_CTRL_S[WLBT_START]: %d\n", ret);
return ret;
}
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"update WIFI_CTRL_S[WIFI_START]: %d\n", ret);
/* At this point WLBT should assert the CFG_REQ IRQ, so wait for it */
if (start &&
wait_for_completion_timeout(&platform->cfg_ack, WLBT_BOOT_TIMEOUT) == 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Timeout waiting for CFG_REQ IRQ\n");
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
SCSC_TAG_INFO(PLAT_MIF, "CENTRAL_SEQ_WLBT_STATUS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_DEBUG, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val);
return -ETIMEDOUT;
}
/* only continue if CFG_REQ IRQ configured WLBT/PMU correctly */
if (platform->boot_state == WLBT_BOOT_CFG_ERROR) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "CFG_REQ failed to configure WLBT.\n");
return -EIO;
}
#else
if (start)
val = WIFI_START;
/* See sequence in 8.6.6 */
ret = regmap_update_bits(platform->pmureg, WIFI_CTRL_S,
WIFI_START, val);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WIFI_CTRL_S[WIFI_START]: %d\n", ret);
return ret;
}
#endif
return 0;
}
static int platform_mif_pmu_reset_release(struct scsc_mif_abs *interface)
{
int ret = 0;
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
#ifdef CONFIG_SOC_EXYNOS9610
/* We're now ready for the IRQ */
platform->boot_state = WLBT_BOOT_WAIT_CFG_REQ;
smp_wmb(); /* commit before irq */
#endif
ret = platform_mif_power(interface, true);
if (ret)
return ret;
ret = platform_mif_hold_reset(interface, false);
if (ret)
return ret;
ret = platform_mif_start(interface, true);
if (ret)
return ret;
return ret;
}
int __platform_mif_9610_recover_reset(struct platform_mif *platform)
{
unsigned long timeout;
int ret;
u32 val;
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Attempt to recover WLBT HW reset");
/*
* Set CFG_ACK = 1
* Poll CFG_REQ until REQ == 0
* Set CFG_ACK = 0
* Poll CENTRAL_SEQ_WLBT_STATUS SM status again
* Print WLBT_DEBUG at each step.
* (Remember to set "echo 1 > /sys/kernel/debug/exynos-rgt/vqmmc/enable" before the test).
*/
/* CFG_ACK = 1 */
regmap_write(platform->boot_cfg, 0x4, 0x1);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Force BOOT_CFG_ACK = 1\n");
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
SCSC_TAG_INFO(PLAT_MIF, "CENTRAL_SEQ_WLBT_STATUS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_DEBUG, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val);
/* TBD poll CFG_REQ status */
udelay(250);
/* CFG_ACK = 0 */
regmap_write(platform->boot_cfg, 0x4, 0x0);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Force BOOT_CFG_ACK = 0\n");
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
SCSC_TAG_INFO(PLAT_MIF, "CENTRAL_SEQ_WLBT_STATUS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_DEBUG, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val);
/* Check WLBT_STATUS again */
timeout = jiffies + msecs_to_jiffies(500);
do {
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
val &= STATES;
val >>= 16;
if (val == 0x80) {
ret = 0; /* Recovered OK */
goto done;
}
} while (time_before(jiffies, timeout));
ret = -ETIME;
done:
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
SCSC_TAG_INFO(PLAT_MIF, "CENTRAL_SEQ_WLBT_STATUS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_DEBUG, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val);
if (ret == 0)
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Recovered reset");
else
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Reset not recovered");
/* Save log at point of failure, last to show recovery attempt */
#if IS_ENABLED(CONFIG_SCSC_LOG_COLLECTION)
scsc_log_collector_schedule_collection(SCSC_LOG_HOST_COMMON, SCSC_LOG_HOST_COMMON_RECOVER_RST);
#endif
return ret;
}
static int platform_mif_pmu_reset(struct scsc_mif_abs *interface, u8 rst_case)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
unsigned long timeout;
int ret;
u32 val;
if (rst_case == 0 || rst_case > 2) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Incorrect pmu reset case %d\n", rst_case);
return -EIO;
}
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
ret = regmap_update_bits(platform->pmureg, RESET_AHEAD_WIFI_PWR_REG,
SYS_PWR_CFG_2, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update RESET_ASB_WIFI_SYS_PWR_REG %d\n", ret);
return ret;
}
#elif defined(CONFIG_SOC_EXYNOS7570)
ret = regmap_update_bits(platform->pmureg, RESET_ASB_WIFI_SYS_PWR_REG,
SYS_PWR_CFG_2, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update RESET_ASB_WIFI_SYS_PWR_REG %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_SOC_EXYNOS9610
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "rst_case %d\n", rst_case);
/* Revert power control ownership to AP, as WLBT is going down (S9.6.6). */
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS,
MASK_PWR_REQ, MASK_PWR_REQ);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WLBT_CTRL_NS[MASK_PWR_REQ]: %d\n", ret);
return ret;
}
/* reset sequence as per excite implementation for Leman */
ret = regmap_update_bits(platform->pmureg, CENTRAL_SEQ_WLBT_CONFIGURATION,
SYS_PWR_CFG_16, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update CENTRAL_SEQ_WLBT_CONFIGURATION %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, RESET_AHEAD_WLBT_SYS_PWR_REG,
SYS_PWR_CFG_2, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update RESET_AHEAD_WLBT_SYS_PWR_REG %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, CLEANY_BUS_WLBT_SYS_PWR_REG,
SYS_PWR_CFG, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update CLEANY_BUS_WLBT_SYS_PWR_REG%d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, LOGIC_RESET_WLBT_SYS_PWR_REG,
SYS_PWR_CFG_2, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update LOGIC_RESET_WLBT_SYS_PWR_REG %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, TCXO_GATE_WLBT_SYS_PWR_REG,
SYS_PWR_CFG, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update TCXO_GATE_WLBT_SYS_PWR_REG %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, WLBT_DISABLE_ISO_SYS_PWR_REG,
SYS_PWR_CFG, 1);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WLBT_DISABLE_ISO_SYS_PWR_REG %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, WLBT_RESET_ISO_SYS_PWR_REG,
SYS_PWR_CFG, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WLBT_RESET_ISO_SYS_PWR_REG %d\n", ret);
return ret;
}
/* rst_case is always 2 on 9610 */
ret = platform_mif_hold_reset(interface, true);
if (ret)
return ret;
timeout = jiffies + msecs_to_jiffies(500);
do {
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
val &= STATES;
val >>= 16;
if (val == 0x80) {
/* OK. Switch CTRL_NS[MASK_PWR_REQ] ownership to FW following
* reset. WLBT PWR_REQ is cleared when it's put in reset.
* The SW PWR_REQ remains asserted, but as ownership is now FW,
* it'll be ignored. This leaves it as we found it.
*/
platform_mif_power(interface, false);
return 0; /* OK - return */
}
} while (time_before(jiffies, timeout));
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Timeout waiting for CENTRAL_SEQ_WLBT_STATUS SM status\n");
regmap_read(platform->pmureg, CENTRAL_SEQ_WLBT_STATUS, &val);
SCSC_TAG_INFO(PLAT_MIF, "CENTRAL_SEQ_WLBT_STATUS 0x%x\n", val);
regmap_read(platform->pmureg, WLBT_DEBUG, &val);
SCSC_TAG_INFO(PLAT_MIF, "WLBT_DEBUG 0x%x\n", val);
#if 0
/* Try to recovery in case of reset failure */
ret = __platform_mif_9610_recover_reset(platform);
if (!ret)
return 0;
#endif
#else
/* 7885, 7872, 7570 */
ret = regmap_update_bits(platform->pmureg, CLEANY_BUS_WIFI_SYS_PWR_REG,
SYS_PWR_CFG, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update CLEANY_BUS_WIFI_SYS_PWR_REG%d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, LOGIC_RESET_WIFI_SYS_PWR_REG,
SYS_PWR_CFG_2, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update LOGIC_RESET_WIFI_SYS_PWR_REG %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, TCXO_GATE_WIFI_SYS_PWR_REG,
SYS_PWR_CFG, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update TCXO_GATE_WIFI_SYS_PWR_REG %d\n", ret);
return ret;
}
#endif
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
ret = regmap_update_bits(platform->pmureg, WIFI_DISABLE_ISO_SYS_PWR_REG,
SYS_PWR_CFG, 1);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WIFI_DISABLE_ISO_SYS_PWR_REG %d\n", ret);
return ret;
}
ret = regmap_update_bits(platform->pmureg, WIFI_RESET_ISO_SYS_PWR_REG,
SYS_PWR_CFG, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update WIFI_RESET_ISO_SYS_PWR_REG %d\n", ret);
return ret;
}
#endif
#ifdef CONFIG_SOC_EXYNOS9610
(void)platform;
(void)timeout;
(void)ret;
(void)val;
#else
/* 7885, 7872, 7570 */
ret = regmap_update_bits(platform->pmureg, CENTRAL_SEQ_WIFI_CONFIGURATION,
SYS_PWR_CFG_16, 0);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to update CENTRAL_SEQ_WIFI_CONFIGURATION %d\n", ret);
return ret;
}
if (rst_case == 1)
ret = platform_mif_power(interface, false);
else
ret = platform_mif_hold_reset(interface, true);
if (ret)
return ret;
timeout = jiffies + msecs_to_jiffies(500);
do {
regmap_read(platform->pmureg, CENTRAL_SEQ_WIFI_STATUS, &val);
val &= STATES;
val >>= 16;
if (val == 0x80)
return 0;
} while (time_before(jiffies, timeout));
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Timeout waiting for CENTRAL_SEQ_WIFI_STATUS SM status\n");
#endif
return -ETIME;
}
/* reset=0 - release from reset */
/* reset=1 - hold reset */
static int platform_mif_reset(struct scsc_mif_abs *interface, bool reset)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 ret = 0;
u32 val;
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n");
if (enable_platform_mif_arm_reset || !reset) {
if (!reset) { /* Release from reset */
#ifdef CONFIG_ARCH_EXYNOS
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"SOC_VERSION: product_id 0x%x, rev 0x%x\n",
exynos_soc_info.product_id, exynos_soc_info.revision);
#endif
#ifdef CONFIG_SOC_EXYNOS9610
(void)val;
#else
/* Enable R4 access to MIF resources */
/* Address is fixed and its value is in the dts */
/* It defines the base address of DRAM space which WIFI accesses.
* Default value is 0x0_6000_0000. It can configure only MSB 14-bit.
* You must align this base address with WIFI memory access size that WIFI_MEM_SIZE
* defines
*/
/* >>12 represents 4K aligment (see size)*/
val = (platform->mem_start & 0xFFFFFC000) >> 12;
regmap_write(platform->pmureg, WIFI2AP_MEM_CONFIG1, val);
/* Size */
/*It defines the DRAM memory size to which WLBT can
* access.
* Definition of the size has 4 KB resolution defined from
* minimum 4 KB to maximum 1GB.
* 20'b0000_0000_0000_0000_0001 = 4KB
* 20'b0000_0000_0000_0000_0010 = 8KB
* 20'b0000_0000_0001_0000_0000 = 1 MB
* 20'b0000_0000_0010_0000_0000 = 2 MB
* 20'b0000_0000_0100_0000_0000 = 4 MB (default)
* 20'b0000_0000_1000_0000_0000 = 8 MB
* 20'b0000_1000_0000_0000_0000 = 128 MB
* 20'b0100_0000_0000_0000_0000 = 1 GB
*/
/* Size is fixed and its value is in the dts */
/* >>12 represents 4K aligment (see address)*/
val = platform->mem_size >> 12;
regmap_write(platform->pmureg, WIFI2AP_MEM_CONFIG0, val);
#endif
#if (defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)) && defined(CONFIG_ACPM_DVFS)
/* Immediately prior to reset release, set up ACPM
* to ensure BUCK2 gets the right voltage
*/
exynos_acpm_set_flag();
#endif
ret = platform_mif_pmu_reset_release(interface);
} else {
/* Put back into reset */
ret = platform_mif_pmu_reset(interface, 2);
#if !defined(CONFIG_SOC_EXYNOS9610)
/* WLBT should be stopped/powered-down at this point */
regmap_write(platform->pmureg, WIFI2AP_MEM_CONFIG1, 0x00000);
regmap_write(platform->pmureg, WIFI2AP_MEM_CONFIG0, 0x00000);
#endif
}
} else
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Not resetting ARM Cores - enable_platform_mif_arm_reset: %d\n",
enable_platform_mif_arm_reset);
return ret;
}
static void __iomem *platform_mif_map_region(unsigned long phys_addr, size_t size)
{
int i;
struct page **pages;
void *vmem;
size = PAGE_ALIGN(size);
pages = kmalloc((size >> PAGE_SHIFT) * sizeof(*pages), GFP_KERNEL);
if (!pages)
return NULL;
/* Map NORMAL_NC pages with kernel virtual space */
for (i = 0; i < (size >> PAGE_SHIFT); i++) {
pages[i] = phys_to_page(phys_addr);
phys_addr += PAGE_SIZE;
}
vmem = vmap(pages, size >> PAGE_SHIFT, VM_MAP, pgprot_writecombine(PAGE_KERNEL));
kfree(pages);
return (void __iomem *)vmem;
}
static void platform_mif_unmap_region(void *vmem)
{
vunmap(vmem);
}
static void *platform_mif_map(struct scsc_mif_abs *interface, size_t *allocated)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u8 i;
if (allocated)
*allocated = 0;
platform->mem =
platform_mif_map_region(platform->mem_start, platform->mem_size);
if (!platform->mem) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Error remaping shared memory\n");
return NULL;
}
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Map: virt %p phys %lx\n", platform->mem, (uintptr_t)platform->mem_start);
/* Initialise MIF registers with documented defaults */
/* MBOXes */
for (i = 0; i < NUM_MBOX_PLAT; i++) {
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(ISSR(i)), 0x00000000);
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
platform_mif_reg_write_m4(platform, MAILBOX_WLBT_REG(ISSR(i)), 0x00000000);
#endif
}
/* MRs */ /*1's - set all as Masked */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), 0xffff0000);
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR1), 0x0000ffff);
#ifdef CONFIG_SOC_EXYNOS7570
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR2), 0x0000ffff);
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
platform_mif_reg_write_m4(platform, MAILBOX_WLBT_REG(INTMR1), 0x0000ffff);
#endif
/* CRs */ /* 1's - clear all the interrupts */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR0), 0xffff0000);
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR1), 0x0000ffff);
#ifdef CONFIG_SOC_EXYNOS7570
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR2), 0x0000ffff);
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
platform_mif_reg_write_m4(platform, MAILBOX_WLBT_REG(INTCR1), 0x0000ffff);
#endif
#if !defined(CONFIG_SOC_EXYNOS9610)
/*Set WLBT_BOOT_TEST_RST_CFG to 0 to boot from external DRAM */
regmap_write(platform->pmureg, WLBT_BOOT_TEST_RST_CFG, 0x00000);
#endif
/* Add more requrired initialization here: */
#ifdef CONFIG_SCSC_GPR4_CON_DEBUG
/* PIO muxes switching to the Maxwell subsystem */
/* GPR4_CON (0x13750040) = 0x00666666 */
if (gpr4_debug) {
reg_bkp = readl(gpio_base);
writel(0x00666666, gpio_base);
SCSC_TAG_WARNING_DEV(PLAT_MIF, platform->dev, "[WARNING] Changing GPR4_CON from 0x%x to 0x%x", reg_bkp, readl(gpio_base));
reg_update = true;
}
#endif
#ifdef CONFIG_SCSC_CHV_SUPPORT
if (chv_disable_irq == true) {
if (allocated)
*allocated = platform->mem_size;
return platform->mem;
}
#endif
/* register interrupts */
if (platform_mif_register_irq(platform)) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unmap: virt %p phys %lx\n", platform->mem, (uintptr_t)platform->mem_start);
platform_mif_unmap_region(platform->mem);
return NULL;
}
if (allocated)
*allocated = platform->mem_size;
/* Set the CR4 base address in Mailbox??*/
return platform->mem;
}
/* HERE: Not sure why mem is passed in - its stored in platform - as it should be */
static void platform_mif_unmap(struct scsc_mif_abs *interface, void *mem)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
#ifdef CONFIG_SCSC_GPR4_CON_DEBUG
u32 prev;
if (gpr4_debug && reg_update) {
prev = readl(gpio_base);
writel(reg_bkp, gpio_base);
SCSC_TAG_WARNING_DEV(PLAT_MIF, platform->dev, "[WARNING] Restoring GPR4_CON from 0x%x to 0x%x", prev, readl(gpio_base));
}
reg_update = false;
#endif
/* Avoid unused parameter error */
(void)mem;
/* MRs */ /*1's - set all as Masked */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), 0xffff0000);
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR1), 0x0000ffff);
#ifdef CONFIG_SOC_EXYNOS7570
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR2), 0x0000ffff);
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
platform_mif_reg_write_m4(platform, MAILBOX_WLBT_REG(INTMR1), 0x0000ffff);
#endif
#ifdef CONFIG_SCSC_CHV_SUPPORT
/* Restore PIO changed by Maxwell subsystem */
if (chv_disable_irq == false)
/* Unregister IRQs */
platform_mif_unregister_irq(platform);
#else
platform_mif_unregister_irq(platform);
#endif
/* CRs */ /* 1's - clear all the interrupts */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR0), 0xffff0000);
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR1), 0x0000ffff);
#ifdef CONFIG_SOC_EXYNOS7570
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR2), 0x0000ffff);
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
platform_mif_reg_write_m4(platform, MAILBOX_WLBT_REG(INTCR1), 0x0000ffff);
#endif
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unmap: virt %p phys %lx\n", platform->mem, (uintptr_t)platform->mem_start);
platform_mif_unmap_region(platform->mem);
platform->mem = NULL;
}
static u32 platform_mif_irq_bit_mask_status_get(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val;
val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0)) >> 16;
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Getting INTMR0: 0x%x\n", val);
return val;
}
static u32 platform_mif_irq_get(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val;
/* Function has to return the interrupts that are enabled *AND* not masked */
val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR0)) >> 16;
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Getting INT-INTMSR0: 0x%x\n", val);
return val;
}
static void platform_mif_irq_bit_set(struct scsc_mif_abs *interface, int bit_num, enum scsc_mif_abs_target target)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 reg;
if (bit_num >= 16) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num);
return;
}
#ifdef CONFIG_SOC_EXYNOS9610
reg = INTGR1;
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(reg), (1 << bit_num));
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTGR1: bit %d on target %d\n", bit_num, target);
#else
/* Generate INT to R4/M4 - VIC */
if (target == SCSC_MIF_ABS_TARGET_R4)
reg = INTGR1;
else if (target == SCSC_MIF_ABS_TARGET_M4)
#ifdef CONFIG_SOC_EXYNOS7570
reg = INTGR2;
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
reg = INTGR1;
#endif
else {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect Target %d\n", target);
return;
}
#endif
#ifdef CONFIG_SOC_EXYNOS7570
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(reg), (1 << bit_num));
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
if (target == SCSC_MIF_ABS_TARGET_R4)
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(reg), (1 << bit_num));
else
platform_mif_reg_write_m4(platform, MAILBOX_WLBT_REG(reg), (1 << bit_num));
#endif
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTGR1: bit %d on target %d\n", bit_num, target);
}
static void platform_mif_irq_bit_clear(struct scsc_mif_abs *interface, int bit_num)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
if (bit_num >= 16) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num);
return;
}
/* WRITE : 1 = Clears Interrupt */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTCR0), ((1 << bit_num) << 16));
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTCR0: bit %d\n", bit_num);
}
static void platform_mif_irq_bit_mask(struct scsc_mif_abs *interface, int bit_num)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val;
unsigned long flags;
if (bit_num >= 16) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num);
return;
}
spin_lock_irqsave(&platform->mif_spinlock, flags);
val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0));
/* WRITE : 1 = Mask Interrupt */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), val | ((1 << bit_num) << 16));
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Setting INTMR0: 0x%x bit %d\n", val | (1 << bit_num), bit_num);
}
static void platform_mif_irq_bit_unmask(struct scsc_mif_abs *interface, int bit_num)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 val;
unsigned long flags;
if (bit_num >= 16) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Incorrect INT number: %d\n", bit_num);
return;
}
spin_lock_irqsave(&platform->mif_spinlock, flags);
val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0));
/* WRITE : 0 = Unmask Interrupt */
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), val & ~((1 << bit_num) << 16));
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "UNMASK Setting INTMR0: 0x%x bit %d\n", val & ~((1 << bit_num) << 16), bit_num);
}
/* Return the contents of the mask register */
static u32 __platform_mif_irq_bit_mask_read(struct platform_mif *platform)
{
u32 val;
unsigned long flags;
spin_lock_irqsave(&platform->mif_spinlock, flags);
val = platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0));
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Read INTMR0: 0x%x\n", val);
return val;
}
/* Write the mask register, destroying previous contents */
static void __platform_mif_irq_bit_mask_write(struct platform_mif *platform, u32 val)
{
unsigned long flags;
spin_lock_irqsave(&platform->mif_spinlock, flags);
platform_mif_reg_write(platform, MAILBOX_WLBT_REG(INTMR0), val);
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "Write INTMR0: 0x%x\n", val);
}
static void platform_mif_irq_reg_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
unsigned long flags;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering mif int handler %pS in %p %p\n", handler, platform, interface);
spin_lock_irqsave(&platform->mif_spinlock, flags);
platform->r4_handler = handler;
platform->irq_dev = dev;
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
}
static void platform_mif_irq_unreg_handler(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
unsigned long flags;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unregistering mif int handler %pS\n", interface);
spin_lock_irqsave(&platform->mif_spinlock, flags);
platform->r4_handler = platform_mif_irq_default_handler;
platform->irq_dev = NULL;
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
}
static void platform_mif_irq_reg_reset_request_handler(struct scsc_mif_abs *interface, void (*handler)(int irq, void *data), void *dev)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering mif reset_request int handler %pS in %p %p\n", handler, platform, interface);
platform->reset_request_handler = handler;
platform->irq_reset_request_dev = dev;
if (atomic_read(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt)) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev,
"Default WDOG handler disabled by spurios IRQ...re-enabling.\n");
enable_irq(platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_num);
atomic_set(&platform->wlbt_irq[PLATFORM_MIF_WDOG].irq_disabled_cnt, 0);
}
}
static void platform_mif_irq_unreg_reset_request_handler(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "UnRegistering mif reset_request int handler %pS\n", interface);
platform->reset_request_handler = platform_mif_irq_reset_request_default_handler;
platform->irq_reset_request_dev = NULL;
}
static void platform_mif_suspend_reg_handler(struct scsc_mif_abs *interface,
int (*suspend)(struct scsc_mif_abs *abs, void *data),
void (*resume)(struct scsc_mif_abs *abs, void *data),
void *data)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Registering mif suspend/resume handlers in %p %p\n", platform, interface);
platform->suspend_handler = suspend;
platform->resume_handler = resume;
platform->suspendresume_data = data;
}
static void platform_mif_suspend_unreg_handler(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Unregistering mif suspend/resume handlers in %p %p\n", platform, interface);
platform->suspend_handler = NULL;
platform->resume_handler = NULL;
platform->suspendresume_data = NULL;
}
static u32 *platform_mif_get_mbox_ptr(struct scsc_mif_abs *interface, u32 mbox_index)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
u32 *addr;
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "mbox_index 0x%x\n", mbox_index);
addr = platform->base + MAILBOX_WLBT_REG(ISSR(mbox_index));
return addr;
}
static int platform_mif_get_mifram_ref(struct scsc_mif_abs *interface, void *ptr, scsc_mifram_ref *ref)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n");
if (!platform->mem) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Memory unmmaped\n");
return -ENOMEM;
}
/* Check limits! */
if (ptr >= (platform->mem + platform->mem_size)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Unable to get pointer reference\n");
return -ENOMEM;
}
*ref = (scsc_mifram_ref)((uintptr_t)ptr - (uintptr_t)platform->mem);
return 0;
}
static void *platform_mif_get_mifram_ptr(struct scsc_mif_abs *interface, scsc_mifram_ref ref)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n");
if (!platform->mem) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Memory unmmaped\n");
return NULL;
}
/* Check limits */
if (ref >= 0 && ref < platform->mem_size)
return (void *)((uintptr_t)platform->mem + (uintptr_t)ref);
else
return NULL;
}
static void *platform_mif_get_mifram_phy_ptr(struct scsc_mif_abs *interface, scsc_mifram_ref ref)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n");
if (!platform->mem_start) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Memory unmmaped\n");
return NULL;
}
return (void *)((uintptr_t)platform->mem_start + (uintptr_t)ref);
}
static uintptr_t platform_mif_get_mif_pfn(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
return vmalloc_to_pfn(platform->mem);
}
static struct device *platform_mif_get_mif_device(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
SCSC_TAG_DEBUG_DEV(PLAT_MIF, platform->dev, "\n");
return platform->dev;
}
static void platform_mif_irq_clear(void)
{
/* Implement if required */
}
static int platform_mif_read_register(struct scsc_mif_abs *interface, u64 id, u32 *val)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
if (id == SCSC_REG_READ_WLBT_STAT) {
regmap_read(platform->pmureg, WLBT_STAT, val);
return 0;
}
return -EIO;
}
static void platform_mif_dump_register(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
unsigned long flags;
spin_lock_irqsave(&platform->mif_spinlock, flags);
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTGR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTGR0)));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTGR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTGR1)));
#ifdef CONFIG_SOC_EXYNOS7570
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTGR2 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTGR2)));
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTGR1(M4) 0x%08x\n", platform_mif_reg_read_m4(platform, MAILBOX_WLBT_REG(INTGR1)));
#endif
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTCR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTCR0)));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTCR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTCR1)));
#ifdef CONFIG_SOC_EXYNOS7570
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTCR2 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTCR2)));
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTCR1(M4) 0x%08x\n", platform_mif_reg_read_m4(platform, MAILBOX_WLBT_REG(INTCR1)));
#endif
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR0)));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR1)));
#ifdef CONFIG_SOC_EXYNOS7570
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMR2 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMR2)));
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMR1(M4) 0x%08x\n", platform_mif_reg_read_m4(platform, MAILBOX_WLBT_REG(INTMR1)));
#endif
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTSR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTSR0)));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTSR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTSR1)));
#ifdef CONFIG_SOC_EXYNOS7570
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTSR2 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTSR2)));
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTSR1(M4) 0x%08x\n", platform_mif_reg_read_m4(platform, MAILBOX_WLBT_REG(INTSR1)));
#endif
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMSR0 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR0)));
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMSR1 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR1)));
#ifdef CONFIG_SOC_EXYNOS7570
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMSR2 0x%08x\n", platform_mif_reg_read(platform, MAILBOX_WLBT_REG(INTMSR2)));
#elif defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "INTMSR1(M4) 0x%08x\n", platform_mif_reg_read_m4(platform, MAILBOX_WLBT_REG(INTMSR1)));
#endif
spin_unlock_irqrestore(&platform->mif_spinlock, flags);
}
static void platform_mif_cleanup(struct scsc_mif_abs *interface)
{
#ifdef CONFIG_SCSC_CLK20MHZ
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
/* Restore USBPLL owenership to the AP so that USB driver may control it */
__platform_mif_usbpll_claim(platform, false);
#endif
}
static void platform_mif_restart(struct scsc_mif_abs *interface)
{
#ifdef CONFIG_SCSC_CLK20MHZ
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
/* Restore USBPLL owenership to the wlbt */
__platform_mif_usbpll_claim(platform, true);
#endif
}
#ifdef CONFIG_OF_RESERVED_MEM
static int __init platform_mif_wifibt_if_reserved_mem_setup(struct reserved_mem *remem)
{
SCSC_TAG_DEBUG(PLAT_MIF, "memory reserved: mem_base=%#lx, mem_size=%zd\n",
(unsigned long)remem->base, (size_t)remem->size);
sharedmem_base = remem->base;
sharedmem_size = remem->size;
return 0;
}
RESERVEDMEM_OF_DECLARE(wifibt_if, "exynos,wifibt_if", platform_mif_wifibt_if_reserved_mem_setup);
#endif
#ifdef CONFIG_SCSC_CLK20MHZ
static void __platform_mif_usbpll_claim(struct platform_mif *platform, bool wlbt)
{
s32 ret = 0;
if (!platform->cmu_base) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "USBPLL claim not enabled\n");
return;
}
/* Set USBPLL ownership, by setting AP2WLBT_USBPLL_WPLL_SEL */
if (wlbt) {
/* WLBT f/w has control */
ret = regmap_update_bits(platform->cmu_base, USBPLL_CON1, AP2WLBT_USBPLL_WPLL_SEL, 0);
if (ret < 0)
goto error;
} else {
/* Ensure PLL runs */
ret = regmap_update_bits(platform->cmu_base, USBPLL_CON1, AP2WLBT_USBPLL_WPLL_EN, AP2WLBT_USBPLL_WPLL_EN);
if (ret < 0)
goto error;
/* AP has control */
udelay(platform->usbpll_delay);
ret = regmap_update_bits(platform->cmu_base, USBPLL_CON1, AP2WLBT_USBPLL_WPLL_SEL, AP2WLBT_USBPLL_WPLL_SEL);
if (ret < 0)
goto error;
}
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "USBPLL_CON1 assigned to %s\n", wlbt ? "WLBT" : "AP");
return;
error:
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Failed to update USBPLL_CON1 to %s\n", wlbt ? "WLBT" : "AP");
}
#endif
struct scsc_mif_abs *platform_mif_create(struct platform_device *pdev)
{
struct scsc_mif_abs *platform_if;
struct platform_mif *platform =
(struct platform_mif *)devm_kzalloc(&pdev->dev, sizeof(struct platform_mif), GFP_KERNEL);
int err = 0;
u8 i = 0;
struct resource *reg_res;
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
struct resource *reg_res_m4;
#endif
#ifdef CONFIG_SCSC_CLK20MHZ
/* usb pll ownership */
const char *usbowner = NULL;
u32 usbdelay = 0;
#endif
#ifdef CONFIG_SCSC_SMAPPER
u32 smapper_banks = 0;
#endif
if (!platform)
return NULL;
SCSC_TAG_INFO_DEV(PLAT_MIF, &pdev->dev, "Creating MIF platform device\n");
platform_if = &platform->interface;
/* initialise interface structure */
platform_if->destroy = platform_mif_destroy;
platform_if->get_uid = platform_mif_get_uid;
platform_if->reset = platform_mif_reset;
platform_if->map = platform_mif_map;
platform_if->unmap = platform_mif_unmap;
platform_if->irq_bit_set = platform_mif_irq_bit_set;
platform_if->irq_get = platform_mif_irq_get;
platform_if->irq_bit_mask_status_get = platform_mif_irq_bit_mask_status_get;
platform_if->irq_bit_clear = platform_mif_irq_bit_clear;
platform_if->irq_bit_mask = platform_mif_irq_bit_mask;
platform_if->irq_bit_unmask = platform_mif_irq_bit_unmask;
platform_if->irq_reg_handler = platform_mif_irq_reg_handler;
platform_if->irq_unreg_handler = platform_mif_irq_unreg_handler;
platform_if->irq_reg_reset_request_handler = platform_mif_irq_reg_reset_request_handler;
platform_if->irq_unreg_reset_request_handler = platform_mif_irq_unreg_reset_request_handler;
platform_if->suspend_reg_handler = platform_mif_suspend_reg_handler;
platform_if->suspend_unreg_handler = platform_mif_suspend_unreg_handler;
platform_if->get_mbox_ptr = platform_mif_get_mbox_ptr;
platform_if->get_mifram_ptr = platform_mif_get_mifram_ptr;
platform_if->get_mifram_ref = platform_mif_get_mifram_ref;
platform_if->get_mifram_pfn = platform_mif_get_mif_pfn;
platform_if->get_mifram_phy_ptr = platform_mif_get_mifram_phy_ptr;
platform_if->get_mif_device = platform_mif_get_mif_device;
platform_if->irq_clear = platform_mif_irq_clear;
platform_if->mif_dump_registers = platform_mif_dump_register;
platform_if->mif_read_register = platform_mif_read_register;
platform_if->mif_cleanup = platform_mif_cleanup;
platform_if->mif_restart = platform_mif_restart;
#ifdef CONFIG_SCSC_SMAPPER
platform_if->mif_smapper_get_mapping = platform_mif_smapper_get_mapping;
platform_if->mif_smapper_get_bank_info = platform_mif_smapper_get_bank_info;
platform_if->mif_smapper_write_sram = platform_mif_smapper_write_sram;
platform_if->mif_smapper_configure = platform_mif_smapper_configure;
platform_if->mif_smapper_get_bank_base_address = platform_mif_smapper_get_bank_base_address;
#endif
#ifdef CONFIG_SCSC_QOS
platform_if->mif_pm_qos_add_request = platform_mif_pm_qos_add_request;
platform_if->mif_pm_qos_update_request = platform_mif_pm_qos_update_request;
platform_if->mif_pm_qos_remove_request = platform_mif_pm_qos_remove_request;
#endif
/* Update state */
platform->pdev = pdev;
platform->dev = &pdev->dev;
platform->r4_handler = platform_mif_irq_default_handler;
platform->irq_dev = NULL;
platform->reset_request_handler = platform_mif_irq_reset_request_default_handler;
platform->irq_reset_request_dev = NULL;
platform->suspend_handler = NULL;
platform->resume_handler = NULL;
platform->suspendresume_data = NULL;
#ifdef CONFIG_OF_RESERVED_MEM
platform->mem_start = sharedmem_base;
platform->mem_size = sharedmem_size;
#else
/* If CONFIG_OF_RESERVED_MEM is not defined, sharedmem values should be
* parsed from the scsc_wifibt binding
*/
if (of_property_read_u32(pdev->dev.of_node, "sharedmem-base", &sharedmem_base)) {
err = -EINVAL;
goto error_exit;
}
platform->mem_start = sharedmem_base;
if (of_property_read_u32(pdev->dev.of_node, "sharedmem-size", &sharedmem_size)) {
err = -EINVAL;
goto error_exit;
}
platform->mem_size = sharedmem_size;
#endif
#ifdef CONFIG_SCSC_SMAPPER
platform->smapper = NULL;
#endif
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
/* TZASC configuration is required for WLBT to access DRAM from Katmai onward */
/* Base address should be 4KB aligned. This call needs proper support in EL3_MON */
err = exynos_smc(EXYNOS_SMC_WLBT_TZASC_CMD, WLBT_TZASC, platform->mem_start,
platform->mem_size);
/* Anyway we keep on WLBT initialization even if TZASC failed to minimize disruption*/
if (err)
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"WLBT Failed to configure TZASC, err=%d. DRAM could be NOT accessible in secure mode\n", err);
else
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WLBT TZASC configured OK\n");
#endif
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "platform->mem_start 0x%x platform->mem_size 0x%x\n", (u32)platform->mem_start, (u32)platform->mem_size);
if (platform->mem_start == 0)
SCSC_TAG_WARNING_DEV(PLAT_MIF, platform->dev, "platform->mem_start is 0");
if (platform->mem_size == 0) {
/* We return return if mem_size is 0 as it does not make any sense.
* This may be an indication of an incorrect platform device binding.
*/
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "platform->mem_size is 0");
err = -EINVAL;
goto error_exit;
}
/* Memory resource - Phys Address of MAILBOX_WLBT register map */
reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!reg_res) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Error getting mem resource for MAILBOX_WLBT\n");
err = -ENOENT;
goto error_exit;
}
platform->reg_start = reg_res->start;
platform->reg_size = resource_size(reg_res);
platform->base =
devm_ioremap_nocache(platform->dev, reg_res->start, resource_size(reg_res));
if (!platform->base) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Error mapping register region\n");
err = -EBUSY;
goto error_exit;
}
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "platform->reg_start %lx size %x base %p\n", (uintptr_t)platform->reg_start, (u32)platform->reg_size, platform->base);
#if defined(CONFIG_SOC_EXYNOS7872) || defined(CONFIG_SOC_EXYNOS7885)
/* Memory resource for M4 MBOX bank - Phys Address of MAILBOX_WLBT register map */
reg_res_m4 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!reg_res_m4) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "Error getting mem resource for MAILBOX_WLBT\n");
err = -ENOENT;
goto error_exit;
}
platform->reg_start_m4 = reg_res_m4->start;
platform->reg_size_m4 = resource_size(reg_res_m4);
platform->base_m4 =
devm_ioremap_nocache(platform->dev, reg_res_m4->start, resource_size(reg_res_m4));
if (!platform->base_m4) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Error mapping M4 register region\n");
err = -EBUSY;
goto error_exit;
}
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "platform->reg_start_m4 %lx size_m4 %x base_m4 %p\n", (uintptr_t)platform->reg_start_m4, (u32)platform->reg_size_m4, platform->base_m4);
#endif
#ifdef CONFIG_SCSC_CLK20MHZ
/* Get usbpll,owner if Maxwell has to provide 20Mhz clock to USB subsystem */
platform->cmu_base = NULL;
if (of_property_read_string(pdev->dev.of_node, "usbpll,owner", &usbowner)) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "usbpll,owner of_property does not exist or is invalid\n");
goto cont;
} else {
if (strcasecmp(usbowner, "y"))
goto skip_usbpll;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "usbpll,owner is enabled\n");
/* CMU_FSYS reg map - syscon */
platform->cmu_base = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,syscon-cmu_fsys");
if (IS_ERR(platform->cmu_base)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"syscon regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->cmu_base));
goto skip_usbpll;
}
if (of_property_read_u32(pdev->dev.of_node, "usbpll,udelay", &usbdelay))
goto skip_usbpll;
platform->usbpll_delay = usbdelay;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Mapping CMU_FSYS with %dus delay\n", usbdelay);
goto cont;
skip_usbpll:
platform->cmu_base = NULL;
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "usbpll owner skipped\n");
}
cont:
#endif
#ifdef CONFIG_SCSC_GPR4_CON_DEBUG
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Mapping GPR4_CON 0x13750040\n");
gpio_base =
devm_ioremap_nocache(platform->dev, 0x13750040, 4);
#endif
/* Get the 4 IRQ resources */
for (i = 0; i < 4; i++) {
struct resource *irq_res;
int irqtag;
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!irq_res) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev, "No IRQ resource at index %d\n", i);
err = -ENOENT;
goto error_exit;
}
if (!strcmp(irq_res->name, "MBOX")) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "MBOX irq %d flag 0x%x\n", (u32)irq_res->start, (u32)irq_res->flags);
irqtag = PLATFORM_MIF_MBOX;
} else if (!strcmp(irq_res->name, "ALIVE")) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "ALIVE irq %d flag 0x%x\n", (u32)irq_res->start, (u32)irq_res->flags);
irqtag = PLATFORM_MIF_ALIVE;
} else if (!strcmp(irq_res->name, "WDOG")) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "WDOG irq %d flag 0x%x\n", (u32)irq_res->start, (u32)irq_res->flags);
irqtag = PLATFORM_MIF_WDOG;
} else if (!strcmp(irq_res->name, "CFG_REQ")) {
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "CFG_REQ irq %d flag 0x%x\n", (u32)irq_res->start, (u32)irq_res->flags);
irqtag = PLATFORM_MIF_CFG_REQ;
} else {
SCSC_TAG_ERR_DEV(PLAT_MIF, &pdev->dev, "Invalid irq res name: %s\n",
irq_res->name);
err = -EINVAL;
goto error_exit;
}
platform->wlbt_irq[irqtag].irq_num = irq_res->start;
platform->wlbt_irq[irqtag].flags = (irq_res->flags & IRQF_TRIGGER_MASK);
atomic_set(&platform->wlbt_irq[irqtag].irq_disabled_cnt, 0);
}
/* PMU reg map - syscon */
platform->pmureg = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(platform->pmureg)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"syscon regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->pmureg));
err = -EINVAL;
goto error_exit;
}
#ifdef CONFIG_SOC_EXYNOS9610
/* Completion event and state used to indicate CFG_REQ IRQ occurred */
init_completion(&platform->cfg_ack);
platform->boot_state = WLBT_BOOT_IN_RESET;
/* BAAW_P_WLBT */
platform->baaw_p_wlbt = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,baaw_p_wlbt-syscon-phandle");
if (IS_ERR(platform->baaw_p_wlbt)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"baaw_p_wlbt regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->baaw_p_wlbt));
err = -EINVAL;
goto error_exit;
}
/* DBUS_BAAW */
platform->dbus_baaw = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,dbus_baaw-syscon-phandle");
if (IS_ERR(platform->dbus_baaw)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"dbus_baaw regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->dbus_baaw));
err = -EINVAL;
goto error_exit;
}
/* PBUS_BAAW */
platform->pbus_baaw = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,pbus_baaw-syscon-phandle");
if (IS_ERR(platform->pbus_baaw)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"pbus_baaw regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->pbus_baaw));
err = -EINVAL;
goto error_exit;
}
/* WLBT_REMAP */
platform->wlbt_remap = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,wlbt_remap-syscon-phandle");
if (IS_ERR(platform->wlbt_remap)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"wlbt_remap regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->wlbt_remap));
err = -EINVAL;
goto error_exit;
}
/* BOOT_CFG */
platform->boot_cfg = syscon_regmap_lookup_by_phandle(platform->dev->of_node,
"samsung,boot_cfg-syscon-phandle");
if (IS_ERR(platform->boot_cfg)) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"boot_cfg regmap lookup failed. Aborting. %ld\n", PTR_ERR(platform->boot_cfg));
err = -EINVAL;
goto error_exit;
}
#endif
#ifdef CONFIG_SCSC_SMAPPER
/* SMAPPER parsing */
if (!of_property_read_u32(pdev->dev.of_node, "smapper_num_banks", &smapper_banks))
platform_mif_parse_smapper(platform, platform->dev->of_node, smapper_banks);
#endif
#ifdef CONFIG_SCSC_QOS
platform_mif_parse_qos(platform, platform->dev->of_node);
#endif
#ifdef CONFIG_SCSC_CLK20MHZ
/* Assign USBPLL ownership to WLBT f/w */
__platform_mif_usbpll_claim(platform, true);
#endif
/* Initialize spinlock */
spin_lock_init(&platform->mif_spinlock);
#ifndef CONFIG_SOC_EXYNOS9610
/* Clear WIFI_ACTIVE flag in WAKEUP_STAT */
err = regmap_update_bits(platform->pmureg, WIFI_CTRL_NS, WIFI_ACTIVE_CLR, 1);
if (err < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to Set WIFI_CTRL_NS[WIFI_ACTIVE_CLR]: %d\n", err);
}
#endif
return platform_if;
error_exit:
devm_kfree(&pdev->dev, platform);
return NULL;
}
void platform_mif_destroy_platform(struct platform_device *pdev, struct scsc_mif_abs *interface)
{
#ifdef CONFIG_SCSC_CLK20MHZ
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
/* Assign USBPLL ownership to AP */
__platform_mif_usbpll_claim(platform, false);
#endif
}
struct platform_device *platform_mif_get_platform_dev(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
BUG_ON(!interface || !platform);
return platform->pdev;
}
struct device *platform_mif_get_dev(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
BUG_ON(!interface || !platform);
return platform->dev;
}
/* Preserve MIF registers during suspend.
* If all users of the MIF (AP, mx140, CP, etc) release it, the registers
* will lose their values. Save the useful subset here.
*
* Assumption: the AP will not change the register values between the suspend
* and resume handlers being called!
*/
static void platform_mif_reg_save(struct platform_mif *platform)
{
platform->mif_preserve.irq_bit_mask = __platform_mif_irq_bit_mask_read(platform);
}
/* Restore MIF registers that may have been lost during suspend */
static void platform_mif_reg_restore(struct platform_mif *platform)
{
__platform_mif_irq_bit_mask_write(platform, platform->mif_preserve.irq_bit_mask);
}
int platform_mif_suspend(struct scsc_mif_abs *interface)
{
int r = 0;
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
if (platform->suspend_handler)
r = platform->suspend_handler(interface, platform->suspendresume_data);
/* Save the MIF registers.
* This must be done last as the suspend_handler may use the MIF
*/
platform_mif_reg_save(platform);
return r;
}
void platform_mif_resume(struct scsc_mif_abs *interface)
{
struct platform_mif *platform = platform_mif_from_mif_abs(interface);
s32 ret;
/* Restore the MIF registers.
* This must be done first as the resume_handler may use the MIF.
*/
platform_mif_reg_restore(platform);
#ifdef CONFIG_SOC_EXYNOS9610
SCSC_TAG_INFO_DEV(PLAT_MIF, platform->dev, "Clear WLBT_ACTIVE_CLR flag\n");
/* Clear WLBT_ACTIVE_CLR flag in WLBT_CTRL_NS */
ret = regmap_update_bits(platform->pmureg, WLBT_CTRL_NS, WLBT_ACTIVE_CLR, 1);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to Set WLBT_CTRL_NS[WLBT_ACTIVE_CLR]: %d\n", ret);
}
#else
/* Clear WIFI_ACTIVE flag in WAKEUP_STAT */
ret = regmap_update_bits(platform->pmureg, WIFI_CTRL_NS, WIFI_ACTIVE_CLR, 1);
if (ret < 0) {
SCSC_TAG_ERR_DEV(PLAT_MIF, platform->dev,
"Failed to Set WIFI_CTRL_NS[WIFI_ACTIVE_CLR]: %d\n", ret);
}
#endif
if (platform->resume_handler)
platform->resume_handler(interface, platform->suspendresume_data);
}