On Tue, May 16, 2017 at 06:13:11PM +0200, Enric Balletbo i Serra wrote: > From: Shawn Nematbakhsh > > Call common functions for read / write to prepare support for future > LPC protocol variants which use different I/O ops than inb / outb. > > Signed-off-by: Shawn Nematbakhsh > Signed-off-by: Thierry Escande Signed-off-by: Benson Leung Applied to my upcoming immutable branch. > --- > drivers/platform/chrome/Makefile | 3 +- > drivers/platform/chrome/cros_ec_lpc.c | 88 +++++++++++++------------------ > drivers/platform/chrome/cros_ec_lpc_reg.c | 64 ++++++++++++++++++++++ > include/linux/mfd/cros_ec_lpc_reg.h | 47 +++++++++++++++++ > 4 files changed, 151 insertions(+), 51 deletions(-) > create mode 100644 drivers/platform/chrome/cros_ec_lpc_reg.c > create mode 100644 include/linux/mfd/cros_ec_lpc_reg.h > > diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile > index 3870afe..61182fd 100644 > --- a/drivers/platform/chrome/Makefile > +++ b/drivers/platform/chrome/Makefile > @@ -5,6 +5,7 @@ cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ > cros_ec_lightbar.o cros_ec_vbc.o \ > cros_ec_debugfs.o > obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o > -obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o > +cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o > +obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o > obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o > obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o > diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c > index f9a2454..6a782a6 100644 > --- a/drivers/platform/chrome/cros_ec_lpc.c > +++ b/drivers/platform/chrome/cros_ec_lpc.c > @@ -26,19 +26,22 @@ > #include > #include > #include > +#include > #include > #include > #include > > -#define DRV_NAME "cros_ec_lpc" > +#define DRV_NAME "cros_ec_lpcs" > > static int ec_response_timed_out(void) > { > unsigned long one_second = jiffies + HZ; > + u8 data; > > usleep_range(200, 300); > do { > - if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK)) > + if (!(cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_CMD, 1, &data) & > + EC_LPC_STATUS_BUSY_MASK)) > return 0; > usleep_range(100, 200); > } while (time_before(jiffies, one_second)); > @@ -51,21 +54,20 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, > { > struct ec_host_request *request; > struct ec_host_response response; > - u8 sum = 0; > - int i; > + u8 sum; > int ret = 0; > u8 *dout; > > ret = cros_ec_prepare_tx(ec, msg); > > /* Write buffer */ > - for (i = 0; i < ret; i++) > - outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i); > + cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); > > request = (struct ec_host_request *)ec->dout; > > /* Here we go */ > - outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); > + sum = EC_COMMAND_PROTOCOL_3; > + cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); > > if (ec_response_timed_out()) { > dev_warn(ec->dev, "EC responsed timed out\n"); > @@ -74,17 +76,15 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, > } > > /* Check result */ > - msg->result = inb(EC_LPC_ADDR_HOST_DATA); > + msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); > ret = cros_ec_check_result(ec, msg); > if (ret) > goto done; > > /* Read back response */ > dout = (u8 *)&response; > - for (i = 0; i < sizeof(response); i++) { > - dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i); > - sum += dout[i]; > - } > + sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response), > + dout); > > msg->result = response.result; > > @@ -97,11 +97,9 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, > } > > /* Read response and process checksum */ > - for (i = 0; i < response.data_len; i++) { > - msg->data[i] = > - inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i); > - sum += msg->data[i]; > - } > + sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET + > + sizeof(response), response.data_len, > + msg->data); > > if (sum) { > dev_err(ec->dev, > @@ -121,8 +119,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, > struct cros_ec_command *msg) > { > struct ec_lpc_host_args args; > - int csum; > - int i; > + u8 sum; > int ret = 0; > > if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || > @@ -139,24 +136,20 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, > args.data_size = msg->outsize; > > /* Initialize checksum */ > - csum = msg->command + args.flags + > - args.command_version + args.data_size; > + sum = msg->command + args.flags + args.command_version + args.data_size; > > /* Copy data and update checksum */ > - for (i = 0; i < msg->outsize; i++) { > - outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i); > - csum += msg->data[i]; > - } > + sum += cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PARAM, msg->outsize, > + msg->data); > > /* Finalize checksum and write args */ > - args.checksum = csum & 0xFF; > - outb(args.flags, EC_LPC_ADDR_HOST_ARGS); > - outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1); > - outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2); > - outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3); > + args.checksum = sum; > + cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), > + (u8 *)&args); > > /* Here we go */ > - outb(msg->command, EC_LPC_ADDR_HOST_CMD); > + sum = msg->command; > + cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); > > if (ec_response_timed_out()) { > dev_warn(ec->dev, "EC responsed timed out\n"); > @@ -165,16 +158,14 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, > } > > /* Check result */ > - msg->result = inb(EC_LPC_ADDR_HOST_DATA); > + msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); > ret = cros_ec_check_result(ec, msg); > if (ret) > goto done; > > /* Read back args */ > - args.flags = inb(EC_LPC_ADDR_HOST_ARGS); > - args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1); > - args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2); > - args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3); > + cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), > + (u8 *)&args); > > if (args.data_size > msg->insize) { > dev_err(ec->dev, > @@ -185,20 +176,17 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, > } > > /* Start calculating response checksum */ > - csum = msg->command + args.flags + > - args.command_version + args.data_size; > + sum = msg->command + args.flags + args.command_version + args.data_size; > > /* Read response and update checksum */ > - for (i = 0; i < args.data_size; i++) { > - msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i); > - csum += msg->data[i]; > - } > + sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PARAM, args.data_size, > + msg->data); > > /* Verify checksum */ > - if (args.checksum != (csum & 0xFF)) { > + if (args.checksum != sum) { > dev_err(ec->dev, > "bad packet checksum, expected %02x, got %02x\n", > - args.checksum, csum & 0xFF); > + args.checksum, sum); > ret = -EBADMSG; > goto done; > } > @@ -222,14 +210,13 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, > > /* fixed length */ > if (bytes) { > - for (; cnt < bytes; i++, s++, cnt++) > - *s = inb(EC_LPC_ADDR_MEMMAP + i); > - return cnt; > + cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + offset, bytes, s); > + return bytes; > } > > /* string */ > for (; i < EC_MEMMAP_SIZE; i++, s++) { > - *s = inb(EC_LPC_ADDR_MEMMAP + i); > + cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + i, 1, s); > cnt++; > if (!*s) > break; > @@ -242,6 +229,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > struct cros_ec_device *ec_dev; > + u8 buf[2]; > int ret; > > if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, > @@ -250,8 +238,8 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) > return -EBUSY; > } > > - if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') || > - (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) { > + cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); > + if (buf[0] != 'E' || buf[1] != 'C') { > dev_err(dev, "EC ID not detected\n"); > return -ENODEV; > } > diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c > new file mode 100644 > index 0000000..03c9781 > --- /dev/null > +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c > @@ -0,0 +1,64 @@ > +/* > + * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller > + * > + * Copyright (C) 2016 Google, Inc > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * This driver uses the Chrome OS EC byte-level message-based protocol for > + * communicating the keyboard state (which keys are pressed) from a keyboard EC > + * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, > + * but everything else (including deghosting) is done here. The main > + * motivation for this is to keep the EC firmware as simple as possible, since > + * it cannot be easily upgraded and EC flash/IRAM space is relatively > + * expensive. > + */ > + > +#include > +#include > +#include > + > +static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) > +{ > + int i; > + int sum = 0; > + > + for (i = 0; i < length; ++i) { > + dest[i] = inb(offset + i); > + sum += dest[i]; > + } > + > + /* Return checksum of all bytes read */ > + return sum; > +} > + > +static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) > +{ > + int i; > + int sum = 0; > + > + for (i = 0; i < length; ++i) { > + outb(msg[i], offset + i); > + sum += msg[i]; > + } > + > + /* Return checksum of all bytes written */ > + return sum; > +} > + > +u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) > +{ > + return lpc_read_bytes(offset, length, dest); > +} > + > +u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg) > +{ > + return lpc_write_bytes(offset, length, msg); > +} > diff --git a/include/linux/mfd/cros_ec_lpc_reg.h b/include/linux/mfd/cros_ec_lpc_reg.h > new file mode 100644 > index 0000000..4089bd5 > --- /dev/null > +++ b/include/linux/mfd/cros_ec_lpc_reg.h > @@ -0,0 +1,47 @@ > +/* > + * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller > + * > + * Copyright (C) 2016 Google, Inc > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * This driver uses the Chrome OS EC byte-level message-based protocol for > + * communicating the keyboard state (which keys are pressed) from a keyboard EC > + * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, > + * but everything else (including deghosting) is done here. The main > + * motivation for this is to keep the EC firmware as simple as possible, since > + * it cannot be easily upgraded and EC flash/IRAM space is relatively > + * expensive. > + */ > + > +#ifndef __LINUX_MFD_CROS_EC_REG_H > +#define __LINUX_MFD_CROS_EC_REG_H > + > +/** > + * cros_ec_lpc_read_bytes - Read bytes from a given LPC-mapped address. > + * Returns 8-bit checksum of all bytes read. > + * > + * @offset: Base read address > + * @length: Number of bytes to read > + * @dest: Destination buffer > + */ > +u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest); > + > +/** > + * cros_ec_lpc_write_bytes - Write bytes to a given LPC-mapped address. > + * Returns 8-bit checksum of all bytes written. > + * > + * @offset: Base write address > + * @length: Number of bytes to write > + * @msg: Write data buffer > + */ > +u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg); > + > +#endif /* __LINUX_MFD_CROS_EC_REG_H */ > -- > 2.9.3 > -- Benson Leung Staff Software Engineer Chrome OS Kernel Google Inc. bleung@google.com Chromium OS Project bleung@chromium.org