FILE
ucsi_acpi.c
File preview with syntax highlighting.
linux-6.19.14/drivers/usb/typec/ucsi/ucsi_acpi.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * UCSI ACPI driver 4 * 5 * Copyright (C) 2017, Intel Corporation 6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 7 */ 8 9#include <linux/platform_device.h> 10#include <linux/module.h> 11#include <linux/acpi.h> 12#include <linux/dmi.h> 13 14#include "ucsi.h" 15 16#define UCSI_DSM_UUID "6f8398c2-7ca4-11e4-ad36-631042b5008f" 17#define UCSI_DSM_FUNC_WRITE 1 18#define UCSI_DSM_FUNC_READ 2 19 20struct ucsi_acpi { 21 struct device *dev; 22 struct ucsi *ucsi; 23 void *base; 24 bool check_bogus_event; 25 guid_t guid; 26 u64 cmd; 27}; 28 29static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func) 30{ 31 union acpi_object *obj; 32 33 obj = acpi_evaluate_dsm(ACPI_HANDLE(ua->dev), &ua->guid, 1, func, 34 NULL); 35 if (!obj) { 36 dev_err(ua->dev, "%s: failed to evaluate _DSM %d\n", 37 __func__, func); 38 return -EIO; 39 } 40 41 ACPI_FREE(obj); 42 return 0; 43} 44 45static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version) 46{ 47 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 48 int ret; 49 50 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); 51 if (ret) 52 return ret; 53 54 memcpy(version, ua->base + UCSI_VERSION, sizeof(*version)); 55 56 return 0; 57} 58 59static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) 60{ 61 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 62 63 memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); 64 65 return 0; 66} 67 68static int ucsi_acpi_poll_cci(struct ucsi *ucsi, u32 *cci) 69{ 70 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 71 int ret; 72 73 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); 74 if (ret) 75 return ret; 76 77 return ucsi_acpi_read_cci(ucsi, cci); 78} 79 80static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) 81{ 82 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 83 84 memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); 85 86 return 0; 87} 88 89static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command) 90{ 91 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 92 93 memcpy(ua->base + UCSI_CONTROL, &command, sizeof(command)); 94 ua->cmd = command; 95 96 return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE); 97} 98 99static const struct ucsi_operations ucsi_acpi_ops = { 100 .read_version = ucsi_acpi_read_version, 101 .read_cci = ucsi_acpi_read_cci, 102 .poll_cci = ucsi_acpi_poll_cci, 103 .read_message_in = ucsi_acpi_read_message_in, 104 .sync_control = ucsi_sync_control_common, 105 .async_control = ucsi_acpi_async_control 106}; 107 108static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, 109 void *val, size_t len) 110{ 111 u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | 112 UCSI_CONSTAT_PDOS_CHANGE; 113 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); 114 int ret; 115 116 ret = ucsi_sync_control_common(ucsi, command, cci, val, len); 117 if (ret < 0) 118 return ret; 119 120 if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS && 121 ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) && 122 ua->cmd & UCSI_GET_PDOS_SRC_PDOS) 123 ua->check_bogus_event = true; 124 125 if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && 126 ua->check_bogus_event) { 127 /* Clear the bogus change */ 128 if (*(u16 *)val == bogus_change) 129 *(u16 *)val = 0; 130 131 ua->check_bogus_event = false; 132 } 133 134 return ret; 135} 136 137static const struct ucsi_operations ucsi_gram_ops = { 138 .read_version = ucsi_acpi_read_version, 139 .read_cci = ucsi_acpi_read_cci, 140 .poll_cci = ucsi_acpi_poll_cci, 141 .read_message_in = ucsi_acpi_read_message_in, 142 .sync_control = ucsi_gram_sync_control, 143 .async_control = ucsi_acpi_async_control 144}; 145 146static const struct dmi_system_id ucsi_acpi_quirks[] = { 147 { 148 .matches = { 149 DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), 150 DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"), 151 DMI_MATCH(DMI_PRODUCT_NAME, "90Q"), 152 }, 153 .driver_data = (void *)&ucsi_gram_ops, 154 }, 155 { } 156}; 157 158static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) 159{ 160 struct ucsi_acpi *ua = data; 161 u32 cci; 162 int ret; 163 164 ret = ua->ucsi->ops->read_cci(ua->ucsi, &cci); 165 if (ret) 166 return; 167 168 ucsi_notify_common(ua->ucsi, cci); 169} 170 171static int ucsi_acpi_probe(struct platform_device *pdev) 172{ 173 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 174 const struct ucsi_operations *ops = &ucsi_acpi_ops; 175 const struct dmi_system_id *id; 176 struct ucsi_acpi *ua; 177 struct resource *res; 178 acpi_status status; 179 int ret; 180 181 if (adev->dep_unmet) 182 return -EPROBE_DEFER; 183 184 ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL); 185 if (!ua) 186 return -ENOMEM; 187 188 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 189 if (!res) { 190 dev_err(&pdev->dev, "missing memory resource\n"); 191 return -ENODEV; 192 } 193 194 ua->base = devm_memremap(&pdev->dev, res->start, resource_size(res), MEMREMAP_WB); 195 if (IS_ERR(ua->base)) 196 return PTR_ERR(ua->base); 197 198 ret = guid_parse(UCSI_DSM_UUID, &ua->guid); 199 if (ret) 200 return ret; 201 202 ua->dev = &pdev->dev; 203 204 id = dmi_first_match(ucsi_acpi_quirks); 205 if (id) 206 ops = id->driver_data; 207 208 ua->ucsi = ucsi_create(&pdev->dev, ops); 209 if (IS_ERR(ua->ucsi)) 210 return PTR_ERR(ua->ucsi); 211 212 ucsi_set_drvdata(ua->ucsi, ua); 213 214 status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev), 215 ACPI_DEVICE_NOTIFY, 216 ucsi_acpi_notify, ua); 217 if (ACPI_FAILURE(status)) { 218 dev_err(&pdev->dev, "failed to install notify handler\n"); 219 ucsi_destroy(ua->ucsi); 220 return -ENODEV; 221 } 222 223 ret = ucsi_register(ua->ucsi); 224 if (ret) { 225 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), 226 ACPI_DEVICE_NOTIFY, 227 ucsi_acpi_notify); 228 ucsi_destroy(ua->ucsi); 229 return ret; 230 } 231 232 platform_set_drvdata(pdev, ua); 233 234 return 0; 235} 236 237static void ucsi_acpi_remove(struct platform_device *pdev) 238{ 239 struct ucsi_acpi *ua = platform_get_drvdata(pdev); 240 241 ucsi_unregister(ua->ucsi); 242 ucsi_destroy(ua->ucsi); 243 244 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY, 245 ucsi_acpi_notify); 246} 247 248static int ucsi_acpi_resume(struct device *dev) 249{ 250 struct ucsi_acpi *ua = dev_get_drvdata(dev); 251 252 return ucsi_resume(ua->ucsi); 253} 254 255static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume); 256 257static const struct acpi_device_id ucsi_acpi_match[] = { 258 { "PNP0CA0", 0 }, 259 { }, 260}; 261MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match); 262 263static struct platform_driver ucsi_acpi_platform_driver = { 264 .driver = { 265 .name = "ucsi_acpi", 266 .pm = pm_ptr(&ucsi_acpi_pm_ops), 267 .acpi_match_table = ACPI_PTR(ucsi_acpi_match), 268 }, 269 .probe = ucsi_acpi_probe, 270 .remove = ucsi_acpi_remove, 271}; 272 273module_platform_driver(ucsi_acpi_platform_driver); 274 275MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); 276MODULE_LICENSE("GPL v2"); 277MODULE_DESCRIPTION("UCSI ACPI driver");
~/
Read-only file preview.