Skip to contentSkip to conversation

Add support for the System Power Management Interface (SPMI)

PATCH Superseded Josh Cartwright 3 participants 12 messages Jan 14, 2014 – Jan 15, 2014

Subsystems (5)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 cover Add support for the System Power Management Interface (SPMI)

The System Power Management Interface (SPMI) is a high-speed, low-latency, bi-directional, two-wire serial bus suitable for real-time control of voltage and frequency scaled multi-core application processors and its power management of auxiliary components. SPMI obsoletes a number of legacy, custom point-to-point interfaces and provides a low pin count, high-speed control bus for up to 4 Master and 16 Slave devices.

SPMI is specified by the MIPI (Mobile Industry Process Interface) Alliance [1].

This patchset is intended both to provide a core implementation of SPMI and also to provide a controller driver implementation.
- Patches 1-2 implement the SPMI core functionality and provide basic DT binding documentation
- Patches 3-5 provide an implementation of an SPMI controller, the Qualcomm SPMI PMIC Arbiter", currently used on the 8x74 series SoCs.
- Patch 6 rounds out regmap support for SPMI

Changes from v3[2]:
- Dropped the pm8x41 PMIC driver and pm8xxx-rtc changes as part of this patchset. I'll be working with Stephen Boyd to see if we can't adapt his pm8xxx patches[3] to suit the needs of the pm8x41.
- Rebased on v3.13-rc2
- Move to simple_ida_* for controller ID allocation
- Addressed documentation fixes and nits
- Provide pm_runtime implementation, which leverages SPMI's SLEEP and WAKEUP commands
- Address spmi_controller object lifetime issues

Changes from v2[4]:
- Dropped RFC.
- Add basic regmap support at Mark Brown's suggestion
- Drop debugfs interface. Debugging SPMI accesses can happen via the regmap debugfs interface if necessary.
- Add second address-cell in SPMI generic device tree binding, encoding the address type (suggestion by Stephen Warren)
- Implement interrupt handling functionality within the PMIC Arbiter driver
- Provide basic MFD driver for the PMIC8x41 PMICs, demonstrating SPMI regmap client use
- Adapt existing pm8xxx-rtc driver to work as a child of the PM8x41 mfd device

Changes from v1[5]:
- Adopted patch (1/5) to #define for_each_available_node() shim in the !CONFIG_OF case
- Moved device tree logic out of drivers/of and into spmi.c core (this mirrors what SPI is doing, and what i2c will soon be doing)
- Move of_spmi_add_devices() call into spmi_device_add(), so drivers don't have to call it explicitly
- Unconditionally build in debugfs code (rely on the underlying CONFIG_DEBUG_FS switch to throw unused code away)
- Change pr_* print functions to their dev_* equivalents
- Fix copy_{to,from}_user error handling
- Renamed "board_lock" to "ctrl_idr_lock" to better describe it's purpose
- Rework device object lifetime management
- Rename PMIC arb binding document, add description of PMIC arb
- Add generic SPMI device tree bindings

[1]: http://www.mipi.org/specifications/system-power-management-interface
[2]: http://lkml.kernel.org/r/cover.1382985169.git.joshc at codeaurora.org [3]: http://lkml.kernel.org/r/1389206270-3728-1-git-send-email-sboyd at codeaurora.org [4]: http://lkml.kernel.org/r/cover.1377202730.git.joshc at codeaurora.org [5]: http://lkml.kernel.org/r/cover.1376596224.git.joshc at codeaurora.org

Josh Cartwright (4):
spmi: add generic SPMI controller binding documentation
spmi: pmic_arb: add support for interrupt handling
spmi: document the PMIC arbiter SPMI bindings
regmap: spmi: support base and extended register spaces

Kenneth Heitke (2):
spmi: Linux driver framework for SPMI
spmi: Add MSM PMIC Arbiter SPMI controller

 .../bindings/spmi/qcom,spmi-pmic-arb.txt           |  46 ++
 Documentation/devicetree/bindings/spmi/spmi.txt    |  41 ++
 drivers/Kconfig                                    |   2 +
 drivers/Makefile                                   |   1 +
 drivers/base/regmap/regmap-spmi.c                  | 228 +++++-
 drivers/spmi/Kconfig                               |  24 +
 drivers/spmi/Makefile                              |   6 +
 drivers/spmi/spmi-pmic-arb.c                       | 802 +++++++++++++++++++++
 drivers/spmi/spmi.c                                | 609 ++++++++++++++++
 include/dt-bindings/spmi/spmi.h                    |  18 +
 include/linux/mod_devicetable.h                    |   8 +
 include/linux/regmap.h                             |  12 +-
 include/linux/spmi.h                               | 191 +++++
 13 files changed, 1953 insertions(+), 35 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
 create mode 100644 Documentation/devicetree/bindings/spmi/spmi.txt
 create mode 100644 drivers/spmi/Kconfig
 create mode 100644 drivers/spmi/Makefile
 create mode 100644 drivers/spmi/spmi-pmic-arb.c
 create mode 100644 drivers/spmi/spmi.c
 create mode 100644 include/dt-bindings/spmi/spmi.h
 create mode 100644 include/linux/spmi.h
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#1 lore raw

PATCH v4 1/6 spmi: Linux driver framework for SPMI

From: Kenneth Heitke <[email protected]>

System Power Management Interface (SPMI) is a specification developed by the MIPI (Mobile Industry Process Interface) Alliance optimized for the real time control of Power Management ICs (PMIC).

SPMI is a two-wire serial interface that supports up to 4 master devices and up to 16 logical slaves.

The framework supports message APIs, multiple busses (1 controller per bus) and multiple clients/slave devices per controller.

---
 drivers/Kconfig                 |   2 +
 drivers/Makefile                |   1 +
 drivers/spmi/Kconfig            |   9 +
 drivers/spmi/Makefile           |   4 +
 drivers/spmi/spmi.c             | 609 ++++++++++++++++++++++++++++++++++++++++
 include/dt-bindings/spmi/spmi.h |  18 ++
 include/linux/mod_devicetable.h |   8 +
 include/linux/spmi.h            | 191 +++++++++++++
 8 files changed, 842 insertions(+)
 create mode 100644 drivers/spmi/Kconfig
 create mode 100644 drivers/spmi/Makefile
 create mode 100644 drivers/spmi/spmi.c
 create mode 100644 include/dt-bindings/spmi/spmi.h
 create mode 100644 include/linux/spmi.h
drivers/Kconfig +2 -0
index b3138fb..e0a4ae6 100644
@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
 
 source "drivers/spi/Kconfig"
 
+source "drivers/spmi/Kconfig"
+
 source "drivers/hsi/Kconfig"
 
 source "drivers/pps/Kconfig"
drivers/Makefile +1 -0
index 3cc8214..0a2fb78 100644
@@ -66,6 +66,7 @@ obj-$(CONFIG_ATA)		+= ata/
 obj-$(CONFIG_TARGET_CORE)	+= target/
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
+obj-$(CONFIG_SPMI)		+= spmi/
 obj-y				+= hsi/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
drivers/spmi/Kconfig new file +9 -0
new file mode 100644
index 0000000..1dbfee0
@@ -0,0 +1,9 @@
+#
+# SPMI driver configuration
+#
+menuconfig SPMI
+	tristate "SPMI support"
+	help
+	  SPMI (System Power Management Interface) is a two-wire
+	  serial interface between baseband and application processors
+	  and Power Management Integrated Circuits (PMIC).
drivers/spmi/Makefile new file +4 -0
new file mode 100644
index 0000000..1de1acd
@@ -0,0 +1,4 @@
+#
+# Makefile for kernel SPMI framework.
+#
+obj-$(CONFIG_SPMI)	+= spmi.o
drivers/spmi/spmi.c new file +609 -0

609 lines not on this page · raw message

include/dt-bindings/spmi/spmi.h new file +18 -0
new file mode 100644
index 0000000..d11e1e5
@@ -0,0 +1,18 @@
+/* Copyright (c) 2013, 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.
+ */
+#ifndef __DT_BINDINGS_SPMI_H
+#define __DT_BINDINGS_SPMI_H
+
+#define SPMI_USID	0
+#define SPMI_GSID	1
+
+#endif
include/linux/mod_devicetable.h +8 -0
index 45e9214..677e474 100644
@@ -432,6 +432,14 @@ struct spi_device_id {
 	kernel_ulong_t driver_data;	/* Data private to the driver */
 };
 
+#define SPMI_NAME_SIZE	32
+#define SPMI_MODULE_PREFIX "spmi:"
+
+struct spmi_device_id {
+	char name[SPMI_NAME_SIZE];
+	kernel_ulong_t driver_data;	/* Data private to the driver */
+};
+
 /* dmi */
 enum dmi_field {
 	DMI_NONE,
include/linux/spmi.h new file +191 -0

191 lines not on this page · raw message

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#2 lore raw

PATCH v4 2/6 spmi: add generic SPMI controller binding documentation

In reply to Josh Cartwright
---
 Documentation/devicetree/bindings/spmi/spmi.txt | 41 +++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spmi/spmi.txt
Documentation/devicetree/bindings/spmi/spmi.txt new file +41 -0
new file mode 100644
index 0000000..462a42f
@@ -0,0 +1,41 @@
+System Power Management Interface (SPMI) Controller
+
+This document defines a generic set of bindings for use by SPMI controllers.  A
+controller is modelled in device tree as a node with zero or more child nodes,
+each representing a unique slave on the bus.
+
+Required properties:
+- #address-cells : must be set to 2
+- #size-cells : must be set to 0
+
+Child nodes:
+
+An SPMI controller node can contain zero or more child nodes representing slave
+devices on the bus.  Child 'reg' properties are specified as an address, type
+pair.  The address must be in the range 0-15 (4 bits).  The type must be one of
+SPMI_USID (0) or SPMI_GSID (1) for Unique Slave ID or Group Slave ID respectively.
+These are the identifiers "statically assigned by the system integrator", as
+per the SPMI spec.
+
+Each child node must have one and only one 'reg' entry of type SPMI_USID.
+
+#include <dt-bindings/spmi/spmi.h>
+
+	spmi at .. {
+		compatible = "...";
+		reg = <...>;
+
+		#address-cells = <2>;
+		#size-cells <0>;
+
+		child at 0 {
+			compatible = "...";
+			reg = <0 SPMI_USID>;
+		};
+
+		child at 7 {
+			compatible = "...";
+			reg = <7 SPMI_USID
+			       3 SPMI_GSID>;
+		};
+	};
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#3 lore raw

PATCH v4 3/6 spmi: Add MSM PMIC Arbiter SPMI controller

In reply to Josh Cartwright

From: Kenneth Heitke <[email protected]>

Qualcomm's PMIC Arbiter SPMI controller functions as a bus master and is used to communication with one or more PMIC (slave) devices on the SPMI bus. The PMIC Arbiter is actually a hardware wrapper around the SPMI controller that provides concurrent and autonomous PMIC access to various entities that need to communicate with the PMIC.

The SPMI controller hardware handles all of the SPMI bus activity (bus arbitration, sequence start condition, transmission of frames, etc). This software driver uses the PMIC Arbiter register interface to initiate command sequences on the SPMI bus. The status register is read to determine when the command sequence has completed and whether or not it completed successfully.

---
 drivers/spmi/Kconfig         |  15 ++
 drivers/spmi/Makefile        |   2 +
 drivers/spmi/spmi-pmic-arb.c | 413 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 430 insertions(+)
 create mode 100644 drivers/spmi/spmi-pmic-arb.c
drivers/spmi/Kconfig +15 -0
index 1dbfee0..bd09c78 100644
@@ -7,3 +7,18 @@ menuconfig SPMI
 	  SPMI (System Power Management Interface) is a two-wire
 	  serial interface between baseband and application processors
 	  and Power Management Integrated Circuits (PMIC).
+
+if SPMI
+
+config SPMI_MSM_PMIC_ARB
+	tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
+	depends on ARCH_MSM
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in SPMI PMIC Arbiter interface on Qualcomm MSM family
+	  processors.
+
+	  This is required for communicating with Qualcomm PMICs and
+	  other devices that have the SPMI interface.
+
+endif
drivers/spmi/Makefile +2 -0
index 1de1acd..fc75104 100644
@@ -2,3 +2,5 @@
 # Makefile for kernel SPMI framework.
 #
 obj-$(CONFIG_SPMI)	+= spmi.o
+
+obj-$(CONFIG_SPMI_MSM_PMIC_ARB)	+= spmi-pmic-arb.o
drivers/spmi/spmi-pmic-arb.c new file +413 -0

413 lines not on this page · raw message

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#4 lore raw

PATCH v4 4/6 spmi: pmic_arb: add support for interrupt handling

In reply to Josh Cartwright

The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller, also implements interrupt handling for slave devices. Note, this is outside the scope of SPMI, as SPMI leaves interrupt handling completely unspecified.

Extend the driver to provide a irq_chip implementation and chained irq handling which allows for these interrupts to be used.

---
 drivers/spmi/spmi-pmic-arb.c | 393 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 391 insertions(+), 2 deletions(-)
drivers/spmi/spmi-pmic-arb.c +391 -2
index 16083cd..32bed54 100644
@@ -13,6 +13,9 @@
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code {
  * @cnfg:		address of the PMIC Arbiter configuration registers.
  * @lock:		lock to synchronize accesses.
  * @channel:		which channel to use for accesses.
+ * @irq:		PMIC ARB interrupt.
+ * @ee:			the current Execution Environment
+ * @min_apid:		minimum APID (used for bounding IRQ search)
+ * @max_apid:		maximum APID
+ * @mapping_table:	in-memory copy of PPID -> APID mapping table.
+ * @domain:		irq domain object for PMIC IRQ domain
+ * @spmic:		SPMI controller object
+ * @apid_to_ppid:	cached mapping from APID to PPID
  */
 struct spmi_pmic_arb_dev {
 	void __iomem		*base;
@@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev {
 	void __iomem		*cnfg;
 	spinlock_t		lock;
 	u8			channel;
+	unsigned int		irq;
+	u8			ee;
+	u8			min_apid;
+	u8			max_apid;
+	u32			mapping_table[SPMI_MAPPING_TABLE_LEN];
+	struct irq_domain	*domain;
+	struct spmi_controller	*spmic;
+	u16			apid_to_ppid[256];
 };
 
 static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
@@ -314,12 +333,333 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
 	return rc;
 }
 
+enum qpnpint_regs {
+	QPNPINT_REG_RT_STS		= 0x10,
+	QPNPINT_REG_SET_TYPE		= 0x11,
+	QPNPINT_REG_POLARITY_HIGH	= 0x12,
+	QPNPINT_REG_POLARITY_LOW	= 0x13,
+	QPNPINT_REG_LATCHED_CLR		= 0x14,
+	QPNPINT_REG_EN_SET		= 0x15,
+	QPNPINT_REG_EN_CLR		= 0x16,
+	QPNPINT_REG_LATCHED_STS		= 0x18,
+};
+
+struct spmi_pmic_arb_qpnpint_type {
+	u8 type; /* 1 -> edge */
+	u8 polarity_high;
+	u8 polarity_low;
+} __packed;
+
+/* Simplified accessor functions for irqchip callbacks */
+static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
+			       size_t len)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	u8 sid = d->hwirq >> 24;
+	u8 per = d->hwirq >> 16;
+
+	if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
+			       (per << 8) + reg, buf, len))
+		dev_err_ratelimited(&pa->spmic->dev,
+				"failed irqchip transaction on %x\n",
+				    d->irq);
+}
+
+static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	u8 sid = d->hwirq >> 24;
+	u8 per = d->hwirq >> 16;
+
+	if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid,
+			      (per << 8) + reg, buf, len))
+		dev_err_ratelimited(&pa->spmic->dev,
+				"failed irqchip transaction on %x\n",
+				    d->irq);
+}
+
+static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid)
+{
+	unsigned int irq;
+	u32 status;
+	int id;
+
+	status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid));
+	while (status) {
+		id = ffs(status) - 1;
+		status &= ~(1 << id);
+		irq = irq_find_mapping(pa->domain,
+				       pa->apid_to_ppid[apid] << 16
+				     | id << 8
+				     | apid);
+		generic_handle_irq(irq);
+	}
+}
+
+static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc)
+{
+	struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq);
+	struct irq_chip *chip = irq_get_chip(irq);
+	void __iomem *intr = pa->intr;
+	int first = pa->min_apid >> 5;
+	int last = pa->max_apid >> 5;
+	u32 status;
+	int i, id;
+
+	chained_irq_enter(chip, desc);
+
+	for (i = first; i <= last; ++i) {
+		status = readl_relaxed(intr +
+				       SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i));
+		while (status) {
+			id = ffs(status) - 1;
+			status &= ~(1 << id);
+			periph_interrupt(pa, id + i * 32);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void qpnpint_irq_ack(struct irq_data *d)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	u8 irq  = d->hwirq >> 8;
+	u8 apid = d->hwirq;
+	unsigned long flags;
+	u8 data;
+
+	dev_dbg(&pa->spmic->dev, "hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	spin_lock_irqsave(&pa->lock, flags);
+	writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid));
+	spin_unlock_irqrestore(&pa->lock, flags);
+
+	data = 1 << irq;
+	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
+}
+
+static void qpnpint_irq_mask(struct irq_data *d)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	u8 irq  = d->hwirq >> 8;
+	u8 apid = d->hwirq;
+	unsigned long flags;
+	u32 status;
+	u8 data;
+
+	dev_dbg(&pa->spmic->dev, "hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	spin_lock_irqsave(&pa->lock, flags);
+	status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (status & SPMI_PIC_ACC_ENABLE_BIT) {
+		status = status & ~SPMI_PIC_ACC_ENABLE_BIT;
+		writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+	}
+	spin_unlock_irqrestore(&pa->lock, flags);
+
+	data = 1 << irq;
+	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1);
+}
+
+static void qpnpint_irq_unmask(struct irq_data *d)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	u8 irq  = d->hwirq >> 8;
+	u8 apid = d->hwirq;
+	unsigned long flags;
+	u32 status;
+	u8 data;
+
+	dev_dbg(&pa->spmic->dev, "hwirq %lu irq: %d, apid = %02x\n", d->hwirq,
+				  d->irq, apid);
+
+	spin_lock_irqsave(&pa->lock, flags);
+	status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+	if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) {
+		writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT,
+				pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+	}
+	spin_unlock_irqrestore(&pa->lock, flags);
+
+	data = 1 << irq;
+	qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1);
+}
+
+static void qpnpint_irq_enable(struct irq_data *d)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	u8 irq  = d->hwirq >> 8;
+	u8 data;
+
+	dev_dbg(&pa->spmic->dev, "hwirq %lu irq: %d\n", d->hwirq, d->irq);
+
+	qpnpint_irq_unmask(d);
+
+	data = 1 << irq;
+	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1);
+}
+
+static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+	struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d);
+	struct spmi_pmic_arb_qpnpint_type type;
+	u8 irq = d->hwirq >> 8;
+
+	dev_dbg(&pa->spmic->dev, "qpnpint_irq_set_type %p, %x\n", d, flow_type);
+
+	qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
+
+	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+		type.type |= 1 << irq;
+		if (flow_type & IRQF_TRIGGER_RISING)
+			type.polarity_high |= 1 << irq;
+		if (flow_type & IRQF_TRIGGER_FALLING)
+			type.polarity_low  |= 1 << irq;
+	} else {
+		if ((flow_type & (IRQF_TRIGGER_HIGH)) &&
+		    (flow_type & (IRQF_TRIGGER_LOW)))
+			return -EINVAL;
+
+		type.type &= ~(1 << irq); /* level trig */
+		if (flow_type & IRQF_TRIGGER_HIGH)
+			type.polarity_high |= 1 << irq;
+		else
+			type.polarity_low  |= 1 << irq;
+	}
+
+	qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type));
+	return 0;
+}
+
+static struct irq_chip pmic_arb_irqchip = {
+	.name		= "pmic_arb",
+	.irq_enable	= qpnpint_irq_enable,
+	.irq_ack	= qpnpint_irq_ack,
+	.irq_mask	= qpnpint_irq_mask,
+	.irq_unmask	= qpnpint_irq_unmask,
+	.irq_set_type	= qpnpint_irq_set_type,
+	.flags		= IRQCHIP_MASK_ON_SUSPEND
+			| IRQCHIP_SKIP_SET_WAKE,
+};
+
+struct spmi_pmic_arb_irq_spec {
+	unsigned slave:4;
+	unsigned per:8;
+	unsigned irq:3;
+};
+
+static int search_mapping_table(struct spmi_pmic_arb_dev *pa,
+				struct spmi_pmic_arb_irq_spec *spec,
+				u8 *apid)
+{
+	u16 ppid = spec->slave << 8 | spec->per;
+	u32 *mapping_table = pa->mapping_table;
+	int index = 0, i;
+	u32 data;
+
+	for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) {
+		data = mapping_table[index];
+
+		if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) {
+			if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) {
+				index = SPMI_MAPPING_BIT_IS_1_RESULT(data);
+			} else {
+				*apid = SPMI_MAPPING_BIT_IS_1_RESULT(data);
+				return 0;
+			}
+		} else {
+			if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) {
+				index = SPMI_MAPPING_BIT_IS_0_RESULT(data);
+			} else {
+				*apid = SPMI_MAPPING_BIT_IS_0_RESULT(data);
+				return 0;
+			}
+		}
+	}
+
+	return -ENODEV;
+}
+
+static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
+					   struct device_node *controller,
+					   const u32 *intspec,
+					   unsigned int intsize,
+					   unsigned long *out_hwirq,
+					   unsigned int *out_type)
+{
+	struct spmi_pmic_arb_dev *pa = d->host_data;
+	struct spmi_pmic_arb_irq_spec spec;
+	int err;
+	u8 apid;
+
+	dev_dbg(&pa->spmic->dev,
+		"intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
+		intspec[0], intspec[1], intspec[2]);
+
+	if (d->of_node != controller)
+		return -EINVAL;
+	if (intsize != 4)
+		return -EINVAL;
+	if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7)
+		return -EINVAL;
+
+	spec.slave = intspec[0];
+	spec.per   = intspec[1];
+	spec.irq   = intspec[2];
+
+	err = search_mapping_table(pa, &spec, &apid);
+	if (err)
+		return err;
+
+	pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per;
+
+	/* Keep track of {max,min}_apid for bounding search during interrupt */
+	if (apid > pa->max_apid)
+		pa->max_apid = apid;
+	if (apid < pa->min_apid)
+		pa->min_apid = apid;
+
+	*out_hwirq = spec.slave << 24
+		   | spec.per   << 16
+		   | spec.irq   << 8
+		   | apid;
+	*out_type  = intspec[3] & IRQ_TYPE_SENSE_MASK;
+
+	dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq);
+
+	return 0;
+}
+
+static int qpnpint_irq_domain_map(struct irq_domain *d,
+				  unsigned int virq,
+				  irq_hw_number_t hwirq)
+{
+	struct spmi_pmic_arb_dev *pa = d->host_data;
+
+	dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq);
+
+	irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq);
+	irq_set_chip_data(virq, d->host_data);
+#ifdef CONFIG_ARM
+	set_irq_flags(virq, IRQF_VALID);
+#else
+	irq_set_noprobe(virq);
+#endif
+	return 0;
+}
+
+static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
+	.map	= qpnpint_irq_domain_map,
+	.xlate	= qpnpint_irq_domain_dt_translate,
+};
+
 static int spmi_pmic_arb_probe(struct platform_device *pdev)
 {
 	struct spmi_pmic_arb_dev *pa;
 	struct spmi_controller *ctrl;
 	struct resource *res;
-	u32 channel;
+	u32 channel, ee;
 	int err, i;
 
 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
@@ -327,6 +667,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	pa = spmi_controller_get_drvdata(ctrl);
+	pa->spmic = ctrl;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
 	pa->base = devm_ioremap_resource(&ctrl->dev, res);
@@ -349,6 +690,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
 		goto err_put_ctrl;
 	}
 
+	pa->irq = platform_get_irq(pdev, 0);
+	if (pa->irq < 0) {
+		err = pa->irq;
+		goto err_put_ctrl;
+	}
+
 	err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel);
 	if (err) {
 		dev_err(&pdev->dev, "channel unspecified.\n");
@@ -363,6 +710,28 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
 
 	pa->channel = channel;
 
+	err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee);
+	if (err) {
+		dev_err(&pdev->dev, "EE unspecified.\n");
+		goto err_put_ctrl;
+	}
+
+	if (ee > 5) {
+		dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee);
+		goto err_put_ctrl;
+	}
+
+	pa->ee = ee;
+
+	for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i)
+		pa->mapping_table[i] = readl_relaxed(
+				pa->cnfg + SPMI_MAPPING_TABLE_REG(i));
+
+	/* Initialize max_apid/min_apid to the opposite bounds, during
+	 * the irq domain translation, we are sure to update these */
+	pa->max_apid = 0;
+	pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1;
+
 	platform_set_drvdata(pdev, ctrl);
 	spin_lock_init(&pa->lock);
 
@@ -370,15 +739,31 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
 	ctrl->read_cmd = pmic_arb_read_cmd;
 	ctrl->write_cmd = pmic_arb_write_cmd;
 
+	dev_dbg(&pdev->dev, "adding irq domain\n");
+	pa->domain = irq_domain_add_tree(pdev->dev.of_node,
+					 &pmic_arb_irq_domain_ops, pa);
+	if (!pa->domain) {
+		dev_err(&pdev->dev, "unable to create irq_domain\n");
+		err = -ENOMEM;
+		goto err_put_ctrl;
+	}
+
+	irq_set_handler_data(pa->irq, pa);
+	irq_set_chained_handler(pa->irq, pmic_arb_chained_irq);
+
 	err = spmi_controller_add(ctrl);
 	if (err)
-		goto err_put_ctrl;
+		goto err_domain_remove;
 
 	dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n",
 		pmic_arb_base_read(pa, PMIC_ARB_VERSION));
 
 	return 0;
 
+err_domain_remove:
+	irq_set_chained_handler(pa->irq, NULL);
+	irq_set_handler_data(pa->irq, NULL);
+	irq_domain_remove(pa->domain);
 err_put_ctrl:
 	spmi_controller_put(ctrl);
 	return err;
@@ -387,7 +772,11 @@ err_put_ctrl:
 static int spmi_pmic_arb_remove(struct platform_device *pdev)
 {
 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
+	struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl);
 	spmi_controller_remove(ctrl);
+	irq_set_chained_handler(pa->irq, NULL);
+	irq_set_handler_data(pa->irq, NULL);
+	irq_domain_remove(pa->domain);
 	spmi_controller_put(ctrl);
 	return 0;
 }
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#5 lore raw

Re: [PATCH v4 4/6] spmi: pmic_arb: add support for interrupt handling

Quoted 5 lines · Josh Cartwright

The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller, also implements interrupt handling for slave devices. Note, this is outside the scope of SPMI, as SPMI leaves interrupt handling completely unspecified.

Quoted 8 lines

Extend the driver to provide a irq_chip implementation and chained irq handling which allows for these interrupts to be used.

---
 drivers/spmi/spmi-pmic-arb.c | 393 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 391 insertions(+), 2 deletions(-)

Yay! Good to see this series.

Quoted 55 lines
drivers/spmi/spmi-pmic-arb.c +26 -0
index 16083cd..32bed54 100644
@@ -13,6 +13,9 @@
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code {
  * @cnfg:              address of the PMIC Arbiter configuration registers.
  * @lock:              lock to synchronize accesses.
  * @channel:           which channel to use for accesses.
+ * @irq:               PMIC ARB interrupt.
+ * @ee:                        the current Execution Environment
+ * @min_apid:          minimum APID (used for bounding IRQ search)
+ * @max_apid:          maximum APID
+ * @mapping_table:     in-memory copy of PPID -> APID mapping table.
+ * @domain:            irq domain object for PMIC IRQ domain
+ * @spmic:             SPMI controller object
+ * @apid_to_ppid:      cached mapping from APID to PPID
  */
 struct spmi_pmic_arb_dev {
        void __iomem            *base;
@@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev {
        void __iomem            *cnfg;
        spinlock_t              lock;
        u8                      channel;
+       unsigned int            irq;
+       u8                      ee;
+       u8                      min_apid;
+       u8                      max_apid;
+       u32                     mapping_table[SPMI_MAPPING_TABLE_LEN];
+       struct irq_domain       *domain;
+       struct spmi_controller  *spmic;
+       u16                     apid_to_ppid[256];
 };
 
 static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
@@ -314,12 +333,333 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
        return rc;
 }
 
+enum qpnpint_regs {
+       QPNPINT_REG_RT_STS              = 0x10,
+       QPNPINT_REG_SET_TYPE            = 0x11,
+       QPNPINT_REG_POLARITY_HIGH       = 0x12,
+       QPNPINT_REG_POLARITY_LOW        = 0x13,
+       QPNPINT_REG_LATCHED_CLR         = 0x14,
+       QPNPINT_REG_EN_SET              = 0x15,

drivers/spmi/spmi-pmic-arb.c static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, · line 352

Quoted 10 lines
drivers/spmi/spmi-pmic-arb.c +10 -0
+       QPNPINT_REG_EN_CLR              = 0x16,
+       QPNPINT_REG_LATCHED_STS         = 0x18,
+};
+
+struct spmi_pmic_arb_qpnpint_type {
+       u8 type; /* 1 -> edge */
+       u8 polarity_high;
+       u8 polarity_low;
+} __packed;
+

While the rest of this driver uses 'pmic' or 'spmi_pmic', this patch adds 'qpnpint'. Can we please just leave the software fabricated name 'qpnp' out of any changes, as it isn't in any hardware spec? Perhaps 'pmic_int' or something along those lines?

drivers/spmi/spmi-pmic-arb.c static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, · line 355

Quoted 3 lines
drivers/spmi/spmi-pmic-arb.c +3 -0
+/* Simplified accessor functions for irqchip callbacks */
+static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf,
+                              size_t len)

[...]

-Courtney

#6 lore raw

Re: [PATCH v4 4/6] spmi: pmic_arb: add support for interrupt handling

Quoted 13 lines · Courtney Cavin
Quoted 12 lines · Josh Cartwright

The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller, also implements interrupt handling for slave devices. Note, this is outside the scope of SPMI, as SPMI leaves interrupt handling completely unspecified.

Extend the driver to provide a irq_chip implementation and chained irq handling which allows for these interrupts to be used.

---
 drivers/spmi/spmi-pmic-arb.c | 393 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 391 insertions(+), 2 deletions(-)

[..]

Quoted 11 lines

drivers/spmi/spmi-pmic-arb.c static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, · line 352

Quoted 6 lines
drivers/spmi/spmi-pmic-arb.c +6 -0
+struct spmi_pmic_arb_qpnpint_type {
+       u8 type; /* 1 -> edge */
+       u8 polarity_high;
+       u8 polarity_low;
+} __packed;
+

While the rest of this driver uses 'pmic' or 'spmi_pmic', this patch adds 'qpnpint'. Can we please just leave the software fabricated name 'qpnp' out of any changes, as it isn't in any hardware spec? Perhaps 'pmic_int' or something along those lines?

QPNP is not a software-created concept. It is a hardware concept, where it places requirements on the layout of a peripherals' register space, and how interrupts are expected to behave, among other things.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#7 lore raw

PATCH v4 5/6 spmi: document the PMIC arbiter SPMI bindings

In reply to Josh Cartwright
---
 .../bindings/spmi/qcom,spmi-pmic-arb.txt           | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +46 -0
new file mode 100644
index 0000000..9e50cb3
@@ -0,0 +1,46 @@
+Qualcomm SPMI Controller (PMIC Arbiter)
+
+The SPMI PMIC Arbiter is found on the Snapdragon 800 Series.  It is an SPMI
+controller with wrapping arbitration logic to allow for multiple on-chip
+devices to control a single SPMI master.
+
+The PMIC Arbiter can also act as an interrupt controller, providing interrupts
+to slave devices.
+
+See spmi.txt for the generic SPMI controller binding requirements for child
+nodes.
+
+Required properties:
+- compatible : should be "qcom,spmi-pmic-arb".
+- reg-names  : should be "core", "intr", "cnfg"
+- reg : register specifiers, must contain 3 entries, in the follow order:
+     core registers, interrupt controller registers, configuration registers
+- #address-cells : must be set to 2
+- #size-cells : must be set to 0
+- qcom,ee : indicates the active Execution Environment identifier (0-5)
+- qcom,channel : which of the PMIC Arb provided channels to use for accesses (0-5)
+- interrupt-controller : indicates the PMIC arbiter is an interrupt controller
+- #interrupt-cells = <4>:  interrupts are specified as a 4-tuple:
+    cell 1: slave ID for the requested interrupt (0-15)
+    cell 2: peripheral ID for requested interrupt (0-255)
+    cell 3: the requested peripheral interrupt (0-7)
+    cell 4: interrupt flags indicating level-sense information, as defined in
+            dt-bindings/interrupt-controller/irq.h
+
+Example:
+
+	spmi {
+		compatible = "qcom,spmi-pmic-arb";
+		reg-names = "core", "intr", "cnfg";
+		reg = <0xfc4cf000 0x1000>,
+		      <0Xfc4cb000 0x1000>,
+		      <0Xfc4ca000 0x1000>;
+		qcom,ee = <0>;
+		qcom,channel = <0>;
+
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		interrupt-controller;
+		#interrupt-cells = <4>;
+	};
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#8 lore raw

Re: [PATCH v4 5/6] spmi: document the PMIC arbiter SPMI bindings

Quoted 19 lines · Josh Cartwright
---
 .../bindings/spmi/qcom,spmi-pmic-arb.txt           | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +7 -0
new file mode 100644
index 0000000..9e50cb3
@@ -0,0 +1,46 @@
+Qualcomm SPMI Controller (PMIC Arbiter)
+
+The SPMI PMIC Arbiter is found on the Snapdragon 800 Series.  It is an SPMI
+controller with wrapping arbitration logic to allow for multiple on-chip
+devices to control a single SPMI master.
+
+The PMIC Arbiter can also act as an interrupt controller, providing interrupts

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 17

Quoted 10 lines
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +10 -0
+to slave devices.
+
+See spmi.txt for the generic SPMI controller binding requirements for child
+nodes.
+
+Required properties:
+- compatible : should be "qcom,spmi-pmic-arb".
+- reg-names  : should be "core", "intr", "cnfg"
+- reg : register specifiers, must contain 3 entries, in the follow order:
+     core registers, interrupt controller registers, configuration registers

As far as I can tell, patch 3/6 doesn't require these to be in any order, as it uses 'reg-names' to fetch the values. Perhaps the following:

reg-names : must contain:
"core" - core registers
"intr" - interrupt controller registers
"cnfg" - configuration registers
reg : A list of address + size pairs for the regs listed in reg-names

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 21

Quoted 4 lines
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +4 -0
+- #address-cells : must be set to 2
+- #size-cells : must be set to 0
+- qcom,ee : indicates the active Execution Environment identifier (0-5)
+- qcom,channel : which of the PMIC Arb provided channels to use for accesses (0-5)

These two are new.... Is it expected that they should be required?

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 22

Quoted 1 line
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +1 -0
+- interrupt-controller : indicates the PMIC arbiter is an interrupt controller

Although it's probably fairly understood that this is a boolean, it wouldn't hurt to mention that here. It might also be worth referencing devicetree/binding/interrupt-controller/interrupts.txt in your opening blurb.

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 23

Quoted 1 line
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +1 -0
+- #interrupt-cells = <4>:  interrupts are specified as a 4-tuple:

Nitpick: This '= <X>' differs from the above 'must be set to X' format.

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 28

Quoted 5 lines
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +5 -0
+    cell 1: slave ID for the requested interrupt (0-15)
+    cell 2: peripheral ID for requested interrupt (0-255)
+    cell 3: the requested peripheral interrupt (0-7)
+    cell 4: interrupt flags indicating level-sense information, as defined in
+            dt-bindings/interrupt-controller/irq.h

[...]

-Courtney

#9 lore raw

Re: [PATCH v4 5/6] spmi: document the PMIC arbiter SPMI bindings

Quoted 35 lines · Courtney Cavin
Quoted 19 lines · Josh Cartwright
---
 .../bindings/spmi/qcom,spmi-pmic-arb.txt           | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +7 -0
new file mode 100644
index 0000000..9e50cb3
@@ -0,0 +1,46 @@
+Qualcomm SPMI Controller (PMIC Arbiter)
+
+The SPMI PMIC Arbiter is found on the Snapdragon 800 Series.  It is an SPMI
+controller with wrapping arbitration logic to allow for multiple on-chip
+devices to control a single SPMI master.
+
+The PMIC Arbiter can also act as an interrupt controller, providing interrupts

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 17

Quoted 10 lines
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +10 -0
+to slave devices.
+
+See spmi.txt for the generic SPMI controller binding requirements for child
+nodes.
+
+Required properties:
+- compatible : should be "qcom,spmi-pmic-arb".
+- reg-names  : should be "core", "intr", "cnfg"
+- reg : register specifiers, must contain 3 entries, in the follow order:
+     core registers, interrupt controller registers, configuration registers

As far as I can tell, patch 3/6 doesn't require these to be in any order, as it uses 'reg-names' to fetch the values. Perhaps the following:

Quoted 5 lines

reg-names : must contain:
"core" - core registers
"intr" - interrupt controller registers
"cnfg" - configuration registers
reg : A list of address + size pairs for the regs listed in reg-names

Yes, I think this reasonable. Thanks.

Quoted 6 lines

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 21

Quoted 4 lines
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +4 -0
+- #address-cells : must be set to 2
+- #size-cells : must be set to 0
+- qcom,ee : indicates the active Execution Environment identifier (0-5)
+- qcom,channel : which of the PMIC Arb provided channels to use for accesses (0-5)

These two are new.... Is it expected that they should be required?

The 'qcom,channel' property is unconditionally required. Previous iterations of the patchset assumed the desired channel was 0, however, this assumption does not hold for other on-SoC masters.

The 'qcom,ee' property is required for the same reason, however it's only technically required if the PMIC Arb's interrupt functionality is to be used. Right now the driver enforces this property's existence, however technically this could be dropped to "optional". But, I imagine nearly everyone using the SPMI PMIC arb will also want interrupts.

Quoted 6 lines

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 22

Quoted 1 line
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +1 -0
+- interrupt-controller : indicates the PMIC arbiter is an interrupt controller

Although it's probably fairly understood that this is a boolean, it wouldn't hurt to mention that here. It might also be worth referencing devicetree/binding/interrupt-controller/interrupts.txt in your opening blurb.

Okay.

Quoted 3 lines

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 23

Quoted 1 line
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +1 -0
+- #interrupt-cells = <4>:  interrupts are specified as a 4-tuple:

Nitpick: This '= <X>' differs from the above 'must be set to X' format.

Indeed. Thanks.

Quoted 8 lines

Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt line 28

Quoted 5 lines
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt new file +5 -0
+    cell 1: slave ID for the requested interrupt (0-15)
+    cell 2: peripheral ID for requested interrupt (0-255)
+    cell 3: the requested peripheral interrupt (0-7)
+    cell 4: interrupt flags indicating level-sense information, as defined in
+            dt-bindings/interrupt-controller/irq.h

[...]

-Courtney

Thanks for the comments!

Josh

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
#10 lore raw

PATCH v4 6/6 regmap: spmi: support base and extended register spaces

In reply to Josh Cartwright

SPMI states that a slave may contain two register spaces, the Base register space is a 5-bit byte-addressable space accessed via the Register Read/Write and Register Zero Write command sequences, and the Extended register space: a 16-bit byte-addressable space accessed via the Extended Read/Write and Extended Read/Write Long command sequences.

Provide support for accessing both of these spaces, taking advantage of the more bandwidth-efficient commands ('Register 0 Write' vs 'Register Write', and 'Extended Register Read/Write' vs 'Extended Register Read/Write Long') when possible.

---
 drivers/base/regmap/regmap-spmi.c | 228 ++++++++++++++++++++++++++++++++------
 include/linux/regmap.h            |  12 +-
 2 files changed, 205 insertions(+), 35 deletions(-)
drivers/base/regmap/regmap-spmi.c +197 -31
index ac23910..d7026dc 100644
@@ -22,69 +22,235 @@
 #include <linux/module.h>
 #include <linux/init.h>
 
-static int regmap_spmi_read(void *context,
-			    const void *reg, size_t reg_size,
-			    void *val, size_t val_size)
+static int regmap_spmi_base_read(void *context,
+				 const void *reg, size_t reg_size,
+				 void *val, size_t val_size)
 {
+	u8 addr = *(u8 *)reg;
+	int err = 0;
+
+	BUG_ON(reg_size != 1);
+
+	while (val_size-- && !err)
+		err = spmi_register_read(context, addr++, val++);
+
+	return err;
+}
+
+static int regmap_spmi_base_gather_write(void *context,
+					 const void *reg, size_t reg_size,
+					 const void *val, size_t val_size)
+{
+	const u8 *data = val;
+	u8 addr = *(u8 *)reg;
+	int err = 0;
+
+	BUG_ON(reg_size != 1);
+
+	/*
+	 * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence,
+	 * use it when possible.
+	 */
+	if (addr == 0 && val_size) {
+		err = spmi_register_zero_write(context, *data);
+		if (err)
+			goto err_out;
+
+		data++;
+		addr++;
+		val_size--;
+	}
+
+	while (val_size) {
+		err = spmi_register_write(context, addr, *data);
+		if (err)
+			goto err_out;
+
+		data++;
+		addr++;
+		val_size--;
+	}
+
+err_out:
+	return err;
+}
+
+static int regmap_spmi_base_write(void *context, const void *data,
+				  size_t count)
+{
+	BUG_ON(count < 1);
+	return regmap_spmi_base_gather_write(context, data, 1, data + 1,
+					     count - 1);
+}
+
+static struct regmap_bus regmap_spmi_base = {
+	.read				= regmap_spmi_base_read,
+	.write				= regmap_spmi_base_write,
+	.gather_write			= regmap_spmi_base_gather_write,
+	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
+	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
+};
+
+/**
+ * regmap_init_spmi_base(): Create regmap for the Base register space
+ * @sdev:	SPMI device that will be interacted with
+ * @config:	Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_spmi_base(struct spmi_device *sdev,
+				     const struct regmap_config *config)
+{
+	return regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_spmi_base);
+
+/**
+ * devm_regmap_init_spmi_base(): Create managed regmap for Base register space
+ * @sdev:	SPMI device that will be interacted with
+ * @config:	Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_spmi_base(struct spmi_device *sdev,
+					  const struct regmap_config *config)
+{
+	return devm_regmap_init(&sdev->dev, &regmap_spmi_base, sdev, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base);
+
+static int regmap_spmi_ext_read(void *context,
+				const void *reg, size_t reg_size,
+				void *val, size_t val_size)
+{
+	int err = 0;
+	size_t len;
+	u16 addr;
+
 	BUG_ON(reg_size != 2);
-	return spmi_ext_register_readl(context, *(u16 *)reg,
-				       val, val_size);
+
+	addr = *(u16 *)reg;
+
+	/*
+	 * Split accesses into two to take advantage of the more
+	 * bandwidth-efficient 'Extended Register Read' command when possible
+	 */
+	while (addr <= 0xFF && val_size) {
+		len = min_t(size_t, val_size, 16);
+
+		err = spmi_ext_register_read(context, addr, val, len);
+		if (err)
+			goto err_out;
+
+		addr += len;
+		val += len;
+		val_size -= len;
+	}
+
+	while (val_size) {
+		len = min_t(size_t, val_size, 8);
+
+		err = spmi_ext_register_readl(context, addr, val, val_size);
+		if (err)
+			goto err_out;
+
+		addr += len;
+		val += len;
+		val_size -= len;
+	}
+
+err_out:
+	return err;
 }
 
-static int regmap_spmi_gather_write(void *context,
-				    const void *reg, size_t reg_size,
-				    const void *val, size_t val_size)
+static int regmap_spmi_ext_gather_write(void *context,
+					const void *reg, size_t reg_size,
+					const void *val, size_t val_size)
 {
+	int err = 0;
+	size_t len;
+	u16 addr;
+
 	BUG_ON(reg_size != 2);
-	return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size);
+
+	addr = *(u16 *)reg;
+
+	while (addr <= 0xFF && val_size) {
+		len = min_t(size_t, val_size, 16);
+
+		err = spmi_ext_register_write(context, addr, val, len);
+		if (err)
+			goto err_out;
+
+		addr += len;
+		val += len;
+		val_size -= len;
+	}
+
+	while (val_size) {
+		len = min_t(size_t, val_size, 8);
+
+		err = spmi_ext_register_writel(context, addr, val, len);
+		if (err)
+			goto err_out;
+
+		addr += len;
+		val += len;
+		val_size -= len;
+	}
+
+err_out:
+	return err;
 }
 
-static int regmap_spmi_write(void *context, const void *data,
-			     size_t count)
+static int regmap_spmi_ext_write(void *context, const void *data,
+				 size_t count)
 {
 	BUG_ON(count < 2);
-	return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2);
+	return regmap_spmi_ext_gather_write(context, data, 2, data + 2,
+					    count - 2);
 }
 
-static struct regmap_bus regmap_spmi = {
-	.read				= regmap_spmi_read,
-	.write				= regmap_spmi_write,
-	.gather_write			= regmap_spmi_gather_write,
+static struct regmap_bus regmap_spmi_ext = {
+	.read				= regmap_spmi_ext_read,
+	.write				= regmap_spmi_ext_write,
+	.gather_write			= regmap_spmi_ext_gather_write,
 	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE,
 	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE,
 };
 
 /**
- * regmap_init_spmi(): Initialize register map
- *
- * @sdev: Device that will be interacted with
- * @config: Configuration for register map
+ * regmap_init_spmi_ext(): Create regmap for Ext register space
+ * @sdev:	Device that will be interacted with
+ * @config:	Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer to
  * a struct regmap.
  */
-struct regmap *regmap_init_spmi(struct spmi_device *sdev,
-				const struct regmap_config *config)
+struct regmap *regmap_init_spmi_ext(struct spmi_device *sdev,
+				    const struct regmap_config *config)
 {
-	return regmap_init(&sdev->dev, &regmap_spmi, sdev, config);
+	return regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config);
 }
-EXPORT_SYMBOL_GPL(regmap_init_spmi);
+EXPORT_SYMBOL_GPL(regmap_init_spmi_ext);
 
 /**
- * devm_regmap_init_spmi(): Initialise managed register map
- *
- * @sdev: Device that will be interacted with
- * @config: Configuration for register map
+ * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space
+ * @sdev:	SPMI device that will be interacted with
+ * @config:	Configuration for register map
  *
  * The return value will be an ERR_PTR() on error or a valid pointer
  * to a struct regmap.  The regmap will be automatically freed by the
  * device management code.
  */
-struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev,
+struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *sdev,
 				     const struct regmap_config *config)
 {
-	return devm_regmap_init(&sdev->dev, &regmap_spmi, sdev, config);
+	return devm_regmap_init(&sdev->dev, &regmap_spmi_ext, sdev, config);
 }
-EXPORT_SYMBOL_GPL(devm_regmap_init_spmi);
+EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext);
 
 MODULE_LICENSE("GPL");
include/linux/regmap.h +8 -4
index e559078..847e7e6 100644
@@ -321,8 +321,10 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
 			       const struct regmap_config *config);
 struct regmap *regmap_init_spi(struct spi_device *dev,
 			       const struct regmap_config *config);
-struct regmap *regmap_init_spmi(struct spmi_device *dev,
-			       const struct regmap_config *config);
+struct regmap *regmap_init_spmi_base(struct spmi_device *dev,
+				     const struct regmap_config *config);
+struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
+				    const struct regmap_config *config);
 struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 				    void __iomem *regs,
 				    const struct regmap_config *config);
@@ -335,8 +337,10 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
 				    const struct regmap_config *config);
 struct regmap *devm_regmap_init_spi(struct spi_device *dev,
 				    const struct regmap_config *config);
-struct regmap *devm_regmap_init_spmi(struct spmi_device *dev,
-				     const struct regmap_config *config);
+struct regmap *devm_regmap_init_spmi_base(struct spmi_device *dev,
+					  const struct regmap_config *config);
+struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
+					 const struct regmap_config *config);
 struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
 					 void __iomem *regs,
 					 const struct regmap_config *config);
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
maintainer Acked-by on v4 6/6
#11 lore raw

Re: [PATCH v4 6/6] regmap: spmi: support base and extended register spaces

Quoted 3 lines · Josh Cartwright

SPMI states that a slave may contain two register spaces, the Base register space is a 5-bit byte-addressable space accessed via the Register Read/Write and Register Zero Write command sequences, and the

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140115/5c25fed0/attachment.sig>