Received: by 2002:a25:31c3:0:0:0:0:0 with SMTP id x186csp801178ybx; Wed, 6 Nov 2019 08:37:24 -0800 (PST) X-Google-Smtp-Source: APXvYqy5jSq0zNnqzzQ96HC4x6FtClJARYzq6Mi9o+T3zAs2FTD+o6iAqZohVgeSp/z/tctSbq7k X-Received: by 2002:aa7:d0ce:: with SMTP id u14mr3595127edo.225.1573058244201; Wed, 06 Nov 2019 08:37:24 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1573058244; cv=none; d=google.com; s=arc-20160816; b=fj9L0EpyhlGeJcWDSaK0wAxx7K4e+npzH7xHz/F+tKdjtGpTYnm6E5+iRePpGhHCqj JLNIMoD+5u1WHgJbqG5bY3U6DWB+lE0VNBxwN5EUVUh22w/U4DcOyM/jndc6PTHIwnwJ vrfynHuQ7/MdD0vWrnJzr5zuq0cYEh1gc+z+9vjBGCIGGmAPWbALR7XW4a4+XOZAhNp/ JncO+I3KjAYYKOa9gfs8FmVuxoRDWEe6PxH5VhaYisLnTwbMSMjfHmirg9N7lcR8vHpi y30w1IniTamYRzqWbn3rRQlJmKWB2ZHzgkT/LZ1Y1V0xSMzsvYGPF8ixpbokFnfy9MMJ B4zg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=JpJ0RKZdBuwsjP9UTfQRNupNqWTgtK1FhT+N5xdXJvA=; b=d4fhlye5Tj31HVvl6iinArzz/62f5ddIOFW5k+SrhGGA1jy+hszYFWxxx314X/+SLS vWPqwxr9yihmxyDdVDpgsClXAL93eW5XYuuwiJdtXuG/MzWYmDoSlkhDGysvLU2CHIW2 2Zu+Z8S72SDxXwf2P+pRW7WrdNXEwqWNXWK8EBHI3yS8VHOPBjppiA17k44mL0r+Ilhy I0hhkvQm728T11CGtawZkKghaBTjfwt+qNwPFMpXu2H/434CDRWXqxZEbYMQyscwwIzM oXXC5ClhA+VHEt7/z1uMYkw/RFObQ2x05tU++hDHFFu/hMtCuJx/AuRPx43Ch2D+edGD otGw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=CielUG2j; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d1si15582517ejh.281.2019.11.06.08.37.00; Wed, 06 Nov 2019 08:37:24 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=CielUG2j; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732137AbfKFQd0 (ORCPT + 99 others); Wed, 6 Nov 2019 11:33:26 -0500 Received: from mail-io1-f65.google.com ([209.85.166.65]:35635 "EHLO mail-io1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727321AbfKFQdZ (ORCPT ); Wed, 6 Nov 2019 11:33:25 -0500 Received: by mail-io1-f65.google.com with SMTP id x21so12022659iol.2 for ; Wed, 06 Nov 2019 08:33:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=JpJ0RKZdBuwsjP9UTfQRNupNqWTgtK1FhT+N5xdXJvA=; b=CielUG2jTjOnDUsGvP84lfgnpVBwfpdYgolhaiDVpxAmKa9qGIIyxlhMPw4CnHX2o3 HRpxkileagZb8/h8upzAcCsJW1yM/RJvhua5SSOzZ7E/bbGv3pipv9Kv+SBB2Dg70x/+ cIeX6lQHVi1RoFaBPDWwRBsG7U1UtK4dIOzY4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=JpJ0RKZdBuwsjP9UTfQRNupNqWTgtK1FhT+N5xdXJvA=; b=hHom/au6aw0kJMZBRLwGjSMKCq4FhOMdIK1q5aKFVMXZfplaQojDm/PLWcdg1t5bU7 Zk3Ua7bW8+zZR7aVtpvDjW5uw9s1xe7AHgSSNO47cv7cTcn+MC1I7fqGJaoCFhmAyVYt zxqQv0t9CxOWdzv2YO1JVo7+qSBHXzEx+qAVxyOLQrqy0VZ7VWLvLzTCq76EkFKI1T7M 6+MAtCPHMKOnciy40adxYQvXsbg2WpOaFIaCNowT0yFQ+4ajNLpxjY59UnAFPO8/1sI9 UprsXp8HpRp6A4G876ikMSjerftCmGCaTmmfnHhvsYAQHCLy7tIT/2GnRmKQblphLnOK pgdw== X-Gm-Message-State: APjAAAXDoxuabKb+CmMOHDww+qtQ4xkQmXUwLdxpqPJphjsWflXgrG38 1PExjJ4qdczrjU2NqewzDQNfaleNynI= X-Received: by 2002:a6b:400e:: with SMTP id k14mr10064919ioa.254.1573058003685; Wed, 06 Nov 2019 08:33:23 -0800 (PST) Received: from localhost ([2620:15c:183:200:2be5:d5bc:3dd:6c78]) by smtp.gmail.com with ESMTPSA id n15sm283166iom.7.2019.11.06.08.33.22 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 06 Nov 2019 08:33:23 -0800 (PST) From: Daniel Campello To: LKML Cc: Daniel Campello , Nick Crews , Arnd Bergmann , Enric Balletbo i Serra , Thomas Gleixner , Wei Yongjun , Alexandre Belloni , Duncan Laurie , Benson Leung Subject: [PATCH v9] platform/chrome: wilco_ec: Add keyboard backlight LED support Date: Wed, 6 Nov 2019 09:33:19 -0700 Message-Id: <20191106093045.1.I81b5d7a86ef1cdf5433d1feb8b51fd9304a5b3c2@changeid> X-Mailer: git-send-email 2.24.0.432.g9d3f5f5b63-goog MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The EC is in charge of controlling the keyboard backlight on the Wilco platform. We expose a standard LED class device named platform::kbd_backlight. Since the EC will never change the backlight level of its own accord, we don't need to implement a brightness_get() method. Signed-off-by: Nick Crews Signed-off-by: Daniel Campello Reviewed-by: Daniel Campello --- v9 changes: -Added dependency on LEDS_CLASS for config WILCO_EC. v8 changes: -Removed unneeded #includes from keyboard_leds.h v7 changes: -Merged the LED stuff into the core wilco_ec module. This allows us to de-duplicate a lot of code, hide a lot of the LED internals from the core driver, and generally simplify things. -Follow reverse xmas tree variable declaration -Remove unneeded warning about BIOS not initializing the LEDs -Fix and standardize some comments and log messages. -Document all fields of wilco_keyboard_leds_msg -rm outdated and useless comment at top of core file. v6 changes: -Rebased patch -Fixed bug related to request/response buffer pointers on send_kbbl_mesg() -Now sends WILCO_KBBL_SUBCMD_SET_STATE instead of WILCO_KBBL_SUBCMD_GET_STATE command for keyboard_led_set_brightness() v5 changes: -Rename the LED device to "platform::kbd_backlight", to denote that this is the built-in system keyboard. v4 changes: -Call keyboard_led_set_brightness() directly within initialize_brightness(), instead of calling the library function. v3 changes: -Since this behaves the same as the standard Chrome OS keyboard backlight, rename the led device to "chromeos::kbd_backlight" -Move wilco_ec_keyboard_backlight_exists() into core module, so that the core does not depend upon the keyboard backlight driver. -This required moving some code into wilco-ec.h -Refactor out some common code in set_brightness() and initialize_brightness() v2 changes: -Remove and fix uses of led vs LED in kconfig -Assume BIOS initializes brightness, but double check in probe() -Remove get_brightness() callback, as EC never changes brightness by itself. -Use a __packed struct as message instead of opaque array -Add exported wilco_ec_keyboard_leds_exist() so the core driver now only creates a platform _device if relevant -Fix use of keyboard_led_set_brightness() since it can sleep drivers/platform/chrome/wilco_ec/Kconfig | 2 +- drivers/platform/chrome/wilco_ec/Makefile | 3 +- drivers/platform/chrome/wilco_ec/core.c | 13 +- .../platform/chrome/wilco_ec/keyboard_leds.c | 191 ++++++++++++++++++ include/linux/platform_data/wilco-ec.h | 13 ++ 5 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 drivers/platform/chrome/wilco_ec/keyboard_leds.c diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig index 89007b0bc743..365f30e116ee 100644 --- a/drivers/platform/chrome/wilco_ec/Kconfig +++ b/drivers/platform/chrome/wilco_ec/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config WILCO_EC tristate "ChromeOS Wilco Embedded Controller" - depends on ACPI && X86 && CROS_EC_LPC + depends on ACPI && X86 && CROS_EC_LPC && LEDS_CLASS help If you say Y here, you get support for talking to the ChromeOS Wilco EC over an eSPI bus. This uses a simple byte-level protocol diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile index bc817164596e..ecb3145cab18 100644 --- a/drivers/platform/chrome/wilco_ec/Makefile +++ b/drivers/platform/chrome/wilco_ec/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -wilco_ec-objs := core.o mailbox.o properties.o sysfs.o +wilco_ec-objs := core.o keyboard_leds.o mailbox.o \ + properties.o sysfs.o obj-$(CONFIG_WILCO_EC) += wilco_ec.o wilco_ec_debugfs-objs := debugfs.o obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c index 3724bf4b77c6..36c78e52ff3c 100644 --- a/drivers/platform/chrome/wilco_ec/core.c +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -5,10 +5,6 @@ * Copyright 2018 Google LLC * * This is the entry point for the drivers that control the Wilco EC. - * This driver is responsible for several tasks: - * - Initialize the register interface that is used by wilco_ec_mailbox() - * - Create a platform device which is picked up by the debugfs driver - * - Create a platform device which is picked up by the RTC driver */ #include @@ -87,6 +83,15 @@ static int wilco_ec_probe(struct platform_device *pdev) goto unregister_debugfs; } + /* Set up the keyboard backlight LEDs. */ + ret = wilco_keyboard_leds_init(ec); + if (ret < 0) { + dev_err(dev, + "Failed to initialize keyboard LEDs: %d\n", + ret); + goto unregister_rtc; + } + ret = wilco_ec_add_sysfs(ec); if (ret < 0) { dev_err(dev, "Failed to create sysfs entries: %d", ret); diff --git a/drivers/platform/chrome/wilco_ec/keyboard_leds.c b/drivers/platform/chrome/wilco_ec/keyboard_leds.c new file mode 100644 index 000000000000..bb0edf51dfda --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/keyboard_leds.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Keyboard backlight LED driver for the Wilco Embedded Controller + * + * Copyright 2019 Google LLC + * + * Since the EC will never change the backlight level of its own accord, + * we don't need to implement a brightness_get() method. + */ + +#include +#include +#include +#include +#include + +#define WILCO_EC_COMMAND_KBBL 0x75 +#define WILCO_KBBL_MODE_FLAG_PWM BIT(1) /* Set brightness by percent. */ +#define WILCO_KBBL_DEFAULT_BRIGHTNESS 0 + +struct wilco_keyboard_leds { + struct wilco_ec_device *ec; + struct led_classdev keyboard; +}; + +enum wilco_kbbl_subcommand { + WILCO_KBBL_SUBCMD_GET_FEATURES = 0x00, + WILCO_KBBL_SUBCMD_GET_STATE = 0x01, + WILCO_KBBL_SUBCMD_SET_STATE = 0x02, +}; + +/** + * struct wilco_keyboard_leds_msg - Message to/from EC for keyboard LED control. + * @command: Always WILCO_EC_COMMAND_KBBL. + * @status: Set by EC to 0 on success, 0xFF on failure. + * @subcmd: One of enum wilco_kbbl_subcommand. + * @reserved3: Should be 0. + * @mode: Bit flags for used mode, we want to use WILCO_KBBL_MODE_FLAG_PWM. + * @reserved5to8: Should be 0. + * @percent: Brightness in 0-100. Only meaningful in PWM mode. + * @reserved10to15: Should be 0. + */ +struct wilco_keyboard_leds_msg { + u8 command; + u8 status; + u8 subcmd; + u8 reserved3; + u8 mode; + u8 reserved5to8[4]; + u8 percent; + u8 reserved10to15[6]; +} __packed; + +/* Send a request, get a response, and check that the response is good. */ +static int send_kbbl_msg(struct wilco_ec_device *ec, + struct wilco_keyboard_leds_msg *request, + struct wilco_keyboard_leds_msg *response) +{ + struct wilco_ec_message msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.type = WILCO_EC_MSG_LEGACY; + msg.request_data = request; + msg.request_size = sizeof(*request); + msg.response_data = response; + msg.response_size = sizeof(*response); + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) { + dev_err(ec->dev, + "Failed sending keyboard LEDs command: %d", ret); + return ret; + } + + if (response->status) { + dev_err(ec->dev, + "EC reported failure sending keyboard LEDs command: %d", + response->status); + return -EIO; + } + + return 0; +} + +static int set_kbbl(struct wilco_ec_device *ec, enum led_brightness brightness) +{ + struct wilco_keyboard_leds_msg request; + struct wilco_keyboard_leds_msg response; + + memset(&request, 0, sizeof(request)); + request.command = WILCO_EC_COMMAND_KBBL; + request.subcmd = WILCO_KBBL_SUBCMD_SET_STATE; + request.mode = WILCO_KBBL_MODE_FLAG_PWM; + request.percent = brightness; + + return send_kbbl_msg(ec, &request, &response); +} + +static int kbbl_exist(struct wilco_ec_device *ec, bool *exists) +{ + struct wilco_keyboard_leds_msg request; + struct wilco_keyboard_leds_msg response; + int ret; + + memset(&request, 0, sizeof(request)); + request.command = WILCO_EC_COMMAND_KBBL; + request.subcmd = WILCO_KBBL_SUBCMD_GET_FEATURES; + + ret = send_kbbl_msg(ec, &request, &response); + if (ret < 0) + return ret; + + *exists = response.status != 0xFF; + + return 0; +} + +/** + * kbbl_init() - Initialize the state of the keyboard backlight. + * @ec: EC device to talk to. + * + * Gets the current brightness, ensuring that the BIOS already initialized the + * backlight to PWM mode. If not in PWM mode, then the current brightness is + * meaningless, so set the brightness to WILCO_KBBL_DEFAULT_BRIGHTNESS. + * + * Return: Final brightness of the keyboard, or negative error code on failure. + */ +static int kbbl_init(struct wilco_ec_device *ec) +{ + struct wilco_keyboard_leds_msg request; + struct wilco_keyboard_leds_msg response; + int ret; + + memset(&request, 0, sizeof(request)); + request.command = WILCO_EC_COMMAND_KBBL; + request.subcmd = WILCO_KBBL_SUBCMD_GET_STATE; + + ret = send_kbbl_msg(ec, &request, &response); + if (ret < 0) + return ret; + + if (response.mode & WILCO_KBBL_MODE_FLAG_PWM) + return response.percent; + + ret = set_kbbl(ec, WILCO_KBBL_DEFAULT_BRIGHTNESS); + if (ret < 0) + return ret; + + return WILCO_KBBL_DEFAULT_BRIGHTNESS; +} + +static int wilco_keyboard_leds_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct wilco_keyboard_leds *wkl = + container_of(cdev, struct wilco_keyboard_leds, keyboard); + return set_kbbl(wkl->ec, brightness); +} + +int wilco_keyboard_leds_init(struct wilco_ec_device *ec) +{ + struct wilco_keyboard_leds *wkl; + bool leds_exist; + int ret; + + ret = kbbl_exist(ec, &leds_exist); + if (ret < 0) { + dev_err(ec->dev, + "Failed checking keyboard LEDs support: %d", ret); + return ret; + } + if (!leds_exist) + return 0; + + wkl = devm_kzalloc(ec->dev, sizeof(*wkl), GFP_KERNEL); + if (!wkl) + return -ENOMEM; + + wkl->ec = ec; + wkl->keyboard.name = "platform::kbd_backlight"; + wkl->keyboard.max_brightness = 100; + wkl->keyboard.flags = LED_CORE_SUSPENDRESUME; + wkl->keyboard.brightness_set_blocking = wilco_keyboard_leds_set; + ret = kbbl_init(ec); + if (ret < 0) + return ret; + wkl->keyboard.brightness = ret; + + return devm_led_classdev_register(ec->dev, &wkl->keyboard); +} diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index ad03b586a095..0f7df3498a24 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -120,6 +120,19 @@ struct wilco_ec_message { */ int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg); +/** + * wilco_keyboard_leds_init() - Set up the keyboard backlight LEDs. + * @ec: EC device to query. + * + * After this call, the keyboard backlight will be exposed through a an LED + * device at /sys/class/leds. + * + * This may sleep because it uses wilco_ec_mailbox(). + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_keyboard_leds_init(struct wilco_ec_device *ec); + /* * A Property is typically a data item that is stored to NVRAM * by the EC. Each of these data items has an index associated -- 2.24.0.432.g9d3f5f5b63-goog