blob: 5e45f0b774acf4c125427c2141b27e3bcc775fec [file] [log] [blame]
Hope Wangadf73ae2024-12-13 16:21:01 +08001/*
2 * Copyright (c) 2025, MediaTek Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <errno.h>
8#include <stdint.h>
9#include <string.h>
10
11#include <lib/mmio.h>
12#include <lib/spinlock.h>
13
14#include <pmif.h>
15#include "pmif_common.h"
16#include "spmi_common.h"
17#include "spmi_sw.h"
18
19#define PMIF_CMD_REG_0 0
20#define PMIF_CMD_REG 1
21#define PMIF_CMD_EXT_REG 2
22#define PMIF_CMD_EXT_REG_LONG 3
23#define PMIF_READ_CMD_MIN 0x60
24#define PMIF_READ_CMD_MAX 0x7F
25#define PMIF_READ_CMD_EXT_MIN 0x20
26#define PMIF_READ_CMD_EXT_MAX 0x2F
27#define PMIF_READ_CMD_EXT_LONG_MIN 0x38
28#define PMIF_READ_CMD_EXT_LONG_MAX 0x3F
29#define PMIF_WRITE_CMD_MIN 0x40
30#define PMIF_WRITE_CMD_MAX 0x5F
31#define PMIF_WRITE_CMD_EXT_MAX 0xF
32#define PMIF_WRITE_CMD_EXT_LONG_MIN 0x30
33#define PMIF_WRITE_CMD_EXT_LONG_MAX 0x37
34#define PMIF_WRITE_CMD_0_MIN 0x80
35
36/* macro for SWINF_FSM */
37#define SWINF_FSM_IDLE 0x00
38#define SWINF_FSM_REQ 0x02
39#define SWINF_FSM_WFDLE 0x04
40#define SWINF_FSM_WFVLDCLR 0x06
41
42#define GET_SWINF_FSM(x) (((x) >> 1) & 0x7)
43#define GET_PMIF_INIT_DONE(x) (((x) >> 15) & 0x1)
44#define TIMEOUT_WAIT_IDLE_US 10000 /* 10ms */
45
46#define PMIF_RW_CMD_SET(opc, rw, sid, bc, addr) \
47 (((opc) << 30) | ((rw) << 29) | ((sid) << 24) | ((bc) << 16) | (addr))
48
49static spinlock_t pmif_lock;
50
51struct pmif *get_pmif_controller(int inf, int mstid)
52{
53 return &pmif_spmi_arb[mstid];
54}
55
56static int pmif_check_idle(int mstid)
57{
58 struct pmif *arb = get_pmif_controller(PMIF_SPMI, mstid);
59 unsigned int reg_rdata, offset = 0;
60
61 do {
62 offset = arb->regs[PMIF_SWINF_3_STA];
63 reg_rdata = mmio_read_32((uintptr_t)(arb->base + offset));
64 } while (GET_SWINF_FSM(reg_rdata) != SWINF_FSM_IDLE);
65
66 return 0;
67}
68
69static int pmif_check_vldclr(int mstid)
70{
71 struct pmif *arb = get_pmif_controller(PMIF_SPMI, mstid);
72 unsigned int reg_rdata, offset = 0;
73
74 do {
75 offset = arb->regs[PMIF_SWINF_3_STA];
76 reg_rdata = mmio_read_32((uintptr_t)(arb->base + offset));
77 } while (GET_SWINF_FSM(reg_rdata) != SWINF_FSM_WFVLDCLR);
78
79 return 0;
80}
81
82int pmif_spmi_read_cmd(struct pmif *arb, uint8_t opc, uint8_t sid,
83 uint16_t addr, uint8_t *buf, uint8_t len)
84{
85 int ret;
86 uint32_t offset = 0, data = 0;
87 uint8_t bc = len - 1;
88
89 if (sid > SPMI_MAX_SLAVE_ID || len > PMIF_BYTECNT_MAX)
90 return -EINVAL;
91
92 /* Check the opcode */
93 if (opc >= PMIF_READ_CMD_MIN && opc <= PMIF_READ_CMD_MAX)
94 opc = PMIF_CMD_REG;
95 else if (opc >= PMIF_READ_CMD_EXT_MIN && opc <= PMIF_READ_CMD_EXT_MAX)
96 opc = PMIF_CMD_EXT_REG;
97 else if (opc >= PMIF_READ_CMD_EXT_LONG_MIN && opc <= PMIF_READ_CMD_EXT_LONG_MAX)
98 opc = PMIF_CMD_EXT_REG_LONG;
99 else
100 return -EINVAL;
101
102 spin_lock(&pmif_lock);
103
104 /* Wait for Software Interface FSM state to be IDLE. */
105 ret = pmif_check_idle(arb->mstid);
106 if (ret)
107 goto done;
108
109 /* Send the command. */
110 offset = arb->regs[PMIF_SWINF_3_ACC];
111 mmio_write_32((uintptr_t)(arb->base + offset), PMIF_RW_CMD_SET(opc, 0, sid, bc, addr));
112 /*
113 * Wait for Software Interface FSM state to be WFVLDCLR,
114 * read the data and clear the valid flag.
115 */
116 ret = pmif_check_vldclr(arb->mstid);
117 if (ret)
118 goto done;
119
120 offset = arb->regs[PMIF_SWINF_3_RDATA_31_0];
121
122 data = mmio_read_32((uintptr_t)(arb->base + offset));
123 memcpy(buf, &data, (bc & 3) + 1);
124
125 offset = arb->regs[PMIF_SWINF_3_VLD_CLR];
126 mmio_write_32((uintptr_t)(arb->base + offset), 0x1);
127
128done:
129 spin_unlock(&pmif_lock);
130 return ret;
131}
132
133int pmif_spmi_write_cmd(struct pmif *arb, uint8_t opc, uint8_t sid, uint16_t addr,
134 const uint8_t *buf, uint8_t len)
135{
136 int ret;
137 uint32_t offset = 0, data = 0;
138 uint8_t bc = len - 1;
139
140 if (sid > SPMI_MAX_SLAVE_ID || len > PMIF_BYTECNT_MAX)
141 return -EINVAL;
142
143 /* Check the opcode */
144 if (opc >= PMIF_WRITE_CMD_MIN && opc <= PMIF_WRITE_CMD_MAX)
145 opc = PMIF_CMD_REG;
146 else if (opc <= PMIF_WRITE_CMD_EXT_MAX)
147 opc = PMIF_CMD_EXT_REG;
148 else if (opc >= PMIF_WRITE_CMD_EXT_LONG_MIN && opc <= PMIF_WRITE_CMD_EXT_LONG_MAX)
149 opc = PMIF_CMD_EXT_REG_LONG;
150 else if (opc >= PMIF_WRITE_CMD_0_MIN)
151 opc = PMIF_CMD_REG_0;
152 else
153 return -EINVAL;
154
155 spin_lock(&pmif_lock);
156
157 /* Wait for Software Interface FSM state to be IDLE. */
158 ret = pmif_check_idle(arb->mstid);
159 if (ret)
160 goto done;
161
162 /* Set the write data. */
163 offset = arb->regs[PMIF_SWINF_3_WDATA_31_0];
164 memcpy(&data, buf, (bc & 3) + 1);
165 mmio_write_32((uintptr_t)(arb->base + offset), data);
166 /* Send the command. */
167 offset = arb->regs[PMIF_SWINF_3_ACC];
168 mmio_write_32((uintptr_t)(arb->base + offset), PMIF_RW_CMD_SET(opc, 1, sid, bc, addr));
169
170done:
171 spin_unlock(&pmif_lock);
172 return ret;
173}