Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1ad0348 authored by David Collins's avatar David Collins
Browse files

spmi: spmi-pmic-arb: add support to map SPMI addresses to physical addr



Add an exported function which can map an SPMI address to the
physical address range of the registers used to write to that
SPMI address.

The feature can be used by consumer drivers that need to lock
down access to certain PMIC peripherals temporarily for certain
secure use cases.

Change-Id: Ib477778346df29ee90661d2e07d2d3a5d160ae87
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent 108427ae
Loading
Loading
Loading
Loading
+118 −1
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */


#include <linux/bitmap.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/delay.h>
@@ -12,9 +12,11 @@
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/spmi.h>
#include <linux/spmi.h>
#include <linux/soc/qcom/spmi-pmic-arb.h>


/* PMIC Arbiter configuration registers */
/* PMIC Arbiter configuration registers */
#define PMIC_ARB_VERSION		0x0000
#define PMIC_ARB_VERSION		0x0000
@@ -123,6 +125,8 @@ struct apid_data {
 *
 *
 * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
 * @rd_base:		on v1 "core", on v2 "observer" register base off DT.
 * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
 * @wr_base:		on v1 "core", on v2 "chnls"    register base off DT.
 * @wr_base_phys:	Base physical address of the register range used for
 *			SPMI write commands.
 * @intr:		address of the SPMI interrupt control registers.
 * @intr:		address of the SPMI interrupt control registers.
 * @cnfg:		address of the PMIC Arbiter configuration registers.
 * @cnfg:		address of the PMIC Arbiter configuration registers.
 * @lock:		lock to synchronize accesses.
 * @lock:		lock to synchronize accesses.
@@ -140,6 +144,7 @@ struct apid_data {
struct spmi_pmic_arb {
struct spmi_pmic_arb {
	void __iomem		*rd_base;
	void __iomem		*rd_base;
	void __iomem		*wr_base;
	void __iomem		*wr_base;
	phys_addr_t		wr_base_phys;
	void __iomem		*intr;
	void __iomem		*intr;
	void __iomem		*cnfg;
	void __iomem		*cnfg;
	void __iomem		*core;
	void __iomem		*core;
@@ -179,6 +184,9 @@ struct spmi_pmic_arb {
 * @irq_clear:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
 * @irq_clear:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
 *			on v2 address of SPMI_PIC_IRQ_CLEARn.
 *			on v2 address of SPMI_PIC_IRQ_CLEARn.
 * @apid_map_offset:	offset of PMIC_ARB_REG_CHNLn
 * @apid_map_offset:	offset of PMIC_ARB_REG_CHNLn
 * @wr_addr_map:	maps from an SPMI address to the physical address
 *			range of the registers used to perform an SPMI write
 *			command to the SPMI address.
 */
 */
struct pmic_arb_ver_ops {
struct pmic_arb_ver_ops {
	const char *ver_str;
	const char *ver_str;
@@ -195,6 +203,8 @@ struct pmic_arb_ver_ops {
	void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n);
	void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n);
	void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n);
	void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n);
	u32 (*apid_map_offset)(u16 n);
	u32 (*apid_map_offset)(u16 n);
	int (*wr_addr_map)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
			   struct resource *res_out);
};
};


static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb,
@@ -861,6 +871,21 @@ static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
	return 0x800 + 0x80 * pmic_arb->channel;
	return 0x800 + 0x80 * pmic_arb->channel;
}
}


static int pmic_arb_wr_addr_map_v1(struct spmi_pmic_arb *pmic_arb, u8 sid,
				   u16 addr, struct resource *res_out)
{
	int rc;

	rc = pmic_arb_offset_v1(pmic_arb, sid, addr, PMIC_ARB_CHANNEL_RW);
	if (rc < 0)
		return rc;

	res_out->start = pmic_arb->wr_base_phys + rc;
	res_out->end = res_out->start + 0x80 - 1;

	return 0;
}

static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid)
{
{
	struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
	struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid];
@@ -1000,6 +1025,21 @@ static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
	return 0x1000 * pmic_arb->ee + 0x8000 * apid;
	return 0x1000 * pmic_arb->ee + 0x8000 * apid;
}
}


static int pmic_arb_wr_addr_map_v2(struct spmi_pmic_arb *pmic_arb, u8 sid,
				   u16 addr, struct resource *res_out)
{
	int rc;

	rc = pmic_arb_offset_v2(pmic_arb, sid, addr, PMIC_ARB_CHANNEL_RW);
	if (rc < 0)
		return rc;

	res_out->start = pmic_arb->wr_base_phys + rc;
	res_out->end = res_out->start + 0x1000 - 1;

	return 0;
}

/*
/*
 * v5 offset per ee and per apid for observer channels and per apid for
 * v5 offset per ee and per apid for observer channels and per apid for
 * read/write channels.
 * read/write channels.
@@ -1034,6 +1074,21 @@ static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr,
	return offset;
	return offset;
}
}


static int pmic_arb_wr_addr_map_v5(struct spmi_pmic_arb *pmic_arb, u8 sid,
				   u16 addr, struct resource *res_out)
{
	int rc;

	rc = pmic_arb_offset_v5(pmic_arb, sid, addr, PMIC_ARB_CHANNEL_RW);
	if (rc < 0)
		return rc;

	res_out->start = pmic_arb->wr_base_phys + rc;
	res_out->end = res_out->start + 0x10000 - 1;

	return 0;
}

static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
{
{
	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
	return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
@@ -1143,6 +1198,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v1 = {
	.irq_status		= pmic_arb_irq_status_v1,
	.irq_status		= pmic_arb_irq_status_v1,
	.irq_clear		= pmic_arb_irq_clear_v1,
	.irq_clear		= pmic_arb_irq_clear_v1,
	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
	.wr_addr_map		= pmic_arb_wr_addr_map_v1,
};
};


static const struct pmic_arb_ver_ops pmic_arb_v2 = {
static const struct pmic_arb_ver_ops pmic_arb_v2 = {
@@ -1156,6 +1212,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = {
	.irq_status		= pmic_arb_irq_status_v2,
	.irq_status		= pmic_arb_irq_status_v2,
	.irq_clear		= pmic_arb_irq_clear_v2,
	.irq_clear		= pmic_arb_irq_clear_v2,
	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
	.wr_addr_map		= pmic_arb_wr_addr_map_v2,
};
};


static const struct pmic_arb_ver_ops pmic_arb_v3 = {
static const struct pmic_arb_ver_ops pmic_arb_v3 = {
@@ -1169,6 +1226,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = {
	.irq_status		= pmic_arb_irq_status_v2,
	.irq_status		= pmic_arb_irq_status_v2,
	.irq_clear		= pmic_arb_irq_clear_v2,
	.irq_clear		= pmic_arb_irq_clear_v2,
	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
	.apid_map_offset	= pmic_arb_apid_map_offset_v2,
	.wr_addr_map		= pmic_arb_wr_addr_map_v2,
};
};


static const struct pmic_arb_ver_ops pmic_arb_v5 = {
static const struct pmic_arb_ver_ops pmic_arb_v5 = {
@@ -1182,6 +1240,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = {
	.irq_status		= pmic_arb_irq_status_v5,
	.irq_status		= pmic_arb_irq_status_v5,
	.irq_clear		= pmic_arb_irq_clear_v5,
	.irq_clear		= pmic_arb_irq_clear_v5,
	.apid_map_offset	= pmic_arb_apid_map_offset_v5,
	.apid_map_offset	= pmic_arb_apid_map_offset_v5,
	.wr_addr_map		= pmic_arb_wr_addr_map_v5,
};
};


static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
@@ -1191,6 +1250,62 @@ static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
	.translate = qpnpint_irq_domain_translate,
	.translate = qpnpint_irq_domain_translate,
};
};


/**
 * spmi_pmic_arb_map_address() - returns physical addresses of registers used to
 *		write to the PMIC peripheral at spmi_address
 * @dev:		Consumer device pointer
 * @spmi_address:	20-bit SPMI address of the form: 0xSPPPP
 *			where S = global PMIC SID and
 *			PPPP = SPMI address within the slave
 * @res_out:		Resource struct (allocated by the caller) in which
 *			physical addresses for the range are passed via start
 *			and end elements
 *
 * Returns: 0 on success or an errno on failure.
 */
int spmi_pmic_arb_map_address(const struct device *dev, u32 spmi_address,
			      struct resource *res_out)
{
	struct device_node *ctrl_node;
	struct platform_device *ctrl_pdev;
	struct spmi_controller *ctrl;
	struct spmi_pmic_arb *pmic_arb;
	u32 sid, addr;

	if (!dev || !dev->of_node || !res_out) {
		pr_err("%s: Invalid pointer\n", __func__);
		return -EINVAL;
	}

	ctrl_node = of_parse_phandle(dev->of_node, "qcom,pmic-arb", 0);
	if (!ctrl_node) {
		pr_err("%s: Could not find PMIC arbiter node via qcom,pmic-arb property\n",
			__func__);
		return -ENODEV;
	}

	ctrl_pdev = of_find_device_by_node(ctrl_node);
	of_node_put(ctrl_node);
	if (!ctrl_pdev)
		return -EPROBE_DEFER;

	ctrl = platform_get_drvdata(ctrl_pdev);
	if (!ctrl)
		return -EPROBE_DEFER;

	pmic_arb = spmi_controller_get_drvdata(ctrl);
	if (!pmic_arb) {
		pr_err("Missing PMIC arbiter device\n");
		return -ENODEV;
	}

	sid = (spmi_address >> 16) & 0xF;
	addr = spmi_address & 0xFFFF;

	return pmic_arb->ver_ops->wr_addr_map(pmic_arb, sid, addr, res_out);
}
EXPORT_SYMBOL(spmi_pmic_arb_map_address);

static int spmi_pmic_arb_probe(struct platform_device *pdev)
static int spmi_pmic_arb_probe(struct platform_device *pdev)
{
{
	struct spmi_pmic_arb *pmic_arb;
	struct spmi_pmic_arb *pmic_arb;
@@ -1230,6 +1345,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
	if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
	if (hw_ver < PMIC_ARB_VERSION_V2_MIN) {
		pmic_arb->ver_ops = &pmic_arb_v1;
		pmic_arb->ver_ops = &pmic_arb_v1;
		pmic_arb->wr_base = core;
		pmic_arb->wr_base = core;
		pmic_arb->wr_base_phys = res->start;
		pmic_arb->rd_base = core;
		pmic_arb->rd_base = core;
	} else {
	} else {
		pmic_arb->core = core;
		pmic_arb->core = core;
@@ -1256,6 +1372,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
			err = PTR_ERR(pmic_arb->wr_base);
			err = PTR_ERR(pmic_arb->wr_base);
			goto err_put_ctrl;
			goto err_put_ctrl;
		}
		}
		pmic_arb->wr_base_phys = res->start;
	}
	}


	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
+22 −0
Original line number Original line Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */

#ifndef _SPMI_PMIC_ARB_H
#define _SPMI_PMIC_ARB_H

#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/types.h>

#if IS_ENABLED(CONFIG_SPMI_MSM_PMIC_ARB)
int spmi_pmic_arb_map_address(const struct device *dev, u32 spmi_address,
			      struct resource *res_out);
#else
static inline int spmi_pmic_arb_map_address(const struct device *dev,
				u32 spmi_address, struct resource *res_out)
{
	return -ENODEV;
}
#endif

#endif