forked from mirrors/linux
		
	hwmon updates for v6.12
* New drivers
 
   - Driver for Sophgo SG2042 external hardware monitor
 
   - Thermal sensor driver for Surface Aggregator Module
 
 * Added support to existing drivers
 
   - oxp-sensors: Support for multiple new devices.
 
   - nct6775: Added G15CF to ASUS WMI monitoring list
 
 * Modernizations
 
   - ina2xx: Driver cleanup and update to use with_info API
 
   - lm92: Driver cleanup and update to use regmap and with_info API
 
   - lm95234: Driver cleanup and update to use regmap and with_info API
 
   - max1619: Driver cleanup and update to use regmap and with_info API
 
   - max1668: Driver cleanup and update to use regmap and with_info API
 
   - max6697: Driver cleanup and update to use regmap and with_info API
 
 * API updates
 
   - Removed unused devm_hwmon_device_unregister() API function
 
 * Other notable changes
 
   - Implement and use generic bus access delay for pmbus drivers
 
   - Use with scoped for each OF child loop in several drivers
 
   - Module unloading fixes for gsc-hwmon and ntc_thermistor drivers
 
   - Converted various drivers to use multi-byte regmap operations
 
   - adt7475: Improved devicetree based configuration
 
   - ltc2947: Move to firmware agnostic API
 
   - ltc2978: Converted devicetree description to yaml
 
   - max16065: Addressed overflows when writing limit attributes
 
 * Various other minor cleanups, fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmboX+AACgkQyx8mb86f
 mYHRGA/7BVlHa3sxDTNLPvL3VMZ1/SgmQqC0xUx57bAOcpkLX0taXD/t+Nm93HaW
 vCKGYPe0jII9tVz1YvZ1VSNwJXZo4X+jfdL3t6RpdKpn/op6vASzhKhh0sGeiyw1
 aQNXzrm4dthFDRfmscZM1+CBQv4aTf6ApyTbRFH2dnViXu9idMZYcxoxz87uody5
 AxUAgNDPb/mQww3x6r+rVv3VQaJZ+yrJxbYvaxgzbm8TqIFCpHgNtRJTVBhGjbOi
 o9rushlUpOjBQE2/jKOajtfV9fWX/kpJu9dUfSbVMCvZgEPU985UX6dpg9Oc0t0o
 oUhID2dHLUVNmn4dTQCtvzuLTEBDi87TcML6VDlMIn3dFi5QG3tJZkaWtbPymHz9
 4Qf3TJ2TV0E/jIh8UueFd2SlRlkCE3HooM04Kbes7H8ftSbddMM3fTah8yzdOJJE
 Dwv6eO3T9REHPaBauFq0Y9hzkx46rqF6Mli0tFUumh7oM1b68ILZ+oJxfpapatzO
 Pa6UPFfaHU63VFmDCzNWc0IiI1beF7i5fzzWwj37HgIdaw1+cS6kNtvbv/6t5/41
 5TVitpP1SLzDtjK6f+VDjroE/Qg4OeodamBOEopMPjYM/nipyWxPoOK1VEtbo1P5
 vAJBmgPn45M02miB7l3ERCCkRVQdeL69ZM5vcRmLB8NQRKML830=
 =z6jf
 -----END PGP SIGNATURE-----
Merge tag 'hwmon-for-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
 "New drivers:
   - driver for Sophgo SG2042 external hardware monitor
   - thermal sensor driver for Surface Aggregator Module
  Added support to existing drivers:
   - oxp-sensors: Support for multiple new devices.
   - nct6775: Added G15CF to ASUS WMI monitoring list
  Modernizations:
   - driver cleanup and update to use with_info API: ina2xx, lm92,
     lm95234, max1619, max1668, and max6697.
  API updates:
   - removed unused devm_hwmon_device_unregister() API function
  Other notable changes
   - implement and use generic bus access delay for pmbus drivers
   - use with scoped for each OF child loop in several drivers
   - module unloading fixes for gsc-hwmon and ntc_thermistor drivers
   - converted various drivers to use multi-byte regmap operations
   - adt7475: Improved devicetree based configuration
   - ltc2947: Move to firmware agnostic API
   - ltc2978: Converted devicetree description to yaml
   - max16065: Addressed overflows when writing limit attributes
  Various other minor cleanups, fixes and improvements"
* tag 'hwmon-for-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (96 commits)
  hwmon: Remove devm_hwmon_device_unregister() API function
  hwmon: (sch5636) Print unknown ID in error string via %*pE
  hwmon: (sht21) Use %*ph to print small buffer
  hwmon: (pmbus/mpq7932) Constify struct regulator_desc
  hwmon: pmbus: pli12096bc: Add write delay
  hwmon: pmbus: zl6100: Use generic code
  hwmon: pmbus: ucd9000: Use generic code
  hwmon: pmbus: max15301: Use generic code
  hwmon: pmbus: Implement generic bus access delay
  hwmon: (ina2xx) Use shunt voltage to calculate current
  hwmon: (ina2xx) Add support for current limits
  hwmon: (ina2xx) Pass register to alert limit write functions
  hwmon: (ina2xx) Convert to use with_info hwmon API
  hwmon: (ina2xx) Move ina2xx_get_value()
  hwmon: (ina2xx) Set alert latch
  hwmon: (ina2xx) Consolidate chip initialization code
  hwmon: (ina2xx) Fix various overflow issues
  hwmon: (ina2xx) Re-initialize chip using regmap functions
  hwmon: (ina2xx) Use local regmap pointer if used more than once
  hwmon: (ina2xx) Mark regmap_config as const
  ...
			
			
This commit is contained in:
		
						commit
						c27ea952c6
					
				
					 65 changed files with 3740 additions and 2880 deletions
				
			
		|  | @ -45,12 +45,31 @@ properties: | |||
|       the pwm uses a logic low output for 100% duty cycle. If set to 1 the pwm | ||||
|       uses a logic high output for 100% duty cycle. | ||||
|     $ref: /schemas/types.yaml#/definitions/uint32-array | ||||
|     deprecated: true | ||||
|     minItems: 3 | ||||
|     maxItems: 3 | ||||
|     items: | ||||
|       enum: [0, 1] | ||||
|       default: 1 | ||||
| 
 | ||||
|   "#pwm-cells": | ||||
|     const: 4 | ||||
|     description: | | ||||
|       Number of cells in a PWM specifier. | ||||
|       - 0: The PWM channel | ||||
|       - 1: The PWM period in nanoseconds | ||||
|            - 90909091 (11 Hz) | ||||
|            - 71428571 (14 Hz) | ||||
|            - 45454545 (22 Hz) | ||||
|            - 34482759 (29 Hz) | ||||
|            - 28571429 (35 Hz) | ||||
|            - 22727273 (44 Hz) | ||||
|            - 17241379 (58 Hz) | ||||
|            - 11363636 (88 Hz) | ||||
|            - 44444 (22 kHz) | ||||
|       - 2: PWM flags 0 or PWM_POLARITY_INVERTED | ||||
|       - 3: The default PWM duty cycle in nanoseconds | ||||
| 
 | ||||
| patternProperties: | ||||
|   "^adi,bypass-attenuator-in[0-4]$": | ||||
|     description: | | ||||
|  | @ -81,6 +100,10 @@ patternProperties: | |||
|       - smbalert# | ||||
|       - gpio | ||||
| 
 | ||||
|   "^fan-[0-9]+$": | ||||
|     $ref: fan-common.yaml# | ||||
|     unevaluatedProperties: false | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
|  | @ -89,17 +112,27 @@ additionalProperties: false | |||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     #include <dt-bindings/pwm/pwm.h> | ||||
|     i2c { | ||||
|       #address-cells = <1>; | ||||
|       #size-cells = <0>; | ||||
| 
 | ||||
|       hwmon@2e { | ||||
|       pwm: hwmon@2e { | ||||
|         compatible = "adi,adt7476"; | ||||
|         reg = <0x2e>; | ||||
|         adi,bypass-attenuator-in0 = <1>; | ||||
|         adi,bypass-attenuator-in1 = <0>; | ||||
|         adi,pwm-active-state = <1 0 1>; | ||||
|         adi,pin10-function = "smbalert#"; | ||||
|         adi,pin14-function = "tach4"; | ||||
|         #pwm-cells = <4>; | ||||
| 
 | ||||
|         /* PWMs at 22.5 kHz frequency, 50% duty*/ | ||||
|         fan-0 { | ||||
|           pwms = <&pwm 0 44444 0 22222>; | ||||
|         }; | ||||
| 
 | ||||
|         fan-1 { | ||||
|           pwms = <&pwm 2 44444 0 22222>; | ||||
|         }; | ||||
|       }; | ||||
|     }; | ||||
|  |  | |||
							
								
								
									
										94
									
								
								Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||||
| %YAML 1.2 | ||||
| --- | ||||
| $id: http://devicetree.org/schemas/hwmon/lltc,ltc2978.yaml# | ||||
| $schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| 
 | ||||
| title: Octal Digital Power-supply monitor/supervisor/sequencer/margin controller. | ||||
| 
 | ||||
| maintainers: | ||||
|   - Frank Li <Frank.Li@nxp.com> | ||||
| 
 | ||||
| properties: | ||||
|   compatible: | ||||
|     enum: | ||||
|       - lltc,ltc2972 | ||||
|       - lltc,ltc2974 | ||||
|       - lltc,ltc2975 | ||||
|       - lltc,ltc2977 | ||||
|       - lltc,ltc2978 | ||||
|       - lltc,ltc2979 | ||||
|       - lltc,ltc2980 | ||||
|       - lltc,ltc3880 | ||||
|       - lltc,ltc3882 | ||||
|       - lltc,ltc3883 | ||||
|       - lltc,ltc3884 | ||||
|       - lltc,ltc3886 | ||||
|       - lltc,ltc3887 | ||||
|       - lltc,ltc3889 | ||||
|       - lltc,ltc7880 | ||||
|       - lltc,ltm2987 | ||||
|       - lltc,ltm4664 | ||||
|       - lltc,ltm4675 | ||||
|       - lltc,ltm4676 | ||||
|       - lltc,ltm4677 | ||||
|       - lltc,ltm4678 | ||||
|       - lltc,ltm4680 | ||||
|       - lltc,ltm4686 | ||||
|       - lltc,ltm4700 | ||||
| 
 | ||||
|   reg: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   regulators: | ||||
|     type: object | ||||
|     description: | | ||||
|       list of regulators provided by this controller. | ||||
|       Valid names of regulators depend on number of supplies supported per device: | ||||
|       * ltc2972 vout0 - vout1 | ||||
|       * ltc2974, ltc2975 : vout0 - vout3 | ||||
|       * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 | ||||
|       * ltc2978 : vout0 - vout7 | ||||
|       * ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1 | ||||
|       * ltc7880 : vout0 - vout1 | ||||
|       * ltc3883 : vout0 | ||||
|       * ltm4664 : vout0 - vout1 | ||||
|       * ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1 | ||||
|       * ltm4680, ltm4686 : vout0 - vout1 | ||||
|       * ltm4700 : vout0 - vout1 | ||||
| 
 | ||||
|     patternProperties: | ||||
|       "^vout[0-7]$": | ||||
|         $ref: /schemas/regulator/regulator.yaml# | ||||
|         type: object | ||||
|         unevaluatedProperties: false | ||||
| 
 | ||||
|     additionalProperties: false | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
| 
 | ||||
| additionalProperties: false | ||||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     i2c { | ||||
|         #address-cells = <1>; | ||||
|         #size-cells = <0>; | ||||
| 
 | ||||
|         regulator@5e { | ||||
|             compatible = "lltc,ltc2978"; | ||||
|             reg = <0x5e>; | ||||
| 
 | ||||
|             regulators { | ||||
|                 vout0 { | ||||
|                      regulator-name = "FPGA-2.5V"; | ||||
|                 }; | ||||
|                 vout2 { | ||||
|                      regulator-name = "FPGA-1.5V"; | ||||
|                 }; | ||||
|             }; | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|  | @ -1,62 +0,0 @@ | |||
| ltc2978 | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible: should contain one of: | ||||
|   * "lltc,ltc2972" | ||||
|   * "lltc,ltc2974" | ||||
|   * "lltc,ltc2975" | ||||
|   * "lltc,ltc2977" | ||||
|   * "lltc,ltc2978" | ||||
|   * "lltc,ltc2979" | ||||
|   * "lltc,ltc2980" | ||||
|   * "lltc,ltc3880" | ||||
|   * "lltc,ltc3882" | ||||
|   * "lltc,ltc3883" | ||||
|   * "lltc,ltc3884" | ||||
|   * "lltc,ltc3886" | ||||
|   * "lltc,ltc3887" | ||||
|   * "lltc,ltc3889" | ||||
|   * "lltc,ltc7880" | ||||
|   * "lltc,ltm2987" | ||||
|   * "lltc,ltm4664" | ||||
|   * "lltc,ltm4675" | ||||
|   * "lltc,ltm4676" | ||||
|   * "lltc,ltm4677" | ||||
|   * "lltc,ltm4678" | ||||
|   * "lltc,ltm4680" | ||||
|   * "lltc,ltm4686" | ||||
|   * "lltc,ltm4700" | ||||
| - reg: I2C slave address | ||||
| 
 | ||||
| Optional properties: | ||||
| - regulators: A node that houses a sub-node for each regulator controlled by | ||||
|   the device. Each sub-node is identified using the node's name, with valid | ||||
|   values listed below. The content of each sub-node is defined by the | ||||
|   standard binding for regulators; see regulator.txt. | ||||
| 
 | ||||
| Valid names of regulators depend on number of supplies supported per device: | ||||
|   * ltc2972 vout0 - vout1 | ||||
|   * ltc2974, ltc2975 : vout0 - vout3 | ||||
|   * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 | ||||
|   * ltc2978 : vout0 - vout7 | ||||
|   * ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1 | ||||
|   * ltc7880 : vout0 - vout1 | ||||
|   * ltc3883 : vout0 | ||||
|   * ltm4664 : vout0 - vout1 | ||||
|   * ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1 | ||||
|   * ltm4680, ltm4686 : vout0 - vout1 | ||||
|   * ltm4700 : vout0 - vout1 | ||||
| 
 | ||||
| Example: | ||||
| ltc2978@5e { | ||||
| 	compatible = "lltc,ltc2978"; | ||||
| 	reg = <0x5e>; | ||||
| 	regulators { | ||||
| 		vout0 { | ||||
| 			regulator-name = "FPGA-2.5V"; | ||||
| 		}; | ||||
| 		vout2 { | ||||
| 			regulator-name = "FPGA-1.5V"; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
							
								
								
									
										70
									
								
								Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) | ||||
| %YAML 1.2 | ||||
| --- | ||||
| $id: http://devicetree.org/schemas/hwmon/maxim,max31790.yaml# | ||||
| $schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| 
 | ||||
| title: The Maxim MAX31790 Fan Controller | ||||
| 
 | ||||
| maintainers: | ||||
|   - Guenter Roeck <linux@roeck-us.net> | ||||
|   - Chanh Nguyen <chanh@os.amperecomputing.com> | ||||
| 
 | ||||
| description: > | ||||
|   The MAX31790 controls the speeds of up to six fans using six | ||||
|   independent PWM outputs. The desired fan speeds (or PWM duty cycles) | ||||
|   are written through the I2C interface. | ||||
| 
 | ||||
|   Datasheets: | ||||
|     https://datasheets.maximintegrated.com/en/ds/MAX31790.pdf | ||||
| 
 | ||||
| properties: | ||||
|   compatible: | ||||
|     const: maxim,max31790 | ||||
| 
 | ||||
|   reg: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   clocks: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   resets: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   "#pwm-cells": | ||||
|     const: 1 | ||||
| 
 | ||||
| patternProperties: | ||||
|   "^fan-[0-9]+$": | ||||
|     $ref: fan-common.yaml# | ||||
|     unevaluatedProperties: false | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
| 
 | ||||
| additionalProperties: false | ||||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     i2c { | ||||
|       #address-cells = <1>; | ||||
|       #size-cells = <0>; | ||||
| 
 | ||||
|       pwm_provider: fan-controller@20 { | ||||
|         compatible = "maxim,max31790"; | ||||
|         reg = <0x20>; | ||||
|         clocks = <&sys_clk>; | ||||
|         resets = <&reset 0>; | ||||
|         #pwm-cells = <1>; | ||||
| 
 | ||||
|         fan-0 { | ||||
|           pwms = <&pwm_provider 1>; | ||||
|         }; | ||||
| 
 | ||||
|         fan-1 { | ||||
|           pwms = <&pwm_provider 2>; | ||||
|         }; | ||||
|       }; | ||||
|     }; | ||||
| 
 | ||||
|  | @ -0,0 +1,43 @@ | |||
| # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause | ||||
| %YAML 1.2 | ||||
| --- | ||||
| $id: http://devicetree.org/schemas/hwmon/sophgo,sg2042-hwmon-mcu.yaml# | ||||
| $schema: http://devicetree.org/meta-schemas/core.yaml# | ||||
| 
 | ||||
| title: Sophgo SG2042 onboard MCU support | ||||
| 
 | ||||
| maintainers: | ||||
|   - Inochi Amaoto <inochiama@outlook.com> | ||||
| 
 | ||||
| properties: | ||||
|   compatible: | ||||
|     const: sophgo,sg2042-hwmon-mcu | ||||
| 
 | ||||
|   reg: | ||||
|     maxItems: 1 | ||||
| 
 | ||||
|   "#thermal-sensor-cells": | ||||
|     const: 1 | ||||
| 
 | ||||
| required: | ||||
|   - compatible | ||||
|   - reg | ||||
|   - "#thermal-sensor-cells" | ||||
| 
 | ||||
| allOf: | ||||
|   - $ref: /schemas/thermal/thermal-sensor.yaml# | ||||
| 
 | ||||
| unevaluatedProperties: false | ||||
| 
 | ||||
| examples: | ||||
|   - | | ||||
|     i2c { | ||||
|         #address-cells = <1>; | ||||
|         #size-cells = <0>; | ||||
| 
 | ||||
|         hwmon@17 { | ||||
|             compatible = "sophgo,sg2042-hwmon-mcu"; | ||||
|             reg = <0x17>; | ||||
|             #thermal-sensor-cells = <1>; | ||||
|         }; | ||||
|     }; | ||||
|  | @ -38,8 +38,6 @@ register/unregister functions:: | |||
| 
 | ||||
|   void hwmon_device_unregister(struct device *dev); | ||||
| 
 | ||||
|   void devm_hwmon_device_unregister(struct device *dev); | ||||
| 
 | ||||
|   char *hwmon_sanitize_name(const char *name); | ||||
| 
 | ||||
|   char *devm_hwmon_sanitize_name(struct device *dev, const char *name); | ||||
|  | @ -64,11 +62,6 @@ monitoring device structure. This function must be called from the driver | |||
| remove function if the hardware monitoring device was registered with | ||||
| hwmon_device_register_with_info. | ||||
| 
 | ||||
| devm_hwmon_device_unregister does not normally have to be called. It is only | ||||
| needed for error handling, and only needed if the driver probe fails after | ||||
| the call to devm_hwmon_device_register_with_info and if the automatic (device | ||||
| managed) removal would be too late. | ||||
| 
 | ||||
| All supported hwmon device registration functions only accept valid device | ||||
| names. Device names including invalid characters (whitespace, '*', or '-') | ||||
| will be rejected. The 'name' parameter is mandatory. | ||||
|  |  | |||
|  | @ -99,6 +99,10 @@ Sysfs entries for ina226, ina230 and ina231 only | |||
| ------------------------------------------------ | ||||
| 
 | ||||
| ======================= ==================================================== | ||||
| curr1_lcrit		Critical low current | ||||
| curr1_crit		Critical high current | ||||
| curr1_lcrit_alarm	Current critical low alarm | ||||
| curr1_crit_alarm	Current critical high alarm | ||||
| in0_lcrit		Critical low shunt voltage | ||||
| in0_crit		Critical high shunt voltage | ||||
| in0_lcrit_alarm		Shunt voltage critical low alarm | ||||
|  |  | |||
|  | @ -206,6 +206,7 @@ Hardware Monitoring Kernel Drivers | |||
|    sch5636 | ||||
|    scpi-hwmon | ||||
|    sfctemp | ||||
|    sg2042-mcu | ||||
|    sht15 | ||||
|    sht21 | ||||
|    sht3x | ||||
|  |  | |||
|  | @ -3,29 +3,29 @@ Kernel driver lm92 | |||
| 
 | ||||
| Supported chips: | ||||
| 
 | ||||
|   * National Semiconductor LM92 | ||||
|   * National Semiconductor / Texas Instruments LM92 | ||||
| 
 | ||||
|     Prefix: 'lm92' | ||||
| 
 | ||||
|     Addresses scanned: I2C 0x48 - 0x4b | ||||
| 
 | ||||
|     Datasheet: http://www.national.com/pf/LM/LM92.html | ||||
|     Datasheet: https://www.ti.com/lit/gpn/LM92 | ||||
| 
 | ||||
|   * National Semiconductor LM76 | ||||
|   * National Semiconductor / Texas Instruments LM76 | ||||
| 
 | ||||
|     Prefix: 'lm92' | ||||
| 
 | ||||
|     Addresses scanned: none, force parameter needed | ||||
|     Addresses scanned: none, must be instantiated explicitly | ||||
| 
 | ||||
|     Datasheet: http://www.national.com/pf/LM/LM76.html | ||||
|     Datasheet: https://www.ti.com/lit/gpn/LM76 | ||||
| 
 | ||||
|   * Maxim MAX6633/MAX6634/MAX6635 | ||||
|   * Maxim /Analog Devices MAX6633/MAX6634/MAX6635 | ||||
| 
 | ||||
|     Prefix: 'max6635' | ||||
| 
 | ||||
|     Addresses scanned: none, force parameter needed | ||||
|     Addresses scanned: none, must be instantiated explicitly | ||||
| 
 | ||||
|     Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 | ||||
|     Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6633-max6635.pdf | ||||
| 
 | ||||
| 
 | ||||
| Authors: | ||||
|  | @ -36,13 +36,13 @@ Authors: | |||
| Description | ||||
| ----------- | ||||
| 
 | ||||
| This driver implements support for the National Semiconductor LM92 | ||||
| temperature sensor. | ||||
| This driver implements support for the National Semiconductor / Texas | ||||
| Instruments LM92 temperature sensor. | ||||
| 
 | ||||
| Each LM92 temperature sensor supports a single temperature sensor. There are | ||||
| alarms for high, low, and critical thresholds. There's also an hysteresis to | ||||
| control the thresholds for resetting alarms. | ||||
| 
 | ||||
| Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635, | ||||
| which are mostly compatible. They have not all been tested, so you | ||||
| may need to use the force parameter. | ||||
| The driver also supports LM76 and Maxim MAX6633/MAX6634/MAX6635, which are | ||||
| mostly compatible but do not have a vendor ID register and therefore must be | ||||
| instantiated explicitly. | ||||
|  |  | |||
|  | @ -27,7 +27,3 @@ All temperature values are given in degrees Celsius. Resolution | |||
| is 1.0 degree for the local temperature and for the remote temperature. | ||||
| 
 | ||||
| Only the external sensor has high and low limits. | ||||
| 
 | ||||
| The max1619 driver will not update its values more frequently than every | ||||
| other second; reading them more often will do no harm, but will return | ||||
| 'old' values. | ||||
|  |  | |||
|  | @ -10,41 +10,59 @@ Authors: | |||
| Description: | ||||
| ------------ | ||||
| 
 | ||||
| Handheld devices from One Netbook and Aya Neo provide fan readings and fan | ||||
| control through their embedded controllers. | ||||
| Handheld devices from OneNetbook, AOKZOE, AYANEO, And OrangePi provide fan | ||||
| readings and fan control through their embedded controllers. | ||||
| 
 | ||||
| Currently only supports AMD boards from One X Player, AOK ZOE, and some Aya | ||||
| Neo devices. One X Player Intel boards could be supported if we could figure | ||||
| out the EC registers and values to write to since the EC layout and model is | ||||
| different. Aya Neo devices preceding the AIR may not be supportable as the EC | ||||
| model is different and do not appear to have manual control capabilities. | ||||
| Currently supports OneXPlayer devices, AOKZOE, AYANEO, and OrangePi | ||||
| handheld devices. AYANEO devices preceding the AIR and OneXPlayer devices | ||||
| preceding the Mini A07 are not supportable as the EC model is different | ||||
| and do not have manual control capabilities. | ||||
| 
 | ||||
| Some models have a toggle for changing the behaviour of the "Turbo/Silent" | ||||
| button of the device. It will change the key event that it triggers with | ||||
| a flip of the `tt_toggle` attribute. See below for boards that support this | ||||
| function. | ||||
| Some OneXPlayer and AOKZOE models have a toggle for changing the behaviour | ||||
| of the "Turbo/Silent" button of the device. It will change the key event | ||||
| that it triggers with a flip of the `tt_toggle` attribute. See below for | ||||
| boards that support this function. | ||||
| 
 | ||||
| Supported devices | ||||
| ----------------- | ||||
| 
 | ||||
| Currently the driver supports the following handhelds: | ||||
| 
 | ||||
|  - AOK ZOE A1 | ||||
|  - AOK ZOE A1 PRO | ||||
|  - Aya Neo 2 | ||||
|  - Aya Neo AIR | ||||
|  - Aya Neo AIR Plus (Mendocino) | ||||
|  - Aya Neo AIR Pro | ||||
|  - Aya Neo Geek | ||||
|  - AOKZOE A1 | ||||
|  - AOKZOE A1 PRO | ||||
|  - AYANEO 2 | ||||
|  - AYANEO 2S | ||||
|  - AYANEO AIR | ||||
|  - AYANEO AIR 1S | ||||
|  - AYANEO AIR Plus (Mendocino) | ||||
|  - AYANEO AIR Pro | ||||
|  - AYANEO Flip DS | ||||
|  - AYANEO Flip KB | ||||
|  - AYANEO Geek | ||||
|  - AYANEO Geek 1S | ||||
|  - AYANEO KUN | ||||
|  - OneXPlayer 2 | ||||
|  - OneXPlayer 2 Pro | ||||
|  - OneXPlayer AMD | ||||
|  - OneXPlayer mini AMD | ||||
|  - OneXPlayer mini AMD PRO | ||||
|  - OneXPlayer OneXFly | ||||
|  - OneXPlayer X1 A | ||||
|  - OneXPlayer X1 i | ||||
|  - OneXPlayer X1 mini | ||||
|  - OrangePi NEO-01 | ||||
| 
 | ||||
| "Turbo/Silent" button behaviour toggle is only supported on: | ||||
|  - AOK ZOE A1 | ||||
|  - AOK ZOE A1 PRO | ||||
|  - OneXPlayer 2 | ||||
|  - OneXPlayer 2 Pro | ||||
|  - OneXPlayer mini AMD (only with updated alpha BIOS) | ||||
|  - OneXPlayer mini AMD PRO | ||||
|  - OneXPlayer OneXFly | ||||
|  - OneXPlayer X1 A | ||||
|  - OneXPlayer X1 i | ||||
|  - OneXPlayer X1 mini | ||||
| 
 | ||||
| Sysfs entries | ||||
| ------------- | ||||
|  | @ -52,7 +70,7 @@ Sysfs entries | |||
| The following attributes are supported: | ||||
| 
 | ||||
| fan1_input | ||||
|   Read Only. Reads current fan RMP. | ||||
|   Read Only. Reads current fan RPM. | ||||
| 
 | ||||
| pwm1_enable | ||||
|   Read Write. Enable manual fan control. Write "1" to set to manual, write "0" | ||||
|  |  | |||
							
								
								
									
										78
									
								
								Documentation/hwmon/sg2042-mcu.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								Documentation/hwmon/sg2042-mcu.rst
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| .. SPDX-License-Identifier: GPL-2.0 | ||||
| 
 | ||||
| Kernel driver sg2042-mcu | ||||
| ======================== | ||||
| 
 | ||||
| Supported chips: | ||||
| 
 | ||||
|   * Onboard MCU for sg2042 | ||||
| 
 | ||||
|     Addresses scanned: - | ||||
| 
 | ||||
|     Prefix: 'sg2042-mcu' | ||||
| 
 | ||||
| Authors: | ||||
| 
 | ||||
|   - Inochi Amaoto <inochiama@outlook.com> | ||||
| 
 | ||||
| Description | ||||
| ----------- | ||||
| 
 | ||||
| This driver supprts hardware monitoring for onboard MCU with | ||||
| i2c interface. | ||||
| 
 | ||||
| Usage Notes | ||||
| ----------- | ||||
| 
 | ||||
| This driver does not auto-detect devices. You will have to instantiate | ||||
| the devices explicitly. | ||||
| Please see Documentation/i2c/instantiating-devices.rst for details. | ||||
| 
 | ||||
| Sysfs Attributes | ||||
| ---------------- | ||||
| 
 | ||||
| The following table shows the standard entries support by the driver: | ||||
| 
 | ||||
| ================= ===================================================== | ||||
| Name              Description | ||||
| ================= ===================================================== | ||||
| temp1_input       Measured temperature of SoC | ||||
| temp1_crit        Critical high temperature | ||||
| temp1_crit_hyst   hysteresis temperature restore from Critical | ||||
| temp2_input       Measured temperature of the base board | ||||
| ================= ===================================================== | ||||
| 
 | ||||
| The following table shows the extra entries support by the driver | ||||
| (the MCU device is in i2c subsystem): | ||||
| 
 | ||||
| ================= ======= ============================================= | ||||
| Name              Perm    Description | ||||
| ================= ======= ============================================= | ||||
| reset_count       RO      Reset count of the SoC | ||||
| uptime            RO      Seconds after the MCU is powered | ||||
| reset_reason      RO      Reset reason for the last reset | ||||
| repower_policy    RW      Execution policy when triggering repower | ||||
| ================= ======= ============================================= | ||||
| 
 | ||||
| ``repower_policy`` | ||||
|   The repower is triggered when the temperature of the SoC falls below | ||||
|   the hysteresis temperature after triggering a shutdown due to | ||||
|   reaching the critical temperature. | ||||
|   The valid values for this entry are "repower" and "keep". "keep" will | ||||
|   leave the SoC down when the triggering repower, and "repower" will | ||||
|   boot the SoC. | ||||
| 
 | ||||
| Debugfs Interfaces | ||||
| ------------------ | ||||
| 
 | ||||
| If debugfs is available, this driver exposes some hardware specific | ||||
| data in ``/sys/kernel/debug/sg2042-mcu/*/``. | ||||
| 
 | ||||
| ================= ======= ============================================= | ||||
| Name              Format  Description | ||||
| ================= ======= ============================================= | ||||
| firmware_version  0x%02x  firmware version of the MCU | ||||
| pcb_version       0x%02x  version number of the base board | ||||
| board_type        0x%02x  identifiers for the base board | ||||
| mcu_type          %d      type of the MCU: 0 is STM32, 1 is GD32 | ||||
| ================= ======= ============================================= | ||||
|  | @ -15295,6 +15295,12 @@ S:	Maintained | |||
| F:	Documentation/hwmon/surface_fan.rst | ||||
| F:	drivers/hwmon/surface_fan.c | ||||
| 
 | ||||
| MICROSOFT SURFACE SENSOR THERMAL DRIVER | ||||
| M:	Maximilian Luz <luzmaximilian@gmail.com> | ||||
| L:	linux-hwmon@vger.kernel.org | ||||
| S:	Maintained | ||||
| F:	drivers/hwmon/surface_temp.c | ||||
| 
 | ||||
| MICROSOFT SURFACE GPE LID SUPPORT DRIVER | ||||
| M:	Maximilian Luz <luzmaximilian@gmail.com> | ||||
| L:	platform-driver-x86@vger.kernel.org | ||||
|  |  | |||
|  | @ -1511,9 +1511,10 @@ config SENSORS_LM90 | |||
| config SENSORS_LM92 | ||||
| 	tristate "National Semiconductor LM92 and compatibles" | ||||
| 	depends on I2C | ||||
| 	select REGMAP_I2C | ||||
| 	help | ||||
| 	  If you say yes here you get support for National Semiconductor LM92 | ||||
| 	  and Maxim MAX6635 sensor chips. | ||||
| 	  and LM76 as well as Maxim MAX6633/6634/6635 sensor chips. | ||||
| 
 | ||||
| 	  This driver can also be built as a module. If so, the module | ||||
| 	  will be called lm92. | ||||
|  | @ -1532,6 +1533,7 @@ config SENSORS_LM93 | |||
| config SENSORS_LM95234 | ||||
| 	tristate "National Semiconductor LM95234 and compatibles" | ||||
| 	depends on I2C | ||||
| 	select REGMAP_I2C | ||||
| 	help | ||||
| 	  If you say yes here you get support for the LM95233 and LM95234 | ||||
| 	  temperature sensor chips. | ||||
|  | @ -2066,6 +2068,17 @@ config SENSORS_SFCTEMP | |||
| 	  This driver can also be built as a module.  If so, the module | ||||
| 	  will be called sfctemp. | ||||
| 
 | ||||
| config SENSORS_SG2042_MCU | ||||
| 	tristate "Sophgo onboard MCU support" | ||||
| 	depends on I2C | ||||
| 	depends on ARCH_SOPHGO || COMPILE_TEST | ||||
| 	help | ||||
| 	  Support for onboard MCU of Sophgo SG2042 SoCs. This mcu provides | ||||
| 	  power control and some basic information. | ||||
| 
 | ||||
| 	  This driver can be built as a module. If so, the module | ||||
| 	  will be called sg2042-mcu. | ||||
| 
 | ||||
| config SENSORS_SURFACE_FAN | ||||
| 	tristate "Surface Fan Driver" | ||||
| 	depends on SURFACE_AGGREGATOR | ||||
|  | @ -2080,6 +2093,17 @@ config SENSORS_SURFACE_FAN | |||
| 
 | ||||
| 	  Select M or Y here, if you want to be able to read the fan's speed. | ||||
| 
 | ||||
| config SENSORS_SURFACE_TEMP | ||||
| 	tristate "Microsoft Surface Thermal Sensor Driver" | ||||
| 	depends on SURFACE_AGGREGATOR | ||||
| 	depends on SURFACE_AGGREGATOR_BUS | ||||
| 	help | ||||
| 	  Driver for monitoring thermal sensors connected via the Surface | ||||
| 	  Aggregator Module (embedded controller) on Microsoft Surface devices. | ||||
| 
 | ||||
| 	  This driver can also be built as a module. If so, the module | ||||
| 	  will be called surface_temp. | ||||
| 
 | ||||
| config SENSORS_ADC128D818 | ||||
| 	tristate "Texas Instruments ADC128D818" | ||||
| 	depends on I2C | ||||
|  |  | |||
|  | @ -194,6 +194,7 @@ obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o | |||
| obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o | ||||
| obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o | ||||
| obj-$(CONFIG_SENSORS_SFCTEMP)	+= sfctemp.o | ||||
| obj-$(CONFIG_SENSORS_SG2042_MCU) += sg2042-mcu.o | ||||
| obj-$(CONFIG_SENSORS_SL28CPLD)	+= sl28cpld-hwmon.o | ||||
| obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o | ||||
| obj-$(CONFIG_SENSORS_SHT21)	+= sht21.o | ||||
|  | @ -209,6 +210,7 @@ obj-$(CONFIG_SENSORS_SPARX5)	+= sparx5-temp.o | |||
| obj-$(CONFIG_SENSORS_SPD5118)	+= spd5118.o | ||||
| obj-$(CONFIG_SENSORS_STTS751)	+= stts751.o | ||||
| obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o | ||||
| obj-$(CONFIG_SENSORS_SURFACE_TEMP)+= surface_temp.o | ||||
| obj-$(CONFIG_SENSORS_SY7636A)	+= sy7636a-hwmon.o | ||||
| obj-$(CONFIG_SENSORS_AMC6821)	+= amc6821.o | ||||
| obj-$(CONFIG_SENSORS_TC74)	+= tc74.o | ||||
|  |  | |||
|  | @ -728,30 +728,22 @@ static const int adt7470_freq_map[] = { | |||
| static int pwm1_freq_get(struct device *dev) | ||||
| { | ||||
| 	struct adt7470_data *data = dev_get_drvdata(dev); | ||||
| 	unsigned int cfg_reg_1, cfg_reg_2; | ||||
| 	unsigned int regs[2] = {ADT7470_REG_CFG, ADT7470_REG_CFG_2}; | ||||
| 	u8 cfg_reg[2]; | ||||
| 	int index; | ||||
| 	int err; | ||||
| 
 | ||||
| 	mutex_lock(&data->lock); | ||||
| 	err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); | ||||
| 	if (err < 0) | ||||
| 		goto out; | ||||
| 	err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); | ||||
| 	if (err < 0) | ||||
| 		goto out; | ||||
| 	mutex_unlock(&data->lock); | ||||
| 	err = regmap_multi_reg_read(data->regmap, regs, cfg_reg, 2); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; | ||||
| 	if (!(cfg_reg_1 & ADT7470_CFG_LF)) | ||||
| 	index = (cfg_reg[1] & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; | ||||
| 	if (!(cfg_reg[0] & ADT7470_CFG_LF)) | ||||
| 		index += 8; | ||||
| 	if (index >= ARRAY_SIZE(adt7470_freq_map)) | ||||
| 		index = ARRAY_SIZE(adt7470_freq_map) - 1; | ||||
| 
 | ||||
| 	return adt7470_freq_map[index]; | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&data->lock); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) | ||||
|  |  | |||
|  | @ -21,6 +21,8 @@ | |||
| #include <linux/of.h> | ||||
| #include <linux/util_macros.h> | ||||
| 
 | ||||
| #include <dt-bindings/pwm/pwm.h> | ||||
| 
 | ||||
| /* Indexes for the sysfs hooks */ | ||||
| enum adt_sysfs_id { | ||||
| 	INPUT		= 0, | ||||
|  | @ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct adt7475_pwm_config { | ||||
| 	int index; | ||||
| 	int freq; | ||||
| 	int flags; | ||||
| 	int duty; | ||||
| }; | ||||
| 
 | ||||
| static int _adt7475_pwm_properties_parse_args(u32 args[4], struct adt7475_pwm_config *cfg) | ||||
| { | ||||
| 	int freq_hz; | ||||
| 	int duty; | ||||
| 
 | ||||
| 	if (args[1] == 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	freq_hz = 1000000000UL / args[1]; | ||||
| 	if (args[3] >= args[1]) | ||||
| 		duty = 255; | ||||
| 	else | ||||
| 		duty = div_u64(255ULL * args[3], args[1]); | ||||
| 
 | ||||
| 	cfg->index = args[0]; | ||||
| 	cfg->freq = find_closest(freq_hz, pwmfreq_table, ARRAY_SIZE(pwmfreq_table)); | ||||
| 	cfg->flags = args[2]; | ||||
| 	cfg->duty = duty; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int adt7475_pwm_properties_parse_reference_args(struct fwnode_handle *fwnode, | ||||
| 						       struct adt7475_pwm_config *cfg) | ||||
| { | ||||
| 	int ret, i; | ||||
| 	struct fwnode_reference_args rargs = {}; | ||||
| 	u32 args[4] = {}; | ||||
| 
 | ||||
| 	ret = fwnode_property_get_reference_args(fwnode, "pwms", "#pwm-cells", 0, 0, &rargs); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (rargs.nargs != 4) { | ||||
| 		fwnode_handle_put(rargs.fwnode); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < 4; i++) | ||||
| 		args[i] = rargs.args[i]; | ||||
| 
 | ||||
| 	ret = _adt7475_pwm_properties_parse_args(args, cfg); | ||||
| 
 | ||||
| 	fwnode_handle_put(rargs.fwnode); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int adt7475_pwm_properties_parse_args(struct fwnode_handle *fwnode, | ||||
| 					     struct adt7475_pwm_config *cfg) | ||||
| { | ||||
| 	int ret; | ||||
| 	u32 args[4] = {}; | ||||
| 
 | ||||
| 	ret = fwnode_property_read_u32_array(fwnode, "pwms", args, ARRAY_SIZE(args)); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return _adt7475_pwm_properties_parse_args(args, cfg); | ||||
| } | ||||
| 
 | ||||
| static int adt7475_fan_pwm_config(struct i2c_client *client) | ||||
| { | ||||
| 	struct adt7475_data *data = i2c_get_clientdata(client); | ||||
| 	struct fwnode_handle *child; | ||||
| 	struct adt7475_pwm_config cfg = {}; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	device_for_each_child_node(&client->dev, child) { | ||||
| 		if (!fwnode_property_present(child, "pwms")) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (is_of_node(child)) | ||||
| 			ret = adt7475_pwm_properties_parse_reference_args(child, &cfg); | ||||
| 		else | ||||
| 			ret = adt7475_pwm_properties_parse_args(child, &cfg); | ||||
| 
 | ||||
| 		if (cfg.index >= ADT7475_PWM_COUNT) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		ret = adt7475_read(PWM_CONFIG_REG(cfg.index)); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		data->pwm[CONTROL][cfg.index] = ret; | ||||
| 		if (cfg.flags & PWM_POLARITY_INVERTED) | ||||
| 			data->pwm[CONTROL][cfg.index] |= BIT(4); | ||||
| 		else | ||||
| 			data->pwm[CONTROL][cfg.index] &= ~BIT(4); | ||||
| 
 | ||||
| 		/* Force to manual mode so PWM values take effect */ | ||||
| 		data->pwm[CONTROL][cfg.index] &= ~0xE0; | ||||
| 		data->pwm[CONTROL][cfg.index] |= 0x07 << 5; | ||||
| 
 | ||||
| 		ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(cfg.index), | ||||
| 						data->pwm[CONTROL][cfg.index]); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		data->pwm[INPUT][cfg.index] = cfg.duty; | ||||
| 		ret = i2c_smbus_write_byte_data(client, PWM_REG(cfg.index), | ||||
| 						data->pwm[INPUT][cfg.index]); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		data->range[cfg.index] = adt7475_read(TEMP_TRANGE_REG(cfg.index)); | ||||
| 		data->range[cfg.index] &= ~0xf; | ||||
| 		data->range[cfg.index] |= cfg.freq; | ||||
| 
 | ||||
| 		ret = i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(cfg.index), | ||||
| 						data->range[cfg.index]); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int adt7475_probe(struct i2c_client *client) | ||||
| { | ||||
| 	enum chips chip; | ||||
|  | @ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client) | |||
| 	if (ret && ret != -EINVAL) | ||||
| 		dev_warn(&client->dev, "Error configuring pwm polarity\n"); | ||||
| 
 | ||||
| 	ret = adt7475_fan_pwm_config(client); | ||||
| 	if (ret) | ||||
| 		dev_warn(&client->dev, "Error %d configuring fan/pwm\n", ret); | ||||
| 
 | ||||
| 	/* Start monitoring */ | ||||
| 	switch (chip) { | ||||
| 	case adt7475: | ||||
|  |  | |||
|  | @ -170,21 +170,15 @@ static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp) | |||
| 
 | ||||
| static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) | ||||
| { | ||||
| 	int hyst, temp, ret; | ||||
| 	unsigned int regs[2] = {ADT7X10_T_HYST, ADT7X10_REG_TEMP[index]}; | ||||
| 	int hyst, ret; | ||||
| 	u16 regdata[2]; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	ret = regmap_read(data->regmap, ADT7X10_T_HYST, &hyst); | ||||
| 	if (ret) { | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &temp); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 	ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	hyst = (hyst & ADT7X10_T_HYST_MASK) * 1000; | ||||
| 	hyst = (regdata[0] & ADT7X10_T_HYST_MASK) * 1000; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * hysteresis is stored as a 4 bit offset in the device, convert it | ||||
|  | @ -194,7 +188,7 @@ static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) | |||
| 	if (index == adt7x10_t_alarm_low) | ||||
| 		hyst = -hyst; | ||||
| 
 | ||||
| 	*val = ADT7X10_REG_TO_TEMP(data, temp) - hyst; | ||||
| 	*val = ADT7X10_REG_TO_TEMP(data, regdata[1]) - hyst; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -136,29 +136,25 @@ struct amc6821_data { | |||
|  */ | ||||
| static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps) | ||||
| { | ||||
| 	u32 pwm, regval; | ||||
| 	u32 regs[] = { | ||||
| 		AMC6821_REG_DCY_LOW_TEMP, | ||||
| 		AMC6821_REG_PSV_TEMP, | ||||
| 		channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL | ||||
| 	}; | ||||
| 	u8 regvals[3]; | ||||
| 	int slope; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm); | ||||
| 	err = regmap_multi_reg_read(regmap, regs, regvals, 3); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, ®val); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	temps[0] = regval; | ||||
| 
 | ||||
| 	err = regmap_read(regmap, | ||||
| 			  channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL, | ||||
| 			  ®val); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regval) * 4; | ||||
| 	temps[0] = regvals[1]; | ||||
| 	temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regvals[2]) * 4; | ||||
| 
 | ||||
| 	/* slope is 32 >> <slope bits> in °C */ | ||||
| 	regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval); | ||||
| 	if (regval) | ||||
| 		temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval); | ||||
| 	slope = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regvals[2]); | ||||
| 	if (slope) | ||||
| 		temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - regvals[0], slope); | ||||
| 	else | ||||
| 		temps[2] = 255; | ||||
| 
 | ||||
|  |  | |||
|  | @ -456,7 +456,6 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) | |||
| { | ||||
| 	struct device *dev = &pdev->dev, *hwmon; | ||||
| 	int ret; | ||||
| 	struct device_node *child; | ||||
| 	struct aspeed_pwm_tach_data *priv; | ||||
| 	struct pwm_chip *chip; | ||||
| 
 | ||||
|  | @ -498,10 +497,9 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) | |||
| 	if (ret) | ||||
| 		return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); | ||||
| 
 | ||||
| 	for_each_child_of_node(dev->of_node, child) { | ||||
| 	for_each_child_of_node_scoped(dev->of_node, child) { | ||||
| 		ret = aspeed_create_fan_monitor(dev, child, priv); | ||||
| 		if (ret) { | ||||
| 			of_node_put(child); | ||||
| 			dev_warn(dev, "Failed to create fan %d", ret); | ||||
| 			return 0; | ||||
| 		} | ||||
|  |  | |||
|  | @ -907,7 +907,7 @@ static void aspeed_pwm_tacho_remove(void *data) | |||
| static int aspeed_pwm_tacho_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct device_node *np, *child; | ||||
| 	struct device_node *np; | ||||
| 	struct aspeed_pwm_tacho_data *priv; | ||||
| 	void __iomem *regs; | ||||
| 	struct device *hwmon; | ||||
|  | @ -951,13 +951,11 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) | |||
| 
 | ||||
| 	aspeed_create_type(priv); | ||||
| 
 | ||||
| 	for_each_child_of_node(np, child) { | ||||
| 	for_each_child_of_node_scoped(np, child) { | ||||
| 		ret = aspeed_create_fan(dev, child, priv); | ||||
| 		if (ret) { | ||||
| 			of_node_put(child); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	priv->groups[0] = &pwm_dev_group; | ||||
| 	priv->groups[1] = &fan_dev_group; | ||||
|  |  | |||
|  | @ -740,37 +740,26 @@ static int cc2_probe(struct i2c_client *client) | |||
| 	data->client = client; | ||||
| 
 | ||||
| 	data->regulator = devm_regulator_get_exclusive(dev, "vdd"); | ||||
| 	if (IS_ERR(data->regulator)) { | ||||
| 		dev_err_probe(dev, PTR_ERR(data->regulator), | ||||
| 	if (IS_ERR(data->regulator)) | ||||
| 		return dev_err_probe(dev, PTR_ERR(data->regulator), | ||||
| 				     "Failed to get regulator\n"); | ||||
| 		return PTR_ERR(data->regulator); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = cc2_request_ready_irq(data, dev); | ||||
| 	if (ret) { | ||||
| 		dev_err_probe(dev, ret, "Failed to request ready irq\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		return dev_err_probe(dev, ret, "Failed to request ready irq\n"); | ||||
| 
 | ||||
| 	ret = cc2_request_alarm_irqs(data, dev); | ||||
| 	if (ret) { | ||||
| 		dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); | ||||
| 		goto disable; | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		return dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); | ||||
| 
 | ||||
| 	data->hwmon = devm_hwmon_device_register_with_info(dev, client->name, | ||||
| 							   data, &cc2_chip_info, | ||||
| 							   NULL); | ||||
| 	if (IS_ERR(data->hwmon)) { | ||||
| 		dev_err_probe(dev, PTR_ERR(data->hwmon), | ||||
| 	if (IS_ERR(data->hwmon)) | ||||
| 		return dev_err_probe(dev, PTR_ERR(data->hwmon), | ||||
| 				     "Failed to register hwmon device\n"); | ||||
| 		ret = PTR_ERR(data->hwmon); | ||||
| 	} | ||||
| 
 | ||||
| disable: | ||||
| 	cc2_disable(data); | ||||
| 
 | ||||
| 	return ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void cc2_remove(struct i2c_client *client) | ||||
|  |  | |||
|  | @ -1488,6 +1488,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { | |||
| 		}, | ||||
| 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], | ||||
| 	}, | ||||
| 	{ | ||||
| 		.ident = "Dell Latitude 7320", | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||||
| 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7320"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], | ||||
| 	}, | ||||
| 	{ | ||||
| 		.ident = "Dell Latitude E6440", | ||||
| 		.matches = { | ||||
|  |  | |||
|  | @ -400,6 +400,7 @@ static const struct of_device_id gsc_hwmon_of_match[] = { | |||
| 	{ .compatible = "gw,gsc-adc", }, | ||||
| 	{} | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, gsc_hwmon_of_match); | ||||
| 
 | ||||
| static struct platform_driver gsc_hwmon_driver = { | ||||
| 	.driver = { | ||||
|  |  | |||
|  | @ -1188,24 +1188,6 @@ devm_hwmon_device_register_with_info(struct device *dev, const char *name, | |||
| } | ||||
| EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info); | ||||
| 
 | ||||
| static int devm_hwmon_match(struct device *dev, void *res, void *data) | ||||
| { | ||||
| 	struct device **hwdev = res; | ||||
| 
 | ||||
| 	return *hwdev == data; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * devm_hwmon_device_unregister - removes a previously registered hwmon device | ||||
|  * | ||||
|  * @dev: the parent device of the device to unregister | ||||
|  */ | ||||
| void devm_hwmon_device_unregister(struct device *dev) | ||||
| { | ||||
| 	WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); | ||||
| 
 | ||||
| static char *__hwmon_sanitize_name(struct device *dev, const char *old_name) | ||||
| { | ||||
| 	char *name, *p; | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -813,7 +813,6 @@ static int ina3221_probe_child_from_dt(struct device *dev, | |||
| static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) | ||||
| { | ||||
| 	const struct device_node *np = dev->of_node; | ||||
| 	struct device_node *child; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* Compatible with non-DT platforms */ | ||||
|  | @ -822,13 +821,11 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) | |||
| 
 | ||||
| 	ina->single_shot = of_property_read_bool(np, "ti,single-shot"); | ||||
| 
 | ||||
| 	for_each_child_of_node(np, child) { | ||||
| 	for_each_child_of_node_scoped(np, child) { | ||||
| 		ret = ina3221_probe_child_from_dt(dev, child, ina); | ||||
| 		if (ret) { | ||||
| 			of_node_put(child); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -438,16 +438,21 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
| 		data->disp_negative = true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (boot_cpu_data.x86 == 0x15 && | ||||
| 	data->is_zen = cpu_feature_enabled(X86_FEATURE_ZEN); | ||||
| 	if (data->is_zen) { | ||||
| 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; | ||||
| 		data->read_tempreg = read_tempreg_nb_zen; | ||||
| 	} else if (boot_cpu_data.x86 == 0x15 && | ||||
| 	    ((boot_cpu_data.x86_model & 0xf0) == 0x60 || | ||||
| 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) { | ||||
| 		data->read_htcreg = read_htcreg_nb_f15; | ||||
| 		data->read_tempreg = read_tempreg_nb_f15; | ||||
| 	} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { | ||||
| 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; | ||||
| 		data->read_tempreg = read_tempreg_nb_zen; | ||||
| 		data->is_zen = true; | ||||
| 	} else { | ||||
| 		data->read_htcreg = read_htcreg_pci; | ||||
| 		data->read_tempreg = read_tempreg_pci; | ||||
| 	} | ||||
| 
 | ||||
| 	if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { | ||||
| 		switch (boot_cpu_data.x86_model) { | ||||
| 		case 0x1:	/* Zen */ | ||||
| 		case 0x8:	/* Zen+ */ | ||||
|  | @ -469,10 +474,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
| 			break; | ||||
| 		} | ||||
| 	} else if (boot_cpu_data.x86 == 0x19) { | ||||
| 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; | ||||
| 		data->read_tempreg = read_tempreg_nb_zen; | ||||
| 		data->is_zen = true; | ||||
| 
 | ||||
| 		switch (boot_cpu_data.x86_model) { | ||||
| 		case 0x0 ... 0x1:	/* Zen3 SP3/TR */ | ||||
| 		case 0x8:		/* Zen3 TR Chagall */ | ||||
|  | @ -496,13 +497,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
| 			k10temp_get_ccd_support(data, 12); | ||||
| 			break; | ||||
| 		} | ||||
| 	} else if (boot_cpu_data.x86 == 0x1a) { | ||||
| 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; | ||||
| 		data->read_tempreg = read_tempreg_nb_zen; | ||||
| 		data->is_zen = true; | ||||
| 	} else { | ||||
| 		data->read_htcreg = read_htcreg_pci; | ||||
| 		data->read_tempreg = read_tempreg_pci; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) { | ||||
|  |  | |||
|  | @ -2674,20 +2674,17 @@ static int lm90_parse_dt_channel_info(struct i2c_client *client, | |||
| 				      struct lm90_data *data) | ||||
| { | ||||
| 	int err; | ||||
| 	struct device_node *child; | ||||
| 	struct device *dev = &client->dev; | ||||
| 	const struct device_node *np = dev->of_node; | ||||
| 
 | ||||
| 	for_each_child_of_node(np, child) { | ||||
| 	for_each_child_of_node_scoped(np, child) { | ||||
| 		if (strcmp(child->name, "channel")) | ||||
| 			continue; | ||||
| 
 | ||||
| 		err = lm90_probe_channel_from_dt(client, child, data); | ||||
| 		if (err) { | ||||
| 			of_node_put(child); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -27,15 +27,14 @@ | |||
|  * with the LM92. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/hwmon-sysfs.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * The LM92 and MAX6635 have 2 two-state pins for address selection, | ||||
|  | @ -43,8 +42,6 @@ | |||
|  */ | ||||
| static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, | ||||
| 						I2C_CLIENT_END }; | ||||
| enum chips { lm92, max6635 }; | ||||
| 
 | ||||
| /* The LM92 registers */ | ||||
| #define LM92_REG_CONFIG			0x01 /* 8-bit, RW */ | ||||
| #define LM92_REG_TEMP			0x00 /* 16-bit, RO */ | ||||
|  | @ -66,10 +63,10 @@ static inline int TEMP_FROM_REG(s16 reg) | |||
| 	return reg / 8 * 625 / 10; | ||||
| } | ||||
| 
 | ||||
| static inline s16 TEMP_TO_REG(long val) | ||||
| static inline s16 TEMP_TO_REG(long val, int resolution) | ||||
| { | ||||
| 	val = clamp_val(val, -60000, 160000); | ||||
| 	return val * 10 / 625 * 8; | ||||
| 	return DIV_ROUND_CLOSEST(val << (resolution - 9), 1000) << (16 - resolution); | ||||
| } | ||||
| 
 | ||||
| /* Alarm flags are stored in the 3 LSB of the temperature register */ | ||||
|  | @ -78,239 +75,336 @@ static inline u8 ALARMS_FROM_REG(s16 reg) | |||
| 	return reg & 0x0007; | ||||
| } | ||||
| 
 | ||||
| enum temp_index { | ||||
| 	t_input, | ||||
| 	t_crit, | ||||
| 	t_min, | ||||
| 	t_max, | ||||
| 	t_hyst, | ||||
| 	t_num_regs | ||||
| }; | ||||
| 
 | ||||
| static const u8 regs[t_num_regs] = { | ||||
| 	[t_input] = LM92_REG_TEMP, | ||||
| 	[t_crit] = LM92_REG_TEMP_CRIT, | ||||
| 	[t_min] = LM92_REG_TEMP_LOW, | ||||
| 	[t_max] = LM92_REG_TEMP_HIGH, | ||||
| 	[t_hyst] = LM92_REG_TEMP_HYST, | ||||
| }; | ||||
| 
 | ||||
| /* Client data (each client gets its own) */ | ||||
| struct lm92_data { | ||||
| 	struct i2c_client *client; | ||||
| 	struct regmap *regmap; | ||||
| 	struct mutex update_lock; | ||||
| 	bool valid; /* false until following fields are valid */ | ||||
| 	unsigned long last_updated; /* in jiffies */ | ||||
| 
 | ||||
| 	/* registers values */ | ||||
| 	s16 temp[t_num_regs];	/* index with enum temp_index */ | ||||
| 	int resolution; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Sysfs attributes and callback functions | ||||
|  */ | ||||
| 
 | ||||
| static struct lm92_data *lm92_update_device(struct device *dev) | ||||
| static int lm92_temp_read(struct lm92_data *data, u32 attr, int channel, long *val) | ||||
| { | ||||
| 	struct lm92_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	int i; | ||||
| 	int reg = -1, hyst_reg = -1, alarm_bit = 0; | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	u32 temp; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 
 | ||||
| 	if (time_after(jiffies, data->last_updated + HZ) || | ||||
| 	    !data->valid) { | ||||
| 		dev_dbg(&client->dev, "Updating lm92 data\n"); | ||||
| 		for (i = 0; i < t_num_regs; i++) { | ||||
| 			data->temp[i] = | ||||
| 				i2c_smbus_read_word_swapped(client, regs[i]); | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_input: | ||||
| 		reg = LM92_REG_TEMP; | ||||
| 		break; | ||||
| 	case hwmon_temp_min: | ||||
| 		reg = LM92_REG_TEMP_LOW; | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		reg = LM92_REG_TEMP_HIGH; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		reg = LM92_REG_TEMP_CRIT; | ||||
| 		break; | ||||
| 	case hwmon_temp_min_hyst: | ||||
| 		hyst_reg = LM92_REG_TEMP_LOW; | ||||
| 		break; | ||||
| 	case hwmon_temp_max_hyst: | ||||
| 		hyst_reg = LM92_REG_TEMP_HIGH; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		hyst_reg = LM92_REG_TEMP_CRIT; | ||||
| 		break; | ||||
| 	case hwmon_temp_min_alarm: | ||||
| 		alarm_bit = 0; | ||||
| 		break; | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		alarm_bit = 1; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_alarm: | ||||
| 		alarm_bit = 2; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 		data->last_updated = jiffies; | ||||
| 		data->valid = true; | ||||
| 	if (reg >= 0) { | ||||
| 		ret = regmap_read(regmap, reg, &temp); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = TEMP_FROM_REG(temp); | ||||
| 	} else if (hyst_reg >= 0) { | ||||
| 		u32 regs[2] = { hyst_reg, LM92_REG_TEMP_HYST }; | ||||
| 		u16 regvals[2]; | ||||
| 
 | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		if (attr == hwmon_temp_min_hyst) | ||||
| 			*val = TEMP_FROM_REG(regvals[0]) + TEMP_FROM_REG(regvals[1]); | ||||
| 		else | ||||
| 			*val = TEMP_FROM_REG(regvals[0]) - TEMP_FROM_REG(regvals[1]); | ||||
| 	} else { | ||||
| 		ret = regmap_read(regmap, LM92_REG_TEMP, &temp); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!(temp & BIT(alarm_bit)); | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return data; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, | ||||
| 			 char *buf) | ||||
| static int lm92_chip_read(struct lm92_data *data, u32 attr, long *val) | ||||
| { | ||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||||
| 	struct lm92_data *data = lm92_update_device(dev); | ||||
| 	u32 temp; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); | ||||
| 	switch (attr) { | ||||
| 	case hwmon_chip_alarms: | ||||
| 		ret = regmap_read(data->regmap, LM92_REG_TEMP, &temp); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = ALARMS_FROM_REG(temp); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_store(struct device *dev, | ||||
| 			  struct device_attribute *devattr, const char *buf, | ||||
| 			  size_t count) | ||||
| static int lm92_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, | ||||
| 		     int channel, long *val) | ||||
| { | ||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||||
| 	struct lm92_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	int nr = attr->index; | ||||
| 	long val; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = kstrtol(buf, 10, &val); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->temp[nr] = TEMP_TO_REG(val); | ||||
| 	i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 	return count; | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		return lm92_chip_read(data, attr, val); | ||||
| 	case hwmon_temp: | ||||
| 		return lm92_temp_read(data, attr, channel, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_hyst_show(struct device *dev, | ||||
| 			      struct device_attribute *devattr, char *buf) | ||||
| static int lm92_temp_write(struct lm92_data *data, u32 attr, long val) | ||||
| { | ||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||||
| 	struct lm92_data *data = lm92_update_device(dev); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]) | ||||
| 		       - TEMP_FROM_REG(data->temp[t_hyst])); | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp1_min_hyst_show(struct device *dev, | ||||
| 				   struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct lm92_data *data = lm92_update_device(dev); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min]) | ||||
| 		       + TEMP_FROM_REG(data->temp[t_hyst])); | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_hyst_store(struct device *dev, | ||||
| 			       struct device_attribute *devattr, | ||||
| 			       const char *buf, size_t count) | ||||
| { | ||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||||
| 	struct lm92_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	long val; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = kstrtol(buf, 10, &val); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	int reg, err; | ||||
| 	u32 temp; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_min: | ||||
| 		reg = LM92_REG_TEMP_LOW; | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		reg = LM92_REG_TEMP_HIGH; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		reg = LM92_REG_TEMP_CRIT; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		val = clamp_val(val, -120000, 220000); | ||||
| 		mutex_lock(&data->update_lock); | ||||
| 	data->temp[t_hyst] = | ||||
| 		TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val); | ||||
| 	i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST, | ||||
| 				     data->temp[t_hyst]); | ||||
| 		err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp); | ||||
| 		if (err) | ||||
| 			goto unlock; | ||||
| 		val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution); | ||||
| 		err = regmap_write(regmap, LM92_REG_TEMP_HYST, val); | ||||
| unlock: | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 	return count; | ||||
| 		return err; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return regmap_write(regmap, reg, TEMP_TO_REG(val, data->resolution)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| static int lm92_write(struct device *dev, enum hwmon_sensor_types type, | ||||
| 		      u32 attr, int channel, long val) | ||||
| { | ||||
| 	struct lm92_data *data = lm92_update_device(dev); | ||||
| 	struct lm92_data *data = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input])); | ||||
| 	switch (type) { | ||||
| 	case hwmon_temp: | ||||
| 		return lm92_temp_write(data, attr, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, | ||||
| 			  char *buf) | ||||
| static umode_t lm92_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||
| 			       u32 attr, int channel) | ||||
| { | ||||
| 	int bitnr = to_sensor_dev_attr(attr)->index; | ||||
| 	struct lm92_data *data = lm92_update_device(dev); | ||||
| 	return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1); | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_chip_alarms: | ||||
| 			return 0444; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	case hwmon_temp: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_temp_min: | ||||
| 		case hwmon_temp_max: | ||||
| 		case hwmon_temp_crit: | ||||
| 		case hwmon_temp_crit_hyst: | ||||
| 			return 0644; | ||||
| 		case hwmon_temp_input: | ||||
| 		case hwmon_temp_min_hyst: | ||||
| 		case hwmon_temp_max_hyst: | ||||
| 		case hwmon_temp_min_alarm: | ||||
| 		case hwmon_temp_max_alarm: | ||||
| 		case hwmon_temp_crit_alarm: | ||||
| 			return 0444; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min); | ||||
| static DEVICE_ATTR_RO(temp1_min_hyst); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_max); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, temp_hyst, t_max); | ||||
| static DEVICE_ATTR_RO(alarms); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1); | ||||
| static const struct hwmon_channel_info * const lm92_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), | ||||
| 	HWMON_CHANNEL_INFO(temp, | ||||
| 			   HWMON_T_INPUT | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_HYST | | ||||
| 			   HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||
| 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||
| 			   HWMON_T_CRIT_ALARM), | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_ops lm92_hwmon_ops = { | ||||
| 	.is_visible = lm92_is_visible, | ||||
| 	.read = lm92_read, | ||||
| 	.write = lm92_write, | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_chip_info lm92_chip_info = { | ||||
| 	.ops = &lm92_hwmon_ops, | ||||
| 	.info = lm92_info, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Detection and registration | ||||
|  */ | ||||
| 
 | ||||
| static void lm92_init_client(struct i2c_client *client) | ||||
| static int lm92_init_client(struct regmap *regmap) | ||||
| { | ||||
| 	u8 config; | ||||
| 
 | ||||
| 	/* Start the conversions if needed */ | ||||
| 	config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); | ||||
| 	if (config & 0x01) | ||||
| 		i2c_smbus_write_byte_data(client, LM92_REG_CONFIG, | ||||
| 					  config & 0xFE); | ||||
| 	return regmap_clear_bits(regmap, LM92_REG_CONFIG, 0x01); | ||||
| } | ||||
| 
 | ||||
| static struct attribute *lm92_attrs[] = { | ||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_min.dev_attr.attr, | ||||
| 	&dev_attr_temp1_min_hyst.attr, | ||||
| 	&sensor_dev_attr_temp1_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr, | ||||
| 	&dev_attr_alarms.attr, | ||||
| 	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, | ||||
| 	NULL | ||||
| }; | ||||
| ATTRIBUTE_GROUPS(lm92); | ||||
| 
 | ||||
| /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||
| static int lm92_detect(struct i2c_client *new_client, | ||||
| 		       struct i2c_board_info *info) | ||||
| { | ||||
| 	struct i2c_adapter *adapter = new_client->adapter; | ||||
| 	u8 config; | ||||
| 	u16 man_id; | ||||
| 	u8 config_addr = LM92_REG_CONFIG; | ||||
| 	u8 man_id_addr = LM92_REG_MAN_ID; | ||||
| 	int i, regval; | ||||
| 
 | ||||
| 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ||||
| 					    | I2C_FUNC_SMBUS_WORD_DATA)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG); | ||||
| 	man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID); | ||||
| 
 | ||||
| 	if ((config & 0xe0) == 0x00 && man_id == 0x0180) | ||||
| 		pr_info("lm92: Found National Semiconductor LM92 chip\n"); | ||||
| 	else | ||||
| 	/*
 | ||||
| 	 * Register values repeat with multiples of 8. | ||||
| 	 * Read twice to improve detection accuracy. | ||||
| 	 */ | ||||
| 	for (i = 0; i < 2; i++) { | ||||
| 		regval = i2c_smbus_read_word_data(new_client, man_id_addr); | ||||
| 		if (regval != 0x0180) | ||||
| 			return -ENODEV; | ||||
| 		regval = i2c_smbus_read_byte_data(new_client, config_addr); | ||||
| 		if (regval < 0 || (regval & 0xe0)) | ||||
| 			return -ENODEV; | ||||
| 		config_addr += 8; | ||||
| 		man_id_addr += 8; | ||||
| 	} | ||||
| 
 | ||||
| 	strscpy(info->type, "lm92", I2C_NAME_SIZE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lm92_probe(struct i2c_client *new_client) | ||||
| /* regmap */ | ||||
| 
 | ||||
| static int lm92_reg_read(void *context, unsigned int reg, unsigned int *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (reg == LM92_REG_CONFIG) | ||||
| 		ret = i2c_smbus_read_byte_data(context, reg); | ||||
| 	else | ||||
| 		ret = i2c_smbus_read_word_swapped(context, reg); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	*val = ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lm92_reg_write(void *context, unsigned int reg, unsigned int val) | ||||
| { | ||||
| 	if (reg == LM92_REG_CONFIG) | ||||
| 		return i2c_smbus_write_byte_data(context, LM92_REG_CONFIG, val); | ||||
| 
 | ||||
| 	return i2c_smbus_write_word_swapped(context, reg, val); | ||||
| } | ||||
| 
 | ||||
| static bool lm92_regmap_is_volatile(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg == LM92_REG_TEMP; | ||||
| } | ||||
| 
 | ||||
| static bool lm92_regmap_is_writeable(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg >= LM92_REG_CONFIG; | ||||
| } | ||||
| 
 | ||||
| static const struct regmap_config lm92_regmap_config = { | ||||
| 	.reg_bits = 8, | ||||
| 	.val_bits = 16, | ||||
| 	.max_register = LM92_REG_TEMP_HIGH, | ||||
| 	.cache_type = REGCACHE_MAPLE, | ||||
| 	.volatile_reg = lm92_regmap_is_volatile, | ||||
| 	.writeable_reg = lm92_regmap_is_writeable, | ||||
| }; | ||||
| 
 | ||||
| static const struct regmap_bus lm92_regmap_bus = { | ||||
| 	.reg_write = lm92_reg_write, | ||||
| 	.reg_read = lm92_reg_read, | ||||
| }; | ||||
| 
 | ||||
| static int lm92_probe(struct i2c_client *client) | ||||
| { | ||||
| 	struct device *dev = &client->dev; | ||||
| 	struct device *hwmon_dev; | ||||
| 	struct lm92_data *data; | ||||
| 	struct regmap *regmap; | ||||
| 	int err; | ||||
| 
 | ||||
| 	data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data), | ||||
| 			    GFP_KERNEL); | ||||
| 	regmap = devm_regmap_init(dev, &lm92_regmap_bus, client, | ||||
| 				  &lm92_regmap_config); | ||||
| 	if (IS_ERR(regmap)) | ||||
| 		return PTR_ERR(regmap); | ||||
| 
 | ||||
| 	data = devm_kzalloc(dev, sizeof(struct lm92_data), GFP_KERNEL); | ||||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	data->client = new_client; | ||||
| 	data->regmap = regmap; | ||||
| 	data->resolution = (unsigned long)i2c_get_match_data(client); | ||||
| 	mutex_init(&data->update_lock); | ||||
| 
 | ||||
| 	/* Initialize the chipset */ | ||||
| 	lm92_init_client(new_client); | ||||
| 	err = lm92_init_client(regmap); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, | ||||
| 							   new_client->name, | ||||
| 							   data, lm92_groups); | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, | ||||
| 							 &lm92_chip_info, NULL); | ||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| } | ||||
| 
 | ||||
|  | @ -318,9 +412,10 @@ static int lm92_probe(struct i2c_client *new_client) | |||
|  * Module and driver stuff | ||||
|  */ | ||||
| 
 | ||||
| /* .driver_data is limit register resolution */  | ||||
| static const struct i2c_device_id lm92_id[] = { | ||||
| 	{ "lm92", lm92 }, | ||||
| 	{ "max6635", max6635 }, | ||||
| 	{ "lm92", 13 }, | ||||
| 	{ "max6635", 9 }, | ||||
| 	{ } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(i2c, lm92_id); | ||||
|  |  | |||
|  | @ -8,16 +8,15 @@ | |||
|  * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/hwmon-sysfs.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/sysfs.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/util_macros.h> | ||||
| 
 | ||||
| #define DRVNAME "lm95234" | ||||
| 
 | ||||
|  | @ -32,6 +31,8 @@ static const unsigned short normal_i2c[] = { | |||
| #define LM95234_REG_STATUS		0x02 | ||||
| #define LM95234_REG_CONFIG		0x03 | ||||
| #define LM95234_REG_CONVRATE		0x04 | ||||
| #define LM95234_REG_ENABLE		0x05 | ||||
| #define LM95234_REG_FILTER		0x06 | ||||
| #define LM95234_REG_STS_FAULT		0x07 | ||||
| #define LM95234_REG_STS_TCRIT1		0x08 | ||||
| #define LM95234_REG_STS_TCRIT2		0x09 | ||||
|  | @ -52,541 +53,372 @@ static const unsigned short normal_i2c[] = { | |||
| 
 | ||||
| /* Client data (each client gets its own) */ | ||||
| struct lm95234_data { | ||||
| 	struct i2c_client *client; | ||||
| 	const struct attribute_group *groups[3]; | ||||
| 	struct regmap *regmap; | ||||
| 	struct mutex update_lock; | ||||
| 	unsigned long last_updated, interval;	/* in jiffies */ | ||||
| 	bool valid;		/* false until following fields are valid */ | ||||
| 	/* registers values */ | ||||
| 	int temp[5];		/* temperature (signed) */ | ||||
| 	u32 status;		/* fault/alarm status */ | ||||
| 	u8 tcrit1[5];		/* critical temperature limit */ | ||||
| 	u8 tcrit2[2];		/* high temperature limit */ | ||||
| 	s8 toffset[4];		/* remote temperature offset */ | ||||
| 	u8 thyst;		/* common hysteresis */ | ||||
| 
 | ||||
| 	u8 sensor_type;		/* temperature sensor type */ | ||||
| 	enum chips type; | ||||
| }; | ||||
| 
 | ||||
| static int lm95234_read_temp(struct i2c_client *client, int index, int *t) | ||||
| static int lm95234_read_temp(struct regmap *regmap, int index, long *t) | ||||
| { | ||||
| 	int val; | ||||
| 	u16 temp = 0; | ||||
| 	unsigned int regs[2]; | ||||
| 	int temp = 0, ret; | ||||
| 	u8 regvals[2]; | ||||
| 
 | ||||
| 	if (index) { | ||||
| 		val = i2c_smbus_read_byte_data(client, | ||||
| 					       LM95234_REG_UTEMPH(index - 1)); | ||||
| 		if (val < 0) | ||||
| 			return val; | ||||
| 		temp = val << 8; | ||||
| 		val = i2c_smbus_read_byte_data(client, | ||||
| 					       LM95234_REG_UTEMPL(index - 1)); | ||||
| 		if (val < 0) | ||||
| 			return val; | ||||
| 		temp |= val; | ||||
| 		*t = temp; | ||||
| 		regs[0] = LM95234_REG_UTEMPH(index - 1); | ||||
| 		regs[1] = LM95234_REG_UTEMPL(index - 1); | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		temp = (regvals[0] << 8) | regvals[1]; | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Read signed temperature if unsigned temperature is 0, | ||||
| 	 * or if this is the local sensor. | ||||
| 	 */ | ||||
| 	if (!temp) { | ||||
| 		val = i2c_smbus_read_byte_data(client, | ||||
| 					       LM95234_REG_TEMPH(index)); | ||||
| 		if (val < 0) | ||||
| 			return val; | ||||
| 		temp = val << 8; | ||||
| 		val = i2c_smbus_read_byte_data(client, | ||||
| 					       LM95234_REG_TEMPL(index)); | ||||
| 		if (val < 0) | ||||
| 			return val; | ||||
| 		temp |= val; | ||||
| 		*t = (s16)temp; | ||||
| 		regs[0] = LM95234_REG_TEMPH(index); | ||||
| 		regs[1] = LM95234_REG_TEMPL(index); | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		temp = (regvals[0] << 8) | regvals[1]; | ||||
| 		temp = sign_extend32(temp, 15); | ||||
| 	} | ||||
| 	*t = DIV_ROUND_CLOSEST(temp * 125, 32); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_hyst_get(struct regmap *regmap, int reg, long *val) | ||||
| { | ||||
| 	unsigned int regs[2] = {reg, LM95234_REG_TCRIT_HYST}; | ||||
| 	u8 regvals[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	*val = (regvals[0] - regvals[1]) * 1000; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t lm95234_hyst_set(struct lm95234_data *data, long val) | ||||
| { | ||||
| 	u32 tcrit; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 
 | ||||
| 	ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(0), &tcrit); | ||||
| 	if (ret) | ||||
| 		goto unlock; | ||||
| 
 | ||||
| 	val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000); | ||||
| 	val = clamp_val((int)tcrit - val, 0, 31); | ||||
| 
 | ||||
| 	ret = regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val); | ||||
| unlock: | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_crit_reg(int channel) | ||||
| { | ||||
| 	if (channel == 1 || channel == 2) | ||||
| 		return LM95234_REG_TCRIT2(channel - 1); | ||||
| 	return LM95234_REG_TCRIT1(channel); | ||||
| } | ||||
| 
 | ||||
| static int lm95234_temp_write(struct device *dev, u32 attr, int channel, long val) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_enable: | ||||
| 		if (val && val != 1) | ||||
| 			return -EINVAL; | ||||
| 		return regmap_update_bits(regmap, LM95234_REG_ENABLE, | ||||
| 					  BIT(channel), val ? BIT(channel) : 0); | ||||
| 	case hwmon_temp_type: | ||||
| 		if (val != 1 && val != 2) | ||||
| 			return -EINVAL; | ||||
| 		return regmap_update_bits(regmap, LM95234_REG_REM_MODEL, | ||||
| 					  BIT(channel), | ||||
| 					  val == 1 ? BIT(channel) : 0); | ||||
| 	case hwmon_temp_offset: | ||||
| 		val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500); | ||||
| 		return regmap_write(regmap, LM95234_REG_OFFSET(channel - 1), val); | ||||
| 	case hwmon_temp_max: | ||||
| 		val = clamp_val(val, 0, channel == 1 ? 127000 : 255000); | ||||
| 		val = DIV_ROUND_CLOSEST(val, 1000); | ||||
| 		return regmap_write(regmap, lm95234_crit_reg(channel), val); | ||||
| 	case hwmon_temp_max_hyst: | ||||
| 		return lm95234_hyst_set(data, val); | ||||
| 	case hwmon_temp_crit: | ||||
| 		val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); | ||||
| 		return regmap_write(regmap, LM95234_REG_TCRIT1(channel), val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_alarm_reg(int channel) | ||||
| { | ||||
| 	if (channel == 1 || channel == 2) | ||||
| 		return LM95234_REG_STS_TCRIT2; | ||||
| 	return LM95234_REG_STS_TCRIT1; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_temp_read(struct device *dev, u32 attr, int channel, long *val) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	u32 regval, mask; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_enable: | ||||
| 		ret = regmap_read(regmap, LM95234_REG_ENABLE, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!(regval & BIT(channel)); | ||||
| 		break; | ||||
| 	case hwmon_temp_input: | ||||
| 		return lm95234_read_temp(regmap, channel, val); | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		ret =  regmap_read(regmap, lm95234_alarm_reg(channel), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!(regval & BIT(channel)); | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_alarm: | ||||
| 		ret =  regmap_read(regmap, LM95234_REG_STS_TCRIT1, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!(regval & BIT(channel)); | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		return lm95234_hyst_get(regmap, LM95234_REG_TCRIT1(channel), val); | ||||
| 	case hwmon_temp_type: | ||||
| 		ret = regmap_read(regmap, LM95234_REG_REM_MODEL, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = (regval & BIT(channel)) ? 1 : 2; | ||||
| 		break; | ||||
| 	case hwmon_temp_offset: | ||||
| 		ret = regmap_read(regmap, LM95234_REG_OFFSET(channel - 1), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = sign_extend32(regval, 7) * 500; | ||||
| 		break; | ||||
| 	case hwmon_temp_fault: | ||||
| 		ret = regmap_read(regmap, LM95234_REG_STS_FAULT, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		mask = (BIT(0) | BIT(1)) << ((channel - 1) << 1); | ||||
| 		*val = !!(regval & mask); | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		ret = regmap_read(regmap, lm95234_crit_reg(channel), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = regval * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_max_hyst: | ||||
| 		return lm95234_hyst_get(regmap, lm95234_crit_reg(channel), val); | ||||
| 	case hwmon_temp_crit: | ||||
| 		ret = regmap_read(regmap, LM95234_REG_TCRIT1(channel), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = regval * 1000; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static u16 update_intervals[] = { 143, 364, 1000, 2500 }; | ||||
| 
 | ||||
| /* Fill value cache. Must be called with update lock held. */ | ||||
| 
 | ||||
| static int lm95234_fill_cache(struct lm95234_data *data, | ||||
| 			      struct i2c_client *client) | ||||
| static int lm95234_chip_write(struct device *dev, u32 attr, long val) | ||||
| { | ||||
| 	int i, ret; | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]); | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) { | ||||
| 		ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i)); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		data->tcrit1[i] = ret; | ||||
| 	switch (attr) { | ||||
| 	case hwmon_chip_update_interval: | ||||
| 		val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); | ||||
| 		return regmap_write(data->regmap, LM95234_REG_CONVRATE, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) { | ||||
| 		ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i)); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		data->tcrit2[i] = ret; | ||||
| 	} | ||||
| 	for (i = 0; i < ARRAY_SIZE(data->toffset); i++) { | ||||
| 		ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i)); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		data->toffset[i] = ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	data->thyst = ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	data->sensor_type = ret; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_update_device(struct lm95234_data *data) | ||||
| static int lm95234_chip_read(struct device *dev, u32 attr, long *val) | ||||
| { | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	u32 convrate; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	switch (attr) { | ||||
| 	case hwmon_chip_update_interval: | ||||
| 		ret = regmap_read(data->regmap, LM95234_REG_CONVRATE, &convrate); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 	if (time_after(jiffies, data->last_updated + data->interval) || | ||||
| 	    !data->valid) { | ||||
| 		int i; | ||||
| 
 | ||||
| 		if (!data->valid) { | ||||
| 			ret = lm95234_fill_cache(data, client); | ||||
| 			if (ret < 0) | ||||
| 				goto abort; | ||||
| 		*val = update_intervals[convrate & 0x03]; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 		data->valid = false; | ||||
| 		for (i = 0; i < ARRAY_SIZE(data->temp); i++) { | ||||
| 			ret = lm95234_read_temp(client, i, &data->temp[i]); | ||||
| 			if (ret < 0) | ||||
| 				goto abort; | ||||
| static int lm95234_write(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			 u32 attr, int channel, long val) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		return lm95234_chip_write(dev, attr, val); | ||||
| 	case hwmon_temp: | ||||
| 		return lm95234_temp_write(dev, attr, channel, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 		ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		data->status = ret; | ||||
| 
 | ||||
| 		ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		data->status |= ret << 8; | ||||
| 
 | ||||
| 		ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		data->status |= ret << 16; | ||||
| 
 | ||||
| 		data->last_updated = jiffies; | ||||
| 		data->valid = true; | ||||
| static int lm95234_read(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			u32 attr, int channel, long *val) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		return lm95234_chip_read(dev, attr, val); | ||||
| 	case hwmon_temp: | ||||
| 		return lm95234_temp_read(dev, attr, channel, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	ret = 0; | ||||
| abort: | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| static umode_t lm95234_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||
| 				  u32 attr, int channel) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 	const struct lm95234_data *data = _data; | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	if (data->type == lm95233 && channel > 2) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", | ||||
| 		       DIV_ROUND_CLOSEST(data->temp[index] * 125, 32)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, | ||||
| 			  char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	u32 mask = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%u", !!(data->status & mask)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t type_show(struct device *dev, struct device_attribute *attr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	u8 mask = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n"); | ||||
| } | ||||
| 
 | ||||
| static ssize_t type_store(struct device *dev, struct device_attribute *attr, | ||||
| 			  const char *buf, size_t count) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	unsigned long val; | ||||
| 	u8 mask = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = kstrtoul(buf, 10, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (val != 1 && val != 2) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	if (val == 1) | ||||
| 		data->sensor_type |= mask; | ||||
| 	else | ||||
| 		data->sensor_type &= ~mask; | ||||
| 	data->valid = false; | ||||
| 	i2c_smbus_write_byte_data(data->client, LM95234_REG_REM_MODEL, | ||||
| 				  data->sensor_type); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit2_show(struct device *dev, struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%u", data->tcrit2[index] * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr, | ||||
| 			    const char *buf, size_t count) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	long val; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	val = DIV_ROUND_CLOSEST(clamp_val(val, 0, (index ? 255 : 127) * 1000), | ||||
| 				1000); | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->tcrit2[index] = val; | ||||
| 	i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT2(index), val); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit2_hyst_show(struct device *dev, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Result can be negative, so be careful with unsigned operands */ | ||||
| 	return sprintf(buf, "%d", | ||||
| 		       ((int)data->tcrit2[index] - (int)data->thyst) * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit1_show(struct device *dev, struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 
 | ||||
| 	return sprintf(buf, "%u", data->tcrit1[index] * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr, | ||||
| 			    const char *buf, size_t count) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 	long val; | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->tcrit1[index] = val; | ||||
| 	i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT1(index), val); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit1_hyst_show(struct device *dev, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Result can be negative, so be careful with unsigned operands */ | ||||
| 	return sprintf(buf, "%d", | ||||
| 		       ((int)data->tcrit1[index] - (int)data->thyst) * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t tcrit1_hyst_store(struct device *dev, | ||||
| 				 struct device_attribute *attr, | ||||
| 				 const char *buf, size_t count) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 	long val; | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000); | ||||
| 	val = clamp_val((int)data->tcrit1[index] - val, 0, 31); | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->thyst = val; | ||||
| 	i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT_HYST, val); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t offset_show(struct device *dev, struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d", data->toffset[index] * 500); | ||||
| } | ||||
| 
 | ||||
| static ssize_t offset_store(struct device *dev, struct device_attribute *attr, | ||||
| 			    const char *buf, size_t count) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 	long val; | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Accuracy is 1/2 degrees C */ | ||||
| 	val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500); | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->toffset[index] = val; | ||||
| 	i2c_smbus_write_byte_data(data->client, LM95234_REG_OFFSET(index), val); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t update_interval_show(struct device *dev, | ||||
| 				    struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%lu\n", | ||||
| 		       DIV_ROUND_CLOSEST(data->interval * 1000, HZ)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t update_interval_store(struct device *dev, | ||||
| 				     struct device_attribute *attr, | ||||
| 				     const char *buf, size_t count) | ||||
| { | ||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||
| 	int ret = lm95234_update_device(data); | ||||
| 	unsigned long val; | ||||
| 	u8 regval; | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = kstrtoul(buf, 10, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	for (regval = 0; regval < 3; regval++) { | ||||
| 		if (val <= update_intervals[regval]) | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_chip_update_interval: | ||||
| 			return 0644; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->interval = msecs_to_jiffies(update_intervals[regval]); | ||||
| 	i2c_smbus_write_byte_data(data->client, LM95234_REG_CONVRATE, regval); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| 		break; | ||||
| 	case hwmon_temp: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_temp_input: | ||||
| 		case hwmon_temp_max_alarm: | ||||
| 			return 0444; | ||||
| 		case hwmon_temp_crit_alarm: | ||||
| 		case hwmon_temp_crit_hyst: | ||||
| 			return (channel && channel < 3) ? 0444 : 0; | ||||
| 		case hwmon_temp_type: | ||||
| 		case hwmon_temp_offset: | ||||
| 			return channel ? 0644 : 0; | ||||
| 		case hwmon_temp_fault: | ||||
| 			return channel ? 0444 : 0; | ||||
| 		case hwmon_temp_max: | ||||
| 		case hwmon_temp_enable: | ||||
| 			return 0644; | ||||
| 		case hwmon_temp_max_hyst: | ||||
| 			return channel ? 0444 : 0644; | ||||
| 		case hwmon_temp_crit: | ||||
| 			return (channel && channel < 3) ? 0644 : 0; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, BIT(0) | BIT(1)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, BIT(2) | BIT(3)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, BIT(4) | BIT(5)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, BIT(6) | BIT(7)); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1)); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2)); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp4_type, type, BIT(3)); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp5_type, type, BIT(4)); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RW(temp1_max, tcrit1, 0); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_max, tcrit2, 0); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp3_max, tcrit2, 1); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp4_max, tcrit1, 3); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp5_max, tcrit1, 4); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, tcrit1_hyst, 0); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_max_hyst, tcrit2_hyst, 0); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, tcrit2_hyst, 1); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, tcrit1_hyst, 3); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_max_hyst, tcrit1_hyst, 4); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, BIT(0 + 8)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, BIT(1 + 16)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, BIT(2 + 16)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, BIT(3 + 8)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, BIT(4 + 8)); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_crit, tcrit1, 1); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp3_crit, tcrit1, 2); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, tcrit1_hyst, 1); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, tcrit1_hyst, 2); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, BIT(1 + 8)); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, BIT(2 + 8)); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 0); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 1); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 2); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 3); | ||||
| 
 | ||||
| static DEVICE_ATTR_RW(update_interval); | ||||
| 
 | ||||
| static struct attribute *lm95234_common_attrs[] = { | ||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_type.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_type.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_offset.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_offset.dev_attr.attr, | ||||
| 	&dev_attr_update_interval.attr, | ||||
| static const struct hwmon_channel_info * const lm95234_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), | ||||
| 	HWMON_CHANNEL_INFO(temp, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_ENABLE, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||
| 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | | ||||
| 			   HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||
| 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | | ||||
| 			   HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||
| 			   HWMON_T_OFFSET | HWMON_T_ENABLE, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||
| 			   HWMON_T_OFFSET | HWMON_T_ENABLE), | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group lm95234_common_group = { | ||||
| 	.attrs = lm95234_common_attrs, | ||||
| static const struct hwmon_ops lm95234_hwmon_ops = { | ||||
| 	.is_visible = lm95234_is_visible, | ||||
| 	.read = lm95234_read, | ||||
| 	.write = lm95234_write, | ||||
| }; | ||||
| 
 | ||||
| static struct attribute *lm95234_attrs[] = { | ||||
| 	&sensor_dev_attr_temp4_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_type.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_type.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_max_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max_hyst.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_offset.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_offset.dev_attr.attr, | ||||
| 	NULL | ||||
| static const struct hwmon_chip_info lm95234_chip_info = { | ||||
| 	.ops = &lm95234_hwmon_ops, | ||||
| 	.info = lm95234_info, | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group lm95234_group = { | ||||
| 	.attrs = lm95234_attrs, | ||||
| static bool lm95234_volatile_reg(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	switch (reg) { | ||||
| 	case LM95234_REG_TEMPH(0) ... LM95234_REG_TEMPH(4): | ||||
| 	case LM95234_REG_TEMPL(0) ... LM95234_REG_TEMPL(4): | ||||
| 	case LM95234_REG_UTEMPH(0) ... LM95234_REG_UTEMPH(3): | ||||
| 	case LM95234_REG_UTEMPL(0) ... LM95234_REG_UTEMPL(3): | ||||
| 	case LM95234_REG_STS_FAULT: | ||||
| 	case LM95234_REG_STS_TCRIT1: | ||||
| 	case LM95234_REG_STS_TCRIT2: | ||||
| 	case LM95234_REG_REM_MODEL_STS: | ||||
| 		return true; | ||||
| 	default: | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static bool lm95234_writeable_reg(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	switch (reg) { | ||||
| 	case LM95234_REG_CONFIG ... LM95234_REG_FILTER: | ||||
| 	case LM95234_REG_REM_MODEL ... LM95234_REG_OFFSET(3): | ||||
| 	case LM95234_REG_TCRIT1(0) ... LM95234_REG_TCRIT1(4): | ||||
| 	case LM95234_REG_TCRIT2(0) ... LM95234_REG_TCRIT2(1): | ||||
| 	case LM95234_REG_TCRIT_HYST: | ||||
| 		return true; | ||||
| 	default: | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct regmap_config lm95234_regmap_config = { | ||||
| 	.reg_bits = 8, | ||||
| 	.val_bits = 8, | ||||
| 	.writeable_reg = lm95234_writeable_reg, | ||||
| 	.volatile_reg = lm95234_volatile_reg, | ||||
| 	.cache_type = REGCACHE_MAPLE, | ||||
| }; | ||||
| 
 | ||||
| static int lm95234_detect(struct i2c_client *client, | ||||
|  | @ -649,61 +481,60 @@ static int lm95234_detect(struct i2c_client *client, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_init_client(struct i2c_client *client) | ||||
| static int lm95234_init_client(struct device *dev, struct regmap *regmap) | ||||
| { | ||||
| 	int val, model; | ||||
| 	u32 val, model; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* start conversion if necessary */ | ||||
| 	val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); | ||||
| 	if (val < 0) | ||||
| 		return val; | ||||
| 	if (val & 0x40) | ||||
| 		i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG, | ||||
| 					  val & ~0x40); | ||||
| 	ret = regmap_clear_bits(regmap, LM95234_REG_CONFIG, 0x40); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* If diode type status reports an error, try to fix it */ | ||||
| 	val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); | ||||
| 	if (val < 0) | ||||
| 		return val; | ||||
| 	model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); | ||||
| 	if (model < 0) | ||||
| 		return model; | ||||
| 	ret = regmap_read(regmap, LM95234_REG_REM_MODEL_STS, &val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	ret = regmap_read(regmap, LM95234_REG_REM_MODEL, &model); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	if (model & val) { | ||||
| 		dev_notice(&client->dev, | ||||
| 		dev_notice(dev, | ||||
| 			   "Fixing remote diode type misconfiguration (0x%x)\n", | ||||
| 			   val); | ||||
| 		i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, | ||||
| 					  model & ~val); | ||||
| 		ret = regmap_write(regmap, LM95234_REG_REM_MODEL, model & ~val); | ||||
| 	} | ||||
| 	return 0; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int lm95234_probe(struct i2c_client *client) | ||||
| { | ||||
| 	enum chips type = (uintptr_t)i2c_get_match_data(client); | ||||
| 	struct device *dev = &client->dev; | ||||
| 	struct lm95234_data *data; | ||||
| 	struct device *hwmon_dev; | ||||
| 	struct regmap *regmap; | ||||
| 	int err; | ||||
| 
 | ||||
| 	data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); | ||||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	data->client = client; | ||||
| 	data->type = (uintptr_t)i2c_get_match_data(client); | ||||
| 
 | ||||
| 	regmap = devm_regmap_init_i2c(client, &lm95234_regmap_config); | ||||
| 	if (IS_ERR(regmap)) | ||||
| 		return PTR_ERR(regmap); | ||||
| 
 | ||||
| 	data->regmap = regmap; | ||||
| 	mutex_init(&data->update_lock); | ||||
| 
 | ||||
| 	/* Initialize the LM95234 chip */ | ||||
| 	err = lm95234_init_client(client); | ||||
| 	err = lm95234_init_client(dev, regmap); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 
 | ||||
| 	data->groups[0] = &lm95234_common_group; | ||||
| 	if (type == lm95234) | ||||
| 		data->groups[1] = &lm95234_group; | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, | ||||
| 							   data, data->groups); | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, | ||||
| 							 data, &lm95234_chip_info, NULL); | ||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -161,18 +161,18 @@ static int lm95245_read_temp(struct device *dev, u32 attr, int channel, | |||
| { | ||||
| 	struct lm95245_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	int ret, regl, regh, regvall, regvalh; | ||||
| 	unsigned int regs[2]; | ||||
| 	unsigned int regval; | ||||
| 	u8 regvals[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_input: | ||||
| 		regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S : | ||||
| 		regs[0] = channel ? LM95245_REG_R_REMOTE_TEMPL_S : | ||||
| 				    LM95245_REG_R_LOCAL_TEMPL_S; | ||||
| 		regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S : | ||||
| 		regs[1] = channel ? LM95245_REG_R_REMOTE_TEMPH_S : | ||||
| 				    LM95245_REG_R_LOCAL_TEMPH_S; | ||||
| 		ret = regmap_read(regmap, regl, ®vall); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = regmap_read(regmap, regh, ®valh); | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		/*
 | ||||
|  | @ -181,92 +181,77 @@ static int lm95245_read_temp(struct device *dev, u32 attr, int channel, | |||
| 		 * Use signed calculation for remote if signed bit is set | ||||
| 		 * or if reported temperature is below signed limit. | ||||
| 		 */ | ||||
| 		if (!channel || (regvalh & 0x80) || regvalh < 0x7f) { | ||||
| 			*val = temp_from_reg_signed(regvalh, regvall); | ||||
| 		if (!channel || (regvals[1] & 0x80) || regvals[1] < 0x7f) { | ||||
| 			*val = temp_from_reg_signed(regvals[1], regvals[0]); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U, | ||||
| 				  ®vall); | ||||
| 		if (ret < 0) | ||||
| 		ret = regmap_bulk_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, regvals, 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, | ||||
| 				  ®valh); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = temp_from_reg_unsigned(regvalh, regvall); | ||||
| 		*val = temp_from_reg_unsigned(regvals[0], regvals[1]); | ||||
| 		return 0; | ||||
| 	case hwmon_temp_max: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, | ||||
| 				  ®valh); | ||||
| 				  ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = regvalh * 1000; | ||||
| 		*val = regval * 1000; | ||||
| 		return 0; | ||||
| 	case hwmon_temp_crit: | ||||
| 		regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : | ||||
| 		regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : | ||||
| 				    LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; | ||||
| 		ret = regmap_read(regmap, regh, ®valh); | ||||
| 		ret = regmap_read(regmap, regs[0], ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = regvalh * 1000; | ||||
| 		*val = regval * 1000; | ||||
| 		return 0; | ||||
| 	case hwmon_temp_max_hyst: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, | ||||
| 				  ®valh); | ||||
| 		regs[0] = LM95245_REG_RW_REMOTE_OS_LIMIT; | ||||
| 		regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS; | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, | ||||
| 				  ®vall); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = (regvalh - regvall) * 1000; | ||||
| 		*val = (regvals[0] - regvals[1]) * 1000; | ||||
| 		return 0; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : | ||||
| 		regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : | ||||
| 				    LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; | ||||
| 		ret = regmap_read(regmap, regh, ®valh); | ||||
| 		regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS; | ||||
| 
 | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, | ||||
| 				  ®vall); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = (regvalh - regvall) * 1000; | ||||
| 		*val = (regvals[0] - regvals[1]) * 1000; | ||||
| 		return 0; | ||||
| 	case hwmon_temp_type: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®valh); | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2; | ||||
| 		*val = (regval & CFG2_REMOTE_TT) ? 1 : 2; | ||||
| 		return 0; | ||||
| 	case hwmon_temp_offset: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL, | ||||
| 				  ®vall); | ||||
| 		ret = regmap_bulk_read(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH, | ||||
| 				  ®valh); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = temp_from_reg_signed(regvalh, regvall); | ||||
| 		*val = temp_from_reg_signed(regvals[0], regvals[1]); | ||||
| 		return 0; | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = !!(regvalh & STATUS1_ROS); | ||||
| 		*val = !!(regval & STATUS1_ROS); | ||||
| 		return 0; | ||||
| 	case hwmon_temp_crit_alarm: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); | ||||
| 		*val = !!(regval & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); | ||||
| 		return 0; | ||||
| 	case hwmon_temp_fault: | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); | ||||
| 		ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = !!(regvalh & STATUS1_DIODE_FAULT); | ||||
| 		*val = !!(regval & STATUS1_DIODE_FAULT); | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
|  | @ -279,6 +264,7 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel, | |||
| 	struct lm95245_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	unsigned int regval; | ||||
| 	u8 regvals[2]; | ||||
| 	int ret, reg; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
|  | @ -311,16 +297,10 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel, | |||
| 	case hwmon_temp_offset: | ||||
| 		val = clamp_val(val, -128000, 127875); | ||||
| 		val = val * 256 / 1000; | ||||
| 		mutex_lock(&data->update_lock); | ||||
| 		ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL, | ||||
| 				   val & 0xe0); | ||||
| 		if (ret < 0) { | ||||
| 			mutex_unlock(&data->update_lock); | ||||
| 			return ret; | ||||
| 		} | ||||
| 		ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH, | ||||
| 				   (val >> 8) & 0xff); | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 		regvals[0] = val >> 8; | ||||
| 		regvals[1] = val & 0xe0; | ||||
| 
 | ||||
| 		ret = regmap_bulk_write(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2); | ||||
| 		return ret; | ||||
| 	case hwmon_temp_type: | ||||
| 		if (val != 1 && val != 2) | ||||
|  |  | |||
|  | @ -11,7 +11,8 @@ | |||
| #include <linux/hwmon.h> | ||||
| #include <linux/hwmon-sysfs.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/mod_devicetable.h> | ||||
| #include <linux/property.h> | ||||
| #include <linux/regmap.h> | ||||
| 
 | ||||
| #include "ltc2947.h" | ||||
|  | @ -1034,9 +1035,8 @@ static int ltc2947_setup(struct ltc2947_data *st) | |||
| 		/* 19.89E-6 * 10E9 */ | ||||
| 		st->lsb_energy = 19890; | ||||
| 	} | ||||
| 	ret = of_property_read_u32_array(st->dev->of_node, | ||||
| 					 "adi,accumulator-ctl-pol", accum, | ||||
| 					  ARRAY_SIZE(accum)); | ||||
| 	ret = device_property_read_u32_array(st->dev, "adi,accumulator-ctl-pol", | ||||
| 					     accum, ARRAY_SIZE(accum)); | ||||
| 	if (!ret) { | ||||
| 		u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) | | ||||
| 				LTC2947_ACCUM_POL_2(accum[1]); | ||||
|  | @ -1045,7 +1045,7 @@ static int ltc2947_setup(struct ltc2947_data *st) | |||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	ret = of_property_read_u32(st->dev->of_node, | ||||
| 	ret = device_property_read_u32(st->dev, | ||||
| 				       "adi,accumulation-deadband-microamp", | ||||
| 				       &deadband); | ||||
| 	if (!ret) { | ||||
|  | @ -1056,7 +1056,7 @@ static int ltc2947_setup(struct ltc2947_data *st) | |||
| 			return ret; | ||||
| 	} | ||||
| 	/* check gpio cfg */ | ||||
| 	ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol); | ||||
| 	ret = device_property_read_u32(st->dev, "adi,gpio-out-pol", &pol); | ||||
| 	if (!ret) { | ||||
| 		/* setup GPIO as output */ | ||||
| 		u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) | | ||||
|  | @ -1067,7 +1067,7 @@ static int ltc2947_setup(struct ltc2947_data *st) | |||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum", | ||||
| 	ret = device_property_read_u32_array(st->dev, "adi,gpio-in-accum", | ||||
| 					     accum, ARRAY_SIZE(accum)); | ||||
| 	if (!ret) { | ||||
| 		/*
 | ||||
|  |  | |||
|  | @ -854,33 +854,24 @@ static const struct regmap_config ltc2992_regmap_config = { | |||
| 
 | ||||
| static int ltc2992_parse_dt(struct ltc2992_state *st) | ||||
| { | ||||
| 	struct fwnode_handle *fwnode; | ||||
| 	struct fwnode_handle *child; | ||||
| 	u32 addr; | ||||
| 	u32 val; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	fwnode = dev_fwnode(&st->client->dev); | ||||
| 
 | ||||
| 	fwnode_for_each_available_child_node(fwnode, child) { | ||||
| 	device_for_each_child_node_scoped(&st->client->dev, child) { | ||||
| 		ret = fwnode_property_read_u32(child, "reg", &addr); | ||||
| 		if (ret < 0) { | ||||
| 			fwnode_handle_put(child); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		if (addr > 1) { | ||||
| 			fwnode_handle_put(child); | ||||
| 		if (addr > 1) | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); | ||||
| 		if (!ret) { | ||||
| 			if (!val) { | ||||
| 				fwnode_handle_put(child); | ||||
| 			if (!val) | ||||
| 				return dev_err_probe(&st->client->dev, -EINVAL, | ||||
| 						     "shunt resistor value cannot be zero\n"); | ||||
| 			} | ||||
| 
 | ||||
| 			st->r_sense_uohm[addr] = val; | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -79,7 +79,7 @@ static const bool max16065_have_current[] = { | |||
| }; | ||||
| 
 | ||||
| struct max16065_data { | ||||
| 	enum chips type; | ||||
| 	enum chips chip; | ||||
| 	struct i2c_client *client; | ||||
| 	const struct attribute_group *groups[4]; | ||||
| 	struct mutex update_lock; | ||||
|  | @ -114,9 +114,10 @@ static inline int LIMIT_TO_MV(int limit, int range) | |||
| 	return limit * range / 256; | ||||
| } | ||||
| 
 | ||||
| static inline int MV_TO_LIMIT(int mv, int range) | ||||
| static inline int MV_TO_LIMIT(unsigned long mv, int range) | ||||
| { | ||||
| 	return clamp_val(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255); | ||||
| 	mv = clamp_val(mv, 0, ULONG_MAX / 256); | ||||
| 	return DIV_ROUND_CLOSEST(clamp_val(mv * 256, 0, range * 255), range); | ||||
| } | ||||
| 
 | ||||
| static inline int ADC_TO_CURR(int adc, int gain) | ||||
|  | @ -161,10 +162,17 @@ static struct max16065_data *max16065_update_device(struct device *dev) | |||
| 						     MAX16065_CURR_SENSE); | ||||
| 		} | ||||
| 
 | ||||
| 		for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++) | ||||
| 		for (i = 0; i < 2; i++) | ||||
| 			data->fault[i] | ||||
| 			  = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i)); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * MAX16067 and MAX16068 have separate undervoltage and | ||||
| 		 * overvoltage alarm bits. Squash them together. | ||||
| 		 */ | ||||
| 		if (data->chip == max16067 || data->chip == max16068) | ||||
| 			data->fault[0] |= data->fault[1]; | ||||
| 
 | ||||
| 		data->last_updated = jiffies; | ||||
| 		data->valid = true; | ||||
| 	} | ||||
|  | @ -513,6 +521,7 @@ static int max16065_probe(struct i2c_client *client) | |||
| 	if (unlikely(!data)) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	data->chip = chip; | ||||
| 	data->client = client; | ||||
| 	mutex_init(&data->update_lock); | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,275 +12,356 @@ | |||
|  *   http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/hwmon-sysfs.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/sysfs.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/util_macros.h> | ||||
| 
 | ||||
| static const unsigned short normal_i2c[] = { | ||||
| 	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; | ||||
| 
 | ||||
| /*
 | ||||
|  * The MAX1619 registers | ||||
|  */ | ||||
| #define MAX1619_REG_LOCAL_TEMP		0x00 | ||||
| #define MAX1619_REG_REMOTE_TEMP		0x01 | ||||
| #define MAX1619_REG_STATUS		0x02 | ||||
| #define MAX1619_REG_CONFIG		0x03 | ||||
| #define MAX1619_REG_CONVRATE		0x04 | ||||
| #define MAX1619_REG_REMOTE_HIGH		0x07 | ||||
| #define MAX1619_REG_REMOTE_LOW		0x08 | ||||
| #define MAX1619_REG_REMOTE_CRIT		0x10 | ||||
| #define MAX1619_REG_REMOTE_CRIT_HYST	0x11 | ||||
| #define MAX1619_REG_MAN_ID		0xFE | ||||
| #define MAX1619_REG_CHIP_ID		0xFF | ||||
| 
 | ||||
| #define MAX1619_REG_R_MAN_ID		0xFE | ||||
| #define MAX1619_REG_R_CHIP_ID		0xFF | ||||
| #define MAX1619_REG_R_CONFIG		0x03 | ||||
| #define MAX1619_REG_W_CONFIG		0x09 | ||||
| #define MAX1619_REG_R_CONVRATE		0x04 | ||||
| #define MAX1619_REG_W_CONVRATE		0x0A | ||||
| #define MAX1619_REG_R_STATUS		0x02 | ||||
| #define MAX1619_REG_R_LOCAL_TEMP	0x00 | ||||
| #define MAX1619_REG_R_REMOTE_TEMP	0x01 | ||||
| #define MAX1619_REG_R_REMOTE_HIGH	0x07 | ||||
| #define MAX1619_REG_W_REMOTE_HIGH	0x0D | ||||
| #define MAX1619_REG_R_REMOTE_LOW	0x08 | ||||
| #define MAX1619_REG_W_REMOTE_LOW	0x0E | ||||
| #define MAX1619_REG_R_REMOTE_CRIT	0x10 | ||||
| #define MAX1619_REG_W_REMOTE_CRIT	0x12 | ||||
| #define MAX1619_REG_R_TCRIT_HYST	0x11 | ||||
| #define MAX1619_REG_W_TCRIT_HYST	0x13 | ||||
| 
 | ||||
| /*
 | ||||
|  * Conversions | ||||
|  */ | ||||
| 
 | ||||
| static int temp_from_reg(int val) | ||||
| static int get_alarms(struct regmap *regmap) | ||||
| { | ||||
| 	return (val & 0x80 ? val-0x100 : val) * 1000; | ||||
| 	static u32 regs[2] = { MAX1619_REG_STATUS, MAX1619_REG_CONFIG }; | ||||
| 	u8 regdata[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = regmap_multi_reg_read(regmap, regs, regdata, 2); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* OVERT status bit may be reversed */ | ||||
| 	if (!(regdata[1] & 0x20)) | ||||
| 		regdata[0] ^= 0x02; | ||||
| 
 | ||||
| 	return regdata[0] & 0x1e; | ||||
| } | ||||
| 
 | ||||
| static int temp_to_reg(int val) | ||||
| static int max1619_temp_read(struct regmap *regmap, u32 attr, int channel, long *val) | ||||
| { | ||||
| 	return (val < 0 ? val+0x100*1000 : val) / 1000; | ||||
| } | ||||
| 	int reg = -1, alarm_bit = 0; | ||||
| 	u32 temp; | ||||
| 	int ret; | ||||
| 
 | ||||
| enum temp_index { | ||||
| 	t_input1 = 0, | ||||
| 	t_input2, | ||||
| 	t_low2, | ||||
| 	t_high2, | ||||
| 	t_crit2, | ||||
| 	t_hyst2, | ||||
| 	t_num_regs | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Client data (each client gets its own) | ||||
|  */ | ||||
| 
 | ||||
| struct max1619_data { | ||||
| 	struct i2c_client *client; | ||||
| 	struct mutex update_lock; | ||||
| 	bool valid; /* false until following fields are valid */ | ||||
| 	unsigned long last_updated; /* in jiffies */ | ||||
| 
 | ||||
| 	/* registers values */ | ||||
| 	u8 temp[t_num_regs];	/* index with enum temp_index */ | ||||
| 	u8 alarms; | ||||
| }; | ||||
| 
 | ||||
| static const u8 regs_read[t_num_regs] = { | ||||
| 	[t_input1] = MAX1619_REG_R_LOCAL_TEMP, | ||||
| 	[t_input2] = MAX1619_REG_R_REMOTE_TEMP, | ||||
| 	[t_low2] = MAX1619_REG_R_REMOTE_LOW, | ||||
| 	[t_high2] = MAX1619_REG_R_REMOTE_HIGH, | ||||
| 	[t_crit2] = MAX1619_REG_R_REMOTE_CRIT, | ||||
| 	[t_hyst2] = MAX1619_REG_R_TCRIT_HYST, | ||||
| }; | ||||
| 
 | ||||
| static const u8 regs_write[t_num_regs] = { | ||||
| 	[t_low2] = MAX1619_REG_W_REMOTE_LOW, | ||||
| 	[t_high2] = MAX1619_REG_W_REMOTE_HIGH, | ||||
| 	[t_crit2] = MAX1619_REG_W_REMOTE_CRIT, | ||||
| 	[t_hyst2] = MAX1619_REG_W_TCRIT_HYST, | ||||
| }; | ||||
| 
 | ||||
| static struct max1619_data *max1619_update_device(struct device *dev) | ||||
| { | ||||
| 	struct max1619_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	int config, i; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 
 | ||||
| 	if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { | ||||
| 		dev_dbg(&client->dev, "Updating max1619 data.\n"); | ||||
| 		for (i = 0; i < t_num_regs; i++) | ||||
| 			data->temp[i] = i2c_smbus_read_byte_data(client, | ||||
| 					regs_read[i]); | ||||
| 		data->alarms = i2c_smbus_read_byte_data(client, | ||||
| 					MAX1619_REG_R_STATUS); | ||||
| 		/* If OVERT polarity is low, reverse alarm bit */ | ||||
| 		config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); | ||||
| 		if (!(config & 0x20)) | ||||
| 			data->alarms ^= 0x02; | ||||
| 
 | ||||
| 		data->last_updated = jiffies; | ||||
| 		data->valid = true; | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_input: | ||||
| 		reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP; | ||||
| 		break; | ||||
| 	case hwmon_temp_min: | ||||
| 		reg = MAX1619_REG_REMOTE_LOW; | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		reg = MAX1619_REG_REMOTE_HIGH; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		reg = MAX1619_REG_REMOTE_CRIT; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		reg = MAX1619_REG_REMOTE_CRIT_HYST; | ||||
| 		break; | ||||
| 	case hwmon_temp_min_alarm: | ||||
| 		alarm_bit = 3; | ||||
| 		break; | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		alarm_bit = 4; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_alarm: | ||||
| 		alarm_bit = 1; | ||||
| 		break; | ||||
| 	case hwmon_temp_fault: | ||||
| 		alarm_bit = 2; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return data; | ||||
| 	if (reg >= 0) { | ||||
| 		ret = regmap_read(regmap, reg, &temp); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = sign_extend32(temp, 7) * 1000; | ||||
| 	} else { | ||||
| 		ret = get_alarms(regmap); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = !!(ret & BIT(alarm_bit)); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Sysfs stuff | ||||
|  */ | ||||
| static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 }; | ||||
| 
 | ||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, | ||||
| 			 char *buf) | ||||
| static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val) | ||||
| { | ||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||||
| 	struct max1619_data *data = max1619_update_device(dev); | ||||
| 	int alarms, ret; | ||||
| 	u32 regval; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index])); | ||||
| 	switch (attr) { | ||||
| 	case hwmon_chip_update_interval: | ||||
| 		ret = regmap_read(regmap, MAX1619_REG_CONVRATE, ®val); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = update_intervals[regval & 7]; | ||||
| 		break; | ||||
| 	case hwmon_chip_alarms: | ||||
| 		alarms = get_alarms(regmap); | ||||
| 		if (alarms < 0) | ||||
| 			return alarms; | ||||
| 		*val = alarms; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_store(struct device *dev, | ||||
| 			  struct device_attribute *devattr, const char *buf, | ||||
| 			  size_t count) | ||||
| static int max1619_read(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			u32 attr, int channel, long *val) | ||||
| { | ||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||||
| 	struct max1619_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	long val; | ||||
| 	int err = kstrtol(buf, 10, &val); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	struct regmap *regmap = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->temp[attr->index] = temp_to_reg(val); | ||||
| 	i2c_smbus_write_byte_data(client, regs_write[attr->index], | ||||
| 				  data->temp[attr->index]); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 	return count; | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		return max1619_chip_read(regmap, attr, val); | ||||
| 	case hwmon_temp: | ||||
| 		return max1619_temp_read(regmap, attr, channel, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| static int max1619_chip_write(struct regmap *regmap, u32 attr, long val) | ||||
| { | ||||
| 	struct max1619_data *data = max1619_update_device(dev); | ||||
| 	return sprintf(buf, "%d\n", data->alarms); | ||||
| 	switch (attr) { | ||||
| 	case hwmon_chip_update_interval: | ||||
| 		val = find_closest_descending(val, update_intervals, ARRAY_SIZE(update_intervals)); | ||||
| 		return regmap_write(regmap, MAX1619_REG_CONVRATE, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, | ||||
| 			  char *buf) | ||||
| static int max1619_temp_write(struct regmap *regmap, | ||||
| 			      u32 attr, int channel, long val) | ||||
| { | ||||
| 	int bitnr = to_sensor_dev_attr(attr)->index; | ||||
| 	struct max1619_data *data = max1619_update_device(dev); | ||||
| 	return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); | ||||
| 	int reg; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_min: | ||||
| 		reg = MAX1619_REG_REMOTE_LOW; | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		reg = MAX1619_REG_REMOTE_HIGH; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		reg = MAX1619_REG_REMOTE_CRIT; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		reg = MAX1619_REG_REMOTE_CRIT_HYST; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); | ||||
| 	return regmap_write(regmap, reg, val); | ||||
| } | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2); | ||||
| static int max1619_write(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			 u32 attr, int channel, long val) | ||||
| { | ||||
| 	struct regmap *regmap = dev_get_drvdata(dev); | ||||
| 
 | ||||
| static DEVICE_ATTR_RO(alarms); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		return max1619_chip_write(regmap, attr, val); | ||||
| 	case hwmon_temp: | ||||
| 		return max1619_temp_write(regmap, attr, channel, val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct attribute *max1619_attrs[] = { | ||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_min.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, | ||||
| static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||
| 				  u32 attr, int channel) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case hwmon_chip: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_chip_update_interval: | ||||
| 			return 0644; | ||||
| 		case hwmon_chip_alarms: | ||||
| 			return 0444; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	case hwmon_temp: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_temp_input: | ||||
| 			return 0444; | ||||
| 		case hwmon_temp_min: | ||||
| 		case hwmon_temp_max: | ||||
| 		case hwmon_temp_crit: | ||||
| 		case hwmon_temp_crit_hyst: | ||||
| 			return 0644; | ||||
| 		case hwmon_temp_min_alarm: | ||||
| 		case hwmon_temp_max_alarm: | ||||
| 		case hwmon_temp_crit_alarm: | ||||
| 		case hwmon_temp_fault: | ||||
| 			return 0444; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 	&dev_attr_alarms.attr, | ||||
| 	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, | ||||
| static const struct hwmon_channel_info * const max1619_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL), | ||||
| 	HWMON_CHANNEL_INFO(temp, | ||||
| 			   HWMON_T_INPUT, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||
| 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||
| 			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT), | ||||
| 	NULL | ||||
| }; | ||||
| ATTRIBUTE_GROUPS(max1619); | ||||
| 
 | ||||
| static const struct hwmon_ops max1619_hwmon_ops = { | ||||
| 	.is_visible = max1619_is_visible, | ||||
| 	.read = max1619_read, | ||||
| 	.write = max1619_write, | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_chip_info max1619_chip_info = { | ||||
| 	.ops = &max1619_hwmon_ops, | ||||
| 	.info = max1619_info, | ||||
| }; | ||||
| 
 | ||||
| /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||
| static int max1619_detect(struct i2c_client *client, | ||||
| 			  struct i2c_board_info *info) | ||||
| { | ||||
| 	struct i2c_adapter *adapter = client->adapter; | ||||
| 	u8 reg_config, reg_convrate, reg_status, man_id, chip_id; | ||||
| 	int regval; | ||||
| 
 | ||||
| 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	/* detection */ | ||||
| 	reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); | ||||
| 	reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE); | ||||
| 	reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS); | ||||
| 	if ((reg_config & 0x03) != 0x00 | ||||
| 	 || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { | ||||
| 		dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", | ||||
| 			client->addr); | ||||
| 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG); | ||||
| 	if (regval < 0 || (regval & 0x03)) | ||||
| 		return -ENODEV; | ||||
| 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE); | ||||
| 	if (regval < 0 || regval > 0x07) | ||||
| 		return -ENODEV; | ||||
| 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS); | ||||
| 	if (regval < 0 || (regval & 0x61)) | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	/* identification */ | ||||
| 	man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID); | ||||
| 	chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID); | ||||
| 	if (man_id != 0x4D || chip_id != 0x04) { | ||||
| 		dev_info(&adapter->dev, | ||||
| 			 "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", | ||||
| 			 man_id, chip_id); | ||||
| 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID); | ||||
| 	if (regval != 0x4d) | ||||
| 		return -ENODEV; | ||||
| 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID); | ||||
| 	if (regval != 0x04) | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	strscpy(info->type, "max1619", I2C_NAME_SIZE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void max1619_init_client(struct i2c_client *client) | ||||
| static int max1619_init_chip(struct regmap *regmap) | ||||
| { | ||||
| 	u8 config; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Start the conversions. | ||||
| 	 */ | ||||
| 	i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, | ||||
| 				  5); /* 2 Hz */ | ||||
| 	config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); | ||||
| 	if (config & 0x40) | ||||
| 		i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG, | ||||
| 					  config & 0xBF); /* run */ | ||||
| 	ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5);	/* 2 Hz */ | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Start conversions */ | ||||
| 	return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40); | ||||
| } | ||||
| 
 | ||||
| static int max1619_probe(struct i2c_client *new_client) | ||||
| /* regmap */ | ||||
| 
 | ||||
| static int max1619_reg_read(void *context, unsigned int reg, unsigned int *val) | ||||
| { | ||||
| 	struct max1619_data *data; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(context, reg); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	*val = ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int max1619_reg_write(void *context, unsigned int reg, unsigned int val) | ||||
| { | ||||
| 	int offset = reg < MAX1619_REG_REMOTE_CRIT ? 6 : 2; | ||||
| 
 | ||||
| 	return i2c_smbus_write_byte_data(context, reg + offset, val); | ||||
| } | ||||
| 
 | ||||
| static bool max1619_regmap_is_volatile(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg <= MAX1619_REG_STATUS; | ||||
| } | ||||
| 
 | ||||
| static bool max1619_regmap_is_writeable(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg > MAX1619_REG_STATUS && reg <= MAX1619_REG_REMOTE_CRIT_HYST; | ||||
| } | ||||
| 
 | ||||
| static const struct regmap_config max1619_regmap_config = { | ||||
| 	.reg_bits = 8, | ||||
| 	.val_bits = 8, | ||||
| 	.max_register = MAX1619_REG_REMOTE_CRIT_HYST, | ||||
| 	.cache_type = REGCACHE_MAPLE, | ||||
| 	.volatile_reg = max1619_regmap_is_volatile, | ||||
| 	.writeable_reg = max1619_regmap_is_writeable, | ||||
| }; | ||||
| 
 | ||||
| static const struct regmap_bus max1619_regmap_bus = { | ||||
| 	.reg_write = max1619_reg_write, | ||||
| 	.reg_read = max1619_reg_read, | ||||
| }; | ||||
| 
 | ||||
| static int max1619_probe(struct i2c_client *client) | ||||
| { | ||||
| 	struct device *dev = &client->dev; | ||||
| 	struct device *hwmon_dev; | ||||
| 	struct regmap *regmap; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data), | ||||
| 			    GFP_KERNEL); | ||||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 	regmap = devm_regmap_init(dev, &max1619_regmap_bus, client, | ||||
| 				  &max1619_regmap_config); | ||||
| 	if (IS_ERR(regmap)) | ||||
| 		return PTR_ERR(regmap); | ||||
| 
 | ||||
| 	data->client = new_client; | ||||
| 	mutex_init(&data->update_lock); | ||||
| 	ret = max1619_init_chip(regmap); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Initialize the MAX1619 chip */ | ||||
| 	max1619_init_client(new_client); | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, | ||||
| 							   new_client->name, | ||||
| 							   data, | ||||
| 							   max1619_groups); | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, | ||||
| 							 regmap, &max1619_chip_info, NULL); | ||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,15 +6,15 @@ | |||
|  * some credit to Christoph Scheurer, but largely a rewrite | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/hwmon-sysfs.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| /* Addresses to scan */ | ||||
| static const unsigned short max1668_addr_list[] = { | ||||
|  | @ -30,14 +30,10 @@ static const unsigned short max1668_addr_list[] = { | |||
| 
 | ||||
| /* limits */ | ||||
| 
 | ||||
| /* write high limits */ | ||||
| #define MAX1668_REG_LIMH_WR(nr)	(0x13 + 2 * (nr)) | ||||
| /* write low limits */ | ||||
| #define MAX1668_REG_LIML_WR(nr)	(0x14 + 2 * (nr)) | ||||
| /* read high limits */ | ||||
| #define MAX1668_REG_LIMH_RD(nr)	(0x08 + 2 * (nr)) | ||||
| /* high limits */ | ||||
| #define MAX1668_REG_LIMH(nr)	(0x08 + 2 * (nr)) | ||||
| /* read low limits */ | ||||
| #define MAX1668_REG_LIML_RD(nr)	(0x09 + 2 * (nr)) | ||||
| #define MAX1668_REG_LIML(nr)	(0x09 + 2 * (nr)) | ||||
| 
 | ||||
| /* manufacturer and device ID Constants */ | ||||
| #define MAN_ID_MAXIM		0x4d | ||||
|  | @ -50,309 +46,146 @@ static bool read_only; | |||
| module_param(read_only, bool, 0); | ||||
| MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); | ||||
| 
 | ||||
| enum chips { max1668, max1805, max1989 }; | ||||
| 
 | ||||
| struct max1668_data { | ||||
| 	struct i2c_client *client; | ||||
| 	const struct attribute_group *groups[3]; | ||||
| 	enum chips type; | ||||
| 
 | ||||
| 	struct mutex update_lock; | ||||
| 	bool valid;		/* true if following fields are valid */ | ||||
| 	unsigned long last_updated;	/* In jiffies */ | ||||
| 
 | ||||
| 	/* 1x local and 4x remote */ | ||||
| 	s8 temp_max[5]; | ||||
| 	s8 temp_min[5]; | ||||
| 	s8 temp[5]; | ||||
| 	u16 alarms; | ||||
| 	struct regmap *regmap; | ||||
| 	int channels; | ||||
| }; | ||||
| 
 | ||||
| static struct max1668_data *max1668_update_device(struct device *dev) | ||||
| static int max1668_read(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			u32 attr, int channel, long *val) | ||||
| { | ||||
| 	struct max1668_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	struct max1668_data *ret = data; | ||||
| 	s32 val; | ||||
| 	int i; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 
 | ||||
| 	if (data->valid && !time_after(jiffies, | ||||
| 			data->last_updated + HZ + HZ / 2)) | ||||
| 		goto abort; | ||||
| 
 | ||||
| 	for (i = 0; i < 5; i++) { | ||||
| 		val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i)); | ||||
| 		if (unlikely(val < 0)) { | ||||
| 			ret = ERR_PTR(val); | ||||
| 			goto abort; | ||||
| 		} | ||||
| 		data->temp[i] = (s8) val; | ||||
| 
 | ||||
| 		val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i)); | ||||
| 		if (unlikely(val < 0)) { | ||||
| 			ret = ERR_PTR(val); | ||||
| 			goto abort; | ||||
| 		} | ||||
| 		data->temp_max[i] = (s8) val; | ||||
| 
 | ||||
| 		val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i)); | ||||
| 		if (unlikely(val < 0)) { | ||||
| 			ret = ERR_PTR(val); | ||||
| 			goto abort; | ||||
| 		} | ||||
| 		data->temp_min[i] = (s8) val; | ||||
| 	} | ||||
| 
 | ||||
| 	val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1); | ||||
| 	if (unlikely(val < 0)) { | ||||
| 		ret = ERR_PTR(val); | ||||
| 		goto abort; | ||||
| 	} | ||||
| 	data->alarms = val << 8; | ||||
| 
 | ||||
| 	val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2); | ||||
| 	if (unlikely(val < 0)) { | ||||
| 		ret = ERR_PTR(val); | ||||
| 		goto abort; | ||||
| 	} | ||||
| 	data->alarms |= val; | ||||
| 
 | ||||
| 	data->last_updated = jiffies; | ||||
| 	data->valid = true; | ||||
| abort: | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_temp(struct device *dev, | ||||
| 			 struct device_attribute *devattr, char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max1668_data *data = max1668_update_device(dev); | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", data->temp[index] * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_temp_max(struct device *dev, | ||||
| 			     struct device_attribute *devattr, char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max1668_data *data = max1668_update_device(dev); | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", data->temp_max[index] * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_temp_min(struct device *dev, | ||||
| 			     struct device_attribute *devattr, char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max1668_data *data = max1668_update_device(dev); | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", data->temp_min[index] * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, | ||||
| 			  char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	struct max1668_data *data = max1668_update_device(dev); | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); | ||||
| } | ||||
| 
 | ||||
| static ssize_t show_fault(struct device *dev, | ||||
| 			  struct device_attribute *devattr, char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max1668_data *data = max1668_update_device(dev); | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	return sprintf(buf, "%u\n", | ||||
| 		       (data->alarms & (1 << 12)) && data->temp[index] == 127); | ||||
| } | ||||
| 
 | ||||
| static ssize_t set_temp_max(struct device *dev, | ||||
| 			    struct device_attribute *devattr, | ||||
| 			    const char *buf, size_t count) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max1668_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	long temp; | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	u32 regs[2] = { MAX1668_REG_STAT1, MAX1668_REG_TEMP(channel) }; | ||||
| 	u8 regvals[2]; | ||||
| 	u32 regval; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &temp); | ||||
| 	if (ret < 0) | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_input: | ||||
| 		ret = regmap_read(regmap, MAX1668_REG_TEMP(channel), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->temp_max[index] = clamp_val(temp/1000, -128, 127); | ||||
| 	ret = i2c_smbus_write_byte_data(client, | ||||
| 					MAX1668_REG_LIMH_WR(index), | ||||
| 					data->temp_max[index]); | ||||
| 	if (ret < 0) | ||||
| 		count = ret; | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| 		*val = sign_extend32(regval, 7) * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_min: | ||||
| 		ret = regmap_read(regmap, MAX1668_REG_LIML(channel), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = sign_extend32(regval, 7) * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		ret = regmap_read(regmap, MAX1668_REG_LIMH(channel), ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = sign_extend32(regval, 7) * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_min_alarm: | ||||
| 		ret = regmap_read(regmap, | ||||
| 				  channel ? MAX1668_REG_STAT2 : MAX1668_REG_STAT1, | ||||
| 				  ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		if (channel) | ||||
| 			*val = !!(regval & BIT(9 - channel * 2)); | ||||
| 		else | ||||
| 			*val = !!(regval & BIT(5)); | ||||
| 		break; | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		ret = regmap_read(regmap, | ||||
| 				  channel ? MAX1668_REG_STAT2 : MAX1668_REG_STAT1, | ||||
| 				  ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		if (channel) | ||||
| 			*val = !!(regval & BIT(8 - channel * 2)); | ||||
| 		else | ||||
| 			*val = !!(regval & BIT(6)); | ||||
| 		break; | ||||
| 	case hwmon_temp_fault: | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!((regvals[0] & BIT(4)) && regvals[1] == 127); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t set_temp_min(struct device *dev, | ||||
| 			    struct device_attribute *devattr, | ||||
| 			    const char *buf, size_t count) | ||||
| static int max1668_write(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			 u32 attr, int channel, long val) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max1668_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	long temp; | ||||
| 	int ret; | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &temp); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	val = clamp_val(val / 1000, -128, 127); | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	data->temp_min[index] = clamp_val(temp/1000, -128, 127); | ||||
| 	ret = i2c_smbus_write_byte_data(client, | ||||
| 					MAX1668_REG_LIML_WR(index), | ||||
| 					data->temp_min[index]); | ||||
| 	if (ret < 0) | ||||
| 		count = ret; | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return count; | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_min: | ||||
| 		return regmap_write(regmap, MAX1668_REG_LIML(channel), val); | ||||
| 	case hwmon_temp_max: | ||||
| 		return regmap_write(regmap, MAX1668_REG_LIMH(channel), val); | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); | ||||
| static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, | ||||
| 				set_temp_max, 0); | ||||
| static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, | ||||
| 				set_temp_min, 0); | ||||
| static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); | ||||
| static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, | ||||
| 				set_temp_max, 1); | ||||
| static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, | ||||
| 				set_temp_min, 1); | ||||
| static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); | ||||
| static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, | ||||
| 				set_temp_max, 2); | ||||
| static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, | ||||
| 				set_temp_min, 2); | ||||
| static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); | ||||
| static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, | ||||
| 				set_temp_max, 3); | ||||
| static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, | ||||
| 				set_temp_min, 3); | ||||
| static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); | ||||
| static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, | ||||
| 				set_temp_max, 4); | ||||
| static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, | ||||
| 				set_temp_min, 4); | ||||
| static umode_t max1668_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||
| 				  u32 attr, int channel) | ||||
| { | ||||
| 	const struct max1668_data *data = _data; | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); | ||||
| static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); | ||||
| static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7); | ||||
| static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6); | ||||
| static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5); | ||||
| static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4); | ||||
| static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3); | ||||
| static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2); | ||||
| static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1); | ||||
| static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0); | ||||
| 	if (channel >= data->channels) | ||||
| 		return 0; | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); | ||||
| static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); | ||||
| static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); | ||||
| static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_min: | ||||
| 	case hwmon_temp_max: | ||||
| 		return read_only ? 0444 : 0644; | ||||
| 	case hwmon_temp_input: | ||||
| 	case hwmon_temp_min_alarm: | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		return 0444; | ||||
| 	case hwmon_temp_fault: | ||||
| 		if (channel) | ||||
| 			return 0444; | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Attributes common to MAX1668, MAX1989 and MAX1805 */ | ||||
| static struct attribute *max1668_attribute_common[] = { | ||||
| 	&sensor_dev_attr_temp1_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_min.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_min.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_min.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_input.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_min_alarm.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp2_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_fault.dev_attr.attr, | ||||
| static const struct hwmon_channel_info * const max1668_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(temp, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||
| 			   HWMON_T_FAULT, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||
| 			   HWMON_T_FAULT, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||
| 			   HWMON_T_FAULT, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||
| 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||
| 			   HWMON_T_FAULT), | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| /* Attributes not present on MAX1805 */ | ||||
| static struct attribute *max1668_attribute_unique[] = { | ||||
| 	&sensor_dev_attr_temp4_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_min.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_min.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_input.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_min_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_min_alarm.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp4_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_fault.dev_attr.attr, | ||||
| 	NULL | ||||
| static const struct hwmon_ops max1668_hwmon_ops = { | ||||
| 	.is_visible = max1668_is_visible, | ||||
| 	.read = max1668_read, | ||||
| 	.write = max1668_write, | ||||
| }; | ||||
| 
 | ||||
| static umode_t max1668_attribute_mode(struct kobject *kobj, | ||||
| 				     struct attribute *attr, int index) | ||||
| { | ||||
| 	umode_t ret = S_IRUGO; | ||||
| 	if (read_only) | ||||
| 		return ret; | ||||
| 	if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp2_max.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp3_max.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp4_max.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp5_max.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp1_min.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp2_min.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp3_min.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp4_min.dev_attr.attr || | ||||
| 	    attr == &sensor_dev_attr_temp5_min.dev_attr.attr) | ||||
| 		ret |= S_IWUSR; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static const struct attribute_group max1668_group_common = { | ||||
| 	.attrs = max1668_attribute_common, | ||||
| 	.is_visible = max1668_attribute_mode | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group max1668_group_unique = { | ||||
| 	.attrs = max1668_attribute_unique, | ||||
| 	.is_visible = max1668_attribute_mode | ||||
| static const struct hwmon_chip_info max1668_chip_info = { | ||||
| 	.ops = &max1668_hwmon_ops, | ||||
| 	.info = max1668_info, | ||||
| }; | ||||
| 
 | ||||
| /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||
|  | @ -391,6 +224,48 @@ static int max1668_detect(struct i2c_client *client, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* regmap */ | ||||
| 
 | ||||
| static int max1668_reg_read(void *context, unsigned int reg, unsigned int *val) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(context, reg); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	*val = ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int max1668_reg_write(void *context, unsigned int reg, unsigned int val) | ||||
| { | ||||
| 	return i2c_smbus_write_byte_data(context, reg + 11, val); | ||||
| } | ||||
| 
 | ||||
| static bool max1668_regmap_is_volatile(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg <= MAX1668_REG_STAT2; | ||||
| } | ||||
| 
 | ||||
| static bool max1668_regmap_is_writeable(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg > MAX1668_REG_STAT2 && reg <= MAX1668_REG_LIML(4); | ||||
| } | ||||
| 
 | ||||
| static const struct regmap_config max1668_regmap_config = { | ||||
| 	.reg_bits = 8, | ||||
| 	.val_bits = 8, | ||||
| 	.cache_type = REGCACHE_MAPLE, | ||||
| 	.volatile_reg = max1668_regmap_is_volatile, | ||||
| 	.writeable_reg = max1668_regmap_is_writeable, | ||||
| }; | ||||
| 
 | ||||
| static const struct regmap_bus max1668_regmap_bus = { | ||||
| 	.reg_write = max1668_reg_write, | ||||
| 	.reg_read = max1668_reg_read, | ||||
| }; | ||||
| 
 | ||||
| static int max1668_probe(struct i2c_client *client) | ||||
| { | ||||
| 	struct i2c_adapter *adapter = client->adapter; | ||||
|  | @ -405,24 +280,22 @@ static int max1668_probe(struct i2c_client *client) | |||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	data->client = client; | ||||
| 	data->type = (uintptr_t)i2c_get_match_data(client); | ||||
| 	mutex_init(&data->update_lock); | ||||
| 	data->regmap = devm_regmap_init(dev, &max1668_regmap_bus, client, | ||||
| 					&max1668_regmap_config); | ||||
| 	if (IS_ERR(data->regmap)) | ||||
| 		return PTR_ERR(data->regmap); | ||||
| 
 | ||||
| 	/* sysfs hooks */ | ||||
| 	data->groups[0] = &max1668_group_common; | ||||
| 	if (data->type == max1668 || data->type == max1989) | ||||
| 		data->groups[1] = &max1668_group_unique; | ||||
| 	data->channels = (uintptr_t)i2c_get_match_data(client); | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, | ||||
| 							   data, data->groups); | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, | ||||
| 							 &max1668_chip_info, NULL); | ||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| } | ||||
| 
 | ||||
| static const struct i2c_device_id max1668_id[] = { | ||||
| 	{ "max1668", max1668 }, | ||||
| 	{ "max1805", max1805 }, | ||||
| 	{ "max1989", max1989 }, | ||||
| 	{ "max1668", 5 }, | ||||
| 	{ "max1805", 3 }, | ||||
| 	{ "max1989", 5 }, | ||||
| 	{ } | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(i2c, max1668_id); | ||||
|  |  | |||
|  | @ -88,25 +88,16 @@ struct max6639_data { | |||
| 
 | ||||
| static int max6639_temp_read_input(struct device *dev, int channel, long *temp) | ||||
| { | ||||
| 	u32 regs[2] = { MAX6639_REG_TEMP_EXT(channel), MAX6639_REG_TEMP(channel) }; | ||||
| 	struct max6639_data *data = dev_get_drvdata(dev); | ||||
| 	unsigned int val; | ||||
| 	u8 regvals[2]; | ||||
| 	int res; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Lock isn't needed as MAX6639_REG_TEMP wpnt change for at least 250ms after reading | ||||
| 	 * MAX6639_REG_TEMP_EXT | ||||
| 	 */ | ||||
| 	res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val); | ||||
| 	res = regmap_multi_reg_read(data->regmap, regs, regvals, 2); | ||||
| 	if (res < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	*temp = val >> 5; | ||||
| 	res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val); | ||||
| 	if (res < 0) | ||||
| 		return res; | ||||
| 
 | ||||
| 	*temp |= val << 3; | ||||
| 	*temp *= 125; | ||||
| 	*temp = ((regvals[0] >> 5) | (regvals[1] << 3)) * 125; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -290,8 +281,10 @@ static umode_t max6639_fan_is_visible(const void *_data, u32 attr, int channel) | |||
| static int max6639_read_pwm(struct device *dev, u32 attr, int channel, | ||||
| 			    long *pwm_val) | ||||
| { | ||||
| 	u32 regs[2] = { MAX6639_REG_FAN_CONFIG3(channel), MAX6639_REG_GCONFIG }; | ||||
| 	struct max6639_data *data = dev_get_drvdata(dev); | ||||
| 	unsigned int val; | ||||
| 	u8 regvals[2]; | ||||
| 	int res; | ||||
| 	u8 i; | ||||
| 
 | ||||
|  | @ -303,26 +296,13 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel, | |||
| 		*pwm_val = val * 255 / 120; | ||||
| 		return 0; | ||||
| 	case hwmon_pwm_freq: | ||||
| 		mutex_lock(&data->update_lock); | ||||
| 		res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val); | ||||
| 		if (res < 0) { | ||||
| 			mutex_unlock(&data->update_lock); | ||||
| 		res = regmap_multi_reg_read(data->regmap, regs, regvals, 2); | ||||
| 		if (res < 0) | ||||
| 			return res; | ||||
| 		} | ||||
| 		i = val & MAX6639_FAN_CONFIG3_FREQ_MASK; | ||||
| 
 | ||||
| 		res = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &val); | ||||
| 		if (res < 0) { | ||||
| 			mutex_unlock(&data->update_lock); | ||||
| 			return res; | ||||
| 		} | ||||
| 
 | ||||
| 		if (val & MAX6639_GCONFIG_PWM_FREQ_HI) | ||||
| 		i = regvals[0] & MAX6639_FAN_CONFIG3_FREQ_MASK; | ||||
| 		if (regvals[1] & MAX6639_GCONFIG_PWM_FREQ_HI) | ||||
| 			i |= 0x4; | ||||
| 		i &= 0x7; | ||||
| 		*pwm_val = freq_table[i]; | ||||
| 
 | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
|  |  | |||
|  | @ -6,18 +6,17 @@ | |||
|  * Copyright (c) 2011 David George <david.george@ska.ac.za> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/hwmon-sysfs.h> | ||||
| #include <linux/bitfield.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #include <linux/platform_data/max6697.h> | ||||
| #include <linux/regmap.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694, | ||||
| 	     max6697, max6698, max6699 }; | ||||
|  | @ -33,21 +32,36 @@ static const u8 MAX6697_REG_MAX[] = { | |||
| static const u8 MAX6697_REG_CRIT[] = { | ||||
| 			0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 }; | ||||
| 
 | ||||
| #define MAX6697_REG_MIN			0x30 | ||||
| /*
 | ||||
|  * Map device tree / platform data register bit map to chip bit map. | ||||
|  * Map device tree / internal register bit map to chip bit map. | ||||
|  * Applies to alert register and over-temperature register. | ||||
|  */ | ||||
| 
 | ||||
| #define MAX6697_EXTERNAL_MASK_DT	GENMASK(7, 1) | ||||
| #define MAX6697_LOCAL_MASK_DT		BIT(0) | ||||
| #define MAX6697_EXTERNAL_MASK_CHIP	GENMASK(6, 0) | ||||
| #define MAX6697_LOCAL_MASK_CHIP		BIT(7) | ||||
| 
 | ||||
| /* alert - local channel is in bit 6 */ | ||||
| #define MAX6697_ALERT_MAP_BITS(reg)	((((reg) & 0x7e) >> 1) | \ | ||||
| 				 (((reg) & 0x01) << 6) | ((reg) & 0x80)) | ||||
| #define MAX6697_OVERT_MAP_BITS(reg) (((reg) >> 1) | (((reg) & 0x01) << 7)) | ||||
| 
 | ||||
| #define MAX6697_REG_STAT(n)		(0x44 + (n)) | ||||
| /* over-temperature - local channel is in bit 7 */ | ||||
| #define MAX6697_OVERT_MAP_BITS(reg)	\ | ||||
| 	(FIELD_PREP(MAX6697_EXTERNAL_MASK_CHIP, FIELD_GET(MAX6697_EXTERNAL_MASK_DT, reg)) | \ | ||||
| 	 FIELD_PREP(MAX6697_LOCAL_MASK_CHIP, FIELD_GET(MAX6697_LOCAL_MASK_DT, reg))) | ||||
| 
 | ||||
| #define MAX6697_REG_STAT_ALARM		0x44 | ||||
| #define MAX6697_REG_STAT_CRIT		0x45 | ||||
| #define MAX6697_REG_STAT_FAULT		0x46 | ||||
| #define MAX6697_REG_STAT_MIN_ALARM	0x47 | ||||
| 
 | ||||
| #define MAX6697_REG_CONFIG		0x41 | ||||
| #define MAX6581_CONF_EXTENDED		(1 << 1) | ||||
| #define MAX6693_CONF_BETA		(1 << 2) | ||||
| #define MAX6697_CONF_RESISTANCE		(1 << 3) | ||||
| #define MAX6697_CONF_TIMEOUT		(1 << 5) | ||||
| #define MAX6581_CONF_EXTENDED		BIT(1) | ||||
| #define MAX6693_CONF_BETA		BIT(2) | ||||
| #define MAX6697_CONF_RESISTANCE		BIT(3) | ||||
| #define MAX6697_CONF_TIMEOUT		BIT(5) | ||||
| #define MAX6697_REG_ALERT_MASK		0x42 | ||||
| #define MAX6697_REG_OVERT_MASK		0x43 | ||||
| 
 | ||||
|  | @ -67,24 +81,18 @@ struct max6697_chip_data { | |||
| 	u32 have_crit; | ||||
| 	u32 have_fault; | ||||
| 	u8 valid_conf; | ||||
| 	const u8 *alarm_map; | ||||
| }; | ||||
| 
 | ||||
| struct max6697_data { | ||||
| 	struct i2c_client *client; | ||||
| 	struct regmap *regmap; | ||||
| 
 | ||||
| 	enum chips type; | ||||
| 	const struct max6697_chip_data *chip; | ||||
| 
 | ||||
| 	int update_interval;	/* in milli-seconds */ | ||||
| 	int temp_offset;	/* in degrees C */ | ||||
| 
 | ||||
| 	struct mutex update_lock; | ||||
| 	unsigned long last_updated;	/* In jiffies */ | ||||
| 	bool valid;		/* true if following fields are valid */ | ||||
| 
 | ||||
| 	/* 1x local and up to 7x remote */ | ||||
| 	u8 temp[8][4];		/* [nr][0]=temp [1]=ext [2]=max [3]=crit */ | ||||
| #define MAX6697_TEMP_INPUT	0 | ||||
| #define MAX6697_TEMP_EXT	1 | ||||
| #define MAX6697_TEMP_MAX	2 | ||||
|  | @ -92,11 +100,6 @@ struct max6697_data { | |||
| 	u32 alarms; | ||||
| }; | ||||
| 
 | ||||
| /* Diode fault status bits on MAX6581 are right shifted by one bit */ | ||||
| static const u8 max6581_alarm_map[] = { | ||||
| 	 0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, | ||||
| 	 16, 17, 18, 19, 20, 21, 22, 23 }; | ||||
| 
 | ||||
| static const struct max6697_chip_data max6697_chip_data[] = { | ||||
| 	[max6581] = { | ||||
| 		.channels = 8, | ||||
|  | @ -104,7 +107,6 @@ static const struct max6697_chip_data max6697_chip_data[] = { | |||
| 		.have_ext = 0x7f, | ||||
| 		.have_fault = 0xfe, | ||||
| 		.valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT, | ||||
| 		.alarm_map = max6581_alarm_map, | ||||
| 	}, | ||||
| 	[max6602] = { | ||||
| 		.channels = 5, | ||||
|  | @ -173,545 +175,398 @@ static const struct max6697_chip_data max6697_chip_data[] = { | |||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static inline int max6581_offset_to_millic(int val) | ||||
| static int max6697_alarm_channel_map(int channel) | ||||
| { | ||||
| 	return sign_extend32(val, 7) * 250; | ||||
| 	switch (channel) { | ||||
| 	case 0: | ||||
| 		return 6; | ||||
| 	case 7: | ||||
| 		return 7; | ||||
| 	default: | ||||
| 		return channel - 1; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct max6697_data *max6697_update_device(struct device *dev) | ||||
| static int max6697_read(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			u32 attr, int channel, long *val) | ||||
| { | ||||
| 	unsigned int offset_regs[2] = { MAX6581_REG_OFFSET_SELECT, MAX6581_REG_OFFSET }; | ||||
| 	unsigned int temp_regs[2] = { MAX6697_REG_TEMP[channel], | ||||
| 				      MAX6697_REG_TEMP_EXT[channel] }; | ||||
| 	struct max6697_data *data = dev_get_drvdata(dev); | ||||
| 	struct i2c_client *client = data->client; | ||||
| 	struct max6697_data *ret = data; | ||||
| 	int val; | ||||
| 	int i; | ||||
| 	u32 alarms; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 
 | ||||
| 	if (data->valid && | ||||
| 	    !time_after(jiffies, data->last_updated | ||||
| 			+ msecs_to_jiffies(data->update_interval))) | ||||
| 		goto abort; | ||||
| 
 | ||||
| 	for (i = 0; i < data->chip->channels; i++) { | ||||
| 		if (data->chip->have_ext & (1 << i)) { | ||||
| 			val = i2c_smbus_read_byte_data(client, | ||||
| 						       MAX6697_REG_TEMP_EXT[i]); | ||||
| 			if (unlikely(val < 0)) { | ||||
| 				ret = ERR_PTR(val); | ||||
| 				goto abort; | ||||
| 			} | ||||
| 			data->temp[i][MAX6697_TEMP_EXT] = val; | ||||
| 		} | ||||
| 
 | ||||
| 		val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]); | ||||
| 		if (unlikely(val < 0)) { | ||||
| 			ret = ERR_PTR(val); | ||||
| 			goto abort; | ||||
| 		} | ||||
| 		data->temp[i][MAX6697_TEMP_INPUT] = val; | ||||
| 
 | ||||
| 		val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]); | ||||
| 		if (unlikely(val < 0)) { | ||||
| 			ret = ERR_PTR(val); | ||||
| 			goto abort; | ||||
| 		} | ||||
| 		data->temp[i][MAX6697_TEMP_MAX] = val; | ||||
| 
 | ||||
| 		if (data->chip->have_crit & (1 << i)) { | ||||
| 			val = i2c_smbus_read_byte_data(client, | ||||
| 						       MAX6697_REG_CRIT[i]); | ||||
| 			if (unlikely(val < 0)) { | ||||
| 				ret = ERR_PTR(val); | ||||
| 				goto abort; | ||||
| 			} | ||||
| 			data->temp[i][MAX6697_TEMP_CRIT] = val; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	alarms = 0; | ||||
| 	for (i = 0; i < 3; i++) { | ||||
| 		val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i)); | ||||
| 		if (unlikely(val < 0)) { | ||||
| 			ret = ERR_PTR(val); | ||||
| 			goto abort; | ||||
| 		} | ||||
| 		alarms = (alarms << 8) | val; | ||||
| 	} | ||||
| 	data->alarms = alarms; | ||||
| 	data->last_updated = jiffies; | ||||
| 	data->valid = true; | ||||
| abort: | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_input_show(struct device *dev, | ||||
| 			       struct device_attribute *devattr, char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(devattr)->index; | ||||
| 	struct max6697_data *data = max6697_update_device(dev); | ||||
| 	int temp; | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3; | ||||
| 	temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", temp * 125); | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, | ||||
| 			 char *buf) | ||||
| { | ||||
| 	int nr = to_sensor_dev_attr_2(devattr)->nr; | ||||
| 	int index = to_sensor_dev_attr_2(devattr)->index; | ||||
| 	struct max6697_data *data = max6697_update_device(dev); | ||||
| 	int temp; | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	temp = data->temp[nr][index]; | ||||
| 	temp -= data->temp_offset; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", temp * 1000); | ||||
| } | ||||
| 
 | ||||
| static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, | ||||
| 			  char *buf) | ||||
| { | ||||
| 	int index = to_sensor_dev_attr(attr)->index; | ||||
| 	struct max6697_data *data = max6697_update_device(dev); | ||||
| 
 | ||||
| 	if (IS_ERR(data)) | ||||
| 		return PTR_ERR(data); | ||||
| 
 | ||||
| 	if (data->chip->alarm_map) | ||||
| 		index = data->chip->alarm_map[index]; | ||||
| 
 | ||||
| 	return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); | ||||
| } | ||||
| 
 | ||||
| static ssize_t temp_store(struct device *dev, | ||||
| 			  struct device_attribute *devattr, const char *buf, | ||||
| 			  size_t count) | ||||
| { | ||||
| 	int nr = to_sensor_dev_attr_2(devattr)->nr; | ||||
| 	int index = to_sensor_dev_attr_2(devattr)->index; | ||||
| 	struct max6697_data *data = dev_get_drvdata(dev); | ||||
| 	long temp; | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	u8 regdata[2] = { }; | ||||
| 	u32 regval; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = kstrtol(buf, 10, &temp); | ||||
| 	if (ret < 0) | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_input: | ||||
| 		ret = regmap_multi_reg_read(regmap, temp_regs, regdata, | ||||
| 					    data->chip->have_ext & BIT(channel) ? 2 : 1); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = (((regdata[0] - data->temp_offset) << 3) | (regdata[1] >> 5)) * 125; | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_MAX[channel], ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = ((int)regval - data->temp_offset) * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_CRIT[channel], ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = ((int)regval - data->temp_offset) * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_min: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_MIN, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = ((int)regval - data->temp_offset) * 1000; | ||||
| 		break; | ||||
| 	case hwmon_temp_offset: | ||||
| 		ret = regmap_multi_reg_read(regmap, offset_regs, regdata, 2); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	temp = clamp_val(temp, -1000000, 1000000);	/* prevent underflow */ | ||||
| 	temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; | ||||
| 	temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); | ||||
| 	data->temp[nr][index] = temp; | ||||
| 	ret = i2c_smbus_write_byte_data(data->client, | ||||
| 					index == 2 ? MAX6697_REG_MAX[nr] | ||||
| 						   : MAX6697_REG_CRIT[nr], | ||||
| 					temp); | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 		if (!(regdata[0] & BIT(channel - 1))) | ||||
| 			regdata[1] = 0; | ||||
| 
 | ||||
| 	return ret < 0 ? ret : count; | ||||
| } | ||||
| 
 | ||||
| static ssize_t offset_store(struct device *dev, struct device_attribute *devattr, const char *buf, | ||||
| 			    size_t count) | ||||
| { | ||||
| 	int val, ret, index, select; | ||||
| 	struct max6697_data *data; | ||||
| 	bool channel_enabled; | ||||
| 	long temp; | ||||
| 
 | ||||
| 	index = to_sensor_dev_attr(devattr)->index; | ||||
| 	data = dev_get_drvdata(dev); | ||||
| 	ret = kstrtol(buf, 10, &temp); | ||||
| 	if (ret < 0) | ||||
| 		*val = sign_extend32(regdata[1], 7) * 250; | ||||
| 		break; | ||||
| 	case hwmon_temp_fault: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_STAT_FAULT, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); | ||||
| 	if (select < 0) { | ||||
| 		ret = select; | ||||
| 		goto abort; | ||||
| 	} | ||||
| 	channel_enabled = (select & (1 << (index - 1))); | ||||
| 	temp = clamp_val(temp, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); | ||||
| 	val = DIV_ROUND_CLOSEST(temp, 250); | ||||
| 	/* disable the offset for channel if the new offset is 0 */ | ||||
| 	if (val == 0) { | ||||
| 		if (channel_enabled) | ||||
| 			ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, | ||||
| 							select & ~(1 << (index - 1))); | ||||
| 		ret = ret < 0 ? ret : count; | ||||
| 		goto abort; | ||||
| 	} | ||||
| 	if (!channel_enabled) { | ||||
| 		ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, | ||||
| 						select | (1 << (index - 1))); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 	} | ||||
| 	ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET, val); | ||||
| 	ret = ret < 0 ? ret : count; | ||||
| 
 | ||||
| abort: | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static ssize_t offset_show(struct device *dev, struct device_attribute *devattr, char *buf) | ||||
| { | ||||
| 	struct max6697_data *data; | ||||
| 	int select, ret, index; | ||||
| 
 | ||||
| 	index = to_sensor_dev_attr(devattr)->index; | ||||
| 	data = dev_get_drvdata(dev); | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 	select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); | ||||
| 	if (select < 0) | ||||
| 		ret = select; | ||||
| 	else if (select & (1 << (index - 1))) | ||||
| 		ret = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET); | ||||
| 		if (data->type == max6581) | ||||
| 			*val = !!(regval & BIT(channel - 1)); | ||||
| 		else | ||||
| 		ret = 0; | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 	return ret < 0 ? ret : sprintf(buf, "%d\n", max6581_offset_to_millic(ret)); | ||||
| 			*val = !!(regval & BIT(channel)); | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_alarm: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_STAT_CRIT, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		/*
 | ||||
| 		 * In the MAX6581 datasheet revision 0 to 3, the local channel | ||||
| 		 * overtemperature status is reported in bit 6 of register 0x45, | ||||
| 		 * and the overtemperature status for remote channel 7 is | ||||
| 		 * reported in bit 7. In Revision 4 and later, the local channel | ||||
| 		 * overtemperature status is reported in bit 7, and the remote | ||||
| 		 * channel 7 overtemperature status is reported in bit 6. A real | ||||
| 		 * chip was found to match the functionality documented in | ||||
| 		 * Revision 4 and later. | ||||
| 		 */ | ||||
| 		*val = !!(regval & BIT(channel ? channel - 1 : 7)); | ||||
| 		break; | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_STAT_ALARM, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!(regval & BIT(max6697_alarm_channel_map(channel))); | ||||
| 		break; | ||||
| 	case hwmon_temp_min_alarm: | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_STAT_MIN_ALARM, ®val); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		*val = !!(regval & BIT(max6697_alarm_channel_map(channel))); | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 0, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp1_crit, temp, 0, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 1, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp2_crit, temp, 1, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_input, 2); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 2, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp3_crit, temp, 2, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_input, 3); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp4_max, temp, 3, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp4_crit, temp, 3, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_input, 4); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp5_max, temp, 4, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp5_crit, temp, 4, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_input, 5); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp6_max, temp, 5, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp6_crit, temp, 5, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp7_input, temp_input, 6); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp7_max, temp, 6, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp7_crit, temp, 6, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp8_input, temp_input, 7); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp8_max, temp, 7, MAX6697_TEMP_MAX); | ||||
| static SENSOR_DEVICE_ATTR_2_RW(temp8_crit, temp, 7, MAX6697_TEMP_CRIT); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 22); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 16); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, 17); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, 18); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, 19); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp6_max_alarm, alarm, 20); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp7_max_alarm, alarm, 21); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp8_max_alarm, alarm, 23); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 15); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 8); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 9); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, alarm, 10); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_crit_alarm, alarm, 11); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp6_crit_alarm, alarm, 12); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp7_crit_alarm, alarm, 13); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 14); | ||||
| 
 | ||||
| static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 1); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, 3); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, 4); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp6_fault, alarm, 5); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp7_fault, alarm, 6); | ||||
| static SENSOR_DEVICE_ATTR_RO(temp8_fault, alarm, 7); | ||||
| 
 | ||||
| /* There is no offset for local temperature so starting from temp2 */ | ||||
| static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 1); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 2); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 3); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 4); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp6_offset, offset, 5); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp7_offset, offset, 6); | ||||
| static SENSOR_DEVICE_ATTR_RW(temp8_offset, offset, 7); | ||||
| 
 | ||||
| static DEVICE_ATTR(dummy, 0, NULL, NULL); | ||||
| 
 | ||||
| static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, | ||||
| 				  int index) | ||||
| static int max6697_write(struct device *dev, enum hwmon_sensor_types type, | ||||
| 			 u32 attr, int channel, long val) | ||||
| { | ||||
| 	struct device *dev = kobj_to_dev(kobj); | ||||
| 	struct max6697_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_max: | ||||
| 		val = clamp_val(val, -1000000, 1000000);	/* prevent underflow */ | ||||
| 		val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; | ||||
| 		val = clamp_val(val, 0, data->type == max6581 ? 255 : 127); | ||||
| 		return regmap_write(regmap, MAX6697_REG_MAX[channel], val); | ||||
| 	case hwmon_temp_crit: | ||||
| 		val = clamp_val(val, -1000000, 1000000);	/* prevent underflow */ | ||||
| 		val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; | ||||
| 		val = clamp_val(val, 0, data->type == max6581 ? 255 : 127); | ||||
| 		return regmap_write(regmap, MAX6697_REG_CRIT[channel], val); | ||||
| 	case hwmon_temp_min: | ||||
| 		val = clamp_val(val, -1000000, 1000000);	/* prevent underflow */ | ||||
| 		val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; | ||||
| 		val = clamp_val(val, 0, 255); | ||||
| 		return regmap_write(regmap, MAX6697_REG_MIN, val); | ||||
| 	case hwmon_temp_offset: | ||||
| 		mutex_lock(&data->update_lock); | ||||
| 		val = clamp_val(val, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); | ||||
| 		val = DIV_ROUND_CLOSEST(val, 250); | ||||
| 		if (!val) {	/* disable this (and only this) channel */ | ||||
| 			ret = regmap_clear_bits(regmap, MAX6581_REG_OFFSET_SELECT, | ||||
| 						BIT(channel - 1)); | ||||
| 		} else { | ||||
| 			/* enable channel and update offset */ | ||||
| 			ret = regmap_set_bits(regmap, MAX6581_REG_OFFSET_SELECT, | ||||
| 					      BIT(channel - 1)); | ||||
| 			if (ret) | ||||
| 				goto unlock; | ||||
| 			ret = regmap_write(regmap, MAX6581_REG_OFFSET, val); | ||||
| 		} | ||||
| unlock: | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 		return ret; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static umode_t max6697_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||
| 				  u32 attr, int channel) | ||||
| { | ||||
| 	const struct max6697_data *data = _data; | ||||
| 	const struct max6697_chip_data *chip = data->chip; | ||||
| 	int channel = index / 7;	/* channel number */ | ||||
| 	int nr = index % 7;		/* attribute index within channel */ | ||||
| 
 | ||||
| 	if (channel >= chip->channels) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel))) | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_max: | ||||
| 		return 0644; | ||||
| 	case hwmon_temp_input: | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		return 0444; | ||||
| 	case hwmon_temp_min: | ||||
| 		if (data->type == max6581) | ||||
| 			return channel ? 0444 : 0644; | ||||
| 		break; | ||||
| 	case hwmon_temp_min_alarm: | ||||
| 		if (data->type == max6581) | ||||
| 			return 0444; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		if (chip->have_crit & BIT(channel)) | ||||
| 			return 0644; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_alarm: | ||||
| 		if (chip->have_crit & BIT(channel)) | ||||
| 			return 0444; | ||||
| 		break; | ||||
| 	case hwmon_temp_fault: | ||||
| 		if (chip->have_fault & BIT(channel)) | ||||
| 			return 0444; | ||||
| 		break; | ||||
| 	case hwmon_temp_offset: | ||||
| 		if (data->type == max6581 && channel) | ||||
| 			return 0644; | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| 	if (nr == 5 && !(chip->have_fault & (1 << channel))) | ||||
| 		return 0; | ||||
| 	/* offset reg is only supported on max6581 remote channels */ | ||||
| 	if (nr == 6) | ||||
| 		if (data->type != max6581 || channel == 0) | ||||
| 			return 0; | ||||
| 
 | ||||
| 	return attr->mode; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * max6697_is_visible uses the index into the following array to determine | ||||
|  * if attributes should be created or not. Any change in order or content | ||||
|  * must be matched in max6697_is_visible. | ||||
|  */ | ||||
| static struct attribute *max6697_attributes[] = { | ||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, | ||||
| 	&dev_attr_dummy.attr, | ||||
| 	&dev_attr_dummy.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp2_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp2_offset.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp3_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp3_offset.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp4_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp4_offset.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp5_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp5_offset.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp6_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp6_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp6_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp6_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp6_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp6_offset.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp7_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp7_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp7_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp7_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp7_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp7_offset.dev_attr.attr, | ||||
| 
 | ||||
| 	&sensor_dev_attr_temp8_input.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp8_max.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp8_max_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp8_crit.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp8_fault.dev_attr.attr, | ||||
| 	&sensor_dev_attr_temp8_offset.dev_attr.attr, | ||||
| /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||
| static const struct hwmon_channel_info * const max6697_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(temp, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET, | ||||
| 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | | ||||
| 			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | | ||||
| 			   HWMON_T_MIN | HWMON_T_MIN_ALARM | | ||||
| 			   HWMON_T_FAULT | HWMON_T_OFFSET), | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group max6697_group = { | ||||
| 	.attrs = max6697_attributes, .is_visible = max6697_is_visible, | ||||
| static const struct hwmon_ops max6697_hwmon_ops = { | ||||
| 	.is_visible = max6697_is_visible, | ||||
| 	.read = max6697_read, | ||||
| 	.write = max6697_write, | ||||
| }; | ||||
| __ATTRIBUTE_GROUPS(max6697); | ||||
| 
 | ||||
| static void max6697_get_config_of(struct device_node *node, | ||||
| 				  struct max6697_platform_data *pdata) | ||||
| static const struct hwmon_chip_info max6697_chip_info = { | ||||
| 	.ops = &max6697_hwmon_ops, | ||||
| 	.info = max6697_info, | ||||
| }; | ||||
| 
 | ||||
| static int max6697_config_of(struct device_node *node, struct max6697_data *data) | ||||
| { | ||||
| 	int len; | ||||
| 	const __be32 *prop; | ||||
| 	const struct max6697_chip_data *chip = data->chip; | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	int ret, confreg; | ||||
| 	u32 vals[2]; | ||||
| 
 | ||||
| 	pdata->smbus_timeout_disable = | ||||
| 		of_property_read_bool(node, "smbus-timeout-disable"); | ||||
| 	pdata->extended_range_enable = | ||||
| 		of_property_read_bool(node, "extended-range-enable"); | ||||
| 	pdata->beta_compensation = | ||||
| 		of_property_read_bool(node, "beta-compensation-enable"); | ||||
| 	confreg = 0; | ||||
| 	if (of_property_read_bool(node, "smbus-timeout-disable") && | ||||
| 	    (chip->valid_conf & MAX6697_CONF_TIMEOUT)) { | ||||
| 		confreg |= MAX6697_CONF_TIMEOUT; | ||||
| 	} | ||||
| 	if (of_property_read_bool(node, "extended-range-enable") && | ||||
| 	    (chip->valid_conf & MAX6581_CONF_EXTENDED)) { | ||||
| 		confreg |= MAX6581_CONF_EXTENDED; | ||||
| 		data->temp_offset = 64; | ||||
| 	} | ||||
| 	if (of_property_read_bool(node, "beta-compensation-enable") && | ||||
| 	    (chip->valid_conf & MAX6693_CONF_BETA)) { | ||||
| 		confreg |= MAX6693_CONF_BETA; | ||||
| 	} | ||||
| 
 | ||||
| 	prop = of_get_property(node, "alert-mask", &len); | ||||
| 	if (prop && len == sizeof(u32)) | ||||
| 		pdata->alert_mask = be32_to_cpu(prop[0]); | ||||
| 	prop = of_get_property(node, "over-temperature-mask", &len); | ||||
| 	if (prop && len == sizeof(u32)) | ||||
| 		pdata->over_temperature_mask = be32_to_cpu(prop[0]); | ||||
| 	prop = of_get_property(node, "resistance-cancellation", &len); | ||||
| 	if (prop) { | ||||
| 		if (len == sizeof(u32)) | ||||
| 			pdata->resistance_cancellation = be32_to_cpu(prop[0]); | ||||
| 	if (of_property_read_u32(node, "alert-mask", vals)) | ||||
| 		vals[0] = 0; | ||||
| 	ret = regmap_write(regmap, MAX6697_REG_ALERT_MASK, | ||||
| 			   MAX6697_ALERT_MAP_BITS(vals[0])); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (of_property_read_u32(node, "over-temperature-mask", vals)) | ||||
| 		vals[0] = 0; | ||||
| 	ret = regmap_write(regmap, MAX6697_REG_OVERT_MASK, | ||||
| 			   MAX6697_OVERT_MAP_BITS(vals[0])); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (data->type != max6581) { | ||||
| 		if (of_property_read_bool(node, "resistance-cancellation") && | ||||
| 		    chip->valid_conf & MAX6697_CONF_RESISTANCE) { | ||||
| 			confreg |= MAX6697_CONF_RESISTANCE; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (of_property_read_u32(node, "resistance-cancellation", &vals[0])) { | ||||
| 			if (of_property_read_bool(node, "resistance-cancellation")) | ||||
| 				vals[0] = 0xfe; | ||||
| 			else | ||||
| 			pdata->resistance_cancellation = 0xfe; | ||||
| 				vals[0] = 0; | ||||
| 		} | ||||
| 	prop = of_get_property(node, "transistor-ideality", &len); | ||||
| 	if (prop && len == 2 * sizeof(u32)) { | ||||
| 			pdata->ideality_mask = be32_to_cpu(prop[0]); | ||||
| 			pdata->ideality_value = be32_to_cpu(prop[1]); | ||||
| 
 | ||||
| 		vals[0] &= 0xfe; | ||||
| 		ret = regmap_write(regmap, MAX6581_REG_RESISTANCE, vals[0] >> 1); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		if (of_property_read_u32_array(node, "transistor-ideality", vals, 2)) { | ||||
| 			vals[0] = 0; | ||||
| 			vals[1] = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		ret = regmap_write(regmap, MAX6581_REG_IDEALITY, vals[1]); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = regmap_write(regmap, MAX6581_REG_IDEALITY_SELECT, | ||||
| 				   (vals[0] & 0xfe) >> 1); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	return regmap_write(regmap, MAX6697_REG_CONFIG, confreg); | ||||
| } | ||||
| 
 | ||||
| static int max6697_init_chip(struct max6697_data *data, | ||||
| 			     struct i2c_client *client) | ||||
| static int max6697_init_chip(struct device_node *np, struct max6697_data *data) | ||||
| { | ||||
| 	struct max6697_platform_data *pdata = dev_get_platdata(&client->dev); | ||||
| 	struct max6697_platform_data p; | ||||
| 	const struct max6697_chip_data *chip = data->chip; | ||||
| 	int factor = chip->channels; | ||||
| 	int ret, reg; | ||||
| 	unsigned int reg; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Don't touch configuration if neither platform data nor OF | ||||
| 	 * configuration was specified. If that is the case, use the | ||||
| 	 * current chip configuration. | ||||
| 	 * Don't touch configuration if there is no devicetree configuration. | ||||
| 	 * If that is the case, use the current chip configuration. | ||||
| 	 */ | ||||
| 	if (!pdata && !client->dev.of_node) { | ||||
| 		reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG); | ||||
| 		if (reg < 0) | ||||
| 			return reg; | ||||
| 	if (!np) { | ||||
| 		struct regmap *regmap = data->regmap; | ||||
| 
 | ||||
| 		ret = regmap_read(regmap, MAX6697_REG_CONFIG, ®); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		if (data->type == max6581) { | ||||
| 			if (reg & MAX6581_CONF_EXTENDED) | ||||
| 				data->temp_offset = 64; | ||||
| 			reg = i2c_smbus_read_byte_data(client, | ||||
| 						       MAX6581_REG_RESISTANCE); | ||||
| 			if (reg < 0) | ||||
| 				return reg; | ||||
| 			factor += hweight8(reg); | ||||
| 			ret = regmap_read(regmap, MAX6581_REG_RESISTANCE, ®); | ||||
| 		} | ||||
| 	} else { | ||||
| 			if (reg & MAX6697_CONF_RESISTANCE) | ||||
| 				factor++; | ||||
| 		} | ||||
| 		goto done; | ||||
| 		ret = max6697_config_of(np, data); | ||||
| 	} | ||||
| 
 | ||||
| 	if (client->dev.of_node) { | ||||
| 		memset(&p, 0, sizeof(p)); | ||||
| 		max6697_get_config_of(client->dev.of_node, &p); | ||||
| 		pdata = &p; | ||||
| 	} | ||||
| 
 | ||||
| 	reg = 0; | ||||
| 	if (pdata->smbus_timeout_disable && | ||||
| 	    (chip->valid_conf & MAX6697_CONF_TIMEOUT)) { | ||||
| 		reg |= MAX6697_CONF_TIMEOUT; | ||||
| 	} | ||||
| 	if (pdata->extended_range_enable && | ||||
| 	    (chip->valid_conf & MAX6581_CONF_EXTENDED)) { | ||||
| 		reg |= MAX6581_CONF_EXTENDED; | ||||
| 		data->temp_offset = 64; | ||||
| 	} | ||||
| 	if (pdata->resistance_cancellation && | ||||
| 	    (chip->valid_conf & MAX6697_CONF_RESISTANCE)) { | ||||
| 		reg |= MAX6697_CONF_RESISTANCE; | ||||
| 		factor++; | ||||
| 	} | ||||
| 	if (pdata->beta_compensation && | ||||
| 	    (chip->valid_conf & MAX6693_CONF_BETA)) { | ||||
| 		reg |= MAX6693_CONF_BETA; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg); | ||||
| 	if (ret < 0) | ||||
| 	return ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, | ||||
| 				MAX6697_ALERT_MAP_BITS(pdata->alert_mask)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, | ||||
| 			MAX6697_OVERT_MAP_BITS(pdata->over_temperature_mask)); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (data->type == max6581) { | ||||
| 		factor += hweight8(pdata->resistance_cancellation >> 1); | ||||
| 		ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE, | ||||
| 					pdata->resistance_cancellation >> 1); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY, | ||||
| 						pdata->ideality_value); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		ret = i2c_smbus_write_byte_data(client, | ||||
| 						MAX6581_REG_IDEALITY_SELECT, | ||||
| 						pdata->ideality_mask >> 1); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| done: | ||||
| 	data->update_interval = factor * MAX6697_CONV_TIME; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static bool max6697_volatile_reg(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	switch (reg) { | ||||
| 	case 0x00 ... 0x09:	/* temperature high bytes */ | ||||
| 	case 0x44 ... 0x47:	/* status */ | ||||
| 	case 0x51 ... 0x58:	/* temperature low bytes */ | ||||
| 		return true; | ||||
| 	default: | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static bool max6697_writeable_reg(struct device *dev, unsigned int reg) | ||||
| { | ||||
| 	return reg != 0x0a && reg != 0x0f && !max6697_volatile_reg(dev, reg); | ||||
| } | ||||
| 
 | ||||
| static const struct regmap_config max6697_regmap_config = { | ||||
| 	.reg_bits = 8, | ||||
| 	.val_bits = 8, | ||||
| 	.max_register = 0x58, | ||||
| 	.writeable_reg = max6697_writeable_reg, | ||||
| 	.volatile_reg = max6697_volatile_reg, | ||||
| 	.cache_type = REGCACHE_MAPLE, | ||||
| }; | ||||
| 
 | ||||
| static int max6697_probe(struct i2c_client *client) | ||||
| { | ||||
| 	struct i2c_adapter *adapter = client->adapter; | ||||
| 	struct device *dev = &client->dev; | ||||
| 	struct max6697_data *data; | ||||
| 	struct device *hwmon_dev; | ||||
| 	struct regmap *regmap; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||||
| 		return -ENODEV; | ||||
| 	regmap = regmap_init_i2c(client, &max6697_regmap_config); | ||||
| 	if (IS_ERR(regmap)) | ||||
| 		return PTR_ERR(regmap); | ||||
| 
 | ||||
| 	data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL); | ||||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	data->regmap = regmap; | ||||
| 	data->type = (uintptr_t)i2c_get_match_data(client); | ||||
| 	data->chip = &max6697_chip_data[data->type]; | ||||
| 	data->client = client; | ||||
| 	mutex_init(&data->update_lock); | ||||
| 
 | ||||
| 	err = max6697_init_chip(data, client); | ||||
| 	err = max6697_init_chip(client->dev.of_node, data); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, | ||||
| 							   data, | ||||
| 							   max6697_groups); | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, | ||||
| 							 &max6697_chip_info, NULL); | ||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1269,6 +1269,7 @@ static const char * const asus_msi_boards[] = { | |||
| 	"EX-B760M-V5 D4", | ||||
| 	"EX-H510M-V3", | ||||
| 	"EX-H610M-V3 D4", | ||||
| 	"G15CF", | ||||
| 	"PRIME A620M-A", | ||||
| 	"PRIME B560-PLUS", | ||||
| 	"PRIME B560-PLUS AC-HES", | ||||
|  |  | |||
|  | @ -229,41 +229,34 @@ static int nct7802_read_temp(struct nct7802_data *data, | |||
| 
 | ||||
| static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan) | ||||
| { | ||||
| 	unsigned int f1, f2; | ||||
| 	unsigned int regs[2] = {reg_fan, REG_FANCOUNT_LOW}; | ||||
| 	u8 f[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->access_lock); | ||||
| 	ret = regmap_read(data->regmap, reg_fan, &f1); | ||||
| 	if (ret < 0) | ||||
| 		goto abort; | ||||
| 	ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2); | ||||
| 	if (ret < 0) | ||||
| 		goto abort; | ||||
| 	ret = (f1 << 5) | (f2 >> 3); | ||||
| 	ret = regmap_multi_reg_read(data->regmap, regs, f, 2); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	ret = (f[0] << 5) | (f[1] >> 3); | ||||
| 	/* convert fan count to rpm */ | ||||
| 	if (ret == 0x1fff)	/* maximum value, assume fan is stopped */ | ||||
| 		ret = 0; | ||||
| 	else if (ret) | ||||
| 		ret = DIV_ROUND_CLOSEST(1350000U, ret); | ||||
| abort: | ||||
| 	mutex_unlock(&data->access_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, | ||||
| 				u8 reg_fan_high) | ||||
| { | ||||
| 	unsigned int f1, f2; | ||||
| 	unsigned int regs[2] = {reg_fan_low, reg_fan_high}; | ||||
| 	u8 f[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->access_lock); | ||||
| 	ret = regmap_read(data->regmap, reg_fan_low, &f1); | ||||
| 	ret = regmap_multi_reg_read(data->regmap, regs, f, 2); | ||||
| 	if (ret < 0) | ||||
| 		goto abort; | ||||
| 	ret = regmap_read(data->regmap, reg_fan_high, &f2); | ||||
| 	if (ret < 0) | ||||
| 		goto abort; | ||||
| 	ret = f1 | ((f2 & 0xf8) << 5); | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = f[0] | ((f[1] & 0xf8) << 5); | ||||
| 	/* convert fan count to rpm */ | ||||
| 	if (ret == 0x1fff)	/* maximum value, assume no limit */ | ||||
| 		ret = 0; | ||||
|  | @ -271,8 +264,6 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, | |||
| 		ret = DIV_ROUND_CLOSEST(1350000U, ret); | ||||
| 	else | ||||
| 		ret = 1350000U; | ||||
| abort: | ||||
| 	mutex_unlock(&data->access_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | @ -302,33 +293,26 @@ static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 }; | |||
| 
 | ||||
| static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) | ||||
| { | ||||
| 	unsigned int v1, v2; | ||||
| 	u8 v[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	mutex_lock(&data->access_lock); | ||||
| 	if (index == 0) {	/* voltage */ | ||||
| 		ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1); | ||||
| 		unsigned int regs[2] = {REG_VOLTAGE[nr], REG_VOLTAGE_LOW}; | ||||
| 
 | ||||
| 		ret = regmap_multi_reg_read(data->regmap, regs, v, 2); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr]; | ||||
| 			return ret; | ||||
| 		ret = ((v[0] << 2) | (v[1] >> 6)) * nct7802_vmul[nr]; | ||||
| 	}  else {	/* limit */ | ||||
| 		int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; | ||||
| 		unsigned int regs[2] = {REG_VOLTAGE_LIMIT_LSB[index - 1][nr], | ||||
| 					REG_VOLTAGE_LIMIT_MSB[nr]}; | ||||
| 
 | ||||
| 		ret = regmap_read(data->regmap, | ||||
| 				  REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1); | ||||
| 		ret = regmap_multi_reg_read(data->regmap, regs, v, 2); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], | ||||
| 				  &v2); | ||||
| 		if (ret < 0) | ||||
| 			goto abort; | ||||
| 		ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr]; | ||||
| 			return ret; | ||||
| 		ret = (v[0] | ((v[1] << shift) & 0x300)) * nct7802_vmul[nr]; | ||||
| 	} | ||||
| abort: | ||||
| 	mutex_unlock(&data->access_lock); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | @ -1145,19 +1129,16 @@ static int nct7802_configure_channels(struct device *dev, | |||
| { | ||||
| 	/* Enable local temperature sensor by default */ | ||||
| 	u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN; | ||||
| 	struct device_node *node; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (dev->of_node) { | ||||
| 		for_each_child_of_node(dev->of_node, node) { | ||||
| 		for_each_child_of_node_scoped(dev->of_node, node) { | ||||
| 			err = nct7802_get_channel_config(dev, node, &mode_mask, | ||||
| 							 &mode_val); | ||||
| 			if (err) { | ||||
| 				of_node_put(node); | ||||
| 			if (err) | ||||
| 				return err; | ||||
| 		} | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	return regmap_update_bits(data->regmap, REG_MODE, mode_mask, mode_val); | ||||
| } | ||||
|  |  | |||
|  | @ -927,7 +927,7 @@ static int npcm7xx_en_pwm_fan(struct device *dev, | |||
| static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct device_node *np, *child; | ||||
| 	struct device_node *np; | ||||
| 	struct npcm7xx_pwm_fan_data *data; | ||||
| 	struct resource *res; | ||||
| 	struct device *hwmon; | ||||
|  | @ -1004,11 +1004,10 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for_each_child_of_node(np, child) { | ||||
| 	for_each_child_of_node_scoped(np, child) { | ||||
| 		ret = npcm7xx_en_pwm_fan(dev, child, data); | ||||
| 		if (ret) { | ||||
| 			dev_err(dev, "enable pwm and fan failed\n"); | ||||
| 			of_node_put(child); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -62,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = { | |||
| 	[NTC_SSG1404001221]   = { "ssg1404_001221",  TYPE_NCPXXWB473 }, | ||||
| 	[NTC_LAST]            = { }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); | ||||
| 
 | ||||
| /*
 | ||||
|  * A compensation table should be sorted by the values of .ohm | ||||
|  |  | |||
|  | @ -1,18 +1,21 @@ | |||
| // SPDX-License-Identifier: GPL-2.0+
 | ||||
| /*
 | ||||
|  * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose | ||||
|  * fan reading and control via hwmon sysfs. | ||||
|  * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds | ||||
|  * that expose fan reading and control via hwmon sysfs. | ||||
|  * | ||||
|  * Old OXP boards have the same DMI strings and they are told apart by | ||||
|  * the boot cpu vendor (Intel/AMD). Currently only AMD boards are | ||||
|  * supported but the code is made to be simple to add other handheld | ||||
|  * boards in the future. | ||||
|  * the boot cpu vendor (Intel/AMD). Of these older models only AMD is | ||||
|  * supported. | ||||
|  * | ||||
|  * Fan control is provided via pwm interface in the range [0-255]. | ||||
|  * Old AMD boards use [0-100] as range in the EC, the written value is | ||||
|  * scaled to accommodate for that. Newer boards like the mini PRO and | ||||
|  * AOK ZOE are not scaled but have the same EC layout. | ||||
|  * AOKZOE are not scaled but have the same EC layout. Newer models | ||||
|  * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi | ||||
|  * are [1-244] and scaled to 0-255. | ||||
|  * | ||||
|  * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com> | ||||
|  * Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/acpi.h> | ||||
|  | @ -43,32 +46,48 @@ enum oxp_board { | |||
| 	aok_zoe_a1 = 1, | ||||
| 	aya_neo_2, | ||||
| 	aya_neo_air, | ||||
| 	aya_neo_air_1s, | ||||
| 	aya_neo_air_plus_mendo, | ||||
| 	aya_neo_air_pro, | ||||
| 	aya_neo_flip, | ||||
| 	aya_neo_geek, | ||||
| 	aya_neo_kun, | ||||
| 	orange_pi_neo, | ||||
| 	oxp_2, | ||||
| 	oxp_fly, | ||||
| 	oxp_mini_amd, | ||||
| 	oxp_mini_amd_a07, | ||||
| 	oxp_mini_amd_pro, | ||||
| 	oxp_x1, | ||||
| }; | ||||
| 
 | ||||
| static enum oxp_board board; | ||||
| 
 | ||||
| /* Fan reading and PWM */ | ||||
| #define OXP_SENSOR_FAN_REG             0x76 /* Fan reading is 2 registers long */ | ||||
| #define OXP_2_SENSOR_FAN_REG           0x58 /* Fan reading is 2 registers long */ | ||||
| #define OXP_SENSOR_PWM_ENABLE_REG      0x4A /* PWM enable is 1 register long */ | ||||
| #define OXP_SENSOR_PWM_REG             0x4B /* PWM reading is 1 register long */ | ||||
| #define PWM_MODE_AUTO                  0x00 | ||||
| #define PWM_MODE_MANUAL                0x01 | ||||
| 
 | ||||
| /* OrangePi fan reading and PWM */ | ||||
| #define ORANGEPI_SENSOR_FAN_REG        0x78 /* Fan reading is 2 registers long */ | ||||
| #define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */ | ||||
| #define ORANGEPI_SENSOR_PWM_REG        0x38 /* PWM reading is 1 register long */ | ||||
| 
 | ||||
| /* Turbo button takeover function
 | ||||
|  * Older boards have different values and EC registers | ||||
|  * Different boards have different values and EC registers | ||||
|  * for the same function | ||||
|  */ | ||||
| #define OXP_OLD_TURBO_SWITCH_REG	0x1E | ||||
| #define OXP_OLD_TURBO_TAKE_VAL		0x01 | ||||
| #define OXP_OLD_TURBO_RETURN_VAL	0x00 | ||||
| #define OXP_TURBO_SWITCH_REG           0xF1 /* Mini Pro, OneXFly, AOKZOE */ | ||||
| #define OXP_2_TURBO_SWITCH_REG         0xEB /* OXP2 and X1 */ | ||||
| #define OXP_MINI_TURBO_SWITCH_REG      0x1E /* Mini AO7 */ | ||||
| 
 | ||||
| #define OXP_TURBO_SWITCH_REG		0xF1 | ||||
| #define OXP_TURBO_TAKE_VAL		0x40 | ||||
| #define OXP_TURBO_RETURN_VAL		0x00 | ||||
| #define OXP_MINI_TURBO_TAKE_VAL        0x01 /* Mini AO7 */ | ||||
| #define OXP_TURBO_TAKE_VAL             0x40 /* All other models */ | ||||
| 
 | ||||
| #define OXP_TURBO_RETURN_VAL           0x00 /* Common return val */ | ||||
| 
 | ||||
| static const struct dmi_system_id dmi_table[] = { | ||||
| 	{ | ||||
|  | @ -88,7 +107,7 @@ static const struct dmi_system_id dmi_table[] = { | |||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||
| 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"), | ||||
| 			DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)aya_neo_2, | ||||
| 	}, | ||||
|  | @ -99,6 +118,13 @@ static const struct dmi_system_id dmi_table[] = { | |||
| 		}, | ||||
| 		.driver_data = (void *)aya_neo_air, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||
| 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)aya_neo_air_1s, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||
|  | @ -116,10 +142,31 @@ static const struct dmi_system_id dmi_table[] = { | |||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||
| 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"), | ||||
| 			DMI_MATCH(DMI_BOARD_NAME, "FLIP"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)aya_neo_flip, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||
| 			DMI_MATCH(DMI_BOARD_NAME, "GEEK"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)aya_neo_geek, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||
| 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)aya_neo_kun, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"), | ||||
| 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)orange_pi_neo, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | ||||
|  | @ -127,6 +174,20 @@ static const struct dmi_system_id dmi_table[] = { | |||
| 		}, | ||||
| 		.driver_data = (void *)oxp_mini_amd, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | ||||
| 			DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)oxp_2, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | ||||
| 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)oxp_fly, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | ||||
|  | @ -141,6 +202,13 @@ static const struct dmi_system_id dmi_table[] = { | |||
| 		}, | ||||
| 		.driver_data = (void *)oxp_mini_amd_pro, | ||||
| 	}, | ||||
| 	{ | ||||
| 		.matches = { | ||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | ||||
| 			DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"), | ||||
| 		}, | ||||
| 		.driver_data = (void *)oxp_x1, | ||||
| 	}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
|  | @ -192,14 +260,20 @@ static int tt_toggle_enable(void) | |||
| 
 | ||||
| 	switch (board) { | ||||
| 	case oxp_mini_amd_a07: | ||||
| 		reg = OXP_OLD_TURBO_SWITCH_REG; | ||||
| 		val = OXP_OLD_TURBO_TAKE_VAL; | ||||
| 		reg = OXP_MINI_TURBO_SWITCH_REG; | ||||
| 		val = OXP_MINI_TURBO_TAKE_VAL; | ||||
| 		break; | ||||
| 	case oxp_mini_amd_pro: | ||||
| 	case aok_zoe_a1: | ||||
| 	case oxp_fly: | ||||
| 	case oxp_mini_amd_pro: | ||||
| 		reg = OXP_TURBO_SWITCH_REG; | ||||
| 		val = OXP_TURBO_TAKE_VAL; | ||||
| 		break; | ||||
| 	case oxp_2: | ||||
| 	case oxp_x1: | ||||
| 		reg = OXP_2_TURBO_SWITCH_REG; | ||||
| 		val = OXP_TURBO_TAKE_VAL; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | @ -213,14 +287,20 @@ static int tt_toggle_disable(void) | |||
| 
 | ||||
| 	switch (board) { | ||||
| 	case oxp_mini_amd_a07: | ||||
| 		reg = OXP_OLD_TURBO_SWITCH_REG; | ||||
| 		val = OXP_OLD_TURBO_RETURN_VAL; | ||||
| 		reg = OXP_MINI_TURBO_SWITCH_REG; | ||||
| 		val = OXP_TURBO_RETURN_VAL; | ||||
| 		break; | ||||
| 	case oxp_mini_amd_pro: | ||||
| 	case aok_zoe_a1: | ||||
| 	case oxp_fly: | ||||
| 	case oxp_mini_amd_pro: | ||||
| 		reg = OXP_TURBO_SWITCH_REG; | ||||
| 		val = OXP_TURBO_RETURN_VAL; | ||||
| 		break; | ||||
| 	case oxp_2: | ||||
| 	case oxp_x1: | ||||
| 		reg = OXP_2_TURBO_SWITCH_REG; | ||||
| 		val = OXP_TURBO_RETURN_VAL; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | @ -233,8 +313,11 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj, | |||
| { | ||||
| 	switch (board) { | ||||
| 	case aok_zoe_a1: | ||||
| 	case oxp_2: | ||||
| 	case oxp_fly: | ||||
| 	case oxp_mini_amd_a07: | ||||
| 	case oxp_mini_amd_pro: | ||||
| 	case oxp_x1: | ||||
| 		return attr->mode; | ||||
| 	default: | ||||
| 		break; | ||||
|  | @ -273,12 +356,17 @@ static ssize_t tt_toggle_show(struct device *dev, | |||
| 
 | ||||
| 	switch (board) { | ||||
| 	case oxp_mini_amd_a07: | ||||
| 		reg = OXP_OLD_TURBO_SWITCH_REG; | ||||
| 		reg = OXP_MINI_TURBO_SWITCH_REG; | ||||
| 		break; | ||||
| 	case oxp_mini_amd_pro: | ||||
| 	case aok_zoe_a1: | ||||
| 	case oxp_fly: | ||||
| 	case oxp_mini_amd_pro: | ||||
| 		reg = OXP_TURBO_SWITCH_REG; | ||||
| 		break; | ||||
| 	case oxp_2: | ||||
| 	case oxp_x1: | ||||
| 		reg = OXP_2_TURBO_SWITCH_REG; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | @ -295,12 +383,53 @@ static DEVICE_ATTR_RW(tt_toggle); | |||
| /* PWM enable/disable functions */ | ||||
| static int oxp_pwm_enable(void) | ||||
| { | ||||
| 	return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01); | ||||
| 	switch (board) { | ||||
| 	case orange_pi_neo: | ||||
| 		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); | ||||
| 	case aok_zoe_a1: | ||||
| 	case aya_neo_2: | ||||
| 	case aya_neo_air: | ||||
| 	case aya_neo_air_plus_mendo: | ||||
| 	case aya_neo_air_pro: | ||||
| 	case aya_neo_flip: | ||||
| 	case aya_neo_geek: | ||||
| 	case aya_neo_kun: | ||||
| 	case oxp_2: | ||||
| 	case oxp_fly: | ||||
| 	case oxp_mini_amd: | ||||
| 	case oxp_mini_amd_a07: | ||||
| 	case oxp_mini_amd_pro: | ||||
| 	case oxp_x1: | ||||
| 		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int oxp_pwm_disable(void) | ||||
| { | ||||
| 	return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00); | ||||
| 	switch (board) { | ||||
| 	case orange_pi_neo: | ||||
| 		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); | ||||
| 	case aok_zoe_a1: | ||||
| 	case aya_neo_2: | ||||
| 	case aya_neo_air: | ||||
| 	case aya_neo_air_1s: | ||||
| 	case aya_neo_air_plus_mendo: | ||||
| 	case aya_neo_air_pro: | ||||
| 	case aya_neo_flip: | ||||
| 	case aya_neo_geek: | ||||
| 	case aya_neo_kun: | ||||
| 	case oxp_2: | ||||
| 	case oxp_fly: | ||||
| 	case oxp_mini_amd: | ||||
| 	case oxp_mini_amd_a07: | ||||
| 	case oxp_mini_amd_pro: | ||||
| 	case oxp_x1: | ||||
| 		return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Callbacks for hwmon interface */ | ||||
|  | @ -326,34 +455,98 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, | |||
| 	case hwmon_fan: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_fan_input: | ||||
| 			switch (board) { | ||||
| 			case orange_pi_neo: | ||||
| 				return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val); | ||||
| 			case oxp_2: | ||||
| 			case oxp_x1: | ||||
| 				return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); | ||||
| 			case aok_zoe_a1: | ||||
| 			case aya_neo_2: | ||||
| 			case aya_neo_air: | ||||
| 			case aya_neo_air_1s: | ||||
| 			case aya_neo_air_plus_mendo: | ||||
| 			case aya_neo_air_pro: | ||||
| 			case aya_neo_flip: | ||||
| 			case aya_neo_geek: | ||||
| 			case aya_neo_kun: | ||||
| 			case oxp_fly: | ||||
| 			case oxp_mini_amd: | ||||
| 			case oxp_mini_amd_a07: | ||||
| 			case oxp_mini_amd_pro: | ||||
| 				return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); | ||||
| 			default: | ||||
| 				break; | ||||
| 			} | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	case hwmon_pwm: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_pwm_input: | ||||
| 			switch (board) { | ||||
| 			case orange_pi_neo: | ||||
| 				ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val); | ||||
| 				if (ret) | ||||
| 					return ret; | ||||
| 				/* scale from range [1-244] */ | ||||
| 				*val = ((*val - 1) * 254 / 243) + 1; | ||||
| 				break; | ||||
| 			case oxp_2: | ||||
| 			case oxp_x1: | ||||
| 				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); | ||||
| 				if (ret) | ||||
| 					return ret; | ||||
| 			switch (board) { | ||||
| 				/* scale from range [0-184] */ | ||||
| 				*val = (*val * 255) / 184; | ||||
| 				break; | ||||
| 			case aya_neo_2: | ||||
| 			case aya_neo_air: | ||||
| 			case aya_neo_air_1s: | ||||
| 			case aya_neo_air_plus_mendo: | ||||
| 			case aya_neo_air_pro: | ||||
| 			case aya_neo_flip: | ||||
| 			case aya_neo_geek: | ||||
| 			case aya_neo_kun: | ||||
| 			case oxp_mini_amd: | ||||
| 			case oxp_mini_amd_a07: | ||||
| 				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); | ||||
| 				if (ret) | ||||
| 					return ret; | ||||
| 				/* scale from range [0-100] */ | ||||
| 				*val = (*val * 255) / 100; | ||||
| 				break; | ||||
| 			case oxp_mini_amd_pro: | ||||
| 			case aok_zoe_a1: | ||||
| 			case oxp_fly: | ||||
| 			case oxp_mini_amd_pro: | ||||
| 			default: | ||||
| 				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); | ||||
| 				if (ret) | ||||
| 					return ret; | ||||
| 				break; | ||||
| 			} | ||||
| 			return 0; | ||||
| 		case hwmon_pwm_enable: | ||||
| 			switch (board) { | ||||
| 			case orange_pi_neo: | ||||
| 				return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val); | ||||
| 			case aok_zoe_a1: | ||||
| 			case aya_neo_2: | ||||
| 			case aya_neo_air: | ||||
| 			case aya_neo_air_1s: | ||||
| 			case aya_neo_air_plus_mendo: | ||||
| 			case aya_neo_air_pro: | ||||
| 			case aya_neo_flip: | ||||
| 			case aya_neo_geek: | ||||
| 			case aya_neo_kun: | ||||
| 			case oxp_2: | ||||
| 			case oxp_fly: | ||||
| 			case oxp_mini_amd: | ||||
| 			case oxp_mini_amd_a07: | ||||
| 			case oxp_mini_amd_pro: | ||||
| 			case oxp_x1: | ||||
| 				return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); | ||||
| 			default: | ||||
| 				break; | ||||
|  | @ -362,6 +555,10 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, | |||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| 
 | ||||
|  | @ -381,21 +578,36 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, | |||
| 			if (val < 0 || val > 255) | ||||
| 				return -EINVAL; | ||||
| 			switch (board) { | ||||
| 			case orange_pi_neo: | ||||
| 				/* scale to range [1-244] */ | ||||
| 				val = ((val - 1) * 243 / 254) + 1; | ||||
| 				return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val); | ||||
| 			case oxp_2: | ||||
| 			case oxp_x1: | ||||
| 				/* scale to range [0-184] */ | ||||
| 				val = (val * 184) / 255; | ||||
| 				return write_to_ec(OXP_SENSOR_PWM_REG, val); | ||||
| 			case aya_neo_2: | ||||
| 			case aya_neo_air: | ||||
| 			case aya_neo_air_1s: | ||||
| 			case aya_neo_air_plus_mendo: | ||||
| 			case aya_neo_air_pro: | ||||
| 			case aya_neo_flip: | ||||
| 			case aya_neo_geek: | ||||
| 			case aya_neo_kun: | ||||
| 			case oxp_mini_amd: | ||||
| 			case oxp_mini_amd_a07: | ||||
| 				/* scale to range [0-100] */ | ||||
| 				val = (val * 100) / 255; | ||||
| 				break; | ||||
| 				return write_to_ec(OXP_SENSOR_PWM_REG, val); | ||||
| 			case aok_zoe_a1: | ||||
| 			case oxp_fly: | ||||
| 			case oxp_mini_amd_pro: | ||||
| 				return write_to_ec(OXP_SENSOR_PWM_REG, val); | ||||
| 			default: | ||||
| 				break; | ||||
| 			} | ||||
| 			return write_to_ec(OXP_SENSOR_PWM_REG, val); | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
|  | @ -467,19 +679,20 @@ static int __init oxp_platform_init(void) | |||
| { | ||||
| 	const struct dmi_system_id *dmi_entry; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Have to check for AMD processor here because DMI strings are the | ||||
| 	 * same between Intel and AMD boards, the only way to tell them apart | ||||
| 	 * is the CPU. | ||||
| 	 * Intel boards seem to have different EC registers and values to | ||||
| 	 * read/write. | ||||
| 	 */ | ||||
| 	dmi_entry = dmi_first_match(dmi_table); | ||||
| 	if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | ||||
| 	if (!dmi_entry) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Have to check for AMD processor here because DMI strings are the same | ||||
| 	 * between Intel and AMD boards on older OneXPlayer devices, the only way | ||||
| 	 * to tell them apart is the CPU. Old Intel boards have an unsupported EC. | ||||
| 	 */ | ||||
| 	if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	oxp_platform_device = | ||||
| 		platform_create_bundle(&oxp_platform_driver, | ||||
| 				       oxp_platform_probe, NULL, 0, NULL, 0); | ||||
|  |  | |||
|  | @ -1315,7 +1315,7 @@ static void pc87360_init_device(struct platform_device *pdev, | |||
| 				    (reg & 0xC0) | 0x11); | ||||
| 	} | ||||
| 
 | ||||
| 	nr = data->innr < 11 ? data->innr : 11; | ||||
| 	nr = min(data->innr, 11); | ||||
| 	for (i = 0; i < nr; i++) { | ||||
| 		reg = pc87360_read_value(data, LD_IN, i, | ||||
| 					 PC87365_REG_IN_STATUS); | ||||
|  |  | |||
|  | @ -31,8 +31,6 @@ MODULE_DEVICE_TABLE(i2c, max15301_id); | |||
| 
 | ||||
| struct max15301_data { | ||||
| 	int id; | ||||
| 	ktime_t access;		/* Chip access time */ | ||||
| 	int delay;		/* Delay between chip accesses in us */ | ||||
| 	struct pmbus_driver_info info; | ||||
| }; | ||||
| 
 | ||||
|  | @ -55,89 +53,6 @@ static struct max15301_data max15301_data = { | |||
| 	} | ||||
| }; | ||||
| 
 | ||||
| /* This chip needs a delay between accesses */ | ||||
| static inline void max15301_wait(const struct max15301_data *data) | ||||
| { | ||||
| 	if (data->delay) { | ||||
| 		s64 delta = ktime_us_delta(ktime_get(), data->access); | ||||
| 
 | ||||
| 		if (delta < data->delay) | ||||
| 			udelay(data->delay - delta); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int max15301_read_word_data(struct i2c_client *client, int page, | ||||
| 				   int phase, int reg) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct max15301_data *data = to_max15301_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (page > 0) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	if (reg >= PMBUS_VIRT_BASE) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	max15301_wait(data); | ||||
| 	ret = pmbus_read_word_data(client, page, phase, reg); | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int max15301_read_byte_data(struct i2c_client *client, int page, int reg) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct max15301_data *data = to_max15301_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (page > 0) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	max15301_wait(data); | ||||
| 	ret = pmbus_read_byte_data(client, page, reg); | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int max15301_write_word_data(struct i2c_client *client, int page, int reg, | ||||
| 				    u16 word) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct max15301_data *data = to_max15301_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (page > 0) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	if (reg >= PMBUS_VIRT_BASE) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	max15301_wait(data); | ||||
| 	ret = pmbus_write_word_data(client, page, reg, word); | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int max15301_write_byte(struct i2c_client *client, int page, u8 value) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct max15301_data *data = to_max15301_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (page > 0) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	max15301_wait(data); | ||||
| 	ret = pmbus_write_byte(client, page, value); | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int max15301_probe(struct i2c_client *client) | ||||
| { | ||||
| 	int status; | ||||
|  | @ -164,12 +79,7 @@ static int max15301_probe(struct i2c_client *client) | |||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	max15301_data.delay = delay; | ||||
| 
 | ||||
| 	info->read_byte_data = max15301_read_byte_data; | ||||
| 	info->read_word_data = max15301_read_word_data; | ||||
| 	info->write_byte = max15301_write_byte; | ||||
| 	info->write_word_data = max15301_write_word_data; | ||||
| 	info->access_delay = delay; | ||||
| 
 | ||||
| 	return pmbus_do_probe(client, info); | ||||
| } | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ struct mpq7932_data { | |||
| }; | ||||
| 
 | ||||
| #if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) | ||||
| static struct regulator_desc mpq7932_regulators_desc[] = { | ||||
| static const struct regulator_desc mpq7932_regulators_desc[] = { | ||||
| 	PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES, | ||||
| 			     MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), | ||||
| 	PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES, | ||||
|  |  | |||
|  | @ -54,30 +54,6 @@ static int pli1209bc_read_word_data(struct i2c_client *client, int page, | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int pli1209bc_write_byte(struct i2c_client *client, int page, u8 reg) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	switch (reg) { | ||||
| 	case PMBUS_CLEAR_FAULTS: | ||||
| 		ret = pmbus_write_byte(client, page, reg); | ||||
| 		/*
 | ||||
| 		 * PLI1209 takes 230 usec to execute the CLEAR_FAULTS command. | ||||
| 		 * During that time it's busy and NACKs all requests on the | ||||
| 		 * SMBUS interface. It also NACKs reads on PMBUS_STATUS_BYTE | ||||
| 		 * making it impossible to poll the BUSY flag. | ||||
| 		 * | ||||
| 		 * Just wait for not BUSY unconditionally. | ||||
| 		 */ | ||||
| 		usleep_range(250, 300); | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = -ENODATA; | ||||
| 		break; | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) | ||||
| static const struct regulator_desc pli1209bc_reg_desc = { | ||||
| 	.name = "vout2", | ||||
|  | @ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = { | |||
| 	    | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | ||||
| 	    | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, | ||||
| 	.read_word_data = pli1209bc_read_word_data, | ||||
| 	.write_byte = pli1209bc_write_byte, | ||||
| 	.write_delay = 250, | ||||
| #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) | ||||
| 	.num_regulators = 1, | ||||
| 	.reg_desc = &pli1209bc_reg_desc, | ||||
|  |  | |||
|  | @ -472,6 +472,16 @@ struct pmbus_driver_info { | |||
| 
 | ||||
| 	/* custom attributes */ | ||||
| 	const struct attribute_group **groups; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Some chips need a little delay between SMBus communication. When | ||||
| 	 * set, the generic PMBus helper functions will wait if necessary | ||||
| 	 * to meet this requirement. The access delay is honored after | ||||
| 	 * every SMBus operation. The write delay is only honored after | ||||
| 	 * SMBus write operations. | ||||
| 	 */ | ||||
| 	int access_delay;		/* in microseconds */ | ||||
| 	int write_delay;		/* in microseconds */ | ||||
| }; | ||||
| 
 | ||||
| /* Regulator ops */ | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/math64.h> | ||||
| #include <linux/module.h> | ||||
|  | @ -110,6 +111,8 @@ struct pmbus_data { | |||
| 
 | ||||
| 	int vout_low[PMBUS_PAGES];	/* voltage low margin */ | ||||
| 	int vout_high[PMBUS_PAGES];	/* voltage high margin */ | ||||
| 	ktime_t write_time;		/* Last SMBUS write timestamp */ | ||||
| 	ktime_t access_time;		/* Last SMBUS access timestamp */ | ||||
| }; | ||||
| 
 | ||||
| struct pmbus_debugfs_entry { | ||||
|  | @ -160,6 +163,39 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) | |||
| } | ||||
| EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); | ||||
| 
 | ||||
| /* Some chips need a delay between accesses. */ | ||||
| static void pmbus_wait(struct i2c_client *client) | ||||
| { | ||||
| 	struct pmbus_data *data = i2c_get_clientdata(client); | ||||
| 	const struct pmbus_driver_info *info = data->info; | ||||
| 	s64 delta; | ||||
| 
 | ||||
| 	if (info->access_delay) { | ||||
| 		delta = ktime_us_delta(ktime_get(), data->access_time); | ||||
| 
 | ||||
| 		if (delta < info->access_delay) | ||||
| 			fsleep(info->access_delay - delta); | ||||
| 	} else if (info->write_delay) { | ||||
| 		delta = ktime_us_delta(ktime_get(), data->write_time); | ||||
| 
 | ||||
| 		if (delta < info->write_delay) | ||||
| 			fsleep(info->write_delay - delta); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Sets the last accessed timestamp for pmbus_wait */ | ||||
| static void pmbus_update_ts(struct i2c_client *client, bool write_op) | ||||
| { | ||||
| 	struct pmbus_data *data = i2c_get_clientdata(client); | ||||
| 	const struct pmbus_driver_info *info = data->info; | ||||
| 
 | ||||
| 	if (info->access_delay) { | ||||
| 		data->access_time = ktime_get(); | ||||
| 	} else if (info->write_delay && write_op) { | ||||
| 		data->write_time = ktime_get(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int pmbus_set_page(struct i2c_client *client, int page, int phase) | ||||
| { | ||||
| 	struct pmbus_data *data = i2c_get_clientdata(client); | ||||
|  | @ -170,11 +206,15 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) | |||
| 
 | ||||
| 	if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && | ||||
| 	    data->info->pages > 1 && page != data->currpage) { | ||||
| 		pmbus_wait(client); | ||||
| 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); | ||||
| 		pmbus_update_ts(client, true); | ||||
| 		if (rv < 0) | ||||
| 			return rv; | ||||
| 
 | ||||
| 		pmbus_wait(client); | ||||
| 		rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); | ||||
| 		pmbus_update_ts(client, false); | ||||
| 		if (rv < 0) | ||||
| 			return rv; | ||||
| 
 | ||||
|  | @ -185,8 +225,10 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) | |||
| 
 | ||||
| 	if (data->info->phases[page] && data->currphase != phase && | ||||
| 	    !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { | ||||
| 		pmbus_wait(client); | ||||
| 		rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, | ||||
| 					       phase); | ||||
| 		pmbus_update_ts(client, true); | ||||
| 		if (rv) | ||||
| 			return rv; | ||||
| 	} | ||||
|  | @ -204,7 +246,11 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) | |||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
| 
 | ||||
| 	return i2c_smbus_write_byte(client, value); | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_write_byte(client, value); | ||||
| 	pmbus_update_ts(client, true); | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); | ||||
| 
 | ||||
|  | @ -235,7 +281,11 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, | |||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
| 
 | ||||
| 	return i2c_smbus_write_word_data(client, reg, word); | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_write_word_data(client, reg, word); | ||||
| 	pmbus_update_ts(client, true); | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); | ||||
| 
 | ||||
|  | @ -353,7 +403,11 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) | |||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
| 
 | ||||
| 	return i2c_smbus_read_word_data(client, reg); | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_read_word_data(client, reg); | ||||
| 	pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); | ||||
| 
 | ||||
|  | @ -412,7 +466,11 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) | |||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
| 
 | ||||
| 	return i2c_smbus_read_byte_data(client, reg); | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_read_byte_data(client, reg); | ||||
| 	pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); | ||||
| 
 | ||||
|  | @ -424,7 +482,11 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) | |||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
| 
 | ||||
| 	return i2c_smbus_write_byte_data(client, reg, value); | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_write_byte_data(client, reg, value); | ||||
| 	pmbus_update_ts(client, true); | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); | ||||
| 
 | ||||
|  | @ -456,7 +518,11 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, | |||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
| 
 | ||||
| 	return i2c_smbus_read_block_data(client, reg, data_buf); | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_read_block_data(client, reg, data_buf); | ||||
| 	pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
| 
 | ||||
| static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, | ||||
|  | @ -2457,9 +2523,11 @@ static int pmbus_read_coefficients(struct i2c_client *client, | |||
| 	data.block[1] = attr->reg; | ||||
| 	data.block[2] = 0x01; | ||||
| 
 | ||||
| 	pmbus_wait(client); | ||||
| 	rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, | ||||
| 			    I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, | ||||
| 			    I2C_SMBUS_BLOCK_PROC_CALL, &data); | ||||
| 	pmbus_update_ts(client, true); | ||||
| 
 | ||||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
|  | @ -2611,7 +2679,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, | |||
| 
 | ||||
| 	/* Enable PEC if the controller and bus supports it */ | ||||
| 	if (!(data->flags & PMBUS_NO_CAPABILITY)) { | ||||
| 		pmbus_wait(client); | ||||
| 		ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); | ||||
| 		pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 		if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { | ||||
| 			if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) | ||||
| 				client->flags |= I2C_CLIENT_PEC; | ||||
|  | @ -2624,10 +2695,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, | |||
| 	 * Bail out if both registers are not supported. | ||||
| 	 */ | ||||
| 	data->read_status = pmbus_read_status_word; | ||||
| 	pmbus_wait(client); | ||||
| 	ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); | ||||
| 	pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 	if (ret < 0 || ret == 0xffff) { | ||||
| 		data->read_status = pmbus_read_status_byte; | ||||
| 		pmbus_wait(client); | ||||
| 		ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); | ||||
| 		pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 		if (ret < 0 || ret == 0xff) { | ||||
| 			dev_err(dev, "PMBus status register not found\n"); | ||||
| 			return -ENODEV; | ||||
|  | @ -2642,7 +2719,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, | |||
| 	 * limit registers need to be disabled. | ||||
| 	 */ | ||||
| 	if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { | ||||
| 		pmbus_wait(client); | ||||
| 		ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); | ||||
| 		pmbus_update_ts(client, false); | ||||
| 
 | ||||
| 		if (ret > 0 && (ret & PB_WP_ANY)) | ||||
| 			data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; | ||||
| 	} | ||||
|  |  | |||
|  | @ -67,7 +67,6 @@ struct ucd9000_data { | |||
| 	struct gpio_chip gpio; | ||||
| #endif | ||||
| 	struct dentry *debugfs; | ||||
| 	ktime_t write_time; | ||||
| }; | ||||
| #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) | ||||
| 
 | ||||
|  | @ -86,63 +85,6 @@ struct ucd9000_debugfs_entry { | |||
|  */ | ||||
| #define UCD90320_WAIT_DELAY_US 500 | ||||
| 
 | ||||
| static inline void ucd90320_wait(const struct ucd9000_data *data) | ||||
| { | ||||
| 	s64 delta = ktime_us_delta(ktime_get(), data->write_time); | ||||
| 
 | ||||
| 	if (delta < UCD90320_WAIT_DELAY_US) | ||||
| 		udelay(UCD90320_WAIT_DELAY_US - delta); | ||||
| } | ||||
| 
 | ||||
| static int ucd90320_read_word_data(struct i2c_client *client, int page, | ||||
| 				   int phase, int reg) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct ucd9000_data *data = to_ucd9000_data(info); | ||||
| 
 | ||||
| 	if (reg >= PMBUS_VIRT_BASE) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	ucd90320_wait(data); | ||||
| 	return pmbus_read_word_data(client, page, phase, reg); | ||||
| } | ||||
| 
 | ||||
| static int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct ucd9000_data *data = to_ucd9000_data(info); | ||||
| 
 | ||||
| 	ucd90320_wait(data); | ||||
| 	return pmbus_read_byte_data(client, page, reg); | ||||
| } | ||||
| 
 | ||||
| static int ucd90320_write_word_data(struct i2c_client *client, int page, | ||||
| 				    int reg, u16 word) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct ucd9000_data *data = to_ucd9000_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ucd90320_wait(data); | ||||
| 	ret = pmbus_write_word_data(client, page, reg, word); | ||||
| 	data->write_time = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int ucd90320_write_byte(struct i2c_client *client, int page, u8 value) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct ucd9000_data *data = to_ucd9000_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ucd90320_wait(data); | ||||
| 	ret = pmbus_write_byte(client, page, value); | ||||
| 	data->write_time = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int ucd9000_get_fan_config(struct i2c_client *client, int fan) | ||||
| { | ||||
| 	int fan_config = 0; | ||||
|  | @ -667,10 +609,8 @@ static int ucd9000_probe(struct i2c_client *client) | |||
| 		info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | ||||
| 		  | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; | ||||
| 	} else if (mid->driver_data == ucd90320) { | ||||
| 		info->read_byte_data = ucd90320_read_byte_data; | ||||
| 		info->read_word_data = ucd90320_read_word_data; | ||||
| 		info->write_byte = ucd90320_write_byte; | ||||
| 		info->write_word_data = ucd90320_write_word_data; | ||||
| 		/* Delay SMBus operations after a write */ | ||||
| 		info->write_delay = UCD90320_WAIT_DELAY_US; | ||||
| 	} | ||||
| 
 | ||||
| 	ucd9000_probe_gpio(client, mid, data); | ||||
|  |  | |||
|  | @ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, | |||
| 
 | ||||
| struct zl6100_data { | ||||
| 	int id; | ||||
| 	ktime_t access;		/* chip access time */ | ||||
| 	int delay;		/* Delay between chip accesses in uS */ | ||||
| 	struct pmbus_driver_info info; | ||||
| }; | ||||
| 
 | ||||
|  | @ -122,16 +120,6 @@ static u16 zl6100_d2l(long val) | |||
| 	return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); | ||||
| } | ||||
| 
 | ||||
| /* Some chips need a delay between accesses */ | ||||
| static inline void zl6100_wait(const struct zl6100_data *data) | ||||
| { | ||||
| 	if (data->delay) { | ||||
| 		s64 delta = ktime_us_delta(ktime_get(), data->access); | ||||
| 		if (delta < data->delay) | ||||
| 			udelay(data->delay - delta); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int zl6100_read_word_data(struct i2c_client *client, int page, | ||||
| 				 int phase, int reg) | ||||
| { | ||||
|  | @ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, | |||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	zl6100_wait(data); | ||||
| 	ret = pmbus_read_word_data(client, page, phase, vreg); | ||||
| 	data->access = ktime_get(); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
|  | @ -195,14 +181,11 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, | |||
| static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct zl6100_data *data = to_zl6100_data(info); | ||||
| 	int ret, status; | ||||
| 
 | ||||
| 	if (page >= info->pages) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	zl6100_wait(data); | ||||
| 
 | ||||
| 	switch (reg) { | ||||
| 	case PMBUS_VIRT_STATUS_VMON: | ||||
| 		ret = pmbus_read_byte_data(client, 0, | ||||
|  | @ -225,7 +208,6 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) | |||
| 		ret = pmbus_read_byte_data(client, page, reg); | ||||
| 		break; | ||||
| 	} | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -234,8 +216,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, | |||
| 				  u16 word) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct zl6100_data *data = to_zl6100_data(info); | ||||
| 	int ret, vreg; | ||||
| 	int vreg; | ||||
| 
 | ||||
| 	if (page >= info->pages) | ||||
| 		return -ENXIO; | ||||
|  | @ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, | |||
| 		vreg = reg; | ||||
| 	} | ||||
| 
 | ||||
| 	zl6100_wait(data); | ||||
| 	ret = pmbus_write_word_data(client, page, vreg, word); | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) | ||||
| { | ||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||
| 	struct zl6100_data *data = to_zl6100_data(info); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (page >= info->pages) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	zl6100_wait(data); | ||||
| 	ret = pmbus_write_byte(client, page, value); | ||||
| 	data->access = ktime_get(); | ||||
| 
 | ||||
| 	return ret; | ||||
| 	return pmbus_write_word_data(client, page, vreg, word); | ||||
| } | ||||
| 
 | ||||
| static const struct i2c_device_id zl6100_id[] = { | ||||
|  | @ -363,14 +324,7 @@ static int zl6100_probe(struct i2c_client *client) | |||
| 	 * supported chips are known to require a wait time between I2C | ||||
| 	 * accesses. | ||||
| 	 */ | ||||
| 	data->delay = delay; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Since there was a direct I2C device access above, wait before | ||||
| 	 * accessing the chip again. | ||||
| 	 */ | ||||
| 	data->access = ktime_get(); | ||||
| 	zl6100_wait(data); | ||||
| 	udelay(delay); | ||||
| 
 | ||||
| 	info = &data->info; | ||||
| 
 | ||||
|  | @ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client) | |||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
| 		data->access = ktime_get(); | ||||
| 		zl6100_wait(data); | ||||
| 		udelay(delay); | ||||
| 
 | ||||
| 		if (ret & ZL8802_MFR_PHASES_MASK) | ||||
| 			info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; | ||||
|  | @ -418,8 +371,7 @@ static int zl6100_probe(struct i2c_client *client) | |||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 
 | ||||
| 			data->access = ktime_get(); | ||||
| 			zl6100_wait(data); | ||||
| 			udelay(delay); | ||||
| 
 | ||||
| 			ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); | ||||
| 			if (ret < 0) | ||||
|  | @ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client) | |||
| 			if (ret & ZL8802_MFR_XTEMP_ENABLE_2) | ||||
| 				info->func[i] |= PMBUS_HAVE_TEMP2; | ||||
| 
 | ||||
| 			data->access = ktime_get(); | ||||
| 			zl6100_wait(data); | ||||
| 			udelay(delay); | ||||
| 		} | ||||
| 		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); | ||||
| 		if (ret < 0) | ||||
|  | @ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client) | |||
| 			info->func[0] |= PMBUS_HAVE_TEMP2; | ||||
| 	} | ||||
| 
 | ||||
| 	data->access = ktime_get(); | ||||
| 	zl6100_wait(data); | ||||
| 	udelay(delay); | ||||
| 
 | ||||
| 	info->access_delay = delay; | ||||
| 	info->read_word_data = zl6100_read_word_data; | ||||
| 	info->read_byte_data = zl6100_read_byte_data; | ||||
| 	info->write_word_data = zl6100_write_word_data; | ||||
| 	info->write_byte = zl6100_write_byte; | ||||
| 
 | ||||
| 	return pmbus_do_probe(client, info); | ||||
| } | ||||
|  |  | |||
|  | @ -167,7 +167,7 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) | ||||
| static int pwm_fan_power_off(struct pwm_fan_ctx *ctx, bool force_disable) | ||||
| { | ||||
| 	struct pwm_state *state = &ctx->pwm_state; | ||||
| 	bool enable_regulator = false; | ||||
|  | @ -180,6 +180,7 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) | |||
| 				    state, | ||||
| 				    &enable_regulator); | ||||
| 
 | ||||
| 	if (force_disable) | ||||
| 		state->enabled = false; | ||||
| 	state->duty_cycle = 0; | ||||
| 	ret = pwm_apply_might_sleep(ctx->pwm, state); | ||||
|  | @ -213,7 +214,7 @@ static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) | |||
| 			return ret; | ||||
| 		ret = pwm_fan_power_on(ctx); | ||||
| 	} else { | ||||
| 		ret = pwm_fan_power_off(ctx); | ||||
| 		ret = pwm_fan_power_off(ctx, false); | ||||
| 	} | ||||
| 	if (!ret) | ||||
| 		ctx->pwm_value = pwm; | ||||
|  | @ -468,7 +469,7 @@ static void pwm_fan_cleanup(void *__ctx) | |||
| 	del_timer_sync(&ctx->rpm_timer); | ||||
| 	/* Switch off everything */ | ||||
| 	ctx->enable_mode = pwm_disable_reg_disable; | ||||
| 	pwm_fan_power_off(ctx); | ||||
| 	pwm_fan_power_off(ctx, true); | ||||
| } | ||||
| 
 | ||||
| static int pwm_fan_probe(struct platform_device *pdev) | ||||
|  | @ -661,7 +662,7 @@ static int pwm_fan_suspend(struct device *dev) | |||
| { | ||||
| 	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	return pwm_fan_power_off(ctx); | ||||
| 	return pwm_fan_power_off(ctx, true); | ||||
| } | ||||
| 
 | ||||
| static int pwm_fan_resume(struct device *dev) | ||||
|  |  | |||
|  | @ -416,8 +416,7 @@ static int sch5636_probe(struct platform_device *pdev) | |||
| 	id[i] = '\0'; | ||||
| 
 | ||||
| 	if (strcmp(id, "THS")) { | ||||
| 		pr_err("Unknown Fujitsu id: %02x%02x%02x\n", | ||||
| 		       id[0], id[1], id[2]); | ||||
| 		pr_err("Unknown Fujitsu id: %3pE (%3ph)\n", id, id); | ||||
| 		err = -ENODEV; | ||||
| 		goto error; | ||||
| 	} | ||||
|  |  | |||
|  | @ -22,4 +22,3 @@ int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, | |||
| 
 | ||||
| void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, | ||||
| 			       struct mutex *io_lock, int check_enabled); | ||||
| void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); | ||||
|  |  | |||
							
								
								
									
										388
									
								
								drivers/hwmon/sg2042-mcu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								drivers/hwmon/sg2042-mcu.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,388 @@ | |||
| // SPDX-License-Identifier: GPL-2.0-only
 | ||||
| /*
 | ||||
|  * Copyright (c) 2024 Inochi Amaoto <inochiama@outlook.com> | ||||
|  * | ||||
|  * Sophgo power control mcu for SG2042 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/cleanup.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/mutex.h> | ||||
| 
 | ||||
| /* fixed MCU registers */ | ||||
| #define REG_BOARD_TYPE				0x00 | ||||
| #define REG_MCU_FIRMWARE_VERSION		0x01 | ||||
| #define REG_PCB_VERSION				0x02 | ||||
| #define REG_PWR_CTRL				0x03 | ||||
| #define REG_SOC_TEMP				0x04 | ||||
| #define REG_BOARD_TEMP				0x05 | ||||
| #define REG_RST_COUNT				0x0a | ||||
| #define REG_UPTIME				0x0b | ||||
| #define REG_RESET_REASON			0x0d | ||||
| #define REG_MCU_TYPE				0x18 | ||||
| #define REG_REPOWER_POLICY			0x65 | ||||
| #define REG_CRITICAL_TEMP			0x66 | ||||
| #define REG_REPOWER_TEMP			0x67 | ||||
| 
 | ||||
| #define REPOWER_POLICY_REBOOT			1 | ||||
| #define REPOWER_POLICY_KEEP_OFF			2 | ||||
| 
 | ||||
| #define MCU_POWER_MAX				0xff | ||||
| 
 | ||||
| #define DEFINE_MCU_DEBUG_ATTR(_name, _reg, _format)			\ | ||||
| 	static int _name##_show(struct seq_file *seqf,			\ | ||||
| 				    void *unused)			\ | ||||
| 	{								\ | ||||
| 		struct sg2042_mcu_data *mcu = seqf->private;		\ | ||||
| 		int ret;						\ | ||||
| 		ret = i2c_smbus_read_byte_data(mcu->client, (_reg));	\ | ||||
| 		if (ret < 0)						\ | ||||
| 			return ret;					\ | ||||
| 		seq_printf(seqf, _format "\n", ret);			\ | ||||
| 		return 0;						\ | ||||
| 	}								\ | ||||
| 	DEFINE_SHOW_ATTRIBUTE(_name)					\ | ||||
| 
 | ||||
| struct sg2042_mcu_data { | ||||
| 	struct i2c_client	*client; | ||||
| 	struct dentry		*debugfs; | ||||
| 	struct mutex		mutex; | ||||
| }; | ||||
| 
 | ||||
| static struct dentry *sgmcu_debugfs; | ||||
| 
 | ||||
| static ssize_t reset_count_show(struct device *dev, | ||||
| 				struct device_attribute *attr, | ||||
| 				char *buf) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(mcu->client, REG_RST_COUNT); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", ret); | ||||
| } | ||||
| 
 | ||||
| static ssize_t uptime_show(struct device *dev, | ||||
| 			   struct device_attribute *attr, | ||||
| 			   char *buf) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	u8 time_val[2]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_i2c_block_data(mcu->client, REG_UPTIME, | ||||
| 					    sizeof(time_val), time_val); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", | ||||
| 		       (time_val[0]) | (time_val[1] << 8)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t reset_reason_show(struct device *dev, | ||||
| 				 struct device_attribute *attr, | ||||
| 				 char *buf) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(mcu->client, REG_RESET_REASON); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return sprintf(buf, "0x%02x\n", ret); | ||||
| } | ||||
| 
 | ||||
| static ssize_t repower_policy_show(struct device *dev, | ||||
| 				   struct device_attribute *attr, | ||||
| 				   char *buf) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	int ret; | ||||
| 	const char *action; | ||||
| 
 | ||||
| 	ret = i2c_smbus_read_byte_data(mcu->client, REG_REPOWER_POLICY); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (ret == REPOWER_POLICY_REBOOT) | ||||
| 		action = "repower"; | ||||
| 	else if (ret == REPOWER_POLICY_KEEP_OFF) | ||||
| 		action = "keep"; | ||||
| 	else | ||||
| 		action = "unknown"; | ||||
| 
 | ||||
| 	return sprintf(buf, "%s\n", action); | ||||
| } | ||||
| 
 | ||||
| static ssize_t repower_policy_store(struct device *dev, | ||||
| 				    struct device_attribute *attr, | ||||
| 				    const char *buf, size_t count) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	u8 value; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (sysfs_streq("repower", buf)) | ||||
| 		value = REPOWER_POLICY_REBOOT; | ||||
| 	else if (sysfs_streq("keep", buf)) | ||||
| 		value = REPOWER_POLICY_KEEP_OFF; | ||||
| 	else | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = i2c_smbus_write_byte_data(mcu->client, | ||||
| 					REG_REPOWER_POLICY, value); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static DEVICE_ATTR_RO(reset_count); | ||||
| static DEVICE_ATTR_RO(uptime); | ||||
| static DEVICE_ATTR_RO(reset_reason); | ||||
| static DEVICE_ATTR_RW(repower_policy); | ||||
| 
 | ||||
| DEFINE_MCU_DEBUG_ATTR(firmware_version, REG_MCU_FIRMWARE_VERSION, "0x%02x"); | ||||
| DEFINE_MCU_DEBUG_ATTR(pcb_version, REG_PCB_VERSION, "0x%02x"); | ||||
| DEFINE_MCU_DEBUG_ATTR(board_type, REG_BOARD_TYPE, "0x%02x"); | ||||
| DEFINE_MCU_DEBUG_ATTR(mcu_type, REG_MCU_TYPE, "%d"); | ||||
| 
 | ||||
| static struct attribute *sg2042_mcu_attrs[] = { | ||||
| 	&dev_attr_reset_count.attr, | ||||
| 	&dev_attr_uptime.attr, | ||||
| 	&dev_attr_reset_reason.attr, | ||||
| 	&dev_attr_repower_policy.attr, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group sg2042_mcu_attr_group = { | ||||
| 	.attrs	= sg2042_mcu_attrs, | ||||
| }; | ||||
| 
 | ||||
| static const struct attribute_group *sg2042_mcu_groups[] = { | ||||
| 	&sg2042_mcu_attr_group, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_channel_info * const sg2042_mcu_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), | ||||
| 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT | | ||||
| 					HWMON_T_CRIT_HYST, | ||||
| 				 HWMON_T_INPUT), | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static int sg2042_mcu_read(struct device *dev, | ||||
| 			   enum hwmon_sensor_types type, | ||||
| 			   u32 attr, int channel, long *val) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	int tmp; | ||||
| 	u8 reg; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_input: | ||||
| 		reg = channel ? REG_BOARD_TEMP : REG_SOC_TEMP; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		reg = REG_CRITICAL_TEMP; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		reg = REG_REPOWER_TEMP; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	tmp = i2c_smbus_read_byte_data(mcu->client, reg); | ||||
| 	if (tmp < 0) | ||||
| 		return tmp; | ||||
| 	*val = tmp * 1000; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sg2042_mcu_write(struct device *dev, | ||||
| 			    enum hwmon_sensor_types type, | ||||
| 			    u32 attr, int channel, long val) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); | ||||
| 	int temp = val / 1000; | ||||
| 	int hyst_temp, crit_temp; | ||||
| 	u8 reg; | ||||
| 
 | ||||
| 	temp = clamp_val(temp, 0, MCU_POWER_MAX); | ||||
| 
 | ||||
| 	guard(mutex)(&mcu->mutex); | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_crit: | ||||
| 		hyst_temp = i2c_smbus_read_byte_data(mcu->client, | ||||
| 						     REG_REPOWER_TEMP); | ||||
| 		if (hyst_temp < 0) | ||||
| 			return hyst_temp; | ||||
| 
 | ||||
| 		crit_temp = temp; | ||||
| 		reg = REG_CRITICAL_TEMP; | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		crit_temp = i2c_smbus_read_byte_data(mcu->client, | ||||
| 						     REG_CRITICAL_TEMP); | ||||
| 		if (crit_temp < 0) | ||||
| 			return crit_temp; | ||||
| 
 | ||||
| 		hyst_temp = temp; | ||||
| 		reg = REG_REPOWER_TEMP; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ensure hyst_temp is smaller to avoid MCU from | ||||
| 	 * keeping triggering repower event. | ||||
| 	 */ | ||||
| 	if (crit_temp < hyst_temp) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return i2c_smbus_write_byte_data(mcu->client, reg, temp); | ||||
| } | ||||
| 
 | ||||
| static umode_t sg2042_mcu_is_visible(const void *_data, | ||||
| 				     enum hwmon_sensor_types type, | ||||
| 				     u32 attr, int channel) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case hwmon_temp: | ||||
| 		switch (attr) { | ||||
| 		case hwmon_temp_input: | ||||
| 			return 0444; | ||||
| 		case hwmon_temp_crit: | ||||
| 		case hwmon_temp_crit_hyst: | ||||
| 			if (channel == 0) | ||||
| 				return 0644; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| 		break; | ||||
| 	default: | ||||
| 			break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct hwmon_ops sg2042_mcu_ops = { | ||||
| 	.is_visible = sg2042_mcu_is_visible, | ||||
| 	.read = sg2042_mcu_read, | ||||
| 	.write = sg2042_mcu_write, | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_chip_info sg2042_mcu_chip_info = { | ||||
| 	.ops = &sg2042_mcu_ops, | ||||
| 	.info = sg2042_mcu_info, | ||||
| }; | ||||
| 
 | ||||
| static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu, | ||||
| 				    struct device *dev) | ||||
| { | ||||
| 	mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs); | ||||
| 
 | ||||
| 	debugfs_create_file("firmware_version", 0444, mcu->debugfs, | ||||
| 			    mcu, &firmware_version_fops); | ||||
| 	debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu, | ||||
| 			    &pcb_version_fops); | ||||
| 	debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu, | ||||
| 			    &mcu_type_fops); | ||||
| 	debugfs_create_file("board_type", 0444, mcu->debugfs, mcu, | ||||
| 			    &board_type_fops); | ||||
| } | ||||
| 
 | ||||
| static int sg2042_mcu_i2c_probe(struct i2c_client *client) | ||||
| { | ||||
| 	struct device *dev = &client->dev; | ||||
| 	struct sg2042_mcu_data *mcu; | ||||
| 	struct device *hwmon_dev; | ||||
| 
 | ||||
| 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | | ||||
| 						I2C_FUNC_SMBUS_BLOCK_DATA)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	mcu = devm_kmalloc(dev, sizeof(*mcu), GFP_KERNEL); | ||||
| 	if (!mcu) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	mutex_init(&mcu->mutex); | ||||
| 	mcu->client = client; | ||||
| 
 | ||||
| 	i2c_set_clientdata(client, mcu); | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "sg2042_mcu", | ||||
| 							 mcu, | ||||
| 							 &sg2042_mcu_chip_info, | ||||
| 							 NULL); | ||||
| 	if (IS_ERR(hwmon_dev)) | ||||
| 		return PTR_ERR(hwmon_dev); | ||||
| 
 | ||||
| 	sg2042_mcu_debugfs_init(mcu, dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sg2042_mcu_i2c_remove(struct i2c_client *client) | ||||
| { | ||||
| 	struct sg2042_mcu_data *mcu = i2c_get_clientdata(client); | ||||
| 
 | ||||
| 	debugfs_remove_recursive(mcu->debugfs); | ||||
| } | ||||
| 
 | ||||
| static const struct i2c_device_id sg2042_mcu_id[] = { | ||||
| 	{ "sg2042-hwmon-mcu", 0 }, | ||||
| 	{}, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id); | ||||
| 
 | ||||
| static const struct of_device_id sg2042_mcu_of_id[] = { | ||||
| 	{ .compatible = "sophgo,sg2042-hwmon-mcu" }, | ||||
| 	{}, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, sg2042_mcu_of_id); | ||||
| 
 | ||||
| static struct i2c_driver sg2042_mcu_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "sg2042-mcu", | ||||
| 		.of_match_table = sg2042_mcu_of_id, | ||||
| 		.dev_groups = sg2042_mcu_groups, | ||||
| 	}, | ||||
| 	.probe = sg2042_mcu_i2c_probe, | ||||
| 	.remove = sg2042_mcu_i2c_remove, | ||||
| 	.id_table = sg2042_mcu_id, | ||||
| }; | ||||
| 
 | ||||
| static int __init sg2042_mcu_init(void) | ||||
| { | ||||
| 	sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL); | ||||
| 	return i2c_add_driver(&sg2042_mcu_driver); | ||||
| } | ||||
| 
 | ||||
| static void __exit sg2042_mcu_exit(void) | ||||
| { | ||||
| 	debugfs_remove_recursive(sgmcu_debugfs); | ||||
| 	i2c_del_driver(&sg2042_mcu_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(sg2042_mcu_init); | ||||
| module_exit(sg2042_mcu_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>"); | ||||
| MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform"); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  | @ -199,10 +199,7 @@ static ssize_t eic_read(struct sht21 *sht21) | |||
| 	eic[6] = rx[0]; | ||||
| 	eic[7] = rx[1]; | ||||
| 
 | ||||
| 	ret = snprintf(sht21->eic, sizeof(sht21->eic), | ||||
| 		       "%02x%02x%02x%02x%02x%02x%02x%02x\n", | ||||
| 		       eic[0], eic[1], eic[2], eic[3], | ||||
| 		       eic[4], eic[5], eic[6], eic[7]); | ||||
| 	ret = snprintf(sht21->eic, sizeof(sht21->eic), "%8phN\n", eic); | ||||
| out: | ||||
| 	if (ret < 0) | ||||
| 		sht21->eic[0] = 0; | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ static const struct i2c_device_id stts751_id[] = { | |||
| }; | ||||
| 
 | ||||
| static const struct of_device_id __maybe_unused stts751_of_match[] = { | ||||
| 	{ .compatible = "stts751" }, | ||||
| 	{ .compatible = "st,stts751" }, | ||||
| 	{ }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, stts751_of_match); | ||||
|  |  | |||
							
								
								
									
										235
									
								
								drivers/hwmon/surface_temp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								drivers/hwmon/surface_temp.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,235 @@ | |||
| // SPDX-License-Identifier: GPL-2.0+
 | ||||
| /*
 | ||||
|  * Thermal sensor subsystem driver for Surface System Aggregator Module (SSAM). | ||||
|  * | ||||
|  * Copyright (C) 2022-2023 Maximilian Luz <luzmaximilian@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/hwmon.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| #include <linux/surface_aggregator/controller.h> | ||||
| #include <linux/surface_aggregator/device.h> | ||||
| 
 | ||||
| /* -- SAM interface. -------------------------------------------------------- */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Available sensors are indicated by a 16-bit bitfield, where a 1 marks the | ||||
|  * presence of a sensor. So we have at most 16 possible sensors/channels. | ||||
|  */ | ||||
| #define SSAM_TMP_SENSOR_MAX_COUNT	16 | ||||
| 
 | ||||
| /*
 | ||||
|  * All names observed so far are 6 characters long, but there's only | ||||
|  * zeros after the name, so perhaps they can be longer. This number reflects | ||||
|  * the maximum zero-padded space observed in the returned buffer. | ||||
|  */ | ||||
| #define SSAM_TMP_SENSOR_NAME_LENGTH	18 | ||||
| 
 | ||||
| struct ssam_tmp_get_name_rsp { | ||||
| 	__le16 unknown1; | ||||
| 	char unknown2; | ||||
| 	char name[SSAM_TMP_SENSOR_NAME_LENGTH]; | ||||
| } __packed; | ||||
| 
 | ||||
| static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21); | ||||
| 
 | ||||
| SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, { | ||||
| 	.target_category = SSAM_SSH_TC_TMP, | ||||
| 	.command_id      = 0x04, | ||||
| }); | ||||
| 
 | ||||
| SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, { | ||||
| 	.target_category = SSAM_SSH_TC_TMP, | ||||
| 	.command_id      = 0x01, | ||||
| }); | ||||
| 
 | ||||
| SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_name, struct ssam_tmp_get_name_rsp, { | ||||
| 	.target_category = SSAM_SSH_TC_TMP, | ||||
| 	.command_id      = 0x0e, | ||||
| }); | ||||
| 
 | ||||
| static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors) | ||||
| { | ||||
| 	__le16 sensors_le; | ||||
| 	int status; | ||||
| 
 | ||||
| 	status = __ssam_tmp_get_available_sensors(sdev, &sensors_le); | ||||
| 	if (status) | ||||
| 		return status; | ||||
| 
 | ||||
| 	*sensors = le16_to_cpu(sensors_le); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temperature) | ||||
| { | ||||
| 	__le16 temp_le; | ||||
| 	int status; | ||||
| 
 | ||||
| 	status = __ssam_tmp_get_temperature(sdev->ctrl, sdev->uid.target, iid, &temp_le); | ||||
| 	if (status) | ||||
| 		return status; | ||||
| 
 | ||||
| 	/* Convert 1/10 °K to 1/1000 °C */ | ||||
| 	*temperature = (le16_to_cpu(temp_le) - 2731) * 100L; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ssam_tmp_get_name(struct ssam_device *sdev, u8 iid, char *buf, size_t buf_len) | ||||
| { | ||||
| 	struct ssam_tmp_get_name_rsp name_rsp; | ||||
| 	int status; | ||||
| 
 | ||||
| 	status =  __ssam_tmp_get_name(sdev->ctrl, sdev->uid.target, iid, &name_rsp); | ||||
| 	if (status) | ||||
| 		return status; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * This should not fail unless the name in the returned struct is not | ||||
| 	 * null-terminated or someone changed something in the struct | ||||
| 	 * definitions above, since our buffer and struct have the same | ||||
| 	 * capacity by design. So if this fails, log an error message. Since | ||||
| 	 * the more likely cause is that the returned string isn't | ||||
| 	 * null-terminated, we might have received garbage (as opposed to just | ||||
| 	 * an incomplete string), so also fail the function. | ||||
| 	 */ | ||||
| 	status = strscpy(buf, name_rsp.name, buf_len); | ||||
| 	if (status < 0) { | ||||
| 		dev_err(&sdev->dev, "received non-null-terminated sensor name string\n"); | ||||
| 		return status; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* -- Driver.---------------------------------------------------------------- */ | ||||
| 
 | ||||
| struct ssam_temp { | ||||
| 	struct ssam_device *sdev; | ||||
| 	s16 sensors; | ||||
| 	char names[SSAM_TMP_SENSOR_MAX_COUNT][SSAM_TMP_SENSOR_NAME_LENGTH]; | ||||
| }; | ||||
| 
 | ||||
| static umode_t ssam_temp_hwmon_is_visible(const void *data, | ||||
| 					  enum hwmon_sensor_types type, | ||||
| 					  u32 attr, int channel) | ||||
| { | ||||
| 	const struct ssam_temp *ssam_temp = data; | ||||
| 
 | ||||
| 	if (!(ssam_temp->sensors & BIT(channel))) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return 0444; | ||||
| } | ||||
| 
 | ||||
| static int ssam_temp_hwmon_read(struct device *dev, | ||||
| 				enum hwmon_sensor_types type, | ||||
| 				u32 attr, int channel, long *value) | ||||
| { | ||||
| 	const struct ssam_temp *ssam_temp = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value); | ||||
| } | ||||
| 
 | ||||
| static int ssam_temp_hwmon_read_string(struct device *dev, | ||||
| 				       enum hwmon_sensor_types type, | ||||
| 				       u32 attr, int channel, const char **str) | ||||
| { | ||||
| 	const struct ssam_temp *ssam_temp = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	*str = ssam_temp->names[channel]; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = { | ||||
| 	HWMON_CHANNEL_INFO(chip, | ||||
| 			   HWMON_C_REGISTER_TZ), | ||||
| 	HWMON_CHANNEL_INFO(temp, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL, | ||||
| 			   HWMON_T_INPUT | HWMON_T_LABEL), | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_ops ssam_temp_hwmon_ops = { | ||||
| 	.is_visible = ssam_temp_hwmon_is_visible, | ||||
| 	.read = ssam_temp_hwmon_read, | ||||
| 	.read_string = ssam_temp_hwmon_read_string, | ||||
| }; | ||||
| 
 | ||||
| static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = { | ||||
| 	.ops = &ssam_temp_hwmon_ops, | ||||
| 	.info = ssam_temp_hwmon_info, | ||||
| }; | ||||
| 
 | ||||
| static int ssam_temp_probe(struct ssam_device *sdev) | ||||
| { | ||||
| 	struct ssam_temp *ssam_temp; | ||||
| 	struct device *hwmon_dev; | ||||
| 	s16 sensors; | ||||
| 	int channel; | ||||
| 	int status; | ||||
| 
 | ||||
| 	status = ssam_tmp_get_available_sensors(sdev, &sensors); | ||||
| 	if (status) | ||||
| 		return status; | ||||
| 
 | ||||
| 	ssam_temp = devm_kzalloc(&sdev->dev, sizeof(*ssam_temp), GFP_KERNEL); | ||||
| 	if (!ssam_temp) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ssam_temp->sdev = sdev; | ||||
| 	ssam_temp->sensors = sensors; | ||||
| 
 | ||||
| 	/* Retrieve the name for each available sensor. */ | ||||
| 	for (channel = 0; channel < SSAM_TMP_SENSOR_MAX_COUNT; channel++) { | ||||
| 		if (!(sensors & BIT(channel))) | ||||
| 			continue; | ||||
| 
 | ||||
| 		status = ssam_tmp_get_name(sdev, channel + 1, ssam_temp->names[channel], | ||||
| 					   SSAM_TMP_SENSOR_NAME_LENGTH); | ||||
| 		if (status) | ||||
| 			return status; | ||||
| 	} | ||||
| 
 | ||||
| 	hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev, "surface_thermal", ssam_temp, | ||||
| 							 &ssam_temp_hwmon_chip_info, NULL); | ||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||
| } | ||||
| 
 | ||||
| static const struct ssam_device_id ssam_temp_match[] = { | ||||
| 	{ SSAM_SDEV(TMP, SAM, 0x00, 0x02) }, | ||||
| 	{ }, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(ssam, ssam_temp_match); | ||||
| 
 | ||||
| static struct ssam_device_driver ssam_temp = { | ||||
| 	.probe = ssam_temp_probe, | ||||
| 	.match_table = ssam_temp_match, | ||||
| 	.driver = { | ||||
| 		.name = "surface_temp", | ||||
| 		.probe_type = PROBE_PREFER_ASYNCHRONOUS, | ||||
| 	}, | ||||
| }; | ||||
| module_ssam_device_driver(ssam_temp); | ||||
| 
 | ||||
| MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); | ||||
| MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module"); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  | @ -308,7 +308,9 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val | |||
| { | ||||
| 	struct tmp401_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	unsigned int regs[2] = { TMP401_TEMP_MSB[3][channel], TMP401_TEMP_CRIT_HYST }; | ||||
| 	unsigned int regval; | ||||
| 	u16 regvals[2]; | ||||
| 	int reg, ret; | ||||
| 
 | ||||
| 	switch (attr) { | ||||
|  | @ -325,20 +327,11 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val | |||
| 		*val = tmp401_register_to_temp(regval, data->extended_range); | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		mutex_lock(&data->update_lock); | ||||
| 		reg = TMP401_TEMP_MSB[3][channel]; | ||||
| 		ret = regmap_read(regmap, reg, ®val); | ||||
| 		if (ret < 0) | ||||
| 			goto unlock; | ||||
| 		*val = tmp401_register_to_temp(regval, data->extended_range); | ||||
| 		ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, ®val); | ||||
| 		if (ret < 0) | ||||
| 			goto unlock; | ||||
| 		*val -= regval * 1000; | ||||
| unlock: | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		*val = tmp401_register_to_temp(regvals[0], data->extended_range) - | ||||
| 							(regvals[1] * 1000); | ||||
| 		break; | ||||
| 	case hwmon_temp_fault: | ||||
| 	case hwmon_temp_min_alarm: | ||||
|  |  | |||
|  | @ -410,19 +410,16 @@ static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *d | |||
| { | ||||
| 	struct device *dev = &client->dev; | ||||
| 	const struct device_node *np = dev->of_node; | ||||
| 	struct device_node *child; | ||||
| 	int err; | ||||
| 
 | ||||
| 	for_each_child_of_node(np, child) { | ||||
| 	for_each_child_of_node_scoped(np, child) { | ||||
| 		if (strcmp(child->name, "channel")) | ||||
| 			continue; | ||||
| 
 | ||||
| 		err = tmp421_probe_child_from_dt(client, child, data); | ||||
| 		if (err) { | ||||
| 			of_node_put(child); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -147,11 +147,11 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val | |||
| { | ||||
| 	struct tmp464_data *data = dev_get_drvdata(dev); | ||||
| 	struct regmap *regmap = data->regmap; | ||||
| 	unsigned int regval, regval2; | ||||
| 	unsigned int regs[2]; | ||||
| 	unsigned int regval; | ||||
| 	u16 regvals[2]; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	mutex_lock(&data->update_lock); | ||||
| 
 | ||||
| 	switch (attr) { | ||||
| 	case hwmon_temp_max_alarm: | ||||
| 		err = regmap_read(regmap, TMP464_THERM_STATUS_REG, ®val); | ||||
|  | @ -172,26 +172,27 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val | |||
| 		 * complete. That means we have to cache the value internally | ||||
| 		 * for one measurement cycle and report the cached value. | ||||
| 		 */ | ||||
| 		mutex_lock(&data->update_lock); | ||||
| 		if (!data->valid || time_after(jiffies, data->last_updated + | ||||
| 					       msecs_to_jiffies(data->update_interval))) { | ||||
| 			err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val); | ||||
| 			if (err < 0) | ||||
| 				break; | ||||
| 				goto unlock; | ||||
| 			data->open_reg = regval; | ||||
| 			data->last_updated = jiffies; | ||||
| 			data->valid = true; | ||||
| 		} | ||||
| 		*val = !!(data->open_reg & BIT(channel + 7)); | ||||
| unlock: | ||||
| 		mutex_unlock(&data->update_lock); | ||||
| 		break; | ||||
| 	case hwmon_temp_max_hyst: | ||||
| 		err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); | ||||
| 		regs[0] = TMP464_THERM_LIMIT[channel]; | ||||
| 		regs[1] = TMP464_TEMP_HYST_REG; | ||||
| 		err = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (err < 0) | ||||
| 			break; | ||||
| 		err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); | ||||
| 		if (err < 0) | ||||
| 			break; | ||||
| 		regval -= regval2; | ||||
| 		*val = temp_from_reg(regval); | ||||
| 		*val = temp_from_reg(regvals[0] - regvals[1]); | ||||
| 		break; | ||||
| 	case hwmon_temp_max: | ||||
| 		err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); | ||||
|  | @ -200,14 +201,12 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val | |||
| 		*val = temp_from_reg(regval); | ||||
| 		break; | ||||
| 	case hwmon_temp_crit_hyst: | ||||
| 		err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); | ||||
| 		regs[0] = TMP464_THERM2_LIMIT[channel]; | ||||
| 		regs[1] = TMP464_TEMP_HYST_REG; | ||||
| 		err = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||
| 		if (err < 0) | ||||
| 			break; | ||||
| 		err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); | ||||
| 		if (err < 0) | ||||
| 			break; | ||||
| 		regval -= regval2; | ||||
| 		*val = temp_from_reg(regval); | ||||
| 		*val = temp_from_reg(regvals[0] - regvals[1]); | ||||
| 		break; | ||||
| 	case hwmon_temp_crit: | ||||
| 		err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); | ||||
|  | @ -239,8 +238,6 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val | |||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&data->update_lock); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
|  | @ -565,19 +562,16 @@ static int tmp464_probe_child_from_dt(struct device *dev, | |||
| static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data) | ||||
| { | ||||
| 	const struct device_node *np = dev->of_node; | ||||
| 	struct device_node *child; | ||||
| 	int err; | ||||
| 
 | ||||
| 	for_each_child_of_node(np, child) { | ||||
| 	for_each_child_of_node_scoped(np, child) { | ||||
| 		if (strcmp(child->name, "channel")) | ||||
| 			continue; | ||||
| 
 | ||||
| 		err = tmp464_probe_child_from_dt(dev, child, data); | ||||
| 		if (err) { | ||||
| 			of_node_put(child); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -72,7 +72,7 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, | |||
| 				struct device_attribute, attr); | ||||
| 
 | ||||
| 	if (dev_attr->show == vexpress_hwmon_label_show && | ||||
| 			!of_get_property(dev->of_node, "label", NULL)) | ||||
| 			!of_property_present(dev->of_node, "label")) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return attr->mode; | ||||
|  |  | |||
|  | @ -481,7 +481,6 @@ devm_hwmon_device_register_with_info(struct device *dev, | |||
| 				const struct attribute_group **extra_groups); | ||||
| 
 | ||||
| void hwmon_device_unregister(struct device *dev); | ||||
| void devm_hwmon_device_unregister(struct device *dev); | ||||
| 
 | ||||
| int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, | ||||
| 		       u32 attr, int channel); | ||||
|  |  | |||
|  | @ -1,33 +0,0 @@ | |||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||||
| /*
 | ||||
|  * max6697.h | ||||
|  *     Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net> | ||||
|  */ | ||||
| 
 | ||||
| #ifndef MAX6697_H | ||||
| #define MAX6697_H | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * For all bit masks: | ||||
|  * bit 0:    local temperature | ||||
|  * bit 1..7: remote temperatures | ||||
|  */ | ||||
| struct max6697_platform_data { | ||||
| 	bool smbus_timeout_disable;	/* set to disable SMBus timeouts */ | ||||
| 	bool extended_range_enable;	/* set to enable extended temp range */ | ||||
| 	bool beta_compensation;		/* set to enable beta compensation */ | ||||
| 	u8 alert_mask;			/* set bit to 1 to disable alert */ | ||||
| 	u8 over_temperature_mask;	/* set bit to 1 to disable */ | ||||
| 	u8 resistance_cancellation;	/* set bit to 0 to disable
 | ||||
| 					 * bit mask for MAX6581, | ||||
| 					 * boolean for other chips | ||||
| 					 */ | ||||
| 	u8 ideality_mask;		/* set bit to 0 to disable */ | ||||
| 	u8 ideality_value;		/* transistor ideality as per
 | ||||
| 					 * MAX6581 datasheet | ||||
| 					 */ | ||||
| }; | ||||
| 
 | ||||
| #endif /* MAX6697_H */ | ||||
		Loading…
	
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds