blob: 196b720c821625b06f64d56f682fc567c56e6852 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <assert.h>
#include <console/console.h>
#include <delay.h>
#include <northbridge/intel/haswell/haswell.h>
#include <northbridge/intel/haswell/vcu_mailbox.h>
#include <types.h>
/*
* This is a library for the VCU (Validation Control Unit) mailbox. This
* mailbox is primarily used to adjust some magic PCIe tuning parameters.
*
* There are two revisions of the VCU mailbox. Rev1 is specific to Haswell
* stepping A0, and all other steppings use Rev2. Haswell stepping A0 CPUs
* are early Engineering Samples with undocumented errata, and most likely
* need special microcode updates to boot. Thus, the code does not support
* VCU mailbox Rev1, because no one should need it anymore.
*/
#define VCU_MAILBOX_INTERFACE 0x6c00
#define VCU_MAILBOX_DATA 0x6c04
#define VCU_RUN_BUSY (1U << 31)
enum vcu_opcode {
VCU_INVALID_OPCODE = 0x00,
VCU_OPCODE_READ_VCU_API_VER_ID = 0x01,
VCU_OPCODE_OPEN_SEQ = 0x02,
VCU_OPCODE_CLOSE_SEQ = 0x03,
VCU_OPCODE_READ_DATA = 0x07,
VCU_OPCODE_WRITE_DATA = 0x08,
VCU_OPCODE_READ_CSR = 0x13,
VCU_OPCODE_WRITE_CSR = 0x14,
VCU_OPCODE_READ_MMIO = 0x15,
VCU_OPCODE_WRITE_MMIO = 0x16,
};
enum vcu_sequence {
SEQ_ID_READ_CSR = 0x1,
SEQ_ID_WRITE_CSR = 0x2,
SEQ_ID_READ_MMIO = 0x3,
SEQ_ID_WRITE_MMIO = 0x4,
};
#define VCU_RESPONSE_MASK 0xffff
#define VCU_RESPONSE_SUCCESS 0x40
#define VCU_RESPONSE_BUSY 0x80
#define VCU_RESPONSE_THREAD_UNAVAILABLE 0x82
#define VCU_RESPONSE_ILLEGAL 0x90
/* FIXME: Use timer API */
static void send_vcu_command(const enum vcu_opcode opcode, const uint32_t data)
{
if (opcode == VCU_INVALID_OPCODE)
return;
for (unsigned int i = 0; i < 10; i++) {
mchbar_write32(VCU_MAILBOX_DATA, data);
mchbar_write32(VCU_MAILBOX_INTERFACE, opcode | VCU_RUN_BUSY);
uint32_t vcu_interface;
for (unsigned int j = 0; j < 100; j++) {
vcu_interface = mchbar_read32(VCU_MAILBOX_INTERFACE);
if (!(vcu_interface & VCU_RUN_BUSY))
break;
udelay(10);
}
if (vcu_interface & VCU_RUN_BUSY)
continue;
if ((vcu_interface & VCU_RESPONSE_MASK) == VCU_RESPONSE_SUCCESS)
return;
}
printk(BIOS_ERR, "VCU: Failed to send command\n");
}
static enum vcu_opcode get_register_opcode(enum vcu_sequence seq)
{
switch (seq) {
case SEQ_ID_READ_CSR:
return VCU_OPCODE_READ_CSR;
case SEQ_ID_WRITE_CSR:
return VCU_OPCODE_WRITE_CSR;
case SEQ_ID_READ_MMIO:
return VCU_OPCODE_READ_MMIO;
case SEQ_ID_WRITE_MMIO:
return VCU_OPCODE_WRITE_MMIO;
default:
BUG();
return VCU_INVALID_OPCODE;
}
}
static enum vcu_opcode get_data_opcode(enum vcu_sequence seq)
{
switch (seq) {
case SEQ_ID_READ_CSR:
case SEQ_ID_READ_MMIO:
return VCU_OPCODE_READ_DATA;
case SEQ_ID_WRITE_CSR:
case SEQ_ID_WRITE_MMIO:
return VCU_OPCODE_WRITE_DATA;
default:
BUG();
return VCU_INVALID_OPCODE;
}
}
static uint32_t send_vcu_sequence(uint32_t addr, enum vcu_sequence seq, uint32_t wr_data)
{
send_vcu_command(VCU_OPCODE_OPEN_SEQ, seq);
send_vcu_command(get_register_opcode(seq), addr);
send_vcu_command(get_data_opcode(seq), wr_data);
const uint32_t rd_data = mchbar_read32(VCU_MAILBOX_DATA);
send_vcu_command(VCU_OPCODE_CLOSE_SEQ, seq);
return rd_data;
}
#define VCU_WRITE_IGNORED 0
uint32_t vcu_read_csr(uint32_t addr)
{
return send_vcu_sequence(addr, SEQ_ID_READ_CSR, VCU_WRITE_IGNORED);
}
void vcu_write_csr(uint32_t addr, uint32_t data)
{
send_vcu_sequence(addr, SEQ_ID_WRITE_CSR, data);
}
void vcu_update_csr(uint32_t addr, uint32_t andvalue, uint32_t orvalue)
{
vcu_write_csr(addr, (vcu_read_csr(addr) & andvalue) | orvalue);
}
uint32_t vcu_read_mmio(uint32_t addr)
{
return send_vcu_sequence(addr, SEQ_ID_READ_MMIO, VCU_WRITE_IGNORED);
}
void vcu_write_mmio(uint32_t addr, uint32_t data)
{
send_vcu_sequence(addr, SEQ_ID_WRITE_MMIO, data);
}
void vcu_update_mmio(uint32_t addr, uint32_t andvalue, uint32_t orvalue)
{
vcu_write_mmio(addr, (vcu_read_mmio(addr) & andvalue) | orvalue);
}