Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2015 Google Inc. All rights reserved. |
| 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. |
Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include <arch/io.h> |
| 17 | #include "ec.h" |
| 18 | #include "ec_commands.h" |
| 19 | |
| 20 | enum { |
| 21 | /* 8-bit access */ |
| 22 | ACCESS_TYPE_BYTE = 0x0, |
| 23 | /* 16-bit access */ |
| 24 | ACCESS_TYPE_WORD = 0x1, |
| 25 | /* 32-bit access */ |
| 26 | ACCESS_TYPE_LONG = 0x2, |
| 27 | /* |
| 28 | * 32-bit access, read or write of MEC_EMI_EC_DATA_B3 causes the |
| 29 | * EC data register to be incremented. |
| 30 | */ |
| 31 | ACCESS_TYPE_LONG_AUTO_INCREMENT = 0x3, |
| 32 | }; |
| 33 | |
| 34 | /* EMI registers are relative to base */ |
| 35 | #define MEC_EMI_BASE 0x800 |
| 36 | #define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0) |
| 37 | #define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1) |
| 38 | #define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2) |
| 39 | #define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3) |
| 40 | #define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4) |
| 41 | #define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5) |
| 42 | #define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6) |
| 43 | #define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7) |
| 44 | |
| 45 | /* |
| 46 | * cros_ec_lpc_mec_emi_write_address |
| 47 | * |
| 48 | * Initialize EMI read / write at a given address. |
| 49 | * |
| 50 | * @addr: Starting read / write address |
| 51 | * @access_mode: Type of access, typically 32-bit auto-increment |
| 52 | */ |
| 53 | static void mec_emi_write_address(u16 addr, u8 access_mode) |
| 54 | { |
| 55 | /* Address relative to start of EMI range */ |
| 56 | addr -= MEC_EMI_RANGE_START; |
| 57 | outb((addr & 0xfc) | access_mode, MEC_EMI_EC_ADDRESS_B0); |
| 58 | outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1); |
| 59 | } |
| 60 | |
| 61 | /* |
| 62 | * mec_io_bytes - Read / write bytes to MEC EMI port |
| 63 | * |
| 64 | * @write: 1 on write operation, 0 on read |
| 65 | * @port: Base read / write address |
| 66 | * @length: Number of bytes to read / write |
| 67 | * @buf: Destination / source buffer |
| 68 | * @csum: Optional parameter, sums data transferred |
| 69 | * |
| 70 | */ |
| 71 | void mec_io_bytes(int write, u16 port, unsigned int length, u8 *buf, u8 *csum) |
| 72 | { |
| 73 | int i = 0; |
| 74 | int io_addr; |
Shawn Nematbakhsh | 16fb9b9 | 2015-04-21 09:30:10 -0700 | [diff] [blame] | 75 | u8 access_mode, new_access_mode; |
Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 76 | |
| 77 | if (length == 0) |
| 78 | return; |
| 79 | |
Shawn Nematbakhsh | 16fb9b9 | 2015-04-21 09:30:10 -0700 | [diff] [blame] | 80 | /* |
| 81 | * Long access cannot be used on misaligned data since reading B0 loads |
| 82 | * the data register and writing B3 flushes it. |
| 83 | */ |
Jagadish Krishnamoorthy | f4e9eb9 | 2015-07-18 11:46:37 -0700 | [diff] [blame] | 84 | if ((port & 0x3) || (length < 4)) |
Shawn Nematbakhsh | 16fb9b9 | 2015-04-21 09:30:10 -0700 | [diff] [blame] | 85 | access_mode = ACCESS_TYPE_BYTE; |
| 86 | else |
| 87 | access_mode = ACCESS_TYPE_LONG_AUTO_INCREMENT; |
| 88 | |
Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 89 | /* Initialize I/O at desired address */ |
Shawn Nematbakhsh | 16fb9b9 | 2015-04-21 09:30:10 -0700 | [diff] [blame] | 90 | mec_emi_write_address(port, access_mode); |
Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 91 | |
| 92 | /* Skip bytes in case of misaligned port */ |
| 93 | io_addr = MEC_EMI_EC_DATA_B0 + (port & 0x3); |
| 94 | while (i < length) { |
| 95 | while (io_addr <= MEC_EMI_EC_DATA_B3) { |
| 96 | if (write) |
| 97 | outb(buf[i], io_addr++); |
| 98 | else |
| 99 | buf[i] = inb(io_addr++); |
| 100 | if (csum) |
| 101 | *csum += buf[i]; |
| 102 | |
Shawn Nematbakhsh | 16fb9b9 | 2015-04-21 09:30:10 -0700 | [diff] [blame] | 103 | port++; |
Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 104 | /* Extra bounds check in case of misaligned length */ |
| 105 | if (++i == length) |
| 106 | return; |
| 107 | } |
| 108 | |
Shawn Nematbakhsh | 16fb9b9 | 2015-04-21 09:30:10 -0700 | [diff] [blame] | 109 | /* |
| 110 | * Use long auto-increment access except for misaligned write, |
| 111 | * since writing B3 triggers the flush. |
| 112 | */ |
| 113 | if (length - i < 4 && write) |
| 114 | new_access_mode = ACCESS_TYPE_BYTE; |
| 115 | else |
| 116 | new_access_mode = ACCESS_TYPE_LONG_AUTO_INCREMENT; |
| 117 | if (new_access_mode != access_mode || |
| 118 | access_mode != ACCESS_TYPE_LONG_AUTO_INCREMENT) { |
| 119 | access_mode = new_access_mode; |
| 120 | mec_emi_write_address(port, access_mode); |
| 121 | } |
| 122 | |
Shawn Nematbakhsh | 5725ea3 | 2015-04-01 16:52:37 -0700 | [diff] [blame] | 123 | /* Access [B0, B3] on each loop pass */ |
| 124 | io_addr = MEC_EMI_EC_DATA_B0; |
| 125 | } |
| 126 | } |