| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * Copyright (c) 2016 Google, Inc | 
 |  * | 
 |  * Modified from coreboot | 
 |  */ | 
 |  | 
 | #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; | 
 | } |