xref: /OK3568_Linux_fs/kernel/drivers/acpi/dptf/dptf_pch_fivr.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * dptf_pch_fivr:  DPTF PCH FIVR Participant driver
4*4882a593Smuzhiyun  * Copyright (c) 2020, Intel Corporation.
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/acpi.h>
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/platform_device.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun struct pch_fivr_resp {
13*4882a593Smuzhiyun 	u64 status;
14*4882a593Smuzhiyun 	u64 result;
15*4882a593Smuzhiyun };
16*4882a593Smuzhiyun 
pch_fivr_read(acpi_handle handle,char * method,struct pch_fivr_resp * fivr_resp)17*4882a593Smuzhiyun static int pch_fivr_read(acpi_handle handle, char *method, struct pch_fivr_resp *fivr_resp)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun 	struct acpi_buffer resp = { sizeof(struct pch_fivr_resp), fivr_resp};
20*4882a593Smuzhiyun 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
21*4882a593Smuzhiyun 	struct acpi_buffer format = { sizeof("NN"), "NN" };
22*4882a593Smuzhiyun 	union acpi_object *obj;
23*4882a593Smuzhiyun 	acpi_status status;
24*4882a593Smuzhiyun 	int ret = -EFAULT;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	status = acpi_evaluate_object(handle, method, NULL, &buffer);
27*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
28*4882a593Smuzhiyun 		return ret;
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	obj = buffer.pointer;
31*4882a593Smuzhiyun 	if (!obj || obj->type != ACPI_TYPE_PACKAGE)
32*4882a593Smuzhiyun 		goto release_buffer;
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	status = acpi_extract_package(obj, &format, &resp);
35*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
36*4882a593Smuzhiyun 		goto release_buffer;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	if (fivr_resp->status)
39*4882a593Smuzhiyun 		goto release_buffer;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	ret = 0;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun release_buffer:
44*4882a593Smuzhiyun 	kfree(buffer.pointer);
45*4882a593Smuzhiyun 	return ret;
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun /*
49*4882a593Smuzhiyun  * Presentation of attributes which are defined for INT1045
50*4882a593Smuzhiyun  * They are:
51*4882a593Smuzhiyun  * freq_mhz_low_clock : Set PCH FIVR switching freq for
52*4882a593Smuzhiyun  *			FIVR clock 19.2MHz and 24MHz
53*4882a593Smuzhiyun  * freq_mhz_high_clock : Set PCH FIVR switching freq for
54*4882a593Smuzhiyun  *			FIVR clock 38.4MHz
55*4882a593Smuzhiyun  */
56*4882a593Smuzhiyun #define PCH_FIVR_SHOW(name, method) \
57*4882a593Smuzhiyun static ssize_t name##_show(struct device *dev,\
58*4882a593Smuzhiyun 			   struct device_attribute *attr,\
59*4882a593Smuzhiyun 			   char *buf)\
60*4882a593Smuzhiyun {\
61*4882a593Smuzhiyun 	struct acpi_device *acpi_dev = dev_get_drvdata(dev);\
62*4882a593Smuzhiyun 	struct pch_fivr_resp fivr_resp;\
63*4882a593Smuzhiyun 	int status;\
64*4882a593Smuzhiyun \
65*4882a593Smuzhiyun 	status = pch_fivr_read(acpi_dev->handle, #method, &fivr_resp);\
66*4882a593Smuzhiyun 	if (status)\
67*4882a593Smuzhiyun 		return status;\
68*4882a593Smuzhiyun \
69*4882a593Smuzhiyun 	return sprintf(buf, "%llu\n", fivr_resp.result);\
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define PCH_FIVR_STORE(name, method) \
73*4882a593Smuzhiyun static ssize_t name##_store(struct device *dev,\
74*4882a593Smuzhiyun 			    struct device_attribute *attr,\
75*4882a593Smuzhiyun 			    const char *buf, size_t count)\
76*4882a593Smuzhiyun {\
77*4882a593Smuzhiyun 	struct acpi_device *acpi_dev = dev_get_drvdata(dev);\
78*4882a593Smuzhiyun 	acpi_status status;\
79*4882a593Smuzhiyun 	u32 val;\
80*4882a593Smuzhiyun \
81*4882a593Smuzhiyun 	if (kstrtouint(buf, 0, &val) < 0)\
82*4882a593Smuzhiyun 		return -EINVAL;\
83*4882a593Smuzhiyun \
84*4882a593Smuzhiyun 	status = acpi_execute_simple_method(acpi_dev->handle, #method, val);\
85*4882a593Smuzhiyun 	if (ACPI_SUCCESS(status))\
86*4882a593Smuzhiyun 		return count;\
87*4882a593Smuzhiyun \
88*4882a593Smuzhiyun 	return -EINVAL;\
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun PCH_FIVR_SHOW(freq_mhz_low_clock, GFC0)
92*4882a593Smuzhiyun PCH_FIVR_SHOW(freq_mhz_high_clock, GFC1)
93*4882a593Smuzhiyun PCH_FIVR_STORE(freq_mhz_low_clock, RFC0)
94*4882a593Smuzhiyun PCH_FIVR_STORE(freq_mhz_high_clock, RFC1)
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun static DEVICE_ATTR_RW(freq_mhz_low_clock);
97*4882a593Smuzhiyun static DEVICE_ATTR_RW(freq_mhz_high_clock);
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun static struct attribute *fivr_attrs[] = {
100*4882a593Smuzhiyun 	&dev_attr_freq_mhz_low_clock.attr,
101*4882a593Smuzhiyun 	&dev_attr_freq_mhz_high_clock.attr,
102*4882a593Smuzhiyun 	NULL
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun static const struct attribute_group pch_fivr_attribute_group = {
106*4882a593Smuzhiyun 	.attrs = fivr_attrs,
107*4882a593Smuzhiyun 	.name = "pch_fivr_switch_frequency"
108*4882a593Smuzhiyun };
109*4882a593Smuzhiyun 
pch_fivr_add(struct platform_device * pdev)110*4882a593Smuzhiyun static int pch_fivr_add(struct platform_device *pdev)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	struct acpi_device *acpi_dev;
113*4882a593Smuzhiyun 	unsigned long long ptype;
114*4882a593Smuzhiyun 	acpi_status status;
115*4882a593Smuzhiyun 	int result;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	acpi_dev = ACPI_COMPANION(&(pdev->dev));
118*4882a593Smuzhiyun 	if (!acpi_dev)
119*4882a593Smuzhiyun 		return -ENODEV;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype);
122*4882a593Smuzhiyun 	if (ACPI_FAILURE(status) || ptype != 0x05)
123*4882a593Smuzhiyun 		return -ENODEV;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	result = sysfs_create_group(&pdev->dev.kobj,
126*4882a593Smuzhiyun 				    &pch_fivr_attribute_group);
127*4882a593Smuzhiyun 	if (result)
128*4882a593Smuzhiyun 		return result;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	platform_set_drvdata(pdev, acpi_dev);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	return 0;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
pch_fivr_remove(struct platform_device * pdev)135*4882a593Smuzhiyun static int pch_fivr_remove(struct platform_device *pdev)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	sysfs_remove_group(&pdev->dev.kobj, &pch_fivr_attribute_group);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	return 0;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun static const struct acpi_device_id pch_fivr_device_ids[] = {
143*4882a593Smuzhiyun 	{"INTC1045", 0},
144*4882a593Smuzhiyun 	{"INTC1049", 0},
145*4882a593Smuzhiyun 	{"", 0},
146*4882a593Smuzhiyun };
147*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, pch_fivr_device_ids);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static struct platform_driver pch_fivr_driver = {
150*4882a593Smuzhiyun 	.probe = pch_fivr_add,
151*4882a593Smuzhiyun 	.remove = pch_fivr_remove,
152*4882a593Smuzhiyun 	.driver = {
153*4882a593Smuzhiyun 		.name = "dptf_pch_fivr",
154*4882a593Smuzhiyun 		.acpi_match_table = pch_fivr_device_ids,
155*4882a593Smuzhiyun 	},
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun module_platform_driver(pch_fivr_driver);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
161*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
162*4882a593Smuzhiyun MODULE_DESCRIPTION("ACPI DPTF PCH FIVR driver");
163