Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | // SPDX-License-Identifier: GPL-2.0 /* * Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers * (USB Micro-B and Type-C connector variants). * * Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com> */ #include <linux/acpi.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> #include "intel_cht_int33fe_common.h" #define EXPECTED_PTYPE 4 static int cht_int33fe_i2c_res_filter(struct acpi_resource *ares, void *data) { struct acpi_resource_i2c_serialbus *sb; int *count = data; if (i2c_acpi_get_i2c_resource(ares, &sb)) (*count)++; return 1; } static int cht_int33fe_count_i2c_clients(struct device *dev) { struct acpi_device *adev; LIST_HEAD(resource_list); int count = 0; adev = ACPI_COMPANION(dev); if (!adev) return -EINVAL; acpi_dev_get_resources(adev, &resource_list, cht_int33fe_i2c_res_filter, &count); acpi_dev_free_resource_list(&resource_list); return count; } static int cht_int33fe_check_hw_type(struct device *dev) { unsigned long long ptyp; acpi_status status; int ret; status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp); if (ACPI_FAILURE(status)) { dev_err(dev, "Error getting PTYPE\n"); return -ENODEV; } /* * The same ACPI HID is used for different configurations check PTYP * to ensure that we are dealing with the expected config. */ if (ptyp != EXPECTED_PTYPE) return -ENODEV; /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */ if (!acpi_dev_present("INT34D3", "1", 3)) { dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n", EXPECTED_PTYPE); return -ENODEV; } ret = cht_int33fe_count_i2c_clients(dev); if (ret < 0) return ret; switch (ret) { case 2: return INT33FE_HW_MICROB; case 4: return INT33FE_HW_TYPEC; default: return -ENODEV; } } static int cht_int33fe_probe(struct platform_device *pdev) { struct cht_int33fe_data *data; struct device *dev = &pdev->dev; int ret; ret = cht_int33fe_check_hw_type(dev); if (ret < 0) return ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->dev = dev; switch (ret) { case INT33FE_HW_MICROB: data->probe = cht_int33fe_microb_probe; data->remove = cht_int33fe_microb_remove; break; case INT33FE_HW_TYPEC: data->probe = cht_int33fe_typec_probe; data->remove = cht_int33fe_typec_remove; break; } platform_set_drvdata(pdev, data); return data->probe(data); } static int cht_int33fe_remove(struct platform_device *pdev) { struct cht_int33fe_data *data = platform_get_drvdata(pdev); return data->remove(data); } static const struct acpi_device_id cht_int33fe_acpi_ids[] = { { "INT33FE", }, { } }; MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids); static struct platform_driver cht_int33fe_driver = { .driver = { .name = "Intel Cherry Trail ACPI INT33FE driver", .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), }, .probe = cht_int33fe_probe, .remove = cht_int33fe_remove, }; module_platform_driver(cht_int33fe_driver); MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver"); MODULE_AUTHOR("Yauhen Kharuzhy <jekhor@gmail.com>"); MODULE_LICENSE("GPL v2"); |