mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-11 13:56:30 +00:00
1e6f4e5886
Add a driver for the broadwell low-power platform controller hub. Signed-off-by: Simon Glass <sjg@chromium.org> Acked-by: Bin Meng <bmeng.cn@gmail.com>
144 lines
2.6 KiB
C
144 lines
2.6 KiB
C
/*
|
|
* Copyright (c) 2016 Google, Inc
|
|
*
|
|
* Modified from coreboot
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <errno.h>
|
|
#include <asm/intel_regs.h>
|
|
#include <asm/io.h>
|
|
#include <asm/arch/pch.h>
|
|
|
|
#define IOBP_RETRY 1000
|
|
|
|
/* IO Buffer Programming */
|
|
#define IOBPIRI 0x2330
|
|
#define IOBPD 0x2334
|
|
#define IOBPS 0x2338
|
|
#define IOBPS_READY 0x0001
|
|
#define IOBPS_TX_MASK 0x0006
|
|
#define IOBPS_MASK 0xff00
|
|
#define IOBPS_READ 0x0600
|
|
#define IOBPS_WRITE 0x0700
|
|
#define IOBPU 0x233a
|
|
#define IOBPU_MAGIC 0xf000
|
|
#define IOBP_PCICFG_READ 0x0400
|
|
#define IOBP_PCICFG_WRITE 0x0500
|
|
|
|
static inline int iobp_poll(void)
|
|
{
|
|
unsigned try;
|
|
|
|
for (try = IOBP_RETRY; try > 0; try--) {
|
|
u16 status = readw(RCB_REG(IOBPS));
|
|
if ((status & IOBPS_READY) == 0)
|
|
return 1;
|
|
udelay(10);
|
|
}
|
|
|
|
printf("IOBP: timeout waiting for transaction to complete\n");
|
|
return 0;
|
|
}
|
|
|
|
int pch_iobp_trans_start(u32 address, int op)
|
|
{
|
|
if (!iobp_poll())
|
|
return 0;
|
|
|
|
/* Set the address */
|
|
writel(address, RCB_REG(IOBPIRI));
|
|
|
|
/* READ OPCODE */
|
|
clrsetbits_le16(RCB_REG(IOBPS), IOBPS_MASK, op);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int pch_iobp_trans_finish(void)
|
|
{
|
|
u16 status;
|
|
|
|
/* Undocumented magic */
|
|
writew(IOBPU_MAGIC, RCB_REG(IOBPU));
|
|
|
|
/* Set ready bit */
|
|
setbits_le16(RCB_REG(IOBPS), IOBPS_READY);
|
|
|
|
if (!iobp_poll())
|
|
return 1;
|
|
|
|
/* Check for successful transaction */
|
|
status = readw(RCB_REG(IOBPS));
|
|
if (status & IOBPS_TX_MASK)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 pch_iobp_read(u32 address)
|
|
{
|
|
if (!pch_iobp_trans_start(address, IOBPS_READ))
|
|
return 0;
|
|
if (pch_iobp_trans_finish()) {
|
|
printf("IOBP: read 0x%08x failed\n", address);
|
|
return 0;
|
|
}
|
|
|
|
/* Read IOBP data */
|
|
return readl(RCB_REG(IOBPD));
|
|
}
|
|
|
|
int pch_iobp_write(u32 address, u32 data)
|
|
{
|
|
if (!pch_iobp_trans_start(address, IOBPS_WRITE))
|
|
return -EIO;
|
|
|
|
writel(data, RCB_REG(IOBPD));
|
|
|
|
if (pch_iobp_trans_finish()) {
|
|
printf("IOBP: write 0x%08x failed\n", address);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
|
|
{
|
|
u32 data = pch_iobp_read(address);
|
|
|
|
/* Update the data */
|
|
data &= andvalue;
|
|
data |= orvalue;
|
|
|
|
return pch_iobp_write(address, data);
|
|
}
|
|
|
|
int pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp)
|
|
{
|
|
if (!data || !resp)
|
|
return 0;
|
|
|
|
*resp = -1;
|
|
if (!iobp_poll())
|
|
return -EIO;
|
|
|
|
writel(addr, RCB_REG(IOBPIRI));
|
|
clrsetbits_le16(RCB_REG(IOBPS), 0xff00, op_code);
|
|
writew(IOBPU_MAGIC | route_id, RCB_REG(IOBPU));
|
|
|
|
writel(*data, RCB_REG(IOBPD));
|
|
/* Set IOBPS[0] to trigger IOBP transaction*/
|
|
setbits_le16(RCB_REG(IOBPS), 1);
|
|
|
|
if (!iobp_poll())
|
|
return -EIO;
|
|
|
|
*resp = (readw(RCB_REG(IOBPS)) & IOBPS_TX_MASK) >> 1;
|
|
*data = readl(RCB_REG(IOBPD));
|
|
|
|
return 0;
|
|
}
|