2 * Qualcomm SPMI bus driver
4 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 * Loosely based on Little Kernel driver
8 * SPDX-License-Identifier: BSD-3-Clause
16 #include <spmi/spmi.h>
18 DECLARE_GLOBAL_DATA_PTR;
20 #define ARB_CHANNEL_OFFSET(n) (0x4 * (n))
21 #define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000)
23 #define SPMI_REG_CMD0 0x0
24 #define SPMI_REG_CONFIG 0x4
25 #define SPMI_REG_STATUS 0x8
26 #define SPMI_REG_WDATA 0x10
27 #define SPMI_REG_RDATA 0x18
29 #define SPMI_CMD_OPCODE_SHIFT 27
30 #define SPMI_CMD_SLAVE_ID_SHIFT 20
31 #define SPMI_CMD_ADDR_SHIFT 12
32 #define SPMI_CMD_ADDR_OFFSET_SHIFT 4
33 #define SPMI_CMD_BYTE_CNT_SHIFT 0
35 #define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
36 #define SPMI_CMD_EXT_REG_READ_LONG 0x01
38 #define SPMI_STATUS_DONE 0x1
40 #define SPMI_MAX_CHANNELS 128
41 #define SPMI_MAX_SLAVES 16
42 #define SPMI_MAX_PERIPH 256
44 struct msm_spmi_priv {
45 phys_addr_t arb_chnl; /* ARB channel mapping base */
46 phys_addr_t spmi_core; /* SPMI core */
47 phys_addr_t spmi_obs; /* SPMI observer */
48 /* SPMI channel map */
49 uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
52 static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
55 struct msm_spmi_priv *priv = dev_get_priv(dev);
59 if (usid >= SPMI_MAX_SLAVES)
61 if (pid >= SPMI_MAX_PERIPH)
64 channel = priv->channel_map[usid][pid];
66 /* Disable IRQ mode for the current channel*/
67 writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) +
70 /* Write single byte */
71 writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
73 /* Prepare write command */
74 reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
75 reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
76 reg |= (pid << SPMI_CMD_ADDR_SHIFT);
77 reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
78 reg |= 1; /* byte count */
80 /* Send write command */
81 writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
83 /* Wait till CMD DONE status */
86 reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) +
90 if (reg ^ SPMI_STATUS_DONE) {
91 printf("SPMI write failure.\n");
98 static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
100 struct msm_spmi_priv *priv = dev_get_priv(dev);
104 if (usid >= SPMI_MAX_SLAVES)
106 if (pid >= SPMI_MAX_PERIPH)
109 channel = priv->channel_map[usid][pid];
111 /* Disable IRQ mode for the current channel*/
112 writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
114 /* Prepare read command */
115 reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
116 reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
117 reg |= (pid << SPMI_CMD_ADDR_SHIFT);
118 reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
119 reg |= 1; /* byte count */
122 writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
124 /* Wait till CMD DONE status */
127 reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
131 if (reg ^ SPMI_STATUS_DONE) {
132 printf("SPMI read failure.\n");
137 return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
138 SPMI_REG_RDATA) & 0xFF;
141 static struct dm_spmi_ops msm_spmi_ops = {
142 .read = msm_spmi_read,
143 .write = msm_spmi_write,
146 static int msm_spmi_probe(struct udevice *dev)
148 struct udevice *parent = dev->parent;
149 struct msm_spmi_priv *priv = dev_get_priv(dev);
150 int node = dev_of_offset(dev);
153 priv->arb_chnl = devfdt_get_addr(dev);
154 priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
155 dev_of_offset(parent), node, "reg", 1, NULL, false);
156 priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
157 dev_of_offset(parent), node, "reg", 2, NULL, false);
158 if (priv->arb_chnl == FDT_ADDR_T_NONE ||
159 priv->spmi_core == FDT_ADDR_T_NONE ||
160 priv->spmi_obs == FDT_ADDR_T_NONE)
163 /* Scan peripherals connected to each SPMI channel */
164 for (i = 0; i < SPMI_MAX_CHANNELS ; i++) {
165 uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
166 uint8_t slave_id = (periph & 0xf0000) >> 16;
167 uint8_t pid = (periph & 0xff00) >> 8;
169 priv->channel_map[slave_id][pid] = i;
174 static const struct udevice_id msm_spmi_ids[] = {
175 { .compatible = "qcom,spmi-pmic-arb" },
179 U_BOOT_DRIVER(msm_spmi) = {
182 .of_match = msm_spmi_ids,
183 .ops = &msm_spmi_ops,
184 .probe = msm_spmi_probe,
185 .priv_auto_alloc_size = sizeof(struct msm_spmi_priv),