blob: 196b720c821625b06f64d56f682fc567c56e6852 [file] [log] [blame]
Angel Pons567ece42022-05-06 21:56:48 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <assert.h>
4#include <console/console.h>
5#include <delay.h>
6#include <northbridge/intel/haswell/haswell.h>
7#include <northbridge/intel/haswell/vcu_mailbox.h>
8#include <types.h>
9
10/*
11 * This is a library for the VCU (Validation Control Unit) mailbox. This
12 * mailbox is primarily used to adjust some magic PCIe tuning parameters.
13 *
14 * There are two revisions of the VCU mailbox. Rev1 is specific to Haswell
15 * stepping A0, and all other steppings use Rev2. Haswell stepping A0 CPUs
16 * are early Engineering Samples with undocumented errata, and most likely
17 * need special microcode updates to boot. Thus, the code does not support
18 * VCU mailbox Rev1, because no one should need it anymore.
19 */
20
21#define VCU_MAILBOX_INTERFACE 0x6c00
22#define VCU_MAILBOX_DATA 0x6c04
23
24#define VCU_RUN_BUSY (1U << 31)
25
26enum vcu_opcode {
27 VCU_INVALID_OPCODE = 0x00,
28 VCU_OPCODE_READ_VCU_API_VER_ID = 0x01,
29 VCU_OPCODE_OPEN_SEQ = 0x02,
30 VCU_OPCODE_CLOSE_SEQ = 0x03,
31 VCU_OPCODE_READ_DATA = 0x07,
32 VCU_OPCODE_WRITE_DATA = 0x08,
33 VCU_OPCODE_READ_CSR = 0x13,
34 VCU_OPCODE_WRITE_CSR = 0x14,
35 VCU_OPCODE_READ_MMIO = 0x15,
36 VCU_OPCODE_WRITE_MMIO = 0x16,
37};
38
39enum vcu_sequence {
40 SEQ_ID_READ_CSR = 0x1,
41 SEQ_ID_WRITE_CSR = 0x2,
42 SEQ_ID_READ_MMIO = 0x3,
43 SEQ_ID_WRITE_MMIO = 0x4,
44};
45
46#define VCU_RESPONSE_MASK 0xffff
47#define VCU_RESPONSE_SUCCESS 0x40
48#define VCU_RESPONSE_BUSY 0x80
49#define VCU_RESPONSE_THREAD_UNAVAILABLE 0x82
50#define VCU_RESPONSE_ILLEGAL 0x90
51
52/* FIXME: Use timer API */
53static void send_vcu_command(const enum vcu_opcode opcode, const uint32_t data)
54{
55 if (opcode == VCU_INVALID_OPCODE)
56 return;
57
58 for (unsigned int i = 0; i < 10; i++) {
59 mchbar_write32(VCU_MAILBOX_DATA, data);
60 mchbar_write32(VCU_MAILBOX_INTERFACE, opcode | VCU_RUN_BUSY);
61 uint32_t vcu_interface;
62 for (unsigned int j = 0; j < 100; j++) {
63 vcu_interface = mchbar_read32(VCU_MAILBOX_INTERFACE);
64 if (!(vcu_interface & VCU_RUN_BUSY))
65 break;
66
67 udelay(10);
68 }
69 if (vcu_interface & VCU_RUN_BUSY)
70 continue;
71
72 if ((vcu_interface & VCU_RESPONSE_MASK) == VCU_RESPONSE_SUCCESS)
73 return;
74 }
75 printk(BIOS_ERR, "VCU: Failed to send command\n");
76}
77
78static enum vcu_opcode get_register_opcode(enum vcu_sequence seq)
79{
80 switch (seq) {
81 case SEQ_ID_READ_CSR:
82 return VCU_OPCODE_READ_CSR;
83 case SEQ_ID_WRITE_CSR:
84 return VCU_OPCODE_WRITE_CSR;
85 case SEQ_ID_READ_MMIO:
86 return VCU_OPCODE_READ_MMIO;
87 case SEQ_ID_WRITE_MMIO:
88 return VCU_OPCODE_WRITE_MMIO;
89 default:
90 BUG();
91 return VCU_INVALID_OPCODE;
92 }
93}
94
95static enum vcu_opcode get_data_opcode(enum vcu_sequence seq)
96{
97 switch (seq) {
98 case SEQ_ID_READ_CSR:
99 case SEQ_ID_READ_MMIO:
100 return VCU_OPCODE_READ_DATA;
101 case SEQ_ID_WRITE_CSR:
102 case SEQ_ID_WRITE_MMIO:
103 return VCU_OPCODE_WRITE_DATA;
104 default:
105 BUG();
106 return VCU_INVALID_OPCODE;
107 }
108}
109
110static uint32_t send_vcu_sequence(uint32_t addr, enum vcu_sequence seq, uint32_t wr_data)
111{
112 send_vcu_command(VCU_OPCODE_OPEN_SEQ, seq);
113
114 send_vcu_command(get_register_opcode(seq), addr);
115
116 send_vcu_command(get_data_opcode(seq), wr_data);
117
118 const uint32_t rd_data = mchbar_read32(VCU_MAILBOX_DATA);
119
120 send_vcu_command(VCU_OPCODE_CLOSE_SEQ, seq);
121
122 return rd_data;
123}
124
125#define VCU_WRITE_IGNORED 0
126
127uint32_t vcu_read_csr(uint32_t addr)
128{
129 return send_vcu_sequence(addr, SEQ_ID_READ_CSR, VCU_WRITE_IGNORED);
130}
131
132void vcu_write_csr(uint32_t addr, uint32_t data)
133{
134 send_vcu_sequence(addr, SEQ_ID_WRITE_CSR, data);
135}
136
137void vcu_update_csr(uint32_t addr, uint32_t andvalue, uint32_t orvalue)
138{
139 vcu_write_csr(addr, (vcu_read_csr(addr) & andvalue) | orvalue);
140}
141
142uint32_t vcu_read_mmio(uint32_t addr)
143{
144 return send_vcu_sequence(addr, SEQ_ID_READ_MMIO, VCU_WRITE_IGNORED);
145}
146
147void vcu_write_mmio(uint32_t addr, uint32_t data)
148{
149 send_vcu_sequence(addr, SEQ_ID_WRITE_MMIO, data);
150}
151
152void vcu_update_mmio(uint32_t addr, uint32_t andvalue, uint32_t orvalue)
153{
154 vcu_write_mmio(addr, (vcu_read_mmio(addr) & andvalue) | orvalue);
155}