Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'platform-drivers-x86-ib-lenovo-c630-v6.11-2'

Immutable branch between pdx86 lenovo c630 branch, power/supply and USB
subsystems due for the v6.11 merge window, which is required for the
Lenovo C630 battery driver.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>

+434 -1
+83
Documentation/devicetree/bindings/platform/lenovo,yoga-c630-ec.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/platform/lenovo,yoga-c630-ec.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Lenovo Yoga C630 Embedded Controller. 8 + 9 + maintainers: 10 + - Bjorn Andersson <andersson@kernel.org> 11 + 12 + description: 13 + The Qualcomm Snapdragon-based Lenovo Yoga C630 has an Embedded Controller 14 + (EC) which handles things such as battery and USB Type-C. This binding 15 + describes the interface, on an I2C bus, to this EC. 16 + 17 + properties: 18 + compatible: 19 + const: lenovo,yoga-c630-ec 20 + 21 + reg: 22 + const: 0x70 23 + 24 + '#address-cells': 25 + const: 1 26 + 27 + '#size-cells': 28 + const: 0 29 + 30 + interrupts: 31 + maxItems: 1 32 + 33 + patternProperties: 34 + '^connector@[01]$': 35 + $ref: /schemas/connector/usb-connector.yaml# 36 + 37 + properties: 38 + reg: 39 + maxItems: 1 40 + 41 + unevaluatedProperties: false 42 + 43 + required: 44 + - compatible 45 + - reg 46 + - interrupts 47 + 48 + additionalProperties: false 49 + 50 + examples: 51 + - |+ 52 + #include <dt-bindings/interrupt-controller/irq.h> 53 + i2c1 { 54 + clock-frequency = <400000>; 55 + 56 + #address-cells = <1>; 57 + #size-cells = <0>; 58 + 59 + embedded-controller@70 { 60 + compatible = "lenovo,yoga-c630-ec"; 61 + reg = <0x70>; 62 + 63 + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>; 64 + 65 + #address-cells = <1>; 66 + #size-cells = <0>; 67 + 68 + connector@0 { 69 + compatible = "usb-c-connector"; 70 + reg = <0>; 71 + power-role = "source"; 72 + data-role = "host"; 73 + }; 74 + 75 + connector@1 { 76 + compatible = "usb-c-connector"; 77 + reg = <1>; 78 + power-role = "source"; 79 + data-role = "host"; 80 + }; 81 + }; 82 + }; 83 + ...
+1 -1
drivers/platform/Makefile
··· 11 11 obj-$(CONFIG_GOLDFISH) += goldfish/ 12 12 obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ 13 13 obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ 14 - obj-$(CONFIG_ARM64) += arm64/ 14 + obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/
+14
drivers/platform/arm64/Kconfig
··· 32 32 laptop where this information is not properly exposed via the 33 33 standard ACPI devices. 34 34 35 + config EC_LENOVO_YOGA_C630 36 + tristate "Lenovo Yoga C630 Embedded Controller driver" 37 + depends on I2C 38 + help 39 + Driver for the Embedded Controller in the Qualcomm Snapdragon-based 40 + Lenovo Yoga C630, which provides battery and power adapter 41 + information. 42 + 43 + This driver provides battery and AC status support for the mentioned 44 + laptop where this information is not properly exposed via the 45 + standard ACPI devices. 46 + 47 + Say M or Y here to include this support. 48 + 35 49 endif # ARM64_PLATFORM_DEVICES
+1
drivers/platform/arm64/Makefile
··· 6 6 # 7 7 8 8 obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o 9 + obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
+291
drivers/platform/arm64/lenovo-yoga-c630.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022-2024, Linaro Ltd 4 + * Authors: 5 + * Bjorn Andersson 6 + * Dmitry Baryshkov 7 + */ 8 + #include <linux/auxiliary_bus.h> 9 + #include <linux/cleanup.h> 10 + #include <linux/device.h> 11 + #include <linux/err.h> 12 + #include <linux/i2c.h> 13 + #include <linux/irqreturn.h> 14 + #include <linux/lockdep.h> 15 + #include <linux/module.h> 16 + #include <linux/mutex.h> 17 + #include <linux/notifier.h> 18 + #include <linux/slab.h> 19 + #include <linux/platform_data/lenovo-yoga-c630.h> 20 + 21 + #define LENOVO_EC_RESPONSE_REG 0x01 22 + #define LENOVO_EC_REQUEST_REG 0x02 23 + 24 + #define LENOVO_EC_UCSI_WRITE 0x20 25 + #define LENOVO_EC_UCSI_READ 0x21 26 + 27 + #define LENOVO_EC_READ_REG 0xb0 28 + #define LENOVO_EC_REQUEST_NEXT_EVENT 0x84 29 + 30 + #define LENOVO_EC_UCSI_VERSION 0x20 31 + 32 + struct yoga_c630_ec { 33 + struct i2c_client *client; 34 + struct mutex lock; 35 + struct blocking_notifier_head notifier_list; 36 + }; 37 + 38 + static int yoga_c630_ec_request(struct yoga_c630_ec *ec, u8 *req, size_t req_len, 39 + u8 *resp, size_t resp_len) 40 + { 41 + int ret; 42 + 43 + lockdep_assert_held(&ec->lock); 44 + 45 + ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_REQUEST_REG, 46 + req_len, req); 47 + if (ret < 0) 48 + return ret; 49 + 50 + return i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_RESPONSE_REG, 51 + resp_len, resp); 52 + } 53 + 54 + int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr) 55 + { 56 + u8 req[2] = { LENOVO_EC_READ_REG, }; 57 + int ret; 58 + u8 val; 59 + 60 + guard(mutex)(&ec->lock); 61 + 62 + req[1] = addr; 63 + ret = yoga_c630_ec_request(ec, req, sizeof(req), &val, 1); 64 + if (ret < 0) 65 + return ret; 66 + 67 + return val; 68 + } 69 + EXPORT_SYMBOL_GPL(yoga_c630_ec_read8); 70 + 71 + int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr) 72 + { 73 + u8 req[2] = { LENOVO_EC_READ_REG, }; 74 + int ret; 75 + u8 msb; 76 + u8 lsb; 77 + 78 + /* don't overflow the address */ 79 + if (addr == 0xff) 80 + return -EINVAL; 81 + 82 + guard(mutex)(&ec->lock); 83 + 84 + req[1] = addr; 85 + ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1); 86 + if (ret < 0) 87 + return ret; 88 + 89 + req[1] = addr + 1; 90 + ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1); 91 + if (ret < 0) 92 + return ret; 93 + 94 + return msb << 8 | lsb; 95 + } 96 + EXPORT_SYMBOL_GPL(yoga_c630_ec_read16); 97 + 98 + u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec) 99 + { 100 + u8 req[3] = { 0xb3, 0xf2, }; 101 + int ret; 102 + u8 msb; 103 + u8 lsb; 104 + 105 + guard(mutex)(&ec->lock); 106 + 107 + req[2] = LENOVO_EC_UCSI_VERSION; 108 + ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1); 109 + if (ret < 0) 110 + return ret; 111 + 112 + req[2] = LENOVO_EC_UCSI_VERSION + 1; 113 + ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1); 114 + if (ret < 0) 115 + return ret; 116 + 117 + return msb << 8 | lsb; 118 + } 119 + EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_get_version); 120 + 121 + int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec, 122 + const u8 req[YOGA_C630_UCSI_WRITE_SIZE]) 123 + { 124 + int ret; 125 + 126 + mutex_lock(&ec->lock); 127 + ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_UCSI_WRITE, 128 + YOGA_C630_UCSI_WRITE_SIZE, req); 129 + mutex_unlock(&ec->lock); 130 + 131 + return ret < 0 ? ret : 0; 132 + } 133 + EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_write); 134 + 135 + int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec, 136 + u8 resp[YOGA_C630_UCSI_READ_SIZE]) 137 + { 138 + int ret; 139 + 140 + mutex_lock(&ec->lock); 141 + ret = i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_UCSI_READ, 142 + YOGA_C630_UCSI_READ_SIZE, resp); 143 + mutex_unlock(&ec->lock); 144 + 145 + return ret < 0 ? ret : 0; 146 + } 147 + EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_read); 148 + 149 + static irqreturn_t yoga_c630_ec_thread_intr(int irq, void *data) 150 + { 151 + u8 req[] = { LENOVO_EC_REQUEST_NEXT_EVENT }; 152 + struct yoga_c630_ec *ec = data; 153 + u8 event; 154 + int ret; 155 + 156 + mutex_lock(&ec->lock); 157 + ret = yoga_c630_ec_request(ec, req, sizeof(req), &event, 1); 158 + mutex_unlock(&ec->lock); 159 + if (ret < 0) 160 + return IRQ_HANDLED; 161 + 162 + blocking_notifier_call_chain(&ec->notifier_list, event, ec); 163 + 164 + return IRQ_HANDLED; 165 + } 166 + 167 + /** 168 + * yoga_c630_ec_register_notify - Register a notifier callback for EC events. 169 + * @ec: Yoga C630 EC 170 + * @nb: Notifier block pointer to register 171 + * 172 + * Return: 0 on success or negative error code. 173 + */ 174 + int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb) 175 + { 176 + return blocking_notifier_chain_register(&ec->notifier_list, nb); 177 + } 178 + EXPORT_SYMBOL_GPL(yoga_c630_ec_register_notify); 179 + 180 + /** 181 + * yoga_c630_ec_unregister_notify - Unregister notifier callback for EC events. 182 + * @ec: Yoga C630 EC 183 + * @nb: Notifier block pointer to unregister 184 + * 185 + * Unregister a notifier callback that was previously registered with 186 + * yoga_c630_ec_register_notify(). 187 + */ 188 + void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb) 189 + { 190 + blocking_notifier_chain_unregister(&ec->notifier_list, nb); 191 + } 192 + EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify); 193 + 194 + static void yoga_c630_aux_release(struct device *dev) 195 + { 196 + struct auxiliary_device *adev = to_auxiliary_dev(dev); 197 + 198 + kfree(adev); 199 + } 200 + 201 + static void yoga_c630_aux_remove(void *data) 202 + { 203 + struct auxiliary_device *adev = data; 204 + 205 + auxiliary_device_delete(adev); 206 + auxiliary_device_uninit(adev); 207 + } 208 + 209 + static int yoga_c630_aux_init(struct device *parent, const char *name, 210 + struct yoga_c630_ec *ec) 211 + { 212 + struct auxiliary_device *adev; 213 + int ret; 214 + 215 + adev = kzalloc(sizeof(*adev), GFP_KERNEL); 216 + if (!adev) 217 + return -ENOMEM; 218 + 219 + adev->name = name; 220 + adev->id = 0; 221 + adev->dev.parent = parent; 222 + adev->dev.release = yoga_c630_aux_release; 223 + adev->dev.platform_data = ec; 224 + 225 + ret = auxiliary_device_init(adev); 226 + if (ret) { 227 + kfree(adev); 228 + return ret; 229 + } 230 + 231 + ret = auxiliary_device_add(adev); 232 + if (ret) { 233 + auxiliary_device_uninit(adev); 234 + return ret; 235 + } 236 + 237 + return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev); 238 + } 239 + 240 + static int yoga_c630_ec_probe(struct i2c_client *client) 241 + { 242 + struct device *dev = &client->dev; 243 + struct yoga_c630_ec *ec; 244 + int ret; 245 + 246 + ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); 247 + if (!ec) 248 + return -ENOMEM; 249 + 250 + mutex_init(&ec->lock); 251 + ec->client = client; 252 + BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list); 253 + 254 + ret = devm_request_threaded_irq(dev, client->irq, 255 + NULL, yoga_c630_ec_thread_intr, 256 + IRQF_ONESHOT, "yoga_c630_ec", ec); 257 + if (ret < 0) 258 + return dev_err_probe(dev, ret, "unable to request irq\n"); 259 + 260 + ret = yoga_c630_aux_init(dev, YOGA_C630_DEV_PSY, ec); 261 + if (ret) 262 + return ret; 263 + 264 + return yoga_c630_aux_init(dev, YOGA_C630_DEV_UCSI, ec); 265 + } 266 + 267 + 268 + static const struct of_device_id yoga_c630_ec_of_match[] = { 269 + { .compatible = "lenovo,yoga-c630-ec" }, 270 + {} 271 + }; 272 + MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match); 273 + 274 + static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = { 275 + { "yoga-c630-ec", }, 276 + {} 277 + }; 278 + MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table); 279 + 280 + static struct i2c_driver yoga_c630_ec_i2c_driver = { 281 + .driver = { 282 + .name = "yoga-c630-ec", 283 + .of_match_table = yoga_c630_ec_of_match 284 + }, 285 + .probe = yoga_c630_ec_probe, 286 + .id_table = yoga_c630_ec_i2c_id_table, 287 + }; 288 + module_i2c_driver(yoga_c630_ec_i2c_driver); 289 + 290 + MODULE_DESCRIPTION("Lenovo Yoga C630 Embedded Controller"); 291 + MODULE_LICENSE("GPL");
+44
include/linux/platform_data/lenovo-yoga-c630.h
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022-2024, Linaro Ltd 4 + * Authors: 5 + * Bjorn Andersson 6 + * Dmitry Baryshkov 7 + */ 8 + 9 + #ifndef _LENOVO_YOGA_C630_DATA_H 10 + #define _LENOVO_YOGA_C630_DATA_H 11 + 12 + struct yoga_c630_ec; 13 + struct notifier_block; 14 + 15 + #define YOGA_C630_MOD_NAME "lenovo_yoga_c630" 16 + 17 + #define YOGA_C630_DEV_UCSI "ucsi" 18 + #define YOGA_C630_DEV_PSY "psy" 19 + 20 + int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr); 21 + int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr); 22 + 23 + int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb); 24 + void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb); 25 + 26 + #define YOGA_C630_UCSI_WRITE_SIZE 8 27 + #define YOGA_C630_UCSI_CCI_SIZE 4 28 + #define YOGA_C630_UCSI_DATA_SIZE 16 29 + #define YOGA_C630_UCSI_READ_SIZE (YOGA_C630_UCSI_CCI_SIZE + YOGA_C630_UCSI_DATA_SIZE) 30 + 31 + u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec); 32 + int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec, 33 + const u8 req[YOGA_C630_UCSI_WRITE_SIZE]); 34 + int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec, 35 + u8 resp[YOGA_C630_UCSI_READ_SIZE]); 36 + 37 + #define LENOVO_EC_EVENT_USB 0x20 38 + #define LENOVO_EC_EVENT_UCSI 0x21 39 + #define LENOVO_EC_EVENT_HPD 0x22 40 + #define LENOVO_EC_EVENT_BAT_STATUS 0x24 41 + #define LENOVO_EC_EVENT_BAT_INFO 0x25 42 + #define LENOVO_EC_EVENT_BAT_ADPT_STATUS 0x37 43 + 44 + #endif