summaryrefslogtreecommitdiff
path: root/chip/npcx/shi.c
blob: 482d0b74b8da805be125930efe231dedb7193f06 (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
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
/*
 * Copyright 2015 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/*
 * SHI driver for Chrome EC.
 *
 * This uses Input/Output buffer to handle SPI transmission and reception.
 */

#include "chipset.h"
#include "clock.h"
#include "console.h"
#include "gpio.h"
#include "task.h"
#include "hooks.h"
#include "host_command.h"
#include "registers.h"
#include "spi.h"
#include "system.h"
#include "timer.h"
#include "util.h"

#define CPUTS(outstr) cputs(CC_SPI, outstr)
#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_SPI, format, ## args)

#if !(DEBUG_SHI)
#define DEBUG_CPUTS(...)
#define DEBUG_CPRINTS(...)
#define DEBUG_CPRINTF(...)
#else
#define DEBUG_CPUTS(outstr) cputs(CC_SPI, outstr)
#define DEBUG_CPRINTS(format, args...) cprints(CC_SPI, format, ## args)
#define DEBUG_CPRINTF(format, args...) cprintf(CC_SPI, format, ## args)
#endif

/* SHI Bus definition */
#define SHI_OBUF_FULL_SIZE  64                     /* Full output buffer size */
#define SHI_IBUF_FULL_SIZE  64                     /* Full input buffer size  */
#define SHI_OBUF_HALF_SIZE  (SHI_OBUF_FULL_SIZE/2) /* Half output buffer size */
#define SHI_IBUF_HALF_SIZE  (SHI_IBUF_FULL_SIZE/2) /* Half input buffer size  */

/* Start address of SHI output buffer */
#define SHI_OBUF_START_ADDR  (volatile uint8_t  *)(NPCX_SHI_BASE_ADDR + 0x020)
/* Middle address of SHI output buffer */
#define SHI_OBUF_HALF_ADDR   (SHI_OBUF_START_ADDR + SHI_OBUF_HALF_SIZE)
/* Top address of SHI output buffer */
#define SHI_OBUF_FULL_ADDR   (SHI_OBUF_START_ADDR + SHI_IBUF_FULL_SIZE)
/*
 * Valid offset of SHI output buffer to write.
 * When SIMUL bit is set, IBUFPTR can be used instead of OBUFPTR
 */
#define SHI_OBUF_VALID_OFFSET ((NPCX_IBUFSTAT + SHI_OUT_PREAMBLE_LENGTH) % \
	SHI_OBUF_FULL_SIZE)
/* Start address of SHI input buffer */
#define SHI_IBUF_START_ADDR  (volatile uint8_t  *)(NPCX_SHI_BASE_ADDR + 0x060)
/* Current address of SHI input buffer */
#define SHI_IBUF_CUR_ADDR    (SHI_IBUF_START_ADDR + NPCX_IBUFSTAT)

/*
 * Timeout to wait for SHI request packet
 *
 * This affects the slowest SPI clock we can support.  A delay of 8192 us
 * permits a 512-byte request at 500 KHz, assuming the master starts sending
 * bytes as soon as it asserts chip select.  That's as slow as we would
 * practically want to run the SHI interface, since running it slower
 * significantly impacts firmware update times.
 */
#define SHI_CMD_RX_TIMEOUT_US 8192

/* Timeout for glitch case. Make sure it will exceed 8 SPI clocks */
#define SHI_GLITCH_TIMEOUT_US 10000

/*
 * The AP blindly clocks back bytes over the SPI interface looking for a
 * framing byte.  So this preamble must always precede the actual response
 * packet.
 */

#define SHI_OUT_PREAMBLE_LENGTH 2
/*
 * Space allocation of the past-end status byte (EC_SPI_PAST_END) in the out_msg
 * buffer.
 */
#define EC_SPI_PAST_END_LENGTH 1
/*
 * Space allocation of the frame status byte (EC_SPI_FRAME_START) in the out_msg
 * buffer.
 */
#define EC_SPI_FRAME_START_LENGTH 1

/*
 * Offset of output parameters needs to account for pad and framing bytes and
 * one last past-end byte at the end so any additional bytes clocked out by
 * the AP will have a known and identifiable value.
 */
#define SHI_PROTO3_OVERHEAD (EC_SPI_PAST_END_LENGTH + EC_SPI_FRAME_START_LENGTH)


#ifdef NPCX_SHI_BYPASS_OVER_256B
/* The boundary which SHI will output invalid data on MISO. */
#define SHI_BYPASS_BOUNDARY 256
/* Increase FRAME_START_LENGTH in case shi outputs invalid FRAME_START byte */
#undef  EC_SPI_FRAME_START_LENGTH
#define EC_SPI_FRAME_START_LENGTH 2
#endif

/*
 * Max data size for a version 3 request/response packet.  This is big enough
 * to handle a request/response header, flash write offset/size, and 512 bytes
 * of flash data.
 */
#define SHI_MAX_REQUEST_SIZE 0x220

#ifdef NPCX_SHI_BYPASS_OVER_256B
/* Make sure SHI_MAX_RESPONSE_SIZE won't exceed 256 bytes */
#define SHI_MAX_RESPONSE_SIZE (160 + EC_SPI_PAST_END_LENGTH + \
		EC_SPI_FRAME_START_LENGTH + sizeof(struct ec_host_response))
BUILD_ASSERT(SHI_MAX_RESPONSE_SIZE <= SHI_BYPASS_BOUNDARY);
#else
#define SHI_MAX_RESPONSE_SIZE 0x220
#endif

/*
 * Our input and output msg buffers. These must be large enough for our largest
 * message, including protocol overhead, and must be 32-bit aligned.
 */
static uint8_t out_msg[SHI_MAX_RESPONSE_SIZE];
static uint8_t in_msg[SHI_MAX_REQUEST_SIZE];

/* Parameters used by host protocols */
static struct host_packet shi_packet;

enum shi_state {
	/* SHI not enabled (initial state, and when chipset is off) */
	SHI_STATE_DISABLED = 0,
	/* Ready to receive next request */
	SHI_STATE_READY_TO_RECV,
	/* Receiving request */
	SHI_STATE_RECEIVING,
	/* Processing request */
	SHI_STATE_PROCESSING,
	/* Canceling response since CS deasserted and output NOT_READY byte */
	SHI_STATE_CNL_RESP_NOT_RDY,
#ifdef NPCX_SHI_BYPASS_OVER_256B
	/* Keep output buffer as PROCESSING byte until reaching 256B boundary */
	SHI_STATE_WAIT_ALIGNMENT,
#endif
	/* Sending response */
	SHI_STATE_SENDING,
	/* Received data is valid. */
	SHI_STATE_BAD_RECEIVED_DATA,
};

volatile enum shi_state state;

/* SHI bus parameters */
struct shi_bus_parameters {
	uint8_t *rx_msg;          /* Entry pointer of msg rx buffer   */
	uint8_t *tx_msg;          /* Entry pointer of msg tx buffer   */
	volatile uint8_t *rx_buf; /* Entry pointer of receive buffer  */
	volatile uint8_t *tx_buf; /* Entry pointer of transmit buffer */
	uint16_t sz_received;     /* Size of received data in bytes   */
	uint16_t sz_sending;      /* Size of sending data in bytes    */
	uint16_t sz_request;      /* request bytes need to receive    */
	uint16_t sz_response;     /* response bytes need to receive   */
	timestamp_t rx_deadline;  /* deadline of receiving            */
	uint8_t  pre_ibufstat;    /* Previous IBUFSTAT value          */
#ifdef NPCX_SHI_BYPASS_OVER_256B
	uint16_t bytes_in_256b;   /* Sent bytes in 256 bytes boundary */
#endif
} shi_params;

/* Forward declaraction */
static void shi_reset_prepare(void);
static void shi_bad_received_data(void);
static void shi_fill_out_status(uint8_t status);
static void shi_write_half_outbuf(void);
static void shi_write_first_pkg_outbuf(uint16_t szbytes);
static int shi_read_inbuf_wait(uint16_t szbytes);

/*****************************************************************************/
/* V3 protocol layer functions */

/**
 * Called to send a response back to the host.
 *
 * Some commands can continue for a while. This function is called by
 * host_command when it completes.
 *
 */
static void shi_send_response_packet(struct host_packet *pkt)
{
	if (state == SHI_STATE_PROCESSING) {
		/*
		* Disable interrupts. This routine is not called from interrupt
		* context and buffer underrun will likely occur if it is
		* preempted after writing its initial reply byte.
		*/
		interrupt_disable();

		/* Append our past-end byte, which we reserved space for. */
		((uint8_t *) pkt->response)[pkt->response_size + 0] =
			EC_SPI_PAST_END;

		/* Computing sending bytes of response */
		shi_params.sz_response =
			pkt->response_size + SHI_PROTO3_OVERHEAD;

		/*
		 * Disable SHI interrupt until we have prepared
		 * the first package to output
		 */
		task_disable_irq(NPCX_IRQ_SHI);

		/* Start to fill output buffer with msg buffer */
		shi_write_first_pkg_outbuf(shi_params.sz_response);
#ifdef NPCX_SHI_BYPASS_OVER_256B
		/*
		 * If response package is over 256B boundary,
		 * keep sending PROCESSING byte
		 */
		if (state != SHI_STATE_WAIT_ALIGNMENT) {
#endif
			/* Transmit the reply */
			state = SHI_STATE_SENDING;
			DEBUG_CPRINTF("SND-");
#ifdef NPCX_SHI_BYPASS_OVER_256B
		}
#endif

		/* Enable SHI interrupt */
		task_enable_irq(NPCX_IRQ_SHI);
		interrupt_enable();
	}
	/*
	 * If we're not processing, then the AP has already terminated the
	 * transaction, and won't be listening for a response.
	 * Reset state machine for next transaction.
	 */
	else if (state == SHI_STATE_CNL_RESP_NOT_RDY) {
		shi_reset_prepare();
		DEBUG_CPRINTF("END\n");
	} else
		DEBUG_CPRINTS("Unexpected state %d in response handler", state);
}

void shi_handle_host_package(void)
{
	uint16_t sz_inbuf_int = shi_params.sz_request / SHI_IBUF_HALF_SIZE;
	uint16_t cnt_inbuf_int = shi_params.sz_received / SHI_IBUF_HALF_SIZE;
	if (sz_inbuf_int - cnt_inbuf_int)
		/* Need to receive data from buffer */
		return;
	else {
		uint16_t remain_bytes = shi_params.sz_request
					- shi_params.sz_received;

		/* Read remaining bytes from input buffer directly */
		if (!shi_read_inbuf_wait(remain_bytes))
			return shi_bad_received_data();
		/* Move to processing state immediately */
		state = SHI_STATE_PROCESSING;
		DEBUG_CPRINTF("PRC-");
	}
	/* Fill output buffer to indicate we`re processing request */
	shi_fill_out_status(EC_SPI_PROCESSING);

	/* Set up parameters for host request */
	shi_packet.send_response = shi_send_response_packet;

	shi_packet.request = in_msg;
	shi_packet.request_temp = NULL;
	shi_packet.request_max = sizeof(in_msg);
	shi_packet.request_size = shi_params.sz_request;


#ifdef NPCX_SHI_BYPASS_OVER_256B
	/* Move FRAME_START to second byte */
	out_msg[0] = EC_SPI_PROCESSING;
	out_msg[1] = EC_SPI_FRAME_START;
#else
	/* Put FRAME_START in first byte */
	out_msg[0] = EC_SPI_FRAME_START;
#endif
	shi_packet.response = out_msg + EC_SPI_FRAME_START_LENGTH;

	/* Reserve space for frame start and trailing past-end byte */
	shi_packet.response_max = sizeof(out_msg) - SHI_PROTO3_OVERHEAD;
	shi_packet.response_size = 0;
	shi_packet.driver_result = EC_RES_SUCCESS;

	/* Go to common-layer to handle request */
	host_packet_receive(&shi_packet);
}

/* Parse header for version of spi-protocol */
static void shi_parse_header(void)
{
	/* Disable SHI interrupt until we're sure the size of request package.*/
	task_disable_irq(NPCX_IRQ_SHI);

	/* We're now inside a transaction */
	state = SHI_STATE_RECEIVING;
	DEBUG_CPRINTF("RV-");

	/* Setup deadline time for receiving */
	shi_params.rx_deadline = get_time();
	shi_params.rx_deadline.val += SHI_CMD_RX_TIMEOUT_US;

	/* Wait for version, command, length bytes */
	if (!shi_read_inbuf_wait(3))
		return shi_bad_received_data();

	if (in_msg[0] == EC_HOST_REQUEST_VERSION) {
		/* Protocol version 3 */
		struct ec_host_request *r = (struct ec_host_request *) in_msg;
		int pkt_size;
		/*
		 * If request is over 32 bytes,
		 * we need to modified the algorithm again.
		 */
		ASSERT(sizeof(*r) < SHI_IBUF_HALF_SIZE);

		/* Wait for the rest of the command header */
		if (!shi_read_inbuf_wait(sizeof(*r) - 3))
			return shi_bad_received_data();

		/* Check how big the packet should be */
		pkt_size = host_request_expected_size(r);
		if (pkt_size == 0 || pkt_size > sizeof(in_msg))
			return shi_bad_received_data();

		/* Computing total bytes need to receive */
		shi_params.sz_request = pkt_size;

		/* Enable SHI interrupt & handle request package */
		task_enable_irq(NPCX_IRQ_SHI);

		shi_handle_host_package();
	} else {
		/* Invalid version number */
		return shi_bad_received_data();
	}
}

/*****************************************************************************/
/* IC specific low-level driver */

/* This routine fills out all SHI output buffer with status byte */
static void shi_fill_out_status(uint8_t status)
{
	uint16_t i;
	uint16_t offset = NPCX_IBUFSTAT + SHI_OUT_PREAMBLE_LENGTH;

	/* Disable interrupts in case the interfere by the other interrupts */
	interrupt_disable();

	/* Fill out all output buffer with status byte */
	for (i = offset; i < SHI_OBUF_FULL_SIZE; i++)
		NPCX_OBUF(i) = status;
	for (i = 0; i < offset; i++)
		NPCX_OBUF(i) = status;

	/* End of critical section */
	interrupt_enable();
}

/*
 * This routine makes sure it's valid transaction or glitch on CS bus.
 */
static int shi_is_cs_glitch(void)
{
	timestamp_t deadline;

	deadline.val = get_time().val + SHI_GLITCH_TIMEOUT_US;
	/*
	 * If input buffer pointer is no changed after timeout, it will
	 * return true
	 */
	while (shi_params.pre_ibufstat == NPCX_IBUFSTAT)
		if (timestamp_expired(deadline, NULL))
			return 1;
	/* valid package */
	return 0;
}

/*
 * This routine write SHI next half output buffer from msg buffer
 */
static void shi_write_half_outbuf(void)
{
	uint16_t i;
	uint16_t size = MIN(SHI_OBUF_HALF_SIZE,
			shi_params.sz_response - shi_params.sz_sending);
	/* Fill half output buffer */
	for (i = 0; i < size; i++, shi_params.sz_sending++)
		*(shi_params.tx_buf++) = *(shi_params.tx_msg++);
}

/*
 * This routine write SHI output buffer from msg buffer over halt of it.
 * It make sure we have enought time to handle next operations.
 */
static void shi_write_first_pkg_outbuf(uint16_t szbytes)
{
	uint16_t i;
	uint16_t offset, size;

#ifdef NPCX_SHI_BYPASS_OVER_256B
	/*
	 * If response package is across 256 bytes boundary,
	 * bypass needs to extend PROCESSING bytes after reaching the boundary.
	 */
	if (shi_params.bytes_in_256b + SHI_OBUF_FULL_SIZE + szbytes
	    > SHI_BYPASS_BOUNDARY) {
		state = SHI_STATE_WAIT_ALIGNMENT;
		/* Set pointer of output buffer to the start address */
		shi_params.tx_buf = SHI_OBUF_START_ADDR;
		DEBUG_CPRINTF("WAT-");
		return;
	}
#endif

	offset = SHI_OBUF_VALID_OFFSET;
	shi_params.tx_buf = SHI_OBUF_START_ADDR + offset;
	/* Fill half output buffer */
	size = MIN(SHI_OBUF_HALF_SIZE - (offset % SHI_OBUF_HALF_SIZE),
					szbytes - shi_params.sz_sending);
	for (i = 0; i < size; i++, shi_params.sz_sending++)
		*(shi_params.tx_buf++) = *(shi_params.tx_msg++);

	/* Write data from bottom address again */
	if (shi_params.tx_buf == SHI_OBUF_FULL_ADDR)
		shi_params.tx_buf = SHI_OBUF_START_ADDR;

	/* Fill next half output buffer */
	size = MIN(SHI_OBUF_HALF_SIZE, szbytes - shi_params.sz_sending);
	for (i = 0; i < size; i++, shi_params.sz_sending++)
		*(shi_params.tx_buf++) = *(shi_params.tx_msg++);
}

/* This routine copies SHI half input buffer data to msg buffer */
static void shi_read_half_inbuf(void)
{
	/*
	 * Copy to read buffer until reaching middle/top address of
	 * input buffer or completing receiving data
	 */
	do {
		/* Restore data to msg buffer */
		*(shi_params.rx_msg++) = *(shi_params.rx_buf++);
		shi_params.sz_received++;
	} while (shi_params.sz_received % SHI_IBUF_HALF_SIZE
		&& shi_params.sz_received != shi_params.sz_request);
}

/*
 * This routine read SHI input buffer to msg buffer until
 * we have received a certain number of bytes
 */
static int shi_read_inbuf_wait(uint16_t szbytes)
{
	uint16_t i;

	/* Copy data to msg buffer from input buffer */
	for (i = 0; i < szbytes; i++, shi_params.sz_received++) {
		/*
		 * If input buffer pointer equals pointer which wants to read,
		 * it means data is not ready.
		 */
		while (shi_params.rx_buf == SHI_IBUF_CUR_ADDR)
			if (timestamp_expired(shi_params.rx_deadline, NULL))
				return 0;
		/* Restore data to msg buffer */
		*(shi_params.rx_msg++) = *(shi_params.rx_buf++);
	}
	return 1;
}

/* This routine handles shi recevied unexcepted data */
static void shi_bad_received_data(void)
{
	uint16_t i;

	/* State machine mismatch, timeout, or protocol we can't handle. */
	shi_fill_out_status(EC_SPI_RX_BAD_DATA);
	state = SHI_STATE_BAD_RECEIVED_DATA;

	CPRINTF("BAD-");
	CPRINTF("in_msg=[");
	for (i = 0; i < shi_params.sz_received; i++)
		CPRINTF("%02x ", in_msg[i]);
	CPRINTF("]\n");

	/*
	 * Enable SHI interrupt again since we disable it
	 * at the begin of SHI_STATE_RECEIVING state
	 */
	task_enable_irq(NPCX_IRQ_SHI);
}

/*
 * Avoid spamming the console with prints every IBF / IBHF interrupt, if
 * we find ourselves in an unexpected state.
 */
static int last_error_state = -1;

static void log_unexpected_state(char *isr_name)
{
#if !(DEBUG_SHI)
	if (state != last_error_state)
		CPRINTS("Unexpected state %d in %s ISR", state, isr_name);
#endif
	last_error_state = state;
}

/* This routine handles all interrupts of this module */
void shi_int_handler(void)
{
	uint8_t stat_reg;

	/* Read status register and clear interrupt status early*/
	stat_reg = NPCX_EVSTAT;
	NPCX_EVSTAT = stat_reg;

	/*
	 * End of data for read/write transaction. ie SHI_CS is deasserted.
	 * Host completed or aborted transaction
	 */
	if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_EOR)) {
		/*
		 * We're not in proper state.
		 * Mark not ready to abort next transaction
		 */
		DEBUG_CPRINTF("CSH-");
		/*
		 * If the buffer is still used by the host command.
		 * Change state machine for response handler.
		 */
		if (state == SHI_STATE_PROCESSING) {
			/*
			 * Mark not ready to prevent the other
			 * transaction immediately
			 */
			shi_fill_out_status(EC_SPI_NOT_READY);

			state = SHI_STATE_CNL_RESP_NOT_RDY;
			DEBUG_CPRINTF("CNL-");
			return;
		/* Next transaction but we're not ready */
		} else if (state == SHI_STATE_CNL_RESP_NOT_RDY)
			return;

		/* Error state for checking*/
		if (state != SHI_STATE_SENDING)
			log_unexpected_state("IBEOR");

		/* reset SHI and prepare to next transaction again */
		shi_reset_prepare();
		DEBUG_CPRINTF("END\n");
		return;
	}

	/*
	 * Indicate input/output buffer pointer reaches the half buffer size.
	 * Transaction is processing.
	 */
	if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_IBHF)) {
		if (state == SHI_STATE_RECEIVING) {
			/* Read data from input to msg buffer */
			shi_read_half_inbuf();
			return shi_handle_host_package();
		} else if (state == SHI_STATE_SENDING) {
			/* Write data from msg buffer to output buffer */
			if (shi_params.tx_buf == SHI_OBUF_START_ADDR +
					SHI_OBUF_FULL_SIZE) {
				/* Write data from bottom address again */
				shi_params.tx_buf = SHI_OBUF_START_ADDR;
				return shi_write_half_outbuf();
			} else /* ignore it */
				return;
		} else if (state == SHI_STATE_PROCESSING)
			/* Wait for host handles request */
			return;
#ifdef NPCX_SHI_BYPASS_OVER_256B
		else if (state == SHI_STATE_WAIT_ALIGNMENT) {
			/*
			 * If pointer of output buffer will reach 256 bytes
			 * boundary soon, start to fill response data.
			 */
			if (shi_params.bytes_in_256b == SHI_BYPASS_BOUNDARY -
					SHI_OBUF_FULL_SIZE) {
				state = SHI_STATE_SENDING;
				DEBUG_CPRINTF("SND-");
				return shi_write_half_outbuf();
			}
		}
#endif
		else
			/* Unexpected status */
			log_unexpected_state("IBHF");
	}

	/*
	 * Indicate input/output buffer pointer reaches the full buffer size.
	 * Transaction is processing.
	 */
	if (IS_BIT_SET(stat_reg, NPCX_EVSTAT_IBF)) {
#ifdef NPCX_SHI_BYPASS_OVER_256B
		/* Record the sent bytes within 256B boundary */
		shi_params.bytes_in_256b = (shi_params.bytes_in_256b +
				SHI_OBUF_FULL_SIZE) % SHI_BYPASS_BOUNDARY;
#endif
		if (state == SHI_STATE_RECEIVING) {
			/* read data from input to msg buffer */
			shi_read_half_inbuf();
			/* Read to bottom address again */
			shi_params.rx_buf = SHI_IBUF_START_ADDR;
			return shi_handle_host_package();
		} else if (state == SHI_STATE_SENDING)
			/* Write data from msg buffer to output buffer */
			if (shi_params.tx_buf == SHI_OBUF_START_ADDR +
					SHI_OBUF_HALF_SIZE)
				return shi_write_half_outbuf();
			else /* ignore it */
				return;
		else if (state == SHI_STATE_PROCESSING
#ifdef NPCX_SHI_BYPASS_OVER_256B
				|| state == SHI_STATE_WAIT_ALIGNMENT
#endif
				)
			/* Wait for host handles request */
			return;
		else
			/* Unexpected status */
			log_unexpected_state("IBF");
	}
}
/*
 * The interrupt priority for SHI interrupt should be higher than
 * GPIO. Then we could receive CS-deasserted event even in CS-asserted ISR.
 */
DECLARE_IRQ(NPCX_IRQ_SHI, shi_int_handler, 0);

/* Handle an CS assert event on the SHI_CS_L pin */
void shi_cs_event(enum gpio_signal signal)
{
	/* If not enabled, ignore glitches on SHI_CS_L */
	if (state == SHI_STATE_DISABLED)
		return;

	/*
	 * IBUFSTAT resets on the 7th clock cycle after CS assertion, which
	 * may not have happened yet. We use NPCX_IBUFSTAT for calculating
	 * buffer fill depth, so make sure it's valid before proceeding.
	 */
	if (shi_is_cs_glitch()) {
		CPRINTS("ERR-GTH");
		shi_reset_prepare();
		DEBUG_CPRINTF("END\n");
		return;
	}

	/* NOT_READY should be sent and there're no spi transaction now. */
	if (state == SHI_STATE_CNL_RESP_NOT_RDY)
		return;

	/* Chip select is low = asserted */
	if (state != SHI_STATE_READY_TO_RECV) {
		/* State machine should be reset in EVSTAT_EOR ISR */
		CPRINTS("Unexpected state %d in CS ISR", state);
		return;
	}

	DEBUG_CPRINTF("CSL-");

	/* Read first three bytes to parse which protocol is receiving */
	shi_parse_header();
}

/*****************************************************************************/
/* Hook functions for chipset and initialization */

/*
 * Reset SHI bus and prepare next transaction
 * Please make sure it is executed when there're no spi transactions
 */
static void shi_reset_prepare(void)
{
	uint16_t i;

	/* Initialize parameters of next transaction */
	shi_params.rx_msg = in_msg;
	shi_params.tx_msg = out_msg;
	shi_params.rx_buf = SHI_IBUF_START_ADDR;
	shi_params.tx_buf = SHI_OBUF_HALF_ADDR;
	shi_params.sz_received = 0;
	shi_params.sz_sending = 0;
	shi_params.sz_request = 0;
	shi_params.sz_response = 0;
#ifdef NPCX_SHI_BYPASS_OVER_256B
	shi_params.bytes_in_256b = 0;
#endif
	/* Record last IBUFSTAT for glitch case */
	shi_params.pre_ibufstat = NPCX_IBUFSTAT;

	/*
	 * Fill output buffer to indicate we`re
	 * ready to receive next transaction.
	 */
	for (i = 1; i < SHI_OBUF_FULL_SIZE; i++)
		NPCX_OBUF(i) = EC_SPI_RECEIVING;
	NPCX_OBUF(0) = EC_SPI_OLD_READY;

	/* Enable SHI & WEN functionality */
	NPCX_SHICFG1 = 0x85;

	/* Ready to receive */
	state = SHI_STATE_READY_TO_RECV;
	last_error_state = -1;

	DEBUG_CPRINTF("RDY-");
}

static void shi_enable(void)
{
	int gpio_flags;

	shi_reset_prepare();

	/* Ensure SHI_CS_L interrupt is disabled */
	gpio_disable_interrupt(GPIO_SHI_CS_L);

	/* Enable PU, if requested */
	gpio_flags = GPIO_INPUT | GPIO_INT_F_FALLING;
#ifdef NPCX_SHI_CS_PU
	gpio_flags |= GPIO_PULL_UP;
#endif
	gpio_set_flags(GPIO_SHI_CS_L, gpio_flags);

	/* Enable SHI_CS_L interrupt */
	gpio_enable_interrupt(GPIO_SHI_CS_L);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, shi_enable, HOOK_PRIO_DEFAULT);

static void shi_reenable_on_sysjump(void)
{
#if !(DEBUG_SHI)
	if (system_jumped_to_this_image() && chipset_in_state(CHIPSET_STATE_ON))
#endif
		shi_enable();
}
/* Call hook after chipset sets initial power state */
DECLARE_HOOK(HOOK_INIT,
	     shi_reenable_on_sysjump,
	     HOOK_PRIO_INIT_CHIPSET + 1);

/* Disable SHI bus */
static void shi_disable(void)
{
	state = SHI_STATE_DISABLED;

	/* Disable SHI_CS_L interrupt */
	gpio_disable_interrupt(GPIO_SHI_CS_L);

	/* Disable pullup and interrupts on SHI_CS_L */
	gpio_set_flags(GPIO_SHI_CS_L, GPIO_INPUT);

	/* Set SPI pins to inputs so we don't leak power when AP is off */
	gpio_config_module(MODULE_SPI, 0);
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, shi_disable, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_SYSJUMP, shi_disable, HOOK_PRIO_DEFAULT);

static void shi_init(void)
{
	/* Power on SHI module first */
	CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5), NPCX_PWDWN_CTL5_SHI_PD);
	/*
	 * Mux SHI related pins
	 * SHI_SDI SHI_SDO SHI_CS# SHI_SCLK are selected to device pins
	 */
	SET_BIT(NPCX_DEVALT(ALT_GROUP_C), NPCX_DEVALTC_SHI_SL);

	/*
	 * SHICFG1 (SHI Configuration 1) setting
	 * [7] - IWRAP	= 1: Wrap input buffer to the first address
	 * [6] - CPOL	= 0: Sampling on rising edge and output on falling edge
	 * [5] - DAS	= 0: return STATUS reg data after Status command
	 * [4] - AUTOBE	= 0: Automatically update the OBES bit in STATUS reg
	 * [3] - AUTIBF	= 0: Automatically update the IBFS bit in STATUS reg
	 * [2] - WEN    = 0: Enable host write to input buffer
	 * [1] - Reserved 0
	 * [0] - ENABLE	= 0: Disable SHI at the beginning
	 */
	NPCX_SHICFG1 = 0x80;

	/*
	 * SHICFG2 (SHI Configuration 2) setting
	 * [7] - Reserved 0
	 * [6] - REEVEN = 0: Restart events are not used
	 * [5] - Reserved 0
	 * [4] - REEN   = 0: Restart transactions are not used
	 * [3] - SLWU   = 0: Seem-less wake-up is enabled by default
	 * [2] - ONESHOT= 0: WEN is cleared at the end of a write transaction
	 * [1] - BUSY   = 0: SHI bus is busy 0: idle.
	 * [0] - SIMUL	= 1: Turn on simultaneous Read/Write
	 */
	NPCX_SHICFG2 = 0x01;

	/*
	 * EVENABLE (Event Enable) setting
	 * [7] - IBOREN = 0: Input buffer overrun interrupt enable
	 * [6] - STSREN = 0: status read interrupt disable
	 * [5] - EOWEN  = 0: End-of-Data for Write Transaction Interrupt Enable
	 * [4] - EOREN  = 1: End-of-Data for Read Transaction Interrupt Enable
	 * [3] - IBHFEN = 1: Input Buffer Half Full Interrupt Enable
	 * [2] - IBFEN  = 1: Input Buffer Full Interrupt Enable
	 * [1] - OBHEEN = 0: Output Buffer Half Empty Interrupt Enable
	 * [0] - OBEEN  = 0: Output Buffer Empty Interrupt Enable
	 */
	NPCX_EVENABLE = 0x1C;

	/* Clear SHI events status register */
	NPCX_EVSTAT = 0XFF;
}
/* Call hook before chipset sets initial power state and calls resume hooks */
DECLARE_HOOK(HOOK_INIT, shi_init, HOOK_PRIO_INIT_CHIPSET - 1);

/**
 * Get protocol information
 */
static int shi_get_protocol_info(struct host_cmd_handler_args *args)
{
	struct ec_response_get_protocol_info *r = args->response;

	memset(r, 0, sizeof(*r));
	r->protocol_versions = (1 << 3);
	r->max_request_packet_size = SHI_MAX_REQUEST_SIZE;
	r->max_response_packet_size = SHI_MAX_RESPONSE_SIZE;
	r->flags = EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED;

	args->response_size = sizeof(*r);

	return EC_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, shi_get_protocol_info,
EC_VER_MASK(0));