aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/intel_cht_int33fe_common.c
blob: 42dd11623f5651fbe51661c0f5ce5390c95b8d1a (plain) (blame)
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");