blob: 5fe2294819bf65155e40b0ccbe8c4e10294a1449 [file] [log] [blame]
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/msm_ipa.h>
#include "ipa_i.h"
#include "ipa_emulation_stubs.h"
# undef strsame
# define strsame(x, y) \
(!strcmp((x), (y)))
/*
* The following enum values used to index tables below.
*/
enum dtsi_index_e {
DTSI_INDEX_3_5_1 = 0,
DTSI_INDEX_4_0 = 1,
};
struct dtsi_replacement_u32 {
char *key;
u32 value;
};
struct dtsi_replacement_u32_table {
struct dtsi_replacement_u32 *p_table;
u32 num_entries;
};
struct dtsi_replacement_bool {
char *key;
bool value;
};
struct dtsi_replacement_bool_table {
struct dtsi_replacement_bool *p_table;
u32 num_entries;
};
struct dtsi_replacement_u32_array {
char *key;
u32 *p_value;
u32 num_elements;
};
struct dtsi_replacement_u32_array_table {
struct dtsi_replacement_u32_array *p_table;
u32 num_entries;
};
struct dtsi_replacement_resource_table {
struct resource *p_table;
u32 num_entries;
};
/*
* Any of the data below with _4_0 in the name represent data taken
* from the 4.0 dtsi file.
*
* Any of the data below with _3_5_1 in the name represent data taken
* from the 3.5.1 dtsi file.
*/
static struct dtsi_replacement_bool ipa3_plat_drv_bool_4_0[] = {
{"qcom,use-ipa-tethering-bridge", true},
{"qcom,modem-cfg-emb-pipe-flt", true},
{"qcom,ipa-wdi2", true},
{"qcom,use-64-bit-dma-mask", false},
{"qcom,bandwidth-vote-for-ipa", false},
{"qcom,skip-uc-pipe-reset", false},
{"qcom,tethered-flow-control", true},
{"qcom,use-rg10-limitation-mitigation", false},
{"qcom,do-not-use-ch-gsi-20", false},
{"qcom,use-ipa-pm", false},
};
static struct dtsi_replacement_bool ipa3_plat_drv_bool_3_5_1[] = {
{"qcom,use-ipa-tethering-bridge", true},
{"qcom,modem-cfg-emb-pipe-flt", true},
{"qcom,ipa-wdi2", true},
{"qcom,use-64-bit-dma-mask", false},
{"qcom,bandwidth-vote-for-ipa", true},
{"qcom,skip-uc-pipe-reset", false},
{"qcom,tethered-flow-control", false},
{"qcom,use-rg10-limitation-mitigation", false},
{"qcom,do-not-use-ch-gsi-20", false},
{"qcom,use-ipa-pm", false},
};
static struct dtsi_replacement_bool_table
ipa3_plat_drv_bool_table[] = {
{ ipa3_plat_drv_bool_3_5_1,
ARRAY_SIZE(ipa3_plat_drv_bool_3_5_1) },
{ ipa3_plat_drv_bool_4_0,
ARRAY_SIZE(ipa3_plat_drv_bool_4_0) },
};
static struct dtsi_replacement_u32 ipa3_plat_drv_u32_4_0[] = {
{"qcom,ipa-hw-ver", IPA_HW_v4_0},
{"qcom,ipa-hw-mode", 3},
{"qcom,wan-rx-ring-size", 192},
{"qcom,lan-rx-ring-size", 192},
{"qcom,ee", 0},
{"emulator-bar0-offset", 0x01C00000},
};
static struct dtsi_replacement_u32 ipa3_plat_drv_u32_3_5_1[] = {
{"qcom,ipa-hw-ver", IPA_HW_v3_5_1},
{"qcom,ipa-hw-mode", 3},
{"qcom,wan-rx-ring-size", 192},
{"qcom,lan-rx-ring-size", 192},
{"qcom,ee", 0},
{"emulator-bar0-offset", 0x01C00000},
};
static struct dtsi_replacement_u32_table ipa3_plat_drv_u32_table[] = {
{ ipa3_plat_drv_u32_3_5_1,
ARRAY_SIZE(ipa3_plat_drv_u32_3_5_1) },
{ ipa3_plat_drv_u32_4_0,
ARRAY_SIZE(ipa3_plat_drv_u32_4_0) },
};
static u32 mhi_event_ring_id_limits_array_4_0[] = {
9, 10
};
static u32 mhi_event_ring_id_limits_array_3_5_1[] = {
IPA_MHI_GSI_EVENT_RING_ID_START, IPA_MHI_GSI_EVENT_RING_ID_END
};
static u32 ipa_tz_unlock_reg_array_4_0[] = {
0x04043583c, 0x00001000
};
static u32 ipa_tz_unlock_reg_array_3_5_1[] = {
0x04043583c, 0x00001000
};
static u32 ipa_ram_mmap_array_4_0[] = {
0x00000280, 0x00000000, 0x00000000, 0x00000288, 0x00000078,
0x00004000, 0x00000308, 0x00000078, 0x00004000, 0x00000388,
0x00000078, 0x00004000, 0x00000408, 0x00000078, 0x00004000,
0x0000000F, 0x00000000, 0x00000007, 0x00000008, 0x0000000E,
0x00000488, 0x00000078, 0x00004000, 0x00000508, 0x00000078,
0x00004000, 0x0000000F, 0x00000000, 0x00000007, 0x00000008,
0x0000000E, 0x00000588, 0x00000078, 0x00004000, 0x00000608,
0x00000078, 0x00004000, 0x00000688, 0x00000140, 0x000007C8,
0x00000000, 0x00000800, 0x000007D0, 0x00000200, 0x000009D0,
0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x000013F0,
0x0000100C, 0x000023FC, 0x00000000, 0x000023FC, 0x00000000,
0x000023FC, 0x00000000, 0x000023FC, 0x00000000, 0x00000080,
0x00000200, 0x00002800, 0x000023FC, 0x00000000, 0x000023FC,
0x00000000, 0x000023FC, 0x00000000, 0x000023FC, 0x00000000,
0x00002400, 0x00000400, 0x00000BD8, 0x00000050, 0x00000C30,
0x00000060, 0x00000C90, 0x00000140, 0x00000DD0, 0x00000180,
0x00000F50, 0x00000180, 0x000010D0, 0x00000180, 0x00001250,
0x00000180, 0x000013D0, 0x00000020
};
static u32 ipa_ram_mmap_array_3_5_1[] = {
0x00000280, 0x00000000, 0x00000000, 0x00000288, 0x00000078,
0x00004000, 0x00000308, 0x00000078, 0x00004000, 0x00000388,
0x00000078, 0x00004000, 0x00000408, 0x00000078, 0x00004000,
0x0000000F, 0x00000000, 0x00000007, 0x00000008, 0x0000000E,
0x00000488, 0x00000078, 0x00004000, 0x00000508, 0x00000078,
0x00004000, 0x0000000F, 0x00000000, 0x00000007, 0x00000008,
0x0000000E, 0x00000588, 0x00000078, 0x00004000, 0x00000608,
0x00000078, 0x00004000, 0x00000688, 0x00000140, 0x000007C8,
0x00000000, 0x00000800, 0x000007D0, 0x00000200, 0x000009D0,
0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x00000BD8,
0x00001024, 0x00002000, 0x00000000, 0x00002000, 0x00000000,
0x00002000, 0x00000000, 0x00002000, 0x00000000, 0x00000080,
0x00000200, 0x00002000, 0x00002000, 0x00000000, 0x00002000,
0x00000000, 0x00002000, 0x00000000, 0x00002000, 0x00000000,
0x00001C00, 0x00000400
};
struct dtsi_replacement_u32_array ipa3_plat_drv_u32_array_4_0[] = {
{"qcom,mhi-event-ring-id-limits",
mhi_event_ring_id_limits_array_4_0,
ARRAY_SIZE(mhi_event_ring_id_limits_array_4_0) },
{"qcom,ipa-tz-unlock-reg",
ipa_tz_unlock_reg_array_4_0,
ARRAY_SIZE(ipa_tz_unlock_reg_array_4_0) },
{"qcom,ipa-ram-mmap",
ipa_ram_mmap_array_4_0,
ARRAY_SIZE(ipa_ram_mmap_array_4_0) },
};
struct dtsi_replacement_u32_array ipa3_plat_drv_u32_array_3_5_1[] = {
{"qcom,mhi-event-ring-id-limits",
mhi_event_ring_id_limits_array_3_5_1,
ARRAY_SIZE(mhi_event_ring_id_limits_array_3_5_1) },
{"qcom,ipa-tz-unlock-reg",
ipa_tz_unlock_reg_array_3_5_1,
ARRAY_SIZE(ipa_tz_unlock_reg_array_3_5_1) },
{"qcom,ipa-ram-mmap",
ipa_ram_mmap_array_3_5_1,
ARRAY_SIZE(ipa_ram_mmap_array_3_5_1) },
};
struct dtsi_replacement_u32_array_table
ipa3_plat_drv_u32_array_table[] = {
{ ipa3_plat_drv_u32_array_3_5_1,
ARRAY_SIZE(ipa3_plat_drv_u32_array_3_5_1) },
{ ipa3_plat_drv_u32_array_4_0,
ARRAY_SIZE(ipa3_plat_drv_u32_array_4_0) },
};
#define INTCTRL_OFFSET 0x083C0000
#define INTCTRL_SIZE 0x00000110
#define IPA_BASE_OFFSET_4_0 0x01e00000
#define IPA_BASE_SIZE_4_0 0x00034000
#define GSI_BASE_OFFSET_4_0 0x01e04000
#define GSI_BASE_SIZE_4_0 0x00028000
struct resource ipa3_plat_drv_resource_4_0[] = {
/*
* PLEASE NOTE WELL: The following offset values below
* ("ipa-base", "gsi-base", and "intctrl-base") are used to
* calculate offsets relative to the PCI BAR0 address provided
* by the PCI probe. After their use to calculate the
* offsets, they are not used again, since PCI ultimately
* dictates where things live.
*/
{
IPA_BASE_OFFSET_4_0,
(IPA_BASE_OFFSET_4_0 + IPA_BASE_SIZE_4_0),
"ipa-base",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
{
GSI_BASE_OFFSET_4_0,
(GSI_BASE_OFFSET_4_0 + GSI_BASE_SIZE_4_0),
"gsi-base",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
/*
* The following entry is germane only to the emulator
* environment. It is needed to locate the emulator's PCI
* interrupt controller...
*/
{
INTCTRL_OFFSET,
(INTCTRL_OFFSET + INTCTRL_SIZE),
"intctrl-base",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
{
IPA_PIPE_MEM_START_OFST,
(IPA_PIPE_MEM_START_OFST + IPA_PIPE_MEM_SIZE),
"ipa-pipe-mem",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
{
0,
0,
"gsi-irq",
IORESOURCE_IRQ,
0,
NULL,
NULL,
NULL
},
{
0,
0,
"ipa-irq",
IORESOURCE_IRQ,
0,
NULL,
NULL,
NULL
},
};
#define IPA_BASE_OFFSET_3_5_1 0x01e00000
#define IPA_BASE_SIZE_3_5_1 0x00034000
#define GSI_BASE_OFFSET_3_5_1 0x01e04000
#define GSI_BASE_SIZE_3_5_1 0x0002c000
struct resource ipa3_plat_drv_resource_3_5_1[] = {
/*
* PLEASE NOTE WELL: The following offset values below
* ("ipa-base", "gsi-base", and "intctrl-base") are used to
* calculate offsets relative to the PCI BAR0 address provided
* by the PCI probe. After their use to calculate the
* offsets, they are not used again, since PCI ultimately
* dictates where things live.
*/
{
IPA_BASE_OFFSET_3_5_1,
(IPA_BASE_OFFSET_3_5_1 + IPA_BASE_SIZE_3_5_1),
"ipa-base",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
{
GSI_BASE_OFFSET_3_5_1,
(GSI_BASE_OFFSET_3_5_1 + GSI_BASE_SIZE_3_5_1),
"gsi-base",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
/*
* The following entry is germane only to the emulator
* environment. It is needed to locate the emulator's PCI
* interrupt controller...
*/
{
INTCTRL_OFFSET,
(INTCTRL_OFFSET + INTCTRL_SIZE),
"intctrl-base",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
{
IPA_PIPE_MEM_START_OFST,
(IPA_PIPE_MEM_START_OFST + IPA_PIPE_MEM_SIZE),
"ipa-pipe-mem",
IORESOURCE_MEM,
0,
NULL,
NULL,
NULL
},
{
0,
0,
"gsi-irq",
IORESOURCE_IRQ,
0,
NULL,
NULL,
NULL
},
{
0,
0,
"ipa-irq",
IORESOURCE_IRQ,
0,
NULL,
NULL,
NULL
},
};
struct dtsi_replacement_resource_table
ipa3_plat_drv_resource_table[] = {
{ ipa3_plat_drv_resource_3_5_1,
ARRAY_SIZE(ipa3_plat_drv_resource_3_5_1) },
{ ipa3_plat_drv_resource_4_0,
ARRAY_SIZE(ipa3_plat_drv_resource_4_0) },
};
/*
* The following code uses the data above...
*/
static u32 emulator_type_to_index(void)
{
/*
* Use the input parameter to the IPA driver loadable module,
* which specifies the type of hardware the driver is running
* on.
*/
u32 index = DTSI_INDEX_4_0;
uint emulation_type = ipa3_get_emulation_type();
switch (emulation_type) {
case IPA_HW_v3_5_1:
index = DTSI_INDEX_3_5_1;
break;
case IPA_HW_v4_0:
index = DTSI_INDEX_4_0;
break;
default:
break;
}
IPADBG("emulation_type(%u) emulation_index(%u)\n",
emulation_type, index);
return index;
}
/* From include/linux/of.h */
/**
* emulator_of_property_read_bool - Find from a property
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
*
* Search for a property in a device node.
* Returns true if the property exists false otherwise.
*/
bool emulator_of_property_read_bool(
const struct device_node *np,
const char *propname)
{
u16 i;
u32 index;
struct dtsi_replacement_bool *ipa3_plat_drv_boolP;
/*
* Get the index for the type of hardware we're running on.
* This is used as a table index.
*/
index = emulator_type_to_index();
if (index >= ARRAY_SIZE(ipa3_plat_drv_bool_table)) {
IPADBG(
"Did not find ipa3_plat_drv_bool_table for index %u\n",
index);
return false;
}
ipa3_plat_drv_boolP =
ipa3_plat_drv_bool_table[index].p_table;
for (i = 0;
i < ipa3_plat_drv_bool_table[index].num_entries;
i++) {
if (strsame(ipa3_plat_drv_boolP[i].key, propname)) {
IPADBG(
"Found value %u for propname %s index %u\n",
ipa3_plat_drv_boolP[i].value,
propname,
index);
return ipa3_plat_drv_boolP[i].value;
}
}
IPADBG("Did not find match for propname %s index %u\n",
propname,
index);
return false;
}
/* From include/linux/of.h */
int emulator_of_property_read_u32(
const struct device_node *np,
const char *propname,
u32 *out_value)
{
u16 i;
u32 index;
struct dtsi_replacement_u32 *ipa3_plat_drv_u32P;
/*
* Get the index for the type of hardware we're running on.
* This is used as a table index.
*/
index = emulator_type_to_index();
if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_table)) {
IPADBG(
"Did not find ipa3_plat_drv_u32_table for index %u\n",
index);
return false;
}
ipa3_plat_drv_u32P =
ipa3_plat_drv_u32_table[index].p_table;
for (i = 0;
i < ipa3_plat_drv_u32_table[index].num_entries;
i++) {
if (strsame(ipa3_plat_drv_u32P[i].key, propname)) {
*out_value = ipa3_plat_drv_u32P[i].value;
IPADBG(
"Found value %u for propname %s index %u\n",
ipa3_plat_drv_u32P[i].value,
propname,
index);
return 0;
}
}
IPADBG("Did not find match for propname %s index %u\n",
propname,
index);
return -EINVAL;
}
/* From include/linux/of.h */
/**
* emulator_of_property_read_u32_array - Find and read an array of 32
* bit integers from a property.
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @out_values: pointer to return value, modified only if return value is 0.
* @sz: number of array elements to read
*
* Search for a property in a device node and read 32-bit value(s) from
* it. Returns 0 on success, -EINVAL if the property does not exist,
* -ENODATA if property does not have a value, and -EOVERFLOW if the
* property data isn't large enough.
*
* The out_values is modified only if a valid u32 value can be decoded.
*/
int emulator_of_property_read_u32_array(
const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz)
{
u16 i;
u32 index;
struct dtsi_replacement_u32_array *u32_arrayP;
/*
* Get the index for the type of hardware we're running on.
* This is used as a table index.
*/
index = emulator_type_to_index();
if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_array_table)) {
IPADBG(
"Did not find ipa3_plat_drv_u32_array_table for index %u\n",
index);
return false;
}
u32_arrayP =
ipa3_plat_drv_u32_array_table[index].p_table;
for (i = 0;
i < ipa3_plat_drv_u32_array_table[index].num_entries;
i++) {
if (strsame(
u32_arrayP[i].key, propname)) {
u32 num_elements =
u32_arrayP[i].num_elements;
u32 *p_element =
&u32_arrayP[i].p_value[0];
size_t j = 0;
if (num_elements > sz) {
IPAERR(
"Found array of %u values for propname %s; only room for %u elements in copy buffer\n",
num_elements,
propname,
(unsigned int) sz);
return -EOVERFLOW;
}
while (j++ < num_elements)
*out_values++ = *p_element++;
IPADBG(
"Found array of values starting with %u for propname %s index %u\n",
u32_arrayP[i].p_value[0],
propname,
index);
return 0;
}
}
IPADBG("Did not find match for propname %s index %u\n",
propname,
index);
return -EINVAL;
}
/* From drivers/base/platform.c */
/**
* emulator_platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
* @name: resource name
*/
struct resource *emulator_platform_get_resource_byname(
struct platform_device *dev,
unsigned int type,
const char *name)
{
u16 i;
u32 index;
struct resource *ipa3_plat_drv_resourceP;
/*
* Get the index for the type of hardware we're running on.
* This is used as a table index.
*/
index = emulator_type_to_index();
if (index >= ARRAY_SIZE(ipa3_plat_drv_resource_table)) {
IPADBG(
"Did not find ipa3_plat_drv_resource_table for index %u\n",
index);
return false;
}
ipa3_plat_drv_resourceP =
ipa3_plat_drv_resource_table[index].p_table;
for (i = 0;
i < ipa3_plat_drv_resource_table[index].num_entries;
i++) {
struct resource *r = &ipa3_plat_drv_resourceP[i];
if (type == resource_type(r) && strsame(r->name, name)) {
IPADBG(
"Found start 0x%x size %u for name %s index %u\n",
(unsigned int) (r->start),
(unsigned int) (resource_size(r)),
name,
index);
return r;
}
}
IPADBG("Did not find match for name %s index %u\n",
name,
index);
return NULL;
}
/* From drivers/of/base.c */
/**
* emulator_of_property_count_elems_of_size - Count the number of
* elements in a property
*
* @np: device node from which the property value is to
* be read. Not used.
* @propname: name of the property to be searched.
* @elem_size: size of the individual element
*
* Search for a property and count the number of elements of size
* elem_size in it. Returns number of elements on success, -EINVAL if
* the property does not exist or its length does not match a multiple
* of elem_size and -ENODATA if the property does not have a value.
*/
int emulator_of_property_count_elems_of_size(
const struct device_node *np,
const char *propname,
int elem_size)
{
u32 index;
/*
* Get the index for the type of hardware we're running on.
* This is used as a table index.
*/
index = emulator_type_to_index();
/*
* Use elem_size to determine which table to search for the
* specified property name
*/
if (elem_size == sizeof(u32)) {
u16 i;
struct dtsi_replacement_u32_array *u32_arrayP;
if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_array_table)) {
IPADBG(
"Did not find ipa3_plat_drv_u32_array_table for index %u\n",
index);
return false;
}
u32_arrayP =
ipa3_plat_drv_u32_array_table[index].p_table;
for (i = 0;
i < ipa3_plat_drv_u32_array_table[index].num_entries;
i++) {
if (strsame(u32_arrayP[i].key, propname)) {
if (u32_arrayP[i].p_value == NULL) {
IPADBG(
"Found no elements for propname %s index %u\n",
propname,
index);
return -ENODATA;
}
IPADBG(
"Found %u elements for propname %s index %u\n",
u32_arrayP[i].num_elements,
propname,
index);
return u32_arrayP[i].num_elements;
}
}
IPADBG(
"Found no match in table with elem_size %d for propname %s index %u\n",
elem_size,
propname,
index);
return -EINVAL;
}
IPAERR(
"Found no tables with element size %u to search for propname %s index %u\n",
elem_size,
propname,
index);
return -EINVAL;
}
int emulator_of_property_read_variable_u32_array(
const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz_min,
size_t sz_max)
{
return emulator_of_property_read_u32_array(
np, propname, out_values, sz_max);
}
resource_size_t emulator_resource_size(const struct resource *res)
{
return res->end - res->start;
}