1 // SPDX-License-Identifier: GPL-2.0 2 // LPC variant I/O for Microchip EC 3 // 4 // Copyright (C) 2016 Google, Inc 5 6 #include <linux/delay.h> 7 #include <linux/io.h> 8 #include <linux/mutex.h> 9 #include <linux/types.h> 10 11 #include "cros_ec_lpc_mec.h" 12 13 /* 14 * This mutex must be held while accessing the EMI unit. We can't rely on the 15 * EC mutex because memmap data may be accessed without it being held. 16 */ 17 static struct mutex io_mutex; 18 static u16 mec_emi_base, mec_emi_end; 19 20 /* 21 * cros_ec_lpc_mec_emi_write_address 22 * 23 * Initialize EMI read / write at a given address. 24 * 25 * @addr: Starting read / write address 26 * @access_type: Type of access, typically 32-bit auto-increment 27 */ 28 static void cros_ec_lpc_mec_emi_write_address(u16 addr, 29 enum cros_ec_lpc_mec_emi_access_mode access_type) 30 { 31 outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base)); 32 outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(mec_emi_base)); 33 } 34 35 /** 36 * cros_ec_lpc_mec_in_range() - Determine if addresses are in MEC EMI range. 37 * 38 * @offset: Address offset 39 * @length: Number of bytes to check 40 * 41 * Return: 1 if in range, 0 if not, and -EINVAL on failure 42 * such as the mec range not being initialized 43 */ 44 int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length) 45 { 46 if (length == 0) 47 return -EINVAL; 48 49 if (WARN_ON(mec_emi_base == 0 || mec_emi_end == 0)) 50 return -EINVAL; 51 52 if (offset >= mec_emi_base && offset < mec_emi_end) { 53 if (WARN_ON(offset + length - 1 >= mec_emi_end)) 54 return -EINVAL; 55 return 1; 56 } 57 58 if (WARN_ON(offset + length > mec_emi_base && offset < mec_emi_end)) 59 return -EINVAL; 60 61 return 0; 62 } 63 64 /* 65 * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port 66 * 67 * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request 68 * @offset: Base read / write address 69 * @length: Number of bytes to read / write 70 * @buf: Destination / source buffer 71 * 72 * @return 8-bit checksum of all bytes read / written 73 */ 74 u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, 75 unsigned int offset, unsigned int length, 76 u8 *buf) 77 { 78 int i = 0; 79 int io_addr; 80 u8 sum = 0; 81 enum cros_ec_lpc_mec_emi_access_mode access, new_access; 82 83 /* Return checksum of 0 if window is not initialized */ 84 WARN_ON(mec_emi_base == 0 || mec_emi_end == 0); 85 if (mec_emi_base == 0 || mec_emi_end == 0) 86 return 0; 87 88 /* 89 * Long access cannot be used on misaligned data since reading B0 loads 90 * the data register and writing B3 flushes. 91 */ 92 if (offset & 0x3 || length < 4) 93 access = ACCESS_TYPE_BYTE; 94 else 95 access = ACCESS_TYPE_LONG_AUTO_INCREMENT; 96 97 mutex_lock(&io_mutex); 98 99 /* Initialize I/O at desired address */ 100 cros_ec_lpc_mec_emi_write_address(offset, access); 101 102 /* Skip bytes in case of misaligned offset */ 103 io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base) + (offset & 0x3); 104 while (i < length) { 105 while (io_addr <= MEC_EMI_EC_DATA_B3(mec_emi_base)) { 106 if (io_type == MEC_IO_READ) 107 buf[i] = inb(io_addr++); 108 else 109 outb(buf[i], io_addr++); 110 111 sum += buf[i++]; 112 offset++; 113 114 /* Extra bounds check in case of misaligned length */ 115 if (i == length) 116 goto done; 117 } 118 119 /* 120 * Use long auto-increment access except for misaligned write, 121 * since writing B3 triggers the flush. 122 */ 123 if (length - i < 4 && io_type == MEC_IO_WRITE) 124 new_access = ACCESS_TYPE_BYTE; 125 else 126 new_access = ACCESS_TYPE_LONG_AUTO_INCREMENT; 127 128 if (new_access != access || 129 access != ACCESS_TYPE_LONG_AUTO_INCREMENT) { 130 access = new_access; 131 cros_ec_lpc_mec_emi_write_address(offset, access); 132 } 133 134 /* Access [B0, B3] on each loop pass */ 135 io_addr = MEC_EMI_EC_DATA_B0(mec_emi_base); 136 } 137 138 done: 139 mutex_unlock(&io_mutex); 140 141 return sum; 142 } 143 EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec); 144 145 void cros_ec_lpc_mec_init(unsigned int base, unsigned int end) 146 { 147 mutex_init(&io_mutex); 148 mec_emi_base = base; 149 mec_emi_end = end; 150 } 151 EXPORT_SYMBOL(cros_ec_lpc_mec_init); 152 153 void cros_ec_lpc_mec_destroy(void) 154 { 155 mutex_destroy(&io_mutex); 156 } 157 EXPORT_SYMBOL(cros_ec_lpc_mec_destroy); 158