forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhost_subs_npcx.c
1181 lines (1046 loc) · 38.3 KB
/
host_subs_npcx.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
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
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2020 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nuvoton_npcx_host_sub
/**
* @file
* @brief Nuvoton NPCX host sub modules driver
*
* This file contains the drivers of NPCX Host Sub-Modules that serve as an
* interface between the Host and Core domains. Please refer the block diagram.
*
* +------------+
* | Serial |---> TXD
* +<--->| Port |<--- RXD
* | | |<--> ...
* | +------------+
* | +------------+ |
* +------------+ |<--->| KBC & PM |<--->|
* eSPI_CLK --->| eSPI Bus | | | Channels | |
* eSPI_RST --->| Controller | | +------------+ |
* eSPI_IO3-0 <-->| |<-->| +------------+ |
* eSPI_CS --->| (eSPI mode)| | | Shared | |
* eSPI_ALERT <-->| | |<--->| Memory |<--->|
* +------------+ | +------------+ |
* | +------------+ |
* |<--->| MSWC |<--->|
* | +------------+ |
* | +------------+ |
* | | Core | |
* |<--->| to Host |<--->|
* | | Access | |
* | +------------+ |
* HMIB | Core Bus
* (Host Modules Internal Bus) +------------
*
*
* For most of them, the Host can configure these modules via eSPI(Peripheral
* Channel)/LPC by accessing 'Configuration and Control register Set' which IO
* base address is 0x4E as default. (The table below illustrates structure of
* 'Configuration and Control Register Set') And the interrupts in core domain
* help handling any events from host side.
*
* Index | Configuration and Control Register Set
* --------|--------------------------------------------------+ Bank Select
* 07h | Logical Device Number Register (LDN) |---------+
* --------|--------------------------------------------------- |
* 20-2Fh | SuperI/O Configuration Registers | |
* ------------------------------------------------------------ |
* --------|---------------------------------------------------_ |
* 30h | Logical Device Control Register | |_ |
* --------|--------------------------------------------------- | |_ |
* 60-63h | I/O Space Configuration Registers | | | | |
* --------|--------------------------------------------------- | | | |
* 70-71h | Interrupt Configuration Registers | | | | |
* --------|--------------------------------------------------- | | | |
* 73-74h | DMA Configuration Registers (No support in NPCX) | | | | |
* --------|--------------------------------------------------- | | |<--+
* F0-FFh | Special Logical Device Configuration Registers | | | |
* --------|--------------------------------------------------- | | |
* |--------------------------------------------------- | |
* |--------------------------------------------------- |
* |---------------------------------------------------
*
*
* This driver introduces six host sub-modules. It includes:
*
* 1. Keyboard and Mouse Controller (KBC) interface.
* ● Intel 8051SL-compatible Host interface
* — 8042 KBD standard interface (ports 60h, 64h)
* — Legacy IRQ: IRQ1 (KBD) and IRQ12 (mouse) support
* ● Configured by two logical devices: Keyboard and Mouse (LDN 0x06/0x05)
*
* 2. Power Management (PM) channels.
* ● PM channel registers
* — Command/Status register
* — Data register
* channel 1: legacy 62h, 66h; channel 2: legacy 68h, 6Ch;
* channel 3: legacy 6Ah, 6Eh; channel 4: legacy 6Bh, 6Fh;
* ● PM interrupt using:
* — Serial IRQ
* — SMI
* — EC_SCI
* ● Configured by four logical devices: PM1/2/3/4 (LDN 0x11/0x12/0x17/0x1E)
*
* 3. Shared Memory mechanism (SHM).
* This module allows sharing of the on-chip RAM by both Core and the Host.
* It also supports the following features:
* ● Four Core/Host communication windows for direct RAM access
* ● Eight Protection regions for each access window
* ● Host IRQ and SMI generation
* ● Port 80 debug support
* ● Configured by one logical device: SHM (LDN 0x0F)
*
* 4. Core Access to Host Modules (C2H).
* ● A interface to access module registers in host domain.
* It enables the Core to access the registers in host domain (i.e., Host
* Configuration, Serial Port, SHM, and MSWC), through HMIB.
*
* 5. Mobile System Wake-Up functions (MSWC).
* It detects and handles wake-up events from various sources in the Host
* modules and alerts the Core for better power consumption.
*
* 6. Serial Port (Legacy UART)
* It provides UART functionality by supporting serial data communication
* with a remote peripheral device or a modem.
*
* INCLUDE FILES: soc_host.h
*
*/
#include <assert.h>
#include <zephyr/drivers/espi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/ring_buffer.h>
#include <soc.h>
#include "espi_utils.h"
#include "soc_host.h"
#include "soc_espi.h"
#include "soc_miwu.h"
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
LOG_MODULE_REGISTER(host_sub_npcx, LOG_LEVEL_ERR);
struct host_sub_npcx_config {
/* host module instances */
struct mswc_reg *const inst_mswc;
struct shm_reg *const inst_shm;
struct c2h_reg *const inst_c2h;
struct kbc_reg *const inst_kbc;
struct pmch_reg *const inst_pm_acpi;
struct pmch_reg *const inst_pm_hcmd;
/* clock configuration */
const uint8_t clks_size;
const struct npcx_clk_cfg *clks_list;
/* mapping table between host access signals and wake-up input */
struct npcx_wui host_acc_wui;
};
struct host_sub_npcx_data {
sys_slist_t *callbacks; /* pointer on the espi callback list */
uint8_t plt_rst_asserted; /* current PLT_RST# status */
uint8_t espi_rst_asserted; /* current ESPI_RST# status */
const struct device *host_bus_dev; /* device for eSPI/LPC bus */
#ifdef CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE
struct ring_buf port80_ring_buf;
uint8_t port80_data[CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_RING_BUF_SIZE];
struct k_work work;
#endif
};
struct npcx_dp80_buf {
union {
uint16_t offset_code_16;
uint8_t offset_code[2];
};
};
static const struct npcx_clk_cfg host_dev_clk_cfg[] =
NPCX_DT_CLK_CFG_ITEMS_LIST(0);
struct host_sub_npcx_config host_sub_cfg = {
.inst_mswc = (struct mswc_reg *)DT_INST_REG_ADDR_BY_NAME(0, mswc),
.inst_shm = (struct shm_reg *)DT_INST_REG_ADDR_BY_NAME(0, shm),
.inst_c2h = (struct c2h_reg *)DT_INST_REG_ADDR_BY_NAME(0, c2h),
.inst_kbc = (struct kbc_reg *)DT_INST_REG_ADDR_BY_NAME(0, kbc),
.inst_pm_acpi = (struct pmch_reg *)DT_INST_REG_ADDR_BY_NAME(0, pm_acpi),
.inst_pm_hcmd = (struct pmch_reg *)DT_INST_REG_ADDR_BY_NAME(0, pm_hcmd),
.host_acc_wui = NPCX_DT_WUI_ITEM_BY_NAME(0, host_acc_wui),
.clks_size = ARRAY_SIZE(host_dev_clk_cfg),
.clks_list = host_dev_clk_cfg,
};
struct host_sub_npcx_data host_sub_data;
/* Application shouldn't touch these flags in KBC status register directly */
#define NPCX_KBC_STS_MASK (BIT(NPCX_HIKMST_IBF) | BIT(NPCX_HIKMST_OBF) \
| BIT(NPCX_HIKMST_A2))
/* IO base address of EC Logical Device Configuration */
#define NPCX_EC_CFG_IO_ADDR 0x4E
/* Timeout to wait for Core-to-Host transaction to be completed. */
#define NPCX_C2H_TRANSACTION_TIMEOUT_US 200
/* Logical Device Number Assignments */
#define EC_CFG_LDN_MOUSE 0x05
#define EC_CFG_LDN_KBC 0x06
#define EC_CFG_LDN_SHM 0x0F
#define EC_CFG_LDN_ACPI 0x11 /* PM Channel 1 */
#define EC_CFG_LDN_HCMD 0x12 /* PM Channel 2 */
/* Index of EC (4E/4F) Configuration Register */
#define EC_CFG_IDX_LDN 0x07
#define EC_CFG_IDX_CTRL 0x30
#define EC_CFG_IDX_CMD_IO_ADDR_H 0x60
#define EC_CFG_IDX_CMD_IO_ADDR_L 0x61
#define EC_CFG_IDX_DATA_IO_ADDR_H 0x62
#define EC_CFG_IDX_DATA_IO_ADDR_L 0x63
/* Index of Special Logical Device Configuration (Shared Memory Module) */
#define EC_CFG_IDX_SHM_CFG 0xF1
#define EC_CFG_IDX_SHM_WND1_ADDR_0 0xF4
#define EC_CFG_IDX_SHM_WND1_ADDR_1 0xF5
#define EC_CFG_IDX_SHM_WND1_ADDR_2 0xF6
#define EC_CFG_IDX_SHM_WND1_ADDR_3 0xF7
#define EC_CFG_IDX_SHM_WND2_ADDR_0 0xF8
#define EC_CFG_IDX_SHM_WND2_ADDR_1 0xF9
#define EC_CFG_IDX_SHM_WND2_ADDR_2 0xFA
#define EC_CFG_IDX_SHM_WND2_ADDR_3 0xFB
#define EC_CFG_IDX_SHM_DP80_ADDR_RANGE 0xFD
/* Host sub-device local inline functions */
static inline uint8_t host_shd_mem_wnd_size_sl(uint32_t size)
{
/* The minimum supported shared memory region size is 8 bytes */
if (size <= 8U) {
size = 8U;
}
/* The maximum supported shared memory region size is 4K bytes */
if (size >= 4096U) {
size = 4096U;
}
/*
* If window size is not a power-of-two, it is rounded-up to the next
* power-of-two value, and return value corresponds to RWINx_SIZE field.
*/
return (32 - __builtin_clz(size - 1U)) & 0xff;
}
/* Host KBC sub-device local functions */
#if defined(CONFIG_ESPI_PERIPHERAL_8042_KBC)
static void host_kbc_ibf_isr(const void *arg)
{
ARG_UNUSED(arg);
struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc;
struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION,
ESPI_PERIPHERAL_8042_KBC, ESPI_PERIPHERAL_NODATA
};
struct espi_evt_data_kbc *kbc_evt =
(struct espi_evt_data_kbc *)&evt.evt_data;
/* KBC Input Buffer Full event */
kbc_evt->evt = HOST_KBC_EVT_IBF;
/* The data in KBC Input Buffer */
kbc_evt->data = inst_kbc->HIKMDI;
/*
* Indicates if the host sent a command or data.
* 0 = data
* 1 = Command.
*/
kbc_evt->type = IS_BIT_SET(inst_kbc->HIKMST, NPCX_HIKMST_A2);
LOG_DBG("%s: kbc data 0x%02x", __func__, evt.evt_data);
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev,
evt);
}
static void host_kbc_obe_isr(const void *arg)
{
ARG_UNUSED(arg);
struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc;
struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION,
ESPI_PERIPHERAL_8042_KBC, ESPI_PERIPHERAL_NODATA
};
struct espi_evt_data_kbc *kbc_evt =
(struct espi_evt_data_kbc *)&evt.evt_data;
/* Disable KBC OBE interrupt first */
inst_kbc->HICTRL &= ~BIT(NPCX_HICTRL_OBECIE);
LOG_DBG("%s: kbc status 0x%02x", __func__, inst_kbc->HIKMST);
/*
* Notify application that host already read out data. The application
* might need to clear status register via espi_api_lpc_write_request()
* with E8042_CLEAR_FLAG opcode in callback.
*/
kbc_evt->evt = HOST_KBC_EVT_OBE;
kbc_evt->data = 0;
kbc_evt->type = 0;
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev,
evt);
}
static void host_kbc_init(void)
{
struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc;
/* Make sure the previous OBF and IRQ has been sent out. */
k_busy_wait(4);
/* Set FW_OBF to clear OBF flag in both STATUS and HIKMST to 0 */
inst_kbc->HICTRL |= BIT(NPCX_HICTRL_FW_OBF);
/* Ensure there is no OBF set in this period. */
k_busy_wait(4);
/*
* Init KBC with:
* 1. Enable Input Buffer Full (IBF) core interrupt for Keyboard/mouse.
* 2. Enable Output Buffer Full Mouse(OBFM) SIRQ 12.
* 3. Enable Output Buffer Full Keyboard (OBFK) SIRQ 1.
*/
inst_kbc->HICTRL = BIT(NPCX_HICTRL_IBFCIE) | BIT(NPCX_HICTRL_OBFMIE)
| BIT(NPCX_HICTRL_OBFKIE);
/* Configure SIRQ 1/12 type (level + high) */
inst_kbc->HIIRQC = 0x00;
}
#endif
/* Host ACPI sub-device local functions */
#if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO)
static void host_acpi_process_input_data(uint8_t data)
{
struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi;
struct espi_event evt = {
.evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION,
.evt_details = ESPI_PERIPHERAL_HOST_IO,
.evt_data = ESPI_PERIPHERAL_NODATA
};
struct espi_evt_data_acpi *acpi_evt =
(struct espi_evt_data_acpi *)&evt.evt_data;
LOG_DBG("%s: acpi data 0x%02x", __func__, data);
/*
* Indicates if the host sent a command or data.
* 0 = data
* 1 = Command.
*/
acpi_evt->type = IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_CMD);
acpi_evt->data = data;
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev,
evt);
}
static void host_acpi_init(void)
{
struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi;
/* Use SMI/SCI postive polarity by default */
inst_acpi->HIPMCTL &= ~BIT(NPCX_HIPMCTL_SCIPOL);
inst_acpi->HIPMIC &= ~BIT(NPCX_HIPMIC_SMIPOL);
/* Set SMIB/SCIB bits to make sure SMI#/SCI# are driven to high */
inst_acpi->HIPMIC |= BIT(NPCX_HIPMIC_SMIB) | BIT(NPCX_HIPMIC_SCIB);
/*
* Allow SMI#/SCI# generated from PM module.
* On eSPI bus, we suggest set VW value of SCI#/SMI# directly.
*/
inst_acpi->HIPMIE |= BIT(NPCX_HIPMIE_SCIE);
inst_acpi->HIPMIE |= BIT(NPCX_HIPMIE_SMIE);
/*
* Init ACPI PM channel (Host IO) with:
* 1. Enable Input-Buffer Full (IBF) core interrupt.
* 2. BIT 7 must be 1.
*/
inst_acpi->HIPMCTL |= BIT(7) | BIT(NPCX_HIPMCTL_IBFIE);
}
#endif
#if defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)
/* Host command argument and memory map buffers */
static uint8_t shm_host_cmd[CONFIG_ESPI_NPCX_PERIPHERAL_HOST_CMD_PARAM_SIZE]
__aligned(8);
/* Host command sub-device local functions */
static void host_hcmd_process_input_data(uint8_t data)
{
struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION,
ESPI_PERIPHERAL_EC_HOST_CMD, ESPI_PERIPHERAL_NODATA
};
evt.evt_data = data;
LOG_DBG("%s: host cmd data 0x%02x", __func__, evt.evt_data);
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev,
evt);
}
static void host_hcmd_init(void)
{
struct pmch_reg *const inst_hcmd = host_sub_cfg.inst_pm_hcmd;
struct shm_reg *const inst_shm = host_sub_cfg.inst_shm;
uint32_t win_size = CONFIG_ESPI_NPCX_PERIPHERAL_HOST_CMD_PARAM_SIZE;
/* Don't stall SHM transactions */
inst_shm->SHM_CTL &= ~0x40;
/* Disable Window 1 protection */
inst_shm->WIN1_WR_PROT = 0;
inst_shm->WIN1_RD_PROT = 0;
/* Configure Win1 size for ec host command. */
SET_FIELD(inst_shm->WIN_SIZE, NPCX_WIN_SIZE_RWIN1_SIZE_FIELD,
host_shd_mem_wnd_size_sl(win_size));
inst_shm->WIN_BASE1 = (uint32_t)shm_host_cmd;
/*
* Clear processing flag before enabling host's interrupts in case
* it's set by the other command during sysjump.
*/
inst_hcmd->HIPMST &= ~BIT(NPCX_HIPMST_F0);
/*
* Init Host Command PM channel (Host IO) with:
* 1. Enable Input-Buffer Full (IBF) core interrupt.
* 2. BIT 7 must be 1.
*/
inst_hcmd->HIPMCTL |= BIT(7) | BIT(NPCX_HIPMCTL_IBFIE);
}
#endif
#if defined(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION)
/* Host command argument and memory map buffers */
static uint8_t shm_acpi_mmap[CONFIG_ESPI_NPCX_PERIPHERAL_ACPI_SHD_MEM_SIZE]
__aligned(8);
static void host_shared_mem_region_init(void)
{
struct shm_reg *const inst_shm = host_sub_cfg.inst_shm;
uint32_t win_size = CONFIG_ESPI_NPCX_PERIPHERAL_ACPI_SHD_MEM_SIZE;
/* Don't stall SHM transactions */
inst_shm->SHM_CTL &= ~0x40;
/* Disable Window 2 protection */
inst_shm->WIN2_WR_PROT = 0;
inst_shm->WIN2_RD_PROT = 0;
/* Configure Win2 size for ACPI shared mem region. */
SET_FIELD(inst_shm->WIN_SIZE, NPCX_WIN_SIZE_RWIN2_SIZE_FIELD,
host_shd_mem_wnd_size_sl(win_size));
inst_shm->WIN_BASE2 = (uint32_t)shm_acpi_mmap;
/* Enable write protect of Share memory window 2 */
inst_shm->WIN2_WR_PROT = 0xFF;
/*
* TODO: Initialize shm_acpi_mmap buffer for host command flags. We
* might use EACPI_GET_SHARED_MEMORY in espi_api_lpc_read_request()
* instead of setting host command flags here directly.
*/
}
#endif
#if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO) || \
defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)
/* Host pm (host io) sub-module isr function for all channels such as ACPI. */
static void host_pmch_ibf_isr(const void *arg)
{
ARG_UNUSED(arg);
struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi;
struct pmch_reg *const inst_hcmd = host_sub_cfg.inst_pm_hcmd;
uint8_t in_data;
/* Host put data on input buffer of ACPI channel */
if (IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_IBF)) {
/* Set processing flag before reading command byte */
inst_acpi->HIPMST |= BIT(NPCX_HIPMST_F0);
/* Read out input data and clear IBF pending bit */
in_data = inst_acpi->HIPMDI;
#if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO)
host_acpi_process_input_data(in_data);
#endif
}
/* Host put data on input buffer of HOSTCMD channel */
if (IS_BIT_SET(inst_hcmd->HIPMST, NPCX_HIPMST_IBF)) {
/* Set processing flag before reading command byte */
inst_hcmd->HIPMST |= BIT(NPCX_HIPMST_F0);
/* Read out input data and clear IBF pending bit */
in_data = inst_hcmd->HIPMDI;
#if defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)
host_hcmd_process_input_data(in_data);
#endif
}
}
#endif
/* Host port80 sub-device local functions */
#if defined(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80)
#if defined(CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE)
static void host_port80_work_handler(struct k_work *item)
{
uint32_t code = 0;
struct host_sub_npcx_data *data = CONTAINER_OF(item, struct host_sub_npcx_data, work);
struct ring_buf *rbuf = &data->port80_ring_buf;
struct espi_event evt = {ESPI_BUS_PERIPHERAL_NOTIFICATION,
(ESPI_PERIPHERAL_INDEX_0 << 16) | ESPI_PERIPHERAL_DEBUG_PORT80,
ESPI_PERIPHERAL_NODATA};
while (!ring_buf_is_empty(rbuf)) {
struct npcx_dp80_buf dp80_buf;
uint8_t offset;
ring_buf_get(rbuf, &dp80_buf.offset_code[0], sizeof(dp80_buf.offset_code));
offset = dp80_buf.offset_code[1];
code |= dp80_buf.offset_code[0] << (8 * offset);
if (ring_buf_is_empty(rbuf)) {
evt.evt_data = code;
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev,
evt);
break;
}
/* peek the offset of the next byte */
ring_buf_peek(rbuf, &dp80_buf.offset_code[0], sizeof(dp80_buf.offset_code));
offset = dp80_buf.offset_code[1];
/*
* If the peeked next byte's offset is 0, it is the start of the new code.
* Pass the current code to the application layer to handle the Port80 code.
*/
if (offset == 0) {
evt.evt_data = code;
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev,
evt);
code = 0;
}
}
}
#endif
static void host_port80_isr(const void *arg)
{
ARG_UNUSED(arg);
struct shm_reg *const inst_shm = host_sub_cfg.inst_shm;
uint8_t status = inst_shm->DP80STS;
#ifdef CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE
struct ring_buf *rbuf = &host_sub_data.port80_ring_buf;
while (IS_BIT_SET(inst_shm->DP80STS, NPCX_DP80STS_FNE)) {
struct npcx_dp80_buf dp80_buf;
dp80_buf.offset_code_16 = inst_shm->DP80BUF;
ring_buf_put(rbuf, &dp80_buf.offset_code[0], sizeof(dp80_buf.offset_code));
}
k_work_submit(&host_sub_data.work);
#else
struct espi_event evt = {ESPI_BUS_PERIPHERAL_NOTIFICATION,
(ESPI_PERIPHERAL_INDEX_0 << 16) | ESPI_PERIPHERAL_DEBUG_PORT80,
ESPI_PERIPHERAL_NODATA};
/* Read out port80 data continuously if FIFO is not empty */
while (IS_BIT_SET(inst_shm->DP80STS, NPCX_DP80STS_FNE)) {
LOG_DBG("p80: %04x", inst_shm->DP80BUF);
evt.evt_data = inst_shm->DP80BUF;
espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, evt);
}
#endif
LOG_DBG("%s: p80 status 0x%02X", __func__, status);
/* If FIFO is overflow, show error msg */
if (IS_BIT_SET(status, NPCX_DP80STS_FOR)) {
inst_shm->DP80STS |= BIT(NPCX_DP80STS_FOR);
LOG_DBG("Port80 FIFO Overflow!");
}
/* If there are pending post codes remains in FIFO after processing and sending previous
* post codes, do not clear the FNE bit. This allows this handler to be called again
* immediately after it exists.
*/
if (!IS_BIT_SET(inst_shm->DP80STS, NPCX_DP80STS_FNE)) {
/* Clear all pending bit indicates that FIFO was written by host */
inst_shm->DP80STS |= BIT(NPCX_DP80STS_FWR);
}
}
static void host_port80_init(void)
{
struct shm_reg *const inst_shm = host_sub_cfg.inst_shm;
/*
* Init PORT80 which includes:
* Enables a Core interrupt on every Host write to the FIFO,
* SYNC mode (It must be 1 in eSPI mode), Read Auto Advance mode, and
* Port80 module itself.
*/
inst_shm->DP80CTL = BIT(NPCX_DP80CTL_CIEN) | BIT(NPCX_DP80CTL_RAA)
| BIT(NPCX_DP80CTL_DP80EN) | BIT(NPCX_DP80CTL_SYNCEN);
}
#endif
#if defined(CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE)
static void host_cus_opcode_enable_interrupts(void)
{
/* Enable host KBC sub-device interrupt */
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC)) {
irq_enable(DT_INST_IRQ_BY_NAME(0, kbc_ibf, irq));
irq_enable(DT_INST_IRQ_BY_NAME(0, kbc_obe, irq));
}
/* Enable host PM channel (Host IO) sub-device interrupt */
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_HOST_IO) ||
IS_ENABLED(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)) {
irq_enable(DT_INST_IRQ_BY_NAME(0, pmch_ibf, irq));
}
/* Enable host Port80 sub-device interrupt installation */
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80)) {
irq_enable(DT_INST_IRQ_BY_NAME(0, p80_fifo, irq));
}
/* Enable host interface interrupts if its interface is eSPI */
if (IS_ENABLED(CONFIG_ESPI)) {
npcx_espi_enable_interrupts(host_sub_data.host_bus_dev);
}
}
static void host_cus_opcode_disable_interrupts(void)
{
/* Disable host KBC sub-device interrupt */
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC)) {
irq_disable(DT_INST_IRQ_BY_NAME(0, kbc_ibf, irq));
irq_disable(DT_INST_IRQ_BY_NAME(0, kbc_obe, irq));
}
/* Disable host PM channel (Host IO) sub-device interrupt */
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_HOST_IO) ||
IS_ENABLED(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)) {
irq_disable(DT_INST_IRQ_BY_NAME(0, pmch_ibf, irq));
}
/* Disable host Port80 sub-device interrupt installation */
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80)) {
irq_disable(DT_INST_IRQ_BY_NAME(0, p80_fifo, irq));
}
/* Disable host interface interrupts if its interface is eSPI */
if (IS_ENABLED(CONFIG_ESPI)) {
npcx_espi_disable_interrupts(host_sub_data.host_bus_dev);
}
}
#endif /* CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE */
#if defined(CONFIG_ESPI_PERIPHERAL_UART)
/* host uart pinmux configuration */
PINCTRL_DT_DEFINE(DT_INST(0, nuvoton_npcx_host_uart));
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(nuvoton_npcx_host_uart) == 1,
"only one 'nuvoton_npcx_host_uart' compatible node may be present");
const struct pinctrl_dev_config *huart_cfg =
PINCTRL_DT_DEV_CONFIG_GET(DT_INST(0, nuvoton_npcx_host_uart));
/* Host UART sub-device local functions */
void host_uart_init(void)
{
struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h;
/* Configure pin-mux for serial port device */
pinctrl_apply_state(huart_cfg, PINCTRL_STATE_DEFAULT);
/* Make sure unlock host access of serial port */
inst_c2h->LKSIOHA &= ~BIT(NPCX_LKSIOHA_LKSPHA);
/* Clear 'Host lock violation occurred' bit of serial port initially */
inst_c2h->SIOLV |= BIT(NPCX_SIOLV_SPLV);
}
#endif
/* host core-to-host interface local functions */
static void host_c2h_wait_write_done(void)
{
struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h;
uint32_t elapsed_cycles;
uint32_t start_cycles = k_cycle_get_32();
uint32_t max_wait_cycles =
k_us_to_cyc_ceil32(NPCX_C2H_TRANSACTION_TIMEOUT_US);
while (IS_BIT_SET(inst_c2h->SIBCTRL, NPCX_SIBCTRL_CSWR)) {
elapsed_cycles = k_cycle_get_32() - start_cycles;
if (elapsed_cycles > max_wait_cycles) {
LOG_ERR("c2h write transaction expired!");
break;
}
}
}
static void host_c2h_wait_read_done(void)
{
struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h;
uint32_t elapsed_cycles;
uint32_t start_cycles = k_cycle_get_32();
uint32_t max_wait_cycles =
k_us_to_cyc_ceil32(NPCX_C2H_TRANSACTION_TIMEOUT_US);
while (IS_BIT_SET(inst_c2h->SIBCTRL, NPCX_SIBCTRL_CSRD)) {
elapsed_cycles = k_cycle_get_32() - start_cycles;
if (elapsed_cycles > max_wait_cycles) {
LOG_ERR("c2h read transaction expired!");
break;
}
}
}
void host_c2h_write_io_cfg_reg(uint8_t reg_index, uint8_t reg_data)
{
struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h;
/* Disable interrupts */
unsigned int key = irq_lock();
/* Lock host access EC configuration registers (0x4E/0x4F) */
inst_c2h->LKSIOHA |= BIT(NPCX_LKSIOHA_LKCFG);
/* Enable Core-to-Host access CFG module */
inst_c2h->CRSMAE |= BIT(NPCX_CRSMAE_CFGAE);
/* Verify core-to-host modules is not in progress */
host_c2h_wait_read_done();
host_c2h_wait_write_done();
/*
* Specifying the in-direct IO address which A0 = 0 indicates the index
* register is accessed. Then write index address directly and it starts
* a write transaction to host sub-module on LPC/eSPI bus.
*/
inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR;
inst_c2h->IHD = reg_index;
host_c2h_wait_write_done();
/*
* Specifying the in-direct IO address which A0 = 1 indicates the data
* register is accessed. Then write data directly and it starts a write
* transaction to host sub-module on LPC/eSPI bus.
*/
inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR + 1;
inst_c2h->IHD = reg_data;
host_c2h_wait_write_done();
/* Disable Core-to-Host access CFG module */
inst_c2h->CRSMAE &= ~BIT(NPCX_CRSMAE_CFGAE);
/* Unlock host access EC configuration registers (0x4E/0x4F) */
inst_c2h->LKSIOHA &= ~BIT(NPCX_LKSIOHA_LKCFG);
/* Enable interrupts */
irq_unlock(key);
}
uint8_t host_c2h_read_io_cfg_reg(uint8_t reg_index)
{
struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h;
uint8_t data_val;
/* Disable interrupts */
unsigned int key = irq_lock();
/* Lock host access EC configuration registers (0x4E/0x4F) */
inst_c2h->LKSIOHA |= BIT(NPCX_LKSIOHA_LKCFG);
/* Enable Core-to-Host access CFG module */
inst_c2h->CRSMAE |= BIT(NPCX_CRSMAE_CFGAE);
/* Verify core-to-host modules is not in progress */
host_c2h_wait_read_done();
host_c2h_wait_write_done();
/*
* Specifying the in-direct IO address which A0 = 0 indicates the index
* register is accessed. Then write index address directly and it starts
* a write transaction to host sub-module on LPC/eSPI bus.
*/
inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR;
inst_c2h->IHD = reg_index;
host_c2h_wait_write_done();
/*
* Specifying the in-direct IO address which A0 = 1 indicates the data
* register is accessed. Then write CSRD bit in SIBCTRL to issue a read
* transaction to host sub-module on LPC/eSPI bus. Once it was done,
* read data out from IHD.
*/
inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR + 1;
inst_c2h->SIBCTRL |= BIT(NPCX_SIBCTRL_CSRD);
host_c2h_wait_read_done();
data_val = inst_c2h->IHD;
/* Disable Core-to-Host access CFG module */
inst_c2h->CRSMAE &= ~BIT(NPCX_CRSMAE_CFGAE);
/* Unlock host access EC configuration registers (0x4E/0x4F) */
inst_c2h->LKSIOHA &= ~BIT(NPCX_LKSIOHA_LKCFG);
/* Enable interrupts */
irq_unlock(key);
return data_val;
}
/* Platform specific host sub modules functions */
int npcx_host_periph_read_request(enum lpc_peripheral_opcode op,
uint32_t *data)
{
if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) {
struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc;
/* Make sure kbc 8042 is on */
if (!IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFKIE) ||
!IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFMIE)) {
return -ENOTSUP;
}
switch (op) {
case E8042_OBF_HAS_CHAR:
/* EC has written data back to host. OBF is
* automatically cleared after host reads
* the data
*/
*data = IS_BIT_SET(inst_kbc->HIKMST, NPCX_HIKMST_OBF);
break;
case E8042_IBF_HAS_CHAR:
*data = IS_BIT_SET(inst_kbc->HIKMST, NPCX_HIKMST_IBF);
break;
case E8042_READ_KB_STS:
*data = inst_kbc->HIKMST;
break;
default:
return -EINVAL;
}
} else if (op >= EACPI_START_OPCODE && op <= EACPI_MAX_OPCODE) {
struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi;
/* Make sure pm channel for apci is on */
if (!IS_BIT_SET(inst_acpi->HIPMCTL, NPCX_HIPMCTL_IBFIE)) {
return -ENOTSUP;
}
switch (op) {
case EACPI_OBF_HAS_CHAR:
/* EC has written data back to host. OBF is
* automatically cleared after host reads
* the data
*/
*data = IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_OBF);
break;
case EACPI_IBF_HAS_CHAR:
*data = IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_IBF);
break;
case EACPI_READ_STS:
*data = inst_acpi->HIPMST;
break;
#if defined(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION)
case EACPI_GET_SHARED_MEMORY:
*data = (uint32_t)shm_acpi_mmap;
break;
#endif /* CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION */
default:
return -EINVAL;
}
}
#if defined(CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE)
else if (op >= ECUSTOM_START_OPCODE && op <= ECUSTOM_MAX_OPCODE) {
/* Other customized op codes */
switch (op) {
case ECUSTOM_HOST_CMD_GET_PARAM_MEMORY:
*data = (uint32_t)shm_host_cmd;
break;
case ECUSTOM_HOST_CMD_GET_PARAM_MEMORY_SIZE:
*data = CONFIG_ESPI_NPCX_PERIPHERAL_HOST_CMD_PARAM_SIZE;
break;
default:
return -EINVAL;
}
}
#endif /* CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE */
else {
return -ENOTSUP;
}
return 0;
}
int npcx_host_periph_write_request(enum lpc_peripheral_opcode op,
const uint32_t *data)
{
volatile uint32_t __attribute__((unused)) dummy;
struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc;
if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) {
/* Make sure kbc 8042 is on */
if (!IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFKIE) ||
!IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFMIE)) {
return -ENOTSUP;
}
if (data) {
LOG_INF("%s: op 0x%x data %x", __func__, op, *data);
} else {
LOG_INF("%s: op 0x%x only", __func__, op);
}
switch (op) {
case E8042_WRITE_KB_CHAR:
inst_kbc->HIKDO = *data & 0xff;
/*
* Enable KBC OBE interrupt after putting data in
* keyboard data register.
*/
inst_kbc->HICTRL |= BIT(NPCX_HICTRL_OBECIE);
break;
case E8042_WRITE_MB_CHAR:
inst_kbc->HIMDO = *data & 0xff;
/*
* Enable KBC OBE interrupt after putting data in
* mouse data register.
*/
inst_kbc->HICTRL |= BIT(NPCX_HICTRL_OBECIE);
break;
case E8042_RESUME_IRQ:
/* Enable KBC IBF interrupt */
inst_kbc->HICTRL |= BIT(NPCX_HICTRL_IBFCIE);
break;
case E8042_PAUSE_IRQ:
/* Disable KBC IBF interrupt */
inst_kbc->HICTRL &= ~BIT(NPCX_HICTRL_IBFCIE);
break;
case E8042_CLEAR_OBF:
/* Clear OBF flag in both STATUS and HIKMST to 0 */
inst_kbc->HICTRL |= BIT(NPCX_HICTRL_FW_OBF);
break;
case E8042_SET_FLAG:
/* FW shouldn't modify these flags directly */
inst_kbc->HIKMST |= *data & ~NPCX_KBC_STS_MASK;
break;
case E8042_CLEAR_FLAG:
/* FW shouldn't modify these flags directly */
inst_kbc->HIKMST &= ~(*data | NPCX_KBC_STS_MASK);
break;
default:
return -EINVAL;
}
} else if (op >= EACPI_START_OPCODE && op <= EACPI_MAX_OPCODE) {
struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi;
/* Make sure pm channel for apci is on */
if (!IS_BIT_SET(inst_acpi->HIPMCTL, NPCX_HIPMCTL_IBFIE)) {
return -ENOTSUP;
}
switch (op) {
case EACPI_WRITE_CHAR:
inst_acpi->HIPMDO = (*data & 0xff);
break;
case EACPI_WRITE_STS:
inst_acpi->HIPMST = (*data & 0xff);
break;
default:
return -EINVAL;
}
}
#if defined(CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE)
else if (op >= ECUSTOM_START_OPCODE && op <= ECUSTOM_MAX_OPCODE) {
/* Other customized op codes */
struct pmch_reg *const inst_hcmd = host_sub_cfg.inst_pm_hcmd;
switch (op) {
case ECUSTOM_HOST_SUBS_INTERRUPT_EN:
if (*data != 0) {
host_cus_opcode_enable_interrupts();
} else {
host_cus_opcode_disable_interrupts();
}
break;
case ECUSTOM_HOST_CMD_SEND_RESULT:
/*
* Write result to the data byte. This sets the TOH
* status bit.
*/
inst_hcmd->HIPMDO = (*data & 0xff);
/* Clear processing flag */
inst_hcmd->HIPMST &= ~BIT(NPCX_HIPMST_F0);
break;
default:
return -EINVAL;
}
}
#endif /* CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE */
else {
return -ENOTSUP;
}
return 0;
}
void npcx_host_init_subs_host_domain(void)
{
struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h;
/* Enable Core-to-Host access module */
inst_c2h->SIBCTRL |= BIT(NPCX_SIBCTRL_CSAE);
if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC)) {
/*
* Select Keyboard/Mouse banks which LDN are 0x06/05 and enable
* modules by setting bit 0 in its Control (index is 0x30) reg.
*/
host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_KBC);
host_c2h_write_io_cfg_reg(EC_CFG_IDX_CTRL, 0x01);
host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_MOUSE);