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

Simon Glass sjg at chromium.org
Mon Jan 11 17:57:58 CET 2016


On 6 January 2016 at 11:21, Mateusz Kulikowski
<mateusz.kulikowski at gmail.com> wrote:
> Support SPMI arbiter on Qualcomm Snapdragon devices.
>
> Signed-off-by: Mateusz Kulikowski <mateusz.kulikowski at 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 at 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 at 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


More information about the U-Boot mailing list