summaryrefslogtreecommitdiff
path: root/bsp/synopsys/embarc/device/device_hal/inc/dev_spi.h
blob: 4469654e5452329436badd2bc0df866c3e4f6735 (plain)
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
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/* ------------------------------------------
 * Copyright (c) 2017, Synopsys, Inc. All rights reserved.

 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:

 * 1) Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.

 * 2) Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.

 * 3) Neither the name of the Synopsys, Inc., nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * \version 2017.03
 * \date 2014-06-16
 * \author Huaqi Fang([email protected])
--------------------------------------------- */

/**
 * \defgroup	DEVICE_HAL_SPI	SPI Device HAL Interface
 * \ingroup	DEVICE_HAL_DEF
 * \brief	definitions for spi device hardware layer (\ref dev_spi.h)
 * \details	provide interfaces for spi driver to implement
 *  Here is a diagram for the spi interface.
 *
 *  \htmlonly
 *  <div class="imagebox">
 *      <div style="width: 600px">
 *          <img src="pic/dev_spi_hal.jpg" alt="SPI Device HAL Interface Diagram"/>
 *          <p>SPI Device HAL Interface Diagram</p>
 *      </div>
 *  </div>
 *  \endhtmlonly
 *
 * @{
 *
 * \file
 * \brief	spi device hardware layer definitions
 * \details	provide common definitions for spi device,
 * 	then software developer can develop spi driver
 * 	following this definitions, and the applications
 * 	can directly call this definition to realize functions
 */

#ifndef _DEVICE_HAL_SPI_H_
#define _DEVICE_HAL_SPI_H_

#include "device/device_hal/inc/dev_common.h"


/**
 * \defgroup	DEVICE_HAL_SPI_CTRLCMD		SPI Device Control Commands
 * \ingroup	DEVICE_HAL_SPI
 * \brief	Definitions for spi control command, used in \ref dev_spi::spi_control "SPI IO Control"
 * \details	These commands defined here can be used in user code directly.
 * - Parameters Usage
 *   - For passing parameters like integer, just use uint32_t/int32_t to directly pass values
 *   - For passing parameters for a structure, please use pointer to pass values
 *   - For getting some data, please use pointer to store the return data
 * - Common Return Values
 *   - \ref E_OK,	Control device successfully
 *   - \ref E_CLSED,	Device is not opened
 *   - \ref E_OBJ,	Device object is not valid or not exists
 *   - \ref E_PAR,	Parameter is not valid for current control command
 *   - \ref E_SYS,	Control device failed, due to hardware issues such as device is disabled
 *   - \ref E_CTX,	Control device failed, due to different reasons like in transfer state
 *   - \ref E_NOSPT,	Control command is not supported or not valid
 * - Usage Comment
 *   - For SPI poll or interrupt read/write/transfer operations, only 1 operation can be triggered.
 *     If there is a operation is running, any other operation will return \ref E_CTX
 *   - If SPI is in transfer, then the following operations may return \ref E_CTX. Like
 *     \ref SPI_CMD_SET_CLK_MODE, \ref SPI_CMD_SET_TXINT_BUF, \ref SPI_CMD_SET_RXINT_BUF,
 *     \ref SPI_CMD_SET_TXINT, \ref SPI_CMD_SET_RXINT, \ref SPI_CMD_ABORT_TX, \ref SPI_CMD_ABORT_RX,
 *     \ref SPI_CMD_FLUSH_TX, \ref SPI_CMD_FLUSH_RX, \ref SPI_CMD_SET_DFS, \ref SPI_CMD_TRANSFER_POLLING,
 *     \ref SPI_CMD_TRANSFER_INT, \ref SPI_CMD_ABORT_XFER, \ref SPI_CMD_MST_SEL_DEV, \ref SPI_CMD_MST_DSEL_DEV,
 *     \ref SPI_CMD_MST_SET_FREQ and \ref dev_spi::spi_write "SPI Poll Write" or \ref dev_spi::spi_read "SPI Poll Read".
 * @{
 */

/** Define SPI control commands for common usage */
#define DEV_SET_SPI_SYSCMD(cmd)		DEV_SET_SYSCMD((cmd))
/** Define SPI control commands for master usage */
#define DEV_SET_SPI_MST_SYSCMD(cmd)	DEV_SET_SYSCMD(0x00001000|(cmd))
/** Define SPI control commands for slave usage */
#define DEV_SET_SPI_SLV_SYSCMD(cmd)	DEV_SET_SYSCMD(0x00002000|(cmd))


/* ++++ Common commands for SPI Device ++++ */
/**
 * Get \ref dev_spi_info::status "current device status"
 * - Param type : uint32_t *
 * - Param usage : store result of current status
 * - Return value explanation :
 */
#define SPI_CMD_GET_STATUS			DEV_SET_SPI_SYSCMD(0)
/**
 * set the \ref dev_spi_info::clk_mode "clock mode" of spi transfer
 * - Param type : uint32_t
 * - Param usage : spi clock mode to choose clock phase and clock polarity
 * - Return value explanation :
 */
#define SPI_CMD_SET_CLK_MODE			DEV_SET_SPI_SYSCMD(1)
/**
 * set spi \ref dev_spi_info::dfs "data frame size"
 * - Param type : uint32_t
 * - Param usage : should > 0
 * - Return value explanation : If dfs is not supported, then return \ref E_SYS
 */
#define SPI_CMD_SET_DFS				DEV_SET_SPI_SYSCMD(2)
/**
 * set the \ref dev_spi_info::dummy "dummy data" during spi transfer
 * - Param type : uint32_t
 * - Param usage : dummy data to transfer
 * - Return value explanation :
 */
#define SPI_CMD_SET_DUMMY_DATA			DEV_SET_SPI_SYSCMD(3)
/**
 * Set \ref dev_spi_cbs::tx_cb "spi transmit success callback" function
 * when all required bytes are transmitted for interrupt method
 * - Param type : \ref DEV_CALLBACK * or NULL
 * - Param usage : transmit success callback function for spi
 * - Return value explanation :
 */
#define SPI_CMD_SET_TXCB			DEV_SET_SPI_SYSCMD(4)
/**
 * Set \ref dev_spi_cbs::rx_cb "spi receive success callback" function
 * when all required bytes are received for interrupt method
 * - Param type : \ref DEV_CALLBACK * or NULL
 * - Param usage : receive success callback function for spi
 * - Return value explanation :
 */
#define SPI_CMD_SET_RXCB			DEV_SET_SPI_SYSCMD(5)
/**
 * Set \ref dev_spi_cbs::xfer_cb "spi transfer success callback" function
 * when all required transfer are done for interrupt method
 * - Param type : \ref DEV_CALLBACK * or NULL
 * - Param usage : transfer success callback function for spi
 * - Return value explanation :
 */
#define SPI_CMD_SET_XFERCB			DEV_SET_SPI_SYSCMD(6)
/**
 * Set \ref dev_spi_cbs::err_cb "spi transfer error callback" function
 * when something error happened for interrupt method
 * - Param type : \ref DEV_CALLBACK * or NULL
 * - Param usage : transfer error callback function for spi
 * - Return value explanation :
 */
#define SPI_CMD_SET_ERRCB			DEV_SET_SPI_SYSCMD(7)
/**
 * Set buffer in interrupt transmit, and it will set \ref dev_spi_info::xfer "spi tranfer".
 * - SPI master and slave mode use case  \n
 *    For both master and slave mode, if you set tx buffer to NULL, when tx interrupt is enabled and entered into tx interrupt,
 * it will automatically disable the tx interrupt, so when you want to transfer something, you need to set the
 * tx buffer to Non-NULL and enable tx interrupt, when the tx buffer is sent, it will disable the tx interrupt
 * and call tx callback function if available.
 * - Param type : DEV_BUFFER * or NULL
 * - Param usage : buffer structure pointer, if param is NULL, then it will set xfer to empty
 * - Return value explanation :
 */
#define SPI_CMD_SET_TXINT_BUF			DEV_SET_SPI_SYSCMD(8)
/**
 * Set buffer in interrupt receive, and it will set \ref dev_spi_info::xfer "spi tranfer".
 * - SPI master mode use case  \n
 *   Similar to \ref SPI_CMD_SET_TXINT_BUF
 * - SPI slave mode use case   \n
 *   Similiar to \ref SPI_CMD_SET_TXINT_BUF
 * - Param type : DEV_BUFFER * or NULL
 * - Param usage : buffer structure pointer, if param is NULL, then it will set xfer to empty
 * - Return value explanation :
 */
#define SPI_CMD_SET_RXINT_BUF			DEV_SET_SPI_SYSCMD(9)
/**
 * Enable or disable transmit interrupt,
 * for master mode, only one of tx and rx interrupt can be enabled,
 * if tx interrupt is enabled, then rx interrupt can't be enabled.
 * - Param type : uint32_t
 * - Param usage : enable(none-zero) or disable(zero) flag
 * - Return value explanation :
 */
#define SPI_CMD_SET_TXINT			DEV_SET_SPI_SYSCMD(10)
/**
 * Enable or disable receive interrupt,
 * for master mode, only one of tx and rx interrupt can be enabled,
 * if rx interrupt is enabled, then tx interrupt can't be enabled.
 * - Param type : uint32_t
 * - Param usage : enable(none-zero) or disable(zero) flag
 * - Return value explanation :
 */
#define SPI_CMD_SET_RXINT			DEV_SET_SPI_SYSCMD(11)
/**
 * start the transfer by polling
 * - Param type : \ref DEV_SPI_TRANSFER *
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_TRANSFER_POLLING		DEV_SET_SPI_SYSCMD(12)
/**
 * start the transfer by interrupt
 * - Param type : \ref DEV_SPI_TRANSFER * or NULL
 * - Param usage : If NULL, it will disable transfer interrupt, if not NULL, it will enable transfer interrupt
 * - Return value explanation :
 */
#define SPI_CMD_TRANSFER_INT			DEV_SET_SPI_SYSCMD(13)
/**
 * Abort current interrupt transmit operation if tx interrupt enabled,
 * it will disable transmit interrupt, and set \ref DEV_IN_TX_ABRT
 * in \ref dev_spi_info::status "status" variable,
 * and call the transmit callback function, when tx callback is finished,
 * it will clear \ref DEV_IN_TX_ABRT and return
 * - Param type : NULL
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_ABORT_TX			DEV_SET_SPI_SYSCMD(14)
/**
 * Abort current interrupt receive operation if rx interrupt enabled,
 * it will disable receive interrupt, and set \ref DEV_IN_TX_ABRT
 * in \ref dev_spi_info::status "status" variable,
 * and call the receive callback function, when rx callback is finished,
 * it will clear \ref DEV_IN_TX_ABRT and return
 * - Param type : NULL
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_ABORT_RX			DEV_SET_SPI_SYSCMD(15)
/**
 * Abort current interrupt transfer operation if transfer is issued,
 * it will disable transfer interrupt, and set \ref DEV_IN_XFER_ABRT
 * in \ref dev_spi_info::status "status" variable,
 * and call the transfer callback function, when xfer callback is finished,
 * it will clear \ref DEV_IN_XFER_ABRT and return
 * - Param type : NULL
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_ABORT_XFER			DEV_SET_SPI_SYSCMD(16)
/**
 * Do a software reset for SPI device, it will stop current transfer,
 * and clear error state and bring device to normal state, set next condition to STOP
 * - Param type : NULL
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_RESET				DEV_SET_SPI_SYSCMD(17)
/**
 * Flush spi tx fifo, this will clear the data in tx fifo
 * - Param type : NULL
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_FLUSH_TX			DEV_SET_SPI_SYSCMD(18)
/**
 * Flush spi rx fifo, this will clear the data in rx fifo
 * - Param type : NULL
 * - Param usage :
 * - Return value explanation :
 */
#define SPI_CMD_FLUSH_RX			DEV_SET_SPI_SYSCMD(19)
/**
 * Enable spi device
 * - Param type : NULL
 * - Param usage : param is not required
 * - Return value explanation :
 */
#define SPI_CMD_ENA_DEV				DEV_SET_SPI_SYSCMD(20)
/**
 * Disable spi device, when device is disabled,
 * only \ref SPI_CMD_ENA_DEV, \ref SPI_CMD_DIS_DEV,
 * \ref SPI_CMD_GET_STATUS and \ref SPI_CMD_RESET
 * commands can be executed, other commands will return \ref E_SYS
 * - Param type : NULL
 * - Param usage : param is not required
 * - Return value explanation :
 */
#define SPI_CMD_DIS_DEV				DEV_SET_SPI_SYSCMD(21)
/**
 * Get how many bytes space in spi are available to transmit,
 * this can be used in interrupt callback functions,
 * cooperate with \ref dev_spi::spi_write "spi_write" API to realize non-blocked write
 * - Param type : int32_t *
 * - Param usage : store the write available bytes, > 0 for available bytes, 0 for not available
 * - Return value explanation :
 */
#define SPI_CMD_GET_TXAVAIL			DEV_SET_SPI_SYSCMD(22)
/**
 * Get how many bytes in spi are available to receive,
 * this can be used in interrupt callback functions,
 * cooperate with \ref dev_spi::spi_read "spi_read" API to realize non-blocked read
 * - Param type : int32_t *
 * - Param usage : store the read available bytes, > 0 for available bytes, 0 for not available
 * - Return value explanation :
 */
#define SPI_CMD_GET_RXAVAIL			DEV_SET_SPI_SYSCMD(23)


/* ++++ Master only commands for SPI Device ++++ */
/**
 *  select spi slave device
 * - Param type : uint32_t
 * - Param usage : the number of spi slave device to select
 * - Return value explanation : return \ref E_SYS when selection can't be done, return \ref E_CTX during transfer
 */
#define SPI_CMD_MST_SEL_DEV			DEV_SET_SPI_MST_SYSCMD(0)
/**
 *  de-select spi slave device
 * - Param type : uint32_t
 * - Param usage : the number of spi slave device to de-select
 * - Return value explanation : return \ref E_SYS when selection can't be done, return \ref E_CTX during transfer
 */
#define SPI_CMD_MST_DSEL_DEV			DEV_SET_SPI_MST_SYSCMD(1)
 /**
 * Set \ref dev_spi_info::freq "spi frequency".
 * - Param type : uint32_t
 * - Param usage : spi freq
 * - Return value explanation : no return
 */
#define SPI_CMD_MST_SET_FREQ			DEV_SET_SPI_MST_SYSCMD(2)


/* ++++ Slave only commands for SPI Device ++++ */

/* \todo add spi slave related CMDs */

/** @} */

/**
 * \defgroup	DEVICE_HAL_SPI_CALLBACK	SPI Interrupt callback functions
 * \ingroup	DEVICE_HAL_SPI
 * \brief	callback function structure for SPI device
 * @{
 */
typedef struct dev_spi_cbs {
	DEV_CALLBACK tx_cb;	/*!< spi data transmit success required bytes callback */
	DEV_CALLBACK rx_cb;	/*!< spi data receive success required bytes callback */
	DEV_CALLBACK err_cb;	/*!< spi error callback */
	DEV_CALLBACK xfer_cb;	/*!< transfer callback */
} DEV_SPI_CBS, *DEV_SPI_CBS_PTR;
/** @} */

/** SPI Clock Mode */
typedef enum spi_clk_mode {
	SPI_CPOL_0_CPHA_0 = 0,	/*!< Inactive state of serial clock is low, serial clock toggles in middle of first data bit */
	SPI_CPOL_0_CPHA_1 = 1,	/*!< Inactive state of serial clock is low, serial clock toggles at start of first data bit  */
	SPI_CPOL_1_CPHA_0 = 2,	/*!< Inactive state of serial clock is high, serial clock toggles in middle of first data bit */
	SPI_CPOL_1_CPHA_1 = 3,	/*!< Inactive state of serial clock is high, serial clock toggles at start of first data bit */

	SPI_CLK_MODE_0    = SPI_CPOL_0_CPHA_0,	/*!< Equal to \ref SPI_CPOL_0_CPHA_0 */
	SPI_CLK_MODE_1    = SPI_CPOL_0_CPHA_1,	/*!< Equal to \ref SPI_CPOL_0_CPHA_1 */
	SPI_CLK_MODE_2    = SPI_CPOL_1_CPHA_0,	/*!< Equal to \ref SPI_CPOL_1_CPHA_0 */
	SPI_CLK_MODE_3    = SPI_CPOL_1_CPHA_1	/*!< Equal to \ref SPI_CPOL_1_CPHA_1 */
} SPI_CLK_MODE;

#define SPI_CLK_MODE_DEFAULT	SPI_CPOL_0_CPHA_0	/*!< Default SPI device clock mode */

/**
 * \defgroup	DEVICE_HAL_SPI_DEVSTRUCT	SPI Device Structure
 * \ingroup	DEVICE_HAL_SPI
 * \brief	contains definitions of spi device structure.
 * \details	this structure will be used in user implemented code, which was called
 * 	Device Driver Implement Layer for spi to realize in user code.
 * @{
 */
typedef struct dev_spi_transfer DEV_SPI_TRANSFER, *DEV_SPI_TRANSFER_PTR;
/**
 * \brief	spi read and write data structure used by \ref SPI_CMD_TRANSFER
 * 	spi write then read data
 *
 */
struct dev_spi_transfer {
	DEV_SPI_TRANSFER *next;
	/* Calc by software */
	/** tot_len = (tx_totlen>rx_totlen)?tx_totlen:rx_totlen */
	uint32_t tot_len;
	/* Set by user */
	uint8_t *tx_buf;
	uint32_t tx_ofs;
	uint32_t tx_len;
	uint8_t *rx_buf;
	uint32_t rx_ofs;
	uint32_t rx_len;
	/* Should auto set to proper value during set buffer value */
	uint32_t tx_idx;
	uint32_t tx_totlen;	/** tx_totlen = tx_len + tx_ofs */
	uint32_t rx_idx;
	uint32_t rx_totlen;	/** rx_totlen = rx_len + rx_ofs */
};

/** Set tx buffer of device spi transfer */
#define DEV_SPI_XFER_SET_TXBUF(xfer, buf, ofs, len)		{		\
					(xfer)->tx_buf = (uint8_t *)(buf);	\
					(xfer)->tx_len = (uint32_t)(len);	\
					(xfer)->tx_ofs = (uint32_t)(ofs);	\
					(xfer)->tx_idx = 0;			\
					(xfer)->tx_totlen = ( (uint32_t)(len) 	\
							+ (uint32_t)(ofs) ) ;	\
				}

/** Set rx buffer of device spi transfer */
#define DEV_SPI_XFER_SET_RXBUF(xfer, buf, ofs, len)		{		\
					(xfer)->rx_buf = (uint8_t *)(buf);	\
					(xfer)->rx_len = (uint32_t)(len);	\
					(xfer)->rx_ofs = (uint32_t)(ofs);	\
					(xfer)->rx_idx = 0;			\
					(xfer)->rx_totlen = ( (uint32_t)(len) 	\
							+ (uint32_t)(ofs) ) ;	\
				}

/** Calculate total length of current transfer without next transfer */
#define DEV_SPI_XFER_CALC_TOTLEN(xfer)		(xfer)->tot_len =	\
				((xfer)->tx_totlen > (xfer)->rx_totlen) ? (xfer)->tx_totlen : (xfer)->rx_totlen ;

/** Set next SPI transfer */
#define DEV_SPI_XFER_SET_NEXT(xfer, next_xfer)	(xfer)->next = (next_xfer);

/** Init spi transfer */
#define DEV_SPI_XFER_INIT(xfer)					{		\
					(xfer)->tx_idx = 0;			\
					(xfer)->rx_idx = 0;			\
					(xfer)->tx_totlen = ((xfer)->tx_len  	\
							+ (xfer)->tx_ofs) ;	\
					(xfer)->rx_totlen = ((xfer)->rx_len  	\
							+ (xfer)->rx_ofs) ;	\
					DEV_SPI_XFER_CALC_TOTLEN(xfer);		\
				}
/**
 * \brief	spi information struct definition
 * \details	informations about spi open state, working state,
 * 	frequency, spi registers, working method, interrupt number
 */
typedef struct dev_spi_info {
	void *spi_ctrl;		/*!< spi control related */
	uint32_t status;	/*!< current working status, refer to \ref DEVICE_HAL_COMMON_DEVSTATUS, this should be \ref DEV_ENABLED for first open */
	uint32_t freq;		/*!< spi working baudrate */
	uint8_t mode;		/*!< spi working mode (master/slave) */
	uint8_t clk_mode;	/*!< spi clock phase and polarity, this should be \ref SPI_CLK_MODE_DEFAULT for first open */
	uint8_t opn_cnt;	/*!< spi open count, open it will increase 1, close it will decrease 1, 0 for close, >0 for open */
	uint8_t slave;		/*!< current selected slave device no, start from 0, this should be \ref SPI_SLAVE_NOT_SELECTED for first open */
	uint8_t dfs;		/*!< data frame size, this should be \ref SPI_DFS_DEFAULT for first open */

	DEV_SPI_TRANSFER xfer;	/*!< spi transfer, this should be set to all zero for first open */
	DEV_SPI_CBS spi_cbs;	/*!< spi callbacks, for both master and slave mode, this should be all NULL for first open */
	void *extra;		/*!< a extra pointer to get hook to applications which should not used by bsp developer,
					this should be NULL for first open and you can \ref DEV_SPI_INFO_SET_EXTRA_OBJECT "set"
					or \ref DEV_SPI_INFO_GET_EXTRA_OBJECT "get" the extra information pointer */
	uint32_t dummy;		/*!< dummy write data when send and receive, this should be \ref SPI_DUMMY_DEFAULT for first open */
} DEV_SPI_INFO, * DEV_SPI_INFO_PTR;

/** Set extra information pointer of spi info */
#define DEV_SPI_INFO_SET_EXTRA_OBJECT(spi_info_ptr, extra_info)		(spi_info_ptr)->extra = (void *)(extra_info)
/** Get extra information pointer of spi info */
#define DEV_SPI_INFO_GET_EXTRA_OBJECT(spi_info_ptr)			((spi_info_ptr)->extra)

#define SPI_DFS_DEFAULT				8		/*!< Default spi data frame size */
#define SPI_SLAVE_NOT_SELECTED			(0xFF)		/*!< Slave is not selected */
#define SPI_DUMMY_DEFAULT			(0xFF)		/*!< default dummy value for first open */

/**
 * \brief	spi device interface definition
 * \details	define spi device interface, like spi information structure,
 * 	fuctions to get spi info, open/close/control spi, send/receive data by spi
 * \note	all this details are implemented by user in user porting code
 */
typedef struct dev_spi {
	DEV_SPI_INFO spi_info;						/*!< spi device information */
	int32_t (*spi_open) (uint32_t mode, uint32_t param);		/*!< open spi device in master/slave mode, \
									when in master mode, param stands for frequency, \
									when in slave mode, param stands for clock mode */
	int32_t (*spi_close) (void);					/*!< close spi device */
	int32_t (*spi_control) (uint32_t ctrl_cmd, void *param);	/*!< control spi device */
	int32_t (*spi_write) (const void *data, uint32_t len);		/*!< send data to spi device (blocking method) */
	int32_t (*spi_read) (void *data, uint32_t len);			/*!< read data from spi device (blocking method) */
} DEV_SPI, * DEV_SPI_PTR;

/**
 * \fn		int32_t (* dev_spi::spi_open) (uint32_t mode, uint32_t param)
 * \details	open an spi device with selected mode (master or slave) with defined \ref param
 * \param[in]	mode	working mode (\ref DEV_MASTER_MODE "master" or \ref DEV_SLAVE_MODE "slave")
 * \param[in]	param	When mode is \ref DEV_MASTER_MODE, param stands for \ref dev_spi_info::freq "frequency",
 * 			when mode is \ref DEV_SLAVE_MODE, param stands for \ref dev_spi_info::clk_mode "slave clock mode"
 * \retval	E_OK	Open successfully without any issues
 * \retval	E_OPNED	If device was opened before with different parameters,
 *			then just increase the \ref dev_spi_info::opn_cnt "opn_cnt" and return \ref E_OPNED
 * \retval	E_OBJ	Device object is not valid
 * \retval	E_SYS	Device is opened for different mode before, if you want to open it with different mode, you need to fully close it first.
 * \retval	E_PAR	Parameter is not valid
 * \retval	E_NOSPT	Open settings are not supported
 */

/**
 * \fn		int32_t (* dev_spi::spi_close) (void)
 * \details	close an spi device, just decrease the \ref dev_spi_info::opn_cnt "opn_cnt",
 *      if \ref dev_spi_info::opn_cnt "opn_cnt" equals 0, then close the device
 * \retval	E_OK	Close successfully without any issues(including scenario that device is already closed)
 * \retval	E_OPNED	Device is still opened, the device \ref dev_spi_info::opn_cnt "opn_cnt" decreased by 1
 * \retval	E_OBJ	Device object is not valid
 */

/**
 * \fn		int32_t (* dev_spi::spi_control) (uint32_t ctrl_cmd, void *param)
 * \details	control an spi device by \ref ctrl_cmd, with passed \ref param.
 * 	you can control spi device using predefined spi control commands defined using \ref DEV_SET_SYSCMD
 * 	(which must be implemented by bsp developer), such as \ref SPI_CMD_MST_SET_FREQ "set spi master frequency",
 * 	\ref SPI_CMD_FLUSH_TX "flush tx" and \ref DEVICE_HAL_SPI_CTRLCMD "more".
 * 	And you can also control spi device using your own specified commands defined using \ref DEV_SET_USRCMD,
 * 	but these specified commands should be defined in your own spi device driver implementation.
 * \param[in]		ctrl_cmd	\ref DEVICE_HAL_SPI_CTRLCMD "control command", to change or get some thing related to spi
 * \param[in,out]	param		parameters that maybe argument of the command,
 * 					or return values of the command, must not be NULL
 * \retval	E_OK	Control device successfully
 * \retval	E_CLSED	Device is not opened
 * \retval	E_OBJ	Device object is not valid or not exists
 * \retval	E_PAR	Parameter is not valid for current control command
 * \retval	E_SYS	Control device failed, due to hardware issues, such as device is disabled
 * \retval	E_CTX	Control device failed, due to different reasons like in transfer state
 * \retval	E_NOSPT	Control command is not supported or not valid
 */

/**
 * \fn		int32_t (* dev_spi::spi_write) (const void *data, uint32_t len)
 * \details	send \ref data through spi with defined \ref len to slave device .
 * \param[in]	data	pointer to data need to send by spi
 * \param[in]	len	length of data to be sent
 * \retval	>0	Byte count that was successfully sent for poll method
 * \retval	E_OBJ	Device object is not valid or not exists
 * \retval	E_PAR	Parameter is not valid
 * \retval	E_CTX	Device is still in transfer state
 * \retval	E_SYS	Can't write data to hardware due to hardware issues, such as device is disabled
 */

/**
 * \fn		int32_t (* dev_spi::spi_read) (void *data, uint32_t len)
 * \details	receive \ref data of defined \ref len through spi from slave device .
 * \param[out]	data	pointer to data need to received by spi
 * \param[in]	len	length of data to be received
 * \retval	>0	Byte count that was successfully received for poll method
 * \retval	E_OBJ	Device object is not valid or not exists
 * \retval	E_CTX	Device is still in transfer state
 * \retval	E_PAR	Parameter is not valid
 * \retval	E_SYS	Can't receive data from hardware due to hardware issues, such as device is disabled
 */
/** @} */

#ifdef __cplusplus
extern "C" {
#endif

/**
 * \brief	get an \ref dev_spi "spi device" by spi device id.
 * 	For how to use spi device hal refer to \ref dev_spi "Functions in spi device structure"
 * \param[in]	spi_id	id of spi, defined by user
 * \retval	!NULL	pointer to an \ref dev_spi "spi device structure"
 * \retval	NULL	failed to find the spi device by \ref spi_id
 * \note	need to implemented by user in user code
 */
extern DEV_SPI_PTR spi_get_dev(int32_t spi_id);

#ifdef __cplusplus
}
#endif

/** @} */
#endif /* _DEVICE_HAL_SPI_H_ */