11058ca94SEnric Balletbo i Serra // SPDX-License-Identifier: GPL-2.0 21058ca94SEnric Balletbo i Serra // LPC interface for ChromeOS Embedded Controller 31058ca94SEnric Balletbo i Serra // 41058ca94SEnric Balletbo i Serra // Copyright (C) 2012-2015 Google, Inc 51058ca94SEnric Balletbo i Serra // 61058ca94SEnric Balletbo i Serra // This driver uses the ChromeOS EC byte-level message-based protocol for 71058ca94SEnric Balletbo i Serra // communicating the keyboard state (which keys are pressed) from a keyboard EC 81058ca94SEnric Balletbo i Serra // to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, 91058ca94SEnric Balletbo i Serra // but everything else (including deghosting) is done here. The main 101058ca94SEnric Balletbo i Serra // motivation for this is to keep the EC firmware as simple as possible, since 111058ca94SEnric Balletbo i Serra // it cannot be easily upgraded and EC flash/IRAM space is relatively 121058ca94SEnric Balletbo i Serra // expensive. 13ec2f33abSBill Richardson 1412278dc7SGwendal Grignou #include <linux/acpi.h> 15ec2f33abSBill Richardson #include <linux/dmi.h> 16ec2f33abSBill Richardson #include <linux/delay.h> 1718d0dc24SJavier Martinez Canillas #include <linux/io.h> 18da1cf5a1SEnrico Granata #include <linux/interrupt.h> 19ec2f33abSBill Richardson #include <linux/module.h> 20840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h> 21840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h> 22ec2f33abSBill Richardson #include <linux/platform_device.h> 23ec2f33abSBill Richardson #include <linux/printk.h> 24d6542b39SWenkai Du #include <linux/suspend.h> 25ec2f33abSBill Richardson 26034dbec1SEnric Balletbo i Serra #include "cros_ec.h" 274116fd25SEnric Balletbo i Serra #include "cros_ec_lpc_mec.h" 28cc8a4ea1SEnric Balletbo i Serra 29bce70fefSShawn Nematbakhsh #define DRV_NAME "cros_ec_lpcs" 3012278dc7SGwendal Grignou #define ACPI_DRV_NAME "GOOG0004" 31ec2f33abSBill Richardson 325f454bdfSEnric Balletbo i Serra /* True if ACPI device is present */ 335f454bdfSEnric Balletbo i Serra static bool cros_ec_lpc_acpi_device_found; 345f454bdfSEnric Balletbo i Serra 3522c040faSEnric Balletbo i Serra /** 3622c040faSEnric Balletbo i Serra * struct lpc_driver_ops - LPC driver operations 3722c040faSEnric Balletbo i Serra * @read: Copy length bytes from EC address offset into buffer dest. Returns 3822c040faSEnric Balletbo i Serra * the 8-bit checksum of all bytes read. 3922c040faSEnric Balletbo i Serra * @write: Copy length bytes from buffer msg into EC address offset. Returns 4022c040faSEnric Balletbo i Serra * the 8-bit checksum of all bytes written. 4122c040faSEnric Balletbo i Serra */ 4222c040faSEnric Balletbo i Serra struct lpc_driver_ops { 4322c040faSEnric Balletbo i Serra u8 (*read)(unsigned int offset, unsigned int length, u8 *dest); 4422c040faSEnric Balletbo i Serra u8 (*write)(unsigned int offset, unsigned int length, const u8 *msg); 4522c040faSEnric Balletbo i Serra }; 4622c040faSEnric Balletbo i Serra 4722c040faSEnric Balletbo i Serra static struct lpc_driver_ops cros_ec_lpc_ops = { }; 4822c040faSEnric Balletbo i Serra 4922c040faSEnric Balletbo i Serra /* 5022c040faSEnric Balletbo i Serra * A generic instance of the read function of struct lpc_driver_ops, used for 5122c040faSEnric Balletbo i Serra * the LPC EC. 5222c040faSEnric Balletbo i Serra */ 5322c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, 5422c040faSEnric Balletbo i Serra u8 *dest) 554116fd25SEnric Balletbo i Serra { 564116fd25SEnric Balletbo i Serra int sum = 0; 574116fd25SEnric Balletbo i Serra int i; 584116fd25SEnric Balletbo i Serra 594116fd25SEnric Balletbo i Serra for (i = 0; i < length; ++i) { 604116fd25SEnric Balletbo i Serra dest[i] = inb(offset + i); 614116fd25SEnric Balletbo i Serra sum += dest[i]; 624116fd25SEnric Balletbo i Serra } 634116fd25SEnric Balletbo i Serra 644116fd25SEnric Balletbo i Serra /* Return checksum of all bytes read */ 654116fd25SEnric Balletbo i Serra return sum; 664116fd25SEnric Balletbo i Serra } 674116fd25SEnric Balletbo i Serra 6822c040faSEnric Balletbo i Serra /* 6922c040faSEnric Balletbo i Serra * A generic instance of the write function of struct lpc_driver_ops, used for 7022c040faSEnric Balletbo i Serra * the LPC EC. 7122c040faSEnric Balletbo i Serra */ 7222c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, 7322c040faSEnric Balletbo i Serra const u8 *msg) 744116fd25SEnric Balletbo i Serra { 754116fd25SEnric Balletbo i Serra int sum = 0; 764116fd25SEnric Balletbo i Serra int i; 774116fd25SEnric Balletbo i Serra 784116fd25SEnric Balletbo i Serra for (i = 0; i < length; ++i) { 794116fd25SEnric Balletbo i Serra outb(msg[i], offset + i); 804116fd25SEnric Balletbo i Serra sum += msg[i]; 814116fd25SEnric Balletbo i Serra } 824116fd25SEnric Balletbo i Serra 834116fd25SEnric Balletbo i Serra /* Return checksum of all bytes written */ 844116fd25SEnric Balletbo i Serra return sum; 854116fd25SEnric Balletbo i Serra } 864116fd25SEnric Balletbo i Serra 8722c040faSEnric Balletbo i Serra /* 8822c040faSEnric Balletbo i Serra * An instance of the read function of struct lpc_driver_ops, used for the 8922c040faSEnric Balletbo i Serra * MEC variant of LPC EC. 9022c040faSEnric Balletbo i Serra */ 9122c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length, 924116fd25SEnric Balletbo i Serra u8 *dest) 934116fd25SEnric Balletbo i Serra { 944116fd25SEnric Balletbo i Serra int in_range = cros_ec_lpc_mec_in_range(offset, length); 954116fd25SEnric Balletbo i Serra 964116fd25SEnric Balletbo i Serra if (in_range < 0) 974116fd25SEnric Balletbo i Serra return 0; 984116fd25SEnric Balletbo i Serra 994116fd25SEnric Balletbo i Serra return in_range ? 1004116fd25SEnric Balletbo i Serra cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 1014116fd25SEnric Balletbo i Serra offset - EC_HOST_CMD_REGION0, 1024116fd25SEnric Balletbo i Serra length, dest) : 10322c040faSEnric Balletbo i Serra cros_ec_lpc_read_bytes(offset, length, dest); 1044116fd25SEnric Balletbo i Serra } 1054116fd25SEnric Balletbo i Serra 10622c040faSEnric Balletbo i Serra /* 10722c040faSEnric Balletbo i Serra * An instance of the write function of struct lpc_driver_ops, used for the 10822c040faSEnric Balletbo i Serra * MEC variant of LPC EC. 10922c040faSEnric Balletbo i Serra */ 11022c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length, 11122c040faSEnric Balletbo i Serra const u8 *msg) 1124116fd25SEnric Balletbo i Serra { 1134116fd25SEnric Balletbo i Serra int in_range = cros_ec_lpc_mec_in_range(offset, length); 1144116fd25SEnric Balletbo i Serra 1154116fd25SEnric Balletbo i Serra if (in_range < 0) 1164116fd25SEnric Balletbo i Serra return 0; 1174116fd25SEnric Balletbo i Serra 1184116fd25SEnric Balletbo i Serra return in_range ? 1194116fd25SEnric Balletbo i Serra cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 1204116fd25SEnric Balletbo i Serra offset - EC_HOST_CMD_REGION0, 12122c040faSEnric Balletbo i Serra length, (u8 *)msg) : 12222c040faSEnric Balletbo i Serra cros_ec_lpc_write_bytes(offset, length, msg); 1234116fd25SEnric Balletbo i Serra } 1244116fd25SEnric Balletbo i Serra 125ec2f33abSBill Richardson static int ec_response_timed_out(void) 126ec2f33abSBill Richardson { 127ec2f33abSBill Richardson unsigned long one_second = jiffies + HZ; 128bce70fefSShawn Nematbakhsh u8 data; 129ec2f33abSBill Richardson 130ec2f33abSBill Richardson usleep_range(200, 300); 131ec2f33abSBill Richardson do { 13222c040faSEnric Balletbo i Serra if (!(cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data) & 133bce70fefSShawn Nematbakhsh EC_LPC_STATUS_BUSY_MASK)) 134ec2f33abSBill Richardson return 0; 135ec2f33abSBill Richardson usleep_range(100, 200); 136ec2f33abSBill Richardson } while (time_before(jiffies, one_second)); 137ec2f33abSBill Richardson 138ec2f33abSBill Richardson return 1; 139ec2f33abSBill Richardson } 140ec2f33abSBill Richardson 141d3654070SStephen Barber static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, 142d3654070SStephen Barber struct cros_ec_command *msg) 143d3654070SStephen Barber { 144d3654070SStephen Barber struct ec_host_response response; 145bce70fefSShawn Nematbakhsh u8 sum; 146d3654070SStephen Barber int ret = 0; 147d3654070SStephen Barber u8 *dout; 148d3654070SStephen Barber 149d3654070SStephen Barber ret = cros_ec_prepare_tx(ec, msg); 15071d3ae7fSTzung-Bi Shih if (ret < 0) 15171d3ae7fSTzung-Bi Shih goto done; 152d3654070SStephen Barber 153d3654070SStephen Barber /* Write buffer */ 15422c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); 155d3654070SStephen Barber 156d3654070SStephen Barber /* Here we go */ 157bce70fefSShawn Nematbakhsh sum = EC_COMMAND_PROTOCOL_3; 15822c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); 159d3654070SStephen Barber 160d3654070SStephen Barber if (ec_response_timed_out()) { 161eb057514SColin Ian King dev_warn(ec->dev, "EC response timed out\n"); 162d3654070SStephen Barber ret = -EIO; 163d3654070SStephen Barber goto done; 164d3654070SStephen Barber } 165d3654070SStephen Barber 166d3654070SStephen Barber /* Check result */ 16722c040faSEnric Balletbo i Serra msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); 168d3654070SStephen Barber ret = cros_ec_check_result(ec, msg); 169d3654070SStephen Barber if (ret) 170d3654070SStephen Barber goto done; 171d3654070SStephen Barber 172d3654070SStephen Barber /* Read back response */ 173d3654070SStephen Barber dout = (u8 *)&response; 17422c040faSEnric Balletbo i Serra sum = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response), 175bce70fefSShawn Nematbakhsh dout); 176d3654070SStephen Barber 177d3654070SStephen Barber msg->result = response.result; 178d3654070SStephen Barber 179d3654070SStephen Barber if (response.data_len > msg->insize) { 180d3654070SStephen Barber dev_err(ec->dev, 181d3654070SStephen Barber "packet too long (%d bytes, expected %d)", 182d3654070SStephen Barber response.data_len, msg->insize); 183d3654070SStephen Barber ret = -EMSGSIZE; 184d3654070SStephen Barber goto done; 185d3654070SStephen Barber } 186d3654070SStephen Barber 187d3654070SStephen Barber /* Read response and process checksum */ 18822c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET + 189bce70fefSShawn Nematbakhsh sizeof(response), response.data_len, 190bce70fefSShawn Nematbakhsh msg->data); 191d3654070SStephen Barber 192d3654070SStephen Barber if (sum) { 193d3654070SStephen Barber dev_err(ec->dev, 194d3654070SStephen Barber "bad packet checksum %02x\n", 195d3654070SStephen Barber response.checksum); 196d3654070SStephen Barber ret = -EBADMSG; 197d3654070SStephen Barber goto done; 198d3654070SStephen Barber } 199d3654070SStephen Barber 200d3654070SStephen Barber /* Return actual amount of data received */ 201d3654070SStephen Barber ret = response.data_len; 202d3654070SStephen Barber done: 203d3654070SStephen Barber return ret; 204d3654070SStephen Barber } 205d3654070SStephen Barber 206ec2f33abSBill Richardson static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, 207ec2f33abSBill Richardson struct cros_ec_command *msg) 208ec2f33abSBill Richardson { 209ec2f33abSBill Richardson struct ec_lpc_host_args args; 210bce70fefSShawn Nematbakhsh u8 sum; 211ec2f33abSBill Richardson int ret = 0; 212ec2f33abSBill Richardson 213ec2f33abSBill Richardson if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || 214ec2f33abSBill Richardson msg->insize > EC_PROTO2_MAX_PARAM_SIZE) { 215ec2f33abSBill Richardson dev_err(ec->dev, 216ec2f33abSBill Richardson "invalid buffer sizes (out %d, in %d)\n", 217ec2f33abSBill Richardson msg->outsize, msg->insize); 218ec2f33abSBill Richardson return -EINVAL; 219ec2f33abSBill Richardson } 220ec2f33abSBill Richardson 221ec2f33abSBill Richardson /* Now actually send the command to the EC and get the result */ 222ec2f33abSBill Richardson args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 223ec2f33abSBill Richardson args.command_version = msg->version; 224ec2f33abSBill Richardson args.data_size = msg->outsize; 225ec2f33abSBill Richardson 226ec2f33abSBill Richardson /* Initialize checksum */ 227bce70fefSShawn Nematbakhsh sum = msg->command + args.flags + args.command_version + args.data_size; 228ec2f33abSBill Richardson 229ec2f33abSBill Richardson /* Copy data and update checksum */ 23022c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize, 231bce70fefSShawn Nematbakhsh msg->data); 232ec2f33abSBill Richardson 233ec2f33abSBill Richardson /* Finalize checksum and write args */ 234bce70fefSShawn Nematbakhsh args.checksum = sum; 23522c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args), 236bce70fefSShawn Nematbakhsh (u8 *)&args); 237ec2f33abSBill Richardson 238ec2f33abSBill Richardson /* Here we go */ 239bce70fefSShawn Nematbakhsh sum = msg->command; 24022c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); 241ec2f33abSBill Richardson 242ec2f33abSBill Richardson if (ec_response_timed_out()) { 243eb057514SColin Ian King dev_warn(ec->dev, "EC response timed out\n"); 244ec2f33abSBill Richardson ret = -EIO; 245ec2f33abSBill Richardson goto done; 246ec2f33abSBill Richardson } 247ec2f33abSBill Richardson 248ec2f33abSBill Richardson /* Check result */ 24922c040faSEnric Balletbo i Serra msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); 250fbf40727SJavier Martinez Canillas ret = cros_ec_check_result(ec, msg); 251fbf40727SJavier Martinez Canillas if (ret) 252ec2f33abSBill Richardson goto done; 253ec2f33abSBill Richardson 254ec2f33abSBill Richardson /* Read back args */ 25522c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); 256ec2f33abSBill Richardson 257ec2f33abSBill Richardson if (args.data_size > msg->insize) { 258ec2f33abSBill Richardson dev_err(ec->dev, 259ec2f33abSBill Richardson "packet too long (%d bytes, expected %d)", 260ec2f33abSBill Richardson args.data_size, msg->insize); 261ec2f33abSBill Richardson ret = -ENOSPC; 262ec2f33abSBill Richardson goto done; 263ec2f33abSBill Richardson } 264ec2f33abSBill Richardson 265ec2f33abSBill Richardson /* Start calculating response checksum */ 266bce70fefSShawn Nematbakhsh sum = msg->command + args.flags + args.command_version + args.data_size; 267ec2f33abSBill Richardson 268ec2f33abSBill Richardson /* Read response and update checksum */ 26922c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size, 270bce70fefSShawn Nematbakhsh msg->data); 271ec2f33abSBill Richardson 272ec2f33abSBill Richardson /* Verify checksum */ 273bce70fefSShawn Nematbakhsh if (args.checksum != sum) { 274ec2f33abSBill Richardson dev_err(ec->dev, 275ec2f33abSBill Richardson "bad packet checksum, expected %02x, got %02x\n", 276bce70fefSShawn Nematbakhsh args.checksum, sum); 277ec2f33abSBill Richardson ret = -EBADMSG; 278ec2f33abSBill Richardson goto done; 279ec2f33abSBill Richardson } 280ec2f33abSBill Richardson 281ec2f33abSBill Richardson /* Return actual amount of data received */ 282ec2f33abSBill Richardson ret = args.data_size; 283ec2f33abSBill Richardson done: 284ec2f33abSBill Richardson return ret; 285ec2f33abSBill Richardson } 286ec2f33abSBill Richardson 287ec2f33abSBill Richardson /* Returns num bytes read, or negative on error. Doesn't need locking. */ 288ec2f33abSBill Richardson static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, 289ec2f33abSBill Richardson unsigned int bytes, void *dest) 290ec2f33abSBill Richardson { 291ec2f33abSBill Richardson int i = offset; 292ec2f33abSBill Richardson char *s = dest; 293ec2f33abSBill Richardson int cnt = 0; 294ec2f33abSBill Richardson 295ec2f33abSBill Richardson if (offset >= EC_MEMMAP_SIZE - bytes) 296ec2f33abSBill Richardson return -EINVAL; 297ec2f33abSBill Richardson 298ec2f33abSBill Richardson /* fixed length */ 299ec2f33abSBill Richardson if (bytes) { 30022c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + offset, bytes, s); 301bce70fefSShawn Nematbakhsh return bytes; 302ec2f33abSBill Richardson } 303ec2f33abSBill Richardson 304ec2f33abSBill Richardson /* string */ 305ec2f33abSBill Richardson for (; i < EC_MEMMAP_SIZE; i++, s++) { 30622c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, 1, s); 307ec2f33abSBill Richardson cnt++; 308ec2f33abSBill Richardson if (!*s) 309ec2f33abSBill Richardson break; 310ec2f33abSBill Richardson } 311ec2f33abSBill Richardson 312ec2f33abSBill Richardson return cnt; 313ec2f33abSBill Richardson } 314ec2f33abSBill Richardson 315a6df7798SGwendal Grignou static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) 316a6df7798SGwendal Grignou { 317a6df7798SGwendal Grignou struct cros_ec_device *ec_dev = data; 3183300fdd6SEnrico Granata bool ec_has_more_events; 3193300fdd6SEnrico Granata int ret; 320a6df7798SGwendal Grignou 32105a3c420SGwendal Grignou ec_dev->last_event_time = cros_ec_get_time_ns(); 32205a3c420SGwendal Grignou 3233300fdd6SEnrico Granata if (ec_dev->mkbp_event_supported) 3243300fdd6SEnrico Granata do { 3253300fdd6SEnrico Granata ret = cros_ec_get_next_event(ec_dev, NULL, 3263300fdd6SEnrico Granata &ec_has_more_events); 3273300fdd6SEnrico Granata if (ret > 0) 3283300fdd6SEnrico Granata blocking_notifier_call_chain( 3293300fdd6SEnrico Granata &ec_dev->event_notifier, 0, 330a6df7798SGwendal Grignou ec_dev); 3313300fdd6SEnrico Granata } while (ec_has_more_events); 332d6542b39SWenkai Du 333d6542b39SWenkai Du if (value == ACPI_NOTIFY_DEVICE_WAKE) 334d6542b39SWenkai Du pm_system_wakeup(); 335a6df7798SGwendal Grignou } 336a6df7798SGwendal Grignou 337ec2f33abSBill Richardson static int cros_ec_lpc_probe(struct platform_device *pdev) 338ec2f33abSBill Richardson { 339ec2f33abSBill Richardson struct device *dev = &pdev->dev; 340a6df7798SGwendal Grignou struct acpi_device *adev; 341a6df7798SGwendal Grignou acpi_status status; 342ec2f33abSBill Richardson struct cros_ec_device *ec_dev; 343bce70fefSShawn Nematbakhsh u8 buf[2]; 344da1cf5a1SEnrico Granata int irq, ret; 345ec2f33abSBill Richardson 346c9bc1a0eSDustin L. Howett /* 347c9bc1a0eSDustin L. Howett * The Framework Laptop (and possibly other non-ChromeOS devices) 348c9bc1a0eSDustin L. Howett * only exposes the eight I/O ports that are required for the Microchip EC. 349c9bc1a0eSDustin L. Howett * Requesting a larger reservation will fail. 350c9bc1a0eSDustin L. Howett */ 351c9bc1a0eSDustin L. Howett if (!devm_request_region(dev, EC_HOST_CMD_REGION0, 352c9bc1a0eSDustin L. Howett EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) { 353c9bc1a0eSDustin L. Howett dev_err(dev, "couldn't reserve MEC region\n"); 354ec2f33abSBill Richardson return -EBUSY; 355ec2f33abSBill Richardson } 356ec2f33abSBill Richardson 357fdf84f9aSBrian Norris cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, 358fdf84f9aSBrian Norris EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); 359fdf84f9aSBrian Norris 36022c040faSEnric Balletbo i Serra /* 36122c040faSEnric Balletbo i Serra * Read the mapped ID twice, the first one is assuming the 36222c040faSEnric Balletbo i Serra * EC is a Microchip Embedded Controller (MEC) variant, if the 36322c040faSEnric Balletbo i Serra * protocol fails, fallback to the non MEC variant and try to 36422c040faSEnric Balletbo i Serra * read again the ID. 36522c040faSEnric Balletbo i Serra */ 36622c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read = cros_ec_lpc_mec_read_bytes; 36722c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes; 36822c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); 36922c040faSEnric Balletbo i Serra if (buf[0] != 'E' || buf[1] != 'C') { 370c9bc1a0eSDustin L. Howett if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, 371c9bc1a0eSDustin L. Howett dev_name(dev))) { 372c9bc1a0eSDustin L. Howett dev_err(dev, "couldn't reserve memmap region\n"); 373c9bc1a0eSDustin L. Howett return -EBUSY; 374c9bc1a0eSDustin L. Howett } 375c9bc1a0eSDustin L. Howett 37622c040faSEnric Balletbo i Serra /* Re-assign read/write operations for the non MEC variant */ 37722c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read = cros_ec_lpc_read_bytes; 37822c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write = cros_ec_lpc_write_bytes; 37922c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, 38022c040faSEnric Balletbo i Serra buf); 381bce70fefSShawn Nematbakhsh if (buf[0] != 'E' || buf[1] != 'C') { 382ec2f33abSBill Richardson dev_err(dev, "EC ID not detected\n"); 383ec2f33abSBill Richardson return -ENODEV; 384ec2f33abSBill Richardson } 385ec2f33abSBill Richardson 386c9bc1a0eSDustin L. Howett /* Reserve the remaining I/O ports required by the non-MEC protocol. */ 387c9bc1a0eSDustin L. Howett if (!devm_request_region(dev, EC_HOST_CMD_REGION0 + EC_HOST_CMD_MEC_REGION_SIZE, 388c9bc1a0eSDustin L. Howett EC_HOST_CMD_REGION_SIZE - EC_HOST_CMD_MEC_REGION_SIZE, 389c9bc1a0eSDustin L. Howett dev_name(dev))) { 390c9bc1a0eSDustin L. Howett dev_err(dev, "couldn't reserve remainder of region0\n"); 391ec2f33abSBill Richardson return -EBUSY; 392ec2f33abSBill Richardson } 393ec2f33abSBill Richardson if (!devm_request_region(dev, EC_HOST_CMD_REGION1, 394ec2f33abSBill Richardson EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 395ec2f33abSBill Richardson dev_err(dev, "couldn't reserve region1\n"); 396ec2f33abSBill Richardson return -EBUSY; 397ec2f33abSBill Richardson } 398c9bc1a0eSDustin L. Howett } 399ec2f33abSBill Richardson 400ec2f33abSBill Richardson ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); 401ec2f33abSBill Richardson if (!ec_dev) 402ec2f33abSBill Richardson return -ENOMEM; 403ec2f33abSBill Richardson 404ec2f33abSBill Richardson platform_set_drvdata(pdev, ec_dev); 405ec2f33abSBill Richardson ec_dev->dev = dev; 406ec2f33abSBill Richardson ec_dev->phys_name = dev_name(dev); 407ec2f33abSBill Richardson ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; 408d3654070SStephen Barber ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc; 409ec2f33abSBill Richardson ec_dev->cmd_readmem = cros_ec_lpc_readmem; 4102c7589afSStephen Barber ec_dev->din_size = sizeof(struct ec_host_response) + 4112c7589afSStephen Barber sizeof(struct ec_response_get_protocol_info); 4122c7589afSStephen Barber ec_dev->dout_size = sizeof(struct ec_host_request); 413ec2f33abSBill Richardson 414da1cf5a1SEnrico Granata /* 415da1cf5a1SEnrico Granata * Some boards do not have an IRQ allotted for cros_ec_lpc, 416da1cf5a1SEnrico Granata * which makes ENXIO an expected (and safe) scenario. 417da1cf5a1SEnrico Granata */ 418a69b4eebSEnric Balletbo i Serra irq = platform_get_irq_optional(pdev, 0); 419da1cf5a1SEnrico Granata if (irq > 0) 420da1cf5a1SEnrico Granata ec_dev->irq = irq; 421da1cf5a1SEnrico Granata else if (irq != -ENXIO) { 422da1cf5a1SEnrico Granata dev_err(dev, "couldn't retrieve IRQ number (%d)\n", irq); 423da1cf5a1SEnrico Granata return irq; 424da1cf5a1SEnrico Granata } 425da1cf5a1SEnrico Granata 426ec2f33abSBill Richardson ret = cros_ec_register(ec_dev); 427ec2f33abSBill Richardson if (ret) { 428ec2f33abSBill Richardson dev_err(dev, "couldn't register ec_dev (%d)\n", ret); 429ec2f33abSBill Richardson return ret; 430ec2f33abSBill Richardson } 431ec2f33abSBill Richardson 432a6df7798SGwendal Grignou /* 433a6df7798SGwendal Grignou * Connect a notify handler to process MKBP messages if we have a 434a6df7798SGwendal Grignou * companion ACPI device. 435a6df7798SGwendal Grignou */ 436a6df7798SGwendal Grignou adev = ACPI_COMPANION(dev); 437a6df7798SGwendal Grignou if (adev) { 438a6df7798SGwendal Grignou status = acpi_install_notify_handler(adev->handle, 439a6df7798SGwendal Grignou ACPI_ALL_NOTIFY, 440a6df7798SGwendal Grignou cros_ec_lpc_acpi_notify, 441a6df7798SGwendal Grignou ec_dev); 442a6df7798SGwendal Grignou if (ACPI_FAILURE(status)) 443a6df7798SGwendal Grignou dev_warn(dev, "Failed to register notifier %08x\n", 444a6df7798SGwendal Grignou status); 445a6df7798SGwendal Grignou } 446a6df7798SGwendal Grignou 447ec2f33abSBill Richardson return 0; 448ec2f33abSBill Richardson } 449ec2f33abSBill Richardson 450ec2f33abSBill Richardson static int cros_ec_lpc_remove(struct platform_device *pdev) 451ec2f33abSBill Richardson { 4527aa703bbSEnric Balletbo i Serra struct cros_ec_device *ec_dev = platform_get_drvdata(pdev); 453a6df7798SGwendal Grignou struct acpi_device *adev; 454a6df7798SGwendal Grignou 455a6df7798SGwendal Grignou adev = ACPI_COMPANION(&pdev->dev); 456a6df7798SGwendal Grignou if (adev) 457a6df7798SGwendal Grignou acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, 458a6df7798SGwendal Grignou cros_ec_lpc_acpi_notify); 459ec2f33abSBill Richardson 460afb0a80eSUwe Kleine-König cros_ec_unregister(ec_dev); 461afb0a80eSUwe Kleine-König 462afb0a80eSUwe Kleine-König return 0; 463ec2f33abSBill Richardson } 464ec2f33abSBill Richardson 46512278dc7SGwendal Grignou static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { 46612278dc7SGwendal Grignou { ACPI_DRV_NAME, 0 }, 46712278dc7SGwendal Grignou { } 46812278dc7SGwendal Grignou }; 46912278dc7SGwendal Grignou MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); 47012278dc7SGwendal Grignou 4716faadbbbSChristoph Hellwig static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { 472ec2f33abSBill Richardson { 473ec2f33abSBill Richardson /* 474ec2f33abSBill Richardson * Today all Chromebooks/boxes ship with Google_* as version and 475ec2f33abSBill Richardson * coreboot as bios vendor. No other systems with this 476ec2f33abSBill Richardson * combination are known to date. 477ec2f33abSBill Richardson */ 478ec2f33abSBill Richardson .matches = { 479ec2f33abSBill Richardson DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 480ec2f33abSBill Richardson DMI_MATCH(DMI_BIOS_VERSION, "Google_"), 481ec2f33abSBill Richardson }, 482ec2f33abSBill Richardson }, 483ec2f33abSBill Richardson { 484f56db262SSalvatore Bellizzi /* 485f56db262SSalvatore Bellizzi * If the box is running custom coreboot firmware then the 486f56db262SSalvatore Bellizzi * DMI BIOS version string will not be matched by "Google_", 487f56db262SSalvatore Bellizzi * but the system vendor string will still be matched by 488f56db262SSalvatore Bellizzi * "GOOGLE". 489f56db262SSalvatore Bellizzi */ 490f56db262SSalvatore Bellizzi .matches = { 491f56db262SSalvatore Bellizzi DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 492f56db262SSalvatore Bellizzi DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 493f56db262SSalvatore Bellizzi }, 494f56db262SSalvatore Bellizzi }, 495f56db262SSalvatore Bellizzi { 496ec2f33abSBill Richardson /* x86-link, the Chromebook Pixel. */ 497ec2f33abSBill Richardson .matches = { 498ec2f33abSBill Richardson DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 499ec2f33abSBill Richardson DMI_MATCH(DMI_PRODUCT_NAME, "Link"), 500ec2f33abSBill Richardson }, 501ec2f33abSBill Richardson }, 502ec2f33abSBill Richardson { 50385bba84eSJavier Martinez Canillas /* x86-samus, the Chromebook Pixel 2. */ 50485bba84eSJavier Martinez Canillas .matches = { 50585bba84eSJavier Martinez Canillas DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 50685bba84eSJavier Martinez Canillas DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), 50785bba84eSJavier Martinez Canillas }, 50885bba84eSJavier Martinez Canillas }, 50985bba84eSJavier Martinez Canillas { 510ec2f33abSBill Richardson /* x86-peppy, the Acer C720 Chromebook. */ 511ec2f33abSBill Richardson .matches = { 512ec2f33abSBill Richardson DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 513ec2f33abSBill Richardson DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), 514ec2f33abSBill Richardson }, 515ec2f33abSBill Richardson }, 516e6751917SThierry Escande { 517e6751917SThierry Escande /* x86-glimmer, the Lenovo Thinkpad Yoga 11e. */ 518e6751917SThierry Escande .matches = { 519e6751917SThierry Escande DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 520e6751917SThierry Escande DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"), 521e6751917SThierry Escande }, 522e6751917SThierry Escande }, 5236a5d778eSDustin L. Howett /* A small number of non-Chromebook/box machines also use the ChromeOS EC */ 5246a5d778eSDustin L. Howett { 5256a5d778eSDustin L. Howett /* the Framework Laptop */ 5266a5d778eSDustin L. Howett .matches = { 5276a5d778eSDustin L. Howett DMI_MATCH(DMI_SYS_VENDOR, "Framework"), 5286a5d778eSDustin L. Howett DMI_MATCH(DMI_PRODUCT_NAME, "Laptop"), 5296a5d778eSDustin L. Howett }, 5306a5d778eSDustin L. Howett }, 531ec2f33abSBill Richardson { /* sentinel */ } 532ec2f33abSBill Richardson }; 533ec2f33abSBill Richardson MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); 534ec2f33abSBill Richardson 535450de8f4SArchana Patni #ifdef CONFIG_PM_SLEEP 536450de8f4SArchana Patni static int cros_ec_lpc_suspend(struct device *dev) 537450de8f4SArchana Patni { 538450de8f4SArchana Patni struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 539450de8f4SArchana Patni 540450de8f4SArchana Patni return cros_ec_suspend(ec_dev); 541450de8f4SArchana Patni } 542450de8f4SArchana Patni 543450de8f4SArchana Patni static int cros_ec_lpc_resume(struct device *dev) 544450de8f4SArchana Patni { 545450de8f4SArchana Patni struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 546450de8f4SArchana Patni 547450de8f4SArchana Patni return cros_ec_resume(ec_dev); 548450de8f4SArchana Patni } 549450de8f4SArchana Patni #endif 550450de8f4SArchana Patni 55181bc8c03SYueHaibing static const struct dev_pm_ops cros_ec_lpc_pm_ops = { 552450de8f4SArchana Patni SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume) 553450de8f4SArchana Patni }; 554450de8f4SArchana Patni 555ec2f33abSBill Richardson static struct platform_driver cros_ec_lpc_driver = { 556ec2f33abSBill Richardson .driver = { 557ec2f33abSBill Richardson .name = DRV_NAME, 55812278dc7SGwendal Grignou .acpi_match_table = cros_ec_lpc_acpi_device_ids, 559450de8f4SArchana Patni .pm = &cros_ec_lpc_pm_ops, 560*ca821c1fSBrian Norris /* 561*ca821c1fSBrian Norris * ACPI child devices may probe before us, and they racily 562*ca821c1fSBrian Norris * check our drvdata pointer. Force synchronous probe until 563*ca821c1fSBrian Norris * those races are resolved. 564*ca821c1fSBrian Norris */ 565*ca821c1fSBrian Norris .probe_type = PROBE_FORCE_SYNCHRONOUS, 566ec2f33abSBill Richardson }, 567ec2f33abSBill Richardson .probe = cros_ec_lpc_probe, 568ec2f33abSBill Richardson .remove = cros_ec_lpc_remove, 569ec2f33abSBill Richardson }; 570ec2f33abSBill Richardson 5715f454bdfSEnric Balletbo i Serra static struct platform_device cros_ec_lpc_device = { 5725f454bdfSEnric Balletbo i Serra .name = DRV_NAME 5735f454bdfSEnric Balletbo i Serra }; 5745f454bdfSEnric Balletbo i Serra 5755f454bdfSEnric Balletbo i Serra static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level, 5765f454bdfSEnric Balletbo i Serra void *context, void **retval) 5775f454bdfSEnric Balletbo i Serra { 5785f454bdfSEnric Balletbo i Serra *(bool *)context = true; 5795f454bdfSEnric Balletbo i Serra return AE_CTRL_TERMINATE; 5805f454bdfSEnric Balletbo i Serra } 5815f454bdfSEnric Balletbo i Serra 582ec2f33abSBill Richardson static int __init cros_ec_lpc_init(void) 583ec2f33abSBill Richardson { 584ec2f33abSBill Richardson int ret; 5855f454bdfSEnric Balletbo i Serra acpi_status status; 586ec2f33abSBill Richardson 587b410b122SDmitry Torokhov status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device, 588b410b122SDmitry Torokhov &cros_ec_lpc_acpi_device_found, NULL); 589b410b122SDmitry Torokhov if (ACPI_FAILURE(status)) 590b410b122SDmitry Torokhov pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME); 591b410b122SDmitry Torokhov 592b410b122SDmitry Torokhov if (!cros_ec_lpc_acpi_device_found && 593b410b122SDmitry Torokhov !dmi_check_system(cros_ec_lpc_dmi_table)) { 594ec2f33abSBill Richardson pr_err(DRV_NAME ": unsupported system.\n"); 595ec2f33abSBill Richardson return -ENODEV; 596ec2f33abSBill Richardson } 597ec2f33abSBill Richardson 598ec2f33abSBill Richardson /* Register the driver */ 599ec2f33abSBill Richardson ret = platform_driver_register(&cros_ec_lpc_driver); 600ec2f33abSBill Richardson if (ret) { 601ec2f33abSBill Richardson pr_err(DRV_NAME ": can't register driver: %d\n", ret); 602ec2f33abSBill Richardson return ret; 603ec2f33abSBill Richardson } 604ec2f33abSBill Richardson 6055f454bdfSEnric Balletbo i Serra if (!cros_ec_lpc_acpi_device_found) { 6065f454bdfSEnric Balletbo i Serra /* Register the device, and it'll get hooked up automatically */ 6075f454bdfSEnric Balletbo i Serra ret = platform_device_register(&cros_ec_lpc_device); 6085f454bdfSEnric Balletbo i Serra if (ret) { 6095f454bdfSEnric Balletbo i Serra pr_err(DRV_NAME ": can't register device: %d\n", ret); 6105f454bdfSEnric Balletbo i Serra platform_driver_unregister(&cros_ec_lpc_driver); 6115f454bdfSEnric Balletbo i Serra } 6125f454bdfSEnric Balletbo i Serra } 6135f454bdfSEnric Balletbo i Serra 6145f454bdfSEnric Balletbo i Serra return ret; 615ec2f33abSBill Richardson } 616ec2f33abSBill Richardson 617ec2f33abSBill Richardson static void __exit cros_ec_lpc_exit(void) 618ec2f33abSBill Richardson { 6195f454bdfSEnric Balletbo i Serra if (!cros_ec_lpc_acpi_device_found) 6205f454bdfSEnric Balletbo i Serra platform_device_unregister(&cros_ec_lpc_device); 621ec2f33abSBill Richardson platform_driver_unregister(&cros_ec_lpc_driver); 622ec2f33abSBill Richardson } 623ec2f33abSBill Richardson 624ec2f33abSBill Richardson module_init(cros_ec_lpc_init); 625ec2f33abSBill Richardson module_exit(cros_ec_lpc_exit); 626ec2f33abSBill Richardson 627ec2f33abSBill Richardson MODULE_LICENSE("GPL"); 628ec2f33abSBill Richardson MODULE_DESCRIPTION("ChromeOS EC LPC driver"); 629