162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * gpio-fan.c - Hwmon driver for fans connected to GPIO lines.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 LaCie
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Simon Guinot <sguinot@lacie.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/kstrtox.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/hwmon.h>
2062306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_platform.h>
2362306a36Sopenharmony_ci#include <linux/thermal.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct gpio_fan_speed {
2662306a36Sopenharmony_ci	int rpm;
2762306a36Sopenharmony_ci	int ctrl_val;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct gpio_fan_data {
3162306a36Sopenharmony_ci	struct device		*dev;
3262306a36Sopenharmony_ci	struct device		*hwmon_dev;
3362306a36Sopenharmony_ci	/* Cooling device if any */
3462306a36Sopenharmony_ci	struct thermal_cooling_device *cdev;
3562306a36Sopenharmony_ci	struct mutex		lock; /* lock GPIOs operations. */
3662306a36Sopenharmony_ci	int			num_gpios;
3762306a36Sopenharmony_ci	struct gpio_desc	**gpios;
3862306a36Sopenharmony_ci	int			num_speed;
3962306a36Sopenharmony_ci	struct gpio_fan_speed	*speed;
4062306a36Sopenharmony_ci	int			speed_index;
4162306a36Sopenharmony_ci	int			resume_speed;
4262306a36Sopenharmony_ci	bool			pwm_enable;
4362306a36Sopenharmony_ci	struct gpio_desc	*alarm_gpio;
4462306a36Sopenharmony_ci	struct work_struct	alarm_work;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Alarm GPIO.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void fan_alarm_notify(struct work_struct *ws)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct gpio_fan_data *fan_data =
5462306a36Sopenharmony_ci		container_of(ws, struct gpio_fan_data, alarm_work);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	sysfs_notify(&fan_data->hwmon_dev->kobj, NULL, "fan1_alarm");
5762306a36Sopenharmony_ci	kobject_uevent(&fan_data->hwmon_dev->kobj, KOBJ_CHANGE);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_id;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	schedule_work(&fan_data->alarm_work);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return IRQ_NONE;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic ssize_t fan1_alarm_show(struct device *dev,
7062306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return sprintf(buf, "%d\n",
7562306a36Sopenharmony_ci		       gpiod_get_value_cansleep(fan_data->alarm_gpio));
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_alarm);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int fan_alarm_init(struct gpio_fan_data *fan_data)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	int alarm_irq;
8362306a36Sopenharmony_ci	struct device *dev = fan_data->dev;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * If the alarm GPIO don't support interrupts, just leave
8762306a36Sopenharmony_ci	 * without initializing the fail notification support.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	alarm_irq = gpiod_to_irq(fan_data->alarm_gpio);
9062306a36Sopenharmony_ci	if (alarm_irq <= 0)
9162306a36Sopenharmony_ci		return 0;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
9462306a36Sopenharmony_ci	irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
9562306a36Sopenharmony_ci	return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
9662306a36Sopenharmony_ci				IRQF_SHARED, "GPIO fan alarm", fan_data);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Control GPIOs.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* Must be called with fan_data->lock held, except during initialization. */
10462306a36Sopenharmony_cistatic void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int i;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (i = 0; i < fan_data->num_gpios; i++)
10962306a36Sopenharmony_ci		gpiod_set_value_cansleep(fan_data->gpios[i],
11062306a36Sopenharmony_ci					 (ctrl_val >> i) & 1);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int __get_fan_ctrl(struct gpio_fan_data *fan_data)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci	int ctrl_val = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	for (i = 0; i < fan_data->num_gpios; i++) {
11962306a36Sopenharmony_ci		int value;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		value = gpiod_get_value_cansleep(fan_data->gpios[i]);
12262306a36Sopenharmony_ci		ctrl_val |= (value << i);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	return ctrl_val;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* Must be called with fan_data->lock held, except during initialization. */
12862306a36Sopenharmony_cistatic void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (fan_data->speed_index == speed_index)
13162306a36Sopenharmony_ci		return;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	__set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
13462306a36Sopenharmony_ci	fan_data->speed_index = speed_index;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int get_fan_speed_index(struct gpio_fan_data *fan_data)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	int ctrl_val = __get_fan_ctrl(fan_data);
14062306a36Sopenharmony_ci	int i;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	for (i = 0; i < fan_data->num_speed; i++)
14362306a36Sopenharmony_ci		if (fan_data->speed[i].ctrl_val == ctrl_val)
14462306a36Sopenharmony_ci			return i;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	dev_warn(fan_data->dev,
14762306a36Sopenharmony_ci		 "missing speed array entry for GPIO value 0x%x\n", ctrl_val);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return -ENODEV;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int rpm_to_speed_index(struct gpio_fan_data *fan_data, unsigned long rpm)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct gpio_fan_speed *speed = fan_data->speed;
15562306a36Sopenharmony_ci	int i;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	for (i = 0; i < fan_data->num_speed; i++)
15862306a36Sopenharmony_ci		if (speed[i].rpm >= rpm)
15962306a36Sopenharmony_ci			return i;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return fan_data->num_speed - 1;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
16562306a36Sopenharmony_ci			 char *buf)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
16862306a36Sopenharmony_ci	u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return sprintf(buf, "%d\n", pwm);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
17462306a36Sopenharmony_ci			  const char *buf, size_t count)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
17762306a36Sopenharmony_ci	unsigned long pwm;
17862306a36Sopenharmony_ci	int speed_index;
17962306a36Sopenharmony_ci	int ret = count;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (kstrtoul(buf, 10, &pwm) || pwm > 255)
18262306a36Sopenharmony_ci		return -EINVAL;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	mutex_lock(&fan_data->lock);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (!fan_data->pwm_enable) {
18762306a36Sopenharmony_ci		ret = -EPERM;
18862306a36Sopenharmony_ci		goto exit_unlock;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
19262306a36Sopenharmony_ci	set_fan_speed(fan_data, speed_index);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciexit_unlock:
19562306a36Sopenharmony_ci	mutex_unlock(&fan_data->lock);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return ret;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic ssize_t pwm1_enable_show(struct device *dev,
20162306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return sprintf(buf, "%d\n", fan_data->pwm_enable);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev,
20962306a36Sopenharmony_ci				 struct device_attribute *attr,
21062306a36Sopenharmony_ci				 const char *buf, size_t count)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
21362306a36Sopenharmony_ci	unsigned long val;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (kstrtoul(buf, 10, &val) || val > 1)
21662306a36Sopenharmony_ci		return -EINVAL;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (fan_data->pwm_enable == val)
21962306a36Sopenharmony_ci		return count;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	mutex_lock(&fan_data->lock);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	fan_data->pwm_enable = val;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Disable manual control mode: set fan at full speed. */
22662306a36Sopenharmony_ci	if (val == 0)
22762306a36Sopenharmony_ci		set_fan_speed(fan_data, fan_data->num_speed - 1);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	mutex_unlock(&fan_data->lock);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return count;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic ssize_t pwm1_mode_show(struct device *dev,
23562306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	return sprintf(buf, "0\n");
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic ssize_t fan1_min_show(struct device *dev,
24162306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return sprintf(buf, "%d\n", fan_data->speed[0].rpm);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic ssize_t fan1_max_show(struct device *dev,
24962306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return sprintf(buf, "%d\n",
25462306a36Sopenharmony_ci		       fan_data->speed[fan_data->num_speed - 1].rpm);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic ssize_t fan1_input_show(struct device *dev,
25862306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
26662306a36Sopenharmony_ci		       const char *buf, size_t count)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
26962306a36Sopenharmony_ci	unsigned long rpm;
27062306a36Sopenharmony_ci	int ret = count;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (kstrtoul(buf, 10, &rpm))
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	mutex_lock(&fan_data->lock);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!fan_data->pwm_enable) {
27862306a36Sopenharmony_ci		ret = -EPERM;
27962306a36Sopenharmony_ci		goto exit_unlock;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ciexit_unlock:
28562306a36Sopenharmony_ci	mutex_unlock(&fan_data->lock);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return ret;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1);
29162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1_enable);
29262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(pwm1_mode);
29362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_min);
29462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_max);
29562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_input);
29662306a36Sopenharmony_cistatic DEVICE_ATTR(fan1_target, 0644, fan1_input_show, set_rpm);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic umode_t gpio_fan_is_visible(struct kobject *kobj,
29962306a36Sopenharmony_ci				   struct attribute *attr, int index)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
30262306a36Sopenharmony_ci	struct gpio_fan_data *data = dev_get_drvdata(dev);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (index == 0 && !data->alarm_gpio)
30562306a36Sopenharmony_ci		return 0;
30662306a36Sopenharmony_ci	if (index > 0 && !data->gpios)
30762306a36Sopenharmony_ci		return 0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return attr->mode;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic struct attribute *gpio_fan_attributes[] = {
31362306a36Sopenharmony_ci	&dev_attr_fan1_alarm.attr,		/* 0 */
31462306a36Sopenharmony_ci	&dev_attr_pwm1.attr,			/* 1 */
31562306a36Sopenharmony_ci	&dev_attr_pwm1_enable.attr,
31662306a36Sopenharmony_ci	&dev_attr_pwm1_mode.attr,
31762306a36Sopenharmony_ci	&dev_attr_fan1_input.attr,
31862306a36Sopenharmony_ci	&dev_attr_fan1_target.attr,
31962306a36Sopenharmony_ci	&dev_attr_fan1_min.attr,
32062306a36Sopenharmony_ci	&dev_attr_fan1_max.attr,
32162306a36Sopenharmony_ci	NULL
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic const struct attribute_group gpio_fan_group = {
32562306a36Sopenharmony_ci	.attrs = gpio_fan_attributes,
32662306a36Sopenharmony_ci	.is_visible = gpio_fan_is_visible,
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic const struct attribute_group *gpio_fan_groups[] = {
33062306a36Sopenharmony_ci	&gpio_fan_group,
33162306a36Sopenharmony_ci	NULL
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int fan_ctrl_init(struct gpio_fan_data *fan_data)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int num_gpios = fan_data->num_gpios;
33762306a36Sopenharmony_ci	struct gpio_desc **gpios = fan_data->gpios;
33862306a36Sopenharmony_ci	int i, err;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0; i < num_gpios; i++) {
34162306a36Sopenharmony_ci		/*
34262306a36Sopenharmony_ci		 * The GPIO descriptors were retrieved with GPIOD_ASIS so here
34362306a36Sopenharmony_ci		 * we set the GPIO into output mode, carefully preserving the
34462306a36Sopenharmony_ci		 * current value by setting it to whatever it is already set
34562306a36Sopenharmony_ci		 * (no surprise changes in default fan speed).
34662306a36Sopenharmony_ci		 */
34762306a36Sopenharmony_ci		err = gpiod_direction_output(gpios[i],
34862306a36Sopenharmony_ci					gpiod_get_value_cansleep(gpios[i]));
34962306a36Sopenharmony_ci		if (err)
35062306a36Sopenharmony_ci			return err;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	fan_data->pwm_enable = true; /* Enable manual fan speed control. */
35462306a36Sopenharmony_ci	fan_data->speed_index = get_fan_speed_index(fan_data);
35562306a36Sopenharmony_ci	if (fan_data->speed_index < 0)
35662306a36Sopenharmony_ci		return fan_data->speed_index;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int gpio_fan_get_max_state(struct thermal_cooling_device *cdev,
36262306a36Sopenharmony_ci				  unsigned long *state)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = cdev->devdata;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!fan_data)
36762306a36Sopenharmony_ci		return -EINVAL;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	*state = fan_data->num_speed - 1;
37062306a36Sopenharmony_ci	return 0;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev,
37462306a36Sopenharmony_ci				  unsigned long *state)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = cdev->devdata;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (!fan_data)
37962306a36Sopenharmony_ci		return -EINVAL;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	*state = fan_data->speed_index;
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
38662306a36Sopenharmony_ci				  unsigned long state)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = cdev->devdata;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (!fan_data)
39162306a36Sopenharmony_ci		return -EINVAL;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (state >= fan_data->num_speed)
39462306a36Sopenharmony_ci		return -EINVAL;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	set_fan_speed(fan_data, state);
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
40162306a36Sopenharmony_ci	.get_max_state = gpio_fan_get_max_state,
40262306a36Sopenharmony_ci	.get_cur_state = gpio_fan_get_cur_state,
40362306a36Sopenharmony_ci	.set_cur_state = gpio_fan_set_cur_state,
40462306a36Sopenharmony_ci};
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci/*
40762306a36Sopenharmony_ci * Translate OpenFirmware node properties into platform_data
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_cistatic int gpio_fan_get_of_data(struct gpio_fan_data *fan_data)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct gpio_fan_speed *speed;
41262306a36Sopenharmony_ci	struct device *dev = fan_data->dev;
41362306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
41462306a36Sopenharmony_ci	struct gpio_desc **gpios;
41562306a36Sopenharmony_ci	unsigned i;
41662306a36Sopenharmony_ci	u32 u;
41762306a36Sopenharmony_ci	struct property *prop;
41862306a36Sopenharmony_ci	const __be32 *p;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* Alarm GPIO if one exists */
42162306a36Sopenharmony_ci	fan_data->alarm_gpio = devm_gpiod_get_optional(dev, "alarm", GPIOD_IN);
42262306a36Sopenharmony_ci	if (IS_ERR(fan_data->alarm_gpio))
42362306a36Sopenharmony_ci		return PTR_ERR(fan_data->alarm_gpio);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* Fill GPIO pin array */
42662306a36Sopenharmony_ci	fan_data->num_gpios = gpiod_count(dev, NULL);
42762306a36Sopenharmony_ci	if (fan_data->num_gpios <= 0) {
42862306a36Sopenharmony_ci		if (fan_data->alarm_gpio)
42962306a36Sopenharmony_ci			return 0;
43062306a36Sopenharmony_ci		dev_err(dev, "DT properties empty / missing");
43162306a36Sopenharmony_ci		return -ENODEV;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci	gpios = devm_kcalloc(dev,
43462306a36Sopenharmony_ci			     fan_data->num_gpios, sizeof(struct gpio_desc *),
43562306a36Sopenharmony_ci			     GFP_KERNEL);
43662306a36Sopenharmony_ci	if (!gpios)
43762306a36Sopenharmony_ci		return -ENOMEM;
43862306a36Sopenharmony_ci	for (i = 0; i < fan_data->num_gpios; i++) {
43962306a36Sopenharmony_ci		gpios[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
44062306a36Sopenharmony_ci		if (IS_ERR(gpios[i]))
44162306a36Sopenharmony_ci			return PTR_ERR(gpios[i]);
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	fan_data->gpios = gpios;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Get number of RPM/ctrl_val pairs in speed map */
44662306a36Sopenharmony_ci	prop = of_find_property(np, "gpio-fan,speed-map", &i);
44762306a36Sopenharmony_ci	if (!prop) {
44862306a36Sopenharmony_ci		dev_err(dev, "gpio-fan,speed-map DT property missing");
44962306a36Sopenharmony_ci		return -ENODEV;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	i = i / sizeof(u32);
45262306a36Sopenharmony_ci	if (i == 0 || i & 1) {
45362306a36Sopenharmony_ci		dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries");
45462306a36Sopenharmony_ci		return -ENODEV;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci	fan_data->num_speed = i / 2;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/*
45962306a36Sopenharmony_ci	 * Populate speed map
46062306a36Sopenharmony_ci	 * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...>
46162306a36Sopenharmony_ci	 * this needs splitting into pairs to create gpio_fan_speed structs
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	speed = devm_kcalloc(dev,
46462306a36Sopenharmony_ci			fan_data->num_speed, sizeof(struct gpio_fan_speed),
46562306a36Sopenharmony_ci			GFP_KERNEL);
46662306a36Sopenharmony_ci	if (!speed)
46762306a36Sopenharmony_ci		return -ENOMEM;
46862306a36Sopenharmony_ci	p = NULL;
46962306a36Sopenharmony_ci	for (i = 0; i < fan_data->num_speed; i++) {
47062306a36Sopenharmony_ci		p = of_prop_next_u32(prop, p, &u);
47162306a36Sopenharmony_ci		if (!p)
47262306a36Sopenharmony_ci			return -ENODEV;
47362306a36Sopenharmony_ci		speed[i].rpm = u;
47462306a36Sopenharmony_ci		p = of_prop_next_u32(prop, p, &u);
47562306a36Sopenharmony_ci		if (!p)
47662306a36Sopenharmony_ci			return -ENODEV;
47762306a36Sopenharmony_ci		speed[i].ctrl_val = u;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	fan_data->speed = speed;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic const struct of_device_id of_gpio_fan_match[] = {
48562306a36Sopenharmony_ci	{ .compatible = "gpio-fan", },
48662306a36Sopenharmony_ci	{},
48762306a36Sopenharmony_ci};
48862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_gpio_fan_match);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic void gpio_fan_stop(void *data)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	set_fan_speed(data, 0);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int gpio_fan_probe(struct platform_device *pdev)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	int err;
49862306a36Sopenharmony_ci	struct gpio_fan_data *fan_data;
49962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
50062306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data),
50362306a36Sopenharmony_ci				GFP_KERNEL);
50462306a36Sopenharmony_ci	if (!fan_data)
50562306a36Sopenharmony_ci		return -ENOMEM;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	fan_data->dev = dev;
50862306a36Sopenharmony_ci	err = gpio_fan_get_of_data(fan_data);
50962306a36Sopenharmony_ci	if (err)
51062306a36Sopenharmony_ci		return err;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	platform_set_drvdata(pdev, fan_data);
51362306a36Sopenharmony_ci	mutex_init(&fan_data->lock);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* Configure control GPIOs if available. */
51662306a36Sopenharmony_ci	if (fan_data->gpios && fan_data->num_gpios > 0) {
51762306a36Sopenharmony_ci		if (!fan_data->speed || fan_data->num_speed <= 1)
51862306a36Sopenharmony_ci			return -EINVAL;
51962306a36Sopenharmony_ci		err = fan_ctrl_init(fan_data);
52062306a36Sopenharmony_ci		if (err)
52162306a36Sopenharmony_ci			return err;
52262306a36Sopenharmony_ci		err = devm_add_action_or_reset(dev, gpio_fan_stop, fan_data);
52362306a36Sopenharmony_ci		if (err)
52462306a36Sopenharmony_ci			return err;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Make this driver part of hwmon class. */
52862306a36Sopenharmony_ci	fan_data->hwmon_dev =
52962306a36Sopenharmony_ci		devm_hwmon_device_register_with_groups(dev,
53062306a36Sopenharmony_ci						       "gpio_fan", fan_data,
53162306a36Sopenharmony_ci						       gpio_fan_groups);
53262306a36Sopenharmony_ci	if (IS_ERR(fan_data->hwmon_dev))
53362306a36Sopenharmony_ci		return PTR_ERR(fan_data->hwmon_dev);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Configure alarm GPIO if available. */
53662306a36Sopenharmony_ci	if (fan_data->alarm_gpio) {
53762306a36Sopenharmony_ci		err = fan_alarm_init(fan_data);
53862306a36Sopenharmony_ci		if (err)
53962306a36Sopenharmony_ci			return err;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* Optional cooling device register for Device tree platforms */
54362306a36Sopenharmony_ci	fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
54462306a36Sopenharmony_ci				"gpio-fan", fan_data, &gpio_fan_cool_ops);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	dev_info(dev, "GPIO fan initialized\n");
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return 0;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic void gpio_fan_shutdown(struct platform_device *pdev)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (fan_data->gpios)
55662306a36Sopenharmony_ci		set_fan_speed(fan_data, 0);
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int gpio_fan_suspend(struct device *dev)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (fan_data->gpios) {
56462306a36Sopenharmony_ci		fan_data->resume_speed = fan_data->speed_index;
56562306a36Sopenharmony_ci		set_fan_speed(fan_data, 0);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	return 0;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic int gpio_fan_resume(struct device *dev)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (fan_data->gpios)
57662306a36Sopenharmony_ci		set_fan_speed(fan_data, fan_data->resume_speed);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return 0;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic struct platform_driver gpio_fan_driver = {
58462306a36Sopenharmony_ci	.probe		= gpio_fan_probe,
58562306a36Sopenharmony_ci	.shutdown	= gpio_fan_shutdown,
58662306a36Sopenharmony_ci	.driver	= {
58762306a36Sopenharmony_ci		.name	= "gpio-fan",
58862306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&gpio_fan_pm),
58962306a36Sopenharmony_ci		.of_match_table = of_gpio_fan_match,
59062306a36Sopenharmony_ci	},
59162306a36Sopenharmony_ci};
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cimodule_platform_driver(gpio_fan_driver);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ciMODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
59662306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO FAN driver");
59762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
59862306a36Sopenharmony_ciMODULE_ALIAS("platform:gpio-fan");
599