hwmon updates for v4.15
- Drivers for MAX31785 and MAX6621 - Support for AMD family 17h (Ryzen, Threadripper) temperature sensors - Various driver cleanups and minor improvements -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJaCKuYAAoJEMsfJm/On5mBjVAP/A/gfAv5KGYsuzEm+Oph3UFq dQsuqEr7cUhBnhApwfotT9ygNWRfVknPyvCjTJanHXca1eAE+4srgXY3yRpo5s6R YlVbkT7fYRO+SrjXsWhKBcctzKjrkyiNYTz+BqGbzQ9Y6QStnkrQTweB1NOAkasy o7WsAlh9bX4966GyAq6OOQZ3dOwvKGVl8dovEJQk5ZtW0e0TOrGbPM5KZsXN+Haa adUAMBhxEzSH7SqO1UIlnn0DfF7Ikp291Vy03LZKnGDIN/myI602HrRerrnZhDw6 Beq4YyYRjNVqwkkIwpAlElKAGK5QXhbuUb9ScvhEMZSf2e80t/NzLmDQrWVKevAy VSnFaP+cKIx9hYMwQe92fbn1dhASOf5z+LxzC8kyAloUHZ8ZGH6JEkPRFPCijoiV MQ+/YM1jpzzAqiW1HilxVouVjq0lhJwrnqozwbrF/HfyeIzBVd76o+4AIrOVFKLR zXtKbiLsFQz3GDRGzCp5CswQnyFMU0K5Hpx2iSd7UYx6OWH6Q5Hf6ieC8LJmst1d akbFSiExgF+CIZc5TYaFIBO740vpo8EXJesGIA6uZYoMxk8iConahMAkzJV5ik3Z H1LghVhkgYjccdJQ/dxfmQ87+P4bYeRp0KQPRHBAHTcUe0htqR006rcsDDBwznGx LwrUeeSMf+dS7dj+jSLV =NpxT -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: - drivers for MAX31785 and MAX6621 - support for AMD family 17h (Ryzen, Threadripper) temperature sensors - various driver cleanups and minor improvements * tag 'hwmon-for-linus-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (30 commits) dt-bindings: pmbus: Add Maxim MAX31785 documentation pmbus: Add driver for Maxim MAX31785 Intelligent Fan Controller hwmon: (aspeed-pwm-tacho) Sort headers hwmon: (xgene) Minor clean up of ifdef and acpi_match_table reference hwmon: (max6621) Inverted if condition in max6621_read() hwmon: (asc7621) remove redundant assignment to newval hwmon: (xgene) Support hwmon v2 hwmon: (gpio-fan) Fix null pointer dereference at probe hwmon: (gpio-fan) Convert to use GPIO descriptors hwmon: (gpio-fan) Rename GPIO line state variables hwmon: (gpio-fan) Get rid of the gpio alarm struct hwmon: (gpio-fan) Get rid of platform data struct hwmon: (gpio-fan) Mandate OF_GPIO and cut pdata path hwmon: (gpio-fan) Send around device pointer hwmon: (gpio-fan) Localize platform data hwmon: (gpio-fan) Use local variable pointers hwmon: (gpio-fan) Move DT bindings to the right place Documentation: devicetree: add max6621 device hwmon: (max6621) Add support for Maxim MAX6621 temperature sensor hwmon: (w83793) make const array watchdog_minors static, reduces object code size ...
This commit is contained in:
commit
1e19bded7f
|
@ -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>;
|
||||||
|
};
|
|
@ -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>;
|
||||||
|
};
|
|
@ -71,6 +71,7 @@ isil,isl29028 Intersil ISL29028 Ambient Light and Proximity Sensor
|
||||||
isil,isl29030 Intersil ISL29030 Ambient Light and Proximity Sensor
|
isil,isl29030 Intersil ISL29030 Ambient Light and Proximity Sensor
|
||||||
maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
||||||
maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
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
|
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
|
||||||
mc,rv3029c2 Real Time Clock Module with I2C-Bus
|
mc,rv3029c2 Real Time Clock Module with I2C-Bus
|
||||||
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
|
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
|
||||||
|
|
|
@ -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 <andrew@aj.id.au>
|
||||||
|
|
||||||
|
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
|
|
@ -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
|
sensors. Disabling the reload of those coefficients allows saving 10ms for each
|
||||||
measurement and decrease power consumption, while losing on precision.
|
measurement and decrease power consumption, while losing on precision.
|
||||||
|
|
||||||
Some options may be set directly in the sht15_platform_data structure
|
Some options may be set via sysfs attributes.
|
||||||
or via sysfs attributes.
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
* The regulator supply name is set to "vcc".
|
* The regulator supply name is set to "vcc".
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <linux/platform_data/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
#include <linux/platform_data/at24.h>
|
#include <linux/platform_data/at24.h>
|
||||||
#include <linux/smc91x.h>
|
#include <linux/smc91x.h>
|
||||||
|
#include <linux/gpio/machine.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
|
|
||||||
|
@ -52,7 +53,6 @@
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
#include <linux/spi/pxa2xx_spi.h>
|
||||||
#include <linux/mfd/da903x.h>
|
#include <linux/mfd/da903x.h>
|
||||||
#include <linux/platform_data/sht15.h>
|
|
||||||
|
|
||||||
#include "devices.h"
|
#include "devices.h"
|
||||||
#include "generic.h"
|
#include "generic.h"
|
||||||
|
@ -137,17 +137,18 @@ static unsigned long sg2_im2_unified_pin_config[] __initdata = {
|
||||||
GPIO10_GPIO, /* large basic connector pin 23 */
|
GPIO10_GPIO, /* large basic connector pin 23 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sht15_platform_data platform_data_sht15 = {
|
static struct gpiod_lookup_table sht15_gpiod_table = {
|
||||||
.gpio_data = 100,
|
.dev_id = "sht15",
|
||||||
.gpio_sck = 98,
|
.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 = {
|
static struct platform_device sht15 = {
|
||||||
.name = "sht15",
|
.name = "sht15",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
.dev = {
|
|
||||||
.platform_data = &platform_data_sht15,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct regulator_consumer_supply stargate2_sensor_3_con[] = {
|
static struct regulator_consumer_supply stargate2_sensor_3_con[] = {
|
||||||
|
@ -608,6 +609,7 @@ static void __init imote2_init(void)
|
||||||
|
|
||||||
imote2_stargate2_init();
|
imote2_stargate2_init();
|
||||||
|
|
||||||
|
gpiod_add_lookup_table(&sht15_gpiod_table);
|
||||||
platform_add_devices(imote2_devices, ARRAY_SIZE(imote2_devices));
|
platform_add_devices(imote2_devices, ARRAY_SIZE(imote2_devices));
|
||||||
|
|
||||||
i2c_register_board_info(0, imote2_i2c_board_info,
|
i2c_register_board_info(0, imote2_i2c_board_info,
|
||||||
|
@ -988,6 +990,7 @@ static void __init stargate2_init(void)
|
||||||
|
|
||||||
imote2_stargate2_init();
|
imote2_stargate2_init();
|
||||||
|
|
||||||
|
gpiod_add_lookup_table(&sht15_gpiod_table);
|
||||||
platform_add_devices(ARRAY_AND_SIZE(stargate2_devices));
|
platform_add_devices(ARRAY_AND_SIZE(stargate2_devices));
|
||||||
|
|
||||||
i2c_register_board_info(0, ARRAY_AND_SIZE(stargate2_i2c_board_info));
|
i2c_register_board_info(0, ARRAY_AND_SIZE(stargate2_i2c_board_info));
|
||||||
|
|
|
@ -552,6 +552,7 @@ config SENSORS_G762
|
||||||
|
|
||||||
config SENSORS_GPIO_FAN
|
config SENSORS_GPIO_FAN
|
||||||
tristate "GPIO fan"
|
tristate "GPIO fan"
|
||||||
|
depends on OF_GPIO
|
||||||
depends on GPIOLIB || COMPILE_TEST
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
depends on THERMAL || THERMAL=n
|
depends on THERMAL || THERMAL=n
|
||||||
help
|
help
|
||||||
|
@ -862,6 +863,20 @@ tristate "MAX31722 temperature sensor"
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called max31722.
|
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
|
config SENSORS_MAX6639
|
||||||
tristate "Maxim MAX6639 sensor chip"
|
tristate "Maxim MAX6639 sensor chip"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
|
|
@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
|
||||||
obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
|
obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
|
||||||
obj-$(CONFIG_SENSORS_MAX197) += max197.o
|
obj-$(CONFIG_SENSORS_MAX197) += max197.o
|
||||||
obj-$(CONFIG_SENSORS_MAX31722) += max31722.o
|
obj-$(CONFIG_SENSORS_MAX31722) += max31722.o
|
||||||
|
obj-$(CONFIG_SENSORS_MAX6621) += max6621.o
|
||||||
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
|
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
|
||||||
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
|
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
|
||||||
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||||
|
|
|
@ -579,7 +579,6 @@ static ssize_t show_pwm_enable(struct device *dev,
|
||||||
mutex_unlock(&data->update_lock);
|
mutex_unlock(&data->update_lock);
|
||||||
|
|
||||||
val = config | (altbit << 3);
|
val = config | (altbit << 3);
|
||||||
newval = 0;
|
|
||||||
|
|
||||||
if (val == 3 || val >= 10)
|
if (val == 3 || val >= 10)
|
||||||
newval = 255;
|
newval = 255;
|
||||||
|
|
|
@ -7,19 +7,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_platform.h>
|
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/sysfs.h>
|
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
/* ASPEED PWM & FAN Tach Register Definition */
|
/* ASPEED PWM & FAN Tach Register Definition */
|
||||||
|
@ -161,7 +161,7 @@
|
||||||
* 11: reserved.
|
* 11: reserved.
|
||||||
*/
|
*/
|
||||||
#define M_TACH_MODE 0x02 /* 10b */
|
#define M_TACH_MODE 0x02 /* 10b */
|
||||||
#define M_TACH_UNIT 0x00c0
|
#define M_TACH_UNIT 0x0210
|
||||||
#define INIT_FAN_CTRL 0xFF
|
#define INIT_FAN_CTRL 0xFF
|
||||||
|
|
||||||
/* How long we sleep in us while waiting for an RPM result. */
|
/* How long we sleep in us while waiting for an RPM result. */
|
||||||
|
|
|
@ -29,21 +29,24 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/gpio-fan.h>
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
|
|
||||||
|
struct gpio_fan_speed {
|
||||||
|
int rpm;
|
||||||
|
int ctrl_val;
|
||||||
|
};
|
||||||
|
|
||||||
struct gpio_fan_data {
|
struct gpio_fan_data {
|
||||||
struct platform_device *pdev;
|
struct device *dev;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
/* Cooling device if any */
|
/* Cooling device if any */
|
||||||
struct thermal_cooling_device *cdev;
|
struct thermal_cooling_device *cdev;
|
||||||
struct mutex lock; /* lock GPIOs operations. */
|
struct mutex lock; /* lock GPIOs operations. */
|
||||||
int num_ctrl;
|
int num_gpios;
|
||||||
unsigned *ctrl;
|
struct gpio_desc **gpios;
|
||||||
int num_speed;
|
int num_speed;
|
||||||
struct gpio_fan_speed *speed;
|
struct gpio_fan_speed *speed;
|
||||||
int speed_index;
|
int speed_index;
|
||||||
|
@ -51,7 +54,7 @@ struct gpio_fan_data {
|
||||||
int resume_speed;
|
int resume_speed;
|
||||||
#endif
|
#endif
|
||||||
bool pwm_enable;
|
bool pwm_enable;
|
||||||
struct gpio_fan_alarm *alarm;
|
struct gpio_desc *alarm_gpio;
|
||||||
struct work_struct alarm_work;
|
struct work_struct alarm_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,8 +67,8 @@ static void fan_alarm_notify(struct work_struct *ws)
|
||||||
struct gpio_fan_data *fan_data =
|
struct gpio_fan_data *fan_data =
|
||||||
container_of(ws, struct gpio_fan_data, alarm_work);
|
container_of(ws, struct gpio_fan_data, alarm_work);
|
||||||
|
|
||||||
sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm");
|
sysfs_notify(&fan_data->dev->kobj, NULL, "fan1_alarm");
|
||||||
kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE);
|
kobject_uevent(&fan_data->dev->kobj, KOBJ_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
|
static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
|
||||||
|
@ -81,47 +84,30 @@ static ssize_t fan1_alarm_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
|
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);
|
|
||||||
|
|
||||||
if (alarm->active_low)
|
return sprintf(buf, "%d\n",
|
||||||
value = !value;
|
gpiod_get_value_cansleep(fan_data->alarm_gpio));
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR_RO(fan1_alarm);
|
static DEVICE_ATTR_RO(fan1_alarm);
|
||||||
|
|
||||||
static int fan_alarm_init(struct gpio_fan_data *fan_data,
|
static int fan_alarm_init(struct gpio_fan_data *fan_data)
|
||||||
struct gpio_fan_alarm *alarm)
|
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
int alarm_irq;
|
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");
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
err = gpio_direction_input(alarm->gpio);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the alarm GPIO don't support interrupts, just leave
|
* If the alarm GPIO don't support interrupts, just leave
|
||||||
* without initializing the fail notification support.
|
* without initializing the fail notification support.
|
||||||
*/
|
*/
|
||||||
alarm_irq = gpio_to_irq(alarm->gpio);
|
alarm_irq = gpiod_to_irq(fan_data->alarm_gpio);
|
||||||
if (alarm_irq < 0)
|
if (alarm_irq <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
|
INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
|
||||||
irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
|
irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
|
||||||
err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler,
|
return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
|
||||||
IRQF_SHARED, "GPIO fan alarm", fan_data);
|
IRQF_SHARED, "GPIO fan alarm", fan_data);
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -133,8 +119,9 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < fan_data->num_ctrl; i++)
|
for (i = 0; i < fan_data->num_gpios; i++)
|
||||||
gpio_set_value_cansleep(fan_data->ctrl[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)
|
static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
|
||||||
|
@ -142,10 +129,10 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
|
||||||
int i;
|
int i;
|
||||||
int ctrl_val = 0;
|
int ctrl_val = 0;
|
||||||
|
|
||||||
for (i = 0; i < fan_data->num_ctrl; i++) {
|
for (i = 0; i < fan_data->num_gpios; i++) {
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
value = gpio_get_value_cansleep(fan_data->ctrl[i]);
|
value = gpiod_get_value_cansleep(fan_data->gpios[i]);
|
||||||
ctrl_val |= (value << i);
|
ctrl_val |= (value << i);
|
||||||
}
|
}
|
||||||
return ctrl_val;
|
return ctrl_val;
|
||||||
|
@ -170,7 +157,7 @@ static int get_fan_speed_index(struct gpio_fan_data *fan_data)
|
||||||
if (fan_data->speed[i].ctrl_val == ctrl_val)
|
if (fan_data->speed[i].ctrl_val == ctrl_val)
|
||||||
return i;
|
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);
|
"missing speed array entry for GPIO value 0x%x\n", ctrl_val);
|
||||||
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -328,9 +315,9 @@ static umode_t gpio_fan_is_visible(struct kobject *kobj,
|
||||||
struct device *dev = container_of(kobj, struct device, kobj);
|
struct device *dev = container_of(kobj, struct device, kobj);
|
||||||
struct gpio_fan_data *data = dev_get_drvdata(dev);
|
struct gpio_fan_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (index == 0 && !data->alarm)
|
if (index == 0 && !data->alarm_gpio)
|
||||||
return 0;
|
return 0;
|
||||||
if (index > 0 && !data->ctrl)
|
if (index > 0 && !data->gpios)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return attr->mode;
|
return attr->mode;
|
||||||
|
@ -358,30 +345,25 @@ static const struct attribute_group *gpio_fan_groups[] = {
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static int fan_ctrl_init(struct gpio_fan_data *fan_data,
|
static int fan_ctrl_init(struct gpio_fan_data *fan_data)
|
||||||
struct gpio_fan_platform_data *pdata)
|
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = fan_data->pdev;
|
int num_gpios = fan_data->num_gpios;
|
||||||
int num_ctrl = pdata->num_ctrl;
|
struct gpio_desc **gpios = fan_data->gpios;
|
||||||
unsigned *ctrl = pdata->ctrl;
|
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
for (i = 0; i < num_ctrl; i++) {
|
for (i = 0; i < num_gpios; i++) {
|
||||||
err = devm_gpio_request(&pdev->dev, ctrl[i],
|
/*
|
||||||
"GPIO fan control");
|
* The GPIO descriptors were retrieved with GPIOD_ASIS so here
|
||||||
if (err)
|
* we set the GPIO into output mode, carefully preserving the
|
||||||
return err;
|
* current value by setting it to whatever it is already set
|
||||||
|
* (no surprise changes in default fan speed).
|
||||||
err = gpio_direction_output(ctrl[i],
|
*/
|
||||||
gpio_get_value_cansleep(ctrl[i]));
|
err = gpiod_direction_output(gpios[i],
|
||||||
|
gpiod_get_value_cansleep(gpios[i]));
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
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->pwm_enable = true; /* Enable manual fan speed control. */
|
||||||
fan_data->speed_index = get_fan_speed_index(fan_data);
|
fan_data->speed_index = get_fan_speed_index(fan_data);
|
||||||
if (fan_data->speed_index < 0)
|
if (fan_data->speed_index < 0)
|
||||||
|
@ -432,67 +414,47 @@ static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
|
||||||
.set_cur_state = gpio_fan_set_cur_state,
|
.set_cur_state = gpio_fan_set_cur_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF_GPIO
|
|
||||||
/*
|
/*
|
||||||
* Translate OpenFirmware node properties into platform_data
|
* Translate OpenFirmware node properties into platform_data
|
||||||
*/
|
*/
|
||||||
static int gpio_fan_get_of_pdata(struct device *dev,
|
static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data)
|
||||||
struct gpio_fan_platform_data *pdata)
|
|
||||||
{
|
{
|
||||||
struct device_node *node;
|
|
||||||
struct gpio_fan_speed *speed;
|
struct gpio_fan_speed *speed;
|
||||||
unsigned *ctrl;
|
struct device *dev = fan_data->dev;
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
struct gpio_desc **gpios;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
u32 u;
|
u32 u;
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
const __be32 *p;
|
const __be32 *p;
|
||||||
|
|
||||||
node = dev->of_node;
|
|
||||||
|
|
||||||
/* Alarm GPIO if one exists */
|
/* Alarm GPIO if one exists */
|
||||||
if (of_gpio_named_count(node, "alarm-gpios") > 0) {
|
fan_data->alarm_gpio = devm_gpiod_get_optional(dev, "alarm", GPIOD_IN);
|
||||||
struct gpio_fan_alarm *alarm;
|
if (IS_ERR(fan_data->alarm_gpio))
|
||||||
int val;
|
return PTR_ERR(fan_data->alarm_gpio);
|
||||||
enum of_gpio_flags flags;
|
|
||||||
|
|
||||||
alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!alarm)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags);
|
|
||||||
if (val < 0)
|
|
||||||
return val;
|
|
||||||
alarm->gpio = val;
|
|
||||||
alarm->active_low = flags & OF_GPIO_ACTIVE_LOW;
|
|
||||||
|
|
||||||
pdata->alarm = alarm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill GPIO pin array */
|
/* Fill GPIO pin array */
|
||||||
pdata->num_ctrl = of_gpio_count(node);
|
fan_data->num_gpios = gpiod_count(dev, NULL);
|
||||||
if (pdata->num_ctrl <= 0) {
|
if (fan_data->num_gpios <= 0) {
|
||||||
if (pdata->alarm)
|
if (fan_data->alarm_gpio)
|
||||||
return 0;
|
return 0;
|
||||||
dev_err(dev, "DT properties empty / missing");
|
dev_err(dev, "DT properties empty / missing");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned),
|
gpios = devm_kzalloc(dev,
|
||||||
|
fan_data->num_gpios * sizeof(struct gpio_desc *),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!ctrl)
|
if (!gpios)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
for (i = 0; i < pdata->num_ctrl; i++) {
|
for (i = 0; i < fan_data->num_gpios; i++) {
|
||||||
int val;
|
gpios[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
|
||||||
|
if (IS_ERR(gpios[i]))
|
||||||
val = of_get_gpio(node, i);
|
return PTR_ERR(gpios[i]);
|
||||||
if (val < 0)
|
|
||||||
return val;
|
|
||||||
ctrl[i] = val;
|
|
||||||
}
|
}
|
||||||
pdata->ctrl = ctrl;
|
fan_data->gpios = gpios;
|
||||||
|
|
||||||
/* Get number of RPM/ctrl_val pairs in speed map */
|
/* 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) {
|
if (!prop) {
|
||||||
dev_err(dev, "gpio-fan,speed-map DT property missing");
|
dev_err(dev, "gpio-fan,speed-map DT property missing");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -502,7 +464,7 @@ static int gpio_fan_get_of_pdata(struct device *dev,
|
||||||
dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries");
|
dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
pdata->num_speed = i / 2;
|
fan_data->num_speed = i / 2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populate speed map
|
* Populate speed map
|
||||||
|
@ -510,12 +472,12 @@ static int gpio_fan_get_of_pdata(struct device *dev,
|
||||||
* this needs splitting into pairs to create gpio_fan_speed structs
|
* this needs splitting into pairs to create gpio_fan_speed structs
|
||||||
*/
|
*/
|
||||||
speed = devm_kzalloc(dev,
|
speed = devm_kzalloc(dev,
|
||||||
pdata->num_speed * sizeof(struct gpio_fan_speed),
|
fan_data->num_speed * sizeof(struct gpio_fan_speed),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!speed)
|
if (!speed)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
p = NULL;
|
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);
|
p = of_prop_next_u32(prop, p, &u);
|
||||||
if (!p)
|
if (!p)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -525,7 +487,7 @@ static int gpio_fan_get_of_pdata(struct device *dev,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
speed[i].ctrl_val = u;
|
speed[i].ctrl_val = u;
|
||||||
}
|
}
|
||||||
pdata->speed = speed;
|
fan_data->speed = speed;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -535,76 +497,58 @@ static const struct of_device_id of_gpio_fan_match[] = {
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, 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)
|
static int gpio_fan_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct gpio_fan_data *fan_data;
|
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;
|
||||||
|
|
||||||
fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data),
|
fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!fan_data)
|
if (!fan_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
#ifdef CONFIG_OF_GPIO
|
fan_data->dev = dev;
|
||||||
if (!pdata) {
|
err = gpio_fan_get_of_data(fan_data);
|
||||||
pdata = devm_kzalloc(&pdev->dev,
|
|
||||||
sizeof(struct gpio_fan_platform_data),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!pdata)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
err = gpio_fan_get_of_pdata(&pdev->dev, pdata);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
|
||||||
#else /* CONFIG_OF_GPIO */
|
|
||||||
if (!pdata)
|
|
||||||
return -EINVAL;
|
|
||||||
#endif /* CONFIG_OF_GPIO */
|
|
||||||
|
|
||||||
fan_data->pdev = pdev;
|
|
||||||
platform_set_drvdata(pdev, fan_data);
|
platform_set_drvdata(pdev, fan_data);
|
||||||
mutex_init(&fan_data->lock);
|
mutex_init(&fan_data->lock);
|
||||||
|
|
||||||
/* Configure alarm GPIO if available. */
|
/* Configure alarm GPIO if available. */
|
||||||
if (pdata->alarm) {
|
if (fan_data->alarm_gpio) {
|
||||||
err = fan_alarm_init(fan_data, pdata->alarm);
|
err = fan_alarm_init(fan_data);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure control GPIOs if available. */
|
/* Configure control GPIOs if available. */
|
||||||
if (pdata->ctrl && pdata->num_ctrl > 0) {
|
if (fan_data->gpios && fan_data->num_gpios > 0) {
|
||||||
if (!pdata->speed || pdata->num_speed <= 1)
|
if (!fan_data->speed || fan_data->num_speed <= 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
err = fan_ctrl_init(fan_data, pdata);
|
err = fan_ctrl_init(fan_data);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make this driver part of hwmon class. */
|
/* Make this driver part of hwmon class. */
|
||||||
fan_data->hwmon_dev =
|
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", fan_data,
|
||||||
gpio_fan_groups);
|
gpio_fan_groups);
|
||||||
if (IS_ERR(fan_data->hwmon_dev))
|
if (IS_ERR(fan_data->hwmon_dev))
|
||||||
return PTR_ERR(fan_data->hwmon_dev);
|
return PTR_ERR(fan_data->hwmon_dev);
|
||||||
#ifdef CONFIG_OF_GPIO
|
|
||||||
/* Optional cooling device register for Device tree platforms */
|
/* 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",
|
"gpio-fan",
|
||||||
fan_data,
|
fan_data,
|
||||||
&gpio_fan_cool_ops);
|
&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(&pdev->dev, "GPIO fan initialized\n");
|
dev_info(dev, "GPIO fan initialized\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -616,7 +560,7 @@ static int gpio_fan_remove(struct platform_device *pdev)
|
||||||
if (!IS_ERR(fan_data->cdev))
|
if (!IS_ERR(fan_data->cdev))
|
||||||
thermal_cooling_device_unregister(fan_data->cdev);
|
thermal_cooling_device_unregister(fan_data->cdev);
|
||||||
|
|
||||||
if (fan_data->ctrl)
|
if (fan_data->gpios)
|
||||||
set_fan_speed(fan_data, 0);
|
set_fan_speed(fan_data, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -632,7 +576,7 @@ static int gpio_fan_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct gpio_fan_data *fan_data = dev_get_drvdata(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;
|
fan_data->resume_speed = fan_data->speed_index;
|
||||||
set_fan_speed(fan_data, 0);
|
set_fan_speed(fan_data, 0);
|
||||||
}
|
}
|
||||||
|
@ -644,7 +588,7 @@ static int gpio_fan_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct gpio_fan_data *fan_data = dev_get_drvdata(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);
|
set_fan_speed(fan_data, fan_data->resume_speed);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -663,9 +607,7 @@ static struct platform_driver gpio_fan_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio-fan",
|
.name = "gpio-fan",
|
||||||
.pm = GPIO_FAN_PM,
|
.pm = GPIO_FAN_PM,
|
||||||
#ifdef CONFIG_OF_GPIO
|
|
||||||
.of_match_table = of_match_ptr(of_gpio_fan_match),
|
.of_match_table = of_match_ptr(of_gpio_fan_match),
|
||||||
#endif
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
|
||||||
/* Provide lock for writing to NB_SMU_IND_ADDR */
|
/* Provide lock for writing to NB_SMU_IND_ADDR */
|
||||||
static DEFINE_MUTEX(nb_smu_ind_mutex);
|
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 */
|
/* CPUID function 0x80000001, ebx */
|
||||||
#define CPUID_PKGTYPE_MASK 0xf0000000
|
#define CPUID_PKGTYPE_MASK 0xf0000000
|
||||||
#define CPUID_PKGTYPE_F 0x00000000
|
#define CPUID_PKGTYPE_F 0x00000000
|
||||||
|
@ -61,31 +65,72 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
|
||||||
*/
|
*/
|
||||||
#define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4
|
#define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4
|
||||||
|
|
||||||
static void amd_nb_smu_index_read(struct pci_dev *pdev, unsigned int devfn,
|
/* F17h M01h Access througn SMN */
|
||||||
int offset, u32 *val)
|
#define F17H_M01H_REPORTED_TEMP_CTRL_OFFSET 0x00059800
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
mutex_lock(&nb_smu_ind_mutex);
|
||||||
pci_bus_write_config_dword(pdev->bus, devfn,
|
pci_bus_write_config_dword(pdev->bus, devfn,
|
||||||
0xb8, offset);
|
base, offset);
|
||||||
pci_bus_read_config_dword(pdev->bus, devfn,
|
pci_bus_read_config_dword(pdev->bus, devfn,
|
||||||
0xbc, val);
|
base + 4, val);
|
||||||
mutex_unlock(&nb_smu_ind_mutex);
|
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 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,
|
static ssize_t temp1_input_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
|
struct k10temp_data *data = dev_get_drvdata(dev);
|
||||||
u32 regval;
|
u32 regval;
|
||||||
struct pci_dev *pdev = dev_get_drvdata(dev);
|
unsigned int temp;
|
||||||
|
|
||||||
if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model == 0x60) {
|
data->read_tempreg(data->pdev, ®val);
|
||||||
amd_nb_smu_index_read(pdev, PCI_DEVFN(0, 0),
|
temp = (regval >> 21) * 125;
|
||||||
F15H_M60H_REPORTED_TEMP_CTRL_OFFSET,
|
temp -= data->temp_offset;
|
||||||
®val);
|
|
||||||
} else {
|
return sprintf(buf, "%u\n", temp);
|
||||||
pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, ®val);
|
|
||||||
}
|
|
||||||
return sprintf(buf, "%u\n", (regval >> 21) * 125);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t temp1_max_show(struct device *dev,
|
static ssize_t temp1_max_show(struct device *dev,
|
||||||
|
@ -98,11 +143,12 @@ static ssize_t show_temp_crit(struct device *dev,
|
||||||
struct device_attribute *devattr, char *buf)
|
struct device_attribute *devattr, char *buf)
|
||||||
{
|
{
|
||||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||||
|
struct k10temp_data *data = dev_get_drvdata(dev);
|
||||||
int show_hyst = attr->index;
|
int show_hyst = attr->index;
|
||||||
u32 regval;
|
u32 regval;
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
pci_read_config_dword(dev_get_drvdata(dev),
|
pci_read_config_dword(data->pdev,
|
||||||
REG_HARDWARE_THERMAL_CONTROL, ®val);
|
REG_HARDWARE_THERMAL_CONTROL, ®val);
|
||||||
value = ((regval >> 16) & 0x7f) * 500 + 52000;
|
value = ((regval >> 16) & 0x7f) * 500 + 52000;
|
||||||
if (show_hyst)
|
if (show_hyst)
|
||||||
|
@ -119,7 +165,8 @@ static umode_t k10temp_is_visible(struct kobject *kobj,
|
||||||
struct attribute *attr, int index)
|
struct attribute *attr, int index)
|
||||||
{
|
{
|
||||||
struct device *dev = container_of(kobj, struct device, kobj);
|
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) {
|
if (index >= 2) {
|
||||||
u32 reg_caps, reg_htc;
|
u32 reg_caps, reg_htc;
|
||||||
|
@ -187,7 +234,9 @@ static int k10temp_probe(struct pci_dev *pdev,
|
||||||
{
|
{
|
||||||
int unreliable = has_erratum_319(pdev);
|
int unreliable = has_erratum_319(pdev);
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
struct k10temp_data *data;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (unreliable) {
|
if (unreliable) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
|
@ -199,7 +248,31 @@ static int k10temp_probe(struct pci_dev *pdev,
|
||||||
"unreliable CPU thermal sensor; check erratum 319\n");
|
"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 if (boot_cpu_data.x86 == 0x17)
|
||||||
|
data->read_tempreg = read_tempreg_nb_f17;
|
||||||
|
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);
|
k10temp_groups);
|
||||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||||
}
|
}
|
||||||
|
@ -214,6 +287,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_15H_M60H_NB_F3) },
|
||||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_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_16H_M30H_NB_F3) },
|
||||||
|
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, k10temp_id_table);
|
MODULE_DEVICE_TABLE(pci, k10temp_id_table);
|
||||||
|
|
|
@ -303,10 +303,20 @@ static const struct i2c_device_id max1619_id[] = {
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, 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 = {
|
static struct i2c_driver max1619_driver = {
|
||||||
.class = I2C_CLASS_HWMON,
|
.class = I2C_CLASS_HWMON,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "max1619",
|
.name = "max1619",
|
||||||
|
.of_match_table = of_match_ptr(max1619_of_match),
|
||||||
},
|
},
|
||||||
.probe = max1619_probe,
|
.probe = max1619_probe,
|
||||||
.id_table = max1619_id,
|
.id_table = max1619_id,
|
||||||
|
|
|
@ -0,0 +1,593 @@
|
||||||
|
/*
|
||||||
|
* Hardware monitoring driver for Maxim MAX6621
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Mellanox Technologies. All rights reserved.
|
||||||
|
* Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
|
||||||
|
*
|
||||||
|
* 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 <linux/bitops.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/hwmon-sysfs.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#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 <vadimp@mellanox.com>");
|
||||||
|
MODULE_DESCRIPTION("Driver for Maxim MAX6621");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -114,6 +114,16 @@ config SENSORS_MAX20751
|
||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called max20751.
|
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
|
config SENSORS_MAX34440
|
||||||
tristate "Maxim MAX34440 and compatibles"
|
tristate "Maxim MAX34440 and compatibles"
|
||||||
default n
|
default n
|
||||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||||
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
||||||
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
|
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
|
||||||
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
|
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
|
||||||
|
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
|
||||||
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
||||||
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
||||||
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
||||||
|
|
|
@ -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 <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#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 <andrew@aj.id.au>");
|
||||||
|
MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -404,9 +404,9 @@ extern const struct regulator_ops pmbus_regulator_ops;
|
||||||
/* Function declarations */
|
/* Function declarations */
|
||||||
|
|
||||||
void pmbus_clear_cache(struct i2c_client *client);
|
void pmbus_clear_cache(struct i2c_client *client);
|
||||||
int pmbus_set_page(struct i2c_client *client, u8 page);
|
int pmbus_set_page(struct i2c_client *client, int page);
|
||||||
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 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 pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
|
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(struct i2c_client *client, int page, u8 value);
|
||||||
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
|
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||||
|
|
|
@ -136,13 +136,13 @@ void pmbus_clear_cache(struct i2c_client *client)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pmbus_clear_cache);
|
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);
|
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
int newpage;
|
int newpage;
|
||||||
|
|
||||||
if (page != data->currpage) {
|
if (page >= 0 && page != data->currpage) {
|
||||||
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
|
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
|
||||||
newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
|
newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
|
||||||
if (newpage != page)
|
if (newpage != page)
|
||||||
|
@ -158,11 +158,9 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (page >= 0) {
|
|
||||||
rv = pmbus_set_page(client, page);
|
rv = pmbus_set_page(client, page);
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
|
||||||
|
|
||||||
return i2c_smbus_write_byte(client, value);
|
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);
|
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;
|
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);
|
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;
|
int rv;
|
||||||
|
|
||||||
|
@ -255,11 +254,9 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (page >= 0) {
|
|
||||||
rv = pmbus_set_page(client, page);
|
rv = pmbus_set_page(client, page);
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
|
||||||
|
|
||||||
return i2c_smbus_read_byte_data(client, reg);
|
return i2c_smbus_read_byte_data(client, reg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,11 @@
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/platform_data/sht15.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -34,7 +32,8 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/bitrev.h>
|
#include <linux/bitrev.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
/* Commands */
|
/* Commands */
|
||||||
#define SHT15_MEASURE_TEMP 0x03
|
#define SHT15_MEASURE_TEMP 0x03
|
||||||
|
@ -122,7 +121,8 @@ static const u8 sht15_crc8_table[] = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct sht15_data - device instance specific data
|
* 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.
|
* @read_work: bh of interrupt handler.
|
||||||
* @wait_queue: wait queue for getting values from device.
|
* @wait_queue: wait queue for getting values from device.
|
||||||
* @val_temp: last temperature value read 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.
|
* @interrupt_handled: flag used to indicate a handler has been scheduled.
|
||||||
*/
|
*/
|
||||||
struct sht15_data {
|
struct sht15_data {
|
||||||
struct sht15_platform_data *pdata;
|
struct gpio_desc *sck;
|
||||||
|
struct gpio_desc *data;
|
||||||
struct work_struct read_work;
|
struct work_struct read_work;
|
||||||
wait_queue_head_t wait_queue;
|
wait_queue_head_t wait_queue;
|
||||||
uint16_t val_temp;
|
uint16_t val_temp;
|
||||||
|
@ -205,16 +206,16 @@ static int sht15_connection_reset(struct sht15_data *data)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
err = gpio_direction_output(data->pdata->gpio_data, 1);
|
err = gpiod_direction_output(data->data, 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
for (i = 0; i < 9; ++i) {
|
for (i = 0; i < 9; ++i) {
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
}
|
}
|
||||||
return 0;
|
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)
|
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);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL); /* clock low time */
|
ndelay(SHT15_TSCKL); /* clock low time */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,23 +249,23 @@ static int sht15_transmission_start(struct sht15_data *data)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* ensure data is high and output */
|
/* ensure data is high and output */
|
||||||
err = gpio_direction_output(data->pdata->gpio_data, 1);
|
err = gpiod_direction_output(data->data, 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
gpio_set_value(data->pdata->gpio_data, 0);
|
gpiod_set_value(data->data, 0);
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
gpio_set_value(data->pdata->gpio_data, 1);
|
gpiod_set_value(data->data, 1);
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -292,20 +293,20 @@ static int sht15_wait_for_response(struct sht15_data *data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = gpio_direction_input(data->pdata->gpio_data);
|
err = gpiod_direction_input(data->data);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
if (gpio_get_value(data->pdata->gpio_data)) {
|
if (gpiod_get_value(data->data)) {
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
dev_err(data->dev, "Command not acknowledged\n");
|
dev_err(data->dev, "Command not acknowledged\n");
|
||||||
err = sht15_connection_reset(data);
|
err = sht15_connection_reset(data);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -360,17 +361,17 @@ static int sht15_ack(struct sht15_data *data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = gpio_direction_output(data->pdata->gpio_data, 0);
|
err = gpiod_direction_output(data->data, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSU);
|
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;
|
int err;
|
||||||
|
|
||||||
err = gpio_direction_output(data->pdata->gpio_data, 1);
|
err = gpiod_direction_output(data->data, 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -405,10 +406,10 @@ static u8 sht15_read_byte(struct sht15_data *data)
|
||||||
|
|
||||||
for (i = 0; i < 8; ++i) {
|
for (i = 0; i < 8; ++i) {
|
||||||
byte <<= 1;
|
byte <<= 1;
|
||||||
gpio_set_value(data->pdata->gpio_sck, 1);
|
gpiod_set_value(data->sck, 1);
|
||||||
ndelay(SHT15_TSCKH);
|
ndelay(SHT15_TSCKH);
|
||||||
byte |= !!gpio_get_value(data->pdata->gpio_data);
|
byte |= !!gpiod_get_value(data->data);
|
||||||
gpio_set_value(data->pdata->gpio_sck, 0);
|
gpiod_set_value(data->sck, 0);
|
||||||
ndelay(SHT15_TSCKL);
|
ndelay(SHT15_TSCKL);
|
||||||
}
|
}
|
||||||
return byte;
|
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);
|
err = sht15_send_cmd(data, SHT15_WRITE_STATUS);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
err = gpio_direction_output(data->pdata->gpio_data, 1);
|
err = gpiod_direction_output(data->data, 1);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
ndelay(SHT15_TSU);
|
ndelay(SHT15_TSU);
|
||||||
|
@ -528,14 +529,14 @@ static int sht15_measurement(struct sht15_data *data,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = gpio_direction_input(data->pdata->gpio_data);
|
ret = gpiod_direction_input(data->data);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
atomic_set(&data->interrupt_handled, 0);
|
atomic_set(&data->interrupt_handled, 0);
|
||||||
|
|
||||||
enable_irq(gpio_to_irq(data->pdata->gpio_data));
|
enable_irq(gpiod_to_irq(data->data));
|
||||||
if (gpio_get_value(data->pdata->gpio_data) == 0) {
|
if (gpiod_get_value(data->data) == 0) {
|
||||||
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
|
disable_irq_nosync(gpiod_to_irq(data->data));
|
||||||
/* Only relevant if the interrupt hasn't occurred. */
|
/* Only relevant if the interrupt hasn't occurred. */
|
||||||
if (!atomic_read(&data->interrupt_handled))
|
if (!atomic_read(&data->interrupt_handled))
|
||||||
schedule_work(&data->read_work);
|
schedule_work(&data->read_work);
|
||||||
|
@ -547,7 +548,7 @@ static int sht15_measurement(struct sht15_data *data,
|
||||||
data->state = SHT15_READING_NOTHING;
|
data->state = SHT15_READING_NOTHING;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
} else if (ret == 0) { /* timeout occurred */
|
} 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);
|
ret = sht15_connection_reset(data);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -826,15 +827,15 @@ static void sht15_bh_read_data(struct work_struct *work_s)
|
||||||
read_work);
|
read_work);
|
||||||
|
|
||||||
/* Firstly, verify the line is low */
|
/* 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
|
* If not, then start the interrupt again - care here as could
|
||||||
* have gone low in meantime so verify it hasn't!
|
* have gone low in meantime so verify it hasn't!
|
||||||
*/
|
*/
|
||||||
atomic_set(&data->interrupt_handled, 0);
|
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 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))
|
|| atomic_read(&data->interrupt_handled))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -918,53 +919,12 @@ static const struct of_device_id sht15_dt_match[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, 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
|
#endif
|
||||||
|
|
||||||
static int sht15_probe(struct platform_device *pdev)
|
static int sht15_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct sht15_data *data;
|
struct sht15_data *data;
|
||||||
u8 status = 0;
|
|
||||||
|
|
||||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
|
@ -977,25 +937,6 @@ static int sht15_probe(struct platform_device *pdev)
|
||||||
data->dev = &pdev->dev;
|
data->dev = &pdev->dev;
|
||||||
init_waitqueue_head(&data->wait_queue);
|
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,
|
* If a regulator is available,
|
||||||
* query what the supply voltage actually is!
|
* query what the supply voltage actually is!
|
||||||
|
@ -1030,21 +971,20 @@ static int sht15_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try requesting the GPIOs */
|
/* Try requesting the GPIOs */
|
||||||
ret = devm_gpio_request_one(&pdev->dev, data->pdata->gpio_sck,
|
data->sck = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_LOW);
|
||||||
GPIOF_OUT_INIT_LOW, "SHT15 sck");
|
if (IS_ERR(data->sck)) {
|
||||||
if (ret) {
|
ret = PTR_ERR(data->sck);
|
||||||
dev_err(&pdev->dev, "clock line GPIO request failed\n");
|
dev_err(&pdev->dev, "clock line GPIO request failed\n");
|
||||||
goto err_release_reg;
|
goto err_release_reg;
|
||||||
}
|
}
|
||||||
|
data->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
|
||||||
ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_data,
|
if (IS_ERR(data->data)) {
|
||||||
"SHT15 data");
|
ret = PTR_ERR(data->data);
|
||||||
if (ret) {
|
|
||||||
dev_err(&pdev->dev, "data line GPIO request failed\n");
|
dev_err(&pdev->dev, "data line GPIO request failed\n");
|
||||||
goto err_release_reg;
|
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,
|
sht15_interrupt_fired,
|
||||||
IRQF_TRIGGER_FALLING,
|
IRQF_TRIGGER_FALLING,
|
||||||
"sht15 data",
|
"sht15 data",
|
||||||
|
@ -1053,7 +993,7 @@ static int sht15_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev, "failed to get irq for data line\n");
|
dev_err(&pdev->dev, "failed to get irq for data line\n");
|
||||||
goto err_release_reg;
|
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);
|
ret = sht15_connection_reset(data);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_release_reg;
|
goto err_release_reg;
|
||||||
|
@ -1061,13 +1001,6 @@ static int sht15_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_release_reg;
|
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);
|
ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "sysfs create failed\n");
|
dev_err(&pdev->dev, "sysfs create failed\n");
|
||||||
|
|
|
@ -396,7 +396,7 @@ static ssize_t show_max_alarm(struct device *dev, struct device_attribute *attr,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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,
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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,
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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,
|
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);
|
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,
|
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);
|
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,
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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,
|
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);
|
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,
|
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);
|
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,
|
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);
|
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]);
|
stts751_intervals[priv->interval]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1676,7 +1676,9 @@ static int w83793_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
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;
|
struct w83793_data *data;
|
||||||
int i, tmp, val, err;
|
int i, tmp, val, err;
|
||||||
int files_fan = ARRAY_SIZE(w83793_left_fan) / 7;
|
int files_fan = ARRAY_SIZE(w83793_left_fan) / 7;
|
||||||
|
|
|
@ -91,6 +91,11 @@
|
||||||
#define to_xgene_hwmon_dev(cl) \
|
#define to_xgene_hwmon_dev(cl) \
|
||||||
container_of(cl, struct xgene_hwmon_dev, mbox_client)
|
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 {
|
struct slimpro_resp_msg {
|
||||||
u32 msg;
|
u32 msg;
|
||||||
u32 param1;
|
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)
|
static int xgene_hwmon_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct xgene_hwmon_dev *ctx;
|
struct xgene_hwmon_dev *ctx;
|
||||||
|
@ -651,6 +665,15 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct acpi_pcct_hw_reduced *cppc_ss;
|
struct acpi_pcct_hw_reduced *cppc_ss;
|
||||||
|
const struct acpi_device_id *acpi_id;
|
||||||
|
int version;
|
||||||
|
|
||||||
|
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
|
||||||
|
&pdev->dev);
|
||||||
|
if (!acpi_id)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
version = (int)acpi_id->driver_data;
|
||||||
|
|
||||||
if (device_property_read_u32(&pdev->dev, "pcc-channel",
|
if (device_property_read_u32(&pdev->dev, "pcc-channel",
|
||||||
&ctx->mbox_idx)) {
|
&ctx->mbox_idx)) {
|
||||||
|
@ -693,7 +716,13 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
ctx->comm_base_addr = cppc_ss->base_address;
|
ctx->comm_base_addr = cppc_ss->base_address;
|
||||||
if (ctx->comm_base_addr) {
|
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,
|
cppc_ss->length,
|
||||||
MEMREMAP_WB);
|
MEMREMAP_WB);
|
||||||
} else {
|
} else {
|
||||||
|
@ -761,14 +790,6 @@ static int xgene_hwmon_remove(struct platform_device *pdev)
|
||||||
return 0;
|
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[] = {
|
static const struct of_device_id xgene_hwmon_of_match[] = {
|
||||||
{.compatible = "apm,xgene-slimpro-hwmon"},
|
{.compatible = "apm,xgene-slimpro-hwmon"},
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -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 */
|
|
|
@ -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 */
|
|
Loading…
Reference in New Issue