| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2015 Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <arch/io.h> |
| #include <device/device.h> |
| #include <device/pci.h> |
| #include <soc/pcr.h> |
| #include <soc/iomap.h> |
| #include <console/console.h> |
| |
| static inline void *pcr_reg_address(u8 pid, u16 offset) |
| { |
| uintptr_t reg_addr; |
| |
| /* Create an address based off of port id and offset. */ |
| reg_addr = PCH_PCR_BASE_ADDRESS; |
| reg_addr += ((uintptr_t)pid) << PCR_PORTID_SHIFT; |
| reg_addr += ((uintptr_t)offset) << PCR_OFFSET_SHIFT; |
| |
| return (void *)reg_addr; |
| } |
| |
| uint8_t *pcr_port_regs(u8 pid) |
| { |
| return pcr_reg_address(pid, 0); |
| } |
| |
| /* |
| * Read PCR register. (This is internal function) |
| * It returns PCR register and size in 1/2/4 bytes. |
| * The offset should not exceed 0xFFFF and must be aligned with size |
| */ |
| static int pch_pcr_read(u8 pid, u16 offset, u32 size, void *data) |
| { |
| if ((offset & (size - 1)) != 0) { |
| printk(BIOS_DEBUG, |
| "PchPcrRead error. Invalid Offset: %x Size: %x", |
| offset, size); |
| return -1; |
| } |
| switch (size) { |
| case 4: |
| *(u32 *) data = read32(pcr_reg_address(pid, offset)); |
| break; |
| case 2: |
| *(u16 *) data = read16(pcr_reg_address(pid, offset)); |
| break; |
| case 1: |
| *(u8 *) data = read8(pcr_reg_address(pid, offset)); |
| break; |
| default: |
| return -1; |
| } |
| return 0; |
| } |
| |
| int pcr_read32(u8 pid, u16 offset, u32 *outdata) |
| { |
| return pch_pcr_read(pid, offset, sizeof(u32), (u32 *)outdata); |
| } |
| |
| int pcr_read16(u8 pid, u16 offset, u16 *outdata) |
| { |
| return pch_pcr_read(pid, offset, sizeof(u16), (u32 *)outdata); |
| } |
| |
| int pcr_read8(u8 pid, u16 offset, u8 *outdata) |
| { |
| return pch_pcr_read(pid, offset, sizeof(u8), (u32 *)outdata); |
| } |
| |
| /* |
| * After every write one needs to perform a read an innocuous register to |
| * ensure the writes are completed for certain ports. This is done for |
| * all ports so that the callers don't need the per-port knowledge for |
| * each transaction. |
| */ |
| static inline void complete_write(void) |
| { |
| /* Read the general control and function disable register. */ |
| const size_t R_PCH_PCR_LPC_GCFD = 0x3418; |
| read32(pcr_reg_address(PID_LPC, R_PCH_PCR_LPC_GCFD)); |
| } |
| |
| /* |
| * Write PCR register. (This is internal function) |
| * It returns PCR register and size in 1/2/4 bytes. |
| * The offset should not exceed 0xFFFF and must be aligned with size |
| */ |
| static int pch_pcr_write(u8 pid, u16 offset, u32 size, u32 data) |
| { |
| if ((offset & (size - 1)) != 0) { |
| printk(BIOS_DEBUG, |
| "PchPcrWrite error. Invalid Offset: %x Size: %x", |
| offset, size); |
| return -1; |
| } |
| /* Write the PCR register with provided data |
| * Then read back PCR register to prevent from back to back write. |
| */ |
| switch (size) { |
| case 4: |
| write32(pcr_reg_address(pid, offset), (u32) data); |
| break; |
| case 2: |
| write16(pcr_reg_address(pid, offset), (u16) data); |
| break; |
| case 1: |
| write8(pcr_reg_address(pid, offset), (u8) data); |
| break; |
| default: |
| return -1; |
| } |
| /* Ensure the writes complete. */ |
| complete_write(); |
| |
| return 0; |
| } |
| |
| int pcr_write32(u8 pid, u16 offset, u32 indata) |
| { |
| return pch_pcr_write(pid, offset, sizeof(u32), indata); |
| } |
| |
| int pcr_write16(u8 pid, u16 offset, u16 indata) |
| { |
| return pch_pcr_write(pid, offset, sizeof(u16), indata); |
| } |
| |
| int pcr_write8(u8 pid, u16 offset, u8 indata) |
| { |
| return pch_pcr_write(pid, offset, sizeof(u8), indata); |
| } |
| |
| /* |
| * Write PCR register. (This is internal function) |
| * It programs PCR register and size in 1/2/4 bytes. |
| * The offset should not exceed 0xFFFF and must be aligned with size |
| * |
| * u8 defines as 8 bit Port ID that will be used when sending |
| * transaction to sideband. |
| */ |
| static int pcr_and_then_or(u8 pid, u16 offset, u32 size, u32 anddata, |
| u32 ordata) |
| { |
| u8 status; |
| u32 data32 = 0; |
| |
| status = pch_pcr_read(pid, offset, size, &data32); |
| if (status != 0) |
| return -1; |
| |
| data32 &= anddata; |
| data32 |= ordata; |
| |
| status = pch_pcr_write(pid, offset, size, data32); |
| return status; |
| } |
| |
| int pcr_andthenor32(u8 pid, u16 offset, u32 anddata, u32 ordata) |
| { |
| return pcr_and_then_or(pid, offset, sizeof(u32), anddata, ordata); |
| } |
| |
| int pcr_andthenor16(u8 pid, u16 offset, u16 anddata, u16 ordata) |
| { |
| return pcr_and_then_or(pid, offset, sizeof(u16), anddata, ordata); |
| } |
| |
| int pcr_andthenor8(u8 pid, u16 offset, u8 anddata, u8 ordata) |
| { |
| return pcr_and_then_or(pid, offset, sizeof(u8), anddata, ordata); |
| } |