Skip to content

Commit 119a3cb

Browse files
djcampelloEnric Balletbo i Serra
authored andcommitted
platform/chrome: wilco_ec: Add keyboard backlight LED support
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 <[email protected]> Signed-off-by: Daniel Campello <[email protected]> Reviewed-by: Daniel Campello <[email protected]> Signed-off-by: Enric Balletbo i Serra <[email protected]>
1 parent 3c4d77b commit 119a3cb

File tree

5 files changed

+216
-6
lines changed

5 files changed

+216
-6
lines changed

drivers/platform/chrome/wilco_ec/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
config WILCO_EC
33
tristate "ChromeOS Wilco Embedded Controller"
4-
depends on ACPI && X86 && CROS_EC_LPC
4+
depends on ACPI && X86 && CROS_EC_LPC && LEDS_CLASS
55
help
66
If you say Y here, you get support for talking to the ChromeOS
77
Wilco EC over an eSPI bus. This uses a simple byte-level protocol

drivers/platform/chrome/wilco_ec/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22

3-
wilco_ec-objs := core.o mailbox.o properties.o sysfs.o
3+
wilco_ec-objs := core.o keyboard_leds.o mailbox.o \
4+
properties.o sysfs.o
45
obj-$(CONFIG_WILCO_EC) += wilco_ec.o
56
wilco_ec_debugfs-objs := debugfs.o
67
obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o

drivers/platform/chrome/wilco_ec/core.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
* Copyright 2018 Google LLC
66
*
77
* This is the entry point for the drivers that control the Wilco EC.
8-
* This driver is responsible for several tasks:
9-
* - Initialize the register interface that is used by wilco_ec_mailbox()
10-
* - Create a platform device which is picked up by the debugfs driver
11-
* - Create a platform device which is picked up by the RTC driver
128
*/
139

1410
#include <linux/acpi.h>
@@ -87,6 +83,15 @@ static int wilco_ec_probe(struct platform_device *pdev)
8783
goto unregister_debugfs;
8884
}
8985

86+
/* Set up the keyboard backlight LEDs. */
87+
ret = wilco_keyboard_leds_init(ec);
88+
if (ret < 0) {
89+
dev_err(dev,
90+
"Failed to initialize keyboard LEDs: %d\n",
91+
ret);
92+
goto unregister_rtc;
93+
}
94+
9095
ret = wilco_ec_add_sysfs(ec);
9196
if (ret < 0) {
9297
dev_err(dev, "Failed to create sysfs entries: %d", ret);
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Keyboard backlight LED driver for the Wilco Embedded Controller
4+
*
5+
* Copyright 2019 Google LLC
6+
*
7+
* Since the EC will never change the backlight level of its own accord,
8+
* we don't need to implement a brightness_get() method.
9+
*/
10+
11+
#include <linux/device.h>
12+
#include <linux/kernel.h>
13+
#include <linux/leds.h>
14+
#include <linux/platform_data/wilco-ec.h>
15+
#include <linux/slab.h>
16+
17+
#define WILCO_EC_COMMAND_KBBL 0x75
18+
#define WILCO_KBBL_MODE_FLAG_PWM BIT(1) /* Set brightness by percent. */
19+
#define WILCO_KBBL_DEFAULT_BRIGHTNESS 0
20+
21+
struct wilco_keyboard_leds {
22+
struct wilco_ec_device *ec;
23+
struct led_classdev keyboard;
24+
};
25+
26+
enum wilco_kbbl_subcommand {
27+
WILCO_KBBL_SUBCMD_GET_FEATURES = 0x00,
28+
WILCO_KBBL_SUBCMD_GET_STATE = 0x01,
29+
WILCO_KBBL_SUBCMD_SET_STATE = 0x02,
30+
};
31+
32+
/**
33+
* struct wilco_keyboard_leds_msg - Message to/from EC for keyboard LED control.
34+
* @command: Always WILCO_EC_COMMAND_KBBL.
35+
* @status: Set by EC to 0 on success, 0xFF on failure.
36+
* @subcmd: One of enum wilco_kbbl_subcommand.
37+
* @reserved3: Should be 0.
38+
* @mode: Bit flags for used mode, we want to use WILCO_KBBL_MODE_FLAG_PWM.
39+
* @reserved5to8: Should be 0.
40+
* @percent: Brightness in 0-100. Only meaningful in PWM mode.
41+
* @reserved10to15: Should be 0.
42+
*/
43+
struct wilco_keyboard_leds_msg {
44+
u8 command;
45+
u8 status;
46+
u8 subcmd;
47+
u8 reserved3;
48+
u8 mode;
49+
u8 reserved5to8[4];
50+
u8 percent;
51+
u8 reserved10to15[6];
52+
} __packed;
53+
54+
/* Send a request, get a response, and check that the response is good. */
55+
static int send_kbbl_msg(struct wilco_ec_device *ec,
56+
struct wilco_keyboard_leds_msg *request,
57+
struct wilco_keyboard_leds_msg *response)
58+
{
59+
struct wilco_ec_message msg;
60+
int ret;
61+
62+
memset(&msg, 0, sizeof(msg));
63+
msg.type = WILCO_EC_MSG_LEGACY;
64+
msg.request_data = request;
65+
msg.request_size = sizeof(*request);
66+
msg.response_data = response;
67+
msg.response_size = sizeof(*response);
68+
69+
ret = wilco_ec_mailbox(ec, &msg);
70+
if (ret < 0) {
71+
dev_err(ec->dev,
72+
"Failed sending keyboard LEDs command: %d", ret);
73+
return ret;
74+
}
75+
76+
if (response->status) {
77+
dev_err(ec->dev,
78+
"EC reported failure sending keyboard LEDs command: %d",
79+
response->status);
80+
return -EIO;
81+
}
82+
83+
return 0;
84+
}
85+
86+
static int set_kbbl(struct wilco_ec_device *ec, enum led_brightness brightness)
87+
{
88+
struct wilco_keyboard_leds_msg request;
89+
struct wilco_keyboard_leds_msg response;
90+
91+
memset(&request, 0, sizeof(request));
92+
request.command = WILCO_EC_COMMAND_KBBL;
93+
request.subcmd = WILCO_KBBL_SUBCMD_SET_STATE;
94+
request.mode = WILCO_KBBL_MODE_FLAG_PWM;
95+
request.percent = brightness;
96+
97+
return send_kbbl_msg(ec, &request, &response);
98+
}
99+
100+
static int kbbl_exist(struct wilco_ec_device *ec, bool *exists)
101+
{
102+
struct wilco_keyboard_leds_msg request;
103+
struct wilco_keyboard_leds_msg response;
104+
int ret;
105+
106+
memset(&request, 0, sizeof(request));
107+
request.command = WILCO_EC_COMMAND_KBBL;
108+
request.subcmd = WILCO_KBBL_SUBCMD_GET_FEATURES;
109+
110+
ret = send_kbbl_msg(ec, &request, &response);
111+
if (ret < 0)
112+
return ret;
113+
114+
*exists = response.status != 0xFF;
115+
116+
return 0;
117+
}
118+
119+
/**
120+
* kbbl_init() - Initialize the state of the keyboard backlight.
121+
* @ec: EC device to talk to.
122+
*
123+
* Gets the current brightness, ensuring that the BIOS already initialized the
124+
* backlight to PWM mode. If not in PWM mode, then the current brightness is
125+
* meaningless, so set the brightness to WILCO_KBBL_DEFAULT_BRIGHTNESS.
126+
*
127+
* Return: Final brightness of the keyboard, or negative error code on failure.
128+
*/
129+
static int kbbl_init(struct wilco_ec_device *ec)
130+
{
131+
struct wilco_keyboard_leds_msg request;
132+
struct wilco_keyboard_leds_msg response;
133+
int ret;
134+
135+
memset(&request, 0, sizeof(request));
136+
request.command = WILCO_EC_COMMAND_KBBL;
137+
request.subcmd = WILCO_KBBL_SUBCMD_GET_STATE;
138+
139+
ret = send_kbbl_msg(ec, &request, &response);
140+
if (ret < 0)
141+
return ret;
142+
143+
if (response.mode & WILCO_KBBL_MODE_FLAG_PWM)
144+
return response.percent;
145+
146+
ret = set_kbbl(ec, WILCO_KBBL_DEFAULT_BRIGHTNESS);
147+
if (ret < 0)
148+
return ret;
149+
150+
return WILCO_KBBL_DEFAULT_BRIGHTNESS;
151+
}
152+
153+
static int wilco_keyboard_leds_set(struct led_classdev *cdev,
154+
enum led_brightness brightness)
155+
{
156+
struct wilco_keyboard_leds *wkl =
157+
container_of(cdev, struct wilco_keyboard_leds, keyboard);
158+
return set_kbbl(wkl->ec, brightness);
159+
}
160+
161+
int wilco_keyboard_leds_init(struct wilco_ec_device *ec)
162+
{
163+
struct wilco_keyboard_leds *wkl;
164+
bool leds_exist;
165+
int ret;
166+
167+
ret = kbbl_exist(ec, &leds_exist);
168+
if (ret < 0) {
169+
dev_err(ec->dev,
170+
"Failed checking keyboard LEDs support: %d", ret);
171+
return ret;
172+
}
173+
if (!leds_exist)
174+
return 0;
175+
176+
wkl = devm_kzalloc(ec->dev, sizeof(*wkl), GFP_KERNEL);
177+
if (!wkl)
178+
return -ENOMEM;
179+
180+
wkl->ec = ec;
181+
wkl->keyboard.name = "platform::kbd_backlight";
182+
wkl->keyboard.max_brightness = 100;
183+
wkl->keyboard.flags = LED_CORE_SUSPENDRESUME;
184+
wkl->keyboard.brightness_set_blocking = wilco_keyboard_leds_set;
185+
ret = kbbl_init(ec);
186+
if (ret < 0)
187+
return ret;
188+
wkl->keyboard.brightness = ret;
189+
190+
return devm_led_classdev_register(ec->dev, &wkl->keyboard);
191+
}

include/linux/platform_data/wilco-ec.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,19 @@ struct wilco_ec_message {
122122
*/
123123
int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg);
124124

125+
/**
126+
* wilco_keyboard_leds_init() - Set up the keyboard backlight LEDs.
127+
* @ec: EC device to query.
128+
*
129+
* After this call, the keyboard backlight will be exposed through a an LED
130+
* device at /sys/class/leds.
131+
*
132+
* This may sleep because it uses wilco_ec_mailbox().
133+
*
134+
* Return: 0 on success, negative error code on failure.
135+
*/
136+
int wilco_keyboard_leds_init(struct wilco_ec_device *ec);
137+
125138
/*
126139
* A Property is typically a data item that is stored to NVRAM
127140
* by the EC. Each of these data items has an index associated

0 commit comments

Comments
 (0)