Duncan Laurie | c88c54c | 2014-04-30 16:36:13 -0700 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2014 Google Inc. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Duncan Laurie | c88c54c | 2014-04-30 16:36:13 -0700 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include <console/console.h> |
| 17 | #include <delay.h> |
| 18 | #include <arch/io.h> |
Julius Werner | 4ee4bd5 | 2014-10-20 13:46:39 -0700 | [diff] [blame] | 19 | #include <soc/iobp.h> |
| 20 | #include <soc/rcba.h> |
Duncan Laurie | c88c54c | 2014-04-30 16:36:13 -0700 | [diff] [blame] | 21 | |
| 22 | #define IOBP_RETRY 1000 |
| 23 | |
| 24 | static inline int iobp_poll(void) |
| 25 | { |
| 26 | unsigned try; |
| 27 | |
| 28 | for (try = IOBP_RETRY; try > 0; try--) { |
| 29 | u16 status = RCBA16(IOBPS); |
| 30 | if ((status & IOBPS_READY) == 0) |
| 31 | return 1; |
| 32 | udelay(10); |
| 33 | } |
| 34 | |
| 35 | printk(BIOS_ERR, "IOBP: timeout waiting for transaction to complete\n"); |
| 36 | return 0; |
| 37 | } |
| 38 | |
| 39 | u32 pch_iobp_read(u32 address) |
| 40 | { |
| 41 | u16 status; |
| 42 | |
| 43 | if (!iobp_poll()) |
| 44 | return 0; |
| 45 | |
| 46 | /* Set the address */ |
| 47 | RCBA32(IOBPIRI) = address; |
| 48 | |
| 49 | /* READ OPCODE */ |
| 50 | status = RCBA16(IOBPS); |
| 51 | status &= ~IOBPS_MASK; |
| 52 | status |= IOBPS_READ; |
| 53 | RCBA16(IOBPS) = status; |
| 54 | |
| 55 | /* Undocumented magic */ |
| 56 | RCBA16(IOBPU) = IOBPU_MAGIC; |
| 57 | |
| 58 | /* Set ready bit */ |
| 59 | status = RCBA16(IOBPS); |
| 60 | status |= IOBPS_READY; |
| 61 | RCBA16(IOBPS) = status; |
| 62 | |
| 63 | if (!iobp_poll()) |
| 64 | return 0; |
| 65 | |
| 66 | /* Check for successful transaction */ |
| 67 | status = RCBA16(IOBPS); |
| 68 | if (status & IOBPS_TX_MASK) { |
| 69 | printk(BIOS_ERR, "IOBP: read 0x%08x failed\n", address); |
| 70 | return 0; |
| 71 | } |
| 72 | |
| 73 | /* Read IOBP data */ |
| 74 | return RCBA32(IOBPD); |
| 75 | } |
| 76 | |
| 77 | void pch_iobp_write(u32 address, u32 data) |
| 78 | { |
| 79 | u16 status; |
| 80 | |
| 81 | if (!iobp_poll()) |
| 82 | return; |
| 83 | |
| 84 | /* Set the address */ |
| 85 | RCBA32(IOBPIRI) = address; |
| 86 | |
| 87 | /* WRITE OPCODE */ |
| 88 | status = RCBA16(IOBPS); |
| 89 | status &= ~IOBPS_MASK; |
| 90 | status |= IOBPS_WRITE; |
| 91 | RCBA16(IOBPS) = status; |
| 92 | |
| 93 | RCBA32(IOBPD) = data; |
| 94 | |
| 95 | /* Undocumented magic */ |
| 96 | RCBA16(IOBPU) = IOBPU_MAGIC; |
| 97 | |
| 98 | /* Set ready bit */ |
| 99 | status = RCBA16(IOBPS); |
| 100 | status |= IOBPS_READY; |
| 101 | RCBA16(IOBPS) = status; |
| 102 | |
| 103 | if (!iobp_poll()) |
| 104 | return; |
| 105 | |
| 106 | /* Check for successful transaction */ |
| 107 | status = RCBA16(IOBPS); |
| 108 | if (status & IOBPS_TX_MASK) |
| 109 | printk(BIOS_ERR, "IOBP: write 0x%08x failed\n", address); |
| 110 | } |
| 111 | |
| 112 | void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) |
| 113 | { |
| 114 | u32 data = pch_iobp_read(address); |
| 115 | |
| 116 | /* Update the data */ |
| 117 | data &= andvalue; |
| 118 | data |= orvalue; |
| 119 | |
| 120 | pch_iobp_write(address, data); |
| 121 | } |
Kenji Chen | e383feb | 2014-09-26 03:14:57 +0800 | [diff] [blame] | 122 | |
| 123 | void pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp) |
| 124 | { |
| 125 | if (!data || !resp) |
| 126 | return; |
| 127 | |
| 128 | *resp = -1; |
| 129 | if (!iobp_poll()) |
| 130 | return; |
| 131 | |
| 132 | /* RCBA2330[31:0] = Address */ |
| 133 | RCBA32(IOBPIRI) = addr; |
| 134 | /* RCBA2338[15:8] = opcode */ |
| 135 | RCBA16(IOBPS) = (RCBA16(IOBPS) & 0x00ff) | op_code; |
| 136 | /* RCBA233A[15:8] = 0xf0 RCBA233A[7:0] = Route ID */ |
| 137 | RCBA16(IOBPU) = IOBPU_MAGIC | route_id; |
| 138 | |
Martin Roth | 2b2ff7f | 2015-12-18 10:46:59 -0700 | [diff] [blame] | 139 | if (op_code == IOBP_PCICFG_WRITE) |
| 140 | RCBA32(IOBPD) = *data; |
Kenji Chen | e383feb | 2014-09-26 03:14:57 +0800 | [diff] [blame] | 141 | /* Set RCBA2338[0] to trigger IOBP transaction*/ |
| 142 | RCBA16(IOBPS) = RCBA16(IOBPS) | 0x1; |
| 143 | |
| 144 | if (!iobp_poll()) |
| 145 | return; |
| 146 | |
| 147 | *resp = (RCBA16(IOBPS) & IOBPS_TX_MASK) >> 1; |
| 148 | *data = RCBA32(IOBPD); |
| 149 | } |