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
),