]>
Commit | Line | Data |
---|---|---|
5b47271c MK |
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); | |
e160f7d4 | 150 | int node = dev_of_offset(dev); |
5b47271c MK |
151 | int i; |
152 | ||
a821c4af | 153 | priv->arb_chnl = devfdt_get_addr(dev); |
5b47271c | 154 | priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, |
e160f7d4 | 155 | dev_of_offset(parent), node, "reg", 1, NULL, false); |
5b47271c | 156 | priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, |
e160f7d4 | 157 | dev_of_offset(parent), node, "reg", 2, NULL, false); |
5b47271c MK |
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 | }; |