[U-Boot] [PATCH v1 12/16] drivers: spmi: Add support for Qualcomm SPMI bus driver

Simon Glass
2016-01-11T16:57:58Z

On 6 January 2016 at 11:21, Mateusz Kulikowski
<mateusz.kulikowski@gmail.com> wrote:
> Support SPMI arbiter on Qualcomm Snapdragon devices.
>
> Signed-off-by: Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
> ---
>
> Changes in v1:
> - add binding documentation and better Kconfig help
> - Changed a bit mapping
> - Change include order
> - Use clrsetbits* where possible
> - Add one more supported dts id
> - Handle missing fields in dt properly
>
> doc/device-tree-bindings/spmi/spmi-msm.txt | 26 ++++
> drivers/spmi/Kconfig | 7 +-
> drivers/spmi/Makefile | 1 +
> drivers/spmi/spmi-msm.c | 188 +++++++++++++++++++++++++++++
> 4 files changed, 221 insertions(+), 1 deletion(-)
> create mode 100644 doc/device-tree-bindings/spmi/spmi-msm.txt
> create mode 100644 drivers/spmi/spmi-msm.c
>

Reviewed-by: Simon Glass <sjg@chromium.org>
nits below

> diff --git a/doc/device-tree-bindings/spmi/spmi-msm.txt b/doc/device-tree-bindings/spmi/spmi-msm.txt
> new file mode 100644
> index 0000000..ae47673
> --- /dev/null
> +++ b/doc/device-tree-bindings/spmi/spmi-msm.txt
> @@ -0,0 +1,26 @@
> +Qualcomm SPMI arbiter/bus driver
> +
> +This is bus driver for Qualcomm chips that use SPMI to communicate with PMICs.
> +
> +Required properties:
> +- compatible: "qcom,spmi-pmic-arb"
> +- reg: Register block adresses and sizes for various parts of device:
> + 1) PMIC arbiter channel mapping base (PMIC_ARB_REG_CHNLn)
> + 2) SPMI write command (master) registers (PMIC_ARB_CORE_SW_DEC_CHANNELS)
> + 3) SPMI read command (observer) registers (PMIC_ARB_CORE_REGISTERS_OBS)
> +
> +Optional properties (if not set by parent):
> +- #address-cells: 0x1 - childs slave ID address
> +- #size-cells: 0x1
> +
> +All PMICs should be placed as a child nodes of bus arbiter.
> +Automatic detection of childs is currently not supported.
> +
> +Example:
> +
> +spmi at 200f000 {
> + compatible = "qcom,spmi-pmic-arb";
> + reg = <0x200f800 0x200 0x2400000 0x400000 0x2c00000 0x400000>;
> + #address-cells = <0x1>;
> + #size-cells = <0x1>;
> +};
> diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig
> index e3e8216..9632b14 100644
> --- a/drivers/spmi/Kconfig
> +++ b/drivers/spmi/Kconfig
> @@ -8,11 +8,16 @@ config DM_SPMI
> SPMI (System Power Management Interface) bus is used
> to connect PMIC devices on various SoCs.
>
> +config SPMI_MSM
> + boolean "Support Qualcomm SPMI bus"
> + depends on DM_SPMI
> + ---help---
> + Support SPMI bus implementation found on Qualcomm Snapdragon SoCs.
> +
> config SPMI_SANDBOX
> boolean "Support for Sandbox SPMI bus"
> depends on DM_SPMI
> ---help---
> Demo SPMI bus implementation. Emulates part of PM8916 as single
> slave (0) on bus. It has 4 GPIO peripherals, pid 0xC0-0xC3.
> -
> endmenu
> diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile
> index 035d466..caad96f 100644
> --- a/drivers/spmi/Makefile
> +++ b/drivers/spmi/Makefile
> @@ -5,4 +5,5 @@
> #
>
> obj-$(CONFIG_DM_SPMI) += spmi-uclass.o
> +obj-$(CONFIG_SPMI_MSM) += spmi-msm.o
> obj-$(CONFIG_SPMI_SANDBOX) += spmi-sandbox.o
> diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c
> new file mode 100644
> index 0000000..c1e7a3d
> --- /dev/null
> +++ b/drivers/spmi/spmi-msm.c
> @@ -0,0 +1,188 @@
> +/*
> + * Qualcomm SPMI bus driver
> + *
> + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
> + *
> + * Loosely based on Little Kernel driver
> + *
> + * SPDX-License-Identifier: BSD-3-Clause
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <fdtdec.h>
> +#include <spmi/spmi.h>
> +#include <asm/io.h>

swap those two lines

> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define ARB_CHANNEL_OFFSET(n) (0x4 * (n))
> +#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000)
> +
> +#define SPMI_REG_CMD0 0x0
> +#define SPMI_REG_CONFIG 0x4
> +#define SPMI_REG_STATUS 0x8
> +#define SPMI_REG_WDATA 0x10
> +#define SPMI_REG_RDATA 0x18
> +
> +#define SPMI_CMD_OPCODE_SHIFT 27
> +#define SPMI_CMD_SLAVE_ID_SHIFT 20
> +#define SPMI_CMD_ADDR_SHIFT 12
> +#define SPMI_CMD_ADDR_OFFSET_SHIFT 4
> +#define SPMI_CMD_BYTE_CNT_SHIFT 0
> +
> +#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
> +#define SPMI_CMD_EXT_REG_READ_LONG 0x01
> +
> +#define SPMI_STATUS_DONE 0x1
> +
> +#define SPMI_MAX_CHANNELS 128
> +#define SPMI_MAX_SLAVES 16
> +#define SPMI_MAX_PERIPH 256
> +
> +struct msm_spmi_priv {
> + phys_addr_t arb_chnl; /* ARB channel mapping base */
> + phys_addr_t spmi_core; /* SPMI core */
> + phys_addr_t spmi_obs; /* SPMI observer */
> + /* SPMI channel map */
> + uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
> +};
> +
> +static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
> + uint8_t val)
> +{
> + struct msm_spmi_priv *p = dev_get_priv(dev);
> + unsigned channel;
> + uint32_t reg = 0;
> +
> + if (usid >= SPMI_MAX_SLAVES)
> + return -EIO;
> + if (pid >= SPMI_MAX_PERIPH)
> + return -EIO;
> +
> + channel = p->channel_map[usid][pid];
> +
> + /* Disable IRQ mode for the current channel*/
> + writel(0x0, p->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
> +
> + /* Write single byte */
> + writel(val, p->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
> +
> + /* Prepare write command */
> + reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
> + reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
> + reg |= (pid << SPMI_CMD_ADDR_SHIFT);
> + reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
> + reg |= 1; /* byte count */
> +
> + /* Send write command */
> + writel(reg, p->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
> +
> + /* Wait till CMD DONE status */
> + reg = 0;
> + while (!reg) {
> + reg = readl(p->spmi_core + SPMI_CH_OFFSET(channel) +
> + SPMI_REG_STATUS);
> + }
> +
> + if (reg ^ SPMI_STATUS_DONE) {
> + printf("SPMI write failure.\n");
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
> +{
> + struct msm_spmi_priv *p = dev_get_priv(dev);

s/p/priv/

> + unsigned channel;
> + uint32_t reg = 0;
> +
> + if (usid >= SPMI_MAX_SLAVES)
> + return -EIO;
> + if (pid >= SPMI_MAX_PERIPH)
> + return -EIO;
> +
> + channel = p->channel_map[usid][pid];
> +
> + /* Disable IRQ mode for the current channel*/
> + writel(0x0, p->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
> +
> + /* Prepare read command */
> + reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
> + reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
> + reg |= (pid << SPMI_CMD_ADDR_SHIFT);
> + reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
> + reg |= 1; /* byte count */
> +
> + /* Request read */
> + writel(reg, p->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
> +
> + /* Wait till CMD DONE status */
> + reg = 0;
> + while (!reg) {
> + reg = readl(p->spmi_obs + SPMI_CH_OFFSET(channel) +
> + SPMI_REG_STATUS);
> + }
> +
> + if (reg ^ SPMI_STATUS_DONE) {
> + printf("SPMI read failure.\n");
> + return -EIO;
> + }
> +
> + /* Read the data */
> + return readl(p->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_RDATA) &
> + 0xFF;
> +}
> +
> +static struct dm_spmi_ops msm_spmi_ops = {
> + .read = msm_spmi_read,
> + .write = msm_spmi_write,
> +};
> +
> +static int msm_spmi_probe(struct udevice *dev)
> +{
> + struct udevice *parent = dev->parent;
> + struct msm_spmi_priv *priv = dev_get_priv(dev);
> + int i;
> +
> + priv->arb_chnl = dev_get_addr(dev);
> + priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
> + parent->of_offset,
> + dev->of_offset,
> + "reg", 1, NULL);
> + priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
> + parent->of_offset,
> + dev->of_offset, "reg",
> + 2, NULL);
> + if (priv->arb_chnl == FDT_ADDR_T_NONE ||
> + priv->spmi_core == FDT_ADDR_T_NONE ||
> + priv->spmi_obs == FDT_ADDR_T_NONE)
> + return -EINVAL;
> +
> + /* Scan peripherals connected to each SPMI channel */
> + for (i = 0; i < SPMI_MAX_CHANNELS ; i++) {

BTW index variables like 'i' are fine as single character.

> + uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
> + uint8_t slave_id = (periph & 0xf0000) >> 16;
> + uint8_t pid = (periph & 0xff00) >> 8;
> +
> + priv->channel_map[slave_id][pid] = i;
> + }
> + return 0;
> +}
> +
> +static const struct udevice_id msm_spmi_ids[] = {
> + { .compatible = "qcom,spmi-pmic-arb" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(msm_spmi) = {
> + .name = "msm_spmi",
> + .id = UCLASS_SPMI,
> + .of_match = msm_spmi_ids,
> + .ops = &msm_spmi_ops,
> + .probe = msm_spmi_probe,
> + .priv_auto_alloc_size = sizeof(struct msm_spmi_priv),
> +};
> --
> 2.5.0
>

Regards,
Simon