/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include "include/ec.h" /* * Notes: * - ACPI "CMDB": Writing to this offset is equivalent to sending commands. * The CMDx bytes contain the command parameters. * * TODO - Implement: * - Commands: 0x58, 0xE1 and 0xE2 * - 0x51, 0x52: EC flash write? * - ACPI CMDB: 0x63 and 0x64, 0xC7 * - 0x0B: Flash lock/write (Set offset 0x0B?) * - Key/recovery detection? * * Vendor's protocols: * - Only read and write are used. * - Query, ACPI "CMDB" processing and command 58 are unused. * - Equivalent KbcPeim is an unused PPI. * * NB: Also look for potential EC library */ #define EC_INDEX_IO_PORT 0x1200 #define EC_INDEX_IO_HIGH_ADDR_PORT (EC_INDEX_IO_PORT + 1) #define EC_INDEX_IO_LOW_ADDR_PORT (EC_INDEX_IO_PORT + 2) #define EC_INDEX_IO_DATA_PORT (EC_INDEX_IO_PORT + 3) uint8_t ec_cmd_90_read(uint8_t addr) { /* EC ports: 0x62/0x66 */ send_ec_command(0x90); send_ec_data(addr); return recv_ec_data(); } void ec_cmd_91_write(uint8_t addr, uint8_t data) { /* EC ports: 0x62/0x66 */ send_ec_command(0x91); send_ec_data(addr); send_ec_data(data); } uint8_t ec_cmd_94_query(void) { send_ec_command(0x94); return recv_ec_data(); } uint8_t ec_idx_read(uint16_t addr) { outb((uint8_t)(addr >> 8), EC_INDEX_IO_HIGH_ADDR_PORT); outb((uint8_t)addr, EC_INDEX_IO_LOW_ADDR_PORT); return inb(EC_INDEX_IO_DATA_PORT); } void ec_idx_write(uint16_t addr, uint8_t data) { outb((uint8_t)(addr >> 8), EC_INDEX_IO_HIGH_ADDR_PORT); outb((uint8_t)addr, EC_INDEX_IO_LOW_ADDR_PORT); outb(data, EC_INDEX_IO_DATA_PORT); } /* TODO: Check if ADC is valid. Are there 4, or actually 8 ADCs? */ uint16_t read_ec_adc_converter(uint8_t adc) { uint8_t adc_converters_enabled; // Contains some ADCs and some DACs uint8_t idx_data; uint16_t adc_data; /* Backup enabled ADCs */ adc_converters_enabled = ec_idx_read(0xff15); // ADDAEN /* Enable desired ADC in bitmask (not enabled by EC FW, not used by vendor FW) */ ec_idx_write(0xff15, adc_converters_enabled | ((1 << adc) & 0xf)); // ADDAEN /* Sample the desired ADC in binary field; OR the start bit */ ec_idx_write(0xff18, ((adc << 1) & 0xf) | 1); // ADCTRL /* Read the desired ADC */ idx_data = ec_idx_read(0xff19); // ADCDAT adc_data = (idx_data << 2); /* Lower 2-bits of 10-bit ADC are in high bits of next register */ idx_data = ec_idx_read(0xff1a); // ECIF adc_data |= ((idx_data & 0xc0) >> 6); /* Restore enabled ADCs */ ec_idx_write(0xff15, adc_converters_enabled); // ADDAEN return adc_data; }