xref: /openbmc/u-boot/drivers/spmi/spmi-msm.c (revision 1d2c0506)
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 i;
151 
152 	priv->arb_chnl = dev_get_addr(dev);
153 	priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
154 							   parent->of_offset,
155 							   dev->of_offset,
156 							   "reg", 1, NULL,
157 							   false);
158 	priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
159 							  parent->of_offset,
160 							  dev->of_offset, "reg",
161 							  2, NULL, false);
162 	if (priv->arb_chnl == FDT_ADDR_T_NONE ||
163 	    priv->spmi_core == FDT_ADDR_T_NONE ||
164 	    priv->spmi_obs == FDT_ADDR_T_NONE)
165 		return -EINVAL;
166 
167 	/* Scan peripherals connected to each SPMI channel */
168 	for (i = 0; i < SPMI_MAX_CHANNELS ; i++) {
169 		uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
170 		uint8_t slave_id = (periph & 0xf0000) >> 16;
171 		uint8_t pid = (periph & 0xff00) >> 8;
172 
173 		priv->channel_map[slave_id][pid] = i;
174 	}
175 	return 0;
176 }
177 
178 static const struct udevice_id msm_spmi_ids[] = {
179 	{ .compatible = "qcom,spmi-pmic-arb" },
180 	{ }
181 };
182 
183 U_BOOT_DRIVER(msm_spmi) = {
184 	.name = "msm_spmi",
185 	.id = UCLASS_SPMI,
186 	.of_match = msm_spmi_ids,
187 	.ops = &msm_spmi_ops,
188 	.probe = msm_spmi_probe,
189 	.priv_auto_alloc_size = sizeof(struct msm_spmi_priv),
190 };
191