forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathi2c_mchp_mss.c
389 lines (323 loc) · 12.7 KB
/
i2c_mchp_mss.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/*
* Copyright (c) 2023 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <string.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/irq.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/sys/util.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/barrier.h>
LOG_MODULE_REGISTER(i2c_mchp, CONFIG_I2C_LOG_LEVEL);
#define DT_DRV_COMPAT microchip_mpfs_i2c
#define CORE_I2C_CTRL (0x00)
#define CORE_I2C_STATUS (0x04)
#define CORE_I2C_DATA (0x08)
#define CORE_I2C_ADDR_0 (0x0C)
#define CORE_I2C_FREQ (0x14)
#define CORE_I2C_GLITCHREG (0x18)
#define CORE_I2C_ADDR_1 (0x1C)
#define CTRL_CR0 BIT(0)
#define CTRL_CR1 BIT(1)
#define CTRL_AA BIT(2)
#define CTRL_SI BIT(3)
#define CTRL_STO BIT(4)
#define CTRL_STA BIT(5)
#define CTRL_ENS1 BIT(6)
#define CTRL_CR2 BIT(7)
#define STATUS_M_START_SENT (0x08)
#define STATUS_M_REPEATED_START_SENT (0x10)
#define STATUS_M_SLAW_ACK (0x18)
#define STATUS_M_SLAW_NACK (0x20)
#define STATUS_M_TX_DATA_ACK (0x28)
#define STATUS_M_TX_DATA_NACK (0x30)
#define STATUS_M_ARB_LOST (0x38)
#define STATUS_M_SLAR_ACK (0x40)
#define STATUS_M_SLAR_NACK (0x48)
#define STATUS_M_RX_DATA_ACKED (0x50)
#define STATUS_M_RX_DATA_NACKED (0x58)
#define STATUS_S_SLAW_ACKED (0x60)
#define STATUS_S_ARB_LOST_SLAW_ACKED (0x68)
#define STATUS_S_GENERAL_CALL_ACKED (0x70)
#define STATUS_S_ARB_LOST_GENERAL_CALL_ACKED (0x78)
#define STATUS_S_RX_DATA_ACKED (0x80)
#define STATUS_S_RX_DATA_NACKED (0x88)
#define STATUS_S_GENERAL_CALL_RX_DATA_ACKED (0x90)
#define STATUS_S_GENERAL_CALL_RX_DATA_NACKED (0x98)
#define STATUS_S_RX_STOP (0xA0)
#define STATUS_S_SLAR_ACKED (0xA8)
#define STATUS_S_ARB_LOST_SLAR_ACKED (0xB0)
#define STATUS_S_TX_DATA_ACK (0xB8)
#define STATUS_S_TX_DATA_NACK (0xC0)
#define STATUS_LAST_DATA_ACK (0xC8)
#define PCLK_DIV_960 (CTRL_CR2)
#define PCLK_DIV_256 (0)
#define PCLK_DIV_224 (CTRL_CR0)
#define PCLK_DIV_192 (CTRL_CR1)
#define PCLK_DIV_160 (CTRL_CR0 | CTRL_CR1)
#define PCLK_DIV_120 (CTRL_CR0 | CTRL_CR2)
#define PCLK_DIV_60 (CTRL_CR1 | CTRL_CR2)
#define BCLK_DIV_8 (CTRL_CR0 | CTRL_CR1 | CTRL_CR2)
#define CLK_MASK (CTRL_CR0 | CTRL_CR1 | CTRL_CR2)
/* -- Transactions types -- */
#define NO_TRANSACTION (0x00)
#define CONTROLLER_WRITE_TRANSACTION (0x01)
#define CONTROLLER_READ_TRANSACTION (0x02)
#define CONTROLLER_RANDOM_READ_TRANSACTION (0x03)
#define WRITE_TARGET_TRANSACTION (0x04)
#define READ_TARGET_TRANSACTION (0x05)
#define MSS_I2C_RELEASE_BUS (0x00)
#define MSS_I2C_HOLD_BUS (0x01)
#define TARGET_ADDR_SHIFT (0x01)
#define MSS_I2C_SUCCESS (0x00)
#define MSS_I2C_IN_PROGRESS (0x01)
#define MSS_I2C_FAILED (0x02)
#define MSS_I2C_TIMED_OUT (0x03)
struct mss_i2c_config {
uint32_t clock_freq;
uintptr_t i2c_base_addr;
uint32_t i2c_irq_base;
};
struct mss_i2c_data {
uint8_t ser_address;
uint8_t target_addr;
uint8_t options;
uint8_t transaction;
const uint8_t *controller_tx_buffer;
uint16_t controller_tx_size;
uint16_t controller_tx_idx;
uint8_t dir;
uint8_t *controller_rx_buffer;
uint16_t controller_rx_size;
uint16_t controller_rx_idx;
atomic_t controller_status;
uint32_t controller_timeout_ms;
const uint8_t *target_tx_buffer;
uint16_t target_tx_size;
uint16_t target_tx_idx;
uint8_t *target_rx_buffer;
uint16_t target_rx_size;
uint16_t target_rx_idx;
atomic_t target_status;
uint8_t target_mem_offset_length;
uint8_t is_target_enabled;
uint8_t bus_status;
uint8_t is_transaction_pending;
uint8_t pending_transaction;
sys_slist_t cb;
};
static int mss_i2c_configure(const struct device *dev, uint32_t dev_config_raw)
{
const struct mss_i2c_config *cfg = dev->config;
uint8_t ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
switch (I2C_SPEED_GET(dev_config_raw)) {
case I2C_SPEED_STANDARD:
sys_write8((ctrl | PCLK_DIV_960), cfg->i2c_base_addr + CORE_I2C_CTRL);
break;
case I2C_SPEED_FAST:
sys_write8((ctrl | PCLK_DIV_256), cfg->i2c_base_addr + CORE_I2C_CTRL);
break;
default:
return -EINVAL;
}
return 0;
}
static int mss_wait_complete(const struct device *dev)
{
struct mss_i2c_data *const data = dev->data;
atomic_t i2c_status = 0;
do {
i2c_status = atomic_get(&data->controller_status);
} while (i2c_status == MSS_I2C_IN_PROGRESS);
return i2c_status;
}
static int mss_i2c_read(const struct device *dev, uint8_t serial_addr, uint8_t *read_buffer,
uint32_t read_size)
{
struct mss_i2c_data *const data = dev->data;
const struct mss_i2c_config *cfg = dev->config;
uint8_t ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
data->target_addr = serial_addr << TARGET_ADDR_SHIFT;
data->pending_transaction = CONTROLLER_READ_TRANSACTION;
data->dir = I2C_MSG_READ;
data->controller_rx_buffer = read_buffer;
data->controller_rx_size = read_size;
data->controller_rx_idx = 0u;
sys_write8((ctrl | CTRL_STA), cfg->i2c_base_addr + CORE_I2C_CTRL);
return 0;
}
static int mss_i2c_write(const struct device *dev, uint8_t serial_addr, uint8_t *tx_buffer,
uint32_t tx_num_write)
{
struct mss_i2c_data *const data = dev->data;
const struct mss_i2c_config *cfg = dev->config;
uint8_t ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
atomic_t target_status = data->target_status;
if (data->transaction == NO_TRANSACTION) {
data->transaction = CONTROLLER_WRITE_TRANSACTION;
}
data->pending_transaction = CONTROLLER_WRITE_TRANSACTION;
data->target_addr = serial_addr << TARGET_ADDR_SHIFT;
data->dir = I2C_MSG_WRITE;
data->controller_tx_buffer = tx_buffer;
data->controller_tx_size = tx_num_write;
data->controller_tx_idx = 0u;
atomic_set(&data->controller_status, MSS_I2C_IN_PROGRESS);
if (target_status == MSS_I2C_IN_PROGRESS) {
data->is_transaction_pending = CONTROLLER_WRITE_TRANSACTION;
} else {
sys_write8((ctrl | CTRL_STA), cfg->i2c_base_addr + CORE_I2C_CTRL);
}
if (data->bus_status == MSS_I2C_HOLD_BUS) {
sys_write8((ctrl & ~CTRL_SI), cfg->i2c_base_addr + CORE_I2C_CTRL);
}
return 0;
}
static int mss_i2c_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr)
{
for (int i = 0; i < num_msgs; i++) {
struct i2c_msg *current = &msgs[i];
if ((current->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
mss_i2c_read(dev, addr, current->buf, current->len);
mss_wait_complete(dev);
} else {
mss_i2c_write(dev, addr, current->buf, current->len);
mss_wait_complete(dev);
}
}
return 0;
}
static DEVICE_API(i2c, mss_i2c_driver_api) = {
.configure = mss_i2c_configure,
.transfer = mss_i2c_transfer,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
static void mss_i2c_reset(const struct device *dev)
{
const struct mss_i2c_config *cfg = dev->config;
uint8_t ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
sys_write8((ctrl & ~CTRL_ENS1), cfg->i2c_base_addr + CORE_I2C_CTRL);
ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
sys_write8((ctrl | CTRL_ENS1), cfg->i2c_base_addr + CORE_I2C_CTRL);
}
static void mss_i2c_irq_handler(const struct device *dev)
{
struct mss_i2c_data *const data = dev->data;
const struct mss_i2c_config *cfg = dev->config;
uint8_t ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
uint8_t status = sys_read8(cfg->i2c_base_addr + CORE_I2C_STATUS);
uint8_t hold_bus = 0;
switch (status) {
case STATUS_M_START_SENT:
case STATUS_M_REPEATED_START_SENT:
sys_write8((ctrl & ~CTRL_STA), cfg->i2c_base_addr + CORE_I2C_CTRL);
sys_write8(data->target_addr | data->dir, cfg->i2c_base_addr + CORE_I2C_DATA);
data->controller_tx_idx = 0;
data->controller_rx_idx = 0;
data->is_transaction_pending = false;
data->transaction = data->pending_transaction;
break;
case STATUS_M_ARB_LOST:
sys_write8((ctrl | CTRL_STA), cfg->i2c_base_addr + CORE_I2C_CTRL);
LOG_WRN("lost arbitration: %x\n", status);
break;
case STATUS_M_SLAW_ACK:
case STATUS_M_TX_DATA_ACK:
if (data->controller_tx_idx < data->controller_tx_size) {
sys_write8(data->controller_tx_buffer[data->controller_tx_idx],
cfg->i2c_base_addr + CORE_I2C_DATA);
data->controller_tx_idx++;
} else if (data->transaction == CONTROLLER_RANDOM_READ_TRANSACTION) {
data->dir = I2C_MSG_READ;
sys_write8((ctrl | CTRL_STA), cfg->i2c_base_addr + CORE_I2C_CTRL);
} else {
data->transaction = NO_TRANSACTION;
hold_bus = data->options & MSS_I2C_HOLD_BUS;
data->bus_status = hold_bus;
if (hold_bus == MSS_I2C_RELEASE_BUS) {
sys_write8((ctrl | CTRL_STO), cfg->i2c_base_addr + CORE_I2C_CTRL);
}
}
atomic_set(&data->controller_status, MSS_I2C_SUCCESS);
break;
case STATUS_M_TX_DATA_NACK:
case STATUS_M_SLAR_NACK:
case STATUS_M_SLAW_NACK:
sys_write8((ctrl | CTRL_STO), cfg->i2c_base_addr + CORE_I2C_CTRL);
atomic_set(&data->controller_status, MSS_I2C_FAILED);
data->transaction = NO_TRANSACTION;
break;
case STATUS_M_SLAR_ACK:
if (data->controller_rx_size > 1u) {
sys_write8((ctrl | CTRL_AA), cfg->i2c_base_addr + CORE_I2C_CTRL);
} else if (data->controller_rx_size == 1u) {
sys_write8((ctrl & ~CTRL_AA), cfg->i2c_base_addr + CORE_I2C_CTRL);
} else {
sys_write8((ctrl | CTRL_AA | CTRL_STO), cfg->i2c_base_addr + CORE_I2C_CTRL);
atomic_set(&data->controller_status, MSS_I2C_SUCCESS);
data->transaction = NO_TRANSACTION;
}
break;
case STATUS_M_RX_DATA_ACKED:
data->controller_rx_buffer[data->controller_rx_idx] =
sys_read8(cfg->i2c_base_addr + CORE_I2C_DATA);
data->controller_rx_idx++;
/* Second Last byte */
if (data->controller_rx_idx >= (data->controller_rx_size - 1u)) {
sys_write8((ctrl & ~CTRL_AA), cfg->i2c_base_addr + CORE_I2C_CTRL);
} else {
atomic_set(&data->controller_status, MSS_I2C_IN_PROGRESS);
}
break;
case STATUS_M_RX_DATA_NACKED:
data->controller_rx_buffer[data->controller_rx_idx] =
sys_read8(cfg->i2c_base_addr + CORE_I2C_DATA);
hold_bus = data->options & MSS_I2C_HOLD_BUS;
data->bus_status = hold_bus;
if (hold_bus == 0u) {
sys_write8((ctrl | CTRL_STO), cfg->i2c_base_addr + CORE_I2C_CTRL);
}
data->transaction = NO_TRANSACTION;
atomic_set(&data->controller_status, MSS_I2C_SUCCESS);
break;
default:
break;
}
ctrl = sys_read8(cfg->i2c_base_addr + CORE_I2C_CTRL);
sys_write8((ctrl & ~CTRL_SI), cfg->i2c_base_addr + CORE_I2C_CTRL);
}
#define MSS_I2C_INIT(n) \
static int mss_i2c_init_##n(const struct device *dev) \
{ \
mss_i2c_reset(dev); \
\
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mss_i2c_irq_handler, \
DEVICE_DT_INST_GET(n), 0); \
\
irq_enable(DT_INST_IRQN(n)); \
\
return 0; \
} \
\
static struct mss_i2c_data mss_i2c_data_##n; \
\
static const struct mss_i2c_config mss_i2c_config_##n = { \
.i2c_base_addr = DT_INST_REG_ADDR(n), \
.i2c_irq_base = DT_INST_IRQN(n), \
.clock_freq = DT_INST_PROP(n, clock_frequency), \
}; \
\
I2C_DEVICE_DT_INST_DEFINE(n, mss_i2c_init_##n, NULL, &mss_i2c_data_##n, \
&mss_i2c_config_##n, PRE_KERNEL_1, CONFIG_I2C_INIT_PRIORITY, \
&mss_i2c_driver_api);
DT_INST_FOREACH_STATUS_OKAY(MSS_I2C_INIT)