diff options
Diffstat (limited to 'drivers/hwmon/gpio-fan.c')
| -rw-r--r-- | drivers/hwmon/gpio-fan.c | 318 | 
1 files changed, 183 insertions, 135 deletions
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index f141a1de519..2566c43dd1e 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -31,6 +31,9 @@  #include <linux/hwmon.h>  #include <linux/gpio.h>  #include <linux/gpio-fan.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h>  struct gpio_fan_data {  	struct platform_device	*pdev; @@ -41,7 +44,7 @@ struct gpio_fan_data {  	int			num_speed;  	struct gpio_fan_speed	*speed;  	int			speed_index; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  	int			resume_speed;  #endif  	bool			pwm_enable; @@ -95,17 +98,13 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data,  	fan_data->alarm = alarm; -	err = gpio_request(alarm->gpio, "GPIO fan alarm"); +	err = devm_gpio_request(&pdev->dev, alarm->gpio, "GPIO fan alarm");  	if (err)  		return err;  	err = gpio_direction_input(alarm->gpio);  	if (err) -		goto err_free_gpio; - -	err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); -	if (err) -		goto err_free_gpio; +		return err;  	/*  	 * If the alarm GPIO don't support interrupts, just leave @@ -116,33 +115,12 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data,  		return 0;  	INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); -	set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); -	err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, -			  "GPIO fan alarm", fan_data); -	if (err) -		goto err_free_sysfs; - -	return 0; - -err_free_sysfs: -	device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); -err_free_gpio: -	gpio_free(alarm->gpio); - +	irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); +	err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler, +			       IRQF_SHARED, "GPIO fan alarm", fan_data);  	return err;  } -static void fan_alarm_free(struct gpio_fan_data *fan_data) -{ -	struct platform_device *pdev = fan_data->pdev; -	int alarm_irq = gpio_to_irq(fan_data->alarm->gpio); - -	if (alarm_irq >= 0) -		free_irq(alarm_irq, fan_data); -	device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); -	gpio_free(fan_data->alarm->gpio); -} -  /*   * Control GPIOs.   */ @@ -192,7 +170,7 @@ static int get_fan_speed_index(struct gpio_fan_data *fan_data)  	dev_warn(&fan_data->pdev->dev,  		 "missing speed array entry for GPIO value 0x%x\n", ctrl_val); -	return -EINVAL; +	return -ENODEV;  }  static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) @@ -224,7 +202,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,  	int speed_index;  	int ret = count; -	if (strict_strtoul(buf, 10, &pwm) || pwm > 255) +	if (kstrtoul(buf, 10, &pwm) || pwm > 255)  		return -EINVAL;  	mutex_lock(&fan_data->lock); @@ -257,7 +235,7 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,  	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);  	unsigned long val; -	if (strict_strtoul(buf, 10, &val) || val > 1) +	if (kstrtoul(buf, 10, &val) || val > 1)  		return -EINVAL;  	if (fan_data->pwm_enable == val) @@ -314,7 +292,7 @@ static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,  	unsigned long rpm;  	int ret = count; -	if (strict_strtoul(buf, 10, &rpm)) +	if (kstrtoul(buf, 10, &rpm))  		return -EINVAL;  	mutex_lock(&fan_data->lock); @@ -341,8 +319,23 @@ static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL);  static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL);  static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); -static struct attribute *gpio_fan_ctrl_attributes[] = { -	&dev_attr_pwm1.attr, +static umode_t gpio_fan_is_visible(struct kobject *kobj, +				   struct attribute *attr, int index) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct gpio_fan_data *data = dev_get_drvdata(dev); + +	if (index == 0 && !data->alarm) +		return 0; +	if (index > 0 && !data->ctrl) +		return 0; + +	return attr->mode; +} + +static struct attribute *gpio_fan_attributes[] = { +	&dev_attr_fan1_alarm.attr,		/* 0 */ +	&dev_attr_pwm1.attr,			/* 1 */  	&dev_attr_pwm1_enable.attr,  	&dev_attr_pwm1_mode.attr,  	&dev_attr_fan1_input.attr, @@ -352,8 +345,14 @@ static struct attribute *gpio_fan_ctrl_attributes[] = {  	NULL  }; -static const struct attribute_group gpio_fan_ctrl_group = { -	.attrs = gpio_fan_ctrl_attributes, +static const struct attribute_group gpio_fan_group = { +	.attrs = gpio_fan_attributes, +	.is_visible = gpio_fan_is_visible, +}; + +static const struct attribute_group *gpio_fan_groups[] = { +	&gpio_fan_group, +	NULL  };  static int fan_ctrl_init(struct gpio_fan_data *fan_data, @@ -365,15 +364,14 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,  	int i, err;  	for (i = 0; i < num_ctrl; i++) { -		err = gpio_request(ctrl[i], "GPIO fan control"); +		err = devm_gpio_request(&pdev->dev, ctrl[i], +					"GPIO fan control");  		if (err) -			goto err_free_gpio; +			return err;  		err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); -		if (err) { -			gpio_free(ctrl[i]); -			goto err_free_gpio; -		} +		if (err) +			return err;  	}  	fan_data->num_ctrl = num_ctrl; @@ -382,56 +380,139 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,  	fan_data->speed = pdata->speed;  	fan_data->pwm_enable = true; /* Enable manual fan speed control. */  	fan_data->speed_index = get_fan_speed_index(fan_data); -	if (fan_data->speed_index < 0) { -		err = -ENODEV; -		goto err_free_gpio; -	} - -	err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); -	if (err) -		goto err_free_gpio; +	if (fan_data->speed_index < 0) +		return fan_data->speed_index;  	return 0; - -err_free_gpio: -	for (i = i - 1; i >= 0; i--) -		gpio_free(ctrl[i]); - -	return err;  } -static void fan_ctrl_free(struct gpio_fan_data *fan_data) +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data + */ +static int gpio_fan_get_of_pdata(struct device *dev, +			    struct gpio_fan_platform_data *pdata)  { -	struct platform_device *pdev = fan_data->pdev; -	int i; +	struct device_node *node; +	struct gpio_fan_speed *speed; +	unsigned *ctrl; +	unsigned i; +	u32 u; +	struct property *prop; +	const __be32 *p; + +	node = dev->of_node; + +	/* Fill GPIO pin array */ +	pdata->num_ctrl = of_gpio_count(node); +	if (pdata->num_ctrl <= 0) { +		dev_err(dev, "gpios DT property empty / missing"); +		return -ENODEV; +	} +	ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned), +				GFP_KERNEL); +	if (!ctrl) +		return -ENOMEM; +	for (i = 0; i < pdata->num_ctrl; i++) { +		int val; -	sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); -	for (i = 0; i < fan_data->num_ctrl; i++) -		gpio_free(fan_data->ctrl[i]); -} +		val = of_get_gpio(node, i); +		if (val < 0) +			return val; +		ctrl[i] = val; +	} +	pdata->ctrl = ctrl; -/* - * Platform driver. - */ +	/* Get number of RPM/ctrl_val pairs in speed map */ +	prop = of_find_property(node, "gpio-fan,speed-map", &i); +	if (!prop) { +		dev_err(dev, "gpio-fan,speed-map DT property missing"); +		return -ENODEV; +	} +	i = i / sizeof(u32); +	if (i == 0 || i & 1) { +		dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries"); +		return -ENODEV; +	} +	pdata->num_speed = i / 2; -static ssize_t show_name(struct device *dev, -			 struct device_attribute *attr, char *buf) -{ -	return sprintf(buf, "gpio-fan\n"); +	/* +	 * Populate speed map +	 * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...> +	 * this needs splitting into pairs to create gpio_fan_speed structs +	 */ +	speed = devm_kzalloc(dev, +			pdata->num_speed * sizeof(struct gpio_fan_speed), +			GFP_KERNEL); +	if (!speed) +		return -ENOMEM; +	p = NULL; +	for (i = 0; i < pdata->num_speed; i++) { +		p = of_prop_next_u32(prop, p, &u); +		if (!p) +			return -ENODEV; +		speed[i].rpm = u; +		p = of_prop_next_u32(prop, p, &u); +		if (!p) +			return -ENODEV; +		speed[i].ctrl_val = u; +	} +	pdata->speed = speed; + +	/* Alarm GPIO if one exists */ +	if (of_gpio_named_count(node, "alarm-gpios") > 0) { +		struct gpio_fan_alarm *alarm; +		int val; +		enum of_gpio_flags flags; + +		alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm), +					GFP_KERNEL); +		if (!alarm) +			return -ENOMEM; + +		val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); +		if (val < 0) +			return val; +		alarm->gpio = val; +		alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; + +		pdata->alarm = alarm; +	} + +	return 0;  } -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static const struct of_device_id of_gpio_fan_match[] = { +	{ .compatible = "gpio-fan", }, +	{}, +}; +#endif /* CONFIG_OF_GPIO */ -static int __devinit gpio_fan_probe(struct platform_device *pdev) +static int gpio_fan_probe(struct platform_device *pdev)  {  	int err;  	struct gpio_fan_data *fan_data; -	struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; +	struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev); + +#ifdef CONFIG_OF_GPIO +	if (!pdata) { +		pdata = devm_kzalloc(&pdev->dev, +					sizeof(struct gpio_fan_platform_data), +					GFP_KERNEL); +		if (!pdata) +			return -ENOMEM; +		err = gpio_fan_get_of_pdata(&pdev->dev, pdata); +		if (err) +			return err; +	} +#else /* CONFIG_OF_GPIO */  	if (!pdata)  		return -EINVAL; +#endif /* CONFIG_OF_GPIO */ -	fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL); +	fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data), +				GFP_KERNEL);  	if (!fan_data)  		return -ENOMEM; @@ -443,69 +524,43 @@ static int __devinit gpio_fan_probe(struct platform_device *pdev)  	if (pdata->alarm) {  		err = fan_alarm_init(fan_data, pdata->alarm);  		if (err) -			goto err_free_data; +			return err;  	}  	/* Configure control GPIOs if available. */  	if (pdata->ctrl && pdata->num_ctrl > 0) { -		if (!pdata->speed || pdata->num_speed <= 1) { -			err = -EINVAL; -			goto err_free_alarm; -		} +		if (!pdata->speed || pdata->num_speed <= 1) +			return -EINVAL;  		err = fan_ctrl_init(fan_data, pdata);  		if (err) -			goto err_free_alarm; +			return err;  	} -	err = device_create_file(&pdev->dev, &dev_attr_name); -	if (err) -		goto err_free_ctrl; -  	/* Make this driver part of hwmon class. */ -	fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); -	if (IS_ERR(fan_data->hwmon_dev)) { -		err = PTR_ERR(fan_data->hwmon_dev); -		goto err_remove_name; -	} +	fan_data->hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, +						"gpio_fan", fan_data, +						gpio_fan_groups); +	if (IS_ERR(fan_data->hwmon_dev)) +		return PTR_ERR(fan_data->hwmon_dev);  	dev_info(&pdev->dev, "GPIO fan initialized\n");  	return 0; - -err_remove_name: -	device_remove_file(&pdev->dev, &dev_attr_name); -err_free_ctrl: -	if (fan_data->ctrl) -		fan_ctrl_free(fan_data); -err_free_alarm: -	if (fan_data->alarm) -		fan_alarm_free(fan_data); -err_free_data: -	platform_set_drvdata(pdev, NULL); -	kfree(fan_data); - -	return err;  } -static int __devexit gpio_fan_remove(struct platform_device *pdev) +static int gpio_fan_remove(struct platform_device *pdev)  {  	struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);  	hwmon_device_unregister(fan_data->hwmon_dev); -	device_remove_file(&pdev->dev, &dev_attr_name); -	if (fan_data->alarm) -		fan_alarm_free(fan_data); -	if (fan_data->ctrl) -		fan_ctrl_free(fan_data); -	kfree(fan_data);  	return 0;  } -#ifdef CONFIG_PM -static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int gpio_fan_suspend(struct device *dev)  { -	struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); +	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);  	if (fan_data->ctrl) {  		fan_data->resume_speed = fan_data->speed_index; @@ -515,42 +570,35 @@ static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state)  	return 0;  } -static int gpio_fan_resume(struct platform_device *pdev) +static int gpio_fan_resume(struct device *dev)  { -	struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); +	struct gpio_fan_data *fan_data = dev_get_drvdata(dev);  	if (fan_data->ctrl)  		set_fan_speed(fan_data, fan_data->resume_speed);  	return 0;  } + +static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); +#define GPIO_FAN_PM	(&gpio_fan_pm)  #else -#define gpio_fan_suspend NULL -#define gpio_fan_resume NULL +#define GPIO_FAN_PM	NULL  #endif  static struct platform_driver gpio_fan_driver = {  	.probe		= gpio_fan_probe, -	.remove		= __devexit_p(gpio_fan_remove), -	.suspend	= gpio_fan_suspend, -	.resume		= gpio_fan_resume, +	.remove		= gpio_fan_remove,  	.driver	= {  		.name	= "gpio-fan", +		.pm	= GPIO_FAN_PM, +#ifdef CONFIG_OF_GPIO +		.of_match_table = of_match_ptr(of_gpio_fan_match), +#endif  	},  }; -static int __init gpio_fan_init(void) -{ -	return platform_driver_register(&gpio_fan_driver); -} - -static void __exit gpio_fan_exit(void) -{ -	platform_driver_unregister(&gpio_fan_driver); -} - -module_init(gpio_fan_init); -module_exit(gpio_fan_exit); +module_platform_driver(gpio_fan_driver);  MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");  MODULE_DESCRIPTION("GPIO FAN driver");  | 
