]> git.sur5r.net Git - u-boot/blob - drivers/spmi/spmi-msm.c
Merge git://git.denx.de/u-boot-fsl-qoriq
[u-boot] / 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 /* PMIC Arbiter configuration registers */
21 #define PMIC_ARB_VERSION                0x0000
22 #define PMIC_ARB_VERSION_V2_MIN         0x20010000
23
24 #define ARB_CHANNEL_OFFSET(n)           (0x4 * (n))
25 #define SPMI_CH_OFFSET(chnl)            ((chnl) * 0x8000)
26
27 #define SPMI_REG_CMD0                   0x0
28 #define SPMI_REG_CONFIG                 0x4
29 #define SPMI_REG_STATUS                 0x8
30 #define SPMI_REG_WDATA                  0x10
31 #define SPMI_REG_RDATA                  0x18
32
33 #define SPMI_CMD_OPCODE_SHIFT           27
34 #define SPMI_CMD_SLAVE_ID_SHIFT         20
35 #define SPMI_CMD_ADDR_SHIFT             12
36 #define SPMI_CMD_ADDR_OFFSET_SHIFT      4
37 #define SPMI_CMD_BYTE_CNT_SHIFT         0
38
39 #define SPMI_CMD_EXT_REG_WRITE_LONG     0x00
40 #define SPMI_CMD_EXT_REG_READ_LONG      0x01
41
42 #define SPMI_STATUS_DONE                0x1
43
44 #define SPMI_MAX_CHANNELS       128
45 #define SPMI_MAX_SLAVES         16
46 #define SPMI_MAX_PERIPH         256
47
48 struct msm_spmi_priv {
49         phys_addr_t arb_chnl; /* ARB channel mapping base */
50         phys_addr_t spmi_core; /* SPMI core */
51         phys_addr_t spmi_obs; /* SPMI observer */
52         /* SPMI channel map */
53         uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
54 };
55
56 static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
57                           uint8_t val)
58 {
59         struct msm_spmi_priv *priv = dev_get_priv(dev);
60         unsigned channel;
61         uint32_t reg = 0;
62
63         if (usid >= SPMI_MAX_SLAVES)
64                 return -EIO;
65         if (pid >= SPMI_MAX_PERIPH)
66                 return -EIO;
67
68         channel = priv->channel_map[usid][pid];
69
70         /* Disable IRQ mode for the current channel*/
71         writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) +
72                SPMI_REG_CONFIG);
73
74         /* Write single byte */
75         writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
76
77         /* Prepare write command */
78         reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
79         reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
80         reg |= (pid << SPMI_CMD_ADDR_SHIFT);
81         reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
82         reg |= 1; /* byte count */
83
84         /* Send write command */
85         writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
86
87         /* Wait till CMD DONE status */
88         reg = 0;
89         while (!reg) {
90                 reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) +
91                             SPMI_REG_STATUS);
92         }
93
94         if (reg ^ SPMI_STATUS_DONE) {
95                 printf("SPMI write failure.\n");
96                 return -EIO;
97         }
98
99         return 0;
100 }
101
102 static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
103 {
104         struct msm_spmi_priv *priv = dev_get_priv(dev);
105         unsigned channel;
106         uint32_t reg = 0;
107
108         if (usid >= SPMI_MAX_SLAVES)
109                 return -EIO;
110         if (pid >= SPMI_MAX_PERIPH)
111                 return -EIO;
112
113         channel = priv->channel_map[usid][pid];
114
115         /* Disable IRQ mode for the current channel*/
116         writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
117
118         /* Prepare read command */
119         reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
120         reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
121         reg |= (pid << SPMI_CMD_ADDR_SHIFT);
122         reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
123         reg |= 1; /* byte count */
124
125         /* Request read */
126         writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
127
128         /* Wait till CMD DONE status */
129         reg = 0;
130         while (!reg) {
131                 reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
132                             SPMI_REG_STATUS);
133         }
134
135         if (reg ^ SPMI_STATUS_DONE) {
136                 printf("SPMI read failure.\n");
137                 return -EIO;
138         }
139
140         /* Read the data */
141         return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
142                      SPMI_REG_RDATA) & 0xFF;
143 }
144
145 static struct dm_spmi_ops msm_spmi_ops = {
146         .read = msm_spmi_read,
147         .write = msm_spmi_write,
148 };
149
150 static int msm_spmi_probe(struct udevice *dev)
151 {
152         struct udevice *parent = dev->parent;
153         struct msm_spmi_priv *priv = dev_get_priv(dev);
154         int node = dev_of_offset(dev);
155         u32 hw_ver;
156         bool is_v1;
157         int i;
158
159         priv->arb_chnl = devfdt_get_addr(dev);
160         priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
161                         dev_of_offset(parent), node, "reg", 1, NULL, false);
162         priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
163                         dev_of_offset(parent), node, "reg", 2, NULL, false);
164
165         hw_ver = readl(priv->arb_chnl + PMIC_ARB_VERSION - 0x800);
166         is_v1  = (hw_ver < PMIC_ARB_VERSION_V2_MIN);
167
168         dev_dbg(dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2), hw_ver);
169
170         if (priv->arb_chnl == FDT_ADDR_T_NONE ||
171             priv->spmi_core == FDT_ADDR_T_NONE ||
172             priv->spmi_obs == FDT_ADDR_T_NONE)
173                 return -EINVAL;
174
175         /* Scan peripherals connected to each SPMI channel */
176         for (i = 0; i < SPMI_MAX_PERIPH ; i++) {
177                 uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
178                 uint8_t slave_id = (periph & 0xf0000) >> 16;
179                 uint8_t pid = (periph & 0xff00) >> 8;
180
181                 priv->channel_map[slave_id][pid] = i;
182         }
183         return 0;
184 }
185
186 static const struct udevice_id msm_spmi_ids[] = {
187         { .compatible = "qcom,spmi-pmic-arb" },
188         { }
189 };
190
191 U_BOOT_DRIVER(msm_spmi) = {
192         .name = "msm_spmi",
193         .id = UCLASS_SPMI,
194         .of_match = msm_spmi_ids,
195         .ops = &msm_spmi_ops,
196         .probe = msm_spmi_probe,
197         .priv_auto_alloc_size = sizeof(struct msm_spmi_priv),
198 };