blob: 6023213daa6fa2f42546562e3b7cb70a1622929c [file] [log] [blame]
Duy Truongf3ac7b32013-02-13 01:07:28 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Deepa Dinamanic2a9b362012-02-23 15:15:54 -08002 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
Duy Truongf3ac7b32013-02-13 01:07:28 -080012 * * Neither the name of The Linux Foundation nor the names of its
Deepa Dinamanic2a9b362012-02-23 15:15:54 -080013 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <debug.h>
30#include <reg.h>
31#include <spmi.h>
32#include <platform/iomap.h>
33#include <platform/irqs.h>
34#include <platform/interrupts.h>
35
36static uint32_t pmic_arb_chnl_num;
37static uint32_t pmic_arb_owner_id;
38static uint8_t pmic_irq_perph_id;
39static spmi_callback callback;
40
41/* Function to initialize SPMI controller.
42 * chnl_num : Channel number to be used by this EE.
43 */
44void spmi_init(uint32_t chnl_num, uint32_t owner_id)
45{
46 /* Initialize PMIC Arbiter Channel Number */
47 pmic_arb_chnl_num = chnl_num;
48 pmic_arb_owner_id = owner_id;
49}
50
51static void write_wdata_from_array(uint8_t *array,
52 uint8_t reg_num,
53 uint8_t array_size,
54 uint8_t* bytes_written)
55{
56 uint32_t shift_value[] = {0, 8, 16, 24};
57 int i;
58 int j;
59 uint32_t val = 0;
60
61 /* Write to WDATA */
62 for (i = 0; (*bytes_written < array_size) && (i < 4); i++)
63 {
64 val |= (uint32_t)(array[*bytes_written]) << shift_value[i];
65 (*bytes_written)++;
66 }
67
68 writel(val, PMIC_ARB_CHNLn_WDATA(pmic_arb_chnl_num, reg_num));
69}
70
71
72/* Initiate a write cmd by writing to cmd register.
73 * Commands are written according to cmd parameters
74 * cmd->opcode : SPMI opcode for the command
75 * cmd->priority : Priority of the command
76 * High priority : 1
77 * Low Priority : 0
78 * cmd->address : SPMI Peripheral Address.
79 * cmd->offset : Offset Address for the command.
80 * cmd->bytecnt : Number of bytes to be written.
81 *
82 * param is the parameter to the command
83 * param->buffer : Value to be written
84 * param->size : Size of the buffer.
85 *
86 * return value : 0 if success, the error bit set on error
87 */
88unsigned int pmic_arb_write_cmd(struct pmic_arb_cmd *cmd,
89 struct pmic_arb_param *param)
90{
91 uint32_t bytes_written = 0;
92 uint32_t error;
93 uint32_t val = 0;
94
95 /* Disable IRQ mode for the current channel*/
96 writel(0x0, PMIC_ARB_CHNLn_CONFIG(pmic_arb_chnl_num));
97
98 /* Write parameters for the cmd */
99 if (cmd == NULL)
100 {
101 dprintf(CRITICAL,"PMIC arbiter error, no command provided\n");
102 return 1;
103 }
104
105 /* Write the data bytes according to the param->size
106 * Can write upto 8 bytes.
107 */
108
109 /* Write first 4 bytes to WDATA0 */
110 write_wdata_from_array(param->buffer, 0, param->size, &bytes_written);
111
112 if (bytes_written < param->size)
113 {
114 /* Write next 4 bytes to WDATA1 */
115 write_wdata_from_array(param->buffer, 1, param->size, &bytes_written);
116 }
117
118 /* Fill in the byte count for the command
119 * Note: Byte count is one less than the number of bytes transferred.
120 */
121 cmd->byte_cnt = param->size - 1;
122 /* Fill in the Write cmd opcode. */
123 cmd->opcode = SPMI_CMD_EXT_REG_WRTIE_LONG;
124
125 /* Write the command */
126 val = 0;
127 val |= ((uint32_t)(cmd->opcode) << PMIC_ARB_CMD_OPCODE_SHIFT);
128 val |= ((uint32_t)(cmd->priority) << PMIC_ARB_CMD_PRIORITY_SHIFT);
129 val |= ((uint32_t)(cmd->slave_id) << PMIC_ARB_CMD_SLAVE_ID_SHIFT);
130 val |= ((uint32_t)(cmd->address) << PMIC_ARB_CMD_ADDR_SHIFT);
131 val |= ((uint32_t)(cmd->offset) << PMIC_ARB_CMD_ADDR_OFFSET_SHIFT);
132 val |= ((uint32_t)(cmd->byte_cnt));
133
134 writel(val, PMIC_ARB_CHNLn_CMD0(pmic_arb_chnl_num));
135
136 /* Wait till CMD DONE status */
137 while (!(val = readl(PMIC_ARB_CHNLn_STATUS(pmic_arb_chnl_num))));
138
139 /* Check for errors */
140 error = val ^ (1 << PMIC_ARB_CMD_DONE);
141 if (error)
142 {
143 dprintf(CRITICAL, "SPMI write command failure: \
144 cmd_id = %u, error = %u\n", cmd->opcode, error);
145 return error;
146 }
147 else
148 return 0;
149}
150
151static void read_rdata_into_array(uint8_t *array,
152 uint8_t reg_num,
153 uint8_t array_size,
154 uint8_t* bytes_read)
155{
156 uint32_t val = 0;
157 uint32_t mask_value[] = {0xFF, 0xFF00, 0xFF0000, 0xFF000000};
158 uint8_t shift_value[] = {0, 8, 16, 24};
159 int i;
160
161 val = readl(PMIC_ARB_CHNLn_RDATA(pmic_arb_chnl_num, reg_num));
162
163 /* Read at most 4 bytes */
164 for (i = 0; (i < 4) && (*bytes_read < array_size); i++)
165 {
166 array[*bytes_read] = (val & mask_value[i]) >> shift_value[i];
167 (*bytes_read)++;
168 }
169}
170
171/* Initiate a read cmd by writing to cmd register.
172 * Commands are written according to cmd parameters
173 * cmd->opcode : SPMI opcode for the command
174 * cmd->priority : Priority of the command
175 * High priority : 1
176 * Low Priority : 0
177 * cmd->address : SPMI Peripheral Address.
178 * cmd->offset : Offset Address for the command.
179 * cmd->bytecnt : Number of bytes to be read.
180 *
181 * param is the buffer to the save command data.
182 * param->buffer : Buffer to store the bytes returned.
183 * param->size : Size of the buffer.
184 *
185 * return value : 0 if success, the error bit set on error
186 */
187unsigned int pmic_arb_read_cmd(struct pmic_arb_cmd *cmd,
188 struct pmic_arb_param *param)
189{
190 uint32_t val = 0;
191 uint32_t error;
192 uint32_t addr;
193 uint8_t bytes_read = 0;
194
195 /* Disable IRQ mode for the current channel*/
196 writel(0x0, PMIC_ARB_CHNLn_CONFIG(pmic_arb_chnl_num));
197
198 /* Fill in the byte count for the command
199 * Note: Byte count is one less than the number of bytes transferred.
200 */
201 cmd->byte_cnt = param->size - 1;
202 /* Fill in the Write cmd opcode. */
203 cmd->opcode = SPMI_CMD_EXT_REG_READ_LONG;
204
205 val |= ((uint32_t)(cmd->opcode) << PMIC_ARB_CMD_OPCODE_SHIFT);
206 val |= ((uint32_t)(cmd->priority) << PMIC_ARB_CMD_PRIORITY_SHIFT);
207 val |= ((uint32_t)(cmd->slave_id) << PMIC_ARB_CMD_SLAVE_ID_SHIFT);
208 val |= ((uint32_t)(cmd->address) << PMIC_ARB_CMD_ADDR_SHIFT);
209 val |= ((uint32_t)(cmd->offset) << PMIC_ARB_CMD_ADDR_OFFSET_SHIFT);
210 val |= ((uint32_t)(cmd->byte_cnt));
211
212 writel(val, PMIC_ARB_CHNLn_CMD0(pmic_arb_chnl_num));
213
214 /* Wait till CMD DONE status */
215 while (!(val = readl(PMIC_ARB_CHNLn_STATUS(pmic_arb_chnl_num))));
216
217 /* Check for errors */
218 error = val ^ (1 << PMIC_ARB_CMD_DONE);
219
220 if (error)
221 {
222 dprintf(CRITICAL, "SPMI read command failure: \
223 cmd_id = %u, error = %u\n", cmd->opcode, error);
224 return error;
225 }
226
227 /* Read the RDATA0 */
228 read_rdata_into_array(param->buffer, 0, param->size , &bytes_read);
229
230 if (bytes_read < param->size)
231 {
232 /* Read the RDATA1 */
233 read_rdata_into_array(param->buffer, 1, param->size , &bytes_read);
234
235 }
236
237 if (bytes_read < param->size)
238 {
239 /* Read the RDATA2 */
240 read_rdata_into_array(param->buffer, 2, param->size , &bytes_read);
241
242 }
243
244 return 0;
245}
246
247
248/* Funtion to determine if the peripheral that caused the interrupt
249 * is of interest.
250 * Also handles callback function and interrupt clearing if the
251 * correct interrupt is fired.
252 * periph_acc_irq: SPMI_PIC_OWNERm_ACC_STATUSn register id.
253 * status: Bits of the periph_acc_irq.
254 * return 1 if the peripheral is of interest,
255 * 0 otherwise.
256 */
257int spmi_acc_irq(uint32_t periph_acc_irq, uint32_t status)
258{
259 uint8_t reg_id;
260 uint8_t offset;
261
262 /* Narrow down the correct register for the peripheral*/
263 reg_id = pmic_irq_perph_id / 32;
264 if (periph_acc_irq * 8 != reg_id)
265 return 0;
266
267 /* Narrow down the correct interrupt within the register */
268 offset = pmic_irq_perph_id & 31;
269 if ((status & offset))
270 {
271 /* Clear the interrupt */
272 writel(offset ^ status, SPMI_PIC_IRQ_CLEARn(reg_id));
273
274 /* Confirm that the interrupt has been cleared */
275 while(readl(SPMI_PIC_IRQ_STATUSn(reg_id)) & offset);
276
277 /* Call the callback */
278 callback();
279 return 1;
280 }
281 else
282 return 0;
283}
284
285void spmi_irq()
286{
287 int i;
288 uint32_t status;
289
290 /* Go through the Peripheral list to figure out the periperal
291 * that caused the interrupt
292 */
293 for (i = 0; i < 8; i++)
294 {
295 status = readl(SPMI_PIC_OWNERm_ACC_STATUSn(pmic_arb_owner_id, i));
296 if (status)
297 if (!spmi_acc_irq(i, status))
298 /* Not the correct interrupt, continue to wait */
299 return;
300 }
301 mask_interrupt(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ);
302}
303
304/* Enable interrupts on a particular peripheral: periph_id */
305void spmi_enable_periph_interrupts(uint8_t periph_id)
306{
307 pmic_irq_perph_id = periph_id;
308
309 register_int_handler(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ , spmi_irq, 0);
310 unmask_interrupt(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ);
311
312}
313
314void spmi_uninit()
315{
316 mask_interrupt(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ);
317}