Skip to content

Commit be4fccb

Browse files
gwendalcrTzung-Bi Shih
authored andcommitted
platform/chrome: cros_ec_lpc: Support direct EC register memory access
Add support to access EC memory region HOST command and ACPI memory region directly. The memory region comes from the CRS ACPI resource descriptor. The driver retrieves the memory information by adding a resource walker for the CRS region. If a memory region is found, it is mapped and the driver uses a new set of read/write primitives to access the EC doorbell and external registers. Once the memory is mapped, it belongs to the driver: grep GOOG0004 /proc/iomem     fe0b0000-fe0bffff : GOOG0004:00 We can verify the communication is established checking the EC version, or monitoring the commands using the cros-ec kernel tracer. Signed-off-by: Gwendal Grignou <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Tzung-Bi Shih <[email protected]>
1 parent 5851721 commit be4fccb

File tree

1 file changed

+100
-21
lines changed

1 file changed

+100
-21
lines changed

drivers/platform/chrome/cros_ec_lpc.c

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ struct lpc_driver_data {
7070
/**
7171
* struct cros_ec_lpc - LPC device-specific data
7272
* @mmio_memory_base: The first I/O port addressing EC mapped memory.
73+
* @base: For EC supporting memory mapping, base address of the mapped region.
74+
* @mem32: Information about the memory mapped register region, if present.
7375
* @read: Copy length bytes from EC address offset into buffer dest.
7476
* Returns a negative error code on error, or the 8-bit checksum
7577
* of all bytes read.
@@ -79,6 +81,8 @@ struct lpc_driver_data {
7981
*/
8082
struct cros_ec_lpc {
8183
u16 mmio_memory_base;
84+
void __iomem *base;
85+
struct acpi_resource_fixed_memory32 mem32;
8286
int (*read)(struct cros_ec_lpc *ec_lpc, unsigned int offset,
8387
unsigned int length, u8 *dest);
8488
int (*write)(struct cros_ec_lpc *ec_lpc, unsigned int offset,
@@ -160,6 +164,45 @@ static int cros_ec_lpc_mec_write_bytes(struct cros_ec_lpc *ec_lpc, unsigned int
160164
length, (u8 *)msg) :
161165
cros_ec_lpc_write_bytes(ec_lpc, offset, length, msg);
162166
}
167+
168+
static int cros_ec_lpc_direct_read(struct cros_ec_lpc *ec_lpc, unsigned int offset,
169+
unsigned int length, u8 *dest)
170+
{
171+
int sum = 0;
172+
int i;
173+
174+
if (offset < EC_HOST_CMD_REGION0 || offset > EC_LPC_ADDR_MEMMAP +
175+
EC_MEMMAP_SIZE) {
176+
return cros_ec_lpc_read_bytes(ec_lpc, offset, length, dest);
177+
}
178+
179+
for (i = 0; i < length; ++i) {
180+
dest[i] = readb(ec_lpc->base + offset - EC_HOST_CMD_REGION0 + i);
181+
sum += dest[i];
182+
}
183+
184+
/* Return checksum of all bytes read */
185+
return sum;
186+
}
187+
188+
static int cros_ec_lpc_direct_write(struct cros_ec_lpc *ec_lpc, unsigned int offset,
189+
unsigned int length, const u8 *msg)
190+
{
191+
int sum = 0;
192+
int i;
193+
194+
if (offset < EC_HOST_CMD_REGION0 || offset > EC_LPC_ADDR_MEMMAP +
195+
EC_MEMMAP_SIZE) {
196+
return cros_ec_lpc_write_bytes(ec_lpc, offset, length, msg);
197+
}
198+
199+
for (i = 0; i < length; ++i) {
200+
writeb(msg[i], ec_lpc->base + offset - EC_HOST_CMD_REGION0 + i);
201+
sum += msg[i];
202+
}
203+
204+
/* Return checksum of all bytes written */
205+
return sum;
163206
}
164207

165208
static int ec_response_timed_out(struct cros_ec_lpc *ec_lpc)
@@ -450,6 +493,20 @@ static struct acpi_device *cros_ec_lpc_get_device(const char *id)
450493
return adev;
451494
}
452495

496+
static acpi_status cros_ec_lpc_resources(struct acpi_resource *res, void *data)
497+
{
498+
struct cros_ec_lpc *ec_lpc = data;
499+
500+
switch (res->type) {
501+
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
502+
ec_lpc->mem32 = res->data.fixed_memory32;
503+
break;
504+
default:
505+
break;
506+
}
507+
return AE_OK;
508+
}
509+
453510
static int cros_ec_lpc_probe(struct platform_device *pdev)
454511
{
455512
struct device *dev = &pdev->dev;
@@ -498,29 +555,52 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
498555
dev_info(dev, "got AML mutex '%s'", name);
499556
}
500557
}
501-
502-
/*
503-
* The Framework Laptop (and possibly other non-ChromeOS devices)
504-
* only exposes the eight I/O ports that are required for the Microchip EC.
505-
* Requesting a larger reservation will fail.
506-
*/
507-
if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
508-
EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) {
509-
dev_err(dev, "couldn't reserve MEC region\n");
510-
return -EBUSY;
558+
adev = ACPI_COMPANION(dev);
559+
if (adev) {
560+
/*
561+
* Retrieve the resource information in the CRS register, if available.
562+
*/
563+
status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
564+
cros_ec_lpc_resources, ec_lpc);
565+
if (ACPI_FAILURE(status)) {
566+
dev_err(dev, "failed to get resources\n");
567+
return -ENODEV;
568+
}
569+
if (ec_lpc->mem32.address_length) {
570+
ec_lpc->base = devm_ioremap(dev,
571+
ec_lpc->mem32.address,
572+
ec_lpc->mem32.address_length);
573+
if (!ec_lpc->base)
574+
return -EINVAL;
575+
576+
ec_lpc->read = cros_ec_lpc_direct_read;
577+
ec_lpc->write = cros_ec_lpc_direct_write;
578+
}
511579
}
580+
if (!ec_lpc->read) {
581+
/*
582+
* The Framework Laptop (and possibly other non-ChromeOS devices)
583+
* only exposes the eight I/O ports that are required for the Microchip EC.
584+
* Requesting a larger reservation will fail.
585+
*/
586+
if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
587+
EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) {
588+
dev_err(dev, "couldn't reserve MEC region\n");
589+
return -EBUSY;
590+
}
512591

513-
cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
514-
EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
592+
cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
593+
EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
515594

516-
/*
517-
* Read the mapped ID twice, the first one is assuming the
518-
* EC is a Microchip Embedded Controller (MEC) variant, if the
519-
* protocol fails, fallback to the non MEC variant and try to
520-
* read again the ID.
521-
*/
522-
ec_lpc->read = cros_ec_lpc_mec_read_bytes;
523-
ec_lpc->write = cros_ec_lpc_mec_write_bytes;
595+
/*
596+
* Read the mapped ID twice, the first one is assuming the
597+
* EC is a Microchip Embedded Controller (MEC) variant, if the
598+
* protocol fails, fallback to the non MEC variant and try to
599+
* read again the ID.
600+
*/
601+
ec_lpc->read = cros_ec_lpc_mec_read_bytes;
602+
ec_lpc->write = cros_ec_lpc_mec_write_bytes;
603+
}
524604
ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
525605
if (ret < 0)
526606
return ret;
@@ -594,7 +674,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
594674
* Connect a notify handler to process MKBP messages if we have a
595675
* companion ACPI device.
596676
*/
597-
adev = ACPI_COMPANION(dev);
598677
if (adev) {
599678
status = acpi_install_notify_handler(adev->handle,
600679
ACPI_ALL_NOTIFY,

0 commit comments

Comments
 (0)