1 /*
2 * Copyright (c) 2022 Nuvoton Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_npcx_peci
8
9 #include <errno.h>
10 #include <soc.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/peci.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/kernel.h>
16
17 #include <zephyr/logging/log.h>
18 #include <zephyr/irq.h>
19 LOG_MODULE_REGISTER(peci_npcx, CONFIG_PECI_LOG_LEVEL);
20
21 #define PECI_TIMEOUT K_MSEC(300)
22 #define PECI_NPCX_MAX_TX_BUF_LEN 65
23 #define PECI_NPCX_MAX_RX_BUF_LEN 64
24
25 struct peci_npcx_config {
26 /* peci controller base address */
27 struct peci_reg *base;
28 struct npcx_clk_cfg clk_cfg;
29 const struct pinctrl_dev_config *pcfg;
30 };
31
32 struct peci_npcx_data {
33 struct k_sem trans_sync_sem;
34 struct k_sem lock;
35 uint32_t peci_src_clk_freq;
36 int trans_error;
37 };
38
39 enum npcx_peci_error_code {
40 NPCX_PECI_NO_ERROR,
41 NPCX_PECI_WR_ABORT_ERROR,
42 NPCX_PECI_RD_CRC_ERROR,
43 };
44
peci_npcx_check_bus_idle(struct peci_reg * reg)45 static int peci_npcx_check_bus_idle(struct peci_reg *reg)
46 {
47 if (IS_BIT_SET(reg->PECI_CTL_STS, NPCX_PECI_CTL_STS_START_BUSY)) {
48 return -EBUSY;
49 }
50
51 return 0;
52 }
53
peci_npcx_wait_completion(const struct device * dev)54 static int peci_npcx_wait_completion(const struct device *dev)
55 {
56 struct peci_npcx_data *const data = dev->data;
57 int ret;
58
59 ret = k_sem_take(&data->trans_sync_sem, PECI_TIMEOUT);
60 if (ret != 0) {
61 LOG_ERR("%s: Timeout", __func__);
62 return -ETIMEDOUT;
63 }
64
65 if (data->trans_error != NPCX_PECI_NO_ERROR) {
66 return -EIO;
67 }
68 return 0;
69 }
peci_npcx_configure(const struct device * dev,uint32_t bitrate)70 static int peci_npcx_configure(const struct device *dev, uint32_t bitrate)
71 {
72 const struct peci_npcx_config *const config = dev->config;
73 struct peci_npcx_data *const data = dev->data;
74 struct peci_reg *const reg = config->base;
75 uint8_t bit_rate_divider;
76
77 k_sem_take(&data->lock, K_FOREVER);
78
79 /*
80 * The unit of the bitrate is in Kbps, need to convert it to bps when
81 * calculate the divider
82 */
83 bit_rate_divider = DIV_ROUND_UP(data->peci_src_clk_freq, bitrate * 1000 * 4) - 1;
84 /*
85 * Make sure the divider doesn't exceed the max valid value and is not lower than the
86 * minimal valid value.
87 */
88 bit_rate_divider = CLAMP(bit_rate_divider, PECI_MAX_BIT_RATE_VALID_MIN,
89 NPCX_PECI_RATE_MAX_BIT_RATE_MASK);
90
91 if (bit_rate_divider < PECI_HIGH_SPEED_MIN_VAL) {
92 reg->PECI_RATE |= BIT(NPCX_PECI_RATE_EHSP);
93 } else {
94 reg->PECI_RATE &= ~BIT(NPCX_PECI_RATE_EHSP);
95 }
96 SET_FIELD(reg->PECI_RATE, NPCX_PECI_RATE_MAX_BIT_RATE, bit_rate_divider);
97
98 k_sem_give(&data->lock);
99
100 return 0;
101 }
102
peci_npcx_disable(const struct device * dev)103 static int peci_npcx_disable(const struct device *dev)
104 {
105 struct peci_npcx_data *const data = dev->data;
106
107 k_sem_take(&data->lock, K_FOREVER);
108
109 irq_disable(DT_INST_IRQN(0));
110
111 k_sem_give(&data->lock);
112
113 return 0;
114 }
115
peci_npcx_enable(const struct device * dev)116 static int peci_npcx_enable(const struct device *dev)
117 {
118 const struct peci_npcx_config *const config = dev->config;
119 struct peci_npcx_data *const data = dev->data;
120 struct peci_reg *const reg = config->base;
121
122 k_sem_take(&data->lock, K_FOREVER);
123
124 reg->PECI_CTL_STS = BIT(NPCX_PECI_CTL_STS_DONE) | BIT(NPCX_PECI_CTL_STS_CRC_ERR) |
125 BIT(NPCX_PECI_CTL_STS_ABRT_ERR);
126 NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
127 irq_enable(DT_INST_IRQN(0));
128
129 k_sem_give(&data->lock);
130
131 return 0;
132 }
133
peci_npcx_transfer(const struct device * dev,struct peci_msg * msg)134 static int peci_npcx_transfer(const struct device *dev, struct peci_msg *msg)
135 {
136 const struct peci_npcx_config *const config = dev->config;
137 struct peci_npcx_data *const data = dev->data;
138 struct peci_reg *const reg = config->base;
139 struct peci_buf *peci_rx_buf = &msg->rx_buffer;
140 struct peci_buf *peci_tx_buf = &msg->tx_buffer;
141 enum peci_command_code cmd_code = msg->cmd_code;
142 int ret = 0;
143
144 k_sem_take(&data->lock, K_FOREVER);
145
146 if (peci_tx_buf->len > PECI_NPCX_MAX_TX_BUF_LEN ||
147 peci_rx_buf->len > PECI_NPCX_MAX_RX_BUF_LEN) {
148 ret = -EINVAL;
149 goto out;
150 }
151
152 ret = peci_npcx_check_bus_idle(reg);
153 if (ret != 0) {
154 goto out;
155 }
156
157 reg->PECI_ADDR = msg->addr;
158 reg->PECI_WR_LENGTH = peci_tx_buf->len;
159 reg->PECI_RD_LENGTH = peci_rx_buf->len;
160 reg->PECI_CMD = cmd_code;
161
162 /*
163 * If command = PING command:
164 * Tx buffer length = 0.
165 * Otherwise:
166 * Tx buffer length = N-bytes data + 1 byte command code.
167 */
168 if (peci_tx_buf->len != 0) {
169 for (int i = 0; i < (peci_tx_buf->len - 1); i++) {
170 reg->PECI_DATA_OUT[i] = peci_tx_buf->buf[i];
171 }
172 }
173
174 /* Enable PECI transaction done interrupt */
175 reg->PECI_CTL_STS |= BIT(NPCX_PECI_CTL_STS_DONE_EN);
176 /* Start PECI transaction */
177 reg->PECI_CTL_STS |= BIT(NPCX_PECI_CTL_STS_START_BUSY);
178
179 ret = peci_npcx_wait_completion(dev);
180 if (ret == 0) {
181 int i;
182
183 for (i = 0; i < peci_rx_buf->len; i++) {
184 peci_rx_buf->buf[i] = reg->PECI_DATA_IN[i];
185 }
186 /*
187 * The application allocates N+1 bytes for rx_buffer.
188 * The read data block is stored at the offset 0 ~ (N-1).
189 * The read block FCS is stored at offset N.
190 */
191 peci_rx_buf->buf[i] = reg->PECI_RD_FCS;
192 LOG_DBG("Wr FCS:0x%02x|Rd FCS:0x%02x", reg->PECI_WR_FCS, reg->PECI_RD_FCS);
193 }
194
195 out:
196 k_sem_give(&data->lock);
197 return ret;
198 }
199
peci_npcx_isr(const struct device * dev)200 static void peci_npcx_isr(const struct device *dev)
201 {
202 const struct peci_npcx_config *const config = dev->config;
203 struct peci_npcx_data *const data = dev->data;
204 struct peci_reg *const reg = config->base;
205 uint8_t status;
206
207 status = reg->PECI_CTL_STS;
208 LOG_DBG("PECI ISR status: 0x%02x", status);
209 /*
210 * Disable the transaction done interrupt, also clear the status bits
211 * if they were set.
212 */
213 reg->PECI_CTL_STS &= ~BIT(NPCX_PECI_CTL_STS_DONE_EN);
214
215 if (IS_BIT_SET(status, NPCX_PECI_CTL_STS_ABRT_ERR)) {
216 data->trans_error = NPCX_PECI_WR_ABORT_ERROR;
217 LOG_ERR("PECI Nego or Wr FCS(0x%02x) error", reg->PECI_WR_FCS);
218 } else if (IS_BIT_SET(status, NPCX_PECI_CTL_STS_CRC_ERR)) {
219 data->trans_error = NPCX_PECI_RD_CRC_ERROR;
220 LOG_ERR("PECI Rd FCS(0x%02x) error", reg->PECI_WR_FCS);
221 } else {
222 data->trans_error = NPCX_PECI_NO_ERROR;
223 }
224
225 k_sem_give(&data->trans_sync_sem);
226 }
227
228 static const struct peci_driver_api peci_npcx_driver_api = {
229 .config = peci_npcx_configure,
230 .enable = peci_npcx_enable,
231 .disable = peci_npcx_disable,
232 .transfer = peci_npcx_transfer,
233 };
234
peci_npcx_init(const struct device * dev)235 static int peci_npcx_init(const struct device *dev)
236 {
237 const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
238 const struct peci_npcx_config *const config = dev->config;
239 struct peci_npcx_data *const data = dev->data;
240 int ret;
241
242 if (!device_is_ready(clk_dev)) {
243 LOG_ERR("%s device not ready", clk_dev->name);
244 return -ENODEV;
245 }
246
247 ret = clock_control_on(clk_dev, (clock_control_subsys_t)&config->clk_cfg);
248 if (ret < 0) {
249 LOG_ERR("Turn on PECI clock fail %d", ret);
250 return ret;
251 }
252
253 ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t)&config->clk_cfg,
254 &data->peci_src_clk_freq);
255 if (ret < 0) {
256 LOG_ERR("Get PECI source clock rate error %d", ret);
257 return ret;
258 }
259
260 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
261
262 if (ret != 0) {
263 LOG_ERR("NPCX PECI pinctrl init failed (%d)", ret);
264 return ret;
265 }
266
267 k_sem_init(&data->trans_sync_sem, 0, 1);
268 k_sem_init(&data->lock, 1, 1);
269
270 IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), peci_npcx_isr, DEVICE_DT_INST_GET(0),
271 0);
272
273 return 0;
274 }
275
276 static struct peci_npcx_data peci_npcx_data0;
277
278 PINCTRL_DT_INST_DEFINE(0);
279
280 static const struct peci_npcx_config peci_npcx_config0 = {
281 .base = (struct peci_reg *)DT_INST_REG_ADDR(0),
282 .clk_cfg = NPCX_DT_CLK_CFG_ITEM(0),
283 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
284 };
285
286 DEVICE_DT_INST_DEFINE(0, &peci_npcx_init, NULL, &peci_npcx_data0, &peci_npcx_config0, POST_KERNEL,
287 CONFIG_PECI_INIT_PRIORITY, &peci_npcx_driver_api);
288
289 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
290 "only one 'nuvoton_npcx_peci' compatible node can be supported");
291