All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v11 00/14] PECI device driver introduction
@ 2019-12-11 19:46 ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo

Introduction of the Platform Environment Control Interface (PECI) bus
device driver. PECI is a one-wire bus interface that provides a
communication channel from Intel processors and chipset components to
external monitoring or control devices. PECI is designed to support the
following sideband functions:

* Processor and DRAM thermal management
  - Processor fan speed control is managed by comparing Digital Thermal
    Sensor (DTS) thermal readings acquired via PECI against the
    processor-specific fan speed control reference point, or TCONTROL. Both
    TCONTROL and DTS thermal readings are accessible via the processor PECI
    client. These variables are referenced to a common temperature, the TCC
    activation point, and are both defined as negative offsets from that
    reference.
  - PECI based access to the processor package configuration space provides
    a means for Baseboard Management Controllers (BMC) or other platform
    management devices to actively manage the processor and memory power
    and thermal features.

* Platform Manageability
  - Platform manageability functions including thermal, power, and error
    monitoring. Note that platform 'power' management includes monitoring
    and control for both the processor and DRAM subsystem to assist with
    data center power limiting.
  - PECI allows read access to certain error registers in the processor MSR
    space and status monitoring registers in the PCI configuration space
    within the processor and downstream devices.
  - PECI permits writes to certain registers in the processor PCI
    configuration space.

* Processor Interface Tuning and Diagnostics
  - Processor interface tuning and diagnostics capabilities
    (Intel Interconnect BIST). The processors Intel Interconnect Built In
    Self Test (Intel IBIST) allows for infield diagnostic capabilities in
    the Intel UPI and memory controller interfaces. PECI provides a port to
    execute these diagnostics via its PCI Configuration read and write
    capabilities.

* Failure Analysis
  - Output the state of the processor after a failure for analysis via
    Crashdump.

PECI uses a single wire for self-clocking and data transfer. The bus
requires no additional control lines. The physical layer is a self-clocked
one-wire bus that begins each bit with a driven, rising edge from an idle
level near zero volts. The duration of the signal driven high depends on
whether the bit value is a logic '0' or logic '1'. PECI also includes
variable data transfer rate established with every message. In this way, it
is highly flexible even though underlying logic is simple.

The interface design was optimized for interfacing between an Intel
processor and chipset components in both single processor and multiple
processor environments. The single wire interface provides low board
routing overhead for the multiple load connections in the congested routing
area near the processor and chipset components. Bus speed, error checking,
and low protocol overhead provides adequate link bandwidth and reliability
to transfer critical device operating conditions and configuration
information.

This implementation provides the basic framework to add PECI extensions to
the Linux bus and device models. A hardware specific 'Adapter' driver can
be attached to the PECI bus to provide sideband functions described above.
It is also possible to access all devices on an adapter from userspace
through the /dev interface. A device specific 'Client' driver also can be
attached to the PECI bus so each processor client's features can be
supported by the 'Client' driver through an adapter connection in the bus.
This patch set includes Aspeed 24xx/25xx/26xx, NPCM7xx PECI drivers and
PECI cputemp/dimmtemp drivers as the first implementation for both adapter
and client drivers on the PECI bus framework.

Please review.

Thanks,

Jae

Changes since v10:
- Split out peci-dev module from peci-core module.
- Changed device tree bindings documents format to DT schema.
- Changed hwmon documents format to rst.
- Added Skylake Xeon D support in PECI hwmon drivers.
- Added DTS temperature reading in peci-cputemp driver which is more thermal
  control friendlier than Die temperature.
- Added max and crit properties into peci-dimmtemp driver for temperature
  threshold checking.
- Refined kconfig dependencies in PECI subsystem.
- Added PECI 4.0 command set support.
- Refined 32-bit boundary alignment for all PECI ioctl command structs.
- Added DMA safe command buffer handling in peci-core.
- Added Nuvoton NPCM7xx PECI support.
- Added Aspeed AST26xx PECI support.
- Moved adapter drivers into 'drivers/peci/busses'.
- Fixed minor bugs and style issues.
- configfs support isn't added in this patch set. Will add that using a
  seperate patch set.

Changes since v9:
- Updated license identifiers to incidate 2019.
- Removed unnecessary member variable and enum type from intel client driver.
- Removed 'type' argument from the access_ok() function call.

Changes since v8:
- Refined descriptions in PECI device tree documents.
- Fixed checking logic of supportable PECI commands using full revision
  number.
- Fixed DIB data size to u64 to make that can contain 8-bytes of information.
- Changed PECI cdev names from pecix to peci-x where x is bus number.
- Refined intel-peci-client driver and moved hwmon relating codes to
  peci-hwmon.h.
- Added missing core label strings into peci-cputemp driver.
- Added kerneldoc comments.

Changes since v7:
- Fixed a typo in the MFD_INTEL_PECI_CLIENT description.
- Made peci_unregister_device() NULL-aware.
- Converted to using %pOF instead of node full name in peci-core.
- Removed OF tables from peci-cputemp and peci-dimmtemp.
- Removed of_compatible strings from intel-peci-client.
- Added an access_ok() check into peci_ioctl in peci-core.
- Changed the DT node name of peci simple-bus from 'peci' to 'bus'.

Changes since v6:
- Dropped off unnecessary examples from dt-bindings document.
- Fixed a bug in DIMM index mask building logic.
- Modified DIMM temp label strings to match with the way in BIOS.
- Changed PECI ioctl base number from B6 to B7 to avoid conflict with
  fpga-dfl.
- Separated the PECI section in MAINTAINERS into two parts - PECI subsystem
  and ASPEED PECI driver.

Changes since v5:
- Added more detailed descriptions for PECI client MFD documents.
- Changed PECI client MFD souce file names.
- Fixed DT example of PECI client MFD.
- Removed unnecessary debug printings.
- Moved the asm/intel-family.h inclusion place.

Changes since v4:
- Fixed an incorrect endianness handling in peci-aspeed.
- Added a comment to explain about the asm/intel-family.h inclusion.
- Added an MFD module to support multi-function PECI client devices.

Changes since v3:
- Made code more simple and compact.
- Removed unused header file inclusion.
- Fixed incorrect error return values and messages.
- Removed DTS margin temperature from the peci-cputemp.
- Made some magic numbers use defines.
- Moved peci_get_cpu_id() into peci-core as a common function.
- Replaced the cancel_delayed_work() call with a cancel_delayed_work_sync().
- Replaced AST and Aspeed uses with ASPEED.
- Simplified peci command timeout checking logic using
  regmap_read_poll_timeout().
- Simplified endian swap codes using endian handling macros.
- Dropped regmap read/write error checking except for the first access.
- Added a PECI reset setting in the device tree node.
- Removed unnecessary sleep from the probe context.
- Removed IRQF_SHARED flag from irq request code in the ASPEED PECI driver.
- Fixed typos in documents.
- Combined peci-bus.txt, peci-adapter.txt and peci-client.txt into peci.txt.
- Fixed and swept documents to drop some incorrect or unnecessary
  descriptions.
- Fixed device tree to make unit-address format use reg contents.
- Simplified bit manipulations using <linux/bitfield.h>.
- Made client CPU model checking use <asm/intel-family.h> if available.
- Modified adapter heap allocation method to use kobject reference count
  based.
- Added the low-level PECI xfer IOCTL again to support the Redfish
  requirement.
- Added PM domain attach/detach code.
- Added logic for device instantiation through sysfs.
- Fix a bug of interrupt status checking code in peci-aspeed driver.

Changes since v2:
- Divided peci-hwmon driver into two drivers, peci-cputemp and
  peci-dimmtemp.
- Added generic dt binding documents for PECI bus, adapter and client.
- Removed in_atomic() call from the PECI core driver.
- Improved PECI commands masking logic.
- Added permission check logic for PECI ioctls.
- Removed unnecessary type casts.
- Fixed some invalid error return codes.
- Added the mark_updated() function to improve update interval checking
  logic.
- Fixed a bug in populated DIMM checking function.
- Fixed some typo, grammar and style issues in documents.
- Rewrote hwmon drivers to use devm_hwmon_device_register_with_info API.
- Made peci_match_id() function as a static.
- Replaced a deprecated create_singlethread_workqueue() call with an
  alloc_ordered_workqueue() call.
- Reordered local variable definitions in reversed xmas tree notation.
- Listed up client CPUs that can be supported by peci-cputemp and
  peci-dimmtemp hwmon drivers.
- Added CPU generation detection logic which checks CPUID signature through
  PECI connection.
- Improved interrupt handling logic in the Aspeed PECI adapter driver.
- Fixed SPDX license identifier style in header files.
- Changed some macros in peci.h to static inline functions.
- Dropped sleepable context checking code in peci-core.
- Adjusted rt_mutex protection scope in peci-core.
- Moved adapter->xfer() checking code into peci_register_adapter().
- Improved PECI command retry checking logic.
- Changed ioctl base from 'P' to 0xb6 to avoid confiliction and updated
  ioctl-number.txt to reflect the ioctl number of PECI subsystem.
- Added a comment to describe PECI retry action.
- Simplified return code handling of peci_ioctl_ping().
- Changed type of peci_ioctl_fn[] to static const.
- Fixed range checking code for valid PECI commands.
- Fixed the error return code on invalid PECI commands.
- Fixed incorrect definitions of PECI ioctl and its handling logic.

Changes since v1:
- Additionally implemented a core driver to support PECI linux bus driver
  model.
- Modified Aspeed PECI driver to make that to be an adapter driver in PECI
  bus.
- Modified PECI hwmon driver to make that to be a client driver in PECI
  bus.
- Simplified hwmon driver attribute labels and removed redundant strings.
- Removed core_nums from device tree setting of hwmon driver and modified
  core number detection logic to check the resolved_core register in client
  CPU's local PCI configuration area.
- Removed dimm_nums from device tree setting of hwmon driver and added
  populated DIMM detection logic to support dynamic creation.
- Removed indexing gap on core temperature and DIMM temperature attributes.
- Improved hwmon registration and dynamic attribute creation logic.
- Fixed structure definitions in PECI uapi header to make that use __u8,
  __u16 and etc.
- Modified wait_for_completion_interruptible_timeout error handling logic
  in Aspeed PECI driver to deliver errors correctly.
- Removed low-level xfer command from ioctl and kept only high-level PECI
  command suite as ioctls.
- Fixed I/O timeout logic in Aspeed PECI driver using ktime.
- Added a function into hwmon driver to simplify update delay checking.
- Added a function into hwmon driver to convert 10.6 to millidegree.
- Dropped non-standard attributes in hwmon driver.
- Fixed OF table for hwmon to make it indicate as a PECI client of Intel
  CPU target.
- Added a maintainer of PECI subsystem into MAINTAINERS document.

Jae Hyun Yoo (11):
  dt-bindings: Add PECI subsystem document
  Documentation: ioctl: Add ioctl numbers for PECI subsystem
  peci: Add support for PECI bus driver core
  dt-bindings: Add bindings document of Aspeed PECI adapter
  ARM: dts: aspeed: Add PECI node
  peci: Add Aspeed PECI adapter driver
  dt-bindings: mfd: Add Intel PECI client bindings document
  mfd: intel-peci-client: Add Intel PECI client driver
  Documentation: hwmon: Add documents for PECI hwmon drivers
  hwmon: Add PECI cputemp driver
  hwmon: Add PECI dimmtemp driver

Tomer Maimon (3):
  dt-bindings: peci: add NPCM PECI documentation
  ARM: dts: npcm7xx: Add PECI node
  peci: npcm: add NPCM PECI driver

 .../bindings/mfd/intel,peci-client.yaml       |   67 +
 .../devicetree/bindings/peci/peci-aspeed.yaml |  124 +
 .../devicetree/bindings/peci/peci-bus.yaml    |  129 +
 .../devicetree/bindings/peci/peci-client.yaml |   54 +
 .../devicetree/bindings/peci/peci-npcm.yaml   |  102 +
 Documentation/hwmon/index.rst                 |    2 +
 Documentation/hwmon/peci-cputemp.rst          |   95 +
 Documentation/hwmon/peci-dimmtemp.rst         |   60 +
 .../userspace-api/ioctl/ioctl-number.rst      |    2 +
 arch/arm/boot/dts/aspeed-g4.dtsi              |   25 +
 arch/arm/boot/dts/aspeed-g5.dtsi              |   25 +
 arch/arm/boot/dts/aspeed-g6.dtsi              |   25 +
 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi |   19 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/hwmon/Kconfig                         |   28 +
 drivers/hwmon/Makefile                        |    2 +
 drivers/hwmon/peci-cputemp.c                  |  448 ++++
 drivers/hwmon/peci-dimmtemp.c                 |  393 ++++
 drivers/hwmon/peci-hwmon.h                    |   46 +
 drivers/mfd/Kconfig                           |   17 +
 drivers/mfd/Makefile                          |    1 +
 drivers/mfd/intel-peci-client.c               |  149 ++
 drivers/peci/Kconfig                          |   38 +
 drivers/peci/Makefile                         |   11 +
 drivers/peci/busses/Kconfig                   |   32 +
 drivers/peci/busses/Makefile                  |    7 +
 drivers/peci/busses/peci-aspeed.c             |  489 ++++
 drivers/peci/busses/peci-npcm.c               |  407 ++++
 drivers/peci/peci-core.c                      | 2086 +++++++++++++++++
 drivers/peci/peci-dev.c                       |  348 +++
 include/linux/mfd/intel-peci-client.h         |  117 +
 include/linux/peci.h                          |  150 ++
 include/uapi/linux/peci-ioctl.h               |  660 ++++++
 34 files changed, 6161 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml
 create mode 100644 Documentation/hwmon/peci-cputemp.rst
 create mode 100644 Documentation/hwmon/peci-dimmtemp.rst
 create mode 100644 drivers/hwmon/peci-cputemp.c
 create mode 100644 drivers/hwmon/peci-dimmtemp.c
 create mode 100644 drivers/hwmon/peci-hwmon.h
 create mode 100644 drivers/mfd/intel-peci-client.c
 create mode 100644 drivers/peci/Kconfig
 create mode 100644 drivers/peci/Makefile
 create mode 100644 drivers/peci/busses/Kconfig
 create mode 100644 drivers/peci/busses/Makefile
 create mode 100644 drivers/peci/busses/peci-aspeed.c
 create mode 100644 drivers/peci/busses/peci-npcm.c
 create mode 100644 drivers/peci/peci-core.c
 create mode 100644 drivers/peci/peci-dev.c
 create mode 100644 include/linux/mfd/intel-peci-client.h
 create mode 100644 include/linux/peci.h
 create mode 100644 include/uapi/linux/peci-ioctl.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH v11 00/14] PECI device driver introduction
@ 2019-12-11 19:46 ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	linux-arm-kernel

Introduction of the Platform Environment Control Interface (PECI) bus
device driver. PECI is a one-wire bus interface that provides a
communication channel from Intel processors and chipset components to
external monitoring or control devices. PECI is designed to support the
following sideband functions:

* Processor and DRAM thermal management
  - Processor fan speed control is managed by comparing Digital Thermal
    Sensor (DTS) thermal readings acquired via PECI against the
    processor-specific fan speed control reference point, or TCONTROL. Both
    TCONTROL and DTS thermal readings are accessible via the processor PECI
    client. These variables are referenced to a common temperature, the TCC
    activation point, and are both defined as negative offsets from that
    reference.
  - PECI based access to the processor package configuration space provides
    a means for Baseboard Management Controllers (BMC) or other platform
    management devices to actively manage the processor and memory power
    and thermal features.

* Platform Manageability
  - Platform manageability functions including thermal, power, and error
    monitoring. Note that platform 'power' management includes monitoring
    and control for both the processor and DRAM subsystem to assist with
    data center power limiting.
  - PECI allows read access to certain error registers in the processor MSR
    space and status monitoring registers in the PCI configuration space
    within the processor and downstream devices.
  - PECI permits writes to certain registers in the processor PCI
    configuration space.

* Processor Interface Tuning and Diagnostics
  - Processor interface tuning and diagnostics capabilities
    (Intel Interconnect BIST). The processors Intel Interconnect Built In
    Self Test (Intel IBIST) allows for infield diagnostic capabilities in
    the Intel UPI and memory controller interfaces. PECI provides a port to
    execute these diagnostics via its PCI Configuration read and write
    capabilities.

* Failure Analysis
  - Output the state of the processor after a failure for analysis via
    Crashdump.

PECI uses a single wire for self-clocking and data transfer. The bus
requires no additional control lines. The physical layer is a self-clocked
one-wire bus that begins each bit with a driven, rising edge from an idle
level near zero volts. The duration of the signal driven high depends on
whether the bit value is a logic '0' or logic '1'. PECI also includes
variable data transfer rate established with every message. In this way, it
is highly flexible even though underlying logic is simple.

The interface design was optimized for interfacing between an Intel
processor and chipset components in both single processor and multiple
processor environments. The single wire interface provides low board
routing overhead for the multiple load connections in the congested routing
area near the processor and chipset components. Bus speed, error checking,
and low protocol overhead provides adequate link bandwidth and reliability
to transfer critical device operating conditions and configuration
information.

This implementation provides the basic framework to add PECI extensions to
the Linux bus and device models. A hardware specific 'Adapter' driver can
be attached to the PECI bus to provide sideband functions described above.
It is also possible to access all devices on an adapter from userspace
through the /dev interface. A device specific 'Client' driver also can be
attached to the PECI bus so each processor client's features can be
supported by the 'Client' driver through an adapter connection in the bus.
This patch set includes Aspeed 24xx/25xx/26xx, NPCM7xx PECI drivers and
PECI cputemp/dimmtemp drivers as the first implementation for both adapter
and client drivers on the PECI bus framework.

Please review.

Thanks,

Jae

Changes since v10:
- Split out peci-dev module from peci-core module.
- Changed device tree bindings documents format to DT schema.
- Changed hwmon documents format to rst.
- Added Skylake Xeon D support in PECI hwmon drivers.
- Added DTS temperature reading in peci-cputemp driver which is more thermal
  control friendlier than Die temperature.
- Added max and crit properties into peci-dimmtemp driver for temperature
  threshold checking.
- Refined kconfig dependencies in PECI subsystem.
- Added PECI 4.0 command set support.
- Refined 32-bit boundary alignment for all PECI ioctl command structs.
- Added DMA safe command buffer handling in peci-core.
- Added Nuvoton NPCM7xx PECI support.
- Added Aspeed AST26xx PECI support.
- Moved adapter drivers into 'drivers/peci/busses'.
- Fixed minor bugs and style issues.
- configfs support isn't added in this patch set. Will add that using a
  seperate patch set.

Changes since v9:
- Updated license identifiers to incidate 2019.
- Removed unnecessary member variable and enum type from intel client driver.
- Removed 'type' argument from the access_ok() function call.

Changes since v8:
- Refined descriptions in PECI device tree documents.
- Fixed checking logic of supportable PECI commands using full revision
  number.
- Fixed DIB data size to u64 to make that can contain 8-bytes of information.
- Changed PECI cdev names from pecix to peci-x where x is bus number.
- Refined intel-peci-client driver and moved hwmon relating codes to
  peci-hwmon.h.
- Added missing core label strings into peci-cputemp driver.
- Added kerneldoc comments.

Changes since v7:
- Fixed a typo in the MFD_INTEL_PECI_CLIENT description.
- Made peci_unregister_device() NULL-aware.
- Converted to using %pOF instead of node full name in peci-core.
- Removed OF tables from peci-cputemp and peci-dimmtemp.
- Removed of_compatible strings from intel-peci-client.
- Added an access_ok() check into peci_ioctl in peci-core.
- Changed the DT node name of peci simple-bus from 'peci' to 'bus'.

Changes since v6:
- Dropped off unnecessary examples from dt-bindings document.
- Fixed a bug in DIMM index mask building logic.
- Modified DIMM temp label strings to match with the way in BIOS.
- Changed PECI ioctl base number from B6 to B7 to avoid conflict with
  fpga-dfl.
- Separated the PECI section in MAINTAINERS into two parts - PECI subsystem
  and ASPEED PECI driver.

Changes since v5:
- Added more detailed descriptions for PECI client MFD documents.
- Changed PECI client MFD souce file names.
- Fixed DT example of PECI client MFD.
- Removed unnecessary debug printings.
- Moved the asm/intel-family.h inclusion place.

Changes since v4:
- Fixed an incorrect endianness handling in peci-aspeed.
- Added a comment to explain about the asm/intel-family.h inclusion.
- Added an MFD module to support multi-function PECI client devices.

Changes since v3:
- Made code more simple and compact.
- Removed unused header file inclusion.
- Fixed incorrect error return values and messages.
- Removed DTS margin temperature from the peci-cputemp.
- Made some magic numbers use defines.
- Moved peci_get_cpu_id() into peci-core as a common function.
- Replaced the cancel_delayed_work() call with a cancel_delayed_work_sync().
- Replaced AST and Aspeed uses with ASPEED.
- Simplified peci command timeout checking logic using
  regmap_read_poll_timeout().
- Simplified endian swap codes using endian handling macros.
- Dropped regmap read/write error checking except for the first access.
- Added a PECI reset setting in the device tree node.
- Removed unnecessary sleep from the probe context.
- Removed IRQF_SHARED flag from irq request code in the ASPEED PECI driver.
- Fixed typos in documents.
- Combined peci-bus.txt, peci-adapter.txt and peci-client.txt into peci.txt.
- Fixed and swept documents to drop some incorrect or unnecessary
  descriptions.
- Fixed device tree to make unit-address format use reg contents.
- Simplified bit manipulations using <linux/bitfield.h>.
- Made client CPU model checking use <asm/intel-family.h> if available.
- Modified adapter heap allocation method to use kobject reference count
  based.
- Added the low-level PECI xfer IOCTL again to support the Redfish
  requirement.
- Added PM domain attach/detach code.
- Added logic for device instantiation through sysfs.
- Fix a bug of interrupt status checking code in peci-aspeed driver.

Changes since v2:
- Divided peci-hwmon driver into two drivers, peci-cputemp and
  peci-dimmtemp.
- Added generic dt binding documents for PECI bus, adapter and client.
- Removed in_atomic() call from the PECI core driver.
- Improved PECI commands masking logic.
- Added permission check logic for PECI ioctls.
- Removed unnecessary type casts.
- Fixed some invalid error return codes.
- Added the mark_updated() function to improve update interval checking
  logic.
- Fixed a bug in populated DIMM checking function.
- Fixed some typo, grammar and style issues in documents.
- Rewrote hwmon drivers to use devm_hwmon_device_register_with_info API.
- Made peci_match_id() function as a static.
- Replaced a deprecated create_singlethread_workqueue() call with an
  alloc_ordered_workqueue() call.
- Reordered local variable definitions in reversed xmas tree notation.
- Listed up client CPUs that can be supported by peci-cputemp and
  peci-dimmtemp hwmon drivers.
- Added CPU generation detection logic which checks CPUID signature through
  PECI connection.
- Improved interrupt handling logic in the Aspeed PECI adapter driver.
- Fixed SPDX license identifier style in header files.
- Changed some macros in peci.h to static inline functions.
- Dropped sleepable context checking code in peci-core.
- Adjusted rt_mutex protection scope in peci-core.
- Moved adapter->xfer() checking code into peci_register_adapter().
- Improved PECI command retry checking logic.
- Changed ioctl base from 'P' to 0xb6 to avoid confiliction and updated
  ioctl-number.txt to reflect the ioctl number of PECI subsystem.
- Added a comment to describe PECI retry action.
- Simplified return code handling of peci_ioctl_ping().
- Changed type of peci_ioctl_fn[] to static const.
- Fixed range checking code for valid PECI commands.
- Fixed the error return code on invalid PECI commands.
- Fixed incorrect definitions of PECI ioctl and its handling logic.

Changes since v1:
- Additionally implemented a core driver to support PECI linux bus driver
  model.
- Modified Aspeed PECI driver to make that to be an adapter driver in PECI
  bus.
- Modified PECI hwmon driver to make that to be a client driver in PECI
  bus.
- Simplified hwmon driver attribute labels and removed redundant strings.
- Removed core_nums from device tree setting of hwmon driver and modified
  core number detection logic to check the resolved_core register in client
  CPU's local PCI configuration area.
- Removed dimm_nums from device tree setting of hwmon driver and added
  populated DIMM detection logic to support dynamic creation.
- Removed indexing gap on core temperature and DIMM temperature attributes.
- Improved hwmon registration and dynamic attribute creation logic.
- Fixed structure definitions in PECI uapi header to make that use __u8,
  __u16 and etc.
- Modified wait_for_completion_interruptible_timeout error handling logic
  in Aspeed PECI driver to deliver errors correctly.
- Removed low-level xfer command from ioctl and kept only high-level PECI
  command suite as ioctls.
- Fixed I/O timeout logic in Aspeed PECI driver using ktime.
- Added a function into hwmon driver to simplify update delay checking.
- Added a function into hwmon driver to convert 10.6 to millidegree.
- Dropped non-standard attributes in hwmon driver.
- Fixed OF table for hwmon to make it indicate as a PECI client of Intel
  CPU target.
- Added a maintainer of PECI subsystem into MAINTAINERS document.

Jae Hyun Yoo (11):
  dt-bindings: Add PECI subsystem document
  Documentation: ioctl: Add ioctl numbers for PECI subsystem
  peci: Add support for PECI bus driver core
  dt-bindings: Add bindings document of Aspeed PECI adapter
  ARM: dts: aspeed: Add PECI node
  peci: Add Aspeed PECI adapter driver
  dt-bindings: mfd: Add Intel PECI client bindings document
  mfd: intel-peci-client: Add Intel PECI client driver
  Documentation: hwmon: Add documents for PECI hwmon drivers
  hwmon: Add PECI cputemp driver
  hwmon: Add PECI dimmtemp driver

Tomer Maimon (3):
  dt-bindings: peci: add NPCM PECI documentation
  ARM: dts: npcm7xx: Add PECI node
  peci: npcm: add NPCM PECI driver

 .../bindings/mfd/intel,peci-client.yaml       |   67 +
 .../devicetree/bindings/peci/peci-aspeed.yaml |  124 +
 .../devicetree/bindings/peci/peci-bus.yaml    |  129 +
 .../devicetree/bindings/peci/peci-client.yaml |   54 +
 .../devicetree/bindings/peci/peci-npcm.yaml   |  102 +
 Documentation/hwmon/index.rst                 |    2 +
 Documentation/hwmon/peci-cputemp.rst          |   95 +
 Documentation/hwmon/peci-dimmtemp.rst         |   60 +
 .../userspace-api/ioctl/ioctl-number.rst      |    2 +
 arch/arm/boot/dts/aspeed-g4.dtsi              |   25 +
 arch/arm/boot/dts/aspeed-g5.dtsi              |   25 +
 arch/arm/boot/dts/aspeed-g6.dtsi              |   25 +
 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi |   19 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/hwmon/Kconfig                         |   28 +
 drivers/hwmon/Makefile                        |    2 +
 drivers/hwmon/peci-cputemp.c                  |  448 ++++
 drivers/hwmon/peci-dimmtemp.c                 |  393 ++++
 drivers/hwmon/peci-hwmon.h                    |   46 +
 drivers/mfd/Kconfig                           |   17 +
 drivers/mfd/Makefile                          |    1 +
 drivers/mfd/intel-peci-client.c               |  149 ++
 drivers/peci/Kconfig                          |   38 +
 drivers/peci/Makefile                         |   11 +
 drivers/peci/busses/Kconfig                   |   32 +
 drivers/peci/busses/Makefile                  |    7 +
 drivers/peci/busses/peci-aspeed.c             |  489 ++++
 drivers/peci/busses/peci-npcm.c               |  407 ++++
 drivers/peci/peci-core.c                      | 2086 +++++++++++++++++
 drivers/peci/peci-dev.c                       |  348 +++
 include/linux/mfd/intel-peci-client.h         |  117 +
 include/linux/peci.h                          |  150 ++
 include/uapi/linux/peci-ioctl.h               |  660 ++++++
 34 files changed, 6161 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml
 create mode 100644 Documentation/hwmon/peci-cputemp.rst
 create mode 100644 Documentation/hwmon/peci-dimmtemp.rst
 create mode 100644 drivers/hwmon/peci-cputemp.c
 create mode 100644 drivers/hwmon/peci-dimmtemp.c
 create mode 100644 drivers/hwmon/peci-hwmon.h
 create mode 100644 drivers/mfd/intel-peci-client.c
 create mode 100644 drivers/peci/Kconfig
 create mode 100644 drivers/peci/Makefile
 create mode 100644 drivers/peci/busses/Kconfig
 create mode 100644 drivers/peci/busses/Makefile
 create mode 100644 drivers/peci/busses/peci-aspeed.c
 create mode 100644 drivers/peci/busses/peci-npcm.c
 create mode 100644 drivers/peci/peci-core.c
 create mode 100644 drivers/peci/peci-dev.c
 create mode 100644 include/linux/mfd/intel-peci-client.h
 create mode 100644 include/linux/peci.h
 create mode 100644 include/uapi/linux/peci-ioctl.h

-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH v11 01/14] dt-bindings: Add PECI subsystem document
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo

This commit adds PECI subsystem document.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Joel Stanley <joel@jms.id.au>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Changed documents format to DT schema format so I dropped all review tags.
  Please review it again.

 .../devicetree/bindings/peci/peci-bus.yaml    | 129 ++++++++++++++++++
 .../devicetree/bindings/peci/peci-client.yaml |  54 ++++++++
 2 files changed, 183 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml

diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
new file mode 100644
index 000000000000..b085e67089cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
@@ -0,0 +1,129 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic Device Tree Bindings for PECI bus
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+description: |
+  PECI (Platform Environment Control Interface) is a one-wire bus interface that
+  provides a communication channel from Intel processors and chipset components
+  to external monitoring or control devices. PECI is designed to support the
+  following sideband functions:
+
+  * Processor and DRAM thermal management
+    - Processor fan speed control is managed by comparing Digital Thermal
+      Sensor (DTS) thermal readings acquired via PECI against the
+      processor-specific fan speed control reference point, or TCONTROL. Both
+      TCONTROL and DTS thermal readings are accessible via the processor PECI
+      client. These variables are referenced to a common temperature, the TCC
+      activation point, and are both defined as negative offsets from that
+      reference.
+    - PECI based access to the processor package configuration space provides
+      a means for Baseboard Management Controllers (BMC) or other platform
+      management devices to actively manage the processor and memory power
+      and thermal features.
+
+  * Platform Manageability
+    - Platform manageability functions including thermal, power, and error
+      monitoring. Note that platform 'power' management includes monitoring
+      and control for both the processor and DRAM subsystem to assist with
+      data center power limiting.
+    - PECI allows read access to certain error registers in the processor MSR
+      space and status monitoring registers in the PCI configuration space
+      within the processor and downstream devices.
+    - PECI permits writes to certain registers in the processor PCI
+      configuration space.
+
+  * Processor Interface Tuning and Diagnostics
+    - Processor interface tuning and diagnostics capabilities
+      (Intel Interconnect BIST). The processors Intel Interconnect Built In
+      Self Test (Intel IBIST) allows for infield diagnostic capabilities in
+      the Intel UPI and memory controller interfaces. PECI provides a port to
+      execute these diagnostics via its PCI Configuration read and write
+      capabilities.
+
+  * Failure Analysis
+    - Output the state of the processor after a failure for analysis via
+      Crashdump.
+
+  PECI uses a single wire for self-clocking and data transfer. The bus
+  requires no additional control lines. The physical layer is a self-clocked
+  one-wire bus that begins each bit with a driven, rising edge from an idle
+  level near zero volts. The duration of the signal driven high depends on
+  whether the bit value is a logic '0' or logic '1'. PECI also includes
+  variable data transfer rate established with every message. In this way, it
+  is highly flexible even though underlying logic is simple.
+
+  The interface design was optimized for interfacing between an Intel
+  processor and chipset components in both single processor and multiple
+  processor environments. The single wire interface provides low board
+  routing overhead for the multiple load connections in the congested routing
+  area near the processor and chipset components. Bus speed, error checking,
+  and low protocol overhead provides adequate link bandwidth and reliability
+  to transfer critical device operating conditions and configuration
+  information.
+
+  PECI subsystem provides single or multiple bus nodes support so each bus can
+  have one adapter node and multiple device specific client nodes that can be
+  attached to the PECI bus so each processor client's features can be supported
+  by the client node through an adapter connection in the bus.
+
+properties:
+  compatible:
+    const: simple-bus
+
+  "#address-cells":
+    # Required to define bus device control resource address.
+    const: 1
+
+  "#size-cells":
+    # Required to define bus device control resource address.
+    const: 1
+
+  ranges: true
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x200>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+        };
+
+        // Just an example. ast2600 doesn't have a second PECI module actually.
+        peci1: peci-bus@100 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x100 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+        };
+    };
+...
diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
new file mode 100644
index 000000000000..fc7c4110e929
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-client.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic Device Tree Bindings for PECI clients
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+properties:
+  compatible:
+    enum:
+      - intel,peci-client
+
+  reg:
+    description: |
+      Address of a client CPU. According to the PECI specification, client
+      addresses start from 0x30.
+    maxItems: 1
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x60>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+
+            peci-client@30 {
+                compatible = "intel,peci-client";
+                reg = <0x30>;
+            };
+
+            peci-client@31 {
+                compatible = "intel,peci-client";
+                reg = <0x31>;
+            };
+        };
+    };
+...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 01/14] dt-bindings: Add PECI subsystem document
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	linux-arm-kernel

This commit adds PECI subsystem document.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Joel Stanley <joel@jms.id.au>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Changed documents format to DT schema format so I dropped all review tags.
  Please review it again.

 .../devicetree/bindings/peci/peci-bus.yaml    | 129 ++++++++++++++++++
 .../devicetree/bindings/peci/peci-client.yaml |  54 ++++++++
 2 files changed, 183 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
 create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml

diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
new file mode 100644
index 000000000000..b085e67089cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
@@ -0,0 +1,129 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic Device Tree Bindings for PECI bus
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+description: |
+  PECI (Platform Environment Control Interface) is a one-wire bus interface that
+  provides a communication channel from Intel processors and chipset components
+  to external monitoring or control devices. PECI is designed to support the
+  following sideband functions:
+
+  * Processor and DRAM thermal management
+    - Processor fan speed control is managed by comparing Digital Thermal
+      Sensor (DTS) thermal readings acquired via PECI against the
+      processor-specific fan speed control reference point, or TCONTROL. Both
+      TCONTROL and DTS thermal readings are accessible via the processor PECI
+      client. These variables are referenced to a common temperature, the TCC
+      activation point, and are both defined as negative offsets from that
+      reference.
+    - PECI based access to the processor package configuration space provides
+      a means for Baseboard Management Controllers (BMC) or other platform
+      management devices to actively manage the processor and memory power
+      and thermal features.
+
+  * Platform Manageability
+    - Platform manageability functions including thermal, power, and error
+      monitoring. Note that platform 'power' management includes monitoring
+      and control for both the processor and DRAM subsystem to assist with
+      data center power limiting.
+    - PECI allows read access to certain error registers in the processor MSR
+      space and status monitoring registers in the PCI configuration space
+      within the processor and downstream devices.
+    - PECI permits writes to certain registers in the processor PCI
+      configuration space.
+
+  * Processor Interface Tuning and Diagnostics
+    - Processor interface tuning and diagnostics capabilities
+      (Intel Interconnect BIST). The processors Intel Interconnect Built In
+      Self Test (Intel IBIST) allows for infield diagnostic capabilities in
+      the Intel UPI and memory controller interfaces. PECI provides a port to
+      execute these diagnostics via its PCI Configuration read and write
+      capabilities.
+
+  * Failure Analysis
+    - Output the state of the processor after a failure for analysis via
+      Crashdump.
+
+  PECI uses a single wire for self-clocking and data transfer. The bus
+  requires no additional control lines. The physical layer is a self-clocked
+  one-wire bus that begins each bit with a driven, rising edge from an idle
+  level near zero volts. The duration of the signal driven high depends on
+  whether the bit value is a logic '0' or logic '1'. PECI also includes
+  variable data transfer rate established with every message. In this way, it
+  is highly flexible even though underlying logic is simple.
+
+  The interface design was optimized for interfacing between an Intel
+  processor and chipset components in both single processor and multiple
+  processor environments. The single wire interface provides low board
+  routing overhead for the multiple load connections in the congested routing
+  area near the processor and chipset components. Bus speed, error checking,
+  and low protocol overhead provides adequate link bandwidth and reliability
+  to transfer critical device operating conditions and configuration
+  information.
+
+  PECI subsystem provides single or multiple bus nodes support so each bus can
+  have one adapter node and multiple device specific client nodes that can be
+  attached to the PECI bus so each processor client's features can be supported
+  by the client node through an adapter connection in the bus.
+
+properties:
+  compatible:
+    const: simple-bus
+
+  "#address-cells":
+    # Required to define bus device control resource address.
+    const: 1
+
+  "#size-cells":
+    # Required to define bus device control resource address.
+    const: 1
+
+  ranges: true
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x200>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+        };
+
+        // Just an example. ast2600 doesn't have a second PECI module actually.
+        peci1: peci-bus@100 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x100 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+        };
+    };
+...
diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
new file mode 100644
index 000000000000..fc7c4110e929
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-client.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic Device Tree Bindings for PECI clients
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+properties:
+  compatible:
+    enum:
+      - intel,peci-client
+
+  reg:
+    description: |
+      Address of a client CPU. According to the PECI specification, client
+      addresses start from 0x30.
+    maxItems: 1
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x60>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+
+            peci-client@30 {
+                compatible = "intel,peci-client";
+                reg = <0x30>;
+            };
+
+            peci-client@31 {
+                compatible = "intel,peci-client";
+                reg = <0x31>;
+            };
+        };
+    };
+...
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 02/14] Documentation: ioctl: Add ioctl numbers for PECI subsystem
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, James Feist, Jason M Biils, Vernon Mauery

This commit updates ioctl-number.rst to reflect ioctl numbers used
by the PECI subsystem.

Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Darrick J. Wong <darrick.wong@oracle.com>
Cc: Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Moved the change from 'Documentation/ioctl/ioctl-number.txt.

 Documentation/userspace-api/ioctl/ioctl-number.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 4ef86433bd67..ce99968032f7 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -338,6 +338,8 @@ Code  Seq#    Include File                                           Comments
 0xB4  00-0F  linux/gpio.h                                            <mailto:linux-gpio@vger.kernel.org>
 0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-remoteproc@vger.kernel.org>
 0xB6  all    linux/fpga-dfl.h
+0xB7  all    uapi/linux/peci-ioctl.h                                 PECI subsystem
+                                                                     <mailto:jae.hyun.yoo@linux.intel.com>
 0xC0  00-0F  linux/usb/iowarrior.h
 0xCA  00-0F  uapi/misc/cxl.h
 0xCA  10-2F  uapi/misc/ocxl.h
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 02/14] Documentation: ioctl: Add ioctl numbers for PECI subsystem
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, Vernon Mauery,
	openbmc, James Feist, Jason M Biils, linux-arm-kernel

This commit updates ioctl-number.rst to reflect ioctl numbers used
by the PECI subsystem.

Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Darrick J. Wong <darrick.wong@oracle.com>
Cc: Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Moved the change from 'Documentation/ioctl/ioctl-number.txt.

 Documentation/userspace-api/ioctl/ioctl-number.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 4ef86433bd67..ce99968032f7 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -338,6 +338,8 @@ Code  Seq#    Include File                                           Comments
 0xB4  00-0F  linux/gpio.h                                            <mailto:linux-gpio@vger.kernel.org>
 0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-remoteproc@vger.kernel.org>
 0xB6  all    linux/fpga-dfl.h
+0xB7  all    uapi/linux/peci-ioctl.h                                 PECI subsystem
+                                                                     <mailto:jae.hyun.yoo@linux.intel.com>
 0xC0  00-0F  linux/usb/iowarrior.h
 0xCA  00-0F  uapi/misc/cxl.h
 0xCA  10-2F  uapi/misc/ocxl.h
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 03/14] peci: Add support for PECI bus driver core
  2019-12-11 19:46 ` Jae Hyun Yoo
                   ` (2 preceding siblings ...)
  (?)
@ 2019-12-11 19:46 ` Jae Hyun Yoo
  2019-12-11 20:18   ` Andy Shevchenko
  -1 siblings, 1 reply; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Gavin Schenk, Viresh Kumar, Cyrille Pitchen,
	Alan Cox, Andrew Lunn, Andy Shevchenko, Benjamin Herrenschmidt,
	Fengguang Wu, Jason M Biils, Julia Cartwright, Yunge Zhu

This commit adds driver implementation for PECI bus core into linux
driver framework.

PECI (Platform Environment Control Interface) is a one-wire bus interface
that provides a communication channel from Intel processors and chipset
components to external monitoring or control devices. PECI is designed to
support the following sideband functions:

* Processor and DRAM thermal management
  - Processor fan speed control is managed by comparing Digital Thermal
    Sensor (DTS) thermal readings acquired via PECI against the
    processor-specific fan speed control reference point, or TCONTROL. Both
    TCONTROL and DTS thermal readings are accessible via the processor PECI
    client. These variables are referenced to a common temperature, the TCC
    activation point, and are both defined as negative offsets from that
    reference.
  - PECI based access to the processor package configuration space provides
    a means for Baseboard Management Controllers (BMC) or other platform
    management devices to actively manage the processor and memory power
    and thermal features.

* Platform Manageability
  - Platform manageability functions including thermal, power, and error
    monitoring. Note that platform 'power' management includes monitoring
    and control for both the processor and DRAM subsystem to assist with
    data center power limiting.
  - PECI allows read access to certain error registers in the processor MSR
    space and status monitoring registers in the PCI configuration space
    within the processor and downstream devices.
  - PECI permits writes to certain registers in the processor PCI
    configuration space.

* Processor Interface Tuning and Diagnostics
  - Processor interface tuning and diagnostics capabilities
    (Intel Interconnect BIST). The processors Intel Interconnect Built In
    Self Test (Intel IBIST) allows for infield diagnostic capabilities in
    the Intel UPI and memory controller interfaces. PECI provides a port to
    execute these diagnostics via its PCI Configuration read and write
    capabilities.

* Failure Analysis
  - Output the state of the processor after a failure for analysis via
    Crashdump.

PECI uses a single wire for self-clocking and data transfer. The bus
requires no additional control lines. The physical layer is a self-clocked
one-wire bus that begins each bit with a driven, rising edge from an idle
level near zero volts. The duration of the signal driven high depends on
whether the bit value is a logic '0' or logic '1'. PECI also includes
variable data transfer rate established with every message. In this way, it
is highly flexible even though underlying logic is simple.

The interface design was optimized for interfacing between an Intel
processor and chipset components in both single processor and multiple
processor environments. The single wire interface provides low board
routing overhead for the multiple load connections in the congested routing
area near the processor and chipset components. Bus speed, error checking,
and low protocol overhead provides adequate link bandwidth and reliability
to transfer critical device operating conditions and configuration
information.

This implementation provides the basic framework to add PECI extensions to
the Linux bus and device models. A hardware specific 'Adapter' driver can
be attached to the PECI bus to provide sideband functions described above.
It is also possible to access all devices on an adapter from userspace
through the /dev interface. A device specific 'Client' driver also can be
attached to the PECI bus so each processor client's features can be
supported by the 'Client' driver through an adapter connection in the bus.

Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Cc: Gavin Schenk <g.schenk@eckelmann.de>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Sagar Dharia <sdharia@codeaurora.org>
Cc: David Kershner <david.kershner@unisys.com>
Cc: Johan Hovold <johan@kernel.org>
Cc: Uwe Kleine-Konig <u.kleine-koenig@pengutronix.de>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Andy Shevchenko <andriy.shevchenko@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Julia Cartwright <juliac@eso.teric.us>
Signed-off-by: Jason M Biils <jason.m.bills@linux.intel.com>
Signed-off-by: Yunge Zhu <yunge.zhu@linux.intel.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
Changes since v10:
- Split out peci-dev module from peci-core module.
- Added PECI 4.0 command set support.
- Refined 32-bit boundary alignment for all PECI ioctl command structs.
- Added DMA safe command buffer handling in peci-core.
- Refined kconfig dependencies in PECI subsystem.
- Fixed minor bugs and style issues.
- configfs support isn't added in this patch set. Will add that using a
  seperate patch set.

 drivers/Kconfig                 |    2 +
 drivers/Makefile                |    1 +
 drivers/peci/Kconfig            |   38 +
 drivers/peci/Makefile           |   11 +
 drivers/peci/busses/Kconfig     |    7 +
 drivers/peci/busses/Makefile    |    4 +
 drivers/peci/peci-core.c        | 2086 +++++++++++++++++++++++++++++++
 drivers/peci/peci-dev.c         |  348 ++++++
 include/linux/peci.h            |  150 +++
 include/uapi/linux/peci-ioctl.h |  660 ++++++++++
 10 files changed, 3307 insertions(+)
 create mode 100644 drivers/peci/Kconfig
 create mode 100644 drivers/peci/Makefile
 create mode 100644 drivers/peci/busses/Kconfig
 create mode 100644 drivers/peci/busses/Makefile
 create mode 100644 drivers/peci/peci-core.c
 create mode 100644 drivers/peci/peci-dev.c
 create mode 100644 include/linux/peci.h
 create mode 100644 include/uapi/linux/peci-ioctl.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 8befa53f43be..4322efa37732 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -228,4 +228,6 @@ source "drivers/interconnect/Kconfig"
 
 source "drivers/counter/Kconfig"
 
+source "drivers/peci/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index aaef17cc6512..82f78cfedf69 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -186,3 +186,4 @@ obj-$(CONFIG_SIOX)		+= siox/
 obj-$(CONFIG_GNSS)		+= gnss/
 obj-$(CONFIG_INTERCONNECT)	+= interconnect/
 obj-$(CONFIG_COUNTER)		+= counter/
+obj-$(CONFIG_PECI)		+= peci/
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
new file mode 100644
index 000000000000..9752feee2454
--- /dev/null
+++ b/drivers/peci/Kconfig
@@ -0,0 +1,38 @@
+#
+# Platform Environment Control Interface (PECI) subsystem configuration
+#
+
+menu "PECI support"
+
+config PECI
+	tristate "PECI support"
+	select CRC8
+	default n
+	help
+	  The Platform Environment Control Interface (PECI) is a one-wire bus
+	  interface that provides a communication channel from Intel processors
+	  and chipset components to external monitoring or control devices.
+
+	  If you want PECI support, you should say Y here and also to the
+	  specific driver for your bus adapter(s) below.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-core.
+
+if PECI
+
+config PECI_CHARDEV
+	tristate "PECI device interface"
+	help
+	  Say Y here to use peci-* device files, usually found in the /dev
+	  directory on your system. They make it possible to have user-space
+	  programs use the PECI bus.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-dev.
+
+source "drivers/peci/busses/Kconfig"
+
+endif # PECI
+
+endmenu
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
new file mode 100644
index 000000000000..da8b0a33fa42
--- /dev/null
+++ b/drivers/peci/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the PECI core drivers.
+#
+
+# Core functionality
+obj-$(CONFIG_PECI)		+= peci-core.o
+obj-$(CONFIG_PECI_CHARDEV)	+= peci-dev.o
+
+# Hardware specific bus drivers
+obj-y				+= busses/
diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
new file mode 100644
index 000000000000..d7e064f52a1c
--- /dev/null
+++ b/drivers/peci/busses/Kconfig
@@ -0,0 +1,7 @@
+#
+# PECI hardware bus configuration
+#
+
+menu "PECI Hardware Bus support"
+
+endmenu
diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
new file mode 100644
index 000000000000..9e9334fee297
--- /dev/null
+++ b/drivers/peci/busses/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the PECI hardware bus drivers.
+#
diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
new file mode 100644
index 000000000000..db34cf7edf63
--- /dev/null
+++ b/drivers/peci/peci-core.c
@@ -0,0 +1,2086 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched/task_stack.h>
+#include <linux/slab.h>
+
+/* Mask for getting minor revision number from DIB */
+#define REVISION_NUM_MASK  GENMASK(15, 8)
+
+/* CRC8 table for Assured Write Frame Check */
+#define PECI_CRC8_POLYNOMIAL  0x07
+DECLARE_CRC8_TABLE(peci_crc8_table);
+
+static bool is_registered;
+
+static DEFINE_MUTEX(core_lock);
+static DEFINE_IDR(peci_adapter_idr);
+
+struct peci_adapter *peci_get_adapter(int nr)
+{
+	struct peci_adapter *adapter;
+
+	mutex_lock(&core_lock);
+	adapter = idr_find(&peci_adapter_idr, nr);
+	if (!adapter)
+		goto out_unlock;
+
+	if (try_module_get(adapter->owner))
+		get_device(&adapter->dev);
+	else
+		adapter = NULL;
+
+out_unlock:
+	mutex_unlock(&core_lock);
+
+	return adapter;
+}
+EXPORT_SYMBOL_GPL(peci_get_adapter);
+
+void peci_put_adapter(struct peci_adapter *adapter)
+{
+	if (!adapter)
+		return;
+
+	put_device(&adapter->dev);
+	module_put(adapter->owner);
+}
+EXPORT_SYMBOL_GPL(peci_put_adapter);
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	return sprintf(buf, "%s\n", dev->type == &peci_client_type ?
+		       to_peci_client(dev)->name : to_peci_adapter(dev)->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static void peci_client_dev_release(struct device *dev)
+{
+	struct peci_client *client = to_peci_client(dev);
+
+	dev_dbg(dev, "%s: %s\n", __func__, client->name);
+	peci_put_adapter(client->adapter);
+	kfree(client);
+}
+
+static struct attribute *peci_device_attrs[] = {
+	&dev_attr_name.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(peci_device);
+
+struct device_type peci_client_type = {
+	.groups		= peci_device_groups,
+	.release	= peci_client_dev_release,
+};
+EXPORT_SYMBOL_GPL(peci_client_type);
+
+/**
+ * peci_verify_client - return parameter as peci_client, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * Return: pointer to peci_client on success, else NULL.
+ */
+struct peci_client *peci_verify_client(struct device *dev)
+{
+	return (dev->type == &peci_client_type)
+			? to_peci_client(dev)
+			: NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_client);
+
+/**
+ * peci_get_xfer_msg() - get a DMA safe peci_xfer_msg for the given tx and rx
+ *			 length
+ * @tx_len: the length of tx_buf. May be 0 if tx_buf isn't needed.
+ * @rx_len: the length of rx_buf. May be 0 if rx_buf isn't needed.
+ *
+ * Return: NULL if a DMA safe buffer was not obtained.
+ *	   Or a valid pointer to be used with DMA. After use, release it by
+ *	   calling peci_put_xfer_msg().
+ *
+ * This function must only be called from process context!
+ */
+struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len)
+{
+	struct peci_xfer_msg *msg;
+	u8 *tx_buf, *rx_buf;
+
+	if (tx_len) {
+		tx_buf = kzalloc(tx_len, GFP_KERNEL);
+		if (!tx_buf)
+			return NULL;
+	} else {
+		tx_buf = NULL;
+	}
+
+	if (rx_len) {
+		rx_buf = kzalloc(rx_len, GFP_KERNEL);
+		if (!rx_buf)
+			goto err_free_tx_buf;
+	} else {
+		rx_buf = NULL;
+	}
+
+	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+	if (!msg)
+		goto err_free_tx_rx_buf;
+
+	msg->tx_len = tx_len;
+	msg->tx_buf = tx_buf;
+	msg->rx_len = rx_len;
+	msg->rx_buf = rx_buf;
+
+	return msg;
+
+err_free_tx_rx_buf:
+	kfree(rx_buf);
+err_free_tx_buf:
+	kfree(tx_buf);
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(peci_get_xfer_msg);
+
+/**
+ * peci_put_xfer_msg - release a DMA safe peci_xfer_msg
+ * @msg: the message obtained from peci_get_xfer_msg(). May be NULL.
+ */
+void peci_put_xfer_msg(struct peci_xfer_msg *msg)
+{
+	if (!msg)
+		return;
+
+	kfree(msg->rx_buf);
+	kfree(msg->tx_buf);
+	kfree(msg);
+}
+EXPORT_SYMBOL_GPL(peci_put_xfer_msg);
+
+/* Calculate an Assured Write Frame Check Sequence byte */
+static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs)
+{
+	u8 *tmp_buf;
+
+	/* Allocate a temporary buffer to use a contiguous byte array */
+	tmp_buf = kmalloc(len, GFP_KERNEL);
+	if (!tmp_buf)
+		return -ENOMEM;
+
+	tmp_buf[0] = msg->addr;
+	tmp_buf[1] = msg->tx_len;
+	tmp_buf[2] = msg->rx_len;
+	memcpy(&tmp_buf[3], msg->tx_buf, len - 3);
+
+	*aw_fcs = crc8(peci_crc8_table, tmp_buf, (size_t)len, 0);
+
+	kfree(tmp_buf);
+
+	return 0;
+}
+
+static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+		       bool do_retry, bool has_aw_fcs)
+{
+	ulong timeout = jiffies;
+	u8 aw_fcs;
+	int ret;
+
+	/*
+	 * In case if adapter uses DMA, check at here whether tx and rx buffers
+	 * are DMA capable or not.
+	 */
+	if (IS_ENABLED(CONFIG_HAS_DMA) && adapter->use_dma) {
+		if (is_vmalloc_addr(msg->tx_buf) ||
+		    is_vmalloc_addr(msg->rx_buf)) {
+			WARN_ONCE(1, "xfer msg is not dma capable\n");
+			return -EAGAIN;
+		} else if (object_is_on_stack(msg->tx_buf) ||
+			   object_is_on_stack(msg->rx_buf)) {
+			WARN_ONCE(1, "xfer msg is on stack\n");
+			return -EAGAIN;
+		}
+	}
+
+	/*
+	 * For some commands, the PECI originator may need to retry a command if
+	 * the processor PECI client responds with a 0x8x completion code. In
+	 * each instance, the processor PECI client may have started the
+	 * operation but not completed it yet. When the 'retry' bit is set, the
+	 * PECI client will ignore a new request if it exactly matches a
+	 * previous valid request.
+	 */
+
+	if (do_retry)
+		timeout += msecs_to_jiffies(PECI_DEV_RETRY_TIME_MS);
+
+	for (;;) {
+		ret = adapter->xfer(adapter, msg);
+
+		if (!do_retry || ret || !msg->rx_buf)
+			break;
+
+		/* Retry is needed when completion code is 0x8x */
+		if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) !=
+		    PECI_DEV_CC_NEED_RETRY)
+			break;
+
+		/* Set the retry bit to indicate a retry attempt */
+		msg->tx_buf[1] |= PECI_DEV_RETRY_BIT;
+
+		/* Recalculate the AW FCS if it has one */
+		if (has_aw_fcs) {
+			ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
+			if (ret)
+				break;
+
+			msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
+		}
+
+		/*
+		 * Retry for at least 250ms before returning an error.
+		 * Retry interval guideline:
+		 *   No minimum < Retry Interval < No maximum
+		 *                (recommend 10ms)
+		 */
+		if (time_after(jiffies, timeout)) {
+			dev_dbg(&adapter->dev, "Timeout retrying xfer!\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		usleep_range((PECI_DEV_RETRY_INTERVAL_USEC >> 2) + 1,
+			     PECI_DEV_RETRY_INTERVAL_USEC);
+	}
+
+	if (ret)
+		dev_dbg(&adapter->dev, "xfer error: %d\n", ret);
+
+	return ret;
+}
+
+static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg)
+{
+	return __peci_xfer(adapter, msg, false, false);
+}
+
+static int peci_xfer_with_retries(struct peci_adapter *adapter,
+				  struct peci_xfer_msg *msg,
+				  bool has_aw_fcs)
+{
+	return __peci_xfer(adapter, msg, true, has_aw_fcs);
+}
+
+static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+{
+	struct peci_xfer_msg *msg;
+	u8 revision;
+	int ret;
+	u64 dib;
+
+	/* Update command mask just once */
+	if (adapter->cmd_mask & BIT(PECI_CMD_XFER))
+		return 0;
+
+	msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr      = PECI_BASE_ADDR;
+	msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+	ret = peci_xfer(adapter, msg);
+	if (ret)
+		return ret;
+
+	dib = le64_to_cpup((__le64 *)msg->rx_buf);
+
+	/* Check special case for Get DIB command */
+	if (dib == 0) {
+		dev_dbg(&adapter->dev, "DIB read as 0\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	/*
+	 * Setting up the supporting commands based on revision number.
+	 * See PECI Spec Table 3-1.
+	 */
+	revision = FIELD_GET(REVISION_NUM_MASK, dib);
+	if (revision >= 0x40) { /* Rev. 4.0 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSREX);
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_END_PT_CFG);
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_END_PT_CFG);
+		adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_DISC);
+		adapter->cmd_mask |= BIT(PECI_CMD_CRASHDUMP_GET_FRAME);
+	}
+	if (revision >= 0x36) /* Rev. 3.6 */
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_IA_MSR);
+	if (revision >= 0x35) /* Rev. 3.5 */
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG);
+	if (revision >= 0x34) /* Rev. 3.4 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG);
+	if (revision >= 0x33) { /* Rev. 3.3 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG_LOCAL);
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG_LOCAL);
+	}
+	if (revision >= 0x32) /* Rev. 3.2 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSR);
+	if (revision >= 0x31) { /* Rev. 3.1 */
+		adapter->cmd_mask |= BIT(PECI_CMD_RD_PKG_CFG);
+		adapter->cmd_mask |= BIT(PECI_CMD_WR_PKG_CFG);
+	}
+
+	adapter->cmd_mask |= BIT(PECI_CMD_XFER);
+	adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP);
+	adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+	adapter->cmd_mask |= BIT(PECI_CMD_PING);
+
+out:
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_check_cmd_support(struct peci_adapter *adapter,
+				  enum peci_cmd cmd)
+{
+	if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) &&
+	    peci_scan_cmd_mask(adapter) < 0) {
+		dev_dbg(&adapter->dev, "Failed to scan command mask\n");
+		return -EIO;
+	}
+
+	if (!(adapter->cmd_mask & BIT(cmd))) {
+		dev_dbg(&adapter->dev, "Command %d is not supported\n", cmd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_xfer_msg *msg = vmsg;
+	u8 aw_fcs;
+	int ret;
+
+	if (!msg->tx_len) {
+		ret = peci_xfer(adapter, msg);
+	} else {
+		switch (msg->tx_buf[0]) {
+		case PECI_RDPKGCFG_CMD:
+		case PECI_RDIAMSR_CMD:
+		case PECI_RDIAMSREX_CMD:
+		case PECI_RDPCICFG_CMD:
+		case PECI_RDPCICFGLOCAL_CMD:
+		case PECI_RDENDPTCFG_CMD:
+		case PECI_CRASHDUMP_CMD:
+			ret = peci_xfer_with_retries(adapter, msg, false);
+			break;
+		case PECI_WRPKGCFG_CMD:
+		case PECI_WRIAMSR_CMD:
+		case PECI_WRPCICFG_CMD:
+		case PECI_WRPCICFGLOCAL_CMD:
+		case PECI_WRENDPTCFG_CMD:
+			/* Check if the AW FCS byte is already provided */
+			ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs);
+			if (ret)
+				break;
+
+			if (msg->tx_buf[msg->tx_len - 1] != (0x80 ^ aw_fcs)) {
+				/*
+				 * Add an Assured Write Frame Check Sequence
+				 * byte and increment the tx_len to include
+				 * the new byte.
+				 */
+				msg->tx_len++;
+				ret = peci_aw_fcs(msg, 2 + msg->tx_len,
+						  &aw_fcs);
+				if (ret)
+					break;
+
+				msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs;
+			}
+
+			ret = peci_xfer_with_retries(adapter, msg, true);
+			break;
+		case PECI_GET_DIB_CMD:
+		case PECI_GET_TEMP_CMD:
+		default:
+			ret = peci_xfer(adapter, msg);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int peci_cmd_ping(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_ping_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	msg = peci_get_xfer_msg(0, 0);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr   = umsg->addr;
+
+	ret = peci_xfer(adapter, msg);
+
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_get_dib(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_get_dib_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr      = umsg->addr;
+	msg->tx_buf[0] = PECI_GET_DIB_CMD;
+
+	ret = peci_xfer(adapter, msg);
+	if (ret)
+		goto out;
+
+	umsg->dib = le64_to_cpup((__le64 *)msg->rx_buf);
+
+out:
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_get_temp(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_get_temp_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	msg = peci_get_xfer_msg(PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr      = umsg->addr;
+	msg->tx_buf[0] = PECI_GET_TEMP_CMD;
+
+	ret = peci_xfer(adapter, msg);
+	if (ret)
+		goto out;
+
+	umsg->temp_raw = le16_to_cpup((__le16 *)msg->rx_buf);
+
+out:
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_pkg_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	/* Per the PECI spec, the read length must be a byte, word, or dword */
+	if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+			umsg->rx_len);
+		return -EINVAL;
+	}
+
+	msg = peci_get_xfer_msg(PECI_RDPKGCFG_WRITE_LEN,
+				PECI_RDPKGCFG_READ_LEN_BASE + umsg->rx_len);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_RDPKGCFG_CMD;
+	msg->tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				    /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = umsg->index;            /* RdPkgConfig index */
+	msg->tx_buf[3] = (u8)umsg->param;        /* LSB - Config parameter */
+	msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len);
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_wr_pkg_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret, i;
+	u8 aw_fcs;
+
+	/* Per the PECI spec, the write length must be a dword */
+	if (umsg->tx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+			umsg->tx_len);
+		return -EINVAL;
+	}
+
+	msg = peci_get_xfer_msg(PECI_WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len,
+				PECI_WRPKGCFG_READ_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_WRPKGCFG_CMD;
+	msg->tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = umsg->index;            /* RdPkgConfig index */
+	msg->tx_buf[3] = (u8)umsg->param;        /* LSB - Config parameter */
+	msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+	for (i = 0; i < umsg->tx_len; i++)
+		msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+	/* Add an Assured Write Frame Check Sequence byte */
+	ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
+	if (ret)
+		goto out;
+
+	msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+	ret = peci_xfer_with_retries(adapter, msg, true);
+
+out:
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_ia_msr_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	msg = peci_get_xfer_msg(PECI_RDIAMSR_WRITE_LEN, PECI_RDIAMSR_READ_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_RDIAMSR_CMD;
+	msg->tx_buf[1] = 0;
+	msg->tx_buf[2] = umsg->thread_id;
+	msg->tx_buf[3] = (u8)umsg->address;
+	msg->tx_buf[4] = (u8)(umsg->address >> 8);
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_rd_ia_msrex(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_ia_msrex_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	msg = peci_get_xfer_msg(PECI_RDIAMSREX_WRITE_LEN,
+				PECI_RDIAMSREX_READ_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_RDIAMSREX_CMD;
+	msg->tx_buf[1] = 0;
+	msg->tx_buf[2] = (u8)umsg->thread_id;
+	msg->tx_buf[3] = (u8)(umsg->thread_id >> 8);
+	msg->tx_buf[4] = (u8)umsg->address;
+	msg->tx_buf[5] = (u8)(umsg->address >> 8);
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t));
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg)
+{
+	return -ENOSYS; /* Not implemented yet */
+}
+
+static int peci_cmd_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_pci_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	u32 address;
+	int ret;
+
+	msg = peci_get_xfer_msg(PECI_RDPCICFG_WRITE_LEN,
+				PECI_RDPCICFG_READ_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	address = umsg->reg;                  /* [11:0]  - Register */
+	address |= (u32)umsg->function << 12; /* [14:12] - Function */
+	address |= (u32)umsg->device << 15;   /* [19:15] - Device   */
+	address |= (u32)umsg->bus << 20;      /* [27:20] - Bus      */
+					      /* [31:28] - Reserved */
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_RDPCICFG_CMD;
+	msg->tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				   /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = (u8)address;         /* LSB - PCI Config Address */
+	msg->tx_buf[3] = (u8)(address >> 8);  /* PCI Config Address */
+	msg->tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
+	msg->tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(umsg->pci_config, &msg->rx_buf[1], 4);
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	return -ENOSYS; /* Not implemented yet */
+}
+
+static int peci_cmd_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_pci_cfg_local_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	u32 address;
+	int ret;
+
+	/* Per the PECI spec, the read length must be a byte, word, or dword */
+	if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+			umsg->rx_len);
+		return -EINVAL;
+	}
+
+	msg = peci_get_xfer_msg(PECI_RDPCICFGLOCAL_WRITE_LEN,
+				PECI_RDPCICFGLOCAL_READ_LEN_BASE +
+				umsg->rx_len);
+	if (!msg)
+		return -ENOMEM;
+
+	address = umsg->reg;                  /* [11:0]  - Register */
+	address |= (u32)umsg->function << 12; /* [14:12] - Function */
+	address |= (u32)umsg->device << 15;   /* [19:15] - Device   */
+	address |= (u32)umsg->bus << 20;      /* [23:20] - Bus      */
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_RDPCICFGLOCAL_CMD;
+	msg->tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				    /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = (u8)address;      /* LSB - PCI Configuration Address */
+	msg->tx_buf[3] = (u8)(address >> 8);  /* PCI Configuration Address */
+	msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(umsg->pci_config, &msg->rx_buf[1], umsg->rx_len);
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_wr_pci_cfg_local_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	u32 address;
+	int ret, i;
+	u8 aw_fcs;
+
+	/* Per the PECI spec, the write length must be a byte, word, or dword */
+	if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) {
+		dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+			umsg->tx_len);
+		return -EINVAL;
+	}
+
+	msg = peci_get_xfer_msg(PECI_WRPCICFGLOCAL_WRITE_LEN_BASE +
+				umsg->tx_len, PECI_WRPCICFGLOCAL_READ_LEN);
+	if (!msg)
+		return -ENOMEM;
+
+	address = umsg->reg;                  /* [11:0]  - Register */
+	address |= (u32)umsg->function << 12; /* [14:12] - Function */
+	address |= (u32)umsg->device << 15;   /* [19:15] - Device   */
+	address |= (u32)umsg->bus << 20;      /* [23:20] - Bus      */
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_WRPCICFGLOCAL_CMD;
+	msg->tx_buf[1] = 0;         /* request byte for Host ID | Retry bit */
+				    /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = (u8)address;      /* LSB - PCI Configuration Address */
+	msg->tx_buf[3] = (u8)(address >> 8);  /* PCI Configuration Address */
+	msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+	for (i = 0; i < umsg->tx_len; i++)
+		msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+	/* Add an Assured Write Frame Check Sequence byte */
+	ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs);
+	if (ret)
+		goto out;
+
+	msg->tx_buf[5 + i] = 0x80 ^ aw_fcs;
+
+	ret = peci_xfer_with_retries(adapter, msg, true);
+
+out:
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_rd_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_rd_end_pt_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg = NULL;
+	u32 address;
+	u8 tx_size;
+	int ret;
+
+	switch (umsg->msg_type) {
+	case PECI_ENDPTCFG_TYPE_LOCAL_PCI:
+	case PECI_ENDPTCFG_TYPE_PCI:
+		/*
+		 * Per the PECI spec, the read length must be a byte, word,
+		 * or dword
+		 */
+		if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
+		    umsg->rx_len != 4) {
+			dev_dbg(&adapter->dev,
+				"Invalid read length, rx_len: %d\n",
+				umsg->rx_len);
+			return -EINVAL;
+		}
+
+		msg = peci_get_xfer_msg(PECI_RDENDPTCFG_PCI_WRITE_LEN,
+					PECI_RDENDPTCFG_READ_LEN_BASE +
+					umsg->rx_len);
+		if (!msg)
+			return -ENOMEM;
+
+		address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
+		address |= (u32)umsg->params.pci_cfg.function
+			   << 12; /* [14:12] - Function */
+		address |= (u32)umsg->params.pci_cfg.device
+			   << 15; /* [19:15] - Device   */
+		address |= (u32)umsg->params.pci_cfg.bus
+			   << 20; /* [27:20] - Bus      */
+				  /* [31:28] - Reserved */
+		msg->addr = umsg->addr;
+		msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
+		msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+		msg->tx_buf[2] = umsg->msg_type;	   /* Message Type */
+		msg->tx_buf[3] = 0x00;			   /* Endpoint ID */
+		msg->tx_buf[4] = 0x00;			   /* Reserved */
+		msg->tx_buf[5] = 0x00;			   /* Reserved */
+		msg->tx_buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
+		msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
+		msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
+		msg->tx_buf[9] = (u8)(address >> 8);   /* PCI Config Address */
+		msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
+		msg->tx_buf[11] =
+			(u8)(address >> 24); /* MSB - PCI Config Address */
+		break;
+
+	case PECI_ENDPTCFG_TYPE_MMIO:
+		/*
+		 * Per the PECI spec, the read length must be a byte, word,
+		 * dword, or qword
+		 */
+		if (umsg->rx_len != 1 && umsg->rx_len != 2 &&
+		    umsg->rx_len != 4 && umsg->rx_len != 8) {
+			dev_dbg(&adapter->dev,
+				"Invalid read length, rx_len: %d\n",
+				umsg->rx_len);
+			return -EINVAL;
+		}
+		/*
+		 * Per the PECI spec, the address type must specify either DWORD
+		 * or QWORD
+		 */
+		if (umsg->params.mmio.addr_type !=
+		    PECI_ENDPTCFG_ADDR_TYPE_MMIO_D &&
+		    umsg->params.mmio.addr_type !=
+		    PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+			dev_dbg(&adapter->dev,
+				"Invalid address type, addr_type: %d\n",
+				umsg->params.mmio.addr_type);
+			return -EINVAL;
+		}
+
+		if (umsg->params.mmio.addr_type ==
+			PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
+			tx_size = PECI_RDENDPTCFG_MMIO_D_WRITE_LEN;
+		else
+			tx_size = PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN;
+		msg = peci_get_xfer_msg(tx_size,
+					PECI_RDENDPTCFG_READ_LEN_BASE +
+					umsg->rx_len);
+		if (!msg)
+			return -ENOMEM;
+
+		address = umsg->params.mmio.function; /* [2:0] - Function */
+		address |= (u32)umsg->params.mmio.device
+			   << 3; /* [7:3] - Device */
+
+		msg->addr = umsg->addr;
+		msg->tx_buf[0] = PECI_RDENDPTCFG_CMD;
+		msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+		msg->tx_buf[2] = umsg->msg_type;	      /* Message Type */
+		msg->tx_buf[3] = 0x00;			      /* Endpoint ID */
+		msg->tx_buf[4] = 0x00;			      /* Reserved */
+		msg->tx_buf[5] = umsg->params.mmio.bar;       /* BAR # */
+		msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
+		msg->tx_buf[7] = umsg->params.mmio.seg;       /* PCI Segment */
+		msg->tx_buf[8] = (u8)address;	   /* Function/Device */
+		msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
+		msg->tx_buf[10] = (u8)umsg->params.mmio
+					 .offset; /* LSB - Register Offset */
+		msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
+				       >> 8); /* Register Offset */
+		msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
+				       >> 16); /* Register Offset */
+		msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
+				       >> 24); /* MSB - DWORD Register Offset */
+		if (umsg->params.mmio.addr_type ==
+		    PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+			msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
+					       >> 32); /* Register Offset */
+			msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
+					       >> 40); /* Register Offset */
+			msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
+					       >> 48); /* Register Offset */
+			msg->tx_buf[17] =
+				(u8)(umsg->params.mmio.offset
+				     >> 56); /* MSB - QWORD Register Offset */
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_wr_end_pt_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_wr_end_pt_cfg_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg = NULL;
+	u8 tx_size, aw_fcs;
+	int ret, i, idx;
+	u32 address;
+
+	switch (umsg->msg_type) {
+	case PECI_ENDPTCFG_TYPE_LOCAL_PCI:
+	case PECI_ENDPTCFG_TYPE_PCI:
+		/*
+		 * Per the PECI spec, the write length must be a byte, word,
+		 * or dword
+		 */
+		if (umsg->tx_len != 1 && umsg->tx_len != 2 &&
+		    umsg->tx_len != 4) {
+			dev_dbg(&adapter->dev,
+				"Invalid write length, tx_len: %d\n",
+				umsg->tx_len);
+			return -EINVAL;
+		}
+
+		msg = peci_get_xfer_msg(PECI_WRENDPTCFG_PCI_WRITE_LEN_BASE +
+					umsg->tx_len, PECI_WRENDPTCFG_READ_LEN);
+		if (!msg)
+			return -ENOMEM;
+
+		address = umsg->params.pci_cfg.reg; /* [11:0] - Register */
+		address |= (u32)umsg->params.pci_cfg.function
+			   << 12; /* [14:12] - Function */
+		address |= (u32)umsg->params.pci_cfg.device
+			   << 15; /* [19:15] - Device   */
+		address |= (u32)umsg->params.pci_cfg.bus
+			   << 20; /* [27:20] - Bus      */
+				  /* [31:28] - Reserved */
+		msg->addr = umsg->addr;
+		msg->tx_buf[0] = PECI_WRENDPTCFG_CMD;
+		msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+		msg->tx_buf[2] = umsg->msg_type;	   /* Message Type */
+		msg->tx_buf[3] = 0x00;			   /* Endpoint ID */
+		msg->tx_buf[4] = 0x00;			   /* Reserved */
+		msg->tx_buf[5] = 0x00;			   /* Reserved */
+		msg->tx_buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; /* Addr Type */
+		msg->tx_buf[7] = umsg->params.pci_cfg.seg; /* PCI Segment */
+		msg->tx_buf[8] = (u8)address; /* LSB - PCI Config Address */
+		msg->tx_buf[9] = (u8)(address >> 8);   /* PCI Config Address */
+		msg->tx_buf[10] = (u8)(address >> 16); /* PCI Config Address */
+		msg->tx_buf[11] =
+			(u8)(address >> 24); /* MSB - PCI Config Address */
+		for (i = 0; i < umsg->tx_len; i++)
+			msg->tx_buf[12 + i] = (u8)(umsg->value >> (i << 3));
+
+		/* Add an Assured Write Frame Check Sequence byte */
+		ret = peci_aw_fcs(msg, 15 + umsg->tx_len, &aw_fcs);
+		if (ret)
+			goto out;
+
+		msg->tx_buf[12 + i] = 0x80 ^ aw_fcs;
+		break;
+
+	case PECI_ENDPTCFG_TYPE_MMIO:
+		/*
+		 * Per the PECI spec, the write length must be a byte, word,
+		 * dword, or qword
+		 */
+		if (umsg->tx_len != 1 && umsg->tx_len != 2 &&
+		    umsg->tx_len != 4 && umsg->tx_len != 8) {
+			dev_dbg(&adapter->dev,
+				"Invalid write length, tx_len: %d\n",
+				umsg->tx_len);
+			return -EINVAL;
+		}
+		/*
+		 * Per the PECI spec, the address type must specify either DWORD
+		 * or QWORD
+		 */
+		if (umsg->params.mmio.addr_type !=
+		    PECI_ENDPTCFG_ADDR_TYPE_MMIO_D &&
+		    umsg->params.mmio.addr_type !=
+		    PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+			dev_dbg(&adapter->dev,
+				"Invalid address type, addr_type: %d\n",
+				umsg->params.mmio.addr_type);
+			return -EINVAL;
+		}
+
+		if (umsg->params.mmio.addr_type ==
+			PECI_ENDPTCFG_ADDR_TYPE_MMIO_D)
+			tx_size = PECI_WRENDPTCFG_MMIO_D_WRITE_LEN_BASE +
+				  umsg->tx_len;
+		else
+			tx_size = PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE +
+				  umsg->tx_len;
+		msg = peci_get_xfer_msg(tx_size, PECI_WRENDPTCFG_READ_LEN);
+		if (!msg)
+			return -ENOMEM;
+
+		address = umsg->params.mmio.function; /* [2:0] - Function */
+		address |= (u32)umsg->params.mmio.device
+			   << 3; /* [7:3] - Device */
+
+		msg->addr = umsg->addr;
+		msg->tx_buf[0] = PECI_WRENDPTCFG_CMD;
+		msg->tx_buf[1] = 0x00; /* request byte for Host ID|Retry bit */
+		msg->tx_buf[2] = umsg->msg_type;	      /* Message Type */
+		msg->tx_buf[3] = 0x00;			      /* Endpoint ID */
+		msg->tx_buf[4] = 0x00;			      /* Reserved */
+		msg->tx_buf[5] = umsg->params.mmio.bar;       /* BAR # */
+		msg->tx_buf[6] = umsg->params.mmio.addr_type; /* Address Type */
+		msg->tx_buf[7] = umsg->params.mmio.seg;       /* PCI Segment */
+		msg->tx_buf[8] = (u8)address;	   /* Function/Device */
+		msg->tx_buf[9] = umsg->params.mmio.bus; /* PCI Bus */
+		msg->tx_buf[10] = (u8)umsg->params.mmio
+					 .offset; /* LSB - Register Offset */
+		msg->tx_buf[11] = (u8)(umsg->params.mmio.offset
+				       >> 8); /* Register Offset */
+		msg->tx_buf[12] = (u8)(umsg->params.mmio.offset
+				       >> 16); /* Register Offset */
+		msg->tx_buf[13] = (u8)(umsg->params.mmio.offset
+				       >> 24); /* MSB - DWORD Register Offset */
+		if (umsg->params.mmio.addr_type ==
+		    PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q) {
+			msg->tx_len = PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE;
+			msg->tx_buf[14] = (u8)(umsg->params.mmio.offset
+					       >> 32); /* Register Offset */
+			msg->tx_buf[15] = (u8)(umsg->params.mmio.offset
+					       >> 40); /* Register Offset */
+			msg->tx_buf[16] = (u8)(umsg->params.mmio.offset
+					       >> 48); /* Register Offset */
+			msg->tx_buf[17] =
+				(u8)(umsg->params.mmio.offset
+				     >> 56); /* MSB - QWORD Register Offset */
+			idx = 18;
+		} else {
+			idx = 14;
+		}
+		for (i = 0; i < umsg->tx_len; i++)
+			msg->tx_buf[idx + i] = (u8)(umsg->value >> (i << 3));
+
+		/* Add an Assured Write Frame Check Sequence byte */
+		ret = peci_aw_fcs(msg, idx + 3 + umsg->tx_len, &aw_fcs);
+		if (ret)
+			goto out;
+
+		msg->tx_buf[idx + i] = 0x80 ^ aw_fcs;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+
+out:
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_crashdump_disc(struct peci_adapter *adapter, void *vmsg)
+{
+	struct peci_crashdump_disc_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	/* Per the EDS, the read length must be a byte, word, or qword */
+	if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 8) {
+		dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+			umsg->rx_len);
+		return -EINVAL;
+	}
+
+	msg = peci_get_xfer_msg(PECI_CRASHDUMP_DISC_WRITE_LEN,
+				PECI_CRASHDUMP_DISC_READ_LEN_BASE +
+				umsg->rx_len);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
+	msg->tx_buf[1] = 0x00;        /* request byte for Host ID | Retry bit */
+				      /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = PECI_CRASHDUMP_DISC_VERSION;
+	msg->tx_buf[3] = PECI_CRASHDUMP_DISC_OPCODE;
+	msg->tx_buf[4] = umsg->subopcode;
+	msg->tx_buf[5] = umsg->param0;
+	msg->tx_buf[6] = (u8)umsg->param1;
+	msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
+	msg->tx_buf[8] = umsg->param2;
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+static int peci_cmd_crashdump_get_frame(struct peci_adapter *adapter,
+					void *vmsg)
+{
+	struct peci_crashdump_get_frame_msg *umsg = vmsg;
+	struct peci_xfer_msg *msg;
+	int ret;
+
+	/* Per the EDS, the read length must be a qword or dqword */
+	if (umsg->rx_len != 8 && umsg->rx_len != 16) {
+		dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+			umsg->rx_len);
+		return -EINVAL;
+	}
+
+	msg = peci_get_xfer_msg(PECI_CRASHDUMP_GET_FRAME_WRITE_LEN,
+				PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE +
+				umsg->rx_len);
+	if (!msg)
+		return -ENOMEM;
+
+	msg->addr = umsg->addr;
+	msg->tx_buf[0] = PECI_CRASHDUMP_CMD;
+	msg->tx_buf[1] = 0x00;        /* request byte for Host ID | Retry bit */
+				      /* Host ID is 0 for PECI 3.0 */
+	msg->tx_buf[2] = PECI_CRASHDUMP_GET_FRAME_VERSION;
+	msg->tx_buf[3] = PECI_CRASHDUMP_GET_FRAME_OPCODE;
+	msg->tx_buf[4] = (u8)umsg->param0;
+	msg->tx_buf[5] = (u8)(umsg->param0 >> 8);
+	msg->tx_buf[6] = (u8)umsg->param1;
+	msg->tx_buf[7] = (u8)(umsg->param1 >> 8);
+	msg->tx_buf[8] = (u8)umsg->param2;
+	msg->tx_buf[9] = (u8)(umsg->param2 >> 8);
+
+	ret = peci_xfer_with_retries(adapter, msg, false);
+	if (!ret)
+		memcpy(umsg->data, &msg->rx_buf[1], umsg->rx_len);
+
+	umsg->cc = msg->rx_buf[0];
+	peci_put_xfer_msg(msg);
+
+	return ret;
+}
+
+typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *);
+
+static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = {
+	peci_cmd_xfer,
+	peci_cmd_ping,
+	peci_cmd_get_dib,
+	peci_cmd_get_temp,
+	peci_cmd_rd_pkg_cfg,
+	peci_cmd_wr_pkg_cfg,
+	peci_cmd_rd_ia_msr,
+	peci_cmd_wr_ia_msr,
+	peci_cmd_rd_ia_msrex,
+	peci_cmd_rd_pci_cfg,
+	peci_cmd_wr_pci_cfg,
+	peci_cmd_rd_pci_cfg_local,
+	peci_cmd_wr_pci_cfg_local,
+	peci_cmd_rd_end_pt_cfg,
+	peci_cmd_wr_end_pt_cfg,
+	peci_cmd_crashdump_disc,
+	peci_cmd_crashdump_get_frame,
+};
+
+/**
+ * peci_command - transfer function of a PECI command
+ * @adapter: pointer to peci_adapter
+ * @vmsg: pointer to PECI messages
+ * Context: can sleep
+ *
+ * This performs a transfer of a PECI command using PECI messages parameter
+ * which has various formats on each command.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg)
+{
+	int ret;
+
+	if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER)
+		return -ENOTTY;
+
+	dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd);
+
+	if (!peci_cmd_fn[cmd])
+		return -EINVAL;
+
+	mutex_lock(&adapter->bus_lock);
+
+	ret = peci_check_cmd_support(adapter, cmd);
+	if (!ret)
+		ret = peci_cmd_fn[cmd](adapter, vmsg);
+
+	mutex_unlock(&adapter->bus_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(peci_command);
+
+static int peci_detect(struct peci_adapter *adapter, u8 addr)
+{
+	struct peci_ping_msg msg;
+
+	msg.addr = addr;
+
+	return peci_command(adapter, PECI_CMD_PING, &msg);
+}
+
+static const struct of_device_id *
+peci_of_match_device(const struct of_device_id *matches,
+		     struct peci_client *client)
+{
+#if IS_ENABLED(CONFIG_OF)
+	if (!(client && matches))
+		return NULL;
+
+	return of_match_device(matches, &client->dev);
+#else /* CONFIG_OF */
+	return NULL;
+#endif /* CONFIG_OF */
+}
+
+static const struct peci_device_id *
+peci_match_id(const struct peci_device_id *id, struct peci_client *client)
+{
+	if (!(id && client))
+		return NULL;
+
+	while (id->name[0]) {
+		if (!strncmp(client->name, id->name, PECI_NAME_SIZE))
+			return id;
+		id++;
+	}
+
+	return NULL;
+}
+
+static int peci_device_match(struct device *dev, struct device_driver *drv)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+
+	/* Attempt an OF style match */
+	if (peci_of_match_device(drv->of_match_table, client))
+		return 1;
+
+	driver = to_peci_driver(drv);
+
+	/* Finally an ID match */
+	if (peci_match_id(driver->id_table, client))
+		return 1;
+
+	return 0;
+}
+
+static int peci_device_probe(struct device *dev)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+	int status = -EINVAL;
+
+	if (!client)
+		return 0;
+
+	driver = to_peci_driver(dev->driver);
+
+	if (!driver->id_table &&
+	    !peci_of_match_device(dev->driver->of_match_table, client))
+		return -ENODEV;
+
+	dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+	status = dev_pm_domain_attach(&client->dev, true);
+	if (status == -EPROBE_DEFER)
+		return status;
+
+	if (driver->probe)
+		status = driver->probe(client);
+	else
+		status = -EINVAL;
+
+	if (status)
+		goto err_detach_pm_domain;
+
+	return 0;
+
+err_detach_pm_domain:
+	dev_pm_domain_detach(&client->dev, true);
+
+	return status;
+}
+
+static int peci_device_remove(struct device *dev)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+	int status = 0;
+
+	if (!client || !dev->driver)
+		return 0;
+
+	driver = to_peci_driver(dev->driver);
+	if (driver->remove) {
+		dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+		status = driver->remove(client);
+	}
+
+	dev_pm_domain_detach(&client->dev, true);
+
+	return status;
+}
+
+static void peci_device_shutdown(struct device *dev)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_driver *driver;
+
+	if (!client || !dev->driver)
+		return;
+
+	dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+	driver = to_peci_driver(dev->driver);
+	if (driver->shutdown)
+		driver->shutdown(client);
+}
+
+struct bus_type peci_bus_type = {
+	.name		= "peci",
+	.match		= peci_device_match,
+	.probe		= peci_device_probe,
+	.remove		= peci_device_remove,
+	.shutdown	= peci_device_shutdown,
+};
+EXPORT_SYMBOL_GPL(peci_bus_type);
+
+static int peci_check_addr_validity(u8 addr)
+{
+	if (addr < PECI_BASE_ADDR && addr > PECI_BASE_ADDR + PECI_OFFSET_MAX)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int peci_check_client_busy(struct device *dev, void *client_new_p)
+{
+	struct peci_client *client = peci_verify_client(dev);
+	struct peci_client *client_new = client_new_p;
+
+	if (client && client->addr == client_new->addr)
+		return -EBUSY;
+
+	return 0;
+}
+
+/**
+ * peci_get_cpu_id - read CPU ID from the Package Configuration Space of CPU
+ * @adapter: pointer to peci_adapter
+ * @addr: address of the PECI client CPU
+ * @cpu_id: where the CPU ID will be stored
+ * Context: can sleep
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id)
+{
+	struct peci_rd_pkg_cfg_msg msg;
+	int ret;
+
+	msg.addr = addr;
+	msg.index = PECI_MBX_INDEX_CPU_ID;
+	msg.param = PECI_PKG_ID_CPU_ID;
+	msg.rx_len = 4;
+
+	ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
+	if (msg.cc != PECI_DEV_CC_SUCCESS)
+		ret = -EAGAIN;
+	if (ret)
+		return ret;
+
+	*cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(peci_get_cpu_id);
+
+static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+					   struct peci_board_info const *info)
+{
+	struct peci_client *client;
+	int ret;
+
+	/* Increase reference count for the adapter assigned */
+	if (!peci_get_adapter(adapter->nr))
+		return NULL;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		goto err_put_adapter;
+
+	client->adapter = adapter;
+	client->addr = info->addr;
+	strlcpy(client->name, info->type, sizeof(client->name));
+
+	ret = peci_check_addr_validity(client->addr);
+	if (ret) {
+		dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n",
+			client->addr);
+		goto err_free_client_silent;
+	}
+
+	/* Check online status of client */
+	ret = peci_detect(adapter, client->addr);
+	if (ret)
+		goto err_free_client;
+
+	ret = device_for_each_child(&adapter->dev, client,
+				    peci_check_client_busy);
+	if (ret)
+		goto err_free_client;
+
+	client->dev.parent = &client->adapter->dev;
+	client->dev.bus = &peci_bus_type;
+	client->dev.type = &peci_client_type;
+	client->dev.of_node = of_node_get(info->of_node);
+	dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr);
+
+	ret = device_register(&client->dev);
+	if (ret)
+		goto err_put_of_node;
+
+	dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
+		client->name, dev_name(&client->dev));
+
+	return client;
+
+err_put_of_node:
+	of_node_put(info->of_node);
+err_free_client:
+	dev_err(&adapter->dev,
+		"Failed to register peci client %s at 0x%02x (%d)\n",
+		client->name, client->addr, ret);
+err_free_client_silent:
+	kfree(client);
+err_put_adapter:
+	peci_put_adapter(adapter);
+
+	return NULL;
+}
+
+static void peci_unregister_device(struct peci_client *client)
+{
+	if (!client)
+		return;
+
+	if (client->dev.of_node) {
+		of_node_clear_flag(client->dev.of_node, OF_POPULATED);
+		of_node_put(client->dev.of_node);
+	}
+
+	device_unregister(&client->dev);
+}
+
+static int peci_unregister_client(struct device *dev, void *dummy)
+{
+	struct peci_client *client = peci_verify_client(dev);
+
+	peci_unregister_device(client);
+
+	return 0;
+}
+
+static void peci_adapter_dev_release(struct device *dev)
+{
+	struct peci_adapter *adapter = to_peci_adapter(dev);
+
+	dev_dbg(dev, "%s: %s\n", __func__, adapter->name);
+	mutex_destroy(&adapter->userspace_clients_lock);
+	mutex_destroy(&adapter->bus_lock);
+	kfree(adapter);
+}
+
+static ssize_t peci_sysfs_new_device(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct peci_adapter *adapter = to_peci_adapter(dev);
+	struct peci_board_info info = {};
+	struct peci_client *client;
+	char *blank, end;
+	short addr;
+	int ret;
+
+	/* Parse device type */
+	blank = strchr(buf, ' ');
+	if (!blank) {
+		dev_err(dev, "%s: Missing parameters\n", "new_device");
+		return -EINVAL;
+	}
+	if (blank - buf > PECI_NAME_SIZE - 1) {
+		dev_err(dev, "%s: Invalid device type\n", "new_device");
+		return -EINVAL;
+	}
+	memcpy(info.type, buf, blank - buf);
+
+	/* Parse remaining parameters, reject extra parameters */
+	ret = sscanf(++blank, "%hi%c", &addr, &end);
+	if (ret < 1) {
+		dev_err(dev, "%s: Can't parse client address\n", "new_device");
+		return -EINVAL;
+	}
+	if (ret > 1 && end != '\n') {
+		dev_err(dev, "%s: Extra parameters\n", "new_device");
+		return -EINVAL;
+	}
+
+	info.addr = (u8)addr;
+	client = peci_new_device(adapter, &info);
+	if (!client)
+		return -EINVAL;
+
+	/* Keep track of the added device */
+	mutex_lock(&adapter->userspace_clients_lock);
+	list_add_tail(&client->detected, &adapter->userspace_clients);
+	mutex_unlock(&adapter->userspace_clients_lock);
+	dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
+		info.type, info.addr);
+
+	return count;
+}
+static DEVICE_ATTR(new_device, 0200, NULL, peci_sysfs_new_device);
+
+static ssize_t peci_sysfs_delete_device(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct peci_adapter *adapter = to_peci_adapter(dev);
+	struct peci_client *client, *next;
+	struct peci_board_info info = {};
+	char *blank, end;
+	short addr;
+	int ret;
+
+	/* Parse device type */
+	blank = strchr(buf, ' ');
+	if (!blank) {
+		dev_err(dev, "%s: Missing parameters\n", "delete_device");
+		return -EINVAL;
+	}
+	if (blank - buf > PECI_NAME_SIZE - 1) {
+		dev_err(dev, "%s: Invalid device type\n", "delete_device");
+		return -EINVAL;
+	}
+	memcpy(info.type, buf, blank - buf);
+
+	/* Parse remaining parameters, reject extra parameters */
+	ret = sscanf(++blank, "%hi%c", &addr, &end);
+	if (ret < 1) {
+		dev_err(dev, "%s: Can't parse client address\n",
+			"delete_device");
+		return -EINVAL;
+	}
+	if (ret > 1 && end != '\n') {
+		dev_err(dev, "%s: Extra parameters\n", "delete_device");
+		return -EINVAL;
+	}
+
+	info.addr = (u8)addr;
+
+	/* Make sure the device was added through sysfs */
+	ret = -ENOENT;
+	mutex_lock(&adapter->userspace_clients_lock);
+	list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+				 detected) {
+		if (client->addr == info.addr &&
+		    !strncmp(client->name, info.type, PECI_NAME_SIZE)) {
+			dev_dbg(dev, "%s: Deleting device %s at 0x%02hx\n",
+				"delete_device", client->name, client->addr);
+			list_del(&client->detected);
+			peci_unregister_device(client);
+			ret = count;
+			break;
+		}
+	}
+	mutex_unlock(&adapter->userspace_clients_lock);
+
+	if (ret < 0)
+		dev_dbg(dev, "%s: Can't find device in list\n",
+			"delete_device");
+
+	return ret;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+				  peci_sysfs_delete_device);
+
+static struct attribute *peci_adapter_attrs[] = {
+	&dev_attr_name.attr,
+	&dev_attr_new_device.attr,
+	&dev_attr_delete_device.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(peci_adapter);
+
+struct device_type peci_adapter_type = {
+	.groups		= peci_adapter_groups,
+	.release	= peci_adapter_dev_release,
+};
+EXPORT_SYMBOL_GPL(peci_adapter_type);
+
+/**
+ * peci_verify_adapter - return parameter as peci_adapter, or NULL
+ * @dev: device, probably from some driver model iterator
+ *
+ * Return: pointer to peci_adapter on success, else NULL.
+ */
+struct peci_adapter *peci_verify_adapter(struct device *dev)
+{
+	return (dev->type == &peci_adapter_type)
+			? to_peci_adapter(dev)
+			: NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_adapter);
+
+#if IS_ENABLED(CONFIG_OF)
+static struct peci_client *peci_of_register_device(struct peci_adapter *adapter,
+						   struct device_node *node)
+{
+	struct peci_board_info info = {};
+	struct peci_client *client;
+	u32 addr;
+	int ret;
+
+	dev_dbg(&adapter->dev, "register %pOF\n", node);
+
+	ret = of_property_read_u32(node, "reg", &addr);
+	if (ret) {
+		dev_err(&adapter->dev, "invalid reg on %pOF\n", node);
+		return ERR_PTR(ret);
+	}
+
+	info.addr = addr;
+	info.of_node = node;
+
+	client = peci_new_device(adapter, &info);
+	if (!client)
+		client = ERR_PTR(-EINVAL);
+
+	return client;
+}
+
+static void peci_of_register_devices(struct peci_adapter *adapter)
+{
+	struct device_node *bus, *node;
+	struct peci_client *client;
+
+	/* Only register child devices if the adapter has a node pointer set */
+	if (!adapter->dev.of_node)
+		return;
+
+	bus = of_get_child_by_name(adapter->dev.of_node, "peci-bus");
+	if (!bus)
+		bus = of_node_get(adapter->dev.of_node);
+
+	for_each_available_child_of_node(bus, node) {
+		if (of_node_test_and_set_flag(node, OF_POPULATED))
+			continue;
+
+		client = peci_of_register_device(adapter, node);
+		if (IS_ERR(client)) {
+			dev_warn(&adapter->dev,
+				 "Failed to create PECI device for %pOF\n",
+				 node);
+			of_node_clear_flag(node, OF_POPULATED);
+		}
+	}
+
+	of_node_put(bus);
+}
+#else /* CONFIG_OF */
+static void peci_of_register_devices(struct peci_adapter *adapter) { }
+#endif /* CONFIG_OF */
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static int peci_of_match_node(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+/* must call put_device() when done with returned peci_client device */
+static struct peci_client *peci_of_find_device(struct device_node *node)
+{
+	struct peci_client *client;
+	struct device *dev;
+
+	dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+	if (!dev)
+		return NULL;
+
+	client = peci_verify_client(dev);
+	if (!client)
+		put_device(dev);
+
+	return client;
+}
+
+/* must call put_device() when done with returned peci_adapter device */
+static struct peci_adapter *peci_of_find_adapter(struct device_node *node)
+{
+	struct peci_adapter *adapter;
+	struct device *dev;
+
+	dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+	if (!dev)
+		return NULL;
+
+	adapter = peci_verify_adapter(dev);
+	if (!adapter)
+		put_device(dev);
+
+	return adapter;
+}
+
+static int peci_of_notify(struct notifier_block *nb, ulong action, void *arg)
+{
+	struct of_reconfig_data *rd = arg;
+	struct peci_adapter *adapter;
+	struct peci_client *client;
+
+	switch (of_reconfig_get_state_change(action, rd)) {
+	case OF_RECONFIG_CHANGE_ADD:
+		adapter = peci_of_find_adapter(rd->dn->parent);
+		if (!adapter)
+			return NOTIFY_OK;	/* not for us */
+
+		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
+			put_device(&adapter->dev);
+			return NOTIFY_OK;
+		}
+
+		client = peci_of_register_device(adapter, rd->dn);
+		put_device(&adapter->dev);
+
+		if (IS_ERR(client)) {
+			dev_err(&adapter->dev,
+				"failed to create client for '%pOF'\n", rd->dn);
+			of_node_clear_flag(rd->dn, OF_POPULATED);
+			return notifier_from_errno(PTR_ERR(client));
+		}
+		break;
+	case OF_RECONFIG_CHANGE_REMOVE:
+		/* already depopulated? */
+		if (!of_node_check_flag(rd->dn, OF_POPULATED))
+			return NOTIFY_OK;
+
+		/* find our device by node */
+		client = peci_of_find_device(rd->dn);
+		if (!client)
+			return NOTIFY_OK;	/* no? not meant for us */
+
+		/* unregister takes one ref away */
+		peci_unregister_device(client);
+
+		/* and put the reference of the find */
+		put_device(&client->dev);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block peci_of_notifier = {
+	.notifier_call = peci_of_notify,
+};
+#else /* CONFIG_OF_DYNAMIC */
+extern struct notifier_block peci_of_notifier;
+#endif /* CONFIG_OF_DYNAMIC */
+
+/**
+ * peci_alloc_adapter - allocate a PECI adapter
+ * @dev: the adapter, possibly using the platform_bus
+ * @size: how much zeroed driver-private data to allocate; the pointer to this
+ *	memory is in the driver_data field of the returned device,
+ *	accessible with peci_get_adapdata().
+ * Context: can sleep
+ *
+ * This call is used only by PECI adapter drivers, which are the only ones
+ * directly touching chip registers.  It's how they allocate a peci_adapter
+ * structure, prior to calling peci_add_adapter().
+ *
+ * This must be called from context that can sleep.
+ *
+ * The caller is responsible for initializing the adapter's methods before
+ * calling peci_add_adapter(); and (after errors while adding the device)
+ * calling put_device() to prevent a memory leak.
+ *
+ * Return: the peci_adapter structure on success, else NULL.
+ */
+struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size)
+{
+	struct peci_adapter *adapter;
+
+	if (!dev)
+		return NULL;
+
+	adapter = kzalloc(size + sizeof(*adapter), GFP_KERNEL);
+	if (!adapter)
+		return NULL;
+
+	device_initialize(&adapter->dev);
+	adapter->dev.parent = dev;
+	adapter->dev.bus = &peci_bus_type;
+	adapter->dev.type = &peci_adapter_type;
+	peci_set_adapdata(adapter, &adapter[1]);
+
+	return adapter;
+}
+EXPORT_SYMBOL_GPL(peci_alloc_adapter);
+
+static int peci_register_adapter(struct peci_adapter *adapter)
+{
+	int ret = -EINVAL;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!is_registered))
+		goto err_free_idr;
+
+	if (WARN(!adapter->name[0], "peci adapter has no name"))
+		goto err_free_idr;
+
+	if (WARN(!adapter->xfer, "peci adapter has no xfer function\n"))
+		goto err_free_idr;
+
+	mutex_init(&adapter->bus_lock);
+	mutex_init(&adapter->userspace_clients_lock);
+	INIT_LIST_HEAD(&adapter->userspace_clients);
+
+	dev_set_name(&adapter->dev, "peci-%d", adapter->nr);
+
+	ret = device_add(&adapter->dev);
+	if (ret) {
+		pr_err("adapter '%s': can't add device (%d)\n",
+		       adapter->name, ret);
+		goto err_free_idr;
+	}
+
+	dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name);
+
+	pm_runtime_no_callbacks(&adapter->dev);
+	pm_suspend_ignore_children(&adapter->dev, true);
+	pm_runtime_enable(&adapter->dev);
+
+	/* create pre-declared device nodes */
+	peci_of_register_devices(adapter);
+
+	return 0;
+
+err_free_idr:
+	mutex_lock(&core_lock);
+	idr_remove(&peci_adapter_idr, adapter->nr);
+	mutex_unlock(&core_lock);
+	return ret;
+}
+
+static int peci_add_numbered_adapter(struct peci_adapter *adapter)
+{
+	int id;
+
+	mutex_lock(&core_lock);
+	id = idr_alloc(&peci_adapter_idr, adapter,
+		       adapter->nr, adapter->nr + 1, GFP_KERNEL);
+	mutex_unlock(&core_lock);
+	if (WARN(id < 0, "couldn't get idr"))
+		return id == -ENOSPC ? -EBUSY : id;
+
+	return peci_register_adapter(adapter);
+}
+
+/**
+ * peci_add_adapter - add a PECI adapter
+ * @adapter: initialized adapter, originally from peci_alloc_adapter()
+ * Context: can sleep
+ *
+ * PECI adapters connect to their drivers using some non-PECI bus,
+ * such as the platform bus.  The final stage of probe() in that code
+ * includes calling peci_add_adapter() to hook up to this PECI bus glue.
+ *
+ * This must be called from context that can sleep.
+ *
+ * It returns zero on success, else a negative error code (dropping the
+ * adapter's refcount).  After a successful return, the caller is responsible
+ * for calling peci_del_adapter().
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_add_adapter(struct peci_adapter *adapter)
+{
+	struct device *dev = &adapter->dev;
+	int id;
+
+	id = of_alias_get_id(dev->of_node, "peci");
+	if (id >= 0) {
+		adapter->nr = id;
+		return peci_add_numbered_adapter(adapter);
+	}
+
+	mutex_lock(&core_lock);
+	id = idr_alloc(&peci_adapter_idr, adapter, 0, 0, GFP_KERNEL);
+	mutex_unlock(&core_lock);
+	if (WARN(id < 0, "couldn't get idr"))
+		return id;
+
+	adapter->nr = id;
+
+	return peci_register_adapter(adapter);
+}
+EXPORT_SYMBOL_GPL(peci_add_adapter);
+
+/**
+ * peci_del_adapter - delete a PECI adapter
+ * @adapter: the adpater being deleted
+ * Context: can sleep
+ *
+ * This call is used only by PECI adpater drivers, which are the only ones
+ * directly touching chip registers.
+ *
+ * This must be called from context that can sleep.
+ *
+ * Note that this function also drops a reference to the adapter.
+ */
+void peci_del_adapter(struct peci_adapter *adapter)
+{
+	struct peci_client *client, *next;
+	struct peci_adapter *found;
+	int nr;
+
+	/* First make sure that this adapter was ever added */
+	mutex_lock(&core_lock);
+	found = idr_find(&peci_adapter_idr, adapter->nr);
+	mutex_unlock(&core_lock);
+
+	if (found != adapter)
+		return;
+
+	/* Remove devices instantiated from sysfs */
+	mutex_lock(&adapter->userspace_clients_lock);
+	list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+				 detected) {
+		dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", client->name,
+			client->addr);
+		list_del(&client->detected);
+		peci_unregister_device(client);
+	}
+	mutex_unlock(&adapter->userspace_clients_lock);
+
+	/*
+	 * Detach any active clients. This can't fail, thus we do not
+	 * check the returned value.
+	 */
+	device_for_each_child(&adapter->dev, NULL, peci_unregister_client);
+
+	/* device name is gone after device_unregister */
+	dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name);
+
+	pm_runtime_disable(&adapter->dev);
+	nr = adapter->nr;
+	device_unregister(&adapter->dev);
+
+	/* free bus id */
+	mutex_lock(&core_lock);
+	idr_remove(&peci_adapter_idr, nr);
+	mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(peci_del_adapter);
+
+int peci_for_each_dev(void *data, int (*fn)(struct device *, void *))
+{
+	int ret;
+
+	mutex_lock(&core_lock);
+	ret = bus_for_each_dev(&peci_bus_type, NULL, data, fn);
+	mutex_unlock(&core_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(peci_for_each_dev);
+
+/**
+ * peci_register_driver - register a PECI driver
+ * @owner: owner module of the driver being registered
+ * @driver: the driver being registered
+ * Context: can sleep
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int peci_register_driver(struct module *owner, struct peci_driver *driver)
+{
+	int ret;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!is_registered))
+		return -EAGAIN;
+
+	/* add the driver to the list of peci drivers in the driver core */
+	driver->driver.owner = owner;
+	driver->driver.bus = &peci_bus_type;
+
+	/*
+	 * When registration returns, the driver core
+	 * will have called probe() for all matching-but-unbound devices.
+	 */
+	ret = driver_register(&driver->driver);
+	if (ret)
+		return ret;
+
+	pr_debug("driver [%s] registered\n", driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(peci_register_driver);
+
+/**
+ * peci_del_driver - unregister a PECI driver
+ * @driver: the driver being unregistered
+ * Context: can sleep
+ */
+void peci_del_driver(struct peci_driver *driver)
+{
+	driver_unregister(&driver->driver);
+	pr_debug("driver [%s] unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL_GPL(peci_del_driver);
+
+static int __init peci_init(void)
+{
+	int ret;
+
+	ret = bus_register(&peci_bus_type);
+	if (ret < 0) {
+		pr_err("peci: Failed to register PECI bus type!\n");
+		return ret;
+	}
+
+	crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL);
+
+	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+		WARN_ON(of_reconfig_notifier_register(&peci_of_notifier));
+
+	is_registered = true;
+
+	return 0;
+}
+
+static void __exit peci_exit(void)
+{
+	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+		WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier));
+
+	bus_unregister(&peci_bus_type);
+}
+
+subsys_initcall(peci_init);
+module_exit(peci_exit);
+
+MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI bus core module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c
new file mode 100644
index 000000000000..f840cd1d4901
--- /dev/null
+++ b/drivers/peci/peci-dev.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/peci.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/*
+ * A peci_dev represents an peci_adapter ... an PECI or SMBus master, not a
+ * slave (peci_client) with which messages will be exchanged.  It's coupled
+ * with a character special file which is accessed by user mode drivers.
+ *
+ * The list of peci_dev structures is parallel to the peci_adapter lists
+ * maintained by the driver model, and is updated using bus notifications.
+ */
+struct peci_dev {
+	struct list_head	list;
+	struct peci_adapter	*adapter;
+	struct device		*dev;
+	struct cdev		cdev;
+};
+
+#define PECI_MINORS  MINORMASK
+
+static dev_t peci_devt;
+static LIST_HEAD(peci_dev_list);
+static DEFINE_SPINLOCK(peci_dev_list_lock);
+
+static struct peci_dev *peci_dev_get_by_minor(uint index)
+{
+	struct peci_dev *peci_dev;
+
+	spin_lock(&peci_dev_list_lock);
+	list_for_each_entry(peci_dev, &peci_dev_list, list) {
+		if (peci_dev->adapter->nr == index)
+			goto found;
+	}
+	peci_dev = NULL;
+found:
+	spin_unlock(&peci_dev_list_lock);
+
+	return peci_dev;
+}
+
+static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter)
+{
+	struct peci_dev *peci_dev;
+
+	if (adapter->nr >= PECI_MINORS) {
+		dev_err(&adapter->dev, "Out of device minors (%d)\n",
+			adapter->nr);
+		return ERR_PTR(-ENODEV);
+	}
+
+	peci_dev = kzalloc(sizeof(*peci_dev), GFP_KERNEL);
+	if (!peci_dev)
+		return ERR_PTR(-ENOMEM);
+	peci_dev->adapter = adapter;
+
+	spin_lock(&peci_dev_list_lock);
+	list_add_tail(&peci_dev->list, &peci_dev_list);
+	spin_unlock(&peci_dev_list_lock);
+
+	return peci_dev;
+}
+
+static void peci_dev_put(struct peci_dev *peci_dev)
+{
+	spin_lock(&peci_dev_list_lock);
+	list_del(&peci_dev->list);
+	spin_unlock(&peci_dev_list_lock);
+	kfree(peci_dev);
+}
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt));
+
+	if (!peci_dev)
+		return -ENODEV;
+
+	return sprintf(buf, "%s\n", peci_dev->adapter->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *peci_dev_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(peci_dev);
+
+static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg)
+{
+	struct peci_dev *peci_dev = file->private_data;
+	void __user *umsg = (void __user *)arg;
+	struct peci_xfer_msg *xmsg = NULL;
+	struct peci_xfer_msg uxmsg;
+	enum peci_cmd cmd;
+	u8 *msg = NULL;
+	uint msg_len;
+	int ret;
+
+	cmd = _IOC_NR(iocmd);
+	msg_len = _IOC_SIZE(iocmd);
+
+	switch (cmd) {
+	case PECI_CMD_XFER:
+		if (msg_len != sizeof(struct peci_xfer_msg)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		if (copy_from_user(&uxmsg, umsg, msg_len)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		xmsg = peci_get_xfer_msg(uxmsg.tx_len, uxmsg.rx_len);
+		if (IS_ERR(xmsg)) {
+			ret = PTR_ERR(xmsg);
+			break;
+		}
+
+		if (uxmsg.tx_len &&
+		    copy_from_user(xmsg->tx_buf, (__u8 __user *)uxmsg.tx_buf,
+				   uxmsg.tx_len)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		xmsg->addr = uxmsg.addr;
+		xmsg->tx_len = uxmsg.tx_len;
+		xmsg->rx_len = uxmsg.rx_len;
+
+		ret = peci_command(peci_dev->adapter, cmd, xmsg);
+		if (!ret && xmsg->rx_len &&
+		    copy_to_user((__u8 __user *)uxmsg.rx_buf, xmsg->rx_buf,
+				 xmsg->rx_len))
+			ret = -EFAULT;
+
+		break;
+
+	default:
+		msg = memdup_user(umsg, msg_len);
+		if (IS_ERR(msg)) {
+			ret = PTR_ERR(msg);
+			break;
+		}
+
+		ret = peci_command(peci_dev->adapter, cmd, msg);
+		if ((!ret || ret == -ETIMEDOUT) &&
+		    copy_to_user(umsg, msg, msg_len))
+			ret = -EFAULT;
+
+		break;
+	}
+
+	peci_put_xfer_msg(xmsg);
+	kfree(msg);
+
+	return (long)ret;
+}
+
+static int peci_dev_open(struct inode *inode, struct file *file)
+{
+	struct peci_adapter *adapter;
+	struct peci_dev *peci_dev;
+
+	peci_dev = peci_dev_get_by_minor(iminor(inode));
+	if (!peci_dev)
+		return -ENODEV;
+
+	adapter = peci_get_adapter(peci_dev->adapter->nr);
+	if (!adapter)
+		return -ENODEV;
+
+	file->private_data = peci_dev;
+
+	return 0;
+}
+
+static int peci_dev_release(struct inode *inode, struct file *file)
+{
+	struct peci_dev *peci_dev = file->private_data;
+
+	peci_put_adapter(peci_dev->adapter);
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static const struct file_operations peci_dev_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= peci_dev_ioctl,
+	.open		= peci_dev_open,
+	.release	= peci_dev_release,
+	.llseek		= no_llseek,
+};
+
+static struct class *peci_dev_class;
+
+static int peci_dev_attach_adapter(struct device *dev, void *dummy)
+{
+	struct peci_adapter *adapter;
+	struct peci_dev *peci_dev;
+	dev_t devt;
+	int ret;
+
+	if (dev->type != &peci_adapter_type)
+		return 0;
+
+	adapter = to_peci_adapter(dev);
+	peci_dev = peci_dev_alloc(adapter);
+	if (IS_ERR(peci_dev))
+		return PTR_ERR(peci_dev);
+
+	cdev_init(&peci_dev->cdev, &peci_dev_fops);
+	peci_dev->cdev.owner = THIS_MODULE;
+	devt = MKDEV(MAJOR(peci_devt), adapter->nr);
+
+	ret = cdev_add(&peci_dev->cdev, devt, 1);
+	if (ret)
+		goto err_put_dev;
+
+	/* register this peci device with the driver core */
+	peci_dev->dev = device_create(peci_dev_class, &adapter->dev, devt, NULL,
+				      "peci-%d", adapter->nr);
+	if (IS_ERR(peci_dev->dev)) {
+		ret = PTR_ERR(peci_dev->dev);
+		goto err_del_cdev;
+	}
+
+	dev_info(dev, "cdev of adapter [%s] registered as minor %d\n",
+		 adapter->name, adapter->nr);
+
+	return 0;
+
+err_del_cdev:
+	cdev_del(&peci_dev->cdev);
+err_put_dev:
+	peci_dev_put(peci_dev);
+
+	return ret;
+}
+
+static int peci_dev_detach_adapter(struct device *dev, void *dummy)
+{
+	struct peci_adapter *adapter;
+	struct peci_dev *peci_dev;
+	dev_t devt;
+
+	if (dev->type != &peci_adapter_type)
+		return 0;
+
+	adapter = to_peci_adapter(dev);
+	peci_dev = peci_dev_get_by_minor(adapter->nr);
+	if (!peci_dev)
+		return 0;
+
+	cdev_del(&peci_dev->cdev);
+	devt = peci_dev->dev->devt;
+	peci_dev_put(peci_dev);
+	device_destroy(peci_dev_class, devt);
+
+	dev_info(dev, "cdev of adapter [%s] unregistered\n", adapter->name);
+
+	return 0;
+}
+
+static int peci_dev_notifier_call(struct notifier_block *nb, ulong action,
+				  void *data)
+{
+	struct device *dev = data;
+
+	switch (action) {
+	case BUS_NOTIFY_ADD_DEVICE:
+		return peci_dev_attach_adapter(dev, NULL);
+	case BUS_NOTIFY_DEL_DEVICE:
+		return peci_dev_detach_adapter(dev, NULL);
+	}
+
+	return 0;
+}
+
+static struct notifier_block peci_dev_notifier = {
+	.notifier_call = peci_dev_notifier_call,
+};
+
+static int __init peci_dev_init(void)
+{
+	int ret;
+
+	pr_debug("peci /dev entries driver\n");
+
+	ret = alloc_chrdev_region(&peci_devt, 0, PECI_MINORS, "peci");
+	if (ret < 0) {
+		pr_err("peci: Failed to allocate chr dev region!\n");
+		bus_unregister(&peci_bus_type);
+		goto err;
+	}
+
+	peci_dev_class = class_create(THIS_MODULE, KBUILD_MODNAME);
+	if (IS_ERR(peci_dev_class)) {
+		ret = PTR_ERR(peci_dev_class);
+		goto err_unreg_chrdev;
+	}
+	peci_dev_class->dev_groups = peci_dev_groups;
+
+	/* Keep track of adapters which will be added or removed later */
+	ret = bus_register_notifier(&peci_bus_type, &peci_dev_notifier);
+	if (ret)
+		goto err_destroy_class;
+
+	/* Bind to already existing adapters right away */
+	peci_for_each_dev(NULL, peci_dev_attach_adapter);
+
+	return 0;
+
+err_destroy_class:
+	class_destroy(peci_dev_class);
+err_unreg_chrdev:
+	unregister_chrdev_region(peci_devt, PECI_MINORS);
+err:
+	pr_err("%s: Driver Initialization failed\n", __FILE__);
+
+	return ret;
+}
+
+static void __exit peci_dev_exit(void)
+{
+	bus_unregister_notifier(&peci_bus_type, &peci_dev_notifier);
+	peci_for_each_dev(NULL, peci_dev_detach_adapter);
+	class_destroy(peci_dev_class);
+	unregister_chrdev_region(peci_devt, PECI_MINORS);
+}
+
+module_init(peci_dev_init);
+module_exit(peci_dev_exit);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI /dev entries driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/peci.h b/include/linux/peci.h
new file mode 100644
index 000000000000..4bc4595c797d
--- /dev/null
+++ b/include/linux/peci.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __LINUX_PECI_H
+#define __LINUX_PECI_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/peci-ioctl.h>
+
+#define PECI_NAME_SIZE   32
+
+struct peci_board_info {
+	char			type[PECI_NAME_SIZE];
+	u8			addr;	/* CPU client address */
+	struct device_node	*of_node;
+};
+
+/**
+ * struct peci_adapter - represent a PECI adapter
+ * @owner: owner module of the PECI adpater
+ * @bus_lock: mutex for exclusion of multiple callers
+ * @dev: device interface to this driver
+ * @nr: the bus number to map
+ * @name: name of the adapter
+ * @userspace_clients_lock: mutex for exclusion of clients handling
+ * @userspace_clients: list of registered clients
+ * @xfer: low-level transfer function pointer of the adapter
+ * @cmd_mask: mask for supportable PECI commands
+ * @use_dma: flag for indicating that adapter uses DMA
+ *
+ * Each PECI adapter can communicate with one or more PECI client children.
+ * These make a small bus, sharing a single wired PECI connection.
+ */
+struct peci_adapter {
+	struct module		*owner;
+	struct mutex		bus_lock; /* mutex for bus locking */
+	struct device		dev;
+	int			nr;
+	char			name[PECI_NAME_SIZE];
+	struct mutex		userspace_clients_lock; /* clients list mutex */
+	struct list_head	userspace_clients;
+	int			(*xfer)(struct peci_adapter *adapter,
+					struct peci_xfer_msg *msg);
+	u32			cmd_mask;
+	bool			use_dma;
+};
+
+static inline struct peci_adapter *to_peci_adapter(void *d)
+{
+	return container_of(d, struct peci_adapter, dev);
+}
+
+static inline void *peci_get_adapdata(const struct peci_adapter *adapter)
+{
+	return dev_get_drvdata(&adapter->dev);
+}
+
+static inline void peci_set_adapdata(struct peci_adapter *adapter, void *data)
+{
+	dev_set_drvdata(&adapter->dev, data);
+}
+
+/**
+ * struct peci_client - represent a PECI client device
+ * @dev: driver model device node for the client
+ * @adapter: manages the bus segment hosting this PECI device
+ * @addr: address used on the PECI bus connected to the parent adapter
+ * @name: indicates the type of the device
+ * @detected: detected PECI clients list
+ *
+ * A peci_client identifies a single device (i.e. CPU) connected to a peci bus.
+ * The behaviour exposed to Linux is defined by the driver managing the device.
+ */
+struct peci_client {
+	struct device		dev;
+	struct peci_adapter	*adapter;
+	u8			addr;
+	char			name[PECI_NAME_SIZE];
+	struct list_head	detected;
+};
+
+static inline struct peci_client *to_peci_client(void *d)
+{
+	return container_of(d, struct peci_client, dev);
+}
+
+struct peci_device_id {
+	char	name[PECI_NAME_SIZE];
+	ulong	driver_data;	/* Data private to the driver */
+};
+
+/**
+ * struct peci_driver - represent a PECI device driver
+ * @probe: callback for device binding
+ * @remove: callback for device unbinding
+ * @shutdown: callback for device shutdown
+ * @driver: device driver model driver
+ * @id_table: list of PECI devices supported by this driver
+ *
+ * The driver.owner field should be set to the module owner of this driver.
+ * The driver.name field should be set to the name of this driver.
+ */
+struct peci_driver {
+	int				(*probe)(struct peci_client *client);
+	int				(*remove)(struct peci_client *client);
+	void				(*shutdown)(struct peci_client *client);
+	struct device_driver		driver;
+	const struct peci_device_id	*id_table;
+};
+
+static inline struct peci_driver *to_peci_driver(void *d)
+{
+	return container_of(d, struct peci_driver, driver);
+}
+
+/**
+ * module_peci_driver - Helper macro for registering a modular PECI driver
+ * @__peci_driver: peci_driver struct
+ *
+ * Helper macro for PECI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_peci_driver(__peci_driver) \
+	module_driver(__peci_driver, peci_add_driver, peci_del_driver)
+
+/* use a define to avoid include chaining to get THIS_MODULE */
+#define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver)
+
+extern struct bus_type peci_bus_type;
+extern struct device_type peci_adapter_type;
+extern struct device_type peci_client_type;
+
+int  peci_register_driver(struct module *owner, struct peci_driver *drv);
+void peci_del_driver(struct peci_driver *driver);
+struct peci_client *peci_verify_client(struct device *dev);
+struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size);
+struct peci_adapter *peci_get_adapter(int nr);
+void peci_put_adapter(struct peci_adapter *adapter);
+int  peci_add_adapter(struct peci_adapter *adapter);
+void peci_del_adapter(struct peci_adapter *adapter);
+struct peci_adapter *peci_verify_adapter(struct device *dev);
+int  peci_for_each_dev(void *data, int (*fn)(struct device *, void *));
+struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len);
+void peci_put_xfer_msg(struct peci_xfer_msg *msg);
+int  peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg);
+int  peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id);
+
+#endif /* __LINUX_PECI_H */
diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
new file mode 100644
index 000000000000..5672690297b6
--- /dev/null
+++ b/include/uapi/linux/peci-ioctl.h
@@ -0,0 +1,660 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __PECI_IOCTL_H
+#define __PECI_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* The PECI client's default address of 0x30 */
+#define PECI_BASE_ADDR					0x30
+
+/* Max number of CPU clients */
+#define PECI_OFFSET_MAX					8
+
+/* PECI read/write data buffer size max */
+#define PECI_BUFFER_SIZE				255
+
+/* Device Specific Completion Code (CC) Definition */
+#define PECI_DEV_CC_SUCCESS				0x40
+#define PECI_DEV_CC_NEED_RETRY				0x80
+#define PECI_DEV_CC_OUT_OF_RESOURCE			0x81
+#define PECI_DEV_CC_UNAVAIL_RESOURCE			0x82
+#define PECI_DEV_CC_INVALID_REQ				0x90
+#define PECI_DEV_CC_MCA_ERROR				0x91
+#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR		0x93
+#define PECI_DEV_CC_FATAL_MCA_DETECTED			0x94
+#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB	0x98
+#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR	0x9B
+#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA	0x9C
+
+/* Completion Code mask to check retry needs */
+#define PECI_DEV_CC_RETRY_CHECK_MASK			0xf0
+
+#define PECI_DEV_RETRY_TIME_MS				700
+#define PECI_DEV_RETRY_INTERVAL_USEC			10000
+#define PECI_DEV_RETRY_BIT				0x01
+
+/**
+ * enum peci_cmd - PECI client commands
+ * @PECI_CMD_XFER: raw PECI transfer
+ * @PECI_CMD_PING: ping, a required message for all PECI devices
+ * @PECI_CMD_GET_DIB: get DIB (Device Info Byte)
+ * @PECI_CMD_GET_TEMP: get maximum die temperature
+ * @PECI_CMD_RD_PKG_CFG: read access to the PCS (Package Configuration Space)
+ * @PECI_CMD_WR_PKG_CFG: write access to the PCS (Package Configuration Space)
+ * @PECI_CMD_RD_IA_MSR: read access to MSRs (Model Specific Registers)
+ * @PECI_CMD_WR_IA_MSR: write access to MSRs (Model Specific Registers)
+ * @PECI_CMD_RD_IA_MSREX: read access to MSRs (Model Specific Registers)
+ * @PECI_CMD_RD_PCI_CFG: sideband read access to the PCI configuration space
+ *	maintained in downstream devices external to the processor
+ * @PECI_CMD_WR_PCI_CFG: sideband write access to the PCI configuration space
+ *	maintained in downstream devices external to the processor
+ * @PECI_CMD_RD_PCI_CFG_LOCAL: sideband read access to the PCI configuration
+ *	space that resides within the processor
+ * @PECI_CMD_WR_PCI_CFG_LOCAL: sideband write access to the PCI configuration
+ *	space that resides within the processor
+ *
+ * Available commands depend on client's PECI revision.
+ */
+enum peci_cmd {
+	PECI_CMD_XFER = 0,
+	PECI_CMD_PING,
+	PECI_CMD_GET_DIB,
+	PECI_CMD_GET_TEMP,
+	PECI_CMD_RD_PKG_CFG,
+	PECI_CMD_WR_PKG_CFG,
+	PECI_CMD_RD_IA_MSR,
+	PECI_CMD_WR_IA_MSR,
+	PECI_CMD_RD_IA_MSREX,
+	PECI_CMD_RD_PCI_CFG,
+	PECI_CMD_WR_PCI_CFG,
+	PECI_CMD_RD_PCI_CFG_LOCAL,
+	PECI_CMD_WR_PCI_CFG_LOCAL,
+	PECI_CMD_RD_END_PT_CFG,
+	PECI_CMD_WR_END_PT_CFG,
+	PECI_CMD_CRASHDUMP_DISC,
+	PECI_CMD_CRASHDUMP_GET_FRAME,
+	PECI_CMD_MAX
+};
+
+/**
+ * struct peci_xfer_msg - raw PECI transfer command
+ * @addr; address of the client
+ * @tx_len: number of data to be written in bytes
+ * @rx_len: number of data to be read in bytes
+ * @tx_buf: data to be written, or NULL
+ * @rx_buf: data to be read, or NULL
+ *
+ * raw PECI transfer
+ */
+struct peci_xfer_msg {
+	__u8	addr;
+	__u8	tx_len;
+	__u8	rx_len;
+	__u8	padding;
+	__u8	*tx_buf;
+	__u8	*rx_buf;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_ping_msg - ping command
+ * @addr: address of the client
+ *
+ * Ping() is a required message for all PECI devices. This message is used to
+ * enumerate devices or determine if a device has been removed, been
+ * powered-off, etc.
+ */
+struct peci_ping_msg {
+	__u8	addr;
+	__u8	padding[3];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_get_dib_msg - GetDIB command
+ * @addr: address of the client
+ * @dib: DIB data to be read
+ *
+ * The processor PECI client implementation of GetDIB() includes an 8-byte
+ * response and provides information regarding client revision number and the
+ * number of supported domains. All processor PECI clients support the GetDIB()
+ * command.
+ */
+struct peci_get_dib_msg {
+#define PECI_GET_DIB_WR_LEN	1
+#define PECI_GET_DIB_RD_LEN	8
+#define PECI_GET_DIB_CMD	0xf7
+
+	__u8	addr;
+	__u8	padding[3];
+	__u64	dib;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_get_temp_msg - GetTemp command
+ * @addr: address of the client
+ * @temp_raw: raw temperature data to be read
+ *
+ * The GetTemp() command is used to retrieve the maximum die temperature from a
+ * target PECI address. The temperature is used by the external thermal
+ * management system to regulate the temperature on the die. The data is
+ * returned as a negative value representing the number of degrees centigrade
+ * below the maximum processor junction temperature.
+ */
+struct peci_get_temp_msg {
+#define PECI_GET_TEMP_WR_LEN	1
+#define PECI_GET_TEMP_RD_LEN	2
+#define PECI_GET_TEMP_CMD	0x01
+
+	__u8	addr;
+	__u8	padding;
+	__s16	temp_raw;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pkg_cfg_msg - RdPkgConfig command
+ * @addr: address of the client
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @rx_len: number of data to be read in bytes
+ * @cc: completion code
+ * @pkg_config: package config data to be read
+ *
+ * The RdPkgConfig() command provides read access to the Package Configuration
+ * Space (PCS) within the processor, including various power and thermal
+ * management functions. Typical PCS read services supported by the processor
+ * may include access to temperature data, energy status, run time information,
+ * DIMM temperatures and so on.
+ */
+struct peci_rd_pkg_cfg_msg {
+#define PECI_RDPKGCFG_WRITE_LEN			5
+#define PECI_RDPKGCFG_READ_LEN_BASE		1
+#define PECI_RDPKGCFG_CMD			0xa1
+
+	__u8	addr;
+	__u8	index;
+#define PECI_MBX_INDEX_CPU_ID			0  /* Package Identifier Read */
+#define PECI_MBX_INDEX_VR_DEBUG			1  /* VR Debug */
+#define PECI_MBX_INDEX_PKG_TEMP_READ		2  /* Package Temperature Read */
+#define PECI_MBX_INDEX_ENERGY_COUNTER		3  /* Energy counter */
+#define PECI_MBX_INDEX_ENERGY_STATUS		4  /* DDR Energy Status */
+#define PECI_MBX_INDEX_WAKE_MODE_BIT		5  /* "Wake on PECI" Mode bit */
+#define PECI_MBX_INDEX_EPI			6  /* Efficient Performance Indication */
+#define PECI_MBX_INDEX_PKG_RAPL_PERF		8  /* Pkg RAPL Performance Status Read */
+#define PECI_MBX_INDEX_PER_CORE_DTS_TEMP	9  /* Per Core DTS Temperature Read */
+#define PECI_MBX_INDEX_DTS_MARGIN		10 /* DTS thermal margin */
+#define PECI_MBX_INDEX_SKT_PWR_THRTL_DUR	11 /* Socket Power Throttled Duration */
+#define PECI_MBX_INDEX_CFG_TDP_CONTROL		12 /* TDP Config Control */
+#define PECI_MBX_INDEX_CFG_TDP_LEVELS		13 /* TDP Config Levels */
+#define PECI_MBX_INDEX_DDR_DIMM_TEMP		14 /* DDR DIMM Temperature */
+#define PECI_MBX_INDEX_CFG_ICCMAX		15 /* Configurable ICCMAX */
+#define PECI_MBX_INDEX_TEMP_TARGET		16 /* Temperature Target Read */
+#define PECI_MBX_INDEX_CURR_CFG_LIMIT		17 /* Current Config Limit */
+#define PECI_MBX_INDEX_DIMM_TEMP_READ		20 /* Package Thermal Status Read */
+#define PECI_MBX_INDEX_DRAM_IMC_TMP_READ	22 /* DRAM IMC Temperature Read */
+#define PECI_MBX_INDEX_DDR_CH_THERM_STAT	23 /* DDR Channel Thermal Status */
+#define PECI_MBX_INDEX_PKG_POWER_LIMIT1		26 /* Package Power Limit1 */
+#define PECI_MBX_INDEX_PKG_POWER_LIMIT2		27 /* Package Power Limit2 */
+#define PECI_MBX_INDEX_TDP			28 /* Thermal design power minimum */
+#define PECI_MBX_INDEX_TDP_HIGH			29 /* Thermal design power maximum */
+#define PECI_MBX_INDEX_TDP_UNITS		30 /* Units for power/energy registers */
+#define PECI_MBX_INDEX_RUN_TIME			31 /* Accumulated Run Time */
+#define PECI_MBX_INDEX_CONSTRAINED_TIME		32 /* Thermally Constrained Time Read */
+#define PECI_MBX_INDEX_TURBO_RATIO		33 /* Turbo Activation Ratio */
+#define PECI_MBX_INDEX_DDR_RAPL_PL1		34 /* DDR RAPL PL1 */
+#define PECI_MBX_INDEX_DDR_PWR_INFO_HIGH	35 /* DRAM Power Info Read (high) */
+#define PECI_MBX_INDEX_DDR_PWR_INFO_LOW		36 /* DRAM Power Info Read (low) */
+#define PECI_MBX_INDEX_DDR_RAPL_PL2		37 /* DDR RAPL PL2 */
+#define PECI_MBX_INDEX_DDR_RAPL_STATUS		38 /* DDR RAPL Performance Status */
+#define PECI_MBX_INDEX_DDR_HOT_ABSOLUTE		43 /* DDR Hottest Dimm Absolute Temp */
+#define PECI_MBX_INDEX_DDR_HOT_RELATIVE		44 /* DDR Hottest Dimm Relative Temp */
+#define PECI_MBX_INDEX_DDR_THROTTLE_TIME	45 /* DDR Throttle Time */
+#define PECI_MBX_INDEX_DDR_THERM_STATUS		46 /* DDR Thermal Status */
+#define PECI_MBX_INDEX_TIME_AVG_TEMP		47 /* Package time-averaged temperature */
+#define PECI_MBX_INDEX_TURBO_RATIO_LIMIT	49 /* Turbo Ratio Limit Read */
+#define PECI_MBX_INDEX_HWP_AUTO_OOB		53 /* HWP Autonomous Out-of-band */
+#define PECI_MBX_INDEX_DDR_WARM_BUDGET		55 /* DDR Warm Power Budget */
+#define PECI_MBX_INDEX_DDR_HOT_BUDGET		56 /* DDR Hot Power Budget */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM3	57 /* Package/Psys Power Limit3 */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM1	58 /* Package/Psys Power Limit1 */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM2	59 /* Package/Psys Power Limit2 */
+#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM4	60 /* Package/Psys Power Limit4 */
+#define PECI_MBX_INDEX_PERF_LIMIT_REASON	65 /* Performance Limit Reasons */
+
+	__u16	param;
+/* When index is PECI_MBX_INDEX_CPU_ID */
+#define PECI_PKG_ID_CPU_ID			0x0000  /* CPUID Info */
+#define PECI_PKG_ID_PLATFORM_ID			0x0001  /* Platform ID */
+#define PECI_PKG_ID_UNCORE_ID			0x0002  /* Uncore Device ID */
+#define PECI_PKG_ID_MAX_THREAD_ID		0x0003  /* Max Thread ID */
+#define PECI_PKG_ID_MICROCODE_REV		0x0004  /* CPU Microcode Update Revision */
+#define PECI_PKG_ID_MACHINE_CHECK_STATUS	0x0005  /* Machine Check Status */
+
+	__u8	rx_len;
+	__u8	cc;
+	__u8	padding[2];
+	__u8	pkg_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pkg_cfg_msg - WrPkgConfig command
+ * @addr: address of the client
+ * @index: encoding index for the requested service
+ * @param: specific data being requested
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @value: package config data to be written
+ *
+ * The WrPkgConfig() command provides write access to the Package Configuration
+ * Space (PCS) within the processor, including various power and thermal
+ * management functions. Typical PCS write services supported by the processor
+ * may include power limiting, thermal averaging constant programming and so
+ * on.
+ */
+struct peci_wr_pkg_cfg_msg {
+#define PECI_WRPKGCFG_WRITE_LEN_BASE	6
+#define PECI_WRPKGCFG_READ_LEN		1
+#define PECI_WRPKGCFG_CMD		0xa5
+
+	__u8	addr;
+	__u8	index;
+#define PECI_MBX_INDEX_DIMM_AMBIENT	19
+#define PECI_MBX_INDEX_DIMM_TEMP	24
+
+	__u16	param;
+	__u8	tx_len;
+	__u8	cc;
+	__u8	padding[2];
+	__u32	value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_ia_msr_msg - RdIAMSR command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
+ * @cc: completion code
+ * @value: data to be read
+ *
+ * The RdIAMSR() PECI command provides read access to Model Specific Registers
+ * (MSRs) defined in the processor's Intel Architecture (IA).
+ */
+struct peci_rd_ia_msr_msg {
+#define PECI_RDIAMSR_WRITE_LEN	5
+#define PECI_RDIAMSR_READ_LEN	9
+#define PECI_RDIAMSR_CMD	0xb1
+
+	__u8	addr;
+	__u8	thread_id;
+	__u16	address;
+	__u8	cc;
+	__u8	padding[3];
+	__u64	value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_ia_msr_msg - WrIAMSR command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to write to
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @value: data to be written
+ *
+ * The WrIAMSR() PECI command provides write access to Model Specific Registers
+ * (MSRs) defined in the processor's Intel Architecture (IA).
+ */
+struct peci_wr_ia_msr_msg {
+#define PECI_WRIAMSR_CMD		0xb5
+
+	__u8	addr;
+	__u8	thread_id;
+	__u16	address;
+	__u8	tx_len;
+	__u8	cc;
+	__u8	padding[2];
+	__u64	value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_ia_msrex_msg - RdIAMSREX command
+ * @addr: address of the client
+ * @thread_id: ID of the specific logical processor
+ * @address: address of MSR to read from
+ * @cc: completion code
+ * @value: data to be read
+ *
+ * The RdIAMSREX() PECI command provides read access to Model Specific
+ * Registers (MSRs) defined in the processor's Intel Architecture (IA).
+ * The differences between RdIAMSREX() and RdIAMSR() are that:
+ * (1)RdIAMSR() can only read MC registers, RdIAMSREX() can read all MSRs
+ * (2)thread_id of RdIAMSR() is u8, thread_id of RdIAMSREX() is u16
+ */
+struct peci_rd_ia_msrex_msg {
+#define PECI_RDIAMSREX_WRITE_LEN	6
+#define PECI_RDIAMSREX_READ_LEN	9
+#define PECI_RDIAMSREX_CMD	0xd1
+
+	__u8	addr;
+	__u8	padding0;
+	__u16	thread_id;
+	__u16	address;
+	__u8	cc;
+	__u8	padding1;
+	__u64	value;
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pci_cfg_msg - RdPCIConfig command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfig() command provides sideband read access to the PCI
+ * configuration space maintained in downstream devices external to the
+ * processor.
+ */
+struct peci_rd_pci_cfg_msg {
+#define PECI_RDPCICFG_WRITE_LEN		6
+#define PECI_RDPCICFG_READ_LEN		5
+#define PECI_RDPCICFG_READ_LEN_MAX	24
+#define PECI_RDPCICFG_CMD		0x61
+
+	__u8	addr;
+	__u8	bus;
+#define PECI_PCI_BUS0_CPU0		0x00
+#define PECI_PCI_BUS0_CPU1		0x80
+#define PECI_PCI_CPUBUSNO_BUS		0x00
+#define PECI_PCI_CPUBUSNO_DEV		0x08
+#define PECI_PCI_CPUBUSNO_FUNC		0x02
+#define PECI_PCI_CPUBUSNO		0xcc
+#define PECI_PCI_CPUBUSNO_1		0xd0
+#define PECI_PCI_CPUBUSNO_VALID		0xd4
+
+	__u8	device;
+	__u8	function;
+	__u16	reg;
+	__u8	cc;
+	__u8	padding[1];
+	__u8	pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pci_cfg_msg - WrPCIConfig command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to write to
+ * @reg: specific register to write to
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @pci_config: config data to be written
+ *
+ * The RdPCIConfig() command provides sideband write access to the PCI
+ * configuration space maintained in downstream devices external to the
+ * processor.
+ */
+struct peci_wr_pci_cfg_msg {
+#define PECI_WRPCICFG_CMD		0x65
+
+	__u8	addr;
+	__u8	bus;
+	__u8	device;
+	__u8	function;
+	__u16	reg;
+	__u8	tx_len;
+	__u8	cc;
+	__u8	pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_rd_pci_cfg_local_msg - RdPCIConfigLocal command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @rx_len: number of data to be read in bytes
+ * @cc: completion code
+ * @pci_config: config data to be read
+ *
+ * The RdPCIConfigLocal() command provides sideband read access to the PCI
+ * configuration space that resides within the processor. This includes all
+ * processor IIO and uncore registers within the PCI configuration space.
+ */
+struct peci_rd_pci_cfg_local_msg {
+#define PECI_RDPCICFGLOCAL_WRITE_LEN		5
+#define PECI_RDPCICFGLOCAL_READ_LEN_BASE	1
+#define PECI_RDPCICFGLOCAL_CMD			0xe1
+
+	__u8	addr;
+	__u8	bus;
+	__u8	device;
+	__u8	function;
+	__u16	reg;
+	__u8	rx_len;
+	__u8	cc;
+	__u8	pci_config[4];
+} __attribute__((__packed__));
+
+/**
+ * struct peci_wr_pci_cfg_local_msg - WrPCIConfigLocal command
+ * @addr: address of the client
+ * @bus: PCI bus number
+ * @device: PCI device number
+ * @function: specific function to read from
+ * @reg: specific register to read from
+ * @tx_len: number of data to be written in bytes
+ * @cc: completion code
+ * @value: config data to be written
+ *
+ * The WrPCIConfigLocal() command provides sideband write access to the PCI
+ * configuration space that resides within the processor. PECI originators can
+ * access this space even before BIOS enumeration of the system buses.
+ */
+struct peci_wr_pci_cfg_local_msg {
+#define PECI_WRPCICFGLOCAL_WRITE_LEN_BASE	6
+#define PECI_WRPCICFGLOCAL_READ_LEN		1
+#define PECI_WRPCICFGLOCAL_CMD			0xe5
+
+	__u8	addr;
+	__u8	bus;
+	__u8	device;
+	__u8	function;
+	__u16	reg;
+	__u8	tx_len;
+	__u8	cc;
+	__u32	value;
+} __attribute__((__packed__));
+
+struct peci_rd_end_pt_cfg_msg {
+#define PECI_RDENDPTCFG_PCI_WRITE_LEN		12
+#define PECI_RDENDPTCFG_MMIO_D_WRITE_LEN	14
+#define PECI_RDENDPTCFG_MMIO_Q_WRITE_LEN	18
+#define PECI_RDENDPTCFG_READ_LEN_BASE		1
+#define PECI_RDENDPTCFG_CMD			0xc1
+
+	__u8	addr;
+	__u8	msg_type;
+#define PECI_ENDPTCFG_TYPE_LOCAL_PCI		0x03
+#define PECI_ENDPTCFG_TYPE_PCI			0x04
+#define PECI_ENDPTCFG_TYPE_MMIO			0x05
+
+	union {
+		struct {
+			__u8	seg;
+			__u8	bus;
+			__u8	device;
+			__u8	function;
+			__u16	reg;
+		} pci_cfg;
+		struct {
+			__u8	seg;
+			__u8	bus;
+			__u8	device;
+			__u8	function;
+			__u8	bar;
+			__u8	addr_type;
+#define PECI_ENDPTCFG_ADDR_TYPE_PCI		0x04
+#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_D		0x05
+#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q		0x06
+
+			__u64	offset;
+		} mmio;
+	} params;
+	__u8	rx_len;
+	__u8	cc;
+	__u8	padding[2];
+	__u8	data[8];
+} __attribute__((__packed__));
+
+struct peci_wr_end_pt_cfg_msg {
+#define PECI_WRENDPTCFG_PCI_WRITE_LEN_BASE	13
+#define PECI_WRENDPTCFG_MMIO_D_WRITE_LEN_BASE	15
+#define PECI_WRENDPTCFG_MMIO_Q_WRITE_LEN_BASE	19
+#define PECI_WRENDPTCFG_READ_LEN		1
+#define PECI_WRENDPTCFG_CMD			0xc5
+
+	__u8	addr;
+	__u8	msg_type;
+	/* See msg_type in struct peci_rd_end_pt_cfg_msg */
+
+	union {
+		struct {
+			__u8	seg;
+			__u8	bus;
+			__u8	device;
+			__u8	function;
+			__u16	reg;
+		} pci_cfg;
+		struct {
+			__u8	seg;
+			__u8	bus;
+			__u8	device;
+			__u8	function;
+			__u8	bar;
+			__u8	addr_type;
+			/* See addr_type in struct peci_rd_end_pt_cfg_msg */
+
+			__u64	offset;
+		} mmio;
+	} params;
+	__u8	tx_len;
+	__u8	cc;
+	__u8	padding[2];
+	__u64	value;
+} __attribute__((__packed__));
+
+/* Crashdump Agent */
+#define PECI_CRASHDUMP_CORE		0x00
+#define PECI_CRASHDUMP_TOR		0x01
+
+/* Crashdump Agent Param */
+#define PECI_CRASHDUMP_PAYLOAD_SIZE	0x00
+
+/* Crashdump Agent Data Param */
+#define PECI_CRASHDUMP_AGENT_ID		0x00
+#define PECI_CRASHDUMP_AGENT_PARAM	0x01
+
+struct peci_crashdump_disc_msg {
+	__u8	addr;
+	__u8	subopcode;
+#define PECI_CRASHDUMP_ENABLED		0x00
+#define PECI_CRASHDUMP_NUM_AGENTS	0x01
+#define PECI_CRASHDUMP_AGENT_DATA	0x02
+
+	__u8	cc;
+	__u8	param0;
+	__u16	param1;
+	__u8	param2;
+	__u8	rx_len;
+	__u8	data[8];
+} __attribute__((__packed__));
+
+struct peci_crashdump_get_frame_msg {
+#define PECI_CRASHDUMP_DISC_WRITE_LEN		9
+#define PECI_CRASHDUMP_DISC_READ_LEN_BASE	1
+#define PECI_CRASHDUMP_DISC_VERSION		0
+#define PECI_CRASHDUMP_DISC_OPCODE		1
+#define PECI_CRASHDUMP_GET_FRAME_WRITE_LEN	10
+#define PECI_CRASHDUMP_GET_FRAME_READ_LEN_BASE	1
+#define PECI_CRASHDUMP_GET_FRAME_VERSION	0
+#define PECI_CRASHDUMP_GET_FRAME_OPCODE		3
+#define PECI_CRASHDUMP_CMD			0x71
+
+	__u8	addr;
+	__u8	padding0;
+	__u16	param0;
+	__u16	param1;
+	__u16	param2;
+	__u8	rx_len;
+	__u8	cc;
+	__u8	padding1[2];
+	__u8	data[16];
+} __attribute__((__packed__));
+
+#define PECI_IOC_BASE  0xb7
+
+#define PECI_IOC_XFER \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_XFER, struct peci_xfer_msg)
+
+#define PECI_IOC_PING \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_PING, struct peci_ping_msg)
+
+#define PECI_IOC_GET_DIB \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_GET_DIB, struct peci_get_dib_msg)
+
+#define PECI_IOC_GET_TEMP \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_GET_TEMP, struct peci_get_temp_msg)
+
+#define PECI_IOC_RD_PKG_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_PKG_CFG, struct peci_rd_pkg_cfg_msg)
+
+#define PECI_IOC_WR_PKG_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_PKG_CFG, struct peci_wr_pkg_cfg_msg)
+
+#define PECI_IOC_RD_IA_MSR \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg)
+
+#define PECI_IOC_WR_IA_MSR \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_ia_msr_msg)
+
+#define PECI_IOC_RD_IA_MSREX \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSREX, struct peci_rd_ia_msrex_msg)
+
+#define PECI_IOC_RD_PCI_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
+#define PECI_IOC_WR_PCI_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG, struct peci_wr_pci_cfg_msg)
+
+#define PECI_IOC_RD_PCI_CFG_LOCAL \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \
+	      struct peci_rd_pci_cfg_local_msg)
+
+#define PECI_IOC_WR_PCI_CFG_LOCAL \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+	      struct peci_wr_pci_cfg_local_msg)
+
+#define PECI_IOC_RD_END_PT_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_RD_END_PT_CFG, \
+	      struct peci_rd_end_pt_cfg_msg)
+
+#define PECI_IOC_WR_END_PT_CFG \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_WR_END_PT_CFG, \
+	      struct peci_wr_end_pt_cfg_msg)
+
+#define PECI_IOC_CRASHDUMP_DISC \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_DISC, \
+	      struct peci_crashdump_disc_msg)
+
+#define PECI_IOC_CRASHDUMP_GET_FRAME \
+	_IOWR(PECI_IOC_BASE, PECI_CMD_CRASHDUMP_GET_FRAME, \
+	      struct peci_crashdump_get_frame_msg)
+
+#endif /* __PECI_IOCTL_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Benjamin Herrenschmidt, Jason M Biils,
	Milton Miller II, Pavel Machek, Robin Murphy, Ryan Chen

This commit adds bindings document of Aspeed PECI adapter for ASPEED
AST24xx/25xx/26xx SoCs.

Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Milton Miller II <miltonm@us.ibm.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Changed documents format to DT schema format so I dropped all review tags.
  Please review it again.

 .../devicetree/bindings/peci/peci-aspeed.yaml | 124 ++++++++++++++++++
 1 file changed, 124 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml

diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
new file mode 100644
index 000000000000..0f5c2993fe9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
@@ -0,0 +1,124 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Aspeed PECI Bus Device Tree Bindings
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+properties:
+  compatible:
+    enum:
+      - aspeed,ast2400-peci
+      - aspeed,ast2500-peci
+      - aspeed,ast2600-peci
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    # Required to define a client address.
+    const: 1
+
+  "#size-cells":
+    # Required to define a client address.
+    const: 0
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    description: |
+      Clock source for PECI controller. Should reference the external
+      oscillator clock.
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  clock-frequency:
+    # Operation frequency of PECI controller in units of Hz.
+    minimum: 187500
+    maximum: 24000000
+
+  msg-timing:
+    description: |
+      Message timing negotiation period. This value will determine the period
+      of message timing negotiation to be issued by PECI controller. The unit
+      of the programmed value is four times of PECI clock period.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 255
+        default: 1
+
+  addr-timing:
+    description: |
+      Address timing negotiation period. This value will determine the period
+      of address timing negotiation to be issued by PECI controller. The unit
+      of the programmed value is four times of PECI clock period.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 255
+        default: 1
+
+  rd-sampling-point:
+    description: |
+      Read sampling point selection. The whole period of a bit time will be
+      divided into 16 time frames. This value will determine the time frame
+      in which the controller will sample PECI signal for data read back.
+      Usually in the middle of a bit time is the best.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 15
+        default: 8
+
+  cmd-timeout-ms:
+    # Command timeout in units of ms.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 1
+        maximum: 60000
+        default: 1000
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - interrupts
+  - clocks
+  - resets
+  - clock-frequency
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x60>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+            msg-timing = <1>;
+            addr-timing = <1>;
+            rd-sampling-point = <8>;
+            cmd-timeout-ms = <1000>;
+        };
+    };
+...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, Ryan Chen, Pavel Machek,
	linux-doc, Benjamin Herrenschmidt, openbmc, Milton Miller II,
	Jason M Biils, Robin Murphy, linux-arm-kernel

This commit adds bindings document of Aspeed PECI adapter for ASPEED
AST24xx/25xx/26xx SoCs.

Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Milton Miller II <miltonm@us.ibm.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Changed documents format to DT schema format so I dropped all review tags.
  Please review it again.

 .../devicetree/bindings/peci/peci-aspeed.yaml | 124 ++++++++++++++++++
 1 file changed, 124 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml

diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
new file mode 100644
index 000000000000..0f5c2993fe9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
@@ -0,0 +1,124 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Aspeed PECI Bus Device Tree Bindings
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+properties:
+  compatible:
+    enum:
+      - aspeed,ast2400-peci
+      - aspeed,ast2500-peci
+      - aspeed,ast2600-peci
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    # Required to define a client address.
+    const: 1
+
+  "#size-cells":
+    # Required to define a client address.
+    const: 0
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    description: |
+      Clock source for PECI controller. Should reference the external
+      oscillator clock.
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  clock-frequency:
+    # Operation frequency of PECI controller in units of Hz.
+    minimum: 187500
+    maximum: 24000000
+
+  msg-timing:
+    description: |
+      Message timing negotiation period. This value will determine the period
+      of message timing negotiation to be issued by PECI controller. The unit
+      of the programmed value is four times of PECI clock period.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 255
+        default: 1
+
+  addr-timing:
+    description: |
+      Address timing negotiation period. This value will determine the period
+      of address timing negotiation to be issued by PECI controller. The unit
+      of the programmed value is four times of PECI clock period.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 255
+        default: 1
+
+  rd-sampling-point:
+    description: |
+      Read sampling point selection. The whole period of a bit time will be
+      divided into 16 time frames. This value will determine the time frame
+      in which the controller will sample PECI signal for data read back.
+      Usually in the middle of a bit time is the best.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 15
+        default: 8
+
+  cmd-timeout-ms:
+    # Command timeout in units of ms.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 1
+        maximum: 60000
+        default: 1000
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - interrupts
+  - clocks
+  - resets
+  - clock-frequency
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x60>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+            msg-timing = <1>;
+            addr-timing = <1>;
+            rd-sampling-point = <8>;
+            cmd-timeout-ms = <1000>;
+        };
+    };
+...
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 05/14] ARM: dts: aspeed: Add PECI node
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Jason M Biils, Ryan Chen

This commit adds PECI bus/adapter node into aspeed-g4, aspeed-g5
and aspeed-g6.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
Changes since v10:
- Added PECI node for Aspeed AST2600.

 arch/arm/boot/dts/aspeed-g4.dtsi | 25 +++++++++++++++++++++++++
 arch/arm/boot/dts/aspeed-g5.dtsi | 25 +++++++++++++++++++++++++
 arch/arm/boot/dts/aspeed-g6.dtsi | 25 +++++++++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index 46c0891aac5a..3b81aebed190 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -378,6 +378,13 @@
 				};
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x60>;
+			};
+
 			uart2: serial@1e78d000 {
 				compatible = "ns16550a";
 				reg = <0x1e78d000 0x20>;
@@ -421,6 +428,24 @@
 	};
 };
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2400-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c_ic: interrupt-controller@0 {
 		#interrupt-cells = <1>;
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index a259c63fff06..41faf0ad8074 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -492,6 +492,13 @@
 				};
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x60>;
+			};
+
 			uart2: serial@1e78d000 {
 				compatible = "ns16550a";
 				reg = <0x1e78d000 0x20>;
@@ -535,6 +542,24 @@
 	};
 };
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2500-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c_ic: interrupt-controller@0 {
 		#interrupt-cells = <1>;
diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index 5f6142d99eeb..1056c1538be5 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -391,6 +391,13 @@
 				status = "disabled";
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x100>;
+			};
+
 			lpc: lpc@1e789000 {
 				compatible = "aspeed,ast2600-lpc", "simple-mfd";
 				reg = <0x1e789000 0x1000>;
@@ -601,6 +608,24 @@
 
 #include "aspeed-g6-pinctrl.dtsi"
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2600-peci";
+		reg = <0x0 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c0: i2c-bus@80 {
 		#address-cells = <1>;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 05/14] ARM: dts: aspeed: Add PECI node
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, Ryan Chen, linux-doc,
	openbmc, Jason M Biils, linux-arm-kernel

This commit adds PECI bus/adapter node into aspeed-g4, aspeed-g5
and aspeed-g6.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
Changes since v10:
- Added PECI node for Aspeed AST2600.

 arch/arm/boot/dts/aspeed-g4.dtsi | 25 +++++++++++++++++++++++++
 arch/arm/boot/dts/aspeed-g5.dtsi | 25 +++++++++++++++++++++++++
 arch/arm/boot/dts/aspeed-g6.dtsi | 25 +++++++++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index 46c0891aac5a..3b81aebed190 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -378,6 +378,13 @@
 				};
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x60>;
+			};
+
 			uart2: serial@1e78d000 {
 				compatible = "ns16550a";
 				reg = <0x1e78d000 0x20>;
@@ -421,6 +428,24 @@
 	};
 };
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2400-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c_ic: interrupt-controller@0 {
 		#interrupt-cells = <1>;
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index a259c63fff06..41faf0ad8074 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -492,6 +492,13 @@
 				};
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x60>;
+			};
+
 			uart2: serial@1e78d000 {
 				compatible = "ns16550a";
 				reg = <0x1e78d000 0x20>;
@@ -535,6 +542,24 @@
 	};
 };
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2500-peci";
+		reg = <0x0 0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <15>;
+		clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c_ic: interrupt-controller@0 {
 		#interrupt-cells = <1>;
diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
index 5f6142d99eeb..1056c1538be5 100644
--- a/arch/arm/boot/dts/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
@@ -391,6 +391,13 @@
 				status = "disabled";
 			};
 
+			peci: bus@1e78b000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x1e78b000 0x100>;
+			};
+
 			lpc: lpc@1e789000 {
 				compatible = "aspeed,ast2600-lpc", "simple-mfd";
 				reg = <0x1e789000 0x1000>;
@@ -601,6 +608,24 @@
 
 #include "aspeed-g6-pinctrl.dtsi"
 
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "aspeed,ast2600-peci";
+		reg = <0x0 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+		resets = <&syscon ASPEED_RESET_PECI>;
+		clock-frequency = <24000000>;
+		msg-timing = <1>;
+		addr-timing = <1>;
+		rd-sampling-point = <8>;
+		cmd-timeout-ms = <1000>;
+		status = "disabled";
+	};
+};
+
 &i2c {
 	i2c0: i2c-bus@80 {
 		#address-cells = <1>;
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Andy Shevchenko, Robin Murphy, Ryan Chen

This commit adds Aspeed PECI adapter driver for Aspeed
AST24xx/25xx/26xx SoCs.

Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
Changes since v10:
- Moved driver into 'drivers/peci/busses'.
- Fixed minor bugs.

 drivers/peci/busses/Kconfig       |  12 +
 drivers/peci/busses/Makefile      |   2 +
 drivers/peci/busses/peci-aspeed.c | 492 ++++++++++++++++++++++++++++++
 3 files changed, 506 insertions(+)
 create mode 100644 drivers/peci/busses/peci-aspeed.c

diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
index d7e064f52a1c..2b2540221b36 100644
--- a/drivers/peci/busses/Kconfig
+++ b/drivers/peci/busses/Kconfig
@@ -4,4 +4,16 @@
 
 menu "PECI Hardware Bus support"
 
+config PECI_ASPEED
+	tristate "ASPEED PECI support"
+	depends on ARCH_ASPEED || COMPILE_TEST
+	depends on OF
+	depends on PECI
+	help
+	  Say Y here if you want support for the Platform Environment Control
+	  Interface (PECI) bus adapter driver on the ASPEED SoCs.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-aspeed.
+
 endmenu
diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
index 9e9334fee297..69e31dfaca19 100644
--- a/drivers/peci/busses/Makefile
+++ b/drivers/peci/busses/Makefile
@@ -2,3 +2,5 @@
 #
 # Makefile for the PECI hardware bus drivers.
 #
+
+obj-$(CONFIG_PECI_ASPEED)	+= peci-aspeed.o
diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c
new file mode 100644
index 000000000000..7dcc663855e7
--- /dev/null
+++ b/drivers/peci/busses/peci-aspeed.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+/* Control Register */
+#define ASPEED_PECI_CTRL			0x00
+#define   ASPEED_PECI_CTRL_SAMPLING_MASK	GENMASK(19, 16)
+#define   ASPEED_PECI_CTRL_READ_MODE_MASK	GENMASK(13, 12)
+#define   ASPEED_PECI_CTRL_READ_MODE_COUNT	BIT(12)
+#define   ASPEED_PECI_CTRL_READ_MODE_DBG	BIT(13)
+#define   ASPEED_PECI_CTRL_CLK_SOURCE_MASK	BIT(11)
+#define   ASPEED_PECI_CTRL_CLK_DIV_MASK		GENMASK(10, 8)
+#define   ASPEED_PECI_CTRL_INVERT_OUT		BIT(7)
+#define   ASPEED_PECI_CTRL_INVERT_IN		BIT(6)
+#define   ASPEED_PECI_CTRL_BUS_CONTENT_EN	BIT(5)
+#define   ASPEED_PECI_CTRL_PECI_EN		BIT(4)
+#define   ASPEED_PECI_CTRL_PECI_CLK_EN		BIT(0)
+
+/* Timing Negotiation Register */
+#define ASPEED_PECI_TIMING_NEGOTIATION		0x04
+#define   ASPEED_PECI_TIMING_MESSAGE_MASK	GENMASK(15, 8)
+#define   ASPEED_PECI_TIMING_ADDRESS_MASK	GENMASK(7, 0)
+
+/* Command Register */
+#define ASPEED_PECI_CMD				0x08
+#define   ASPEED_PECI_CMD_PIN_MON		BIT(31)
+#define   ASPEED_PECI_CMD_STS_MASK		GENMASK(27, 24)
+#define   ASPEED_PECI_CMD_IDLE_MASK		(ASPEED_PECI_CMD_STS_MASK | \
+						 ASPEED_PECI_CMD_PIN_MON)
+#define   ASPEED_PECI_CMD_FIRE			BIT(0)
+
+/* Read/Write Length Register */
+#define ASPEED_PECI_RW_LENGTH			0x0c
+#define   ASPEED_PECI_AW_FCS_EN			BIT(31)
+#define   ASPEED_PECI_READ_LEN_MASK		GENMASK(23, 16)
+#define   ASPEED_PECI_WRITE_LEN_MASK		GENMASK(15, 8)
+#define   ASPEED_PECI_TAGET_ADDR_MASK		GENMASK(7, 0)
+
+/* Expected FCS Data Register */
+#define ASPEED_PECI_EXP_FCS			0x10
+#define   ASPEED_PECI_EXP_READ_FCS_MASK		GENMASK(23, 16)
+#define   ASPEED_PECI_EXP_AW_FCS_AUTO_MASK	GENMASK(15, 8)
+#define   ASPEED_PECI_EXP_WRITE_FCS_MASK	GENMASK(7, 0)
+
+/* Captured FCS Data Register */
+#define ASPEED_PECI_CAP_FCS			0x14
+#define   ASPEED_PECI_CAP_READ_FCS_MASK		GENMASK(23, 16)
+#define   ASPEED_PECI_CAP_WRITE_FCS_MASK	GENMASK(7, 0)
+
+/* Interrupt Register */
+#define ASPEED_PECI_INT_CTRL			0x18
+#define   ASPEED_PECI_TIMING_NEGO_SEL_MASK	GENMASK(31, 30)
+#define     ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO	0
+#define     ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO	1
+#define     ASPEED_PECI_MESSAGE_NEGO		2
+#define   ASPEED_PECI_INT_MASK			GENMASK(4, 0)
+#define   ASPEED_PECI_INT_BUS_TIMEOUT		BIT(4)
+#define   ASPEED_PECI_INT_BUS_CONNECT		BIT(3)
+#define   ASPEED_PECI_INT_W_FCS_BAD		BIT(2)
+#define   ASPEED_PECI_INT_W_FCS_ABORT		BIT(1)
+#define   ASPEED_PECI_INT_CMD_DONE		BIT(0)
+
+/* Interrupt Status Register */
+#define ASPEED_PECI_INT_STS			0x1c
+#define   ASPEED_PECI_INT_TIMING_RESULT_MASK	GENMASK(29, 16)
+	  /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
+
+/* Rx/Tx Data Buffer Registers */
+#define ASPEED_PECI_W_DATA0			0x20
+#define ASPEED_PECI_W_DATA1			0x24
+#define ASPEED_PECI_W_DATA2			0x28
+#define ASPEED_PECI_W_DATA3			0x2c
+#define ASPEED_PECI_R_DATA0			0x30
+#define ASPEED_PECI_R_DATA1			0x34
+#define ASPEED_PECI_R_DATA2			0x38
+#define ASPEED_PECI_R_DATA3			0x3c
+#define ASPEED_PECI_W_DATA4			0x40
+#define ASPEED_PECI_W_DATA5			0x44
+#define ASPEED_PECI_W_DATA6			0x48
+#define ASPEED_PECI_W_DATA7			0x4c
+#define ASPEED_PECI_R_DATA4			0x50
+#define ASPEED_PECI_R_DATA5			0x54
+#define ASPEED_PECI_R_DATA6			0x58
+#define ASPEED_PECI_R_DATA7			0x5c
+#define   ASPEED_PECI_DATA_BUF_SIZE_MAX		32
+
+/* Timing Negotiation */
+#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT	8
+#define ASPEED_PECI_RD_SAMPLING_POINT_MAX	15
+#define ASPEED_PECI_CLK_DIV_DEFAULT		0
+#define ASPEED_PECI_CLK_DIV_MAX			7
+#define ASPEED_PECI_MSG_TIMING_DEFAULT		1
+#define ASPEED_PECI_MSG_TIMING_MAX		255
+#define ASPEED_PECI_ADDR_TIMING_DEFAULT		1
+#define ASPEED_PECI_ADDR_TIMING_MAX		255
+
+/* Timeout */
+#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC	50000
+#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC	10000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT	1000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX		60000
+
+struct aspeed_peci {
+	struct peci_adapter	*adapter;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	struct reset_control	*rst;
+	int			irq;
+	spinlock_t		lock; /* to sync completion status handling */
+	struct completion	xfer_complete;
+	u32			status;
+	u32			cmd_timeout_ms;
+};
+
+static int aspeed_peci_check_idle(struct aspeed_peci *priv)
+{
+	ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	u32 cmd_sts;
+
+	for (;;) {
+		cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
+		if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
+			break;
+		if (time_after(jiffies, timeout)) {
+			cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
+			break;
+		}
+		usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
+			     ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
+	}
+
+	return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;
+}
+
+static int aspeed_peci_xfer(struct peci_adapter *adapter,
+			    struct peci_xfer_msg *msg)
+{
+	struct aspeed_peci *priv = peci_get_adapdata(adapter);
+	long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+	u32 peci_head, peci_state, rx_data = 0;
+	ulong flags;
+	int i, ret;
+	uint reg;
+
+	if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
+	    msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
+		return -EINVAL;
+
+	/* Check command sts and bus idle state */
+	ret = aspeed_peci_check_idle(priv);
+	if (ret)
+		return ret; /* -ETIMEDOUT */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reinit_completion(&priv->xfer_complete);
+
+	peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) |
+		    FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) |
+		    FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len);
+
+	writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
+
+	for (i = 0; i < msg->tx_len; i += 4) {
+		reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
+			       ASPEED_PECI_W_DATA4 + i % 16;
+		writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]),
+		       priv->base + reg);
+	}
+
+	dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+	print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
+			     msg->tx_buf, msg->tx_len, true);
+
+	priv->status = 0;
+	writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+							timeout);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
+	peci_state = readl(priv->base + ASPEED_PECI_CMD);
+	dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+		FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
+
+	writel(0, priv->base + ASPEED_PECI_CMD);
+
+	if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) {
+		if (err < 0) { /* -ERESTARTSYS */
+			ret = (int)err;
+			goto err_irqrestore;
+		} else if (err == 0) {
+			dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+			ret = -ETIMEDOUT;
+			goto err_irqrestore;
+		}
+
+		dev_dbg(priv->dev, "No valid response!\n");
+		ret = -EIO;
+		goto err_irqrestore;
+	}
+
+	/*
+	 * Note that rx_len and rx_buf size can be an odd number.
+	 * Byte handling is more efficient.
+	 */
+	for (i = 0; i < msg->rx_len; i++) {
+		u8 byte_offset = i % 4;
+
+		if (byte_offset == 0) {
+			reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
+				       ASPEED_PECI_R_DATA4 + i % 16;
+			rx_data = readl(priv->base + reg);
+		}
+
+		msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+	}
+
+	print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
+			     msg->rx_buf, msg->rx_len, true);
+
+	peci_state = readl(priv->base + ASPEED_PECI_CMD);
+	dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+		FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
+	dev_dbg(priv->dev, "------------------------\n");
+
+err_irqrestore:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return ret;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+	struct aspeed_peci *priv = arg;
+	u32 status;
+
+	spin_lock(&priv->lock);
+	status = readl(priv->base + ASPEED_PECI_INT_STS);
+	writel(status, priv->base + ASPEED_PECI_INT_STS);
+	priv->status |= (status & ASPEED_PECI_INT_MASK);
+
+	/*
+	 * In most cases, interrupt bits will be set one by one but also note
+	 * that multiple interrupt bits could be set at the same time.
+	 */
+	if (status & ASPEED_PECI_INT_BUS_TIMEOUT)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n");
+
+	if (status & ASPEED_PECI_INT_BUS_CONNECT)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n");
+
+	if (status & ASPEED_PECI_INT_W_FCS_BAD)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n");
+
+	if (status & ASPEED_PECI_INT_W_FCS_ABORT)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n");
+
+	/*
+	 * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
+	 * set even in an error case.
+	 */
+	if (status & ASPEED_PECI_INT_CMD_DONE) {
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n");
+		complete(&priv->xfer_complete);
+	}
+
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+{
+	u32 msg_timing, addr_timing, rd_sampling_point;
+	u32 clk_freq, clk_divisor, clk_div_val = 0;
+	int ret;
+
+	priv->clk = devm_clk_get(priv->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get clk source.\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(priv->dev, "Failed to enable clock.\n");
+		return ret;
+	}
+
+	ret = device_property_read_u32(priv->dev, "clock-frequency", &clk_freq);
+	if (ret) {
+		dev_err(priv->dev,
+			"Could not read clock-frequency property.\n");
+		clk_disable_unprepare(priv->clk);
+		return ret;
+	}
+
+	clk_divisor = clk_get_rate(priv->clk) / clk_freq;
+
+	while ((clk_divisor >> 1) && (clk_div_val < ASPEED_PECI_CLK_DIV_MAX))
+		clk_div_val++;
+
+	ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing);
+	if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid msg-timing : %u, Use default : %u\n",
+				 msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT);
+		msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT;
+	}
+
+	ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing);
+	if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid addr-timing : %u, Use default : %u\n",
+				 addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT);
+		addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT;
+	}
+
+	ret = device_property_read_u32(priv->dev, "rd-sampling-point",
+				       &rd_sampling_point);
+	if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid rd-sampling-point : %u. Use default : %u\n",
+				 rd_sampling_point,
+				 ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
+		rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT;
+	}
+
+	ret = device_property_read_u32(priv->dev, "cmd-timeout-ms",
+				       &priv->cmd_timeout_ms);
+	if (ret || priv->cmd_timeout_ms > ASPEED_PECI_CMD_TIMEOUT_MS_MAX ||
+	    priv->cmd_timeout_ms == 0) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+				 priv->cmd_timeout_ms,
+				 ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT);
+		priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT;
+	}
+
+	writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK,
+			  ASPEED_PECI_CLK_DIV_DEFAULT) |
+	       ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL);
+
+	/*
+	 * Timing negotiation period setting.
+	 * The unit of the programmed value is 4 times of PECI clock period.
+	 */
+	writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) |
+	       FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing),
+	       priv->base + ASPEED_PECI_TIMING_NEGOTIATION);
+
+	/* Clear interrupts */
+	writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK,
+	       priv->base + ASPEED_PECI_INT_STS);
+
+	/* Set timing negotiation mode and enable interrupts */
+	writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK,
+			  ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) |
+	       ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL);
+
+	/* Read sampling point and clock speed setting */
+	writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
+	       FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
+	       ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN,
+	       priv->base + ASPEED_PECI_CTRL);
+
+	return 0;
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+	struct peci_adapter *adapter;
+	struct aspeed_peci *priv;
+	int ret;
+
+	adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+	if (!adapter)
+		return -ENOMEM;
+
+	priv = peci_get_adapdata(adapter);
+	priv->adapter = adapter;
+	priv->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, priv);
+
+	priv->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->base)) {
+		ret = PTR_ERR(priv->base);
+		goto err_put_adapter_dev;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (!priv->irq) {
+		ret = -ENODEV;
+		goto err_put_adapter_dev;
+	}
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+			       0, "peci-aspeed-irq", priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	init_completion(&priv->xfer_complete);
+	spin_lock_init(&priv->lock);
+
+	priv->adapter->owner = THIS_MODULE;
+	priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+	strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+	priv->adapter->xfer = aspeed_peci_xfer;
+	priv->adapter->use_dma = false;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->rst)) {
+		dev_err(&pdev->dev,
+			"missing or invalid reset controller entry\n");
+		ret = PTR_ERR(priv->rst);
+		goto err_put_adapter_dev;
+	}
+	reset_control_deassert(priv->rst);
+
+	ret = aspeed_peci_init_ctrl(priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	ret = peci_add_adapter(priv->adapter);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
+		 priv->adapter->nr, priv->irq);
+
+	return 0;
+
+err_put_adapter_dev:
+	put_device(&adapter->dev);
+
+	return ret;
+}
+
+static int aspeed_peci_remove(struct platform_device *pdev)
+{
+	struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(priv->clk);
+	reset_control_assert(priv->rst);
+	peci_del_adapter(priv->adapter);
+	of_node_put(priv->adapter->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+	{ .compatible = "aspeed,ast2400-peci", },
+	{ .compatible = "aspeed,ast2500-peci", },
+	{ .compatible = "aspeed,ast2600-peci", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+	.probe  = aspeed_peci_probe,
+	.remove = aspeed_peci_remove,
+	.driver = {
+		.name           = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(aspeed_peci_of_table),
+	},
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, Ryan Chen,
	Andy Shevchenko, linux-doc, openbmc, Robin Murphy,
	linux-arm-kernel

This commit adds Aspeed PECI adapter driver for Aspeed
AST24xx/25xx/26xx SoCs.

Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
Changes since v10:
- Moved driver into 'drivers/peci/busses'.
- Fixed minor bugs.

 drivers/peci/busses/Kconfig       |  12 +
 drivers/peci/busses/Makefile      |   2 +
 drivers/peci/busses/peci-aspeed.c | 492 ++++++++++++++++++++++++++++++
 3 files changed, 506 insertions(+)
 create mode 100644 drivers/peci/busses/peci-aspeed.c

diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
index d7e064f52a1c..2b2540221b36 100644
--- a/drivers/peci/busses/Kconfig
+++ b/drivers/peci/busses/Kconfig
@@ -4,4 +4,16 @@
 
 menu "PECI Hardware Bus support"
 
+config PECI_ASPEED
+	tristate "ASPEED PECI support"
+	depends on ARCH_ASPEED || COMPILE_TEST
+	depends on OF
+	depends on PECI
+	help
+	  Say Y here if you want support for the Platform Environment Control
+	  Interface (PECI) bus adapter driver on the ASPEED SoCs.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-aspeed.
+
 endmenu
diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
index 9e9334fee297..69e31dfaca19 100644
--- a/drivers/peci/busses/Makefile
+++ b/drivers/peci/busses/Makefile
@@ -2,3 +2,5 @@
 #
 # Makefile for the PECI hardware bus drivers.
 #
+
+obj-$(CONFIG_PECI_ASPEED)	+= peci-aspeed.o
diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c
new file mode 100644
index 000000000000..7dcc663855e7
--- /dev/null
+++ b/drivers/peci/busses/peci-aspeed.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+/* Control Register */
+#define ASPEED_PECI_CTRL			0x00
+#define   ASPEED_PECI_CTRL_SAMPLING_MASK	GENMASK(19, 16)
+#define   ASPEED_PECI_CTRL_READ_MODE_MASK	GENMASK(13, 12)
+#define   ASPEED_PECI_CTRL_READ_MODE_COUNT	BIT(12)
+#define   ASPEED_PECI_CTRL_READ_MODE_DBG	BIT(13)
+#define   ASPEED_PECI_CTRL_CLK_SOURCE_MASK	BIT(11)
+#define   ASPEED_PECI_CTRL_CLK_DIV_MASK		GENMASK(10, 8)
+#define   ASPEED_PECI_CTRL_INVERT_OUT		BIT(7)
+#define   ASPEED_PECI_CTRL_INVERT_IN		BIT(6)
+#define   ASPEED_PECI_CTRL_BUS_CONTENT_EN	BIT(5)
+#define   ASPEED_PECI_CTRL_PECI_EN		BIT(4)
+#define   ASPEED_PECI_CTRL_PECI_CLK_EN		BIT(0)
+
+/* Timing Negotiation Register */
+#define ASPEED_PECI_TIMING_NEGOTIATION		0x04
+#define   ASPEED_PECI_TIMING_MESSAGE_MASK	GENMASK(15, 8)
+#define   ASPEED_PECI_TIMING_ADDRESS_MASK	GENMASK(7, 0)
+
+/* Command Register */
+#define ASPEED_PECI_CMD				0x08
+#define   ASPEED_PECI_CMD_PIN_MON		BIT(31)
+#define   ASPEED_PECI_CMD_STS_MASK		GENMASK(27, 24)
+#define   ASPEED_PECI_CMD_IDLE_MASK		(ASPEED_PECI_CMD_STS_MASK | \
+						 ASPEED_PECI_CMD_PIN_MON)
+#define   ASPEED_PECI_CMD_FIRE			BIT(0)
+
+/* Read/Write Length Register */
+#define ASPEED_PECI_RW_LENGTH			0x0c
+#define   ASPEED_PECI_AW_FCS_EN			BIT(31)
+#define   ASPEED_PECI_READ_LEN_MASK		GENMASK(23, 16)
+#define   ASPEED_PECI_WRITE_LEN_MASK		GENMASK(15, 8)
+#define   ASPEED_PECI_TAGET_ADDR_MASK		GENMASK(7, 0)
+
+/* Expected FCS Data Register */
+#define ASPEED_PECI_EXP_FCS			0x10
+#define   ASPEED_PECI_EXP_READ_FCS_MASK		GENMASK(23, 16)
+#define   ASPEED_PECI_EXP_AW_FCS_AUTO_MASK	GENMASK(15, 8)
+#define   ASPEED_PECI_EXP_WRITE_FCS_MASK	GENMASK(7, 0)
+
+/* Captured FCS Data Register */
+#define ASPEED_PECI_CAP_FCS			0x14
+#define   ASPEED_PECI_CAP_READ_FCS_MASK		GENMASK(23, 16)
+#define   ASPEED_PECI_CAP_WRITE_FCS_MASK	GENMASK(7, 0)
+
+/* Interrupt Register */
+#define ASPEED_PECI_INT_CTRL			0x18
+#define   ASPEED_PECI_TIMING_NEGO_SEL_MASK	GENMASK(31, 30)
+#define     ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO	0
+#define     ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO	1
+#define     ASPEED_PECI_MESSAGE_NEGO		2
+#define   ASPEED_PECI_INT_MASK			GENMASK(4, 0)
+#define   ASPEED_PECI_INT_BUS_TIMEOUT		BIT(4)
+#define   ASPEED_PECI_INT_BUS_CONNECT		BIT(3)
+#define   ASPEED_PECI_INT_W_FCS_BAD		BIT(2)
+#define   ASPEED_PECI_INT_W_FCS_ABORT		BIT(1)
+#define   ASPEED_PECI_INT_CMD_DONE		BIT(0)
+
+/* Interrupt Status Register */
+#define ASPEED_PECI_INT_STS			0x1c
+#define   ASPEED_PECI_INT_TIMING_RESULT_MASK	GENMASK(29, 16)
+	  /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
+
+/* Rx/Tx Data Buffer Registers */
+#define ASPEED_PECI_W_DATA0			0x20
+#define ASPEED_PECI_W_DATA1			0x24
+#define ASPEED_PECI_W_DATA2			0x28
+#define ASPEED_PECI_W_DATA3			0x2c
+#define ASPEED_PECI_R_DATA0			0x30
+#define ASPEED_PECI_R_DATA1			0x34
+#define ASPEED_PECI_R_DATA2			0x38
+#define ASPEED_PECI_R_DATA3			0x3c
+#define ASPEED_PECI_W_DATA4			0x40
+#define ASPEED_PECI_W_DATA5			0x44
+#define ASPEED_PECI_W_DATA6			0x48
+#define ASPEED_PECI_W_DATA7			0x4c
+#define ASPEED_PECI_R_DATA4			0x50
+#define ASPEED_PECI_R_DATA5			0x54
+#define ASPEED_PECI_R_DATA6			0x58
+#define ASPEED_PECI_R_DATA7			0x5c
+#define   ASPEED_PECI_DATA_BUF_SIZE_MAX		32
+
+/* Timing Negotiation */
+#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT	8
+#define ASPEED_PECI_RD_SAMPLING_POINT_MAX	15
+#define ASPEED_PECI_CLK_DIV_DEFAULT		0
+#define ASPEED_PECI_CLK_DIV_MAX			7
+#define ASPEED_PECI_MSG_TIMING_DEFAULT		1
+#define ASPEED_PECI_MSG_TIMING_MAX		255
+#define ASPEED_PECI_ADDR_TIMING_DEFAULT		1
+#define ASPEED_PECI_ADDR_TIMING_MAX		255
+
+/* Timeout */
+#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC	50000
+#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC	10000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT	1000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX		60000
+
+struct aspeed_peci {
+	struct peci_adapter	*adapter;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	struct reset_control	*rst;
+	int			irq;
+	spinlock_t		lock; /* to sync completion status handling */
+	struct completion	xfer_complete;
+	u32			status;
+	u32			cmd_timeout_ms;
+};
+
+static int aspeed_peci_check_idle(struct aspeed_peci *priv)
+{
+	ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	u32 cmd_sts;
+
+	for (;;) {
+		cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
+		if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
+			break;
+		if (time_after(jiffies, timeout)) {
+			cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
+			break;
+		}
+		usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
+			     ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
+	}
+
+	return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;
+}
+
+static int aspeed_peci_xfer(struct peci_adapter *adapter,
+			    struct peci_xfer_msg *msg)
+{
+	struct aspeed_peci *priv = peci_get_adapdata(adapter);
+	long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+	u32 peci_head, peci_state, rx_data = 0;
+	ulong flags;
+	int i, ret;
+	uint reg;
+
+	if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
+	    msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
+		return -EINVAL;
+
+	/* Check command sts and bus idle state */
+	ret = aspeed_peci_check_idle(priv);
+	if (ret)
+		return ret; /* -ETIMEDOUT */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reinit_completion(&priv->xfer_complete);
+
+	peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) |
+		    FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) |
+		    FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len);
+
+	writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
+
+	for (i = 0; i < msg->tx_len; i += 4) {
+		reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
+			       ASPEED_PECI_W_DATA4 + i % 16;
+		writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]),
+		       priv->base + reg);
+	}
+
+	dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+	print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
+			     msg->tx_buf, msg->tx_len, true);
+
+	priv->status = 0;
+	writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+							timeout);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
+	peci_state = readl(priv->base + ASPEED_PECI_CMD);
+	dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+		FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
+
+	writel(0, priv->base + ASPEED_PECI_CMD);
+
+	if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) {
+		if (err < 0) { /* -ERESTARTSYS */
+			ret = (int)err;
+			goto err_irqrestore;
+		} else if (err == 0) {
+			dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+			ret = -ETIMEDOUT;
+			goto err_irqrestore;
+		}
+
+		dev_dbg(priv->dev, "No valid response!\n");
+		ret = -EIO;
+		goto err_irqrestore;
+	}
+
+	/*
+	 * Note that rx_len and rx_buf size can be an odd number.
+	 * Byte handling is more efficient.
+	 */
+	for (i = 0; i < msg->rx_len; i++) {
+		u8 byte_offset = i % 4;
+
+		if (byte_offset == 0) {
+			reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
+				       ASPEED_PECI_R_DATA4 + i % 16;
+			rx_data = readl(priv->base + reg);
+		}
+
+		msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+	}
+
+	print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
+			     msg->rx_buf, msg->rx_len, true);
+
+	peci_state = readl(priv->base + ASPEED_PECI_CMD);
+	dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+		FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state));
+	dev_dbg(priv->dev, "------------------------\n");
+
+err_irqrestore:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return ret;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+	struct aspeed_peci *priv = arg;
+	u32 status;
+
+	spin_lock(&priv->lock);
+	status = readl(priv->base + ASPEED_PECI_INT_STS);
+	writel(status, priv->base + ASPEED_PECI_INT_STS);
+	priv->status |= (status & ASPEED_PECI_INT_MASK);
+
+	/*
+	 * In most cases, interrupt bits will be set one by one but also note
+	 * that multiple interrupt bits could be set at the same time.
+	 */
+	if (status & ASPEED_PECI_INT_BUS_TIMEOUT)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n");
+
+	if (status & ASPEED_PECI_INT_BUS_CONNECT)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n");
+
+	if (status & ASPEED_PECI_INT_W_FCS_BAD)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n");
+
+	if (status & ASPEED_PECI_INT_W_FCS_ABORT)
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n");
+
+	/*
+	 * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
+	 * set even in an error case.
+	 */
+	if (status & ASPEED_PECI_INT_CMD_DONE) {
+		dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n");
+		complete(&priv->xfer_complete);
+	}
+
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+{
+	u32 msg_timing, addr_timing, rd_sampling_point;
+	u32 clk_freq, clk_divisor, clk_div_val = 0;
+	int ret;
+
+	priv->clk = devm_clk_get(priv->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get clk source.\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(priv->dev, "Failed to enable clock.\n");
+		return ret;
+	}
+
+	ret = device_property_read_u32(priv->dev, "clock-frequency", &clk_freq);
+	if (ret) {
+		dev_err(priv->dev,
+			"Could not read clock-frequency property.\n");
+		clk_disable_unprepare(priv->clk);
+		return ret;
+	}
+
+	clk_divisor = clk_get_rate(priv->clk) / clk_freq;
+
+	while ((clk_divisor >> 1) && (clk_div_val < ASPEED_PECI_CLK_DIV_MAX))
+		clk_div_val++;
+
+	ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing);
+	if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid msg-timing : %u, Use default : %u\n",
+				 msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT);
+		msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT;
+	}
+
+	ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing);
+	if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid addr-timing : %u, Use default : %u\n",
+				 addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT);
+		addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT;
+	}
+
+	ret = device_property_read_u32(priv->dev, "rd-sampling-point",
+				       &rd_sampling_point);
+	if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid rd-sampling-point : %u. Use default : %u\n",
+				 rd_sampling_point,
+				 ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
+		rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT;
+	}
+
+	ret = device_property_read_u32(priv->dev, "cmd-timeout-ms",
+				       &priv->cmd_timeout_ms);
+	if (ret || priv->cmd_timeout_ms > ASPEED_PECI_CMD_TIMEOUT_MS_MAX ||
+	    priv->cmd_timeout_ms == 0) {
+		if (!ret)
+			dev_warn(priv->dev,
+				 "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+				 priv->cmd_timeout_ms,
+				 ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT);
+		priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT;
+	}
+
+	writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK,
+			  ASPEED_PECI_CLK_DIV_DEFAULT) |
+	       ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL);
+
+	/*
+	 * Timing negotiation period setting.
+	 * The unit of the programmed value is 4 times of PECI clock period.
+	 */
+	writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) |
+	       FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing),
+	       priv->base + ASPEED_PECI_TIMING_NEGOTIATION);
+
+	/* Clear interrupts */
+	writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK,
+	       priv->base + ASPEED_PECI_INT_STS);
+
+	/* Set timing negotiation mode and enable interrupts */
+	writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK,
+			  ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) |
+	       ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL);
+
+	/* Read sampling point and clock speed setting */
+	writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
+	       FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
+	       ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN,
+	       priv->base + ASPEED_PECI_CTRL);
+
+	return 0;
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+	struct peci_adapter *adapter;
+	struct aspeed_peci *priv;
+	int ret;
+
+	adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+	if (!adapter)
+		return -ENOMEM;
+
+	priv = peci_get_adapdata(adapter);
+	priv->adapter = adapter;
+	priv->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, priv);
+
+	priv->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->base)) {
+		ret = PTR_ERR(priv->base);
+		goto err_put_adapter_dev;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (!priv->irq) {
+		ret = -ENODEV;
+		goto err_put_adapter_dev;
+	}
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+			       0, "peci-aspeed-irq", priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	init_completion(&priv->xfer_complete);
+	spin_lock_init(&priv->lock);
+
+	priv->adapter->owner = THIS_MODULE;
+	priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+	strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+	priv->adapter->xfer = aspeed_peci_xfer;
+	priv->adapter->use_dma = false;
+
+	priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->rst)) {
+		dev_err(&pdev->dev,
+			"missing or invalid reset controller entry\n");
+		ret = PTR_ERR(priv->rst);
+		goto err_put_adapter_dev;
+	}
+	reset_control_deassert(priv->rst);
+
+	ret = aspeed_peci_init_ctrl(priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	ret = peci_add_adapter(priv->adapter);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
+		 priv->adapter->nr, priv->irq);
+
+	return 0;
+
+err_put_adapter_dev:
+	put_device(&adapter->dev);
+
+	return ret;
+}
+
+static int aspeed_peci_remove(struct platform_device *pdev)
+{
+	struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(priv->clk);
+	reset_control_assert(priv->rst);
+	peci_del_adapter(priv->adapter);
+	of_node_put(priv->adapter->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+	{ .compatible = "aspeed,ast2400-peci", },
+	{ .compatible = "aspeed,ast2500-peci", },
+	{ .compatible = "aspeed,ast2600-peci", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+	.probe  = aspeed_peci_probe,
+	.remove = aspeed_peci_remove,
+	.driver = {
+		.name           = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(aspeed_peci_of_table),
+	},
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo

From: Tomer Maimon <tmaimon77@gmail.com>

Added device tree binding documentation for Nuvoton BMC
NPCM Platform Environment Control Interface(PECI).

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Newly added in v11.

 .../devicetree/bindings/peci/peci-npcm.yaml   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml

diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
new file mode 100644
index 000000000000..bcd5626e68e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NPCM PECI Bus Device Tree Bindings
+
+maintainers:
+  - Tomer Maimon <tmaimon77@gmail.com>
+
+properties:
+  compatible:
+    const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    # Required to define a client address.
+    const: 1
+
+  "#size-cells":
+    # Required to define a client address.
+    const: 0
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    # PECI reference clock.
+    maxItems: 1
+
+  cmd-timeout-ms:
+    # Command timeout in units of ms.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 1
+        maximum: 60000
+        default: 1000
+
+  pull-down:
+    description: |
+      Defines the PECI I/O internal pull down operation.
+        0: pull down always enable
+        1: pull down only during transactions.
+        2: pull down always disable.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 2
+        default: 0
+
+  host-neg-bit-rate:
+    description: |
+      Define host negotiation bit rate divider.
+      the host negotiation bit rate calculate with formula:
+      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 7
+        maximum: 31
+        default: 15
+
+  high-volt-range:
+    description: |
+      Adapts PECI I/O interface to voltage range.
+        0: PECI I/O interface voltage range of 0.8-1.06V (default)
+        1: PECI I/O interface voltage range of 0.95-1.26V
+    type: boolean
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - interrupts
+  - clocks
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+    peci: bus@100000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x100000 0x200>;
+
+        peci0: peci-bus@0 {
+            compatible = "nuvoton,npcm750-peci";
+            reg = <0x0 0x200>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&clk NPCM7XX_CLK_APB3>;
+            cmd-timeout-ms = <1000>;
+            pull-down = <0>;
+            host-neg-bit-rate = <15>;
+        };
+    };
+...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	linux-arm-kernel

From: Tomer Maimon <tmaimon77@gmail.com>

Added device tree binding documentation for Nuvoton BMC
NPCM Platform Environment Control Interface(PECI).

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Newly added in v11.

 .../devicetree/bindings/peci/peci-npcm.yaml   | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml

diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
new file mode 100644
index 000000000000..bcd5626e68e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NPCM PECI Bus Device Tree Bindings
+
+maintainers:
+  - Tomer Maimon <tmaimon77@gmail.com>
+
+properties:
+  compatible:
+    const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    # Required to define a client address.
+    const: 1
+
+  "#size-cells":
+    # Required to define a client address.
+    const: 0
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    # PECI reference clock.
+    maxItems: 1
+
+  cmd-timeout-ms:
+    # Command timeout in units of ms.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 1
+        maximum: 60000
+        default: 1000
+
+  pull-down:
+    description: |
+      Defines the PECI I/O internal pull down operation.
+        0: pull down always enable
+        1: pull down only during transactions.
+        2: pull down always disable.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 0
+        maximum: 2
+        default: 0
+
+  host-neg-bit-rate:
+    description: |
+      Define host negotiation bit rate divider.
+      the host negotiation bit rate calculate with formula:
+      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - minimum: 7
+        maximum: 31
+        default: 15
+
+  high-volt-range:
+    description: |
+      Adapts PECI I/O interface to voltage range.
+        0: PECI I/O interface voltage range of 0.8-1.06V (default)
+        1: PECI I/O interface voltage range of 0.95-1.26V
+    type: boolean
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - interrupts
+  - clocks
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+    peci: bus@100000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x100000 0x200>;
+
+        peci0: peci-bus@0 {
+            compatible = "nuvoton,npcm750-peci";
+            reg = <0x0 0x200>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&clk NPCM7XX_CLK_APB3>;
+            cmd-timeout-ms = <1000>;
+            pull-down = <0>;
+            host-neg-bit-rate = <15>;
+        };
+    };
+...
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 08/14] ARM: dts: npcm7xx: Add PECI node
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo

From: Tomer Maimon <tmaimon77@gmail.com>

This commit adds PECI node for npcm7xx.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Newly added in v11.

 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
index d2d0761295a4..526c56770388 100644
--- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
+++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
@@ -116,6 +116,13 @@
 			interrupt-parent = <&gic>;
 			ranges = <0x0 0xf0000000 0x00300000>;
 
+			peci: bus@100000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x100000 0x200>;
+			};
+
 			timer0: timer@8000 {
 				compatible = "nuvoton,npcm750-timer";
 				interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
@@ -185,3 +192,15 @@
 		};
 	};
 };
+
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "nuvoton,npcm750-peci";
+		reg = <0x0 0x200>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk NPCM7XX_CLK_APB3>;
+		status = "disabled";
+	};
+};
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 08/14] ARM: dts: npcm7xx: Add PECI node
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	linux-arm-kernel

From: Tomer Maimon <tmaimon77@gmail.com>

This commit adds PECI node for npcm7xx.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Newly added in v11.

 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
index d2d0761295a4..526c56770388 100644
--- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
+++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
@@ -116,6 +116,13 @@
 			interrupt-parent = <&gic>;
 			ranges = <0x0 0xf0000000 0x00300000>;
 
+			peci: bus@100000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0x0 0x100000 0x200>;
+			};
+
 			timer0: timer@8000 {
 				compatible = "nuvoton,npcm750-timer";
 				interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
@@ -185,3 +192,15 @@
 		};
 	};
 };
+
+&peci {
+	peci0: peci-bus@0 {
+		compatible = "nuvoton,npcm750-peci";
+		reg = <0x0 0x200>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk NPCM7XX_CLK_APB3>;
+		status = "disabled";
+	};
+};
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 09/14] peci: npcm: add NPCM PECI driver
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo

From: Tomer Maimon <tmaimon77@gmail.com>

Add support for the Nuvoton NPCM BMC hardware to the Platform
Environment Control Interface (PECI) subsystem.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Newly added in v11Split out peci-dev module from peci-core module.

 drivers/peci/busses/Kconfig     |  13 +
 drivers/peci/busses/Makefile    |   1 +
 drivers/peci/busses/peci-npcm.c | 407 ++++++++++++++++++++++++++++++++
 3 files changed, 421 insertions(+)
 create mode 100644 drivers/peci/busses/peci-npcm.c

diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
index 2b2540221b36..bfacafb7a7ba 100644
--- a/drivers/peci/busses/Kconfig
+++ b/drivers/peci/busses/Kconfig
@@ -16,4 +16,17 @@ config PECI_ASPEED
 	  This support is also available as a module. If so, the module
 	  will be called peci-aspeed.
 
+config PECI_NPCM
+	tristate "Nuvoton NPCM PECI support"
+	select REGMAP_MMIO
+	depends on OF
+	depends on ARCH_NPCM || COMPILE_TEST
+	depends on PECI
+	help
+	  Say Y here if you want support for the Platform Environment Control
+	  Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-npcm.
+
 endmenu
diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
index 69e31dfaca19..aa8ce3ae5947 100644
--- a/drivers/peci/busses/Makefile
+++ b/drivers/peci/busses/Makefile
@@ -4,3 +4,4 @@
 #
 
 obj-$(CONFIG_PECI_ASPEED)	+= peci-aspeed.o
+obj-$(CONFIG_PECI_NPCM)		+= peci-npcm.o
diff --git a/drivers/peci/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c
new file mode 100644
index 000000000000..36d061e6e53d
--- /dev/null
+++ b/drivers/peci/busses/peci-npcm.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset.h>
+
+/* NPCM7xx GCR module */
+#define NPCM7XX_INTCR3_OFFSET		0x9C
+#define NPCM7XX_INTCR3_PECIVSEL		BIT(19)
+
+/* NPCM PECI Registers */
+#define NPCM_PECI_CTL_STS	0x00
+#define NPCM_PECI_RD_LENGTH	0x04
+#define NPCM_PECI_ADDR		0x08
+#define NPCM_PECI_CMD		0x0C
+#define NPCM_PECI_CTL2		0x10
+#define NPCM_PECI_WR_LENGTH	0x1C
+#define NPCM_PECI_PDDR		0x2C
+#define NPCM_PECI_DAT_INOUT(n)	(0x100 + ((n) * 4))
+
+#define NPCM_PECI_MAX_REG	0x200
+
+/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
+#define NPCM_PECI_CTRL_DONE_INT_EN	BIT(6)
+#define NPCM_PECI_CTRL_ABRT_ERR		BIT(4)
+#define NPCM_PECI_CTRL_CRC_ERR		BIT(3)
+#define NPCM_PECI_CTRL_DONE		BIT(1)
+#define NPCM_PECI_CTRL_START_BUSY	BIT(0)
+
+/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
+#define NPCM_PECI_RD_LEN_MASK		GENMASK(6, 0)
+
+/* NPCM_PECI_CMD - 0x10 : Command Register */
+#define NPCM_PECI_CTL2_MASK		GENMASK(7, 6)
+
+/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
+#define NPCM_PECI_WR_LEN_MASK		GENMASK(6, 0)
+
+/* NPCM_PECI_PDDR - 0x2C : Command Register */
+#define NPCM_PECI_PDDR_MASK		GENMASK(4, 0)
+
+#define NPCM_PECI_INT_MASK	(NPCM_PECI_CTRL_ABRT_ERR | \
+				NPCM_PECI_CTRL_CRC_ERR | \
+				NPCM_PECI_CTRL_DONE)
+
+#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC	50000
+#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC	10000
+#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT	1000
+#define NPCM_PECI_CMD_TIMEOUT_MS_MAX		60000
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX		31
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN		7
+#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT	15
+#define NPCM_PECI_PULL_DOWN_DEFAULT		0
+#define NPCM_PECI_PULL_DOWN_MAX			2
+
+struct npcm_peci {
+	u32			cmd_timeout_ms;
+	u32			host_bit_rate;
+	struct completion	xfer_complete;
+	struct regmap		*gcr_regmap;
+	struct peci_adapter	*adapter;
+	struct regmap		*regmap;
+	u32			status;
+	spinlock_t		lock; /* to sync completion status handling */
+	struct device		*dev;
+	struct clk		*clk;
+	int			irq;
+};
+
+static int npcm_peci_xfer_native(struct npcm_peci *priv,
+				 struct peci_xfer_msg *msg)
+{
+	long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+	unsigned long flags;
+	unsigned int msg_rd;
+	u32 cmd_sts;
+	int i, rc;
+
+	/* Check command sts and bus idle state */
+	rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+				      !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+				      NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+				      NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (rc)
+		return rc; /* -ETIMEDOUT */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reinit_completion(&priv->xfer_complete);
+
+	regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr);
+	regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH,
+		     NPCM_PECI_WR_LEN_MASK & msg->rx_len);
+	regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH,
+		     NPCM_PECI_WR_LEN_MASK & msg->tx_len);
+
+	if (msg->tx_len) {
+		regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]);
+
+		for (i = 0; i < (msg->tx_len - 1); i++)
+			regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i),
+				     msg->tx_buf[i + 1]);
+	}
+
+	priv->status = 0;
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			   NPCM_PECI_CTRL_START_BUSY,
+			   NPCM_PECI_CTRL_START_BUSY);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+							timeout);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
+
+	if (err <= 0 || priv->status  != NPCM_PECI_CTRL_DONE) {
+		if (err < 0) { /* -ERESTARTSYS */
+			rc = (int)err;
+			goto err_irqrestore;
+		} else if (err == 0) {
+			dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+			rc = -ETIMEDOUT;
+			goto err_irqrestore;
+		}
+
+		dev_dbg(priv->dev, "No valid response!\n");
+		rc = -EIO;
+		goto err_irqrestore;
+	}
+
+	for (i = 0; i < msg->rx_len; i++) {
+		regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
+		msg->rx_buf[i] = (u8)msg_rd;
+	}
+
+err_irqrestore:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return rc;
+}
+
+static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
+{
+	struct npcm_peci *priv = arg;
+	u32 status_ack = 0;
+	u32 status;
+
+	spin_lock(&priv->lock);
+	regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
+	priv->status |= (status & NPCM_PECI_INT_MASK);
+
+	if (status & NPCM_PECI_CTRL_CRC_ERR) {
+		dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+		status_ack |= NPCM_PECI_CTRL_CRC_ERR;
+	}
+
+	if (status & NPCM_PECI_CTRL_ABRT_ERR) {
+		dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n");
+		status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
+	}
+
+	/*
+	 * All commands should be ended up with a NPCM_PECI_CTRL_DONE
+	 * bit set even in an error case.
+	 */
+	if (status & NPCM_PECI_CTRL_DONE) {
+		dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n");
+		status_ack |= NPCM_PECI_CTRL_DONE;
+		complete(&priv->xfer_complete);
+	}
+
+	regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			  NPCM_PECI_INT_MASK, status_ack);
+
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+}
+
+static int npcm_peci_init_ctrl(struct npcm_peci *priv)
+{
+	u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0;
+	int ret;
+
+	priv->clk = devm_clk_get(priv->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get clk source.\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(priv->dev, "Failed to enable clock.\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+				   &priv->cmd_timeout_ms);
+	if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
+	    priv->cmd_timeout_ms == 0) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "cmd-timeout-ms not found, use default : %u\n",
+				 NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+				 priv->cmd_timeout_ms,
+				 NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+
+		priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+	}
+
+	if (of_device_is_compatible(priv->dev->of_node,
+				    "nuvoton,npcm750-peci")) {
+		priv->gcr_regmap = syscon_regmap_lookup_by_compatible
+			("nuvoton,npcm750-gcr");
+		if (!IS_ERR(priv->gcr_regmap)) {
+			bool volt = of_property_read_bool(priv->dev->of_node,
+							  "high-volt-range");
+			if (volt)
+				regmap_update_bits(priv->gcr_regmap,
+						   NPCM7XX_INTCR3_OFFSET,
+						   NPCM7XX_INTCR3_PECIVSEL,
+						   NPCM7XX_INTCR3_PECIVSEL);
+			else
+				regmap_update_bits(priv->gcr_regmap,
+						   NPCM7XX_INTCR3_OFFSET,
+						   NPCM7XX_INTCR3_PECIVSEL, 0);
+		}
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "pull-down",
+				   &pull_down);
+	if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "pull-down not found, use default : %u\n",
+				 NPCM_PECI_PULL_DOWN_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid pull-down : %u. Use default : %u\n",
+				 pull_down,
+				 NPCM_PECI_PULL_DOWN_DEFAULT);
+		pull_down = NPCM_PECI_PULL_DOWN_DEFAULT;
+	}
+
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
+			   pull_down << 6);
+
+	ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate",
+				   &host_neg_bit_rate);
+	if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX ||
+	    host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "host-neg-bit-rate not found, use default : %u\n",
+				 NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid host-neg-bit-rate : %u. Use default : %u\n",
+				 host_neg_bit_rate,
+				 NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+		host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT;
+	}
+
+	regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
+			   host_neg_bit_rate);
+
+	priv->host_bit_rate = clk_get_rate(priv->clk) /
+		(4 * (host_neg_bit_rate + 1));
+
+	ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+				       !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+				       NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+				       NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (ret)
+		return ret; /* -ETIMEDOUT */
+
+	/* PECI interrupt enable */
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			   NPCM_PECI_CTRL_DONE_INT_EN,
+			   NPCM_PECI_CTRL_DONE_INT_EN);
+
+	return 0;
+}
+
+static const struct regmap_config npcm_peci_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = NPCM_PECI_MAX_REG,
+	.fast_io = true,
+};
+
+static int npcm_peci_xfer(struct peci_adapter *adapter,
+			  struct peci_xfer_msg *msg)
+{
+	struct npcm_peci *priv = peci_get_adapdata(adapter);
+
+	return npcm_peci_xfer_native(priv, msg);
+}
+
+static int npcm_peci_probe(struct platform_device *pdev)
+{
+	struct peci_adapter *adapter;
+	struct npcm_peci *priv;
+	void __iomem *base;
+	int ret;
+
+	adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+	if (!adapter)
+		return -ENOMEM;
+
+	priv = peci_get_adapdata(adapter);
+	priv->adapter = adapter;
+	priv->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, priv);
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base)) {
+		ret = PTR_ERR(base);
+		goto err_put_adapter_dev;
+	}
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					     &npcm_peci_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		goto err_put_adapter_dev;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (!priv->irq) {
+		ret = -ENODEV;
+		goto err_put_adapter_dev;
+	}
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
+			       0, "peci-npcm-irq", priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	init_completion(&priv->xfer_complete);
+	spin_lock_init(&priv->lock);
+
+	priv->adapter->owner = THIS_MODULE;
+	priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+	strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+	priv->adapter->xfer = npcm_peci_xfer;
+
+	ret = npcm_peci_init_ctrl(priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	ret = peci_add_adapter(priv->adapter);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	dev_info(&pdev->dev, "peci bus %d registered, host negotiation bit rate %dHz",
+		 priv->adapter->nr, priv->host_bit_rate);
+
+	return 0;
+
+err_put_adapter_dev:
+	put_device(&adapter->dev);
+	return ret;
+}
+
+static int npcm_peci_remove(struct platform_device *pdev)
+{
+	struct npcm_peci *priv = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(priv->clk);
+	peci_del_adapter(priv->adapter);
+	of_node_put(priv->adapter->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id npcm_peci_of_table[] = {
+	{ .compatible = "nuvoton,npcm750-peci", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
+
+static struct platform_driver npcm_peci_driver = {
+	.probe  = npcm_peci_probe,
+	.remove = npcm_peci_remove,
+	.driver = {
+		.name           = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(npcm_peci_of_table),
+	},
+};
+module_platform_driver(npcm_peci_driver);
+
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 09/14] peci: npcm: add NPCM PECI driver
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	linux-arm-kernel

From: Tomer Maimon <tmaimon77@gmail.com>

Add support for the Nuvoton NPCM BMC hardware to the Platform
Environment Control Interface (PECI) subsystem.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Newly added in v11Split out peci-dev module from peci-core module.

 drivers/peci/busses/Kconfig     |  13 +
 drivers/peci/busses/Makefile    |   1 +
 drivers/peci/busses/peci-npcm.c | 407 ++++++++++++++++++++++++++++++++
 3 files changed, 421 insertions(+)
 create mode 100644 drivers/peci/busses/peci-npcm.c

diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig
index 2b2540221b36..bfacafb7a7ba 100644
--- a/drivers/peci/busses/Kconfig
+++ b/drivers/peci/busses/Kconfig
@@ -16,4 +16,17 @@ config PECI_ASPEED
 	  This support is also available as a module. If so, the module
 	  will be called peci-aspeed.
 
+config PECI_NPCM
+	tristate "Nuvoton NPCM PECI support"
+	select REGMAP_MMIO
+	depends on OF
+	depends on ARCH_NPCM || COMPILE_TEST
+	depends on PECI
+	help
+	  Say Y here if you want support for the Platform Environment Control
+	  Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs.
+
+	  This support is also available as a module. If so, the module
+	  will be called peci-npcm.
+
 endmenu
diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile
index 69e31dfaca19..aa8ce3ae5947 100644
--- a/drivers/peci/busses/Makefile
+++ b/drivers/peci/busses/Makefile
@@ -4,3 +4,4 @@
 #
 
 obj-$(CONFIG_PECI_ASPEED)	+= peci-aspeed.o
+obj-$(CONFIG_PECI_NPCM)		+= peci-npcm.o
diff --git a/drivers/peci/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c
new file mode 100644
index 000000000000..36d061e6e53d
--- /dev/null
+++ b/drivers/peci/busses/peci-npcm.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset.h>
+
+/* NPCM7xx GCR module */
+#define NPCM7XX_INTCR3_OFFSET		0x9C
+#define NPCM7XX_INTCR3_PECIVSEL		BIT(19)
+
+/* NPCM PECI Registers */
+#define NPCM_PECI_CTL_STS	0x00
+#define NPCM_PECI_RD_LENGTH	0x04
+#define NPCM_PECI_ADDR		0x08
+#define NPCM_PECI_CMD		0x0C
+#define NPCM_PECI_CTL2		0x10
+#define NPCM_PECI_WR_LENGTH	0x1C
+#define NPCM_PECI_PDDR		0x2C
+#define NPCM_PECI_DAT_INOUT(n)	(0x100 + ((n) * 4))
+
+#define NPCM_PECI_MAX_REG	0x200
+
+/* NPCM_PECI_CTL_STS - 0x00 : Control Register */
+#define NPCM_PECI_CTRL_DONE_INT_EN	BIT(6)
+#define NPCM_PECI_CTRL_ABRT_ERR		BIT(4)
+#define NPCM_PECI_CTRL_CRC_ERR		BIT(3)
+#define NPCM_PECI_CTRL_DONE		BIT(1)
+#define NPCM_PECI_CTRL_START_BUSY	BIT(0)
+
+/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */
+#define NPCM_PECI_RD_LEN_MASK		GENMASK(6, 0)
+
+/* NPCM_PECI_CMD - 0x10 : Command Register */
+#define NPCM_PECI_CTL2_MASK		GENMASK(7, 6)
+
+/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */
+#define NPCM_PECI_WR_LEN_MASK		GENMASK(6, 0)
+
+/* NPCM_PECI_PDDR - 0x2C : Command Register */
+#define NPCM_PECI_PDDR_MASK		GENMASK(4, 0)
+
+#define NPCM_PECI_INT_MASK	(NPCM_PECI_CTRL_ABRT_ERR | \
+				NPCM_PECI_CTRL_CRC_ERR | \
+				NPCM_PECI_CTRL_DONE)
+
+#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC	50000
+#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC	10000
+#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT	1000
+#define NPCM_PECI_CMD_TIMEOUT_MS_MAX		60000
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX		31
+#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN		7
+#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT	15
+#define NPCM_PECI_PULL_DOWN_DEFAULT		0
+#define NPCM_PECI_PULL_DOWN_MAX			2
+
+struct npcm_peci {
+	u32			cmd_timeout_ms;
+	u32			host_bit_rate;
+	struct completion	xfer_complete;
+	struct regmap		*gcr_regmap;
+	struct peci_adapter	*adapter;
+	struct regmap		*regmap;
+	u32			status;
+	spinlock_t		lock; /* to sync completion status handling */
+	struct device		*dev;
+	struct clk		*clk;
+	int			irq;
+};
+
+static int npcm_peci_xfer_native(struct npcm_peci *priv,
+				 struct peci_xfer_msg *msg)
+{
+	long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+	unsigned long flags;
+	unsigned int msg_rd;
+	u32 cmd_sts;
+	int i, rc;
+
+	/* Check command sts and bus idle state */
+	rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+				      !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+				      NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+				      NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (rc)
+		return rc; /* -ETIMEDOUT */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	reinit_completion(&priv->xfer_complete);
+
+	regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr);
+	regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH,
+		     NPCM_PECI_WR_LEN_MASK & msg->rx_len);
+	regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH,
+		     NPCM_PECI_WR_LEN_MASK & msg->tx_len);
+
+	if (msg->tx_len) {
+		regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]);
+
+		for (i = 0; i < (msg->tx_len - 1); i++)
+			regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i),
+				     msg->tx_buf[i + 1]);
+	}
+
+	priv->status = 0;
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			   NPCM_PECI_CTRL_START_BUSY,
+			   NPCM_PECI_CTRL_START_BUSY);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+							timeout);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	regmap_write(priv->regmap, NPCM_PECI_CMD, 0);
+
+	if (err <= 0 || priv->status  != NPCM_PECI_CTRL_DONE) {
+		if (err < 0) { /* -ERESTARTSYS */
+			rc = (int)err;
+			goto err_irqrestore;
+		} else if (err == 0) {
+			dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+			rc = -ETIMEDOUT;
+			goto err_irqrestore;
+		}
+
+		dev_dbg(priv->dev, "No valid response!\n");
+		rc = -EIO;
+		goto err_irqrestore;
+	}
+
+	for (i = 0; i < msg->rx_len; i++) {
+		regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd);
+		msg->rx_buf[i] = (u8)msg_rd;
+	}
+
+err_irqrestore:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return rc;
+}
+
+static irqreturn_t npcm_peci_irq_handler(int irq, void *arg)
+{
+	struct npcm_peci *priv = arg;
+	u32 status_ack = 0;
+	u32 status;
+
+	spin_lock(&priv->lock);
+	regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status);
+	priv->status |= (status & NPCM_PECI_INT_MASK);
+
+	if (status & NPCM_PECI_CTRL_CRC_ERR) {
+		dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+		status_ack |= NPCM_PECI_CTRL_CRC_ERR;
+	}
+
+	if (status & NPCM_PECI_CTRL_ABRT_ERR) {
+		dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n");
+		status_ack |= NPCM_PECI_CTRL_ABRT_ERR;
+	}
+
+	/*
+	 * All commands should be ended up with a NPCM_PECI_CTRL_DONE
+	 * bit set even in an error case.
+	 */
+	if (status & NPCM_PECI_CTRL_DONE) {
+		dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n");
+		status_ack |= NPCM_PECI_CTRL_DONE;
+		complete(&priv->xfer_complete);
+	}
+
+	regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			  NPCM_PECI_INT_MASK, status_ack);
+
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+}
+
+static int npcm_peci_init_ctrl(struct npcm_peci *priv)
+{
+	u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0;
+	int ret;
+
+	priv->clk = devm_clk_get(priv->dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		dev_err(priv->dev, "Failed to get clk source.\n");
+		return PTR_ERR(priv->clk);
+	}
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(priv->dev, "Failed to enable clock.\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+				   &priv->cmd_timeout_ms);
+	if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX ||
+	    priv->cmd_timeout_ms == 0) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "cmd-timeout-ms not found, use default : %u\n",
+				 NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+				 priv->cmd_timeout_ms,
+				 NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT);
+
+		priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT;
+	}
+
+	if (of_device_is_compatible(priv->dev->of_node,
+				    "nuvoton,npcm750-peci")) {
+		priv->gcr_regmap = syscon_regmap_lookup_by_compatible
+			("nuvoton,npcm750-gcr");
+		if (!IS_ERR(priv->gcr_regmap)) {
+			bool volt = of_property_read_bool(priv->dev->of_node,
+							  "high-volt-range");
+			if (volt)
+				regmap_update_bits(priv->gcr_regmap,
+						   NPCM7XX_INTCR3_OFFSET,
+						   NPCM7XX_INTCR3_PECIVSEL,
+						   NPCM7XX_INTCR3_PECIVSEL);
+			else
+				regmap_update_bits(priv->gcr_regmap,
+						   NPCM7XX_INTCR3_OFFSET,
+						   NPCM7XX_INTCR3_PECIVSEL, 0);
+		}
+	}
+
+	ret = of_property_read_u32(priv->dev->of_node, "pull-down",
+				   &pull_down);
+	if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "pull-down not found, use default : %u\n",
+				 NPCM_PECI_PULL_DOWN_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid pull-down : %u. Use default : %u\n",
+				 pull_down,
+				 NPCM_PECI_PULL_DOWN_DEFAULT);
+		pull_down = NPCM_PECI_PULL_DOWN_DEFAULT;
+	}
+
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK,
+			   pull_down << 6);
+
+	ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate",
+				   &host_neg_bit_rate);
+	if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX ||
+	    host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) {
+		if (ret)
+			dev_warn(priv->dev,
+				 "host-neg-bit-rate not found, use default : %u\n",
+				 NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+		else
+			dev_warn(priv->dev,
+				 "Invalid host-neg-bit-rate : %u. Use default : %u\n",
+				 host_neg_bit_rate,
+				 NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT);
+		host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT;
+	}
+
+	regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK,
+			   host_neg_bit_rate);
+
+	priv->host_bit_rate = clk_get_rate(priv->clk) /
+		(4 * (host_neg_bit_rate + 1));
+
+	ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts,
+				       !(cmd_sts & NPCM_PECI_CTRL_START_BUSY),
+				       NPCM_PECI_IDLE_CHECK_INTERVAL_USEC,
+				       NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC);
+	if (ret)
+		return ret; /* -ETIMEDOUT */
+
+	/* PECI interrupt enable */
+	regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS,
+			   NPCM_PECI_CTRL_DONE_INT_EN,
+			   NPCM_PECI_CTRL_DONE_INT_EN);
+
+	return 0;
+}
+
+static const struct regmap_config npcm_peci_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = NPCM_PECI_MAX_REG,
+	.fast_io = true,
+};
+
+static int npcm_peci_xfer(struct peci_adapter *adapter,
+			  struct peci_xfer_msg *msg)
+{
+	struct npcm_peci *priv = peci_get_adapdata(adapter);
+
+	return npcm_peci_xfer_native(priv, msg);
+}
+
+static int npcm_peci_probe(struct platform_device *pdev)
+{
+	struct peci_adapter *adapter;
+	struct npcm_peci *priv;
+	void __iomem *base;
+	int ret;
+
+	adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+	if (!adapter)
+		return -ENOMEM;
+
+	priv = peci_get_adapdata(adapter);
+	priv->adapter = adapter;
+	priv->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, priv);
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base)) {
+		ret = PTR_ERR(base);
+		goto err_put_adapter_dev;
+	}
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					     &npcm_peci_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		goto err_put_adapter_dev;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (!priv->irq) {
+		ret = -ENODEV;
+		goto err_put_adapter_dev;
+	}
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
+			       0, "peci-npcm-irq", priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	init_completion(&priv->xfer_complete);
+	spin_lock_init(&priv->lock);
+
+	priv->adapter->owner = THIS_MODULE;
+	priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+	strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+	priv->adapter->xfer = npcm_peci_xfer;
+
+	ret = npcm_peci_init_ctrl(priv);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	ret = peci_add_adapter(priv->adapter);
+	if (ret)
+		goto err_put_adapter_dev;
+
+	dev_info(&pdev->dev, "peci bus %d registered, host negotiation bit rate %dHz",
+		 priv->adapter->nr, priv->host_bit_rate);
+
+	return 0;
+
+err_put_adapter_dev:
+	put_device(&adapter->dev);
+	return ret;
+}
+
+static int npcm_peci_remove(struct platform_device *pdev)
+{
+	struct npcm_peci *priv = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(priv->clk);
+	peci_del_adapter(priv->adapter);
+	of_node_put(priv->adapter->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id npcm_peci_of_table[] = {
+	{ .compatible = "nuvoton,npcm750-peci", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, npcm_peci_of_table);
+
+static struct platform_driver npcm_peci_driver = {
+	.probe  = npcm_peci_probe,
+	.remove = npcm_peci_remove,
+	.driver = {
+		.name           = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(npcm_peci_of_table),
+	},
+};
+module_platform_driver(npcm_peci_driver);
+
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 10/14] dt-bindings: mfd: Add Intel PECI client bindings document
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, James Feist, Jason M Biils, Vernon Mauery

This commit adds Intel PECI client bindings document.

Cc: Lee Jones <lee.jones@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Changed documents format to DT schema format so I dropped all review tags.
  Please review it again.

 .../bindings/mfd/intel,peci-client.yaml       | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/intel,peci-client.yaml

diff --git a/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml b/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
new file mode 100644
index 000000000000..7baddce0a92c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/intel,peci-client.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel PECI Client Device Tree Bindings
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+description: |
+  PECI (Platform Environment Control Interface) is a one-wire bus interface
+  that provides a communication channel from PECI clients in Intel processors
+  and chipset components to external monitoring or control devices. PECI is
+  designed to support the following sideband functions:
+  - Processor and DRAM thermal management
+  - Platform Manageability
+  - Processor Interface Tuning and Diagnostics
+  - Failure Analysis
+
+properties:
+  compatible:
+    const: intel,peci-client
+
+  reg:
+    description: |
+      Address of a client CPU. According to the PECI specification, client
+      addresses start from 0x30.
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x60>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+
+            peci-client@30 {
+                compatible = "intel,peci-client";
+                reg = <0x30>;
+            };
+
+            peci-client@31 {
+                compatible = "intel,peci-client";
+                reg = <0x31>;
+            };
+        };
+    };
+...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 10/14] dt-bindings: mfd: Add Intel PECI client bindings document
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, Vernon Mauery,
	openbmc, James Feist, Jason M Biils, linux-arm-kernel

This commit adds Intel PECI client bindings document.

Cc: Lee Jones <lee.jones@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Changed documents format to DT schema format so I dropped all review tags.
  Please review it again.

 .../bindings/mfd/intel,peci-client.yaml       | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/intel,peci-client.yaml

diff --git a/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml b/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
new file mode 100644
index 000000000000..7baddce0a92c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/intel,peci-client.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/intel,peci-client.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel PECI Client Device Tree Bindings
+
+maintainers:
+  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+description: |
+  PECI (Platform Environment Control Interface) is a one-wire bus interface
+  that provides a communication channel from PECI clients in Intel processors
+  and chipset components to external monitoring or control devices. PECI is
+  designed to support the following sideband functions:
+  - Processor and DRAM thermal management
+  - Platform Manageability
+  - Processor Interface Tuning and Diagnostics
+  - Failure Analysis
+
+properties:
+  compatible:
+    const: intel,peci-client
+
+  reg:
+    description: |
+      Address of a client CPU. According to the PECI specification, client
+      addresses start from 0x30.
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/ast2600-clock.h>
+    peci: bus@1e78b000 {
+        compatible = "simple-bus";
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges = <0x0 0x1e78b000 0x60>;
+
+        peci0: peci-bus@0 {
+            compatible = "aspeed,ast2600-peci";
+            reg = <0x0 0x100>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
+            resets = <&syscon ASPEED_RESET_PECI>;
+            clock-frequency = <24000000>;
+
+            peci-client@30 {
+                compatible = "intel,peci-client";
+                reg = <0x30>;
+            };
+
+            peci-client@31 {
+                compatible = "intel,peci-client";
+                reg = <0x31>;
+            };
+        };
+    };
+...
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, James Feist, Jason M Biils, Vernon Mauery

This commit adds Intel PECI client driver.

Cc: Lee Jones <lee.jones@linaro.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Fixed minor style issues.

 drivers/mfd/Kconfig                   |  17 +++
 drivers/mfd/Makefile                  |   1 +
 drivers/mfd/intel-peci-client.c       | 149 ++++++++++++++++++++++++++
 include/linux/mfd/intel-peci-client.h | 117 ++++++++++++++++++++
 4 files changed, 284 insertions(+)
 create mode 100644 drivers/mfd/intel-peci-client.c
 create mode 100644 include/linux/mfd/intel-peci-client.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 420900852166..7022e54a4703 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -632,6 +632,23 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_INTEL_PECI_CLIENT
+	tristate "Intel PECI client"
+	depends on (PECI || COMPILE_TEST)
+	select MFD_CORE
+	help
+	  If you say yes to this option, support will be included for the
+	  Intel PECI (Platform Environment Control Interface) client. PECI is a
+	  one-wire bus interface that provides a communication channel from PECI
+	  clients in Intel processors and chipset components to external
+	  monitoring or control devices.
+
+	  Additional drivers must be enabled in order to use the functionality
+	  of the device.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called intel-peci-client.
+
 config MFD_IPAQ_MICRO
 	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
 	depends on SA1100_H3100 || SA1100_H3600
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index aed99f08739f..91c6fda5cec6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,6 +211,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
 obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
+obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
new file mode 100644
index 000000000000..18bf0af0e09e
--- /dev/null
+++ b/drivers/mfd/intel-peci-client.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+
+#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
+#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
+#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
+#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
+
+#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
+#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
+#define LOWER_BYTE_MASK        GENMASK(7, 0)
+#define UPPER_BYTE_MASK        GENMASK(16, 8)
+
+static struct mfd_cell peci_functions[] = {
+	{ .name = "peci-cputemp", },
+	{ .name = "peci-dimmtemp", },
+	/* TODO: Add additional PECI sideband functions into here */
+};
+
+static const struct cpu_gen_info cpu_gen_info_table[] = {
+	{ /* Haswell Xeon */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_HASWELL_X,
+		.core_max      = CORE_MAX_ON_HSX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
+	{ /* Broadwell Xeon */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_BROADWELL_X,
+		.core_max      = CORE_MAX_ON_BDX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
+	{ /* Skylake Xeon */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_SKYLAKE_X,
+		.core_max      = CORE_MAX_ON_SKX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
+	{ /* Skylake Xeon D */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_SKYLAKE_XD,
+		.core_max      = CORE_MAX_ON_SKXD,
+		.chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKXD },
+};
+
+static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+{
+	struct device *dev = &priv->client->dev;
+	u32 cpu_id;
+	u16 family;
+	u8 model;
+	int ret;
+	int i;
+
+	ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
+			      &cpu_id);
+	if (ret)
+		return ret;
+
+	family = FIELD_PREP(LOWER_BYTE_MASK,
+			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
+		 FIELD_PREP(UPPER_BYTE_MASK,
+			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
+	model = FIELD_PREP(LOWER_NIBBLE_MASK,
+			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
+		FIELD_PREP(UPPER_NIBBLE_MASK,
+			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
+
+	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
+		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
+
+		if (family == cpu_info->family && model == cpu_info->model) {
+			priv->gen_info = cpu_info;
+			break;
+		}
+	}
+
+	if (!priv->gen_info) {
+		dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+static int peci_client_probe(struct peci_client *client)
+{
+	struct device *dev = &client->dev;
+	struct peci_client_manager *priv;
+	uint cpu_no;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->client = client;
+	cpu_no = client->addr - PECI_BASE_ADDR;
+
+	ret = peci_client_get_cpu_gen_info(priv);
+	if (ret)
+		return ret;
+
+	ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
+				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register child devices: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id peci_client_of_table[] = {
+	{ .compatible = "intel,peci-client" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, peci_client_of_table);
+#endif /* CONFIG_OF */
+
+static const struct peci_device_id peci_client_ids[] = {
+	{ .name = "peci-client" },
+	{ }
+};
+MODULE_DEVICE_TABLE(peci, peci_client_ids);
+
+static struct peci_driver peci_client_driver = {
+	.probe    = peci_client_probe,
+	.id_table = peci_client_ids,
+	.driver   = {
+		.name           = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(peci_client_of_table),
+	},
+};
+module_peci_driver(peci_client_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI client driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
new file mode 100644
index 000000000000..9854303bbc26
--- /dev/null
+++ b/include/linux/mfd/intel-peci-client.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
+#define __LINUX_MFD_INTEL_PECI_CLIENT_H
+
+#include <linux/peci.h>
+
+#if IS_ENABLED(CONFIG_X86)
+#include <asm/intel-family.h>
+#else
+/*
+ * Architectures other than x86 cannot include the header file so define these
+ * at here. These are needed for detecting type of client x86 CPUs behind a PECI
+ * connection.
+ */
+#define INTEL_FAM6_HASWELL_X		0x3F
+#define INTEL_FAM6_BROADWELL_X		0x4F
+#define INTEL_FAM6_SKYLAKE_X		0x55
+#define INTEL_FAM6_SKYLAKE_XD		0x56
+#endif
+
+#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
+#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
+#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
+
+#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
+#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
+#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
+
+#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
+#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
+#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
+
+#define CORE_MAX_ON_SKXD       16 /* Max number of cores on Skylake D */
+#define CHAN_RANK_MAX_ON_SKXD  2  /* Max number of channel ranks on Skylake D */
+#define DIMM_IDX_MAX_ON_SKXD   2  /* Max DIMM index per channel on Skylake D */
+
+#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
+#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
+#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
+#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
+
+/**
+ * struct cpu_gen_info - CPU generation specific information
+ * @family: CPU family ID
+ * @model: CPU model
+ * @core_max: max number of cores
+ * @chan_rank_max: max number of channel ranks
+ * @dimm_idx_max: max number of DIMM indices
+ *
+ * CPU generation specific information to identify maximum number of cores and
+ * DIMM slots.
+ */
+struct cpu_gen_info {
+	u16  family;
+	u8   model;
+	uint core_max;
+	uint chan_rank_max;
+	uint dimm_idx_max;
+};
+
+/**
+ * struct peci_client_manager - PECI client manager information
+ * @client; pointer to the PECI client
+ * @name: PECI client manager name
+ * @gen_info: CPU generation info of the detected CPU
+ *
+ * PECI client manager information for managing PECI sideband functions on a CPU
+ * client.
+ */
+struct peci_client_manager {
+	struct peci_client *client;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+};
+
+/**
+ * peci_client_read_package_config - read from the Package Configuration Space
+ * @priv: driver private data structure
+ * @index: encoding index for the requested service
+ * @param: parameter to specify the exact data being requested
+ * @data: data buffer to store the result
+ * Context: can sleep
+ *
+ * A generic PECI command that provides read access to the
+ * "Package Configuration Space" that is maintained by the PCU, including
+ * various power and thermal management functions. Typical PCS read services
+ * supported by the processor may include access to temperature data, energy
+ * status, run time information, DIMM temperatures and so on.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static inline int
+peci_client_read_package_config(struct peci_client_manager *priv,
+				u8 index, u16 param, u8 *data)
+{
+	struct peci_rd_pkg_cfg_msg msg;
+	int ret;
+
+	msg.addr = priv->client->addr;
+	msg.index = index;
+	msg.param = param;
+	msg.rx_len = 4;
+
+	ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
+	if (msg.cc != PECI_DEV_CC_SUCCESS)
+		ret = -EAGAIN;
+	if (ret)
+		return ret;
+
+	memcpy(data, msg.pkg_config, 4);
+
+	return 0;
+}
+
+#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, Vernon Mauery,
	openbmc, James Feist, Jason M Biils, linux-arm-kernel

This commit adds Intel PECI client driver.

Cc: Lee Jones <lee.jones@linaro.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Changes since v10:
- Fixed minor style issues.

 drivers/mfd/Kconfig                   |  17 +++
 drivers/mfd/Makefile                  |   1 +
 drivers/mfd/intel-peci-client.c       | 149 ++++++++++++++++++++++++++
 include/linux/mfd/intel-peci-client.h | 117 ++++++++++++++++++++
 4 files changed, 284 insertions(+)
 create mode 100644 drivers/mfd/intel-peci-client.c
 create mode 100644 include/linux/mfd/intel-peci-client.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 420900852166..7022e54a4703 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -632,6 +632,23 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_INTEL_PECI_CLIENT
+	tristate "Intel PECI client"
+	depends on (PECI || COMPILE_TEST)
+	select MFD_CORE
+	help
+	  If you say yes to this option, support will be included for the
+	  Intel PECI (Platform Environment Control Interface) client. PECI is a
+	  one-wire bus interface that provides a communication channel from PECI
+	  clients in Intel processors and chipset components to external
+	  monitoring or control devices.
+
+	  Additional drivers must be enabled in order to use the functionality
+	  of the device.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called intel-peci-client.
+
 config MFD_IPAQ_MICRO
 	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
 	depends on SA1100_H3100 || SA1100_H3600
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index aed99f08739f..91c6fda5cec6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,6 +211,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
 obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
+obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
new file mode 100644
index 000000000000..18bf0af0e09e
--- /dev/null
+++ b/drivers/mfd/intel-peci-client.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+
+#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
+#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
+#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
+#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
+
+#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
+#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
+#define LOWER_BYTE_MASK        GENMASK(7, 0)
+#define UPPER_BYTE_MASK        GENMASK(16, 8)
+
+static struct mfd_cell peci_functions[] = {
+	{ .name = "peci-cputemp", },
+	{ .name = "peci-dimmtemp", },
+	/* TODO: Add additional PECI sideband functions into here */
+};
+
+static const struct cpu_gen_info cpu_gen_info_table[] = {
+	{ /* Haswell Xeon */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_HASWELL_X,
+		.core_max      = CORE_MAX_ON_HSX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
+	{ /* Broadwell Xeon */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_BROADWELL_X,
+		.core_max      = CORE_MAX_ON_BDX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
+	{ /* Skylake Xeon */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_SKYLAKE_X,
+		.core_max      = CORE_MAX_ON_SKX,
+		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
+	{ /* Skylake Xeon D */
+		.family        = 6, /* Family code */
+		.model         = INTEL_FAM6_SKYLAKE_XD,
+		.core_max      = CORE_MAX_ON_SKXD,
+		.chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
+		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKXD },
+};
+
+static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
+{
+	struct device *dev = &priv->client->dev;
+	u32 cpu_id;
+	u16 family;
+	u8 model;
+	int ret;
+	int i;
+
+	ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
+			      &cpu_id);
+	if (ret)
+		return ret;
+
+	family = FIELD_PREP(LOWER_BYTE_MASK,
+			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
+		 FIELD_PREP(UPPER_BYTE_MASK,
+			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
+	model = FIELD_PREP(LOWER_NIBBLE_MASK,
+			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
+		FIELD_PREP(UPPER_NIBBLE_MASK,
+			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
+
+	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
+		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
+
+		if (family == cpu_info->family && model == cpu_info->model) {
+			priv->gen_info = cpu_info;
+			break;
+		}
+	}
+
+	if (!priv->gen_info) {
+		dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+static int peci_client_probe(struct peci_client *client)
+{
+	struct device *dev = &client->dev;
+	struct peci_client_manager *priv;
+	uint cpu_no;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->client = client;
+	cpu_no = client->addr - PECI_BASE_ADDR;
+
+	ret = peci_client_get_cpu_gen_info(priv);
+	if (ret)
+		return ret;
+
+	ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
+				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register child devices: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id peci_client_of_table[] = {
+	{ .compatible = "intel,peci-client" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, peci_client_of_table);
+#endif /* CONFIG_OF */
+
+static const struct peci_device_id peci_client_ids[] = {
+	{ .name = "peci-client" },
+	{ }
+};
+MODULE_DEVICE_TABLE(peci, peci_client_ids);
+
+static struct peci_driver peci_client_driver = {
+	.probe    = peci_client_probe,
+	.id_table = peci_client_ids,
+	.driver   = {
+		.name           = KBUILD_MODNAME,
+		.of_match_table = of_match_ptr(peci_client_of_table),
+	},
+};
+module_peci_driver(peci_client_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI client driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
new file mode 100644
index 000000000000..9854303bbc26
--- /dev/null
+++ b/include/linux/mfd/intel-peci-client.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
+#define __LINUX_MFD_INTEL_PECI_CLIENT_H
+
+#include <linux/peci.h>
+
+#if IS_ENABLED(CONFIG_X86)
+#include <asm/intel-family.h>
+#else
+/*
+ * Architectures other than x86 cannot include the header file so define these
+ * at here. These are needed for detecting type of client x86 CPUs behind a PECI
+ * connection.
+ */
+#define INTEL_FAM6_HASWELL_X		0x3F
+#define INTEL_FAM6_BROADWELL_X		0x4F
+#define INTEL_FAM6_SKYLAKE_X		0x55
+#define INTEL_FAM6_SKYLAKE_XD		0x56
+#endif
+
+#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
+#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
+#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
+
+#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
+#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
+#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
+
+#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
+#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
+#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
+
+#define CORE_MAX_ON_SKXD       16 /* Max number of cores on Skylake D */
+#define CHAN_RANK_MAX_ON_SKXD  2  /* Max number of channel ranks on Skylake D */
+#define DIMM_IDX_MAX_ON_SKXD   2  /* Max DIMM index per channel on Skylake D */
+
+#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
+#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
+#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
+#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
+
+/**
+ * struct cpu_gen_info - CPU generation specific information
+ * @family: CPU family ID
+ * @model: CPU model
+ * @core_max: max number of cores
+ * @chan_rank_max: max number of channel ranks
+ * @dimm_idx_max: max number of DIMM indices
+ *
+ * CPU generation specific information to identify maximum number of cores and
+ * DIMM slots.
+ */
+struct cpu_gen_info {
+	u16  family;
+	u8   model;
+	uint core_max;
+	uint chan_rank_max;
+	uint dimm_idx_max;
+};
+
+/**
+ * struct peci_client_manager - PECI client manager information
+ * @client; pointer to the PECI client
+ * @name: PECI client manager name
+ * @gen_info: CPU generation info of the detected CPU
+ *
+ * PECI client manager information for managing PECI sideband functions on a CPU
+ * client.
+ */
+struct peci_client_manager {
+	struct peci_client *client;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+};
+
+/**
+ * peci_client_read_package_config - read from the Package Configuration Space
+ * @priv: driver private data structure
+ * @index: encoding index for the requested service
+ * @param: parameter to specify the exact data being requested
+ * @data: data buffer to store the result
+ * Context: can sleep
+ *
+ * A generic PECI command that provides read access to the
+ * "Package Configuration Space" that is maintained by the PCU, including
+ * various power and thermal management functions. Typical PCS read services
+ * supported by the processor may include access to temperature data, energy
+ * status, run time information, DIMM temperatures and so on.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static inline int
+peci_client_read_package_config(struct peci_client_manager *priv,
+				u8 index, u16 param, u8 *data)
+{
+	struct peci_rd_pkg_cfg_msg msg;
+	int ret;
+
+	msg.addr = priv->client->addr;
+	msg.index = index;
+	msg.param = param;
+	msg.rx_len = 4;
+
+	ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
+	if (msg.cc != PECI_DEV_CC_SUCCESS)
+		ret = -EAGAIN;
+	if (ret)
+		return ret;
+
+	memcpy(data, msg.pkg_config, 4);
+
+	return 0;
+}
+
+#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 12/14] Documentation: hwmon: Add documents for PECI hwmon drivers
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Jason M Biils

This commit adds hwmon documents for PECI cputemp and dimmtemp drivers.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes since v10:
- Changed documents format to rst.

 Documentation/hwmon/index.rst         |  2 +
 Documentation/hwmon/peci-cputemp.rst  | 95 +++++++++++++++++++++++++++
 Documentation/hwmon/peci-dimmtemp.rst | 60 +++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 Documentation/hwmon/peci-cputemp.rst
 create mode 100644 Documentation/hwmon/peci-dimmtemp.rst

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 43cc605741ea..8cb0265c8e9b 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -131,6 +131,8 @@ Hardware Monitoring Kernel Drivers
    pc87360
    pc87427
    pcf8591
+   peci-cputemp
+   peci-dimmtemp
    pmbus
    powr1220
    pxe1610
diff --git a/Documentation/hwmon/peci-cputemp.rst b/Documentation/hwmon/peci-cputemp.rst
new file mode 100644
index 000000000000..bf08e16dd989
--- /dev/null
+++ b/Documentation/hwmon/peci-cputemp.rst
@@ -0,0 +1,95 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver peci-cputemp
+==========================
+
+:Copyright: |copy| 2018-2019 Intel Corporation
+
+Supported chips:
+	One of Intel server CPUs listed below which is connected to a PECI bus.
+		* Intel Xeon E5/E7 v3 server processors
+			Intel Xeon E5-14xx v3 family
+			Intel Xeon E5-24xx v3 family
+			Intel Xeon E5-16xx v3 family
+			Intel Xeon E5-26xx v3 family
+			Intel Xeon E5-46xx v3 family
+			Intel Xeon E7-48xx v3 family
+			Intel Xeon E7-88xx v3 family
+		* Intel Xeon E5/E7 v4 server processors
+			Intel Xeon E5-16xx v4 family
+			Intel Xeon E5-26xx v4 family
+			Intel Xeon E5-46xx v4 family
+			Intel Xeon E7-48xx v4 family
+			Intel Xeon E7-88xx v4 family
+		* Intel Xeon Scalable server processors
+			Intel Xeon D family
+			Intel Xeon Bronze family
+			Intel Xeon Silver family
+			Intel Xeon Gold family
+			Intel Xeon Platinum family
+
+	Addresses scanned: PECI client address 0x30 - 0x37
+	Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that are
+accessible using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+``sysfs`` interface
+-------------------
+======================= =======================================================
+temp1_label		"Die"
+temp1_input		Provides current die temperature of the CPU package.
+temp1_max		Provides thermal control temperature of the CPU package
+			which is also known as Tcontrol.
+temp1_crit		Provides shutdown temperature of the CPU package which
+			is also known as the maximum processor junction
+			temperature, Tjmax or Tprochot.
+temp1_crit_hyst		Provides the hysteresis value from Tcontrol to Tjmax of
+			the CPU package.
+
+temp2_label		"DTS"
+temp2_input		Provides current DTS temperature of the CPU package.
+temp2_max		Provides thermal control temperature of the CPU package
+			which is also known as Tcontrol.
+temp2_crit		Provides shutdown temperature of the CPU package which
+			is also known as the maximum processor junction
+			temperature, Tjmax or Tprochot.
+temp2_crit_hyst		Provides the hysteresis value from Tcontrol to Tjmax of
+			the CPU package.
+
+temp3_label		"Tcontrol"
+temp3_input		Provides current Tcontrol temperature of the CPU
+			package which is also known as Fan Temperature target.
+			Indicates the relative value from thermal monitor trip
+			temperature at which fans should be engaged.
+temp3_crit		Provides Tcontrol critical value of the CPU package
+			which is same to Tjmax.
+
+temp4_label		"Tthrottle"
+temp4_input		Provides current Tthrottle temperature of the CPU
+			package. Used for throttling temperature. If this value
+			is allowed and lower than Tjmax - the throttle will
+			occur and reported at lower than Tjmax.
+
+temp5_label		"Tjmax"
+temp5_input		Provides the maximum junction temperature, Tjmax of the
+			CPU package.
+
+temp[6-N]_label		Provides string "Core X", where X is resolved core
+			number.
+temp[6-N]_input		Provides current temperature of each core.
+temp[6-N]_max		Provides thermal control temperature of the core.
+temp[6-N]_crit		Provides shutdown temperature of the core.
+temp[6-N]_crit_hyst	Provides the hysteresis value from Tcontrol to Tjmax of
+			the core.
+======================= =======================================================
\ No newline at end of file
diff --git a/Documentation/hwmon/peci-dimmtemp.rst b/Documentation/hwmon/peci-dimmtemp.rst
new file mode 100644
index 000000000000..e3581811de2d
--- /dev/null
+++ b/Documentation/hwmon/peci-dimmtemp.rst
@@ -0,0 +1,60 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver peci-dimmtemp
+===========================
+
+:Copyright: |copy| 2018-2019 Intel Corporation
+
+Supported chips:
+	One of Intel server CPUs listed below which is connected to a PECI bus.
+		* Intel Xeon E5/E7 v3 server processors
+			Intel Xeon E5-14xx v3 family
+			Intel Xeon E5-24xx v3 family
+			Intel Xeon E5-16xx v3 family
+			Intel Xeon E5-26xx v3 family
+			Intel Xeon E5-46xx v3 family
+			Intel Xeon E7-48xx v3 family
+			Intel Xeon E7-88xx v3 family
+		* Intel Xeon E5/E7 v4 server processors
+			Intel Xeon E5-16xx v4 family
+			Intel Xeon E5-26xx v4 family
+			Intel Xeon E5-46xx v4 family
+			Intel Xeon E7-48xx v4 family
+			Intel Xeon E7-88xx v4 family
+		* Intel Xeon Scalable server processors
+			Intel Xeon D family
+			Intel Xeon Bronze family
+			Intel Xeon Silver family
+			Intel Xeon Gold family
+			Intel Xeon Platinum family
+
+	Addresses scanned: PECI client address 0x30 - 0x37
+	Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of DIMM components that are accessible
+using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+``sysfs`` interface
+-------------------
+======================= =======================================================
+
+temp[N]_label		Provides string "DIMM CI", where C is DIMM channel and
+			I is DIMM index of the populated DIMM.
+temp[N]_input		Provides current temperature of the populated DIMM.
+temp[N]_max		Provides thermal control temperature of the DIMM.
+temp[N]_crit		Provides shutdown temperature of the DIMM.
+======================= =======================================================
+
+Note:
+	DIMM temperature attributes will appear when the client CPU's BIOS
+	completes memory training and testing.
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 12/14] Documentation: hwmon: Add documents for PECI hwmon drivers
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	Jason M Biils, linux-arm-kernel

This commit adds hwmon documents for PECI cputemp and dimmtemp drivers.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes since v10:
- Changed documents format to rst.

 Documentation/hwmon/index.rst         |  2 +
 Documentation/hwmon/peci-cputemp.rst  | 95 +++++++++++++++++++++++++++
 Documentation/hwmon/peci-dimmtemp.rst | 60 +++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 Documentation/hwmon/peci-cputemp.rst
 create mode 100644 Documentation/hwmon/peci-dimmtemp.rst

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 43cc605741ea..8cb0265c8e9b 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -131,6 +131,8 @@ Hardware Monitoring Kernel Drivers
    pc87360
    pc87427
    pcf8591
+   peci-cputemp
+   peci-dimmtemp
    pmbus
    powr1220
    pxe1610
diff --git a/Documentation/hwmon/peci-cputemp.rst b/Documentation/hwmon/peci-cputemp.rst
new file mode 100644
index 000000000000..bf08e16dd989
--- /dev/null
+++ b/Documentation/hwmon/peci-cputemp.rst
@@ -0,0 +1,95 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver peci-cputemp
+==========================
+
+:Copyright: |copy| 2018-2019 Intel Corporation
+
+Supported chips:
+	One of Intel server CPUs listed below which is connected to a PECI bus.
+		* Intel Xeon E5/E7 v3 server processors
+			Intel Xeon E5-14xx v3 family
+			Intel Xeon E5-24xx v3 family
+			Intel Xeon E5-16xx v3 family
+			Intel Xeon E5-26xx v3 family
+			Intel Xeon E5-46xx v3 family
+			Intel Xeon E7-48xx v3 family
+			Intel Xeon E7-88xx v3 family
+		* Intel Xeon E5/E7 v4 server processors
+			Intel Xeon E5-16xx v4 family
+			Intel Xeon E5-26xx v4 family
+			Intel Xeon E5-46xx v4 family
+			Intel Xeon E7-48xx v4 family
+			Intel Xeon E7-88xx v4 family
+		* Intel Xeon Scalable server processors
+			Intel Xeon D family
+			Intel Xeon Bronze family
+			Intel Xeon Silver family
+			Intel Xeon Gold family
+			Intel Xeon Platinum family
+
+	Addresses scanned: PECI client address 0x30 - 0x37
+	Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that are
+accessible using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+``sysfs`` interface
+-------------------
+======================= =======================================================
+temp1_label		"Die"
+temp1_input		Provides current die temperature of the CPU package.
+temp1_max		Provides thermal control temperature of the CPU package
+			which is also known as Tcontrol.
+temp1_crit		Provides shutdown temperature of the CPU package which
+			is also known as the maximum processor junction
+			temperature, Tjmax or Tprochot.
+temp1_crit_hyst		Provides the hysteresis value from Tcontrol to Tjmax of
+			the CPU package.
+
+temp2_label		"DTS"
+temp2_input		Provides current DTS temperature of the CPU package.
+temp2_max		Provides thermal control temperature of the CPU package
+			which is also known as Tcontrol.
+temp2_crit		Provides shutdown temperature of the CPU package which
+			is also known as the maximum processor junction
+			temperature, Tjmax or Tprochot.
+temp2_crit_hyst		Provides the hysteresis value from Tcontrol to Tjmax of
+			the CPU package.
+
+temp3_label		"Tcontrol"
+temp3_input		Provides current Tcontrol temperature of the CPU
+			package which is also known as Fan Temperature target.
+			Indicates the relative value from thermal monitor trip
+			temperature at which fans should be engaged.
+temp3_crit		Provides Tcontrol critical value of the CPU package
+			which is same to Tjmax.
+
+temp4_label		"Tthrottle"
+temp4_input		Provides current Tthrottle temperature of the CPU
+			package. Used for throttling temperature. If this value
+			is allowed and lower than Tjmax - the throttle will
+			occur and reported at lower than Tjmax.
+
+temp5_label		"Tjmax"
+temp5_input		Provides the maximum junction temperature, Tjmax of the
+			CPU package.
+
+temp[6-N]_label		Provides string "Core X", where X is resolved core
+			number.
+temp[6-N]_input		Provides current temperature of each core.
+temp[6-N]_max		Provides thermal control temperature of the core.
+temp[6-N]_crit		Provides shutdown temperature of the core.
+temp[6-N]_crit_hyst	Provides the hysteresis value from Tcontrol to Tjmax of
+			the core.
+======================= =======================================================
\ No newline at end of file
diff --git a/Documentation/hwmon/peci-dimmtemp.rst b/Documentation/hwmon/peci-dimmtemp.rst
new file mode 100644
index 000000000000..e3581811de2d
--- /dev/null
+++ b/Documentation/hwmon/peci-dimmtemp.rst
@@ -0,0 +1,60 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver peci-dimmtemp
+===========================
+
+:Copyright: |copy| 2018-2019 Intel Corporation
+
+Supported chips:
+	One of Intel server CPUs listed below which is connected to a PECI bus.
+		* Intel Xeon E5/E7 v3 server processors
+			Intel Xeon E5-14xx v3 family
+			Intel Xeon E5-24xx v3 family
+			Intel Xeon E5-16xx v3 family
+			Intel Xeon E5-26xx v3 family
+			Intel Xeon E5-46xx v3 family
+			Intel Xeon E7-48xx v3 family
+			Intel Xeon E7-88xx v3 family
+		* Intel Xeon E5/E7 v4 server processors
+			Intel Xeon E5-16xx v4 family
+			Intel Xeon E5-26xx v4 family
+			Intel Xeon E5-46xx v4 family
+			Intel Xeon E7-48xx v4 family
+			Intel Xeon E7-88xx v4 family
+		* Intel Xeon Scalable server processors
+			Intel Xeon D family
+			Intel Xeon Bronze family
+			Intel Xeon Silver family
+			Intel Xeon Gold family
+			Intel Xeon Platinum family
+
+	Addresses scanned: PECI client address 0x30 - 0x37
+	Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+	Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of DIMM components that are accessible
+using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+``sysfs`` interface
+-------------------
+======================= =======================================================
+
+temp[N]_label		Provides string "DIMM CI", where C is DIMM channel and
+			I is DIMM index of the populated DIMM.
+temp[N]_input		Provides current temperature of the populated DIMM.
+temp[N]_max		Provides thermal control temperature of the DIMM.
+temp[N]_crit		Provides shutdown temperature of the DIMM.
+======================= =======================================================
+
+Note:
+	DIMM temperature attributes will appear when the client CPU's BIOS
+	completes memory training and testing.
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 13/14] hwmon: Add PECI cputemp driver
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Alan Cox, Andy Shevchenko, Jason M Biils,
	Miguel Ojeda, Andrew Lunn, Stef van Os

This commit adds PECI cputemp hwmon driver.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes since v10:
- Added Skylake Xeon D support.
- Added DTS temperature which is more thermal control friendlier than Die
  temperature.
- Fixed minor bugs and style issues.

 drivers/hwmon/Kconfig        |  14 ++
 drivers/hwmon/Makefile       |   1 +
 drivers/hwmon/peci-cputemp.c | 448 +++++++++++++++++++++++++++++++++++
 drivers/hwmon/peci-hwmon.h   |  46 ++++
 4 files changed, 509 insertions(+)
 create mode 100644 drivers/hwmon/peci-cputemp.c
 create mode 100644 drivers/hwmon/peci-hwmon.h

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 23dfe848979a..b6604759579c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1349,6 +1349,20 @@ config SENSORS_PCF8591
 	  These devices are hard to detect and rarely found on mainstream
 	  hardware. If unsure, say N.
 
+config SENSORS_PECI_CPUTEMP
+	tristate "PECI CPU temperature monitoring client"
+	depends on PECI
+	select MFD_INTEL_PECI_CLIENT
+	help
+	  If you say yes here you get support for the generic Intel PECI
+	  cputemp driver which provides Digital Thermal Sensor (DTS) thermal
+	  readings of the CPU package and CPU cores that are accessible using
+	  the PECI Client Command Suite via the processor PECI client.
+	  Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called peci-cputemp.
+
 source "drivers/hwmon/pmbus/Kconfig"
 
 config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6db5db9cdc29..d6fea48697af 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
new file mode 100644
index 000000000000..70ced9f9299f
--- /dev/null
+++ b/drivers/hwmon/peci-cputemp.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "peci-hwmon.h"
+
+#define DEFAULT_CHANNEL_NUMS   5
+#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX
+#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
+
+struct temp_group {
+	struct peci_sensor_data die;
+	struct peci_sensor_data dts;
+	struct peci_sensor_data tcontrol;
+	struct peci_sensor_data tthrottle;
+	struct peci_sensor_data tjmax;
+	struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
+};
+
+struct peci_cputemp {
+	struct peci_client_manager *mgr;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+	struct temp_group temp;
+	u64 core_mask;
+	u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
+	uint config_idx;
+	struct hwmon_channel_info temp_info;
+	const struct hwmon_channel_info *info[2];
+	struct hwmon_chip_info chip;
+};
+
+enum cputemp_channels {
+	channel_die,
+	channel_dts,
+	channel_tcontrol,
+	channel_tthrottle,
+	channel_tjmax,
+	channel_core,
+};
+
+static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+	/* Die temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+
+	/* DTS margin */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+
+	/* Tcontrol temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+
+	/* Tthrottle temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT,
+
+	/* Tjmax temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT,
+
+	/* Core temperature - for all core channels */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+};
+
+static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
+	"Die",
+	"DTS",
+	"Tcontrol",
+	"Tthrottle",
+	"Tjmax",
+	"Core 0", "Core 1", "Core 2", "Core 3",
+	"Core 4", "Core 5", "Core 6", "Core 7",
+	"Core 8", "Core 9", "Core 10", "Core 11",
+	"Core 12", "Core 13", "Core 14", "Core 15",
+	"Core 16", "Core 17", "Core 18", "Core 19",
+	"Core 20", "Core 21", "Core 22", "Core 23",
+	"Core 24", "Core 25", "Core 26", "Core 27",
+};
+
+static s32 ten_dot_six_to_millidegree(s32 val)
+{
+	return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
+}
+
+static int get_temp_targets(struct peci_cputemp *priv)
+{
+	s32 tthrottle_offset;
+	s32 tcontrol_margin;
+	u8  pkg_cfg[4];
+	int ret;
+
+	/*
+	 * Just use only the tcontrol marker to determine if target values need
+	 * update.
+	 */
+	if (!peci_sensor_need_update(&priv->temp.tcontrol))
+		return 0;
+
+	ret = peci_client_read_package_config(priv->mgr,
+					      PECI_MBX_INDEX_TEMP_TARGET, 0,
+					      pkg_cfg);
+	if (ret)
+		return ret;
+
+	priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+	tcontrol_margin = pkg_cfg[1];
+	tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
+	priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
+
+	tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
+	priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
+
+	peci_sensor_mark_updated(&priv->temp.tcontrol);
+
+	return 0;
+}
+
+static int get_die_temp(struct peci_cputemp *priv)
+{
+	struct peci_get_temp_msg msg;
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp.die))
+		return 0;
+
+	msg.addr = priv->mgr->client->addr;
+
+	ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
+	if (ret)
+		return ret;
+
+	/* Note that the tjmax should be available before calling it */
+	priv->temp.die.value = priv->temp.tjmax.value +
+			       (msg.temp_raw * 1000 / 64);
+
+	peci_sensor_mark_updated(&priv->temp.die);
+
+	return 0;
+}
+
+static int get_dts(struct peci_cputemp *priv)
+{
+	s32 dts_margin;
+	u8  pkg_cfg[4];
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp.dts))
+		return 0;
+
+	ret = peci_client_read_package_config(priv->mgr,
+					      PECI_MBX_INDEX_DTS_MARGIN, 0,
+					      pkg_cfg);
+
+	if (ret)
+		return ret;
+
+	dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
+
+	/**
+	 * Processors return a value of DTS reading in 10.6 format
+	 * (10 bits signed decimal, 6 bits fractional).
+	 * Error codes:
+	 *   0x8000: General sensor error
+	 *   0x8001: Reserved
+	 *   0x8002: Underflow on reading value
+	 *   0x8003-0x81ff: Reserved
+	 */
+	if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
+		return -EIO;
+
+	dts_margin = ten_dot_six_to_millidegree(dts_margin);
+
+	/* Note that the tcontrol should be available before calling it */
+	priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
+
+	peci_sensor_mark_updated(&priv->temp.dts);
+
+	return 0;
+}
+
+static int get_core_temp(struct peci_cputemp *priv, int core_index)
+{
+	s32 core_dts_margin;
+	u8  pkg_cfg[4];
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp.core[core_index]))
+		return 0;
+
+	ret = peci_client_read_package_config(priv->mgr,
+					      PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
+					      core_index, pkg_cfg);
+	if (ret)
+		return ret;
+
+	core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+	/*
+	 * Processors return a value of the core DTS reading in 10.6 format
+	 * (10 bits signed decimal, 6 bits fractional).
+	 * Error codes:
+	 *   0x8000: General sensor error
+	 *   0x8001: Reserved
+	 *   0x8002: Underflow on reading value
+	 *   0x8003-0x81ff: Reserved
+	 */
+	if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
+		return -EIO;
+
+	core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
+
+	/* Note that the tjmax should be available before calling it */
+	priv->temp.core[core_index].value = priv->temp.tjmax.value +
+					    core_dts_margin;
+
+	peci_sensor_mark_updated(&priv->temp.core[core_index]);
+
+	return 0;
+}
+
+static int cputemp_read_string(struct device *dev,
+			       enum hwmon_sensor_types type,
+			       u32 attr, int channel, const char **str)
+{
+	if (attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+
+	*str = cputemp_label[channel];
+
+	return 0;
+}
+
+static int cputemp_read(struct device *dev,
+			enum hwmon_sensor_types type,
+			u32 attr, int channel, long *val)
+{
+	struct peci_cputemp *priv = dev_get_drvdata(dev);
+	int ret, core_index;
+
+	if (channel >= CPUTEMP_CHANNEL_NUMS ||
+	    !(priv->temp_config[channel] & BIT(attr)))
+		return -EOPNOTSUPP;
+
+	ret = get_temp_targets(priv);
+	if (ret)
+		return ret;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		switch (channel) {
+		case channel_die:
+			ret = get_die_temp(priv);
+			if (ret)
+				break;
+
+			*val = priv->temp.die.value;
+			break;
+		case channel_dts:
+			ret = get_dts(priv);
+			if (ret)
+				break;
+
+			*val = priv->temp.dts.value;
+			break;
+		case channel_tcontrol:
+			*val = priv->temp.tcontrol.value;
+			break;
+		case channel_tthrottle:
+			*val = priv->temp.tthrottle.value;
+			break;
+		case channel_tjmax:
+			*val = priv->temp.tjmax.value;
+			break;
+		default:
+			core_index = channel - DEFAULT_CHANNEL_NUMS;
+			ret = get_core_temp(priv, core_index);
+			if (ret)
+				break;
+
+			*val = priv->temp.core[core_index].value;
+			break;
+		}
+		break;
+	case hwmon_temp_max:
+		*val = priv->temp.tcontrol.value;
+		break;
+	case hwmon_temp_crit:
+		*val = priv->temp.tjmax.value;
+		break;
+	case hwmon_temp_crit_hyst:
+		*val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+static umode_t cputemp_is_visible(const void *data,
+				  enum hwmon_sensor_types type,
+				  u32 attr, int channel)
+{
+	const struct peci_cputemp *priv = data;
+
+	if (channel < ARRAY_SIZE(priv->temp_config) &&
+	    (priv->temp_config[channel] & BIT(attr)) &&
+	    (channel < DEFAULT_CHANNEL_NUMS ||
+	     (channel >= DEFAULT_CHANNEL_NUMS &&
+	      (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops cputemp_ops = {
+	.is_visible = cputemp_is_visible,
+	.read_string = cputemp_read_string,
+	.read = cputemp_read,
+};
+
+static int check_resolved_cores(struct peci_cputemp *priv)
+{
+	struct peci_rd_pci_cfg_local_msg msg;
+	int ret;
+
+	/* Get the RESOLVED_CORES register value */
+	msg.addr = priv->mgr->client->addr;
+	msg.device = 30;
+	msg.function = 3;
+	msg.rx_len = 4;
+	msg.bus = 1;
+	msg.reg = 0xb4;
+
+	ret = peci_command(priv->mgr->client->adapter,
+			   PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+	if (msg.cc != PECI_DEV_CC_SUCCESS)
+		ret = -EAGAIN;
+	if (ret)
+		return ret;
+
+	priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
+	if (!priv->core_mask)
+		return -EAGAIN;
+
+	dev_dbg(priv->dev, "Scanned resolved cores: 0x%llx\n", priv->core_mask);
+
+	return 0;
+}
+
+static int create_core_temp_info(struct peci_cputemp *priv)
+{
+	int ret, i;
+
+	ret = check_resolved_cores(priv);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < priv->gen_info->core_max; i++)
+		if (priv->core_mask & BIT(i))
+			while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
+				priv->temp_config[priv->config_idx++] =
+					config_table[channel_core];
+
+	return 0;
+}
+
+static int peci_cputemp_probe(struct platform_device *pdev)
+{
+	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct peci_cputemp *priv;
+	struct device *hwmon_dev;
+	int ret;
+
+	if ((mgr->client->adapter->cmd_mask &
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->mgr = mgr;
+	priv->dev = dev;
+	priv->gen_info = mgr->gen_info;
+
+	snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
+		 mgr->client->addr - PECI_BASE_ADDR);
+
+	priv->temp_config[priv->config_idx++] = config_table[channel_die];
+	priv->temp_config[priv->config_idx++] = config_table[channel_dts];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
+
+	ret = create_core_temp_info(priv);
+	if (ret)
+		dev_dbg(dev, "Skipped creating core temp info\n");
+
+	priv->chip.ops = &cputemp_ops;
+	priv->chip.info = priv->info;
+
+	priv->info[0] = &priv->temp_info;
+
+	priv->temp_info.type = hwmon_temp;
+	priv->temp_info.config = priv->temp_config;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+							 priv->name,
+							 priv,
+							 &priv->chip,
+							 NULL);
+
+	if (IS_ERR(hwmon_dev))
+		return PTR_ERR(hwmon_dev);
+
+	dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
+
+	return 0;
+}
+
+static const struct platform_device_id peci_cputemp_ids[] = {
+	{ .name = "peci-cputemp", .driver_data = 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
+
+static struct platform_driver peci_cputemp_driver = {
+	.probe    = peci_cputemp_probe,
+	.id_table = peci_cputemp_ids,
+	.driver   = { .name = KBUILD_MODNAME, },
+};
+module_platform_driver(peci_cputemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI cputemp driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
new file mode 100644
index 000000000000..e0e3c901c6e4
--- /dev/null
+++ b/drivers/hwmon/peci-hwmon.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __PECI_HWMON_H
+#define __PECI_HWMON_H
+
+#include <linux/peci.h>
+
+#define TEMP_TYPE_PECI   6 /* Sensor type 6: Intel PECI */
+#define UPDATE_INTERVAL  HZ
+
+/**
+ * struct peci_sensor_data - PECI sensor information
+ * @valid: flag to indicate the sensor value is valid
+ * @value: sensor value in millidegree Celsius
+ * @last_updated: time of the last update in jiffies
+ */
+struct peci_sensor_data {
+	uint  valid;
+	s32   value;
+	ulong last_updated;
+};
+
+/**
+ * peci_sensor_need_update - check whether sensor update is needed or not
+ * @sensor: pointer to sensor data struct
+ *
+ * Return: true if update is needed, false if not.
+ */
+static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
+{
+	return !sensor->valid ||
+	       time_after(jiffies, sensor->last_updated + UPDATE_INTERVAL);
+}
+
+/**
+ * peci_sensor_mark_updated - mark the sensor is updated
+ * @sensor: pointer to sensor data struct
+ */
+static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
+{
+	sensor->valid = 1;
+	sensor->last_updated = jiffies;
+}
+
+#endif /* __PECI_HWMON_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 13/14] hwmon: Add PECI cputemp driver
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	Miguel Ojeda, Andrew Lunn, Stef van Os, Jason M Biils,
	Andy Shevchenko, linux-arm-kernel, Alan Cox

This commit adds PECI cputemp hwmon driver.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes since v10:
- Added Skylake Xeon D support.
- Added DTS temperature which is more thermal control friendlier than Die
  temperature.
- Fixed minor bugs and style issues.

 drivers/hwmon/Kconfig        |  14 ++
 drivers/hwmon/Makefile       |   1 +
 drivers/hwmon/peci-cputemp.c | 448 +++++++++++++++++++++++++++++++++++
 drivers/hwmon/peci-hwmon.h   |  46 ++++
 4 files changed, 509 insertions(+)
 create mode 100644 drivers/hwmon/peci-cputemp.c
 create mode 100644 drivers/hwmon/peci-hwmon.h

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 23dfe848979a..b6604759579c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1349,6 +1349,20 @@ config SENSORS_PCF8591
 	  These devices are hard to detect and rarely found on mainstream
 	  hardware. If unsure, say N.
 
+config SENSORS_PECI_CPUTEMP
+	tristate "PECI CPU temperature monitoring client"
+	depends on PECI
+	select MFD_INTEL_PECI_CLIENT
+	help
+	  If you say yes here you get support for the generic Intel PECI
+	  cputemp driver which provides Digital Thermal Sensor (DTS) thermal
+	  readings of the CPU package and CPU cores that are accessible using
+	  the PECI Client Command Suite via the processor PECI client.
+	  Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called peci-cputemp.
+
 source "drivers/hwmon/pmbus/Kconfig"
 
 config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6db5db9cdc29..d6fea48697af 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
new file mode 100644
index 000000000000..70ced9f9299f
--- /dev/null
+++ b/drivers/hwmon/peci-cputemp.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include "peci-hwmon.h"
+
+#define DEFAULT_CHANNEL_NUMS   5
+#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX
+#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
+
+struct temp_group {
+	struct peci_sensor_data die;
+	struct peci_sensor_data dts;
+	struct peci_sensor_data tcontrol;
+	struct peci_sensor_data tthrottle;
+	struct peci_sensor_data tjmax;
+	struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
+};
+
+struct peci_cputemp {
+	struct peci_client_manager *mgr;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+	struct temp_group temp;
+	u64 core_mask;
+	u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
+	uint config_idx;
+	struct hwmon_channel_info temp_info;
+	const struct hwmon_channel_info *info[2];
+	struct hwmon_chip_info chip;
+};
+
+enum cputemp_channels {
+	channel_die,
+	channel_dts,
+	channel_tcontrol,
+	channel_tthrottle,
+	channel_tjmax,
+	channel_core,
+};
+
+static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+	/* Die temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+
+	/* DTS margin */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+
+	/* Tcontrol temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+
+	/* Tthrottle temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT,
+
+	/* Tjmax temperature */
+	HWMON_T_LABEL | HWMON_T_INPUT,
+
+	/* Core temperature - for all core channels */
+	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+	HWMON_T_CRIT_HYST,
+};
+
+static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
+	"Die",
+	"DTS",
+	"Tcontrol",
+	"Tthrottle",
+	"Tjmax",
+	"Core 0", "Core 1", "Core 2", "Core 3",
+	"Core 4", "Core 5", "Core 6", "Core 7",
+	"Core 8", "Core 9", "Core 10", "Core 11",
+	"Core 12", "Core 13", "Core 14", "Core 15",
+	"Core 16", "Core 17", "Core 18", "Core 19",
+	"Core 20", "Core 21", "Core 22", "Core 23",
+	"Core 24", "Core 25", "Core 26", "Core 27",
+};
+
+static s32 ten_dot_six_to_millidegree(s32 val)
+{
+	return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
+}
+
+static int get_temp_targets(struct peci_cputemp *priv)
+{
+	s32 tthrottle_offset;
+	s32 tcontrol_margin;
+	u8  pkg_cfg[4];
+	int ret;
+
+	/*
+	 * Just use only the tcontrol marker to determine if target values need
+	 * update.
+	 */
+	if (!peci_sensor_need_update(&priv->temp.tcontrol))
+		return 0;
+
+	ret = peci_client_read_package_config(priv->mgr,
+					      PECI_MBX_INDEX_TEMP_TARGET, 0,
+					      pkg_cfg);
+	if (ret)
+		return ret;
+
+	priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+	tcontrol_margin = pkg_cfg[1];
+	tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
+	priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
+
+	tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
+	priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
+
+	peci_sensor_mark_updated(&priv->temp.tcontrol);
+
+	return 0;
+}
+
+static int get_die_temp(struct peci_cputemp *priv)
+{
+	struct peci_get_temp_msg msg;
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp.die))
+		return 0;
+
+	msg.addr = priv->mgr->client->addr;
+
+	ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
+	if (ret)
+		return ret;
+
+	/* Note that the tjmax should be available before calling it */
+	priv->temp.die.value = priv->temp.tjmax.value +
+			       (msg.temp_raw * 1000 / 64);
+
+	peci_sensor_mark_updated(&priv->temp.die);
+
+	return 0;
+}
+
+static int get_dts(struct peci_cputemp *priv)
+{
+	s32 dts_margin;
+	u8  pkg_cfg[4];
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp.dts))
+		return 0;
+
+	ret = peci_client_read_package_config(priv->mgr,
+					      PECI_MBX_INDEX_DTS_MARGIN, 0,
+					      pkg_cfg);
+
+	if (ret)
+		return ret;
+
+	dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
+
+	/**
+	 * Processors return a value of DTS reading in 10.6 format
+	 * (10 bits signed decimal, 6 bits fractional).
+	 * Error codes:
+	 *   0x8000: General sensor error
+	 *   0x8001: Reserved
+	 *   0x8002: Underflow on reading value
+	 *   0x8003-0x81ff: Reserved
+	 */
+	if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
+		return -EIO;
+
+	dts_margin = ten_dot_six_to_millidegree(dts_margin);
+
+	/* Note that the tcontrol should be available before calling it */
+	priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
+
+	peci_sensor_mark_updated(&priv->temp.dts);
+
+	return 0;
+}
+
+static int get_core_temp(struct peci_cputemp *priv, int core_index)
+{
+	s32 core_dts_margin;
+	u8  pkg_cfg[4];
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp.core[core_index]))
+		return 0;
+
+	ret = peci_client_read_package_config(priv->mgr,
+					      PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
+					      core_index, pkg_cfg);
+	if (ret)
+		return ret;
+
+	core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+	/*
+	 * Processors return a value of the core DTS reading in 10.6 format
+	 * (10 bits signed decimal, 6 bits fractional).
+	 * Error codes:
+	 *   0x8000: General sensor error
+	 *   0x8001: Reserved
+	 *   0x8002: Underflow on reading value
+	 *   0x8003-0x81ff: Reserved
+	 */
+	if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
+		return -EIO;
+
+	core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
+
+	/* Note that the tjmax should be available before calling it */
+	priv->temp.core[core_index].value = priv->temp.tjmax.value +
+					    core_dts_margin;
+
+	peci_sensor_mark_updated(&priv->temp.core[core_index]);
+
+	return 0;
+}
+
+static int cputemp_read_string(struct device *dev,
+			       enum hwmon_sensor_types type,
+			       u32 attr, int channel, const char **str)
+{
+	if (attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+
+	*str = cputemp_label[channel];
+
+	return 0;
+}
+
+static int cputemp_read(struct device *dev,
+			enum hwmon_sensor_types type,
+			u32 attr, int channel, long *val)
+{
+	struct peci_cputemp *priv = dev_get_drvdata(dev);
+	int ret, core_index;
+
+	if (channel >= CPUTEMP_CHANNEL_NUMS ||
+	    !(priv->temp_config[channel] & BIT(attr)))
+		return -EOPNOTSUPP;
+
+	ret = get_temp_targets(priv);
+	if (ret)
+		return ret;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		switch (channel) {
+		case channel_die:
+			ret = get_die_temp(priv);
+			if (ret)
+				break;
+
+			*val = priv->temp.die.value;
+			break;
+		case channel_dts:
+			ret = get_dts(priv);
+			if (ret)
+				break;
+
+			*val = priv->temp.dts.value;
+			break;
+		case channel_tcontrol:
+			*val = priv->temp.tcontrol.value;
+			break;
+		case channel_tthrottle:
+			*val = priv->temp.tthrottle.value;
+			break;
+		case channel_tjmax:
+			*val = priv->temp.tjmax.value;
+			break;
+		default:
+			core_index = channel - DEFAULT_CHANNEL_NUMS;
+			ret = get_core_temp(priv, core_index);
+			if (ret)
+				break;
+
+			*val = priv->temp.core[core_index].value;
+			break;
+		}
+		break;
+	case hwmon_temp_max:
+		*val = priv->temp.tcontrol.value;
+		break;
+	case hwmon_temp_crit:
+		*val = priv->temp.tjmax.value;
+		break;
+	case hwmon_temp_crit_hyst:
+		*val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+static umode_t cputemp_is_visible(const void *data,
+				  enum hwmon_sensor_types type,
+				  u32 attr, int channel)
+{
+	const struct peci_cputemp *priv = data;
+
+	if (channel < ARRAY_SIZE(priv->temp_config) &&
+	    (priv->temp_config[channel] & BIT(attr)) &&
+	    (channel < DEFAULT_CHANNEL_NUMS ||
+	     (channel >= DEFAULT_CHANNEL_NUMS &&
+	      (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops cputemp_ops = {
+	.is_visible = cputemp_is_visible,
+	.read_string = cputemp_read_string,
+	.read = cputemp_read,
+};
+
+static int check_resolved_cores(struct peci_cputemp *priv)
+{
+	struct peci_rd_pci_cfg_local_msg msg;
+	int ret;
+
+	/* Get the RESOLVED_CORES register value */
+	msg.addr = priv->mgr->client->addr;
+	msg.device = 30;
+	msg.function = 3;
+	msg.rx_len = 4;
+	msg.bus = 1;
+	msg.reg = 0xb4;
+
+	ret = peci_command(priv->mgr->client->adapter,
+			   PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+	if (msg.cc != PECI_DEV_CC_SUCCESS)
+		ret = -EAGAIN;
+	if (ret)
+		return ret;
+
+	priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
+	if (!priv->core_mask)
+		return -EAGAIN;
+
+	dev_dbg(priv->dev, "Scanned resolved cores: 0x%llx\n", priv->core_mask);
+
+	return 0;
+}
+
+static int create_core_temp_info(struct peci_cputemp *priv)
+{
+	int ret, i;
+
+	ret = check_resolved_cores(priv);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < priv->gen_info->core_max; i++)
+		if (priv->core_mask & BIT(i))
+			while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
+				priv->temp_config[priv->config_idx++] =
+					config_table[channel_core];
+
+	return 0;
+}
+
+static int peci_cputemp_probe(struct platform_device *pdev)
+{
+	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct peci_cputemp *priv;
+	struct device *hwmon_dev;
+	int ret;
+
+	if ((mgr->client->adapter->cmd_mask &
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->mgr = mgr;
+	priv->dev = dev;
+	priv->gen_info = mgr->gen_info;
+
+	snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
+		 mgr->client->addr - PECI_BASE_ADDR);
+
+	priv->temp_config[priv->config_idx++] = config_table[channel_die];
+	priv->temp_config[priv->config_idx++] = config_table[channel_dts];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
+	priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
+
+	ret = create_core_temp_info(priv);
+	if (ret)
+		dev_dbg(dev, "Skipped creating core temp info\n");
+
+	priv->chip.ops = &cputemp_ops;
+	priv->chip.info = priv->info;
+
+	priv->info[0] = &priv->temp_info;
+
+	priv->temp_info.type = hwmon_temp;
+	priv->temp_info.config = priv->temp_config;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+							 priv->name,
+							 priv,
+							 &priv->chip,
+							 NULL);
+
+	if (IS_ERR(hwmon_dev))
+		return PTR_ERR(hwmon_dev);
+
+	dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
+
+	return 0;
+}
+
+static const struct platform_device_id peci_cputemp_ids[] = {
+	{ .name = "peci-cputemp", .driver_data = 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
+
+static struct platform_driver peci_cputemp_driver = {
+	.probe    = peci_cputemp_probe,
+	.id_table = peci_cputemp_ids,
+	.driver   = { .name = KBUILD_MODNAME, },
+};
+module_platform_driver(peci_cputemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI cputemp driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
new file mode 100644
index 000000000000..e0e3c901c6e4
--- /dev/null
+++ b/drivers/hwmon/peci-hwmon.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2019 Intel Corporation */
+
+#ifndef __PECI_HWMON_H
+#define __PECI_HWMON_H
+
+#include <linux/peci.h>
+
+#define TEMP_TYPE_PECI   6 /* Sensor type 6: Intel PECI */
+#define UPDATE_INTERVAL  HZ
+
+/**
+ * struct peci_sensor_data - PECI sensor information
+ * @valid: flag to indicate the sensor value is valid
+ * @value: sensor value in millidegree Celsius
+ * @last_updated: time of the last update in jiffies
+ */
+struct peci_sensor_data {
+	uint  valid;
+	s32   value;
+	ulong last_updated;
+};
+
+/**
+ * peci_sensor_need_update - check whether sensor update is needed or not
+ * @sensor: pointer to sensor data struct
+ *
+ * Return: true if update is needed, false if not.
+ */
+static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
+{
+	return !sensor->valid ||
+	       time_after(jiffies, sensor->last_updated + UPDATE_INTERVAL);
+}
+
+/**
+ * peci_sensor_mark_updated - mark the sensor is updated
+ * @sensor: pointer to sensor data struct
+ */
+static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
+{
+	sensor->valid = 1;
+	sensor->last_updated = jiffies;
+}
+
+#endif /* __PECI_HWMON_H */
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-11 19:46 ` Jae Hyun Yoo
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Jae Hyun Yoo, Alan Cox, Andy Shevchenko, Jason M Biils,
	Miguel Ojeda, Andrew Lunn, Stef van Os

This commit adds PECI dimmtemp hwmon driver.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes since v10:
- Added Skylake Xeon D support.
- Added max and crit properties for temperature threshold checking.
- Fixed minor bugs and style issues.

 drivers/hwmon/Kconfig         |  14 ++
 drivers/hwmon/Makefile        |   1 +
 drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
 3 files changed, 408 insertions(+)
 create mode 100644 drivers/hwmon/peci-dimmtemp.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b6604759579c..d3370fbab40c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
 	  This driver can also be built as a module. If so, the module
 	  will be called peci-cputemp.
 
+config SENSORS_PECI_DIMMTEMP
+	tristate "PECI DIMM temperature monitoring client"
+	depends on PECI
+	select MFD_INTEL_PECI_CLIENT
+	help
+	  If you say yes here you get support for the generic Intel PECI hwmon
+	  driver which provides Digital Thermal Sensor (DTS) thermal readings of
+	  DIMM components that are accessible using the PECI Client Command
+	  Suite via the processor PECI client.
+	  Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called peci-dimmtemp.
+
 source "drivers/hwmon/pmbus/Kconfig"
 
 config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d6fea48697af..4015c4b60bf4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
 obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
+obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)	+= peci-dimmtemp.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
new file mode 100644
index 000000000000..974f453f9366
--- /dev/null
+++ b/drivers/hwmon/peci-dimmtemp.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include "peci-hwmon.h"
+
+#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
+#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
+
+struct peci_dimmtemp {
+	struct peci_client_manager *mgr;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+	struct workqueue_struct *work_queue;
+	struct delayed_work work_handler;
+	struct peci_sensor_data temp[DIMM_NUMS_MAX];
+	long temp_max[DIMM_NUMS_MAX];
+	long temp_crit[DIMM_NUMS_MAX];
+	u32 dimm_mask;
+	int retry_count;
+	u32 temp_config[DIMM_NUMS_MAX + 1];
+	struct hwmon_channel_info temp_info;
+	const struct hwmon_channel_info *info[2];
+	struct hwmon_chip_info chip;
+};
+
+static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
+	{ "DIMM A1", "DIMM A2", "DIMM A3" },
+	{ "DIMM B1", "DIMM B2", "DIMM B3" },
+	{ "DIMM C1", "DIMM C2", "DIMM C3" },
+	{ "DIMM D1", "DIMM D2", "DIMM D3" },
+	{ "DIMM E1", "DIMM E2", "DIMM E3" },
+	{ "DIMM F1", "DIMM F2", "DIMM F3" },
+	{ "DIMM G1", "DIMM G2", "DIMM G3" },
+	{ "DIMM H1", "DIMM H2", "DIMM H3" },
+};
+
+static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
+					    int chan_rank,
+					    u8 *cfg_data)
+{
+	return peci_client_read_package_config(priv->mgr,
+					       PECI_MBX_INDEX_DDR_DIMM_TEMP,
+					       chan_rank, cfg_data);
+}
+
+static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
+{
+	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+	struct peci_rd_pci_cfg_local_msg rp_msg;
+	u8  cfg_data[4];
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp[dimm_no]))
+		return 0;
+
+	ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
+	if (ret)
+		return ret;
+
+	priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
+	switch (priv->gen_info->model) {
+	case INTEL_FAM6_SKYLAKE_X:
+		rp_msg.addr = priv->mgr->client->addr;
+		rp_msg.bus = 2;
+		/*
+		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+		 * Device 11, Function 2: IMC 0 channel 2 -> rank 2
+		 * Device 12, Function 2: IMC 1 channel 0 -> rank 3
+		 * Device 12, Function 6: IMC 1 channel 1 -> rank 4
+		 * Device 13, Function 2: IMC 1 channel 2 -> rank 5
+		 */
+		rp_msg.device = 10 + chan_rank / 3 * 2 +
+			     (chan_rank % 3 == 2 ? 1 : 0);
+		rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
+		rp_msg.reg = 0x120 + dimm_order * 4;
+		rp_msg.rx_len = 4;
+
+		ret = peci_command(priv->mgr->client->adapter,
+				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+			ret = -EAGAIN;
+		if (ret)
+			return ret;
+
+		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+		break;
+	case INTEL_FAM6_SKYLAKE_XD:
+		rp_msg.addr = priv->mgr->client->addr;
+		rp_msg.bus = 2;
+		/*
+		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+		 * Device 12, Function 2: IMC 1 channel 0 -> rank 2
+		 * Device 12, Function 6: IMC 1 channel 1 -> rank 3
+		 */
+		rp_msg.device = 10 + chan_rank / 2 * 2;
+		rp_msg.function = (chan_rank % 2) ? 6 : 2;
+		rp_msg.reg = 0x120 + dimm_order * 4;
+		rp_msg.rx_len = 4;
+
+		ret = peci_command(priv->mgr->client->adapter,
+				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+			ret = -EAGAIN;
+		if (ret)
+			return ret;
+
+		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+		break;
+	case INTEL_FAM6_HASWELL_X:
+	case INTEL_FAM6_BROADWELL_X:
+		rp_msg.addr = priv->mgr->client->addr;
+		rp_msg.bus = 1;
+		/*
+		 * Device 20, Function 0: IMC 0 channel 0 -> rank 0
+		 * Device 20, Function 1: IMC 0 channel 1 -> rank 1
+		 * Device 21, Function 0: IMC 0 channel 2 -> rank 2
+		 * Device 21, Function 1: IMC 0 channel 3 -> rank 3
+		 * Device 23, Function 0: IMC 1 channel 0 -> rank 4
+		 * Device 23, Function 1: IMC 1 channel 1 -> rank 5
+		 * Device 24, Function 0: IMC 1 channel 2 -> rank 6
+		 * Device 24, Function 1: IMC 1 channel 3 -> rank 7
+		 */
+		rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
+		rp_msg.function = chan_rank % 2;
+		rp_msg.reg = 0x120 + dimm_order * 4;
+		rp_msg.rx_len = 4;
+
+		ret = peci_command(priv->mgr->client->adapter,
+				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+			ret = -EAGAIN;
+		if (ret)
+			return ret;
+
+		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	peci_sensor_mark_updated(&priv->temp[dimm_no]);
+
+	return 0;
+}
+
+static int dimmtemp_read_string(struct device *dev,
+				enum hwmon_sensor_types type,
+				u32 attr, int channel, const char **str)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+	int chan_rank, dimm_idx;
+
+	if (attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+
+	chan_rank = channel / dimm_idx_max;
+	dimm_idx = channel % dimm_idx_max;
+	*str = dimmtemp_label[chan_rank][dimm_idx];
+
+	return 0;
+}
+
+static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+			 u32 attr, int channel, long *val)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = get_dimm_temp(priv, channel);
+	if (ret)
+		return ret;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		*val = priv->temp[channel].value;
+		break;
+	case hwmon_temp_max:
+		*val = priv->temp_max[channel];
+		break;
+	case hwmon_temp_crit:
+		*val = priv->temp_crit[channel];
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+static umode_t dimmtemp_is_visible(const void *data,
+				   enum hwmon_sensor_types type,
+				   u32 attr, int channel)
+{
+	const struct peci_dimmtemp *priv = data;
+
+	if (priv->temp_config[channel] & BIT(attr) &&
+	    priv->dimm_mask & BIT(channel))
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops dimmtemp_ops = {
+	.is_visible = dimmtemp_is_visible,
+	.read_string = dimmtemp_read_string,
+	.read = dimmtemp_read,
+};
+
+static int check_populated_dimms(struct peci_dimmtemp *priv)
+{
+	u32 chan_rank_max = priv->gen_info->chan_rank_max;
+	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+	int chan_rank, dimm_idx;
+	u8  cfg_data[4];
+
+	for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+		int ret;
+
+		ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
+		if (ret) {
+			priv->dimm_mask = 0;
+			return ret;
+		}
+
+		for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+			if (cfg_data[dimm_idx])
+				priv->dimm_mask |= BIT(chan_rank *
+						       dimm_idx_max +
+						       dimm_idx);
+	}
+
+	if (!priv->dimm_mask)
+		return -EAGAIN;
+
+	dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
+
+	return 0;
+}
+
+static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+{
+	int ret, i, config_idx, channels;
+	struct device *hwmon_dev;
+
+	ret = check_populated_dimms(priv);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+				queue_delayed_work(priv->work_queue,
+						   &priv->work_handler,
+						 DIMM_MASK_CHECK_DELAY_JIFFIES);
+				priv->retry_count++;
+				dev_dbg(priv->dev,
+					"Deferred DIMM temp info creation\n");
+			} else {
+				dev_err(priv->dev,
+					"Timeout DIMM temp info creation\n");
+				ret = -ETIMEDOUT;
+			}
+		}
+
+		return ret;
+	}
+
+	channels = priv->gen_info->chan_rank_max *
+		   priv->gen_info->dimm_idx_max;
+	for (i = 0, config_idx = 0; i < channels; i++)
+		if (priv->dimm_mask & BIT(i))
+			while (i >= config_idx)
+				priv->temp_config[config_idx++] =
+					HWMON_T_LABEL | HWMON_T_INPUT |
+					HWMON_T_MAX | HWMON_T_CRIT;
+
+	priv->chip.ops = &dimmtemp_ops;
+	priv->chip.info = priv->info;
+
+	priv->info[0] = &priv->temp_info;
+
+	priv->temp_info.type = hwmon_temp;
+	priv->temp_info.config = priv->temp_config;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+							 priv->name,
+							 priv,
+							 &priv->chip,
+							 NULL);
+	ret = PTR_ERR_OR_ZERO(hwmon_dev);
+	if (!ret)
+		dev_dbg(priv->dev, "%s: sensor '%s'\n",
+			dev_name(hwmon_dev), priv->name);
+
+	return ret;
+}
+
+static void create_dimm_temp_info_delayed(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
+						  work_handler);
+	int ret;
+
+	ret = create_dimm_temp_info(priv);
+	if (ret && ret != -EAGAIN)
+		dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+}
+
+static int peci_dimmtemp_probe(struct platform_device *pdev)
+{
+	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct peci_dimmtemp *priv;
+	int ret;
+
+	if ((mgr->client->adapter->cmd_mask &
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->mgr = mgr;
+	priv->dev = dev;
+	priv->gen_info = mgr->gen_info;
+
+	snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
+		 priv->mgr->client->addr - PECI_BASE_ADDR);
+
+	priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
+	if (!priv->work_queue)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+	ret = create_dimm_temp_info(priv);
+	if (ret && ret != -EAGAIN) {
+		dev_err(dev, "Failed to create DIMM temp info\n");
+		goto err_free_wq;
+	}
+
+	return 0;
+
+err_free_wq:
+	destroy_workqueue(priv->work_queue);
+	return ret;
+}
+
+static int peci_dimmtemp_remove(struct platform_device *pdev)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
+
+	cancel_delayed_work_sync(&priv->work_handler);
+	destroy_workqueue(priv->work_queue);
+
+	return 0;
+}
+
+static const struct platform_device_id peci_dimmtemp_ids[] = {
+	{ .name = "peci-dimmtemp", .driver_data = 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
+
+static struct platform_driver peci_dimmtemp_driver = {
+	.probe    = peci_dimmtemp_probe,
+	.remove   = peci_dimmtemp_remove,
+	.id_table = peci_dimmtemp_ids,
+	.driver   = { .name = KBUILD_MODNAME, },
+};
+module_platform_driver(peci_dimmtemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI dimmtemp driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-11 19:46   ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-11 19:46 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, Jae Hyun Yoo, linux-doc, openbmc,
	Miguel Ojeda, Andrew Lunn, Stef van Os, Jason M Biils,
	Andy Shevchenko, linux-arm-kernel, Alan Cox

This commit adds PECI dimmtemp hwmon driver.

Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Changes since v10:
- Added Skylake Xeon D support.
- Added max and crit properties for temperature threshold checking.
- Fixed minor bugs and style issues.

 drivers/hwmon/Kconfig         |  14 ++
 drivers/hwmon/Makefile        |   1 +
 drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
 3 files changed, 408 insertions(+)
 create mode 100644 drivers/hwmon/peci-dimmtemp.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b6604759579c..d3370fbab40c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
 	  This driver can also be built as a module. If so, the module
 	  will be called peci-cputemp.
 
+config SENSORS_PECI_DIMMTEMP
+	tristate "PECI DIMM temperature monitoring client"
+	depends on PECI
+	select MFD_INTEL_PECI_CLIENT
+	help
+	  If you say yes here you get support for the generic Intel PECI hwmon
+	  driver which provides Digital Thermal Sensor (DTS) thermal readings of
+	  DIMM components that are accessible using the PECI Client Command
+	  Suite via the processor PECI client.
+	  Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called peci-dimmtemp.
+
 source "drivers/hwmon/pmbus/Kconfig"
 
 config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d6fea48697af..4015c4b60bf4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
 obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
+obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)	+= peci-dimmtemp.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
new file mode 100644
index 000000000000..974f453f9366
--- /dev/null
+++ b/drivers/hwmon/peci-dimmtemp.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018-2019 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include "peci-hwmon.h"
+
+#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
+#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
+
+struct peci_dimmtemp {
+	struct peci_client_manager *mgr;
+	struct device *dev;
+	char name[PECI_NAME_SIZE];
+	const struct cpu_gen_info *gen_info;
+	struct workqueue_struct *work_queue;
+	struct delayed_work work_handler;
+	struct peci_sensor_data temp[DIMM_NUMS_MAX];
+	long temp_max[DIMM_NUMS_MAX];
+	long temp_crit[DIMM_NUMS_MAX];
+	u32 dimm_mask;
+	int retry_count;
+	u32 temp_config[DIMM_NUMS_MAX + 1];
+	struct hwmon_channel_info temp_info;
+	const struct hwmon_channel_info *info[2];
+	struct hwmon_chip_info chip;
+};
+
+static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
+	{ "DIMM A1", "DIMM A2", "DIMM A3" },
+	{ "DIMM B1", "DIMM B2", "DIMM B3" },
+	{ "DIMM C1", "DIMM C2", "DIMM C3" },
+	{ "DIMM D1", "DIMM D2", "DIMM D3" },
+	{ "DIMM E1", "DIMM E2", "DIMM E3" },
+	{ "DIMM F1", "DIMM F2", "DIMM F3" },
+	{ "DIMM G1", "DIMM G2", "DIMM G3" },
+	{ "DIMM H1", "DIMM H2", "DIMM H3" },
+};
+
+static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
+					    int chan_rank,
+					    u8 *cfg_data)
+{
+	return peci_client_read_package_config(priv->mgr,
+					       PECI_MBX_INDEX_DDR_DIMM_TEMP,
+					       chan_rank, cfg_data);
+}
+
+static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
+{
+	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+	struct peci_rd_pci_cfg_local_msg rp_msg;
+	u8  cfg_data[4];
+	int ret;
+
+	if (!peci_sensor_need_update(&priv->temp[dimm_no]))
+		return 0;
+
+	ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
+	if (ret)
+		return ret;
+
+	priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
+	switch (priv->gen_info->model) {
+	case INTEL_FAM6_SKYLAKE_X:
+		rp_msg.addr = priv->mgr->client->addr;
+		rp_msg.bus = 2;
+		/*
+		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+		 * Device 11, Function 2: IMC 0 channel 2 -> rank 2
+		 * Device 12, Function 2: IMC 1 channel 0 -> rank 3
+		 * Device 12, Function 6: IMC 1 channel 1 -> rank 4
+		 * Device 13, Function 2: IMC 1 channel 2 -> rank 5
+		 */
+		rp_msg.device = 10 + chan_rank / 3 * 2 +
+			     (chan_rank % 3 == 2 ? 1 : 0);
+		rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
+		rp_msg.reg = 0x120 + dimm_order * 4;
+		rp_msg.rx_len = 4;
+
+		ret = peci_command(priv->mgr->client->adapter,
+				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+			ret = -EAGAIN;
+		if (ret)
+			return ret;
+
+		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+		break;
+	case INTEL_FAM6_SKYLAKE_XD:
+		rp_msg.addr = priv->mgr->client->addr;
+		rp_msg.bus = 2;
+		/*
+		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
+		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
+		 * Device 12, Function 2: IMC 1 channel 0 -> rank 2
+		 * Device 12, Function 6: IMC 1 channel 1 -> rank 3
+		 */
+		rp_msg.device = 10 + chan_rank / 2 * 2;
+		rp_msg.function = (chan_rank % 2) ? 6 : 2;
+		rp_msg.reg = 0x120 + dimm_order * 4;
+		rp_msg.rx_len = 4;
+
+		ret = peci_command(priv->mgr->client->adapter,
+				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+			ret = -EAGAIN;
+		if (ret)
+			return ret;
+
+		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+		break;
+	case INTEL_FAM6_HASWELL_X:
+	case INTEL_FAM6_BROADWELL_X:
+		rp_msg.addr = priv->mgr->client->addr;
+		rp_msg.bus = 1;
+		/*
+		 * Device 20, Function 0: IMC 0 channel 0 -> rank 0
+		 * Device 20, Function 1: IMC 0 channel 1 -> rank 1
+		 * Device 21, Function 0: IMC 0 channel 2 -> rank 2
+		 * Device 21, Function 1: IMC 0 channel 3 -> rank 3
+		 * Device 23, Function 0: IMC 1 channel 0 -> rank 4
+		 * Device 23, Function 1: IMC 1 channel 1 -> rank 5
+		 * Device 24, Function 0: IMC 1 channel 2 -> rank 6
+		 * Device 24, Function 1: IMC 1 channel 3 -> rank 7
+		 */
+		rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
+		rp_msg.function = chan_rank % 2;
+		rp_msg.reg = 0x120 + dimm_order * 4;
+		rp_msg.rx_len = 4;
+
+		ret = peci_command(priv->mgr->client->adapter,
+				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
+		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
+			ret = -EAGAIN;
+		if (ret)
+			return ret;
+
+		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
+		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	peci_sensor_mark_updated(&priv->temp[dimm_no]);
+
+	return 0;
+}
+
+static int dimmtemp_read_string(struct device *dev,
+				enum hwmon_sensor_types type,
+				u32 attr, int channel, const char **str)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+	int chan_rank, dimm_idx;
+
+	if (attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+
+	chan_rank = channel / dimm_idx_max;
+	dimm_idx = channel % dimm_idx_max;
+	*str = dimmtemp_label[chan_rank][dimm_idx];
+
+	return 0;
+}
+
+static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+			 u32 attr, int channel, long *val)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = get_dimm_temp(priv, channel);
+	if (ret)
+		return ret;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		*val = priv->temp[channel].value;
+		break;
+	case hwmon_temp_max:
+		*val = priv->temp_max[channel];
+		break;
+	case hwmon_temp_crit:
+		*val = priv->temp_crit[channel];
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+static umode_t dimmtemp_is_visible(const void *data,
+				   enum hwmon_sensor_types type,
+				   u32 attr, int channel)
+{
+	const struct peci_dimmtemp *priv = data;
+
+	if (priv->temp_config[channel] & BIT(attr) &&
+	    priv->dimm_mask & BIT(channel))
+		return 0444;
+
+	return 0;
+}
+
+static const struct hwmon_ops dimmtemp_ops = {
+	.is_visible = dimmtemp_is_visible,
+	.read_string = dimmtemp_read_string,
+	.read = dimmtemp_read,
+};
+
+static int check_populated_dimms(struct peci_dimmtemp *priv)
+{
+	u32 chan_rank_max = priv->gen_info->chan_rank_max;
+	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+	int chan_rank, dimm_idx;
+	u8  cfg_data[4];
+
+	for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+		int ret;
+
+		ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
+		if (ret) {
+			priv->dimm_mask = 0;
+			return ret;
+		}
+
+		for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+			if (cfg_data[dimm_idx])
+				priv->dimm_mask |= BIT(chan_rank *
+						       dimm_idx_max +
+						       dimm_idx);
+	}
+
+	if (!priv->dimm_mask)
+		return -EAGAIN;
+
+	dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
+
+	return 0;
+}
+
+static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+{
+	int ret, i, config_idx, channels;
+	struct device *hwmon_dev;
+
+	ret = check_populated_dimms(priv);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+				queue_delayed_work(priv->work_queue,
+						   &priv->work_handler,
+						 DIMM_MASK_CHECK_DELAY_JIFFIES);
+				priv->retry_count++;
+				dev_dbg(priv->dev,
+					"Deferred DIMM temp info creation\n");
+			} else {
+				dev_err(priv->dev,
+					"Timeout DIMM temp info creation\n");
+				ret = -ETIMEDOUT;
+			}
+		}
+
+		return ret;
+	}
+
+	channels = priv->gen_info->chan_rank_max *
+		   priv->gen_info->dimm_idx_max;
+	for (i = 0, config_idx = 0; i < channels; i++)
+		if (priv->dimm_mask & BIT(i))
+			while (i >= config_idx)
+				priv->temp_config[config_idx++] =
+					HWMON_T_LABEL | HWMON_T_INPUT |
+					HWMON_T_MAX | HWMON_T_CRIT;
+
+	priv->chip.ops = &dimmtemp_ops;
+	priv->chip.info = priv->info;
+
+	priv->info[0] = &priv->temp_info;
+
+	priv->temp_info.type = hwmon_temp;
+	priv->temp_info.config = priv->temp_config;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+							 priv->name,
+							 priv,
+							 &priv->chip,
+							 NULL);
+	ret = PTR_ERR_OR_ZERO(hwmon_dev);
+	if (!ret)
+		dev_dbg(priv->dev, "%s: sensor '%s'\n",
+			dev_name(hwmon_dev), priv->name);
+
+	return ret;
+}
+
+static void create_dimm_temp_info_delayed(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
+						  work_handler);
+	int ret;
+
+	ret = create_dimm_temp_info(priv);
+	if (ret && ret != -EAGAIN)
+		dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+}
+
+static int peci_dimmtemp_probe(struct platform_device *pdev)
+{
+	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct peci_dimmtemp *priv;
+	int ret;
+
+	if ((mgr->client->adapter->cmd_mask &
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->mgr = mgr;
+	priv->dev = dev;
+	priv->gen_info = mgr->gen_info;
+
+	snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
+		 priv->mgr->client->addr - PECI_BASE_ADDR);
+
+	priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
+	if (!priv->work_queue)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+	ret = create_dimm_temp_info(priv);
+	if (ret && ret != -EAGAIN) {
+		dev_err(dev, "Failed to create DIMM temp info\n");
+		goto err_free_wq;
+	}
+
+	return 0;
+
+err_free_wq:
+	destroy_workqueue(priv->work_queue);
+	return ret;
+}
+
+static int peci_dimmtemp_remove(struct platform_device *pdev)
+{
+	struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
+
+	cancel_delayed_work_sync(&priv->work_handler);
+	destroy_workqueue(priv->work_queue);
+
+	return 0;
+}
+
+static const struct platform_device_id peci_dimmtemp_ids[] = {
+	{ .name = "peci-dimmtemp", .driver_data = 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
+
+static struct platform_driver peci_dimmtemp_driver = {
+	.probe    = peci_dimmtemp_probe,
+	.remove   = peci_dimmtemp_remove,
+	.id_table = peci_dimmtemp_ids,
+	.driver   = { .name = KBUILD_MODNAME, },
+};
+module_platform_driver(peci_dimmtemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI dimmtemp driver");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 03/14] peci: Add support for PECI bus driver core
  2019-12-11 19:46 ` [PATCH v11 03/14] peci: Add support for PECI bus driver core Jae Hyun Yoo
@ 2019-12-11 20:18   ` Andy Shevchenko
  2019-12-12  0:46     ` Jae Hyun Yoo
  0 siblings, 1 reply; 71+ messages in thread
From: Andy Shevchenko @ 2019-12-11 20:18 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Gavin Schenk, Viresh Kumar, Cyrille Pitchen, Alan Cox,
	Andrew Lunn, Benjamin Herrenschmidt, Fengguang Wu, Jason M Biils,
	Julia Cartwright, Yunge Zhu

On Wed, Dec 11, 2019 at 11:46:13AM -0800, Jae Hyun Yoo wrote:
> This commit adds driver implementation for PECI bus core into linux
> driver framework.
> 
> PECI (Platform Environment Control Interface) is a one-wire bus interface
> that provides a communication channel from Intel processors and chipset
> components to external monitoring or control devices. PECI is designed to
> support the following sideband functions:
> 
> * Processor and DRAM thermal management
>   - Processor fan speed control is managed by comparing Digital Thermal
>     Sensor (DTS) thermal readings acquired via PECI against the
>     processor-specific fan speed control reference point, or TCONTROL. Both
>     TCONTROL and DTS thermal readings are accessible via the processor PECI
>     client. These variables are referenced to a common temperature, the TCC
>     activation point, and are both defined as negative offsets from that
>     reference.
>   - PECI based access to the processor package configuration space provides
>     a means for Baseboard Management Controllers (BMC) or other platform
>     management devices to actively manage the processor and memory power
>     and thermal features.
> 
> * Platform Manageability
>   - Platform manageability functions including thermal, power, and error
>     monitoring. Note that platform 'power' management includes monitoring
>     and control for both the processor and DRAM subsystem to assist with
>     data center power limiting.
>   - PECI allows read access to certain error registers in the processor MSR
>     space and status monitoring registers in the PCI configuration space
>     within the processor and downstream devices.
>   - PECI permits writes to certain registers in the processor PCI
>     configuration space.
> 
> * Processor Interface Tuning and Diagnostics
>   - Processor interface tuning and diagnostics capabilities
>     (Intel Interconnect BIST). The processors Intel Interconnect Built In
>     Self Test (Intel IBIST) allows for infield diagnostic capabilities in
>     the Intel UPI and memory controller interfaces. PECI provides a port to
>     execute these diagnostics via its PCI Configuration read and write
>     capabilities.
> 
> * Failure Analysis
>   - Output the state of the processor after a failure for analysis via
>     Crashdump.
> 
> PECI uses a single wire for self-clocking and data transfer. The bus
> requires no additional control lines. The physical layer is a self-clocked
> one-wire bus that begins each bit with a driven, rising edge from an idle
> level near zero volts. The duration of the signal driven high depends on
> whether the bit value is a logic '0' or logic '1'. PECI also includes
> variable data transfer rate established with every message. In this way, it
> is highly flexible even though underlying logic is simple.
> 
> The interface design was optimized for interfacing between an Intel
> processor and chipset components in both single processor and multiple
> processor environments. The single wire interface provides low board
> routing overhead for the multiple load connections in the congested routing
> area near the processor and chipset components. Bus speed, error checking,
> and low protocol overhead provides adequate link bandwidth and reliability
> to transfer critical device operating conditions and configuration
> information.
> 
> This implementation provides the basic framework to add PECI extensions to
> the Linux bus and device models. A hardware specific 'Adapter' driver can
> be attached to the PECI bus to provide sideband functions described above.
> It is also possible to access all devices on an adapter from userspace
> through the /dev interface. A device specific 'Client' driver also can be
> attached to the PECI bus so each processor client's features can be
> supported by the 'Client' driver through an adapter connection in the bus.

Nice, we have some drivers under drivers/hwmon. Are they using PECI? How they
will be integrated to this? Can this be part of drivers/hwmon?

> Changes since v10:

It's funny I don't remember previous version(s), but anyway I'll comment on
this later on -- it has at least several style issues / inconveniences.

> - Split out peci-dev module from peci-core module.
> - Added PECI 4.0 command set support.
> - Refined 32-bit boundary alignment for all PECI ioctl command structs.
> - Added DMA safe command buffer handling in peci-core.
> - Refined kconfig dependencies in PECI subsystem.
> - Fixed minor bugs and style issues.
> - configfs support isn't added in this patch set. Will add that using a
>   seperate patch set.

> +config PECI
> +	tristate "PECI support"
> +	select CRC8

> +	default n

As for beginning, this one is redundant.
If you have more, drop them.

> +#include <linux/bitfield.h>
> +#include <linux/crc8.h>
> +#include <linux/delay.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>

> +#include <linux/of_device.h>

What about ACPI? Can you use fwnode API?

> +#include <linux/peci.h>
> +#include <linux/pm_domain.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/sched/task_stack.h>
> +#include <linux/slab.h>

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-11 20:28     ` Andy Shevchenko
  -1 siblings, 0 replies; 71+ messages in thread
From: Andy Shevchenko @ 2019-12-11 20:28 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Robin Murphy, Ryan Chen

On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:
> This commit adds Aspeed PECI adapter driver for Aspeed
> AST24xx/25xx/26xx SoCs.

...

> +#define   ASPEED_PECI_CMD_IDLE_MASK		(ASPEED_PECI_CMD_STS_MASK | \
> +						 ASPEED_PECI_CMD_PIN_MON)

Better looking when the value completely occupies second line without touching
the first.

...

> +static int aspeed_peci_check_idle(struct aspeed_peci *priv)
> +{
> +	ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
> +	u32 cmd_sts;

Like in the previous patch this one has hard to read timeout loops with inefficient code.

> +	for (;;) {
> +		cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
> +		if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
> +			break;

> +		if (time_after(jiffies, timeout)) {

This is actually main exit condition (vs. infinite loop).

> +			cmd_sts = readl(priv->base + ASPEED_PECI_CMD);

This make no sense. If you would like to have one more iteration, just spell it
explicitly.

> +			break;
> +		}

> +		usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
> +			     ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
> +	}
> +

> +	return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;

Ditto.

> +}

Now look at the other variant:

	do {
		...do something...
		if (success)
			return 0;
		usleep(...);
	} while (time_before(...));

	return -ETIMEDOUT;

* Easy
* less LOCs
* guaranteed always to be at least one iteration
* has explicitly spelled exit condition

BUT!

In this very case you may do even better if you read iopoll.h, i.e
readl_poll_timeout() has this functionality embedded in the macro.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
@ 2019-12-11 20:28     ` Andy Shevchenko
  0 siblings, 0 replies; 71+ messages in thread
From: Andy Shevchenko @ 2019-12-11 20:28 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Thomas Gleixner,
	Sagar Dharia, linux-arm-kernel, Juergen Gross, Cyrille Pitchen,
	Ryan Chen, Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
	Stephen Boyd, Vinod Koul, Gustavo Pimentel, Andrew Morton,
	Robin Murphy, David S . Miller

On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:
> This commit adds Aspeed PECI adapter driver for Aspeed
> AST24xx/25xx/26xx SoCs.

...

> +#define   ASPEED_PECI_CMD_IDLE_MASK		(ASPEED_PECI_CMD_STS_MASK | \
> +						 ASPEED_PECI_CMD_PIN_MON)

Better looking when the value completely occupies second line without touching
the first.

...

> +static int aspeed_peci_check_idle(struct aspeed_peci *priv)
> +{
> +	ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
> +	u32 cmd_sts;

Like in the previous patch this one has hard to read timeout loops with inefficient code.

> +	for (;;) {
> +		cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
> +		if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
> +			break;

> +		if (time_after(jiffies, timeout)) {

This is actually main exit condition (vs. infinite loop).

> +			cmd_sts = readl(priv->base + ASPEED_PECI_CMD);

This make no sense. If you would like to have one more iteration, just spell it
explicitly.

> +			break;
> +		}

> +		usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
> +			     ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
> +	}
> +

> +	return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;

Ditto.

> +}

Now look at the other variant:

	do {
		...do something...
		if (success)
			return 0;
		usleep(...);
	} while (time_before(...));

	return -ETIMEDOUT;

* Easy
* less LOCs
* guaranteed always to be at least one iteration
* has explicitly spelled exit condition

BUT!

In this very case you may do even better if you read iopoll.h, i.e
readl_poll_timeout() has this functionality embedded in the macro.

-- 
With Best Regards,
Andy Shevchenko



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 03/14] peci: Add support for PECI bus driver core
  2019-12-11 20:18   ` Andy Shevchenko
@ 2019-12-12  0:46     ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-12  0:46 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Gavin Schenk, Viresh Kumar, Cyrille Pitchen, Alan Cox,
	Andrew Lunn, Benjamin Herrenschmidt, Fengguang Wu, Jason M Biils,
	Julia Cartwright, Yunge Zhu

Hi Andy,

On 12/11/2019 12:18 PM, Andy Shevchenko wrote:
> 
> Nice, we have some drivers under drivers/hwmon. Are they using PECI? How they
> will be integrated to this? Can this be part of drivers/hwmon?

This is designed for SoCs have one-wire PECI hardware which can handle
raw PECI protocol. Some drivers under 'drivers/hwmon' that use
PECI-to-I2C translation hardware are out of scope from this patch.
In case if an SoC supports both PECI-to-I2C translation mode and raw
PECI mode, only raw PECI mode can be integrated to this. NPCM7xx is
the case and peci-npcm driver in this patch set is an example of
the raw PECI driver.

This patch includes peci-cputemp and peci-dimmtemp as raw PECI hwmon
drivers.

>> Changes since v10:
> 
> It's funny I don't remember previous version(s), but anyway I'll comment on
> this later on -- it has at least several style issues / inconveniences.

I CC'ed you in every submissions but you probably forgot that because I
submitted v10 in January this year. Thanks for your review.

>> - Split out peci-dev module from peci-core module.
>> - Added PECI 4.0 command set support.
>> - Refined 32-bit boundary alignment for all PECI ioctl command structs.
>> - Added DMA safe command buffer handling in peci-core.
>> - Refined kconfig dependencies in PECI subsystem.
>> - Fixed minor bugs and style issues.
>> - configfs support isn't added in this patch set. Will add that using a
>>    seperate patch set.
> 
>> +config PECI
>> +	tristate "PECI support"
>> +	select CRC8
> 
>> +	default n
> 
> As for beginning, this one is redundant.
> If you have more, drop them.

I see. I'll drop the default setting.

>> +#include <linux/bitfield.h>
>> +#include <linux/crc8.h>
>> +#include <linux/delay.h>
>> +#include <linux/mm.h>
>> +#include <linux/module.h>
> 
>> +#include <linux/of_device.h>
> 
> What about ACPI? Can you use fwnode API?

Currently, it's targeting BMC (Baseboard Management Controller) SoCs
that are running on ARM kernel. If any needs of ACPI support comes in
the future, the ACPI support will be added.

Thanks a lot for your review!

-Jae

>> +#include <linux/peci.h>
>> +#include <linux/pm_domain.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/sched/task_stack.h>
>> +#include <linux/slab.h>
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
  2019-12-11 20:28     ` Andy Shevchenko
@ 2019-12-12  0:50       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-12  0:50 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Robin Murphy, Ryan Chen

Hi Andy,

On 12/11/2019 12:28 PM, Andy Shevchenko wrote:
> On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:
>> This commit adds Aspeed PECI adapter driver for Aspeed
>> AST24xx/25xx/26xx SoCs.
> 
> ...
> 
>> +#define   ASPEED_PECI_CMD_IDLE_MASK		(ASPEED_PECI_CMD_STS_MASK | \
>> +						 ASPEED_PECI_CMD_PIN_MON)
> 
> Better looking when the value completely occupies second line without touching
> the first.

Yes. Will change it.

> ...
> 
>> +static int aspeed_peci_check_idle(struct aspeed_peci *priv)
>> +{
>> +	ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
>> +	u32 cmd_sts;
> 
> Like in the previous patch this one has hard to read timeout loops with inefficient code.
> 
>> +	for (;;) {
>> +		cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
>> +		if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
>> +			break;
> 
>> +		if (time_after(jiffies, timeout)) {
> 
> This is actually main exit condition (vs. infinite loop).
> 
>> +			cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
> 
> This make no sense. If you would like to have one more iteration, just spell it
> explicitly.
> 
>> +			break;
>> +		}
> 
>> +		usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
>> +			     ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
>> +	}
>> +
> 
>> +	return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;
> 
> Ditto.
> 
>> +}
> 
> Now look at the other variant:
> 
> 	do {
> 		...do something...
> 		if (success)
> 			return 0;
> 		usleep(...);
> 	} while (time_before(...));
> 
> 	return -ETIMEDOUT;
> 
> * Easy
> * less LOCs
> * guaranteed always to be at least one iteration
> * has explicitly spelled exit condition
> 
> BUT!
> 
> In this very case you may do even better if you read iopoll.h, i.e
> readl_poll_timeout() has this functionality embedded in the macro.
> 

I see. I'll simplify this function like below:

#include <linux/iopoll.h>

static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
{
	u32 cmd_sts;

	return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
				  cmd_sts,
				  !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
				  ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
				  ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
}

Thanks a lot for your review!

-Jae

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
@ 2019-12-12  0:50       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-12  0:50 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Thomas Gleixner,
	Sagar Dharia, linux-arm-kernel, Juergen Gross, Cyrille Pitchen,
	Ryan Chen, Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
	Stephen Boyd, Vinod Koul, Gustavo Pimentel, Andrew Morton,
	Robin Murphy, David S . Miller

Hi Andy,

On 12/11/2019 12:28 PM, Andy Shevchenko wrote:
> On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:
>> This commit adds Aspeed PECI adapter driver for Aspeed
>> AST24xx/25xx/26xx SoCs.
> 
> ...
> 
>> +#define   ASPEED_PECI_CMD_IDLE_MASK		(ASPEED_PECI_CMD_STS_MASK | \
>> +						 ASPEED_PECI_CMD_PIN_MON)
> 
> Better looking when the value completely occupies second line without touching
> the first.

Yes. Will change it.

> ...
> 
>> +static int aspeed_peci_check_idle(struct aspeed_peci *priv)
>> +{
>> +	ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
>> +	u32 cmd_sts;
> 
> Like in the previous patch this one has hard to read timeout loops with inefficient code.
> 
>> +	for (;;) {
>> +		cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
>> +		if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK))
>> +			break;
> 
>> +		if (time_after(jiffies, timeout)) {
> 
> This is actually main exit condition (vs. infinite loop).
> 
>> +			cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
> 
> This make no sense. If you would like to have one more iteration, just spell it
> explicitly.
> 
>> +			break;
>> +		}
> 
>> +		usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1,
>> +			     ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC);
>> +	}
>> +
> 
>> +	return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT;
> 
> Ditto.
> 
>> +}
> 
> Now look at the other variant:
> 
> 	do {
> 		...do something...
> 		if (success)
> 			return 0;
> 		usleep(...);
> 	} while (time_before(...));
> 
> 	return -ETIMEDOUT;
> 
> * Easy
> * less LOCs
> * guaranteed always to be at least one iteration
> * has explicitly spelled exit condition
> 
> BUT!
> 
> In this very case you may do even better if you read iopoll.h, i.e
> readl_poll_timeout() has this functionality embedded in the macro.
> 

I see. I'll simplify this function like below:

#include <linux/iopoll.h>

static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
{
	u32 cmd_sts;

	return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
				  cmd_sts,
				  !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
				  ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
				  ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
}

Thanks a lot for your review!

-Jae

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
  2019-12-12  0:50       ` Jae Hyun Yoo
@ 2019-12-12  8:47         ` Andy Shevchenko
  -1 siblings, 0 replies; 71+ messages in thread
From: Andy Shevchenko @ 2019-12-12  8:47 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Robin Murphy, Ryan Chen

On Wed, Dec 11, 2019 at 04:50:04PM -0800, Jae Hyun Yoo wrote:
> On 12/11/2019 12:28 PM, Andy Shevchenko wrote:
> > On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:

> > Like in the previous patch...

(1)

> I see. I'll simplify this function like below:
> 
> #include <linux/iopoll.h>
> 
> static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
> {
> 	u32 cmd_sts;
> 
> 	return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
> 				  cmd_sts,
> 				  !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
> 				  ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
> 				  ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
> }

Good for *this* case, but please fix all the rest accordingly.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
@ 2019-12-12  8:47         ` Andy Shevchenko
  0 siblings, 0 replies; 71+ messages in thread
From: Andy Shevchenko @ 2019-12-12  8:47 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Thomas Gleixner,
	Sagar Dharia, linux-arm-kernel, Juergen Gross, Cyrille Pitchen,
	Ryan Chen, Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
	Stephen Boyd, Vinod Koul, Gustavo Pimentel, Andrew Morton,
	Robin Murphy, David S . Miller

On Wed, Dec 11, 2019 at 04:50:04PM -0800, Jae Hyun Yoo wrote:
> On 12/11/2019 12:28 PM, Andy Shevchenko wrote:
> > On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:

> > Like in the previous patch...

(1)

> I see. I'll simplify this function like below:
> 
> #include <linux/iopoll.h>
> 
> static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
> {
> 	u32 cmd_sts;
> 
> 	return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
> 				  cmd_sts,
> 				  !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
> 				  ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
> 				  ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
> }

Good for *this* case, but please fix all the rest accordingly.

-- 
With Best Regards,
Andy Shevchenko



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
  2019-12-12  8:47         ` Andy Shevchenko
@ 2019-12-12 18:51           ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-12 18:51 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Guenter Roeck, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Robin Murphy, Ryan Chen

Hi Andy,

On 12/12/2019 12:47 AM, Andy Shevchenko wrote:
> On Wed, Dec 11, 2019 at 04:50:04PM -0800, Jae Hyun Yoo wrote:
>> On 12/11/2019 12:28 PM, Andy Shevchenko wrote:
>>> On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:
> 
>>> Like in the previous patch...
> 
> (1)
> 
>> I see. I'll simplify this function like below:
>>
>> #include <linux/iopoll.h>
>>
>> static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
>> {
>> 	u32 cmd_sts;
>>
>> 	return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
>> 				  cmd_sts,
>> 				  !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
>> 				  ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
>> 				  ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
>> }
> 
> Good for *this* case, but please fix all the rest accordingly.

Sure. Will do that.

Thanks,

Jae

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver
@ 2019-12-12 18:51           ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-12 18:51 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Thomas Gleixner,
	Sagar Dharia, linux-arm-kernel, Juergen Gross, Cyrille Pitchen,
	Ryan Chen, Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
	Stephen Boyd, Vinod Koul, Gustavo Pimentel, Andrew Morton,
	Robin Murphy, David S . Miller

Hi Andy,

On 12/12/2019 12:47 AM, Andy Shevchenko wrote:
> On Wed, Dec 11, 2019 at 04:50:04PM -0800, Jae Hyun Yoo wrote:
>> On 12/11/2019 12:28 PM, Andy Shevchenko wrote:
>>> On Wed, Dec 11, 2019 at 11:46:16AM -0800, Jae Hyun Yoo wrote:
> 
>>> Like in the previous patch...
> 
> (1)
> 
>> I see. I'll simplify this function like below:
>>
>> #include <linux/iopoll.h>
>>
>> static inline int aspeed_peci_check_idle(struct aspeed_peci *priv)
>> {
>> 	u32 cmd_sts;
>>
>> 	return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
>> 				  cmd_sts,
>> 				  !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
>> 				  ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC,
>> 				  ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC);
>> }
> 
> Good for *this* case, but please fix all the rest accordingly.

Sure. Will do that.

Thanks,

Jae

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 13/14] hwmon: Add PECI cputemp driver
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-13  6:24     ` Guenter Roeck
  -1 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-13  6:24 UTC (permalink / raw)
  To: Jae Hyun Yoo, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
> This commit adds PECI cputemp hwmon driver.
> 
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Jean Delvare <jdelvare@suse.com>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> Reviewed-by: James Feist <james.feist@linux.intel.com>
> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> Changes since v10:
> - Added Skylake Xeon D support.
> - Added DTS temperature which is more thermal control friendlier than Die
>    temperature.
> - Fixed minor bugs and style issues.
> 
>   drivers/hwmon/Kconfig        |  14 ++
>   drivers/hwmon/Makefile       |   1 +
>   drivers/hwmon/peci-cputemp.c | 448 +++++++++++++++++++++++++++++++++++
>   drivers/hwmon/peci-hwmon.h   |  46 ++++
>   4 files changed, 509 insertions(+)
>   create mode 100644 drivers/hwmon/peci-cputemp.c
>   create mode 100644 drivers/hwmon/peci-hwmon.h
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 23dfe848979a..b6604759579c 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1349,6 +1349,20 @@ config SENSORS_PCF8591
>   	  These devices are hard to detect and rarely found on mainstream
>   	  hardware. If unsure, say N.
>   
> +config SENSORS_PECI_CPUTEMP
> +	tristate "PECI CPU temperature monitoring client"
> +	depends on PECI
> +	select MFD_INTEL_PECI_CLIENT
> +	help
> +	  If you say yes here you get support for the generic Intel PECI
> +	  cputemp driver which provides Digital Thermal Sensor (DTS) thermal
> +	  readings of the CPU package and CPU cores that are accessible using
> +	  the PECI Client Command Suite via the processor PECI client.
> +	  Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called peci-cputemp.
> +
>   source "drivers/hwmon/pmbus/Kconfig"
>   
>   config SENSORS_PWM_FAN
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 6db5db9cdc29..d6fea48697af 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o
>   obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
>   obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
>   obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
> +obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>   obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
> diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
> new file mode 100644
> index 000000000000..70ced9f9299f
> --- /dev/null
> +++ b/drivers/hwmon/peci-cputemp.c
> @@ -0,0 +1,448 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018-2019 Intel Corporation
> +
> +#include <linux/hwmon.h>
> +#include <linux/jiffies.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include "peci-hwmon.h"
> +
> +#define DEFAULT_CHANNEL_NUMS   5
> +#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX

Why not just use CORE_NUMS_MAX ?

> +#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
> +
> +struct temp_group {
> +	struct peci_sensor_data die;
> +	struct peci_sensor_data dts;
> +	struct peci_sensor_data tcontrol;
> +	struct peci_sensor_data tthrottle;
> +	struct peci_sensor_data tjmax;
> +	struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
> +};
> +
> +struct peci_cputemp {
> +	struct peci_client_manager *mgr;
> +	struct device *dev;
> +	char name[PECI_NAME_SIZE];
> +	const struct cpu_gen_info *gen_info;
> +	struct temp_group temp;
> +	u64 core_mask;
> +	u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
> +	uint config_idx;
> +	struct hwmon_channel_info temp_info;
> +	const struct hwmon_channel_info *info[2];
> +	struct hwmon_chip_info chip;
> +};
> +
> +enum cputemp_channels {
> +	channel_die,
> +	channel_dts,
> +	channel_tcontrol,
> +	channel_tthrottle,
> +	channel_tjmax,
> +	channel_core,
> +};
> +
> +static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
> +	/* Die temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> +	HWMON_T_CRIT_HYST,
> +
> +	/* DTS margin */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> +	HWMON_T_CRIT_HYST,
> +
> +	/* Tcontrol temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
> +
> +	/* Tthrottle temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT,
> +
> +	/* Tjmax temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT,
> +
> +	/* Core temperature - for all core channels */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> +	HWMON_T_CRIT_HYST,
> +};
> +
> +static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
> +	"Die",
> +	"DTS",
> +	"Tcontrol",
> +	"Tthrottle",
> +	"Tjmax",
> +	"Core 0", "Core 1", "Core 2", "Core 3",
> +	"Core 4", "Core 5", "Core 6", "Core 7",
> +	"Core 8", "Core 9", "Core 10", "Core 11",
> +	"Core 12", "Core 13", "Core 14", "Core 15",
> +	"Core 16", "Core 17", "Core 18", "Core 19",
> +	"Core 20", "Core 21", "Core 22", "Core 23",
> +	"Core 24", "Core 25", "Core 26", "Core 27",
> +};
> +

What happens if CPUTEMP_CHANNEL_NUMS is increased in the future,
and some of this array includes NULL pointers because this file
doesn't track CORE_NUMS_MAX ?

> +static s32 ten_dot_six_to_millidegree(s32 val)
> +{
> +	return ((val ^ 0x8000) - 0x8000) * 1000 / 64;

Normally I suggest to use DIV_ROUND_CLOSEST in situations like this
to improve rounding, but ultimately that is your call.

> +}
> +
> +static int get_temp_targets(struct peci_cputemp *priv)
> +{
> +	s32 tthrottle_offset;
> +	s32 tcontrol_margin;
> +	u8  pkg_cfg[4];
> +	int ret;
> +
> +	/*
> +	 * Just use only the tcontrol marker to determine if target values need
> +	 * update.
> +	 */
> +	if (!peci_sensor_need_update(&priv->temp.tcontrol))
> +		return 0;
> +
> +	ret = peci_client_read_package_config(priv->mgr,
> +					      PECI_MBX_INDEX_TEMP_TARGET, 0,
> +					      pkg_cfg);
> +	if (ret)
> +		return ret;
> +
> +	priv->temp.tjmax.value = pkg_cfg[2] * 1000;
> +
> +	tcontrol_margin = pkg_cfg[1];
> +	tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
> +	priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
> +
> +	tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
> +	priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
> +
> +	peci_sensor_mark_updated(&priv->temp.tcontrol);
> +
> +	return 0;
> +}
> +
> +static int get_die_temp(struct peci_cputemp *priv)
> +{
> +	struct peci_get_temp_msg msg;
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp.die))
> +		return 0;
> +
> +	msg.addr = priv->mgr->client->addr;
> +
> +	ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
> +	if (ret)
> +		return ret;
> +
> +	/* Note that the tjmax should be available before calling it */
> +	priv->temp.die.value = priv->temp.tjmax.value +
> +			       (msg.temp_raw * 1000 / 64);
> +
> +	peci_sensor_mark_updated(&priv->temp.die);
> +
> +	return 0;
> +}
> +
> +static int get_dts(struct peci_cputemp *priv)
> +{
> +	s32 dts_margin;
> +	u8  pkg_cfg[4];
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp.dts))
> +		return 0;
> +
> +	ret = peci_client_read_package_config(priv->mgr,
> +					      PECI_MBX_INDEX_DTS_MARGIN, 0,
> +					      pkg_cfg);
> +
> +	if (ret)
> +		return ret;
> +
> +	dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
> +
> +	/**
> +	 * Processors return a value of DTS reading in 10.6 format
> +	 * (10 bits signed decimal, 6 bits fractional).
> +	 * Error codes:
> +	 *   0x8000: General sensor error
> +	 *   0x8001: Reserved
> +	 *   0x8002: Underflow on reading value
> +	 *   0x8003-0x81ff: Reserved
> +	 */
> +	if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
> +		return -EIO;
> +
> +	dts_margin = ten_dot_six_to_millidegree(dts_margin);
> +
> +	/* Note that the tcontrol should be available before calling it */
> +	priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
> +
> +	peci_sensor_mark_updated(&priv->temp.dts);
> +
> +	return 0;
> +}
> +
> +static int get_core_temp(struct peci_cputemp *priv, int core_index)
> +{
> +	s32 core_dts_margin;
> +	u8  pkg_cfg[4];
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp.core[core_index]))
> +		return 0;
> +
> +	ret = peci_client_read_package_config(priv->mgr,
> +					      PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
> +					      core_index, pkg_cfg);
> +	if (ret)
> +		return ret;
> +
> +	core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
> +

Any special readon for using the helper function here but not
above ?

> +	/*
> +	 * Processors return a value of the core DTS reading in 10.6 format
> +	 * (10 bits signed decimal, 6 bits fractional).
> +	 * Error codes:
> +	 *   0x8000: General sensor error
> +	 *   0x8001: Reserved
> +	 *   0x8002: Underflow on reading value
> +	 *   0x8003-0x81ff: Reserved
> +	 */
> +	if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
> +		return -EIO;
> +
> +	core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
> +
> +	/* Note that the tjmax should be available before calling it */
> +	priv->temp.core[core_index].value = priv->temp.tjmax.value +
> +					    core_dts_margin;
> +
> +	peci_sensor_mark_updated(&priv->temp.core[core_index]);
> +
> +	return 0;
> +}
> +
> +static int cputemp_read_string(struct device *dev,
> +			       enum hwmon_sensor_types type,
> +			       u32 attr, int channel, const char **str)
> +{
> +	if (attr != hwmon_temp_label)
> +		return -EOPNOTSUPP;
> +
> +	*str = cputemp_label[channel];
> +
> +	return 0;
> +}
> +
> +static int cputemp_read(struct device *dev,
> +			enum hwmon_sensor_types type,
> +			u32 attr, int channel, long *val)
> +{
> +	struct peci_cputemp *priv = dev_get_drvdata(dev);
> +	int ret, core_index;
> +
> +	if (channel >= CPUTEMP_CHANNEL_NUMS ||
> +	    !(priv->temp_config[channel] & BIT(attr)))
> +		return -EOPNOTSUPP;
> +
> +	ret = get_temp_targets(priv);
> +	if (ret)
> +		return ret;
> +
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		switch (channel) {
> +		case channel_die:
> +			ret = get_die_temp(priv);
> +			if (ret)
> +				break;
> +
> +			*val = priv->temp.die.value;
> +			break;
> +		case channel_dts:
> +			ret = get_dts(priv);
> +			if (ret)
> +				break;
> +
> +			*val = priv->temp.dts.value;
> +			break;
> +		case channel_tcontrol:
> +			*val = priv->temp.tcontrol.value;
> +			break;
> +		case channel_tthrottle:
> +			*val = priv->temp.tthrottle.value;
> +			break;
> +		case channel_tjmax:
> +			*val = priv->temp.tjmax.value;
> +			break;
> +		default:
> +			core_index = channel - DEFAULT_CHANNEL_NUMS;
> +			ret = get_core_temp(priv, core_index);
> +			if (ret)
> +				break;
> +
> +			*val = priv->temp.core[core_index].value;
> +			break;
> +		}
> +		break;
> +	case hwmon_temp_max:
> +		*val = priv->temp.tcontrol.value;
> +		break;
> +	case hwmon_temp_crit:
> +		*val = priv->temp.tjmax.value;
> +		break;
> +	case hwmon_temp_crit_hyst:
> +		*val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
> +		break;
> +	default:
> +		ret = -EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static umode_t cputemp_is_visible(const void *data,
> +				  enum hwmon_sensor_types type,
> +				  u32 attr, int channel)
> +{
> +	const struct peci_cputemp *priv = data;
> +
> +	if (channel < ARRAY_SIZE(priv->temp_config) &&
> +	    (priv->temp_config[channel] & BIT(attr)) &&
> +	    (channel < DEFAULT_CHANNEL_NUMS ||
> +	     (channel >= DEFAULT_CHANNEL_NUMS &&
> +	      (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops cputemp_ops = {
> +	.is_visible = cputemp_is_visible,
> +	.read_string = cputemp_read_string,
> +	.read = cputemp_read,
> +};
> +
> +static int check_resolved_cores(struct peci_cputemp *priv)
> +{
> +	struct peci_rd_pci_cfg_local_msg msg;
> +	int ret;
> +
> +	/* Get the RESOLVED_CORES register value */
> +	msg.addr = priv->mgr->client->addr;
> +	msg.device = 30;
> +	msg.function = 3;
> +	msg.rx_len = 4;
> +	msg.bus = 1;
> +	msg.reg = 0xb4;
> +
> +	ret = peci_command(priv->mgr->client->adapter,
> +			   PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
> +	if (msg.cc != PECI_DEV_CC_SUCCESS)
> +		ret = -EAGAIN;
> +	if (ret)
> +		return ret;
> +
> +	priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
> +	if (!priv->core_mask)
> +		return -EAGAIN;
> +
> +	dev_dbg(priv->dev, "Scanned resolved cores: 0x%llx\n", priv->core_mask);
> +
> +	return 0;
> +}
> +
> +static int create_core_temp_info(struct peci_cputemp *priv)
> +{
> +	int ret, i;
> +
> +	ret = check_resolved_cores(priv);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < priv->gen_info->core_max; i++)
> +		if (priv->core_mask & BIT(i))
> +			while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
> +				priv->temp_config[priv->config_idx++] =
> +					config_table[channel_core];
> +
> +	return 0;
> +}
> +
> +static int peci_cputemp_probe(struct platform_device *pdev)
> +{
> +	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
> +	struct device *dev = &pdev->dev;
> +	struct peci_cputemp *priv;
> +	struct device *hwmon_dev;
> +	int ret;
> +
> +	if ((mgr->client->adapter->cmd_mask &
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
> +		return -ENODEV;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, priv);
> +	priv->mgr = mgr;
> +	priv->dev = dev;
> +	priv->gen_info = mgr->gen_info;
> +
> +	snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
> +		 mgr->client->addr - PECI_BASE_ADDR);
> +
> +	priv->temp_config[priv->config_idx++] = config_table[channel_die];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_dts];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
> +
> +	ret = create_core_temp_info(priv);
> +	if (ret)
> +		dev_dbg(dev, "Skipped creating core temp info\n");
> +
> +	priv->chip.ops = &cputemp_ops;
> +	priv->chip.info = priv->info;
> +
> +	priv->info[0] = &priv->temp_info;
> +
> +	priv->temp_info.type = hwmon_temp;
> +	priv->temp_info.config = priv->temp_config;
> +
> +	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
> +							 priv->name,
> +							 priv,
> +							 &priv->chip,
> +							 NULL);
> +
> +	if (IS_ERR(hwmon_dev))
> +		return PTR_ERR(hwmon_dev);
> +
> +	dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id peci_cputemp_ids[] = {
> +	{ .name = "peci-cputemp", .driver_data = 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
> +
> +static struct platform_driver peci_cputemp_driver = {
> +	.probe    = peci_cputemp_probe,
> +	.id_table = peci_cputemp_ids,
> +	.driver   = { .name = KBUILD_MODNAME, },
> +};
> +module_platform_driver(peci_cputemp_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI cputemp driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
> new file mode 100644
> index 000000000000..e0e3c901c6e4
> --- /dev/null
> +++ b/drivers/hwmon/peci-hwmon.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2018-2019 Intel Corporation */
> +
> +#ifndef __PECI_HWMON_H
> +#define __PECI_HWMON_H
> +
> +#include <linux/peci.h>
> +
> +#define TEMP_TYPE_PECI   6 /* Sensor type 6: Intel PECI */
> +#define UPDATE_INTERVAL  HZ
> +
> +/**
> + * struct peci_sensor_data - PECI sensor information
> + * @valid: flag to indicate the sensor value is valid
> + * @value: sensor value in millidegree Celsius
> + * @last_updated: time of the last update in jiffies
> + */
> +struct peci_sensor_data {
> +	uint  valid;
> +	s32   value;
> +	ulong last_updated;
> +};
> +
> +/**
> + * peci_sensor_need_update - check whether sensor update is needed or not
> + * @sensor: pointer to sensor data struct
> + *
> + * Return: true if update is needed, false if not.
> + */
> +static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
> +{
> +	return !sensor->valid ||
> +	       time_after(jiffies, sensor->last_updated + UPDATE_INTERVAL);
> +}
> +
> +/**
> + * peci_sensor_mark_updated - mark the sensor is updated
> + * @sensor: pointer to sensor data struct
> + */
> +static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
> +{
> +	sensor->valid = 1;
> +	sensor->last_updated = jiffies;
> +}
> +
> +#endif /* __PECI_HWMON_H */
> 


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 13/14] hwmon: Add PECI cputemp driver
@ 2019-12-13  6:24     ` Guenter Roeck
  0 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-13  6:24 UTC (permalink / raw)
  To: Jae Hyun Yoo, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-doc, openbmc, Miguel Ojeda,
	Andrew Lunn, Stef van Os, Jason M Biils, Andy Shevchenko,
	linux-arm-kernel, Alan Cox

On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
> This commit adds PECI cputemp hwmon driver.
> 
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Jean Delvare <jdelvare@suse.com>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> Reviewed-by: James Feist <james.feist@linux.intel.com>
> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> Changes since v10:
> - Added Skylake Xeon D support.
> - Added DTS temperature which is more thermal control friendlier than Die
>    temperature.
> - Fixed minor bugs and style issues.
> 
>   drivers/hwmon/Kconfig        |  14 ++
>   drivers/hwmon/Makefile       |   1 +
>   drivers/hwmon/peci-cputemp.c | 448 +++++++++++++++++++++++++++++++++++
>   drivers/hwmon/peci-hwmon.h   |  46 ++++
>   4 files changed, 509 insertions(+)
>   create mode 100644 drivers/hwmon/peci-cputemp.c
>   create mode 100644 drivers/hwmon/peci-hwmon.h
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 23dfe848979a..b6604759579c 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1349,6 +1349,20 @@ config SENSORS_PCF8591
>   	  These devices are hard to detect and rarely found on mainstream
>   	  hardware. If unsure, say N.
>   
> +config SENSORS_PECI_CPUTEMP
> +	tristate "PECI CPU temperature monitoring client"
> +	depends on PECI
> +	select MFD_INTEL_PECI_CLIENT
> +	help
> +	  If you say yes here you get support for the generic Intel PECI
> +	  cputemp driver which provides Digital Thermal Sensor (DTS) thermal
> +	  readings of the CPU package and CPU cores that are accessible using
> +	  the PECI Client Command Suite via the processor PECI client.
> +	  Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called peci-cputemp.
> +
>   source "drivers/hwmon/pmbus/Kconfig"
>   
>   config SENSORS_PWM_FAN
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 6db5db9cdc29..d6fea48697af 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o
>   obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
>   obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
>   obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
> +obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>   obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
> diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
> new file mode 100644
> index 000000000000..70ced9f9299f
> --- /dev/null
> +++ b/drivers/hwmon/peci-cputemp.c
> @@ -0,0 +1,448 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018-2019 Intel Corporation
> +
> +#include <linux/hwmon.h>
> +#include <linux/jiffies.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include "peci-hwmon.h"
> +
> +#define DEFAULT_CHANNEL_NUMS   5
> +#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX

Why not just use CORE_NUMS_MAX ?

> +#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
> +
> +struct temp_group {
> +	struct peci_sensor_data die;
> +	struct peci_sensor_data dts;
> +	struct peci_sensor_data tcontrol;
> +	struct peci_sensor_data tthrottle;
> +	struct peci_sensor_data tjmax;
> +	struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
> +};
> +
> +struct peci_cputemp {
> +	struct peci_client_manager *mgr;
> +	struct device *dev;
> +	char name[PECI_NAME_SIZE];
> +	const struct cpu_gen_info *gen_info;
> +	struct temp_group temp;
> +	u64 core_mask;
> +	u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
> +	uint config_idx;
> +	struct hwmon_channel_info temp_info;
> +	const struct hwmon_channel_info *info[2];
> +	struct hwmon_chip_info chip;
> +};
> +
> +enum cputemp_channels {
> +	channel_die,
> +	channel_dts,
> +	channel_tcontrol,
> +	channel_tthrottle,
> +	channel_tjmax,
> +	channel_core,
> +};
> +
> +static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
> +	/* Die temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> +	HWMON_T_CRIT_HYST,
> +
> +	/* DTS margin */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> +	HWMON_T_CRIT_HYST,
> +
> +	/* Tcontrol temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
> +
> +	/* Tthrottle temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT,
> +
> +	/* Tjmax temperature */
> +	HWMON_T_LABEL | HWMON_T_INPUT,
> +
> +	/* Core temperature - for all core channels */
> +	HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
> +	HWMON_T_CRIT_HYST,
> +};
> +
> +static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
> +	"Die",
> +	"DTS",
> +	"Tcontrol",
> +	"Tthrottle",
> +	"Tjmax",
> +	"Core 0", "Core 1", "Core 2", "Core 3",
> +	"Core 4", "Core 5", "Core 6", "Core 7",
> +	"Core 8", "Core 9", "Core 10", "Core 11",
> +	"Core 12", "Core 13", "Core 14", "Core 15",
> +	"Core 16", "Core 17", "Core 18", "Core 19",
> +	"Core 20", "Core 21", "Core 22", "Core 23",
> +	"Core 24", "Core 25", "Core 26", "Core 27",
> +};
> +

What happens if CPUTEMP_CHANNEL_NUMS is increased in the future,
and some of this array includes NULL pointers because this file
doesn't track CORE_NUMS_MAX ?

> +static s32 ten_dot_six_to_millidegree(s32 val)
> +{
> +	return ((val ^ 0x8000) - 0x8000) * 1000 / 64;

Normally I suggest to use DIV_ROUND_CLOSEST in situations like this
to improve rounding, but ultimately that is your call.

> +}
> +
> +static int get_temp_targets(struct peci_cputemp *priv)
> +{
> +	s32 tthrottle_offset;
> +	s32 tcontrol_margin;
> +	u8  pkg_cfg[4];
> +	int ret;
> +
> +	/*
> +	 * Just use only the tcontrol marker to determine if target values need
> +	 * update.
> +	 */
> +	if (!peci_sensor_need_update(&priv->temp.tcontrol))
> +		return 0;
> +
> +	ret = peci_client_read_package_config(priv->mgr,
> +					      PECI_MBX_INDEX_TEMP_TARGET, 0,
> +					      pkg_cfg);
> +	if (ret)
> +		return ret;
> +
> +	priv->temp.tjmax.value = pkg_cfg[2] * 1000;
> +
> +	tcontrol_margin = pkg_cfg[1];
> +	tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
> +	priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
> +
> +	tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
> +	priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
> +
> +	peci_sensor_mark_updated(&priv->temp.tcontrol);
> +
> +	return 0;
> +}
> +
> +static int get_die_temp(struct peci_cputemp *priv)
> +{
> +	struct peci_get_temp_msg msg;
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp.die))
> +		return 0;
> +
> +	msg.addr = priv->mgr->client->addr;
> +
> +	ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg);
> +	if (ret)
> +		return ret;
> +
> +	/* Note that the tjmax should be available before calling it */
> +	priv->temp.die.value = priv->temp.tjmax.value +
> +			       (msg.temp_raw * 1000 / 64);
> +
> +	peci_sensor_mark_updated(&priv->temp.die);
> +
> +	return 0;
> +}
> +
> +static int get_dts(struct peci_cputemp *priv)
> +{
> +	s32 dts_margin;
> +	u8  pkg_cfg[4];
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp.dts))
> +		return 0;
> +
> +	ret = peci_client_read_package_config(priv->mgr,
> +					      PECI_MBX_INDEX_DTS_MARGIN, 0,
> +					      pkg_cfg);
> +
> +	if (ret)
> +		return ret;
> +
> +	dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
> +
> +	/**
> +	 * Processors return a value of DTS reading in 10.6 format
> +	 * (10 bits signed decimal, 6 bits fractional).
> +	 * Error codes:
> +	 *   0x8000: General sensor error
> +	 *   0x8001: Reserved
> +	 *   0x8002: Underflow on reading value
> +	 *   0x8003-0x81ff: Reserved
> +	 */
> +	if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
> +		return -EIO;
> +
> +	dts_margin = ten_dot_six_to_millidegree(dts_margin);
> +
> +	/* Note that the tcontrol should be available before calling it */
> +	priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
> +
> +	peci_sensor_mark_updated(&priv->temp.dts);
> +
> +	return 0;
> +}
> +
> +static int get_core_temp(struct peci_cputemp *priv, int core_index)
> +{
> +	s32 core_dts_margin;
> +	u8  pkg_cfg[4];
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp.core[core_index]))
> +		return 0;
> +
> +	ret = peci_client_read_package_config(priv->mgr,
> +					      PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
> +					      core_index, pkg_cfg);
> +	if (ret)
> +		return ret;
> +
> +	core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
> +

Any special readon for using the helper function here but not
above ?

> +	/*
> +	 * Processors return a value of the core DTS reading in 10.6 format
> +	 * (10 bits signed decimal, 6 bits fractional).
> +	 * Error codes:
> +	 *   0x8000: General sensor error
> +	 *   0x8001: Reserved
> +	 *   0x8002: Underflow on reading value
> +	 *   0x8003-0x81ff: Reserved
> +	 */
> +	if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
> +		return -EIO;
> +
> +	core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
> +
> +	/* Note that the tjmax should be available before calling it */
> +	priv->temp.core[core_index].value = priv->temp.tjmax.value +
> +					    core_dts_margin;
> +
> +	peci_sensor_mark_updated(&priv->temp.core[core_index]);
> +
> +	return 0;
> +}
> +
> +static int cputemp_read_string(struct device *dev,
> +			       enum hwmon_sensor_types type,
> +			       u32 attr, int channel, const char **str)
> +{
> +	if (attr != hwmon_temp_label)
> +		return -EOPNOTSUPP;
> +
> +	*str = cputemp_label[channel];
> +
> +	return 0;
> +}
> +
> +static int cputemp_read(struct device *dev,
> +			enum hwmon_sensor_types type,
> +			u32 attr, int channel, long *val)
> +{
> +	struct peci_cputemp *priv = dev_get_drvdata(dev);
> +	int ret, core_index;
> +
> +	if (channel >= CPUTEMP_CHANNEL_NUMS ||
> +	    !(priv->temp_config[channel] & BIT(attr)))
> +		return -EOPNOTSUPP;
> +
> +	ret = get_temp_targets(priv);
> +	if (ret)
> +		return ret;
> +
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		switch (channel) {
> +		case channel_die:
> +			ret = get_die_temp(priv);
> +			if (ret)
> +				break;
> +
> +			*val = priv->temp.die.value;
> +			break;
> +		case channel_dts:
> +			ret = get_dts(priv);
> +			if (ret)
> +				break;
> +
> +			*val = priv->temp.dts.value;
> +			break;
> +		case channel_tcontrol:
> +			*val = priv->temp.tcontrol.value;
> +			break;
> +		case channel_tthrottle:
> +			*val = priv->temp.tthrottle.value;
> +			break;
> +		case channel_tjmax:
> +			*val = priv->temp.tjmax.value;
> +			break;
> +		default:
> +			core_index = channel - DEFAULT_CHANNEL_NUMS;
> +			ret = get_core_temp(priv, core_index);
> +			if (ret)
> +				break;
> +
> +			*val = priv->temp.core[core_index].value;
> +			break;
> +		}
> +		break;
> +	case hwmon_temp_max:
> +		*val = priv->temp.tcontrol.value;
> +		break;
> +	case hwmon_temp_crit:
> +		*val = priv->temp.tjmax.value;
> +		break;
> +	case hwmon_temp_crit_hyst:
> +		*val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
> +		break;
> +	default:
> +		ret = -EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static umode_t cputemp_is_visible(const void *data,
> +				  enum hwmon_sensor_types type,
> +				  u32 attr, int channel)
> +{
> +	const struct peci_cputemp *priv = data;
> +
> +	if (channel < ARRAY_SIZE(priv->temp_config) &&
> +	    (priv->temp_config[channel] & BIT(attr)) &&
> +	    (channel < DEFAULT_CHANNEL_NUMS ||
> +	     (channel >= DEFAULT_CHANNEL_NUMS &&
> +	      (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS)))))
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops cputemp_ops = {
> +	.is_visible = cputemp_is_visible,
> +	.read_string = cputemp_read_string,
> +	.read = cputemp_read,
> +};
> +
> +static int check_resolved_cores(struct peci_cputemp *priv)
> +{
> +	struct peci_rd_pci_cfg_local_msg msg;
> +	int ret;
> +
> +	/* Get the RESOLVED_CORES register value */
> +	msg.addr = priv->mgr->client->addr;
> +	msg.device = 30;
> +	msg.function = 3;
> +	msg.rx_len = 4;
> +	msg.bus = 1;
> +	msg.reg = 0xb4;
> +
> +	ret = peci_command(priv->mgr->client->adapter,
> +			   PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
> +	if (msg.cc != PECI_DEV_CC_SUCCESS)
> +		ret = -EAGAIN;
> +	if (ret)
> +		return ret;
> +
> +	priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
> +	if (!priv->core_mask)
> +		return -EAGAIN;
> +
> +	dev_dbg(priv->dev, "Scanned resolved cores: 0x%llx\n", priv->core_mask);
> +
> +	return 0;
> +}
> +
> +static int create_core_temp_info(struct peci_cputemp *priv)
> +{
> +	int ret, i;
> +
> +	ret = check_resolved_cores(priv);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < priv->gen_info->core_max; i++)
> +		if (priv->core_mask & BIT(i))
> +			while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS)
> +				priv->temp_config[priv->config_idx++] =
> +					config_table[channel_core];
> +
> +	return 0;
> +}
> +
> +static int peci_cputemp_probe(struct platform_device *pdev)
> +{
> +	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
> +	struct device *dev = &pdev->dev;
> +	struct peci_cputemp *priv;
> +	struct device *hwmon_dev;
> +	int ret;
> +
> +	if ((mgr->client->adapter->cmd_mask &
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
> +		return -ENODEV;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, priv);
> +	priv->mgr = mgr;
> +	priv->dev = dev;
> +	priv->gen_info = mgr->gen_info;
> +
> +	snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
> +		 mgr->client->addr - PECI_BASE_ADDR);
> +
> +	priv->temp_config[priv->config_idx++] = config_table[channel_die];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_dts];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
> +	priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
> +
> +	ret = create_core_temp_info(priv);
> +	if (ret)
> +		dev_dbg(dev, "Skipped creating core temp info\n");
> +
> +	priv->chip.ops = &cputemp_ops;
> +	priv->chip.info = priv->info;
> +
> +	priv->info[0] = &priv->temp_info;
> +
> +	priv->temp_info.type = hwmon_temp;
> +	priv->temp_info.config = priv->temp_config;
> +
> +	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
> +							 priv->name,
> +							 priv,
> +							 &priv->chip,
> +							 NULL);
> +
> +	if (IS_ERR(hwmon_dev))
> +		return PTR_ERR(hwmon_dev);
> +
> +	dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id peci_cputemp_ids[] = {
> +	{ .name = "peci-cputemp", .driver_data = 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
> +
> +static struct platform_driver peci_cputemp_driver = {
> +	.probe    = peci_cputemp_probe,
> +	.id_table = peci_cputemp_ids,
> +	.driver   = { .name = KBUILD_MODNAME, },
> +};
> +module_platform_driver(peci_cputemp_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI cputemp driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
> new file mode 100644
> index 000000000000..e0e3c901c6e4
> --- /dev/null
> +++ b/drivers/hwmon/peci-hwmon.h
> @@ -0,0 +1,46 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2018-2019 Intel Corporation */
> +
> +#ifndef __PECI_HWMON_H
> +#define __PECI_HWMON_H
> +
> +#include <linux/peci.h>
> +
> +#define TEMP_TYPE_PECI   6 /* Sensor type 6: Intel PECI */
> +#define UPDATE_INTERVAL  HZ
> +
> +/**
> + * struct peci_sensor_data - PECI sensor information
> + * @valid: flag to indicate the sensor value is valid
> + * @value: sensor value in millidegree Celsius
> + * @last_updated: time of the last update in jiffies
> + */
> +struct peci_sensor_data {
> +	uint  valid;
> +	s32   value;
> +	ulong last_updated;
> +};
> +
> +/**
> + * peci_sensor_need_update - check whether sensor update is needed or not
> + * @sensor: pointer to sensor data struct
> + *
> + * Return: true if update is needed, false if not.
> + */
> +static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
> +{
> +	return !sensor->valid ||
> +	       time_after(jiffies, sensor->last_updated + UPDATE_INTERVAL);
> +}
> +
> +/**
> + * peci_sensor_mark_updated - mark the sensor is updated
> + * @sensor: pointer to sensor data struct
> + */
> +static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
> +{
> +	sensor->valid = 1;
> +	sensor->last_updated = jiffies;
> +}
> +
> +#endif /* __PECI_HWMON_H */
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-13  6:32     ` Guenter Roeck
  -1 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-13  6:32 UTC (permalink / raw)
  To: Jae Hyun Yoo, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
> This commit adds PECI dimmtemp hwmon driver.
> 
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Jean Delvare <jdelvare@suse.com>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> Reviewed-by: James Feist <james.feist@linux.intel.com>
> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> Changes since v10:
> - Added Skylake Xeon D support.
> - Added max and crit properties for temperature threshold checking.
> - Fixed minor bugs and style issues.
> 
>   drivers/hwmon/Kconfig         |  14 ++
>   drivers/hwmon/Makefile        |   1 +
>   drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
>   3 files changed, 408 insertions(+)
>   create mode 100644 drivers/hwmon/peci-dimmtemp.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index b6604759579c..d3370fbab40c 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
>   	  This driver can also be built as a module. If so, the module
>   	  will be called peci-cputemp.
>   
> +config SENSORS_PECI_DIMMTEMP
> +	tristate "PECI DIMM temperature monitoring client"
> +	depends on PECI
> +	select MFD_INTEL_PECI_CLIENT
> +	help
> +	  If you say yes here you get support for the generic Intel PECI hwmon
> +	  driver which provides Digital Thermal Sensor (DTS) thermal readings of
> +	  DIMM components that are accessible using the PECI Client Command
> +	  Suite via the processor PECI client.
> +	  Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called peci-dimmtemp.
> +
>   source "drivers/hwmon/pmbus/Kconfig"
>   
>   config SENSORS_PWM_FAN
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index d6fea48697af..4015c4b60bf4 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
>   obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
>   obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
>   obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
> +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)	+= peci-dimmtemp.o
>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>   obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
> diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
> new file mode 100644
> index 000000000000..974f453f9366
> --- /dev/null
> +++ b/drivers/hwmon/peci-dimmtemp.c
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018-2019 Intel Corporation
> +
> +#include <linux/hwmon.h>
> +#include <linux/jiffies.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/workqueue.h>
> +#include "peci-hwmon.h"
> +
> +#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
> +#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
> +
> +struct peci_dimmtemp {
> +	struct peci_client_manager *mgr;
> +	struct device *dev;
> +	char name[PECI_NAME_SIZE];
> +	const struct cpu_gen_info *gen_info;
> +	struct workqueue_struct *work_queue;
> +	struct delayed_work work_handler;
> +	struct peci_sensor_data temp[DIMM_NUMS_MAX];
> +	long temp_max[DIMM_NUMS_MAX];
> +	long temp_crit[DIMM_NUMS_MAX];
> +	u32 dimm_mask;
> +	int retry_count;
> +	u32 temp_config[DIMM_NUMS_MAX + 1];
> +	struct hwmon_channel_info temp_info;
> +	const struct hwmon_channel_info *info[2];
> +	struct hwmon_chip_info chip;
> +};
> +
> +static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
> +	{ "DIMM A1", "DIMM A2", "DIMM A3" },
> +	{ "DIMM B1", "DIMM B2", "DIMM B3" },
> +	{ "DIMM C1", "DIMM C2", "DIMM C3" },
> +	{ "DIMM D1", "DIMM D2", "DIMM D3" },
> +	{ "DIMM E1", "DIMM E2", "DIMM E3" },
> +	{ "DIMM F1", "DIMM F2", "DIMM F3" },
> +	{ "DIMM G1", "DIMM G2", "DIMM G3" },
> +	{ "DIMM H1", "DIMM H2", "DIMM H3" },
> +};
> +
> +static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
> +					    int chan_rank,
> +					    u8 *cfg_data)
> +{
> +	return peci_client_read_package_config(priv->mgr,
> +					       PECI_MBX_INDEX_DDR_DIMM_TEMP,
> +					       chan_rank, cfg_data);
> +}
> +
> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
> +{
> +	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
> +	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
> +	struct peci_rd_pci_cfg_local_msg rp_msg;
> +	u8  cfg_data[4];
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp[dimm_no]))
> +		return 0;
> +
> +	ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> +	if (ret)
> +		return ret;
> +
> +	priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
> +
> +	switch (priv->gen_info->model) {
> +	case INTEL_FAM6_SKYLAKE_X:
> +		rp_msg.addr = priv->mgr->client->addr;
> +		rp_msg.bus = 2;
> +		/*
> +		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> +		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> +		 * Device 11, Function 2: IMC 0 channel 2 -> rank 2
> +		 * Device 12, Function 2: IMC 1 channel 0 -> rank 3
> +		 * Device 12, Function 6: IMC 1 channel 1 -> rank 4
> +		 * Device 13, Function 2: IMC 1 channel 2 -> rank 5
> +		 */
> +		rp_msg.device = 10 + chan_rank / 3 * 2 +
> +			     (chan_rank % 3 == 2 ? 1 : 0);
> +		rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
> +		rp_msg.reg = 0x120 + dimm_order * 4;
> +		rp_msg.rx_len = 4;
> +
> +		ret = peci_command(priv->mgr->client->adapter,
> +				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> +		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> +			ret = -EAGAIN;
> +		if (ret)
> +			return ret;
> +
> +		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> +		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> +		break;
> +	case INTEL_FAM6_SKYLAKE_XD:
> +		rp_msg.addr = priv->mgr->client->addr;
> +		rp_msg.bus = 2;
> +		/*
> +		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> +		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> +		 * Device 12, Function 2: IMC 1 channel 0 -> rank 2
> +		 * Device 12, Function 6: IMC 1 channel 1 -> rank 3
> +		 */
> +		rp_msg.device = 10 + chan_rank / 2 * 2;
> +		rp_msg.function = (chan_rank % 2) ? 6 : 2;
> +		rp_msg.reg = 0x120 + dimm_order * 4;
> +		rp_msg.rx_len = 4;
> +
> +		ret = peci_command(priv->mgr->client->adapter,
> +				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> +		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> +			ret = -EAGAIN;
> +		if (ret)
> +			return ret;
> +
> +		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> +		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> +		break;
> +	case INTEL_FAM6_HASWELL_X:
> +	case INTEL_FAM6_BROADWELL_X:
> +		rp_msg.addr = priv->mgr->client->addr;
> +		rp_msg.bus = 1;
> +		/*
> +		 * Device 20, Function 0: IMC 0 channel 0 -> rank 0
> +		 * Device 20, Function 1: IMC 0 channel 1 -> rank 1
> +		 * Device 21, Function 0: IMC 0 channel 2 -> rank 2
> +		 * Device 21, Function 1: IMC 0 channel 3 -> rank 3
> +		 * Device 23, Function 0: IMC 1 channel 0 -> rank 4
> +		 * Device 23, Function 1: IMC 1 channel 1 -> rank 5
> +		 * Device 24, Function 0: IMC 1 channel 2 -> rank 6
> +		 * Device 24, Function 1: IMC 1 channel 3 -> rank 7
> +		 */
> +		rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
> +		rp_msg.function = chan_rank % 2;
> +		rp_msg.reg = 0x120 + dimm_order * 4;
> +		rp_msg.rx_len = 4;
> +
> +		ret = peci_command(priv->mgr->client->adapter,
> +				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> +		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> +			ret = -EAGAIN;
> +		if (ret)
> +			return ret;
> +
> +		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> +		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> +		break;
> +	default:
> +		return -EOPNOTSUPP;

It looks like the sensors are created even on unsupported platforms,
which would generate error messages whenever someone tries to read
the attributes.

There should be some code early on checking this, and the driver
should not even instantiate if the CPU model is not supported.

> +	}
> +
> +	peci_sensor_mark_updated(&priv->temp[dimm_no]);
> +
> +	return 0;
> +}
> +
> +static int dimmtemp_read_string(struct device *dev,
> +				enum hwmon_sensor_types type,
> +				u32 attr, int channel, const char **str)
> +{
> +	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> +	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> +	int chan_rank, dimm_idx;
> +
> +	if (attr != hwmon_temp_label)
> +		return -EOPNOTSUPP;
> +
> +	chan_rank = channel / dimm_idx_max;
> +	dimm_idx = channel % dimm_idx_max;
> +	*str = dimmtemp_label[chan_rank][dimm_idx];

Similar to the other patch, I am concerned that this can end up setting *str
to NULL at some point in the future.

> +
> +	return 0;
> +}
> +
> +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
> +			 u32 attr, int channel, long *val)
> +{
> +	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = get_dimm_temp(priv, channel);
> +	if (ret)
> +		return ret;
> +
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		*val = priv->temp[channel].value;
> +		break;
> +	case hwmon_temp_max:
> +		*val = priv->temp_max[channel];
> +		break;
> +	case hwmon_temp_crit:
> +		*val = priv->temp_crit[channel];
> +		break;
> +	default:
> +		ret = -EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static umode_t dimmtemp_is_visible(const void *data,
> +				   enum hwmon_sensor_types type,
> +				   u32 attr, int channel)
> +{
> +	const struct peci_dimmtemp *priv = data;
> +
> +	if (priv->temp_config[channel] & BIT(attr) &&
> +	    priv->dimm_mask & BIT(channel))
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops dimmtemp_ops = {
> +	.is_visible = dimmtemp_is_visible,
> +	.read_string = dimmtemp_read_string,
> +	.read = dimmtemp_read,
> +};
> +
> +static int check_populated_dimms(struct peci_dimmtemp *priv)
> +{
> +	u32 chan_rank_max = priv->gen_info->chan_rank_max;
> +	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> +	int chan_rank, dimm_idx;
> +	u8  cfg_data[4];
> +
> +	for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
> +		int ret;
> +
> +		ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> +		if (ret) {
> +			priv->dimm_mask = 0;
> +			return ret;
> +		}
> +
> +		for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
> +			if (cfg_data[dimm_idx])
> +				priv->dimm_mask |= BIT(chan_rank *
> +						       dimm_idx_max +
> +						       dimm_idx);
> +	}
> +
> +	if (!priv->dimm_mask)
> +		return -EAGAIN;
> +
> +	dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
> +
> +	return 0;
> +}
> +
> +static int create_dimm_temp_info(struct peci_dimmtemp *priv)
> +{
> +	int ret, i, config_idx, channels;
> +	struct device *hwmon_dev;
> +
> +	ret = check_populated_dimms(priv);
> +	if (ret) {
> +		if (ret == -EAGAIN) {
> +			if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
> +				queue_delayed_work(priv->work_queue,
> +						   &priv->work_handler,
> +						 DIMM_MASK_CHECK_DELAY_JIFFIES);
> +				priv->retry_count++;
> +				dev_dbg(priv->dev,
> +					"Deferred DIMM temp info creation\n");
> +			} else {
> +				dev_err(priv->dev,
> +					"Timeout DIMM temp info creation\n");
> +				ret = -ETIMEDOUT;
> +			}
> +		}
> +
> +		return ret;
> +	}
> +
> +	channels = priv->gen_info->chan_rank_max *
> +		   priv->gen_info->dimm_idx_max;
> +	for (i = 0, config_idx = 0; i < channels; i++)
> +		if (priv->dimm_mask & BIT(i))
> +			while (i >= config_idx)
> +				priv->temp_config[config_idx++] =
> +					HWMON_T_LABEL | HWMON_T_INPUT |
> +					HWMON_T_MAX | HWMON_T_CRIT;
> +
> +	priv->chip.ops = &dimmtemp_ops;
> +	priv->chip.info = priv->info;
> +
> +	priv->info[0] = &priv->temp_info;
> +
> +	priv->temp_info.type = hwmon_temp;
> +	priv->temp_info.config = priv->temp_config;
> +
> +	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
> +							 priv->name,
> +							 priv,
> +							 &priv->chip,
> +							 NULL);
> +	ret = PTR_ERR_OR_ZERO(hwmon_dev);
> +	if (!ret)
> +		dev_dbg(priv->dev, "%s: sensor '%s'\n",
> +			dev_name(hwmon_dev), priv->name);
> +

Any chance to make this consistent with the other driver ?

> +	return ret;
> +}
> +
> +static void create_dimm_temp_info_delayed(struct work_struct *work)
> +{
> +	struct delayed_work *dwork = to_delayed_work(work);
> +	struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
> +						  work_handler);
> +	int ret;
> +
> +	ret = create_dimm_temp_info(priv);
> +	if (ret && ret != -EAGAIN)
> +		dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
> +}
> +
> +static int peci_dimmtemp_probe(struct platform_device *pdev)
> +{
> +	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
> +	struct device *dev = &pdev->dev;
> +	struct peci_dimmtemp *priv;
> +	int ret;
> +
> +	if ((mgr->client->adapter->cmd_mask &
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
> +		return -ENODEV;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, priv);
> +	priv->mgr = mgr;
> +	priv->dev = dev;
> +	priv->gen_info = mgr->gen_info;
> +
> +	snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
> +		 priv->mgr->client->addr - PECI_BASE_ADDR);
> +
> +	priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
> +	if (!priv->work_queue)
> +		return -ENOMEM;
> +
> +	INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
> +
> +	ret = create_dimm_temp_info(priv);
> +	if (ret && ret != -EAGAIN) {
> +		dev_err(dev, "Failed to create DIMM temp info\n");

Does this generate error messages if there are no DIMMS ?

> +		goto err_free_wq;
> +	}
> +
> +	return 0;
> +
> +err_free_wq:
> +	destroy_workqueue(priv->work_queue);
> +	return ret;
> +}
> +
> +static int peci_dimmtemp_remove(struct platform_device *pdev)
> +{
> +	struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
> +
> +	cancel_delayed_work_sync(&priv->work_handler);
> +	destroy_workqueue(priv->work_queue);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id peci_dimmtemp_ids[] = {
> +	{ .name = "peci-dimmtemp", .driver_data = 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
> +
> +static struct platform_driver peci_dimmtemp_driver = {
> +	.probe    = peci_dimmtemp_probe,
> +	.remove   = peci_dimmtemp_remove,
> +	.id_table = peci_dimmtemp_ids,
> +	.driver   = { .name = KBUILD_MODNAME, },
> +};
> +module_platform_driver(peci_dimmtemp_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI dimmtemp driver");
> +MODULE_LICENSE("GPL v2");
> 


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-13  6:32     ` Guenter Roeck
  0 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-13  6:32 UTC (permalink / raw)
  To: Jae Hyun Yoo, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-doc, openbmc, Miguel Ojeda,
	Andrew Lunn, Stef van Os, Jason M Biils, Andy Shevchenko,
	linux-arm-kernel, Alan Cox

On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
> This commit adds PECI dimmtemp hwmon driver.
> 
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Jean Delvare <jdelvare@suse.com>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
> Cc: Andrew Lunn <andrew@lunn.ch>
> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> Reviewed-by: James Feist <james.feist@linux.intel.com>
> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> Acked-by: Guenter Roeck <linux@roeck-us.net>
> ---
> Changes since v10:
> - Added Skylake Xeon D support.
> - Added max and crit properties for temperature threshold checking.
> - Fixed minor bugs and style issues.
> 
>   drivers/hwmon/Kconfig         |  14 ++
>   drivers/hwmon/Makefile        |   1 +
>   drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
>   3 files changed, 408 insertions(+)
>   create mode 100644 drivers/hwmon/peci-dimmtemp.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index b6604759579c..d3370fbab40c 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
>   	  This driver can also be built as a module. If so, the module
>   	  will be called peci-cputemp.
>   
> +config SENSORS_PECI_DIMMTEMP
> +	tristate "PECI DIMM temperature monitoring client"
> +	depends on PECI
> +	select MFD_INTEL_PECI_CLIENT
> +	help
> +	  If you say yes here you get support for the generic Intel PECI hwmon
> +	  driver which provides Digital Thermal Sensor (DTS) thermal readings of
> +	  DIMM components that are accessible using the PECI Client Command
> +	  Suite via the processor PECI client.
> +	  Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called peci-dimmtemp.
> +
>   source "drivers/hwmon/pmbus/Kconfig"
>   
>   config SENSORS_PWM_FAN
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index d6fea48697af..4015c4b60bf4 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
>   obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
>   obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
>   obj-$(CONFIG_SENSORS_PECI_CPUTEMP)	+= peci-cputemp.o
> +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)	+= peci-dimmtemp.o
>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>   obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
> diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
> new file mode 100644
> index 000000000000..974f453f9366
> --- /dev/null
> +++ b/drivers/hwmon/peci-dimmtemp.c
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018-2019 Intel Corporation
> +
> +#include <linux/hwmon.h>
> +#include <linux/jiffies.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/workqueue.h>
> +#include "peci-hwmon.h"
> +
> +#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
> +#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
> +
> +struct peci_dimmtemp {
> +	struct peci_client_manager *mgr;
> +	struct device *dev;
> +	char name[PECI_NAME_SIZE];
> +	const struct cpu_gen_info *gen_info;
> +	struct workqueue_struct *work_queue;
> +	struct delayed_work work_handler;
> +	struct peci_sensor_data temp[DIMM_NUMS_MAX];
> +	long temp_max[DIMM_NUMS_MAX];
> +	long temp_crit[DIMM_NUMS_MAX];
> +	u32 dimm_mask;
> +	int retry_count;
> +	u32 temp_config[DIMM_NUMS_MAX + 1];
> +	struct hwmon_channel_info temp_info;
> +	const struct hwmon_channel_info *info[2];
> +	struct hwmon_chip_info chip;
> +};
> +
> +static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
> +	{ "DIMM A1", "DIMM A2", "DIMM A3" },
> +	{ "DIMM B1", "DIMM B2", "DIMM B3" },
> +	{ "DIMM C1", "DIMM C2", "DIMM C3" },
> +	{ "DIMM D1", "DIMM D2", "DIMM D3" },
> +	{ "DIMM E1", "DIMM E2", "DIMM E3" },
> +	{ "DIMM F1", "DIMM F2", "DIMM F3" },
> +	{ "DIMM G1", "DIMM G2", "DIMM G3" },
> +	{ "DIMM H1", "DIMM H2", "DIMM H3" },
> +};
> +
> +static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
> +					    int chan_rank,
> +					    u8 *cfg_data)
> +{
> +	return peci_client_read_package_config(priv->mgr,
> +					       PECI_MBX_INDEX_DDR_DIMM_TEMP,
> +					       chan_rank, cfg_data);
> +}
> +
> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
> +{
> +	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
> +	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
> +	struct peci_rd_pci_cfg_local_msg rp_msg;
> +	u8  cfg_data[4];
> +	int ret;
> +
> +	if (!peci_sensor_need_update(&priv->temp[dimm_no]))
> +		return 0;
> +
> +	ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> +	if (ret)
> +		return ret;
> +
> +	priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
> +
> +	switch (priv->gen_info->model) {
> +	case INTEL_FAM6_SKYLAKE_X:
> +		rp_msg.addr = priv->mgr->client->addr;
> +		rp_msg.bus = 2;
> +		/*
> +		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> +		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> +		 * Device 11, Function 2: IMC 0 channel 2 -> rank 2
> +		 * Device 12, Function 2: IMC 1 channel 0 -> rank 3
> +		 * Device 12, Function 6: IMC 1 channel 1 -> rank 4
> +		 * Device 13, Function 2: IMC 1 channel 2 -> rank 5
> +		 */
> +		rp_msg.device = 10 + chan_rank / 3 * 2 +
> +			     (chan_rank % 3 == 2 ? 1 : 0);
> +		rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
> +		rp_msg.reg = 0x120 + dimm_order * 4;
> +		rp_msg.rx_len = 4;
> +
> +		ret = peci_command(priv->mgr->client->adapter,
> +				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> +		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> +			ret = -EAGAIN;
> +		if (ret)
> +			return ret;
> +
> +		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> +		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> +		break;
> +	case INTEL_FAM6_SKYLAKE_XD:
> +		rp_msg.addr = priv->mgr->client->addr;
> +		rp_msg.bus = 2;
> +		/*
> +		 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> +		 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> +		 * Device 12, Function 2: IMC 1 channel 0 -> rank 2
> +		 * Device 12, Function 6: IMC 1 channel 1 -> rank 3
> +		 */
> +		rp_msg.device = 10 + chan_rank / 2 * 2;
> +		rp_msg.function = (chan_rank % 2) ? 6 : 2;
> +		rp_msg.reg = 0x120 + dimm_order * 4;
> +		rp_msg.rx_len = 4;
> +
> +		ret = peci_command(priv->mgr->client->adapter,
> +				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> +		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> +			ret = -EAGAIN;
> +		if (ret)
> +			return ret;
> +
> +		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> +		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> +		break;
> +	case INTEL_FAM6_HASWELL_X:
> +	case INTEL_FAM6_BROADWELL_X:
> +		rp_msg.addr = priv->mgr->client->addr;
> +		rp_msg.bus = 1;
> +		/*
> +		 * Device 20, Function 0: IMC 0 channel 0 -> rank 0
> +		 * Device 20, Function 1: IMC 0 channel 1 -> rank 1
> +		 * Device 21, Function 0: IMC 0 channel 2 -> rank 2
> +		 * Device 21, Function 1: IMC 0 channel 3 -> rank 3
> +		 * Device 23, Function 0: IMC 1 channel 0 -> rank 4
> +		 * Device 23, Function 1: IMC 1 channel 1 -> rank 5
> +		 * Device 24, Function 0: IMC 1 channel 2 -> rank 6
> +		 * Device 24, Function 1: IMC 1 channel 3 -> rank 7
> +		 */
> +		rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
> +		rp_msg.function = chan_rank % 2;
> +		rp_msg.reg = 0x120 + dimm_order * 4;
> +		rp_msg.rx_len = 4;
> +
> +		ret = peci_command(priv->mgr->client->adapter,
> +				   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> +		if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> +			ret = -EAGAIN;
> +		if (ret)
> +			return ret;
> +
> +		priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> +		priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> +		break;
> +	default:
> +		return -EOPNOTSUPP;

It looks like the sensors are created even on unsupported platforms,
which would generate error messages whenever someone tries to read
the attributes.

There should be some code early on checking this, and the driver
should not even instantiate if the CPU model is not supported.

> +	}
> +
> +	peci_sensor_mark_updated(&priv->temp[dimm_no]);
> +
> +	return 0;
> +}
> +
> +static int dimmtemp_read_string(struct device *dev,
> +				enum hwmon_sensor_types type,
> +				u32 attr, int channel, const char **str)
> +{
> +	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> +	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> +	int chan_rank, dimm_idx;
> +
> +	if (attr != hwmon_temp_label)
> +		return -EOPNOTSUPP;
> +
> +	chan_rank = channel / dimm_idx_max;
> +	dimm_idx = channel % dimm_idx_max;
> +	*str = dimmtemp_label[chan_rank][dimm_idx];

Similar to the other patch, I am concerned that this can end up setting *str
to NULL at some point in the future.

> +
> +	return 0;
> +}
> +
> +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
> +			 u32 attr, int channel, long *val)
> +{
> +	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = get_dimm_temp(priv, channel);
> +	if (ret)
> +		return ret;
> +
> +	switch (attr) {
> +	case hwmon_temp_input:
> +		*val = priv->temp[channel].value;
> +		break;
> +	case hwmon_temp_max:
> +		*val = priv->temp_max[channel];
> +		break;
> +	case hwmon_temp_crit:
> +		*val = priv->temp_crit[channel];
> +		break;
> +	default:
> +		ret = -EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static umode_t dimmtemp_is_visible(const void *data,
> +				   enum hwmon_sensor_types type,
> +				   u32 attr, int channel)
> +{
> +	const struct peci_dimmtemp *priv = data;
> +
> +	if (priv->temp_config[channel] & BIT(attr) &&
> +	    priv->dimm_mask & BIT(channel))
> +		return 0444;
> +
> +	return 0;
> +}
> +
> +static const struct hwmon_ops dimmtemp_ops = {
> +	.is_visible = dimmtemp_is_visible,
> +	.read_string = dimmtemp_read_string,
> +	.read = dimmtemp_read,
> +};
> +
> +static int check_populated_dimms(struct peci_dimmtemp *priv)
> +{
> +	u32 chan_rank_max = priv->gen_info->chan_rank_max;
> +	u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> +	int chan_rank, dimm_idx;
> +	u8  cfg_data[4];
> +
> +	for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
> +		int ret;
> +
> +		ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> +		if (ret) {
> +			priv->dimm_mask = 0;
> +			return ret;
> +		}
> +
> +		for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
> +			if (cfg_data[dimm_idx])
> +				priv->dimm_mask |= BIT(chan_rank *
> +						       dimm_idx_max +
> +						       dimm_idx);
> +	}
> +
> +	if (!priv->dimm_mask)
> +		return -EAGAIN;
> +
> +	dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
> +
> +	return 0;
> +}
> +
> +static int create_dimm_temp_info(struct peci_dimmtemp *priv)
> +{
> +	int ret, i, config_idx, channels;
> +	struct device *hwmon_dev;
> +
> +	ret = check_populated_dimms(priv);
> +	if (ret) {
> +		if (ret == -EAGAIN) {
> +			if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
> +				queue_delayed_work(priv->work_queue,
> +						   &priv->work_handler,
> +						 DIMM_MASK_CHECK_DELAY_JIFFIES);
> +				priv->retry_count++;
> +				dev_dbg(priv->dev,
> +					"Deferred DIMM temp info creation\n");
> +			} else {
> +				dev_err(priv->dev,
> +					"Timeout DIMM temp info creation\n");
> +				ret = -ETIMEDOUT;
> +			}
> +		}
> +
> +		return ret;
> +	}
> +
> +	channels = priv->gen_info->chan_rank_max *
> +		   priv->gen_info->dimm_idx_max;
> +	for (i = 0, config_idx = 0; i < channels; i++)
> +		if (priv->dimm_mask & BIT(i))
> +			while (i >= config_idx)
> +				priv->temp_config[config_idx++] =
> +					HWMON_T_LABEL | HWMON_T_INPUT |
> +					HWMON_T_MAX | HWMON_T_CRIT;
> +
> +	priv->chip.ops = &dimmtemp_ops;
> +	priv->chip.info = priv->info;
> +
> +	priv->info[0] = &priv->temp_info;
> +
> +	priv->temp_info.type = hwmon_temp;
> +	priv->temp_info.config = priv->temp_config;
> +
> +	hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
> +							 priv->name,
> +							 priv,
> +							 &priv->chip,
> +							 NULL);
> +	ret = PTR_ERR_OR_ZERO(hwmon_dev);
> +	if (!ret)
> +		dev_dbg(priv->dev, "%s: sensor '%s'\n",
> +			dev_name(hwmon_dev), priv->name);
> +

Any chance to make this consistent with the other driver ?

> +	return ret;
> +}
> +
> +static void create_dimm_temp_info_delayed(struct work_struct *work)
> +{
> +	struct delayed_work *dwork = to_delayed_work(work);
> +	struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
> +						  work_handler);
> +	int ret;
> +
> +	ret = create_dimm_temp_info(priv);
> +	if (ret && ret != -EAGAIN)
> +		dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
> +}
> +
> +static int peci_dimmtemp_probe(struct platform_device *pdev)
> +{
> +	struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
> +	struct device *dev = &pdev->dev;
> +	struct peci_dimmtemp *priv;
> +	int ret;
> +
> +	if ((mgr->client->adapter->cmd_mask &
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
> +	    (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
> +		return -ENODEV;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, priv);
> +	priv->mgr = mgr;
> +	priv->dev = dev;
> +	priv->gen_info = mgr->gen_info;
> +
> +	snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
> +		 priv->mgr->client->addr - PECI_BASE_ADDR);
> +
> +	priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
> +	if (!priv->work_queue)
> +		return -ENOMEM;
> +
> +	INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
> +
> +	ret = create_dimm_temp_info(priv);
> +	if (ret && ret != -EAGAIN) {
> +		dev_err(dev, "Failed to create DIMM temp info\n");

Does this generate error messages if there are no DIMMS ?

> +		goto err_free_wq;
> +	}
> +
> +	return 0;
> +
> +err_free_wq:
> +	destroy_workqueue(priv->work_queue);
> +	return ret;
> +}
> +
> +static int peci_dimmtemp_remove(struct platform_device *pdev)
> +{
> +	struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
> +
> +	cancel_delayed_work_sync(&priv->work_handler);
> +	destroy_workqueue(priv->work_queue);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id peci_dimmtemp_ids[] = {
> +	{ .name = "peci-dimmtemp", .driver_data = 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
> +
> +static struct platform_driver peci_dimmtemp_driver = {
> +	.probe    = peci_dimmtemp_probe,
> +	.remove   = peci_dimmtemp_remove,
> +	.id_table = peci_dimmtemp_ids,
> +	.driver   = { .name = KBUILD_MODNAME, },
> +};
> +module_platform_driver(peci_dimmtemp_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI dimmtemp driver");
> +MODULE_LICENSE("GPL v2");
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-16 16:01     ` Lee Jones
  -1 siblings, 0 replies; 71+ messages in thread
From: Lee Jones @ 2019-12-16 16:01 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Rob Herring, Greg Kroah-Hartman, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	James Feist, Jason M Biils, Vernon Mauery

On Wed, 11 Dec 2019, Jae Hyun Yoo wrote:

> This commit adds Intel PECI client driver.
> 
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Randy Dunlap <rdunlap@infradead.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: James Feist <james.feist@linux.intel.com>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Fixed minor style issues.
> 
>  drivers/mfd/Kconfig                   |  17 +++
>  drivers/mfd/Makefile                  |   1 +
>  drivers/mfd/intel-peci-client.c       | 149 ++++++++++++++++++++++++++
>  include/linux/mfd/intel-peci-client.h | 117 ++++++++++++++++++++
>  4 files changed, 284 insertions(+)
>  create mode 100644 drivers/mfd/intel-peci-client.c
>  create mode 100644 include/linux/mfd/intel-peci-client.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 420900852166..7022e54a4703 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -632,6 +632,23 @@ config MFD_INTEL_MSIC
>  	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
>  	  devices used in Intel Medfield platforms.
>  
> +config MFD_INTEL_PECI_CLIENT
> +	tristate "Intel PECI client"
> +	depends on (PECI || COMPILE_TEST)
> +	select MFD_CORE
> +	help
> +	  If you say yes to this option, support will be included for the
> +	  Intel PECI (Platform Environment Control Interface) client. PECI is a
> +	  one-wire bus interface that provides a communication channel from PECI
> +	  clients in Intel processors and chipset components to external
> +	  monitoring or control devices.
> +
> +	  Additional drivers must be enabled in order to use the functionality
> +	  of the device.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called intel-peci-client.
> +
>  config MFD_IPAQ_MICRO
>  	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
>  	depends on SA1100_H3100 || SA1100_H3600
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index aed99f08739f..91c6fda5cec6 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,6 +211,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
>  obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
>  obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
>  obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
> +obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
>  obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
>  obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
>  obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
> diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
> new file mode 100644
> index 000000000000..18bf0af0e09e
> --- /dev/null
> +++ b/drivers/mfd/intel-peci-client.c
> @@ -0,0 +1,149 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018-2019 Intel Corporation
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/peci.h>
> +
> +#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
> +#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
> +#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
> +
> +#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
> +#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
> +#define LOWER_BYTE_MASK        GENMASK(7, 0)
> +#define UPPER_BYTE_MASK        GENMASK(16, 8)
> +
> +static struct mfd_cell peci_functions[] = {
> +	{ .name = "peci-cputemp", },
> +	{ .name = "peci-dimmtemp", },
> +	/* TODO: Add additional PECI sideband functions into here */

No need for this comment.  It's implied.

> +};
> +
> +static const struct cpu_gen_info cpu_gen_info_table[] = {
> +	{ /* Haswell Xeon */
> +		.family        = 6, /* Family code */

Nit: Why don't you just define the number, instead of feeling the need
to further clarify by providing a comment?

> +		.model         = INTEL_FAM6_HASWELL_X,
> +		.core_max      = CORE_MAX_ON_HSX,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
> +	{ /* Broadwell Xeon */
> +		.family        = 6, /* Family code */
> +		.model         = INTEL_FAM6_BROADWELL_X,
> +		.core_max      = CORE_MAX_ON_BDX,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
> +	{ /* Skylake Xeon */
> +		.family        = 6, /* Family code */
> +		.model         = INTEL_FAM6_SKYLAKE_X,
> +		.core_max      = CORE_MAX_ON_SKX,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
> +	{ /* Skylake Xeon D */
> +		.family        = 6, /* Family code */
> +		.model         = INTEL_FAM6_SKYLAKE_XD,
> +		.core_max      = CORE_MAX_ON_SKXD,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKXD },
> +};
> +
> +static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
> +{
> +	struct device *dev = &priv->client->dev;
> +	u32 cpu_id;
> +	u16 family;
> +	u8 model;
> +	int ret;
> +	int i;
> +
> +	ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
> +			      &cpu_id);
> +	if (ret)
> +		return ret;
> +
> +	family = FIELD_PREP(LOWER_BYTE_MASK,
> +			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
> +		 FIELD_PREP(UPPER_BYTE_MASK,
> +			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
> +	model = FIELD_PREP(LOWER_NIBBLE_MASK,
> +			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
> +		FIELD_PREP(UPPER_NIBBLE_MASK,
> +			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
> +
> +	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
> +		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
> +
> +		if (family == cpu_info->family && model == cpu_info->model) {
> +			priv->gen_info = cpu_info;
> +			break;
> +		}
> +	}
> +
> +	if (!priv->gen_info) {
> +		dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
> +		ret = -ENODEV;
> +	}
> +
> +	return ret;
> +}
> +
> +static int peci_client_probe(struct peci_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct peci_client_manager *priv;
> +	uint cpu_no;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, priv);
> +	priv->client = client;
> +	cpu_no = client->addr - PECI_BASE_ADDR;
> +
> +	ret = peci_client_get_cpu_gen_info(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
> +				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to register child devices: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +#if IS_ENABLED(CONFIG_OF)

#ifdef CONFIG_OF

> +static const struct of_device_id peci_client_of_table[] = {
> +	{ .compatible = "intel,peci-client" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, peci_client_of_table);
> +#endif /* CONFIG_OF */

Please remove this comment.  It doesn't provide anything here.

> +static const struct peci_device_id peci_client_ids[] = {
> +	{ .name = "peci-client" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(peci, peci_client_ids);

Is this a requirement?  If so, why?

We're trying to get rid of unnecessary tables.

Please grep for "probe_new".

> +static struct peci_driver peci_client_driver = {
> +	.probe    = peci_client_probe,
> +	.id_table = peci_client_ids,
> +	.driver   = {
> +		.name           = KBUILD_MODNAME,
> +		.of_match_table = of_match_ptr(peci_client_of_table),
> +	},
> +};
> +module_peci_driver(peci_client_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI client driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
> new file mode 100644
> index 000000000000..9854303bbc26
> --- /dev/null
> +++ b/include/linux/mfd/intel-peci-client.h
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2018-2019 Intel Corporation */
> +
> +#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
> +#define __LINUX_MFD_INTEL_PECI_CLIENT_H
> +
> +#include <linux/peci.h>
> +
> +#if IS_ENABLED(CONFIG_X86)
> +#include <asm/intel-family.h>
> +#else
> +/*
> + * Architectures other than x86 cannot include the header file so define these
> + * at here. These are needed for detecting type of client x86 CPUs behind a PECI
> + * connection.
> + */
> +#define INTEL_FAM6_HASWELL_X		0x3F
> +#define INTEL_FAM6_BROADWELL_X		0x4F
> +#define INTEL_FAM6_SKYLAKE_X		0x55
> +#define INTEL_FAM6_SKYLAKE_XD		0x56
> +#endif
> +
> +#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
> +#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
> +#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
> +
> +#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
> +#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
> +#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
> +
> +#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
> +#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
> +#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
> +
> +#define CORE_MAX_ON_SKXD       16 /* Max number of cores on Skylake D */
> +#define CHAN_RANK_MAX_ON_SKXD  2  /* Max number of channel ranks on Skylake D */
> +#define DIMM_IDX_MAX_ON_SKXD   2  /* Max DIMM index per channel on Skylake D */
> +
> +#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
> +#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
> +#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
> +#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
> +
> +/**
> + * struct cpu_gen_info - CPU generation specific information
> + * @family: CPU family ID
> + * @model: CPU model
> + * @core_max: max number of cores
> + * @chan_rank_max: max number of channel ranks
> + * @dimm_idx_max: max number of DIMM indices
> + *
> + * CPU generation specific information to identify maximum number of cores and
> + * DIMM slots.
> + */
> +struct cpu_gen_info {
> +	u16  family;
> +	u8   model;
> +	uint core_max;
> +	uint chan_rank_max;
> +	uint dimm_idx_max;
> +};
> +
> +/**
> + * struct peci_client_manager - PECI client manager information
> + * @client; pointer to the PECI client
> + * @name: PECI client manager name
> + * @gen_info: CPU generation info of the detected CPU
> + *
> + * PECI client manager information for managing PECI sideband functions on a CPU
> + * client.
> + */
> +struct peci_client_manager {
> +	struct peci_client *client;
> +	char name[PECI_NAME_SIZE];
> +	const struct cpu_gen_info *gen_info;
> +};
> +
> +/**
> + * peci_client_read_package_config - read from the Package Configuration Space
> + * @priv: driver private data structure
> + * @index: encoding index for the requested service
> + * @param: parameter to specify the exact data being requested
> + * @data: data buffer to store the result
> + * Context: can sleep
> + *
> + * A generic PECI command that provides read access to the
> + * "Package Configuration Space" that is maintained by the PCU, including
> + * various power and thermal management functions. Typical PCS read services
> + * supported by the processor may include access to temperature data, energy
> + * status, run time information, DIMM temperatures and so on.
> + *
> + * Return: zero on success, else a negative error code.
> + */
> +static inline int
> +peci_client_read_package_config(struct peci_client_manager *priv,
> +				u8 index, u16 param, u8 *data)
> +{
> +	struct peci_rd_pkg_cfg_msg msg;
> +	int ret;
> +
> +	msg.addr = priv->client->addr;
> +	msg.index = index;
> +	msg.param = param;
> +	msg.rx_len = 4;
> +
> +	ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
> +	if (msg.cc != PECI_DEV_CC_SUCCESS)
> +		ret = -EAGAIN;
> +	if (ret)
> +		return ret;
> +
> +	memcpy(data, msg.pkg_config, 4);
> +
> +	return 0;
> +}

Where is this function used?

> +#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver
@ 2019-12-16 16:01     ` Lee Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Lee Jones @ 2019-12-16 16:01 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Vernon Mauery, Frederic Barrat, Mauro Carvalho Chehab,
	Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
	openbmc, linux-doc, Kishon Vijay Abraham I, Joel Stanley,
	Bryant G . Ly, Uwe Kleine-Konig, David Kershner, Guenter Roeck,
	Wu Hao, linux-hwmon, Jean Delvare, Arnd Bergmann,
	Philippe Ombredanne, Johan Hovold, Tomohiro Kusumi, Rob Herring,
	Thomas Gleixner, Sagar Dharia, linux-arm-kernel, Juergen Gross,
	Cyrille Pitchen, Andrew Jeffery, Greg Kroah-Hartman,
	Darrick J . Wong, Stephen Boyd, Vinod Koul, James Feist,
	Gustavo Pimentel, Andrew Morton, David S . Miller

On Wed, 11 Dec 2019, Jae Hyun Yoo wrote:

> This commit adds Intel PECI client driver.
> 
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Randy Dunlap <rdunlap@infradead.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: James Feist <james.feist@linux.intel.com>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Fixed minor style issues.
> 
>  drivers/mfd/Kconfig                   |  17 +++
>  drivers/mfd/Makefile                  |   1 +
>  drivers/mfd/intel-peci-client.c       | 149 ++++++++++++++++++++++++++
>  include/linux/mfd/intel-peci-client.h | 117 ++++++++++++++++++++
>  4 files changed, 284 insertions(+)
>  create mode 100644 drivers/mfd/intel-peci-client.c
>  create mode 100644 include/linux/mfd/intel-peci-client.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 420900852166..7022e54a4703 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -632,6 +632,23 @@ config MFD_INTEL_MSIC
>  	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
>  	  devices used in Intel Medfield platforms.
>  
> +config MFD_INTEL_PECI_CLIENT
> +	tristate "Intel PECI client"
> +	depends on (PECI || COMPILE_TEST)
> +	select MFD_CORE
> +	help
> +	  If you say yes to this option, support will be included for the
> +	  Intel PECI (Platform Environment Control Interface) client. PECI is a
> +	  one-wire bus interface that provides a communication channel from PECI
> +	  clients in Intel processors and chipset components to external
> +	  monitoring or control devices.
> +
> +	  Additional drivers must be enabled in order to use the functionality
> +	  of the device.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called intel-peci-client.
> +
>  config MFD_IPAQ_MICRO
>  	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
>  	depends on SA1100_H3100 || SA1100_H3600
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index aed99f08739f..91c6fda5cec6 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,6 +211,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
>  obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
>  obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
>  obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
> +obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
>  obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
>  obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
>  obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
> diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
> new file mode 100644
> index 000000000000..18bf0af0e09e
> --- /dev/null
> +++ b/drivers/mfd/intel-peci-client.c
> @@ -0,0 +1,149 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018-2019 Intel Corporation
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/peci.h>
> +
> +#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
> +#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
> +#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
> +
> +#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
> +#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
> +#define LOWER_BYTE_MASK        GENMASK(7, 0)
> +#define UPPER_BYTE_MASK        GENMASK(16, 8)
> +
> +static struct mfd_cell peci_functions[] = {
> +	{ .name = "peci-cputemp", },
> +	{ .name = "peci-dimmtemp", },
> +	/* TODO: Add additional PECI sideband functions into here */

No need for this comment.  It's implied.

> +};
> +
> +static const struct cpu_gen_info cpu_gen_info_table[] = {
> +	{ /* Haswell Xeon */
> +		.family        = 6, /* Family code */

Nit: Why don't you just define the number, instead of feeling the need
to further clarify by providing a comment?

> +		.model         = INTEL_FAM6_HASWELL_X,
> +		.core_max      = CORE_MAX_ON_HSX,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
> +	{ /* Broadwell Xeon */
> +		.family        = 6, /* Family code */
> +		.model         = INTEL_FAM6_BROADWELL_X,
> +		.core_max      = CORE_MAX_ON_BDX,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
> +	{ /* Skylake Xeon */
> +		.family        = 6, /* Family code */
> +		.model         = INTEL_FAM6_SKYLAKE_X,
> +		.core_max      = CORE_MAX_ON_SKX,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
> +	{ /* Skylake Xeon D */
> +		.family        = 6, /* Family code */
> +		.model         = INTEL_FAM6_SKYLAKE_XD,
> +		.core_max      = CORE_MAX_ON_SKXD,
> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKXD },
> +};
> +
> +static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
> +{
> +	struct device *dev = &priv->client->dev;
> +	u32 cpu_id;
> +	u16 family;
> +	u8 model;
> +	int ret;
> +	int i;
> +
> +	ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
> +			      &cpu_id);
> +	if (ret)
> +		return ret;
> +
> +	family = FIELD_PREP(LOWER_BYTE_MASK,
> +			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
> +		 FIELD_PREP(UPPER_BYTE_MASK,
> +			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
> +	model = FIELD_PREP(LOWER_NIBBLE_MASK,
> +			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
> +		FIELD_PREP(UPPER_NIBBLE_MASK,
> +			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
> +
> +	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
> +		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
> +
> +		if (family == cpu_info->family && model == cpu_info->model) {
> +			priv->gen_info = cpu_info;
> +			break;
> +		}
> +	}
> +
> +	if (!priv->gen_info) {
> +		dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
> +		ret = -ENODEV;
> +	}
> +
> +	return ret;
> +}
> +
> +static int peci_client_probe(struct peci_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct peci_client_manager *priv;
> +	uint cpu_no;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, priv);
> +	priv->client = client;
> +	cpu_no = client->addr - PECI_BASE_ADDR;
> +
> +	ret = peci_client_get_cpu_gen_info(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
> +				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to register child devices: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +#if IS_ENABLED(CONFIG_OF)

#ifdef CONFIG_OF

> +static const struct of_device_id peci_client_of_table[] = {
> +	{ .compatible = "intel,peci-client" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, peci_client_of_table);
> +#endif /* CONFIG_OF */

Please remove this comment.  It doesn't provide anything here.

> +static const struct peci_device_id peci_client_ids[] = {
> +	{ .name = "peci-client" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(peci, peci_client_ids);

Is this a requirement?  If so, why?

We're trying to get rid of unnecessary tables.

Please grep for "probe_new".

> +static struct peci_driver peci_client_driver = {
> +	.probe    = peci_client_probe,
> +	.id_table = peci_client_ids,
> +	.driver   = {
> +		.name           = KBUILD_MODNAME,
> +		.of_match_table = of_match_ptr(peci_client_of_table),
> +	},
> +};
> +module_peci_driver(peci_client_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI client driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
> new file mode 100644
> index 000000000000..9854303bbc26
> --- /dev/null
> +++ b/include/linux/mfd/intel-peci-client.h
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2018-2019 Intel Corporation */
> +
> +#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
> +#define __LINUX_MFD_INTEL_PECI_CLIENT_H
> +
> +#include <linux/peci.h>
> +
> +#if IS_ENABLED(CONFIG_X86)
> +#include <asm/intel-family.h>
> +#else
> +/*
> + * Architectures other than x86 cannot include the header file so define these
> + * at here. These are needed for detecting type of client x86 CPUs behind a PECI
> + * connection.
> + */
> +#define INTEL_FAM6_HASWELL_X		0x3F
> +#define INTEL_FAM6_BROADWELL_X		0x4F
> +#define INTEL_FAM6_SKYLAKE_X		0x55
> +#define INTEL_FAM6_SKYLAKE_XD		0x56
> +#endif
> +
> +#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
> +#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
> +#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
> +
> +#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
> +#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
> +#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
> +
> +#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
> +#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
> +#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
> +
> +#define CORE_MAX_ON_SKXD       16 /* Max number of cores on Skylake D */
> +#define CHAN_RANK_MAX_ON_SKXD  2  /* Max number of channel ranks on Skylake D */
> +#define DIMM_IDX_MAX_ON_SKXD   2  /* Max DIMM index per channel on Skylake D */
> +
> +#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
> +#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
> +#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
> +#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
> +
> +/**
> + * struct cpu_gen_info - CPU generation specific information
> + * @family: CPU family ID
> + * @model: CPU model
> + * @core_max: max number of cores
> + * @chan_rank_max: max number of channel ranks
> + * @dimm_idx_max: max number of DIMM indices
> + *
> + * CPU generation specific information to identify maximum number of cores and
> + * DIMM slots.
> + */
> +struct cpu_gen_info {
> +	u16  family;
> +	u8   model;
> +	uint core_max;
> +	uint chan_rank_max;
> +	uint dimm_idx_max;
> +};
> +
> +/**
> + * struct peci_client_manager - PECI client manager information
> + * @client; pointer to the PECI client
> + * @name: PECI client manager name
> + * @gen_info: CPU generation info of the detected CPU
> + *
> + * PECI client manager information for managing PECI sideband functions on a CPU
> + * client.
> + */
> +struct peci_client_manager {
> +	struct peci_client *client;
> +	char name[PECI_NAME_SIZE];
> +	const struct cpu_gen_info *gen_info;
> +};
> +
> +/**
> + * peci_client_read_package_config - read from the Package Configuration Space
> + * @priv: driver private data structure
> + * @index: encoding index for the requested service
> + * @param: parameter to specify the exact data being requested
> + * @data: data buffer to store the result
> + * Context: can sleep
> + *
> + * A generic PECI command that provides read access to the
> + * "Package Configuration Space" that is maintained by the PCU, including
> + * various power and thermal management functions. Typical PCS read services
> + * supported by the processor may include access to temperature data, energy
> + * status, run time information, DIMM temperatures and so on.
> + *
> + * Return: zero on success, else a negative error code.
> + */
> +static inline int
> +peci_client_read_package_config(struct peci_client_manager *priv,
> +				u8 index, u16 param, u8 *data)
> +{
> +	struct peci_rd_pkg_cfg_msg msg;
> +	int ret;
> +
> +	msg.addr = priv->client->addr;
> +	msg.index = index;
> +	msg.param = param;
> +	msg.rx_len = 4;
> +
> +	ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
> +	if (msg.cc != PECI_DEV_CC_SUCCESS)
> +		ret = -EAGAIN;
> +	if (ret)
> +		return ret;
> +
> +	memcpy(data, msg.pkg_config, 4);
> +
> +	return 0;
> +}

Where is this function used?

> +#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 13/14] hwmon: Add PECI cputemp driver
  2019-12-13  6:24     ` Guenter Roeck
@ 2019-12-16 20:43       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 20:43 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

Hi Guenter,

On 12/12/2019 10:24 PM, Guenter Roeck wrote:
> On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
>> This commit adds PECI cputemp hwmon driver.
>>
>> Cc: Guenter Roeck <linux@roeck-us.net>
>> Cc: Jean Delvare <jdelvare@suse.com>
>> Cc: Alan Cox <alan@linux.intel.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
>> Cc: Andrew Lunn <andrew@lunn.ch>
>> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
>> Reviewed-by: James Feist <james.feist@linux.intel.com>
>> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Acked-by: Guenter Roeck <linux@roeck-us.net>
>> ---
>> Changes since v10:
>> - Added Skylake Xeon D support.
>> - Added DTS temperature which is more thermal control friendlier than Die
>>    temperature.
>> - Fixed minor bugs and style issues.
>>
>>   drivers/hwmon/Kconfig        |  14 ++
>>   drivers/hwmon/Makefile       |   1 +
>>   drivers/hwmon/peci-cputemp.c | 448 +++++++++++++++++++++++++++++++++++
>>   drivers/hwmon/peci-hwmon.h   |  46 ++++
>>   4 files changed, 509 insertions(+)
>>   create mode 100644 drivers/hwmon/peci-cputemp.c
>>   create mode 100644 drivers/hwmon/peci-hwmon.h
>>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 23dfe848979a..b6604759579c 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1349,6 +1349,20 @@ config SENSORS_PCF8591
>>         These devices are hard to detect and rarely found on mainstream
>>         hardware. If unsure, say N.
>> +config SENSORS_PECI_CPUTEMP
>> +    tristate "PECI CPU temperature monitoring client"
>> +    depends on PECI
>> +    select MFD_INTEL_PECI_CLIENT
>> +    help
>> +      If you say yes here you get support for the generic Intel PECI
>> +      cputemp driver which provides Digital Thermal Sensor (DTS) thermal
>> +      readings of the CPU package and CPU cores that are accessible 
>> using
>> +      the PECI Client Command Suite via the processor PECI client.
>> +      Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
>> +
>> +      This driver can also be built as a module. If so, the module
>> +      will be called peci-cputemp.
>> +
>>   source "drivers/hwmon/pmbus/Kconfig"
>>   config SENSORS_PWM_FAN
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index 6db5db9cdc29..d6fea48697af 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)    += 
>> ntc_thermistor.o
>>   obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
>>   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
>>   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
>> +obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o
>>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>>   obj-$(CONFIG_SENSORS_PWM_FAN)    += pwm-fan.o
>>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)    += raspberrypi-hwmon.o
>> diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
>> new file mode 100644
>> index 000000000000..70ced9f9299f
>> --- /dev/null
>> +++ b/drivers/hwmon/peci-cputemp.c
>> @@ -0,0 +1,448 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2018-2019 Intel Corporation
>> +
>> +#include <linux/hwmon.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/mfd/intel-peci-client.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include "peci-hwmon.h"
>> +
>> +#define DEFAULT_CHANNEL_NUMS   5
>> +#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX
> 
> Why not just use CORE_NUMS_MAX ?

For better readability in below code to represent max core numbers
as core temp channel numbers. If you don't like it, I'll use
CORE_NUMS_MAX as it is for the core temp channel numbers.

>> +#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + 
>> CORETEMP_CHANNEL_NUMS)
>> +
>> +struct temp_group {
>> +    struct peci_sensor_data die;
>> +    struct peci_sensor_data dts;
>> +    struct peci_sensor_data tcontrol;
>> +    struct peci_sensor_data tthrottle;
>> +    struct peci_sensor_data tjmax;
>> +    struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
>> +};
>> +
>> +struct peci_cputemp {
>> +    struct peci_client_manager *mgr;
>> +    struct device *dev;
>> +    char name[PECI_NAME_SIZE];
>> +    const struct cpu_gen_info *gen_info;
>> +    struct temp_group temp;
>> +    u64 core_mask;
>> +    u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
>> +    uint config_idx;
>> +    struct hwmon_channel_info temp_info;
>> +    const struct hwmon_channel_info *info[2];
>> +    struct hwmon_chip_info chip;
>> +};
>> +
>> +enum cputemp_channels {
>> +    channel_die,
>> +    channel_dts,
>> +    channel_tcontrol,
>> +    channel_tthrottle,
>> +    channel_tjmax,
>> +    channel_core,
>> +};
>> +
>> +static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
>> +    /* Die temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>> +    HWMON_T_CRIT_HYST,
>> +
>> +    /* DTS margin */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>> +    HWMON_T_CRIT_HYST,
>> +
>> +    /* Tcontrol temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
>> +
>> +    /* Tthrottle temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT,
>> +
>> +    /* Tjmax temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT,
>> +
>> +    /* Core temperature - for all core channels */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>> +    HWMON_T_CRIT_HYST,
>> +};
>> +
>> +static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
>> +    "Die",
>> +    "DTS",
>> +    "Tcontrol",
>> +    "Tthrottle",
>> +    "Tjmax",
>> +    "Core 0", "Core 1", "Core 2", "Core 3",
>> +    "Core 4", "Core 5", "Core 6", "Core 7",
>> +    "Core 8", "Core 9", "Core 10", "Core 11",
>> +    "Core 12", "Core 13", "Core 14", "Core 15",
>> +    "Core 16", "Core 17", "Core 18", "Core 19",
>> +    "Core 20", "Core 21", "Core 22", "Core 23",
>> +    "Core 24", "Core 25", "Core 26", "Core 27",
>> +};
>> +
> 
> What happens if CPUTEMP_CHANNEL_NUMS is increased in the future,
> and some of this array includes NULL pointers because this file
> doesn't track CORE_NUMS_MAX ?

Okay. It's reasonable concern. I'll fix it to make this driver generate
the core temp label strings dynamically based on detected CPU generation
information.

>> +static s32 ten_dot_six_to_millidegree(s32 val)
>> +{
>> +    return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
> 
> Normally I suggest to use DIV_ROUND_CLOSEST in situations like this
> to improve rounding, but ultimately that is your call.

This is for conversion of 16-bit signed 10.6
(10 bits signed decimal, 6 bits fractional) format to 32-bits signed
integer in millidegree units. It's right as it is.

>> +}
>> +
>> +static int get_temp_targets(struct peci_cputemp *priv)
>> +{
>> +    s32 tthrottle_offset;
>> +    s32 tcontrol_margin;
>> +    u8  pkg_cfg[4];
>> +    int ret;
>> +
>> +    /*
>> +     * Just use only the tcontrol marker to determine if target 
>> values need
>> +     * update.
>> +     */
>> +    if (!peci_sensor_need_update(&priv->temp.tcontrol))
>> +        return 0;
>> +
>> +    ret = peci_client_read_package_config(priv->mgr,
>> +                          PECI_MBX_INDEX_TEMP_TARGET, 0,
>> +                          pkg_cfg);
>> +    if (ret)
>> +        return ret;
>> +
>> +    priv->temp.tjmax.value = pkg_cfg[2] * 1000;
>> +
>> +    tcontrol_margin = pkg_cfg[1];
>> +    tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
>> +    priv->temp.tcontrol.value = priv->temp.tjmax.value - 
>> tcontrol_margin;
>> +
>> +    tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
>> +    priv->temp.tthrottle.value = priv->temp.tjmax.value - 
>> tthrottle_offset;
>> +
>> +    peci_sensor_mark_updated(&priv->temp.tcontrol);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_die_temp(struct peci_cputemp *priv)
>> +{
>> +    struct peci_get_temp_msg msg;
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp.die))
>> +        return 0;
>> +
>> +    msg.addr = priv->mgr->client->addr;
>> +
>> +    ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, 
>> &msg);
>> +    if (ret)
>> +        return ret;
>> +
>> +    /* Note that the tjmax should be available before calling it */
>> +    priv->temp.die.value = priv->temp.tjmax.value +
>> +                   (msg.temp_raw * 1000 / 64);
>> +
>> +    peci_sensor_mark_updated(&priv->temp.die);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_dts(struct peci_cputemp *priv)
>> +{
>> +    s32 dts_margin;
>> +    u8  pkg_cfg[4];
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp.dts))
>> +        return 0;
>> +
>> +    ret = peci_client_read_package_config(priv->mgr,
>> +                          PECI_MBX_INDEX_DTS_MARGIN, 0,
>> +                          pkg_cfg);
>> +
>> +    if (ret)
>> +        return ret;
>> +
>> +    dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
>> +
>> +    /**
>> +     * Processors return a value of DTS reading in 10.6 format
>> +     * (10 bits signed decimal, 6 bits fractional).
>> +     * Error codes:
>> +     *   0x8000: General sensor error
>> +     *   0x8001: Reserved
>> +     *   0x8002: Underflow on reading value
>> +     *   0x8003-0x81ff: Reserved
>> +     */
>> +    if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
>> +        return -EIO;
>> +
>> +    dts_margin = ten_dot_six_to_millidegree(dts_margin);
>> +
>> +    /* Note that the tcontrol should be available before calling it */
>> +    priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
>> +
>> +    peci_sensor_mark_updated(&priv->temp.dts);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_core_temp(struct peci_cputemp *priv, int core_index)
>> +{
>> +    s32 core_dts_margin;
>> +    u8  pkg_cfg[4];
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp.core[core_index]))
>> +        return 0;
>> +
>> +    ret = peci_client_read_package_config(priv->mgr,
>> +                          PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
>> +                          core_index, pkg_cfg);
>> +    if (ret)
>> +        return ret;
>> +
>> +    core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
>> +
> 
> Any special readon for using the helper function here but not
> above ?

Ah, I have to use le16_to_cpup for the above one too. Thanks!

Thanks a lot for your review!

-Jae


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 13/14] hwmon: Add PECI cputemp driver
@ 2019-12-16 20:43       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 20:43 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-doc, openbmc, Miguel Ojeda,
	Andrew Lunn, Stef van Os, Jason M Biils, Andy Shevchenko,
	linux-arm-kernel, Alan Cox

Hi Guenter,

On 12/12/2019 10:24 PM, Guenter Roeck wrote:
> On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
>> This commit adds PECI cputemp hwmon driver.
>>
>> Cc: Guenter Roeck <linux@roeck-us.net>
>> Cc: Jean Delvare <jdelvare@suse.com>
>> Cc: Alan Cox <alan@linux.intel.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
>> Cc: Andrew Lunn <andrew@lunn.ch>
>> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
>> Reviewed-by: James Feist <james.feist@linux.intel.com>
>> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Acked-by: Guenter Roeck <linux@roeck-us.net>
>> ---
>> Changes since v10:
>> - Added Skylake Xeon D support.
>> - Added DTS temperature which is more thermal control friendlier than Die
>>    temperature.
>> - Fixed minor bugs and style issues.
>>
>>   drivers/hwmon/Kconfig        |  14 ++
>>   drivers/hwmon/Makefile       |   1 +
>>   drivers/hwmon/peci-cputemp.c | 448 +++++++++++++++++++++++++++++++++++
>>   drivers/hwmon/peci-hwmon.h   |  46 ++++
>>   4 files changed, 509 insertions(+)
>>   create mode 100644 drivers/hwmon/peci-cputemp.c
>>   create mode 100644 drivers/hwmon/peci-hwmon.h
>>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 23dfe848979a..b6604759579c 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1349,6 +1349,20 @@ config SENSORS_PCF8591
>>         These devices are hard to detect and rarely found on mainstream
>>         hardware. If unsure, say N.
>> +config SENSORS_PECI_CPUTEMP
>> +    tristate "PECI CPU temperature monitoring client"
>> +    depends on PECI
>> +    select MFD_INTEL_PECI_CLIENT
>> +    help
>> +      If you say yes here you get support for the generic Intel PECI
>> +      cputemp driver which provides Digital Thermal Sensor (DTS) thermal
>> +      readings of the CPU package and CPU cores that are accessible 
>> using
>> +      the PECI Client Command Suite via the processor PECI client.
>> +      Check <file:Documentation/hwmon/peci-cputemp.rst> for details.
>> +
>> +      This driver can also be built as a module. If so, the module
>> +      will be called peci-cputemp.
>> +
>>   source "drivers/hwmon/pmbus/Kconfig"
>>   config SENSORS_PWM_FAN
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index 6db5db9cdc29..d6fea48697af 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)    += 
>> ntc_thermistor.o
>>   obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
>>   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
>>   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
>> +obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o
>>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>>   obj-$(CONFIG_SENSORS_PWM_FAN)    += pwm-fan.o
>>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)    += raspberrypi-hwmon.o
>> diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
>> new file mode 100644
>> index 000000000000..70ced9f9299f
>> --- /dev/null
>> +++ b/drivers/hwmon/peci-cputemp.c
>> @@ -0,0 +1,448 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2018-2019 Intel Corporation
>> +
>> +#include <linux/hwmon.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/mfd/intel-peci-client.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include "peci-hwmon.h"
>> +
>> +#define DEFAULT_CHANNEL_NUMS   5
>> +#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX
> 
> Why not just use CORE_NUMS_MAX ?

For better readability in below code to represent max core numbers
as core temp channel numbers. If you don't like it, I'll use
CORE_NUMS_MAX as it is for the core temp channel numbers.

>> +#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + 
>> CORETEMP_CHANNEL_NUMS)
>> +
>> +struct temp_group {
>> +    struct peci_sensor_data die;
>> +    struct peci_sensor_data dts;
>> +    struct peci_sensor_data tcontrol;
>> +    struct peci_sensor_data tthrottle;
>> +    struct peci_sensor_data tjmax;
>> +    struct peci_sensor_data core[CORETEMP_CHANNEL_NUMS];
>> +};
>> +
>> +struct peci_cputemp {
>> +    struct peci_client_manager *mgr;
>> +    struct device *dev;
>> +    char name[PECI_NAME_SIZE];
>> +    const struct cpu_gen_info *gen_info;
>> +    struct temp_group temp;
>> +    u64 core_mask;
>> +    u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
>> +    uint config_idx;
>> +    struct hwmon_channel_info temp_info;
>> +    const struct hwmon_channel_info *info[2];
>> +    struct hwmon_chip_info chip;
>> +};
>> +
>> +enum cputemp_channels {
>> +    channel_die,
>> +    channel_dts,
>> +    channel_tcontrol,
>> +    channel_tthrottle,
>> +    channel_tjmax,
>> +    channel_core,
>> +};
>> +
>> +static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
>> +    /* Die temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>> +    HWMON_T_CRIT_HYST,
>> +
>> +    /* DTS margin */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>> +    HWMON_T_CRIT_HYST,
>> +
>> +    /* Tcontrol temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
>> +
>> +    /* Tthrottle temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT,
>> +
>> +    /* Tjmax temperature */
>> +    HWMON_T_LABEL | HWMON_T_INPUT,
>> +
>> +    /* Core temperature - for all core channels */
>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>> +    HWMON_T_CRIT_HYST,
>> +};
>> +
>> +static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
>> +    "Die",
>> +    "DTS",
>> +    "Tcontrol",
>> +    "Tthrottle",
>> +    "Tjmax",
>> +    "Core 0", "Core 1", "Core 2", "Core 3",
>> +    "Core 4", "Core 5", "Core 6", "Core 7",
>> +    "Core 8", "Core 9", "Core 10", "Core 11",
>> +    "Core 12", "Core 13", "Core 14", "Core 15",
>> +    "Core 16", "Core 17", "Core 18", "Core 19",
>> +    "Core 20", "Core 21", "Core 22", "Core 23",
>> +    "Core 24", "Core 25", "Core 26", "Core 27",
>> +};
>> +
> 
> What happens if CPUTEMP_CHANNEL_NUMS is increased in the future,
> and some of this array includes NULL pointers because this file
> doesn't track CORE_NUMS_MAX ?

Okay. It's reasonable concern. I'll fix it to make this driver generate
the core temp label strings dynamically based on detected CPU generation
information.

>> +static s32 ten_dot_six_to_millidegree(s32 val)
>> +{
>> +    return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
> 
> Normally I suggest to use DIV_ROUND_CLOSEST in situations like this
> to improve rounding, but ultimately that is your call.

This is for conversion of 16-bit signed 10.6
(10 bits signed decimal, 6 bits fractional) format to 32-bits signed
integer in millidegree units. It's right as it is.

>> +}
>> +
>> +static int get_temp_targets(struct peci_cputemp *priv)
>> +{
>> +    s32 tthrottle_offset;
>> +    s32 tcontrol_margin;
>> +    u8  pkg_cfg[4];
>> +    int ret;
>> +
>> +    /*
>> +     * Just use only the tcontrol marker to determine if target 
>> values need
>> +     * update.
>> +     */
>> +    if (!peci_sensor_need_update(&priv->temp.tcontrol))
>> +        return 0;
>> +
>> +    ret = peci_client_read_package_config(priv->mgr,
>> +                          PECI_MBX_INDEX_TEMP_TARGET, 0,
>> +                          pkg_cfg);
>> +    if (ret)
>> +        return ret;
>> +
>> +    priv->temp.tjmax.value = pkg_cfg[2] * 1000;
>> +
>> +    tcontrol_margin = pkg_cfg[1];
>> +    tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
>> +    priv->temp.tcontrol.value = priv->temp.tjmax.value - 
>> tcontrol_margin;
>> +
>> +    tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
>> +    priv->temp.tthrottle.value = priv->temp.tjmax.value - 
>> tthrottle_offset;
>> +
>> +    peci_sensor_mark_updated(&priv->temp.tcontrol);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_die_temp(struct peci_cputemp *priv)
>> +{
>> +    struct peci_get_temp_msg msg;
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp.die))
>> +        return 0;
>> +
>> +    msg.addr = priv->mgr->client->addr;
>> +
>> +    ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, 
>> &msg);
>> +    if (ret)
>> +        return ret;
>> +
>> +    /* Note that the tjmax should be available before calling it */
>> +    priv->temp.die.value = priv->temp.tjmax.value +
>> +                   (msg.temp_raw * 1000 / 64);
>> +
>> +    peci_sensor_mark_updated(&priv->temp.die);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_dts(struct peci_cputemp *priv)
>> +{
>> +    s32 dts_margin;
>> +    u8  pkg_cfg[4];
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp.dts))
>> +        return 0;
>> +
>> +    ret = peci_client_read_package_config(priv->mgr,
>> +                          PECI_MBX_INDEX_DTS_MARGIN, 0,
>> +                          pkg_cfg);
>> +
>> +    if (ret)
>> +        return ret;
>> +
>> +    dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[0];
>> +
>> +    /**
>> +     * Processors return a value of DTS reading in 10.6 format
>> +     * (10 bits signed decimal, 6 bits fractional).
>> +     * Error codes:
>> +     *   0x8000: General sensor error
>> +     *   0x8001: Reserved
>> +     *   0x8002: Underflow on reading value
>> +     *   0x8003-0x81ff: Reserved
>> +     */
>> +    if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
>> +        return -EIO;
>> +
>> +    dts_margin = ten_dot_six_to_millidegree(dts_margin);
>> +
>> +    /* Note that the tcontrol should be available before calling it */
>> +    priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin;
>> +
>> +    peci_sensor_mark_updated(&priv->temp.dts);
>> +
>> +    return 0;
>> +}
>> +
>> +static int get_core_temp(struct peci_cputemp *priv, int core_index)
>> +{
>> +    s32 core_dts_margin;
>> +    u8  pkg_cfg[4];
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp.core[core_index]))
>> +        return 0;
>> +
>> +    ret = peci_client_read_package_config(priv->mgr,
>> +                          PECI_MBX_INDEX_PER_CORE_DTS_TEMP,
>> +                          core_index, pkg_cfg);
>> +    if (ret)
>> +        return ret;
>> +
>> +    core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
>> +
> 
> Any special readon for using the helper function here but not
> above ?

Ah, I have to use le16_to_cpup for the above one too. Thanks!

Thanks a lot for your review!

-Jae


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-13  6:32     ` Guenter Roeck
@ 2019-12-16 21:04       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 21:04 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

Hi Guenter,

On 12/12/2019 10:32 PM, Guenter Roeck wrote:
> On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
>> This commit adds PECI dimmtemp hwmon driver.
>>
>> Cc: Guenter Roeck <linux@roeck-us.net>
>> Cc: Jean Delvare <jdelvare@suse.com>
>> Cc: Alan Cox <alan@linux.intel.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
>> Cc: Andrew Lunn <andrew@lunn.ch>
>> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
>> Reviewed-by: James Feist <james.feist@linux.intel.com>
>> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Acked-by: Guenter Roeck <linux@roeck-us.net>
>> ---
>> Changes since v10:
>> - Added Skylake Xeon D support.
>> - Added max and crit properties for temperature threshold checking.
>> - Fixed minor bugs and style issues.
>>
>>   drivers/hwmon/Kconfig         |  14 ++
>>   drivers/hwmon/Makefile        |   1 +
>>   drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
>>   3 files changed, 408 insertions(+)
>>   create mode 100644 drivers/hwmon/peci-dimmtemp.c
>>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index b6604759579c..d3370fbab40c 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
>>         This driver can also be built as a module. If so, the module
>>         will be called peci-cputemp.
>> +config SENSORS_PECI_DIMMTEMP
>> +    tristate "PECI DIMM temperature monitoring client"
>> +    depends on PECI
>> +    select MFD_INTEL_PECI_CLIENT
>> +    help
>> +      If you say yes here you get support for the generic Intel PECI 
>> hwmon
>> +      driver which provides Digital Thermal Sensor (DTS) thermal 
>> readings of
>> +      DIMM components that are accessible using the PECI Client Command
>> +      Suite via the processor PECI client.
>> +      Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
>> +
>> +      This driver can also be built as a module. If so, the module
>> +      will be called peci-dimmtemp.
>> +
>>   source "drivers/hwmon/pmbus/Kconfig"
>>   config SENSORS_PWM_FAN
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index d6fea48697af..4015c4b60bf4 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
>>   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
>>   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
>>   obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o
>> +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)    += peci-dimmtemp.o
>>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>>   obj-$(CONFIG_SENSORS_PWM_FAN)    += pwm-fan.o
>>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)    += raspberrypi-hwmon.o
>> diff --git a/drivers/hwmon/peci-dimmtemp.c 
>> b/drivers/hwmon/peci-dimmtemp.c
>> new file mode 100644
>> index 000000000000..974f453f9366
>> --- /dev/null
>> +++ b/drivers/hwmon/peci-dimmtemp.c
>> @@ -0,0 +1,393 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2018-2019 Intel Corporation
>> +
>> +#include <linux/hwmon.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/mfd/intel-peci-client.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/workqueue.h>
>> +#include "peci-hwmon.h"
>> +
>> +#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
>> +#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
>> +
>> +struct peci_dimmtemp {
>> +    struct peci_client_manager *mgr;
>> +    struct device *dev;
>> +    char name[PECI_NAME_SIZE];
>> +    const struct cpu_gen_info *gen_info;
>> +    struct workqueue_struct *work_queue;
>> +    struct delayed_work work_handler;
>> +    struct peci_sensor_data temp[DIMM_NUMS_MAX];
>> +    long temp_max[DIMM_NUMS_MAX];
>> +    long temp_crit[DIMM_NUMS_MAX];
>> +    u32 dimm_mask;
>> +    int retry_count;
>> +    u32 temp_config[DIMM_NUMS_MAX + 1];
>> +    struct hwmon_channel_info temp_info;
>> +    const struct hwmon_channel_info *info[2];
>> +    struct hwmon_chip_info chip;
>> +};
>> +
>> +static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
>> +    { "DIMM A1", "DIMM A2", "DIMM A3" },
>> +    { "DIMM B1", "DIMM B2", "DIMM B3" },
>> +    { "DIMM C1", "DIMM C2", "DIMM C3" },
>> +    { "DIMM D1", "DIMM D2", "DIMM D3" },
>> +    { "DIMM E1", "DIMM E2", "DIMM E3" },
>> +    { "DIMM F1", "DIMM F2", "DIMM F3" },
>> +    { "DIMM G1", "DIMM G2", "DIMM G3" },
>> +    { "DIMM H1", "DIMM H2", "DIMM H3" },
>> +};
>> +
>> +static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
>> +                        int chan_rank,
>> +                        u8 *cfg_data)
>> +{
>> +    return peci_client_read_package_config(priv->mgr,
>> +                           PECI_MBX_INDEX_DDR_DIMM_TEMP,
>> +                           chan_rank, cfg_data);
>> +}
>> +
>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
>> +{
>> +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
>> +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
>> +    struct peci_rd_pci_cfg_local_msg rp_msg;
>> +    u8  cfg_data[4];
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
>> +        return 0;
>> +
>> +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>> +    if (ret)
>> +        return ret;
>> +
>> +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
>> +
>> +    switch (priv->gen_info->model) {
>> +    case INTEL_FAM6_SKYLAKE_X:
>> +        rp_msg.addr = priv->mgr->client->addr;
>> +        rp_msg.bus = 2;
>> +        /*
>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>> +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
>> +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
>> +         */
>> +        rp_msg.device = 10 + chan_rank / 3 * 2 +
>> +                 (chan_rank % 3 == 2 ? 1 : 0);
>> +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>> +        rp_msg.rx_len = 4;
>> +
>> +        ret = peci_command(priv->mgr->client->adapter,
>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>> +            ret = -EAGAIN;
>> +        if (ret)
>> +            return ret;
>> +
>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>> +        break;
>> +    case INTEL_FAM6_SKYLAKE_XD:
>> +        rp_msg.addr = priv->mgr->client->addr;
>> +        rp_msg.bus = 2;
>> +        /*
>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
>> +         */
>> +        rp_msg.device = 10 + chan_rank / 2 * 2;
>> +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>> +        rp_msg.rx_len = 4;
>> +
>> +        ret = peci_command(priv->mgr->client->adapter,
>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>> +            ret = -EAGAIN;
>> +        if (ret)
>> +            return ret;
>> +
>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>> +        break;
>> +    case INTEL_FAM6_HASWELL_X:
>> +    case INTEL_FAM6_BROADWELL_X:
>> +        rp_msg.addr = priv->mgr->client->addr;
>> +        rp_msg.bus = 1;
>> +        /*
>> +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
>> +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
>> +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
>> +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
>> +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
>> +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
>> +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
>> +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
>> +         */
>> +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
>> +        rp_msg.function = chan_rank % 2;
>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>> +        rp_msg.rx_len = 4;
>> +
>> +        ret = peci_command(priv->mgr->client->adapter,
>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>> +            ret = -EAGAIN;
>> +        if (ret)
>> +            return ret;
>> +
>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>> +        break;
>> +    default:
>> +        return -EOPNOTSUPP;
> 
> It looks like the sensors are created even on unsupported platforms,
> which would generate error messages whenever someone tries to read
> the attributes.
> 
> There should be some code early on checking this, and the driver
> should not even instantiate if the CPU model is not supported.

Actually, this 'default' case will not be happened because this driver
will be registered only when the CPU model is supported. The CPU model
checking code is in 'intel-peci-client.c' which is [11/14] of this
patch set.

>> +    }
>> +
>> +    peci_sensor_mark_updated(&priv->temp[dimm_no]);
>> +
>> +    return 0;
>> +}
>> +
>> +static int dimmtemp_read_string(struct device *dev,
>> +                enum hwmon_sensor_types type,
>> +                u32 attr, int channel, const char **str)
>> +{
>> +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
>> +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
>> +    int chan_rank, dimm_idx;
>> +
>> +    if (attr != hwmon_temp_label)
>> +        return -EOPNOTSUPP;
>> +
>> +    chan_rank = channel / dimm_idx_max;
>> +    dimm_idx = channel % dimm_idx_max;
>> +    *str = dimmtemp_label[chan_rank][dimm_idx];
> 
> Similar to the other patch, I am concerned that this can end up setting 
> *str
> to NULL at some point in the future.

Okay. I'll make dynamic label string table generation code for it as
well.

>> +
>> +    return 0;
>> +}
>> +
>> +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types 
>> type,
>> +             u32 attr, int channel, long *val)
>> +{
>> +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
>> +    int ret;
>> +
>> +    ret = get_dimm_temp(priv, channel);
>> +    if (ret)
>> +        return ret;
>> +
>> +    switch (attr) {
>> +    case hwmon_temp_input:
>> +        *val = priv->temp[channel].value;
>> +        break;
>> +    case hwmon_temp_max:
>> +        *val = priv->temp_max[channel];
>> +        break;
>> +    case hwmon_temp_crit:
>> +        *val = priv->temp_crit[channel];
>> +        break;
>> +    default:
>> +        ret = -EOPNOTSUPP;
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static umode_t dimmtemp_is_visible(const void *data,
>> +                   enum hwmon_sensor_types type,
>> +                   u32 attr, int channel)
>> +{
>> +    const struct peci_dimmtemp *priv = data;
>> +
>> +    if (priv->temp_config[channel] & BIT(attr) &&
>> +        priv->dimm_mask & BIT(channel))
>> +        return 0444;
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct hwmon_ops dimmtemp_ops = {
>> +    .is_visible = dimmtemp_is_visible,
>> +    .read_string = dimmtemp_read_string,
>> +    .read = dimmtemp_read,
>> +};
>> +
>> +static int check_populated_dimms(struct peci_dimmtemp *priv)
>> +{
>> +    u32 chan_rank_max = priv->gen_info->chan_rank_max;
>> +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
>> +    int chan_rank, dimm_idx;
>> +    u8  cfg_data[4];
>> +
>> +    for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
>> +        int ret;
>> +
>> +        ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>> +        if (ret) {
>> +            priv->dimm_mask = 0;
>> +            return ret;
>> +        }
>> +
>> +        for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
>> +            if (cfg_data[dimm_idx])
>> +                priv->dimm_mask |= BIT(chan_rank *
>> +                               dimm_idx_max +
>> +                               dimm_idx);
>> +    }
>> +
>> +    if (!priv->dimm_mask)
>> +        return -EAGAIN;
>> +
>> +    dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", 
>> priv->dimm_mask);
>> +
>> +    return 0;
>> +}
>> +
>> +static int create_dimm_temp_info(struct peci_dimmtemp *priv)
>> +{
>> +    int ret, i, config_idx, channels;
>> +    struct device *hwmon_dev;
>> +
>> +    ret = check_populated_dimms(priv);
>> +    if (ret) {
>> +        if (ret == -EAGAIN) {
>> +            if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
>> +                queue_delayed_work(priv->work_queue,
>> +                           &priv->work_handler,
>> +                         DIMM_MASK_CHECK_DELAY_JIFFIES);
>> +                priv->retry_count++;
>> +                dev_dbg(priv->dev,
>> +                    "Deferred DIMM temp info creation\n");
>> +            } else {
>> +                dev_err(priv->dev,
>> +                    "Timeout DIMM temp info creation\n");
>> +                ret = -ETIMEDOUT;
>> +            }
>> +        }
>> +
>> +        return ret;
>> +    }
>> +
>> +    channels = priv->gen_info->chan_rank_max *
>> +           priv->gen_info->dimm_idx_max;
>> +    for (i = 0, config_idx = 0; i < channels; i++)
>> +        if (priv->dimm_mask & BIT(i))
>> +            while (i >= config_idx)
>> +                priv->temp_config[config_idx++] =
>> +                    HWMON_T_LABEL | HWMON_T_INPUT |
>> +                    HWMON_T_MAX | HWMON_T_CRIT;
>> +
>> +    priv->chip.ops = &dimmtemp_ops;
>> +    priv->chip.info = priv->info;
>> +
>> +    priv->info[0] = &priv->temp_info;
>> +
>> +    priv->temp_info.type = hwmon_temp;
>> +    priv->temp_info.config = priv->temp_config;
>> +
>> +    hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
>> +                             priv->name,
>> +                             priv,
>> +                             &priv->chip,
>> +                             NULL);
>> +    ret = PTR_ERR_OR_ZERO(hwmon_dev);
>> +    if (!ret)
>> +        dev_dbg(priv->dev, "%s: sensor '%s'\n",
>> +            dev_name(hwmon_dev), priv->name);
>> +
> 
> Any chance to make this consistent with the other driver ?

Will change this to:

if (IS_ERR(hwmon_dev)) {
	dev_err(&priv->dev, "Failed to register hwmon device\n");
	return PTR_ERR(hwmon_dev);
}

>> +    return ret;
>> +}
>> +
>> +static void create_dimm_temp_info_delayed(struct work_struct *work)
>> +{
>> +    struct delayed_work *dwork = to_delayed_work(work);
>> +    struct peci_dimmtemp *priv = container_of(dwork, struct 
>> peci_dimmtemp,
>> +                          work_handler);
>> +    int ret;
>> +
>> +    ret = create_dimm_temp_info(priv);
>> +    if (ret && ret != -EAGAIN)
>> +        dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
>> +}
>> +
>> +static int peci_dimmtemp_probe(struct platform_device *pdev)
>> +{
>> +    struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
>> +    struct device *dev = &pdev->dev;
>> +    struct peci_dimmtemp *priv;
>> +    int ret;
>> +
>> +    if ((mgr->client->adapter->cmd_mask &
>> +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
>> +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
>> +        return -ENODEV;
>> +
>> +    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +    if (!priv)
>> +        return -ENOMEM;
>> +
>> +    dev_set_drvdata(dev, priv);
>> +    priv->mgr = mgr;
>> +    priv->dev = dev;
>> +    priv->gen_info = mgr->gen_info;
>> +
>> +    snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
>> +         priv->mgr->client->addr - PECI_BASE_ADDR);
>> +
>> +    priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
>> +    if (!priv->work_queue)
>> +        return -ENOMEM;
>> +
>> +    INIT_DELAYED_WORK(&priv->work_handler, 
>> create_dimm_temp_info_delayed);
>> +
>> +    ret = create_dimm_temp_info(priv);
>> +    if (ret && ret != -EAGAIN) {
>> +        dev_err(dev, "Failed to create DIMM temp info\n");
> 
> Does this generate error messages if there are no DIMMS ?

Yes, this error message will be printed out once if it meets a timeout
in DIMM scanning when there is no DIMM.

Thanks a lot for your review!

-Jae

>> +        goto err_free_wq;
>> +    }
>> +
>> +    return 0;
>> +
>> +err_free_wq:
>> +    destroy_workqueue(priv->work_queue);
>> +    return ret;
>> +}
>> +
>> +static int peci_dimmtemp_remove(struct platform_device *pdev)
>> +{
>> +    struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
>> +
>> +    cancel_delayed_work_sync(&priv->work_handler);
>> +    destroy_workqueue(priv->work_queue);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct platform_device_id peci_dimmtemp_ids[] = {
>> +    { .name = "peci-dimmtemp", .driver_data = 0 },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
>> +
>> +static struct platform_driver peci_dimmtemp_driver = {
>> +    .probe    = peci_dimmtemp_probe,
>> +    .remove   = peci_dimmtemp_remove,
>> +    .id_table = peci_dimmtemp_ids,
>> +    .driver   = { .name = KBUILD_MODNAME, },
>> +};
>> +module_platform_driver(peci_dimmtemp_driver);
>> +
>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>> +MODULE_DESCRIPTION("PECI dimmtemp driver");
>> +MODULE_LICENSE("GPL v2");
>>
> 
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-16 21:04       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 21:04 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Greg Kroah-Hartman, Lee Jones,
	Jean Delvare, Mark Rutland, Joel Stanley, Andrew Jeffery,
	Jonathan Corbet, Gustavo Pimentel, Kishon Vijay Abraham I,
	Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
	Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon
  Cc: linux-hwmon, devicetree, linux-doc, openbmc, Miguel Ojeda,
	Andrew Lunn, Stef van Os, Jason M Biils, Andy Shevchenko,
	linux-arm-kernel, Alan Cox

Hi Guenter,

On 12/12/2019 10:32 PM, Guenter Roeck wrote:
> On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
>> This commit adds PECI dimmtemp hwmon driver.
>>
>> Cc: Guenter Roeck <linux@roeck-us.net>
>> Cc: Jean Delvare <jdelvare@suse.com>
>> Cc: Alan Cox <alan@linux.intel.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
>> Cc: Andrew Lunn <andrew@lunn.ch>
>> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
>> Reviewed-by: James Feist <james.feist@linux.intel.com>
>> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Acked-by: Guenter Roeck <linux@roeck-us.net>
>> ---
>> Changes since v10:
>> - Added Skylake Xeon D support.
>> - Added max and crit properties for temperature threshold checking.
>> - Fixed minor bugs and style issues.
>>
>>   drivers/hwmon/Kconfig         |  14 ++
>>   drivers/hwmon/Makefile        |   1 +
>>   drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
>>   3 files changed, 408 insertions(+)
>>   create mode 100644 drivers/hwmon/peci-dimmtemp.c
>>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index b6604759579c..d3370fbab40c 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
>>         This driver can also be built as a module. If so, the module
>>         will be called peci-cputemp.
>> +config SENSORS_PECI_DIMMTEMP
>> +    tristate "PECI DIMM temperature monitoring client"
>> +    depends on PECI
>> +    select MFD_INTEL_PECI_CLIENT
>> +    help
>> +      If you say yes here you get support for the generic Intel PECI 
>> hwmon
>> +      driver which provides Digital Thermal Sensor (DTS) thermal 
>> readings of
>> +      DIMM components that are accessible using the PECI Client Command
>> +      Suite via the processor PECI client.
>> +      Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
>> +
>> +      This driver can also be built as a module. If so, the module
>> +      will be called peci-dimmtemp.
>> +
>>   source "drivers/hwmon/pmbus/Kconfig"
>>   config SENSORS_PWM_FAN
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index d6fea48697af..4015c4b60bf4 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
>>   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
>>   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
>>   obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o
>> +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)    += peci-dimmtemp.o
>>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>>   obj-$(CONFIG_SENSORS_PWM_FAN)    += pwm-fan.o
>>   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)    += raspberrypi-hwmon.o
>> diff --git a/drivers/hwmon/peci-dimmtemp.c 
>> b/drivers/hwmon/peci-dimmtemp.c
>> new file mode 100644
>> index 000000000000..974f453f9366
>> --- /dev/null
>> +++ b/drivers/hwmon/peci-dimmtemp.c
>> @@ -0,0 +1,393 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2018-2019 Intel Corporation
>> +
>> +#include <linux/hwmon.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/mfd/intel-peci-client.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/workqueue.h>
>> +#include "peci-hwmon.h"
>> +
>> +#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
>> +#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
>> +
>> +struct peci_dimmtemp {
>> +    struct peci_client_manager *mgr;
>> +    struct device *dev;
>> +    char name[PECI_NAME_SIZE];
>> +    const struct cpu_gen_info *gen_info;
>> +    struct workqueue_struct *work_queue;
>> +    struct delayed_work work_handler;
>> +    struct peci_sensor_data temp[DIMM_NUMS_MAX];
>> +    long temp_max[DIMM_NUMS_MAX];
>> +    long temp_crit[DIMM_NUMS_MAX];
>> +    u32 dimm_mask;
>> +    int retry_count;
>> +    u32 temp_config[DIMM_NUMS_MAX + 1];
>> +    struct hwmon_channel_info temp_info;
>> +    const struct hwmon_channel_info *info[2];
>> +    struct hwmon_chip_info chip;
>> +};
>> +
>> +static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
>> +    { "DIMM A1", "DIMM A2", "DIMM A3" },
>> +    { "DIMM B1", "DIMM B2", "DIMM B3" },
>> +    { "DIMM C1", "DIMM C2", "DIMM C3" },
>> +    { "DIMM D1", "DIMM D2", "DIMM D3" },
>> +    { "DIMM E1", "DIMM E2", "DIMM E3" },
>> +    { "DIMM F1", "DIMM F2", "DIMM F3" },
>> +    { "DIMM G1", "DIMM G2", "DIMM G3" },
>> +    { "DIMM H1", "DIMM H2", "DIMM H3" },
>> +};
>> +
>> +static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
>> +                        int chan_rank,
>> +                        u8 *cfg_data)
>> +{
>> +    return peci_client_read_package_config(priv->mgr,
>> +                           PECI_MBX_INDEX_DDR_DIMM_TEMP,
>> +                           chan_rank, cfg_data);
>> +}
>> +
>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
>> +{
>> +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
>> +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
>> +    struct peci_rd_pci_cfg_local_msg rp_msg;
>> +    u8  cfg_data[4];
>> +    int ret;
>> +
>> +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
>> +        return 0;
>> +
>> +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>> +    if (ret)
>> +        return ret;
>> +
>> +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
>> +
>> +    switch (priv->gen_info->model) {
>> +    case INTEL_FAM6_SKYLAKE_X:
>> +        rp_msg.addr = priv->mgr->client->addr;
>> +        rp_msg.bus = 2;
>> +        /*
>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>> +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
>> +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
>> +         */
>> +        rp_msg.device = 10 + chan_rank / 3 * 2 +
>> +                 (chan_rank % 3 == 2 ? 1 : 0);
>> +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>> +        rp_msg.rx_len = 4;
>> +
>> +        ret = peci_command(priv->mgr->client->adapter,
>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>> +            ret = -EAGAIN;
>> +        if (ret)
>> +            return ret;
>> +
>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>> +        break;
>> +    case INTEL_FAM6_SKYLAKE_XD:
>> +        rp_msg.addr = priv->mgr->client->addr;
>> +        rp_msg.bus = 2;
>> +        /*
>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
>> +         */
>> +        rp_msg.device = 10 + chan_rank / 2 * 2;
>> +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>> +        rp_msg.rx_len = 4;
>> +
>> +        ret = peci_command(priv->mgr->client->adapter,
>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>> +            ret = -EAGAIN;
>> +        if (ret)
>> +            return ret;
>> +
>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>> +        break;
>> +    case INTEL_FAM6_HASWELL_X:
>> +    case INTEL_FAM6_BROADWELL_X:
>> +        rp_msg.addr = priv->mgr->client->addr;
>> +        rp_msg.bus = 1;
>> +        /*
>> +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
>> +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
>> +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
>> +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
>> +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
>> +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
>> +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
>> +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
>> +         */
>> +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
>> +        rp_msg.function = chan_rank % 2;
>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>> +        rp_msg.rx_len = 4;
>> +
>> +        ret = peci_command(priv->mgr->client->adapter,
>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>> +            ret = -EAGAIN;
>> +        if (ret)
>> +            return ret;
>> +
>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>> +        break;
>> +    default:
>> +        return -EOPNOTSUPP;
> 
> It looks like the sensors are created even on unsupported platforms,
> which would generate error messages whenever someone tries to read
> the attributes.
> 
> There should be some code early on checking this, and the driver
> should not even instantiate if the CPU model is not supported.

Actually, this 'default' case will not be happened because this driver
will be registered only when the CPU model is supported. The CPU model
checking code is in 'intel-peci-client.c' which is [11/14] of this
patch set.

>> +    }
>> +
>> +    peci_sensor_mark_updated(&priv->temp[dimm_no]);
>> +
>> +    return 0;
>> +}
>> +
>> +static int dimmtemp_read_string(struct device *dev,
>> +                enum hwmon_sensor_types type,
>> +                u32 attr, int channel, const char **str)
>> +{
>> +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
>> +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
>> +    int chan_rank, dimm_idx;
>> +
>> +    if (attr != hwmon_temp_label)
>> +        return -EOPNOTSUPP;
>> +
>> +    chan_rank = channel / dimm_idx_max;
>> +    dimm_idx = channel % dimm_idx_max;
>> +    *str = dimmtemp_label[chan_rank][dimm_idx];
> 
> Similar to the other patch, I am concerned that this can end up setting 
> *str
> to NULL at some point in the future.

Okay. I'll make dynamic label string table generation code for it as
well.

>> +
>> +    return 0;
>> +}
>> +
>> +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types 
>> type,
>> +             u32 attr, int channel, long *val)
>> +{
>> +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
>> +    int ret;
>> +
>> +    ret = get_dimm_temp(priv, channel);
>> +    if (ret)
>> +        return ret;
>> +
>> +    switch (attr) {
>> +    case hwmon_temp_input:
>> +        *val = priv->temp[channel].value;
>> +        break;
>> +    case hwmon_temp_max:
>> +        *val = priv->temp_max[channel];
>> +        break;
>> +    case hwmon_temp_crit:
>> +        *val = priv->temp_crit[channel];
>> +        break;
>> +    default:
>> +        ret = -EOPNOTSUPP;
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static umode_t dimmtemp_is_visible(const void *data,
>> +                   enum hwmon_sensor_types type,
>> +                   u32 attr, int channel)
>> +{
>> +    const struct peci_dimmtemp *priv = data;
>> +
>> +    if (priv->temp_config[channel] & BIT(attr) &&
>> +        priv->dimm_mask & BIT(channel))
>> +        return 0444;
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct hwmon_ops dimmtemp_ops = {
>> +    .is_visible = dimmtemp_is_visible,
>> +    .read_string = dimmtemp_read_string,
>> +    .read = dimmtemp_read,
>> +};
>> +
>> +static int check_populated_dimms(struct peci_dimmtemp *priv)
>> +{
>> +    u32 chan_rank_max = priv->gen_info->chan_rank_max;
>> +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
>> +    int chan_rank, dimm_idx;
>> +    u8  cfg_data[4];
>> +
>> +    for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
>> +        int ret;
>> +
>> +        ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>> +        if (ret) {
>> +            priv->dimm_mask = 0;
>> +            return ret;
>> +        }
>> +
>> +        for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
>> +            if (cfg_data[dimm_idx])
>> +                priv->dimm_mask |= BIT(chan_rank *
>> +                               dimm_idx_max +
>> +                               dimm_idx);
>> +    }
>> +
>> +    if (!priv->dimm_mask)
>> +        return -EAGAIN;
>> +
>> +    dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", 
>> priv->dimm_mask);
>> +
>> +    return 0;
>> +}
>> +
>> +static int create_dimm_temp_info(struct peci_dimmtemp *priv)
>> +{
>> +    int ret, i, config_idx, channels;
>> +    struct device *hwmon_dev;
>> +
>> +    ret = check_populated_dimms(priv);
>> +    if (ret) {
>> +        if (ret == -EAGAIN) {
>> +            if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
>> +                queue_delayed_work(priv->work_queue,
>> +                           &priv->work_handler,
>> +                         DIMM_MASK_CHECK_DELAY_JIFFIES);
>> +                priv->retry_count++;
>> +                dev_dbg(priv->dev,
>> +                    "Deferred DIMM temp info creation\n");
>> +            } else {
>> +                dev_err(priv->dev,
>> +                    "Timeout DIMM temp info creation\n");
>> +                ret = -ETIMEDOUT;
>> +            }
>> +        }
>> +
>> +        return ret;
>> +    }
>> +
>> +    channels = priv->gen_info->chan_rank_max *
>> +           priv->gen_info->dimm_idx_max;
>> +    for (i = 0, config_idx = 0; i < channels; i++)
>> +        if (priv->dimm_mask & BIT(i))
>> +            while (i >= config_idx)
>> +                priv->temp_config[config_idx++] =
>> +                    HWMON_T_LABEL | HWMON_T_INPUT |
>> +                    HWMON_T_MAX | HWMON_T_CRIT;
>> +
>> +    priv->chip.ops = &dimmtemp_ops;
>> +    priv->chip.info = priv->info;
>> +
>> +    priv->info[0] = &priv->temp_info;
>> +
>> +    priv->temp_info.type = hwmon_temp;
>> +    priv->temp_info.config = priv->temp_config;
>> +
>> +    hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
>> +                             priv->name,
>> +                             priv,
>> +                             &priv->chip,
>> +                             NULL);
>> +    ret = PTR_ERR_OR_ZERO(hwmon_dev);
>> +    if (!ret)
>> +        dev_dbg(priv->dev, "%s: sensor '%s'\n",
>> +            dev_name(hwmon_dev), priv->name);
>> +
> 
> Any chance to make this consistent with the other driver ?

Will change this to:

if (IS_ERR(hwmon_dev)) {
	dev_err(&priv->dev, "Failed to register hwmon device\n");
	return PTR_ERR(hwmon_dev);
}

>> +    return ret;
>> +}
>> +
>> +static void create_dimm_temp_info_delayed(struct work_struct *work)
>> +{
>> +    struct delayed_work *dwork = to_delayed_work(work);
>> +    struct peci_dimmtemp *priv = container_of(dwork, struct 
>> peci_dimmtemp,
>> +                          work_handler);
>> +    int ret;
>> +
>> +    ret = create_dimm_temp_info(priv);
>> +    if (ret && ret != -EAGAIN)
>> +        dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
>> +}
>> +
>> +static int peci_dimmtemp_probe(struct platform_device *pdev)
>> +{
>> +    struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
>> +    struct device *dev = &pdev->dev;
>> +    struct peci_dimmtemp *priv;
>> +    int ret;
>> +
>> +    if ((mgr->client->adapter->cmd_mask &
>> +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
>> +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
>> +        return -ENODEV;
>> +
>> +    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +    if (!priv)
>> +        return -ENOMEM;
>> +
>> +    dev_set_drvdata(dev, priv);
>> +    priv->mgr = mgr;
>> +    priv->dev = dev;
>> +    priv->gen_info = mgr->gen_info;
>> +
>> +    snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
>> +         priv->mgr->client->addr - PECI_BASE_ADDR);
>> +
>> +    priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
>> +    if (!priv->work_queue)
>> +        return -ENOMEM;
>> +
>> +    INIT_DELAYED_WORK(&priv->work_handler, 
>> create_dimm_temp_info_delayed);
>> +
>> +    ret = create_dimm_temp_info(priv);
>> +    if (ret && ret != -EAGAIN) {
>> +        dev_err(dev, "Failed to create DIMM temp info\n");
> 
> Does this generate error messages if there are no DIMMS ?

Yes, this error message will be printed out once if it meets a timeout
in DIMM scanning when there is no DIMM.

Thanks a lot for your review!

-Jae

>> +        goto err_free_wq;
>> +    }
>> +
>> +    return 0;
>> +
>> +err_free_wq:
>> +    destroy_workqueue(priv->work_queue);
>> +    return ret;
>> +}
>> +
>> +static int peci_dimmtemp_remove(struct platform_device *pdev)
>> +{
>> +    struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
>> +
>> +    cancel_delayed_work_sync(&priv->work_handler);
>> +    destroy_workqueue(priv->work_queue);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct platform_device_id peci_dimmtemp_ids[] = {
>> +    { .name = "peci-dimmtemp", .driver_data = 0 },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
>> +
>> +static struct platform_driver peci_dimmtemp_driver = {
>> +    .probe    = peci_dimmtemp_probe,
>> +    .remove   = peci_dimmtemp_remove,
>> +    .id_table = peci_dimmtemp_ids,
>> +    .driver   = { .name = KBUILD_MODNAME, },
>> +};
>> +module_platform_driver(peci_dimmtemp_driver);
>> +
>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>> +MODULE_DESCRIPTION("PECI dimmtemp driver");
>> +MODULE_LICENSE("GPL v2");
>>
> 
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-16 21:04       ` Jae Hyun Yoo
@ 2019-12-16 21:21         ` Guenter Roeck
  -1 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-16 21:21 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

On Mon, Dec 16, 2019 at 01:04:31PM -0800, Jae Hyun Yoo wrote:
> Hi Guenter,
> 
> On 12/12/2019 10:32 PM, Guenter Roeck wrote:
> > On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
> > > This commit adds PECI dimmtemp hwmon driver.
> > > 
> > > Cc: Guenter Roeck <linux@roeck-us.net>
> > > Cc: Jean Delvare <jdelvare@suse.com>
> > > Cc: Alan Cox <alan@linux.intel.com>
> > > Cc: Andrew Jeffery <andrew@aj.id.au>
> > > Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > > Cc: Arnd Bergmann <arnd@arndb.de>
> > > Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> > > Cc: Joel Stanley <joel@jms.id.au>
> > > Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
> > > Cc: Andrew Lunn <andrew@lunn.ch>
> > > Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
> > > Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> > > Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> > > Reviewed-by: James Feist <james.feist@linux.intel.com>
> > > Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> > > Acked-by: Guenter Roeck <linux@roeck-us.net>
> > > ---
> > > Changes since v10:
> > > - Added Skylake Xeon D support.
> > > - Added max and crit properties for temperature threshold checking.
> > > - Fixed minor bugs and style issues.
> > > 
> > >   drivers/hwmon/Kconfig         |  14 ++
> > >   drivers/hwmon/Makefile        |   1 +
> > >   drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
> > >   3 files changed, 408 insertions(+)
> > >   create mode 100644 drivers/hwmon/peci-dimmtemp.c
> > > 
> > > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> > > index b6604759579c..d3370fbab40c 100644
> > > --- a/drivers/hwmon/Kconfig
> > > +++ b/drivers/hwmon/Kconfig
> > > @@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
> > >         This driver can also be built as a module. If so, the module
> > >         will be called peci-cputemp.
> > > +config SENSORS_PECI_DIMMTEMP
> > > +    tristate "PECI DIMM temperature monitoring client"
> > > +    depends on PECI
> > > +    select MFD_INTEL_PECI_CLIENT
> > > +    help
> > > +      If you say yes here you get support for the generic Intel
> > > PECI hwmon
> > > +      driver which provides Digital Thermal Sensor (DTS) thermal
> > > readings of
> > > +      DIMM components that are accessible using the PECI Client Command
> > > +      Suite via the processor PECI client.
> > > +      Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
> > > +
> > > +      This driver can also be built as a module. If so, the module
> > > +      will be called peci-dimmtemp.
> > > +
> > >   source "drivers/hwmon/pmbus/Kconfig"
> > >   config SENSORS_PWM_FAN
> > > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> > > index d6fea48697af..4015c4b60bf4 100644
> > > --- a/drivers/hwmon/Makefile
> > > +++ b/drivers/hwmon/Makefile
> > > @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
> > >   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
> > >   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
> > >   obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o
> > > +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)    += peci-dimmtemp.o
> > >   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
> > >   obj-$(CONFIG_SENSORS_PWM_FAN)    += pwm-fan.o
> > >   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)    += raspberrypi-hwmon.o
> > > diff --git a/drivers/hwmon/peci-dimmtemp.c
> > > b/drivers/hwmon/peci-dimmtemp.c
> > > new file mode 100644
> > > index 000000000000..974f453f9366
> > > --- /dev/null
> > > +++ b/drivers/hwmon/peci-dimmtemp.c
> > > @@ -0,0 +1,393 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +// Copyright (c) 2018-2019 Intel Corporation
> > > +
> > > +#include <linux/hwmon.h>
> > > +#include <linux/jiffies.h>
> > > +#include <linux/mfd/intel-peci-client.h>
> > > +#include <linux/module.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/workqueue.h>
> > > +#include "peci-hwmon.h"
> > > +
> > > +#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
> > > +#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
> > > +
> > > +struct peci_dimmtemp {
> > > +    struct peci_client_manager *mgr;
> > > +    struct device *dev;
> > > +    char name[PECI_NAME_SIZE];
> > > +    const struct cpu_gen_info *gen_info;
> > > +    struct workqueue_struct *work_queue;
> > > +    struct delayed_work work_handler;
> > > +    struct peci_sensor_data temp[DIMM_NUMS_MAX];
> > > +    long temp_max[DIMM_NUMS_MAX];
> > > +    long temp_crit[DIMM_NUMS_MAX];
> > > +    u32 dimm_mask;
> > > +    int retry_count;
> > > +    u32 temp_config[DIMM_NUMS_MAX + 1];
> > > +    struct hwmon_channel_info temp_info;
> > > +    const struct hwmon_channel_info *info[2];
> > > +    struct hwmon_chip_info chip;
> > > +};
> > > +
> > > +static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
> > > +    { "DIMM A1", "DIMM A2", "DIMM A3" },
> > > +    { "DIMM B1", "DIMM B2", "DIMM B3" },
> > > +    { "DIMM C1", "DIMM C2", "DIMM C3" },
> > > +    { "DIMM D1", "DIMM D2", "DIMM D3" },
> > > +    { "DIMM E1", "DIMM E2", "DIMM E3" },
> > > +    { "DIMM F1", "DIMM F2", "DIMM F3" },
> > > +    { "DIMM G1", "DIMM G2", "DIMM G3" },
> > > +    { "DIMM H1", "DIMM H2", "DIMM H3" },
> > > +};
> > > +
> > > +static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
> > > +                        int chan_rank,
> > > +                        u8 *cfg_data)
> > > +{
> > > +    return peci_client_read_package_config(priv->mgr,
> > > +                           PECI_MBX_INDEX_DDR_DIMM_TEMP,
> > > +                           chan_rank, cfg_data);
> > > +}
> > > +
> > > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
> > > +{
> > > +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
> > > +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
> > > +    struct peci_rd_pci_cfg_local_msg rp_msg;
> > > +    u8  cfg_data[4];
> > > +    int ret;
> > > +
> > > +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
> > > +        return 0;
> > > +
> > > +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> > > +    if (ret)
> > > +        return ret;
> > > +
> > > +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
> > > +
> > > +    switch (priv->gen_info->model) {
> > > +    case INTEL_FAM6_SKYLAKE_X:
> > > +        rp_msg.addr = priv->mgr->client->addr;
> > > +        rp_msg.bus = 2;
> > > +        /*
> > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
> > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
> > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
> > > +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
> > > +         */
> > > +        rp_msg.device = 10 + chan_rank / 3 * 2 +
> > > +                 (chan_rank % 3 == 2 ? 1 : 0);
> > > +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
> > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > +        rp_msg.rx_len = 4;
> > > +
> > > +        ret = peci_command(priv->mgr->client->adapter,
> > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > +            ret = -EAGAIN;
> > > +        if (ret)
> > > +            return ret;
> > > +
> > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > +        break;
> > > +    case INTEL_FAM6_SKYLAKE_XD:
> > > +        rp_msg.addr = priv->mgr->client->addr;
> > > +        rp_msg.bus = 2;
> > > +        /*
> > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
> > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
> > > +         */
> > > +        rp_msg.device = 10 + chan_rank / 2 * 2;
> > > +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
> > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > +        rp_msg.rx_len = 4;
> > > +
> > > +        ret = peci_command(priv->mgr->client->adapter,
> > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > +            ret = -EAGAIN;
> > > +        if (ret)
> > > +            return ret;
> > > +
> > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > +        break;
> > > +    case INTEL_FAM6_HASWELL_X:
> > > +    case INTEL_FAM6_BROADWELL_X:
> > > +        rp_msg.addr = priv->mgr->client->addr;
> > > +        rp_msg.bus = 1;
> > > +        /*
> > > +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
> > > +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
> > > +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
> > > +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
> > > +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
> > > +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
> > > +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
> > > +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
> > > +         */
> > > +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
> > > +        rp_msg.function = chan_rank % 2;
> > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > +        rp_msg.rx_len = 4;
> > > +
> > > +        ret = peci_command(priv->mgr->client->adapter,
> > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > +            ret = -EAGAIN;
> > > +        if (ret)
> > > +            return ret;
> > > +
> > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > +        break;
> > > +    default:
> > > +        return -EOPNOTSUPP;
> > 
> > It looks like the sensors are created even on unsupported platforms,
> > which would generate error messages whenever someone tries to read
> > the attributes.
> > 
> > There should be some code early on checking this, and the driver
> > should not even instantiate if the CPU model is not supported.
> 
> Actually, this 'default' case will not be happened because this driver
> will be registered only when the CPU model is supported. The CPU model
> checking code is in 'intel-peci-client.c' which is [11/14] of this
> patch set.
> 

That again assumes that both drivers will be modified in sync in the future.
We can not make that assumption.

> > > +    }
> > > +
> > > +    peci_sensor_mark_updated(&priv->temp[dimm_no]);
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int dimmtemp_read_string(struct device *dev,
> > > +                enum hwmon_sensor_types type,
> > > +                u32 attr, int channel, const char **str)
> > > +{
> > > +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> > > +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> > > +    int chan_rank, dimm_idx;
> > > +
> > > +    if (attr != hwmon_temp_label)
> > > +        return -EOPNOTSUPP;
> > > +
> > > +    chan_rank = channel / dimm_idx_max;
> > > +    dimm_idx = channel % dimm_idx_max;
> > > +    *str = dimmtemp_label[chan_rank][dimm_idx];
> > 
> > Similar to the other patch, I am concerned that this can end up setting
> > *str
> > to NULL at some point in the future.
> 
> Okay. I'll make dynamic label string table generation code for it as
> well.
> 
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int dimmtemp_read(struct device *dev, enum
> > > hwmon_sensor_types type,
> > > +             u32 attr, int channel, long *val)
> > > +{
> > > +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> > > +    int ret;
> > > +
> > > +    ret = get_dimm_temp(priv, channel);
> > > +    if (ret)
> > > +        return ret;
> > > +
> > > +    switch (attr) {
> > > +    case hwmon_temp_input:
> > > +        *val = priv->temp[channel].value;
> > > +        break;
> > > +    case hwmon_temp_max:
> > > +        *val = priv->temp_max[channel];
> > > +        break;
> > > +    case hwmon_temp_crit:
> > > +        *val = priv->temp_crit[channel];
> > > +        break;
> > > +    default:
> > > +        ret = -EOPNOTSUPP;
> > > +        break;
> > > +    }
> > > +
> > > +    return ret;
> > > +}
> > > +
> > > +static umode_t dimmtemp_is_visible(const void *data,
> > > +                   enum hwmon_sensor_types type,
> > > +                   u32 attr, int channel)
> > > +{
> > > +    const struct peci_dimmtemp *priv = data;
> > > +
> > > +    if (priv->temp_config[channel] & BIT(attr) &&
> > > +        priv->dimm_mask & BIT(channel))
> > > +        return 0444;
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static const struct hwmon_ops dimmtemp_ops = {
> > > +    .is_visible = dimmtemp_is_visible,
> > > +    .read_string = dimmtemp_read_string,
> > > +    .read = dimmtemp_read,
> > > +};
> > > +
> > > +static int check_populated_dimms(struct peci_dimmtemp *priv)
> > > +{
> > > +    u32 chan_rank_max = priv->gen_info->chan_rank_max;
> > > +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> > > +    int chan_rank, dimm_idx;
> > > +    u8  cfg_data[4];
> > > +
> > > +    for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
> > > +        int ret;
> > > +
> > > +        ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> > > +        if (ret) {
> > > +            priv->dimm_mask = 0;
> > > +            return ret;
> > > +        }
> > > +
> > > +        for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
> > > +            if (cfg_data[dimm_idx])
> > > +                priv->dimm_mask |= BIT(chan_rank *
> > > +                               dimm_idx_max +
> > > +                               dimm_idx);
> > > +    }
> > > +
> > > +    if (!priv->dimm_mask)
> > > +        return -EAGAIN;
> > > +
> > > +    dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n",
> > > priv->dimm_mask);
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int create_dimm_temp_info(struct peci_dimmtemp *priv)
> > > +{
> > > +    int ret, i, config_idx, channels;
> > > +    struct device *hwmon_dev;
> > > +
> > > +    ret = check_populated_dimms(priv);
> > > +    if (ret) {
> > > +        if (ret == -EAGAIN) {
> > > +            if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
> > > +                queue_delayed_work(priv->work_queue,
> > > +                           &priv->work_handler,
> > > +                         DIMM_MASK_CHECK_DELAY_JIFFIES);
> > > +                priv->retry_count++;
> > > +                dev_dbg(priv->dev,
> > > +                    "Deferred DIMM temp info creation\n");
> > > +            } else {
> > > +                dev_err(priv->dev,
> > > +                    "Timeout DIMM temp info creation\n");
> > > +                ret = -ETIMEDOUT;
> > > +            }
> > > +        }
> > > +
> > > +        return ret;
> > > +    }
> > > +
> > > +    channels = priv->gen_info->chan_rank_max *
> > > +           priv->gen_info->dimm_idx_max;
> > > +    for (i = 0, config_idx = 0; i < channels; i++)
> > > +        if (priv->dimm_mask & BIT(i))
> > > +            while (i >= config_idx)
> > > +                priv->temp_config[config_idx++] =
> > > +                    HWMON_T_LABEL | HWMON_T_INPUT |
> > > +                    HWMON_T_MAX | HWMON_T_CRIT;
> > > +
> > > +    priv->chip.ops = &dimmtemp_ops;
> > > +    priv->chip.info = priv->info;
> > > +
> > > +    priv->info[0] = &priv->temp_info;
> > > +
> > > +    priv->temp_info.type = hwmon_temp;
> > > +    priv->temp_info.config = priv->temp_config;
> > > +
> > > +    hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
> > > +                             priv->name,
> > > +                             priv,
> > > +                             &priv->chip,
> > > +                             NULL);
> > > +    ret = PTR_ERR_OR_ZERO(hwmon_dev);
> > > +    if (!ret)
> > > +        dev_dbg(priv->dev, "%s: sensor '%s'\n",
> > > +            dev_name(hwmon_dev), priv->name);
> > > +
> > 
> > Any chance to make this consistent with the other driver ?
> 
> Will change this to:
> 
> if (IS_ERR(hwmon_dev)) {
> 	dev_err(&priv->dev, "Failed to register hwmon device\n");
> 	return PTR_ERR(hwmon_dev);
> }
> 
> > > +    return ret;
> > > +}
> > > +
> > > +static void create_dimm_temp_info_delayed(struct work_struct *work)
> > > +{
> > > +    struct delayed_work *dwork = to_delayed_work(work);
> > > +    struct peci_dimmtemp *priv = container_of(dwork, struct
> > > peci_dimmtemp,
> > > +                          work_handler);
> > > +    int ret;
> > > +
> > > +    ret = create_dimm_temp_info(priv);
> > > +    if (ret && ret != -EAGAIN)
> > > +        dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
> > > +}
> > > +
> > > +static int peci_dimmtemp_probe(struct platform_device *pdev)
> > > +{
> > > +    struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
> > > +    struct device *dev = &pdev->dev;
> > > +    struct peci_dimmtemp *priv;
> > > +    int ret;
> > > +
> > > +    if ((mgr->client->adapter->cmd_mask &
> > > +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
> > > +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
> > > +        return -ENODEV;
> > > +
> > > +    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > > +    if (!priv)
> > > +        return -ENOMEM;
> > > +
> > > +    dev_set_drvdata(dev, priv);
> > > +    priv->mgr = mgr;
> > > +    priv->dev = dev;
> > > +    priv->gen_info = mgr->gen_info;
> > > +
> > > +    snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
> > > +         priv->mgr->client->addr - PECI_BASE_ADDR);
> > > +
> > > +    priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
> > > +    if (!priv->work_queue)
> > > +        return -ENOMEM;
> > > +
> > > +    INIT_DELAYED_WORK(&priv->work_handler,
> > > create_dimm_temp_info_delayed);
> > > +
> > > +    ret = create_dimm_temp_info(priv);
> > > +    if (ret && ret != -EAGAIN) {
> > > +        dev_err(dev, "Failed to create DIMM temp info\n");
> > 
> > Does this generate error messages if there are no DIMMS ?
> 
> Yes, this error message will be printed out once if it meets a timeout
> in DIMM scanning when there is no DIMM.
> 

Is that indeed an error, or are there situations where no DIMMs are
detected and that is a perfectly valid situation ? An error message
is only acceptable if this is indeed an error in all situations.

Guenter

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-16 21:21         ` Guenter Roeck
  0 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-16 21:21 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Andrew Lunn, Randy Dunlap, Tomer Maimon,
	devicetree, Frederic Barrat, Mauro Carvalho Chehab, Lee Jones,
	Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
	openbmc, linux-doc, Kishon Vijay Abraham I, Joel Stanley,
	Bryant G . Ly, Uwe Kleine-Konig, David Kershner, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Stef van Os,
	Thomas Gleixner, Andy Shevchenko, Sagar Dharia, linux-arm-kernel,
	Alan Cox, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Miguel Ojeda,
	Vinod Koul, Gustavo Pimentel, Andrew Morton, David S . Miller

On Mon, Dec 16, 2019 at 01:04:31PM -0800, Jae Hyun Yoo wrote:
> Hi Guenter,
> 
> On 12/12/2019 10:32 PM, Guenter Roeck wrote:
> > On 12/11/19 11:46 AM, Jae Hyun Yoo wrote:
> > > This commit adds PECI dimmtemp hwmon driver.
> > > 
> > > Cc: Guenter Roeck <linux@roeck-us.net>
> > > Cc: Jean Delvare <jdelvare@suse.com>
> > > Cc: Alan Cox <alan@linux.intel.com>
> > > Cc: Andrew Jeffery <andrew@aj.id.au>
> > > Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > > Cc: Arnd Bergmann <arnd@arndb.de>
> > > Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> > > Cc: Joel Stanley <joel@jms.id.au>
> > > Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
> > > Cc: Andrew Lunn <andrew@lunn.ch>
> > > Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
> > > Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> > > Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> > > Reviewed-by: James Feist <james.feist@linux.intel.com>
> > > Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> > > Acked-by: Guenter Roeck <linux@roeck-us.net>
> > > ---
> > > Changes since v10:
> > > - Added Skylake Xeon D support.
> > > - Added max and crit properties for temperature threshold checking.
> > > - Fixed minor bugs and style issues.
> > > 
> > >   drivers/hwmon/Kconfig         |  14 ++
> > >   drivers/hwmon/Makefile        |   1 +
> > >   drivers/hwmon/peci-dimmtemp.c | 393 ++++++++++++++++++++++++++++++++++
> > >   3 files changed, 408 insertions(+)
> > >   create mode 100644 drivers/hwmon/peci-dimmtemp.c
> > > 
> > > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> > > index b6604759579c..d3370fbab40c 100644
> > > --- a/drivers/hwmon/Kconfig
> > > +++ b/drivers/hwmon/Kconfig
> > > @@ -1363,6 +1363,20 @@ config SENSORS_PECI_CPUTEMP
> > >         This driver can also be built as a module. If so, the module
> > >         will be called peci-cputemp.
> > > +config SENSORS_PECI_DIMMTEMP
> > > +    tristate "PECI DIMM temperature monitoring client"
> > > +    depends on PECI
> > > +    select MFD_INTEL_PECI_CLIENT
> > > +    help
> > > +      If you say yes here you get support for the generic Intel
> > > PECI hwmon
> > > +      driver which provides Digital Thermal Sensor (DTS) thermal
> > > readings of
> > > +      DIMM components that are accessible using the PECI Client Command
> > > +      Suite via the processor PECI client.
> > > +      Check <file:Documentation/hwmon/peci-dimmtemp.rst> for details.
> > > +
> > > +      This driver can also be built as a module. If so, the module
> > > +      will be called peci-dimmtemp.
> > > +
> > >   source "drivers/hwmon/pmbus/Kconfig"
> > >   config SENSORS_PWM_FAN
> > > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> > > index d6fea48697af..4015c4b60bf4 100644
> > > --- a/drivers/hwmon/Makefile
> > > +++ b/drivers/hwmon/Makefile
> > > @@ -145,6 +145,7 @@ obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
> > >   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
> > >   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
> > >   obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o
> > > +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)    += peci-dimmtemp.o
> > >   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
> > >   obj-$(CONFIG_SENSORS_PWM_FAN)    += pwm-fan.o
> > >   obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)    += raspberrypi-hwmon.o
> > > diff --git a/drivers/hwmon/peci-dimmtemp.c
> > > b/drivers/hwmon/peci-dimmtemp.c
> > > new file mode 100644
> > > index 000000000000..974f453f9366
> > > --- /dev/null
> > > +++ b/drivers/hwmon/peci-dimmtemp.c
> > > @@ -0,0 +1,393 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +// Copyright (c) 2018-2019 Intel Corporation
> > > +
> > > +#include <linux/hwmon.h>
> > > +#include <linux/jiffies.h>
> > > +#include <linux/mfd/intel-peci-client.h>
> > > +#include <linux/module.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/workqueue.h>
> > > +#include "peci-hwmon.h"
> > > +
> > > +#define DIMM_MASK_CHECK_DELAY_JIFFIES  msecs_to_jiffies(5000)
> > > +#define DIMM_MASK_CHECK_RETRY_MAX      60 /* 60 x 5 secs = 5 minutes */
> > > +
> > > +struct peci_dimmtemp {
> > > +    struct peci_client_manager *mgr;
> > > +    struct device *dev;
> > > +    char name[PECI_NAME_SIZE];
> > > +    const struct cpu_gen_info *gen_info;
> > > +    struct workqueue_struct *work_queue;
> > > +    struct delayed_work work_handler;
> > > +    struct peci_sensor_data temp[DIMM_NUMS_MAX];
> > > +    long temp_max[DIMM_NUMS_MAX];
> > > +    long temp_crit[DIMM_NUMS_MAX];
> > > +    u32 dimm_mask;
> > > +    int retry_count;
> > > +    u32 temp_config[DIMM_NUMS_MAX + 1];
> > > +    struct hwmon_channel_info temp_info;
> > > +    const struct hwmon_channel_info *info[2];
> > > +    struct hwmon_chip_info chip;
> > > +};
> > > +
> > > +static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
> > > +    { "DIMM A1", "DIMM A2", "DIMM A3" },
> > > +    { "DIMM B1", "DIMM B2", "DIMM B3" },
> > > +    { "DIMM C1", "DIMM C2", "DIMM C3" },
> > > +    { "DIMM D1", "DIMM D2", "DIMM D3" },
> > > +    { "DIMM E1", "DIMM E2", "DIMM E3" },
> > > +    { "DIMM F1", "DIMM F2", "DIMM F3" },
> > > +    { "DIMM G1", "DIMM G2", "DIMM G3" },
> > > +    { "DIMM H1", "DIMM H2", "DIMM H3" },
> > > +};
> > > +
> > > +static inline int read_ddr_dimm_temp_config(struct peci_dimmtemp *priv,
> > > +                        int chan_rank,
> > > +                        u8 *cfg_data)
> > > +{
> > > +    return peci_client_read_package_config(priv->mgr,
> > > +                           PECI_MBX_INDEX_DDR_DIMM_TEMP,
> > > +                           chan_rank, cfg_data);
> > > +}
> > > +
> > > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
> > > +{
> > > +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
> > > +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
> > > +    struct peci_rd_pci_cfg_local_msg rp_msg;
> > > +    u8  cfg_data[4];
> > > +    int ret;
> > > +
> > > +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
> > > +        return 0;
> > > +
> > > +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> > > +    if (ret)
> > > +        return ret;
> > > +
> > > +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
> > > +
> > > +    switch (priv->gen_info->model) {
> > > +    case INTEL_FAM6_SKYLAKE_X:
> > > +        rp_msg.addr = priv->mgr->client->addr;
> > > +        rp_msg.bus = 2;
> > > +        /*
> > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
> > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
> > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
> > > +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
> > > +         */
> > > +        rp_msg.device = 10 + chan_rank / 3 * 2 +
> > > +                 (chan_rank % 3 == 2 ? 1 : 0);
> > > +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
> > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > +        rp_msg.rx_len = 4;
> > > +
> > > +        ret = peci_command(priv->mgr->client->adapter,
> > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > +            ret = -EAGAIN;
> > > +        if (ret)
> > > +            return ret;
> > > +
> > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > +        break;
> > > +    case INTEL_FAM6_SKYLAKE_XD:
> > > +        rp_msg.addr = priv->mgr->client->addr;
> > > +        rp_msg.bus = 2;
> > > +        /*
> > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
> > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
> > > +         */
> > > +        rp_msg.device = 10 + chan_rank / 2 * 2;
> > > +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
> > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > +        rp_msg.rx_len = 4;
> > > +
> > > +        ret = peci_command(priv->mgr->client->adapter,
> > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > +            ret = -EAGAIN;
> > > +        if (ret)
> > > +            return ret;
> > > +
> > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > +        break;
> > > +    case INTEL_FAM6_HASWELL_X:
> > > +    case INTEL_FAM6_BROADWELL_X:
> > > +        rp_msg.addr = priv->mgr->client->addr;
> > > +        rp_msg.bus = 1;
> > > +        /*
> > > +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
> > > +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
> > > +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
> > > +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
> > > +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
> > > +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
> > > +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
> > > +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
> > > +         */
> > > +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
> > > +        rp_msg.function = chan_rank % 2;
> > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > +        rp_msg.rx_len = 4;
> > > +
> > > +        ret = peci_command(priv->mgr->client->adapter,
> > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > +            ret = -EAGAIN;
> > > +        if (ret)
> > > +            return ret;
> > > +
> > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > +        break;
> > > +    default:
> > > +        return -EOPNOTSUPP;
> > 
> > It looks like the sensors are created even on unsupported platforms,
> > which would generate error messages whenever someone tries to read
> > the attributes.
> > 
> > There should be some code early on checking this, and the driver
> > should not even instantiate if the CPU model is not supported.
> 
> Actually, this 'default' case will not be happened because this driver
> will be registered only when the CPU model is supported. The CPU model
> checking code is in 'intel-peci-client.c' which is [11/14] of this
> patch set.
> 

That again assumes that both drivers will be modified in sync in the future.
We can not make that assumption.

> > > +    }
> > > +
> > > +    peci_sensor_mark_updated(&priv->temp[dimm_no]);
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int dimmtemp_read_string(struct device *dev,
> > > +                enum hwmon_sensor_types type,
> > > +                u32 attr, int channel, const char **str)
> > > +{
> > > +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> > > +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> > > +    int chan_rank, dimm_idx;
> > > +
> > > +    if (attr != hwmon_temp_label)
> > > +        return -EOPNOTSUPP;
> > > +
> > > +    chan_rank = channel / dimm_idx_max;
> > > +    dimm_idx = channel % dimm_idx_max;
> > > +    *str = dimmtemp_label[chan_rank][dimm_idx];
> > 
> > Similar to the other patch, I am concerned that this can end up setting
> > *str
> > to NULL at some point in the future.
> 
> Okay. I'll make dynamic label string table generation code for it as
> well.
> 
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int dimmtemp_read(struct device *dev, enum
> > > hwmon_sensor_types type,
> > > +             u32 attr, int channel, long *val)
> > > +{
> > > +    struct peci_dimmtemp *priv = dev_get_drvdata(dev);
> > > +    int ret;
> > > +
> > > +    ret = get_dimm_temp(priv, channel);
> > > +    if (ret)
> > > +        return ret;
> > > +
> > > +    switch (attr) {
> > > +    case hwmon_temp_input:
> > > +        *val = priv->temp[channel].value;
> > > +        break;
> > > +    case hwmon_temp_max:
> > > +        *val = priv->temp_max[channel];
> > > +        break;
> > > +    case hwmon_temp_crit:
> > > +        *val = priv->temp_crit[channel];
> > > +        break;
> > > +    default:
> > > +        ret = -EOPNOTSUPP;
> > > +        break;
> > > +    }
> > > +
> > > +    return ret;
> > > +}
> > > +
> > > +static umode_t dimmtemp_is_visible(const void *data,
> > > +                   enum hwmon_sensor_types type,
> > > +                   u32 attr, int channel)
> > > +{
> > > +    const struct peci_dimmtemp *priv = data;
> > > +
> > > +    if (priv->temp_config[channel] & BIT(attr) &&
> > > +        priv->dimm_mask & BIT(channel))
> > > +        return 0444;
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static const struct hwmon_ops dimmtemp_ops = {
> > > +    .is_visible = dimmtemp_is_visible,
> > > +    .read_string = dimmtemp_read_string,
> > > +    .read = dimmtemp_read,
> > > +};
> > > +
> > > +static int check_populated_dimms(struct peci_dimmtemp *priv)
> > > +{
> > > +    u32 chan_rank_max = priv->gen_info->chan_rank_max;
> > > +    u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
> > > +    int chan_rank, dimm_idx;
> > > +    u8  cfg_data[4];
> > > +
> > > +    for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
> > > +        int ret;
> > > +
> > > +        ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> > > +        if (ret) {
> > > +            priv->dimm_mask = 0;
> > > +            return ret;
> > > +        }
> > > +
> > > +        for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
> > > +            if (cfg_data[dimm_idx])
> > > +                priv->dimm_mask |= BIT(chan_rank *
> > > +                               dimm_idx_max +
> > > +                               dimm_idx);
> > > +    }
> > > +
> > > +    if (!priv->dimm_mask)
> > > +        return -EAGAIN;
> > > +
> > > +    dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n",
> > > priv->dimm_mask);
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int create_dimm_temp_info(struct peci_dimmtemp *priv)
> > > +{
> > > +    int ret, i, config_idx, channels;
> > > +    struct device *hwmon_dev;
> > > +
> > > +    ret = check_populated_dimms(priv);
> > > +    if (ret) {
> > > +        if (ret == -EAGAIN) {
> > > +            if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
> > > +                queue_delayed_work(priv->work_queue,
> > > +                           &priv->work_handler,
> > > +                         DIMM_MASK_CHECK_DELAY_JIFFIES);
> > > +                priv->retry_count++;
> > > +                dev_dbg(priv->dev,
> > > +                    "Deferred DIMM temp info creation\n");
> > > +            } else {
> > > +                dev_err(priv->dev,
> > > +                    "Timeout DIMM temp info creation\n");
> > > +                ret = -ETIMEDOUT;
> > > +            }
> > > +        }
> > > +
> > > +        return ret;
> > > +    }
> > > +
> > > +    channels = priv->gen_info->chan_rank_max *
> > > +           priv->gen_info->dimm_idx_max;
> > > +    for (i = 0, config_idx = 0; i < channels; i++)
> > > +        if (priv->dimm_mask & BIT(i))
> > > +            while (i >= config_idx)
> > > +                priv->temp_config[config_idx++] =
> > > +                    HWMON_T_LABEL | HWMON_T_INPUT |
> > > +                    HWMON_T_MAX | HWMON_T_CRIT;
> > > +
> > > +    priv->chip.ops = &dimmtemp_ops;
> > > +    priv->chip.info = priv->info;
> > > +
> > > +    priv->info[0] = &priv->temp_info;
> > > +
> > > +    priv->temp_info.type = hwmon_temp;
> > > +    priv->temp_info.config = priv->temp_config;
> > > +
> > > +    hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
> > > +                             priv->name,
> > > +                             priv,
> > > +                             &priv->chip,
> > > +                             NULL);
> > > +    ret = PTR_ERR_OR_ZERO(hwmon_dev);
> > > +    if (!ret)
> > > +        dev_dbg(priv->dev, "%s: sensor '%s'\n",
> > > +            dev_name(hwmon_dev), priv->name);
> > > +
> > 
> > Any chance to make this consistent with the other driver ?
> 
> Will change this to:
> 
> if (IS_ERR(hwmon_dev)) {
> 	dev_err(&priv->dev, "Failed to register hwmon device\n");
> 	return PTR_ERR(hwmon_dev);
> }
> 
> > > +    return ret;
> > > +}
> > > +
> > > +static void create_dimm_temp_info_delayed(struct work_struct *work)
> > > +{
> > > +    struct delayed_work *dwork = to_delayed_work(work);
> > > +    struct peci_dimmtemp *priv = container_of(dwork, struct
> > > peci_dimmtemp,
> > > +                          work_handler);
> > > +    int ret;
> > > +
> > > +    ret = create_dimm_temp_info(priv);
> > > +    if (ret && ret != -EAGAIN)
> > > +        dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
> > > +}
> > > +
> > > +static int peci_dimmtemp_probe(struct platform_device *pdev)
> > > +{
> > > +    struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
> > > +    struct device *dev = &pdev->dev;
> > > +    struct peci_dimmtemp *priv;
> > > +    int ret;
> > > +
> > > +    if ((mgr->client->adapter->cmd_mask &
> > > +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
> > > +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
> > > +        return -ENODEV;
> > > +
> > > +    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > > +    if (!priv)
> > > +        return -ENOMEM;
> > > +
> > > +    dev_set_drvdata(dev, priv);
> > > +    priv->mgr = mgr;
> > > +    priv->dev = dev;
> > > +    priv->gen_info = mgr->gen_info;
> > > +
> > > +    snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
> > > +         priv->mgr->client->addr - PECI_BASE_ADDR);
> > > +
> > > +    priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
> > > +    if (!priv->work_queue)
> > > +        return -ENOMEM;
> > > +
> > > +    INIT_DELAYED_WORK(&priv->work_handler,
> > > create_dimm_temp_info_delayed);
> > > +
> > > +    ret = create_dimm_temp_info(priv);
> > > +    if (ret && ret != -EAGAIN) {
> > > +        dev_err(dev, "Failed to create DIMM temp info\n");
> > 
> > Does this generate error messages if there are no DIMMS ?
> 
> Yes, this error message will be printed out once if it meets a timeout
> in DIMM scanning when there is no DIMM.
> 

Is that indeed an error, or are there situations where no DIMMs are
detected and that is a perfectly valid situation ? An error message
is only acceptable if this is indeed an error in all situations.

Guenter

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver
  2019-12-16 16:01     ` Lee Jones
@ 2019-12-16 21:57       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 21:57 UTC (permalink / raw)
  To: Lee Jones
  Cc: Rob Herring, Greg Kroah-Hartman, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	James Feist, Jason M Biils, Vernon Mauery

Hi Lee,

On 12/16/2019 8:01 AM, Lee Jones wrote:
> On Wed, 11 Dec 2019, Jae Hyun Yoo wrote:
> 
>> This commit adds Intel PECI client driver.
>>
>> Cc: Lee Jones <lee.jones@linaro.org>
>> Cc: Randy Dunlap <rdunlap@infradead.org>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: James Feist <james.feist@linux.intel.com>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Fixed minor style issues.
>>
>>   drivers/mfd/Kconfig                   |  17 +++
>>   drivers/mfd/Makefile                  |   1 +
>>   drivers/mfd/intel-peci-client.c       | 149 ++++++++++++++++++++++++++
>>   include/linux/mfd/intel-peci-client.h | 117 ++++++++++++++++++++
>>   4 files changed, 284 insertions(+)
>>   create mode 100644 drivers/mfd/intel-peci-client.c
>>   create mode 100644 include/linux/mfd/intel-peci-client.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 420900852166..7022e54a4703 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -632,6 +632,23 @@ config MFD_INTEL_MSIC
>>   	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
>>   	  devices used in Intel Medfield platforms.
>>   
>> +config MFD_INTEL_PECI_CLIENT
>> +	tristate "Intel PECI client"
>> +	depends on (PECI || COMPILE_TEST)
>> +	select MFD_CORE
>> +	help
>> +	  If you say yes to this option, support will be included for the
>> +	  Intel PECI (Platform Environment Control Interface) client. PECI is a
>> +	  one-wire bus interface that provides a communication channel from PECI
>> +	  clients in Intel processors and chipset components to external
>> +	  monitoring or control devices.
>> +
>> +	  Additional drivers must be enabled in order to use the functionality
>> +	  of the device.
>> +
>> +	  This driver can also be built as a module. If so, the module
>> +	  will be called intel-peci-client.
>> +
>>   config MFD_IPAQ_MICRO
>>   	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
>>   	depends on SA1100_H3100 || SA1100_H3600
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index aed99f08739f..91c6fda5cec6 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -211,6 +211,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
>>   obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
>>   obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
>>   obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
>> +obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
>>   obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
>>   obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
>>   obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
>> diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
>> new file mode 100644
>> index 000000000000..18bf0af0e09e
>> --- /dev/null
>> +++ b/drivers/mfd/intel-peci-client.c
>> @@ -0,0 +1,149 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2018-2019 Intel Corporation
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/mfd/intel-peci-client.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/peci.h>
>> +
>> +#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
>> +#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
>> +#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
>> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
>> +
>> +#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
>> +#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
>> +#define LOWER_BYTE_MASK        GENMASK(7, 0)
>> +#define UPPER_BYTE_MASK        GENMASK(16, 8)
>> +
>> +static struct mfd_cell peci_functions[] = {
>> +	{ .name = "peci-cputemp", },
>> +	{ .name = "peci-dimmtemp", },
>> +	/* TODO: Add additional PECI sideband functions into here */
> 
> No need for this comment.  It's implied.

I see. I'll remove this comment.

>> +};
>> +
>> +static const struct cpu_gen_info cpu_gen_info_table[] = {
>> +	{ /* Haswell Xeon */
>> +		.family        = 6, /* Family code */
> 
> Nit: Why don't you just define the number, instead of feeling the need
> to further clarify by providing a comment?

Makes sense. Will define the number.

>> +		.model         = INTEL_FAM6_HASWELL_X,
>> +		.core_max      = CORE_MAX_ON_HSX,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
>> +	{ /* Broadwell Xeon */
>> +		.family        = 6, /* Family code */
>> +		.model         = INTEL_FAM6_BROADWELL_X,
>> +		.core_max      = CORE_MAX_ON_BDX,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
>> +	{ /* Skylake Xeon */
>> +		.family        = 6, /* Family code */
>> +		.model         = INTEL_FAM6_SKYLAKE_X,
>> +		.core_max      = CORE_MAX_ON_SKX,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
>> +	{ /* Skylake Xeon D */
>> +		.family        = 6, /* Family code */
>> +		.model         = INTEL_FAM6_SKYLAKE_XD,
>> +		.core_max      = CORE_MAX_ON_SKXD,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKXD },
>> +};
>> +
>> +static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
>> +{
>> +	struct device *dev = &priv->client->dev;
>> +	u32 cpu_id;
>> +	u16 family;
>> +	u8 model;
>> +	int ret;
>> +	int i;
>> +
>> +	ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
>> +			      &cpu_id);
>> +	if (ret)
>> +		return ret;
>> +
>> +	family = FIELD_PREP(LOWER_BYTE_MASK,
>> +			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
>> +		 FIELD_PREP(UPPER_BYTE_MASK,
>> +			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
>> +	model = FIELD_PREP(LOWER_NIBBLE_MASK,
>> +			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
>> +		FIELD_PREP(UPPER_NIBBLE_MASK,
>> +			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
>> +
>> +	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
>> +		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
>> +
>> +		if (family == cpu_info->family && model == cpu_info->model) {
>> +			priv->gen_info = cpu_info;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (!priv->gen_info) {
>> +		dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
>> +		ret = -ENODEV;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int peci_client_probe(struct peci_client *client)
>> +{
>> +	struct device *dev = &client->dev;
>> +	struct peci_client_manager *priv;
>> +	uint cpu_no;
>> +	int ret;
>> +
>> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	dev_set_drvdata(dev, priv);
>> +	priv->client = client;
>> +	cpu_no = client->addr - PECI_BASE_ADDR;
>> +
>> +	ret = peci_client_get_cpu_gen_info(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
>> +				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to register child devices: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_OF)
> 
> #ifdef CONFIG_OF

Will fix it.

>> +static const struct of_device_id peci_client_of_table[] = {
>> +	{ .compatible = "intel,peci-client" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, peci_client_of_table);
>> +#endif /* CONFIG_OF */
> 
> Please remove this comment.  It doesn't provide anything here.

I see. I'll remove this comment.

>> +static const struct peci_device_id peci_client_ids[] = {
>> +	{ .name = "peci-client" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(peci, peci_client_ids);
> 
> Is this a requirement?  If so, why?
> 
> We're trying to get rid of unnecessary tables.
> 
> Please grep for "probe_new".

This is needed because peci-core.c provides sub tree searching while
registering a peci bus using of_match_device, also it provides
runtime registration through sysfs interface using ID match just like
I2C subsystem does. The probe member in 'struct peci_driver' is defined
as the new style like this:

struct peci_driver {
	int (*probe)(struct peci_client *client);
	[....]
}

>> +static struct peci_driver peci_client_driver = {
>> +	.probe    = peci_client_probe,
>> +	.id_table = peci_client_ids,
>> +	.driver   = {
>> +		.name           = KBUILD_MODNAME,
>> +		.of_match_table = of_match_ptr(peci_client_of_table),
>> +	},
>> +};
>> +module_peci_driver(peci_client_driver);
>> +
>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>> +MODULE_DESCRIPTION("PECI client driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
>> new file mode 100644
>> index 000000000000..9854303bbc26
>> --- /dev/null
>> +++ b/include/linux/mfd/intel-peci-client.h
>> @@ -0,0 +1,117 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright (c) 2018-2019 Intel Corporation */
>> +
>> +#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
>> +#define __LINUX_MFD_INTEL_PECI_CLIENT_H
>> +
>> +#include <linux/peci.h>
>> +
>> +#if IS_ENABLED(CONFIG_X86)
>> +#include <asm/intel-family.h>
>> +#else
>> +/*
>> + * Architectures other than x86 cannot include the header file so define these
>> + * at here. These are needed for detecting type of client x86 CPUs behind a PECI
>> + * connection.
>> + */
>> +#define INTEL_FAM6_HASWELL_X		0x3F
>> +#define INTEL_FAM6_BROADWELL_X		0x4F
>> +#define INTEL_FAM6_SKYLAKE_X		0x55
>> +#define INTEL_FAM6_SKYLAKE_XD		0x56
>> +#endif
>> +
>> +#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
>> +#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
>> +#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
>> +
>> +#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
>> +#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
>> +#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
>> +
>> +#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
>> +#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
>> +#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
>> +
>> +#define CORE_MAX_ON_SKXD       16 /* Max number of cores on Skylake D */
>> +#define CHAN_RANK_MAX_ON_SKXD  2  /* Max number of channel ranks on Skylake D */
>> +#define DIMM_IDX_MAX_ON_SKXD   2  /* Max DIMM index per channel on Skylake D */
>> +
>> +#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
>> +#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
>> +#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
>> +#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
>> +
>> +/**
>> + * struct cpu_gen_info - CPU generation specific information
>> + * @family: CPU family ID
>> + * @model: CPU model
>> + * @core_max: max number of cores
>> + * @chan_rank_max: max number of channel ranks
>> + * @dimm_idx_max: max number of DIMM indices
>> + *
>> + * CPU generation specific information to identify maximum number of cores and
>> + * DIMM slots.
>> + */
>> +struct cpu_gen_info {
>> +	u16  family;
>> +	u8   model;
>> +	uint core_max;
>> +	uint chan_rank_max;
>> +	uint dimm_idx_max;
>> +};
>> +
>> +/**
>> + * struct peci_client_manager - PECI client manager information
>> + * @client; pointer to the PECI client
>> + * @name: PECI client manager name
>> + * @gen_info: CPU generation info of the detected CPU
>> + *
>> + * PECI client manager information for managing PECI sideband functions on a CPU
>> + * client.
>> + */
>> +struct peci_client_manager {
>> +	struct peci_client *client;
>> +	char name[PECI_NAME_SIZE];
>> +	const struct cpu_gen_info *gen_info;
>> +};
>> +
>> +/**
>> + * peci_client_read_package_config - read from the Package Configuration Space
>> + * @priv: driver private data structure
>> + * @index: encoding index for the requested service
>> + * @param: parameter to specify the exact data being requested
>> + * @data: data buffer to store the result
>> + * Context: can sleep
>> + *
>> + * A generic PECI command that provides read access to the
>> + * "Package Configuration Space" that is maintained by the PCU, including
>> + * various power and thermal management functions. Typical PCS read services
>> + * supported by the processor may include access to temperature data, energy
>> + * status, run time information, DIMM temperatures and so on.
>> + *
>> + * Return: zero on success, else a negative error code.
>> + */
>> +static inline int
>> +peci_client_read_package_config(struct peci_client_manager *priv,
>> +				u8 index, u16 param, u8 *data)
>> +{
>> +	struct peci_rd_pkg_cfg_msg msg;
>> +	int ret;
>> +
>> +	msg.addr = priv->client->addr;
>> +	msg.index = index;
>> +	msg.param = param;
>> +	msg.rx_len = 4;
>> +
>> +	ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
>> +	if (msg.cc != PECI_DEV_CC_SUCCESS)
>> +		ret = -EAGAIN;
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(data, msg.pkg_config, 4);
>> +
>> +	return 0;
>> +}
> 
> Where is this function used?

This function is used in MFD cell drivers that are included in this
patch set, peci-cputemp and peci-dimmtemp drivers. I moved it from
intel-peci-client.c to this header file as you commented in the previous
review spin. I also thought about moving it into peci-hwmon.h but
this command macro is a generic thing for Intel CPUs so I moved into
here.

Thanks a lot for your review!

-Jae

>> +#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver
@ 2019-12-16 21:57       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 21:57 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Vernon Mauery, Frederic Barrat, Mauro Carvalho Chehab,
	Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
	openbmc, linux-doc, Kishon Vijay Abraham I, Joel Stanley,
	Bryant G . Ly, Uwe Kleine-Konig, David Kershner, Guenter Roeck,
	Wu Hao, linux-hwmon, Jean Delvare, Arnd Bergmann,
	Philippe Ombredanne, Johan Hovold, Tomohiro Kusumi, Rob Herring,
	Thomas Gleixner, Sagar Dharia, linux-arm-kernel, Juergen Gross,
	Cyrille Pitchen, Andrew Jeffery, Greg Kroah-Hartman,
	Darrick J . Wong, Stephen Boyd, Vinod Koul, James Feist,
	Gustavo Pimentel, Andrew Morton, David S . Miller

Hi Lee,

On 12/16/2019 8:01 AM, Lee Jones wrote:
> On Wed, 11 Dec 2019, Jae Hyun Yoo wrote:
> 
>> This commit adds Intel PECI client driver.
>>
>> Cc: Lee Jones <lee.jones@linaro.org>
>> Cc: Randy Dunlap <rdunlap@infradead.org>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: James Feist <james.feist@linux.intel.com>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Fixed minor style issues.
>>
>>   drivers/mfd/Kconfig                   |  17 +++
>>   drivers/mfd/Makefile                  |   1 +
>>   drivers/mfd/intel-peci-client.c       | 149 ++++++++++++++++++++++++++
>>   include/linux/mfd/intel-peci-client.h | 117 ++++++++++++++++++++
>>   4 files changed, 284 insertions(+)
>>   create mode 100644 drivers/mfd/intel-peci-client.c
>>   create mode 100644 include/linux/mfd/intel-peci-client.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 420900852166..7022e54a4703 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -632,6 +632,23 @@ config MFD_INTEL_MSIC
>>   	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
>>   	  devices used in Intel Medfield platforms.
>>   
>> +config MFD_INTEL_PECI_CLIENT
>> +	tristate "Intel PECI client"
>> +	depends on (PECI || COMPILE_TEST)
>> +	select MFD_CORE
>> +	help
>> +	  If you say yes to this option, support will be included for the
>> +	  Intel PECI (Platform Environment Control Interface) client. PECI is a
>> +	  one-wire bus interface that provides a communication channel from PECI
>> +	  clients in Intel processors and chipset components to external
>> +	  monitoring or control devices.
>> +
>> +	  Additional drivers must be enabled in order to use the functionality
>> +	  of the device.
>> +
>> +	  This driver can also be built as a module. If so, the module
>> +	  will be called intel-peci-client.
>> +
>>   config MFD_IPAQ_MICRO
>>   	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
>>   	depends on SA1100_H3100 || SA1100_H3600
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index aed99f08739f..91c6fda5cec6 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -211,6 +211,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
>>   obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
>>   obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
>>   obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
>> +obj-$(CONFIG_MFD_INTEL_PECI_CLIENT)	+= intel-peci-client.o
>>   obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
>>   obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
>>   obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
>> diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
>> new file mode 100644
>> index 000000000000..18bf0af0e09e
>> --- /dev/null
>> +++ b/drivers/mfd/intel-peci-client.c
>> @@ -0,0 +1,149 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2018-2019 Intel Corporation
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/mfd/intel-peci-client.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/peci.h>
>> +
>> +#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
>> +#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
>> +#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
>> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
>> +
>> +#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
>> +#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
>> +#define LOWER_BYTE_MASK        GENMASK(7, 0)
>> +#define UPPER_BYTE_MASK        GENMASK(16, 8)
>> +
>> +static struct mfd_cell peci_functions[] = {
>> +	{ .name = "peci-cputemp", },
>> +	{ .name = "peci-dimmtemp", },
>> +	/* TODO: Add additional PECI sideband functions into here */
> 
> No need for this comment.  It's implied.

I see. I'll remove this comment.

>> +};
>> +
>> +static const struct cpu_gen_info cpu_gen_info_table[] = {
>> +	{ /* Haswell Xeon */
>> +		.family        = 6, /* Family code */
> 
> Nit: Why don't you just define the number, instead of feeling the need
> to further clarify by providing a comment?

Makes sense. Will define the number.

>> +		.model         = INTEL_FAM6_HASWELL_X,
>> +		.core_max      = CORE_MAX_ON_HSX,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
>> +	{ /* Broadwell Xeon */
>> +		.family        = 6, /* Family code */
>> +		.model         = INTEL_FAM6_BROADWELL_X,
>> +		.core_max      = CORE_MAX_ON_BDX,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_BDX,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
>> +	{ /* Skylake Xeon */
>> +		.family        = 6, /* Family code */
>> +		.model         = INTEL_FAM6_SKYLAKE_X,
>> +		.core_max      = CORE_MAX_ON_SKX,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKX,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
>> +	{ /* Skylake Xeon D */
>> +		.family        = 6, /* Family code */
>> +		.model         = INTEL_FAM6_SKYLAKE_XD,
>> +		.core_max      = CORE_MAX_ON_SKXD,
>> +		.chan_rank_max = CHAN_RANK_MAX_ON_SKXD,
>> +		.dimm_idx_max  = DIMM_IDX_MAX_ON_SKXD },
>> +};
>> +
>> +static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv)
>> +{
>> +	struct device *dev = &priv->client->dev;
>> +	u32 cpu_id;
>> +	u16 family;
>> +	u8 model;
>> +	int ret;
>> +	int i;
>> +
>> +	ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr,
>> +			      &cpu_id);
>> +	if (ret)
>> +		return ret;
>> +
>> +	family = FIELD_PREP(LOWER_BYTE_MASK,
>> +			    FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) |
>> +		 FIELD_PREP(UPPER_BYTE_MASK,
>> +			    FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id));
>> +	model = FIELD_PREP(LOWER_NIBBLE_MASK,
>> +			   FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) |
>> +		FIELD_PREP(UPPER_NIBBLE_MASK,
>> +			   FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id));
>> +
>> +	for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
>> +		const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i];
>> +
>> +		if (family == cpu_info->family && model == cpu_info->model) {
>> +			priv->gen_info = cpu_info;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (!priv->gen_info) {
>> +		dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id);
>> +		ret = -ENODEV;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int peci_client_probe(struct peci_client *client)
>> +{
>> +	struct device *dev = &client->dev;
>> +	struct peci_client_manager *priv;
>> +	uint cpu_no;
>> +	int ret;
>> +
>> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	dev_set_drvdata(dev, priv);
>> +	priv->client = client;
>> +	cpu_no = client->addr - PECI_BASE_ADDR;
>> +
>> +	ret = peci_client_get_cpu_gen_info(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_mfd_add_devices(dev, cpu_no, peci_functions,
>> +				   ARRAY_SIZE(peci_functions), NULL, 0, NULL);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to register child devices: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_OF)
> 
> #ifdef CONFIG_OF

Will fix it.

>> +static const struct of_device_id peci_client_of_table[] = {
>> +	{ .compatible = "intel,peci-client" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, peci_client_of_table);
>> +#endif /* CONFIG_OF */
> 
> Please remove this comment.  It doesn't provide anything here.

I see. I'll remove this comment.

>> +static const struct peci_device_id peci_client_ids[] = {
>> +	{ .name = "peci-client" },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(peci, peci_client_ids);
> 
> Is this a requirement?  If so, why?
> 
> We're trying to get rid of unnecessary tables.
> 
> Please grep for "probe_new".

This is needed because peci-core.c provides sub tree searching while
registering a peci bus using of_match_device, also it provides
runtime registration through sysfs interface using ID match just like
I2C subsystem does. The probe member in 'struct peci_driver' is defined
as the new style like this:

struct peci_driver {
	int (*probe)(struct peci_client *client);
	[....]
}

>> +static struct peci_driver peci_client_driver = {
>> +	.probe    = peci_client_probe,
>> +	.id_table = peci_client_ids,
>> +	.driver   = {
>> +		.name           = KBUILD_MODNAME,
>> +		.of_match_table = of_match_ptr(peci_client_of_table),
>> +	},
>> +};
>> +module_peci_driver(peci_client_driver);
>> +
>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>> +MODULE_DESCRIPTION("PECI client driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
>> new file mode 100644
>> index 000000000000..9854303bbc26
>> --- /dev/null
>> +++ b/include/linux/mfd/intel-peci-client.h
>> @@ -0,0 +1,117 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright (c) 2018-2019 Intel Corporation */
>> +
>> +#ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H
>> +#define __LINUX_MFD_INTEL_PECI_CLIENT_H
>> +
>> +#include <linux/peci.h>
>> +
>> +#if IS_ENABLED(CONFIG_X86)
>> +#include <asm/intel-family.h>
>> +#else
>> +/*
>> + * Architectures other than x86 cannot include the header file so define these
>> + * at here. These are needed for detecting type of client x86 CPUs behind a PECI
>> + * connection.
>> + */
>> +#define INTEL_FAM6_HASWELL_X		0x3F
>> +#define INTEL_FAM6_BROADWELL_X		0x4F
>> +#define INTEL_FAM6_SKYLAKE_X		0x55
>> +#define INTEL_FAM6_SKYLAKE_XD		0x56
>> +#endif
>> +
>> +#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
>> +#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on Haswell */
>> +#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on Haswell */
>> +
>> +#define CORE_MAX_ON_BDX        24 /* Max number of cores on Broadwell */
>> +#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on Broadwell */
>> +#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on Broadwell */
>> +
>> +#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
>> +#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on Skylake */
>> +#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on Skylake */
>> +
>> +#define CORE_MAX_ON_SKXD       16 /* Max number of cores on Skylake D */
>> +#define CHAN_RANK_MAX_ON_SKXD  2  /* Max number of channel ranks on Skylake D */
>> +#define DIMM_IDX_MAX_ON_SKXD   2  /* Max DIMM index per channel on Skylake D */
>> +
>> +#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
>> +#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
>> +#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
>> +#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
>> +
>> +/**
>> + * struct cpu_gen_info - CPU generation specific information
>> + * @family: CPU family ID
>> + * @model: CPU model
>> + * @core_max: max number of cores
>> + * @chan_rank_max: max number of channel ranks
>> + * @dimm_idx_max: max number of DIMM indices
>> + *
>> + * CPU generation specific information to identify maximum number of cores and
>> + * DIMM slots.
>> + */
>> +struct cpu_gen_info {
>> +	u16  family;
>> +	u8   model;
>> +	uint core_max;
>> +	uint chan_rank_max;
>> +	uint dimm_idx_max;
>> +};
>> +
>> +/**
>> + * struct peci_client_manager - PECI client manager information
>> + * @client; pointer to the PECI client
>> + * @name: PECI client manager name
>> + * @gen_info: CPU generation info of the detected CPU
>> + *
>> + * PECI client manager information for managing PECI sideband functions on a CPU
>> + * client.
>> + */
>> +struct peci_client_manager {
>> +	struct peci_client *client;
>> +	char name[PECI_NAME_SIZE];
>> +	const struct cpu_gen_info *gen_info;
>> +};
>> +
>> +/**
>> + * peci_client_read_package_config - read from the Package Configuration Space
>> + * @priv: driver private data structure
>> + * @index: encoding index for the requested service
>> + * @param: parameter to specify the exact data being requested
>> + * @data: data buffer to store the result
>> + * Context: can sleep
>> + *
>> + * A generic PECI command that provides read access to the
>> + * "Package Configuration Space" that is maintained by the PCU, including
>> + * various power and thermal management functions. Typical PCS read services
>> + * supported by the processor may include access to temperature data, energy
>> + * status, run time information, DIMM temperatures and so on.
>> + *
>> + * Return: zero on success, else a negative error code.
>> + */
>> +static inline int
>> +peci_client_read_package_config(struct peci_client_manager *priv,
>> +				u8 index, u16 param, u8 *data)
>> +{
>> +	struct peci_rd_pkg_cfg_msg msg;
>> +	int ret;
>> +
>> +	msg.addr = priv->client->addr;
>> +	msg.index = index;
>> +	msg.param = param;
>> +	msg.rx_len = 4;
>> +
>> +	ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg);
>> +	if (msg.cc != PECI_DEV_CC_SUCCESS)
>> +		ret = -EAGAIN;
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(data, msg.pkg_config, 4);
>> +
>> +	return 0;
>> +}
> 
> Where is this function used?

This function is used in MFD cell drivers that are included in this
patch set, peci-cputemp and peci-dimmtemp drivers. I moved it from
intel-peci-client.c to this header file as you commented in the previous
review spin. I also thought about moving it into peci-hwmon.h but
this command macro is a generic thing for Intel CPUs so I moved into
here.

Thanks a lot for your review!

-Jae

>> +#endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-16 21:21         ` Guenter Roeck
@ 2019-12-16 22:17           ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 22:17 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

[...]

>>>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
>>>> +{
>>>> +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
>>>> +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
>>>> +    struct peci_rd_pci_cfg_local_msg rp_msg;
>>>> +    u8  cfg_data[4];
>>>> +    int ret;
>>>> +
>>>> +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
>>>> +        return 0;
>>>> +
>>>> +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
>>>> +
>>>> +    switch (priv->gen_info->model) {
>>>> +    case INTEL_FAM6_SKYLAKE_X:
>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>> +        rp_msg.bus = 2;
>>>> +        /*
>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>> +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
>>>> +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
>>>> +         */
>>>> +        rp_msg.device = 10 + chan_rank / 3 * 2 +
>>>> +                 (chan_rank % 3 == 2 ? 1 : 0);
>>>> +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>> +        rp_msg.rx_len = 4;
>>>> +
>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>> +            ret = -EAGAIN;
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>> +        break;
>>>> +    case INTEL_FAM6_SKYLAKE_XD:
>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>> +        rp_msg.bus = 2;
>>>> +        /*
>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
>>>> +         */
>>>> +        rp_msg.device = 10 + chan_rank / 2 * 2;
>>>> +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>> +        rp_msg.rx_len = 4;
>>>> +
>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>> +            ret = -EAGAIN;
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>> +        break;
>>>> +    case INTEL_FAM6_HASWELL_X:
>>>> +    case INTEL_FAM6_BROADWELL_X:
>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>> +        rp_msg.bus = 1;
>>>> +        /*
>>>> +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
>>>> +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
>>>> +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
>>>> +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
>>>> +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
>>>> +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
>>>> +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
>>>> +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
>>>> +         */
>>>> +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
>>>> +        rp_msg.function = chan_rank % 2;
>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>> +        rp_msg.rx_len = 4;
>>>> +
>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>> +            ret = -EAGAIN;
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>> +        break;
>>>> +    default:
>>>> +        return -EOPNOTSUPP;
>>>
>>> It looks like the sensors are created even on unsupported platforms,
>>> which would generate error messages whenever someone tries to read
>>> the attributes.
>>>
>>> There should be some code early on checking this, and the driver
>>> should not even instantiate if the CPU model is not supported.
>>
>> Actually, this 'default' case will not be happened because this driver
>> will be registered only when the CPU model is supported. The CPU model
>> checking code is in 'intel-peci-client.c' which is [11/14] of this
>> patch set.
>>
> 
> That again assumes that both drivers will be modified in sync in the future.
> We can not make that assumption.

As you said, both drivers must be modified in sync in the future because
each Intel CPU family uses different way of reading DIMM temperature.
In case if supported CPU checking code updated without making sync with
it, this driver will return the error.

[...]

>>>> +    ret = create_dimm_temp_info(priv);
>>>> +    if (ret && ret != -EAGAIN) {
>>>> +        dev_err(dev, "Failed to create DIMM temp info\n");
>>>
>>> Does this generate error messages if there are no DIMMS ?
>>
>> Yes, this error message will be printed out once if it meets a timeout
>> in DIMM scanning when there is no DIMM.
>>
> 
> Is that indeed an error, or are there situations where no DIMMs are
> detected and that is a perfectly valid situation ? An error message
> is only acceptable if this is indeed an error in all situations.

If a machine under monitoring has two Intel CPUs installed but only one
CPU has a DIMM, it's also an working configuration although it's an
unusual H/W configuration. I'll fix that to dbg printing.

Thanks,

Jae

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-16 22:17           ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 22:17 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mark Rutland, Andrew Lunn, Randy Dunlap, Tomer Maimon,
	devicetree, Frederic Barrat, Mauro Carvalho Chehab, Lee Jones,
	Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
	openbmc, linux-doc, Kishon Vijay Abraham I, Joel Stanley,
	Bryant G . Ly, Uwe Kleine-Konig, David Kershner, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Stef van Os,
	Thomas Gleixner, Andy Shevchenko, Sagar Dharia, linux-arm-kernel,
	Alan Cox, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Miguel Ojeda,
	Vinod Koul, Gustavo Pimentel, Andrew Morton, David S . Miller

[...]

>>>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
>>>> +{
>>>> +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
>>>> +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
>>>> +    struct peci_rd_pci_cfg_local_msg rp_msg;
>>>> +    u8  cfg_data[4];
>>>> +    int ret;
>>>> +
>>>> +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
>>>> +        return 0;
>>>> +
>>>> +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
>>>> +
>>>> +    switch (priv->gen_info->model) {
>>>> +    case INTEL_FAM6_SKYLAKE_X:
>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>> +        rp_msg.bus = 2;
>>>> +        /*
>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>> +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
>>>> +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
>>>> +         */
>>>> +        rp_msg.device = 10 + chan_rank / 3 * 2 +
>>>> +                 (chan_rank % 3 == 2 ? 1 : 0);
>>>> +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>> +        rp_msg.rx_len = 4;
>>>> +
>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>> +            ret = -EAGAIN;
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>> +        break;
>>>> +    case INTEL_FAM6_SKYLAKE_XD:
>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>> +        rp_msg.bus = 2;
>>>> +        /*
>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
>>>> +         */
>>>> +        rp_msg.device = 10 + chan_rank / 2 * 2;
>>>> +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>> +        rp_msg.rx_len = 4;
>>>> +
>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>> +            ret = -EAGAIN;
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>> +        break;
>>>> +    case INTEL_FAM6_HASWELL_X:
>>>> +    case INTEL_FAM6_BROADWELL_X:
>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>> +        rp_msg.bus = 1;
>>>> +        /*
>>>> +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
>>>> +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
>>>> +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
>>>> +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
>>>> +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
>>>> +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
>>>> +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
>>>> +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
>>>> +         */
>>>> +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
>>>> +        rp_msg.function = chan_rank % 2;
>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>> +        rp_msg.rx_len = 4;
>>>> +
>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>> +            ret = -EAGAIN;
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>> +        break;
>>>> +    default:
>>>> +        return -EOPNOTSUPP;
>>>
>>> It looks like the sensors are created even on unsupported platforms,
>>> which would generate error messages whenever someone tries to read
>>> the attributes.
>>>
>>> There should be some code early on checking this, and the driver
>>> should not even instantiate if the CPU model is not supported.
>>
>> Actually, this 'default' case will not be happened because this driver
>> will be registered only when the CPU model is supported. The CPU model
>> checking code is in 'intel-peci-client.c' which is [11/14] of this
>> patch set.
>>
> 
> That again assumes that both drivers will be modified in sync in the future.
> We can not make that assumption.

As you said, both drivers must be modified in sync in the future because
each Intel CPU family uses different way of reading DIMM temperature.
In case if supported CPU checking code updated without making sync with
it, this driver will return the error.

[...]

>>>> +    ret = create_dimm_temp_info(priv);
>>>> +    if (ret && ret != -EAGAIN) {
>>>> +        dev_err(dev, "Failed to create DIMM temp info\n");
>>>
>>> Does this generate error messages if there are no DIMMS ?
>>
>> Yes, this error message will be printed out once if it meets a timeout
>> in DIMM scanning when there is no DIMM.
>>
> 
> Is that indeed an error, or are there situations where no DIMMs are
> detected and that is a perfectly valid situation ? An error message
> is only acceptable if this is indeed an error in all situations.

If a machine under monitoring has two Intel CPUs installed but only one
CPU has a DIMM, it's also an working configuration although it's an
unusual H/W configuration. I'll fix that to dbg printing.

Thanks,

Jae

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-16 22:17           ` Jae Hyun Yoo
@ 2019-12-16 23:27             ` Guenter Roeck
  -1 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-16 23:27 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

On Mon, Dec 16, 2019 at 02:17:34PM -0800, Jae Hyun Yoo wrote:
> [...]
> 
> > > > > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
> > > > > +{
> > > > > +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
> > > > > +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
> > > > > +    struct peci_rd_pci_cfg_local_msg rp_msg;
> > > > > +    u8  cfg_data[4];
> > > > > +    int ret;
> > > > > +
> > > > > +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
> > > > > +        return 0;
> > > > > +
> > > > > +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> > > > > +    if (ret)
> > > > > +        return ret;
> > > > > +
> > > > > +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
> > > > > +
> > > > > +    switch (priv->gen_info->model) {
> > > > > +    case INTEL_FAM6_SKYLAKE_X:
> > > > > +        rp_msg.addr = priv->mgr->client->addr;
> > > > > +        rp_msg.bus = 2;
> > > > > +        /*
> > > > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > > > +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
> > > > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
> > > > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
> > > > > +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
> > > > > +         */
> > > > > +        rp_msg.device = 10 + chan_rank / 3 * 2 +
> > > > > +                 (chan_rank % 3 == 2 ? 1 : 0);
> > > > > +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
> > > > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > > > +        rp_msg.rx_len = 4;
> > > > > +
> > > > > +        ret = peci_command(priv->mgr->client->adapter,
> > > > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > > > +            ret = -EAGAIN;
> > > > > +        if (ret)
> > > > > +            return ret;
> > > > > +
> > > > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > > > +        break;
> > > > > +    case INTEL_FAM6_SKYLAKE_XD:
> > > > > +        rp_msg.addr = priv->mgr->client->addr;
> > > > > +        rp_msg.bus = 2;
> > > > > +        /*
> > > > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
> > > > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
> > > > > +         */
> > > > > +        rp_msg.device = 10 + chan_rank / 2 * 2;
> > > > > +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
> > > > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > > > +        rp_msg.rx_len = 4;
> > > > > +
> > > > > +        ret = peci_command(priv->mgr->client->adapter,
> > > > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > > > +            ret = -EAGAIN;
> > > > > +        if (ret)
> > > > > +            return ret;
> > > > > +
> > > > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > > > +        break;
> > > > > +    case INTEL_FAM6_HASWELL_X:
> > > > > +    case INTEL_FAM6_BROADWELL_X:
> > > > > +        rp_msg.addr = priv->mgr->client->addr;
> > > > > +        rp_msg.bus = 1;
> > > > > +        /*
> > > > > +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
> > > > > +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
> > > > > +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
> > > > > +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
> > > > > +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
> > > > > +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
> > > > > +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
> > > > > +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
> > > > > +         */
> > > > > +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
> > > > > +        rp_msg.function = chan_rank % 2;
> > > > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > > > +        rp_msg.rx_len = 4;
> > > > > +
> > > > > +        ret = peci_command(priv->mgr->client->adapter,
> > > > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > > > +            ret = -EAGAIN;
> > > > > +        if (ret)
> > > > > +            return ret;
> > > > > +
> > > > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > > > +        break;
> > > > > +    default:
> > > > > +        return -EOPNOTSUPP;
> > > > 
> > > > It looks like the sensors are created even on unsupported platforms,
> > > > which would generate error messages whenever someone tries to read
> > > > the attributes.
> > > > 
> > > > There should be some code early on checking this, and the driver
> > > > should not even instantiate if the CPU model is not supported.
> > > 
> > > Actually, this 'default' case will not be happened because this driver
> > > will be registered only when the CPU model is supported. The CPU model
> > > checking code is in 'intel-peci-client.c' which is [11/14] of this
> > > patch set.
> > > 
> > 
> > That again assumes that both drivers will be modified in sync in the future.
> > We can not make that assumption.
> 
> As you said, both drivers must be modified in sync in the future because
> each Intel CPU family uses different way of reading DIMM temperature.
> In case if supported CPU checking code updated without making sync with
> it, this driver will return the error.
> 

... and in that situation the driver should not instantiate in the
first place. Its probe function should return -ENODEV.

Guenter

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-16 23:27             ` Guenter Roeck
  0 siblings, 0 replies; 71+ messages in thread
From: Guenter Roeck @ 2019-12-16 23:27 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Andrew Lunn, Randy Dunlap, Tomer Maimon,
	devicetree, Frederic Barrat, Mauro Carvalho Chehab, Lee Jones,
	Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
	openbmc, linux-doc, Kishon Vijay Abraham I, Joel Stanley,
	Bryant G . Ly, Uwe Kleine-Konig, David Kershner, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Stef van Os,
	Thomas Gleixner, Andy Shevchenko, Sagar Dharia, linux-arm-kernel,
	Alan Cox, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Miguel Ojeda,
	Vinod Koul, Gustavo Pimentel, Andrew Morton, David S . Miller

On Mon, Dec 16, 2019 at 02:17:34PM -0800, Jae Hyun Yoo wrote:
> [...]
> 
> > > > > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
> > > > > +{
> > > > > +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
> > > > > +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
> > > > > +    struct peci_rd_pci_cfg_local_msg rp_msg;
> > > > > +    u8  cfg_data[4];
> > > > > +    int ret;
> > > > > +
> > > > > +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
> > > > > +        return 0;
> > > > > +
> > > > > +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
> > > > > +    if (ret)
> > > > > +        return ret;
> > > > > +
> > > > > +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
> > > > > +
> > > > > +    switch (priv->gen_info->model) {
> > > > > +    case INTEL_FAM6_SKYLAKE_X:
> > > > > +        rp_msg.addr = priv->mgr->client->addr;
> > > > > +        rp_msg.bus = 2;
> > > > > +        /*
> > > > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > > > +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
> > > > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
> > > > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
> > > > > +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
> > > > > +         */
> > > > > +        rp_msg.device = 10 + chan_rank / 3 * 2 +
> > > > > +                 (chan_rank % 3 == 2 ? 1 : 0);
> > > > > +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
> > > > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > > > +        rp_msg.rx_len = 4;
> > > > > +
> > > > > +        ret = peci_command(priv->mgr->client->adapter,
> > > > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > > > +            ret = -EAGAIN;
> > > > > +        if (ret)
> > > > > +            return ret;
> > > > > +
> > > > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > > > +        break;
> > > > > +    case INTEL_FAM6_SKYLAKE_XD:
> > > > > +        rp_msg.addr = priv->mgr->client->addr;
> > > > > +        rp_msg.bus = 2;
> > > > > +        /*
> > > > > +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
> > > > > +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
> > > > > +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
> > > > > +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
> > > > > +         */
> > > > > +        rp_msg.device = 10 + chan_rank / 2 * 2;
> > > > > +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
> > > > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > > > +        rp_msg.rx_len = 4;
> > > > > +
> > > > > +        ret = peci_command(priv->mgr->client->adapter,
> > > > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > > > +            ret = -EAGAIN;
> > > > > +        if (ret)
> > > > > +            return ret;
> > > > > +
> > > > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > > > +        break;
> > > > > +    case INTEL_FAM6_HASWELL_X:
> > > > > +    case INTEL_FAM6_BROADWELL_X:
> > > > > +        rp_msg.addr = priv->mgr->client->addr;
> > > > > +        rp_msg.bus = 1;
> > > > > +        /*
> > > > > +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
> > > > > +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
> > > > > +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
> > > > > +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
> > > > > +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
> > > > > +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
> > > > > +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
> > > > > +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
> > > > > +         */
> > > > > +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
> > > > > +        rp_msg.function = chan_rank % 2;
> > > > > +        rp_msg.reg = 0x120 + dimm_order * 4;
> > > > > +        rp_msg.rx_len = 4;
> > > > > +
> > > > > +        ret = peci_command(priv->mgr->client->adapter,
> > > > > +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
> > > > > +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
> > > > > +            ret = -EAGAIN;
> > > > > +        if (ret)
> > > > > +            return ret;
> > > > > +
> > > > > +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
> > > > > +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
> > > > > +        break;
> > > > > +    default:
> > > > > +        return -EOPNOTSUPP;
> > > > 
> > > > It looks like the sensors are created even on unsupported platforms,
> > > > which would generate error messages whenever someone tries to read
> > > > the attributes.
> > > > 
> > > > There should be some code early on checking this, and the driver
> > > > should not even instantiate if the CPU model is not supported.
> > > 
> > > Actually, this 'default' case will not be happened because this driver
> > > will be registered only when the CPU model is supported. The CPU model
> > > checking code is in 'intel-peci-client.c' which is [11/14] of this
> > > patch set.
> > > 
> > 
> > That again assumes that both drivers will be modified in sync in the future.
> > We can not make that assumption.
> 
> As you said, both drivers must be modified in sync in the future because
> each Intel CPU family uses different way of reading DIMM temperature.
> In case if supported CPU checking code updated without making sync with
> it, this driver will return the error.
> 

... and in that situation the driver should not instantiate in the
first place. Its probe function should return -ENODEV.

Guenter

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
  2019-12-16 23:27             ` Guenter Roeck
@ 2019-12-16 23:31               ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 23:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Rob Herring, Greg Kroah-Hartman, Lee Jones, Jean Delvare,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Alan Cox, Andy Shevchenko, Jason M Biils, Miguel Ojeda,
	Andrew Lunn, Stef van Os

On 12/16/2019 3:27 PM, Guenter Roeck wrote:
> On Mon, Dec 16, 2019 at 02:17:34PM -0800, Jae Hyun Yoo wrote:
>> [...]
>>
>>>>>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
>>>>>> +{
>>>>>> +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
>>>>>> +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
>>>>>> +    struct peci_rd_pci_cfg_local_msg rp_msg;
>>>>>> +    u8  cfg_data[4];
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
>>>>>> +        return 0;
>>>>>> +
>>>>>> +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>> +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
>>>>>> +
>>>>>> +    switch (priv->gen_info->model) {
>>>>>> +    case INTEL_FAM6_SKYLAKE_X:
>>>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>>>> +        rp_msg.bus = 2;
>>>>>> +        /*
>>>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>>>> +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
>>>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
>>>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
>>>>>> +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
>>>>>> +         */
>>>>>> +        rp_msg.device = 10 + chan_rank / 3 * 2 +
>>>>>> +                 (chan_rank % 3 == 2 ? 1 : 0);
>>>>>> +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
>>>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>>>> +        rp_msg.rx_len = 4;
>>>>>> +
>>>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>>>> +            ret = -EAGAIN;
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>> +
>>>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>>>> +        break;
>>>>>> +    case INTEL_FAM6_SKYLAKE_XD:
>>>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>>>> +        rp_msg.bus = 2;
>>>>>> +        /*
>>>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
>>>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
>>>>>> +         */
>>>>>> +        rp_msg.device = 10 + chan_rank / 2 * 2;
>>>>>> +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
>>>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>>>> +        rp_msg.rx_len = 4;
>>>>>> +
>>>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>>>> +            ret = -EAGAIN;
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>> +
>>>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>>>> +        break;
>>>>>> +    case INTEL_FAM6_HASWELL_X:
>>>>>> +    case INTEL_FAM6_BROADWELL_X:
>>>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>>>> +        rp_msg.bus = 1;
>>>>>> +        /*
>>>>>> +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
>>>>>> +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
>>>>>> +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
>>>>>> +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
>>>>>> +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
>>>>>> +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
>>>>>> +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
>>>>>> +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
>>>>>> +         */
>>>>>> +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
>>>>>> +        rp_msg.function = chan_rank % 2;
>>>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>>>> +        rp_msg.rx_len = 4;
>>>>>> +
>>>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>>>> +            ret = -EAGAIN;
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>> +
>>>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>>>> +        break;
>>>>>> +    default:
>>>>>> +        return -EOPNOTSUPP;
>>>>>
>>>>> It looks like the sensors are created even on unsupported platforms,
>>>>> which would generate error messages whenever someone tries to read
>>>>> the attributes.
>>>>>
>>>>> There should be some code early on checking this, and the driver
>>>>> should not even instantiate if the CPU model is not supported.
>>>>
>>>> Actually, this 'default' case will not be happened because this driver
>>>> will be registered only when the CPU model is supported. The CPU model
>>>> checking code is in 'intel-peci-client.c' which is [11/14] of this
>>>> patch set.
>>>>
>>>
>>> That again assumes that both drivers will be modified in sync in the future.
>>> We can not make that assumption.
>>
>> As you said, both drivers must be modified in sync in the future because
>> each Intel CPU family uses different way of reading DIMM temperature.
>> In case if supported CPU checking code updated without making sync with
>> it, this driver will return the error.
>>
> 
> ... and in that situation the driver should not instantiate in the
> first place. Its probe function should return -ENODEV.

Got the point. I'll add the checking code even in this driver module
too.

Thanks a lot!

-Jae

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver
@ 2019-12-16 23:31               ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-16 23:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mark Rutland, Andrew Lunn, Randy Dunlap, Tomer Maimon,
	devicetree, Frederic Barrat, Mauro Carvalho Chehab, Lee Jones,
	Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
	openbmc, linux-doc, Kishon Vijay Abraham I, Joel Stanley,
	Bryant G . Ly, Uwe Kleine-Konig, David Kershner, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Rob Herring, Stef van Os,
	Thomas Gleixner, Andy Shevchenko, Sagar Dharia, linux-arm-kernel,
	Alan Cox, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Miguel Ojeda,
	Vinod Koul, Gustavo Pimentel, Andrew Morton, David S . Miller

On 12/16/2019 3:27 PM, Guenter Roeck wrote:
> On Mon, Dec 16, 2019 at 02:17:34PM -0800, Jae Hyun Yoo wrote:
>> [...]
>>
>>>>>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
>>>>>> +{
>>>>>> +    int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
>>>>>> +    int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
>>>>>> +    struct peci_rd_pci_cfg_local_msg rp_msg;
>>>>>> +    u8  cfg_data[4];
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    if (!peci_sensor_need_update(&priv->temp[dimm_no]))
>>>>>> +        return 0;
>>>>>> +
>>>>>> +    ret = read_ddr_dimm_temp_config(priv, chan_rank, cfg_data);
>>>>>> +    if (ret)
>>>>>> +        return ret;
>>>>>> +
>>>>>> +    priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
>>>>>> +
>>>>>> +    switch (priv->gen_info->model) {
>>>>>> +    case INTEL_FAM6_SKYLAKE_X:
>>>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>>>> +        rp_msg.bus = 2;
>>>>>> +        /*
>>>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>>>> +         * Device 11, Function 2: IMC 0 channel 2 -> rank 2
>>>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 3
>>>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 4
>>>>>> +         * Device 13, Function 2: IMC 1 channel 2 -> rank 5
>>>>>> +         */
>>>>>> +        rp_msg.device = 10 + chan_rank / 3 * 2 +
>>>>>> +                 (chan_rank % 3 == 2 ? 1 : 0);
>>>>>> +        rp_msg.function = chan_rank % 3 == 1 ? 6 : 2;
>>>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>>>> +        rp_msg.rx_len = 4;
>>>>>> +
>>>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>>>> +            ret = -EAGAIN;
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>> +
>>>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>>>> +        break;
>>>>>> +    case INTEL_FAM6_SKYLAKE_XD:
>>>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>>>> +        rp_msg.bus = 2;
>>>>>> +        /*
>>>>>> +         * Device 10, Function 2: IMC 0 channel 0 -> rank 0
>>>>>> +         * Device 10, Function 6: IMC 0 channel 1 -> rank 1
>>>>>> +         * Device 12, Function 2: IMC 1 channel 0 -> rank 2
>>>>>> +         * Device 12, Function 6: IMC 1 channel 1 -> rank 3
>>>>>> +         */
>>>>>> +        rp_msg.device = 10 + chan_rank / 2 * 2;
>>>>>> +        rp_msg.function = (chan_rank % 2) ? 6 : 2;
>>>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>>>> +        rp_msg.rx_len = 4;
>>>>>> +
>>>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>>>> +            ret = -EAGAIN;
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>> +
>>>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>>>> +        break;
>>>>>> +    case INTEL_FAM6_HASWELL_X:
>>>>>> +    case INTEL_FAM6_BROADWELL_X:
>>>>>> +        rp_msg.addr = priv->mgr->client->addr;
>>>>>> +        rp_msg.bus = 1;
>>>>>> +        /*
>>>>>> +         * Device 20, Function 0: IMC 0 channel 0 -> rank 0
>>>>>> +         * Device 20, Function 1: IMC 0 channel 1 -> rank 1
>>>>>> +         * Device 21, Function 0: IMC 0 channel 2 -> rank 2
>>>>>> +         * Device 21, Function 1: IMC 0 channel 3 -> rank 3
>>>>>> +         * Device 23, Function 0: IMC 1 channel 0 -> rank 4
>>>>>> +         * Device 23, Function 1: IMC 1 channel 1 -> rank 5
>>>>>> +         * Device 24, Function 0: IMC 1 channel 2 -> rank 6
>>>>>> +         * Device 24, Function 1: IMC 1 channel 3 -> rank 7
>>>>>> +         */
>>>>>> +        rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4;
>>>>>> +        rp_msg.function = chan_rank % 2;
>>>>>> +        rp_msg.reg = 0x120 + dimm_order * 4;
>>>>>> +        rp_msg.rx_len = 4;
>>>>>> +
>>>>>> +        ret = peci_command(priv->mgr->client->adapter,
>>>>>> +                   PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg);
>>>>>> +        if (rp_msg.cc != PECI_DEV_CC_SUCCESS)
>>>>>> +            ret = -EAGAIN;
>>>>>> +        if (ret)
>>>>>> +            return ret;
>>>>>> +
>>>>>> +        priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000;
>>>>>> +        priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000;
>>>>>> +        break;
>>>>>> +    default:
>>>>>> +        return -EOPNOTSUPP;
>>>>>
>>>>> It looks like the sensors are created even on unsupported platforms,
>>>>> which would generate error messages whenever someone tries to read
>>>>> the attributes.
>>>>>
>>>>> There should be some code early on checking this, and the driver
>>>>> should not even instantiate if the CPU model is not supported.
>>>>
>>>> Actually, this 'default' case will not be happened because this driver
>>>> will be registered only when the CPU model is supported. The CPU model
>>>> checking code is in 'intel-peci-client.c' which is [11/14] of this
>>>> patch set.
>>>>
>>>
>>> That again assumes that both drivers will be modified in sync in the future.
>>> We can not make that assumption.
>>
>> As you said, both drivers must be modified in sync in the future because
>> each Intel CPU family uses different way of reading DIMM temperature.
>> In case if supported CPU checking code updated without making sync with
>> it, this driver will return the error.
>>
> 
> ... and in that situation the driver should not instantiate in the
> first place. Its probe function should return -ENODEV.

Got the point. I'll add the checking code even in this driver module
too.

Thanks a lot!

-Jae

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 01/14] dt-bindings: Add PECI subsystem document
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-18  2:52     ` Rob Herring
  -1 siblings, 0 replies; 71+ messages in thread
From: Rob Herring @ 2019-12-18  2:52 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Greg Kroah-Hartman, Lee Jones, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc

On Wed, Dec 11, 2019 at 11:46:11AM -0800, Jae Hyun Yoo wrote:
> This commit adds PECI subsystem document.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Changed documents format to DT schema format so I dropped all review tags.
>   Please review it again.
> 
>  .../devicetree/bindings/peci/peci-bus.yaml    | 129 ++++++++++++++++++
>  .../devicetree/bindings/peci/peci-client.yaml |  54 ++++++++
>  2 files changed, 183 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml
> 
> diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
> new file mode 100644
> index 000000000000..b085e67089cf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
> @@ -0,0 +1,129 @@
> +# SPDX-License-Identifier: GPL-2.0

Dual license new bindings please:

(GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic Device Tree Bindings for PECI bus
> +
> +maintainers:
> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> +
> +description: |
> +  PECI (Platform Environment Control Interface) is a one-wire bus interface that
> +  provides a communication channel from Intel processors and chipset components
> +  to external monitoring or control devices. PECI is designed to support the
> +  following sideband functions:
> +
> +  * Processor and DRAM thermal management
> +    - Processor fan speed control is managed by comparing Digital Thermal
> +      Sensor (DTS) thermal readings acquired via PECI against the
> +      processor-specific fan speed control reference point, or TCONTROL. Both
> +      TCONTROL and DTS thermal readings are accessible via the processor PECI
> +      client. These variables are referenced to a common temperature, the TCC
> +      activation point, and are both defined as negative offsets from that
> +      reference.
> +    - PECI based access to the processor package configuration space provides
> +      a means for Baseboard Management Controllers (BMC) or other platform
> +      management devices to actively manage the processor and memory power
> +      and thermal features.
> +
> +  * Platform Manageability
> +    - Platform manageability functions including thermal, power, and error
> +      monitoring. Note that platform 'power' management includes monitoring
> +      and control for both the processor and DRAM subsystem to assist with
> +      data center power limiting.
> +    - PECI allows read access to certain error registers in the processor MSR
> +      space and status monitoring registers in the PCI configuration space
> +      within the processor and downstream devices.
> +    - PECI permits writes to certain registers in the processor PCI
> +      configuration space.
> +
> +  * Processor Interface Tuning and Diagnostics
> +    - Processor interface tuning and diagnostics capabilities
> +      (Intel Interconnect BIST). The processors Intel Interconnect Built In
> +      Self Test (Intel IBIST) allows for infield diagnostic capabilities in
> +      the Intel UPI and memory controller interfaces. PECI provides a port to
> +      execute these diagnostics via its PCI Configuration read and write
> +      capabilities.
> +
> +  * Failure Analysis
> +    - Output the state of the processor after a failure for analysis via
> +      Crashdump.
> +
> +  PECI uses a single wire for self-clocking and data transfer. The bus
> +  requires no additional control lines. The physical layer is a self-clocked
> +  one-wire bus that begins each bit with a driven, rising edge from an idle
> +  level near zero volts. The duration of the signal driven high depends on
> +  whether the bit value is a logic '0' or logic '1'. PECI also includes
> +  variable data transfer rate established with every message. In this way, it
> +  is highly flexible even though underlying logic is simple.
> +
> +  The interface design was optimized for interfacing between an Intel
> +  processor and chipset components in both single processor and multiple
> +  processor environments. The single wire interface provides low board
> +  routing overhead for the multiple load connections in the congested routing
> +  area near the processor and chipset components. Bus speed, error checking,
> +  and low protocol overhead provides adequate link bandwidth and reliability
> +  to transfer critical device operating conditions and configuration
> +  information.
> +
> +  PECI subsystem provides single or multiple bus nodes support so each bus can
> +  have one adapter node and multiple device specific client nodes that can be
> +  attached to the PECI bus so each processor client's features can be supported
> +  by the client node through an adapter connection in the bus.
> +
> +properties:
> +  compatible:
> +    const: simple-bus

This is wrong. We already have a schema for this.

What's needed is a peci-bus schema that defines the bus node structure 
and then schemas for the specific controllers and child devices. See 
i2c-controller.yaml for an example.

> +
> +  "#address-cells":
> +    # Required to define bus device control resource address.
> +    const: 1
> +
> +  "#size-cells":
> +    # Required to define bus device control resource address.
> +    const: 1
> +
> +  ranges: true
> +
> +required:
> +  - compatible
> +  - "#address-cells"
> +  - "#size-cells"
> +  - ranges
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/ast2600-clock.h>
> +    peci: bus@1e78b000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x1e78b000 0x200>;
> +
> +        peci0: peci-bus@0 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x0 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +        };
> +
> +        // Just an example. ast2600 doesn't have a second PECI module actually.
> +        peci1: peci-bus@100 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x100 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +        };
> +    };
> +...
> diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
> new file mode 100644
> index 000000000000..fc7c4110e929
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
> @@ -0,0 +1,54 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-client.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic Device Tree Bindings for PECI clients
> +
> +maintainers:
> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - intel,peci-client
> +
> +  reg:
> +    description: |
> +      Address of a client CPU. According to the PECI specification, client
> +      addresses start from 0x30.

0x30 being the min should be a constraint in the bus schema.

> +    maxItems: 1
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/ast2600-clock.h>
> +    peci: bus@1e78b000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x1e78b000 0x60>;
> +
> +        peci0: peci-bus@0 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x0 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +
> +            peci-client@30 {
> +                compatible = "intel,peci-client";
> +                reg = <0x30>;
> +            };
> +
> +            peci-client@31 {
> +                compatible = "intel,peci-client";
> +                reg = <0x31>;
> +            };
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 01/14] dt-bindings: Add PECI subsystem document
@ 2019-12-18  2:52     ` Rob Herring
  0 siblings, 0 replies; 71+ messages in thread
From: Rob Herring @ 2019-12-18  2:52 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Thomas Gleixner, Sagar Dharia,
	linux-arm-kernel, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Vinod Koul,
	Gustavo Pimentel, Andrew Morton, David S . Miller

On Wed, Dec 11, 2019 at 11:46:11AM -0800, Jae Hyun Yoo wrote:
> This commit adds PECI subsystem document.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Changed documents format to DT schema format so I dropped all review tags.
>   Please review it again.
> 
>  .../devicetree/bindings/peci/peci-bus.yaml    | 129 ++++++++++++++++++
>  .../devicetree/bindings/peci/peci-client.yaml |  54 ++++++++
>  2 files changed, 183 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml
> 
> diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
> new file mode 100644
> index 000000000000..b085e67089cf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
> @@ -0,0 +1,129 @@
> +# SPDX-License-Identifier: GPL-2.0

Dual license new bindings please:

(GPL-2.0-only OR BSD-2-Clause)

> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic Device Tree Bindings for PECI bus
> +
> +maintainers:
> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> +
> +description: |
> +  PECI (Platform Environment Control Interface) is a one-wire bus interface that
> +  provides a communication channel from Intel processors and chipset components
> +  to external monitoring or control devices. PECI is designed to support the
> +  following sideband functions:
> +
> +  * Processor and DRAM thermal management
> +    - Processor fan speed control is managed by comparing Digital Thermal
> +      Sensor (DTS) thermal readings acquired via PECI against the
> +      processor-specific fan speed control reference point, or TCONTROL. Both
> +      TCONTROL and DTS thermal readings are accessible via the processor PECI
> +      client. These variables are referenced to a common temperature, the TCC
> +      activation point, and are both defined as negative offsets from that
> +      reference.
> +    - PECI based access to the processor package configuration space provides
> +      a means for Baseboard Management Controllers (BMC) or other platform
> +      management devices to actively manage the processor and memory power
> +      and thermal features.
> +
> +  * Platform Manageability
> +    - Platform manageability functions including thermal, power, and error
> +      monitoring. Note that platform 'power' management includes monitoring
> +      and control for both the processor and DRAM subsystem to assist with
> +      data center power limiting.
> +    - PECI allows read access to certain error registers in the processor MSR
> +      space and status monitoring registers in the PCI configuration space
> +      within the processor and downstream devices.
> +    - PECI permits writes to certain registers in the processor PCI
> +      configuration space.
> +
> +  * Processor Interface Tuning and Diagnostics
> +    - Processor interface tuning and diagnostics capabilities
> +      (Intel Interconnect BIST). The processors Intel Interconnect Built In
> +      Self Test (Intel IBIST) allows for infield diagnostic capabilities in
> +      the Intel UPI and memory controller interfaces. PECI provides a port to
> +      execute these diagnostics via its PCI Configuration read and write
> +      capabilities.
> +
> +  * Failure Analysis
> +    - Output the state of the processor after a failure for analysis via
> +      Crashdump.
> +
> +  PECI uses a single wire for self-clocking and data transfer. The bus
> +  requires no additional control lines. The physical layer is a self-clocked
> +  one-wire bus that begins each bit with a driven, rising edge from an idle
> +  level near zero volts. The duration of the signal driven high depends on
> +  whether the bit value is a logic '0' or logic '1'. PECI also includes
> +  variable data transfer rate established with every message. In this way, it
> +  is highly flexible even though underlying logic is simple.
> +
> +  The interface design was optimized for interfacing between an Intel
> +  processor and chipset components in both single processor and multiple
> +  processor environments. The single wire interface provides low board
> +  routing overhead for the multiple load connections in the congested routing
> +  area near the processor and chipset components. Bus speed, error checking,
> +  and low protocol overhead provides adequate link bandwidth and reliability
> +  to transfer critical device operating conditions and configuration
> +  information.
> +
> +  PECI subsystem provides single or multiple bus nodes support so each bus can
> +  have one adapter node and multiple device specific client nodes that can be
> +  attached to the PECI bus so each processor client's features can be supported
> +  by the client node through an adapter connection in the bus.
> +
> +properties:
> +  compatible:
> +    const: simple-bus

This is wrong. We already have a schema for this.

What's needed is a peci-bus schema that defines the bus node structure 
and then schemas for the specific controllers and child devices. See 
i2c-controller.yaml for an example.

> +
> +  "#address-cells":
> +    # Required to define bus device control resource address.
> +    const: 1
> +
> +  "#size-cells":
> +    # Required to define bus device control resource address.
> +    const: 1
> +
> +  ranges: true
> +
> +required:
> +  - compatible
> +  - "#address-cells"
> +  - "#size-cells"
> +  - ranges
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/ast2600-clock.h>
> +    peci: bus@1e78b000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x1e78b000 0x200>;
> +
> +        peci0: peci-bus@0 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x0 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +        };
> +
> +        // Just an example. ast2600 doesn't have a second PECI module actually.
> +        peci1: peci-bus@100 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x100 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +        };
> +    };
> +...
> diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
> new file mode 100644
> index 000000000000..fc7c4110e929
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
> @@ -0,0 +1,54 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-client.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic Device Tree Bindings for PECI clients
> +
> +maintainers:
> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - intel,peci-client
> +
> +  reg:
> +    description: |
> +      Address of a client CPU. According to the PECI specification, client
> +      addresses start from 0x30.

0x30 being the min should be a constraint in the bus schema.

> +    maxItems: 1
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/ast2600-clock.h>
> +    peci: bus@1e78b000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x1e78b000 0x60>;
> +
> +        peci0: peci-bus@0 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x0 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +
> +            peci-client@30 {
> +                compatible = "intel,peci-client";
> +                reg = <0x30>;
> +            };
> +
> +            peci-client@31 {
> +                compatible = "intel,peci-client";
> +                reg = <0x31>;
> +            };
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-18  2:57     ` Rob Herring
  -1 siblings, 0 replies; 71+ messages in thread
From: Rob Herring @ 2019-12-18  2:57 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Greg Kroah-Hartman, Lee Jones, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Benjamin Herrenschmidt, Jason M Biils, Milton Miller II,
	Pavel Machek, Robin Murphy, Ryan Chen

On Wed, Dec 11, 2019 at 11:46:14AM -0800, Jae Hyun Yoo wrote:
> This commit adds bindings document of Aspeed PECI adapter for ASPEED
> AST24xx/25xx/26xx SoCs.
> 
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Milton Miller II <miltonm@us.ibm.com>
> Cc: Pavel Machek <pavel@ucw.cz>
> Cc: Robin Murphy <robin.murphy@arm.com>
> Cc: Ryan Chen <ryan_chen@aspeedtech.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Changed documents format to DT schema format so I dropped all review tags.
>   Please review it again.
> 
>  .../devicetree/bindings/peci/peci-aspeed.yaml | 124 ++++++++++++++++++
>  1 file changed, 124 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml
> 
> diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
> new file mode 100644
> index 000000000000..0f5c2993fe9b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
> @@ -0,0 +1,124 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Aspeed PECI Bus Device Tree Bindings
> +
> +maintainers:
> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - aspeed,ast2400-peci
> +      - aspeed,ast2500-peci
> +      - aspeed,ast2600-peci
> +
> +  reg:
> +    maxItems: 1
> +

> +  "#address-cells":
> +    # Required to define a client address.
> +    const: 1
> +
> +  "#size-cells":
> +    # Required to define a client address.
> +    const: 0

These 2 can be defined by the bus schema.

> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    description: |

You can drop the '|' if there's no formatting to preserve.

> +      Clock source for PECI controller. Should reference the external
> +      oscillator clock.
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 1
> +
> +  clock-frequency:
> +    # Operation frequency of PECI controller in units of Hz.
> +    minimum: 187500
> +    maximum: 24000000
> +
> +  msg-timing:
> +    description: |
> +      Message timing negotiation period. This value will determine the period
> +      of message timing negotiation to be issued by PECI controller. The unit
> +      of the programmed value is four times of PECI clock period.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 255
> +        default: 1
> +
> +  addr-timing:
> +    description: |
> +      Address timing negotiation period. This value will determine the period
> +      of address timing negotiation to be issued by PECI controller. The unit
> +      of the programmed value is four times of PECI clock period.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 255
> +        default: 1
> +
> +  rd-sampling-point:
> +    description: |
> +      Read sampling point selection. The whole period of a bit time will be
> +      divided into 16 time frames. This value will determine the time frame
> +      in which the controller will sample PECI signal for data read back.
> +      Usually in the middle of a bit time is the best.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 15
> +        default: 8
> +
> +  cmd-timeout-ms:
> +    # Command timeout in units of ms.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 1
> +        maximum: 60000
> +        default: 1000
> +
> +required:
> +  - compatible
> +  - reg
> +  - "#address-cells"
> +  - "#size-cells"
> +  - interrupts
> +  - clocks
> +  - resets
> +  - clock-frequency
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/ast2600-clock.h>
> +    peci: bus@1e78b000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x1e78b000 0x60>;

You can drop this node in the examples.

> +
> +        peci0: peci-bus@0 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x0 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +            msg-timing = <1>;
> +            addr-timing = <1>;
> +            rd-sampling-point = <8>;
> +            cmd-timeout-ms = <1000>;
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter
@ 2019-12-18  2:57     ` Rob Herring
  0 siblings, 0 replies; 71+ messages in thread
From: Rob Herring @ 2019-12-18  2:57 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Benjamin Herrenschmidt, Milton Miller II, Frederic Barrat,
	Pavel Machek, Mauro Carvalho Chehab, Lee Jones, Jason M Biils,
	Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet, openbmc,
	linux-doc, Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Thomas Gleixner, Sagar Dharia,
	linux-arm-kernel, Juergen Gross, Cyrille Pitchen, Ryan Chen,
	Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
	Stephen Boyd, Vinod Koul, Gustavo Pimentel, Andrew Morton,
	Robin Murphy, David S . Miller

On Wed, Dec 11, 2019 at 11:46:14AM -0800, Jae Hyun Yoo wrote:
> This commit adds bindings document of Aspeed PECI adapter for ASPEED
> AST24xx/25xx/26xx SoCs.
> 
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Milton Miller II <miltonm@us.ibm.com>
> Cc: Pavel Machek <pavel@ucw.cz>
> Cc: Robin Murphy <robin.murphy@arm.com>
> Cc: Ryan Chen <ryan_chen@aspeedtech.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Changed documents format to DT schema format so I dropped all review tags.
>   Please review it again.
> 
>  .../devicetree/bindings/peci/peci-aspeed.yaml | 124 ++++++++++++++++++
>  1 file changed, 124 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml
> 
> diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
> new file mode 100644
> index 000000000000..0f5c2993fe9b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
> @@ -0,0 +1,124 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Aspeed PECI Bus Device Tree Bindings
> +
> +maintainers:
> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - aspeed,ast2400-peci
> +      - aspeed,ast2500-peci
> +      - aspeed,ast2600-peci
> +
> +  reg:
> +    maxItems: 1
> +

> +  "#address-cells":
> +    # Required to define a client address.
> +    const: 1
> +
> +  "#size-cells":
> +    # Required to define a client address.
> +    const: 0

These 2 can be defined by the bus schema.

> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    description: |

You can drop the '|' if there's no formatting to preserve.

> +      Clock source for PECI controller. Should reference the external
> +      oscillator clock.
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 1
> +
> +  clock-frequency:
> +    # Operation frequency of PECI controller in units of Hz.
> +    minimum: 187500
> +    maximum: 24000000
> +
> +  msg-timing:
> +    description: |
> +      Message timing negotiation period. This value will determine the period
> +      of message timing negotiation to be issued by PECI controller. The unit
> +      of the programmed value is four times of PECI clock period.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 255
> +        default: 1
> +
> +  addr-timing:
> +    description: |
> +      Address timing negotiation period. This value will determine the period
> +      of address timing negotiation to be issued by PECI controller. The unit
> +      of the programmed value is four times of PECI clock period.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 255
> +        default: 1
> +
> +  rd-sampling-point:
> +    description: |
> +      Read sampling point selection. The whole period of a bit time will be
> +      divided into 16 time frames. This value will determine the time frame
> +      in which the controller will sample PECI signal for data read back.
> +      Usually in the middle of a bit time is the best.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 15
> +        default: 8
> +
> +  cmd-timeout-ms:
> +    # Command timeout in units of ms.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 1
> +        maximum: 60000
> +        default: 1000
> +
> +required:
> +  - compatible
> +  - reg
> +  - "#address-cells"
> +  - "#size-cells"
> +  - interrupts
> +  - clocks
> +  - resets
> +  - clock-frequency
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/ast2600-clock.h>
> +    peci: bus@1e78b000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x1e78b000 0x60>;

You can drop this node in the examples.

> +
> +        peci0: peci-bus@0 {
> +            compatible = "aspeed,ast2600-peci";
> +            reg = <0x0 0x100>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
> +            resets = <&syscon ASPEED_RESET_PECI>;
> +            clock-frequency = <24000000>;
> +            msg-timing = <1>;
> +            addr-timing = <1>;
> +            rd-sampling-point = <8>;
> +            cmd-timeout-ms = <1000>;
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation
  2019-12-11 19:46   ` Jae Hyun Yoo
@ 2019-12-18 14:42     ` Rob Herring
  -1 siblings, 0 replies; 71+ messages in thread
From: Rob Herring @ 2019-12-18 14:42 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Greg Kroah-Hartman, Lee Jones, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc

On Wed, Dec 11, 2019 at 11:46:17AM -0800, Jae Hyun Yoo wrote:
> From: Tomer Maimon <tmaimon77@gmail.com>
> 
> Added device tree binding documentation for Nuvoton BMC
> NPCM Platform Environment Control Interface(PECI).
> 
> Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Newly added in v11.
> 
>  .../devicetree/bindings/peci/peci-npcm.yaml   | 102 ++++++++++++++++++
>  1 file changed, 102 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml
> 
> diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
> new file mode 100644
> index 000000000000..bcd5626e68e7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
> @@ -0,0 +1,102 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton NPCM PECI Bus Device Tree Bindings
> +
> +maintainers:
> +  - Tomer Maimon <tmaimon77@gmail.com>
> +
> +properties:
> +  compatible:
> +    const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
> +
> +  reg:
> +    maxItems: 1
> +
> +  "#address-cells":
> +    # Required to define a client address.
> +    const: 1
> +
> +  "#size-cells":
> +    # Required to define a client address.
> +    const: 0
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    # PECI reference clock.
> +    maxItems: 1
> +
> +  cmd-timeout-ms:
> +    # Command timeout in units of ms.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32

You can drop this as standard units already have a type.

> +      - minimum: 1
> +        maximum: 60000
> +        default: 1000
> +
> +  pull-down:
> +    description: |
> +      Defines the PECI I/O internal pull down operation.
> +        0: pull down always enable
> +        1: pull down only during transactions.
> +        2: pull down always disable.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 2
> +        default: 0
> +
> +  host-neg-bit-rate:
> +    description: |
> +      Define host negotiation bit rate divider.
> +      the host negotiation bit rate calculate with formula:
> +      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 7
> +        maximum: 31
> +        default: 15
> +
> +  high-volt-range:
> +    description: |
> +      Adapts PECI I/O interface to voltage range.
> +        0: PECI I/O interface voltage range of 0.8-1.06V (default)
> +        1: PECI I/O interface voltage range of 0.95-1.26V
> +    type: boolean

These last 4 properties are vendor specific or PECI common. For the 
former, needs a vendor prefix. For the latter, needs to be moved to 
common location.

> +
> +required:
> +  - compatible
> +  - reg
> +  - "#address-cells"
> +  - "#size-cells"
> +  - interrupts
> +  - clocks
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
> +    peci: bus@100000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x100000 0x200>;
> +
> +        peci0: peci-bus@0 {
> +            compatible = "nuvoton,npcm750-peci";
> +            reg = <0x0 0x200>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&clk NPCM7XX_CLK_APB3>;
> +            cmd-timeout-ms = <1000>;
> +            pull-down = <0>;
> +            host-neg-bit-rate = <15>;
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation
@ 2019-12-18 14:42     ` Rob Herring
  0 siblings, 0 replies; 71+ messages in thread
From: Rob Herring @ 2019-12-18 14:42 UTC (permalink / raw)
  To: Jae Hyun Yoo
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Thomas Gleixner, Sagar Dharia,
	linux-arm-kernel, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Vinod Koul,
	Gustavo Pimentel, Andrew Morton, David S . Miller

On Wed, Dec 11, 2019 at 11:46:17AM -0800, Jae Hyun Yoo wrote:
> From: Tomer Maimon <tmaimon77@gmail.com>
> 
> Added device tree binding documentation for Nuvoton BMC
> NPCM Platform Environment Control Interface(PECI).
> 
> Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> Changes since v10:
> - Newly added in v11.
> 
>  .../devicetree/bindings/peci/peci-npcm.yaml   | 102 ++++++++++++++++++
>  1 file changed, 102 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml
> 
> diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
> new file mode 100644
> index 000000000000..bcd5626e68e7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
> @@ -0,0 +1,102 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton NPCM PECI Bus Device Tree Bindings
> +
> +maintainers:
> +  - Tomer Maimon <tmaimon77@gmail.com>
> +
> +properties:
> +  compatible:
> +    const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
> +
> +  reg:
> +    maxItems: 1
> +
> +  "#address-cells":
> +    # Required to define a client address.
> +    const: 1
> +
> +  "#size-cells":
> +    # Required to define a client address.
> +    const: 0
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    # PECI reference clock.
> +    maxItems: 1
> +
> +  cmd-timeout-ms:
> +    # Command timeout in units of ms.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32

You can drop this as standard units already have a type.

> +      - minimum: 1
> +        maximum: 60000
> +        default: 1000
> +
> +  pull-down:
> +    description: |
> +      Defines the PECI I/O internal pull down operation.
> +        0: pull down always enable
> +        1: pull down only during transactions.
> +        2: pull down always disable.
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 0
> +        maximum: 2
> +        default: 0
> +
> +  host-neg-bit-rate:
> +    description: |
> +      Define host negotiation bit rate divider.
> +      the host negotiation bit rate calculate with formula:
> +      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
> +    allOf:
> +      - $ref: /schemas/types.yaml#/definitions/uint32
> +      - minimum: 7
> +        maximum: 31
> +        default: 15
> +
> +  high-volt-range:
> +    description: |
> +      Adapts PECI I/O interface to voltage range.
> +        0: PECI I/O interface voltage range of 0.8-1.06V (default)
> +        1: PECI I/O interface voltage range of 0.95-1.26V
> +    type: boolean

These last 4 properties are vendor specific or PECI common. For the 
former, needs a vendor prefix. For the latter, needs to be moved to 
common location.

> +
> +required:
> +  - compatible
> +  - reg
> +  - "#address-cells"
> +  - "#size-cells"
> +  - interrupts
> +  - clocks
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
> +    peci: bus@100000 {
> +        compatible = "simple-bus";
> +        #address-cells = <1>;
> +        #size-cells = <1>;
> +        ranges = <0x0 0x100000 0x200>;
> +
> +        peci0: peci-bus@0 {
> +            compatible = "nuvoton,npcm750-peci";
> +            reg = <0x0 0x200>;
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
> +            clocks = <&clk NPCM7XX_CLK_APB3>;
> +            cmd-timeout-ms = <1000>;
> +            pull-down = <0>;
> +            host-neg-bit-rate = <15>;
> +        };
> +    };
> +...
> -- 
> 2.17.1
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 01/14] dt-bindings: Add PECI subsystem document
  2019-12-18  2:52     ` Rob Herring
@ 2019-12-18 23:12       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-18 23:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: Greg Kroah-Hartman, Lee Jones, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc

Hi Rob,

On 12/17/2019 6:52 PM, Rob Herring wrote:
> On Wed, Dec 11, 2019 at 11:46:11AM -0800, Jae Hyun Yoo wrote:
>> This commit adds PECI subsystem document.
>>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Changed documents format to DT schema format so I dropped all review tags.
>>    Please review it again.
>>
>>   .../devicetree/bindings/peci/peci-bus.yaml    | 129 ++++++++++++++++++
>>   .../devicetree/bindings/peci/peci-client.yaml |  54 ++++++++
>>   2 files changed, 183 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
>> new file mode 100644
>> index 000000000000..b085e67089cf
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
>> @@ -0,0 +1,129 @@
>> +# SPDX-License-Identifier: GPL-2.0
> 
> Dual license new bindings please:
> 
> (GPL-2.0-only OR BSD-2-Clause)

I see. I'll replace that with it for all new bindings in this patch set.

>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Generic Device Tree Bindings for PECI bus
>> +
>> +maintainers:
>> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> +
>> +description: |
>> +  PECI (Platform Environment Control Interface) is a one-wire bus interface that
>> +  provides a communication channel from Intel processors and chipset components
>> +  to external monitoring or control devices. PECI is designed to support the
>> +  following sideband functions:
>> +
>> +  * Processor and DRAM thermal management
>> +    - Processor fan speed control is managed by comparing Digital Thermal
>> +      Sensor (DTS) thermal readings acquired via PECI against the
>> +      processor-specific fan speed control reference point, or TCONTROL. Both
>> +      TCONTROL and DTS thermal readings are accessible via the processor PECI
>> +      client. These variables are referenced to a common temperature, the TCC
>> +      activation point, and are both defined as negative offsets from that
>> +      reference.
>> +    - PECI based access to the processor package configuration space provides
>> +      a means for Baseboard Management Controllers (BMC) or other platform
>> +      management devices to actively manage the processor and memory power
>> +      and thermal features.
>> +
>> +  * Platform Manageability
>> +    - Platform manageability functions including thermal, power, and error
>> +      monitoring. Note that platform 'power' management includes monitoring
>> +      and control for both the processor and DRAM subsystem to assist with
>> +      data center power limiting.
>> +    - PECI allows read access to certain error registers in the processor MSR
>> +      space and status monitoring registers in the PCI configuration space
>> +      within the processor and downstream devices.
>> +    - PECI permits writes to certain registers in the processor PCI
>> +      configuration space.
>> +
>> +  * Processor Interface Tuning and Diagnostics
>> +    - Processor interface tuning and diagnostics capabilities
>> +      (Intel Interconnect BIST). The processors Intel Interconnect Built In
>> +      Self Test (Intel IBIST) allows for infield diagnostic capabilities in
>> +      the Intel UPI and memory controller interfaces. PECI provides a port to
>> +      execute these diagnostics via its PCI Configuration read and write
>> +      capabilities.
>> +
>> +  * Failure Analysis
>> +    - Output the state of the processor after a failure for analysis via
>> +      Crashdump.
>> +
>> +  PECI uses a single wire for self-clocking and data transfer. The bus
>> +  requires no additional control lines. The physical layer is a self-clocked
>> +  one-wire bus that begins each bit with a driven, rising edge from an idle
>> +  level near zero volts. The duration of the signal driven high depends on
>> +  whether the bit value is a logic '0' or logic '1'. PECI also includes
>> +  variable data transfer rate established with every message. In this way, it
>> +  is highly flexible even though underlying logic is simple.
>> +
>> +  The interface design was optimized for interfacing between an Intel
>> +  processor and chipset components in both single processor and multiple
>> +  processor environments. The single wire interface provides low board
>> +  routing overhead for the multiple load connections in the congested routing
>> +  area near the processor and chipset components. Bus speed, error checking,
>> +  and low protocol overhead provides adequate link bandwidth and reliability
>> +  to transfer critical device operating conditions and configuration
>> +  information.
>> +
>> +  PECI subsystem provides single or multiple bus nodes support so each bus can
>> +  have one adapter node and multiple device specific client nodes that can be
>> +  attached to the PECI bus so each processor client's features can be supported
>> +  by the client node through an adapter connection in the bus.
>> +
>> +properties:
>> +  compatible:
>> +    const: simple-bus
> 
> This is wrong. We already have a schema for this.
> 
> What's needed is a peci-bus schema that defines the bus node structure
> and then schemas for the specific controllers and child devices. See
> i2c-controller.yaml for an example.

Oh, I see. I'll fix and submit it as '/schema/peci-bus.yaml' into
dt-schema tree.

>> +
>> +  "#address-cells":
>> +    # Required to define bus device control resource address.
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    # Required to define bus device control resource address.
>> +    const: 1
>> +
>> +  ranges: true
>> +
>> +required:
>> +  - compatible
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - ranges
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/ast2600-clock.h>
>> +    peci: bus@1e78b000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x1e78b000 0x200>;
>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x0 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +        };
>> +
>> +        // Just an example. ast2600 doesn't have a second PECI module actually.
>> +        peci1: peci-bus@100 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x100 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +        };
>> +    };
>> +...
>> diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
>> new file mode 100644
>> index 000000000000..fc7c4110e929
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
>> @@ -0,0 +1,54 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-client.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Generic Device Tree Bindings for PECI clients
>> +
>> +maintainers:
>> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - intel,peci-client
>> +
>> +  reg:
>> +    description: |
>> +      Address of a client CPU. According to the PECI specification, client
>> +      addresses start from 0x30.
> 
> 0x30 being the min should be a constraint in the bus schema.

Right. Will add that.

Thanks a lot for your review!

-Jae

>> +    maxItems: 1
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/ast2600-clock.h>
>> +    peci: bus@1e78b000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x1e78b000 0x60>;
>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x0 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +
>> +            peci-client@30 {
>> +                compatible = "intel,peci-client";
>> +                reg = <0x30>;
>> +            };
>> +
>> +            peci-client@31 {
>> +                compatible = "intel,peci-client";
>> +                reg = <0x31>;
>> +            };
>> +        };
>> +    };
>> +...
>> -- 
>> 2.17.1
>>
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 01/14] dt-bindings: Add PECI subsystem document
@ 2019-12-18 23:12       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-18 23:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Thomas Gleixner, Sagar Dharia,
	linux-arm-kernel, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Vinod Koul,
	Gustavo Pimentel, Andrew Morton, David S . Miller

Hi Rob,

On 12/17/2019 6:52 PM, Rob Herring wrote:
> On Wed, Dec 11, 2019 at 11:46:11AM -0800, Jae Hyun Yoo wrote:
>> This commit adds PECI subsystem document.
>>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Changed documents format to DT schema format so I dropped all review tags.
>>    Please review it again.
>>
>>   .../devicetree/bindings/peci/peci-bus.yaml    | 129 ++++++++++++++++++
>>   .../devicetree/bindings/peci/peci-client.yaml |  54 ++++++++
>>   2 files changed, 183 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-bus.yaml
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-client.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/peci/peci-bus.yaml b/Documentation/devicetree/bindings/peci/peci-bus.yaml
>> new file mode 100644
>> index 000000000000..b085e67089cf
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-bus.yaml
>> @@ -0,0 +1,129 @@
>> +# SPDX-License-Identifier: GPL-2.0
> 
> Dual license new bindings please:
> 
> (GPL-2.0-only OR BSD-2-Clause)

I see. I'll replace that with it for all new bindings in this patch set.

>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-bus.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Generic Device Tree Bindings for PECI bus
>> +
>> +maintainers:
>> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> +
>> +description: |
>> +  PECI (Platform Environment Control Interface) is a one-wire bus interface that
>> +  provides a communication channel from Intel processors and chipset components
>> +  to external monitoring or control devices. PECI is designed to support the
>> +  following sideband functions:
>> +
>> +  * Processor and DRAM thermal management
>> +    - Processor fan speed control is managed by comparing Digital Thermal
>> +      Sensor (DTS) thermal readings acquired via PECI against the
>> +      processor-specific fan speed control reference point, or TCONTROL. Both
>> +      TCONTROL and DTS thermal readings are accessible via the processor PECI
>> +      client. These variables are referenced to a common temperature, the TCC
>> +      activation point, and are both defined as negative offsets from that
>> +      reference.
>> +    - PECI based access to the processor package configuration space provides
>> +      a means for Baseboard Management Controllers (BMC) or other platform
>> +      management devices to actively manage the processor and memory power
>> +      and thermal features.
>> +
>> +  * Platform Manageability
>> +    - Platform manageability functions including thermal, power, and error
>> +      monitoring. Note that platform 'power' management includes monitoring
>> +      and control for both the processor and DRAM subsystem to assist with
>> +      data center power limiting.
>> +    - PECI allows read access to certain error registers in the processor MSR
>> +      space and status monitoring registers in the PCI configuration space
>> +      within the processor and downstream devices.
>> +    - PECI permits writes to certain registers in the processor PCI
>> +      configuration space.
>> +
>> +  * Processor Interface Tuning and Diagnostics
>> +    - Processor interface tuning and diagnostics capabilities
>> +      (Intel Interconnect BIST). The processors Intel Interconnect Built In
>> +      Self Test (Intel IBIST) allows for infield diagnostic capabilities in
>> +      the Intel UPI and memory controller interfaces. PECI provides a port to
>> +      execute these diagnostics via its PCI Configuration read and write
>> +      capabilities.
>> +
>> +  * Failure Analysis
>> +    - Output the state of the processor after a failure for analysis via
>> +      Crashdump.
>> +
>> +  PECI uses a single wire for self-clocking and data transfer. The bus
>> +  requires no additional control lines. The physical layer is a self-clocked
>> +  one-wire bus that begins each bit with a driven, rising edge from an idle
>> +  level near zero volts. The duration of the signal driven high depends on
>> +  whether the bit value is a logic '0' or logic '1'. PECI also includes
>> +  variable data transfer rate established with every message. In this way, it
>> +  is highly flexible even though underlying logic is simple.
>> +
>> +  The interface design was optimized for interfacing between an Intel
>> +  processor and chipset components in both single processor and multiple
>> +  processor environments. The single wire interface provides low board
>> +  routing overhead for the multiple load connections in the congested routing
>> +  area near the processor and chipset components. Bus speed, error checking,
>> +  and low protocol overhead provides adequate link bandwidth and reliability
>> +  to transfer critical device operating conditions and configuration
>> +  information.
>> +
>> +  PECI subsystem provides single or multiple bus nodes support so each bus can
>> +  have one adapter node and multiple device specific client nodes that can be
>> +  attached to the PECI bus so each processor client's features can be supported
>> +  by the client node through an adapter connection in the bus.
>> +
>> +properties:
>> +  compatible:
>> +    const: simple-bus
> 
> This is wrong. We already have a schema for this.
> 
> What's needed is a peci-bus schema that defines the bus node structure
> and then schemas for the specific controllers and child devices. See
> i2c-controller.yaml for an example.

Oh, I see. I'll fix and submit it as '/schema/peci-bus.yaml' into
dt-schema tree.

>> +
>> +  "#address-cells":
>> +    # Required to define bus device control resource address.
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    # Required to define bus device control resource address.
>> +    const: 1
>> +
>> +  ranges: true
>> +
>> +required:
>> +  - compatible
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - ranges
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/ast2600-clock.h>
>> +    peci: bus@1e78b000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x1e78b000 0x200>;
>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x0 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +        };
>> +
>> +        // Just an example. ast2600 doesn't have a second PECI module actually.
>> +        peci1: peci-bus@100 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x100 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +        };
>> +    };
>> +...
>> diff --git a/Documentation/devicetree/bindings/peci/peci-client.yaml b/Documentation/devicetree/bindings/peci/peci-client.yaml
>> new file mode 100644
>> index 000000000000..fc7c4110e929
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-client.yaml
>> @@ -0,0 +1,54 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-client.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Generic Device Tree Bindings for PECI clients
>> +
>> +maintainers:
>> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - intel,peci-client
>> +
>> +  reg:
>> +    description: |
>> +      Address of a client CPU. According to the PECI specification, client
>> +      addresses start from 0x30.
> 
> 0x30 being the min should be a constraint in the bus schema.

Right. Will add that.

Thanks a lot for your review!

-Jae

>> +    maxItems: 1
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/ast2600-clock.h>
>> +    peci: bus@1e78b000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x1e78b000 0x60>;
>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x0 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +
>> +            peci-client@30 {
>> +                compatible = "intel,peci-client";
>> +                reg = <0x30>;
>> +            };
>> +
>> +            peci-client@31 {
>> +                compatible = "intel,peci-client";
>> +                reg = <0x31>;
>> +            };
>> +        };
>> +    };
>> +...
>> -- 
>> 2.17.1
>>
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter
  2019-12-18  2:57     ` Rob Herring
@ 2019-12-18 23:21       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-18 23:21 UTC (permalink / raw)
  To: Rob Herring
  Cc: Greg Kroah-Hartman, Lee Jones, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc,
	Benjamin Herrenschmidt, Jason M Biils, Milton Miller II,
	Pavel Machek, Robin Murphy, Ryan Chen

Hi Rob,

On 12/17/2019 6:57 PM, Rob Herring wrote:
> On Wed, Dec 11, 2019 at 11:46:14AM -0800, Jae Hyun Yoo wrote:
>> This commit adds bindings document of Aspeed PECI adapter for ASPEED
>> AST24xx/25xx/26xx SoCs.
>>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Milton Miller II <miltonm@us.ibm.com>
>> Cc: Pavel Machek <pavel@ucw.cz>
>> Cc: Robin Murphy <robin.murphy@arm.com>
>> Cc: Ryan Chen <ryan_chen@aspeedtech.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Changed documents format to DT schema format so I dropped all review tags.
>>    Please review it again.
>>
>>   .../devicetree/bindings/peci/peci-aspeed.yaml | 124 ++++++++++++++++++
>>   1 file changed, 124 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
>> new file mode 100644
>> index 000000000000..0f5c2993fe9b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
>> @@ -0,0 +1,124 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Aspeed PECI Bus Device Tree Bindings
>> +
>> +maintainers:
>> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - aspeed,ast2400-peci
>> +      - aspeed,ast2500-peci
>> +      - aspeed,ast2600-peci
>> +
>> +  reg:
>> +    maxItems: 1
>> +
> 
>> +  "#address-cells":
>> +    # Required to define a client address.
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    # Required to define a client address.
>> +    const: 0
> 
> These 2 can be defined by the bus schema.

I see. I'll add these to peci-bus schema.

>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    description: |
> 
> You can drop the '|' if there's no formatting to preserve.

Will check this for all bindings in this patch set.

>> +      Clock source for PECI controller. Should reference the external
>> +      oscillator clock.
>> +    maxItems: 1
>> +
>> +  resets:
>> +    maxItems: 1
>> +
>> +  clock-frequency:
>> +    # Operation frequency of PECI controller in units of Hz.
>> +    minimum: 187500
>> +    maximum: 24000000
>> +
>> +  msg-timing:
>> +    description: |
>> +      Message timing negotiation period. This value will determine the period
>> +      of message timing negotiation to be issued by PECI controller. The unit
>> +      of the programmed value is four times of PECI clock period.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 255
>> +        default: 1
>> +
>> +  addr-timing:
>> +    description: |
>> +      Address timing negotiation period. This value will determine the period
>> +      of address timing negotiation to be issued by PECI controller. The unit
>> +      of the programmed value is four times of PECI clock period.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 255
>> +        default: 1
>> +
>> +  rd-sampling-point:
>> +    description: |
>> +      Read sampling point selection. The whole period of a bit time will be
>> +      divided into 16 time frames. This value will determine the time frame
>> +      in which the controller will sample PECI signal for data read back.
>> +      Usually in the middle of a bit time is the best.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 15
>> +        default: 8
>> +
>> +  cmd-timeout-ms:
>> +    # Command timeout in units of ms.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 1
>> +        maximum: 60000
>> +        default: 1000
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - interrupts
>> +  - clocks
>> +  - resets
>> +  - clock-frequency
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/ast2600-clock.h>
>> +    peci: bus@1e78b000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x1e78b000 0x60>;
> 
> You can drop this node in the examples.

I see. Will drop the parent node in this example.

Thanks a lot for your review!

-Jae

>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x0 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +            msg-timing = <1>;
>> +            addr-timing = <1>;
>> +            rd-sampling-point = <8>;
>> +            cmd-timeout-ms = <1000>;
>> +        };
>> +    };
>> +...
>> -- 
>> 2.17.1
>>
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter
@ 2019-12-18 23:21       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-18 23:21 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Benjamin Herrenschmidt, Milton Miller II, Frederic Barrat,
	Pavel Machek, Mauro Carvalho Chehab, Lee Jones, Jason M Biils,
	Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet, openbmc,
	linux-doc, Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Thomas Gleixner, Sagar Dharia,
	linux-arm-kernel, Juergen Gross, Cyrille Pitchen, Ryan Chen,
	Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
	Stephen Boyd, Vinod Koul, Gustavo Pimentel, Andrew Morton,
	Robin Murphy, David S . Miller

Hi Rob,

On 12/17/2019 6:57 PM, Rob Herring wrote:
> On Wed, Dec 11, 2019 at 11:46:14AM -0800, Jae Hyun Yoo wrote:
>> This commit adds bindings document of Aspeed PECI adapter for ASPEED
>> AST24xx/25xx/26xx SoCs.
>>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Milton Miller II <miltonm@us.ibm.com>
>> Cc: Pavel Machek <pavel@ucw.cz>
>> Cc: Robin Murphy <robin.murphy@arm.com>
>> Cc: Ryan Chen <ryan_chen@aspeedtech.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Changed documents format to DT schema format so I dropped all review tags.
>>    Please review it again.
>>
>>   .../devicetree/bindings/peci/peci-aspeed.yaml | 124 ++++++++++++++++++
>>   1 file changed, 124 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
>> new file mode 100644
>> index 000000000000..0f5c2993fe9b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml
>> @@ -0,0 +1,124 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Aspeed PECI Bus Device Tree Bindings
>> +
>> +maintainers:
>> +  - Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - aspeed,ast2400-peci
>> +      - aspeed,ast2500-peci
>> +      - aspeed,ast2600-peci
>> +
>> +  reg:
>> +    maxItems: 1
>> +
> 
>> +  "#address-cells":
>> +    # Required to define a client address.
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    # Required to define a client address.
>> +    const: 0
> 
> These 2 can be defined by the bus schema.

I see. I'll add these to peci-bus schema.

>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    description: |
> 
> You can drop the '|' if there's no formatting to preserve.

Will check this for all bindings in this patch set.

>> +      Clock source for PECI controller. Should reference the external
>> +      oscillator clock.
>> +    maxItems: 1
>> +
>> +  resets:
>> +    maxItems: 1
>> +
>> +  clock-frequency:
>> +    # Operation frequency of PECI controller in units of Hz.
>> +    minimum: 187500
>> +    maximum: 24000000
>> +
>> +  msg-timing:
>> +    description: |
>> +      Message timing negotiation period. This value will determine the period
>> +      of message timing negotiation to be issued by PECI controller. The unit
>> +      of the programmed value is four times of PECI clock period.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 255
>> +        default: 1
>> +
>> +  addr-timing:
>> +    description: |
>> +      Address timing negotiation period. This value will determine the period
>> +      of address timing negotiation to be issued by PECI controller. The unit
>> +      of the programmed value is four times of PECI clock period.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 255
>> +        default: 1
>> +
>> +  rd-sampling-point:
>> +    description: |
>> +      Read sampling point selection. The whole period of a bit time will be
>> +      divided into 16 time frames. This value will determine the time frame
>> +      in which the controller will sample PECI signal for data read back.
>> +      Usually in the middle of a bit time is the best.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 15
>> +        default: 8
>> +
>> +  cmd-timeout-ms:
>> +    # Command timeout in units of ms.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 1
>> +        maximum: 60000
>> +        default: 1000
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - interrupts
>> +  - clocks
>> +  - resets
>> +  - clock-frequency
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/ast2600-clock.h>
>> +    peci: bus@1e78b000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x1e78b000 0x60>;
> 
> You can drop this node in the examples.

I see. Will drop the parent node in this example.

Thanks a lot for your review!

-Jae

>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "aspeed,ast2600-peci";
>> +            reg = <0x0 0x100>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>;
>> +            resets = <&syscon ASPEED_RESET_PECI>;
>> +            clock-frequency = <24000000>;
>> +            msg-timing = <1>;
>> +            addr-timing = <1>;
>> +            rd-sampling-point = <8>;
>> +            cmd-timeout-ms = <1000>;
>> +        };
>> +    };
>> +...
>> -- 
>> 2.17.1
>>
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation
  2019-12-18 14:42     ` Rob Herring
@ 2019-12-18 23:30       ` Jae Hyun Yoo
  -1 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-18 23:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Greg Kroah-Hartman, Lee Jones, Jean Delvare, Guenter Roeck,
	Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
	Gustavo Pimentel, Kishon Vijay Abraham I, Lorenzo Pieralisi,
	Darrick J . Wong, Eric Sandeen, Arnd Bergmann, Wu Hao,
	Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
	David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
	Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
	David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
	Thomas Gleixner, Juergen Gross, Cyrille Pitchen, Tomer Maimon,
	linux-hwmon, devicetree, linux-arm-kernel, linux-doc, openbmc

Hi Rob,

On 12/18/2019 6:42 AM, Rob Herring wrote:
> On Wed, Dec 11, 2019 at 11:46:17AM -0800, Jae Hyun Yoo wrote:
>> From: Tomer Maimon <tmaimon77@gmail.com>
>>
>> Added device tree binding documentation for Nuvoton BMC
>> NPCM Platform Environment Control Interface(PECI).
>>
>> Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Newly added in v11.
>>
>>   .../devicetree/bindings/peci/peci-npcm.yaml   | 102 ++++++++++++++++++
>>   1 file changed, 102 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
>> new file mode 100644
>> index 000000000000..bcd5626e68e7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
>> @@ -0,0 +1,102 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton NPCM PECI Bus Device Tree Bindings
>> +
>> +maintainers:
>> +  - Tomer Maimon <tmaimon77@gmail.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  "#address-cells":
>> +    # Required to define a client address.
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    # Required to define a client address.
>> +    const: 0
>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    # PECI reference clock.
>> +    maxItems: 1
>> +
>> +  cmd-timeout-ms:
>> +    # Command timeout in units of ms.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
> 
> You can drop this as standard units already have a type.

I'm assuming you pointed the timeout-ms as one of standard
units, right? I'll drop this ref.

>> +      - minimum: 1
>> +        maximum: 60000
>> +        default: 1000
>> +
>> +  pull-down:
>> +    description: |
>> +      Defines the PECI I/O internal pull down operation.
>> +        0: pull down always enable
>> +        1: pull down only during transactions.
>> +        2: pull down always disable.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 2
>> +        default: 0
>> +
>> +  host-neg-bit-rate:
>> +    description: |
>> +      Define host negotiation bit rate divider.
>> +      the host negotiation bit rate calculate with formula:
>> +      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 7
>> +        maximum: 31
>> +        default: 15
>> +
>> +  high-volt-range:
>> +    description: |
>> +      Adapts PECI I/O interface to voltage range.
>> +        0: PECI I/O interface voltage range of 0.8-1.06V (default)
>> +        1: PECI I/O interface voltage range of 0.95-1.26V
>> +    type: boolean
> 
> These last 4 properties are vendor specific or PECI common. For the
> former, needs a vendor prefix. For the latter, needs to be moved to
> common location.

These are Nuvoton vendor specifics. I'll add prefix for them and will
check Aspeed bindings too.

Thanks a lot for your review!

-Jae

>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - interrupts
>> +  - clocks
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
>> +    peci: bus@100000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x100000 0x200>;
>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "nuvoton,npcm750-peci";
>> +            reg = <0x0 0x200>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&clk NPCM7XX_CLK_APB3>;
>> +            cmd-timeout-ms = <1000>;
>> +            pull-down = <0>;
>> +            host-neg-bit-rate = <15>;
>> +        };
>> +    };
>> +...
>> -- 
>> 2.17.1
>>
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation
@ 2019-12-18 23:30       ` Jae Hyun Yoo
  0 siblings, 0 replies; 71+ messages in thread
From: Jae Hyun Yoo @ 2019-12-18 23:30 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, Randy Dunlap, Tomer Maimon, devicetree,
	Frederic Barrat, Mauro Carvalho Chehab, Lee Jones, Eric Sandeen,
	Lorenzo Pieralisi, Jonathan Corbet, openbmc, linux-doc,
	Kishon Vijay Abraham I, Joel Stanley, Bryant G . Ly,
	Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
	linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
	Johan Hovold, Tomohiro Kusumi, Thomas Gleixner, Sagar Dharia,
	linux-arm-kernel, Juergen Gross, Cyrille Pitchen, Andrew Jeffery,
	Greg Kroah-Hartman, Darrick J . Wong, Stephen Boyd, Vinod Koul,
	Gustavo Pimentel, Andrew Morton, David S . Miller

Hi Rob,

On 12/18/2019 6:42 AM, Rob Herring wrote:
> On Wed, Dec 11, 2019 at 11:46:17AM -0800, Jae Hyun Yoo wrote:
>> From: Tomer Maimon <tmaimon77@gmail.com>
>>
>> Added device tree binding documentation for Nuvoton BMC
>> NPCM Platform Environment Control Interface(PECI).
>>
>> Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> Changes since v10:
>> - Newly added in v11.
>>
>>   .../devicetree/bindings/peci/peci-npcm.yaml   | 102 ++++++++++++++++++
>>   1 file changed, 102 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/peci/peci-npcm.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/peci/peci-npcm.yaml b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
>> new file mode 100644
>> index 000000000000..bcd5626e68e7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/peci/peci-npcm.yaml
>> @@ -0,0 +1,102 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/peci/peci-npcm.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton NPCM PECI Bus Device Tree Bindings
>> +
>> +maintainers:
>> +  - Tomer Maimon <tmaimon77@gmail.com>
>> +
>> +properties:
>> +  compatible:
>> +    const: nuvoton,npcm750-peci # for the NPCM7XX BMC.
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  "#address-cells":
>> +    # Required to define a client address.
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    # Required to define a client address.
>> +    const: 0
>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    # PECI reference clock.
>> +    maxItems: 1
>> +
>> +  cmd-timeout-ms:
>> +    # Command timeout in units of ms.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
> 
> You can drop this as standard units already have a type.

I'm assuming you pointed the timeout-ms as one of standard
units, right? I'll drop this ref.

>> +      - minimum: 1
>> +        maximum: 60000
>> +        default: 1000
>> +
>> +  pull-down:
>> +    description: |
>> +      Defines the PECI I/O internal pull down operation.
>> +        0: pull down always enable
>> +        1: pull down only during transactions.
>> +        2: pull down always disable.
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 0
>> +        maximum: 2
>> +        default: 0
>> +
>> +  host-neg-bit-rate:
>> +    description: |
>> +      Define host negotiation bit rate divider.
>> +      the host negotiation bit rate calculate with formula:
>> +      clock frequency[Hz] / [4 x {host-neg-bit-rate + 1}]
>> +    allOf:
>> +      - $ref: /schemas/types.yaml#/definitions/uint32
>> +      - minimum: 7
>> +        maximum: 31
>> +        default: 15
>> +
>> +  high-volt-range:
>> +    description: |
>> +      Adapts PECI I/O interface to voltage range.
>> +        0: PECI I/O interface voltage range of 0.8-1.06V (default)
>> +        1: PECI I/O interface voltage range of 0.95-1.26V
>> +    type: boolean
> 
> These last 4 properties are vendor specific or PECI common. For the
> former, needs a vendor prefix. For the latter, needs to be moved to
> common location.

These are Nuvoton vendor specifics. I'll add prefix for them and will
check Aspeed bindings too.

Thanks a lot for your review!

-Jae

>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - interrupts
>> +  - clocks
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
>> +    peci: bus@100000 {
>> +        compatible = "simple-bus";
>> +        #address-cells = <1>;
>> +        #size-cells = <1>;
>> +        ranges = <0x0 0x100000 0x200>;
>> +
>> +        peci0: peci-bus@0 {
>> +            compatible = "nuvoton,npcm750-peci";
>> +            reg = <0x0 0x200>;
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
>> +            clocks = <&clk NPCM7XX_CLK_APB3>;
>> +            cmd-timeout-ms = <1000>;
>> +            pull-down = <0>;
>> +            host-neg-bit-rate = <15>;
>> +        };
>> +    };
>> +...
>> -- 
>> 2.17.1
>>
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 71+ messages in thread

end of thread, other threads:[~2019-12-18 23:30 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-11 19:46 [PATCH v11 00/14] PECI device driver introduction Jae Hyun Yoo
2019-12-11 19:46 ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 01/14] dt-bindings: Add PECI subsystem document Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-18  2:52   ` Rob Herring
2019-12-18  2:52     ` Rob Herring
2019-12-18 23:12     ` Jae Hyun Yoo
2019-12-18 23:12       ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 02/14] Documentation: ioctl: Add ioctl numbers for PECI subsystem Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 03/14] peci: Add support for PECI bus driver core Jae Hyun Yoo
2019-12-11 20:18   ` Andy Shevchenko
2019-12-12  0:46     ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 04/14] dt-bindings: Add bindings document of Aspeed PECI adapter Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-18  2:57   ` Rob Herring
2019-12-18  2:57     ` Rob Herring
2019-12-18 23:21     ` Jae Hyun Yoo
2019-12-18 23:21       ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 05/14] ARM: dts: aspeed: Add PECI node Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 06/14] peci: Add Aspeed PECI adapter driver Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 20:28   ` Andy Shevchenko
2019-12-11 20:28     ` Andy Shevchenko
2019-12-12  0:50     ` Jae Hyun Yoo
2019-12-12  0:50       ` Jae Hyun Yoo
2019-12-12  8:47       ` Andy Shevchenko
2019-12-12  8:47         ` Andy Shevchenko
2019-12-12 18:51         ` Jae Hyun Yoo
2019-12-12 18:51           ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 07/14] dt-bindings: peci: add NPCM PECI documentation Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-18 14:42   ` Rob Herring
2019-12-18 14:42     ` Rob Herring
2019-12-18 23:30     ` Jae Hyun Yoo
2019-12-18 23:30       ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 08/14] ARM: dts: npcm7xx: Add PECI node Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 09/14] peci: npcm: add NPCM PECI driver Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 10/14] dt-bindings: mfd: Add Intel PECI client bindings document Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 11/14] mfd: intel-peci-client: Add Intel PECI client driver Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-16 16:01   ` Lee Jones
2019-12-16 16:01     ` Lee Jones
2019-12-16 21:57     ` Jae Hyun Yoo
2019-12-16 21:57       ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 12/14] Documentation: hwmon: Add documents for PECI hwmon drivers Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 13/14] hwmon: Add PECI cputemp driver Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-13  6:24   ` Guenter Roeck
2019-12-13  6:24     ` Guenter Roeck
2019-12-16 20:43     ` Jae Hyun Yoo
2019-12-16 20:43       ` Jae Hyun Yoo
2019-12-11 19:46 ` [PATCH v11 14/14] hwmon: Add PECI dimmtemp driver Jae Hyun Yoo
2019-12-11 19:46   ` Jae Hyun Yoo
2019-12-13  6:32   ` Guenter Roeck
2019-12-13  6:32     ` Guenter Roeck
2019-12-16 21:04     ` Jae Hyun Yoo
2019-12-16 21:04       ` Jae Hyun Yoo
2019-12-16 21:21       ` Guenter Roeck
2019-12-16 21:21         ` Guenter Roeck
2019-12-16 22:17         ` Jae Hyun Yoo
2019-12-16 22:17           ` Jae Hyun Yoo
2019-12-16 23:27           ` Guenter Roeck
2019-12-16 23:27             ` Guenter Roeck
2019-12-16 23:31             ` Jae Hyun Yoo
2019-12-16 23:31               ` Jae Hyun Yoo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.