1 // SPDX-License-Identifier: GPL-2.0+
3 * GPD Pocket fan controller driver
5 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
8 #include <linux/acpi.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/module.h>
11 #include <linux/moduleparam.h>
12 #include <linux/platform_device.h>
13 #include <linux/power_supply.h>
14 #include <linux/thermal.h>
15 #include <linux/workqueue.h>
19 static int temp_limits
[3] = { 55000, 60000, 65000 };
20 module_param_array(temp_limits
, int, NULL
, 0444);
21 MODULE_PARM_DESC(temp_limits
,
22 "Milli-celcius values above which the fan speed increases");
24 static int hysteresis
= 3000;
25 module_param(hysteresis
, int, 0444);
26 MODULE_PARM_DESC(hysteresis
,
27 "Hysteresis in milli-celcius before lowering the fan speed");
29 static int speed_on_ac
= 2;
30 module_param(speed_on_ac
, int, 0444);
31 MODULE_PARM_DESC(speed_on_ac
,
32 "minimum fan speed to allow when system is powered by AC");
34 struct gpd_pocket_fan_data
{
36 struct thermal_zone_device
*dts0
;
37 struct thermal_zone_device
*dts1
;
38 struct gpio_desc
*gpio0
;
39 struct gpio_desc
*gpio1
;
40 struct delayed_work work
;
44 static void gpd_pocket_fan_set_speed(struct gpd_pocket_fan_data
*fan
, int speed
)
46 if (speed
== fan
->last_speed
)
49 gpiod_direction_output(fan
->gpio0
, !!(speed
& 1));
50 gpiod_direction_output(fan
->gpio1
, !!(speed
& 2));
52 fan
->last_speed
= speed
;
55 static int gpd_pocket_fan_min_speed(void)
57 if (power_supply_is_system_supplied())
63 static void gpd_pocket_fan_worker(struct work_struct
*work
)
65 struct gpd_pocket_fan_data
*fan
=
66 container_of(work
, struct gpd_pocket_fan_data
, work
.work
);
67 int t0
, t1
, temp
, speed
, min_speed
, i
;
69 if (thermal_zone_get_temp(fan
->dts0
, &t0
) ||
70 thermal_zone_get_temp(fan
->dts1
, &t1
)) {
71 dev_warn(fan
->dev
, "Error getting temperature\n");
78 speed
= fan
->last_speed
;
79 min_speed
= gpd_pocket_fan_min_speed();
81 /* Determine minimum speed */
82 for (i
= min_speed
; i
< ARRAY_SIZE(temp_limits
); i
++) {
83 if (temp
< temp_limits
[i
])
89 /* Use hysteresis before lowering speed again */
90 for (i
= min_speed
; i
< ARRAY_SIZE(temp_limits
); i
++) {
91 if (temp
<= (temp_limits
[i
] - hysteresis
))
97 if (fan
->last_speed
<= 0 && speed
)
98 speed
= MAX_SPEED
; /* kick start motor */
101 gpd_pocket_fan_set_speed(fan
, speed
);
103 /* When mostly idle (low temp/speed), slow down the poll interval. */
104 queue_delayed_work(system_wq
, &fan
->work
,
105 msecs_to_jiffies(4000 / (speed
+ 1)));
108 static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data
*fan
)
110 fan
->last_speed
= -1;
111 mod_delayed_work(system_wq
, &fan
->work
, 0);
114 static int gpd_pocket_fan_probe(struct platform_device
*pdev
)
116 struct gpd_pocket_fan_data
*fan
;
119 for (i
= 0; i
< ARRAY_SIZE(temp_limits
); i
++) {
120 if (temp_limits
[i
] < 40000 || temp_limits
[i
] > 70000) {
121 dev_err(&pdev
->dev
, "Invalid temp-limit %d (must be between 40000 and 70000)\n",
126 if (hysteresis
< 1000 || hysteresis
> 10000) {
127 dev_err(&pdev
->dev
, "Invalid hysteresis %d (must be between 1000 and 10000)\n",
131 if (speed_on_ac
< 0 || speed_on_ac
> MAX_SPEED
) {
132 dev_err(&pdev
->dev
, "Invalid speed_on_ac %d (must be between 0 and 3)\n",
137 fan
= devm_kzalloc(&pdev
->dev
, sizeof(*fan
), GFP_KERNEL
);
141 fan
->dev
= &pdev
->dev
;
142 INIT_DELAYED_WORK(&fan
->work
, gpd_pocket_fan_worker
);
144 /* Note this returns a "weak" reference which we don't need to free */
145 fan
->dts0
= thermal_zone_get_zone_by_name("soc_dts0");
146 if (IS_ERR(fan
->dts0
))
147 return -EPROBE_DEFER
;
149 fan
->dts1
= thermal_zone_get_zone_by_name("soc_dts1");
150 if (IS_ERR(fan
->dts1
))
151 return -EPROBE_DEFER
;
153 fan
->gpio0
= devm_gpiod_get_index(fan
->dev
, NULL
, 0, GPIOD_ASIS
);
154 if (IS_ERR(fan
->gpio0
))
155 return PTR_ERR(fan
->gpio0
);
157 fan
->gpio1
= devm_gpiod_get_index(fan
->dev
, NULL
, 1, GPIOD_ASIS
);
158 if (IS_ERR(fan
->gpio1
))
159 return PTR_ERR(fan
->gpio1
);
161 gpd_pocket_fan_force_update(fan
);
163 platform_set_drvdata(pdev
, fan
);
167 static int gpd_pocket_fan_remove(struct platform_device
*pdev
)
169 struct gpd_pocket_fan_data
*fan
= platform_get_drvdata(pdev
);
171 cancel_delayed_work_sync(&fan
->work
);
175 #ifdef CONFIG_PM_SLEEP
176 static int gpd_pocket_fan_suspend(struct device
*dev
)
178 struct gpd_pocket_fan_data
*fan
= dev_get_drvdata(dev
);
180 cancel_delayed_work_sync(&fan
->work
);
181 gpd_pocket_fan_set_speed(fan
, gpd_pocket_fan_min_speed());
185 static int gpd_pocket_fan_resume(struct device
*dev
)
187 struct gpd_pocket_fan_data
*fan
= dev_get_drvdata(dev
);
189 gpd_pocket_fan_force_update(fan
);
193 static SIMPLE_DEV_PM_OPS(gpd_pocket_fan_pm_ops
,
194 gpd_pocket_fan_suspend
,
195 gpd_pocket_fan_resume
);
197 static struct acpi_device_id gpd_pocket_fan_acpi_match
[] = {
201 MODULE_DEVICE_TABLE(acpi
, gpd_pocket_fan_acpi_match
);
203 static struct platform_driver gpd_pocket_fan_driver
= {
204 .probe
= gpd_pocket_fan_probe
,
205 .remove
= gpd_pocket_fan_remove
,
207 .name
= "gpd_pocket_fan",
208 .acpi_match_table
= gpd_pocket_fan_acpi_match
,
209 .pm
= &gpd_pocket_fan_pm_ops
,
213 module_platform_driver(gpd_pocket_fan_driver
);
214 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
215 MODULE_DESCRIPTION("GPD pocket fan driver");
216 MODULE_LICENSE("GPL");