brintos

brintos / linux-shallow public Read only

0
0
Text · 3.1 KiB · e020f25 Raw
133 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sl28cpld hardware monitoring driver4 *5 * Copyright 2020 Kontron Europe GmbH6 */7 8#include <linux/bitfield.h>9#include <linux/hwmon.h>10#include <linux/kernel.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/property.h>15#include <linux/regmap.h>16 17#define FAN_INPUT		0x0018#define   FAN_SCALE_X8		BIT(7)19#define   FAN_VALUE_MASK	GENMASK(6, 0)20 21struct sl28cpld_hwmon {22	struct regmap *regmap;23	u32 offset;24};25 26static umode_t sl28cpld_hwmon_is_visible(const void *data,27					 enum hwmon_sensor_types type,28					 u32 attr, int channel)29{30	return 0444;31}32 33static int sl28cpld_hwmon_read(struct device *dev,34			       enum hwmon_sensor_types type, u32 attr,35			       int channel, long *input)36{37	struct sl28cpld_hwmon *hwmon = dev_get_drvdata(dev);38	unsigned int value;39	int ret;40 41	switch (attr) {42	case hwmon_fan_input:43		ret = regmap_read(hwmon->regmap, hwmon->offset + FAN_INPUT,44				  &value);45		if (ret)46			return ret;47		/*48		 * The register has a 7 bit value and 1 bit which indicates the49		 * scale. If the MSB is set, then the lower 7 bit has to be50		 * multiplied by 8, to get the correct reading.51		 */52		if (value & FAN_SCALE_X8)53			value = FIELD_GET(FAN_VALUE_MASK, value) << 3;54 55		/*56		 * The counter period is 1000ms and the sysfs specification57		 * says we should assume 2 pulses per revolution.58		 */59		value *= 60 / 2;60 61		break;62	default:63		return -EOPNOTSUPP;64	}65 66	*input = value;67	return 0;68}69 70static const struct hwmon_channel_info * const sl28cpld_hwmon_info[] = {71	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),72	NULL73};74 75static const struct hwmon_ops sl28cpld_hwmon_ops = {76	.is_visible = sl28cpld_hwmon_is_visible,77	.read = sl28cpld_hwmon_read,78};79 80static const struct hwmon_chip_info sl28cpld_hwmon_chip_info = {81	.ops = &sl28cpld_hwmon_ops,82	.info = sl28cpld_hwmon_info,83};84 85static int sl28cpld_hwmon_probe(struct platform_device *pdev)86{87	struct sl28cpld_hwmon *hwmon;88	struct device *hwmon_dev;89	int ret;90 91	if (!pdev->dev.parent)92		return -ENODEV;93 94	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);95	if (!hwmon)96		return -ENOMEM;97 98	hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);99	if (!hwmon->regmap)100		return -ENODEV;101 102	ret = device_property_read_u32(&pdev->dev, "reg", &hwmon->offset);103	if (ret)104		return -EINVAL;105 106	hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,107				"sl28cpld_hwmon", hwmon,108				&sl28cpld_hwmon_chip_info, NULL);109	if (IS_ERR(hwmon_dev))110		dev_err(&pdev->dev, "failed to register as hwmon device");111 112	return PTR_ERR_OR_ZERO(hwmon_dev);113}114 115static const struct of_device_id sl28cpld_hwmon_of_match[] = {116	{ .compatible = "kontron,sl28cpld-fan" },117	{}118};119MODULE_DEVICE_TABLE(of, sl28cpld_hwmon_of_match);120 121static struct platform_driver sl28cpld_hwmon_driver = {122	.probe = sl28cpld_hwmon_probe,123	.driver = {124		.name = "sl28cpld-fan",125		.of_match_table = sl28cpld_hwmon_of_match,126	},127};128module_platform_driver(sl28cpld_hwmon_driver);129 130MODULE_DESCRIPTION("sl28cpld Hardware Monitoring Driver");131MODULE_AUTHOR("Michael Walle <michael@walle.cc>");132MODULE_LICENSE("GPL");133