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

Commit 24024467 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "spmi: spmi-pmic-arb: make interrupt support optional"

parents 20f88c84 f049b524
Loading
Loading
Loading
Loading
+146 −18
Original line number Diff line number Diff line
// 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/delay.h>
@@ -12,9 +12,11 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spmi.h>
#include <linux/soc/qcom/spmi-pmic-arb.h>

/* PMIC Arbiter configuration registers */
#define PMIC_ARB_VERSION		0x0000
@@ -123,6 +125,8 @@ struct apid_data {
 *
 * @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_phys:	Base physical address of the register range used for
 *			SPMI write commands.
 * @intr:		address of the SPMI interrupt control registers.
 * @cnfg:		address of the PMIC Arbiter configuration registers.
 * @lock:		lock to synchronize accesses.
@@ -140,6 +144,7 @@ struct apid_data {
struct spmi_pmic_arb {
	void __iomem		*rd_base;
	void __iomem		*wr_base;
	phys_addr_t		wr_base_phys;
	void __iomem		*intr;
	void __iomem		*cnfg;
	void __iomem		*core;
@@ -179,6 +184,9 @@ struct spmi_pmic_arb {
 * @irq_clear:		on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
 *			on v2 address of SPMI_PIC_IRQ_CLEARn.
 * @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 {
	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_clear)(struct spmi_pmic_arb *pmic_arb, 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,
@@ -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;
}

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)
{
	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;
}

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
 * 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;
}

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)
{
	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_clear		= pmic_arb_irq_clear_v1,
	.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 = {
@@ -1156,6 +1212,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = {
	.irq_status		= pmic_arb_irq_status_v2,
	.irq_clear		= pmic_arb_irq_clear_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 = {
@@ -1169,6 +1226,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = {
	.irq_status		= pmic_arb_irq_status_v2,
	.irq_clear		= pmic_arb_irq_clear_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 = {
@@ -1182,6 +1240,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = {
	.irq_status		= pmic_arb_irq_status_v5,
	.irq_clear		= pmic_arb_irq_clear_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 = {
@@ -1191,6 +1250,62 @@ static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
	.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)
{
	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) {
		pmic_arb->ver_ops = &pmic_arb_v1;
		pmic_arb->wr_base = core;
		pmic_arb->wr_base_phys = res->start;
		pmic_arb->rd_base = core;
	} else {
		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);
			goto err_put_ctrl;
		}
		pmic_arb->wr_base_phys = res->start;
	}

	dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n",
@@ -1275,11 +1392,13 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
		goto err_put_ctrl;
	}

	if (of_find_property(pdev->dev.of_node, "interrupt-names", NULL)) {
		pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq");
		if (pmic_arb->irq < 0) {
			err = pmic_arb->irq;
			goto err_put_ctrl;
		}
	}

	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
	if (err) {
@@ -1338,6 +1457,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
		}
	}

	if (pmic_arb->irq > 0) {
		dev_dbg(&pdev->dev, "adding irq domain\n");
		pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node,
					    &pmic_arb_irq_domain_ops, pmic_arb);
@@ -1347,8 +1467,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
			goto err_put_ctrl;
		}

	irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq,
					pmic_arb);
		irq_set_chained_handler_and_data(pmic_arb->irq,
						pmic_arb_chained_irq, pmic_arb);
	} else {
		dev_dbg(&pdev->dev, "not supporting PMIC interrupts\n");
	}

	err = spmi_controller_add(ctrl);
	if (err)
		goto err_domain_remove;
@@ -1356,8 +1480,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
	return 0;

err_domain_remove:
	if (pmic_arb->irq > 0) {
		irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
		irq_domain_remove(pmic_arb->domain);
	}
err_put_ctrl:
	spmi_controller_put(ctrl);
	return err;
@@ -1368,8 +1494,10 @@ static int spmi_pmic_arb_remove(struct platform_device *pdev)
	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
	struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl);
	spmi_controller_remove(ctrl);
	if (pmic_arb->irq > 0) {
		irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL);
		irq_domain_remove(pmic_arb->domain);
	}
	spmi_controller_put(ctrl);
	return 0;
}
+22 −0
Original line number 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