xref: /linux/drivers/hwmon/gpd-fan.c (revision 0ab88e2394392f475b8857ac82c0c987841217f8)
1*0ab88e23SCryolitia PukNgae // SPDX-License-Identifier: GPL-2.0+
2*0ab88e23SCryolitia PukNgae 
3*0ab88e23SCryolitia PukNgae /* Platform driver for GPD devices that expose fan control via hwmon sysfs.
4*0ab88e23SCryolitia PukNgae  *
5*0ab88e23SCryolitia PukNgae  * Fan control is provided via pwm interface in the range [0-255].
6*0ab88e23SCryolitia PukNgae  * Each model has a different range in the EC, the written value is scaled to
7*0ab88e23SCryolitia PukNgae  * accommodate for that.
8*0ab88e23SCryolitia PukNgae  *
9*0ab88e23SCryolitia PukNgae  * Based on this repo:
10*0ab88e23SCryolitia PukNgae  * https://github.com/Cryolitia/gpd-fan-driver
11*0ab88e23SCryolitia PukNgae  *
12*0ab88e23SCryolitia PukNgae  * Copyright (c) 2024 Cryolitia PukNgae
13*0ab88e23SCryolitia PukNgae  */
14*0ab88e23SCryolitia PukNgae 
15*0ab88e23SCryolitia PukNgae #include <linux/acpi.h>
16*0ab88e23SCryolitia PukNgae #include <linux/dmi.h>
17*0ab88e23SCryolitia PukNgae #include <linux/hwmon.h>
18*0ab88e23SCryolitia PukNgae #include <linux/ioport.h>
19*0ab88e23SCryolitia PukNgae #include <linux/kernel.h>
20*0ab88e23SCryolitia PukNgae #include <linux/module.h>
21*0ab88e23SCryolitia PukNgae #include <linux/platform_device.h>
22*0ab88e23SCryolitia PukNgae 
23*0ab88e23SCryolitia PukNgae #define DRIVER_NAME "gpdfan"
24*0ab88e23SCryolitia PukNgae #define GPD_PWM_CTR_OFFSET 0x1841
25*0ab88e23SCryolitia PukNgae 
26*0ab88e23SCryolitia PukNgae static char *gpd_fan_board = "";
27*0ab88e23SCryolitia PukNgae module_param(gpd_fan_board, charp, 0444);
28*0ab88e23SCryolitia PukNgae 
29*0ab88e23SCryolitia PukNgae // EC read/write locker, protecting a sequence of EC operations
30*0ab88e23SCryolitia PukNgae static DEFINE_MUTEX(gpd_fan_sequence_lock);
31*0ab88e23SCryolitia PukNgae 
32*0ab88e23SCryolitia PukNgae enum gpd_board {
33*0ab88e23SCryolitia PukNgae 	win_mini,
34*0ab88e23SCryolitia PukNgae 	win4_6800u,
35*0ab88e23SCryolitia PukNgae 	win_max_2,
36*0ab88e23SCryolitia PukNgae 	duo,
37*0ab88e23SCryolitia PukNgae };
38*0ab88e23SCryolitia PukNgae 
39*0ab88e23SCryolitia PukNgae enum FAN_PWM_ENABLE {
40*0ab88e23SCryolitia PukNgae 	DISABLE		= 0,
41*0ab88e23SCryolitia PukNgae 	MANUAL		= 1,
42*0ab88e23SCryolitia PukNgae 	AUTOMATIC	= 2,
43*0ab88e23SCryolitia PukNgae };
44*0ab88e23SCryolitia PukNgae 
45*0ab88e23SCryolitia PukNgae static struct {
46*0ab88e23SCryolitia PukNgae 	enum FAN_PWM_ENABLE pwm_enable;
47*0ab88e23SCryolitia PukNgae 	u8 pwm_value;
48*0ab88e23SCryolitia PukNgae 
49*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *drvdata;
50*0ab88e23SCryolitia PukNgae } gpd_driver_priv;
51*0ab88e23SCryolitia PukNgae 
52*0ab88e23SCryolitia PukNgae struct gpd_fan_drvdata {
53*0ab88e23SCryolitia PukNgae 	const char *board_name; // Board name for module param comparison
54*0ab88e23SCryolitia PukNgae 	const enum gpd_board board;
55*0ab88e23SCryolitia PukNgae 
56*0ab88e23SCryolitia PukNgae 	const u8 addr_port;
57*0ab88e23SCryolitia PukNgae 	const u8 data_port;
58*0ab88e23SCryolitia PukNgae 	const u16 manual_control_enable;
59*0ab88e23SCryolitia PukNgae 	const u16 rpm_read;
60*0ab88e23SCryolitia PukNgae 	const u16 pwm_write;
61*0ab88e23SCryolitia PukNgae 	const u16 pwm_max;
62*0ab88e23SCryolitia PukNgae };
63*0ab88e23SCryolitia PukNgae 
64*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_win_mini_drvdata = {
65*0ab88e23SCryolitia PukNgae 	.board_name		= "win_mini",
66*0ab88e23SCryolitia PukNgae 	.board			= win_mini,
67*0ab88e23SCryolitia PukNgae 
68*0ab88e23SCryolitia PukNgae 	.addr_port		= 0x4E,
69*0ab88e23SCryolitia PukNgae 	.data_port		= 0x4F,
70*0ab88e23SCryolitia PukNgae 	.manual_control_enable	= 0x047A,
71*0ab88e23SCryolitia PukNgae 	.rpm_read		= 0x0478,
72*0ab88e23SCryolitia PukNgae 	.pwm_write		= 0x047A,
73*0ab88e23SCryolitia PukNgae 	.pwm_max		= 244,
74*0ab88e23SCryolitia PukNgae };
75*0ab88e23SCryolitia PukNgae 
76*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_duo_drvdata = {
77*0ab88e23SCryolitia PukNgae 	.board_name		= "duo",
78*0ab88e23SCryolitia PukNgae 	.board			= duo,
79*0ab88e23SCryolitia PukNgae 
80*0ab88e23SCryolitia PukNgae 	.addr_port		= 0x4E,
81*0ab88e23SCryolitia PukNgae 	.data_port		= 0x4F,
82*0ab88e23SCryolitia PukNgae 	.manual_control_enable	= 0x047A,
83*0ab88e23SCryolitia PukNgae 	.rpm_read		= 0x0478,
84*0ab88e23SCryolitia PukNgae 	.pwm_write		= 0x047A,
85*0ab88e23SCryolitia PukNgae 	.pwm_max		= 244,
86*0ab88e23SCryolitia PukNgae };
87*0ab88e23SCryolitia PukNgae 
88*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_win4_drvdata = {
89*0ab88e23SCryolitia PukNgae 	.board_name		= "win4",
90*0ab88e23SCryolitia PukNgae 	.board			= win4_6800u,
91*0ab88e23SCryolitia PukNgae 
92*0ab88e23SCryolitia PukNgae 	.addr_port		= 0x2E,
93*0ab88e23SCryolitia PukNgae 	.data_port		= 0x2F,
94*0ab88e23SCryolitia PukNgae 	.manual_control_enable	= 0xC311,
95*0ab88e23SCryolitia PukNgae 	.rpm_read		= 0xC880,
96*0ab88e23SCryolitia PukNgae 	.pwm_write		= 0xC311,
97*0ab88e23SCryolitia PukNgae 	.pwm_max		= 127,
98*0ab88e23SCryolitia PukNgae };
99*0ab88e23SCryolitia PukNgae 
100*0ab88e23SCryolitia PukNgae static struct gpd_fan_drvdata gpd_wm2_drvdata = {
101*0ab88e23SCryolitia PukNgae 	.board_name		= "wm2",
102*0ab88e23SCryolitia PukNgae 	.board			= win_max_2,
103*0ab88e23SCryolitia PukNgae 
104*0ab88e23SCryolitia PukNgae 	.addr_port		= 0x4E,
105*0ab88e23SCryolitia PukNgae 	.data_port		= 0x4F,
106*0ab88e23SCryolitia PukNgae 	.manual_control_enable	= 0x0275,
107*0ab88e23SCryolitia PukNgae 	.rpm_read		= 0x0218,
108*0ab88e23SCryolitia PukNgae 	.pwm_write		= 0x1809,
109*0ab88e23SCryolitia PukNgae 	.pwm_max		= 184,
110*0ab88e23SCryolitia PukNgae };
111*0ab88e23SCryolitia PukNgae 
112*0ab88e23SCryolitia PukNgae static const struct dmi_system_id dmi_table[] = {
113*0ab88e23SCryolitia PukNgae 	{
114*0ab88e23SCryolitia PukNgae 		// GPD Win Mini
115*0ab88e23SCryolitia PukNgae 		// GPD Win Mini with AMD Ryzen 8840U
116*0ab88e23SCryolitia PukNgae 		.matches = {
117*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
118*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01")
119*0ab88e23SCryolitia PukNgae 		},
120*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_win_mini_drvdata,
121*0ab88e23SCryolitia PukNgae 	},
122*0ab88e23SCryolitia PukNgae 	{
123*0ab88e23SCryolitia PukNgae 		// GPD Win Mini
124*0ab88e23SCryolitia PukNgae 		// GPD Win Mini with AMD Ryzen HX370
125*0ab88e23SCryolitia PukNgae 		.matches = {
126*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
127*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02")
128*0ab88e23SCryolitia PukNgae 		},
129*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_win_mini_drvdata,
130*0ab88e23SCryolitia PukNgae 	},
131*0ab88e23SCryolitia PukNgae 	{
132*0ab88e23SCryolitia PukNgae 		// GPD Win Mini
133*0ab88e23SCryolitia PukNgae 		// GPD Win Mini with AMD Ryzen HX370
134*0ab88e23SCryolitia PukNgae 		.matches = {
135*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
136*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02-L")
137*0ab88e23SCryolitia PukNgae 		},
138*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_win_mini_drvdata,
139*0ab88e23SCryolitia PukNgae 	},
140*0ab88e23SCryolitia PukNgae 	{
141*0ab88e23SCryolitia PukNgae 		// GPD Win 4 with AMD Ryzen 6800U
142*0ab88e23SCryolitia PukNgae 		.matches = {
143*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
144*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
145*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_BOARD_VERSION, "Default string"),
146*0ab88e23SCryolitia PukNgae 		},
147*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_win4_drvdata,
148*0ab88e23SCryolitia PukNgae 	},
149*0ab88e23SCryolitia PukNgae 	{
150*0ab88e23SCryolitia PukNgae 		// GPD Win 4 with Ryzen 7840U
151*0ab88e23SCryolitia PukNgae 		.matches = {
152*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
153*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
154*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_BOARD_VERSION, "Ver. 1.0"),
155*0ab88e23SCryolitia PukNgae 		},
156*0ab88e23SCryolitia PukNgae 		// Since 7840U, win4 uses the same drvdata as wm2
157*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_wm2_drvdata,
158*0ab88e23SCryolitia PukNgae 	},
159*0ab88e23SCryolitia PukNgae 	{
160*0ab88e23SCryolitia PukNgae 		// GPD Win 4 with Ryzen 7840U (another)
161*0ab88e23SCryolitia PukNgae 		.matches = {
162*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
163*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
164*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_BOARD_VERSION, "Ver.1.0"),
165*0ab88e23SCryolitia PukNgae 		},
166*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_wm2_drvdata,
167*0ab88e23SCryolitia PukNgae 	},
168*0ab88e23SCryolitia PukNgae 	{
169*0ab88e23SCryolitia PukNgae 		// GPD Win Max 2 with Ryzen 6800U
170*0ab88e23SCryolitia PukNgae 		// GPD Win Max 2 2023 with Ryzen 7840U
171*0ab88e23SCryolitia PukNgae 		// GPD Win Max 2 2024 with Ryzen 8840U
172*0ab88e23SCryolitia PukNgae 		.matches = {
173*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
174*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"),
175*0ab88e23SCryolitia PukNgae 		},
176*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_wm2_drvdata,
177*0ab88e23SCryolitia PukNgae 	},
178*0ab88e23SCryolitia PukNgae 	{
179*0ab88e23SCryolitia PukNgae 		// GPD Win Max 2 with AMD Ryzen HX370
180*0ab88e23SCryolitia PukNgae 		.matches = {
181*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
182*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1619-05"),
183*0ab88e23SCryolitia PukNgae 		},
184*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_wm2_drvdata,
185*0ab88e23SCryolitia PukNgae 	},
186*0ab88e23SCryolitia PukNgae 	{
187*0ab88e23SCryolitia PukNgae 		// GPD Duo
188*0ab88e23SCryolitia PukNgae 		.matches = {
189*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
190*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01"),
191*0ab88e23SCryolitia PukNgae 		},
192*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_duo_drvdata,
193*0ab88e23SCryolitia PukNgae 	},
194*0ab88e23SCryolitia PukNgae 	{
195*0ab88e23SCryolitia PukNgae 		// GPD Duo (another)
196*0ab88e23SCryolitia PukNgae 		.matches = {
197*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
198*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01-L"),
199*0ab88e23SCryolitia PukNgae 		},
200*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_duo_drvdata,
201*0ab88e23SCryolitia PukNgae 	},
202*0ab88e23SCryolitia PukNgae 	{
203*0ab88e23SCryolitia PukNgae 		// GPD Pocket 4
204*0ab88e23SCryolitia PukNgae 		.matches = {
205*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
206*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04"),
207*0ab88e23SCryolitia PukNgae 		},
208*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_win_mini_drvdata,
209*0ab88e23SCryolitia PukNgae 	},
210*0ab88e23SCryolitia PukNgae 	{
211*0ab88e23SCryolitia PukNgae 		// GPD Pocket 4 (another)
212*0ab88e23SCryolitia PukNgae 		.matches = {
213*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
214*0ab88e23SCryolitia PukNgae 			DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04-L"),
215*0ab88e23SCryolitia PukNgae 		},
216*0ab88e23SCryolitia PukNgae 		.driver_data = &gpd_win_mini_drvdata,
217*0ab88e23SCryolitia PukNgae 	},
218*0ab88e23SCryolitia PukNgae 	{}
219*0ab88e23SCryolitia PukNgae };
220*0ab88e23SCryolitia PukNgae 
221*0ab88e23SCryolitia PukNgae static const struct gpd_fan_drvdata *gpd_module_drvdata[] = {
222*0ab88e23SCryolitia PukNgae 	&gpd_win_mini_drvdata, &gpd_win4_drvdata, &gpd_wm2_drvdata, NULL
223*0ab88e23SCryolitia PukNgae };
224*0ab88e23SCryolitia PukNgae 
225*0ab88e23SCryolitia PukNgae // Helper functions to handle EC read/write
226*0ab88e23SCryolitia PukNgae static void gpd_ecram_read(u16 offset, u8 *val)
227*0ab88e23SCryolitia PukNgae {
228*0ab88e23SCryolitia PukNgae 	u16 addr_port = gpd_driver_priv.drvdata->addr_port;
229*0ab88e23SCryolitia PukNgae 	u16 data_port = gpd_driver_priv.drvdata->data_port;
230*0ab88e23SCryolitia PukNgae 
231*0ab88e23SCryolitia PukNgae 	outb(0x2E, addr_port);
232*0ab88e23SCryolitia PukNgae 	outb(0x11, data_port);
233*0ab88e23SCryolitia PukNgae 	outb(0x2F, addr_port);
234*0ab88e23SCryolitia PukNgae 	outb((u8)((offset >> 8) & 0xFF), data_port);
235*0ab88e23SCryolitia PukNgae 
236*0ab88e23SCryolitia PukNgae 	outb(0x2E, addr_port);
237*0ab88e23SCryolitia PukNgae 	outb(0x10, data_port);
238*0ab88e23SCryolitia PukNgae 	outb(0x2F, addr_port);
239*0ab88e23SCryolitia PukNgae 	outb((u8)(offset & 0xFF), data_port);
240*0ab88e23SCryolitia PukNgae 
241*0ab88e23SCryolitia PukNgae 	outb(0x2E, addr_port);
242*0ab88e23SCryolitia PukNgae 	outb(0x12, data_port);
243*0ab88e23SCryolitia PukNgae 	outb(0x2F, addr_port);
244*0ab88e23SCryolitia PukNgae 	*val = inb(data_port);
245*0ab88e23SCryolitia PukNgae }
246*0ab88e23SCryolitia PukNgae 
247*0ab88e23SCryolitia PukNgae static void gpd_ecram_write(u16 offset, u8 value)
248*0ab88e23SCryolitia PukNgae {
249*0ab88e23SCryolitia PukNgae 	u16 addr_port = gpd_driver_priv.drvdata->addr_port;
250*0ab88e23SCryolitia PukNgae 	u16 data_port = gpd_driver_priv.drvdata->data_port;
251*0ab88e23SCryolitia PukNgae 
252*0ab88e23SCryolitia PukNgae 	outb(0x2E, addr_port);
253*0ab88e23SCryolitia PukNgae 	outb(0x11, data_port);
254*0ab88e23SCryolitia PukNgae 	outb(0x2F, addr_port);
255*0ab88e23SCryolitia PukNgae 	outb((u8)((offset >> 8) & 0xFF), data_port);
256*0ab88e23SCryolitia PukNgae 
257*0ab88e23SCryolitia PukNgae 	outb(0x2E, addr_port);
258*0ab88e23SCryolitia PukNgae 	outb(0x10, data_port);
259*0ab88e23SCryolitia PukNgae 	outb(0x2F, addr_port);
260*0ab88e23SCryolitia PukNgae 	outb((u8)(offset & 0xFF), data_port);
261*0ab88e23SCryolitia PukNgae 
262*0ab88e23SCryolitia PukNgae 	outb(0x2E, addr_port);
263*0ab88e23SCryolitia PukNgae 	outb(0x12, data_port);
264*0ab88e23SCryolitia PukNgae 	outb(0x2F, addr_port);
265*0ab88e23SCryolitia PukNgae 	outb(value, data_port);
266*0ab88e23SCryolitia PukNgae }
267*0ab88e23SCryolitia PukNgae 
268*0ab88e23SCryolitia PukNgae static int gpd_generic_read_rpm(void)
269*0ab88e23SCryolitia PukNgae {
270*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
271*0ab88e23SCryolitia PukNgae 	u8 high, low;
272*0ab88e23SCryolitia PukNgae 
273*0ab88e23SCryolitia PukNgae 	gpd_ecram_read(drvdata->rpm_read, &high);
274*0ab88e23SCryolitia PukNgae 	gpd_ecram_read(drvdata->rpm_read + 1, &low);
275*0ab88e23SCryolitia PukNgae 
276*0ab88e23SCryolitia PukNgae 	return (u16)high << 8 | low;
277*0ab88e23SCryolitia PukNgae }
278*0ab88e23SCryolitia PukNgae 
279*0ab88e23SCryolitia PukNgae static void gpd_win4_init_ec(void)
280*0ab88e23SCryolitia PukNgae {
281*0ab88e23SCryolitia PukNgae 	u8 chip_id, chip_ver;
282*0ab88e23SCryolitia PukNgae 
283*0ab88e23SCryolitia PukNgae 	gpd_ecram_read(0x2000, &chip_id);
284*0ab88e23SCryolitia PukNgae 
285*0ab88e23SCryolitia PukNgae 	if (chip_id == 0x55) {
286*0ab88e23SCryolitia PukNgae 		gpd_ecram_read(0x1060, &chip_ver);
287*0ab88e23SCryolitia PukNgae 		gpd_ecram_write(0x1060, chip_ver | 0x80);
288*0ab88e23SCryolitia PukNgae 	}
289*0ab88e23SCryolitia PukNgae }
290*0ab88e23SCryolitia PukNgae 
291*0ab88e23SCryolitia PukNgae static int gpd_win4_read_rpm(void)
292*0ab88e23SCryolitia PukNgae {
293*0ab88e23SCryolitia PukNgae 	int ret;
294*0ab88e23SCryolitia PukNgae 
295*0ab88e23SCryolitia PukNgae 	ret = gpd_generic_read_rpm();
296*0ab88e23SCryolitia PukNgae 
297*0ab88e23SCryolitia PukNgae 	if (ret == 0)
298*0ab88e23SCryolitia PukNgae 		// Re-init EC when speed is 0
299*0ab88e23SCryolitia PukNgae 		gpd_win4_init_ec();
300*0ab88e23SCryolitia PukNgae 
301*0ab88e23SCryolitia PukNgae 	return ret;
302*0ab88e23SCryolitia PukNgae }
303*0ab88e23SCryolitia PukNgae 
304*0ab88e23SCryolitia PukNgae static int gpd_wm2_read_rpm(void)
305*0ab88e23SCryolitia PukNgae {
306*0ab88e23SCryolitia PukNgae 	for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET;
307*0ab88e23SCryolitia PukNgae 	     pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) {
308*0ab88e23SCryolitia PukNgae 		u8 PWMCTR;
309*0ab88e23SCryolitia PukNgae 
310*0ab88e23SCryolitia PukNgae 		gpd_ecram_read(pwm_ctr_offset, &PWMCTR);
311*0ab88e23SCryolitia PukNgae 
312*0ab88e23SCryolitia PukNgae 		if (PWMCTR != 0xB8)
313*0ab88e23SCryolitia PukNgae 			gpd_ecram_write(pwm_ctr_offset, 0xB8);
314*0ab88e23SCryolitia PukNgae 	}
315*0ab88e23SCryolitia PukNgae 
316*0ab88e23SCryolitia PukNgae 	return gpd_generic_read_rpm();
317*0ab88e23SCryolitia PukNgae }
318*0ab88e23SCryolitia PukNgae 
319*0ab88e23SCryolitia PukNgae // Read value for fan1_input
320*0ab88e23SCryolitia PukNgae static int gpd_read_rpm(void)
321*0ab88e23SCryolitia PukNgae {
322*0ab88e23SCryolitia PukNgae 	switch (gpd_driver_priv.drvdata->board) {
323*0ab88e23SCryolitia PukNgae 	case win_mini:
324*0ab88e23SCryolitia PukNgae 	case duo:
325*0ab88e23SCryolitia PukNgae 		return gpd_generic_read_rpm();
326*0ab88e23SCryolitia PukNgae 	case win4_6800u:
327*0ab88e23SCryolitia PukNgae 		return gpd_win4_read_rpm();
328*0ab88e23SCryolitia PukNgae 	case win_max_2:
329*0ab88e23SCryolitia PukNgae 		return gpd_wm2_read_rpm();
330*0ab88e23SCryolitia PukNgae 	}
331*0ab88e23SCryolitia PukNgae 
332*0ab88e23SCryolitia PukNgae 	return 0;
333*0ab88e23SCryolitia PukNgae }
334*0ab88e23SCryolitia PukNgae 
335*0ab88e23SCryolitia PukNgae static int gpd_wm2_read_pwm(void)
336*0ab88e23SCryolitia PukNgae {
337*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
338*0ab88e23SCryolitia PukNgae 	u8 var;
339*0ab88e23SCryolitia PukNgae 
340*0ab88e23SCryolitia PukNgae 	gpd_ecram_read(drvdata->pwm_write, &var);
341*0ab88e23SCryolitia PukNgae 
342*0ab88e23SCryolitia PukNgae 	// Match gpd_generic_write_pwm(u8) below
343*0ab88e23SCryolitia PukNgae 	return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1));
344*0ab88e23SCryolitia PukNgae }
345*0ab88e23SCryolitia PukNgae 
346*0ab88e23SCryolitia PukNgae // Read value for pwm1
347*0ab88e23SCryolitia PukNgae static int gpd_read_pwm(void)
348*0ab88e23SCryolitia PukNgae {
349*0ab88e23SCryolitia PukNgae 	switch (gpd_driver_priv.drvdata->board) {
350*0ab88e23SCryolitia PukNgae 	case win_mini:
351*0ab88e23SCryolitia PukNgae 	case duo:
352*0ab88e23SCryolitia PukNgae 	case win4_6800u:
353*0ab88e23SCryolitia PukNgae 		switch (gpd_driver_priv.pwm_enable) {
354*0ab88e23SCryolitia PukNgae 		case DISABLE:
355*0ab88e23SCryolitia PukNgae 			return 255;
356*0ab88e23SCryolitia PukNgae 		case MANUAL:
357*0ab88e23SCryolitia PukNgae 			return gpd_driver_priv.pwm_value;
358*0ab88e23SCryolitia PukNgae 		case AUTOMATIC:
359*0ab88e23SCryolitia PukNgae 			return -EOPNOTSUPP;
360*0ab88e23SCryolitia PukNgae 		}
361*0ab88e23SCryolitia PukNgae 		break;
362*0ab88e23SCryolitia PukNgae 	case win_max_2:
363*0ab88e23SCryolitia PukNgae 		return gpd_wm2_read_pwm();
364*0ab88e23SCryolitia PukNgae 	}
365*0ab88e23SCryolitia PukNgae 	return 0;
366*0ab88e23SCryolitia PukNgae }
367*0ab88e23SCryolitia PukNgae 
368*0ab88e23SCryolitia PukNgae // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it.
369*0ab88e23SCryolitia PukNgae static inline u8 gpd_cast_pwm_range(u8 val)
370*0ab88e23SCryolitia PukNgae {
371*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
372*0ab88e23SCryolitia PukNgae 
373*0ab88e23SCryolitia PukNgae 	return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1;
374*0ab88e23SCryolitia PukNgae }
375*0ab88e23SCryolitia PukNgae 
376*0ab88e23SCryolitia PukNgae static void gpd_generic_write_pwm(u8 val)
377*0ab88e23SCryolitia PukNgae {
378*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
379*0ab88e23SCryolitia PukNgae 	u8 pwm_reg;
380*0ab88e23SCryolitia PukNgae 
381*0ab88e23SCryolitia PukNgae 	pwm_reg = gpd_cast_pwm_range(val);
382*0ab88e23SCryolitia PukNgae 	gpd_ecram_write(drvdata->pwm_write, pwm_reg);
383*0ab88e23SCryolitia PukNgae }
384*0ab88e23SCryolitia PukNgae 
385*0ab88e23SCryolitia PukNgae static void gpd_duo_write_pwm(u8 val)
386*0ab88e23SCryolitia PukNgae {
387*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
388*0ab88e23SCryolitia PukNgae 	u8 pwm_reg;
389*0ab88e23SCryolitia PukNgae 
390*0ab88e23SCryolitia PukNgae 	pwm_reg = gpd_cast_pwm_range(val);
391*0ab88e23SCryolitia PukNgae 	gpd_ecram_write(drvdata->pwm_write, pwm_reg);
392*0ab88e23SCryolitia PukNgae 	gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg);
393*0ab88e23SCryolitia PukNgae }
394*0ab88e23SCryolitia PukNgae 
395*0ab88e23SCryolitia PukNgae // Write value for pwm1
396*0ab88e23SCryolitia PukNgae static int gpd_write_pwm(u8 val)
397*0ab88e23SCryolitia PukNgae {
398*0ab88e23SCryolitia PukNgae 	if (gpd_driver_priv.pwm_enable != MANUAL)
399*0ab88e23SCryolitia PukNgae 		return -EPERM;
400*0ab88e23SCryolitia PukNgae 
401*0ab88e23SCryolitia PukNgae 	switch (gpd_driver_priv.drvdata->board) {
402*0ab88e23SCryolitia PukNgae 	case duo:
403*0ab88e23SCryolitia PukNgae 		gpd_duo_write_pwm(val);
404*0ab88e23SCryolitia PukNgae 		break;
405*0ab88e23SCryolitia PukNgae 	case win_mini:
406*0ab88e23SCryolitia PukNgae 	case win4_6800u:
407*0ab88e23SCryolitia PukNgae 	case win_max_2:
408*0ab88e23SCryolitia PukNgae 		gpd_generic_write_pwm(val);
409*0ab88e23SCryolitia PukNgae 		break;
410*0ab88e23SCryolitia PukNgae 	}
411*0ab88e23SCryolitia PukNgae 
412*0ab88e23SCryolitia PukNgae 	return 0;
413*0ab88e23SCryolitia PukNgae }
414*0ab88e23SCryolitia PukNgae 
415*0ab88e23SCryolitia PukNgae static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
416*0ab88e23SCryolitia PukNgae {
417*0ab88e23SCryolitia PukNgae 	switch (pwm_enable) {
418*0ab88e23SCryolitia PukNgae 	case DISABLE:
419*0ab88e23SCryolitia PukNgae 		gpd_generic_write_pwm(255);
420*0ab88e23SCryolitia PukNgae 		break;
421*0ab88e23SCryolitia PukNgae 	case MANUAL:
422*0ab88e23SCryolitia PukNgae 		gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
423*0ab88e23SCryolitia PukNgae 		break;
424*0ab88e23SCryolitia PukNgae 	case AUTOMATIC:
425*0ab88e23SCryolitia PukNgae 		gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0);
426*0ab88e23SCryolitia PukNgae 		break;
427*0ab88e23SCryolitia PukNgae 	}
428*0ab88e23SCryolitia PukNgae }
429*0ab88e23SCryolitia PukNgae 
430*0ab88e23SCryolitia PukNgae static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
431*0ab88e23SCryolitia PukNgae {
432*0ab88e23SCryolitia PukNgae 	switch (pwm_enable) {
433*0ab88e23SCryolitia PukNgae 	case DISABLE:
434*0ab88e23SCryolitia PukNgae 		gpd_duo_write_pwm(255);
435*0ab88e23SCryolitia PukNgae 		break;
436*0ab88e23SCryolitia PukNgae 	case MANUAL:
437*0ab88e23SCryolitia PukNgae 		gpd_duo_write_pwm(gpd_driver_priv.pwm_value);
438*0ab88e23SCryolitia PukNgae 		break;
439*0ab88e23SCryolitia PukNgae 	case AUTOMATIC:
440*0ab88e23SCryolitia PukNgae 		gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0);
441*0ab88e23SCryolitia PukNgae 		break;
442*0ab88e23SCryolitia PukNgae 	}
443*0ab88e23SCryolitia PukNgae }
444*0ab88e23SCryolitia PukNgae 
445*0ab88e23SCryolitia PukNgae static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
446*0ab88e23SCryolitia PukNgae {
447*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
448*0ab88e23SCryolitia PukNgae 
449*0ab88e23SCryolitia PukNgae 	switch (enable) {
450*0ab88e23SCryolitia PukNgae 	case DISABLE:
451*0ab88e23SCryolitia PukNgae 		gpd_generic_write_pwm(255);
452*0ab88e23SCryolitia PukNgae 		gpd_ecram_write(drvdata->manual_control_enable, 1);
453*0ab88e23SCryolitia PukNgae 		break;
454*0ab88e23SCryolitia PukNgae 	case MANUAL:
455*0ab88e23SCryolitia PukNgae 		gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
456*0ab88e23SCryolitia PukNgae 		gpd_ecram_write(drvdata->manual_control_enable, 1);
457*0ab88e23SCryolitia PukNgae 		break;
458*0ab88e23SCryolitia PukNgae 	case AUTOMATIC:
459*0ab88e23SCryolitia PukNgae 		gpd_ecram_write(drvdata->manual_control_enable, 0);
460*0ab88e23SCryolitia PukNgae 		break;
461*0ab88e23SCryolitia PukNgae 	}
462*0ab88e23SCryolitia PukNgae }
463*0ab88e23SCryolitia PukNgae 
464*0ab88e23SCryolitia PukNgae // Write value for pwm1_enable
465*0ab88e23SCryolitia PukNgae static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
466*0ab88e23SCryolitia PukNgae {
467*0ab88e23SCryolitia PukNgae 	if (enable == MANUAL)
468*0ab88e23SCryolitia PukNgae 		// Set pwm_value to max firstly when switching to manual mode, in
469*0ab88e23SCryolitia PukNgae 		// consideration of device safety.
470*0ab88e23SCryolitia PukNgae 		gpd_driver_priv.pwm_value = 255;
471*0ab88e23SCryolitia PukNgae 
472*0ab88e23SCryolitia PukNgae 	switch (gpd_driver_priv.drvdata->board) {
473*0ab88e23SCryolitia PukNgae 	case win_mini:
474*0ab88e23SCryolitia PukNgae 	case win4_6800u:
475*0ab88e23SCryolitia PukNgae 		gpd_win_mini_set_pwm_enable(enable);
476*0ab88e23SCryolitia PukNgae 		break;
477*0ab88e23SCryolitia PukNgae 	case duo:
478*0ab88e23SCryolitia PukNgae 		gpd_duo_set_pwm_enable(enable);
479*0ab88e23SCryolitia PukNgae 		break;
480*0ab88e23SCryolitia PukNgae 	case win_max_2:
481*0ab88e23SCryolitia PukNgae 		gpd_wm2_set_pwm_enable(enable);
482*0ab88e23SCryolitia PukNgae 		break;
483*0ab88e23SCryolitia PukNgae 	}
484*0ab88e23SCryolitia PukNgae }
485*0ab88e23SCryolitia PukNgae 
486*0ab88e23SCryolitia PukNgae static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
487*0ab88e23SCryolitia PukNgae 					enum hwmon_sensor_types type, u32 attr,
488*0ab88e23SCryolitia PukNgae 					__always_unused int channel)
489*0ab88e23SCryolitia PukNgae {
490*0ab88e23SCryolitia PukNgae 	if (type == hwmon_fan && attr == hwmon_fan_input) {
491*0ab88e23SCryolitia PukNgae 		return 0444;
492*0ab88e23SCryolitia PukNgae 	} else if (type == hwmon_pwm) {
493*0ab88e23SCryolitia PukNgae 		switch (attr) {
494*0ab88e23SCryolitia PukNgae 		case hwmon_pwm_enable:
495*0ab88e23SCryolitia PukNgae 		case hwmon_pwm_input:
496*0ab88e23SCryolitia PukNgae 			return 0644;
497*0ab88e23SCryolitia PukNgae 		default:
498*0ab88e23SCryolitia PukNgae 			return 0;
499*0ab88e23SCryolitia PukNgae 		}
500*0ab88e23SCryolitia PukNgae 	}
501*0ab88e23SCryolitia PukNgae 	return 0;
502*0ab88e23SCryolitia PukNgae }
503*0ab88e23SCryolitia PukNgae 
504*0ab88e23SCryolitia PukNgae static int gpd_fan_hwmon_read(__always_unused struct device *dev,
505*0ab88e23SCryolitia PukNgae 			      enum hwmon_sensor_types type, u32 attr,
506*0ab88e23SCryolitia PukNgae 			      __always_unused int channel, long *val)
507*0ab88e23SCryolitia PukNgae {
508*0ab88e23SCryolitia PukNgae 	int ret;
509*0ab88e23SCryolitia PukNgae 
510*0ab88e23SCryolitia PukNgae 	ret = mutex_lock_interruptible(&gpd_fan_sequence_lock);
511*0ab88e23SCryolitia PukNgae 	if (ret)
512*0ab88e23SCryolitia PukNgae 		return ret;
513*0ab88e23SCryolitia PukNgae 
514*0ab88e23SCryolitia PukNgae 	if (type == hwmon_fan) {
515*0ab88e23SCryolitia PukNgae 		if (attr == hwmon_fan_input) {
516*0ab88e23SCryolitia PukNgae 			ret = gpd_read_rpm();
517*0ab88e23SCryolitia PukNgae 
518*0ab88e23SCryolitia PukNgae 			if (ret < 0)
519*0ab88e23SCryolitia PukNgae 				goto OUT;
520*0ab88e23SCryolitia PukNgae 
521*0ab88e23SCryolitia PukNgae 			*val = ret;
522*0ab88e23SCryolitia PukNgae 			ret = 0;
523*0ab88e23SCryolitia PukNgae 			goto OUT;
524*0ab88e23SCryolitia PukNgae 		}
525*0ab88e23SCryolitia PukNgae 	} else if (type == hwmon_pwm) {
526*0ab88e23SCryolitia PukNgae 		switch (attr) {
527*0ab88e23SCryolitia PukNgae 		case hwmon_pwm_enable:
528*0ab88e23SCryolitia PukNgae 			*val = gpd_driver_priv.pwm_enable;
529*0ab88e23SCryolitia PukNgae 			ret = 0;
530*0ab88e23SCryolitia PukNgae 			goto OUT;
531*0ab88e23SCryolitia PukNgae 		case hwmon_pwm_input:
532*0ab88e23SCryolitia PukNgae 			ret = gpd_read_pwm();
533*0ab88e23SCryolitia PukNgae 
534*0ab88e23SCryolitia PukNgae 			if (ret < 0)
535*0ab88e23SCryolitia PukNgae 				goto OUT;
536*0ab88e23SCryolitia PukNgae 
537*0ab88e23SCryolitia PukNgae 			*val = ret;
538*0ab88e23SCryolitia PukNgae 			ret = 0;
539*0ab88e23SCryolitia PukNgae 			goto OUT;
540*0ab88e23SCryolitia PukNgae 		}
541*0ab88e23SCryolitia PukNgae 	}
542*0ab88e23SCryolitia PukNgae 
543*0ab88e23SCryolitia PukNgae 	ret = -EOPNOTSUPP;
544*0ab88e23SCryolitia PukNgae 
545*0ab88e23SCryolitia PukNgae OUT:
546*0ab88e23SCryolitia PukNgae 	mutex_unlock(&gpd_fan_sequence_lock);
547*0ab88e23SCryolitia PukNgae 	return ret;
548*0ab88e23SCryolitia PukNgae }
549*0ab88e23SCryolitia PukNgae 
550*0ab88e23SCryolitia PukNgae static int gpd_fan_hwmon_write(__always_unused struct device *dev,
551*0ab88e23SCryolitia PukNgae 			       enum hwmon_sensor_types type, u32 attr,
552*0ab88e23SCryolitia PukNgae 			       __always_unused int channel, long val)
553*0ab88e23SCryolitia PukNgae {
554*0ab88e23SCryolitia PukNgae 	int ret;
555*0ab88e23SCryolitia PukNgae 
556*0ab88e23SCryolitia PukNgae 	ret = mutex_lock_interruptible(&gpd_fan_sequence_lock);
557*0ab88e23SCryolitia PukNgae 	if (ret)
558*0ab88e23SCryolitia PukNgae 		return ret;
559*0ab88e23SCryolitia PukNgae 
560*0ab88e23SCryolitia PukNgae 	if (type == hwmon_pwm) {
561*0ab88e23SCryolitia PukNgae 		switch (attr) {
562*0ab88e23SCryolitia PukNgae 		case hwmon_pwm_enable:
563*0ab88e23SCryolitia PukNgae 			if (!in_range(val, 0, 3)) {
564*0ab88e23SCryolitia PukNgae 				ret = -EINVAL;
565*0ab88e23SCryolitia PukNgae 				goto OUT;
566*0ab88e23SCryolitia PukNgae 			}
567*0ab88e23SCryolitia PukNgae 
568*0ab88e23SCryolitia PukNgae 			gpd_driver_priv.pwm_enable = val;
569*0ab88e23SCryolitia PukNgae 
570*0ab88e23SCryolitia PukNgae 			gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
571*0ab88e23SCryolitia PukNgae 			ret = 0;
572*0ab88e23SCryolitia PukNgae 			goto OUT;
573*0ab88e23SCryolitia PukNgae 		case hwmon_pwm_input:
574*0ab88e23SCryolitia PukNgae 			if (!in_range(val, 0, 255)) {
575*0ab88e23SCryolitia PukNgae 				ret = -ERANGE;
576*0ab88e23SCryolitia PukNgae 				goto OUT;
577*0ab88e23SCryolitia PukNgae 			}
578*0ab88e23SCryolitia PukNgae 
579*0ab88e23SCryolitia PukNgae 			gpd_driver_priv.pwm_value = val;
580*0ab88e23SCryolitia PukNgae 
581*0ab88e23SCryolitia PukNgae 			ret = gpd_write_pwm(val);
582*0ab88e23SCryolitia PukNgae 			goto OUT;
583*0ab88e23SCryolitia PukNgae 		}
584*0ab88e23SCryolitia PukNgae 	}
585*0ab88e23SCryolitia PukNgae 
586*0ab88e23SCryolitia PukNgae 	ret = -EOPNOTSUPP;
587*0ab88e23SCryolitia PukNgae 
588*0ab88e23SCryolitia PukNgae OUT:
589*0ab88e23SCryolitia PukNgae 	mutex_unlock(&gpd_fan_sequence_lock);
590*0ab88e23SCryolitia PukNgae 	return ret;
591*0ab88e23SCryolitia PukNgae }
592*0ab88e23SCryolitia PukNgae 
593*0ab88e23SCryolitia PukNgae static const struct hwmon_ops gpd_fan_ops = {
594*0ab88e23SCryolitia PukNgae 	.is_visible = gpd_fan_hwmon_is_visible,
595*0ab88e23SCryolitia PukNgae 	.read = gpd_fan_hwmon_read,
596*0ab88e23SCryolitia PukNgae 	.write = gpd_fan_hwmon_write,
597*0ab88e23SCryolitia PukNgae };
598*0ab88e23SCryolitia PukNgae 
599*0ab88e23SCryolitia PukNgae static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = {
600*0ab88e23SCryolitia PukNgae 	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
601*0ab88e23SCryolitia PukNgae 	HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
602*0ab88e23SCryolitia PukNgae 	NULL
603*0ab88e23SCryolitia PukNgae };
604*0ab88e23SCryolitia PukNgae 
605*0ab88e23SCryolitia PukNgae static struct hwmon_chip_info gpd_fan_chip_info = {
606*0ab88e23SCryolitia PukNgae 	.ops = &gpd_fan_ops,
607*0ab88e23SCryolitia PukNgae 	.info = gpd_fan_hwmon_channel_info
608*0ab88e23SCryolitia PukNgae };
609*0ab88e23SCryolitia PukNgae 
610*0ab88e23SCryolitia PukNgae static int gpd_fan_probe(struct platform_device *pdev)
611*0ab88e23SCryolitia PukNgae {
612*0ab88e23SCryolitia PukNgae 	struct device *dev = &pdev->dev;
613*0ab88e23SCryolitia PukNgae 	const struct resource *region;
614*0ab88e23SCryolitia PukNgae 	const struct resource *res;
615*0ab88e23SCryolitia PukNgae 	const struct device *hwdev;
616*0ab88e23SCryolitia PukNgae 
617*0ab88e23SCryolitia PukNgae 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
618*0ab88e23SCryolitia PukNgae 	if (IS_ERR(res))
619*0ab88e23SCryolitia PukNgae 		return dev_err_probe(dev, PTR_ERR(res),
620*0ab88e23SCryolitia PukNgae 				     "Failed to get platform resource\n");
621*0ab88e23SCryolitia PukNgae 
622*0ab88e23SCryolitia PukNgae 	region = devm_request_region(dev, res->start,
623*0ab88e23SCryolitia PukNgae 				     resource_size(res), DRIVER_NAME);
624*0ab88e23SCryolitia PukNgae 	if (IS_ERR(region))
625*0ab88e23SCryolitia PukNgae 		return dev_err_probe(dev, PTR_ERR(region),
626*0ab88e23SCryolitia PukNgae 				     "Failed to request region\n");
627*0ab88e23SCryolitia PukNgae 
628*0ab88e23SCryolitia PukNgae 	hwdev = devm_hwmon_device_register_with_info(dev,
629*0ab88e23SCryolitia PukNgae 						     DRIVER_NAME,
630*0ab88e23SCryolitia PukNgae 						     NULL,
631*0ab88e23SCryolitia PukNgae 						     &gpd_fan_chip_info,
632*0ab88e23SCryolitia PukNgae 						     NULL);
633*0ab88e23SCryolitia PukNgae 	if (IS_ERR(hwdev))
634*0ab88e23SCryolitia PukNgae 		return dev_err_probe(dev, PTR_ERR(region),
635*0ab88e23SCryolitia PukNgae 				     "Failed to register hwmon device\n");
636*0ab88e23SCryolitia PukNgae 
637*0ab88e23SCryolitia PukNgae 	return 0;
638*0ab88e23SCryolitia PukNgae }
639*0ab88e23SCryolitia PukNgae 
640*0ab88e23SCryolitia PukNgae static void gpd_fan_remove(__always_unused struct platform_device *pdev)
641*0ab88e23SCryolitia PukNgae {
642*0ab88e23SCryolitia PukNgae 	gpd_driver_priv.pwm_enable = AUTOMATIC;
643*0ab88e23SCryolitia PukNgae 	gpd_set_pwm_enable(AUTOMATIC);
644*0ab88e23SCryolitia PukNgae }
645*0ab88e23SCryolitia PukNgae 
646*0ab88e23SCryolitia PukNgae static struct platform_driver gpd_fan_driver = {
647*0ab88e23SCryolitia PukNgae 	.probe = gpd_fan_probe,
648*0ab88e23SCryolitia PukNgae 	.remove = gpd_fan_remove,
649*0ab88e23SCryolitia PukNgae 	.driver = {
650*0ab88e23SCryolitia PukNgae 		.name = KBUILD_MODNAME,
651*0ab88e23SCryolitia PukNgae 	},
652*0ab88e23SCryolitia PukNgae };
653*0ab88e23SCryolitia PukNgae 
654*0ab88e23SCryolitia PukNgae static struct platform_device *gpd_fan_platform_device;
655*0ab88e23SCryolitia PukNgae 
656*0ab88e23SCryolitia PukNgae static int __init gpd_fan_init(void)
657*0ab88e23SCryolitia PukNgae {
658*0ab88e23SCryolitia PukNgae 	const struct gpd_fan_drvdata *match = NULL;
659*0ab88e23SCryolitia PukNgae 
660*0ab88e23SCryolitia PukNgae 	for (const struct gpd_fan_drvdata **p = gpd_module_drvdata; *p; p++) {
661*0ab88e23SCryolitia PukNgae 		if (strcmp(gpd_fan_board, (*p)->board_name) == 0) {
662*0ab88e23SCryolitia PukNgae 			match = *p;
663*0ab88e23SCryolitia PukNgae 			break;
664*0ab88e23SCryolitia PukNgae 		}
665*0ab88e23SCryolitia PukNgae 	}
666*0ab88e23SCryolitia PukNgae 
667*0ab88e23SCryolitia PukNgae 	if (!match) {
668*0ab88e23SCryolitia PukNgae 		const struct dmi_system_id *dmi_match =
669*0ab88e23SCryolitia PukNgae 			dmi_first_match(dmi_table);
670*0ab88e23SCryolitia PukNgae 		if (dmi_match)
671*0ab88e23SCryolitia PukNgae 			match = dmi_match->driver_data;
672*0ab88e23SCryolitia PukNgae 	}
673*0ab88e23SCryolitia PukNgae 
674*0ab88e23SCryolitia PukNgae 	if (!match)
675*0ab88e23SCryolitia PukNgae 		return -ENODEV;
676*0ab88e23SCryolitia PukNgae 
677*0ab88e23SCryolitia PukNgae 	gpd_driver_priv.pwm_enable = AUTOMATIC;
678*0ab88e23SCryolitia PukNgae 	gpd_driver_priv.pwm_value = 255;
679*0ab88e23SCryolitia PukNgae 	gpd_driver_priv.drvdata = match;
680*0ab88e23SCryolitia PukNgae 
681*0ab88e23SCryolitia PukNgae 	struct resource gpd_fan_resources[] = {
682*0ab88e23SCryolitia PukNgae 		{
683*0ab88e23SCryolitia PukNgae 			.start = match->addr_port,
684*0ab88e23SCryolitia PukNgae 			.end = match->data_port,
685*0ab88e23SCryolitia PukNgae 			.flags = IORESOURCE_IO,
686*0ab88e23SCryolitia PukNgae 		},
687*0ab88e23SCryolitia PukNgae 	};
688*0ab88e23SCryolitia PukNgae 
689*0ab88e23SCryolitia PukNgae 	gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver,
690*0ab88e23SCryolitia PukNgae 							 gpd_fan_probe,
691*0ab88e23SCryolitia PukNgae 							 gpd_fan_resources,
692*0ab88e23SCryolitia PukNgae 							 1, NULL, 0);
693*0ab88e23SCryolitia PukNgae 
694*0ab88e23SCryolitia PukNgae 	if (IS_ERR(gpd_fan_platform_device)) {
695*0ab88e23SCryolitia PukNgae 		pr_warn("Failed to create platform device\n");
696*0ab88e23SCryolitia PukNgae 		return PTR_ERR(gpd_fan_platform_device);
697*0ab88e23SCryolitia PukNgae 	}
698*0ab88e23SCryolitia PukNgae 
699*0ab88e23SCryolitia PukNgae 	return 0;
700*0ab88e23SCryolitia PukNgae }
701*0ab88e23SCryolitia PukNgae 
702*0ab88e23SCryolitia PukNgae static void __exit gpd_fan_exit(void)
703*0ab88e23SCryolitia PukNgae {
704*0ab88e23SCryolitia PukNgae 	platform_device_unregister(gpd_fan_platform_device);
705*0ab88e23SCryolitia PukNgae 	platform_driver_unregister(&gpd_fan_driver);
706*0ab88e23SCryolitia PukNgae }
707*0ab88e23SCryolitia PukNgae 
708*0ab88e23SCryolitia PukNgae MODULE_DEVICE_TABLE(dmi, dmi_table);
709*0ab88e23SCryolitia PukNgae 
710*0ab88e23SCryolitia PukNgae module_init(gpd_fan_init);
711*0ab88e23SCryolitia PukNgae module_exit(gpd_fan_exit);
712*0ab88e23SCryolitia PukNgae 
713*0ab88e23SCryolitia PukNgae MODULE_LICENSE("GPL");
714*0ab88e23SCryolitia PukNgae MODULE_AUTHOR("Cryolitia PukNgae <cryolitia@uniontech.com>");
715*0ab88e23SCryolitia PukNgae MODULE_DESCRIPTION("GPD Devices fan control driver");
716