blob: 72ec7cb1894d7a92c244445796e9167c1e99a0fe [file] [log] [blame]
Aparna Mallavarapucd5e7a02015-03-28 08:39:10 +05301/* Copyright (c) 2012, 2014-2015, 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>
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070032#include <bits.h>
Deepa Dinamanic2a9b362012-02-23 15:15:54 -080033#include <platform/iomap.h>
34#include <platform/irqs.h>
35#include <platform/interrupts.h>
vijay kumar4f4405f2014-08-08 11:49:53 +053036#include <malloc.h>
Aparna Mallavarapucd5e7a02015-03-28 08:39:10 +053037#include <platform.h>
Deepa Dinamanic2a9b362012-02-23 15:15:54 -080038
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070039#define PMIC_ARB_V2 0x20010000
40#define CHNL_IDX(sid, pid) ((sid << 8) | pid)
41
Deepa Dinamanic2a9b362012-02-23 15:15:54 -080042static uint32_t pmic_arb_chnl_num;
43static uint32_t pmic_arb_owner_id;
44static uint8_t pmic_irq_perph_id;
45static spmi_callback callback;
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070046static uint32_t pmic_arb_ver;
47static uint8_t *chnl_tbl;
Aparna Mallavarapucd5e7a02015-03-28 08:39:10 +053048static uint32_t max_peripherals;
Channagoud Kadabid7166e82015-04-24 17:25:23 -070049static bool spmi_init_done;
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070050
51static void spmi_lookup_chnl_number()
52{
Aparna Mallavarapucd5e7a02015-03-28 08:39:10 +053053 uint32_t i;
vijay kumar4f4405f2014-08-08 11:49:53 +053054 uint8_t slave_id = 0;
55 uint8_t ppid_address = 0;
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070056 /* We need a max of sid (4 bits) + pid (8bits) of uint8_t's */
57 uint32_t chnl_tbl_sz = BIT(12) * sizeof(uint8_t);
58
59 /* Allocate the channel table */
60 chnl_tbl = (uint8_t *) malloc(chnl_tbl_sz);
61 ASSERT(chnl_tbl);
62
Aparna Mallavarapucd5e7a02015-03-28 08:39:10 +053063 for(i = 0; i < max_peripherals; i++)
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070064 {
65#if SPMI_CORE_V2
66 slave_id = (readl(PMIC_ARB_REG_CHLN(i)) & 0xf0000) >> 16;
67 ppid_address = (readl(PMIC_ARB_REG_CHLN(i)) & 0xff00) >> 8;
68#endif
69 chnl_tbl[CHNL_IDX(slave_id, ppid_address)] = i;
70 }
71}
Deepa Dinamanic2a9b362012-02-23 15:15:54 -080072
73/* Function to initialize SPMI controller.
74 * chnl_num : Channel number to be used by this EE.
75 */
76void spmi_init(uint32_t chnl_num, uint32_t owner_id)
77{
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070078 /* Read the version numver */
79 pmic_arb_ver = readl(PMIC_ARB_SPMI_HW_VERSION);
Aparna Mallavarapucd5e7a02015-03-28 08:39:10 +053080 max_peripherals = platform_get_max_periph();
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -070081
82 if (pmic_arb_ver < PMIC_ARB_V2)
83 {
84 /* Initialize PMIC Arbiter Channel Number to
85 * 0 by default of V1 HW
86 */
87 pmic_arb_chnl_num = chnl_num;
88 pmic_arb_owner_id = owner_id;
89 }
90 else
91 {
92 spmi_lookup_chnl_number();
93 }
Channagoud Kadabid7166e82015-04-24 17:25:23 -070094
95 spmi_init_done = true;
Deepa Dinamanic2a9b362012-02-23 15:15:54 -080096}
97
98static void write_wdata_from_array(uint8_t *array,
99 uint8_t reg_num,
100 uint8_t array_size,
101 uint8_t* bytes_written)
102{
103 uint32_t shift_value[] = {0, 8, 16, 24};
104 int i;
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800105 uint32_t val = 0;
106
107 /* Write to WDATA */
108 for (i = 0; (*bytes_written < array_size) && (i < 4); i++)
109 {
110 val |= (uint32_t)(array[*bytes_written]) << shift_value[i];
111 (*bytes_written)++;
112 }
113
114 writel(val, PMIC_ARB_CHNLn_WDATA(pmic_arb_chnl_num, reg_num));
115}
116
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800117/* Initiate a write cmd by writing to cmd register.
118 * Commands are written according to cmd parameters
119 * cmd->opcode : SPMI opcode for the command
120 * cmd->priority : Priority of the command
121 * High priority : 1
122 * Low Priority : 0
123 * cmd->address : SPMI Peripheral Address.
124 * cmd->offset : Offset Address for the command.
125 * cmd->bytecnt : Number of bytes to be written.
126 *
127 * param is the parameter to the command
128 * param->buffer : Value to be written
129 * param->size : Size of the buffer.
130 *
131 * return value : 0 if success, the error bit set on error
132 */
133unsigned int pmic_arb_write_cmd(struct pmic_arb_cmd *cmd,
134 struct pmic_arb_param *param)
135{
136 uint32_t bytes_written = 0;
137 uint32_t error;
138 uint32_t val = 0;
139
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700140 /* Look up for pmic channel only for V2 hardware
141 * For V1-HW we dont care for channel number & always
142 * use '0'
143 */
144 if (pmic_arb_ver >= PMIC_ARB_V2)
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530145 {
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700146 pmic_arb_chnl_num = chnl_tbl[CHNL_IDX(cmd->slave_id, cmd->address)];
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530147 }
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700148
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800149 /* Disable IRQ mode for the current channel*/
150 writel(0x0, PMIC_ARB_CHNLn_CONFIG(pmic_arb_chnl_num));
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800151 /* Write parameters for the cmd */
152 if (cmd == NULL)
153 {
154 dprintf(CRITICAL,"PMIC arbiter error, no command provided\n");
155 return 1;
156 }
157
158 /* Write the data bytes according to the param->size
159 * Can write upto 8 bytes.
160 */
161
162 /* Write first 4 bytes to WDATA0 */
vijay kumar4f4405f2014-08-08 11:49:53 +0530163 write_wdata_from_array(param->buffer, 0, param->size,(uint8_t *)&bytes_written);
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800164
165 if (bytes_written < param->size)
166 {
167 /* Write next 4 bytes to WDATA1 */
vijay kumar4f4405f2014-08-08 11:49:53 +0530168 write_wdata_from_array(param->buffer, 1, param->size, (uint8_t *)&bytes_written);
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800169 }
170
171 /* Fill in the byte count for the command
172 * Note: Byte count is one less than the number of bytes transferred.
173 */
174 cmd->byte_cnt = param->size - 1;
175 /* Fill in the Write cmd opcode. */
176 cmd->opcode = SPMI_CMD_EXT_REG_WRTIE_LONG;
177
178 /* Write the command */
179 val = 0;
180 val |= ((uint32_t)(cmd->opcode) << PMIC_ARB_CMD_OPCODE_SHIFT);
181 val |= ((uint32_t)(cmd->priority) << PMIC_ARB_CMD_PRIORITY_SHIFT);
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530182#ifndef SPMI_CORE_V2
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800183 val |= ((uint32_t)(cmd->slave_id) << PMIC_ARB_CMD_SLAVE_ID_SHIFT);
184 val |= ((uint32_t)(cmd->address) << PMIC_ARB_CMD_ADDR_SHIFT);
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530185#endif
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800186 val |= ((uint32_t)(cmd->offset) << PMIC_ARB_CMD_ADDR_OFFSET_SHIFT);
187 val |= ((uint32_t)(cmd->byte_cnt));
188
189 writel(val, PMIC_ARB_CHNLn_CMD0(pmic_arb_chnl_num));
190
191 /* Wait till CMD DONE status */
192 while (!(val = readl(PMIC_ARB_CHNLn_STATUS(pmic_arb_chnl_num))));
193
194 /* Check for errors */
195 error = val ^ (1 << PMIC_ARB_CMD_DONE);
196 if (error)
197 {
198 dprintf(CRITICAL, "SPMI write command failure: \
199 cmd_id = %u, error = %u\n", cmd->opcode, error);
200 return error;
201 }
202 else
203 return 0;
204}
205
206static void read_rdata_into_array(uint8_t *array,
207 uint8_t reg_num,
208 uint8_t array_size,
209 uint8_t* bytes_read)
210{
211 uint32_t val = 0;
212 uint32_t mask_value[] = {0xFF, 0xFF00, 0xFF0000, 0xFF000000};
213 uint8_t shift_value[] = {0, 8, 16, 24};
214 int i;
215
Aparna Mallavarapu62ab42f2014-08-28 17:47:41 +0530216#if SPMI_CORE_V2
217 val = readl(PMIC_ARB_OBS_CHNLn_RDATA(pmic_arb_chnl_num, reg_num));
218#else
219 val = readl(PMIC_ARB_CHNLn_RDATA(pmic_arb_chnl_num, reg_num));
220#endif
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800221 /* Read at most 4 bytes */
222 for (i = 0; (i < 4) && (*bytes_read < array_size); i++)
223 {
224 array[*bytes_read] = (val & mask_value[i]) >> shift_value[i];
225 (*bytes_read)++;
226 }
227}
228
229/* Initiate a read cmd by writing to cmd register.
230 * Commands are written according to cmd parameters
231 * cmd->opcode : SPMI opcode for the command
232 * cmd->priority : Priority of the command
233 * High priority : 1
234 * Low Priority : 0
235 * cmd->address : SPMI Peripheral Address.
236 * cmd->offset : Offset Address for the command.
237 * cmd->bytecnt : Number of bytes to be read.
238 *
239 * param is the buffer to the save command data.
240 * param->buffer : Buffer to store the bytes returned.
241 * param->size : Size of the buffer.
242 *
243 * return value : 0 if success, the error bit set on error
244 */
245unsigned int pmic_arb_read_cmd(struct pmic_arb_cmd *cmd,
246 struct pmic_arb_param *param)
247{
248 uint32_t val = 0;
249 uint32_t error;
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800250 uint8_t bytes_read = 0;
251
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700252 /* Look up for pmic channel only for V2 hardware
253 * For V1-HW we dont care for channel number & always
254 * use '0'
255 */
256 if (pmic_arb_ver >= PMIC_ARB_V2)
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530257 {
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700258 pmic_arb_chnl_num = chnl_tbl[CHNL_IDX(cmd->slave_id, cmd->address)];
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530259 }
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700260
Aparna Mallavarapu62ab42f2014-08-28 17:47:41 +0530261
Channagoud Kadabi6edec1e2014-04-16 15:02:43 -0700262 /* Disable IRQ mode for the current channel*/
Aparna Mallavarapu62ab42f2014-08-28 17:47:41 +0530263#if SPMI_CORE_V2
264 writel(0x0, PMIC_ARB_OBS_CHNLn_CONFIG(pmic_arb_chnl_num));
265#else
266 writel(0x0, PMIC_ARB_CHNLn_CONFIG(pmic_arb_chnl_num));
267#endif
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800268
269 /* Fill in the byte count for the command
270 * Note: Byte count is one less than the number of bytes transferred.
271 */
272 cmd->byte_cnt = param->size - 1;
273 /* Fill in the Write cmd opcode. */
274 cmd->opcode = SPMI_CMD_EXT_REG_READ_LONG;
275
276 val |= ((uint32_t)(cmd->opcode) << PMIC_ARB_CMD_OPCODE_SHIFT);
277 val |= ((uint32_t)(cmd->priority) << PMIC_ARB_CMD_PRIORITY_SHIFT);
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530278#ifndef SPMI_CORE_V2
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800279 val |= ((uint32_t)(cmd->slave_id) << PMIC_ARB_CMD_SLAVE_ID_SHIFT);
280 val |= ((uint32_t)(cmd->address) << PMIC_ARB_CMD_ADDR_SHIFT);
Aparna Mallavarapu52b0a722014-03-28 16:49:36 +0530281#endif
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800282 val |= ((uint32_t)(cmd->offset) << PMIC_ARB_CMD_ADDR_OFFSET_SHIFT);
283 val |= ((uint32_t)(cmd->byte_cnt));
284
Aparna Mallavarapu62ab42f2014-08-28 17:47:41 +0530285#if SPMI_CORE_V2
286 writel(val, PMIC_ARB_OBS_CHNLn_CMD0(pmic_arb_chnl_num));
287#else
288 writel(val, PMIC_ARB_CHNLn_CMD0(pmic_arb_chnl_num));
289#endif
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800290
291 /* Wait till CMD DONE status */
Aparna Mallavarapu62ab42f2014-08-28 17:47:41 +0530292#if SPMI_CORE_V2
293 while (!(val = readl(PMIC_ARB_OBS_CHNLn_STATUS(pmic_arb_chnl_num))));
294#else
295 while (!(val = readl(PMIC_ARB_CHNLn_STATUS(pmic_arb_chnl_num))));
296#endif
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800297
298 /* Check for errors */
299 error = val ^ (1 << PMIC_ARB_CMD_DONE);
300
301 if (error)
302 {
303 dprintf(CRITICAL, "SPMI read command failure: \
304 cmd_id = %u, error = %u\n", cmd->opcode, error);
305 return error;
306 }
307
308 /* Read the RDATA0 */
309 read_rdata_into_array(param->buffer, 0, param->size , &bytes_read);
310
311 if (bytes_read < param->size)
312 {
313 /* Read the RDATA1 */
314 read_rdata_into_array(param->buffer, 1, param->size , &bytes_read);
315
316 }
317
318 if (bytes_read < param->size)
319 {
320 /* Read the RDATA2 */
321 read_rdata_into_array(param->buffer, 2, param->size , &bytes_read);
322
323 }
324
325 return 0;
326}
327
328
329/* Funtion to determine if the peripheral that caused the interrupt
330 * is of interest.
331 * Also handles callback function and interrupt clearing if the
332 * correct interrupt is fired.
333 * periph_acc_irq: SPMI_PIC_OWNERm_ACC_STATUSn register id.
334 * status: Bits of the periph_acc_irq.
335 * return 1 if the peripheral is of interest,
336 * 0 otherwise.
337 */
338int spmi_acc_irq(uint32_t periph_acc_irq, uint32_t status)
339{
340 uint8_t reg_id;
341 uint8_t offset;
342
343 /* Narrow down the correct register for the peripheral*/
344 reg_id = pmic_irq_perph_id / 32;
345 if (periph_acc_irq * 8 != reg_id)
346 return 0;
347
348 /* Narrow down the correct interrupt within the register */
349 offset = pmic_irq_perph_id & 31;
350 if ((status & offset))
351 {
352 /* Clear the interrupt */
353 writel(offset ^ status, SPMI_PIC_IRQ_CLEARn(reg_id));
354
355 /* Confirm that the interrupt has been cleared */
356 while(readl(SPMI_PIC_IRQ_STATUSn(reg_id)) & offset);
357
358 /* Call the callback */
359 callback();
360 return 1;
361 }
362 else
363 return 0;
364}
365
366void spmi_irq()
367{
368 int i;
369 uint32_t status;
370
371 /* Go through the Peripheral list to figure out the periperal
372 * that caused the interrupt
373 */
374 for (i = 0; i < 8; i++)
375 {
376 status = readl(SPMI_PIC_OWNERm_ACC_STATUSn(pmic_arb_owner_id, i));
377 if (status)
378 if (!spmi_acc_irq(i, status))
379 /* Not the correct interrupt, continue to wait */
380 return;
381 }
382 mask_interrupt(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ);
383}
384
385/* Enable interrupts on a particular peripheral: periph_id */
386void spmi_enable_periph_interrupts(uint8_t periph_id)
387{
388 pmic_irq_perph_id = periph_id;
389
vijay kumar4f4405f2014-08-08 11:49:53 +0530390 register_int_handler(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ ,(int_handler)spmi_irq, 0);
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800391 unmask_interrupt(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ);
392
393}
394
Matthew Qin91d824b2015-06-26 17:03:36 +0800395/* SPMI helper functions */
396uint8_t pmic_spmi_reg_read(uint32_t addr)
397{
398 uint8_t val = 0;
399 struct pmic_arb_cmd cmd;
400 struct pmic_arb_param param;
401
402 cmd.address = SPMI_PERIPH_ID(addr);
403 cmd.offset = SPMI_REG_OFFSET(addr);
404 cmd.slave_id = SPMI_SLAVE_ID(addr);
405 cmd.priority = 0;
406
407 param.buffer = &val;
408 param.size = 1;
409
410 pmic_arb_read_cmd(&cmd, &param);
411
412 return val;
413}
414
415void pmic_spmi_reg_write(uint32_t addr, uint8_t val)
416{
417 struct pmic_arb_cmd cmd;
418 struct pmic_arb_param param;
419
420 cmd.address = SPMI_PERIPH_ID(addr);
421 cmd.offset = SPMI_REG_OFFSET(addr);
422 cmd.slave_id = SPMI_SLAVE_ID(addr);
423 cmd.priority = 0;
424
425 param.buffer = &val;
426 param.size = 1;
427
428 pmic_arb_write_cmd(&cmd, &param);
429}
430
431void pmic_spmi_reg_mask_write(uint32_t addr, uint8_t mask, uint8_t val)
432{
433 uint8_t reg;
434
435 reg = pmic_spmi_reg_read(addr);
436
437 reg &= ~mask;
438 reg |= val & mask;
439 pmic_spmi_reg_write(addr, reg);
440}
441
Deepa Dinamanic2a9b362012-02-23 15:15:54 -0800442void spmi_uninit()
443{
444 mask_interrupt(EE0_KRAIT_HLOS_SPMI_PERIPH_IRQ);
445}
Channagoud Kadabid7166e82015-04-24 17:25:23 -0700446
447bool spmi_initialized()
448{
449 return spmi_init_done;
450}