GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / platform / x86 / intel_cht_int33fe.c
1 /*
2  * Intel Cherry Trail ACPI INT33FE pseudo device driver
3  *
4  * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Some Intel Cherry Trail based device which ship with Windows 10, have
11  * this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2
12  * resources, for 4 different chips attached to various i2c busses:
13  * 1. The Whiskey Cove pmic, which is also described by the INT34D3 ACPI device
14  * 2. Maxim MAX17047 Fuel Gauge Controller
15  * 3. FUSB302 USB Type-C Controller
16  * 4. PI3USB30532 USB switch
17  *
18  * So this driver is a stub / pseudo driver whose only purpose is to
19  * instantiate i2c-clients for chips 2 - 4, so that standard i2c drivers
20  * for these chips can bind to the them.
21  */
22
23 #include <linux/acpi.h>
24 #include <linux/i2c.h>
25 #include <linux/interrupt.h>
26 #include <linux/module.h>
27 #include <linux/slab.h>
28
29 #define EXPECTED_PTYPE          4
30
31 struct cht_int33fe_data {
32         struct i2c_client *max17047;
33         struct i2c_client *fusb302;
34         struct i2c_client *pi3usb30532;
35 };
36
37 static const char * const max17047_suppliers[] = { "bq24190-charger" };
38
39 static const struct property_entry max17047_props[] = {
40         PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers),
41         { }
42 };
43
44 static int cht_int33fe_probe(struct i2c_client *client)
45 {
46         struct device *dev = &client->dev;
47         struct i2c_board_info board_info;
48         struct cht_int33fe_data *data;
49         unsigned long long ptyp;
50         acpi_status status;
51         int fusb302_irq;
52
53         status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
54         if (ACPI_FAILURE(status)) {
55                 dev_err(dev, "Error getting PTYPE\n");
56                 return -ENODEV;
57         }
58
59         /*
60          * The same ACPI HID is used for different configurations check PTYP
61          * to ensure that we are dealing with the expected config.
62          */
63         if (ptyp != EXPECTED_PTYPE)
64                 return -ENODEV;
65
66         /* The FUSB302 uses the irq at index 1 and is the only irq user */
67         fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
68         if (fusb302_irq < 0) {
69                 if (fusb302_irq != -EPROBE_DEFER)
70                         dev_err(dev, "Error getting FUSB302 irq\n");
71                 return fusb302_irq;
72         }
73
74         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
75         if (!data)
76                 return -ENOMEM;
77
78         memset(&board_info, 0, sizeof(board_info));
79         strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
80         board_info.properties = max17047_props;
81
82         data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
83         if (!data->max17047)
84                 return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
85
86         memset(&board_info, 0, sizeof(board_info));
87         strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE);
88         board_info.irq = fusb302_irq;
89
90         data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info);
91         if (!data->fusb302)
92                 goto out_unregister_max17047;
93
94         memset(&board_info, 0, sizeof(board_info));
95         strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
96
97         data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
98         if (!data->pi3usb30532)
99                 goto out_unregister_fusb302;
100
101         i2c_set_clientdata(client, data);
102
103         return 0;
104
105 out_unregister_fusb302:
106         i2c_unregister_device(data->fusb302);
107
108 out_unregister_max17047:
109         i2c_unregister_device(data->max17047);
110
111         return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
112 }
113
114 static int cht_int33fe_remove(struct i2c_client *i2c)
115 {
116         struct cht_int33fe_data *data = i2c_get_clientdata(i2c);
117
118         i2c_unregister_device(data->pi3usb30532);
119         i2c_unregister_device(data->fusb302);
120         i2c_unregister_device(data->max17047);
121
122         return 0;
123 }
124
125 static const struct i2c_device_id cht_int33fe_i2c_id[] = {
126         { }
127 };
128 MODULE_DEVICE_TABLE(i2c, cht_int33fe_i2c_id);
129
130 static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
131         { "INT33FE", },
132         { }
133 };
134 MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids);
135
136 static struct i2c_driver cht_int33fe_driver = {
137         .driver = {
138                 .name = "Intel Cherry Trail ACPI INT33FE driver",
139                 .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
140         },
141         .probe_new = cht_int33fe_probe,
142         .remove = cht_int33fe_remove,
143         .id_table = cht_int33fe_i2c_id,
144         .disable_i2c_core_irq_mapping = true,
145 };
146
147 module_i2c_driver(cht_int33fe_driver);
148
149 MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver");
150 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
151 MODULE_LICENSE("GPL");