From 6dcf2fb5e8db3704f50af1f198256cb4e2453f8b Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Fri, 27 Oct 2017 11:55:05 -0500 Subject: hwmon: (pmbus/core) Prevent unintentional setting of page to 0xFF The pmbus core may call read/write word data functions with a page value of -1, intending to perform the operation without setting the page. However, the read/write word data functions accept only unsigned 8-bit page numbers, and therefore cannot check for negative page number to avoid setting the page. This results in setting the page number to 0xFF. This may result in errors or undefined behavior of some devices (specifically the ir35221, which allows the page to be set to 0xFF, but some subsequent operations to read registers may fail). Switch the pmbus_set_page page parameter to an integer and perform the check for negative page there. Make read/write functions consistent in accepting an integer page number parameter. Signed-off-by: Edward A. James Fixes: cbcdec6202c9 ("hwmon: (pmbus): Access word data for STATUS_WORD") Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 6 +++--- drivers/hwmon/pmbus/pmbus_core.c | 25 +++++++++++-------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 4efa2bd4f6d8..fa613bd209e3 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -404,9 +404,9 @@ extern const struct regulator_ops pmbus_regulator_ops; /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); -int pmbus_set_page(struct i2c_client *client, u8 page); -int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); -int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_set_page(struct i2c_client *client, int page); +int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 word); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); int pmbus_write_byte(struct i2c_client *client, int page, u8 value); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 302f0aef59de..52a58b8b6e1b 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -136,13 +136,13 @@ void pmbus_clear_cache(struct i2c_client *client) } EXPORT_SYMBOL_GPL(pmbus_clear_cache); -int pmbus_set_page(struct i2c_client *client, u8 page) +int pmbus_set_page(struct i2c_client *client, int page) { struct pmbus_data *data = i2c_get_clientdata(client); int rv = 0; int newpage; - if (page != data->currpage) { + if (page >= 0 && page != data->currpage) { rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); if (newpage != page) @@ -158,11 +158,9 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { int rv; - if (page >= 0) { - rv = pmbus_set_page(client, page); - if (rv < 0) - return rv; - } + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; return i2c_smbus_write_byte(client, value); } @@ -186,7 +184,8 @@ static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) return pmbus_write_byte(client, page, value); } -int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word) +int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, + u16 word) { int rv; @@ -219,7 +218,7 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, return pmbus_write_word_data(client, page, reg, word); } -int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg) +int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg) { int rv; @@ -255,11 +254,9 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) { int rv; - if (page >= 0) { - rv = pmbus_set_page(client, page); - if (rv < 0) - return rv; - } + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; return i2c_smbus_read_byte_data(client, reg); } -- cgit v1.2.3 From fc483a9bf795ebf49bf0c872d125391d8fee2f3f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 19 Sep 2017 14:59:34 +0200 Subject: hwmon: (stts751) Fix buffer size passed to snprintf Function snprintf already cares for the terminating NUL at the end of the string, the caller doesn't need to do it. Signed-off-by: Jean Delvare Cc: Andrea Merello Cc: Guenter Roeck Signed-off-by: Guenter Roeck --- drivers/hwmon/stts751.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index 3f940fb67dc6..7fe152d92350 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -396,7 +396,7 @@ static ssize_t show_max_alarm(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->max_alert); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->max_alert); } static ssize_t show_min_alarm(struct device *dev, struct device_attribute *attr, @@ -413,7 +413,7 @@ static ssize_t show_min_alarm(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->min_alert); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->min_alert); } static ssize_t show_input(struct device *dev, struct device_attribute *attr, @@ -428,7 +428,7 @@ static ssize_t show_input(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->temp); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->temp); } static ssize_t show_therm(struct device *dev, struct device_attribute *attr, @@ -436,7 +436,7 @@ static ssize_t show_therm(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->therm); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm); } static ssize_t set_therm(struct device *dev, struct device_attribute *attr, @@ -478,7 +478,7 @@ static ssize_t show_hyst(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->hyst); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->hyst); } static ssize_t set_hyst(struct device *dev, struct device_attribute *attr, @@ -518,7 +518,7 @@ static ssize_t show_therm_trip(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->therm_trip); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm_trip); } static ssize_t show_max(struct device *dev, struct device_attribute *attr, @@ -526,7 +526,7 @@ static ssize_t show_max(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->event_max); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_max); } static ssize_t set_max(struct device *dev, struct device_attribute *attr, @@ -560,7 +560,7 @@ static ssize_t show_min(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", priv->event_min); + return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_min); } static ssize_t set_min(struct device *dev, struct device_attribute *attr, @@ -594,7 +594,7 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", + return snprintf(buf, PAGE_SIZE, "%d\n", stts751_intervals[priv->interval]); } -- cgit v1.2.3 From 68546abf7a3a63f199e53d6dcaa7375df37a6aaa Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Sep 2017 18:33:53 -0700 Subject: hwmon: (k10temp) Move chip specific code into probe function Introduce a local data structure and determine the temperature read function at probe time to reduce runtime complexity. Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 62 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index ce3b91f22e30..fc8076c7e1a1 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -61,31 +61,44 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); */ #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 -static void amd_nb_smu_index_read(struct pci_dev *pdev, unsigned int devfn, - int offset, u32 *val) +struct k10temp_data { + struct pci_dev *pdev; + void (*read_tempreg)(struct pci_dev *pdev, u32 *regval); +}; + +static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval) +{ + pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval); +} + +static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn, + unsigned int base, int offset, u32 *val) { mutex_lock(&nb_smu_ind_mutex); pci_bus_write_config_dword(pdev->bus, devfn, - 0xb8, offset); + base, offset); pci_bus_read_config_dword(pdev->bus, devfn, - 0xbc, val); + base + 4, val); mutex_unlock(&nb_smu_ind_mutex); } +static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval) +{ + amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8, + F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval); +} + static ssize_t temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct k10temp_data *data = dev_get_drvdata(dev); u32 regval; - struct pci_dev *pdev = dev_get_drvdata(dev); - - if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model == 0x60) { - amd_nb_smu_index_read(pdev, PCI_DEVFN(0, 0), - F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, - ®val); - } else { - pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, ®val); - } - return sprintf(buf, "%u\n", (regval >> 21) * 125); + unsigned int temp; + + data->read_tempreg(data->pdev, ®val); + temp = (regval >> 21) * 125; + + return sprintf(buf, "%u\n", temp); } static ssize_t temp1_max_show(struct device *dev, @@ -98,11 +111,12 @@ static ssize_t show_temp_crit(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct k10temp_data *data = dev_get_drvdata(dev); int show_hyst = attr->index; u32 regval; int value; - pci_read_config_dword(dev_get_drvdata(dev), + pci_read_config_dword(data->pdev, REG_HARDWARE_THERMAL_CONTROL, ®val); value = ((regval >> 16) & 0x7f) * 500 + 52000; if (show_hyst) @@ -119,7 +133,8 @@ static umode_t k10temp_is_visible(struct kobject *kobj, struct attribute *attr, int index) { struct device *dev = container_of(kobj, struct device, kobj); - struct pci_dev *pdev = dev_get_drvdata(dev); + struct k10temp_data *data = dev_get_drvdata(dev); + struct pci_dev *pdev = data->pdev; if (index >= 2) { u32 reg_caps, reg_htc; @@ -187,6 +202,7 @@ static int k10temp_probe(struct pci_dev *pdev, { int unreliable = has_erratum_319(pdev); struct device *dev = &pdev->dev; + struct k10temp_data *data; struct device *hwmon_dev; if (unreliable) { @@ -199,7 +215,19 @@ static int k10temp_probe(struct pci_dev *pdev, "unreliable CPU thermal sensor; check erratum 319\n"); } - hwmon_dev = devm_hwmon_device_register_with_groups(dev, "k10temp", pdev, + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->pdev = pdev; + + if (boot_cpu_data.x86 == 0x15 && (boot_cpu_data.x86_model == 0x60 || + boot_cpu_data.x86_model == 0x70)) + data->read_tempreg = read_tempreg_nb_f15; + else + data->read_tempreg = read_tempreg_pci; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "k10temp", data, k10temp_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } -- cgit v1.2.3 From 9af0a9aecdb945cd5513941ffdcbcc031009b402 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Sep 2017 18:33:53 -0700 Subject: hwmon: (k10temp) Add support for family 17h Add support for temperature sensors on Family 17h (Ryzen) processors. Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index fc8076c7e1a1..c4dac53206c3 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -36,6 +36,10 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); /* Provide lock for writing to NB_SMU_IND_ADDR */ static DEFINE_MUTEX(nb_smu_ind_mutex); +#ifndef PCI_DEVICE_ID_AMD_17H_DF_F3 +#define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463 +#endif + /* CPUID function 0x80000001, ebx */ #define CPUID_PKGTYPE_MASK 0xf0000000 #define CPUID_PKGTYPE_F 0x00000000 @@ -61,6 +65,9 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); */ #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 +/* F17h M01h Access througn SMN */ +#define F17H_M01H_REPORTED_TEMP_CTRL_OFFSET 0x00059800 + struct k10temp_data { struct pci_dev *pdev; void (*read_tempreg)(struct pci_dev *pdev, u32 *regval); @@ -88,6 +95,12 @@ static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval) F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval); } +static void read_tempreg_nb_f17(struct pci_dev *pdev, u32 *regval) +{ + amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0x60, + F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, regval); +} + static ssize_t temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -224,6 +237,8 @@ static int k10temp_probe(struct pci_dev *pdev, if (boot_cpu_data.x86 == 0x15 && (boot_cpu_data.x86_model == 0x60 || boot_cpu_data.x86_model == 0x70)) data->read_tempreg = read_tempreg_nb_f15; + else if (boot_cpu_data.x86 == 0x17) + data->read_tempreg = read_tempreg_nb_f17; else data->read_tempreg = read_tempreg_pci; @@ -242,6 +257,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); -- cgit v1.2.3 From 1b50b776355fa6c6d7b3281a63c275d5c18d629d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Sep 2017 18:33:53 -0700 Subject: hwmon: (k10temp) Add support for temperature offsets Add support for handling temperature offset values for various AMD CPUs, similar to the code used in the coretemp driver for Intel CPUs. This is primarily for Ryzen CPUs (which has documented temperature offsets), but the code is kept generic to simplify adding additional CPUs. Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index c4dac53206c3..46a54ed23410 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -71,6 +71,24 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); struct k10temp_data { struct pci_dev *pdev; void (*read_tempreg)(struct pci_dev *pdev, u32 *regval); + int temp_offset; +}; + +struct tctl_offset { + u8 model; + char const *id; + int offset; +}; + +static const struct tctl_offset tctl_offset_table[] = { + { 0x17, "AMD Ryzen 7 1600X", 20000 }, + { 0x17, "AMD Ryzen 7 1700X", 20000 }, + { 0x17, "AMD Ryzen 7 1800X", 20000 }, + { 0x17, "AMD Ryzen Threadripper 1950X", 27000 }, + { 0x17, "AMD Ryzen Threadripper 1920X", 27000 }, + { 0x17, "AMD Ryzen Threadripper 1950", 10000 }, + { 0x17, "AMD Ryzen Threadripper 1920", 10000 }, + { 0x17, "AMD Ryzen Threadripper 1910", 10000 }, }; static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval) @@ -110,6 +128,7 @@ static ssize_t temp1_input_show(struct device *dev, data->read_tempreg(data->pdev, ®val); temp = (regval >> 21) * 125; + temp -= data->temp_offset; return sprintf(buf, "%u\n", temp); } @@ -217,6 +236,7 @@ static int k10temp_probe(struct pci_dev *pdev, struct device *dev = &pdev->dev; struct k10temp_data *data; struct device *hwmon_dev; + int i; if (unreliable) { if (!force) { @@ -242,6 +262,16 @@ static int k10temp_probe(struct pci_dev *pdev, else data->read_tempreg = read_tempreg_pci; + for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) { + const struct tctl_offset *entry = &tctl_offset_table[i]; + + if (boot_cpu_data.x86 == entry->model && + strstr(boot_cpu_data.x86_model_id, entry->id)) { + data->temp_offset = entry->offset; + break; + } + } + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "k10temp", data, k10temp_groups); return PTR_ERR_OR_ZERO(hwmon_dev); -- cgit v1.2.3 From 186731145f920fb1514200043bcaf9c689693857 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 10 Sep 2017 11:44:46 +0200 Subject: hwmon: (sht15) Root out platform data After finding out there are active users of this sensor I noticed: - It has a single PXA27x board file using the platform data - The platform data is only used to carry two GPIO pins, all other fields are unused - The driver does not use GPIO descriptors but the legacy GPIO API I saw we can swiftly fix this by: - Killing off the platform data entirely - Define a GPIO descriptor lookup table in the board file - Use the standard devm_gpiod_get() to grab the GPIO descriptors from either the device tree or the board file table. This compiles, but needs testing. Cc: arm@kernel.org Cc: Marco Franchi Cc: Davide Hug Cc: Jonathan Cameron Signed-off-by: Linus Walleij Acked-by: Arnd Bergmann Tested-by: Marco Franchi Acked-by: Arnd Bergmann Signed-off-by: Guenter Roeck --- Documentation/hwmon/sht15 | 3 +- arch/arm/mach-pxa/stargate2.c | 17 ++-- drivers/hwmon/sht15.c | 167 ++++++++++++------------------------ include/linux/platform_data/sht15.h | 38 -------- 4 files changed, 65 insertions(+), 160 deletions(-) delete mode 100644 include/linux/platform_data/sht15.h diff --git a/Documentation/hwmon/sht15 b/Documentation/hwmon/sht15 index 778987d1856f..5e3207c3b177 100644 --- a/Documentation/hwmon/sht15 +++ b/Documentation/hwmon/sht15 @@ -42,8 +42,7 @@ chip. These coefficients are used to internally calibrate the signals from the sensors. Disabling the reload of those coefficients allows saving 10ms for each measurement and decrease power consumption, while losing on precision. -Some options may be set directly in the sht15_platform_data structure -or via sysfs attributes. +Some options may be set via sysfs attributes. Notes: * The regulator supply name is set to "vcc". diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c index 2d45d18b1a5e..6b7df6fd2448 100644 --- a/arch/arm/mach-pxa/stargate2.c +++ b/arch/arm/mach-pxa/stargate2.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,6 @@ #include #include #include -#include #include "devices.h" #include "generic.h" @@ -137,17 +137,18 @@ static unsigned long sg2_im2_unified_pin_config[] __initdata = { GPIO10_GPIO, /* large basic connector pin 23 */ }; -static struct sht15_platform_data platform_data_sht15 = { - .gpio_data = 100, - .gpio_sck = 98, +static struct gpiod_lookup_table sht15_gpiod_table = { + .dev_id = "sht15", + .table = { + /* FIXME: should this have |GPIO_OPEN_DRAIN set? */ + GPIO_LOOKUP("gpio-pxa", 100, "data", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("gpio-pxa", 98, "clk", GPIO_ACTIVE_HIGH), + }, }; static struct platform_device sht15 = { .name = "sht15", .id = -1, - .dev = { - .platform_data = &platform_data_sht15, - }, }; static struct regulator_consumer_supply stargate2_sensor_3_con[] = { @@ -608,6 +609,7 @@ static void __init imote2_init(void) imote2_stargate2_init(); + gpiod_add_lookup_table(&sht15_gpiod_table); platform_add_devices(imote2_devices, ARRAY_SIZE(imote2_devices)); i2c_register_board_info(0, imote2_i2c_board_info, @@ -988,6 +990,7 @@ static void __init stargate2_init(void) imote2_stargate2_init(); + gpiod_add_lookup_table(&sht15_gpiod_table); platform_add_devices(ARRAY_AND_SIZE(stargate2_devices)); i2c_register_board_info(0, ARRAY_AND_SIZE(stargate2_i2c_board_info)); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index e4d642b673c6..0e3e5f83f5cf 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -18,13 +18,11 @@ #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -34,7 +32,8 @@ #include #include #include -#include +#include +#include /* Commands */ #define SHT15_MEASURE_TEMP 0x03 @@ -122,7 +121,8 @@ static const u8 sht15_crc8_table[] = { /** * struct sht15_data - device instance specific data - * @pdata: platform data (gpio's etc). + * @sck: clock GPIO line + * @data: data GPIO line * @read_work: bh of interrupt handler. * @wait_queue: wait queue for getting values from device. * @val_temp: last temperature value read from device. @@ -150,7 +150,8 @@ static const u8 sht15_crc8_table[] = { * @interrupt_handled: flag used to indicate a handler has been scheduled. */ struct sht15_data { - struct sht15_platform_data *pdata; + struct gpio_desc *sck; + struct gpio_desc *data; struct work_struct read_work; wait_queue_head_t wait_queue; uint16_t val_temp; @@ -205,16 +206,16 @@ static int sht15_connection_reset(struct sht15_data *data) { int i, err; - err = gpio_direction_output(data->pdata->gpio_data, 1); + err = gpiod_direction_output(data->data, 1); if (err) return err; ndelay(SHT15_TSCKL); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); for (i = 0; i < 9; ++i) { - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); } return 0; @@ -227,11 +228,11 @@ static int sht15_connection_reset(struct sht15_data *data) */ static inline void sht15_send_bit(struct sht15_data *data, int val) { - gpio_set_value(data->pdata->gpio_data, val); + gpiod_set_value(data->data, val); ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); /* clock low time */ } @@ -248,23 +249,23 @@ static int sht15_transmission_start(struct sht15_data *data) int err; /* ensure data is high and output */ - err = gpio_direction_output(data->pdata->gpio_data, 1); + err = gpiod_direction_output(data->data, 1); if (err) return err; ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - gpio_set_value(data->pdata->gpio_data, 0); + gpiod_set_value(data->data, 0); ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - gpio_set_value(data->pdata->gpio_data, 1); + gpiod_set_value(data->data, 1); ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); return 0; } @@ -292,20 +293,20 @@ static int sht15_wait_for_response(struct sht15_data *data) { int err; - err = gpio_direction_input(data->pdata->gpio_data); + err = gpiod_direction_input(data->data); if (err) return err; - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - if (gpio_get_value(data->pdata->gpio_data)) { - gpio_set_value(data->pdata->gpio_sck, 0); + if (gpiod_get_value(data->data)) { + gpiod_set_value(data->sck, 0); dev_err(data->dev, "Command not acknowledged\n"); err = sht15_connection_reset(data); if (err) return err; return -EIO; } - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); return 0; } @@ -360,17 +361,17 @@ static int sht15_ack(struct sht15_data *data) { int err; - err = gpio_direction_output(data->pdata->gpio_data, 0); + err = gpiod_direction_output(data->data, 0); if (err) return err; ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_data, 1); + gpiod_set_value(data->data, 1); - return gpio_direction_input(data->pdata->gpio_data); + return gpiod_direction_input(data->data); } /** @@ -383,13 +384,13 @@ static int sht15_end_transmission(struct sht15_data *data) { int err; - err = gpio_direction_output(data->pdata->gpio_data, 1); + err = gpiod_direction_output(data->data, 1); if (err) return err; ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - gpio_set_value(data->pdata->gpio_sck, 0); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); return 0; } @@ -405,10 +406,10 @@ static u8 sht15_read_byte(struct sht15_data *data) for (i = 0; i < 8; ++i) { byte <<= 1; - gpio_set_value(data->pdata->gpio_sck, 1); + gpiod_set_value(data->sck, 1); ndelay(SHT15_TSCKH); - byte |= !!gpio_get_value(data->pdata->gpio_data); - gpio_set_value(data->pdata->gpio_sck, 0); + byte |= !!gpiod_get_value(data->data); + gpiod_set_value(data->sck, 0); ndelay(SHT15_TSCKL); } return byte; @@ -428,7 +429,7 @@ static int sht15_send_status(struct sht15_data *data, u8 status) err = sht15_send_cmd(data, SHT15_WRITE_STATUS); if (err) return err; - err = gpio_direction_output(data->pdata->gpio_data, 1); + err = gpiod_direction_output(data->data, 1); if (err) return err; ndelay(SHT15_TSU); @@ -528,14 +529,14 @@ static int sht15_measurement(struct sht15_data *data, if (ret) return ret; - ret = gpio_direction_input(data->pdata->gpio_data); + ret = gpiod_direction_input(data->data); if (ret) return ret; atomic_set(&data->interrupt_handled, 0); - enable_irq(gpio_to_irq(data->pdata->gpio_data)); - if (gpio_get_value(data->pdata->gpio_data) == 0) { - disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); + enable_irq(gpiod_to_irq(data->data)); + if (gpiod_get_value(data->data) == 0) { + disable_irq_nosync(gpiod_to_irq(data->data)); /* Only relevant if the interrupt hasn't occurred. */ if (!atomic_read(&data->interrupt_handled)) schedule_work(&data->read_work); @@ -547,7 +548,7 @@ static int sht15_measurement(struct sht15_data *data, data->state = SHT15_READING_NOTHING; return -EIO; } else if (ret == 0) { /* timeout occurred */ - disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); + disable_irq_nosync(gpiod_to_irq(data->data)); ret = sht15_connection_reset(data); if (ret) return ret; @@ -826,15 +827,15 @@ static void sht15_bh_read_data(struct work_struct *work_s) read_work); /* Firstly, verify the line is low */ - if (gpio_get_value(data->pdata->gpio_data)) { + if (gpiod_get_value(data->data)) { /* * If not, then start the interrupt again - care here as could * have gone low in meantime so verify it hasn't! */ atomic_set(&data->interrupt_handled, 0); - enable_irq(gpio_to_irq(data->pdata->gpio_data)); + enable_irq(gpiod_to_irq(data->data)); /* If still not occurred or another handler was scheduled */ - if (gpio_get_value(data->pdata->gpio_data) + if (gpiod_get_value(data->data) || atomic_read(&data->interrupt_handled)) return; } @@ -918,46 +919,6 @@ static const struct of_device_id sht15_dt_match[] = { { }, }; MODULE_DEVICE_TABLE(of, sht15_dt_match); - -/* - * This function returns NULL if pdev isn't a device instatiated by dt, - * a pointer to pdata if it could successfully get all information - * from dt or a negative ERR_PTR() on error. - */ -static struct sht15_platform_data *sht15_probe_dt(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct sht15_platform_data *pdata; - - /* no device tree device */ - if (!np) - return NULL; - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - pdata->gpio_data = of_get_named_gpio(np, "data-gpios", 0); - if (pdata->gpio_data < 0) { - if (pdata->gpio_data != -EPROBE_DEFER) - dev_err(dev, "data-gpios not found\n"); - return ERR_PTR(pdata->gpio_data); - } - - pdata->gpio_sck = of_get_named_gpio(np, "clk-gpios", 0); - if (pdata->gpio_sck < 0) { - if (pdata->gpio_sck != -EPROBE_DEFER) - dev_err(dev, "clk-gpios not found\n"); - return ERR_PTR(pdata->gpio_sck); - } - - return pdata; -} -#else -static inline struct sht15_platform_data *sht15_probe_dt(struct device *dev) -{ - return NULL; -} #endif static int sht15_probe(struct platform_device *pdev) @@ -977,25 +938,6 @@ static int sht15_probe(struct platform_device *pdev) data->dev = &pdev->dev; init_waitqueue_head(&data->wait_queue); - data->pdata = sht15_probe_dt(&pdev->dev); - if (IS_ERR(data->pdata)) - return PTR_ERR(data->pdata); - if (data->pdata == NULL) { - data->pdata = dev_get_platdata(&pdev->dev); - if (data->pdata == NULL) { - dev_err(&pdev->dev, "no platform data supplied\n"); - return -EINVAL; - } - } - - data->supply_uv = data->pdata->supply_mv * 1000; - if (data->pdata->checksum) - data->checksumming = true; - if (data->pdata->no_otp_reload) - status |= SHT15_STATUS_NO_OTP_RELOAD; - if (data->pdata->low_resolution) - status |= SHT15_STATUS_LOW_RESOLUTION; - /* * If a regulator is available, * query what the supply voltage actually is! @@ -1030,21 +972,20 @@ static int sht15_probe(struct platform_device *pdev) } /* Try requesting the GPIOs */ - ret = devm_gpio_request_one(&pdev->dev, data->pdata->gpio_sck, - GPIOF_OUT_INIT_LOW, "SHT15 sck"); - if (ret) { + data->sck = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_LOW); + if (IS_ERR(data->sck)) { + ret = PTR_ERR(data->sck); dev_err(&pdev->dev, "clock line GPIO request failed\n"); goto err_release_reg; } - - ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_data, - "SHT15 data"); - if (ret) { + data->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN); + if (IS_ERR(data->data)) { + ret = PTR_ERR(data->data); dev_err(&pdev->dev, "data line GPIO request failed\n"); goto err_release_reg; } - ret = devm_request_irq(&pdev->dev, gpio_to_irq(data->pdata->gpio_data), + ret = devm_request_irq(&pdev->dev, gpiod_to_irq(data->data), sht15_interrupt_fired, IRQF_TRIGGER_FALLING, "sht15 data", @@ -1053,7 +994,7 @@ static int sht15_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get irq for data line\n"); goto err_release_reg; } - disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); + disable_irq_nosync(gpiod_to_irq(data->data)); ret = sht15_connection_reset(data); if (ret) goto err_release_reg; diff --git a/include/linux/platform_data/sht15.h b/include/linux/platform_data/sht15.h deleted file mode 100644 index 12289c1e9413..000000000000 --- a/include/linux/platform_data/sht15.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * sht15.h - support for the SHT15 Temperature and Humidity Sensor - * - * Copyright (c) 2009 Jonathan Cameron - * - * Copyright (c) 2007 Wouter Horre - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * For further information, see the Documentation/hwmon/sht15 file. - */ - -#ifndef _PDATA_SHT15_H -#define _PDATA_SHT15_H - -/** - * struct sht15_platform_data - sht15 connectivity info - * @gpio_data: no. of gpio to which bidirectional data line is - * connected. - * @gpio_sck: no. of gpio to which the data clock is connected. - * @supply_mv: supply voltage in mv. Overridden by regulator if - * available. - * @checksum: flag to indicate the checksum should be validated. - * @no_otp_reload: flag to indicate no reload from OTP. - * @low_resolution: flag to indicate the temp/humidity resolution to use. - */ -struct sht15_platform_data { - int gpio_data; - int gpio_sck; - int supply_mv; - bool checksum; - bool no_otp_reload; - bool low_resolution; -}; - -#endif /* _PDATA_SHT15_H */ -- cgit v1.2.3 From 90b863dd63d63f4d97c3f5b6d6ed5bd239500f7b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 18 Sep 2017 15:45:48 +0100 Subject: hwmon: (sht15) remove redundant check on status and send of status value A previous commit removed bit or'ing into to the integer status so now status is now always zero. This means that the non-zero check on status and the sht15_send_status call will never occur; it is deadcode. Clean this up by removing the dead code. Detected by: CoverityScan CID#1456835 ("Logically dead code") Fixes: aa7ab80c578c ("hwmon: (sht15) Root out platform data") Signed-off-by: Colin Ian King Signed-off-by: Guenter Roeck --- drivers/hwmon/sht15.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 0e3e5f83f5cf..25d28343ba93 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -925,7 +925,6 @@ static int sht15_probe(struct platform_device *pdev) { int ret; struct sht15_data *data; - u8 status = 0; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -1002,13 +1001,6 @@ static int sht15_probe(struct platform_device *pdev) if (ret) goto err_release_reg; - /* write status with platform data options */ - if (status) { - ret = sht15_send_status(data, status); - if (ret) - goto err_release_reg; - } - ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group); if (ret) { dev_err(&pdev->dev, "sysfs create failed\n"); -- cgit v1.2.3 From 0ec54a2e8ee22b13e9a89dfd76e66e5b75ca9885 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Mon, 11 Sep 2017 14:16:48 -0500 Subject: dt-bindings: hwmon: add compatible for max1619 Add new device tree bindings document for max1619 device including a new compatible string. Signed-off-by: Alan Tull Cc: Jean Delvare Cc: Guenter Roeck Cc: Rob Herring Cc: Mark Rutland Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/max1619.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/max1619.txt diff --git a/Documentation/devicetree/bindings/hwmon/max1619.txt b/Documentation/devicetree/bindings/hwmon/max1619.txt new file mode 100644 index 000000000000..c70dbbe1e56f --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/max1619.txt @@ -0,0 +1,12 @@ +Bindings for MAX1619 Temperature Sensor + +Required properties: +- compatible : "maxim,max1619" +- reg : I2C address, one of 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, or + 0x4d, 0x4e + +Example: + temp@4c { + compatible = "maxim,max1619"; + reg = <0x4c>; + }; -- cgit v1.2.3 From fd53f62160e5fcea18f6adcddd40ba429dc127b6 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Mon, 11 Sep 2017 14:16:49 -0500 Subject: hwmon: (max1619) Add dt binding Add new device tree binding for max1619. Signed-off-by: Alan Tull Cc: Jean Delvare Cc: Guenter Roeck Cc: Rob Herring Cc: Mark Rutland Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index a18278938494..76d966932941 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -303,10 +303,20 @@ static const struct i2c_device_id max1619_id[] = { }; MODULE_DEVICE_TABLE(i2c, max1619_id); +#ifdef CONFIG_OF +static const struct of_device_id max1619_of_match[] = { + { .compatible = "maxim,max1619", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, max1619_of_match); +#endif + static struct i2c_driver max1619_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "max1619", + .of_match_table = of_match_ptr(max1619_of_match), }, .probe = max1619_probe, .id_table = max1619_id, -- cgit v1.2.3 From 762b1e88801357770889d013c5d20fe110d1f456 Mon Sep 17 00:00:00 2001 From: Patrick Venture Date: Mon, 11 Sep 2017 15:41:55 -0700 Subject: hwmon: (aspeed-pwm-tacho) increase fan tach period The previous value reduced the time required to determine the fan value, however, it's also used as the final timeout mechanism. The prevous value would work for any fan speed greater than around 3k RPM. This increased value, lets the fan speeds return quickly but will wait longer to handle speeds below 3k RPM. Testing: this value was determined through experimentation on the ast2400 on the Quanta-q71l. This configurations runs 8 fans attached to the controller. Signed-off-by: Patrick Venture Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 69b97d45e3cb..f914e5f41048 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -161,7 +161,7 @@ * 11: reserved. */ #define M_TACH_MODE 0x02 /* 10b */ -#define M_TACH_UNIT 0x00c0 +#define M_TACH_UNIT 0x0210 #define INIT_FAN_CTRL 0xFF /* How long we sleep in us while waiting for an RPM result. */ -- cgit v1.2.3 From 7a76a7f34afdfb080ec8e51ed18891b4f72ec907 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 22 Sep 2017 15:36:20 +0100 Subject: hwmon: (w83793) make const array watchdog_minors static, reduces object code size Don't populate const array watchdog_minors on the stack, instead make it static. Makes the object code smaller by over 350 bytes: Before: text data bss dec hex filename 48019 38144 256 86419 15193 drivers/hwmon/w83793.o After: text data bss dec hex filename 47574 38232 256 86062 1502e drivers/hwmon/w83793.o (gcc 6.3.0, x86-64) Signed-off-by: Colin Ian King Signed-off-by: Guenter Roeck --- drivers/hwmon/w83793.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index dab5c515d5a3..5ba9d9f1daa1 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1676,7 +1676,9 @@ static int w83793_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; + static const int watchdog_minors[] = { + WATCHDOG_MINOR, 212, 213, 214, 215 + }; struct w83793_data *data; int i, tmp, val, err; int files_fan = ARRAY_SIZE(w83793_left_fan) / 7; -- cgit v1.2.3 From 92b64580f14b24a3d5cfd1e9dff0b745826a824b Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 3 Oct 2017 18:08:27 +0000 Subject: hwmon: (max6621) Add support for Maxim MAX6621 temperature sensor MAX6621 is a PECI-to-I2C translator provides an efficient, low-cost solution for PECI-to-SMBus/I2C protocol conversion. It allows reading the temperature from the PECI-compliant host directly from up to four PECI-enabled CPUs. Signed-off-by: Vadim Pasternak Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 14 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/max6621.c | 593 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 608 insertions(+) create mode 100644 drivers/hwmon/max6621.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d65431417b17..fae8a8904c10 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -862,6 +862,20 @@ tristate "MAX31722 temperature sensor" This driver can also be built as a module. If so, the module will be called max31722. +config SENSORS_MAX6621 + tristate "Maxim MAX6621 sensor chip" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for MAX6621 sensor chip. + MAX6621 is a PECI-to-I2C translator provides an efficient, + low-cost solution for PECI-to-SMBus/I2C protocol conversion. + It allows reading the temperature from the PECI-compliant + host directly from up to four PECI-enabled CPUs. + + This driver can also be built as a module. If so, the module + will be called max6621. + config SENSORS_MAX6639 tristate "Maxim MAX6639 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c84d9784be98..8941a478d2a7 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1668) += max1668.o obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX31722) += max31722.o +obj-$(CONFIG_SENSORS_MAX6621) += max6621.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c new file mode 100644 index 000000000000..22079ec29660 --- /dev/null +++ b/drivers/hwmon/max6621.c @@ -0,0 +1,593 @@ +/* + * Hardware monitoring driver for Maxim MAX6621 + * + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Vadim Pasternak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX6621_DRV_NAME "max6621" +#define MAX6621_TEMP_INPUT_REG_NUM 9 +#define MAX6621_TEMP_INPUT_MIN -127000 +#define MAX6621_TEMP_INPUT_MAX 128000 +#define MAX6621_TEMP_ALERT_CHAN_SHIFT 1 + +#define MAX6621_TEMP_S0D0_REG 0x00 +#define MAX6621_TEMP_S0D1_REG 0x01 +#define MAX6621_TEMP_S1D0_REG 0x02 +#define MAX6621_TEMP_S1D1_REG 0x03 +#define MAX6621_TEMP_S2D0_REG 0x04 +#define MAX6621_TEMP_S2D1_REG 0x05 +#define MAX6621_TEMP_S3D0_REG 0x06 +#define MAX6621_TEMP_S3D1_REG 0x07 +#define MAX6621_TEMP_MAX_REG 0x08 +#define MAX6621_TEMP_MAX_ADDR_REG 0x0a +#define MAX6621_TEMP_ALERT_CAUSE_REG 0x0b +#define MAX6621_CONFIG0_REG 0x0c +#define MAX6621_CONFIG1_REG 0x0d +#define MAX6621_CONFIG2_REG 0x0e +#define MAX6621_CONFIG3_REG 0x0f +#define MAX6621_TEMP_S0_ALERT_REG 0x10 +#define MAX6621_TEMP_S1_ALERT_REG 0x11 +#define MAX6621_TEMP_S2_ALERT_REG 0x12 +#define MAX6621_TEMP_S3_ALERT_REG 0x13 +#define MAX6621_CLEAR_ALERT_REG 0x15 +#define MAX6621_REG_MAX (MAX6621_CLEAR_ALERT_REG + 1) +#define MAX6621_REG_TEMP_SHIFT 0x06 + +#define MAX6621_ENABLE_TEMP_ALERTS_BIT 4 +#define MAX6621_ENABLE_I2C_CRC_BIT 5 +#define MAX6621_ENABLE_ALTERNATE_DATA 6 +#define MAX6621_ENABLE_LOCKUP_TO 7 +#define MAX6621_ENABLE_S0D0_BIT 8 +#define MAX6621_ENABLE_S3D1_BIT 15 +#define MAX6621_ENABLE_TEMP_ALL GENMASK(MAX6621_ENABLE_S3D1_BIT, \ + MAX6621_ENABLE_S0D0_BIT) +#define MAX6621_POLL_DELAY_MASK 0x5 +#define MAX6621_CONFIG0_INIT (MAX6621_ENABLE_TEMP_ALL | \ + BIT(MAX6621_ENABLE_LOCKUP_TO) | \ + BIT(MAX6621_ENABLE_I2C_CRC_BIT) | \ + MAX6621_POLL_DELAY_MASK) +#define MAX6621_PECI_BIT_TIME 0x2 +#define MAX6621_PECI_RETRY_NUM 0x3 +#define MAX6621_CONFIG1_INIT ((MAX6621_PECI_BIT_TIME << 8) | \ + MAX6621_PECI_RETRY_NUM) + +/* Error codes */ +#define MAX6621_TRAN_FAILED 0x8100 /* + * PECI transaction failed for more + * than the configured number of + * consecutive retries. + */ +#define MAX6621_POOL_DIS 0x8101 /* + * Polling disabled for requested + * socket/domain. + */ +#define MAX6621_POOL_UNCOMPLETE 0x8102 /* + * First poll not yet completed for + * requested socket/domain (on + * startup). + */ +#define MAX6621_SD_DIS 0x8103 /* + * Read maximum temperature requested, + * but no sockets/domains enabled or + * all enabled sockets/domains have + * errors; or read maximum temperature + * address requested, but read maximum + * temperature was not called. + */ +#define MAX6621_ALERT_DIS 0x8104 /* + * Get alert socket/domain requested, + * but no alert active. + */ +#define MAX6621_PECI_ERR_MIN 0x8000 /* Intel spec PECI error min value. */ +#define MAX6621_PECI_ERR_MAX 0x80ff /* Intel spec PECI error max value. */ + +static const u32 max6621_temp_regs[] = { + MAX6621_TEMP_MAX_REG, MAX6621_TEMP_S0D0_REG, MAX6621_TEMP_S1D0_REG, + MAX6621_TEMP_S2D0_REG, MAX6621_TEMP_S3D0_REG, MAX6621_TEMP_S0D1_REG, + MAX6621_TEMP_S1D1_REG, MAX6621_TEMP_S2D1_REG, MAX6621_TEMP_S3D1_REG, +}; + +static const char *const max6621_temp_labels[] = { + "maximum", + "socket0_0", + "socket1_0", + "socket2_0", + "socket3_0", + "socket0_1", + "socket1_1", + "socket2_1", + "socket3_1", +}; + +static const int max6621_temp_alert_chan2reg[] = { + MAX6621_TEMP_S0_ALERT_REG, + MAX6621_TEMP_S1_ALERT_REG, + MAX6621_TEMP_S2_ALERT_REG, + MAX6621_TEMP_S3_ALERT_REG, +}; + +/** + * struct max6621_data - private data: + * + * @client: I2C client; + * @regmap: register map handle; + * @input_chan2reg: mapping from channel to register; + */ +struct max6621_data { + struct i2c_client *client; + struct regmap *regmap; + int input_chan2reg[MAX6621_TEMP_INPUT_REG_NUM + 1]; +}; + +static long max6621_temp_mc2reg(long val) +{ + return (val / 1000L) << MAX6621_REG_TEMP_SHIFT; +} + +static umode_t +max6621_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + /* Skip channels which are not physically conncted. */ + if (((struct max6621_data *)data)->input_chan2reg[channel] < 0) + return 0; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + case hwmon_temp_crit_alarm: + return 0444; + case hwmon_temp_offset: + case hwmon_temp_crit: + return 0644; + default: + break; + } + + default: + break; + } + + return 0; +} + +static int max6621_verify_reg_data(struct device *dev, int regval) +{ + if (regval >= MAX6621_PECI_ERR_MIN && + regval <= MAX6621_PECI_ERR_MAX) { + dev_dbg(dev, "PECI error code - err 0x%04x.\n", + regval); + + return -EIO; + } + + switch (regval) { + case MAX6621_TRAN_FAILED: + dev_dbg(dev, "PECI transaction failed - err 0x%04x.\n", + regval); + return -EIO; + case MAX6621_POOL_DIS: + dev_dbg(dev, "Polling disabled - err 0x%04x.\n", regval); + return -EOPNOTSUPP; + case MAX6621_POOL_UNCOMPLETE: + dev_dbg(dev, "First poll not completed on startup - err 0x%04x.\n", + regval); + return -EIO; + case MAX6621_SD_DIS: + dev_dbg(dev, "Resource is disabled - err 0x%04x.\n", regval); + return -EOPNOTSUPP; + case MAX6621_ALERT_DIS: + dev_dbg(dev, "No alert active - err 0x%04x.\n", regval); + return -EOPNOTSUPP; + default: + return 0; + } +} + +static int +max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct max6621_data *data = dev_get_drvdata(dev); + u32 regval; + int reg; + s8 temp; + int ret; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + reg = data->input_chan2reg[channel]; + ret = regmap_read(data->regmap, reg, ®val); + if (ret) + return ret; + + ret = max6621_verify_reg_data(dev, regval); + if (ret) + return ret; + + /* + * Bit MAX6621_REG_TEMP_SHIFT represents 1 degree step. + * The temperature is given in two's complement and 8 + * bits is used for the register conversion. + */ + temp = (regval >> MAX6621_REG_TEMP_SHIFT); + *val = temp * 1000L; + + break; + case hwmon_temp_offset: + ret = regmap_read(data->regmap, MAX6621_CONFIG2_REG, + ®val); + if (ret) + return ret; + + ret = max6621_verify_reg_data(dev, regval); + if (ret) + return ret; + + *val = (regval >> MAX6621_REG_TEMP_SHIFT) * + 1000L; + + break; + case hwmon_temp_crit: + channel -= MAX6621_TEMP_ALERT_CHAN_SHIFT; + reg = max6621_temp_alert_chan2reg[channel]; + ret = regmap_read(data->regmap, reg, ®val); + if (ret) + return ret; + + ret = max6621_verify_reg_data(dev, regval); + if (ret) + return ret; + + *val = regval * 1000L; + + break; + case hwmon_temp_crit_alarm: + /* + * Set val to zero to recover the case, when reading + * MAX6621_TEMP_ALERT_CAUSE_REG results in for example + * MAX6621_ALERT_DIS. Reading will return with error, + * but in such case alarm should be returned as 0. + */ + *val = 0; + ret = regmap_read(data->regmap, + MAX6621_TEMP_ALERT_CAUSE_REG, + ®val); + if (ret) + return ret; + + ret = max6621_verify_reg_data(dev, regval); + if (ret) { + /* Do not report error if alert is disabled. */ + if (regval == MAX6621_ALERT_DIS) + return 0; + else + return ret; + } + + /* + * Clear the alert automatically, using send-byte + * smbus protocol for clearing alert. + */ + if (regval) { + ret = i2c_smbus_write_byte(data->client, + MAX6621_CLEAR_ALERT_REG); + if (!ret) + return ret; + } + + *val = !!regval; + + break; + default: + return -EOPNOTSUPP; + } + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int +max6621_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct max6621_data *data = dev_get_drvdata(dev); + u32 reg; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_offset: + /* Clamp to allowed range to prevent overflow. */ + val = clamp_val(val, MAX6621_TEMP_INPUT_MIN, + MAX6621_TEMP_INPUT_MAX); + val = max6621_temp_mc2reg(val); + + return regmap_write(data->regmap, + MAX6621_CONFIG2_REG, val); + case hwmon_temp_crit: + channel -= MAX6621_TEMP_ALERT_CHAN_SHIFT; + reg = max6621_temp_alert_chan2reg[channel]; + /* Clamp to allowed range to prevent overflow. */ + val = clamp_val(val, MAX6621_TEMP_INPUT_MIN, + MAX6621_TEMP_INPUT_MAX); + val = val / 1000L; + + return regmap_write(data->regmap, reg, val); + default: + return -EOPNOTSUPP; + } + break; + + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static int +max6621_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = max6621_temp_labels[channel]; + return 0; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static bool max6621_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX6621_CONFIG0_REG: + case MAX6621_CONFIG1_REG: + case MAX6621_CONFIG2_REG: + case MAX6621_CONFIG3_REG: + case MAX6621_TEMP_S0_ALERT_REG: + case MAX6621_TEMP_S1_ALERT_REG: + case MAX6621_TEMP_S2_ALERT_REG: + case MAX6621_TEMP_S3_ALERT_REG: + case MAX6621_TEMP_ALERT_CAUSE_REG: + return true; + } + return false; +} + +static bool max6621_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX6621_TEMP_S0D0_REG: + case MAX6621_TEMP_S0D1_REG: + case MAX6621_TEMP_S1D0_REG: + case MAX6621_TEMP_S1D1_REG: + case MAX6621_TEMP_S2D0_REG: + case MAX6621_TEMP_S2D1_REG: + case MAX6621_TEMP_S3D0_REG: + case MAX6621_TEMP_S3D1_REG: + case MAX6621_TEMP_MAX_REG: + case MAX6621_TEMP_MAX_ADDR_REG: + case MAX6621_CONFIG0_REG: + case MAX6621_CONFIG1_REG: + case MAX6621_CONFIG2_REG: + case MAX6621_CONFIG3_REG: + case MAX6621_TEMP_S0_ALERT_REG: + case MAX6621_TEMP_S1_ALERT_REG: + case MAX6621_TEMP_S2_ALERT_REG: + case MAX6621_TEMP_S3_ALERT_REG: + return true; + } + return false; +} + +static bool max6621_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX6621_TEMP_S0D0_REG: + case MAX6621_TEMP_S0D1_REG: + case MAX6621_TEMP_S1D0_REG: + case MAX6621_TEMP_S1D1_REG: + case MAX6621_TEMP_S2D0_REG: + case MAX6621_TEMP_S2D1_REG: + case MAX6621_TEMP_S3D0_REG: + case MAX6621_TEMP_S3D1_REG: + case MAX6621_TEMP_MAX_REG: + case MAX6621_TEMP_S0_ALERT_REG: + case MAX6621_TEMP_S1_ALERT_REG: + case MAX6621_TEMP_S2_ALERT_REG: + case MAX6621_TEMP_S3_ALERT_REG: + case MAX6621_TEMP_ALERT_CAUSE_REG: + return true; + } + return false; +} + +static const struct reg_default max6621_regmap_default[] = { + { MAX6621_CONFIG0_REG, MAX6621_CONFIG0_INIT }, + { MAX6621_CONFIG1_REG, MAX6621_CONFIG1_INIT }, +}; + +static const struct regmap_config max6621_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = MAX6621_REG_MAX, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .cache_type = REGCACHE_FLAT, + .writeable_reg = max6621_writeable_reg, + .readable_reg = max6621_readable_reg, + .volatile_reg = max6621_volatile_reg, + .reg_defaults = max6621_regmap_default, + .num_reg_defaults = ARRAY_SIZE(max6621_regmap_default), +}; + +static u32 max6621_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info max6621_chip = { + .type = hwmon_chip, + .config = max6621_chip_config, +}; + +static const u32 max6621_temp_config[] = { + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + 0 +}; + +static const struct hwmon_channel_info max6621_temp = { + .type = hwmon_temp, + .config = max6621_temp_config, +}; + +static const struct hwmon_channel_info *max6621_info[] = { + &max6621_chip, + &max6621_temp, + NULL +}; + +static const struct hwmon_ops max6621_hwmon_ops = { + .read = max6621_read, + .write = max6621_write, + .read_string = max6621_read_string, + .is_visible = max6621_is_visible, +}; + +static const struct hwmon_chip_info max6621_chip_info = { + .ops = &max6621_hwmon_ops, + .info = max6621_info, +}; + +static int max6621_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct max6621_data *data; + struct device *hwmon_dev; + int i; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &max6621_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + i2c_set_clientdata(client, data); + data->client = client; + + /* Set CONFIG0 register masking temperature alerts and PEC. */ + ret = regmap_write(data->regmap, MAX6621_CONFIG0_REG, + MAX6621_CONFIG0_INIT); + if (ret) + return ret; + + /* Set CONFIG1 register for PEC access retry number. */ + ret = regmap_write(data->regmap, MAX6621_CONFIG1_REG, + MAX6621_CONFIG1_INIT); + if (ret) + return ret; + + /* Sync registers with hardware. */ + regcache_mark_dirty(data->regmap); + ret = regcache_sync(data->regmap); + if (ret) + return ret; + + /* Verify which temperature input registers are enabled. */ + for (i = 0; i < MAX6621_TEMP_INPUT_REG_NUM; i++) { + ret = i2c_smbus_read_word_data(client, max6621_temp_regs[i]); + if (ret < 0) + return ret; + ret = max6621_verify_reg_data(dev, ret); + if (ret) { + data->input_chan2reg[i] = -1; + continue; + } + + data->input_chan2reg[i] = max6621_temp_regs[i]; + } + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &max6621_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max6621_id[] = { + { MAX6621_DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6621_id); + +static const struct of_device_id max6621_of_match[] = { + { .compatible = "maxim,max6621" }, + { } +}; +MODULE_DEVICE_TABLE(of, max6621_of_match); + +static struct i2c_driver max6621_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = MAX6621_DRV_NAME, + .of_match_table = of_match_ptr(max6621_of_match), + }, + .probe = max6621_probe, + .id_table = max6621_id, +}; + +module_i2c_driver(max6621_driver); + +MODULE_AUTHOR("Vadim Pasternak "); +MODULE_DESCRIPTION("Driver for Maxim MAX6621"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 24fae2b5b61da0cec6b94059552ae97ccc498ccc Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 3 Oct 2017 18:08:28 +0000 Subject: Documentation: devicetree: add max6621 device Add device record for Maxim MAX6621 temperature sensor device. Signed-off-by: Vadim Pasternak Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt index af284fbd4d23..8bcac6ee73da 100644 --- a/Documentation/devicetree/bindings/trivial-devices.txt +++ b/Documentation/devicetree/bindings/trivial-devices.txt @@ -71,6 +71,7 @@ isil,isl29028 Intersil ISL29028 Ambient Light and Proximity Sensor isil,isl29030 Intersil ISL29030 Ambient Light and Proximity Sensor maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs +maxim,max6621 PECI-to-I2C translator for PECI-to-SMBus/I2C protocol conversion maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface mc,rv3029c2 Real Time Clock Module with I2C-Bus mcube,mc3230 mCube 3-axis 8-bit digital accelerometer -- cgit v1.2.3 From 9dfe310ed5f0802cba5ec29f2b5f5f92ba8ad9e9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:03 +0200 Subject: hwmon: (gpio-fan) Move DT bindings to the right place This moves the GPIO fan bindings to the hwmon bindings. The GPIO fan is a hwmon class hardware, not related to GPIO other than being a consumer of GPIOs. Cc: devicetree@vger.kernel.org Signed-off-by: Linus Walleij Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- .../devicetree/bindings/gpio/gpio-fan.txt | 40 ---------------------- .../devicetree/bindings/hwmon/gpio-fan.txt | 40 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-fan.txt create mode 100644 Documentation/devicetree/bindings/hwmon/gpio-fan.txt diff --git a/Documentation/devicetree/bindings/gpio/gpio-fan.txt b/Documentation/devicetree/bindings/gpio/gpio-fan.txt deleted file mode 100644 index 439a7430fc68..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-fan.txt +++ /dev/null @@ -1,40 +0,0 @@ -Bindings for fan connected to GPIO lines - -Required properties: -- compatible : "gpio-fan" - -Optional properties: -- gpios: Specifies the pins that map to bits in the control value, - ordered MSB-->LSB. -- gpio-fan,speed-map: A mapping of possible fan RPM speeds and the - control value that should be set to achieve them. This array - must have the RPM values in ascending order. -- alarm-gpios: This pin going active indicates something is wrong with - the fan, and a udev event will be fired. -- cooling-cells: If used as a cooling device, must be <2> - Also see: Documentation/devicetree/bindings/thermal/thermal.txt - min and max states are derived from the speed-map of the fan. - -Note: At least one the "gpios" or "alarm-gpios" properties must be set. - -Examples: - - gpio_fan { - compatible = "gpio-fan"; - gpios = <&gpio1 14 1 - &gpio1 13 1>; - gpio-fan,speed-map = <0 0 - 3000 1 - 6000 2>; - alarm-gpios = <&gpio1 15 1>; - }; - gpio_fan_cool: gpio_fan { - compatible = "gpio-fan"; - gpios = <&gpio2 14 1 - &gpio2 13 1>; - gpio-fan,speed-map = <0 0>, - <3000 1>, - <6000 2>; - alarm-gpios = <&gpio2 15 1>; - #cooling-cells = <2>; /* min followed by max */ - }; diff --git a/Documentation/devicetree/bindings/hwmon/gpio-fan.txt b/Documentation/devicetree/bindings/hwmon/gpio-fan.txt new file mode 100644 index 000000000000..439a7430fc68 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/gpio-fan.txt @@ -0,0 +1,40 @@ +Bindings for fan connected to GPIO lines + +Required properties: +- compatible : "gpio-fan" + +Optional properties: +- gpios: Specifies the pins that map to bits in the control value, + ordered MSB-->LSB. +- gpio-fan,speed-map: A mapping of possible fan RPM speeds and the + control value that should be set to achieve them. This array + must have the RPM values in ascending order. +- alarm-gpios: This pin going active indicates something is wrong with + the fan, and a udev event will be fired. +- cooling-cells: If used as a cooling device, must be <2> + Also see: Documentation/devicetree/bindings/thermal/thermal.txt + min and max states are derived from the speed-map of the fan. + +Note: At least one the "gpios" or "alarm-gpios" properties must be set. + +Examples: + + gpio_fan { + compatible = "gpio-fan"; + gpios = <&gpio1 14 1 + &gpio1 13 1>; + gpio-fan,speed-map = <0 0 + 3000 1 + 6000 2>; + alarm-gpios = <&gpio1 15 1>; + }; + gpio_fan_cool: gpio_fan { + compatible = "gpio-fan"; + gpios = <&gpio2 14 1 + &gpio2 13 1>; + gpio-fan,speed-map = <0 0>, + <3000 1>, + <6000 2>; + alarm-gpios = <&gpio2 15 1>; + #cooling-cells = <2>; /* min followed by max */ + }; -- cgit v1.2.3 From f9013c1677426df09022fe6fa0121e6fe9e1a0fa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:04 +0200 Subject: hwmon: (gpio-fan) Use local variable pointers Create local struct device *dev and device_node *np pointers to make the code easier to read. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 9c355b9d31c5..f29cee9398ef 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -541,22 +541,24 @@ static int gpio_fan_probe(struct platform_device *pdev) { int err; struct gpio_fan_data *fan_data; - struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct gpio_fan_platform_data *pdata = dev_get_platdata(dev); - fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data), + fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data), GFP_KERNEL); if (!fan_data) return -ENOMEM; #ifdef CONFIG_OF_GPIO if (!pdata) { - pdata = devm_kzalloc(&pdev->dev, + pdata = devm_kzalloc(dev, sizeof(struct gpio_fan_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - err = gpio_fan_get_of_pdata(&pdev->dev, pdata); + err = gpio_fan_get_of_pdata(dev, pdata); if (err) return err; } @@ -587,14 +589,14 @@ static int gpio_fan_probe(struct platform_device *pdev) /* Make this driver part of hwmon class. */ fan_data->hwmon_dev = - devm_hwmon_device_register_with_groups(&pdev->dev, + devm_hwmon_device_register_with_groups(dev, "gpio_fan", fan_data, gpio_fan_groups); if (IS_ERR(fan_data->hwmon_dev)) return PTR_ERR(fan_data->hwmon_dev); #ifdef CONFIG_OF_GPIO /* Optional cooling device register for Device tree platforms */ - fan_data->cdev = thermal_of_cooling_device_register(pdev->dev.of_node, + fan_data->cdev = thermal_of_cooling_device_register(np, "gpio-fan", fan_data, &gpio_fan_cool_ops); @@ -604,7 +606,7 @@ static int gpio_fan_probe(struct platform_device *pdev) &gpio_fan_cool_ops); #endif /* CONFIG_OF_GPIO */ - dev_info(&pdev->dev, "GPIO fan initialized\n"); + dev_info(dev, "GPIO fan initialized\n"); return 0; } -- cgit v1.2.3 From ef7a612415958de1f9afd86235d38b14975d0b7c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:05 +0200 Subject: hwmon: (gpio-fan) Localize platform data There is not a single user of the platform data header in . We can conclude that all current users are probing from the device tree, so start simplifying the code by pulling the header into the driver. Convert "unsigned" to "unsigned int" in the process to make checkpatch happy. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 23 ++++++++++++++++++++++- include/linux/gpio-fan.h | 36 ------------------------------------ 2 files changed, 22 insertions(+), 37 deletions(-) delete mode 100644 include/linux/gpio-fan.h diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index f29cee9398ef..cfa8d9b578dd 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -30,12 +30,33 @@ #include #include #include -#include #include #include #include #include +struct gpio_fan_alarm { + unsigned int gpio; + unsigned int active_low; +}; + +struct gpio_fan_speed { + int rpm; + int ctrl_val; +}; + +struct gpio_fan_platform_data { + int num_ctrl; + unsigned int *ctrl; /* fan control GPIOs. */ + struct gpio_fan_alarm *alarm; /* fan alarm GPIO. */ + /* + * Speed conversion array: rpm from/to GPIO bit field. + * This array _must_ be sorted in ascending rpm order. + */ + int num_speed; + struct gpio_fan_speed *speed; +}; + struct gpio_fan_data { struct platform_device *pdev; struct device *hwmon_dev; diff --git a/include/linux/gpio-fan.h b/include/linux/gpio-fan.h deleted file mode 100644 index 096659169215..000000000000 --- a/include/linux/gpio-fan.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * include/linux/gpio-fan.h - * - * Platform data structure for GPIO fan driver - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __LINUX_GPIO_FAN_H -#define __LINUX_GPIO_FAN_H - -struct gpio_fan_alarm { - unsigned gpio; - unsigned active_low; -}; - -struct gpio_fan_speed { - int rpm; - int ctrl_val; -}; - -struct gpio_fan_platform_data { - int num_ctrl; - unsigned *ctrl; /* fan control GPIOs. */ - struct gpio_fan_alarm *alarm; /* fan alarm GPIO. */ - /* - * Speed conversion array: rpm from/to GPIO bit field. - * This array _must_ be sorted in ascending rpm order. - */ - int num_speed; - struct gpio_fan_speed *speed; -}; - -#endif /* __LINUX_GPIO_FAN_H */ -- cgit v1.2.3 From 8c0eb9bc52fad2fec7a5ff40d5da85b74232f5de Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:06 +0200 Subject: hwmon: (gpio-fan) Send around device pointer The driver is storing the struct platform_device *pdev pointer but what it is really using and want to pass around is a struct device *dev pointer. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index cfa8d9b578dd..ad7d8fdf4f81 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -58,7 +58,7 @@ struct gpio_fan_platform_data { }; struct gpio_fan_data { - struct platform_device *pdev; + struct device *dev; struct device *hwmon_dev; /* Cooling device if any */ struct thermal_cooling_device *cdev; @@ -85,8 +85,8 @@ static void fan_alarm_notify(struct work_struct *ws) struct gpio_fan_data *fan_data = container_of(ws, struct gpio_fan_data, alarm_work); - sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm"); - kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE); + sysfs_notify(&fan_data->dev->kobj, NULL, "fan1_alarm"); + kobject_uevent(&fan_data->dev->kobj, KOBJ_CHANGE); } static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) @@ -118,11 +118,11 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data, { int err; int alarm_irq; - struct platform_device *pdev = fan_data->pdev; + struct device *dev = fan_data->dev; fan_data->alarm = alarm; - err = devm_gpio_request(&pdev->dev, alarm->gpio, "GPIO fan alarm"); + err = devm_gpio_request(dev, alarm->gpio, "GPIO fan alarm"); if (err) return err; @@ -140,7 +140,7 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data, INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); - err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler, + err = devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, "GPIO fan alarm", fan_data); return err; } @@ -191,7 +191,7 @@ static int get_fan_speed_index(struct gpio_fan_data *fan_data) if (fan_data->speed[i].ctrl_val == ctrl_val) return i; - dev_warn(&fan_data->pdev->dev, + dev_warn(fan_data->dev, "missing speed array entry for GPIO value 0x%x\n", ctrl_val); return -ENODEV; @@ -382,13 +382,13 @@ static const struct attribute_group *gpio_fan_groups[] = { static int fan_ctrl_init(struct gpio_fan_data *fan_data, struct gpio_fan_platform_data *pdata) { - struct platform_device *pdev = fan_data->pdev; + struct device *dev = fan_data->dev; int num_ctrl = pdata->num_ctrl; unsigned *ctrl = pdata->ctrl; int i, err; for (i = 0; i < num_ctrl; i++) { - err = devm_gpio_request(&pdev->dev, ctrl[i], + err = devm_gpio_request(dev, ctrl[i], "GPIO fan control"); if (err) return err; @@ -588,7 +588,7 @@ static int gpio_fan_probe(struct platform_device *pdev) return -EINVAL; #endif /* CONFIG_OF_GPIO */ - fan_data->pdev = pdev; + fan_data->dev = dev; platform_set_drvdata(pdev, fan_data); mutex_init(&fan_data->lock); -- cgit v1.2.3 From a9b4c8afcd3d6c2b068e6ecf1a22ab26dd8c200e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:07 +0200 Subject: hwmon: (gpio-fan) Mandate OF_GPIO and cut pdata path We have no users of platform data, we made platform data driver-local, so cut all #ifdefs for the platform data case, and depend on the Kconfig CONFIG_OF_GPIO symbol. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/gpio-fan.c | 36 ++++++++++-------------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index fae8a8904c10..7ad017690e3a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -552,6 +552,7 @@ config SENSORS_G762 config SENSORS_GPIO_FAN tristate "GPIO fan" + depends on OF_GPIO depends on GPIOLIB || COMPILE_TEST depends on THERMAL || THERMAL=n help diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index ad7d8fdf4f81..55dbdb223e02 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -453,7 +453,6 @@ static const struct thermal_cooling_device_ops gpio_fan_cool_ops = { .set_cur_state = gpio_fan_set_cur_state, }; -#ifdef CONFIG_OF_GPIO /* * Translate OpenFirmware node properties into platform_data */ @@ -556,7 +555,6 @@ static const struct of_device_id of_gpio_fan_match[] = { {}, }; MODULE_DEVICE_TABLE(of, of_gpio_fan_match); -#endif /* CONFIG_OF_GPIO */ static int gpio_fan_probe(struct platform_device *pdev) { @@ -564,29 +562,22 @@ static int gpio_fan_probe(struct platform_device *pdev) struct gpio_fan_data *fan_data; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct gpio_fan_platform_data *pdata = dev_get_platdata(dev); + struct gpio_fan_platform_data *pdata; fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data), GFP_KERNEL); if (!fan_data) return -ENOMEM; -#ifdef CONFIG_OF_GPIO - if (!pdata) { - pdata = devm_kzalloc(dev, - sizeof(struct gpio_fan_platform_data), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - err = gpio_fan_get_of_pdata(dev, pdata); - if (err) - return err; - } -#else /* CONFIG_OF_GPIO */ + pdata = devm_kzalloc(dev, + sizeof(struct gpio_fan_platform_data), + GFP_KERNEL); if (!pdata) - return -EINVAL; -#endif /* CONFIG_OF_GPIO */ + return -ENOMEM; + + err = gpio_fan_get_of_pdata(dev, pdata); + if (err) + return err; fan_data->dev = dev; platform_set_drvdata(pdev, fan_data); @@ -615,17 +606,12 @@ static int gpio_fan_probe(struct platform_device *pdev) gpio_fan_groups); if (IS_ERR(fan_data->hwmon_dev)) return PTR_ERR(fan_data->hwmon_dev); -#ifdef CONFIG_OF_GPIO + /* Optional cooling device register for Device tree platforms */ fan_data->cdev = thermal_of_cooling_device_register(np, "gpio-fan", fan_data, &gpio_fan_cool_ops); -#else /* CONFIG_OF_GPIO */ - /* Optional cooling device register for non Device tree platforms */ - fan_data->cdev = thermal_cooling_device_register("gpio-fan", fan_data, - &gpio_fan_cool_ops); -#endif /* CONFIG_OF_GPIO */ dev_info(dev, "GPIO fan initialized\n"); @@ -686,9 +672,7 @@ static struct platform_driver gpio_fan_driver = { .driver = { .name = "gpio-fan", .pm = GPIO_FAN_PM, -#ifdef CONFIG_OF_GPIO .of_match_table = of_match_ptr(of_gpio_fan_match), -#endif }, }; -- cgit v1.2.3 From b5482f7e6cc5639440bfa0b9bb4a3a9732883f53 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:08 +0200 Subject: hwmon: (gpio-fan) Get rid of platform data struct We are not passing the platform data struct into the driver from the outside, there is no point of having it around separately so instead of first populating the platform data struct and assigning the result into the same variables in the state container (struct gpio_fan_data) just assign the configuration from the device tree directly into the state container members. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 88 +++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 58 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 55dbdb223e02..000c8d2e0987 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -45,18 +45,6 @@ struct gpio_fan_speed { int ctrl_val; }; -struct gpio_fan_platform_data { - int num_ctrl; - unsigned int *ctrl; /* fan control GPIOs. */ - struct gpio_fan_alarm *alarm; /* fan alarm GPIO. */ - /* - * Speed conversion array: rpm from/to GPIO bit field. - * This array _must_ be sorted in ascending rpm order. - */ - int num_speed; - struct gpio_fan_speed *speed; -}; - struct gpio_fan_data { struct device *dev; struct device *hwmon_dev; @@ -113,14 +101,12 @@ static ssize_t fan1_alarm_show(struct device *dev, static DEVICE_ATTR_RO(fan1_alarm); -static int fan_alarm_init(struct gpio_fan_data *fan_data, - struct gpio_fan_alarm *alarm) +static int fan_alarm_init(struct gpio_fan_data *fan_data) { int err; int alarm_irq; struct device *dev = fan_data->dev; - - fan_data->alarm = alarm; + struct gpio_fan_alarm *alarm = fan_data->alarm; err = devm_gpio_request(dev, alarm->gpio, "GPIO fan alarm"); if (err) @@ -379,12 +365,11 @@ static const struct attribute_group *gpio_fan_groups[] = { NULL }; -static int fan_ctrl_init(struct gpio_fan_data *fan_data, - struct gpio_fan_platform_data *pdata) +static int fan_ctrl_init(struct gpio_fan_data *fan_data) { struct device *dev = fan_data->dev; - int num_ctrl = pdata->num_ctrl; - unsigned *ctrl = pdata->ctrl; + int num_ctrl = fan_data->num_ctrl; + unsigned int *ctrl = fan_data->ctrl; int i, err; for (i = 0; i < num_ctrl; i++) { @@ -399,10 +384,6 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, return err; } - fan_data->num_ctrl = num_ctrl; - fan_data->ctrl = ctrl; - fan_data->num_speed = pdata->num_speed; - 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) @@ -456,21 +437,19 @@ static const struct thermal_cooling_device_ops gpio_fan_cool_ops = { /* * Translate OpenFirmware node properties into platform_data */ -static int gpio_fan_get_of_pdata(struct device *dev, - struct gpio_fan_platform_data *pdata) +static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) { - struct device_node *node; struct gpio_fan_speed *speed; + struct device *dev = fan_data->dev; + struct device_node *np = dev->of_node; unsigned *ctrl; unsigned i; u32 u; struct property *prop; const __be32 *p; - node = dev->of_node; - /* Alarm GPIO if one exists */ - if (of_gpio_named_count(node, "alarm-gpios") > 0) { + if (of_gpio_named_count(np, "alarm-gpios") > 0) { struct gpio_fan_alarm *alarm; int val; enum of_gpio_flags flags; @@ -480,39 +459,39 @@ static int gpio_fan_get_of_pdata(struct device *dev, if (!alarm) return -ENOMEM; - val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); + val = of_get_named_gpio_flags(np, "alarm-gpios", 0, &flags); if (val < 0) return val; alarm->gpio = val; alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; - pdata->alarm = alarm; + fan_data->alarm = alarm; } /* Fill GPIO pin array */ - pdata->num_ctrl = of_gpio_count(node); - if (pdata->num_ctrl <= 0) { - if (pdata->alarm) + fan_data->num_ctrl = of_gpio_count(np); + if (fan_data->num_ctrl <= 0) { + if (fan_data->alarm) return 0; dev_err(dev, "DT properties empty / missing"); return -ENODEV; } - ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned), - GFP_KERNEL); + ctrl = devm_kzalloc(dev, fan_data->num_ctrl * sizeof(unsigned int), + GFP_KERNEL); if (!ctrl) return -ENOMEM; - for (i = 0; i < pdata->num_ctrl; i++) { + for (i = 0; i < fan_data->num_ctrl; i++) { int val; - val = of_get_gpio(node, i); + val = of_get_gpio(np, i); if (val < 0) return val; ctrl[i] = val; } - pdata->ctrl = ctrl; + fan_data->ctrl = ctrl; /* Get number of RPM/ctrl_val pairs in speed map */ - prop = of_find_property(node, "gpio-fan,speed-map", &i); + prop = of_find_property(np, "gpio-fan,speed-map", &i); if (!prop) { dev_err(dev, "gpio-fan,speed-map DT property missing"); return -ENODEV; @@ -522,7 +501,7 @@ static int gpio_fan_get_of_pdata(struct device *dev, dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries"); return -ENODEV; } - pdata->num_speed = i / 2; + fan_data->num_speed = i / 2; /* * Populate speed map @@ -530,12 +509,12 @@ static int gpio_fan_get_of_pdata(struct device *dev, * this needs splitting into pairs to create gpio_fan_speed structs */ speed = devm_kzalloc(dev, - pdata->num_speed * sizeof(struct gpio_fan_speed), + fan_data->num_speed * sizeof(struct gpio_fan_speed), GFP_KERNEL); if (!speed) return -ENOMEM; p = NULL; - for (i = 0; i < pdata->num_speed; i++) { + for (i = 0; i < fan_data->num_speed; i++) { p = of_prop_next_u32(prop, p, &u); if (!p) return -ENODEV; @@ -545,7 +524,7 @@ static int gpio_fan_get_of_pdata(struct device *dev, return -ENODEV; speed[i].ctrl_val = u; } - pdata->speed = speed; + fan_data->speed = speed; return 0; } @@ -562,20 +541,13 @@ static int gpio_fan_probe(struct platform_device *pdev) struct gpio_fan_data *fan_data; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct gpio_fan_platform_data *pdata; fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data), GFP_KERNEL); if (!fan_data) return -ENOMEM; - pdata = devm_kzalloc(dev, - sizeof(struct gpio_fan_platform_data), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - err = gpio_fan_get_of_pdata(dev, pdata); + err = gpio_fan_get_of_data(fan_data); if (err) return err; @@ -584,17 +556,17 @@ static int gpio_fan_probe(struct platform_device *pdev) mutex_init(&fan_data->lock); /* Configure alarm GPIO if available. */ - if (pdata->alarm) { - err = fan_alarm_init(fan_data, pdata->alarm); + if (fan_data->alarm) { + err = fan_alarm_init(fan_data); if (err) return err; } /* Configure control GPIOs if available. */ - if (pdata->ctrl && pdata->num_ctrl > 0) { - if (!pdata->speed || pdata->num_speed <= 1) + if (fan_data->ctrl && fan_data->num_ctrl > 0) { + if (!fan_data->speed || fan_data->num_speed <= 1) return -EINVAL; - err = fan_ctrl_init(fan_data, pdata); + err = fan_ctrl_init(fan_data); if (err) return err; } -- cgit v1.2.3 From c9933cb16f166de57b0b8bce170c1b9476b89836 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:09 +0200 Subject: hwmon: (gpio-fan) Get rid of the gpio alarm struct There is no point in storing the GPIO alarm settings in their own struct so merge this into the main state container. Convert the variables from "unsigned" to "unsigned int" to make checkpatch happy. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 000c8d2e0987..568ce4b25a9e 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -35,11 +35,6 @@ #include #include -struct gpio_fan_alarm { - unsigned int gpio; - unsigned int active_low; -}; - struct gpio_fan_speed { int rpm; int ctrl_val; @@ -60,7 +55,8 @@ struct gpio_fan_data { int resume_speed; #endif bool pwm_enable; - struct gpio_fan_alarm *alarm; + unsigned int alarm_gpio; + unsigned int alarm_gpio_active_low; struct work_struct alarm_work; }; @@ -90,10 +86,9 @@ static ssize_t fan1_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); - struct gpio_fan_alarm *alarm = fan_data->alarm; - int value = gpio_get_value_cansleep(alarm->gpio); + int value = gpio_get_value_cansleep(fan_data->alarm_gpio); - if (alarm->active_low) + if (fan_data->alarm_gpio_active_low) value = !value; return sprintf(buf, "%d\n", value); @@ -106,13 +101,12 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data) int err; int alarm_irq; struct device *dev = fan_data->dev; - struct gpio_fan_alarm *alarm = fan_data->alarm; - err = devm_gpio_request(dev, alarm->gpio, "GPIO fan alarm"); + err = devm_gpio_request(dev, fan_data->alarm_gpio, "GPIO fan alarm"); if (err) return err; - err = gpio_direction_input(alarm->gpio); + err = gpio_direction_input(fan_data->alarm_gpio); if (err) return err; @@ -120,7 +114,7 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data) * If the alarm GPIO don't support interrupts, just leave * without initializing the fail notification support. */ - alarm_irq = gpio_to_irq(alarm->gpio); + alarm_irq = gpio_to_irq(fan_data->alarm_gpio); if (alarm_irq < 0) return 0; @@ -335,7 +329,7 @@ static umode_t gpio_fan_is_visible(struct kobject *kobj, struct device *dev = container_of(kobj, struct device, kobj); struct gpio_fan_data *data = dev_get_drvdata(dev); - if (index == 0 && !data->alarm) + if (index == 0 && !data->alarm_gpio) return 0; if (index > 0 && !data->ctrl) return 0; @@ -450,28 +444,20 @@ static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) /* Alarm GPIO if one exists */ if (of_gpio_named_count(np, "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(np, "alarm-gpios", 0, &flags); if (val < 0) return val; - alarm->gpio = val; - alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; - - fan_data->alarm = alarm; + fan_data->alarm_gpio = val; + fan_data->alarm_gpio_active_low = flags & OF_GPIO_ACTIVE_LOW; } /* Fill GPIO pin array */ fan_data->num_ctrl = of_gpio_count(np); if (fan_data->num_ctrl <= 0) { - if (fan_data->alarm) + if (fan_data->alarm_gpio) return 0; dev_err(dev, "DT properties empty / missing"); return -ENODEV; @@ -556,7 +542,7 @@ static int gpio_fan_probe(struct platform_device *pdev) mutex_init(&fan_data->lock); /* Configure alarm GPIO if available. */ - if (fan_data->alarm) { + if (fan_data->alarm_gpio) { err = fan_alarm_init(fan_data); if (err) return err; -- cgit v1.2.3 From e99c2e5d6cde7f06dac0444b5edd6ed0d1abc431 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 01:09:10 +0200 Subject: hwmon: (gpio-fan) Rename GPIO line state variables The "ctrl" and "num_ctrl" entries in the state container struct is ambiguously named "ctrl" and "num_ctrl" overlapping with some hwmon lingo and making it hard to understand. Since this array actually contains the GPIO line numbers, from the Linux global GPIO numberspace, used to control the different fan speeds. Rename these fields to "gpios" (pluralis) and "num_gpios" so as to make it unambiguous. Convert some instances of "unsigned" to "unsigned int" to keep checkpatch happy. Signed-off-by: Linus Walleij Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 51 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 568ce4b25a9e..18b3c7c27d36 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -46,8 +46,8 @@ struct gpio_fan_data { /* Cooling device if any */ struct thermal_cooling_device *cdev; struct mutex lock; /* lock GPIOs operations. */ - int num_ctrl; - unsigned *ctrl; + int num_gpios; + unsigned int *gpios; int num_speed; struct gpio_fan_speed *speed; int speed_index; @@ -134,8 +134,9 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) { int i; - for (i = 0; i < fan_data->num_ctrl; i++) - gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1); + for (i = 0; i < fan_data->num_gpios; i++) + gpio_set_value_cansleep(fan_data->gpios[i], + (ctrl_val >> i) & 1); } static int __get_fan_ctrl(struct gpio_fan_data *fan_data) @@ -143,10 +144,10 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data) int i; int ctrl_val = 0; - for (i = 0; i < fan_data->num_ctrl; i++) { + for (i = 0; i < fan_data->num_gpios; i++) { int value; - value = gpio_get_value_cansleep(fan_data->ctrl[i]); + value = gpio_get_value_cansleep(fan_data->gpios[i]); ctrl_val |= (value << i); } return ctrl_val; @@ -331,7 +332,7 @@ static umode_t gpio_fan_is_visible(struct kobject *kobj, if (index == 0 && !data->alarm_gpio) return 0; - if (index > 0 && !data->ctrl) + if (index > 0 && !data->gpios) return 0; return attr->mode; @@ -362,18 +363,18 @@ static const struct attribute_group *gpio_fan_groups[] = { static int fan_ctrl_init(struct gpio_fan_data *fan_data) { struct device *dev = fan_data->dev; - int num_ctrl = fan_data->num_ctrl; - unsigned int *ctrl = fan_data->ctrl; + int num_gpios = fan_data->num_gpios; + unsigned int *gpios = fan_data->gpios; int i, err; - for (i = 0; i < num_ctrl; i++) { - err = devm_gpio_request(dev, ctrl[i], + for (i = 0; i < num_gpios; i++) { + err = devm_gpio_request(dev, gpios[i], "GPIO fan control"); if (err) return err; - err = gpio_direction_output(ctrl[i], - gpio_get_value_cansleep(ctrl[i])); + err = gpio_direction_output(gpios[i], + gpio_get_value_cansleep(gpios[i])); if (err) return err; } @@ -436,7 +437,7 @@ static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) struct gpio_fan_speed *speed; struct device *dev = fan_data->dev; struct device_node *np = dev->of_node; - unsigned *ctrl; + unsigned int *gpios; unsigned i; u32 u; struct property *prop; @@ -455,26 +456,26 @@ static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) } /* Fill GPIO pin array */ - fan_data->num_ctrl = of_gpio_count(np); - if (fan_data->num_ctrl <= 0) { + fan_data->num_gpios = of_gpio_count(np); + if (fan_data->num_gpios <= 0) { if (fan_data->alarm_gpio) return 0; dev_err(dev, "DT properties empty / missing"); return -ENODEV; } - ctrl = devm_kzalloc(dev, fan_data->num_ctrl * sizeof(unsigned int), + gpios = devm_kzalloc(dev, fan_data->num_gpios * sizeof(unsigned int), GFP_KERNEL); - if (!ctrl) + if (!gpios) return -ENOMEM; - for (i = 0; i < fan_data->num_ctrl; i++) { + for (i = 0; i < fan_data->num_gpios; i++) { int val; val = of_get_gpio(np, i); if (val < 0) return val; - ctrl[i] = val; + gpios[i] = val; } - fan_data->ctrl = ctrl; + fan_data->gpios = gpios; /* Get number of RPM/ctrl_val pairs in speed map */ prop = of_find_property(np, "gpio-fan,speed-map", &i); @@ -549,7 +550,7 @@ static int gpio_fan_probe(struct platform_device *pdev) } /* Configure control GPIOs if available. */ - if (fan_data->ctrl && fan_data->num_ctrl > 0) { + if (fan_data->gpios && fan_data->num_gpios > 0) { if (!fan_data->speed || fan_data->num_speed <= 1) return -EINVAL; err = fan_ctrl_init(fan_data); @@ -583,7 +584,7 @@ static int gpio_fan_remove(struct platform_device *pdev) if (!IS_ERR(fan_data->cdev)) thermal_cooling_device_unregister(fan_data->cdev); - if (fan_data->ctrl) + if (fan_data->gpios) set_fan_speed(fan_data, 0); return 0; @@ -599,7 +600,7 @@ static int gpio_fan_suspend(struct device *dev) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); - if (fan_data->ctrl) { + if (fan_data->gpios) { fan_data->resume_speed = fan_data->speed_index; set_fan_speed(fan_data, 0); } @@ -611,7 +612,7 @@ static int gpio_fan_resume(struct device *dev) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); - if (fan_data->ctrl) + if (fan_data->gpios) set_fan_speed(fan_data, fan_data->resume_speed); return 0; -- cgit v1.2.3 From 9de382fddf18f673436f1058d822e1236a0b4c2a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Oct 2017 01:14:32 +0200 Subject: hwmon: (gpio-fan) Convert to use GPIO descriptors This converts the GPIO fan driver to use GPIO descriptors. This way we avoid indirection since the gpiolib anyway just use descriptors inside, and we also get rid of explicit polarity handling: the descriptors internally knows if the line is active high or active low. Signed-off-by: Linus Walleij [groeck: Line length] Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 88 ++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 18b3c7c27d36..43b697380987 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -29,10 +29,9 @@ #include #include #include -#include +#include #include #include -#include #include struct gpio_fan_speed { @@ -47,7 +46,7 @@ struct gpio_fan_data { struct thermal_cooling_device *cdev; struct mutex lock; /* lock GPIOs operations. */ int num_gpios; - unsigned int *gpios; + struct gpio_desc **gpios; int num_speed; struct gpio_fan_speed *speed; int speed_index; @@ -55,8 +54,7 @@ struct gpio_fan_data { int resume_speed; #endif bool pwm_enable; - unsigned int alarm_gpio; - unsigned int alarm_gpio_active_low; + struct gpio_desc *alarm_gpio; struct work_struct alarm_work; }; @@ -86,43 +84,30 @@ static ssize_t fan1_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); - int value = gpio_get_value_cansleep(fan_data->alarm_gpio); - if (fan_data->alarm_gpio_active_low) - value = !value; - - return sprintf(buf, "%d\n", value); + return sprintf(buf, "%d\n", + gpiod_get_value_cansleep(fan_data->alarm_gpio)); } static DEVICE_ATTR_RO(fan1_alarm); static int fan_alarm_init(struct gpio_fan_data *fan_data) { - int err; int alarm_irq; struct device *dev = fan_data->dev; - err = devm_gpio_request(dev, fan_data->alarm_gpio, "GPIO fan alarm"); - if (err) - return err; - - err = gpio_direction_input(fan_data->alarm_gpio); - if (err) - return err; - /* * If the alarm GPIO don't support interrupts, just leave * without initializing the fail notification support. */ - alarm_irq = gpio_to_irq(fan_data->alarm_gpio); - if (alarm_irq < 0) + alarm_irq = gpiod_to_irq(fan_data->alarm_gpio); + if (alarm_irq <= 0) return 0; INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); - err = devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler, - IRQF_SHARED, "GPIO fan alarm", fan_data); - return err; + return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler, + IRQF_SHARED, "GPIO fan alarm", fan_data); } /* @@ -135,8 +120,8 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) int i; for (i = 0; i < fan_data->num_gpios; i++) - gpio_set_value_cansleep(fan_data->gpios[i], - (ctrl_val >> i) & 1); + gpiod_set_value_cansleep(fan_data->gpios[i], + (ctrl_val >> i) & 1); } static int __get_fan_ctrl(struct gpio_fan_data *fan_data) @@ -147,7 +132,7 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data) for (i = 0; i < fan_data->num_gpios; i++) { int value; - value = gpio_get_value_cansleep(fan_data->gpios[i]); + value = gpiod_get_value_cansleep(fan_data->gpios[i]); ctrl_val |= (value << i); } return ctrl_val; @@ -362,19 +347,19 @@ static const struct attribute_group *gpio_fan_groups[] = { static int fan_ctrl_init(struct gpio_fan_data *fan_data) { - struct device *dev = fan_data->dev; int num_gpios = fan_data->num_gpios; - unsigned int *gpios = fan_data->gpios; + struct gpio_desc **gpios = fan_data->gpios; int i, err; for (i = 0; i < num_gpios; i++) { - err = devm_gpio_request(dev, gpios[i], - "GPIO fan control"); - if (err) - return err; - - err = gpio_direction_output(gpios[i], - gpio_get_value_cansleep(gpios[i])); + /* + * The GPIO descriptors were retrieved with GPIOD_ASIS so here + * we set the GPIO into output mode, carefully preserving the + * current value by setting it to whatever it is already set + * (no surprise changes in default fan speed). + */ + err = gpiod_direction_output(gpios[i], + gpiod_get_value_cansleep(gpios[i])); if (err) return err; } @@ -437,43 +422,34 @@ static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data) struct gpio_fan_speed *speed; struct device *dev = fan_data->dev; struct device_node *np = dev->of_node; - unsigned int *gpios; + struct gpio_desc **gpios; unsigned i; u32 u; struct property *prop; const __be32 *p; /* Alarm GPIO if one exists */ - if (of_gpio_named_count(np, "alarm-gpios") > 0) { - int val; - enum of_gpio_flags flags; - - val = of_get_named_gpio_flags(np, "alarm-gpios", 0, &flags); - if (val < 0) - return val; - fan_data->alarm_gpio = val; - fan_data->alarm_gpio_active_low = flags & OF_GPIO_ACTIVE_LOW; - } + fan_data->alarm_gpio = devm_gpiod_get_optional(dev, "alarm", GPIOD_IN); + if (IS_ERR(fan_data->alarm_gpio)) + return PTR_ERR(fan_data->alarm_gpio); /* Fill GPIO pin array */ - fan_data->num_gpios = of_gpio_count(np); + fan_data->num_gpios = gpiod_count(dev, NULL); if (fan_data->num_gpios <= 0) { if (fan_data->alarm_gpio) return 0; dev_err(dev, "DT properties empty / missing"); return -ENODEV; } - gpios = devm_kzalloc(dev, fan_data->num_gpios * sizeof(unsigned int), - GFP_KERNEL); + gpios = devm_kzalloc(dev, + fan_data->num_gpios * sizeof(struct gpio_desc *), + GFP_KERNEL); if (!gpios) return -ENOMEM; for (i = 0; i < fan_data->num_gpios; i++) { - int val; - - val = of_get_gpio(np, i); - if (val < 0) - return val; - gpios[i] = val; + gpios[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); + if (IS_ERR(gpios[i])) + return PTR_ERR(gpios[i]); } fan_data->gpios = gpios; -- cgit v1.2.3 From 534e28d876926669bba0dc31519a0b0026f3dfcb Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 16 Oct 2017 14:12:10 +0200 Subject: hwmon: (gpio-fan) Fix null pointer dereference at probe A previous commit changed the argument list of gpio_fan_get_of_data(), removing the "struct *dev" argument and retrieving it instead from the gpio_fan_data structure. The "dev" entry of gpio_fan_data was then dereferenced to access the of_node field, leading to a kernel panic during the probe as the "dev" entry of the gpio_fan_data structure was not filled yet. Fix this by setting fan_data->dev before calling gpio_fan_get_of_data(). Fixes: 5859d8d30737 ("hwmon: (gpio-fan) Get rid of platform data struct") Signed-off-by: Miquel Raynal Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 43b697380987..5c9a52599cf6 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -510,11 +510,11 @@ static int gpio_fan_probe(struct platform_device *pdev) if (!fan_data) return -ENOMEM; + fan_data->dev = dev; err = gpio_fan_get_of_data(fan_data); if (err) return err; - fan_data->dev = dev; platform_set_drvdata(pdev, fan_data); mutex_init(&fan_data->lock); -- cgit v1.2.3 From 749d782d80de6db454af4336a4f4c8dfe79a5fc4 Mon Sep 17 00:00:00 2001 From: hotran Date: Tue, 17 Oct 2017 11:28:34 -0700 Subject: hwmon: (xgene) Support hwmon v2 This patch supports xgene-hwmon v2 which uses the non-cachable memory as the PCC shared memory. Signed-off-by: Hoan Tran Signed-off-by: Guenter Roeck --- drivers/hwmon/xgene-hwmon.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index e1be61095532..57834361fb3c 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -91,6 +91,11 @@ #define to_xgene_hwmon_dev(cl) \ container_of(cl, struct xgene_hwmon_dev, mbox_client) +enum xgene_hwmon_version { + XGENE_HWMON_V1 = 0, + XGENE_HWMON_V2 = 1, +}; + struct slimpro_resp_msg { u32 msg; u32 param1; @@ -609,6 +614,15 @@ static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret) } } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_hwmon_acpi_match[] = { + {"APMC0D29", XGENE_HWMON_V1}, + {"APMC0D8A", XGENE_HWMON_V2}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match); +#endif + static int xgene_hwmon_probe(struct platform_device *pdev) { struct xgene_hwmon_dev *ctx; @@ -651,6 +665,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev) } } else { struct acpi_pcct_hw_reduced *cppc_ss; + int version = XGENE_HWMON_V1; +#ifdef CONFIG_ACPI + const struct acpi_device_id *acpi_id; + + acpi_id = acpi_match_device(xgene_hwmon_acpi_match, &pdev->dev); + if (!acpi_id) + return -EINVAL; + + version = (int)acpi_id->driver_data; +#endif if (device_property_read_u32(&pdev->dev, "pcc-channel", &ctx->mbox_idx)) { @@ -693,7 +717,13 @@ static int xgene_hwmon_probe(struct platform_device *pdev) */ ctx->comm_base_addr = cppc_ss->base_address; if (ctx->comm_base_addr) { - ctx->pcc_comm_addr = memremap(ctx->comm_base_addr, + if (version == XGENE_HWMON_V2) + ctx->pcc_comm_addr = (void __force *)ioremap( + ctx->comm_base_addr, + cppc_ss->length); + else + ctx->pcc_comm_addr = memremap( + ctx->comm_base_addr, cppc_ss->length, MEMREMAP_WB); } else { @@ -761,14 +791,6 @@ static int xgene_hwmon_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_ACPI -static const struct acpi_device_id xgene_hwmon_acpi_match[] = { - {"APMC0D29", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match); -#endif - static const struct of_device_id xgene_hwmon_of_match[] = { {.compatible = "apm,xgene-slimpro-hwmon"}, {} -- cgit v1.2.3 From a3bdc5b5bd369fcab8306b80d2c740e332183d20 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 18 Oct 2017 13:10:38 +0100 Subject: hwmon: (asc7621) remove redundant assignment to newval The setting of newval to zero is redundant as the following if/else stanzas will always update newval to a new value. Remove the redundant setting, cleans up clang build warning: drivers/hwmon/asc7621.c:582:2: warning: Value stored to 'newval' is never read Signed-off-by: Colin Ian King Signed-off-by: Guenter Roeck --- drivers/hwmon/asc7621.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 4875e99b59c9..6d34c05a4f83 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -579,7 +579,6 @@ static ssize_t show_pwm_enable(struct device *dev, mutex_unlock(&data->update_lock); val = config | (altbit << 3); - newval = 0; if (val == 3 || val >= 10) newval = 255; -- cgit v1.2.3 From 5813da157f7dbe03d268c8d0dd9425d0e3944910 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 24 Oct 2017 12:47:42 +0300 Subject: hwmon: (max6621) Inverted if condition in max6621_read() We intended to test for failure here but accidentally tested for success. It means that we don't set "*val" to true and it means that if i2c_smbus_write_byte() does fail then we return success. Fixes: e7895864b0d7 ("hwmon: (max6621) Add support for Maxim MAX6621 temperature sensor") Signed-off-by: Dan Carpenter Acked-by: Vadim Pasternak Signed-off-by: Guenter Roeck --- drivers/hwmon/max6621.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c index 22079ec29660..35555f0eefb9 100644 --- a/drivers/hwmon/max6621.c +++ b/drivers/hwmon/max6621.c @@ -296,7 +296,7 @@ max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (regval) { ret = i2c_smbus_write_byte(data->client, MAX6621_CLEAR_ALERT_REG); - if (!ret) + if (ret) return ret; } -- cgit v1.2.3 From 2305a18ba9ffa105ad1686f40a7861718c338e3c Mon Sep 17 00:00:00 2001 From: hotran Date: Tue, 31 Oct 2017 13:58:07 -0700 Subject: hwmon: (xgene) Minor clean up of ifdef and acpi_match_table reference This patch removes the un-necessary ifdef CONFIG_ACPI and directly uses the acpi_match_table from the driver pdev. Signed-off-by: Hoan Tran [groeck: Dropped unnecessary initialization] Signed-off-by: Guenter Roeck --- drivers/hwmon/xgene-hwmon.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index 57834361fb3c..a3cd91f23267 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -665,16 +665,15 @@ static int xgene_hwmon_probe(struct platform_device *pdev) } } else { struct acpi_pcct_hw_reduced *cppc_ss; - int version = XGENE_HWMON_V1; -#ifdef CONFIG_ACPI const struct acpi_device_id *acpi_id; + int version; - acpi_id = acpi_match_device(xgene_hwmon_acpi_match, &pdev->dev); + acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table, + &pdev->dev); if (!acpi_id) return -EINVAL; version = (int)acpi_id->driver_data; -#endif if (device_property_read_u32(&pdev->dev, "pcc-channel", &ctx->mbox_idx)) { -- cgit v1.2.3 From 54b943e69691329805349440b157b36e34051a57 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 2 Nov 2017 14:53:47 +1100 Subject: hwmon: (aspeed-pwm-tacho) Sort headers Sort the headers in preperation for future changes. Signed-off-by: Joel Stanley Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index f914e5f41048..63a95e23ca81 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -7,19 +7,19 @@ */ #include +#include #include #include -#include #include #include #include #include #include -#include #include +#include #include -#include #include +#include #include /* ASPEED PWM & FAN Tach Register Definition */ -- cgit v1.2.3 From 4d420a6a9ddd72bd25baa6e667dd0581506eeacb Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 3 Nov 2017 15:53:02 +1100 Subject: pmbus: Add driver for Maxim MAX31785 Intelligent Fan Controller The Maxim MAX31785 is a PMBus device providing closed-loop, multi-channel fan management with temperature and remote voltage sensing. It supports various fan control features, including PWM frequency control, temperature hysteresis, dual tachometer measurements, and fan health monitoring. This patch presents a basic driver using only the existing features of the PMBus subsystem. Signed-off-by: Andrew Jeffery [groeck: Modified description to clarify that fan control is not yet provided] Signed-off-by: Guenter Roeck --- Documentation/hwmon/max31785 | 51 ++++++++++++++++++ drivers/hwmon/pmbus/Kconfig | 10 ++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/max31785.c | 116 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 Documentation/hwmon/max31785 create mode 100644 drivers/hwmon/pmbus/max31785.c diff --git a/Documentation/hwmon/max31785 b/Documentation/hwmon/max31785 new file mode 100644 index 000000000000..45fb6093dec2 --- /dev/null +++ b/Documentation/hwmon/max31785 @@ -0,0 +1,51 @@ +Kernel driver max31785 +====================== + +Supported chips: + * Maxim MAX31785, MAX31785A + Prefix: 'max31785' or 'max31785a' + Addresses scanned: - + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf + +Author: Andrew Jeffery + +Description +----------- + +The Maxim MAX31785 is a PMBus device providing closed-loop, multi-channel fan +management with temperature and remote voltage sensing. Various fan control +features are provided, including PWM frequency control, temperature hysteresis, +dual tachometer measurements, and fan health monitoring. + +For dual rotor fan configuration, the MAX31785 exposes the slowest rotor of the +two in the fan[1-4]_input attributes. + +Usage Notes +----------- + +This driver does not probe for PMBus devices. You will have to instantiate +devices explicitly. + +Sysfs attributes +---------------- + +fan[1-4]_alarm Fan alarm. +fan[1-4]_fault Fan fault. +fan[1-4]_input Fan RPM. + +in[1-6]_crit Critical maximum output voltage +in[1-6]_crit_alarm Output voltage critical high alarm +in[1-6]_input Measured output voltage +in[1-6]_label "vout[18-23]" +in[1-6]_lcrit Critical minimum output voltage +in[1-6]_lcrit_alarm Output voltage critical low alarm +in[1-6]_max Maximum output voltage +in[1-6]_max_alarm Output voltage high alarm +in[1-6]_min Minimum output voltage +in[1-6]_min_alarm Output voltage low alarm + +temp[1-11]_crit Critical high temperature +temp[1-11]_crit_alarm Chip temperature critical high alarm +temp[1-11]_input Measured temperature +temp[1-11]_max Maximum temperature +temp[1-11]_max_alarm Chip temperature high alarm diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 40019325b517..08479006c7f9 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -114,6 +114,16 @@ config SENSORS_MAX20751 This driver can also be built as a module. If so, the module will be called max20751. +config SENSORS_MAX31785 + tristate "Maxim MAX31785 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX31785. + + This driver can also be built as a module. If so, the module will + be called max31785. + config SENSORS_MAX34440 tristate "Maxim MAX34440 and compatibles" default n diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 459a6be3390e..a8bf0e490db9 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o +obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c new file mode 100644 index 000000000000..9313849d5160 --- /dev/null +++ b/drivers/hwmon/pmbus/max31785.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 IBM Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +enum max31785_regs { + MFR_REVISION = 0x9b, +}; + +#define MAX31785_NR_PAGES 23 + +#define MAX31785_FAN_FUNCS \ + (PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12) + +#define MAX31785_TEMP_FUNCS \ + (PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP) + +#define MAX31785_VOUT_FUNCS \ + (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT) + +static const struct pmbus_driver_info max31785_info = { + .pages = MAX31785_NR_PAGES, + + /* RPM */ + .format[PSC_FAN] = direct, + .m[PSC_FAN] = 1, + .b[PSC_FAN] = 0, + .R[PSC_FAN] = 0, + .func[0] = MAX31785_FAN_FUNCS, + .func[1] = MAX31785_FAN_FUNCS, + .func[2] = MAX31785_FAN_FUNCS, + .func[3] = MAX31785_FAN_FUNCS, + .func[4] = MAX31785_FAN_FUNCS, + .func[5] = MAX31785_FAN_FUNCS, + + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[6] = MAX31785_TEMP_FUNCS, + .func[7] = MAX31785_TEMP_FUNCS, + .func[8] = MAX31785_TEMP_FUNCS, + .func[9] = MAX31785_TEMP_FUNCS, + .func[10] = MAX31785_TEMP_FUNCS, + .func[11] = MAX31785_TEMP_FUNCS, + .func[12] = MAX31785_TEMP_FUNCS, + .func[13] = MAX31785_TEMP_FUNCS, + .func[14] = MAX31785_TEMP_FUNCS, + .func[15] = MAX31785_TEMP_FUNCS, + .func[16] = MAX31785_TEMP_FUNCS, + + .format[PSC_VOLTAGE_OUT] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .func[17] = MAX31785_VOUT_FUNCS, + .func[18] = MAX31785_VOUT_FUNCS, + .func[19] = MAX31785_VOUT_FUNCS, + .func[20] = MAX31785_VOUT_FUNCS, + .func[21] = MAX31785_VOUT_FUNCS, + .func[22] = MAX31785_VOUT_FUNCS, +}; + +static int max31785_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + s64 ret; + + info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + *info = max31785_info; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255); + if (ret < 0) + return ret; + + return pmbus_do_probe(client, id, info); +} + +static const struct i2c_device_id max31785_id[] = { + { "max31785", 0 }, + { "max31785a", 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max31785_id); + +static struct i2c_driver max31785_driver = { + .driver = { + .name = "max31785", + }, + .probe = max31785_probe, + .remove = pmbus_do_remove, + .id_table = max31785_id, +}; + +module_i2c_driver(max31785_driver); + +MODULE_AUTHOR("Andrew Jeffery "); +MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ded0eb83449e8fcba22fd2736826336e101ffbcb Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 3 Nov 2017 15:53:01 +1100 Subject: dt-bindings: pmbus: Add Maxim MAX31785 documentation Signed-off-by: Andrew Jeffery Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/max31785.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/max31785.txt diff --git a/Documentation/devicetree/bindings/hwmon/max31785.txt b/Documentation/devicetree/bindings/hwmon/max31785.txt new file mode 100644 index 000000000000..106e08c56aaa --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/max31785.txt @@ -0,0 +1,22 @@ +Bindings for the Maxim MAX31785 Intelligent Fan Controller +========================================================== + +Reference: + +https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf + +The Maxim MAX31785 is a PMBus device providing closed-loop, multi-channel fan +management with temperature and remote voltage sensing. Various fan control +features are provided, including PWM frequency control, temperature hysteresis, +dual tachometer measurements, and fan health monitoring. + +Required properties: +- compatible : One of "maxim,max31785" or "maxim,max31785a" +- reg : I2C address, one of 0x52, 0x53, 0x54, 0x55. + +Example: + + fans@52 { + compatible = "maxim,max31785"; + reg = <0x52>; + }; -- cgit v1.2.3