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