mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
hwmon updates for v5.15
New drivers for: - Aquacomputer D5 Next - SB-RMI power module Added chip support t oexisting 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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmEtTKoACgkQyx8mb86f mYFBRA/+N2xANCT6pxEILm9RaOj7ezQQNH1dlVGIus15zu+5sFlqSmmFvWK32KSB n7B/0poO323Hf5llRVDenelDLfZ2gMryRqvt12tGyqTm9dsOVL8DLqz50K/BEjfQ cq5YMJZfAr2EtTc19Re3Mk5b5V87/U/a/BRWeG/4wIO7TCP38TqmfIUcxbTRTeiC n1SUA1xJMKyxkUoJl0kXtMqFLDaz9FESLQc0ioe/yWqI5P22j9KJqNhJ01PPAOiS 4sfwtXwwR9b5nD3nX0AfcRCqJVAzwfqCrj66WKFLIcat0kSWXJVHhxz7go9McEqa jV1Z4MCV1uuQ8m6eqYako+Fda+lpNQKE71Z89582aY9cB7xw1bxaHuebJdzH20CD GtXQfDrDAQcRSnFlXCTz/wvAQN96TSu9gvgQyLYgAGyuv3EIEuDbQaixl8xzWcpB W6Pg1V/FgBShK/LDj03A/oCszOBI5lL9ZgV1GVZjwjVc/UxmMpBlDiikfM2VUy3T dPsjfCiXqWiZ4FcESXjLlXGsZG5AM7ZfMiKG8T1JkGm1UokWdGhALiF7B3o1Cfmw QE/I5VM2gArgCTNzeFF11rP8lNMqoXk3KAm6KVxNIl6ltTWuH2dRkIhfDp/1/R32 ySbhy4FMvl9zjQFwUK6JG3tmrEN6DXlh+OFeAmW0/uf+wlinBlc= =tQk6 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
e7c1bbcf0c
25 changed files with 2316 additions and 1217 deletions
53
Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml
Normal file
53
Documentation/devicetree/bindings/hwmon/amd,sbrmi.yaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
# 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>;
|
||||
};
|
||||
};
|
||||
...
|
41
Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml
Normal file
41
Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml
Normal file
|
@ -0,0 +1,41 @@
|
|||
# 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>;
|
||||
};
|
||||
};
|
61
Documentation/hwmon/aquacomputer_d5next.rst
Normal file
61
Documentation/hwmon/aquacomputer_d5next.rst
Normal file
|
@ -0,0 +1,61 @@
|
|||
.. 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
|
||||
|
|
79
Documentation/hwmon/sbrmi.rst
Normal file
79
Documentation/hwmon/sbrmi.rst
Normal file
|
@ -0,0 +1,79 @@
|
|||
.. 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
|
||||
|
|
File diff suppressed because it is too large
Load diff
363
drivers/hwmon/aquacomputer_d5next.c
Normal file
363
drivers/hwmon/aquacomputer_d5next.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
// 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);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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), ®val);
|
||||
ZEN_CCD_TEMP(data->ccd_offset, channel - 2),
|
||||
®val);
|
||||
*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), ®val);
|
||||
ZEN_CCD_TEMP(data->ccd_offset, i), ®val);
|
||||
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 {
|
||||
|
|
359
drivers/hwmon/sbrmi.c
Normal file
359
drivers/hwmon/sbrmi.c
Normal file
|
@ -0,0 +1,359 @@
|
|||
// 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;
|
||||
return err;
|
||||
|
||||
err = platform_device_add_resources(pdev, &res, 1);
|
||||
if (err) {
|
||||
pr_err("Device resource addition failed (%d)\n", err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
pdev = platform_create_bundle(&w83627ehf_driver, w83627ehf_probe, &res, 1, &sio_data,
|
||||
sizeof(struct w83627ehf_sio_data));
|
||||
|
||||
/* 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 0;
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue