Commit e7c1bbcf authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:
 "New drivers for:

   - Aquacomputer D5 Next

   - SB-RMI power module

  Added chip support to existing drivers:

   - Support for various Zen2 and Zen3 APUs and for Yellow Carp (SMU
     v13) added to k10temp driver

   - Support for Silicom n5010 PAC added to intel-m10-bmc driver

   - Support for BPD-RS600 added to pmbus/bpa-rs600 driver

  Other notable changes:

   - In k10temp, do not display Tdie on Zen CPUs if there is no
     difference between Tdie and Tctl

   - Converted adt7470 and dell-smm drivers to use
     devm_hwmon_device_register_with_info API

   - Support for temperature/pwm tables added to axi-fan-control driver

   - Enabled fan control for Dell Precision 7510 in dell-smm driver

  Various other minor improvements and fixes in several drivers"

* tag 'hwmon-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (41 commits)
  hwmon: add driver for Aquacomputer D5 Next
  hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API
  hwmon: (adt7470) Convert to use regmap
  hwmon: (adt7470) Fix some style issues
  hwmon: (k10temp) Add support for yellow carp
  hwmon: (k10temp) Rework the temperature offset calculation
  hwmon: (k10temp) Don't show Tdie for all Zen/Zen2/Zen3 CPU/APU
  hwmon: (k10temp) Add additional missing Zen2 and Zen3 APUs
  hwmon: remove amd_energy driver in Makefile
  hwmon: (dell-smm) Rework SMM function debugging
  hwmon: (dell-smm) Mark i8k_get_fan_nominal_speed as __init
  hwmon: (dell-smm) Mark tables as __initconst
  hwmon: (pmbus/bpa-rs600) Add workaround for incorrect Pin max
  hwmon: (pmbus/bpa-rs600) Don't use rated limits as warn limits
  hwmon: (axi-fan-control) Support temperature vs pwm points
  hwmon: (axi-fan-control) Handle irqs in natural order
  hwmon: (axi-fan-control) Make sure the clock is enabled
  hwmon: (pmbus/ibm-cffps) Fix write bits for LED control
  hwmon: (w83781d) Match on device tree compatibles
  dt-bindings: hwmon: Add bindings for Winbond W83781D
  ...
parents 871dda46 0e35f63f
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/amd,sbrmi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: >
Sideband Remote Management Interface (SB-RMI) compliant
AMD SoC power device.
maintainers:
- Akshay Gupta <Akshay.Gupta@amd.com>
description: |
SB Remote Management Interface (SB-RMI) is an SMBus compatible
interface that reports AMD SoC's Power (normalized Power) using,
Mailbox Service Request and resembles a typical 8-pin remote power
sensor's I2C interface to BMC. The power attributes in hwmon
reports power in microwatts.
properties:
compatible:
enum:
- amd,sbrmi
reg:
maxItems: 1
description: |
I2C bus address of the device as specified in Section SBI SMBus Address
of the SoC register reference. The SB-RMI address is normally 78h for
socket 0 and 70h for socket 1, but it could vary based on hardware
address select pins.
\[open source SoC register reference\]
https://www.amd.com/en/support/tech-docs?keyword=55898
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
sbrmi@3c {
compatible = "amd,sbrmi";
reg = <0x3c>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/winbond,w83781d.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Winbond W83781 and compatible hardware monitor IC
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
properties:
compatible:
enum:
- winbond,w83781d
- winbond,w83781g
- winbond,w83782d
- winbond,w83783s
- asus,as99127f
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@28 {
compatible = "winbond,w83781d";
reg = <0x28>;
};
};
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver aquacomputer-d5next
=================================
Supported devices:
* Aquacomputer D5 Next watercooling pump
Author: Aleksa Savic
Description
-----------
This driver exposes hardware sensors of the Aquacomputer D5 Next watercooling
pump, which communicates through a proprietary USB HID protocol.
Available sensors are pump and fan speed, power, voltage and current, as
well as coolant temperature. Also available through debugfs are the serial
number, firmware version and power-on count.
Attaching a fan is optional and allows it to be controlled using temperature
curves directly from the pump. If it's not connected, the fan-related sensors
will report zeroes.
The pump can be configured either through software or via its physical
interface. Configuring the pump through this driver is not implemented, as it
seems to require sending it a complete configuration. That includes addressable
RGB LEDs, for which there is no standard sysfs interface. Thus, that task is
better suited for userspace tools.
Usage notes
-----------
The pump communicates via HID reports. The driver is loaded automatically by
the kernel and supports hotswapping.
Sysfs entries
-------------
============ =============================================
temp1_input Coolant temperature (in millidegrees Celsius)
fan1_input Pump speed (in RPM)
fan2_input Fan speed (in RPM)
power1_input Pump power (in micro Watts)
power2_input Fan power (in micro Watts)
in0_input Pump voltage (in milli Volts)
in1_input Fan voltage (in milli Volts)
in2_input +5V rail voltage (in milli Volts)
curr1_input Pump current (in milli Amperes)
curr2_input Fan current (in milli Amperes)
============ =============================================
Debugfs entries
---------------
================ ===============================================
serial_number Serial number of the pump
firmware_version Version of installed firmware
power_cycles Count of how many times the pump was powered on
================ ===============================================
......@@ -39,6 +39,7 @@ Hardware Monitoring Kernel Drivers
adt7475
aht10
amc6821
aquacomputer_d5next
asb100
asc7621
aspeed-pwm-tacho
......@@ -160,6 +161,7 @@ Hardware Monitoring Kernel Drivers
pwm-fan
q54sj108a2
raspberrypi-hwmon
sbrmi
sbtsi_temp
sch5627
sch5636
......
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver sbrmi
===================
Supported hardware:
* Sideband Remote Management Interface (SB-RMI) compliant AMD SoC
device connected to the BMC via the APML.
Prefix: 'sbrmi'
Addresses scanned: This driver doesn't support address scanning.
To instantiate this driver on an AMD CPU with SB-RMI
support, the i2c bus number would be the bus connected from the board
management controller (BMC) to the CPU.
The SMBus address is really 7 bits. Some vendors and the SMBus
specification show the address as 8 bits, left justified with the R/W
bit as a write (0) making bit 0. Some vendors use only the 7 bits
to describe the address.
As mentioned in AMD's APML specification, The SB-RMI address is
normally 78h(0111 100W) or 3Ch(011 1100) for socket 0 and 70h(0111 000W)
or 38h(011 1000) for socket 1, but it could vary based on hardware
address select pins.
Datasheet: The SB-RMI interface and protocol along with the Advanced
Platform Management Link (APML) Specification is available
as part of the open source SoC register reference at:
https://www.amd.com/en/support/tech-docs?keyword=55898
Author: Akshay Gupta <akshay.gupta@amd.com>
Description
-----------
The APML provides a way to communicate with the SB Remote Management interface
(SB-RMI) module from the external SMBus master that can be used to report socket
power on AMD platforms using mailbox command and resembles a typical 8-pin remote
power sensor's I2C interface to BMC.
This driver implements current power with power cap and power cap max.
sysfs-Interface
---------------
Power sensors can be queried and set via the standard ``hwmon`` interface
on ``sysfs``, under the directory ``/sys/class/hwmon/hwmonX`` for some value
of ``X`` (search for the ``X`` such that ``/sys/class/hwmon/hwmonX/name`` has
content ``sbrmi``)
================ ===== ========================================================
Name Perm Description
================ ===== ========================================================
power1_input RO Current Power consumed
power1_cap RW Power limit can be set between 0 and power1_cap_max
power1_cap_max RO Maximum powerlimit calculated and reported by the SMU FW
================ ===== ========================================================
The following example show how the 'Power' attribute from the i2c-addresses
can be monitored using the userspace utilities like ``sensors`` binary::
# sensors
sbrmi-i2c-1-38
Adapter: bcm2835 I2C adapter
power1: 61.00 W (cap = 225.00 W)
sbrmi-i2c-1-3c
Adapter: bcm2835 I2C adapter
power1: 28.39 W (cap = 224.77 W)
#
Also, Below shows how get and set the values from sysfs entries individually::
# cat /sys/class/hwmon/hwmon1/power1_cap_max
225000000
# echo 180000000 > /sys/class/hwmon/hwmon1/power1_cap
# cat /sys/class/hwmon/hwmon1/power1_cap
180000000
......@@ -32,5 +32,5 @@ Usage Notes
The driver relies on device tree node to indicate the presence of SCPI
support in the kernel. See
Documentation/devicetree/bindings/arm/arm,scpi.txt for details of the
Documentation/devicetree/bindings/firmware/arm,scpi.yaml for details of the
devicetree node.
......@@ -42,4 +42,4 @@ humidity1_input Measured humidity in %H
update_interval The minimum interval for polling the sensor,
in milliseconds. Writable. Must be at least
2000.
============== =============================================
=============== ============================================
......@@ -1316,6 +1316,13 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/aptina-pll.*
AQUACOMPUTER D5 NEXT PUMP SENSOR DRIVER
M: Aleksa Savic <savicaleksa83@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/aquacomputer_d5next.rst
F: drivers/hwmon/aquacomputer_d5next.c
AQUANTIA ETHERNET DRIVER (atlantic)
M: Igor Russkikh <irusskikh@marvell.com>
L: netdev@vger.kernel.org
......
......@@ -25,6 +25,8 @@
#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F4 0x144c
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F4 0x1444
#define PCI_DEVICE_ID_AMD_19H_DF_F4 0x1654
#define PCI_DEVICE_ID_AMD_19H_M40H_ROOT 0x14b5
#define PCI_DEVICE_ID_AMD_19H_M40H_DF_F4 0x167d
#define PCI_DEVICE_ID_AMD_19H_M50H_DF_F4 0x166e
/* Protect the PCI config register pairs used for SMN and DF indirect access. */
......@@ -37,6 +39,7 @@ static const struct pci_device_id amd_root_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_ROOT) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_ROOT) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_ROOT) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_ROOT) },
{}
};
......@@ -58,6 +61,7 @@ static const struct pci_device_id amd_nb_misc_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) },
{}
};
......@@ -74,6 +78,7 @@ static const struct pci_device_id amd_nb_link_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F4) },
{}
......
......@@ -254,6 +254,16 @@ config SENSORS_AHT10
This driver can also be built as a module. If so, the module
will be called aht10.
config SENSORS_AQUACOMPUTER_D5NEXT
tristate "Aquacomputer D5 Next watercooling pump"
depends on USB_HID
help
If you say yes here you get support for the Aquacomputer D5 Next
watercooling pump sensors.
This driver can also be built as a module. If so, the module
will be called aquacomputer_d5next.
config SENSORS_AS370
tristate "Synaptics AS370 SoC hardware monitoring driver"
help
......@@ -1551,6 +1561,16 @@ config SENSORS_SBTSI
This driver can also be built as a module. If so, the module will
be called sbtsi_temp.
config SENSORS_SBRMI
tristate "Emulated SB-RMI sensor"
depends on I2C
help
If you say yes here you get support for emulated RMI
sensors on AMD SoCs with APML interface connected to a BMC device.
This driver can also be built as a module. If so, the module will
be called sbrmi.
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
depends on GPIOLIB || COMPILE_TEST
......
......@@ -45,8 +45,8 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
obj-$(CONFIG_SENSORS_AHT10) += aht10.o
obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
......@@ -164,6 +164,7 @@ obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o
obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o
obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
......
......@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/util_macros.h>
......@@ -35,7 +36,10 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38
#define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B
#define ADT7470_REG_CFG 0x40
#define ADT7470_STRT_MASK 0x01
#define ADT7470_TEST_MASK 0x02
#define ADT7470_FSPD_MASK 0x04
#define ADT7470_T05_STB_MASK 0x80
#define ADT7470_REG_ALARM1 0x41
#define ADT7470_R1T_ALARM 0x01
#define ADT7470_R2T_ALARM 0x02
......@@ -137,7 +141,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_FREQ_SHIFT 4
struct adt7470_data {
struct i2c_client *client;
struct regmap *regmap;
struct mutex lock;
char sensors_valid;
char limits_valid;
......@@ -171,51 +175,76 @@ struct adt7470_data {
* 16-bit registers on the ADT7470 are low-byte first. The data sheet says
* that the low byte must be read before the high byte.
*/
static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg)
static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg,
unsigned int *val)
{
u16 foo;
foo = i2c_smbus_read_byte_data(client, reg);
foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8);
return foo;
u8 regval[2];
int err;
err = regmap_bulk_read(data->regmap, reg, &regval, 2);
if (err < 0)
return err;
*val = regval[0] | (regval[1] << 8);
return 0;
}
static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg,
u16 value)
static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg,
unsigned int val)
{
return i2c_smbus_write_byte_data(client, reg, value & 0xFF)
|| i2c_smbus_write_byte_data(client, reg + 1, value >> 8);
u8 regval[2];
regval[0] = val & 0xFF;
regval[1] = val >> 8;
return regmap_bulk_write(data->regmap, reg, &regval, 2);
}
/* Probe for temperature sensors. Assumes lock is held */
static int adt7470_read_temperatures(struct i2c_client *client,
struct adt7470_data *data)
static int adt7470_read_temperatures(struct adt7470_data *data)
{
unsigned long res;
unsigned int pwm_cfg[2];
int err;
int i;
u8 cfg, pwm[4], pwm_cfg[2];
u8 pwm[ADT7470_FAN_COUNT];
/* save pwm[1-4] config register */
pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0));
pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2));
err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
if (err < 0)
return err;
err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]);
if (err < 0)
return err;
/* set manual pwm to whatever it is set to now */
for (i = 0; i < ADT7470_FAN_COUNT; i++)
pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i));
err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
ADT7470_PWM_COUNT);
if (err < 0)
return err;
/* put pwm in manual mode */
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0),
pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK));
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2),
pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK));
err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0),
ADT7470_PWM_AUTO_MASK, 0);
if (err < 0)
return err;
err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
ADT7470_PWM_AUTO_MASK, 0);
if (err < 0)
return err;
/* write pwm control to whatever it was */
for (i = 0; i < ADT7470_FAN_COUNT; i++)
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]);
err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
ADT7470_PWM_COUNT);
if (err < 0)
return err;
/* start reading temperature sensors */
cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
cfg |= 0x80;
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
if (err < 0)
return err;
/* Delay is 200ms * number of temp sensors. */
res = msleep_interruptible((data->num_temp_sensors >= 0 ?
......@@ -223,26 +252,31 @@ static int adt7470_read_temperatures(struct i2c_client *client,
TEMP_COLLECTION_TIME));
/* done reading temperature sensors */
cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
cfg &= ~0x80;
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_T05_STB_MASK, 0);
if (err < 0)
return err;
/* restore pwm[1-4] config registers */
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
if (err < 0)
return err;
err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
if (err < 0)
return err;
if (res) {
pr_err("ha ha, interrupted\n");
if (res)
return -EAGAIN;
}
/* Only count fans if we have to */
if (data->num_temp_sensors >= 0)
return 0;
err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0],
ADT7470_TEMP_COUNT);
if (err < 0)
return err;
for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
data->temp[i] = i2c_smbus_read_byte_data(client,
ADT7470_TEMP_REG(i));
if (data->temp[i])
data->num_temp_sensors = i + 1;
}
......@@ -257,7 +291,7 @@ static int adt7470_update_thread(void *p)
while (!kthread_should_stop()) {
mutex_lock(&data->lock);
adt7470_read_temperatures(client, data);
adt7470_read_temperatures(data);
mutex_unlock(&data->lock);
set_current_state(TASK_INTERRUPTIBLE);
......@@ -272,89 +306,116 @@ static int adt7470_update_thread(void *p)
static int adt7470_update_sensors(struct adt7470_data *data)
{
struct i2c_client *client = data->client;
u8 cfg;
unsigned int val;
int err;
int i;
if (!data->temperatures_probed)
adt7470_read_temperatures(client, data);
err = adt7470_read_temperatures(data);
else
for (i = 0; i < ADT7470_TEMP_COUNT; i++)
data->temp[i] = i2c_smbus_read_byte_data(client,
ADT7470_TEMP_REG(i));
err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0],
ADT7470_TEMP_COUNT);
if (err < 0)
return err;
for (i = 0; i < ADT7470_FAN_COUNT; i++)
data->fan[i] = adt7470_read_word_data(client,
ADT7470_REG_FAN(i));
for (i = 0; i < ADT7470_FAN_COUNT; i++) {
err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val);
if (err < 0)
return err;
data->fan[i] = val;
}
for (i = 0; i < ADT7470_PWM_COUNT; i++) {
int reg;
int reg_mask;
err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT);
if (err < 0)
return err;
data->pwm[i] = i2c_smbus_read_byte_data(client,
ADT7470_REG_PWM(i));
for (i = 0; i < ADT7470_PWM_COUNT; i++) {
unsigned int mask;
if (i % 2)
reg_mask = ADT7470_PWM2_AUTO_MASK;
mask = ADT7470_PWM2_AUTO_MASK;
else
reg_mask = ADT7470_PWM1_AUTO_MASK;
mask = ADT7470_PWM1_AUTO_MASK;
reg = ADT7470_REG_PWM_CFG(i);
if (i2c_smbus_read_byte_data(client, reg) & reg_mask)
data->pwm_automatic[i] = 1;
else
data->pwm_automatic[i] = 0;
err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val);
if (err < 0)
return err;
data->pwm_automatic[i] = !!(val & mask);
reg = ADT7470_REG_PWM_AUTO_TEMP(i);
cfg = i2c_smbus_read_byte_data(client, reg);
err = regmap_read(data->regmap, ADT7470_REG_PWM_AUTO_TEMP(i), &val);
if (err < 0)
return err;
if (!(i % 2))
data->pwm_auto_temp[i] = cfg >> 4;
data->pwm_auto_temp[i] = val >> 4;
else
data->pwm_auto_temp[i] = cfg & 0xF;
data->pwm_auto_temp[i] = val & 0xF;
}
if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) &
ADT7470_FSPD_MASK)
data->force_pwm_max = 1;
else
data->force_pwm_max = 0;
err = regmap_read(data->regmap, ADT7470_REG_CFG, &val);
if (err < 0)
return err;
data->force_pwm_max = !!(val & ADT7470_FSPD_MASK);
data->alarm = i2c_smbus_read_byte_data(client, ADT7470_REG_ALARM1);
if (data->alarm & ADT7470_OOL_ALARM)
data->alarm |= ALARM2(i2c_smbus_read_byte_data(client,
ADT7470_REG_ALARM2));
data->alarms_mask = adt7470_read_word_data(client,
ADT7470_REG_ALARM1_MASK);
err = regmap_read(data->regmap, ADT7470_REG_ALARM1, &val);
if (err < 0)
return err;
data->alarm = val;
if (data->alarm & ADT7470_OOL_ALARM) {
err = regmap_read(data->regmap, ADT7470_REG_ALARM2, &val);
if (err < 0)
return err;
data->alarm |= ALARM2(val);
}
err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val);
if (err < 0)
return err;
data->alarms_mask = val;
return 0;
}
static int adt7470_update_limits(struct adt7470_data *data)
{
struct i2c_client *client = data->client;
unsigned int val;
int err;
int i;
for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
data->temp_min[i] = i2c_smbus_read_byte_data(client,
ADT7470_TEMP_MIN_REG(i));
data->temp_max[i] = i2c_smbus_read_byte_data(client,
ADT7470_TEMP_MAX_REG(i));
err = regmap_read(data->regmap, ADT7470_TEMP_MIN_REG(i), &val);
if (err < 0)
return err;
data->temp_min[i] = (s8)val;
err = regmap_read(data->regmap, ADT7470_TEMP_MAX_REG(i), &val);
if (err < 0)
return err;
data->temp_max[i] = (s8)val;
}
for (i = 0; i < ADT7470_FAN_COUNT; i++) {
data->fan_min[i] = adt7470_read_word_data(client,
ADT7470_REG_FAN_MIN(i));
data->fan_max[i] = adt7470_read_word_data(client,
ADT7470_REG_FAN_MAX(i));
err = adt7470_read_word_data(data, ADT7470_REG_FAN_MIN(i), &val);
if (err < 0)
return err;
data->fan_min[i] = val;
err = adt7470_read_word_data(data, ADT7470_REG_FAN_MAX(i), &val);
if (err < 0)
return err;
data->fan_max[i] = val;
}
for (i = 0; i < ADT7470_PWM_COUNT; i++) {
data->pwm_max[i] = i2c_smbus_read_byte_data(client,
ADT7470_REG_PWM_MAX(i));
data->pwm_min[i] = i2c_smbus_read_byte_data(client,
ADT7470_REG_PWM_MIN(i));
data->pwm_tmin[i] = i2c_smbus_read_byte_data(client,
ADT7470_REG_PWM_TMIN(i));
err = regmap_read(data->regmap, ADT7470_REG_PWM_MAX(i), &val);
if (err < 0)
return err;
data->pwm_max[i] = val;
err = regmap_read(data->regmap, ADT7470_REG_PWM_MIN(i), &val);
if (err < 0)
return err;
data->pwm_min[i] = val;
err = regmap_read(data->regmap, ADT7470_REG_PWM_TMIN(i), &val);
if (err < 0)
return err;
data->pwm_tmin[i] = (s8)val;
}
return 0;
......@@ -472,93 +533,63 @@ static ssize_t num_temp_sensors_store(struct device *dev,
return count;
}
static ssize_t temp_min_show(struct device *dev,
struct device_attribute *devattr, char *buf)
static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *val)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]);
switch (attr) {
case hwmon_temp_input:
*val = 1000 * data->temp[channel];
break;
case hwmon_temp_min:
*val = 1000 * data->temp_min[channel];
break;
case hwmon_temp_max:
*val = 1000 * data->temp_max[channel];
break;
case hwmon_temp_alarm:
*val = !!(data->alarm & channel);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static ssize_t temp_min_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long val)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
int err;
temp = clamp_val(temp, -128000, 127000);
temp = DIV_ROUND_CLOSEST(temp, 1000);
val = clamp_val(val, -128000, 127000);
val = DIV_ROUND_CLOSEST(val, 1000);
switch (attr) {
case hwmon_temp_min:
mutex_lock(&data->lock);
data->temp_min[attr->index] = temp;
i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index),
temp);
data->temp_min[channel] = val;
err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
mutex_unlock(&data->lock);
return count;
}
static ssize_t temp_max_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]);
}
static ssize_t temp_max_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
temp = clamp_val(temp, -128000, 127000);
temp = DIV_ROUND_CLOSEST(temp, 1000);
break;
case hwmon_temp_max:
mutex_lock(&data->lock);
data->temp_max[attr->index] = temp;
i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index),
temp);
data->temp_max[channel] = val;
err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
mutex_unlock(&data->lock);
break;
default:
return -EOPNOTSUPP;
}
return count;
}
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]);
return err;
}
static ssize_t alarm_mask_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
struct device_attribute *devattr, char *buf)
{
struct adt7470_data *data = adt7470_update_device(dev);
......@@ -574,6 +605,7 @@ static ssize_t alarm_mask_store(struct device *dev,
{
struct adt7470_data *data = dev_get_drvdata(dev);
long mask;
int err;
if (kstrtoul(buf, 0, &mask))
return -EINVAL;
......@@ -583,104 +615,74 @@ static ssize_t alarm_mask_store(struct device *dev,
mutex_lock(&data->lock);
data->alarms_mask = mask;
adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask);
err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask);
mutex_unlock(&data->lock);
return count;
return err < 0 ? err : count;
}
static ssize_t fan_max_show(struct device *dev,
struct device_attribute *devattr, char *buf)
static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
if (FAN_DATA_VALID(data->fan_max[attr->index]))
return sprintf(buf, "%d\n",
FAN_PERIOD_TO_RPM(data->fan_max[attr->index]));
switch (attr) {
case hwmon_fan_input:
if (FAN_DATA_VALID(data->fan[channel]))
*val = FAN_PERIOD_TO_RPM(data->fan[channel]);
else
return sprintf(buf, "0\n");
*val = 0;
break;
case hwmon_fan_min:
if (FAN_DATA_VALID(data->fan_min[channel]))
*val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
else
*val = 0;
break;
case hwmon_fan_max:
if (FAN_DATA_VALID(data->fan_max[channel]))
*val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
else
*val = 0;
break;
case hwmon_fan_alarm:
*val = !!(data->alarm & (1 << (12 + channel)));
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static ssize_t fan_max_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
static int adt7470_fan_write(struct device *dev, u32 attr, int channel, long val)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
if (kstrtol(buf, 10, &temp) || !temp)
return -EINVAL;
int err;
temp = FAN_RPM_TO_PERIOD(temp);
temp = clamp_val(temp, 1, 65534);
val = FAN_RPM_TO_PERIOD(val);
val = clamp_val(val, 1, 65534);
switch (attr) {
case hwmon_fan_min:
mutex_lock(&data->lock);
data->fan_max[attr->index] = temp;
adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp);
data->fan_min[channel] = val;
err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(channel), val);
mutex_unlock(&data->lock);
return count;
}
static ssize_t fan_min_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
if (FAN_DATA_VALID(data->fan_min[attr->index]))
return sprintf(buf, "%d\n",
FAN_PERIOD_TO_RPM(data->fan_min[attr->index]));
else
return sprintf(buf, "0\n");
}
static ssize_t fan_min_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
if (kstrtol(buf, 10, &temp) || !temp)
return -EINVAL;
temp = FAN_RPM_TO_PERIOD(temp);
temp = clamp_val(temp, 1, 65534);
break;
case hwmon_fan_max:
mutex_lock(&data->lock);
data->fan_min[attr->index] = temp;
adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp);
data->fan_max[channel] = val;
err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(channel), val);
mutex_unlock(&data->lock);
break;
default:
return -EOPNOTSUPP;
}
return count;
}
static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
if (FAN_DATA_VALID(data->fan[attr->index]))
return sprintf(buf, "%d\n",
FAN_PERIOD_TO_RPM(data->fan[attr->index]));
else
return sprintf(buf, "0\n");
return err;
}
static ssize_t force_pwm_max_show(struct device *dev,
......@@ -699,57 +701,20 @@ static ssize_t force_pwm_max_store(struct device *dev,
const char *buf, size_t count)
{
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
u8 reg;
int err;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
mutex_lock(&data->lock);
data->force_pwm_max = temp;
reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
if (temp)
reg |= ADT7470_FSPD_MASK;
else
reg &= ~ADT7470_FSPD_MASK;
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg);
mutex_unlock(&data->lock);
return count;
}
static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->pwm[attr->index]);
}
static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
temp = clamp_val(temp, 0, 255);
mutex_lock(&data->lock);
data->pwm[attr->index] = temp;
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp);
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_FSPD_MASK,
temp ? ADT7470_FSPD_MASK : 0);
mutex_unlock(&data->lock);
return count;
return err < 0 ? err : count;
}
/* These are the valid PWM frequencies to the nearest Hz */
......@@ -757,17 +722,20 @@ static const int adt7470_freq_map[] = {
11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
};
static ssize_t pwm1_freq_show(struct device *dev,
struct device_attribute *devattr, char *buf)
static int pwm1_freq_get(struct device *dev)
{
struct adt7470_data *data = adt7470_update_device(dev);
unsigned char cfg_reg_1;
unsigned char cfg_reg_2;
struct adt7470_data *data = dev_get_drvdata(dev);
unsigned int cfg_reg_1, cfg_reg_2;
int index;
int err;
mutex_lock(&data->lock);
cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1);
if (err < 0)
goto out;
err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2);
if (err < 0)
goto out;
mutex_unlock(&data->lock);
index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
......@@ -776,22 +744,43 @@ static ssize_t pwm1_freq_show(struct device *dev,
if (index >= ARRAY_SIZE(adt7470_freq_map))
index = ARRAY_SIZE(adt7470_freq_map) - 1;
return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
return adt7470_freq_map[index];
out:
mutex_unlock(&data->lock);
return err;
}
static ssize_t pwm1_freq_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val)
{
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
switch (attr) {
case hwmon_pwm_input:
*val = data->pwm[channel];
break;
case hwmon_pwm_enable:
*val = 1 + data->pwm_automatic[channel];
break;
case hwmon_pwm_freq:
*val = pwm1_freq_get(dev);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int pwm1_freq_set(struct device *dev, long freq)
{
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long freq;
unsigned int low_freq = ADT7470_CFG_LF;
int index;
int low_freq = ADT7470_CFG_LF;
unsigned char val;
if (kstrtol(buf, 10, &freq))
return -EINVAL;
int err;
/* Round the user value given to the closest available frequency */
index = find_closest(freq, adt7470_freq_map,
......@@ -804,16 +793,61 @@ static ssize_t pwm1_freq_store(struct device *dev,
mutex_lock(&data->lock);
/* Configuration Register 1 */
val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
(val & ~ADT7470_CFG_LF) | low_freq);
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_CFG_LF, low_freq);
if (err < 0)
goto out;
/* Configuration Register 2 */
val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
(val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
ADT7470_FREQ_MASK,
index << ADT7470_FREQ_SHIFT);
out:
mutex_unlock(&data->lock);
return count;
return err;
}
static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val)
{
struct adt7470_data *data = dev_get_drvdata(dev);
unsigned int pwm_auto_reg_mask;
int err;
switch (attr) {
case hwmon_pwm_input:
val = clamp_val(val, 0, 255);
mutex_lock(&data->lock);
data->pwm[channel] = val;
err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
data->pwm[channel]);
mutex_unlock(&data->lock);
break;
case hwmon_pwm_enable:
if (channel % 2)
pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK;
else
pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK;
if (val != 2 && val != 1)
return -EINVAL;
val--;
mutex_lock(&data->lock);
data->pwm_automatic[channel] = val;
err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
pwm_auto_reg_mask,
val ? pwm_auto_reg_mask : 0);
mutex_unlock(&data->lock);
break;
case hwmon_pwm_freq:
err = pwm1_freq_set(dev, val);
break;
default:
return -EOPNOTSUPP;
}
return err;
}
static ssize_t pwm_max_show(struct device *dev,
......@@ -834,8 +868,8 @@ static ssize_t pwm_max_store(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
int err;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
......@@ -844,11 +878,11 @@ static ssize_t pwm_max_store(struct device *dev,
mutex_lock(&data->lock);
data->pwm_max[attr->index] = temp;
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index),
err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index),
temp);
mutex_unlock(&data->lock);
return count;
return err < 0 ? err : count;
}
static ssize_t pwm_min_show(struct device *dev,
......@@ -869,8 +903,8 @@ static ssize_t pwm_min_store(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
int err;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
......@@ -879,11 +913,11 @@ static ssize_t pwm_min_store(struct device *dev,
mutex_lock(&data->lock);
data->pwm_min[attr->index] = temp;
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index),
err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index),
temp);
mutex_unlock(&data->lock);
return count;
return err < 0 ? err : count;
}
static ssize_t pwm_tmax_show(struct device *dev,
......@@ -917,8 +951,8 @@ static ssize_t pwm_tmin_store(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
int err;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
......@@ -928,60 +962,11 @@ static ssize_t pwm_tmin_store(struct device *dev,
mutex_lock(&data->lock);
data->pwm_tmin[attr->index] = temp;
i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index),
err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index),
temp);
mutex_unlock(&data->lock);
return count;
}
static ssize_t pwm_auto_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]);
}
static ssize_t pwm_auto_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index);
int pwm_auto_reg_mask;
long temp;
u8 reg;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
if (attr->index % 2)
pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK;
else
pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK;
if (temp != 2 && temp != 1)
return -EINVAL;
temp--;
mutex_lock(&data->lock);
data->pwm_automatic[attr->index] = temp;
reg = i2c_smbus_read_byte_data(client, pwm_auto_reg);
if (temp)
reg |= pwm_auto_reg_mask;
else
reg &= ~pwm_auto_reg_mask;
i2c_smbus_write_byte_data(client, pwm_auto_reg, reg);
mutex_unlock(&data->lock);
return count;
return err < 0 ? err : count;
}
static ssize_t pwm_auto_temp_show(struct device *dev,
......@@ -1016,10 +1001,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index);
unsigned int mask, val;
long temp;
u8 reg;
int err;
if (kstrtol(buf, 10, &temp))
return -EINVAL;
......@@ -1030,111 +1015,27 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
mutex_lock(&data->lock);
data->pwm_automatic[attr->index] = temp;
reg = i2c_smbus_read_byte_data(client, pwm_auto_reg);
if (!(attr->index % 2)) {
reg &= 0xF;
reg |= (temp << 4) & 0xF0;
mask = 0xF0;
val = (temp << 4) & 0xF0;
} else {
reg &= 0xF0;
reg |= temp & 0xF;
mask = 0x0F;
val = temp & 0x0F;
}
i2c_smbus_write_byte_data(client, pwm_auto_reg, reg);
err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
mutex_unlock(&data->lock);
return count;
}
static ssize_t alarm_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
if (data->alarm & attr->index)
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
return err < 0 ? err : count;
}
static DEVICE_ATTR_RW(alarm_mask);
static DEVICE_ATTR_RW(num_temp_sensors);
static DEVICE_ATTR_RW(auto_update_interval);
static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
static SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3);
static SENSOR_DEVICE_ATTR_RW(temp5_max, temp_max, 4);
static SENSOR_DEVICE_ATTR_RW(temp6_max, temp_max, 5);
static SENSOR_DEVICE_ATTR_RW(temp7_max, temp_max, 6);
static SENSOR_DEVICE_ATTR_RW(temp8_max, temp_max, 7);
static SENSOR_DEVICE_ATTR_RW(temp9_max, temp_max, 8);
static SENSOR_DEVICE_ATTR_RW(temp10_max, temp_max, 9);
static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
static SENSOR_DEVICE_ATTR_RW(temp4_min, temp_min, 3);
static SENSOR_DEVICE_ATTR_RW(temp5_min, temp_min, 4);
static SENSOR_DEVICE_ATTR_RW(temp6_min, temp_min, 5);
static SENSOR_DEVICE_ATTR_RW(temp7_min, temp_min, 6);
static SENSOR_DEVICE_ATTR_RW(temp8_min, temp_min, 7);
static SENSOR_DEVICE_ATTR_RW(temp9_min, temp_min, 8);
static SENSOR_DEVICE_ATTR_RW(temp10_min, temp_min, 9);
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4);
static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5);
static SENSOR_DEVICE_ATTR_RO(temp7_input, temp, 6);
static SENSOR_DEVICE_ATTR_RO(temp8_input, temp, 7);
static SENSOR_DEVICE_ATTR_RO(temp9_input, temp, 8);
static SENSOR_DEVICE_ATTR_RO(temp10_input, temp, 9);
static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, ADT7470_R1T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, ADT7470_R2T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, ADT7470_R3T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp4_alarm, alarm, ADT7470_R4T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp5_alarm, alarm, ADT7470_R5T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp6_alarm, alarm, ADT7470_R6T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp7_alarm, alarm, ADT7470_R7T_ALARM);
static SENSOR_DEVICE_ATTR_RO(temp8_alarm, alarm, ALARM2(ADT7470_R8T_ALARM));
static SENSOR_DEVICE_ATTR_RO(temp9_alarm, alarm, ALARM2(ADT7470_R9T_ALARM));
static SENSOR_DEVICE_ATTR_RO(temp10_alarm, alarm, ALARM2(ADT7470_R10T_ALARM));
static SENSOR_DEVICE_ATTR_RW(fan1_max, fan_max, 0);
static SENSOR_DEVICE_ATTR_RW(fan2_max, fan_max, 1);
static SENSOR_DEVICE_ATTR_RW(fan3_max, fan_max, 2);
static SENSOR_DEVICE_ATTR_RW(fan4_max, fan_max, 3);
static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0);
static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2);
static SENSOR_DEVICE_ATTR_RW(fan4_min, fan_min, 3);
static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3);
static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, ALARM2(ADT7470_FAN1_ALARM));
static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, ALARM2(ADT7470_FAN2_ALARM));
static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, ALARM2(ADT7470_FAN3_ALARM));
static SENSOR_DEVICE_ATTR_RO(fan4_alarm, alarm, ALARM2(ADT7470_FAN4_ALARM));
static SENSOR_DEVICE_ATTR_RW(force_pwm_max, force_pwm_max, 0);
static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1);
static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2);
static SENSOR_DEVICE_ATTR_RW(pwm4, pwm, 3);
static DEVICE_ATTR_RW(pwm1_freq);
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_min, 0);
static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm_min, 1);
static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm_min, 2);
......@@ -1155,11 +1056,6 @@ static SENSOR_DEVICE_ATTR_RO(pwm2_auto_point2_temp, pwm_tmax, 1);
static SENSOR_DEVICE_ATTR_RO(pwm3_auto_point2_temp, pwm_tmax, 2);
static SENSOR_DEVICE_ATTR_RO(pwm4_auto_point2_temp, pwm_tmax, 3);
static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_auto, 0);
static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_auto, 1);
static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_auto, 2);
static SENSOR_DEVICE_ATTR_RW(pwm4_enable, pwm_auto, 3);
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels_temp, pwm_auto_temp, 0);
static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels_temp, pwm_auto_temp, 1);
static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels_temp, pwm_auto_temp, 2);
......@@ -1169,68 +1065,7 @@ static struct attribute *adt7470_attrs[] = {
&dev_attr_alarm_mask.attr,
&dev_attr_num_temp_sensors.attr,
&dev_attr_auto_update_interval.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp6_max.dev_attr.attr,
&sensor_dev_attr_temp7_max.dev_attr.attr,
&sensor_dev_attr_temp8_max.dev_attr.attr,
&sensor_dev_attr_temp9_max.dev_attr.attr,
&sensor_dev_attr_temp10_max.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp4_min.dev_attr.attr,
&sensor_dev_attr_temp5_min.dev_attr.attr,
&sensor_dev_attr_temp6_min.dev_attr.attr,
&sensor_dev_attr_temp7_min.dev_attr.attr,
&sensor_dev_attr_temp8_min.dev_attr.attr,
&sensor_dev_attr_temp9_min.dev_attr.attr,
&sensor_dev_attr_temp10_min.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_temp7_input.dev_attr.attr,
&sensor_dev_attr_temp8_input.dev_attr.attr,
&sensor_dev_attr_temp9_input.dev_attr.attr,
&sensor_dev_attr_temp10_input.dev_attr.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_alarm.dev_attr.attr,
&sensor_dev_attr_temp7_alarm.dev_attr.attr,
&sensor_dev_attr_temp8_alarm.dev_attr.attr,
&sensor_dev_attr_temp9_alarm.dev_attr.attr,
&sensor_dev_attr_temp10_alarm.dev_attr.attr,
&sensor_dev_attr_fan1_max.dev_attr.attr,
&sensor_dev_attr_fan2_max.dev_attr.attr,
&sensor_dev_attr_fan3_max.dev_attr.attr,
&sensor_dev_attr_fan4_max.dev_attr.attr,
&sensor_dev_attr_fan1_min.dev_attr.attr,
&sensor_dev_attr_fan2_min.dev_attr.attr,
&sensor_dev_attr_fan3_min.dev_attr.attr,
&sensor_dev_attr_fan4_min.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan4_input.dev_attr.attr,
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
&sensor_dev_attr_fan3_alarm.dev_attr.attr,
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_force_pwm_max.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
&dev_attr_pwm1_freq.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm4.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
......@@ -1247,10 +1082,6 @@ static struct attribute *adt7470_attrs[] = {
&sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
&sensor_dev_attr_pwm4_enable.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
......@@ -1260,6 +1091,129 @@ static struct attribute *adt7470_attrs[] = {
ATTRIBUTE_GROUPS(adt7470);
static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
switch (type) {
case hwmon_temp:
return adt7470_temp_read(dev, attr, channel, val);
case hwmon_fan:
return adt7470_fan_read(dev, attr, channel, val);
case hwmon_pwm:
return adt7470_pwm_read(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long val)
{
switch (type) {
case hwmon_temp:
return adt7470_temp_write(dev, attr, channel, val);
case hwmon_fan:
return adt7470_fan_write(dev, attr, channel, val);
case hwmon_pwm:
return adt7470_pwm_write(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
umode_t mode = 0;
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp:
case hwmon_temp_alarm:
mode = 0444;
break;
case hwmon_temp_min:
case hwmon_temp_max:
mode = 0644;
break;
default:
break;
}
break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_alarm:
mode = 0444;
break;
case hwmon_fan_min:
case hwmon_fan_max:
mode = 0644;
break;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
case hwmon_pwm_enable:
mode = 0644;
break;
case hwmon_pwm_freq:
if (channel == 0)
mode = 0644;
else
mode = 0;
break;
default:
break;
}
break;
default:
break;
}
return mode;
}
static const struct hwmon_ops adt7470_hwmon_ops = {
.is_visible = adt7470_is_visible,
.read = adt7470_read,
.write = adt7470_write,
};
static const struct hwmon_channel_info *adt7470_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
NULL
};
static const struct hwmon_chip_info adt7470_chip_info = {
.ops = &adt7470_hwmon_ops,
.info = adt7470_info,
};
/* Return 0 if detection is successful, -ENODEV otherwise */
static int adt7470_detect(struct i2c_client *client,
struct i2c_board_info *info)
......@@ -1282,28 +1236,24 @@ static int adt7470_detect(struct i2c_client *client,
if (revision != ADT7470_REVISION)
return -ENODEV;
strlcpy(info->type, "adt7470", I2C_NAME_SIZE);
strscpy(info->type, "adt7470", I2C_NAME_SIZE);
return 0;
}
static void adt7470_init_client(struct i2c_client *client)
{
int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
if (reg < 0) {
dev_err(&client->dev, "cannot read configuration register\n");
} else {
/* start monitoring (and do a self-test) */
i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3);
}
}
static const struct regmap_config adt7470_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.use_single_read = true,
.use_single_write = true,
};
static int adt7470_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct adt7470_data *data;
struct device *hwmon_dev;
int err;
data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
if (!data)
......@@ -1311,19 +1261,25 @@ static int adt7470_probe(struct i2c_client *client)
data->num_temp_sensors = -1;
data->auto_update_interval = AUTO_UPDATE_INTERVAL;
data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
i2c_set_clientdata(client, data);
data->client = client;
mutex_init(&data->lock);
dev_info(&client->dev, "%s chip found\n", client->name);
/* Initialize the ADT7470 chip */
adt7470_init_client(client);
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_STRT_MASK | ADT7470_TEST_MASK,
ADT7470_STRT_MASK | ADT7470_TEST_MASK);
if (err < 0)
return err;
/* Register sysfs hooks */
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data,
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&adt7470_chip_info,
adt7470_groups);
if (IS_ERR(hwmon_dev))
......@@ -1331,9 +1287,8 @@ static int adt7470_probe(struct i2c_client *client)
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev));
if (IS_ERR(data->auto_update)) {
if (IS_ERR(data->auto_update))
return PTR_ERR(data->auto_update);
}
return 0;
}
......
// SPDX-License-Identifier: GPL-2.0+
/*
* hwmon driver for Aquacomputer D5 Next watercooling pump
*
* The D5 Next sends HID reports (with ID 0x01) every second to report sensor values
* (coolant temperature, pump and fan speed, voltage, current and power). It responds to
* Get_Report requests, but returns a dummy value of no use.
*
* Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
*/
#include <asm/unaligned.h>
#include <linux/debugfs.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#define DRIVER_NAME "aquacomputer-d5next"
#define D5NEXT_STATUS_REPORT_ID 0x01
#define D5NEXT_STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */
/* Register offsets for the D5 Next pump */
#define D5NEXT_SERIAL_FIRST_PART 3
#define D5NEXT_SERIAL_SECOND_PART 5
#define D5NEXT_FIRMWARE_VERSION 13
#define D5NEXT_POWER_CYCLES 24
#define D5NEXT_COOLANT_TEMP 87
#define D5NEXT_PUMP_SPEED 116
#define D5NEXT_FAN_SPEED 103
#define D5NEXT_PUMP_POWER 114
#define D5NEXT_FAN_POWER 101
#define D5NEXT_PUMP_VOLTAGE 110
#define D5NEXT_FAN_VOLTAGE 97
#define D5NEXT_5V_VOLTAGE 57
#define D5NEXT_PUMP_CURRENT 112
#define D5NEXT_FAN_CURRENT 99
/* Labels for provided values */
#define L_COOLANT_TEMP "Coolant temp"
#define L_PUMP_SPEED "Pump speed"
#define L_FAN_SPEED "Fan speed"
#define L_PUMP_POWER "Pump power"
#define L_FAN_POWER "Fan power"
#define L_PUMP_VOLTAGE "Pump voltage"
#define L_FAN_VOLTAGE "Fan voltage"
#define L_5V_VOLTAGE "+5V voltage"
#define L_PUMP_CURRENT "Pump current"
#define L_FAN_CURRENT "Fan current"
static const char *const label_speeds[] = {
L_PUMP_SPEED,
L_FAN_SPEED,
};
static const char *const label_power[] = {
L_PUMP_POWER,
L_FAN_POWER,
};
static const char *const label_voltages[] = {
L_PUMP_VOLTAGE,
L_FAN_VOLTAGE,
L_5V_VOLTAGE,
};
static const char *const label_current[] = {
L_PUMP_CURRENT,
L_FAN_CURRENT,
};
struct d5next_data {
struct hid_device *hdev;
struct device *hwmon_dev;
struct dentry *debugfs;
s32 temp_input;
u16 speed_input[2];
u32 power_input[2];
u16 voltage_input[3];
u16 current_input[2];
u32 serial_number[2];
u16 firmware_version;
u32 power_cycles; /* How many times the device was powered on */
unsigned long updated;
};
static umode_t d5next_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
int channel)
{
return 0444;
}
static int d5next_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
long *val)
{
struct d5next_data *priv = dev_get_drvdata(dev);
if (time_after(jiffies, priv->updated + D5NEXT_STATUS_UPDATE_INTERVAL))
return -ENODATA;
switch (type) {
case hwmon_temp:
*val = priv->temp_input;
break;
case hwmon_fan:
*val = priv->speed_input[channel];
break;
case hwmon_power:
*val = priv->power_input[channel];
break;
case hwmon_in:
*val = priv->voltage_input[channel];
break;
case hwmon_curr:
*val = priv->current_input[channel];
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int d5next_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
switch (type) {
case hwmon_temp:
*str = L_COOLANT_TEMP;
break;
case hwmon_fan:
*str = label_speeds[channel];
break;
case hwmon_power:
*str = label_power[channel];
break;
case hwmon_in:
*str = label_voltages[channel];
break;
case hwmon_curr:
*str = label_current[channel];
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static const struct hwmon_ops d5next_hwmon_ops = {
.is_visible = d5next_is_visible,
.read = d5next_read,
.read_string = d5next_read_string,
};
static const struct hwmon_channel_info *d5next_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL),
HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL, HWMON_P_INPUT | HWMON_P_LABEL),
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL),
NULL
};
static const struct hwmon_chip_info d5next_chip_info = {
.ops = &d5next_hwmon_ops,
.info = d5next_info,
};
static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
{
struct d5next_data *priv;
if (report->id != D5NEXT_STATUS_REPORT_ID)
return 0;
priv = hid_get_drvdata(hdev);
/* Info provided with every report */
priv->serial_number[0] = get_unaligned_be16(data + D5NEXT_SERIAL_FIRST_PART);
priv->serial_number[1] = get_unaligned_be16(data + D5NEXT_SERIAL_SECOND_PART);
priv->firmware_version = get_unaligned_be16(data + D5NEXT_FIRMWARE_VERSION);
priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES);
/* Sensor readings */
priv->temp_input = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10;
priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED);
priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED);
priv->power_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_POWER) * 10000;
priv->power_input[1] = get_unaligned_be16(data + D5NEXT_FAN_POWER) * 10000;
priv->voltage_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_VOLTAGE) * 10;
priv->voltage_input[1] = get_unaligned_be16(data + D5NEXT_FAN_VOLTAGE) * 10;
priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
priv->updated = jiffies;
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int serial_number_show(struct seq_file *seqf, void *unused)
{
struct d5next_data *priv = seqf->private;
seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(serial_number);
static int firmware_version_show(struct seq_file *seqf, void *unused)
{
struct d5next_data *priv = seqf->private;
seq_printf(seqf, "%u\n", priv->firmware_version);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(firmware_version);
static int power_cycles_show(struct seq_file *seqf, void *unused)
{
struct d5next_data *priv = seqf->private;
seq_printf(seqf, "%u\n", priv->power_cycles);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(power_cycles);
static void d5next_debugfs_init(struct d5next_data *priv)
{
char name[32];
scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev));
priv->debugfs = debugfs_create_dir(name, NULL);
debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
}
#else
static void d5next_debugfs_init(struct d5next_data *priv)
{
}
#endif
static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct d5next_data *priv;
int ret;
priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->hdev = hdev;
hid_set_drvdata(hdev, priv);
priv->updated = jiffies - D5NEXT_STATUS_UPDATE_INTERVAL;
ret = hid_parse(hdev);
if (ret)
return ret;
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (ret)
return ret;
ret = hid_hw_open(hdev);
if (ret)
goto fail_and_stop;
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "d5next", priv,
&d5next_chip_info, NULL);
if (IS_ERR(priv->hwmon_dev)) {
ret = PTR_ERR(priv->hwmon_dev);
goto fail_and_close;
}
d5next_debugfs_init(priv);
return 0;
fail_and_close:
hid_hw_close(hdev);
fail_and_stop:
hid_hw_stop(hdev);
return ret;
}
static void d5next_remove(struct hid_device *hdev)
{
struct d5next_data *priv = hid_get_drvdata(hdev);
debugfs_remove_recursive(priv->debugfs);
hwmon_device_unregister(priv->hwmon_dev);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
static const struct hid_device_id d5next_table[] = {
{ HID_USB_DEVICE(0x0c70, 0xf00e) }, /* Aquacomputer D5 Next */
{},
};
MODULE_DEVICE_TABLE(hid, d5next_table);
static struct hid_driver d5next_driver = {
.name = DRIVER_NAME,
.id_table = d5next_table,
.probe = d5next_probe,
.remove = d5next_remove,
.raw_event = d5next_raw_event,
};
static int __init d5next_init(void)
{
return hid_register_driver(&d5next_driver);
}
static void __exit d5next_exit(void)
{
hid_unregister_driver(&d5next_driver);
}
/* Request to initialize after the HID bus to ensure it's not being loaded before */
late_initcall(d5next_init);
module_exit(d5next_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
MODULE_DESCRIPTION("Hwmon driver for Aquacomputer D5 Next pump");
......@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/fpga/adi-axi-common.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
......@@ -23,6 +24,14 @@
#define ADI_REG_PWM_PERIOD 0x00c0
#define ADI_REG_TACH_MEASUR 0x00c4
#define ADI_REG_TEMPERATURE 0x00c8
#define ADI_REG_TEMP_00_H 0x0100
#define ADI_REG_TEMP_25_L 0x0104
#define ADI_REG_TEMP_25_H 0x0108
#define ADI_REG_TEMP_50_L 0x010c
#define ADI_REG_TEMP_50_H 0x0110
#define ADI_REG_TEMP_75_L 0x0114
#define ADI_REG_TEMP_75_H 0x0118
#define ADI_REG_TEMP_100_L 0x011c
#define ADI_REG_IRQ_MASK 0x0040
#define ADI_REG_IRQ_PENDING 0x0044
......@@ -62,6 +71,39 @@ static inline u32 axi_ioread(const u32 reg,
return ioread32(ctl->base + reg);
}
/*
* The core calculates the temperature as:
* T = /raw * 509.3140064 / 65535) - 280.2308787
*/
static ssize_t axi_fan_control_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct axi_fan_control_data *ctl = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
u32 temp = axi_ioread(attr->index, ctl);
temp = DIV_ROUND_CLOSEST_ULL(temp * 509314ULL, 65535) - 280230;
return sprintf(buf, "%u\n", temp);
}
static ssize_t axi_fan_control_store(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct axi_fan_control_data *ctl = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
u32 temp;
int ret;
ret = kstrtou32(buf, 10, &temp);
if (ret)
return ret;
temp = DIV_ROUND_CLOSEST_ULL((temp + 280230) * 65535ULL, 509314);
axi_iowrite(temp, attr->index, ctl);
return count;
}
static long axi_fan_control_get_pwm_duty(const struct axi_fan_control_data *ctl)
{
u32 pwm_width = axi_ioread(ADI_REG_PWM_WIDTH, ctl);
......@@ -283,18 +325,9 @@ static irqreturn_t axi_fan_control_irq_handler(int irq, void *data)
u32 irq_pending = axi_ioread(ADI_REG_IRQ_PENDING, ctl);
u32 clear_mask;
if (irq_pending & ADI_IRQ_SRC_NEW_MEASUR) {
if (ctl->update_tacho_params) {
u32 new_tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl);
/* get 25% tolerance */
u32 tach_tol = DIV_ROUND_CLOSEST(new_tach * 25, 100);
/* set new tacho parameters */
axi_iowrite(new_tach, ADI_REG_TACH_PERIOD, ctl);
axi_iowrite(tach_tol, ADI_REG_TACH_TOLERANCE, ctl);
ctl->update_tacho_params = false;
}
}
if (irq_pending & ADI_IRQ_SRC_TEMP_INCREASE)
/* hardware requested a new pwm */
ctl->hw_pwm_req = true;
if (irq_pending & ADI_IRQ_SRC_PWM_CHANGED) {
/*
......@@ -310,9 +343,18 @@ static irqreturn_t axi_fan_control_irq_handler(int irq, void *data)
}
}
if (irq_pending & ADI_IRQ_SRC_TEMP_INCREASE)
/* hardware requested a new pwm */
ctl->hw_pwm_req = true;
if (irq_pending & ADI_IRQ_SRC_NEW_MEASUR) {
if (ctl->update_tacho_params) {
u32 new_tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl);
/* get 25% tolerance */
u32 tach_tol = DIV_ROUND_CLOSEST(new_tach * 25, 100);
/* set new tacho parameters */
axi_iowrite(new_tach, ADI_REG_TACH_PERIOD, ctl);
axi_iowrite(tach_tol, ADI_REG_TACH_TOLERANCE, ctl);
ctl->update_tacho_params = false;
}
}
if (irq_pending & ADI_IRQ_SRC_TACH_ERR)
ctl->fan_fault = 1;
......@@ -351,6 +393,11 @@ static int axi_fan_control_init(struct axi_fan_control_data *ctl,
return ret;
}
static void axi_fan_control_clk_disable(void *clk)
{
clk_disable_unprepare(clk);
}
static const struct hwmon_channel_info *axi_fan_control_info[] = {
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL),
......@@ -370,6 +417,36 @@ static const struct hwmon_chip_info axi_chip_info = {
.info = axi_fan_control_info,
};
/* temperature threshold below which PWM should be 0% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp_hyst, axi_fan_control, ADI_REG_TEMP_00_H);
/* temperature threshold above which PWM should be 25% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, axi_fan_control, ADI_REG_TEMP_25_L);
/* temperature threshold below which PWM should be 25% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp_hyst, axi_fan_control, ADI_REG_TEMP_25_H);
/* temperature threshold above which PWM should be 50% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, axi_fan_control, ADI_REG_TEMP_50_L);
/* temperature threshold below which PWM should be 50% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp_hyst, axi_fan_control, ADI_REG_TEMP_50_H);
/* temperature threshold above which PWM should be 75% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, axi_fan_control, ADI_REG_TEMP_75_L);
/* temperature threshold below which PWM should be 75% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp_hyst, axi_fan_control, ADI_REG_TEMP_75_H);
/* temperature threshold above which PWM should be 100% */
static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, axi_fan_control, ADI_REG_TEMP_100_L);
static struct attribute *axi_fan_control_attrs[] = {
&sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(axi_fan_control);
static const u32 version_1_0_0 = ADI_AXI_PCORE_VER(1, 0, 'a');
static const struct of_device_id axi_fan_control_of_match[] = {
......@@ -406,6 +483,14 @@ static int axi_fan_control_probe(struct platform_device *pdev)
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(&pdev->dev, axi_fan_control_clk_disable, clk);
if (ret)
return ret;
ctl->clk_rate = clk_get_rate(clk);
if (!ctl->clk_rate)
return -EINVAL;
......@@ -446,7 +531,7 @@ static int axi_fan_control_probe(struct platform_device *pdev)
name,
ctl,
&axi_chip_info,
NULL);
axi_fan_control_groups);
return PTR_ERR_OR_ZERO(ctl->hdev);
}
......
......@@ -14,7 +14,9 @@
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
......@@ -23,7 +25,6 @@
#include <linux/capability.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/sched.h>
......@@ -58,32 +59,24 @@
#define I8K_POWER_AC 0x05
#define I8K_POWER_BATTERY 0x01
static DEFINE_MUTEX(i8k_mutex);
static char bios_version[4];
static char bios_machineid[16];
static struct device *i8k_hwmon_dev;
static u32 i8k_hwmon_flags;
static uint i8k_fan_mult = I8K_FAN_MULT;
static uint i8k_pwm_mult;
static uint i8k_fan_max = I8K_FAN_HIGH;
static bool disallow_fan_type_call;
static bool disallow_fan_support;
static unsigned int manual_fan;
static unsigned int auto_fan;
#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
#define I8K_HWMON_HAVE_TEMP2 (1 << 1)
#define I8K_HWMON_HAVE_TEMP3 (1 << 2)
#define I8K_HWMON_HAVE_TEMP4 (1 << 3)
#define I8K_HWMON_HAVE_TEMP5 (1 << 4)
#define I8K_HWMON_HAVE_TEMP6 (1 << 5)
#define I8K_HWMON_HAVE_TEMP7 (1 << 6)
#define I8K_HWMON_HAVE_TEMP8 (1 << 7)
#define I8K_HWMON_HAVE_TEMP9 (1 << 8)
#define I8K_HWMON_HAVE_TEMP10 (1 << 9)
#define I8K_HWMON_HAVE_FAN1 (1 << 10)
#define I8K_HWMON_HAVE_FAN2 (1 << 11)
#define I8K_HWMON_HAVE_FAN3 (1 << 12)
#define DELL_SMM_NO_TEMP 10
#define DELL_SMM_NO_FANS 3
struct dell_smm_data {
struct mutex i8k_mutex; /* lock for sensors writes */
char bios_version[4];
char bios_machineid[16];
uint i8k_fan_mult;
uint i8k_pwm_mult;
uint i8k_fan_max;
bool disallow_fan_type_call;
bool disallow_fan_support;
unsigned int manual_fan;
unsigned int auto_fan;
int temp_type[DELL_SMM_NO_TEMP];
bool fan[DELL_SMM_NO_FANS];
int fan_type[DELL_SMM_NO_FANS];
};
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
......@@ -126,7 +119,34 @@ struct smm_regs {
unsigned int edi __packed;
};
static inline const char *i8k_get_dmi_data(int field)
static const char * const temp_labels[] = {
"CPU",
"GPU",
"SODIMM",
"Other",
"Ambient",
"Other",
};
static const char * const fan_labels[] = {
"Processor Fan",
"Motherboard Fan",
"Video Fan",
"Power Supply Fan",
"Chipset Fan",
"Other Fan",
};
static const char * const docking_labels[] = {
"Docking Processor Fan",
"Docking Motherboard Fan",
"Docking Video Fan",
"Docking Power Supply Fan",
"Docking Chipset Fan",
"Docking Other Fan",
};
static inline const char __init *i8k_get_dmi_data(int field)
{
const char *dmi_data = dmi_get_system_info(field);
......@@ -138,17 +158,12 @@ static inline const char *i8k_get_dmi_data(int field)
*/
static int i8k_smm_func(void *par)
{
int rc;
ktime_t calltime = ktime_get();
struct smm_regs *regs = par;
int eax = regs->eax;
#ifdef DEBUG
int ebx = regs->ebx;
unsigned long duration;
ktime_t calltime, delta, rettime;
calltime = ktime_get();
#endif
long long duration;
int rc;
/* SMM requires CPU 0 */
if (smp_processor_id() != 0)
......@@ -210,13 +225,9 @@ static int i8k_smm_func(void *par)
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
rc = -EINVAL;
#ifdef DEBUG
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = ktime_to_ns(delta) >> 10;
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx,
duration = ktime_us_delta(ktime_get(), calltime);
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
(rc ? 0xffff : regs->eax & 0xffff), duration);
#endif
return rc;
}
......@@ -228,9 +239,9 @@ static int i8k_smm(struct smm_regs *regs)
{
int ret;
get_online_cpus();
cpus_read_lock();
ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
put_online_cpus();
cpus_read_unlock();
return ret;
}
......@@ -238,11 +249,11 @@ static int i8k_smm(struct smm_regs *regs)
/*
* Read the fan status.
*/
static int i8k_get_fan_status(int fan)
static int i8k_get_fan_status(const struct dell_smm_data *data, int fan)
{
struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
if (disallow_fan_support)
if (data->disallow_fan_support)
return -EINVAL;
regs.ebx = fan & 0xff;
......@@ -252,87 +263,85 @@ static int i8k_get_fan_status(int fan)
/*
* Read the fan speed in RPM.
*/
static int i8k_get_fan_speed(int fan)
static int i8k_get_fan_speed(const struct dell_smm_data *data, int fan)
{
struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
if (disallow_fan_support)
if (data->disallow_fan_support)
return -EINVAL;
regs.ebx = fan & 0xff;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
}
/*
* Read the fan type.
*/
static int _i8k_get_fan_type(int fan)
static int _i8k_get_fan_type(const struct dell_smm_data *data, int fan)
{
struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, };
if (disallow_fan_support || disallow_fan_type_call)
if (data->disallow_fan_support || data->disallow_fan_type_call)
return -EINVAL;
regs.ebx = fan & 0xff;
return i8k_smm(&regs) ? : regs.eax & 0xff;
}
static int i8k_get_fan_type(int fan)
static int i8k_get_fan_type(struct dell_smm_data *data, int fan)
{
/* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */
static int types[3] = { INT_MIN, INT_MIN, INT_MIN };
if (types[fan] == INT_MIN)
types[fan] = _i8k_get_fan_type(fan);
if (data->fan_type[fan] == INT_MIN)
data->fan_type[fan] = _i8k_get_fan_type(data, fan);
return types[fan];
return data->fan_type[fan];
}
/*
* Read the fan nominal rpm for specific fan speed.
*/
static int i8k_get_fan_nominal_speed(int fan, int speed)
static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, int fan, int speed)
{
struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, };
if (disallow_fan_support)
if (data->disallow_fan_support)
return -EINVAL;
regs.ebx = (fan & 0xff) | (speed << 8);
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
}
/*
* Enable or disable automatic BIOS fan control support
*/
static int i8k_enable_fan_auto_mode(bool enable)
static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable)
{
struct smm_regs regs = { };
if (disallow_fan_support)
if (data->disallow_fan_support)
return -EINVAL;
regs.eax = enable ? auto_fan : manual_fan;
regs.eax = enable ? data->auto_fan : data->manual_fan;
return i8k_smm(&regs);
}
/*
* Set the fan speed (off, low, high). Returns the new fan status.
*/
static int i8k_set_fan(int fan, int speed)
static int i8k_set_fan(const struct dell_smm_data *data, int fan, int speed)
{
struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
if (disallow_fan_support)
if (data->disallow_fan_support)
return -EINVAL;
speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed);
regs.ebx = (fan & 0xff) | (speed << 8);
return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
return i8k_smm(&regs) ? : i8k_get_fan_status(data, fan);
}
static int i8k_get_temp_type(int sensor)
static int __init i8k_get_temp_type(int sensor)
{
struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, };
......@@ -382,7 +391,7 @@ static int i8k_get_temp(int sensor)
return temp;
}
static int i8k_get_dell_signature(int req_fn)
static int __init i8k_get_dell_signature(int req_fn)
{
struct smm_regs regs = { .eax = req_fn, };
int rc;
......@@ -440,7 +449,7 @@ static int i8k_get_power_status(void)
*/
static int
i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
i8k_ioctl_unlocked(struct file *fp, struct dell_smm_data *data, unsigned int cmd, unsigned long arg)
{
int val = 0;
int speed;
......@@ -452,12 +461,12 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
switch (cmd) {
case I8K_BIOS_VERSION:
if (!isdigit(bios_version[0]) || !isdigit(bios_version[1]) ||
!isdigit(bios_version[2]))
if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) ||
!isdigit(data->bios_version[2]))
return -EINVAL;
val = (bios_version[0] << 16) |
(bios_version[1] << 8) | bios_version[2];
val = (data->bios_version[0] << 16) |
(data->bios_version[1] << 8) | data->bios_version[2];
break;
case I8K_MACHINE_ID:
......@@ -465,7 +474,7 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
return -EPERM;
memset(buff, 0, sizeof(buff));
strlcpy(buff, bios_machineid, sizeof(buff));
strscpy(buff, data->bios_machineid, sizeof(buff));
break;
case I8K_FN_STATUS:
......@@ -484,14 +493,14 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
if (copy_from_user(&val, argp, sizeof(int)))
return -EFAULT;
val = i8k_get_fan_speed(val);
val = i8k_get_fan_speed(data, val);
break;
case I8K_GET_FAN:
if (copy_from_user(&val, argp, sizeof(int)))
return -EFAULT;
val = i8k_get_fan_status(val);
val = i8k_get_fan_status(data, val);
break;
case I8K_SET_FAN:
......@@ -504,7 +513,7 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
if (copy_from_user(&speed, argp + 1, sizeof(int)))
return -EFAULT;
val = i8k_set_fan(val, speed);
val = i8k_set_fan(data, val, speed);
break;
default:
......@@ -537,11 +546,12 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
struct dell_smm_data *data = PDE_DATA(file_inode(fp));
long ret;
mutex_lock(&i8k_mutex);
ret = i8k_ioctl_unlocked(fp, cmd, arg);
mutex_unlock(&i8k_mutex);
mutex_lock(&data->i8k_mutex);
ret = i8k_ioctl_unlocked(fp, data, cmd, arg);
mutex_unlock(&data->i8k_mutex);
return ret;
}
......@@ -551,14 +561,15 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
*/
static int i8k_proc_show(struct seq_file *seq, void *offset)
{
struct dell_smm_data *data = seq->private;
int fn_key, cpu_temp, ac_power;
int left_fan, right_fan, left_speed, right_speed;
cpu_temp = i8k_get_temp(0); /* 11100 µs */
left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */
right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */
left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */
right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */
left_fan = i8k_get_fan_status(data, I8K_FAN_LEFT); /* 580 µs */
right_fan = i8k_get_fan_status(data, I8K_FAN_RIGHT); /* 580 µs */
left_speed = i8k_get_fan_speed(data, I8K_FAN_LEFT); /* 580 µs */
right_speed = i8k_get_fan_speed(data, I8K_FAN_RIGHT); /* 580 µs */
fn_key = i8k_get_fn_status(); /* 750 µs */
if (power_status)
ac_power = i8k_get_power_status(); /* 14700 µs */
......@@ -581,8 +592,8 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
*/
seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
I8K_PROC_FMT,
bios_version,
(restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid,
data->bios_version,
(restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid,
cpu_temp,
left_fan, right_fan, left_speed, right_speed,
ac_power, fn_key);
......@@ -592,7 +603,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
static int i8k_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, i8k_proc_show, NULL);
return single_open(file, i8k_proc_show, PDE_DATA(inode));
}
static const struct proc_ops i8k_proc_ops = {
......@@ -603,24 +614,24 @@ static const struct proc_ops i8k_proc_ops = {
.proc_ioctl = i8k_ioctl,
};
static void __init i8k_init_procfs(void)
static void i8k_exit_procfs(void *param)
{
/* Register the proc entry */
proc_create("i8k", 0, NULL, &i8k_proc_ops);
remove_proc_entry("i8k", NULL);
}
static void __exit i8k_exit_procfs(void)
static void __init i8k_init_procfs(struct device *dev)
{
remove_proc_entry("i8k", NULL);
}
struct dell_smm_data *data = dev_get_drvdata(dev);
#else
/* Register the proc entry */
proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data);
static inline void __init i8k_init_procfs(void)
{
devm_add_action_or_reset(dev, i8k_exit_procfs, NULL);
}
static inline void __exit i8k_exit_procfs(void)
#else
static void __init i8k_init_procfs(struct device *dev)
{
}
......@@ -630,341 +641,299 @@ static inline void __exit i8k_exit_procfs(void)
* Hwmon interface
*/
static ssize_t i8k_hwmon_temp_label_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
{
static const char * const labels[] = {
"CPU",
"GPU",
"SODIMM",
"Other",
"Ambient",
"Other",
};
int index = to_sensor_dev_attr(devattr)->index;
int type;
const struct dell_smm_data *data = drvdata;
type = i8k_get_temp_type(index);
if (type < 0)
return type;
if (type >= ARRAY_SIZE(labels))
type = ARRAY_SIZE(labels) - 1;
return sprintf(buf, "%s\n", labels[type]);
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_label:
if (data->temp_type[channel] >= 0)
return 0444;
break;
default:
break;
}
break;
case hwmon_fan:
if (data->disallow_fan_support)
break;
switch (attr) {
case hwmon_fan_input:
if (data->fan[channel])
return 0444;
break;
case hwmon_fan_label:
if (data->fan[channel] && !data->disallow_fan_type_call)
return 0444;
break;
default:
break;
}
break;
case hwmon_pwm:
if (data->disallow_fan_support)
break;
switch (attr) {
case hwmon_pwm_input:
if (data->fan[channel])
return 0644;
break;
case hwmon_pwm_enable:
if (data->auto_fan)
/*
* There is no command for retrieve the current status
* from BIOS, and userspace/firmware itself can change
* it.
* Thus we can only provide write-only access for now.
*/
return 0200;
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static ssize_t i8k_hwmon_temp_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
long *val)
{
int index = to_sensor_dev_attr(devattr)->index;
int temp;
struct dell_smm_data *data = dev_get_drvdata(dev);
int ret;
temp = i8k_get_temp(index);
if (temp < 0)
return temp;
return sprintf(buf, "%d\n", temp * 1000);
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
ret = i8k_get_temp(channel);
if (ret < 0)
return ret;
*val = ret * 1000;
return 0;
default:
break;
}
break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
ret = i8k_get_fan_speed(data, channel);
if (ret < 0)
return ret;
*val = ret;
return 0;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
ret = i8k_get_fan_status(data, channel);
if (ret < 0)
return ret;
*val = clamp_val(ret * data->i8k_pwm_mult, 0, 255);
return 0;
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static ssize_t i8k_hwmon_fan_label_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel)
{
static const char * const labels[] = {
"Processor Fan",
"Motherboard Fan",
"Video Fan",
"Power Supply Fan",
"Chipset Fan",
"Other Fan",
};
int index = to_sensor_dev_attr(devattr)->index;
bool dock = false;
int type;
int type = i8k_get_fan_type(data, channel);
type = i8k_get_fan_type(index);
if (type < 0)
return type;
return ERR_PTR(type);
if (type & 0x10) {
dock = true;
type &= 0x0F;
}
if (type >= ARRAY_SIZE(labels))
type = (ARRAY_SIZE(labels) - 1);
if (type >= ARRAY_SIZE(fan_labels))
type = ARRAY_SIZE(fan_labels) - 1;
return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]);
return dock ? docking_labels[type] : fan_labels[type];
}
static ssize_t i8k_hwmon_fan_show(struct device *dev,
struct device_attribute *devattr, char *buf)
static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
int index = to_sensor_dev_attr(devattr)->index;
int fan_speed;
struct dell_smm_data *data = dev_get_drvdata(dev);
fan_speed = i8k_get_fan_speed(index);
if (fan_speed < 0)
return fan_speed;
return sprintf(buf, "%d\n", fan_speed);
}
static ssize_t i8k_hwmon_pwm_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
int status;
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_label:
*str = temp_labels[data->temp_type[channel]];
return 0;
default:
break;
}
break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_label:
*str = dell_smm_fan_label(data, channel);
return PTR_ERR_OR_ZERO(*str);
default:
break;
}
break;
default:
break;
}
status = i8k_get_fan_status(index);
if (status < 0)
return -EIO;
return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255));
return -EOPNOTSUPP;
}
static ssize_t i8k_hwmon_pwm_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
long val)
{
int index = to_sensor_dev_attr(attr)->index;
unsigned long val;
struct dell_smm_data *data = dev_get_drvdata(dev);
unsigned long pwm;
bool enable;
int err;
err = kstrtoul(buf, 10, &val);
if (err)
return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max);
mutex_lock(&i8k_mutex);
err = i8k_set_fan(index, val);
mutex_unlock(&i8k_mutex);
switch (type) {
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0,
data->i8k_fan_max);
return err < 0 ? -EIO : count;
}
static ssize_t i8k_hwmon_pwm_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
bool enable;
unsigned long val;
mutex_lock(&data->i8k_mutex);
err = i8k_set_fan(data, channel, pwm);
mutex_unlock(&data->i8k_mutex);
if (!auto_fan)
return -ENODEV;
err = kstrtoul(buf, 10, &val);
if (err)
if (err < 0)
return err;
return 0;
case hwmon_pwm_enable:
if (!val)
return -EINVAL;
if (val == 1)
enable = false;
else if (val == 2)
enable = true;
else
return -EINVAL;
mutex_lock(&i8k_mutex);
err = i8k_enable_fan_auto_mode(enable);
mutex_unlock(&i8k_mutex);
enable = true;
return err ? err : count;
}
mutex_lock(&data->i8k_mutex);
err = i8k_enable_fan_auto_mode(data, enable);
mutex_unlock(&data->i8k_mutex);
static SENSOR_DEVICE_ATTR_RO(temp1_input, i8k_hwmon_temp, 0);
static SENSOR_DEVICE_ATTR_RO(temp1_label, i8k_hwmon_temp_label, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_input, i8k_hwmon_temp, 1);
static SENSOR_DEVICE_ATTR_RO(temp2_label, i8k_hwmon_temp_label, 1);
static SENSOR_DEVICE_ATTR_RO(temp3_input, i8k_hwmon_temp, 2);
static SENSOR_DEVICE_ATTR_RO(temp3_label, i8k_hwmon_temp_label, 2);
static SENSOR_DEVICE_ATTR_RO(temp4_input, i8k_hwmon_temp, 3);
static SENSOR_DEVICE_ATTR_RO(temp4_label, i8k_hwmon_temp_label, 3);
static SENSOR_DEVICE_ATTR_RO(temp5_input, i8k_hwmon_temp, 4);
static SENSOR_DEVICE_ATTR_RO(temp5_label, i8k_hwmon_temp_label, 4);
static SENSOR_DEVICE_ATTR_RO(temp6_input, i8k_hwmon_temp, 5);
static SENSOR_DEVICE_ATTR_RO(temp6_label, i8k_hwmon_temp_label, 5);
static SENSOR_DEVICE_ATTR_RO(temp7_input, i8k_hwmon_temp, 6);
static SENSOR_DEVICE_ATTR_RO(temp7_label, i8k_hwmon_temp_label, 6);
static SENSOR_DEVICE_ATTR_RO(temp8_input, i8k_hwmon_temp, 7);
static SENSOR_DEVICE_ATTR_RO(temp8_label, i8k_hwmon_temp_label, 7);
static SENSOR_DEVICE_ATTR_RO(temp9_input, i8k_hwmon_temp, 8);
static SENSOR_DEVICE_ATTR_RO(temp9_label, i8k_hwmon_temp_label, 8);
static SENSOR_DEVICE_ATTR_RO(temp10_input, i8k_hwmon_temp, 9);
static SENSOR_DEVICE_ATTR_RO(temp10_label, i8k_hwmon_temp_label, 9);
static SENSOR_DEVICE_ATTR_RO(fan1_input, i8k_hwmon_fan, 0);
static SENSOR_DEVICE_ATTR_RO(fan1_label, i8k_hwmon_fan_label, 0);
static SENSOR_DEVICE_ATTR_RW(pwm1, i8k_hwmon_pwm, 0);
static SENSOR_DEVICE_ATTR_WO(pwm1_enable, i8k_hwmon_pwm_enable, 0);
static SENSOR_DEVICE_ATTR_RO(fan2_input, i8k_hwmon_fan, 1);
static SENSOR_DEVICE_ATTR_RO(fan2_label, i8k_hwmon_fan_label, 1);
static SENSOR_DEVICE_ATTR_RW(pwm2, i8k_hwmon_pwm, 1);
static SENSOR_DEVICE_ATTR_RO(fan3_input, i8k_hwmon_fan, 2);
static SENSOR_DEVICE_ATTR_RO(fan3_label, i8k_hwmon_fan_label, 2);
static SENSOR_DEVICE_ATTR_RW(pwm3, i8k_hwmon_pwm, 2);
static struct attribute *i8k_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
&sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */
&sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */
&sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */
&sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */
&sensor_dev_attr_temp5_input.dev_attr.attr, /* 8 */
&sensor_dev_attr_temp5_label.dev_attr.attr, /* 9 */
&sensor_dev_attr_temp6_input.dev_attr.attr, /* 10 */
&sensor_dev_attr_temp6_label.dev_attr.attr, /* 11 */
&sensor_dev_attr_temp7_input.dev_attr.attr, /* 12 */
&sensor_dev_attr_temp7_label.dev_attr.attr, /* 13 */
&sensor_dev_attr_temp8_input.dev_attr.attr, /* 14 */
&sensor_dev_attr_temp8_label.dev_attr.attr, /* 15 */
&sensor_dev_attr_temp9_input.dev_attr.attr, /* 16 */
&sensor_dev_attr_temp9_label.dev_attr.attr, /* 17 */
&sensor_dev_attr_temp10_input.dev_attr.attr, /* 18 */
&sensor_dev_attr_temp10_label.dev_attr.attr, /* 19 */
&sensor_dev_attr_fan1_input.dev_attr.attr, /* 20 */
&sensor_dev_attr_fan1_label.dev_attr.attr, /* 21 */
&sensor_dev_attr_pwm1.dev_attr.attr, /* 22 */
&sensor_dev_attr_pwm1_enable.dev_attr.attr, /* 23 */
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 24 */
&sensor_dev_attr_fan2_label.dev_attr.attr, /* 25 */
&sensor_dev_attr_pwm2.dev_attr.attr, /* 26 */
&sensor_dev_attr_fan3_input.dev_attr.attr, /* 27 */
&sensor_dev_attr_fan3_label.dev_attr.attr, /* 28 */
&sensor_dev_attr_pwm3.dev_attr.attr, /* 29 */
NULL
};
if (err < 0)
return err;
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
int index)
{
if (disallow_fan_support && index >= 20)
return 0;
if (disallow_fan_type_call &&
(index == 21 || index == 25 || index == 28))
return 0;
if (index >= 0 && index <= 1 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
return 0;
if (index >= 2 && index <= 3 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
return 0;
if (index >= 4 && index <= 5 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
return 0;
if (index >= 6 && index <= 7 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
return 0;
if (index >= 8 && index <= 9 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP5))
return 0;
if (index >= 10 && index <= 11 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP6))
return 0;
if (index >= 12 && index <= 13 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP7))
return 0;
if (index >= 14 && index <= 15 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP8))
return 0;
if (index >= 16 && index <= 17 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP9))
return 0;
if (index >= 18 && index <= 19 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP10))
return 0;
default:
break;
}
break;
default:
break;
}
if (index >= 20 && index <= 23 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
return 0;
if (index >= 24 && index <= 26 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
return 0;
if (index >= 27 && index <= 29 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN3))
return 0;
return -EOPNOTSUPP;
}
if (index == 23 && !auto_fan)
return 0;
static const struct hwmon_ops dell_smm_ops = {
.is_visible = dell_smm_is_visible,
.read = dell_smm_read,
.read_string = dell_smm_read_string,
.write = dell_smm_write,
};
return attr->mode;
}
static const struct hwmon_channel_info *dell_smm_info[] = {
HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | 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,
HWMON_T_INPUT | 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
),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL
),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT
),
NULL
};
static const struct attribute_group i8k_group = {
.attrs = i8k_attrs,
.is_visible = i8k_is_visible,
static const struct hwmon_chip_info dell_smm_chip_info = {
.ops = &dell_smm_ops,
.info = dell_smm_info,
};
__ATTRIBUTE_GROUPS(i8k);
static int __init i8k_init_hwmon(void)
static int __init dell_smm_init_hwmon(struct device *dev)
{
int err;
struct dell_smm_data *data = dev_get_drvdata(dev);
struct device *dell_smm_hwmon_dev;
int i, err;
i8k_hwmon_flags = 0;
for (i = 0; i < DELL_SMM_NO_TEMP; i++) {
data->temp_type[i] = i8k_get_temp_type(i);
if (data->temp_type[i] < 0)
continue;
/* CPU temperature attributes, if temperature type is OK */
err = i8k_get_temp_type(0);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1;
/* check for additional temperature sensors */
err = i8k_get_temp_type(1);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
err = i8k_get_temp_type(2);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
err = i8k_get_temp_type(3);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
err = i8k_get_temp_type(4);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP5;
err = i8k_get_temp_type(5);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP6;
err = i8k_get_temp_type(6);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP7;
err = i8k_get_temp_type(7);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP8;
err = i8k_get_temp_type(8);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP9;
err = i8k_get_temp_type(9);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP10;
if (data->temp_type[i] >= ARRAY_SIZE(temp_labels))
data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1;
}
/* First fan attributes, if fan status or type is OK */
err = i8k_get_fan_status(0);
for (i = 0; i < DELL_SMM_NO_FANS; i++) {
data->fan_type[i] = INT_MIN;
err = i8k_get_fan_status(data, i);
if (err < 0)
err = i8k_get_fan_type(0);
err = i8k_get_fan_type(data, i);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
data->fan[i] = true;
}
/* Second fan attributes, if fan status or type is OK */
err = i8k_get_fan_status(1);
if (err < 0)
err = i8k_get_fan_type(1);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data,
&dell_smm_chip_info, NULL);
/* Third fan attributes, if fan status or type is OK */
err = i8k_get_fan_status(2);
if (err < 0)
err = i8k_get_fan_type(2);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN3;
i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm",
NULL, i8k_groups);
if (IS_ERR(i8k_hwmon_dev)) {
err = PTR_ERR(i8k_hwmon_dev);
i8k_hwmon_dev = NULL;
pr_err("hwmon registration failed (%d)\n", err);
return err;
}
return 0;
return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev);
}
struct i8k_config_data {
......@@ -979,7 +948,7 @@ enum i8k_configs {
DELL_XPS,
};
static const struct i8k_config_data i8k_config_data[] = {
static const struct i8k_config_data i8k_config_data[] __initconst = {
[DELL_LATITUDE_D520] = {
.fan_mult = 1,
.fan_max = I8K_FAN_TURBO,
......@@ -1137,7 +1106,7 @@ static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst
* support for affected blacklisted Dell machines stay disabled.
* See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751
*/
static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = {
static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = {
{
.ident = "Dell Inspiron 7720",
.matches = {
......@@ -1178,22 +1147,14 @@ enum i8k_fan_controls {
I8K_FAN_34A3_35A3,
};
static const struct i8k_fan_control_data i8k_fan_control_data[] = {
static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = {
[I8K_FAN_34A3_35A3] = {
.manual_fan = 0x34a3,
.auto_fan = 0x35a3,
},
};
static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = {
{
.ident = "Dell Precision 5530",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"),
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
},
static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
{
.ident = "Dell Latitude 5480",
.matches = {
......@@ -1218,57 +1179,56 @@ static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = {
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
},
{
.ident = "Dell Precision 5530",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"),
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
},
{
.ident = "Dell Precision 7510",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510"),
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
},
{ }
};
/*
* Probe for the presence of a supported laptop.
*/
static int __init i8k_probe(void)
static int __init dell_smm_probe(struct platform_device *pdev)
{
struct dell_smm_data *data;
const struct dmi_system_id *id, *fan_control;
int fan, ret;
/*
* Get DMI information
*/
if (!dmi_check_system(i8k_dmi_table)) {
if (!ignore_dmi && !force)
return -ENODEV;
data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
pr_info("not running on a supported Dell system.\n");
pr_info("vendor=%s, model=%s, version=%s\n",
i8k_get_dmi_data(DMI_SYS_VENDOR),
i8k_get_dmi_data(DMI_PRODUCT_NAME),
i8k_get_dmi_data(DMI_BIOS_VERSION));
}
mutex_init(&data->i8k_mutex);
data->i8k_fan_mult = I8K_FAN_MULT;
data->i8k_fan_max = I8K_FAN_HIGH;
platform_set_drvdata(pdev, data);
if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
pr_warn("broken Dell BIOS detected, disallow fan support\n");
dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n");
if (!force)
disallow_fan_support = true;
data->disallow_fan_support = true;
}
if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) {
pr_warn("broken Dell BIOS detected, disallow fan type call\n");
dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n");
if (!force)
disallow_fan_type_call = true;
data->disallow_fan_type_call = true;
}
strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
sizeof(bios_version));
strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
sizeof(bios_machineid));
/*
* Get SMM Dell signature
*/
if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
pr_err("unable to get SMM Dell signature\n");
if (!force)
return -ENODEV;
}
strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
sizeof(data->bios_version));
strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
sizeof(data->bios_machineid));
/*
* Set fan multiplier and maximal fan speed from dmi config
......@@ -1277,22 +1237,24 @@ static int __init i8k_probe(void)
id = dmi_first_match(i8k_dmi_table);
if (id && id->driver_data) {
const struct i8k_config_data *conf = id->driver_data;
if (!fan_mult && conf->fan_mult)
fan_mult = conf->fan_mult;
if (!fan_max && conf->fan_max)
fan_max = conf->fan_max;
}
i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max);
data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max);
fan_control = dmi_first_match(i8k_whitelist_fan_control);
if (fan_control && fan_control->driver_data) {
const struct i8k_fan_control_data *data = fan_control->driver_data;
const struct i8k_fan_control_data *control = fan_control->driver_data;
manual_fan = data->manual_fan;
auto_fan = data->auto_fan;
pr_info("enabling support for setting automatic/manual fan control\n");
data->manual_fan = control->manual_fan;
data->auto_fan = control->auto_fan;
dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n");
}
if (!fan_mult) {
......@@ -1300,42 +1262,76 @@ static int __init i8k_probe(void)
* Autodetect fan multiplier based on nominal rpm
* If fan reports rpm value too high then set multiplier to 1
*/
for (fan = 0; fan < 2; ++fan) {
ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max);
for (fan = 0; fan < DELL_SMM_NO_FANS; ++fan) {
ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max);
if (ret < 0)
continue;
if (ret > I8K_FAN_MAX_RPM)
i8k_fan_mult = 1;
data->i8k_fan_mult = 1;
break;
}
} else {
/* Fan multiplier was specified in module param or in dmi */
i8k_fan_mult = fan_mult;
data->i8k_fan_mult = fan_mult;
}
ret = dell_smm_init_hwmon(&pdev->dev);
if (ret)
return ret;
i8k_init_procfs(&pdev->dev);
return 0;
}
static struct platform_driver dell_smm_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
};
static struct platform_device *dell_smm_device;
/*
* Probe for the presence of a supported laptop.
*/
static int __init i8k_init(void)
{
int err;
/*
* Get DMI information
*/
if (!dmi_check_system(i8k_dmi_table)) {
if (!ignore_dmi && !force)
return -ENODEV;
/* Are we running on an supported laptop? */
if (i8k_probe())
pr_info("not running on a supported Dell system.\n");
pr_info("vendor=%s, model=%s, version=%s\n",
i8k_get_dmi_data(DMI_SYS_VENDOR),
i8k_get_dmi_data(DMI_PRODUCT_NAME),
i8k_get_dmi_data(DMI_BIOS_VERSION));
}
/*
* Get SMM Dell signature
*/
if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
pr_err("unable to get SMM Dell signature\n");
if (!force)
return -ENODEV;
}
err = i8k_init_hwmon();
if (err)
return err;
dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL,
0);
i8k_init_procfs();
return 0;
return PTR_ERR_OR_ZERO(dell_smm_device);
}
static void __exit i8k_exit(void)
{
hwmon_device_unregister(i8k_hwmon_dev);
i8k_exit_procfs();
platform_device_unregister(dell_smm_device);
platform_driver_unregister(&dell_smm_driver);
}
module_init(i8k_init);
......
......@@ -166,7 +166,7 @@ static int read_registers(struct fam15h_power_data *data)
memset(data->cu_on, 0, sizeof(int) * MAX_CUS);
get_online_cpus();
cpus_read_lock();
/*
* Choose the first online core of each compute unit, and then
......@@ -190,7 +190,7 @@ static int read_registers(struct fam15h_power_data *data)
on_each_cpu_mask(mask, do_read_registers_on_cu, data, true);
put_online_cpus();
cpus_read_unlock();
free_cpumask_var(mask);
return 0;
......
......@@ -228,6 +228,118 @@ static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = {
.hinfo = d5005bmc_hinfo,
};
static const struct m10bmc_sdata n5010bmc_temp_tbl[] = {
{ 0x100, 0x0, 0x104, 0x0, 0x0, 1000, "Board Local Temperature" },
{ 0x108, 0x0, 0x10c, 0x0, 0x0, 1000, "FPGA 1 Temperature" },
{ 0x110, 0x0, 0x114, 0x0, 0x0, 1000, "FPGA 2 Temperature" },
{ 0x118, 0x0, 0x0, 0x0, 0x0, 1000, "Card Top Temperature" },
{ 0x11c, 0x0, 0x0, 0x0, 0x0, 1000, "Card Bottom Temperature" },
{ 0x128, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 1.2V Temperature" },
{ 0x134, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 5V Temperature" },
{ 0x140, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.9V Temperature" },
{ 0x14c, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.85V Temperature" },
{ 0x158, 0x0, 0x0, 0x0, 0x0, 1000, "AUX 12V Temperature" },
{ 0x164, 0x0, 0x0, 0x0, 0x0, 1000, "Backplane 12V Temperature" },
{ 0x1a8, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-1 Temperature" },
{ 0x1ac, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-2 Temperature" },
{ 0x1b0, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-3 Temperature" },
{ 0x1b4, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-4 Temperature" },
{ 0x1b8, 0x0, 0x0, 0x0, 0x0, 1000, "CVL1 Internal Temperature" },
{ 0x1bc, 0x0, 0x0, 0x0, 0x0, 1000, "CVL2 Internal Temperature" },
};
static const struct m10bmc_sdata n5010bmc_in_tbl[] = {
{ 0x120, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Voltage" },
{ 0x12c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Voltage" },
{ 0x138, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Voltage" },
{ 0x144, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Voltage" },
{ 0x150, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Voltage" },
{ 0x15c, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Voltage" },
{ 0x16c, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Voltage" },
{ 0x17c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Voltage" },
{ 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Voltage" },
{ 0x18c, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Voltage" },
{ 0x194, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Voltage" },
{ 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Voltage" },
{ 0x1a4, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Voltage" },
};
static const struct m10bmc_sdata n5010bmc_curr_tbl[] = {
{ 0x124, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Current" },
{ 0x130, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Current" },
{ 0x13c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Current" },
{ 0x148, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Current" },
{ 0x154, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Current" },
{ 0x160, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Current" },
{ 0x168, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Current" },
{ 0x178, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Current" },
{ 0x180, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Current" },
{ 0x188, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Current" },
{ 0x190, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Current" },
{ 0x198, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Current" },
{ 0x1a0, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Current" },
};
static const struct hwmon_channel_info *n5010bmc_hinfo[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_CRIT | 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,
HWMON_T_INPUT | 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,
HWMON_T_INPUT | 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),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),
NULL
};
static const struct m10bmc_hwmon_board_data n5010bmc_hwmon_bdata = {
.tables = {
[hwmon_temp] = n5010bmc_temp_tbl,
[hwmon_in] = n5010bmc_in_tbl,
[hwmon_curr] = n5010bmc_curr_tbl,
},
.hinfo = n5010bmc_hinfo,
};
static umode_t
m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
......@@ -438,6 +550,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
.name = "d5005bmc-hwmon",
.driver_data = (unsigned long)&d5005bmc_hwmon_bdata,
},
{
.name = "n5010bmc-hwmon",
.driver_data = (unsigned long)&n5010bmc_hwmon_bdata,
},
{ }
};
......
......@@ -65,10 +65,11 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
#define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET 0xd8200c64
#define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4
/* Common for Zen CPU families (Family 17h and 18h) */
#define ZEN_REPORTED_TEMP_CTRL_OFFSET 0x00059800
/* Common for Zen CPU families (Family 17h and 18h and 19h) */
#define ZEN_REPORTED_TEMP_CTRL_BASE 0x00059800
#define ZEN_CCD_TEMP(x) (0x00059954 + ((x) * 4))
#define ZEN_CCD_TEMP(offset, x) (ZEN_REPORTED_TEMP_CTRL_BASE + \
(offset) + ((x) * 4))
#define ZEN_CCD_TEMP_VALID BIT(11)
#define ZEN_CCD_TEMP_MASK GENMASK(10, 0)
......@@ -103,6 +104,7 @@ struct k10temp_data {
u32 temp_adjust_mask;
u32 show_temp;
bool is_zen;
u32 ccd_offset;
};
#define TCTL_BIT 0
......@@ -163,7 +165,7 @@ static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval)
{
amd_smn_read(amd_pci_dev_to_node_id(pdev),
ZEN_REPORTED_TEMP_CTRL_OFFSET, regval);
ZEN_REPORTED_TEMP_CTRL_BASE, regval);
}
static long get_raw_temp(struct k10temp_data *data)
......@@ -226,7 +228,8 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
break;
case 2 ... 9: /* Tccd{1-8} */
amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
ZEN_CCD_TEMP(channel - 2), &regval);
ZEN_CCD_TEMP(data->ccd_offset, channel - 2),
&regval);
*val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000;
break;
default:
......@@ -387,7 +390,7 @@ static void k10temp_get_ccd_support(struct pci_dev *pdev,
for (i = 0; i < limit; i++) {
amd_smn_read(amd_pci_dev_to_node_id(pdev),
ZEN_CCD_TEMP(i), &regval);
ZEN_CCD_TEMP(data->ccd_offset, i), &regval);
if (regval & ZEN_CCD_TEMP_VALID)
data->show_temp |= BIT(TCCD_BIT(i));
}
......@@ -426,7 +429,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
data->read_tempreg = read_tempreg_nb_zen;
data->show_temp |= BIT(TDIE_BIT); /* show Tdie */
data->is_zen = true;
switch (boot_cpu_data.x86_model) {
......@@ -434,22 +436,31 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
case 0x8: /* Zen+ */
case 0x11: /* Zen APU */
case 0x18: /* Zen+ APU */
data->ccd_offset = 0x154;
k10temp_get_ccd_support(pdev, data, 4);
break;
case 0x31: /* Zen2 Threadripper */
case 0x60: /* Renoir */
case 0x68: /* Lucienne */
case 0x71: /* Zen2 */
data->ccd_offset = 0x154;
k10temp_get_ccd_support(pdev, data, 8);
break;
}
} else if (boot_cpu_data.x86 == 0x19) {
data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
data->read_tempreg = read_tempreg_nb_zen;
data->show_temp |= BIT(TDIE_BIT);
data->is_zen = true;
switch (boot_cpu_data.x86_model) {
case 0x0 ... 0x1: /* Zen3 SP3/TR */
case 0x21: /* Zen3 Ryzen Desktop */
case 0x50 ... 0x5f: /* Green Sardine */
data->ccd_offset = 0x154;
k10temp_get_ccd_support(pdev, data, 8);
break;
case 0x40 ... 0x4f: /* Yellow Carp */
data->ccd_offset = 0x300;
k10temp_get_ccd_support(pdev, data, 8);
break;
}
......@@ -463,6 +474,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (boot_cpu_data.x86 == entry->model &&
strstr(boot_cpu_data.x86_model_id, entry->id)) {
data->show_temp |= BIT(TDIE_BIT); /* show Tdie */
data->temp_offset = entry->offset;
break;
}
......@@ -491,6 +503,8 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) },
{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
{}
};
......
......@@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/fixp-arith.h>
#include <linux/platform_data/ntc_thermistor.h>
......@@ -549,15 +550,16 @@ static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
int temp;
lookup_comp(data, ohm, &low, &high);
if (low == high) {
/* Unable to use linear approximation */
temp = data->comp[low].temp_c * 1000;
} else {
temp = data->comp[low].temp_c * 1000 +
((data->comp[high].temp_c - data->comp[low].temp_c) *
1000 * ((int)ohm - (int)data->comp[low].ohm)) /
((int)data->comp[high].ohm - (int)data->comp[low].ohm);
}
/*
* First multiplying the table temperatures with 1000 to get to
* millicentigrades (which is what we want) and then interpolating
* will give the best precision.
*/
temp = fixp_linear_interpolate(data->comp[low].ohm,
data->comp[low].temp_c * 1000,
data->comp[high].ohm,
data->comp[high].temp_c * 1000,
ohm);
return temp;
}
......
......@@ -12,14 +12,7 @@
#include <linux/pmbus.h>
#include "pmbus.h"
#define BPARS600_MFR_VIN_MIN 0xa0
#define BPARS600_MFR_VIN_MAX 0xa1
#define BPARS600_MFR_IIN_MAX 0xa2
#define BPARS600_MFR_PIN_MAX 0xa3
#define BPARS600_MFR_VOUT_MIN 0xa4
#define BPARS600_MFR_VOUT_MAX 0xa5
#define BPARS600_MFR_IOUT_MAX 0xa6
#define BPARS600_MFR_POUT_MAX 0xa7
enum chips { bpa_rs600, bpd_rs600 };
static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg)
{
......@@ -72,6 +65,26 @@ static int bpa_rs600_read_vin(struct i2c_client *client)
return ret;
}
/*
* Firmware V5.70 incorrectly reports 1640W for MFR_PIN_MAX.
* Deal with this by returning a sensible value.
*/
static int bpa_rs600_read_pin_max(struct i2c_client *client)
{
int ret;
ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_MFR_PIN_MAX);
if (ret < 0)
return ret;
/* Detect invalid 1640W (linear encoding) */
if (ret == 0x0b34)
/* Report 700W (linear encoding) */
return 0x095e;
return ret;
}
static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
{
int ret;
......@@ -81,29 +94,13 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha
switch (reg) {
case PMBUS_VIN_UV_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MIN);
break;
case PMBUS_VIN_OV_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MAX);
break;
case PMBUS_VOUT_UV_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MIN);
break;
case PMBUS_VOUT_OV_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MAX);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IIN_MAX);
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IOUT_MAX);
break;
case PMBUS_PIN_OP_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_PIN_MAX);
break;
case PMBUS_POUT_OP_WARN_LIMIT:
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_POUT_MAX);
break;
case PMBUS_VIN_UV_FAULT_LIMIT:
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VOUT_UV_FAULT_LIMIT:
......@@ -114,6 +111,9 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha
case PMBUS_READ_VIN:
ret = bpa_rs600_read_vin(client);
break;
case PMBUS_MFR_PIN_MAX:
ret = bpa_rs600_read_pin_max(client);
break;
default:
if (reg >= PMBUS_VIRT_BASE)
ret = -ENXIO;
......@@ -146,11 +146,19 @@ static struct pmbus_driver_info bpa_rs600_info = {
.read_word_data = bpa_rs600_read_word_data,
};
static const struct i2c_device_id bpa_rs600_id[] = {
{ "bpa-rs600", bpa_rs600 },
{ "bpd-rs600", bpd_rs600 },
{},
};
MODULE_DEVICE_TABLE(i2c, bpa_rs600_id);
static int bpa_rs600_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
int ret;
const struct i2c_device_id *mid;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA
......@@ -164,7 +172,11 @@ static int bpa_rs600_probe(struct i2c_client *client)
return ret;
}
if (strncmp(buf, "BPA-RS600", 8)) {
for (mid = bpa_rs600_id; mid->name[0]; mid++) {
if (!strncasecmp(buf, mid->name, strlen(mid->name)))
break;
}
if (!mid->name[0]) {
buf[ret] = '\0';
dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
return -ENODEV;
......@@ -173,12 +185,6 @@ static int bpa_rs600_probe(struct i2c_client *client)
return pmbus_do_probe(client, &bpa_rs600_info);
}
static const struct i2c_device_id bpa_rs600_id[] = {
{ "bpars600", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, bpa_rs600_id);
static const struct of_device_id __maybe_unused bpa_rs600_of_match[] = {
{ .compatible = "blutek,bpa-rs600" },
{},
......
......@@ -50,9 +50,9 @@
#define CFFPS_MFR_VAUX_FAULT BIT(6)
#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
#define CFFPS_LED_BLINK BIT(0)
#define CFFPS_LED_ON BIT(1)
#define CFFPS_LED_OFF BIT(2)
#define CFFPS_LED_BLINK (BIT(0) | BIT(6))
#define CFFPS_LED_ON (BIT(1) | BIT(6))
#define CFFPS_LED_OFF (BIT(2) | BIT(6))
#define CFFPS_BLINK_RATE_MS 250
enum {
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sbrmi.c - hwmon driver for a SB-RMI mailbox
* compliant AMD SoC device.
*
* Copyright (C) 2020-2021 Advanced Micro Devices, Inc.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
/* Do not allow setting negative power limit */
#define SBRMI_PWR_MIN 0
/* Mask for Status Register bit[1] */
#define SW_ALERT_MASK 0x2
/* Software Interrupt for triggering */
#define START_CMD 0x80
#define TRIGGER_MAILBOX 0x01
/*
* SB-RMI supports soft mailbox service request to MP1 (power management
* firmware) through SBRMI inbound/outbound message registers.
* SB-RMI message IDs
*/
enum sbrmi_msg_id {
SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1,
SBRMI_WRITE_PKG_PWR_LIMIT,
SBRMI_READ_PKG_PWR_LIMIT,
SBRMI_READ_PKG_MAX_PWR_LIMIT,
};
/* SB-RMI registers */
enum sbrmi_reg {
SBRMI_CTRL = 0x01,
SBRMI_STATUS,
SBRMI_OUTBNDMSG0 = 0x30,
SBRMI_OUTBNDMSG1,
SBRMI_OUTBNDMSG2,
SBRMI_OUTBNDMSG3,
SBRMI_OUTBNDMSG4,
SBRMI_OUTBNDMSG5,
SBRMI_OUTBNDMSG6,
SBRMI_OUTBNDMSG7,
SBRMI_INBNDMSG0,
SBRMI_INBNDMSG1,
SBRMI_INBNDMSG2,
SBRMI_INBNDMSG3,
SBRMI_INBNDMSG4,
SBRMI_INBNDMSG5,
SBRMI_INBNDMSG6,
SBRMI_INBNDMSG7,
SBRMI_SW_INTERRUPT,
};
/* Each client has this additional data */
struct sbrmi_data {
struct i2c_client *client;
struct mutex lock;
u32 pwr_limit_max;
};
struct sbrmi_mailbox_msg {
u8 cmd;
bool read;
u32 data_in;
u32 data_out;
};
static int sbrmi_enable_alert(struct i2c_client *client)
{
int ctrl;
/*
* Enable the SB-RMI Software alert status
* by writing 0 to bit 4 of Control register(0x1)
*/
ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL);
if (ctrl < 0)
return ctrl;
if (ctrl & 0x10) {
ctrl &= ~0x10;
return i2c_smbus_write_byte_data(client,
SBRMI_CTRL, ctrl);
}
return 0;
}
static int rmi_mailbox_xfer(struct sbrmi_data *data,
struct sbrmi_mailbox_msg *msg)
{
int i, ret, retry = 10;
int sw_status;
u8 byte;
mutex_lock(&data->lock);
/* Indicate firmware a command is to be serviced */
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG7, START_CMD);
if (ret < 0)
goto exit_unlock;
/* Write the command to SBRMI::InBndMsg_inst0 */
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG0, msg->cmd);
if (ret < 0)
goto exit_unlock;
/*
* For both read and write the initiator (BMC) writes
* Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
* SBRMI_x3C(MSB):SBRMI_x39(LSB)
*/
for (i = 0; i < 4; i++) {
byte = (msg->data_in >> i * 8) & 0xff;
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG1 + i, byte);
if (ret < 0)
goto exit_unlock;
}
/*
* Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
* perform the requested read or write command
*/
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
if (ret < 0)
goto exit_unlock;
/*
* Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
* an ALERT (if enabled) to initiator (BMC) to indicate completion
* of the requested command
*/
do {
sw_status = i2c_smbus_read_byte_data(data->client,
SBRMI_STATUS);
if (sw_status < 0) {
ret = sw_status;
goto exit_unlock;
}
if (sw_status & SW_ALERT_MASK)
break;
usleep_range(50, 100);
} while (retry--);
if (retry < 0) {
dev_err(&data->client->dev,
"Firmware fail to indicate command completion\n");
ret = -EIO;
goto exit_unlock;
}
/*
* For a read operation, the initiator (BMC) reads the firmware
* response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
* {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
*/
if (msg->read) {
for (i = 0; i < 4; i++) {
ret = i2c_smbus_read_byte_data(data->client,
SBRMI_OUTBNDMSG1 + i);
if (ret < 0)
goto exit_unlock;
msg->data_out |= ret << i * 8;
}
}
/*
* BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
* ALERT to initiator
*/
ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS,
sw_status | SW_ALERT_MASK);
exit_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct sbrmi_data *data = dev_get_drvdata(dev);
struct sbrmi_mailbox_msg msg = { 0 };
int ret;
if (type != hwmon_power)
return -EINVAL;
msg.read = true;
switch (attr) {
case hwmon_power_input:
msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
ret = rmi_mailbox_xfer(data, &msg);
break;
case hwmon_power_cap:
msg.cmd = SBRMI_READ_PKG_PWR_LIMIT;
ret = rmi_mailbox_xfer(data, &msg);
break;
case hwmon_power_cap_max:
msg.data_out = data->pwr_limit_max;
ret = 0;
break;
default:
return -EINVAL;
}
if (ret < 0)
return ret;
/* hwmon power attributes are in microWatt */
*val = (long)msg.data_out * 1000;
return ret;
}
static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct sbrmi_data *data = dev_get_drvdata(dev);
struct sbrmi_mailbox_msg msg = { 0 };
if (type != hwmon_power && attr != hwmon_power_cap)
return -EINVAL;
/*
* hwmon power attributes are in microWatt
* mailbox read/write is in mWatt
*/
val /= 1000;
val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
msg.data_in = val;
msg.read = false;
return rmi_mailbox_xfer(data, &msg);
}
static umode_t sbrmi_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_power:
switch (attr) {
case hwmon_power_input:
case hwmon_power_cap_max:
return 0444;
case hwmon_power_cap:
return 0644;
}
break;
default:
break;
}
return 0;
}
static const struct hwmon_channel_info *sbrmi_info[] = {
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),
NULL
};
static const struct hwmon_ops sbrmi_hwmon_ops = {
.is_visible = sbrmi_is_visible,
.read = sbrmi_read,
.write = sbrmi_write,
};
static const struct hwmon_chip_info sbrmi_chip_info = {
.ops = &sbrmi_hwmon_ops,
.info = sbrmi_info,
};
static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
{
struct sbrmi_mailbox_msg msg = { 0 };
int ret;
msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
msg.read = true;
ret = rmi_mailbox_xfer(data, &msg);
if (ret < 0)
return ret;
data->pwr_limit_max = msg.data_out;
return ret;
}
static int sbrmi_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct sbrmi_data *data;
int ret;
data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
mutex_init(&data->lock);
/* Enable alert for SB-RMI sequence */
ret = sbrmi_enable_alert(client);
if (ret < 0)
return ret;
/* Cache maximum power limit */
ret = sbrmi_get_max_pwr_limit(data);
if (ret < 0)
return ret;
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&sbrmi_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id sbrmi_id[] = {
{"sbrmi", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sbrmi_id);
static const struct of_device_id __maybe_unused sbrmi_of_match[] = {
{
.compatible = "amd,sbrmi",
},
{ },
};
MODULE_DEVICE_TABLE(of, sbrmi_of_match);
static struct i2c_driver sbrmi_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "sbrmi",
.of_match_table = of_match_ptr(sbrmi_of_match),
},
.probe = sbrmi_probe,
.id_table = sbrmi_id,
};
module_i2c_driver(sbrmi_driver);
MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>");
MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor");
MODULE_LICENSE("GPL");
......@@ -372,12 +372,10 @@ struct w83627ehf_data {
u8 temp3_val_only:1;
u8 have_vid:1;
#ifdef CONFIG_PM
/* Remember extra register values over suspend/resume */
u8 vbat;
u8 fandiv1;
u8 fandiv2;
#endif
};
struct w83627ehf_sio_data {
......@@ -1083,7 +1081,7 @@ cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
struct w83627ehf_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
DEVICE_ATTR_RO(cpu0_vid);
static DEVICE_ATTR_RO(cpu0_vid);
/* Case open detection */
......@@ -1694,7 +1692,7 @@ static const struct hwmon_chip_info w83627ehf_chip_info = {
.info = w83627ehf_info,
};
static int w83627ehf_probe(struct platform_device *pdev)
static int __init w83627ehf_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
......@@ -1705,20 +1703,12 @@ static int w83627ehf_probe(struct platform_device *pdev)
struct device *hwmon_dev;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start, IOREGION_LENGTH, DRVNAME)) {
err = -EBUSY;
dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
(unsigned long)res->start,
(unsigned long)res->start + IOREGION_LENGTH - 1);
goto exit;
}
if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
return -EBUSY;
data = devm_kzalloc(&pdev->dev, sizeof(struct w83627ehf_data),
GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit_release;
}
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->addr = res->start;
mutex_init(&data->lock);
......@@ -1882,7 +1872,7 @@ static int w83627ehf_probe(struct platform_device *pdev)
err = superio_enter(sio_data->sioreg);
if (err)
goto exit_release;
return err;
/* Read VID value */
if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
......@@ -1951,30 +1941,10 @@ static int w83627ehf_probe(struct platform_device *pdev)
data,
&w83627ehf_chip_info,
w83627ehf_groups);
if (IS_ERR(hwmon_dev)) {
err = PTR_ERR(hwmon_dev);
goto exit_release;
}
return 0;
exit_release:
release_region(res->start, IOREGION_LENGTH);
exit:
return err;
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static int w83627ehf_remove(struct platform_device *pdev)
{
struct w83627ehf_data *data = platform_get_drvdata(pdev);
release_region(data->addr, IOREGION_LENGTH);
return 0;
}
#ifdef CONFIG_PM
static int w83627ehf_suspend(struct device *dev)
static int __maybe_unused w83627ehf_suspend(struct device *dev)
{
struct w83627ehf_data *data = w83627ehf_update_device(dev);
......@@ -1985,7 +1955,7 @@ static int w83627ehf_suspend(struct device *dev)
return 0;
}
static int w83627ehf_resume(struct device *dev)
static int __maybe_unused w83627ehf_resume(struct device *dev)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
int i;
......@@ -2040,25 +2010,13 @@ static int w83627ehf_resume(struct device *dev)
return 0;
}
static const struct dev_pm_ops w83627ehf_dev_pm_ops = {
.suspend = w83627ehf_suspend,
.resume = w83627ehf_resume,
.freeze = w83627ehf_suspend,
.restore = w83627ehf_resume,
};
#define W83627EHF_DEV_PM_OPS (&w83627ehf_dev_pm_ops)
#else
#define W83627EHF_DEV_PM_OPS NULL
#endif /* CONFIG_PM */
static SIMPLE_DEV_PM_OPS(w83627ehf_dev_pm_ops, w83627ehf_suspend, w83627ehf_resume);
static struct platform_driver w83627ehf_driver = {
.driver = {
.name = DRVNAME,
.pm = W83627EHF_DEV_PM_OPS,
.pm = &w83627ehf_dev_pm_ops,
},
.probe = w83627ehf_probe,
.remove = w83627ehf_remove,
};
/* w83627ehf_find() looks for a '627 in the Super-I/O config space */
......@@ -2150,8 +2108,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
/*
* when Super-I/O functions move to a separate file, the Super-I/O
* bus will manage the lifetime of the device and this module will only keep
* track of the w83627ehf driver. But since we platform_device_alloc(), we
* must keep track of the device
* track of the w83627ehf driver.
*/
static struct platform_device *pdev;
......@@ -2159,7 +2116,10 @@ static int __init sensors_w83627ehf_init(void)
{
int err;
unsigned short address;
struct resource res;
struct resource res = {
.name = DRVNAME,
.flags = IORESOURCE_IO,
};
struct w83627ehf_sio_data sio_data;
/*
......@@ -2173,55 +2133,17 @@ static int __init sensors_w83627ehf_init(void)
w83627ehf_find(0x4e, &address, &sio_data))
return -ENODEV;
err = platform_driver_register(&w83627ehf_driver);
if (err)
goto exit;
pdev = platform_device_alloc(DRVNAME, address);
if (!pdev) {
err = -ENOMEM;
pr_err("Device allocation failed\n");
goto exit_unregister;
}
err = platform_device_add_data(pdev, &sio_data,
sizeof(struct w83627ehf_sio_data));
if (err) {
pr_err("Platform data allocation failed\n");
goto exit_device_put;
}
memset(&res, 0, sizeof(res));
res.name = DRVNAME;
res.start = address + IOREGION_OFFSET;
res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
res.flags = IORESOURCE_IO;
err = acpi_check_resource_conflict(&res);
if (err)
goto exit_device_put;
err = platform_device_add_resources(pdev, &res, 1);
if (err) {
pr_err("Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
/* platform_device_add calls probe() */
err = platform_device_add(pdev);
if (err) {
pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
}
return err;
return 0;
pdev = platform_create_bundle(&w83627ehf_driver, w83627ehf_probe, &res, 1, &sio_data,
sizeof(struct w83627ehf_sio_data));
exit_device_put:
platform_device_put(pdev);
exit_unregister:
platform_driver_unregister(&w83627ehf_driver);
exit:
return err;
return PTR_ERR_OR_ZERO(pdev);
}
static void __exit sensors_w83627ehf_exit(void)
......
......@@ -1571,10 +1571,21 @@ static const struct i2c_device_id w83781d_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, w83781d_ids);
static const struct of_device_id w83781d_of_match[] = {
{ .compatible = "winbond,w83781d" },
{ .compatible = "winbond,w83781g" },
{ .compatible = "winbond,w83782d" },
{ .compatible = "winbond,w83783s" },
{ .compatible = "asus,as99127f" },
{ },
};
MODULE_DEVICE_TABLE(of, w83781d_of_match);
static struct i2c_driver w83781d_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "w83781d",
.of_match_table = w83781d_of_match,
},
.probe_new = w83781d_probe,
.remove = w83781d_remove,
......
......@@ -555,6 +555,7 @@
#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F3 0x144b
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F3 0x1443
#define PCI_DEVICE_ID_AMD_19H_DF_F3 0x1653
#define PCI_DEVICE_ID_AMD_19H_M40H_DF_F3 0x167c
#define PCI_DEVICE_ID_AMD_19H_M50H_DF_F3 0x166d
#define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703
#define PCI_DEVICE_ID_AMD_LANCE 0x2000
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment