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 26*034dbec1SEnric 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); 150d3654070SStephen Barber 151d3654070SStephen Barber /* Write buffer */ 15222c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); 153d3654070SStephen Barber 154d3654070SStephen Barber /* Here we go */ 155bce70fefSShawn Nematbakhsh sum = EC_COMMAND_PROTOCOL_3; 15622c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); 157d3654070SStephen Barber 158d3654070SStephen Barber if (ec_response_timed_out()) { 159d3654070SStephen Barber dev_warn(ec->dev, "EC responsed timed out\n"); 160d3654070SStephen Barber ret = -EIO; 161d3654070SStephen Barber goto done; 162d3654070SStephen Barber } 163d3654070SStephen Barber 164d3654070SStephen Barber /* Check result */ 16522c040faSEnric Balletbo i Serra msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); 166d3654070SStephen Barber ret = cros_ec_check_result(ec, msg); 167d3654070SStephen Barber if (ret) 168d3654070SStephen Barber goto done; 169d3654070SStephen Barber 170d3654070SStephen Barber /* Read back response */ 171d3654070SStephen Barber dout = (u8 *)&response; 17222c040faSEnric Balletbo i Serra sum = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response), 173bce70fefSShawn Nematbakhsh dout); 174d3654070SStephen Barber 175d3654070SStephen Barber msg->result = response.result; 176d3654070SStephen Barber 177d3654070SStephen Barber if (response.data_len > msg->insize) { 178d3654070SStephen Barber dev_err(ec->dev, 179d3654070SStephen Barber "packet too long (%d bytes, expected %d)", 180d3654070SStephen Barber response.data_len, msg->insize); 181d3654070SStephen Barber ret = -EMSGSIZE; 182d3654070SStephen Barber goto done; 183d3654070SStephen Barber } 184d3654070SStephen Barber 185d3654070SStephen Barber /* Read response and process checksum */ 18622c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET + 187bce70fefSShawn Nematbakhsh sizeof(response), response.data_len, 188bce70fefSShawn Nematbakhsh msg->data); 189d3654070SStephen Barber 190d3654070SStephen Barber if (sum) { 191d3654070SStephen Barber dev_err(ec->dev, 192d3654070SStephen Barber "bad packet checksum %02x\n", 193d3654070SStephen Barber response.checksum); 194d3654070SStephen Barber ret = -EBADMSG; 195d3654070SStephen Barber goto done; 196d3654070SStephen Barber } 197d3654070SStephen Barber 198d3654070SStephen Barber /* Return actual amount of data received */ 199d3654070SStephen Barber ret = response.data_len; 200d3654070SStephen Barber done: 201d3654070SStephen Barber return ret; 202d3654070SStephen Barber } 203d3654070SStephen Barber 204ec2f33abSBill Richardson static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, 205ec2f33abSBill Richardson struct cros_ec_command *msg) 206ec2f33abSBill Richardson { 207ec2f33abSBill Richardson struct ec_lpc_host_args args; 208bce70fefSShawn Nematbakhsh u8 sum; 209ec2f33abSBill Richardson int ret = 0; 210ec2f33abSBill Richardson 211ec2f33abSBill Richardson if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || 212ec2f33abSBill Richardson msg->insize > EC_PROTO2_MAX_PARAM_SIZE) { 213ec2f33abSBill Richardson dev_err(ec->dev, 214ec2f33abSBill Richardson "invalid buffer sizes (out %d, in %d)\n", 215ec2f33abSBill Richardson msg->outsize, msg->insize); 216ec2f33abSBill Richardson return -EINVAL; 217ec2f33abSBill Richardson } 218ec2f33abSBill Richardson 219ec2f33abSBill Richardson /* Now actually send the command to the EC and get the result */ 220ec2f33abSBill Richardson args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 221ec2f33abSBill Richardson args.command_version = msg->version; 222ec2f33abSBill Richardson args.data_size = msg->outsize; 223ec2f33abSBill Richardson 224ec2f33abSBill Richardson /* Initialize checksum */ 225bce70fefSShawn Nematbakhsh sum = msg->command + args.flags + args.command_version + args.data_size; 226ec2f33abSBill Richardson 227ec2f33abSBill Richardson /* Copy data and update checksum */ 22822c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize, 229bce70fefSShawn Nematbakhsh msg->data); 230ec2f33abSBill Richardson 231ec2f33abSBill Richardson /* Finalize checksum and write args */ 232bce70fefSShawn Nematbakhsh args.checksum = sum; 23322c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args), 234bce70fefSShawn Nematbakhsh (u8 *)&args); 235ec2f33abSBill Richardson 236ec2f33abSBill Richardson /* Here we go */ 237bce70fefSShawn Nematbakhsh sum = msg->command; 23822c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); 239ec2f33abSBill Richardson 240ec2f33abSBill Richardson if (ec_response_timed_out()) { 241ec2f33abSBill Richardson dev_warn(ec->dev, "EC responsed timed out\n"); 242ec2f33abSBill Richardson ret = -EIO; 243ec2f33abSBill Richardson goto done; 244ec2f33abSBill Richardson } 245ec2f33abSBill Richardson 246ec2f33abSBill Richardson /* Check result */ 24722c040faSEnric Balletbo i Serra msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); 248fbf40727SJavier Martinez Canillas ret = cros_ec_check_result(ec, msg); 249fbf40727SJavier Martinez Canillas if (ret) 250ec2f33abSBill Richardson goto done; 251ec2f33abSBill Richardson 252ec2f33abSBill Richardson /* Read back args */ 25322c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); 254ec2f33abSBill Richardson 255ec2f33abSBill Richardson if (args.data_size > msg->insize) { 256ec2f33abSBill Richardson dev_err(ec->dev, 257ec2f33abSBill Richardson "packet too long (%d bytes, expected %d)", 258ec2f33abSBill Richardson args.data_size, msg->insize); 259ec2f33abSBill Richardson ret = -ENOSPC; 260ec2f33abSBill Richardson goto done; 261ec2f33abSBill Richardson } 262ec2f33abSBill Richardson 263ec2f33abSBill Richardson /* Start calculating response checksum */ 264bce70fefSShawn Nematbakhsh sum = msg->command + args.flags + args.command_version + args.data_size; 265ec2f33abSBill Richardson 266ec2f33abSBill Richardson /* Read response and update checksum */ 26722c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size, 268bce70fefSShawn Nematbakhsh msg->data); 269ec2f33abSBill Richardson 270ec2f33abSBill Richardson /* Verify checksum */ 271bce70fefSShawn Nematbakhsh if (args.checksum != sum) { 272ec2f33abSBill Richardson dev_err(ec->dev, 273ec2f33abSBill Richardson "bad packet checksum, expected %02x, got %02x\n", 274bce70fefSShawn Nematbakhsh args.checksum, sum); 275ec2f33abSBill Richardson ret = -EBADMSG; 276ec2f33abSBill Richardson goto done; 277ec2f33abSBill Richardson } 278ec2f33abSBill Richardson 279ec2f33abSBill Richardson /* Return actual amount of data received */ 280ec2f33abSBill Richardson ret = args.data_size; 281ec2f33abSBill Richardson done: 282ec2f33abSBill Richardson return ret; 283ec2f33abSBill Richardson } 284ec2f33abSBill Richardson 285ec2f33abSBill Richardson /* Returns num bytes read, or negative on error. Doesn't need locking. */ 286ec2f33abSBill Richardson static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, 287ec2f33abSBill Richardson unsigned int bytes, void *dest) 288ec2f33abSBill Richardson { 289ec2f33abSBill Richardson int i = offset; 290ec2f33abSBill Richardson char *s = dest; 291ec2f33abSBill Richardson int cnt = 0; 292ec2f33abSBill Richardson 293ec2f33abSBill Richardson if (offset >= EC_MEMMAP_SIZE - bytes) 294ec2f33abSBill Richardson return -EINVAL; 295ec2f33abSBill Richardson 296ec2f33abSBill Richardson /* fixed length */ 297ec2f33abSBill Richardson if (bytes) { 29822c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + offset, bytes, s); 299bce70fefSShawn Nematbakhsh return bytes; 300ec2f33abSBill Richardson } 301ec2f33abSBill Richardson 302ec2f33abSBill Richardson /* string */ 303ec2f33abSBill Richardson for (; i < EC_MEMMAP_SIZE; i++, s++) { 30422c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, 1, s); 305ec2f33abSBill Richardson cnt++; 306ec2f33abSBill Richardson if (!*s) 307ec2f33abSBill Richardson break; 308ec2f33abSBill Richardson } 309ec2f33abSBill Richardson 310ec2f33abSBill Richardson return cnt; 311ec2f33abSBill Richardson } 312ec2f33abSBill Richardson 313a6df7798SGwendal Grignou static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) 314a6df7798SGwendal Grignou { 315a6df7798SGwendal Grignou struct cros_ec_device *ec_dev = data; 3163300fdd6SEnrico Granata bool ec_has_more_events; 3173300fdd6SEnrico Granata int ret; 318a6df7798SGwendal Grignou 31905a3c420SGwendal Grignou ec_dev->last_event_time = cros_ec_get_time_ns(); 32005a3c420SGwendal Grignou 3213300fdd6SEnrico Granata if (ec_dev->mkbp_event_supported) 3223300fdd6SEnrico Granata do { 3233300fdd6SEnrico Granata ret = cros_ec_get_next_event(ec_dev, NULL, 3243300fdd6SEnrico Granata &ec_has_more_events); 3253300fdd6SEnrico Granata if (ret > 0) 3263300fdd6SEnrico Granata blocking_notifier_call_chain( 3273300fdd6SEnrico Granata &ec_dev->event_notifier, 0, 328a6df7798SGwendal Grignou ec_dev); 3293300fdd6SEnrico Granata } while (ec_has_more_events); 330d6542b39SWenkai Du 331d6542b39SWenkai Du if (value == ACPI_NOTIFY_DEVICE_WAKE) 332d6542b39SWenkai Du pm_system_wakeup(); 333a6df7798SGwendal Grignou } 334a6df7798SGwendal Grignou 335ec2f33abSBill Richardson static int cros_ec_lpc_probe(struct platform_device *pdev) 336ec2f33abSBill Richardson { 337ec2f33abSBill Richardson struct device *dev = &pdev->dev; 338a6df7798SGwendal Grignou struct acpi_device *adev; 339a6df7798SGwendal Grignou acpi_status status; 340ec2f33abSBill Richardson struct cros_ec_device *ec_dev; 341bce70fefSShawn Nematbakhsh u8 buf[2]; 342da1cf5a1SEnrico Granata int irq, ret; 343ec2f33abSBill Richardson 344ec2f33abSBill Richardson if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, 345ec2f33abSBill Richardson dev_name(dev))) { 346ec2f33abSBill Richardson dev_err(dev, "couldn't reserve memmap region\n"); 347ec2f33abSBill Richardson return -EBUSY; 348ec2f33abSBill Richardson } 349ec2f33abSBill Richardson 35022c040faSEnric Balletbo i Serra /* 35122c040faSEnric Balletbo i Serra * Read the mapped ID twice, the first one is assuming the 35222c040faSEnric Balletbo i Serra * EC is a Microchip Embedded Controller (MEC) variant, if the 35322c040faSEnric Balletbo i Serra * protocol fails, fallback to the non MEC variant and try to 35422c040faSEnric Balletbo i Serra * read again the ID. 35522c040faSEnric Balletbo i Serra */ 35622c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read = cros_ec_lpc_mec_read_bytes; 35722c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes; 35822c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); 35922c040faSEnric Balletbo i Serra if (buf[0] != 'E' || buf[1] != 'C') { 36022c040faSEnric Balletbo i Serra /* Re-assign read/write operations for the non MEC variant */ 36122c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read = cros_ec_lpc_read_bytes; 36222c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write = cros_ec_lpc_write_bytes; 36322c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, 36422c040faSEnric Balletbo i Serra buf); 365bce70fefSShawn Nematbakhsh if (buf[0] != 'E' || buf[1] != 'C') { 366ec2f33abSBill Richardson dev_err(dev, "EC ID not detected\n"); 367ec2f33abSBill Richardson return -ENODEV; 368ec2f33abSBill Richardson } 36922c040faSEnric Balletbo i Serra } 370ec2f33abSBill Richardson 371ec2f33abSBill Richardson if (!devm_request_region(dev, EC_HOST_CMD_REGION0, 372ec2f33abSBill Richardson EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 373ec2f33abSBill Richardson dev_err(dev, "couldn't reserve region0\n"); 374ec2f33abSBill Richardson return -EBUSY; 375ec2f33abSBill Richardson } 376ec2f33abSBill Richardson if (!devm_request_region(dev, EC_HOST_CMD_REGION1, 377ec2f33abSBill Richardson EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 378ec2f33abSBill Richardson dev_err(dev, "couldn't reserve region1\n"); 379ec2f33abSBill Richardson return -EBUSY; 380ec2f33abSBill Richardson } 381ec2f33abSBill Richardson 382ec2f33abSBill Richardson ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); 383ec2f33abSBill Richardson if (!ec_dev) 384ec2f33abSBill Richardson return -ENOMEM; 385ec2f33abSBill Richardson 386ec2f33abSBill Richardson platform_set_drvdata(pdev, ec_dev); 387ec2f33abSBill Richardson ec_dev->dev = dev; 388ec2f33abSBill Richardson ec_dev->phys_name = dev_name(dev); 389ec2f33abSBill Richardson ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; 390d3654070SStephen Barber ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc; 391ec2f33abSBill Richardson ec_dev->cmd_readmem = cros_ec_lpc_readmem; 3922c7589afSStephen Barber ec_dev->din_size = sizeof(struct ec_host_response) + 3932c7589afSStephen Barber sizeof(struct ec_response_get_protocol_info); 3942c7589afSStephen Barber ec_dev->dout_size = sizeof(struct ec_host_request); 395ec2f33abSBill Richardson 396da1cf5a1SEnrico Granata /* 397da1cf5a1SEnrico Granata * Some boards do not have an IRQ allotted for cros_ec_lpc, 398da1cf5a1SEnrico Granata * which makes ENXIO an expected (and safe) scenario. 399da1cf5a1SEnrico Granata */ 400a69b4eebSEnric Balletbo i Serra irq = platform_get_irq_optional(pdev, 0); 401da1cf5a1SEnrico Granata if (irq > 0) 402da1cf5a1SEnrico Granata ec_dev->irq = irq; 403da1cf5a1SEnrico Granata else if (irq != -ENXIO) { 404da1cf5a1SEnrico Granata dev_err(dev, "couldn't retrieve IRQ number (%d)\n", irq); 405da1cf5a1SEnrico Granata return irq; 406da1cf5a1SEnrico Granata } 407da1cf5a1SEnrico Granata 408ec2f33abSBill Richardson ret = cros_ec_register(ec_dev); 409ec2f33abSBill Richardson if (ret) { 410ec2f33abSBill Richardson dev_err(dev, "couldn't register ec_dev (%d)\n", ret); 411ec2f33abSBill Richardson return ret; 412ec2f33abSBill Richardson } 413ec2f33abSBill Richardson 414a6df7798SGwendal Grignou /* 415a6df7798SGwendal Grignou * Connect a notify handler to process MKBP messages if we have a 416a6df7798SGwendal Grignou * companion ACPI device. 417a6df7798SGwendal Grignou */ 418a6df7798SGwendal Grignou adev = ACPI_COMPANION(dev); 419a6df7798SGwendal Grignou if (adev) { 420a6df7798SGwendal Grignou status = acpi_install_notify_handler(adev->handle, 421a6df7798SGwendal Grignou ACPI_ALL_NOTIFY, 422a6df7798SGwendal Grignou cros_ec_lpc_acpi_notify, 423a6df7798SGwendal Grignou ec_dev); 424a6df7798SGwendal Grignou if (ACPI_FAILURE(status)) 425a6df7798SGwendal Grignou dev_warn(dev, "Failed to register notifier %08x\n", 426a6df7798SGwendal Grignou status); 427a6df7798SGwendal Grignou } 428a6df7798SGwendal Grignou 429ec2f33abSBill Richardson return 0; 430ec2f33abSBill Richardson } 431ec2f33abSBill Richardson 432ec2f33abSBill Richardson static int cros_ec_lpc_remove(struct platform_device *pdev) 433ec2f33abSBill Richardson { 4347aa703bbSEnric Balletbo i Serra struct cros_ec_device *ec_dev = platform_get_drvdata(pdev); 435a6df7798SGwendal Grignou struct acpi_device *adev; 436a6df7798SGwendal Grignou 437a6df7798SGwendal Grignou adev = ACPI_COMPANION(&pdev->dev); 438a6df7798SGwendal Grignou if (adev) 439a6df7798SGwendal Grignou acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, 440a6df7798SGwendal Grignou cros_ec_lpc_acpi_notify); 441ec2f33abSBill Richardson 4427aa703bbSEnric Balletbo i Serra return cros_ec_unregister(ec_dev); 443ec2f33abSBill Richardson } 444ec2f33abSBill Richardson 44512278dc7SGwendal Grignou static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { 44612278dc7SGwendal Grignou { ACPI_DRV_NAME, 0 }, 44712278dc7SGwendal Grignou { } 44812278dc7SGwendal Grignou }; 44912278dc7SGwendal Grignou MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); 45012278dc7SGwendal Grignou 4516faadbbbSChristoph Hellwig static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { 452ec2f33abSBill Richardson { 453ec2f33abSBill Richardson /* 454ec2f33abSBill Richardson * Today all Chromebooks/boxes ship with Google_* as version and 455ec2f33abSBill Richardson * coreboot as bios vendor. No other systems with this 456ec2f33abSBill Richardson * combination are known to date. 457ec2f33abSBill Richardson */ 458ec2f33abSBill Richardson .matches = { 459ec2f33abSBill Richardson DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 460ec2f33abSBill Richardson DMI_MATCH(DMI_BIOS_VERSION, "Google_"), 461ec2f33abSBill Richardson }, 462ec2f33abSBill Richardson }, 463ec2f33abSBill Richardson { 464f56db262SSalvatore Bellizzi /* 465f56db262SSalvatore Bellizzi * If the box is running custom coreboot firmware then the 466f56db262SSalvatore Bellizzi * DMI BIOS version string will not be matched by "Google_", 467f56db262SSalvatore Bellizzi * but the system vendor string will still be matched by 468f56db262SSalvatore Bellizzi * "GOOGLE". 469f56db262SSalvatore Bellizzi */ 470f56db262SSalvatore Bellizzi .matches = { 471f56db262SSalvatore Bellizzi DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 472f56db262SSalvatore Bellizzi DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 473f56db262SSalvatore Bellizzi }, 474f56db262SSalvatore Bellizzi }, 475f56db262SSalvatore Bellizzi { 476ec2f33abSBill Richardson /* x86-link, the Chromebook Pixel. */ 477ec2f33abSBill Richardson .matches = { 478ec2f33abSBill Richardson DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 479ec2f33abSBill Richardson DMI_MATCH(DMI_PRODUCT_NAME, "Link"), 480ec2f33abSBill Richardson }, 481ec2f33abSBill Richardson }, 482ec2f33abSBill Richardson { 48385bba84eSJavier Martinez Canillas /* x86-samus, the Chromebook Pixel 2. */ 48485bba84eSJavier Martinez Canillas .matches = { 48585bba84eSJavier Martinez Canillas DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 48685bba84eSJavier Martinez Canillas DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), 48785bba84eSJavier Martinez Canillas }, 48885bba84eSJavier Martinez Canillas }, 48985bba84eSJavier Martinez Canillas { 490ec2f33abSBill Richardson /* x86-peppy, the Acer C720 Chromebook. */ 491ec2f33abSBill Richardson .matches = { 492ec2f33abSBill Richardson DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 493ec2f33abSBill Richardson DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), 494ec2f33abSBill Richardson }, 495ec2f33abSBill Richardson }, 496e6751917SThierry Escande { 497e6751917SThierry Escande /* x86-glimmer, the Lenovo Thinkpad Yoga 11e. */ 498e6751917SThierry Escande .matches = { 499e6751917SThierry Escande DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 500e6751917SThierry Escande DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"), 501e6751917SThierry Escande }, 502e6751917SThierry Escande }, 503ec2f33abSBill Richardson { /* sentinel */ } 504ec2f33abSBill Richardson }; 505ec2f33abSBill Richardson MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); 506ec2f33abSBill Richardson 507450de8f4SArchana Patni #ifdef CONFIG_PM_SLEEP 508450de8f4SArchana Patni static int cros_ec_lpc_suspend(struct device *dev) 509450de8f4SArchana Patni { 510450de8f4SArchana Patni struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 511450de8f4SArchana Patni 512450de8f4SArchana Patni return cros_ec_suspend(ec_dev); 513450de8f4SArchana Patni } 514450de8f4SArchana Patni 515450de8f4SArchana Patni static int cros_ec_lpc_resume(struct device *dev) 516450de8f4SArchana Patni { 517450de8f4SArchana Patni struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 518450de8f4SArchana Patni 519450de8f4SArchana Patni return cros_ec_resume(ec_dev); 520450de8f4SArchana Patni } 521450de8f4SArchana Patni #endif 522450de8f4SArchana Patni 52381bc8c03SYueHaibing static const struct dev_pm_ops cros_ec_lpc_pm_ops = { 524450de8f4SArchana Patni SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume) 525450de8f4SArchana Patni }; 526450de8f4SArchana Patni 527ec2f33abSBill Richardson static struct platform_driver cros_ec_lpc_driver = { 528ec2f33abSBill Richardson .driver = { 529ec2f33abSBill Richardson .name = DRV_NAME, 53012278dc7SGwendal Grignou .acpi_match_table = cros_ec_lpc_acpi_device_ids, 531450de8f4SArchana Patni .pm = &cros_ec_lpc_pm_ops, 532ec2f33abSBill Richardson }, 533ec2f33abSBill Richardson .probe = cros_ec_lpc_probe, 534ec2f33abSBill Richardson .remove = cros_ec_lpc_remove, 535ec2f33abSBill Richardson }; 536ec2f33abSBill Richardson 5375f454bdfSEnric Balletbo i Serra static struct platform_device cros_ec_lpc_device = { 5385f454bdfSEnric Balletbo i Serra .name = DRV_NAME 5395f454bdfSEnric Balletbo i Serra }; 5405f454bdfSEnric Balletbo i Serra 5415f454bdfSEnric Balletbo i Serra static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level, 5425f454bdfSEnric Balletbo i Serra void *context, void **retval) 5435f454bdfSEnric Balletbo i Serra { 5445f454bdfSEnric Balletbo i Serra *(bool *)context = true; 5455f454bdfSEnric Balletbo i Serra return AE_CTRL_TERMINATE; 5465f454bdfSEnric Balletbo i Serra } 5475f454bdfSEnric Balletbo i Serra 548ec2f33abSBill Richardson static int __init cros_ec_lpc_init(void) 549ec2f33abSBill Richardson { 550ec2f33abSBill Richardson int ret; 5515f454bdfSEnric Balletbo i Serra acpi_status status; 552ec2f33abSBill Richardson 553b410b122SDmitry Torokhov status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device, 554b410b122SDmitry Torokhov &cros_ec_lpc_acpi_device_found, NULL); 555b410b122SDmitry Torokhov if (ACPI_FAILURE(status)) 556b410b122SDmitry Torokhov pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME); 557b410b122SDmitry Torokhov 558b410b122SDmitry Torokhov if (!cros_ec_lpc_acpi_device_found && 559b410b122SDmitry Torokhov !dmi_check_system(cros_ec_lpc_dmi_table)) { 560ec2f33abSBill Richardson pr_err(DRV_NAME ": unsupported system.\n"); 561ec2f33abSBill Richardson return -ENODEV; 562ec2f33abSBill Richardson } 563ec2f33abSBill Richardson 56422c040faSEnric Balletbo i Serra cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, 56522c040faSEnric Balletbo i Serra EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); 5668d4a3dc4SShawn Nematbakhsh 567ec2f33abSBill Richardson /* Register the driver */ 568ec2f33abSBill Richardson ret = platform_driver_register(&cros_ec_lpc_driver); 569ec2f33abSBill Richardson if (ret) { 570ec2f33abSBill Richardson pr_err(DRV_NAME ": can't register driver: %d\n", ret); 57122c040faSEnric Balletbo i Serra cros_ec_lpc_mec_destroy(); 572ec2f33abSBill Richardson return ret; 573ec2f33abSBill Richardson } 574ec2f33abSBill Richardson 5755f454bdfSEnric Balletbo i Serra if (!cros_ec_lpc_acpi_device_found) { 5765f454bdfSEnric Balletbo i Serra /* Register the device, and it'll get hooked up automatically */ 5775f454bdfSEnric Balletbo i Serra ret = platform_device_register(&cros_ec_lpc_device); 5785f454bdfSEnric Balletbo i Serra if (ret) { 5795f454bdfSEnric Balletbo i Serra pr_err(DRV_NAME ": can't register device: %d\n", ret); 5805f454bdfSEnric Balletbo i Serra platform_driver_unregister(&cros_ec_lpc_driver); 58122c040faSEnric Balletbo i Serra cros_ec_lpc_mec_destroy(); 5825f454bdfSEnric Balletbo i Serra } 5835f454bdfSEnric Balletbo i Serra } 5845f454bdfSEnric Balletbo i Serra 5855f454bdfSEnric Balletbo i Serra return ret; 586ec2f33abSBill Richardson } 587ec2f33abSBill Richardson 588ec2f33abSBill Richardson static void __exit cros_ec_lpc_exit(void) 589ec2f33abSBill Richardson { 5905f454bdfSEnric Balletbo i Serra if (!cros_ec_lpc_acpi_device_found) 5915f454bdfSEnric Balletbo i Serra platform_device_unregister(&cros_ec_lpc_device); 592ec2f33abSBill Richardson platform_driver_unregister(&cros_ec_lpc_driver); 59322c040faSEnric Balletbo i Serra cros_ec_lpc_mec_destroy(); 594ec2f33abSBill Richardson } 595ec2f33abSBill Richardson 596ec2f33abSBill Richardson module_init(cros_ec_lpc_init); 597ec2f33abSBill Richardson module_exit(cros_ec_lpc_exit); 598ec2f33abSBill Richardson 599ec2f33abSBill Richardson MODULE_LICENSE("GPL"); 600ec2f33abSBill Richardson MODULE_DESCRIPTION("ChromeOS EC LPC driver"); 601