]> git.ipfire.org Git - people/ms/u-boot.git/blob - drivers/spmi/spmi-msm.c
Merge git://git.denx.de/u-boot-dm
[people/ms/u-boot.git] / drivers / spmi / spmi-msm.c
1 /*
2 * Qualcomm SPMI bus driver
3 *
4 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
5 *
6 * Loosely based on Little Kernel driver
7 *
8 * SPDX-License-Identifier: BSD-3-Clause
9 */
10
11 #include <common.h>
12 #include <dm.h>
13 #include <errno.h>
14 #include <fdtdec.h>
15 #include <asm/io.h>
16 #include <spmi/spmi.h>
17
18 DECLARE_GLOBAL_DATA_PTR;
19
20 #define ARB_CHANNEL_OFFSET(n) (0x4 * (n))
21 #define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000)
22
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
28
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
34
35 #define SPMI_CMD_EXT_REG_WRITE_LONG 0x00
36 #define SPMI_CMD_EXT_REG_READ_LONG 0x01
37
38 #define SPMI_STATUS_DONE 0x1
39
40 #define SPMI_MAX_CHANNELS 128
41 #define SPMI_MAX_SLAVES 16
42 #define SPMI_MAX_PERIPH 256
43
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];
50 };
51
52 static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
53 uint8_t val)
54 {
55 struct msm_spmi_priv *priv = dev_get_priv(dev);
56 unsigned channel;
57 uint32_t reg = 0;
58
59 if (usid >= SPMI_MAX_SLAVES)
60 return -EIO;
61 if (pid >= SPMI_MAX_PERIPH)
62 return -EIO;
63
64 channel = priv->channel_map[usid][pid];
65
66 /* Disable IRQ mode for the current channel*/
67 writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) +
68 SPMI_REG_CONFIG);
69
70 /* Write single byte */
71 writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
72
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 */
79
80 /* Send write command */
81 writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
82
83 /* Wait till CMD DONE status */
84 reg = 0;
85 while (!reg) {
86 reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) +
87 SPMI_REG_STATUS);
88 }
89
90 if (reg ^ SPMI_STATUS_DONE) {
91 printf("SPMI write failure.\n");
92 return -EIO;
93 }
94
95 return 0;
96 }
97
98 static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
99 {
100 struct msm_spmi_priv *priv = dev_get_priv(dev);
101 unsigned channel;
102 uint32_t reg = 0;
103
104 if (usid >= SPMI_MAX_SLAVES)
105 return -EIO;
106 if (pid >= SPMI_MAX_PERIPH)
107 return -EIO;
108
109 channel = priv->channel_map[usid][pid];
110
111 /* Disable IRQ mode for the current channel*/
112 writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
113
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 */
120
121 /* Request read */
122 writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
123
124 /* Wait till CMD DONE status */
125 reg = 0;
126 while (!reg) {
127 reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
128 SPMI_REG_STATUS);
129 }
130
131 if (reg ^ SPMI_STATUS_DONE) {
132 printf("SPMI read failure.\n");
133 return -EIO;
134 }
135
136 /* Read the data */
137 return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
138 SPMI_REG_RDATA) & 0xFF;
139 }
140
141 static struct dm_spmi_ops msm_spmi_ops = {
142 .read = msm_spmi_read,
143 .write = msm_spmi_write,
144 };
145
146 static int msm_spmi_probe(struct udevice *dev)
147 {
148 struct udevice *parent = dev->parent;
149 struct msm_spmi_priv *priv = dev_get_priv(dev);
150 int node = dev_of_offset(dev);
151 int i;
152
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)
161 return -EINVAL;
162
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;
168
169 priv->channel_map[slave_id][pid] = i;
170 }
171 return 0;
172 }
173
174 static const struct udevice_id msm_spmi_ids[] = {
175 { .compatible = "qcom,spmi-pmic-arb" },
176 { }
177 };
178
179 U_BOOT_DRIVER(msm_spmi) = {
180 .name = "msm_spmi",
181 .id = UCLASS_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),
186 };