mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
ARM: SoC driver updates for v4.15
This branch contains platform-related driver updates for ARM and ARM64,
these are the areas that bring the changes:
New drivers:
- Driver support for Renesas R-Car V3M (R8A77970)
- Power management support for Amlogic GX
- A new driver for the Tegra BPMP thermal sensor
- A new bus driver for Technologic Systems NBUS
Changes for subsystems that prefer to merge through arm-soc:
- The usual updates for reset controller drivers from Philipp Zabel,
with five added drivers for SoCs in the arc, meson, socfpa, uniphier
and mediatek families.
- Updates to the ARM SCPI and PSCI frameworks, from Sudeep Holla,
Heiner Kallweit and Lorenzo Pieralisi.
Changes specific to some ARM-based SoC
- The Freescale/NXP DPAA QBMan drivers from PowerPC can now work
on ARM as well.
- Several changes for power management on Broadcom SoCs
- Various improvements on Qualcomm, Broadcom, Amlogic, Atmel, Mediatek
- Minor Cleanups for Samsung, TI OMAP SoCs
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJaDggbAAoJEGCrR//JCVInIeQQAN1MDyO1UaWiFYnbkVOgzFcj
dqbFOc41DBE/90JoBWE8kR/rjyF83OqztiaYpx9viu2qMMBZVcOwxhCUthWK59c/
IujYdw4zGevLscF+jdrLbXgk97nfaWebsHyTAF307WAdZVJxiVGGzQEcgm71d6Zp
CXjLiUii4winHUMK9FLRY2st0HKAevXhuvZJVV432+sTg3p7fGVilYeGOL5G62WO
zQfCisqzC5q677kGGyUlPRGlHWMPkllsTTnfXcmV/FUiGyVa3lUWY5sEu+wCl96O
U1ffPENeNj/A/4fa1dbErtbiNnC2z/+jf+Dg7Cn8w/dPk4Suf0ppjP8RqIGyxmDl
Wm/UxbwDClxaeF4GSaYh2yKgGRJMH5N87bJnZRINE5ccGiol8Ww/34bFG0xNnfyh
jSAFAc318AFG62WD4lvqWc7LSpzOYxp/MNqIFXKN692St/MJLkx8/q0nTwY1qPY0
3SELz9II3hz+3MfDRqtRi7hZpkgHgQ+UG7S5+Xhmqrl309GOEldCjPVJhhXxWoxK
ZPtZOuyYvGhIC+YAnHaN6lUjADIdNJZHwbuXFImx85oKHVofoxHbcni5vk8Uu7z1
sQNYOtdDGaPG/2u9RJdJlPg/jIgLKxxt/Xm9TYVawpZ5hFANhBTtIq5ExCRAil68
j9sMOrpZ1DzCQyR7zN2v
=qDhq
-----END PGP SIGNATURE-----
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Arnd Bergmann:
"This branch contains platform-related driver updates for ARM and
ARM64, these are the areas that bring the changes:
New drivers:
- driver support for Renesas R-Car V3M (R8A77970)
- power management support for Amlogic GX
- a new driver for the Tegra BPMP thermal sensor
- a new bus driver for Technologic Systems NBUS
Changes for subsystems that prefer to merge through arm-soc:
- the usual updates for reset controller drivers from Philipp Zabel,
with five added drivers for SoCs in the arc, meson, socfpa,
uniphier and mediatek families
- updates to the ARM SCPI and PSCI frameworks, from Sudeep Holla,
Heiner Kallweit and Lorenzo Pieralisi
Changes specific to some ARM-based SoC
- the Freescale/NXP DPAA QBMan drivers from PowerPC can now work on
ARM as well
- several changes for power management on Broadcom SoCs
- various improvements on Qualcomm, Broadcom, Amlogic, Atmel,
Mediatek
- minor Cleanups for Samsung, TI OMAP SoCs"
[ NOTE! This doesn't work without the previous ARM SoC device-tree pull,
because the R8A77970 driver is missing a header file that came from
that pull.
The fact that this got merged afterwards only fixes it at this point,
and bisection of that driver will fail if/when you walk into the
history of that driver. - Linus ]
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (96 commits)
soc: amlogic: meson-gx-pwrc-vpu: fix power-off when powered by bootloader
bus: add driver for the Technologic Systems NBUS
memory: omap-gpmc: Remove deprecated gpmc_update_nand_reg()
soc: qcom: remove unused label
soc: amlogic: gx pm domain: add PM and OF dependencies
drivers/firmware: psci_checker: Add missing destroy_timer_on_stack()
dt-bindings: power: add amlogic meson power domain bindings
soc: amlogic: add Meson GX VPU Domains driver
soc: qcom: Remote filesystem memory driver
dt-binding: soc: qcom: Add binding for rmtfs memory
of: reserved_mem: Accessor for acquiring reserved_mem
of/platform: Generalize /reserved-memory handling
soc: mediatek: pwrap: fix fatal compiler error
soc: mediatek: pwrap: fix compiler errors
arm64: mediatek: cleanup message for platform selection
soc: Allow test-building of MediaTek drivers
soc: mediatek: place Kconfig for all SoC drivers under menu
soc: mediatek: pwrap: add support for MT7622 SoC
soc: mediatek: pwrap: add common way for setup CS timing extenstion
soc: mediatek: pwrap: add MediaTek MT6380 as one slave of pwrap
..
This commit is contained in:
commit
cf9b0772f2
110 changed files with 6933 additions and 996 deletions
|
|
@ -215,6 +215,17 @@ config QCOM_SCM_64
|
|||
def_bool y
|
||||
depends on QCOM_SCM && ARM64
|
||||
|
||||
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
|
||||
bool "Qualcomm download mode enabled by default"
|
||||
depends on QCOM_SCM
|
||||
help
|
||||
A device with "download mode" enabled will upon an unexpected
|
||||
warm-restart enter a special debug mode that allows the user to
|
||||
"download" memory content over USB for offline postmortem analysis.
|
||||
The feature can be enabled/disabled on the kernel command line.
|
||||
|
||||
Say Y here to enable "download mode" by default.
|
||||
|
||||
config TI_SCI_PROTOCOL
|
||||
tristate "TI System Control Interface (TISCI) Message Protocol"
|
||||
depends on TI_MESSAGE_MANAGER
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
|
|
@ -72,21 +73,13 @@
|
|||
|
||||
#define MAX_DVFS_DOMAINS 8
|
||||
#define MAX_DVFS_OPPS 16
|
||||
#define DVFS_LATENCY(hdr) (le32_to_cpu(hdr) >> 16)
|
||||
#define DVFS_OPP_COUNT(hdr) ((le32_to_cpu(hdr) >> 8) & 0xff)
|
||||
|
||||
#define PROTOCOL_REV_MINOR_BITS 16
|
||||
#define PROTOCOL_REV_MINOR_MASK ((1U << PROTOCOL_REV_MINOR_BITS) - 1)
|
||||
#define PROTOCOL_REV_MAJOR(x) ((x) >> PROTOCOL_REV_MINOR_BITS)
|
||||
#define PROTOCOL_REV_MINOR(x) ((x) & PROTOCOL_REV_MINOR_MASK)
|
||||
#define PROTO_REV_MAJOR_MASK GENMASK(31, 16)
|
||||
#define PROTO_REV_MINOR_MASK GENMASK(15, 0)
|
||||
|
||||
#define FW_REV_MAJOR_BITS 24
|
||||
#define FW_REV_MINOR_BITS 16
|
||||
#define FW_REV_PATCH_MASK ((1U << FW_REV_MINOR_BITS) - 1)
|
||||
#define FW_REV_MINOR_MASK ((1U << FW_REV_MAJOR_BITS) - 1)
|
||||
#define FW_REV_MAJOR(x) ((x) >> FW_REV_MAJOR_BITS)
|
||||
#define FW_REV_MINOR(x) (((x) & FW_REV_MINOR_MASK) >> FW_REV_MINOR_BITS)
|
||||
#define FW_REV_PATCH(x) ((x) & FW_REV_PATCH_MASK)
|
||||
#define FW_REV_MAJOR_MASK GENMASK(31, 24)
|
||||
#define FW_REV_MINOR_MASK GENMASK(23, 16)
|
||||
#define FW_REV_PATCH_MASK GENMASK(15, 0)
|
||||
|
||||
#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
|
||||
|
||||
|
|
@ -311,10 +304,6 @@ struct clk_get_info {
|
|||
u8 name[20];
|
||||
} __packed;
|
||||
|
||||
struct clk_get_value {
|
||||
__le32 rate;
|
||||
} __packed;
|
||||
|
||||
struct clk_set_value {
|
||||
__le16 id;
|
||||
__le16 reserved;
|
||||
|
|
@ -328,7 +317,9 @@ struct legacy_clk_set_value {
|
|||
} __packed;
|
||||
|
||||
struct dvfs_info {
|
||||
__le32 header;
|
||||
u8 domain;
|
||||
u8 opp_count;
|
||||
__le16 latency;
|
||||
struct {
|
||||
__le32 freq;
|
||||
__le32 m_volt;
|
||||
|
|
@ -351,11 +342,6 @@ struct _scpi_sensor_info {
|
|||
char name[20];
|
||||
};
|
||||
|
||||
struct sensor_value {
|
||||
__le32 lo_val;
|
||||
__le32 hi_val;
|
||||
} __packed;
|
||||
|
||||
struct dev_pstate_set {
|
||||
__le16 dev_id;
|
||||
u8 pstate;
|
||||
|
|
@ -419,19 +405,20 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
|
|||
unsigned int len;
|
||||
|
||||
if (scpi_info->is_legacy) {
|
||||
struct legacy_scpi_shared_mem *mem = ch->rx_payload;
|
||||
struct legacy_scpi_shared_mem __iomem *mem =
|
||||
ch->rx_payload;
|
||||
|
||||
/* RX Length is not replied by the legacy Firmware */
|
||||
len = match->rx_len;
|
||||
|
||||
match->status = le32_to_cpu(mem->status);
|
||||
match->status = ioread32(&mem->status);
|
||||
memcpy_fromio(match->rx_buf, mem->payload, len);
|
||||
} else {
|
||||
struct scpi_shared_mem *mem = ch->rx_payload;
|
||||
struct scpi_shared_mem __iomem *mem = ch->rx_payload;
|
||||
|
||||
len = min(match->rx_len, CMD_SIZE(cmd));
|
||||
|
||||
match->status = le32_to_cpu(mem->status);
|
||||
match->status = ioread32(&mem->status);
|
||||
memcpy_fromio(match->rx_buf, mem->payload, len);
|
||||
}
|
||||
|
||||
|
|
@ -445,11 +432,11 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
|
|||
static void scpi_handle_remote_msg(struct mbox_client *c, void *msg)
|
||||
{
|
||||
struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
|
||||
struct scpi_shared_mem *mem = ch->rx_payload;
|
||||
struct scpi_shared_mem __iomem *mem = ch->rx_payload;
|
||||
u32 cmd = 0;
|
||||
|
||||
if (!scpi_info->is_legacy)
|
||||
cmd = le32_to_cpu(mem->command);
|
||||
cmd = ioread32(&mem->command);
|
||||
|
||||
scpi_process_cmd(ch, cmd);
|
||||
}
|
||||
|
|
@ -459,7 +446,7 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
|
|||
unsigned long flags;
|
||||
struct scpi_xfer *t = msg;
|
||||
struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
|
||||
struct scpi_shared_mem *mem = (struct scpi_shared_mem *)ch->tx_payload;
|
||||
struct scpi_shared_mem __iomem *mem = ch->tx_payload;
|
||||
|
||||
if (t->tx_buf) {
|
||||
if (scpi_info->is_legacy)
|
||||
|
|
@ -478,7 +465,7 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
|
|||
}
|
||||
|
||||
if (!scpi_info->is_legacy)
|
||||
mem->command = cpu_to_le32(t->cmd);
|
||||
iowrite32(t->cmd, &mem->command);
|
||||
}
|
||||
|
||||
static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
|
||||
|
|
@ -583,13 +570,13 @@ scpi_clk_get_range(u16 clk_id, unsigned long *min, unsigned long *max)
|
|||
static unsigned long scpi_clk_get_val(u16 clk_id)
|
||||
{
|
||||
int ret;
|
||||
struct clk_get_value clk;
|
||||
__le32 rate;
|
||||
__le16 le_clk_id = cpu_to_le16(clk_id);
|
||||
|
||||
ret = scpi_send_message(CMD_GET_CLOCK_VALUE, &le_clk_id,
|
||||
sizeof(le_clk_id), &clk, sizeof(clk));
|
||||
sizeof(le_clk_id), &rate, sizeof(rate));
|
||||
|
||||
return ret ? ret : le32_to_cpu(clk.rate);
|
||||
return ret ? ret : le32_to_cpu(rate);
|
||||
}
|
||||
|
||||
static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
|
||||
|
|
@ -644,35 +631,35 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
|
|||
}
|
||||
|
||||
static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
|
||||
{
|
||||
if (domain >= MAX_DVFS_DOMAINS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return scpi_info->dvfs[domain] ?: ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static int scpi_dvfs_populate_info(struct device *dev, u8 domain)
|
||||
{
|
||||
struct scpi_dvfs_info *info;
|
||||
struct scpi_opp *opp;
|
||||
struct dvfs_info buf;
|
||||
int ret, i;
|
||||
|
||||
if (domain >= MAX_DVFS_DOMAINS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (scpi_info->dvfs[domain]) /* data already populated */
|
||||
return scpi_info->dvfs[domain];
|
||||
|
||||
ret = scpi_send_message(CMD_GET_DVFS_INFO, &domain, sizeof(domain),
|
||||
&buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
return ret;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
info = devm_kmalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return -ENOMEM;
|
||||
|
||||
info->count = DVFS_OPP_COUNT(buf.header);
|
||||
info->latency = DVFS_LATENCY(buf.header) * 1000; /* uS to nS */
|
||||
info->count = buf.opp_count;
|
||||
info->latency = le16_to_cpu(buf.latency) * 1000; /* uS to nS */
|
||||
|
||||
info->opps = kcalloc(info->count, sizeof(*opp), GFP_KERNEL);
|
||||
if (!info->opps) {
|
||||
kfree(info);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
info->opps = devm_kcalloc(dev, info->count, sizeof(*opp), GFP_KERNEL);
|
||||
if (!info->opps)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0, opp = info->opps; i < info->count; i++, opp++) {
|
||||
opp->freq = le32_to_cpu(buf.opps[i].freq);
|
||||
|
|
@ -682,7 +669,15 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
|
|||
sort(info->opps, info->count, sizeof(*opp), opp_cmp_func, NULL);
|
||||
|
||||
scpi_info->dvfs[domain] = info;
|
||||
return info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scpi_dvfs_populate(struct device *dev)
|
||||
{
|
||||
int domain;
|
||||
|
||||
for (domain = 0; domain < MAX_DVFS_DOMAINS; domain++)
|
||||
scpi_dvfs_populate_info(dev, domain);
|
||||
}
|
||||
|
||||
static int scpi_dev_domain_id(struct device *dev)
|
||||
|
|
@ -713,9 +708,6 @@ static int scpi_dvfs_get_transition_latency(struct device *dev)
|
|||
if (IS_ERR(info))
|
||||
return PTR_ERR(info);
|
||||
|
||||
if (!info->latency)
|
||||
return 0;
|
||||
|
||||
return info->latency;
|
||||
}
|
||||
|
||||
|
|
@ -776,20 +768,19 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
|
|||
static int scpi_sensor_get_value(u16 sensor, u64 *val)
|
||||
{
|
||||
__le16 id = cpu_to_le16(sensor);
|
||||
struct sensor_value buf;
|
||||
__le64 value;
|
||||
int ret;
|
||||
|
||||
ret = scpi_send_message(CMD_SENSOR_VALUE, &id, sizeof(id),
|
||||
&buf, sizeof(buf));
|
||||
&value, sizeof(value));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (scpi_info->is_legacy)
|
||||
/* only 32-bits supported, hi_val can be junk */
|
||||
*val = le32_to_cpu(buf.lo_val);
|
||||
/* only 32-bits supported, upper 32 bits can be junk */
|
||||
*val = le32_to_cpup((__le32 *)&value);
|
||||
else
|
||||
*val = (u64)le32_to_cpu(buf.hi_val) << 32 |
|
||||
le32_to_cpu(buf.lo_val);
|
||||
*val = le64_to_cpu(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -862,23 +853,19 @@ static int scpi_init_versions(struct scpi_drvinfo *info)
|
|||
static ssize_t protocol_version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(scpi_info->protocol_version),
|
||||
PROTOCOL_REV_MINOR(scpi_info->protocol_version));
|
||||
return sprintf(buf, "%lu.%lu\n",
|
||||
FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
|
||||
FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version));
|
||||
}
|
||||
static DEVICE_ATTR_RO(protocol_version);
|
||||
|
||||
static ssize_t firmware_version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d.%d.%d\n",
|
||||
FW_REV_MAJOR(scpi_info->firmware_version),
|
||||
FW_REV_MINOR(scpi_info->firmware_version),
|
||||
FW_REV_PATCH(scpi_info->firmware_version));
|
||||
return sprintf(buf, "%lu.%lu.%lu\n",
|
||||
FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
|
||||
FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
|
||||
FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
|
||||
}
|
||||
static DEVICE_ATTR_RO(firmware_version);
|
||||
|
||||
|
|
@ -889,39 +876,13 @@ static struct attribute *versions_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(versions);
|
||||
|
||||
static void
|
||||
scpi_free_channels(struct device *dev, struct scpi_chan *pchan, int count)
|
||||
static void scpi_free_channels(void *data)
|
||||
{
|
||||
struct scpi_drvinfo *info = data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count && pchan->chan; i++, pchan++) {
|
||||
mbox_free_channel(pchan->chan);
|
||||
devm_kfree(dev, pchan->xfers);
|
||||
devm_iounmap(dev, pchan->rx_payload);
|
||||
}
|
||||
}
|
||||
|
||||
static int scpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct scpi_drvinfo *info = platform_get_drvdata(pdev);
|
||||
|
||||
scpi_info = NULL; /* stop exporting SCPI ops through get_scpi_ops */
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
sysfs_remove_groups(&dev->kobj, versions_groups);
|
||||
scpi_free_channels(dev, info->channels, info->num_chans);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
for (i = 0; i < MAX_DVFS_DOMAINS && info->dvfs[i]; i++) {
|
||||
kfree(info->dvfs[i]->opps);
|
||||
kfree(info->dvfs[i]);
|
||||
}
|
||||
devm_kfree(dev, info->channels);
|
||||
devm_kfree(dev, info);
|
||||
|
||||
return 0;
|
||||
for (i = 0; i < info->num_chans; i++)
|
||||
mbox_free_channel(info->channels[i].chan);
|
||||
}
|
||||
|
||||
#define MAX_SCPI_XFERS 10
|
||||
|
|
@ -952,7 +913,6 @@ static int scpi_probe(struct platform_device *pdev)
|
|||
{
|
||||
int count, idx, ret;
|
||||
struct resource res;
|
||||
struct scpi_chan *scpi_chan;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
|
|
@ -969,13 +929,19 @@ static int scpi_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
scpi_chan = devm_kcalloc(dev, count, sizeof(*scpi_chan), GFP_KERNEL);
|
||||
if (!scpi_chan)
|
||||
scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
|
||||
GFP_KERNEL);
|
||||
if (!scpi_info->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (idx = 0; idx < count; idx++) {
|
||||
ret = devm_add_action(dev, scpi_free_channels, scpi_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (; scpi_info->num_chans < count; scpi_info->num_chans++) {
|
||||
resource_size_t size;
|
||||
struct scpi_chan *pchan = scpi_chan + idx;
|
||||
int idx = scpi_info->num_chans;
|
||||
struct scpi_chan *pchan = scpi_info->channels + idx;
|
||||
struct mbox_client *cl = &pchan->cl;
|
||||
struct device_node *shmem = of_parse_phandle(np, "shmem", idx);
|
||||
|
||||
|
|
@ -983,15 +949,14 @@ static int scpi_probe(struct platform_device *pdev)
|
|||
of_node_put(shmem);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get SCPI payload mem resource\n");
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = resource_size(&res);
|
||||
pchan->rx_payload = devm_ioremap(dev, res.start, size);
|
||||
if (!pchan->rx_payload) {
|
||||
dev_err(dev, "failed to ioremap SCPI payload\n");
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto err;
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
pchan->tx_payload = pchan->rx_payload + (size >> 1);
|
||||
|
||||
|
|
@ -1017,17 +982,11 @@ static int scpi_probe(struct platform_device *pdev)
|
|||
dev_err(dev, "failed to get channel%d err %d\n",
|
||||
idx, ret);
|
||||
}
|
||||
err:
|
||||
scpi_free_channels(dev, scpi_chan, idx);
|
||||
scpi_info = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
scpi_info->channels = scpi_chan;
|
||||
scpi_info->num_chans = count;
|
||||
scpi_info->commands = scpi_std_commands;
|
||||
|
||||
platform_set_drvdata(pdev, scpi_info);
|
||||
scpi_info->scpi_ops = &scpi_ops;
|
||||
|
||||
if (scpi_info->is_legacy) {
|
||||
/* Replace with legacy variants */
|
||||
|
|
@ -1043,23 +1002,23 @@ err:
|
|||
ret = scpi_init_versions(scpi_info);
|
||||
if (ret) {
|
||||
dev_err(dev, "incorrect or no SCP firmware found\n");
|
||||
scpi_remove(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_dev_info(dev, "SCP Protocol %d.%d Firmware %d.%d.%d version\n",
|
||||
PROTOCOL_REV_MAJOR(scpi_info->protocol_version),
|
||||
PROTOCOL_REV_MINOR(scpi_info->protocol_version),
|
||||
FW_REV_MAJOR(scpi_info->firmware_version),
|
||||
FW_REV_MINOR(scpi_info->firmware_version),
|
||||
FW_REV_PATCH(scpi_info->firmware_version));
|
||||
scpi_info->scpi_ops = &scpi_ops;
|
||||
scpi_dvfs_populate(dev);
|
||||
|
||||
ret = sysfs_create_groups(&dev->kobj, versions_groups);
|
||||
_dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n",
|
||||
FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
|
||||
FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version),
|
||||
FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
|
||||
FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
|
||||
FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
|
||||
|
||||
ret = devm_device_add_groups(dev, versions_groups);
|
||||
if (ret)
|
||||
dev_err(dev, "unable to create sysfs version group\n");
|
||||
|
||||
return of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
return devm_of_platform_populate(dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id scpi_of_match[] = {
|
||||
|
|
@ -1076,7 +1035,6 @@ static struct platform_driver scpi_driver = {
|
|||
.of_match_table = scpi_of_match,
|
||||
},
|
||||
.probe = scpi_probe,
|
||||
.remove = scpi_remove,
|
||||
};
|
||||
module_platform_driver(scpi_driver);
|
||||
|
||||
|
|
|
|||
|
|
@ -340,6 +340,7 @@ static int suspend_test_thread(void *arg)
|
|||
* later.
|
||||
*/
|
||||
del_timer(&wakeup_timer);
|
||||
destroy_timer_on_stack(&wakeup_timer);
|
||||
|
||||
if (atomic_dec_return_relaxed(&nb_active_threads) == 0)
|
||||
complete(&suspend_threads_done);
|
||||
|
|
|
|||
|
|
@ -561,6 +561,12 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
|
|||
return ret ? : le32_to_cpu(out);
|
||||
}
|
||||
|
||||
int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
|
||||
{
|
||||
return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
|
||||
enable ? QCOM_SCM_SET_DLOAD_MODE : 0, 0);
|
||||
}
|
||||
|
||||
int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
|
||||
{
|
||||
struct {
|
||||
|
|
@ -596,3 +602,21 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
|
|||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
|
||||
unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_call_atomic1(QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ, addr);
|
||||
if (ret >= 0)
|
||||
*val = ret;
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
|
||||
{
|
||||
return qcom_scm_call_atomic2(QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE,
|
||||
addr, val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -439,3 +439,47 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
|
||||
{
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = QCOM_SCM_SET_DLOAD_MODE;
|
||||
desc.args[1] = enable ? QCOM_SCM_SET_DLOAD_MODE : 0;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2);
|
||||
|
||||
return qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
|
||||
&desc, &res);
|
||||
}
|
||||
|
||||
int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
int ret;
|
||||
|
||||
desc.args[0] = addr;
|
||||
desc.arginfo = QCOM_SCM_ARGS(1);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ,
|
||||
&desc, &res);
|
||||
if (ret >= 0)
|
||||
*val = res.a1;
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
|
||||
{
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = addr;
|
||||
desc.args[1] = val;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2);
|
||||
|
||||
return qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE,
|
||||
&desc, &res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,20 @@
|
|||
#include <linux/cpumask.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
|
||||
module_param(download_mode, bool, 0);
|
||||
|
||||
#define SCM_HAS_CORE_CLK BIT(0)
|
||||
#define SCM_HAS_IFACE_CLK BIT(1)
|
||||
#define SCM_HAS_BUS_CLK BIT(2)
|
||||
|
|
@ -38,6 +43,8 @@ struct qcom_scm {
|
|||
struct clk *iface_clk;
|
||||
struct clk *bus_clk;
|
||||
struct reset_controller_dev reset;
|
||||
|
||||
u64 dload_mode_addr;
|
||||
};
|
||||
|
||||
static struct qcom_scm *__scm;
|
||||
|
|
@ -333,6 +340,66 @@ int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
|
|||
}
|
||||
EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init);
|
||||
|
||||
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val)
|
||||
{
|
||||
return __qcom_scm_io_readl(__scm->dev, addr, val);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_io_readl);
|
||||
|
||||
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
|
||||
{
|
||||
return __qcom_scm_io_writel(__scm->dev, addr, val);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_io_writel);
|
||||
|
||||
static void qcom_scm_set_download_mode(bool enable)
|
||||
{
|
||||
bool avail;
|
||||
int ret = 0;
|
||||
|
||||
avail = __qcom_scm_is_call_available(__scm->dev,
|
||||
QCOM_SCM_SVC_BOOT,
|
||||
QCOM_SCM_SET_DLOAD_MODE);
|
||||
if (avail) {
|
||||
ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
|
||||
} else if (__scm->dload_mode_addr) {
|
||||
ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr,
|
||||
enable ? QCOM_SCM_SET_DLOAD_MODE : 0);
|
||||
} else {
|
||||
dev_err(__scm->dev,
|
||||
"No available mechanism for setting download mode\n");
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
|
||||
}
|
||||
|
||||
static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
|
||||
{
|
||||
struct device_node *tcsr;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct resource res;
|
||||
u32 offset;
|
||||
int ret;
|
||||
|
||||
tcsr = of_parse_phandle(np, "qcom,dload-mode", 0);
|
||||
if (!tcsr)
|
||||
return 0;
|
||||
|
||||
ret = of_address_to_resource(tcsr, 0, &res);
|
||||
of_node_put(tcsr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*addr = res.start + offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_is_available() - Checks if SCM is available
|
||||
*/
|
||||
|
|
@ -358,6 +425,10 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
if (!scm)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
clks = (unsigned long)of_device_get_match_data(&pdev->dev);
|
||||
if (clks & SCM_HAS_CORE_CLK) {
|
||||
scm->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||
|
|
@ -406,9 +477,24 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
|
||||
__qcom_scm_init();
|
||||
|
||||
/*
|
||||
* If requested enable "download mode", from this point on warmboot
|
||||
* will cause the the boot stages to enter download mode, unless
|
||||
* disabled below by a clean shutdown/reboot.
|
||||
*/
|
||||
if (download_mode)
|
||||
qcom_scm_set_download_mode(true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_scm_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
/* Clean shutdown, disable download mode to allow normal restart */
|
||||
if (download_mode)
|
||||
qcom_scm_set_download_mode(false);
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_scm_dt_match[] = {
|
||||
{ .compatible = "qcom,scm-apq8064",
|
||||
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
|
||||
|
|
@ -436,6 +522,7 @@ static struct platform_driver qcom_scm_driver = {
|
|||
.of_match_table = qcom_scm_dt_match,
|
||||
},
|
||||
.probe = qcom_scm_probe,
|
||||
.shutdown = qcom_scm_shutdown,
|
||||
};
|
||||
|
||||
static int __init qcom_scm_init(void)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@
|
|||
|
||||
#define QCOM_SCM_SVC_BOOT 0x1
|
||||
#define QCOM_SCM_BOOT_ADDR 0x1
|
||||
#define QCOM_SCM_SET_DLOAD_MODE 0x10
|
||||
#define QCOM_SCM_BOOT_ADDR_MC 0x11
|
||||
#define QCOM_SCM_SET_REMOTE_STATE 0xa
|
||||
extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id);
|
||||
extern int __qcom_scm_set_dload_mode(struct device *dev, bool enable);
|
||||
|
||||
#define QCOM_SCM_FLAG_HLOS 0x01
|
||||
#define QCOM_SCM_FLAG_COLDBOOT_MC 0x02
|
||||
|
|
@ -30,6 +32,12 @@ extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus);
|
|||
#define QCOM_SCM_CMD_CORE_HOTPLUGGED 0x10
|
||||
extern void __qcom_scm_cpu_power_down(u32 flags);
|
||||
|
||||
#define QCOM_SCM_SVC_IO 0x5
|
||||
#define QCOM_SCM_IO_READ 0x1
|
||||
#define QCOM_SCM_IO_WRITE 0x2
|
||||
extern int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, unsigned int *val);
|
||||
extern int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val);
|
||||
|
||||
#define QCOM_SCM_SVC_INFO 0x6
|
||||
#define QCOM_IS_CALL_AVAIL_CMD 0x1
|
||||
extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
obj-$(CONFIG_TEGRA_BPMP) += bpmp.o
|
||||
tegra-bpmp-y = bpmp.o
|
||||
tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o
|
||||
obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o
|
||||
obj-$(CONFIG_TEGRA_IVC) += ivc.o
|
||||
|
|
|
|||
444
drivers/firmware/tegra/bpmp-debugfs.c
Normal file
444
drivers/firmware/tegra/bpmp-debugfs.c
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/bpmp-abi.h>
|
||||
|
||||
struct seqbuf {
|
||||
char *buf;
|
||||
size_t pos;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static void seqbuf_init(struct seqbuf *seqbuf, void *buf, size_t size)
|
||||
{
|
||||
seqbuf->buf = buf;
|
||||
seqbuf->size = size;
|
||||
seqbuf->pos = 0;
|
||||
}
|
||||
|
||||
static size_t seqbuf_avail(struct seqbuf *seqbuf)
|
||||
{
|
||||
return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0;
|
||||
}
|
||||
|
||||
static size_t seqbuf_status(struct seqbuf *seqbuf)
|
||||
{
|
||||
return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW;
|
||||
}
|
||||
|
||||
static int seqbuf_eof(struct seqbuf *seqbuf)
|
||||
{
|
||||
return seqbuf->pos >= seqbuf->size;
|
||||
}
|
||||
|
||||
static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte)
|
||||
{
|
||||
nbyte = min(nbyte, seqbuf_avail(seqbuf));
|
||||
memcpy(buf, seqbuf->buf + seqbuf->pos, nbyte);
|
||||
seqbuf->pos += nbyte;
|
||||
return seqbuf_status(seqbuf);
|
||||
}
|
||||
|
||||
static int seqbuf_read_u32(struct seqbuf *seqbuf, uint32_t *v)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = seqbuf_read(seqbuf, v, 4);
|
||||
*v = le32_to_cpu(*v);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str)
|
||||
{
|
||||
*str = seqbuf->buf + seqbuf->pos;
|
||||
seqbuf->pos += strnlen(*str, seqbuf_avail(seqbuf));
|
||||
seqbuf->pos++;
|
||||
return seqbuf_status(seqbuf);
|
||||
}
|
||||
|
||||
static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset)
|
||||
{
|
||||
seqbuf->pos += offset;
|
||||
}
|
||||
|
||||
/* map filename in Linux debugfs to corresponding entry in BPMP */
|
||||
static const char *get_filename(struct tegra_bpmp *bpmp,
|
||||
const struct file *file, char *buf, int size)
|
||||
{
|
||||
char root_path_buf[512];
|
||||
const char *root_path;
|
||||
const char *filename;
|
||||
size_t root_len;
|
||||
|
||||
root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
|
||||
sizeof(root_path_buf));
|
||||
if (IS_ERR(root_path))
|
||||
return NULL;
|
||||
|
||||
root_len = strlen(root_path);
|
||||
|
||||
filename = dentry_path(file->f_path.dentry, buf, size);
|
||||
if (IS_ERR(filename))
|
||||
return NULL;
|
||||
|
||||
if (strlen(filename) < root_len ||
|
||||
strncmp(filename, root_path, root_len))
|
||||
return NULL;
|
||||
|
||||
filename += root_len;
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
static int mrq_debugfs_read(struct tegra_bpmp *bpmp,
|
||||
dma_addr_t name, size_t sz_name,
|
||||
dma_addr_t data, size_t sz_data,
|
||||
size_t *nbytes)
|
||||
{
|
||||
struct mrq_debugfs_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUGFS_READ),
|
||||
.fop = {
|
||||
.fnameaddr = cpu_to_le32((uint32_t)name),
|
||||
.fnamelen = cpu_to_le32((uint32_t)sz_name),
|
||||
.dataaddr = cpu_to_le32((uint32_t)data),
|
||||
.datalen = cpu_to_le32((uint32_t)sz_data),
|
||||
},
|
||||
};
|
||||
struct mrq_debugfs_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUGFS,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
int err;
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*nbytes = (size_t)resp.fop.nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrq_debugfs_write(struct tegra_bpmp *bpmp,
|
||||
dma_addr_t name, size_t sz_name,
|
||||
dma_addr_t data, size_t sz_data)
|
||||
{
|
||||
const struct mrq_debugfs_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUGFS_WRITE),
|
||||
.fop = {
|
||||
.fnameaddr = cpu_to_le32((uint32_t)name),
|
||||
.fnamelen = cpu_to_le32((uint32_t)sz_name),
|
||||
.dataaddr = cpu_to_le32((uint32_t)data),
|
||||
.datalen = cpu_to_le32((uint32_t)sz_data),
|
||||
},
|
||||
};
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUGFS,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
};
|
||||
|
||||
return tegra_bpmp_transfer(bpmp, &msg);
|
||||
}
|
||||
|
||||
static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr,
|
||||
size_t size, size_t *nbytes)
|
||||
{
|
||||
const struct mrq_debugfs_request req = {
|
||||
.cmd = cpu_to_le32(CMD_DEBUGFS_DUMPDIR),
|
||||
.dumpdir = {
|
||||
.dataaddr = cpu_to_le32((uint32_t)addr),
|
||||
.datalen = cpu_to_le32((uint32_t)size),
|
||||
},
|
||||
};
|
||||
struct mrq_debugfs_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_DEBUGFS,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
int err;
|
||||
|
||||
err = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*nbytes = (size_t)resp.dumpdir.nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debugfs_show(struct seq_file *m, void *p)
|
||||
{
|
||||
struct file *file = m->private;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct tegra_bpmp *bpmp = inode->i_private;
|
||||
const size_t datasize = m->size;
|
||||
const size_t namesize = SZ_256;
|
||||
void *datavirt, *namevirt;
|
||||
dma_addr_t dataphys, namephys;
|
||||
char buf[256];
|
||||
const char *filename;
|
||||
size_t len, nbytes;
|
||||
int ret;
|
||||
|
||||
filename = get_filename(bpmp, file, buf, sizeof(buf));
|
||||
if (!filename)
|
||||
return -ENOENT;
|
||||
|
||||
namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!namevirt)
|
||||
return -ENOMEM;
|
||||
|
||||
datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!datavirt) {
|
||||
ret = -ENOMEM;
|
||||
goto free_namebuf;
|
||||
}
|
||||
|
||||
len = strlen(filename);
|
||||
strncpy(namevirt, filename, namesize);
|
||||
|
||||
ret = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,
|
||||
&nbytes);
|
||||
|
||||
if (!ret)
|
||||
seq_write(m, datavirt, nbytes);
|
||||
|
||||
dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
|
||||
free_namebuf:
|
||||
dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open_size(file, debugfs_show, file, SZ_128K);
|
||||
}
|
||||
|
||||
static ssize_t debugfs_store(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct tegra_bpmp *bpmp = inode->i_private;
|
||||
const size_t datasize = count;
|
||||
const size_t namesize = SZ_256;
|
||||
void *datavirt, *namevirt;
|
||||
dma_addr_t dataphys, namephys;
|
||||
char fnamebuf[256];
|
||||
const char *filename;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
|
||||
if (!filename)
|
||||
return -ENOENT;
|
||||
|
||||
namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!namevirt)
|
||||
return -ENOMEM;
|
||||
|
||||
datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!datavirt) {
|
||||
ret = -ENOMEM;
|
||||
goto free_namebuf;
|
||||
}
|
||||
|
||||
len = strlen(filename);
|
||||
strncpy(namevirt, filename, namesize);
|
||||
|
||||
if (copy_from_user(datavirt, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
goto free_databuf;
|
||||
}
|
||||
|
||||
ret = mrq_debugfs_write(bpmp, namephys, len, dataphys,
|
||||
count);
|
||||
|
||||
free_databuf:
|
||||
dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
|
||||
free_namebuf:
|
||||
dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
|
||||
|
||||
return ret ?: count;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_fops = {
|
||||
.open = debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.write = debugfs_store,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
|
||||
struct dentry *parent, uint32_t depth)
|
||||
{
|
||||
int err;
|
||||
uint32_t d, t;
|
||||
const char *name;
|
||||
struct dentry *dentry;
|
||||
|
||||
while (!seqbuf_eof(seqbuf)) {
|
||||
err = seqbuf_read_u32(seqbuf, &d);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (d < depth) {
|
||||
seqbuf_seek(seqbuf, -4);
|
||||
/* go up a level */
|
||||
return 0;
|
||||
} else if (d != depth) {
|
||||
/* malformed data received from BPMP */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = seqbuf_read_u32(seqbuf, &t);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = seqbuf_read_str(seqbuf, &name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (t & DEBUGFS_S_ISDIR) {
|
||||
dentry = debugfs_create_dir(name, parent);
|
||||
if (!dentry)
|
||||
return -ENOMEM;
|
||||
err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
umode_t mode;
|
||||
|
||||
mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0;
|
||||
mode |= t & DEBUGFS_S_IWUSR ? S_IWUSR : 0;
|
||||
dentry = debugfs_create_file(name, mode,
|
||||
parent, bpmp,
|
||||
&debugfs_fops);
|
||||
if (!dentry)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf,
|
||||
size_t bufsize, struct dentry *root)
|
||||
{
|
||||
struct seqbuf seqbuf;
|
||||
int err;
|
||||
|
||||
bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
|
||||
if (!bpmp->debugfs_mirror)
|
||||
return -ENOMEM;
|
||||
|
||||
seqbuf_init(&seqbuf, buf, bufsize);
|
||||
err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);
|
||||
if (err < 0) {
|
||||
debugfs_remove_recursive(bpmp->debugfs_mirror);
|
||||
bpmp->debugfs_mirror = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq)
|
||||
{
|
||||
struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) };
|
||||
struct mrq_query_abi_response resp;
|
||||
struct tegra_bpmp_message msg = {
|
||||
.mrq = MRQ_QUERY_ABI,
|
||||
.tx = {
|
||||
.data = &req,
|
||||
.size = sizeof(req),
|
||||
},
|
||||
.rx = {
|
||||
.data = &resp,
|
||||
.size = sizeof(resp),
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = tegra_bpmp_transfer(bpmp, &msg);
|
||||
if (ret < 0) {
|
||||
/* something went wrong; assume not supported */
|
||||
dev_warn(bpmp->dev, "tegra_bpmp_transfer failed (%d)\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return resp.status ? 0 : 1;
|
||||
}
|
||||
|
||||
int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
|
||||
{
|
||||
dma_addr_t phys;
|
||||
void *virt;
|
||||
const size_t sz = SZ_256K;
|
||||
size_t nbytes;
|
||||
int ret;
|
||||
struct dentry *root;
|
||||
|
||||
if (!mrq_is_supported(bpmp, MRQ_DEBUGFS))
|
||||
return 0;
|
||||
|
||||
root = debugfs_create_dir("bpmp", NULL);
|
||||
if (!root)
|
||||
return -ENOMEM;
|
||||
|
||||
virt = dma_alloc_coherent(bpmp->dev, sz, &phys,
|
||||
GFP_KERNEL | GFP_DMA32);
|
||||
if (!virt) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);
|
||||
if (ret < 0)
|
||||
goto free;
|
||||
|
||||
ret = create_debugfs_mirror(bpmp, virt, nbytes, root);
|
||||
free:
|
||||
dma_free_coherent(bpmp->dev, sz, virt, phys);
|
||||
out:
|
||||
if (ret < 0)
|
||||
debugfs_remove(root);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -194,16 +194,24 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel)
|
|||
}
|
||||
|
||||
static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
|
||||
void *data, size_t size)
|
||||
void *data, size_t size, int *ret)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (data && size > 0)
|
||||
memcpy(data, channel->ib->data, size);
|
||||
|
||||
return tegra_ivc_read_advance(channel->ivc);
|
||||
err = tegra_ivc_read_advance(channel->ivc);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*ret = channel->ib->code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
|
||||
void *data, size_t size)
|
||||
void *data, size_t size, int *ret)
|
||||
{
|
||||
struct tegra_bpmp *bpmp = channel->bpmp;
|
||||
unsigned long flags;
|
||||
|
|
@ -217,7 +225,7 @@ static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&bpmp->lock, flags);
|
||||
err = __tegra_bpmp_channel_read(channel, data, size);
|
||||
err = __tegra_bpmp_channel_read(channel, data, size, ret);
|
||||
clear_bit(index, bpmp->threaded.allocated);
|
||||
spin_unlock_irqrestore(&bpmp->lock, flags);
|
||||
|
||||
|
|
@ -337,7 +345,8 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
|
||||
return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size,
|
||||
&msg->rx.ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
|
||||
|
||||
|
|
@ -371,7 +380,8 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
|
|||
if (err == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
|
||||
return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size,
|
||||
&msg->rx.ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
|
||||
|
||||
|
|
@ -387,8 +397,8 @@ static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
|
||||
int code, const void *data, size_t size)
|
||||
void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
unsigned long flags = channel->ib->flags;
|
||||
struct tegra_bpmp *bpmp = channel->bpmp;
|
||||
|
|
@ -426,6 +436,7 @@ static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
|
|||
mbox_client_txdone(bpmp->mbox.channel, 0);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return);
|
||||
|
||||
static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp,
|
||||
unsigned int mrq,
|
||||
|
|
@ -824,6 +835,10 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
|
|||
if (err < 0)
|
||||
goto free_mrq;
|
||||
|
||||
err = tegra_bpmp_init_debugfs(bpmp);
|
||||
if (err < 0)
|
||||
dev_err(&pdev->dev, "debugfs initialization failed: %d\n", err);
|
||||
|
||||
return 0;
|
||||
|
||||
free_mrq:
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info,
|
|||
/* And we wait for the response. */
|
||||
timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
|
||||
if (!wait_for_completion_timeout(&xfer->done, timeout)) {
|
||||
dev_err(dev, "Mbox timedout in resp(caller: %pF)\n",
|
||||
dev_err(dev, "Mbox timedout in resp(caller: %pS)\n",
|
||||
(void *)_RET_IP_);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue