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/mfd/cros_ec.h> 20ec2f33abSBill Richardson #include <linux/mfd/cros_ec_commands.h> 21ec2f33abSBill Richardson #include <linux/module.h> 22ec2f33abSBill Richardson #include <linux/platform_device.h> 23ec2f33abSBill Richardson #include <linux/printk.h> 24d6542b39SWenkai Du #include <linux/suspend.h> 25ec2f33abSBill Richardson 264116fd25SEnric Balletbo i Serra #include "cros_ec_lpc_mec.h" 27cc8a4ea1SEnric Balletbo i Serra 28bce70fefSShawn Nematbakhsh #define DRV_NAME "cros_ec_lpcs" 2912278dc7SGwendal Grignou #define ACPI_DRV_NAME "GOOG0004" 30ec2f33abSBill Richardson 315f454bdfSEnric Balletbo i Serra /* True if ACPI device is present */ 325f454bdfSEnric Balletbo i Serra static bool cros_ec_lpc_acpi_device_found; 335f454bdfSEnric Balletbo i Serra 34*22c040faSEnric Balletbo i Serra /** 35*22c040faSEnric Balletbo i Serra * struct lpc_driver_ops - LPC driver operations 36*22c040faSEnric Balletbo i Serra * @read: Copy length bytes from EC address offset into buffer dest. Returns 37*22c040faSEnric Balletbo i Serra * the 8-bit checksum of all bytes read. 38*22c040faSEnric Balletbo i Serra * @write: Copy length bytes from buffer msg into EC address offset. Returns 39*22c040faSEnric Balletbo i Serra * the 8-bit checksum of all bytes written. 40*22c040faSEnric Balletbo i Serra */ 41*22c040faSEnric Balletbo i Serra struct lpc_driver_ops { 42*22c040faSEnric Balletbo i Serra u8 (*read)(unsigned int offset, unsigned int length, u8 *dest); 43*22c040faSEnric Balletbo i Serra u8 (*write)(unsigned int offset, unsigned int length, const u8 *msg); 44*22c040faSEnric Balletbo i Serra }; 45*22c040faSEnric Balletbo i Serra 46*22c040faSEnric Balletbo i Serra static struct lpc_driver_ops cros_ec_lpc_ops = { }; 47*22c040faSEnric Balletbo i Serra 48*22c040faSEnric Balletbo i Serra /* 49*22c040faSEnric Balletbo i Serra * A generic instance of the read function of struct lpc_driver_ops, used for 50*22c040faSEnric Balletbo i Serra * the LPC EC. 51*22c040faSEnric Balletbo i Serra */ 52*22c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, 53*22c040faSEnric Balletbo i Serra u8 *dest) 544116fd25SEnric Balletbo i Serra { 554116fd25SEnric Balletbo i Serra int sum = 0; 564116fd25SEnric Balletbo i Serra int i; 574116fd25SEnric Balletbo i Serra 584116fd25SEnric Balletbo i Serra for (i = 0; i < length; ++i) { 594116fd25SEnric Balletbo i Serra dest[i] = inb(offset + i); 604116fd25SEnric Balletbo i Serra sum += dest[i]; 614116fd25SEnric Balletbo i Serra } 624116fd25SEnric Balletbo i Serra 634116fd25SEnric Balletbo i Serra /* Return checksum of all bytes read */ 644116fd25SEnric Balletbo i Serra return sum; 654116fd25SEnric Balletbo i Serra } 664116fd25SEnric Balletbo i Serra 67*22c040faSEnric Balletbo i Serra /* 68*22c040faSEnric Balletbo i Serra * A generic instance of the write function of struct lpc_driver_ops, used for 69*22c040faSEnric Balletbo i Serra * the LPC EC. 70*22c040faSEnric Balletbo i Serra */ 71*22c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, 72*22c040faSEnric Balletbo i Serra const u8 *msg) 734116fd25SEnric Balletbo i Serra { 744116fd25SEnric Balletbo i Serra int sum = 0; 754116fd25SEnric Balletbo i Serra int i; 764116fd25SEnric Balletbo i Serra 774116fd25SEnric Balletbo i Serra for (i = 0; i < length; ++i) { 784116fd25SEnric Balletbo i Serra outb(msg[i], offset + i); 794116fd25SEnric Balletbo i Serra sum += msg[i]; 804116fd25SEnric Balletbo i Serra } 814116fd25SEnric Balletbo i Serra 824116fd25SEnric Balletbo i Serra /* Return checksum of all bytes written */ 834116fd25SEnric Balletbo i Serra return sum; 844116fd25SEnric Balletbo i Serra } 854116fd25SEnric Balletbo i Serra 86*22c040faSEnric Balletbo i Serra /* 87*22c040faSEnric Balletbo i Serra * An instance of the read function of struct lpc_driver_ops, used for the 88*22c040faSEnric Balletbo i Serra * MEC variant of LPC EC. 89*22c040faSEnric Balletbo i Serra */ 90*22c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length, 914116fd25SEnric Balletbo i Serra u8 *dest) 924116fd25SEnric Balletbo i Serra { 934116fd25SEnric Balletbo i Serra int in_range = cros_ec_lpc_mec_in_range(offset, length); 944116fd25SEnric Balletbo i Serra 954116fd25SEnric Balletbo i Serra if (in_range < 0) 964116fd25SEnric Balletbo i Serra return 0; 974116fd25SEnric Balletbo i Serra 984116fd25SEnric Balletbo i Serra return in_range ? 994116fd25SEnric Balletbo i Serra cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 1004116fd25SEnric Balletbo i Serra offset - EC_HOST_CMD_REGION0, 1014116fd25SEnric Balletbo i Serra length, dest) : 102*22c040faSEnric Balletbo i Serra cros_ec_lpc_read_bytes(offset, length, dest); 1034116fd25SEnric Balletbo i Serra } 1044116fd25SEnric Balletbo i Serra 105*22c040faSEnric Balletbo i Serra /* 106*22c040faSEnric Balletbo i Serra * An instance of the write function of struct lpc_driver_ops, used for the 107*22c040faSEnric Balletbo i Serra * MEC variant of LPC EC. 108*22c040faSEnric Balletbo i Serra */ 109*22c040faSEnric Balletbo i Serra static u8 cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length, 110*22c040faSEnric Balletbo i Serra const u8 *msg) 1114116fd25SEnric Balletbo i Serra { 1124116fd25SEnric Balletbo i Serra int in_range = cros_ec_lpc_mec_in_range(offset, length); 1134116fd25SEnric Balletbo i Serra 1144116fd25SEnric Balletbo i Serra if (in_range < 0) 1154116fd25SEnric Balletbo i Serra return 0; 1164116fd25SEnric Balletbo i Serra 1174116fd25SEnric Balletbo i Serra return in_range ? 1184116fd25SEnric Balletbo i Serra cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 1194116fd25SEnric Balletbo i Serra offset - EC_HOST_CMD_REGION0, 120*22c040faSEnric Balletbo i Serra length, (u8 *)msg) : 121*22c040faSEnric Balletbo i Serra cros_ec_lpc_write_bytes(offset, length, msg); 1224116fd25SEnric Balletbo i Serra } 1234116fd25SEnric Balletbo i Serra 124ec2f33abSBill Richardson static int ec_response_timed_out(void) 125ec2f33abSBill Richardson { 126ec2f33abSBill Richardson unsigned long one_second = jiffies + HZ; 127bce70fefSShawn Nematbakhsh u8 data; 128ec2f33abSBill Richardson 129ec2f33abSBill Richardson usleep_range(200, 300); 130ec2f33abSBill Richardson do { 131*22c040faSEnric Balletbo i Serra if (!(cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data) & 132bce70fefSShawn Nematbakhsh EC_LPC_STATUS_BUSY_MASK)) 133ec2f33abSBill Richardson return 0; 134ec2f33abSBill Richardson usleep_range(100, 200); 135ec2f33abSBill Richardson } while (time_before(jiffies, one_second)); 136ec2f33abSBill Richardson 137ec2f33abSBill Richardson return 1; 138ec2f33abSBill Richardson } 139ec2f33abSBill Richardson 140d3654070SStephen Barber static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, 141d3654070SStephen Barber struct cros_ec_command *msg) 142d3654070SStephen Barber { 143d3654070SStephen Barber struct ec_host_response response; 144bce70fefSShawn Nematbakhsh u8 sum; 145d3654070SStephen Barber int ret = 0; 146d3654070SStephen Barber u8 *dout; 147d3654070SStephen Barber 148d3654070SStephen Barber ret = cros_ec_prepare_tx(ec, msg); 149d3654070SStephen Barber 150d3654070SStephen Barber /* Write buffer */ 151*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); 152d3654070SStephen Barber 153d3654070SStephen Barber /* Here we go */ 154bce70fefSShawn Nematbakhsh sum = EC_COMMAND_PROTOCOL_3; 155*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); 156d3654070SStephen Barber 157d3654070SStephen Barber if (ec_response_timed_out()) { 158d3654070SStephen Barber dev_warn(ec->dev, "EC responsed timed out\n"); 159d3654070SStephen Barber ret = -EIO; 160d3654070SStephen Barber goto done; 161d3654070SStephen Barber } 162d3654070SStephen Barber 163d3654070SStephen Barber /* Check result */ 164*22c040faSEnric Balletbo i Serra msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); 165d3654070SStephen Barber ret = cros_ec_check_result(ec, msg); 166d3654070SStephen Barber if (ret) 167d3654070SStephen Barber goto done; 168d3654070SStephen Barber 169d3654070SStephen Barber /* Read back response */ 170d3654070SStephen Barber dout = (u8 *)&response; 171*22c040faSEnric Balletbo i Serra sum = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response), 172bce70fefSShawn Nematbakhsh dout); 173d3654070SStephen Barber 174d3654070SStephen Barber msg->result = response.result; 175d3654070SStephen Barber 176d3654070SStephen Barber if (response.data_len > msg->insize) { 177d3654070SStephen Barber dev_err(ec->dev, 178d3654070SStephen Barber "packet too long (%d bytes, expected %d)", 179d3654070SStephen Barber response.data_len, msg->insize); 180d3654070SStephen Barber ret = -EMSGSIZE; 181d3654070SStephen Barber goto done; 182d3654070SStephen Barber } 183d3654070SStephen Barber 184d3654070SStephen Barber /* Read response and process checksum */ 185*22c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET + 186bce70fefSShawn Nematbakhsh sizeof(response), response.data_len, 187bce70fefSShawn Nematbakhsh msg->data); 188d3654070SStephen Barber 189d3654070SStephen Barber if (sum) { 190d3654070SStephen Barber dev_err(ec->dev, 191d3654070SStephen Barber "bad packet checksum %02x\n", 192d3654070SStephen Barber response.checksum); 193d3654070SStephen Barber ret = -EBADMSG; 194d3654070SStephen Barber goto done; 195d3654070SStephen Barber } 196d3654070SStephen Barber 197d3654070SStephen Barber /* Return actual amount of data received */ 198d3654070SStephen Barber ret = response.data_len; 199d3654070SStephen Barber done: 200d3654070SStephen Barber return ret; 201d3654070SStephen Barber } 202d3654070SStephen Barber 203ec2f33abSBill Richardson static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, 204ec2f33abSBill Richardson struct cros_ec_command *msg) 205ec2f33abSBill Richardson { 206ec2f33abSBill Richardson struct ec_lpc_host_args args; 207bce70fefSShawn Nematbakhsh u8 sum; 208ec2f33abSBill Richardson int ret = 0; 209ec2f33abSBill Richardson 210ec2f33abSBill Richardson if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || 211ec2f33abSBill Richardson msg->insize > EC_PROTO2_MAX_PARAM_SIZE) { 212ec2f33abSBill Richardson dev_err(ec->dev, 213ec2f33abSBill Richardson "invalid buffer sizes (out %d, in %d)\n", 214ec2f33abSBill Richardson msg->outsize, msg->insize); 215ec2f33abSBill Richardson return -EINVAL; 216ec2f33abSBill Richardson } 217ec2f33abSBill Richardson 218ec2f33abSBill Richardson /* Now actually send the command to the EC and get the result */ 219ec2f33abSBill Richardson args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 220ec2f33abSBill Richardson args.command_version = msg->version; 221ec2f33abSBill Richardson args.data_size = msg->outsize; 222ec2f33abSBill Richardson 223ec2f33abSBill Richardson /* Initialize checksum */ 224bce70fefSShawn Nematbakhsh sum = msg->command + args.flags + args.command_version + args.data_size; 225ec2f33abSBill Richardson 226ec2f33abSBill Richardson /* Copy data and update checksum */ 227*22c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize, 228bce70fefSShawn Nematbakhsh msg->data); 229ec2f33abSBill Richardson 230ec2f33abSBill Richardson /* Finalize checksum and write args */ 231bce70fefSShawn Nematbakhsh args.checksum = sum; 232*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args), 233bce70fefSShawn Nematbakhsh (u8 *)&args); 234ec2f33abSBill Richardson 235ec2f33abSBill Richardson /* Here we go */ 236bce70fefSShawn Nematbakhsh sum = msg->command; 237*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); 238ec2f33abSBill Richardson 239ec2f33abSBill Richardson if (ec_response_timed_out()) { 240ec2f33abSBill Richardson dev_warn(ec->dev, "EC responsed timed out\n"); 241ec2f33abSBill Richardson ret = -EIO; 242ec2f33abSBill Richardson goto done; 243ec2f33abSBill Richardson } 244ec2f33abSBill Richardson 245ec2f33abSBill Richardson /* Check result */ 246*22c040faSEnric Balletbo i Serra msg->result = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum); 247fbf40727SJavier Martinez Canillas ret = cros_ec_check_result(ec, msg); 248fbf40727SJavier Martinez Canillas if (ret) 249ec2f33abSBill Richardson goto done; 250ec2f33abSBill Richardson 251ec2f33abSBill Richardson /* Read back args */ 252*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args); 253ec2f33abSBill Richardson 254ec2f33abSBill Richardson if (args.data_size > msg->insize) { 255ec2f33abSBill Richardson dev_err(ec->dev, 256ec2f33abSBill Richardson "packet too long (%d bytes, expected %d)", 257ec2f33abSBill Richardson args.data_size, msg->insize); 258ec2f33abSBill Richardson ret = -ENOSPC; 259ec2f33abSBill Richardson goto done; 260ec2f33abSBill Richardson } 261ec2f33abSBill Richardson 262ec2f33abSBill Richardson /* Start calculating response checksum */ 263bce70fefSShawn Nematbakhsh sum = msg->command + args.flags + args.command_version + args.data_size; 264ec2f33abSBill Richardson 265ec2f33abSBill Richardson /* Read response and update checksum */ 266*22c040faSEnric Balletbo i Serra sum += cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size, 267bce70fefSShawn Nematbakhsh msg->data); 268ec2f33abSBill Richardson 269ec2f33abSBill Richardson /* Verify checksum */ 270bce70fefSShawn Nematbakhsh if (args.checksum != sum) { 271ec2f33abSBill Richardson dev_err(ec->dev, 272ec2f33abSBill Richardson "bad packet checksum, expected %02x, got %02x\n", 273bce70fefSShawn Nematbakhsh args.checksum, sum); 274ec2f33abSBill Richardson ret = -EBADMSG; 275ec2f33abSBill Richardson goto done; 276ec2f33abSBill Richardson } 277ec2f33abSBill Richardson 278ec2f33abSBill Richardson /* Return actual amount of data received */ 279ec2f33abSBill Richardson ret = args.data_size; 280ec2f33abSBill Richardson done: 281ec2f33abSBill Richardson return ret; 282ec2f33abSBill Richardson } 283ec2f33abSBill Richardson 284ec2f33abSBill Richardson /* Returns num bytes read, or negative on error. Doesn't need locking. */ 285ec2f33abSBill Richardson static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, 286ec2f33abSBill Richardson unsigned int bytes, void *dest) 287ec2f33abSBill Richardson { 288ec2f33abSBill Richardson int i = offset; 289ec2f33abSBill Richardson char *s = dest; 290ec2f33abSBill Richardson int cnt = 0; 291ec2f33abSBill Richardson 292ec2f33abSBill Richardson if (offset >= EC_MEMMAP_SIZE - bytes) 293ec2f33abSBill Richardson return -EINVAL; 294ec2f33abSBill Richardson 295ec2f33abSBill Richardson /* fixed length */ 296ec2f33abSBill Richardson if (bytes) { 297*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + offset, bytes, s); 298bce70fefSShawn Nematbakhsh return bytes; 299ec2f33abSBill Richardson } 300ec2f33abSBill Richardson 301ec2f33abSBill Richardson /* string */ 302ec2f33abSBill Richardson for (; i < EC_MEMMAP_SIZE; i++, s++) { 303*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, 1, s); 304ec2f33abSBill Richardson cnt++; 305ec2f33abSBill Richardson if (!*s) 306ec2f33abSBill Richardson break; 307ec2f33abSBill Richardson } 308ec2f33abSBill Richardson 309ec2f33abSBill Richardson return cnt; 310ec2f33abSBill Richardson } 311ec2f33abSBill Richardson 312a6df7798SGwendal Grignou static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) 313a6df7798SGwendal Grignou { 314a6df7798SGwendal Grignou struct cros_ec_device *ec_dev = data; 315a6df7798SGwendal Grignou 31629d99b96SShawn Nematbakhsh if (ec_dev->mkbp_event_supported && 31729d99b96SShawn Nematbakhsh cros_ec_get_next_event(ec_dev, NULL) > 0) 318a6df7798SGwendal Grignou blocking_notifier_call_chain(&ec_dev->event_notifier, 0, 319a6df7798SGwendal Grignou ec_dev); 320d6542b39SWenkai Du 321d6542b39SWenkai Du if (value == ACPI_NOTIFY_DEVICE_WAKE) 322d6542b39SWenkai Du pm_system_wakeup(); 323a6df7798SGwendal Grignou } 324a6df7798SGwendal Grignou 325ec2f33abSBill Richardson static int cros_ec_lpc_probe(struct platform_device *pdev) 326ec2f33abSBill Richardson { 327ec2f33abSBill Richardson struct device *dev = &pdev->dev; 328a6df7798SGwendal Grignou struct acpi_device *adev; 329a6df7798SGwendal Grignou acpi_status status; 330ec2f33abSBill Richardson struct cros_ec_device *ec_dev; 331bce70fefSShawn Nematbakhsh u8 buf[2]; 332da1cf5a1SEnrico Granata int irq, ret; 333ec2f33abSBill Richardson 334ec2f33abSBill Richardson if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, 335ec2f33abSBill Richardson dev_name(dev))) { 336ec2f33abSBill Richardson dev_err(dev, "couldn't reserve memmap region\n"); 337ec2f33abSBill Richardson return -EBUSY; 338ec2f33abSBill Richardson } 339ec2f33abSBill Richardson 340*22c040faSEnric Balletbo i Serra /* 341*22c040faSEnric Balletbo i Serra * Read the mapped ID twice, the first one is assuming the 342*22c040faSEnric Balletbo i Serra * EC is a Microchip Embedded Controller (MEC) variant, if the 343*22c040faSEnric Balletbo i Serra * protocol fails, fallback to the non MEC variant and try to 344*22c040faSEnric Balletbo i Serra * read again the ID. 345*22c040faSEnric Balletbo i Serra */ 346*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read = cros_ec_lpc_mec_read_bytes; 347*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes; 348*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); 349*22c040faSEnric Balletbo i Serra if (buf[0] != 'E' || buf[1] != 'C') { 350*22c040faSEnric Balletbo i Serra /* Re-assign read/write operations for the non MEC variant */ 351*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read = cros_ec_lpc_read_bytes; 352*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.write = cros_ec_lpc_write_bytes; 353*22c040faSEnric Balletbo i Serra cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, 354*22c040faSEnric Balletbo i Serra buf); 355bce70fefSShawn Nematbakhsh if (buf[0] != 'E' || buf[1] != 'C') { 356ec2f33abSBill Richardson dev_err(dev, "EC ID not detected\n"); 357ec2f33abSBill Richardson return -ENODEV; 358ec2f33abSBill Richardson } 359*22c040faSEnric Balletbo i Serra } 360ec2f33abSBill Richardson 361ec2f33abSBill Richardson if (!devm_request_region(dev, EC_HOST_CMD_REGION0, 362ec2f33abSBill Richardson EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 363ec2f33abSBill Richardson dev_err(dev, "couldn't reserve region0\n"); 364ec2f33abSBill Richardson return -EBUSY; 365ec2f33abSBill Richardson } 366ec2f33abSBill Richardson if (!devm_request_region(dev, EC_HOST_CMD_REGION1, 367ec2f33abSBill Richardson EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 368ec2f33abSBill Richardson dev_err(dev, "couldn't reserve region1\n"); 369ec2f33abSBill Richardson return -EBUSY; 370ec2f33abSBill Richardson } 371ec2f33abSBill Richardson 372ec2f33abSBill Richardson ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); 373ec2f33abSBill Richardson if (!ec_dev) 374ec2f33abSBill Richardson return -ENOMEM; 375ec2f33abSBill Richardson 376ec2f33abSBill Richardson platform_set_drvdata(pdev, ec_dev); 377ec2f33abSBill Richardson ec_dev->dev = dev; 378ec2f33abSBill Richardson ec_dev->phys_name = dev_name(dev); 379ec2f33abSBill Richardson ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; 380d3654070SStephen Barber ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc; 381ec2f33abSBill Richardson ec_dev->cmd_readmem = cros_ec_lpc_readmem; 3822c7589afSStephen Barber ec_dev->din_size = sizeof(struct ec_host_response) + 3832c7589afSStephen Barber sizeof(struct ec_response_get_protocol_info); 3842c7589afSStephen Barber ec_dev->dout_size = sizeof(struct ec_host_request); 385ec2f33abSBill Richardson 386da1cf5a1SEnrico Granata /* 387da1cf5a1SEnrico Granata * Some boards do not have an IRQ allotted for cros_ec_lpc, 388da1cf5a1SEnrico Granata * which makes ENXIO an expected (and safe) scenario. 389da1cf5a1SEnrico Granata */ 390da1cf5a1SEnrico Granata irq = platform_get_irq(pdev, 0); 391da1cf5a1SEnrico Granata if (irq > 0) 392da1cf5a1SEnrico Granata ec_dev->irq = irq; 393da1cf5a1SEnrico Granata else if (irq != -ENXIO) { 394da1cf5a1SEnrico Granata dev_err(dev, "couldn't retrieve IRQ number (%d)\n", irq); 395da1cf5a1SEnrico Granata return irq; 396da1cf5a1SEnrico Granata } 397da1cf5a1SEnrico Granata 398ec2f33abSBill Richardson ret = cros_ec_register(ec_dev); 399ec2f33abSBill Richardson if (ret) { 400ec2f33abSBill Richardson dev_err(dev, "couldn't register ec_dev (%d)\n", ret); 401ec2f33abSBill Richardson return ret; 402ec2f33abSBill Richardson } 403ec2f33abSBill Richardson 404a6df7798SGwendal Grignou /* 405a6df7798SGwendal Grignou * Connect a notify handler to process MKBP messages if we have a 406a6df7798SGwendal Grignou * companion ACPI device. 407a6df7798SGwendal Grignou */ 408a6df7798SGwendal Grignou adev = ACPI_COMPANION(dev); 409a6df7798SGwendal Grignou if (adev) { 410a6df7798SGwendal Grignou status = acpi_install_notify_handler(adev->handle, 411a6df7798SGwendal Grignou ACPI_ALL_NOTIFY, 412a6df7798SGwendal Grignou cros_ec_lpc_acpi_notify, 413a6df7798SGwendal Grignou ec_dev); 414a6df7798SGwendal Grignou if (ACPI_FAILURE(status)) 415a6df7798SGwendal Grignou dev_warn(dev, "Failed to register notifier %08x\n", 416a6df7798SGwendal Grignou status); 417a6df7798SGwendal Grignou } 418a6df7798SGwendal Grignou 419ec2f33abSBill Richardson return 0; 420ec2f33abSBill Richardson } 421ec2f33abSBill Richardson 422ec2f33abSBill Richardson static int cros_ec_lpc_remove(struct platform_device *pdev) 423ec2f33abSBill Richardson { 424a6df7798SGwendal Grignou struct acpi_device *adev; 425a6df7798SGwendal Grignou 426a6df7798SGwendal Grignou adev = ACPI_COMPANION(&pdev->dev); 427a6df7798SGwendal Grignou if (adev) 428a6df7798SGwendal Grignou acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, 429a6df7798SGwendal Grignou cros_ec_lpc_acpi_notify); 430ec2f33abSBill Richardson 431ec2f33abSBill Richardson return 0; 432ec2f33abSBill Richardson } 433ec2f33abSBill Richardson 43412278dc7SGwendal Grignou static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { 43512278dc7SGwendal Grignou { ACPI_DRV_NAME, 0 }, 43612278dc7SGwendal Grignou { } 43712278dc7SGwendal Grignou }; 43812278dc7SGwendal Grignou MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); 43912278dc7SGwendal Grignou 4406faadbbbSChristoph Hellwig static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { 441ec2f33abSBill Richardson { 442ec2f33abSBill Richardson /* 443ec2f33abSBill Richardson * Today all Chromebooks/boxes ship with Google_* as version and 444ec2f33abSBill Richardson * coreboot as bios vendor. No other systems with this 445ec2f33abSBill Richardson * combination are known to date. 446ec2f33abSBill Richardson */ 447ec2f33abSBill Richardson .matches = { 448ec2f33abSBill Richardson DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 449ec2f33abSBill Richardson DMI_MATCH(DMI_BIOS_VERSION, "Google_"), 450ec2f33abSBill Richardson }, 451ec2f33abSBill Richardson }, 452ec2f33abSBill Richardson { 453f56db262SSalvatore Bellizzi /* 454f56db262SSalvatore Bellizzi * If the box is running custom coreboot firmware then the 455f56db262SSalvatore Bellizzi * DMI BIOS version string will not be matched by "Google_", 456f56db262SSalvatore Bellizzi * but the system vendor string will still be matched by 457f56db262SSalvatore Bellizzi * "GOOGLE". 458f56db262SSalvatore Bellizzi */ 459f56db262SSalvatore Bellizzi .matches = { 460f56db262SSalvatore Bellizzi DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 461f56db262SSalvatore Bellizzi DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 462f56db262SSalvatore Bellizzi }, 463f56db262SSalvatore Bellizzi }, 464f56db262SSalvatore Bellizzi { 465ec2f33abSBill Richardson /* x86-link, the Chromebook Pixel. */ 466ec2f33abSBill Richardson .matches = { 467ec2f33abSBill Richardson DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 468ec2f33abSBill Richardson DMI_MATCH(DMI_PRODUCT_NAME, "Link"), 469ec2f33abSBill Richardson }, 470ec2f33abSBill Richardson }, 471ec2f33abSBill Richardson { 47285bba84eSJavier Martinez Canillas /* x86-samus, the Chromebook Pixel 2. */ 47385bba84eSJavier Martinez Canillas .matches = { 47485bba84eSJavier Martinez Canillas DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 47585bba84eSJavier Martinez Canillas DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), 47685bba84eSJavier Martinez Canillas }, 47785bba84eSJavier Martinez Canillas }, 47885bba84eSJavier Martinez Canillas { 479ec2f33abSBill Richardson /* x86-peppy, the Acer C720 Chromebook. */ 480ec2f33abSBill Richardson .matches = { 481ec2f33abSBill Richardson DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 482ec2f33abSBill Richardson DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), 483ec2f33abSBill Richardson }, 484ec2f33abSBill Richardson }, 485e6751917SThierry Escande { 486e6751917SThierry Escande /* x86-glimmer, the Lenovo Thinkpad Yoga 11e. */ 487e6751917SThierry Escande .matches = { 488e6751917SThierry Escande DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 489e6751917SThierry Escande DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"), 490e6751917SThierry Escande }, 491e6751917SThierry Escande }, 492ec2f33abSBill Richardson { /* sentinel */ } 493ec2f33abSBill Richardson }; 494ec2f33abSBill Richardson MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); 495ec2f33abSBill Richardson 496450de8f4SArchana Patni #ifdef CONFIG_PM_SLEEP 497450de8f4SArchana Patni static int cros_ec_lpc_suspend(struct device *dev) 498450de8f4SArchana Patni { 499450de8f4SArchana Patni struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 500450de8f4SArchana Patni 501450de8f4SArchana Patni return cros_ec_suspend(ec_dev); 502450de8f4SArchana Patni } 503450de8f4SArchana Patni 504450de8f4SArchana Patni static int cros_ec_lpc_resume(struct device *dev) 505450de8f4SArchana Patni { 506450de8f4SArchana Patni struct cros_ec_device *ec_dev = dev_get_drvdata(dev); 507450de8f4SArchana Patni 508450de8f4SArchana Patni return cros_ec_resume(ec_dev); 509450de8f4SArchana Patni } 510450de8f4SArchana Patni #endif 511450de8f4SArchana Patni 51281bc8c03SYueHaibing static const struct dev_pm_ops cros_ec_lpc_pm_ops = { 513450de8f4SArchana Patni SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume) 514450de8f4SArchana Patni }; 515450de8f4SArchana Patni 516ec2f33abSBill Richardson static struct platform_driver cros_ec_lpc_driver = { 517ec2f33abSBill Richardson .driver = { 518ec2f33abSBill Richardson .name = DRV_NAME, 51912278dc7SGwendal Grignou .acpi_match_table = cros_ec_lpc_acpi_device_ids, 520450de8f4SArchana Patni .pm = &cros_ec_lpc_pm_ops, 521ec2f33abSBill Richardson }, 522ec2f33abSBill Richardson .probe = cros_ec_lpc_probe, 523ec2f33abSBill Richardson .remove = cros_ec_lpc_remove, 524ec2f33abSBill Richardson }; 525ec2f33abSBill Richardson 5265f454bdfSEnric Balletbo i Serra static struct platform_device cros_ec_lpc_device = { 5275f454bdfSEnric Balletbo i Serra .name = DRV_NAME 5285f454bdfSEnric Balletbo i Serra }; 5295f454bdfSEnric Balletbo i Serra 5305f454bdfSEnric Balletbo i Serra static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level, 5315f454bdfSEnric Balletbo i Serra void *context, void **retval) 5325f454bdfSEnric Balletbo i Serra { 5335f454bdfSEnric Balletbo i Serra *(bool *)context = true; 5345f454bdfSEnric Balletbo i Serra return AE_CTRL_TERMINATE; 5355f454bdfSEnric Balletbo i Serra } 5365f454bdfSEnric Balletbo i Serra 537ec2f33abSBill Richardson static int __init cros_ec_lpc_init(void) 538ec2f33abSBill Richardson { 539ec2f33abSBill Richardson int ret; 5405f454bdfSEnric Balletbo i Serra acpi_status status; 541ec2f33abSBill Richardson 542b410b122SDmitry Torokhov status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device, 543b410b122SDmitry Torokhov &cros_ec_lpc_acpi_device_found, NULL); 544b410b122SDmitry Torokhov if (ACPI_FAILURE(status)) 545b410b122SDmitry Torokhov pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME); 546b410b122SDmitry Torokhov 547b410b122SDmitry Torokhov if (!cros_ec_lpc_acpi_device_found && 548b410b122SDmitry Torokhov !dmi_check_system(cros_ec_lpc_dmi_table)) { 549ec2f33abSBill Richardson pr_err(DRV_NAME ": unsupported system.\n"); 550ec2f33abSBill Richardson return -ENODEV; 551ec2f33abSBill Richardson } 552ec2f33abSBill Richardson 553*22c040faSEnric Balletbo i Serra cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0, 554*22c040faSEnric Balletbo i Serra EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE); 5558d4a3dc4SShawn Nematbakhsh 556ec2f33abSBill Richardson /* Register the driver */ 557ec2f33abSBill Richardson ret = platform_driver_register(&cros_ec_lpc_driver); 558ec2f33abSBill Richardson if (ret) { 559ec2f33abSBill Richardson pr_err(DRV_NAME ": can't register driver: %d\n", ret); 560*22c040faSEnric Balletbo i Serra cros_ec_lpc_mec_destroy(); 561ec2f33abSBill Richardson return ret; 562ec2f33abSBill Richardson } 563ec2f33abSBill Richardson 5645f454bdfSEnric Balletbo i Serra if (!cros_ec_lpc_acpi_device_found) { 5655f454bdfSEnric Balletbo i Serra /* Register the device, and it'll get hooked up automatically */ 5665f454bdfSEnric Balletbo i Serra ret = platform_device_register(&cros_ec_lpc_device); 5675f454bdfSEnric Balletbo i Serra if (ret) { 5685f454bdfSEnric Balletbo i Serra pr_err(DRV_NAME ": can't register device: %d\n", ret); 5695f454bdfSEnric Balletbo i Serra platform_driver_unregister(&cros_ec_lpc_driver); 570*22c040faSEnric Balletbo i Serra cros_ec_lpc_mec_destroy(); 5715f454bdfSEnric Balletbo i Serra } 5725f454bdfSEnric Balletbo i Serra } 5735f454bdfSEnric Balletbo i Serra 5745f454bdfSEnric Balletbo i Serra return ret; 575ec2f33abSBill Richardson } 576ec2f33abSBill Richardson 577ec2f33abSBill Richardson static void __exit cros_ec_lpc_exit(void) 578ec2f33abSBill Richardson { 5795f454bdfSEnric Balletbo i Serra if (!cros_ec_lpc_acpi_device_found) 5805f454bdfSEnric Balletbo i Serra platform_device_unregister(&cros_ec_lpc_device); 581ec2f33abSBill Richardson platform_driver_unregister(&cros_ec_lpc_driver); 582*22c040faSEnric Balletbo i Serra cros_ec_lpc_mec_destroy(); 583ec2f33abSBill Richardson } 584ec2f33abSBill Richardson 585ec2f33abSBill Richardson module_init(cros_ec_lpc_init); 586ec2f33abSBill Richardson module_exit(cros_ec_lpc_exit); 587ec2f33abSBill Richardson 588ec2f33abSBill Richardson MODULE_LICENSE("GPL"); 589ec2f33abSBill Richardson MODULE_DESCRIPTION("ChromeOS EC LPC driver"); 590