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 |       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. |       uses a logic high output for 100% duty cycle. | ||||||
|     $ref: /schemas/types.yaml#/definitions/uint32-array |     $ref: /schemas/types.yaml#/definitions/uint32-array | ||||||
|  |     deprecated: true | ||||||
|     minItems: 3 |     minItems: 3 | ||||||
|     maxItems: 3 |     maxItems: 3 | ||||||
|     items: |     items: | ||||||
|       enum: [0, 1] |       enum: [0, 1] | ||||||
|       default: 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: | patternProperties: | ||||||
|   "^adi,bypass-attenuator-in[0-4]$": |   "^adi,bypass-attenuator-in[0-4]$": | ||||||
|     description: | |     description: | | ||||||
|  | @ -81,6 +100,10 @@ patternProperties: | ||||||
|       - smbalert# |       - smbalert# | ||||||
|       - gpio |       - gpio | ||||||
| 
 | 
 | ||||||
|  |   "^fan-[0-9]+$": | ||||||
|  |     $ref: fan-common.yaml# | ||||||
|  |     unevaluatedProperties: false | ||||||
|  | 
 | ||||||
| required: | required: | ||||||
|   - compatible |   - compatible | ||||||
|   - reg |   - reg | ||||||
|  | @ -89,17 +112,27 @@ additionalProperties: false | ||||||
| 
 | 
 | ||||||
| examples: | examples: | ||||||
|   - | |   - | | ||||||
|  |     #include <dt-bindings/pwm/pwm.h> | ||||||
|     i2c { |     i2c { | ||||||
|       #address-cells = <1>; |       #address-cells = <1>; | ||||||
|       #size-cells = <0>; |       #size-cells = <0>; | ||||||
| 
 | 
 | ||||||
|       hwmon@2e { |       pwm: hwmon@2e { | ||||||
|         compatible = "adi,adt7476"; |         compatible = "adi,adt7476"; | ||||||
|         reg = <0x2e>; |         reg = <0x2e>; | ||||||
|         adi,bypass-attenuator-in0 = <1>; |         adi,bypass-attenuator-in0 = <1>; | ||||||
|         adi,bypass-attenuator-in1 = <0>; |         adi,bypass-attenuator-in1 = <0>; | ||||||
|         adi,pwm-active-state = <1 0 1>; |  | ||||||
|         adi,pin10-function = "smbalert#"; |         adi,pin10-function = "smbalert#"; | ||||||
|         adi,pin14-function = "tach4"; |         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 hwmon_device_unregister(struct device *dev); | ||||||
| 
 | 
 | ||||||
|   void devm_hwmon_device_unregister(struct device *dev); |  | ||||||
| 
 |  | ||||||
|   char *hwmon_sanitize_name(const char *name); |   char *hwmon_sanitize_name(const char *name); | ||||||
| 
 | 
 | ||||||
|   char *devm_hwmon_sanitize_name(struct device *dev, 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 | remove function if the hardware monitoring device was registered with | ||||||
| hwmon_device_register_with_info. | 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 | All supported hwmon device registration functions only accept valid device | ||||||
| names. Device names including invalid characters (whitespace, '*', or '-') | names. Device names including invalid characters (whitespace, '*', or '-') | ||||||
| will be rejected. The 'name' parameter is mandatory. | 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_lcrit		Critical low shunt voltage | ||||||
| in0_crit		Critical high shunt voltage | in0_crit		Critical high shunt voltage | ||||||
| in0_lcrit_alarm		Shunt voltage critical low alarm | in0_lcrit_alarm		Shunt voltage critical low alarm | ||||||
|  |  | ||||||
|  | @ -206,6 +206,7 @@ Hardware Monitoring Kernel Drivers | ||||||
|    sch5636 |    sch5636 | ||||||
|    scpi-hwmon |    scpi-hwmon | ||||||
|    sfctemp |    sfctemp | ||||||
|  |    sg2042-mcu | ||||||
|    sht15 |    sht15 | ||||||
|    sht21 |    sht21 | ||||||
|    sht3x |    sht3x | ||||||
|  |  | ||||||
|  | @ -3,29 +3,29 @@ Kernel driver lm92 | ||||||
| 
 | 
 | ||||||
| Supported chips: | Supported chips: | ||||||
| 
 | 
 | ||||||
|   * National Semiconductor LM92 |   * National Semiconductor / Texas Instruments LM92 | ||||||
| 
 | 
 | ||||||
|     Prefix: 'lm92' |     Prefix: 'lm92' | ||||||
| 
 | 
 | ||||||
|     Addresses scanned: I2C 0x48 - 0x4b |     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' |     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' |     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: | Authors: | ||||||
|  | @ -36,13 +36,13 @@ Authors: | ||||||
| Description | Description | ||||||
| ----------- | ----------- | ||||||
| 
 | 
 | ||||||
| This driver implements support for the National Semiconductor LM92 | This driver implements support for the National Semiconductor / Texas | ||||||
| temperature sensor. | Instruments LM92 temperature sensor. | ||||||
| 
 | 
 | ||||||
| Each LM92 temperature sensor supports a single temperature sensor. There are | Each LM92 temperature sensor supports a single temperature sensor. There are | ||||||
| alarms for high, low, and critical thresholds. There's also an hysteresis to | alarms for high, low, and critical thresholds. There's also an hysteresis to | ||||||
| control the thresholds for resetting alarms. | control the thresholds for resetting alarms. | ||||||
| 
 | 
 | ||||||
| Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635, | The driver also supports LM76 and Maxim MAX6633/MAX6634/MAX6635, which are | ||||||
| which are mostly compatible. They have not all been tested, so you | mostly compatible but do not have a vendor ID register and therefore must be | ||||||
| may need to use the force parameter. | 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. | is 1.0 degree for the local temperature and for the remote temperature. | ||||||
| 
 | 
 | ||||||
| Only the external sensor has high and low limits. | 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: | Description: | ||||||
| ------------ | ------------ | ||||||
| 
 | 
 | ||||||
| Handheld devices from One Netbook and Aya Neo provide fan readings and fan | Handheld devices from OneNetbook, AOKZOE, AYANEO, And OrangePi provide fan | ||||||
| control through their embedded controllers. | readings and fan control through their embedded controllers. | ||||||
| 
 | 
 | ||||||
| Currently only supports AMD boards from One X Player, AOK ZOE, and some Aya | Currently supports OneXPlayer devices, AOKZOE, AYANEO, and OrangePi | ||||||
| Neo devices. One X Player Intel boards could be supported if we could figure | handheld devices. AYANEO devices preceding the AIR and OneXPlayer devices | ||||||
| out the EC registers and values to write to since the EC layout and model is | preceding the Mini A07 are not supportable as the EC model is different | ||||||
| different. Aya Neo devices preceding the AIR may not be supportable as the EC | and do not have manual control capabilities. | ||||||
| model is different and do not appear to have manual control capabilities. |  | ||||||
| 
 | 
 | ||||||
| Some models have a toggle for changing the behaviour of the "Turbo/Silent" | Some OneXPlayer and AOKZOE models have a toggle for changing the behaviour | ||||||
| button of the device. It will change the key event that it triggers with | of the "Turbo/Silent" button of the device. It will change the key event | ||||||
| a flip of the `tt_toggle` attribute. See below for boards that support this | that it triggers with a flip of the `tt_toggle` attribute. See below for | ||||||
| function. | boards that support this function. | ||||||
| 
 | 
 | ||||||
| Supported devices | Supported devices | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
| Currently the driver supports the following handhelds: | Currently the driver supports the following handhelds: | ||||||
| 
 | 
 | ||||||
|  - AOK ZOE A1 |  - AOKZOE A1 | ||||||
|  - AOK ZOE A1 PRO |  - AOKZOE A1 PRO | ||||||
|  - Aya Neo 2 |  - AYANEO 2 | ||||||
|  - Aya Neo AIR |  - AYANEO 2S | ||||||
|  - Aya Neo AIR Plus (Mendocino) |  - AYANEO AIR | ||||||
|  - Aya Neo AIR Pro |  - AYANEO AIR 1S | ||||||
|  - Aya Neo Geek |  - 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 AMD | ||||||
|  - OneXPlayer mini AMD |  - OneXPlayer mini AMD | ||||||
|  - OneXPlayer mini AMD PRO |  - 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: | "Turbo/Silent" button behaviour toggle is only supported on: | ||||||
|  - AOK ZOE A1 |  - AOK ZOE A1 | ||||||
|  - AOK ZOE A1 PRO |  - AOK ZOE A1 PRO | ||||||
|  |  - OneXPlayer 2 | ||||||
|  |  - OneXPlayer 2 Pro | ||||||
|  - OneXPlayer mini AMD (only with updated alpha BIOS) |  - OneXPlayer mini AMD (only with updated alpha BIOS) | ||||||
|  - OneXPlayer mini AMD PRO |  - OneXPlayer mini AMD PRO | ||||||
|  |  - OneXPlayer OneXFly | ||||||
|  |  - OneXPlayer X1 A | ||||||
|  |  - OneXPlayer X1 i | ||||||
|  |  - OneXPlayer X1 mini | ||||||
| 
 | 
 | ||||||
| Sysfs entries | Sysfs entries | ||||||
| ------------- | ------------- | ||||||
|  | @ -52,7 +70,7 @@ Sysfs entries | ||||||
| The following attributes are supported: | The following attributes are supported: | ||||||
| 
 | 
 | ||||||
| fan1_input | fan1_input | ||||||
|   Read Only. Reads current fan RMP. |   Read Only. Reads current fan RPM. | ||||||
| 
 | 
 | ||||||
| pwm1_enable | pwm1_enable | ||||||
|   Read Write. Enable manual fan control. Write "1" to set to manual, write "0" |   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:	Documentation/hwmon/surface_fan.rst | ||||||
| F:	drivers/hwmon/surface_fan.c | 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 | MICROSOFT SURFACE GPE LID SUPPORT DRIVER | ||||||
| M:	Maximilian Luz <luzmaximilian@gmail.com> | M:	Maximilian Luz <luzmaximilian@gmail.com> | ||||||
| L:	platform-driver-x86@vger.kernel.org | L:	platform-driver-x86@vger.kernel.org | ||||||
|  |  | ||||||
|  | @ -1511,9 +1511,10 @@ config SENSORS_LM90 | ||||||
| config SENSORS_LM92 | config SENSORS_LM92 | ||||||
| 	tristate "National Semiconductor LM92 and compatibles" | 	tristate "National Semiconductor LM92 and compatibles" | ||||||
| 	depends on I2C | 	depends on I2C | ||||||
|  | 	select REGMAP_I2C | ||||||
| 	help | 	help | ||||||
| 	  If you say yes here you get support for National Semiconductor LM92 | 	  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 | 	  This driver can also be built as a module. If so, the module | ||||||
| 	  will be called lm92. | 	  will be called lm92. | ||||||
|  | @ -1532,6 +1533,7 @@ config SENSORS_LM93 | ||||||
| config SENSORS_LM95234 | config SENSORS_LM95234 | ||||||
| 	tristate "National Semiconductor LM95234 and compatibles" | 	tristate "National Semiconductor LM95234 and compatibles" | ||||||
| 	depends on I2C | 	depends on I2C | ||||||
|  | 	select REGMAP_I2C | ||||||
| 	help | 	help | ||||||
| 	  If you say yes here you get support for the LM95233 and LM95234 | 	  If you say yes here you get support for the LM95233 and LM95234 | ||||||
| 	  temperature sensor chips. | 	  temperature sensor chips. | ||||||
|  | @ -2066,6 +2068,17 @@ config SENSORS_SFCTEMP | ||||||
| 	  This driver can also be built as a module.  If so, the module | 	  This driver can also be built as a module.  If so, the module | ||||||
| 	  will be called sfctemp. | 	  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 | config SENSORS_SURFACE_FAN | ||||||
| 	tristate "Surface Fan Driver" | 	tristate "Surface Fan Driver" | ||||||
| 	depends on SURFACE_AGGREGATOR | 	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. | 	  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 | config SENSORS_ADC128D818 | ||||||
| 	tristate "Texas Instruments ADC128D818" | 	tristate "Texas Instruments ADC128D818" | ||||||
| 	depends on I2C | 	depends on I2C | ||||||
|  |  | ||||||
|  | @ -194,6 +194,7 @@ obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o | ||||||
| obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o | obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o | ||||||
| obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o | obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o | ||||||
| obj-$(CONFIG_SENSORS_SFCTEMP)	+= sfctemp.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_SL28CPLD)	+= sl28cpld-hwmon.o | ||||||
| obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o | obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o | ||||||
| obj-$(CONFIG_SENSORS_SHT21)	+= sht21.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_SPD5118)	+= spd5118.o | ||||||
| obj-$(CONFIG_SENSORS_STTS751)	+= stts751.o | obj-$(CONFIG_SENSORS_STTS751)	+= stts751.o | ||||||
| obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.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_SY7636A)	+= sy7636a-hwmon.o | ||||||
| obj-$(CONFIG_SENSORS_AMC6821)	+= amc6821.o | obj-$(CONFIG_SENSORS_AMC6821)	+= amc6821.o | ||||||
| obj-$(CONFIG_SENSORS_TC74)	+= tc74.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) | static int pwm1_freq_get(struct device *dev) | ||||||
| { | { | ||||||
| 	struct adt7470_data *data = dev_get_drvdata(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 index; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->lock); | 	err = regmap_multi_reg_read(data->regmap, regs, cfg_reg, 2); | ||||||
| 	err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); | 	if (err) | ||||||
| 	if (err < 0) | 		return err; | ||||||
| 		goto out; |  | ||||||
| 	err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); |  | ||||||
| 	if (err < 0) |  | ||||||
| 		goto out; |  | ||||||
| 	mutex_unlock(&data->lock); |  | ||||||
| 
 | 
 | ||||||
| 	index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; | 	index = (cfg_reg[1] & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; | ||||||
| 	if (!(cfg_reg_1 & ADT7470_CFG_LF)) | 	if (!(cfg_reg[0] & ADT7470_CFG_LF)) | ||||||
| 		index += 8; | 		index += 8; | ||||||
| 	if (index >= ARRAY_SIZE(adt7470_freq_map)) | 	if (index >= ARRAY_SIZE(adt7470_freq_map)) | ||||||
| 		index = ARRAY_SIZE(adt7470_freq_map) - 1; | 		index = ARRAY_SIZE(adt7470_freq_map) - 1; | ||||||
| 
 | 
 | ||||||
| 	return adt7470_freq_map[index]; | 	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) | static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) | ||||||
|  |  | ||||||
|  | @ -21,6 +21,8 @@ | ||||||
| #include <linux/of.h> | #include <linux/of.h> | ||||||
| #include <linux/util_macros.h> | #include <linux/util_macros.h> | ||||||
| 
 | 
 | ||||||
|  | #include <dt-bindings/pwm/pwm.h> | ||||||
|  | 
 | ||||||
| /* Indexes for the sysfs hooks */ | /* Indexes for the sysfs hooks */ | ||||||
| enum adt_sysfs_id { | enum adt_sysfs_id { | ||||||
| 	INPUT		= 0, | 	INPUT		= 0, | ||||||
|  | @ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client) | ||||||
| 	return 0; | 	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) | static int adt7475_probe(struct i2c_client *client) | ||||||
| { | { | ||||||
| 	enum chips chip; | 	enum chips chip; | ||||||
|  | @ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client) | ||||||
| 	if (ret && ret != -EINVAL) | 	if (ret && ret != -EINVAL) | ||||||
| 		dev_warn(&client->dev, "Error configuring pwm polarity\n"); | 		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 */ | 	/* Start monitoring */ | ||||||
| 	switch (chip) { | 	switch (chip) { | ||||||
| 	case adt7475: | 	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) | 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_multi_reg_read(data->regmap, regs, regdata, 2); | ||||||
| 	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); |  | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		return 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 | 	 * 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) | 	if (index == adt7x10_t_alarm_low) | ||||||
| 		hyst = -hyst; | 		hyst = -hyst; | ||||||
| 
 | 
 | ||||||
| 	*val = ADT7X10_REG_TO_TEMP(data, temp) - hyst; | 	*val = ADT7X10_REG_TO_TEMP(data, regdata[1]) - hyst; | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -136,29 +136,25 @@ struct amc6821_data { | ||||||
|  */ |  */ | ||||||
| static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps) | 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; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm); | 	err = regmap_multi_reg_read(regmap, regs, regvals, 3); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 	temps[0] = regvals[1]; | ||||||
| 	err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, ®val); | 	temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regvals[2]) * 4; | ||||||
| 	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; |  | ||||||
| 
 | 
 | ||||||
| 	/* slope is 32 >> <slope bits> in °C */ | 	/* slope is 32 >> <slope bits> in °C */ | ||||||
| 	regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval); | 	slope = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regvals[2]); | ||||||
| 	if (regval) | 	if (slope) | ||||||
| 		temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval); | 		temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - regvals[0], slope); | ||||||
| 	else | 	else | ||||||
| 		temps[2] = 255; | 		temps[2] = 255; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -456,7 +456,6 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct device *dev = &pdev->dev, *hwmon; | 	struct device *dev = &pdev->dev, *hwmon; | ||||||
| 	int ret; | 	int ret; | ||||||
| 	struct device_node *child; |  | ||||||
| 	struct aspeed_pwm_tach_data *priv; | 	struct aspeed_pwm_tach_data *priv; | ||||||
| 	struct pwm_chip *chip; | 	struct pwm_chip *chip; | ||||||
| 
 | 
 | ||||||
|  | @ -498,10 +497,9 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); | 		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); | 		ret = aspeed_create_fan_monitor(dev, child, priv); | ||||||
| 		if (ret) { | 		if (ret) { | ||||||
| 			of_node_put(child); |  | ||||||
| 			dev_warn(dev, "Failed to create fan %d", ret); | 			dev_warn(dev, "Failed to create fan %d", ret); | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -907,7 +907,7 @@ static void aspeed_pwm_tacho_remove(void *data) | ||||||
| static int aspeed_pwm_tacho_probe(struct platform_device *pdev) | static int aspeed_pwm_tacho_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct device *dev = &pdev->dev; | 	struct device *dev = &pdev->dev; | ||||||
| 	struct device_node *np, *child; | 	struct device_node *np; | ||||||
| 	struct aspeed_pwm_tacho_data *priv; | 	struct aspeed_pwm_tacho_data *priv; | ||||||
| 	void __iomem *regs; | 	void __iomem *regs; | ||||||
| 	struct device *hwmon; | 	struct device *hwmon; | ||||||
|  | @ -951,12 +951,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) | ||||||
| 
 | 
 | ||||||
| 	aspeed_create_type(priv); | 	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); | 		ret = aspeed_create_fan(dev, child, priv); | ||||||
| 		if (ret) { | 		if (ret) | ||||||
| 			of_node_put(child); |  | ||||||
| 			return ret; | 			return ret; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	priv->groups[0] = &pwm_dev_group; | 	priv->groups[0] = &pwm_dev_group; | ||||||
|  |  | ||||||
|  | @ -740,37 +740,26 @@ static int cc2_probe(struct i2c_client *client) | ||||||
| 	data->client = client; | 	data->client = client; | ||||||
| 
 | 
 | ||||||
| 	data->regulator = devm_regulator_get_exclusive(dev, "vdd"); | 	data->regulator = devm_regulator_get_exclusive(dev, "vdd"); | ||||||
| 	if (IS_ERR(data->regulator)) { | 	if (IS_ERR(data->regulator)) | ||||||
| 		dev_err_probe(dev, PTR_ERR(data->regulator), | 		return dev_err_probe(dev, PTR_ERR(data->regulator), | ||||||
| 			      "Failed to get regulator\n"); | 				     "Failed to get regulator\n"); | ||||||
| 		return PTR_ERR(data->regulator); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	ret = cc2_request_ready_irq(data, dev); | 	ret = cc2_request_ready_irq(data, dev); | ||||||
| 	if (ret) { | 	if (ret) | ||||||
| 		dev_err_probe(dev, ret, "Failed to request ready irq\n"); | 		return dev_err_probe(dev, ret, "Failed to request ready irq\n"); | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	ret = cc2_request_alarm_irqs(data, dev); | 	ret = cc2_request_alarm_irqs(data, dev); | ||||||
| 	if (ret) { | 	if (ret) | ||||||
| 		dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); | 		return dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); | ||||||
| 		goto disable; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	data->hwmon = devm_hwmon_device_register_with_info(dev, client->name, | 	data->hwmon = devm_hwmon_device_register_with_info(dev, client->name, | ||||||
| 							   data, &cc2_chip_info, | 							   data, &cc2_chip_info, | ||||||
| 							   NULL); | 							   NULL); | ||||||
| 	if (IS_ERR(data->hwmon)) { | 	if (IS_ERR(data->hwmon)) | ||||||
| 		dev_err_probe(dev, PTR_ERR(data->hwmon), | 		return dev_err_probe(dev, PTR_ERR(data->hwmon), | ||||||
| 			      "Failed to register hwmon device\n"); | 				     "Failed to register hwmon device\n"); | ||||||
| 		ret = PTR_ERR(data->hwmon); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| disable: | 	return 0; | ||||||
| 	cc2_disable(data); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void cc2_remove(struct i2c_client *client) | 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], | 		.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", | 		.ident = "Dell Latitude E6440", | ||||||
| 		.matches = { | 		.matches = { | ||||||
|  |  | ||||||
|  | @ -400,6 +400,7 @@ static const struct of_device_id gsc_hwmon_of_match[] = { | ||||||
| 	{ .compatible = "gw,gsc-adc", }, | 	{ .compatible = "gw,gsc-adc", }, | ||||||
| 	{} | 	{} | ||||||
| }; | }; | ||||||
|  | MODULE_DEVICE_TABLE(of, gsc_hwmon_of_match); | ||||||
| 
 | 
 | ||||||
| static struct platform_driver gsc_hwmon_driver = { | static struct platform_driver gsc_hwmon_driver = { | ||||||
| 	.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); | 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) | static char *__hwmon_sanitize_name(struct device *dev, const char *old_name) | ||||||
| { | { | ||||||
| 	char *name, *p; | 	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) | static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) | ||||||
| { | { | ||||||
| 	const struct device_node *np = dev->of_node; | 	const struct device_node *np = dev->of_node; | ||||||
| 	struct device_node *child; |  | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	/* Compatible with non-DT platforms */ | 	/* Compatible with non-DT platforms */ | ||||||
|  | @ -822,12 +821,10 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) | ||||||
| 
 | 
 | ||||||
| 	ina->single_shot = of_property_read_bool(np, "ti,single-shot"); | 	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); | 		ret = ina3221_probe_child_from_dt(dev, child, ina); | ||||||
| 		if (ret) { | 		if (ret) | ||||||
| 			of_node_put(child); |  | ||||||
| 			return ret; | 			return ret; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | @ -438,16 +438,21 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||||||
| 		data->disp_negative = true; | 		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) == 0x60 || | ||||||
| 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) { | 	     (boot_cpu_data.x86_model & 0xf0) == 0x70)) { | ||||||
| 		data->read_htcreg = read_htcreg_nb_f15; | 		data->read_htcreg = read_htcreg_nb_f15; | ||||||
| 		data->read_tempreg = read_tempreg_nb_f15; | 		data->read_tempreg = read_tempreg_nb_f15; | ||||||
| 	} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { | 	} else { | ||||||
| 		data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; | 		data->read_htcreg = read_htcreg_pci; | ||||||
| 		data->read_tempreg = read_tempreg_nb_zen; | 		data->read_tempreg = read_tempreg_pci; | ||||||
| 		data->is_zen = true; | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { | ||||||
| 		switch (boot_cpu_data.x86_model) { | 		switch (boot_cpu_data.x86_model) { | ||||||
| 		case 0x1:	/* Zen */ | 		case 0x1:	/* Zen */ | ||||||
| 		case 0x8:	/* Zen+ */ | 		case 0x8:	/* Zen+ */ | ||||||
|  | @ -469,10 +474,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} else if (boot_cpu_data.x86 == 0x19) { | 	} 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) { | 		switch (boot_cpu_data.x86_model) { | ||||||
| 		case 0x0 ... 0x1:	/* Zen3 SP3/TR */ | 		case 0x0 ... 0x1:	/* Zen3 SP3/TR */ | ||||||
| 		case 0x8:		/* Zen3 TR Chagall */ | 		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); | 			k10temp_get_ccd_support(data, 12); | ||||||
| 			break; | 			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++) { | 	for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) { | ||||||
|  |  | ||||||
|  | @ -2674,19 +2674,16 @@ static int lm90_parse_dt_channel_info(struct i2c_client *client, | ||||||
| 				      struct lm90_data *data) | 				      struct lm90_data *data) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
| 	struct device_node *child; |  | ||||||
| 	struct device *dev = &client->dev; | 	struct device *dev = &client->dev; | ||||||
| 	const struct device_node *np = dev->of_node; | 	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")) | 		if (strcmp(child->name, "channel")) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		err = lm90_probe_channel_from_dt(client, child, data); | 		err = lm90_probe_channel_from_dt(client, child, data); | ||||||
| 		if (err) { | 		if (err) | ||||||
| 			of_node_put(child); |  | ||||||
| 			return err; | 			return err; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | @ -27,15 +27,14 @@ | ||||||
|  * with the LM92. |  * 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/err.h> | ||||||
|  | #include <linux/hwmon.h> | ||||||
|  | #include <linux/i2c.h> | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/module.h> | ||||||
| #include <linux/mutex.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, |  * 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, | static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, | ||||||
| 						I2C_CLIENT_END }; | 						I2C_CLIENT_END }; | ||||||
| enum chips { lm92, max6635 }; |  | ||||||
| 
 |  | ||||||
| /* The LM92 registers */ | /* The LM92 registers */ | ||||||
| #define LM92_REG_CONFIG			0x01 /* 8-bit, RW */ | #define LM92_REG_CONFIG			0x01 /* 8-bit, RW */ | ||||||
| #define LM92_REG_TEMP			0x00 /* 16-bit, RO */ | #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; | 	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); | 	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 */ | /* 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; | 	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) */ | /* Client data (each client gets its own) */ | ||||||
| struct lm92_data { | struct lm92_data { | ||||||
| 	struct i2c_client *client; | 	struct regmap *regmap; | ||||||
| 	struct mutex update_lock; | 	struct mutex update_lock; | ||||||
| 	bool valid; /* false until following fields are valid */ | 	int resolution; | ||||||
| 	unsigned long last_updated; /* in jiffies */ |  | ||||||
| 
 |  | ||||||
| 	/* registers values */ |  | ||||||
| 	s16 temp[t_num_regs];	/* index with enum temp_index */ |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /*
 | static int lm92_temp_read(struct lm92_data *data, u32 attr, int channel, long *val) | ||||||
|  * Sysfs attributes and callback functions |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| static struct lm92_data *lm92_update_device(struct device *dev) |  | ||||||
| { | { | ||||||
| 	struct lm92_data *data = dev_get_drvdata(dev); | 	int reg = -1, hyst_reg = -1, alarm_bit = 0; | ||||||
| 	struct i2c_client *client = data->client; | 	struct regmap *regmap = data->regmap; | ||||||
| 	int i; | 	u32 temp; | ||||||
|  | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->update_lock); | 	switch (attr) { | ||||||
| 
 | 	case hwmon_temp_input: | ||||||
| 	if (time_after(jiffies, data->last_updated + HZ) || | 		reg = LM92_REG_TEMP; | ||||||
| 	    !data->valid) { | 		break; | ||||||
| 		dev_dbg(&client->dev, "Updating lm92 data\n"); | 	case hwmon_temp_min: | ||||||
| 		for (i = 0; i < t_num_regs; i++) { | 		reg = LM92_REG_TEMP_LOW; | ||||||
| 			data->temp[i] = | 		break; | ||||||
| 				i2c_smbus_read_word_swapped(client, regs[i]); | 	case hwmon_temp_max: | ||||||
| 		} | 		reg = LM92_REG_TEMP_HIGH; | ||||||
| 		data->last_updated = jiffies; | 		break; | ||||||
| 		data->valid = true; | 	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; | ||||||
| 	} | 	} | ||||||
|  | 	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]; | ||||||
| 
 | 
 | ||||||
| 	mutex_unlock(&data->update_lock); | 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||||
| 
 | 		if (ret) | ||||||
| 	return data; | 			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)); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, | static int lm92_chip_read(struct lm92_data *data, u32 attr, long *val) | ||||||
| 			 char *buf) |  | ||||||
| { | { | ||||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 	u32 temp; | ||||||
| 	struct lm92_data *data = lm92_update_device(dev); | 	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, | static int lm92_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, | ||||||
| 			  struct device_attribute *devattr, const char *buf, | 		     int channel, long *val) | ||||||
| 			  size_t count) |  | ||||||
| { | { | ||||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |  | ||||||
| 	struct lm92_data *data = dev_get_drvdata(dev); | 	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); | 	switch (type) { | ||||||
| 	if (err) | 	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 int lm92_temp_write(struct lm92_data *data, u32 attr, long val) | ||||||
|  | { | ||||||
|  | 	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); | ||||||
|  | 		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 err; | 		return err; | ||||||
| 
 | 	default: | ||||||
| 	mutex_lock(&data->update_lock); | 		return -EOPNOTSUPP; | ||||||
| 	data->temp[nr] = TEMP_TO_REG(val); | 	} | ||||||
| 	i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]); | 	return regmap_write(regmap, reg, TEMP_TO_REG(val, data->resolution)); | ||||||
| 	mutex_unlock(&data->update_lock); |  | ||||||
| 	return count; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t temp_hyst_show(struct device *dev, | static int lm92_write(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 			      struct device_attribute *devattr, char *buf) | 		      u32 attr, int channel, 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 lm92_data *data = dev_get_drvdata(dev); | ||||||
| 	struct i2c_client *client = data->client; |  | ||||||
| 	long val; |  | ||||||
| 	int err; |  | ||||||
| 
 | 
 | ||||||
| 	err = kstrtol(buf, 10, &val); | 	switch (type) { | ||||||
| 	if (err) | 	case hwmon_temp: | ||||||
| 		return err; | 		return lm92_temp_write(data, attr, val); | ||||||
| 
 | 	default: | ||||||
| 	val = clamp_val(val, -120000, 220000); | 		return -EOPNOTSUPP; | ||||||
| 	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]); |  | ||||||
| 	mutex_unlock(&data->update_lock); |  | ||||||
| 	return count; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, | static umode_t lm92_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||||
| 			   char *buf) | 			       u32 attr, int channel) | ||||||
| { | { | ||||||
| 	struct lm92_data *data = lm92_update_device(dev); | 	switch (type) { | ||||||
| 
 | 	case hwmon_chip: | ||||||
| 	return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input])); | 		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 ssize_t alarm_show(struct device *dev, struct device_attribute *attr, | static const struct hwmon_channel_info * const lm92_info[] = { | ||||||
| 			  char *buf) | 	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), | ||||||
| { | 	HWMON_CHANNEL_INFO(temp, | ||||||
| 	int bitnr = to_sensor_dev_attr(attr)->index; | 			   HWMON_T_INPUT | | ||||||
| 	struct lm92_data *data = lm92_update_device(dev); | 			   HWMON_T_MIN | HWMON_T_MIN_HYST | | ||||||
| 	return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1); | 			   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 SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input); | static const struct hwmon_ops lm92_hwmon_ops = { | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit); | 	.is_visible = lm92_is_visible, | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit); | 	.read = lm92_read, | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min); | 	.write = lm92_write, | ||||||
| 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 const struct hwmon_chip_info lm92_chip_info = { | ||||||
| static DEVICE_ATTR_RO(alarms); | 	.ops = &lm92_hwmon_ops, | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2); | 	.info = lm92_info, | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0); | }; | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1); |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Detection and registration |  * Detection and registration | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static void lm92_init_client(struct i2c_client *client) | static int lm92_init_client(struct regmap *regmap) | ||||||
| { | { | ||||||
| 	u8 config; | 	return regmap_clear_bits(regmap, LM92_REG_CONFIG, 0x01); | ||||||
| 
 |  | ||||||
| 	/* 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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 */ | /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||||
| static int lm92_detect(struct i2c_client *new_client, | static int lm92_detect(struct i2c_client *new_client, | ||||||
| 		       struct i2c_board_info *info) | 		       struct i2c_board_info *info) | ||||||
| { | { | ||||||
| 	struct i2c_adapter *adapter = new_client->adapter; | 	struct i2c_adapter *adapter = new_client->adapter; | ||||||
| 	u8 config; | 	u8 config_addr = LM92_REG_CONFIG; | ||||||
| 	u16 man_id; | 	u8 man_id_addr = LM92_REG_MAN_ID; | ||||||
|  | 	int i, regval; | ||||||
| 
 | 
 | ||||||
| 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ||||||
| 					    | I2C_FUNC_SMBUS_WORD_DATA)) | 					    | I2C_FUNC_SMBUS_WORD_DATA)) | ||||||
| 		return -ENODEV; | 		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); | 	 * Register values repeat with multiples of 8. | ||||||
| 
 | 	 * Read twice to improve detection accuracy. | ||||||
| 	if ((config & 0xe0) == 0x00 && man_id == 0x0180) | 	 */ | ||||||
| 		pr_info("lm92: Found National Semiconductor LM92 chip\n"); | 	for (i = 0; i < 2; i++) { | ||||||
| 	else | 		regval = i2c_smbus_read_word_data(new_client, man_id_addr); | ||||||
| 		return -ENODEV; | 		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); | 	strscpy(info->type, "lm92", I2C_NAME_SIZE); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	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 device *hwmon_dev; | ||||||
| 	struct lm92_data *data; | 	struct lm92_data *data; | ||||||
|  | 	struct regmap *regmap; | ||||||
|  | 	int err; | ||||||
| 
 | 
 | ||||||
| 	data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data), | 	regmap = devm_regmap_init(dev, &lm92_regmap_bus, client, | ||||||
| 			    GFP_KERNEL); | 				  &lm92_regmap_config); | ||||||
|  | 	if (IS_ERR(regmap)) | ||||||
|  | 		return PTR_ERR(regmap); | ||||||
|  | 
 | ||||||
|  | 	data = devm_kzalloc(dev, sizeof(struct lm92_data), GFP_KERNEL); | ||||||
| 	if (!data) | 	if (!data) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	data->client = new_client; | 	data->regmap = regmap; | ||||||
|  | 	data->resolution = (unsigned long)i2c_get_match_data(client); | ||||||
| 	mutex_init(&data->update_lock); | 	mutex_init(&data->update_lock); | ||||||
| 
 | 
 | ||||||
| 	/* Initialize the chipset */ | 	/* 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, | 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, | ||||||
| 							   new_client->name, | 							 &lm92_chip_info, NULL); | ||||||
| 							   data, lm92_groups); |  | ||||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -318,9 +412,10 @@ static int lm92_probe(struct i2c_client *new_client) | ||||||
|  * Module and driver stuff |  * Module and driver stuff | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | /* .driver_data is limit register resolution */  | ||||||
| static const struct i2c_device_id lm92_id[] = { | static const struct i2c_device_id lm92_id[] = { | ||||||
| 	{ "lm92", lm92 }, | 	{ "lm92", 13 }, | ||||||
| 	{ "max6635", max6635 }, | 	{ "max6635", 9 }, | ||||||
| 	{ } | 	{ } | ||||||
| }; | }; | ||||||
| MODULE_DEVICE_TABLE(i2c, lm92_id); | MODULE_DEVICE_TABLE(i2c, lm92_id); | ||||||
|  |  | ||||||
|  | @ -8,16 +8,15 @@ | ||||||
|  * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> |  * 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/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/mutex.h> | ||||||
| #include <linux/sysfs.h> | #include <linux/regmap.h> | ||||||
|  | #include <linux/util_macros.h> | ||||||
| 
 | 
 | ||||||
| #define DRVNAME "lm95234" | #define DRVNAME "lm95234" | ||||||
| 
 | 
 | ||||||
|  | @ -32,6 +31,8 @@ static const unsigned short normal_i2c[] = { | ||||||
| #define LM95234_REG_STATUS		0x02 | #define LM95234_REG_STATUS		0x02 | ||||||
| #define LM95234_REG_CONFIG		0x03 | #define LM95234_REG_CONFIG		0x03 | ||||||
| #define LM95234_REG_CONVRATE		0x04 | #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_FAULT		0x07 | ||||||
| #define LM95234_REG_STS_TCRIT1		0x08 | #define LM95234_REG_STS_TCRIT1		0x08 | ||||||
| #define LM95234_REG_STS_TCRIT2		0x09 | #define LM95234_REG_STS_TCRIT2		0x09 | ||||||
|  | @ -52,541 +53,372 @@ static const unsigned short normal_i2c[] = { | ||||||
| 
 | 
 | ||||||
| /* Client data (each client gets its own) */ | /* Client data (each client gets its own) */ | ||||||
| struct lm95234_data { | struct lm95234_data { | ||||||
| 	struct i2c_client *client; | 	struct regmap *regmap; | ||||||
| 	const struct attribute_group *groups[3]; |  | ||||||
| 	struct mutex update_lock; | 	struct mutex update_lock; | ||||||
| 	unsigned long last_updated, interval;	/* in jiffies */ | 	enum chips type; | ||||||
| 	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 */ |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 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; | 	unsigned int regs[2]; | ||||||
| 	u16 temp = 0; | 	int temp = 0, ret; | ||||||
|  | 	u8 regvals[2]; | ||||||
| 
 | 
 | ||||||
| 	if (index) { | 	if (index) { | ||||||
| 		val = i2c_smbus_read_byte_data(client, | 		regs[0] = LM95234_REG_UTEMPH(index - 1); | ||||||
| 					       LM95234_REG_UTEMPH(index - 1)); | 		regs[1] = LM95234_REG_UTEMPL(index - 1); | ||||||
| 		if (val < 0) | 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||||
| 			return val; | 		if (ret) | ||||||
| 		temp = val << 8; | 			return ret; | ||||||
| 		val = i2c_smbus_read_byte_data(client, | 		temp = (regvals[0] << 8) | regvals[1]; | ||||||
| 					       LM95234_REG_UTEMPL(index - 1)); |  | ||||||
| 		if (val < 0) |  | ||||||
| 			return val; |  | ||||||
| 		temp |= val; |  | ||||||
| 		*t = temp; |  | ||||||
| 	} | 	} | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Read signed temperature if unsigned temperature is 0, | 	 * Read signed temperature if unsigned temperature is 0, | ||||||
| 	 * or if this is the local sensor. | 	 * or if this is the local sensor. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (!temp) { | 	if (!temp) { | ||||||
| 		val = i2c_smbus_read_byte_data(client, | 		regs[0] = LM95234_REG_TEMPH(index); | ||||||
| 					       LM95234_REG_TEMPH(index)); | 		regs[1] = LM95234_REG_TEMPL(index); | ||||||
| 		if (val < 0) | 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||||
| 			return val; | 		if (ret) | ||||||
| 		temp = val << 8; | 			return ret; | ||||||
| 		val = i2c_smbus_read_byte_data(client, | 		temp = (regvals[0] << 8) | regvals[1]; | ||||||
| 					       LM95234_REG_TEMPL(index)); | 		temp = sign_extend32(temp, 15); | ||||||
| 		if (val < 0) | 	} | ||||||
| 			return val; | 	*t = DIV_ROUND_CLOSEST(temp * 125, 32); | ||||||
| 		temp |= val; | 	return 0; | ||||||
| 		*t = (s16)temp; | } | ||||||
|  | 
 | ||||||
|  | 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; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static u16 update_intervals[] = { 143, 364, 1000, 2500 }; | static u16 update_intervals[] = { 143, 364, 1000, 2500 }; | ||||||
| 
 | 
 | ||||||
| /* Fill value cache. Must be called with update lock held. */ | static int lm95234_chip_write(struct device *dev, u32 attr, long val) | ||||||
| 
 |  | ||||||
| static int lm95234_fill_cache(struct lm95234_data *data, |  | ||||||
| 			      struct i2c_client *client) |  | ||||||
| { | { | ||||||
| 	int i, ret; | 	struct lm95234_data *data = dev_get_drvdata(dev); | ||||||
| 
 | 
 | ||||||
| 	ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); | 	switch (attr) { | ||||||
| 	if (ret < 0) | 	case hwmon_chip_update_interval: | ||||||
| 		return ret; | 		val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); | ||||||
| 
 | 		return regmap_write(data->regmap, LM95234_REG_CONVRATE, val); | ||||||
| 	data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]); | 	default: | ||||||
| 
 | 		return -EOPNOTSUPP; | ||||||
| 	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; |  | ||||||
| 	} | 	} | ||||||
| 	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; | 	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; | 	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) || | 		*val = update_intervals[convrate & 0x03]; | ||||||
| 	    !data->valid) { | 		break; | ||||||
| 		int i; | 	default: | ||||||
| 
 | 		return -EOPNOTSUPP; | ||||||
| 		if (!data->valid) { |  | ||||||
| 			ret = lm95234_fill_cache(data, client); |  | ||||||
| 			if (ret < 0) |  | ||||||
| 				goto abort; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		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; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		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; |  | ||||||
| 	} | 	} | ||||||
| 	ret = 0; | 	return 0; | ||||||
| abort: |  | ||||||
| 	mutex_unlock(&data->update_lock); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *attr, | static int lm95234_write(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 			 char *buf) | 			 u32 attr, int channel, long val) | ||||||
| { | { | ||||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | 	switch (type) { | ||||||
| 	int index = to_sensor_dev_attr(attr)->index; | 	case hwmon_chip: | ||||||
| 	int ret = lm95234_update_device(data); | 		return lm95234_chip_write(dev, attr, val); | ||||||
| 
 | 	case hwmon_temp: | ||||||
| 	if (ret) | 		return lm95234_temp_write(dev, attr, channel, val); | ||||||
| 		return ret; | 	default: | ||||||
| 
 | 		return -EOPNOTSUPP; | ||||||
| 	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, | static int lm95234_read(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 			  char *buf) | 			u32 attr, int channel, long *val) | ||||||
| { | { | ||||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | 	switch (type) { | ||||||
| 	u32 mask = to_sensor_dev_attr(attr)->index; | 	case hwmon_chip: | ||||||
| 	int ret = lm95234_update_device(data); | 		return lm95234_chip_read(dev, attr, val); | ||||||
| 
 | 	case hwmon_temp: | ||||||
| 	if (ret) | 		return lm95234_temp_read(dev, attr, channel, val); | ||||||
| 		return ret; | 	default: | ||||||
| 
 | 		return -EOPNOTSUPP; | ||||||
| 	return sprintf(buf, "%u", !!(data->status & mask)); | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t type_show(struct device *dev, struct device_attribute *attr, | static umode_t lm95234_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||||
| 			 char *buf) | 				  u32 attr, int channel) | ||||||
| { | { | ||||||
| 	struct lm95234_data *data = dev_get_drvdata(dev); | 	const struct lm95234_data *data = _data; | ||||||
| 	u8 mask = to_sensor_dev_attr(attr)->index; |  | ||||||
| 	int ret = lm95234_update_device(data); |  | ||||||
| 
 | 
 | ||||||
| 	if (ret) | 	if (data->type == lm95233 && channel > 2) | ||||||
| 		return ret; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n"); | 	switch (type) { | ||||||
| } | 	case hwmon_chip: | ||||||
| 
 | 		switch (attr) { | ||||||
| static ssize_t type_store(struct device *dev, struct device_attribute *attr, | 		case hwmon_chip_update_interval: | ||||||
| 			  const char *buf, size_t count) | 			return 0644; | ||||||
| { | 		default: | ||||||
| 	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]) |  | ||||||
| 			break; | 			break; | ||||||
|  | 		} | ||||||
|  | 		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; | ||||||
| 	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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); | static const struct hwmon_channel_info * const lm95234_info[] = { | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); | 	HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); | 	HWMON_CHANNEL_INFO(temp, | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); | 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); | 			   HWMON_T_MAX_ALARM | HWMON_T_ENABLE, | ||||||
| 
 | 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, BIT(0) | BIT(1)); | 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, BIT(2) | BIT(3)); | 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, BIT(4) | BIT(5)); | 			   HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE, | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, BIT(6) | BIT(7)); | 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||||
| 
 | 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1)); | 			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2)); | 			   HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE, | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp4_type, type, BIT(3)); | 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp5_type, type, BIT(4)); | 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||||
| 
 | 			   HWMON_T_OFFSET | HWMON_T_ENABLE, | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp1_max, tcrit1, 0); | 			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp2_max, tcrit2, 0); | 			   HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp3_max, tcrit2, 1); | 			   HWMON_T_OFFSET | HWMON_T_ENABLE), | ||||||
| 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, |  | ||||||
| 	NULL | 	NULL | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct attribute_group lm95234_common_group = { | static const struct hwmon_ops lm95234_hwmon_ops = { | ||||||
| 	.attrs = lm95234_common_attrs, | 	.is_visible = lm95234_is_visible, | ||||||
|  | 	.read = lm95234_read, | ||||||
|  | 	.write = lm95234_write, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct attribute *lm95234_attrs[] = { | static const struct hwmon_chip_info lm95234_chip_info = { | ||||||
| 	&sensor_dev_attr_temp4_input.dev_attr.attr, | 	.ops = &lm95234_hwmon_ops, | ||||||
| 	&sensor_dev_attr_temp5_input.dev_attr.attr, | 	.info = lm95234_info, | ||||||
| 	&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 attribute_group lm95234_group = { | static bool lm95234_volatile_reg(struct device *dev, unsigned int reg) | ||||||
| 	.attrs = lm95234_attrs, | { | ||||||
|  | 	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, | static int lm95234_detect(struct i2c_client *client, | ||||||
|  | @ -649,61 +481,60 @@ static int lm95234_detect(struct i2c_client *client, | ||||||
| 	return 0; | 	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 */ | 	/* start conversion if necessary */ | ||||||
| 	val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); | 	ret = regmap_clear_bits(regmap, LM95234_REG_CONFIG, 0x40); | ||||||
| 	if (val < 0) | 	if (ret) | ||||||
| 		return val; | 		return ret; | ||||||
| 	if (val & 0x40) |  | ||||||
| 		i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG, |  | ||||||
| 					  val & ~0x40); |  | ||||||
| 
 | 
 | ||||||
| 	/* If diode type status reports an error, try to fix it */ | 	/* If diode type status reports an error, try to fix it */ | ||||||
| 	val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); | 	ret = regmap_read(regmap, LM95234_REG_REM_MODEL_STS, &val); | ||||||
| 	if (val < 0) | 	if (ret < 0) | ||||||
| 		return val; | 		return ret; | ||||||
| 	model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); | 	ret = regmap_read(regmap, LM95234_REG_REM_MODEL, &model); | ||||||
| 	if (model < 0) | 	if (ret < 0) | ||||||
| 		return model; | 		return ret; | ||||||
| 	if (model & val) { | 	if (model & val) { | ||||||
| 		dev_notice(&client->dev, | 		dev_notice(dev, | ||||||
| 			   "Fixing remote diode type misconfiguration (0x%x)\n", | 			   "Fixing remote diode type misconfiguration (0x%x)\n", | ||||||
| 			   val); | 			   val); | ||||||
| 		i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, | 		ret = regmap_write(regmap, LM95234_REG_REM_MODEL, model & ~val); | ||||||
| 					  model & ~val); |  | ||||||
| 	} | 	} | ||||||
| 	return 0; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int lm95234_probe(struct i2c_client *client) | static int lm95234_probe(struct i2c_client *client) | ||||||
| { | { | ||||||
| 	enum chips type = (uintptr_t)i2c_get_match_data(client); |  | ||||||
| 	struct device *dev = &client->dev; | 	struct device *dev = &client->dev; | ||||||
| 	struct lm95234_data *data; | 	struct lm95234_data *data; | ||||||
| 	struct device *hwmon_dev; | 	struct device *hwmon_dev; | ||||||
|  | 	struct regmap *regmap; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); | 	data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); | ||||||
| 	if (!data) | 	if (!data) | ||||||
| 		return -ENOMEM; | 		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); | 	mutex_init(&data->update_lock); | ||||||
| 
 | 
 | ||||||
| 	/* Initialize the LM95234 chip */ | 	/* Initialize the LM95234 chip */ | ||||||
| 	err = lm95234_init_client(client); | 	err = lm95234_init_client(dev, regmap); | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
| 	data->groups[0] = &lm95234_common_group; | 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, | ||||||
| 	if (type == lm95234) | 							 data, &lm95234_chip_info, NULL); | ||||||
| 		data->groups[1] = &lm95234_group; |  | ||||||
| 
 |  | ||||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, |  | ||||||
| 							   data, data->groups); |  | ||||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | 	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 lm95245_data *data = dev_get_drvdata(dev); | ||||||
| 	struct regmap *regmap = data->regmap; | 	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) { | 	switch (attr) { | ||||||
| 	case hwmon_temp_input: | 	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; | 				    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; | 				    LM95245_REG_R_LOCAL_TEMPH_S; | ||||||
| 		ret = regmap_read(regmap, regl, ®vall); | 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||||
| 		if (ret < 0) |  | ||||||
| 			return ret; |  | ||||||
| 		ret = regmap_read(regmap, regh, ®valh); |  | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			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 | 		 * Use signed calculation for remote if signed bit is set | ||||||
| 		 * or if reported temperature is below signed limit. | 		 * or if reported temperature is below signed limit. | ||||||
| 		 */ | 		 */ | ||||||
| 		if (!channel || (regvalh & 0x80) || regvalh < 0x7f) { | 		if (!channel || (regvals[1] & 0x80) || regvals[1] < 0x7f) { | ||||||
| 			*val = temp_from_reg_signed(regvalh, regvall); | 			*val = temp_from_reg_signed(regvals[1], regvals[0]); | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} | 		} | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U, | 		ret = regmap_bulk_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, regvals, 2); | ||||||
| 				  ®vall); | 		if (ret) | ||||||
| 		if (ret < 0) |  | ||||||
| 			return ret; | 			return ret; | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, | 		*val = temp_from_reg_unsigned(regvals[0], regvals[1]); | ||||||
| 				  ®valh); |  | ||||||
| 		if (ret < 0) |  | ||||||
| 			return ret; |  | ||||||
| 		*val = temp_from_reg_unsigned(regvalh, regvall); |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_max: | 	case hwmon_temp_max: | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, | 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, | ||||||
| 				  ®valh); | 				  ®val); | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		*val = regvalh * 1000; | 		*val = regval * 1000; | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_crit: | 	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; | 				    LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; | ||||||
| 		ret = regmap_read(regmap, regh, ®valh); | 		ret = regmap_read(regmap, regs[0], ®val); | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		*val = regvalh * 1000; | 		*val = regval * 1000; | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_max_hyst: | 	case hwmon_temp_max_hyst: | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, | 		regs[0] = LM95245_REG_RW_REMOTE_OS_LIMIT; | ||||||
| 				  ®valh); | 		regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS; | ||||||
|  | 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, | 		*val = (regvals[0] - regvals[1]) * 1000; | ||||||
| 				  ®vall); |  | ||||||
| 		if (ret < 0) |  | ||||||
| 			return ret; |  | ||||||
| 		*val = (regvalh - regvall) * 1000; |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_crit_hyst: | 	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; | 				    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) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, | 		*val = (regvals[0] - regvals[1]) * 1000; | ||||||
| 				  ®vall); |  | ||||||
| 		if (ret < 0) |  | ||||||
| 			return ret; |  | ||||||
| 		*val = (regvalh - regvall) * 1000; |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_type: | 	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) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		*val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2; | 		*val = (regval & CFG2_REMOTE_TT) ? 1 : 2; | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_offset: | 	case hwmon_temp_offset: | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL, | 		ret = regmap_bulk_read(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2); | ||||||
| 				  ®vall); |  | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH, | 		*val = temp_from_reg_signed(regvals[0], regvals[1]); | ||||||
| 				  ®valh); |  | ||||||
| 		if (ret < 0) |  | ||||||
| 			return ret; |  | ||||||
| 		*val = temp_from_reg_signed(regvalh, regvall); |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_max_alarm: | 	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) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		*val = !!(regvalh & STATUS1_ROS); | 		*val = !!(regval & STATUS1_ROS); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_crit_alarm: | 	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) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		*val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); | 		*val = !!(regval & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_temp_fault: | 	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) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		*val = !!(regvalh & STATUS1_DIODE_FAULT); | 		*val = !!(regval & STATUS1_DIODE_FAULT); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	default: | 	default: | ||||||
| 		return -EOPNOTSUPP; | 		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 lm95245_data *data = dev_get_drvdata(dev); | ||||||
| 	struct regmap *regmap = data->regmap; | 	struct regmap *regmap = data->regmap; | ||||||
| 	unsigned int regval; | 	unsigned int regval; | ||||||
|  | 	u8 regvals[2]; | ||||||
| 	int ret, reg; | 	int ret, reg; | ||||||
| 
 | 
 | ||||||
| 	switch (attr) { | 	switch (attr) { | ||||||
|  | @ -311,16 +297,10 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel, | ||||||
| 	case hwmon_temp_offset: | 	case hwmon_temp_offset: | ||||||
| 		val = clamp_val(val, -128000, 127875); | 		val = clamp_val(val, -128000, 127875); | ||||||
| 		val = val * 256 / 1000; | 		val = val * 256 / 1000; | ||||||
| 		mutex_lock(&data->update_lock); | 		regvals[0] = val >> 8; | ||||||
| 		ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL, | 		regvals[1] = val & 0xe0; | ||||||
| 				   val & 0xe0); | 
 | ||||||
| 		if (ret < 0) { | 		ret = regmap_bulk_write(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2); | ||||||
| 			mutex_unlock(&data->update_lock); |  | ||||||
| 			return ret; |  | ||||||
| 		} |  | ||||||
| 		ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH, |  | ||||||
| 				   (val >> 8) & 0xff); |  | ||||||
| 		mutex_unlock(&data->update_lock); |  | ||||||
| 		return ret; | 		return ret; | ||||||
| 	case hwmon_temp_type: | 	case hwmon_temp_type: | ||||||
| 		if (val != 1 && val != 2) | 		if (val != 1 && val != 2) | ||||||
|  |  | ||||||
|  | @ -11,7 +11,8 @@ | ||||||
| #include <linux/hwmon.h> | #include <linux/hwmon.h> | ||||||
| #include <linux/hwmon-sysfs.h> | #include <linux/hwmon-sysfs.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
| #include <linux/of.h> | #include <linux/mod_devicetable.h> | ||||||
|  | #include <linux/property.h> | ||||||
| #include <linux/regmap.h> | #include <linux/regmap.h> | ||||||
| 
 | 
 | ||||||
| #include "ltc2947.h" | #include "ltc2947.h" | ||||||
|  | @ -1034,9 +1035,8 @@ static int ltc2947_setup(struct ltc2947_data *st) | ||||||
| 		/* 19.89E-6 * 10E9 */ | 		/* 19.89E-6 * 10E9 */ | ||||||
| 		st->lsb_energy = 19890; | 		st->lsb_energy = 19890; | ||||||
| 	} | 	} | ||||||
| 	ret = of_property_read_u32_array(st->dev->of_node, | 	ret = device_property_read_u32_array(st->dev, "adi,accumulator-ctl-pol", | ||||||
| 					 "adi,accumulator-ctl-pol", accum, | 					     accum, ARRAY_SIZE(accum)); | ||||||
| 					  ARRAY_SIZE(accum)); |  | ||||||
| 	if (!ret) { | 	if (!ret) { | ||||||
| 		u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) | | 		u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) | | ||||||
| 				LTC2947_ACCUM_POL_2(accum[1]); | 				LTC2947_ACCUM_POL_2(accum[1]); | ||||||
|  | @ -1045,9 +1045,9 @@ static int ltc2947_setup(struct ltc2947_data *st) | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			return ret; | 			return ret; | ||||||
| 	} | 	} | ||||||
| 	ret = of_property_read_u32(st->dev->of_node, | 	ret = device_property_read_u32(st->dev, | ||||||
| 				   "adi,accumulation-deadband-microamp", | 				       "adi,accumulation-deadband-microamp", | ||||||
| 				   &deadband); | 				       &deadband); | ||||||
| 	if (!ret) { | 	if (!ret) { | ||||||
| 		/* the LSB is the same as the current, so 3mA */ | 		/* the LSB is the same as the current, so 3mA */ | ||||||
| 		ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND, | 		ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND, | ||||||
|  | @ -1056,7 +1056,7 @@ static int ltc2947_setup(struct ltc2947_data *st) | ||||||
| 			return ret; | 			return ret; | ||||||
| 	} | 	} | ||||||
| 	/* check gpio cfg */ | 	/* 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) { | 	if (!ret) { | ||||||
| 		/* setup GPIO as output */ | 		/* setup GPIO as output */ | ||||||
| 		u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) | | 		u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) | | ||||||
|  | @ -1067,8 +1067,8 @@ static int ltc2947_setup(struct ltc2947_data *st) | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			return 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)); | 					     accum, ARRAY_SIZE(accum)); | ||||||
| 	if (!ret) { | 	if (!ret) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Setup the accum options. The gpioctl is already defined as | 		 * Setup the accum options. The gpioctl is already defined as | ||||||
|  |  | ||||||
|  | @ -854,33 +854,24 @@ static const struct regmap_config ltc2992_regmap_config = { | ||||||
| 
 | 
 | ||||||
| static int ltc2992_parse_dt(struct ltc2992_state *st) | static int ltc2992_parse_dt(struct ltc2992_state *st) | ||||||
| { | { | ||||||
| 	struct fwnode_handle *fwnode; |  | ||||||
| 	struct fwnode_handle *child; |  | ||||||
| 	u32 addr; | 	u32 addr; | ||||||
| 	u32 val; | 	u32 val; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	fwnode = dev_fwnode(&st->client->dev); | 	device_for_each_child_node_scoped(&st->client->dev, child) { | ||||||
| 
 |  | ||||||
| 	fwnode_for_each_available_child_node(fwnode, child) { |  | ||||||
| 		ret = fwnode_property_read_u32(child, "reg", &addr); | 		ret = fwnode_property_read_u32(child, "reg", &addr); | ||||||
| 		if (ret < 0) { | 		if (ret < 0) | ||||||
| 			fwnode_handle_put(child); |  | ||||||
| 			return ret; | 			return ret; | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		if (addr > 1) { | 		if (addr > 1) | ||||||
| 			fwnode_handle_put(child); |  | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); | 		ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); | ||||||
| 		if (!ret) { | 		if (!ret) { | ||||||
| 			if (!val) { | 			if (!val) | ||||||
| 				fwnode_handle_put(child); |  | ||||||
| 				return dev_err_probe(&st->client->dev, -EINVAL, | 				return dev_err_probe(&st->client->dev, -EINVAL, | ||||||
| 						     "shunt resistor value cannot be zero\n"); | 						     "shunt resistor value cannot be zero\n"); | ||||||
| 			} | 
 | ||||||
| 			st->r_sense_uohm[addr] = val; | 			st->r_sense_uohm[addr] = val; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -79,7 +79,7 @@ static const bool max16065_have_current[] = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct max16065_data { | struct max16065_data { | ||||||
| 	enum chips type; | 	enum chips chip; | ||||||
| 	struct i2c_client *client; | 	struct i2c_client *client; | ||||||
| 	const struct attribute_group *groups[4]; | 	const struct attribute_group *groups[4]; | ||||||
| 	struct mutex update_lock; | 	struct mutex update_lock; | ||||||
|  | @ -114,9 +114,10 @@ static inline int LIMIT_TO_MV(int limit, int range) | ||||||
| 	return limit * range / 256; | 	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) | 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); | 						     MAX16065_CURR_SENSE); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++) | 		for (i = 0; i < 2; i++) | ||||||
| 			data->fault[i] | 			data->fault[i] | ||||||
| 			  = i2c_smbus_read_byte_data(client, MAX16065_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->last_updated = jiffies; | ||||||
| 		data->valid = true; | 		data->valid = true; | ||||||
| 	} | 	} | ||||||
|  | @ -513,6 +521,7 @@ static int max16065_probe(struct i2c_client *client) | ||||||
| 	if (unlikely(!data)) | 	if (unlikely(!data)) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
|  | 	data->chip = chip; | ||||||
| 	data->client = client; | 	data->client = client; | ||||||
| 	mutex_init(&data->update_lock); | 	mutex_init(&data->update_lock); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,275 +12,356 @@ | ||||||
|  *   http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
 |  *   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/err.h> | ||||||
| #include <linux/mutex.h> | #include <linux/hwmon.h> | ||||||
| #include <linux/sysfs.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[] = { | static const unsigned short normal_i2c[] = { | ||||||
| 	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; | 	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; | ||||||
| 
 | 
 | ||||||
| /*
 | #define MAX1619_REG_LOCAL_TEMP		0x00 | ||||||
|  * The MAX1619 registers | #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 | static int get_alarms(struct regmap *regmap) | ||||||
| #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) |  | ||||||
| { | { | ||||||
| 	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 { | 	switch (attr) { | ||||||
| 	t_input1 = 0, | 	case hwmon_temp_input: | ||||||
| 	t_input2, | 		reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP; | ||||||
| 	t_low2, | 		break; | ||||||
| 	t_high2, | 	case hwmon_temp_min: | ||||||
| 	t_crit2, | 		reg = MAX1619_REG_REMOTE_LOW; | ||||||
| 	t_hyst2, | 		break; | ||||||
| 	t_num_regs | 	case hwmon_temp_max: | ||||||
| }; | 		reg = MAX1619_REG_REMOTE_HIGH; | ||||||
| 
 | 		break; | ||||||
| /*
 | 	case hwmon_temp_crit: | ||||||
|  * Client data (each client gets its own) | 		reg = MAX1619_REG_REMOTE_CRIT; | ||||||
|  */ | 		break; | ||||||
| 
 | 	case hwmon_temp_crit_hyst: | ||||||
| struct max1619_data { | 		reg = MAX1619_REG_REMOTE_CRIT_HYST; | ||||||
| 	struct i2c_client *client; | 		break; | ||||||
| 	struct mutex update_lock; | 	case hwmon_temp_min_alarm: | ||||||
| 	bool valid; /* false until following fields are valid */ | 		alarm_bit = 3; | ||||||
| 	unsigned long last_updated; /* in jiffies */ | 		break; | ||||||
| 
 | 	case hwmon_temp_max_alarm: | ||||||
| 	/* registers values */ | 		alarm_bit = 4; | ||||||
| 	u8 temp[t_num_regs];	/* index with enum temp_index */ | 		break; | ||||||
| 	u8 alarms; | 	case hwmon_temp_crit_alarm: | ||||||
| }; | 		alarm_bit = 1; | ||||||
| 
 | 		break; | ||||||
| static const u8 regs_read[t_num_regs] = { | 	case hwmon_temp_fault: | ||||||
| 	[t_input1] = MAX1619_REG_R_LOCAL_TEMP, | 		alarm_bit = 2; | ||||||
| 	[t_input2] = MAX1619_REG_R_REMOTE_TEMP, | 		break; | ||||||
| 	[t_low2] = MAX1619_REG_R_REMOTE_LOW, | 	default: | ||||||
| 	[t_high2] = MAX1619_REG_R_REMOTE_HIGH, | 		return -EOPNOTSUPP; | ||||||
| 	[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; |  | ||||||
| 	} | 	} | ||||||
| 
 | 	if (reg >= 0) { | ||||||
| 	mutex_unlock(&data->update_lock); | 		ret = regmap_read(regmap, reg, &temp); | ||||||
| 
 | 		if (ret < 0) | ||||||
| 	return data; | 			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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 }; | ||||||
|  * Sysfs stuff |  | ||||||
|  */ |  | ||||||
| 
 | 
 | ||||||
| static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, | static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val) | ||||||
| 			 char *buf) |  | ||||||
| { | { | ||||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 	int alarms, ret; | ||||||
| 	struct max1619_data *data = max1619_update_device(dev); | 	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, | static int max1619_read(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 			  struct device_attribute *devattr, const char *buf, | 			u32 attr, int channel, long *val) | ||||||
| 			  size_t count) |  | ||||||
| { | { | ||||||
| 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | 	struct regmap *regmap = dev_get_drvdata(dev); | ||||||
| 	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; |  | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->update_lock); | 	switch (type) { | ||||||
| 	data->temp[attr->index] = temp_to_reg(val); | 	case hwmon_chip: | ||||||
| 	i2c_smbus_write_byte_data(client, regs_write[attr->index], | 		return max1619_chip_read(regmap, attr, val); | ||||||
| 				  data->temp[attr->index]); | 	case hwmon_temp: | ||||||
| 	mutex_unlock(&data->update_lock); | 		return max1619_temp_read(regmap, attr, channel, val); | ||||||
| 	return count; | 	default: | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, | static int max1619_chip_write(struct regmap *regmap, u32 attr, long val) | ||||||
| 			   char *buf) |  | ||||||
| { | { | ||||||
| 	struct max1619_data *data = max1619_update_device(dev); | 	switch (attr) { | ||||||
| 	return sprintf(buf, "%d\n", data->alarms); | 	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, | static int max1619_temp_write(struct regmap *regmap, | ||||||
| 			  char *buf) | 			      u32 attr, int channel, long val) | ||||||
| { | { | ||||||
| 	int bitnr = to_sensor_dev_attr(attr)->index; | 	int reg; | ||||||
| 	struct max1619_data *data = max1619_update_device(dev); | 
 | ||||||
| 	return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); | 	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 int max1619_write(struct device *dev, enum hwmon_sensor_types type, | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2); | 			 u32 attr, int channel, long val) | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2); | { | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2); | 	struct regmap *regmap = dev_get_drvdata(dev); | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2); |  | ||||||
| static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2); |  | ||||||
| 
 | 
 | ||||||
| static DEVICE_ATTR_RO(alarms); | 	switch (type) { | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1); | 	case hwmon_chip: | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); | 		return max1619_chip_write(regmap, attr, val); | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); | 	case hwmon_temp: | ||||||
| static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); | 		return max1619_temp_write(regmap, attr, channel, val); | ||||||
|  | 	default: | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| static struct attribute *max1619_attrs[] = { | static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | 				  u32 attr, int channel) | ||||||
| 	&sensor_dev_attr_temp2_input.dev_attr.attr, | { | ||||||
| 	&sensor_dev_attr_temp2_min.dev_attr.attr, | 	switch (type) { | ||||||
| 	&sensor_dev_attr_temp2_max.dev_attr.attr, | 	case hwmon_chip: | ||||||
| 	&sensor_dev_attr_temp2_crit.dev_attr.attr, | 		switch (attr) { | ||||||
| 	&sensor_dev_attr_temp2_crit_hyst.dev_attr.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, | static const struct hwmon_channel_info * const max1619_info[] = { | ||||||
| 	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, | 	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL), | ||||||
| 	&sensor_dev_attr_temp2_fault.dev_attr.attr, | 	HWMON_CHANNEL_INFO(temp, | ||||||
| 	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, | 			   HWMON_T_INPUT, | ||||||
| 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, | 			   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 | 	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 */ | /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||||
| static int max1619_detect(struct i2c_client *client, | static int max1619_detect(struct i2c_client *client, | ||||||
| 			  struct i2c_board_info *info) | 			  struct i2c_board_info *info) | ||||||
| { | { | ||||||
| 	struct i2c_adapter *adapter = client->adapter; | 	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)) | 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 
 | 
 | ||||||
| 	/* detection */ | 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG); | ||||||
| 	reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); | 	if (regval < 0 || (regval & 0x03)) | ||||||
| 	reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE); | 		return -ENODEV; | ||||||
| 	reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS); | 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE); | ||||||
| 	if ((reg_config & 0x03) != 0x00 | 	if (regval < 0 || regval > 0x07) | ||||||
| 	 || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { | 		return -ENODEV; | ||||||
| 		dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", | 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS); | ||||||
| 			client->addr); | 	if (regval < 0 || (regval & 0x61)) | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	/* identification */ | 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID); | ||||||
| 	man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID); | 	if (regval != 0x4d) | ||||||
| 	chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID); | 		return -ENODEV; | ||||||
| 	if (man_id != 0x4D || chip_id != 0x04) { | 	regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID); | ||||||
| 		dev_info(&adapter->dev, | 	if (regval != 0x04) | ||||||
| 			 "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", |  | ||||||
| 			 man_id, chip_id); |  | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	strscpy(info->type, "max1619", I2C_NAME_SIZE); | 	strscpy(info->type, "max1619", I2C_NAME_SIZE); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void max1619_init_client(struct i2c_client *client) | static int max1619_init_chip(struct regmap *regmap) | ||||||
| { | { | ||||||
| 	u8 config; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5);	/* 2 Hz */ | ||||||
| 	 * Start the conversions. | 	if (ret) | ||||||
| 	 */ | 		return ret; | ||||||
| 	i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, | 
 | ||||||
| 				  5); /* 2 Hz */ | 	/* Start conversions */ | ||||||
| 	config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); | 	return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40); | ||||||
| 	if (config & 0x40) |  | ||||||
| 		i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG, |  | ||||||
| 					  config & 0xBF); /* run */ |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 device *hwmon_dev; | ||||||
|  | 	struct regmap *regmap; | ||||||
|  | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data), | 	regmap = devm_regmap_init(dev, &max1619_regmap_bus, client, | ||||||
| 			    GFP_KERNEL); | 				  &max1619_regmap_config); | ||||||
| 	if (!data) | 	if (IS_ERR(regmap)) | ||||||
| 		return -ENOMEM; | 		return PTR_ERR(regmap); | ||||||
| 
 | 
 | ||||||
| 	data->client = new_client; | 	ret = max1619_init_chip(regmap); | ||||||
| 	mutex_init(&data->update_lock); | 	if (ret) | ||||||
|  | 		return ret; | ||||||
| 
 | 
 | ||||||
| 	/* Initialize the MAX1619 chip */ | 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, | ||||||
| 	max1619_init_client(new_client); | 							 regmap, &max1619_chip_info, NULL); | ||||||
| 
 |  | ||||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, |  | ||||||
| 							   new_client->name, |  | ||||||
| 							   data, |  | ||||||
| 							   max1619_groups); |  | ||||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,15 +6,15 @@ | ||||||
|  * some credit to Christoph Scheurer, but largely a rewrite |  * some credit to Christoph Scheurer, but largely a rewrite | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/module.h> | #include <linux/bitops.h> | ||||||
| #include <linux/init.h> | #include <linux/bits.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/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 */ | /* Addresses to scan */ | ||||||
| static const unsigned short max1668_addr_list[] = { | static const unsigned short max1668_addr_list[] = { | ||||||
|  | @ -30,14 +30,10 @@ static const unsigned short max1668_addr_list[] = { | ||||||
| 
 | 
 | ||||||
| /* limits */ | /* limits */ | ||||||
| 
 | 
 | ||||||
| /* write high limits */ | /* high limits */ | ||||||
| #define MAX1668_REG_LIMH_WR(nr)	(0x13 + 2 * (nr)) | #define MAX1668_REG_LIMH(nr)	(0x08 + 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)) |  | ||||||
| /* read low limits */ | /* 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 */ | /* manufacturer and device ID Constants */ | ||||||
| #define MAN_ID_MAXIM		0x4d | #define MAN_ID_MAXIM		0x4d | ||||||
|  | @ -50,309 +46,146 @@ static bool read_only; | ||||||
| module_param(read_only, bool, 0); | module_param(read_only, bool, 0); | ||||||
| MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); | MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); | ||||||
| 
 | 
 | ||||||
| enum chips { max1668, max1805, max1989 }; |  | ||||||
| 
 |  | ||||||
| struct max1668_data { | struct max1668_data { | ||||||
| 	struct i2c_client *client; | 	struct regmap *regmap; | ||||||
| 	const struct attribute_group *groups[3]; | 	int channels; | ||||||
| 	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; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 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 max1668_data *data = dev_get_drvdata(dev); | ||||||
| 	struct i2c_client *client = data->client; | 	struct regmap *regmap = data->regmap; | ||||||
| 	struct max1668_data *ret = data; | 	u32 regs[2] = { MAX1668_REG_STAT1, MAX1668_REG_TEMP(channel) }; | ||||||
| 	s32 val; | 	u8 regvals[2]; | ||||||
| 	int i; | 	u32 regval; | ||||||
| 
 |  | ||||||
| 	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; |  | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	ret = kstrtol(buf, 10, &temp); | 	switch (attr) { | ||||||
| 	if (ret < 0) | 	case hwmon_temp_input: | ||||||
| 		return ret; | 		ret = regmap_read(regmap, MAX1668_REG_TEMP(channel), ®val); | ||||||
| 
 | 		if (ret) | ||||||
| 	mutex_lock(&data->update_lock); | 			return ret; | ||||||
| 	data->temp_max[index] = clamp_val(temp/1000, -128, 127); | 		*val = sign_extend32(regval, 7) * 1000; | ||||||
| 	ret = i2c_smbus_write_byte_data(client, | 		break; | ||||||
| 					MAX1668_REG_LIMH_WR(index), | 	case hwmon_temp_min: | ||||||
| 					data->temp_max[index]); | 		ret = regmap_read(regmap, MAX1668_REG_LIML(channel), ®val); | ||||||
| 	if (ret < 0) | 		if (ret) | ||||||
| 		count = ret; | 			return ret; | ||||||
| 	mutex_unlock(&data->update_lock); | 		*val = sign_extend32(regval, 7) * 1000; | ||||||
| 
 | 		break; | ||||||
| 	return count; | 	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, | static int max1668_write(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 			    struct device_attribute *devattr, | 			 u32 attr, int channel, long val) | ||||||
| 			    const char *buf, size_t count) |  | ||||||
| { | { | ||||||
| 	int index = to_sensor_dev_attr(devattr)->index; |  | ||||||
| 	struct max1668_data *data = dev_get_drvdata(dev); | 	struct max1668_data *data = dev_get_drvdata(dev); | ||||||
| 	struct i2c_client *client = data->client; | 	struct regmap *regmap = data->regmap; | ||||||
| 	long temp; |  | ||||||
| 	int ret; |  | ||||||
| 
 | 
 | ||||||
| 	ret = kstrtol(buf, 10, &temp); | 	val = clamp_val(val / 1000, -128, 127); | ||||||
| 	if (ret < 0) |  | ||||||
| 		return ret; |  | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->update_lock); | 	switch (attr) { | ||||||
| 	data->temp_min[index] = clamp_val(temp/1000, -128, 127); | 	case hwmon_temp_min: | ||||||
| 	ret = i2c_smbus_write_byte_data(client, | 		return regmap_write(regmap, MAX1668_REG_LIML(channel), val); | ||||||
| 					MAX1668_REG_LIML_WR(index), | 	case hwmon_temp_max: | ||||||
| 					data->temp_min[index]); | 		return regmap_write(regmap, MAX1668_REG_LIMH(channel), val); | ||||||
| 	if (ret < 0) | 	default: | ||||||
| 		count = ret; | 		return -EOPNOTSUPP; | ||||||
| 	mutex_unlock(&data->update_lock); | 	} | ||||||
| 
 |  | ||||||
| 	return count; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); | static umode_t max1668_is_visible(const void *_data, enum hwmon_sensor_types type, | ||||||
| static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, | 				  u32 attr, int channel) | ||||||
| 				set_temp_max, 0); | { | ||||||
| static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, | 	const struct max1668_data *data = _data; | ||||||
| 				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 SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); | 	if (channel >= data->channels) | ||||||
| static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); | 		return 0; | ||||||
| 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); |  | ||||||
| 
 | 
 | ||||||
| static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); | 	switch (attr) { | ||||||
| static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); | 	case hwmon_temp_min: | ||||||
| static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); | 	case hwmon_temp_max: | ||||||
| static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); | 		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 const struct hwmon_channel_info * const max1668_info[] = { | ||||||
| static struct attribute *max1668_attribute_common[] = { | 	HWMON_CHANNEL_INFO(temp, | ||||||
| 	&sensor_dev_attr_temp1_max.dev_attr.attr, | 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||||
| 	&sensor_dev_attr_temp1_min.dev_attr.attr, | 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, | ||||||
| 	&sensor_dev_attr_temp1_input.dev_attr.attr, | 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||||
| 	&sensor_dev_attr_temp2_max.dev_attr.attr, | 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||||
| 	&sensor_dev_attr_temp2_min.dev_attr.attr, | 			   HWMON_T_FAULT, | ||||||
| 	&sensor_dev_attr_temp2_input.dev_attr.attr, | 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||||
| 	&sensor_dev_attr_temp3_max.dev_attr.attr, | 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||||
| 	&sensor_dev_attr_temp3_min.dev_attr.attr, | 			   HWMON_T_FAULT, | ||||||
| 	&sensor_dev_attr_temp3_input.dev_attr.attr, | 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||||
| 
 | 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||||
| 	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, | 			   HWMON_T_FAULT, | ||||||
| 	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr, | 			   HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | | ||||||
| 	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, | 			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | ||||||
| 	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, | 			   HWMON_T_FAULT), | ||||||
| 	&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, |  | ||||||
| 	NULL | 	NULL | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Attributes not present on MAX1805 */ | static const struct hwmon_ops max1668_hwmon_ops = { | ||||||
| static struct attribute *max1668_attribute_unique[] = { | 	.is_visible = max1668_is_visible, | ||||||
| 	&sensor_dev_attr_temp4_max.dev_attr.attr, | 	.read = max1668_read, | ||||||
| 	&sensor_dev_attr_temp4_min.dev_attr.attr, | 	.write = max1668_write, | ||||||
| 	&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 umode_t max1668_attribute_mode(struct kobject *kobj, | static const struct hwmon_chip_info max1668_chip_info = { | ||||||
| 				     struct attribute *attr, int index) | 	.ops = &max1668_hwmon_ops, | ||||||
| { | 	.info = max1668_info, | ||||||
| 	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 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Return 0 if detection is successful, -ENODEV otherwise */ | /* Return 0 if detection is successful, -ENODEV otherwise */ | ||||||
|  | @ -391,6 +224,48 @@ static int max1668_detect(struct i2c_client *client, | ||||||
| 	return 0; | 	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) | static int max1668_probe(struct i2c_client *client) | ||||||
| { | { | ||||||
| 	struct i2c_adapter *adapter = client->adapter; | 	struct i2c_adapter *adapter = client->adapter; | ||||||
|  | @ -405,24 +280,22 @@ static int max1668_probe(struct i2c_client *client) | ||||||
| 	if (!data) | 	if (!data) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	data->client = client; | 	data->regmap = devm_regmap_init(dev, &max1668_regmap_bus, client, | ||||||
| 	data->type = (uintptr_t)i2c_get_match_data(client); | 					&max1668_regmap_config); | ||||||
| 	mutex_init(&data->update_lock); | 	if (IS_ERR(data->regmap)) | ||||||
|  | 		return PTR_ERR(data->regmap); | ||||||
| 
 | 
 | ||||||
| 	/* sysfs hooks */ | 	data->channels = (uintptr_t)i2c_get_match_data(client); | ||||||
| 	data->groups[0] = &max1668_group_common; |  | ||||||
| 	if (data->type == max1668 || data->type == max1989) |  | ||||||
| 		data->groups[1] = &max1668_group_unique; |  | ||||||
| 
 | 
 | ||||||
| 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, | 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, | ||||||
| 							   data, data->groups); | 							 &max1668_chip_info, NULL); | ||||||
| 	return PTR_ERR_OR_ZERO(hwmon_dev); | 	return PTR_ERR_OR_ZERO(hwmon_dev); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct i2c_device_id max1668_id[] = { | static const struct i2c_device_id max1668_id[] = { | ||||||
| 	{ "max1668", max1668 }, | 	{ "max1668", 5 }, | ||||||
| 	{ "max1805", max1805 }, | 	{ "max1805", 3 }, | ||||||
| 	{ "max1989", max1989 }, | 	{ "max1989", 5 }, | ||||||
| 	{ } | 	{ } | ||||||
| }; | }; | ||||||
| MODULE_DEVICE_TABLE(i2c, max1668_id); | 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) | 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); | 	struct max6639_data *data = dev_get_drvdata(dev); | ||||||
| 	unsigned int val; | 	u8 regvals[2]; | ||||||
| 	int res; | 	int res; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	res = regmap_multi_reg_read(data->regmap, regs, regvals, 2); | ||||||
| 	 * 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); |  | ||||||
| 	if (res < 0) | 	if (res < 0) | ||||||
| 		return res; | 		return res; | ||||||
| 
 | 
 | ||||||
| 	*temp = val >> 5; | 	*temp = ((regvals[0] >> 5) | (regvals[1] << 3)) * 125; | ||||||
| 	res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val); |  | ||||||
| 	if (res < 0) |  | ||||||
| 		return res; |  | ||||||
| 
 |  | ||||||
| 	*temp |= val << 3; |  | ||||||
| 	*temp *= 125; |  | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	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, | static int max6639_read_pwm(struct device *dev, u32 attr, int channel, | ||||||
| 			    long *pwm_val) | 			    long *pwm_val) | ||||||
| { | { | ||||||
|  | 	u32 regs[2] = { MAX6639_REG_FAN_CONFIG3(channel), MAX6639_REG_GCONFIG }; | ||||||
| 	struct max6639_data *data = dev_get_drvdata(dev); | 	struct max6639_data *data = dev_get_drvdata(dev); | ||||||
| 	unsigned int val; | 	unsigned int val; | ||||||
|  | 	u8 regvals[2]; | ||||||
| 	int res; | 	int res; | ||||||
| 	u8 i; | 	u8 i; | ||||||
| 
 | 
 | ||||||
|  | @ -303,26 +296,13 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel, | ||||||
| 		*pwm_val = val * 255 / 120; | 		*pwm_val = val * 255 / 120; | ||||||
| 		return 0; | 		return 0; | ||||||
| 	case hwmon_pwm_freq: | 	case hwmon_pwm_freq: | ||||||
| 		mutex_lock(&data->update_lock); | 		res = regmap_multi_reg_read(data->regmap, regs, regvals, 2); | ||||||
| 		res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val); | 		if (res < 0) | ||||||
| 		if (res < 0) { |  | ||||||
| 			mutex_unlock(&data->update_lock); |  | ||||||
| 			return res; | 			return res; | ||||||
| 		} | 		i = regvals[0] & MAX6639_FAN_CONFIG3_FREQ_MASK; | ||||||
| 		i = val & MAX6639_FAN_CONFIG3_FREQ_MASK; | 		if (regvals[1] & MAX6639_GCONFIG_PWM_FREQ_HI) | ||||||
| 
 |  | ||||||
| 		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 |= 0x4; | 			i |= 0x4; | ||||||
| 		i &= 0x7; |  | ||||||
| 		*pwm_val = freq_table[i]; | 		*pwm_val = freq_table[i]; | ||||||
| 
 |  | ||||||
| 		mutex_unlock(&data->update_lock); |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 	default: | 	default: | ||||||
| 		return -EOPNOTSUPP; | 		return -EOPNOTSUPP; | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1269,6 +1269,7 @@ static const char * const asus_msi_boards[] = { | ||||||
| 	"EX-B760M-V5 D4", | 	"EX-B760M-V5 D4", | ||||||
| 	"EX-H510M-V3", | 	"EX-H510M-V3", | ||||||
| 	"EX-H610M-V3 D4", | 	"EX-H610M-V3 D4", | ||||||
|  | 	"G15CF", | ||||||
| 	"PRIME A620M-A", | 	"PRIME A620M-A", | ||||||
| 	"PRIME B560-PLUS", | 	"PRIME B560-PLUS", | ||||||
| 	"PRIME B560-PLUS AC-HES", | 	"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) | 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; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->access_lock); | 	ret = regmap_multi_reg_read(data->regmap, regs, f, 2); | ||||||
| 	ret = regmap_read(data->regmap, reg_fan, &f1); | 	if (ret) | ||||||
| 	if (ret < 0) | 		return ret; | ||||||
| 		goto abort; | 	ret = (f[0] << 5) | (f[1] >> 3); | ||||||
| 	ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2); |  | ||||||
| 	if (ret < 0) |  | ||||||
| 		goto abort; |  | ||||||
| 	ret = (f1 << 5) | (f2 >> 3); |  | ||||||
| 	/* convert fan count to rpm */ | 	/* convert fan count to rpm */ | ||||||
| 	if (ret == 0x1fff)	/* maximum value, assume fan is stopped */ | 	if (ret == 0x1fff)	/* maximum value, assume fan is stopped */ | ||||||
| 		ret = 0; | 		ret = 0; | ||||||
| 	else if (ret) | 	else if (ret) | ||||||
| 		ret = DIV_ROUND_CLOSEST(1350000U, ret); | 		ret = DIV_ROUND_CLOSEST(1350000U, ret); | ||||||
| abort: |  | ||||||
| 	mutex_unlock(&data->access_lock); |  | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, | static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, | ||||||
| 				u8 reg_fan_high) | 				u8 reg_fan_high) | ||||||
| { | { | ||||||
| 	unsigned int f1, f2; | 	unsigned int regs[2] = {reg_fan_low, reg_fan_high}; | ||||||
|  | 	u8 f[2]; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->access_lock); | 	ret = regmap_multi_reg_read(data->regmap, regs, f, 2); | ||||||
| 	ret = regmap_read(data->regmap, reg_fan_low, &f1); |  | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		goto abort; | 		return ret; | ||||||
| 	ret = regmap_read(data->regmap, reg_fan_high, &f2); | 
 | ||||||
| 	if (ret < 0) | 	ret = f[0] | ((f[1] & 0xf8) << 5); | ||||||
| 		goto abort; |  | ||||||
| 	ret = f1 | ((f2 & 0xf8) << 5); |  | ||||||
| 	/* convert fan count to rpm */ | 	/* convert fan count to rpm */ | ||||||
| 	if (ret == 0x1fff)	/* maximum value, assume no limit */ | 	if (ret == 0x1fff)	/* maximum value, assume no limit */ | ||||||
| 		ret = 0; | 		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); | 		ret = DIV_ROUND_CLOSEST(1350000U, ret); | ||||||
| 	else | 	else | ||||||
| 		ret = 1350000U; | 		ret = 1350000U; | ||||||
| abort: |  | ||||||
| 	mutex_unlock(&data->access_lock); |  | ||||||
| 	return ret; | 	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) | static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) | ||||||
| { | { | ||||||
| 	unsigned int v1, v2; | 	u8 v[2]; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->access_lock); |  | ||||||
| 	if (index == 0) {	/* voltage */ | 	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) | 		if (ret < 0) | ||||||
| 			goto abort; | 			return ret; | ||||||
| 		ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); | 		ret = ((v[0] << 2) | (v[1] >> 6)) * nct7802_vmul[nr]; | ||||||
| 		if (ret < 0) |  | ||||||
| 			goto abort; |  | ||||||
| 		ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr]; |  | ||||||
| 	}  else {	/* limit */ | 	}  else {	/* limit */ | ||||||
| 		int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; | 		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, | 		ret = regmap_multi_reg_read(data->regmap, regs, v, 2); | ||||||
| 				  REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1); |  | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			goto abort; | 			return ret; | ||||||
| 		ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], | 		ret = (v[0] | ((v[1] << shift) & 0x300)) * nct7802_vmul[nr]; | ||||||
| 				  &v2); |  | ||||||
| 		if (ret < 0) |  | ||||||
| 			goto abort; |  | ||||||
| 		ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr]; |  | ||||||
| 	} | 	} | ||||||
| abort: |  | ||||||
| 	mutex_unlock(&data->access_lock); |  | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1145,17 +1129,14 @@ static int nct7802_configure_channels(struct device *dev, | ||||||
| { | { | ||||||
| 	/* Enable local temperature sensor by default */ | 	/* Enable local temperature sensor by default */ | ||||||
| 	u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN; | 	u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN; | ||||||
| 	struct device_node *node; |  | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	if (dev->of_node) { | 	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, | 			err = nct7802_get_channel_config(dev, node, &mode_mask, | ||||||
| 							 &mode_val); | 							 &mode_val); | ||||||
| 			if (err) { | 			if (err) | ||||||
| 				of_node_put(node); |  | ||||||
| 				return err; | 				return err; | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -927,7 +927,7 @@ static int npcm7xx_en_pwm_fan(struct device *dev, | ||||||
| static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) | static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct device *dev = &pdev->dev; | 	struct device *dev = &pdev->dev; | ||||||
| 	struct device_node *np, *child; | 	struct device_node *np; | ||||||
| 	struct npcm7xx_pwm_fan_data *data; | 	struct npcm7xx_pwm_fan_data *data; | ||||||
| 	struct resource *res; | 	struct resource *res; | ||||||
| 	struct device *hwmon; | 	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); | 		ret = npcm7xx_en_pwm_fan(dev, child, data); | ||||||
| 		if (ret) { | 		if (ret) { | ||||||
| 			dev_err(dev, "enable pwm and fan failed\n"); | 			dev_err(dev, "enable pwm and fan failed\n"); | ||||||
| 			of_node_put(child); |  | ||||||
| 			return ret; | 			return ret; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = { | ||||||
| 	[NTC_SSG1404001221]   = { "ssg1404_001221",  TYPE_NCPXXWB473 }, | 	[NTC_SSG1404001221]   = { "ssg1404_001221",  TYPE_NCPXXWB473 }, | ||||||
| 	[NTC_LAST]            = { }, | 	[NTC_LAST]            = { }, | ||||||
| }; | }; | ||||||
|  | MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * A compensation table should be sorted by the values of .ohm |  * A compensation table should be sorted by the values of .ohm | ||||||
|  |  | ||||||
|  | @ -1,18 +1,21 @@ | ||||||
| // SPDX-License-Identifier: GPL-2.0+
 | // SPDX-License-Identifier: GPL-2.0+
 | ||||||
| /*
 | /*
 | ||||||
|  * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose |  * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds | ||||||
|  * fan reading and control via hwmon sysfs. |  * that expose fan reading and control via hwmon sysfs. | ||||||
|  * |  * | ||||||
|  * Old OXP boards have the same DMI strings and they are told apart by |  * 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 |  * the boot cpu vendor (Intel/AMD). Of these older models only AMD is | ||||||
|  * supported but the code is made to be simple to add other handheld |  * supported. | ||||||
|  * boards in the future. |  * | ||||||
|  * Fan control is provided via pwm interface in the range [0-255]. |  * 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 |  * 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 |  * 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) 2022 Joaquín I. Aramendía <samsagax@gmail.com> | ||||||
|  |  * Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com> | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/acpi.h> | #include <linux/acpi.h> | ||||||
|  | @ -43,32 +46,48 @@ enum oxp_board { | ||||||
| 	aok_zoe_a1 = 1, | 	aok_zoe_a1 = 1, | ||||||
| 	aya_neo_2, | 	aya_neo_2, | ||||||
| 	aya_neo_air, | 	aya_neo_air, | ||||||
|  | 	aya_neo_air_1s, | ||||||
| 	aya_neo_air_plus_mendo, | 	aya_neo_air_plus_mendo, | ||||||
| 	aya_neo_air_pro, | 	aya_neo_air_pro, | ||||||
|  | 	aya_neo_flip, | ||||||
| 	aya_neo_geek, | 	aya_neo_geek, | ||||||
|  | 	aya_neo_kun, | ||||||
|  | 	orange_pi_neo, | ||||||
|  | 	oxp_2, | ||||||
|  | 	oxp_fly, | ||||||
| 	oxp_mini_amd, | 	oxp_mini_amd, | ||||||
| 	oxp_mini_amd_a07, | 	oxp_mini_amd_a07, | ||||||
| 	oxp_mini_amd_pro, | 	oxp_mini_amd_pro, | ||||||
|  | 	oxp_x1, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static enum oxp_board board; | static enum oxp_board board; | ||||||
| 
 | 
 | ||||||
| /* Fan reading and PWM */ | /* Fan reading and PWM */ | ||||||
| #define OXP_SENSOR_FAN_REG		0x76 /* Fan reading is 2 registers long */ | #define OXP_SENSOR_FAN_REG             0x76 /* Fan reading is 2 registers long */ | ||||||
| #define OXP_SENSOR_PWM_ENABLE_REG	0x4A /* PWM enable is 1 register long */ | #define OXP_2_SENSOR_FAN_REG           0x58 /* Fan reading is 2 registers long */ | ||||||
| #define OXP_SENSOR_PWM_REG		0x4B /* PWM reading is 1 register 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
 | /* Turbo button takeover function
 | ||||||
|  * Older boards have different values and EC registers |  * Different boards have different values and EC registers | ||||||
|  * for the same function |  * for the same function | ||||||
|  */ |  */ | ||||||
| #define OXP_OLD_TURBO_SWITCH_REG	0x1E | #define OXP_TURBO_SWITCH_REG           0xF1 /* Mini Pro, OneXFly, AOKZOE */ | ||||||
| #define OXP_OLD_TURBO_TAKE_VAL		0x01 | #define OXP_2_TURBO_SWITCH_REG         0xEB /* OXP2 and X1 */ | ||||||
| #define OXP_OLD_TURBO_RETURN_VAL	0x00 | #define OXP_MINI_TURBO_SWITCH_REG      0x1E /* Mini AO7 */ | ||||||
| 
 | 
 | ||||||
| #define OXP_TURBO_SWITCH_REG		0xF1 | #define OXP_MINI_TURBO_TAKE_VAL        0x01 /* Mini AO7 */ | ||||||
| #define OXP_TURBO_TAKE_VAL		0x40 | #define OXP_TURBO_TAKE_VAL             0x40 /* All other models */ | ||||||
| #define OXP_TURBO_RETURN_VAL		0x00 | 
 | ||||||
|  | #define OXP_TURBO_RETURN_VAL           0x00 /* Common return val */ | ||||||
| 
 | 
 | ||||||
| static const struct dmi_system_id dmi_table[] = { | static const struct dmi_system_id dmi_table[] = { | ||||||
| 	{ | 	{ | ||||||
|  | @ -88,7 +107,7 @@ static const struct dmi_system_id dmi_table[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.matches = { | 		.matches = { | ||||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | 			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, | 		.driver_data = (void *)aya_neo_2, | ||||||
| 	}, | 	}, | ||||||
|  | @ -99,6 +118,13 @@ static const struct dmi_system_id dmi_table[] = { | ||||||
| 		}, | 		}, | ||||||
| 		.driver_data = (void *)aya_neo_air, | 		.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 = { | 		.matches = { | ||||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | ||||||
|  | @ -116,10 +142,31 @@ static const struct dmi_system_id dmi_table[] = { | ||||||
| 	{ | 	{ | ||||||
| 		.matches = { | 		.matches = { | ||||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), | 			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, | 		.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 = { | 		.matches = { | ||||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | 			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, | 		.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 = { | 		.matches = { | ||||||
| 			DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), | 			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, | 		.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) { | 	switch (board) { | ||||||
| 	case oxp_mini_amd_a07: | 	case oxp_mini_amd_a07: | ||||||
| 		reg = OXP_OLD_TURBO_SWITCH_REG; | 		reg = OXP_MINI_TURBO_SWITCH_REG; | ||||||
| 		val = OXP_OLD_TURBO_TAKE_VAL; | 		val = OXP_MINI_TURBO_TAKE_VAL; | ||||||
| 		break; | 		break; | ||||||
| 	case oxp_mini_amd_pro: |  | ||||||
| 	case aok_zoe_a1: | 	case aok_zoe_a1: | ||||||
|  | 	case oxp_fly: | ||||||
|  | 	case oxp_mini_amd_pro: | ||||||
| 		reg = OXP_TURBO_SWITCH_REG; | 		reg = OXP_TURBO_SWITCH_REG; | ||||||
| 		val = OXP_TURBO_TAKE_VAL; | 		val = OXP_TURBO_TAKE_VAL; | ||||||
| 		break; | 		break; | ||||||
|  | 	case oxp_2: | ||||||
|  | 	case oxp_x1: | ||||||
|  | 		reg = OXP_2_TURBO_SWITCH_REG; | ||||||
|  | 		val = OXP_TURBO_TAKE_VAL; | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  | @ -213,14 +287,20 @@ static int tt_toggle_disable(void) | ||||||
| 
 | 
 | ||||||
| 	switch (board) { | 	switch (board) { | ||||||
| 	case oxp_mini_amd_a07: | 	case oxp_mini_amd_a07: | ||||||
| 		reg = OXP_OLD_TURBO_SWITCH_REG; | 		reg = OXP_MINI_TURBO_SWITCH_REG; | ||||||
| 		val = OXP_OLD_TURBO_RETURN_VAL; | 		val = OXP_TURBO_RETURN_VAL; | ||||||
| 		break; | 		break; | ||||||
| 	case oxp_mini_amd_pro: |  | ||||||
| 	case aok_zoe_a1: | 	case aok_zoe_a1: | ||||||
|  | 	case oxp_fly: | ||||||
|  | 	case oxp_mini_amd_pro: | ||||||
| 		reg = OXP_TURBO_SWITCH_REG; | 		reg = OXP_TURBO_SWITCH_REG; | ||||||
| 		val = OXP_TURBO_RETURN_VAL; | 		val = OXP_TURBO_RETURN_VAL; | ||||||
| 		break; | 		break; | ||||||
|  | 	case oxp_2: | ||||||
|  | 	case oxp_x1: | ||||||
|  | 		reg = OXP_2_TURBO_SWITCH_REG; | ||||||
|  | 		val = OXP_TURBO_RETURN_VAL; | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  | @ -233,8 +313,11 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj, | ||||||
| { | { | ||||||
| 	switch (board) { | 	switch (board) { | ||||||
| 	case aok_zoe_a1: | 	case aok_zoe_a1: | ||||||
|  | 	case oxp_2: | ||||||
|  | 	case oxp_fly: | ||||||
| 	case oxp_mini_amd_a07: | 	case oxp_mini_amd_a07: | ||||||
| 	case oxp_mini_amd_pro: | 	case oxp_mini_amd_pro: | ||||||
|  | 	case oxp_x1: | ||||||
| 		return attr->mode; | 		return attr->mode; | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
|  | @ -273,12 +356,17 @@ static ssize_t tt_toggle_show(struct device *dev, | ||||||
| 
 | 
 | ||||||
| 	switch (board) { | 	switch (board) { | ||||||
| 	case oxp_mini_amd_a07: | 	case oxp_mini_amd_a07: | ||||||
| 		reg = OXP_OLD_TURBO_SWITCH_REG; | 		reg = OXP_MINI_TURBO_SWITCH_REG; | ||||||
| 		break; | 		break; | ||||||
| 	case oxp_mini_amd_pro: |  | ||||||
| 	case aok_zoe_a1: | 	case aok_zoe_a1: | ||||||
|  | 	case oxp_fly: | ||||||
|  | 	case oxp_mini_amd_pro: | ||||||
| 		reg = OXP_TURBO_SWITCH_REG; | 		reg = OXP_TURBO_SWITCH_REG; | ||||||
| 		break; | 		break; | ||||||
|  | 	case oxp_2: | ||||||
|  | 	case oxp_x1: | ||||||
|  | 		reg = OXP_2_TURBO_SWITCH_REG; | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  | @ -295,12 +383,53 @@ static DEVICE_ATTR_RW(tt_toggle); | ||||||
| /* PWM enable/disable functions */ | /* PWM enable/disable functions */ | ||||||
| static int oxp_pwm_enable(void) | 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) | 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 */ | /* Callbacks for hwmon interface */ | ||||||
|  | @ -326,7 +455,30 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 	case hwmon_fan: | 	case hwmon_fan: | ||||||
| 		switch (attr) { | 		switch (attr) { | ||||||
| 		case hwmon_fan_input: | 		case hwmon_fan_input: | ||||||
| 			return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); | 			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: | 		default: | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  | @ -334,27 +486,72 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 	case hwmon_pwm: | 	case hwmon_pwm: | ||||||
| 		switch (attr) { | 		switch (attr) { | ||||||
| 		case hwmon_pwm_input: | 		case hwmon_pwm_input: | ||||||
| 			ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); |  | ||||||
| 			if (ret) |  | ||||||
| 				return ret; |  | ||||||
| 			switch (board) { | 			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; | ||||||
|  | 				/* scale from range [0-184] */ | ||||||
|  | 				*val = (*val * 255) / 184; | ||||||
|  | 				break; | ||||||
| 			case aya_neo_2: | 			case aya_neo_2: | ||||||
| 			case aya_neo_air: | 			case aya_neo_air: | ||||||
|  | 			case aya_neo_air_1s: | ||||||
| 			case aya_neo_air_plus_mendo: | 			case aya_neo_air_plus_mendo: | ||||||
| 			case aya_neo_air_pro: | 			case aya_neo_air_pro: | ||||||
|  | 			case aya_neo_flip: | ||||||
| 			case aya_neo_geek: | 			case aya_neo_geek: | ||||||
|  | 			case aya_neo_kun: | ||||||
| 			case oxp_mini_amd: | 			case oxp_mini_amd: | ||||||
| 			case oxp_mini_amd_a07: | 			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; | 				*val = (*val * 255) / 100; | ||||||
| 				break; | 				break; | ||||||
| 			case oxp_mini_amd_pro: |  | ||||||
| 			case aok_zoe_a1: | 			case aok_zoe_a1: | ||||||
|  | 			case oxp_fly: | ||||||
|  | 			case oxp_mini_amd_pro: | ||||||
| 			default: | 			default: | ||||||
|  | 				ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); | ||||||
|  | 				if (ret) | ||||||
|  | 					return ret; | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 			return 0; | 			return 0; | ||||||
| 		case hwmon_pwm_enable: | 		case hwmon_pwm_enable: | ||||||
| 			return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); | 			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; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  | @ -381,21 +578,36 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 			if (val < 0 || val > 255) | 			if (val < 0 || val > 255) | ||||||
| 				return -EINVAL; | 				return -EINVAL; | ||||||
| 			switch (board) { | 			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_2: | ||||||
| 			case aya_neo_air: | 			case aya_neo_air: | ||||||
|  | 			case aya_neo_air_1s: | ||||||
| 			case aya_neo_air_plus_mendo: | 			case aya_neo_air_plus_mendo: | ||||||
| 			case aya_neo_air_pro: | 			case aya_neo_air_pro: | ||||||
|  | 			case aya_neo_flip: | ||||||
| 			case aya_neo_geek: | 			case aya_neo_geek: | ||||||
|  | 			case aya_neo_kun: | ||||||
| 			case oxp_mini_amd: | 			case oxp_mini_amd: | ||||||
| 			case oxp_mini_amd_a07: | 			case oxp_mini_amd_a07: | ||||||
|  | 				/* scale to range [0-100] */ | ||||||
| 				val = (val * 100) / 255; | 				val = (val * 100) / 255; | ||||||
| 				break; | 				return write_to_ec(OXP_SENSOR_PWM_REG, val); | ||||||
| 			case aok_zoe_a1: | 			case aok_zoe_a1: | ||||||
|  | 			case oxp_fly: | ||||||
| 			case oxp_mini_amd_pro: | 			case oxp_mini_amd_pro: | ||||||
|  | 				return write_to_ec(OXP_SENSOR_PWM_REG, val); | ||||||
| 			default: | 			default: | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 			return write_to_ec(OXP_SENSOR_PWM_REG, val); | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  | @ -467,19 +679,20 @@ static int __init oxp_platform_init(void) | ||||||
| { | { | ||||||
| 	const struct dmi_system_id *dmi_entry; | 	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); | 	dmi_entry = dmi_first_match(dmi_table); | ||||||
| 	if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | 	if (!dmi_entry) | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 
 | 
 | ||||||
| 	board = (enum oxp_board)(unsigned long)dmi_entry->driver_data; | 	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 = | 	oxp_platform_device = | ||||||
| 		platform_create_bundle(&oxp_platform_driver, | 		platform_create_bundle(&oxp_platform_driver, | ||||||
| 				       oxp_platform_probe, NULL, 0, NULL, 0); | 				       oxp_platform_probe, NULL, 0, NULL, 0); | ||||||
|  |  | ||||||
|  | @ -1315,7 +1315,7 @@ static void pc87360_init_device(struct platform_device *pdev, | ||||||
| 				    (reg & 0xC0) | 0x11); | 				    (reg & 0xC0) | 0x11); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nr = data->innr < 11 ? data->innr : 11; | 	nr = min(data->innr, 11); | ||||||
| 	for (i = 0; i < nr; i++) { | 	for (i = 0; i < nr; i++) { | ||||||
| 		reg = pc87360_read_value(data, LD_IN, i, | 		reg = pc87360_read_value(data, LD_IN, i, | ||||||
| 					 PC87365_REG_IN_STATUS); | 					 PC87365_REG_IN_STATUS); | ||||||
|  |  | ||||||
|  | @ -31,8 +31,6 @@ MODULE_DEVICE_TABLE(i2c, max15301_id); | ||||||
| 
 | 
 | ||||||
| struct max15301_data { | struct max15301_data { | ||||||
| 	int id; | 	int id; | ||||||
| 	ktime_t access;		/* Chip access time */ |  | ||||||
| 	int delay;		/* Delay between chip accesses in us */ |  | ||||||
| 	struct pmbus_driver_info info; | 	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) | static int max15301_probe(struct i2c_client *client) | ||||||
| { | { | ||||||
| 	int status; | 	int status; | ||||||
|  | @ -164,12 +79,7 @@ static int max15301_probe(struct i2c_client *client) | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	max15301_data.delay = delay; | 	info->access_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; |  | ||||||
| 
 | 
 | ||||||
| 	return pmbus_do_probe(client, info); | 	return pmbus_do_probe(client, info); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ struct mpq7932_data { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) | #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, | 	PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES, | ||||||
| 			     MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), | 			     MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), | ||||||
| 	PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES, | 	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) | #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) | ||||||
| static const struct regulator_desc pli1209bc_reg_desc = { | static const struct regulator_desc pli1209bc_reg_desc = { | ||||||
| 	.name = "vout2", | 	.name = "vout2", | ||||||
|  | @ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = { | ||||||
| 	    | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 	    | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | ||||||
| 	    | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, | 	    | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, | ||||||
| 	.read_word_data = pli1209bc_read_word_data, | 	.read_word_data = pli1209bc_read_word_data, | ||||||
| 	.write_byte = pli1209bc_write_byte, | 	.write_delay = 250, | ||||||
| #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) | #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) | ||||||
| 	.num_regulators = 1, | 	.num_regulators = 1, | ||||||
| 	.reg_desc = &pli1209bc_reg_desc, | 	.reg_desc = &pli1209bc_reg_desc, | ||||||
|  |  | ||||||
|  | @ -472,6 +472,16 @@ struct pmbus_driver_info { | ||||||
| 
 | 
 | ||||||
| 	/* custom attributes */ | 	/* custom attributes */ | ||||||
| 	const struct attribute_group **groups; | 	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 */ | /* Regulator ops */ | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/debugfs.h> | #include <linux/debugfs.h> | ||||||
|  | #include <linux/delay.h> | ||||||
| #include <linux/kernel.h> | #include <linux/kernel.h> | ||||||
| #include <linux/math64.h> | #include <linux/math64.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
|  | @ -110,6 +111,8 @@ struct pmbus_data { | ||||||
| 
 | 
 | ||||||
| 	int vout_low[PMBUS_PAGES];	/* voltage low margin */ | 	int vout_low[PMBUS_PAGES];	/* voltage low margin */ | ||||||
| 	int vout_high[PMBUS_PAGES];	/* voltage high 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 { | 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); | 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) | int pmbus_set_page(struct i2c_client *client, int page, int phase) | ||||||
| { | { | ||||||
| 	struct pmbus_data *data = i2c_get_clientdata(client); | 	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) && | 	if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && | ||||||
| 	    data->info->pages > 1 && page != data->currpage) { | 	    data->info->pages > 1 && page != data->currpage) { | ||||||
|  | 		pmbus_wait(client); | ||||||
| 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); | 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); | ||||||
|  | 		pmbus_update_ts(client, true); | ||||||
| 		if (rv < 0) | 		if (rv < 0) | ||||||
| 			return rv; | 			return rv; | ||||||
| 
 | 
 | ||||||
|  | 		pmbus_wait(client); | ||||||
| 		rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); | 		rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); | ||||||
|  | 		pmbus_update_ts(client, false); | ||||||
| 		if (rv < 0) | 		if (rv < 0) | ||||||
| 			return rv; | 			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 && | 	if (data->info->phases[page] && data->currphase != phase && | ||||||
| 	    !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { | 	    !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { | ||||||
|  | 		pmbus_wait(client); | ||||||
| 		rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, | 		rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, | ||||||
| 					       phase); | 					       phase); | ||||||
|  | 		pmbus_update_ts(client, true); | ||||||
| 		if (rv) | 		if (rv) | ||||||
| 			return rv; | 			return rv; | ||||||
| 	} | 	} | ||||||
|  | @ -204,7 +246,11 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) | ||||||
| 	if (rv < 0) | 	if (rv < 0) | ||||||
| 		return rv; | 		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); | 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) | 	if (rv < 0) | ||||||
| 		return rv; | 		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); | 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) | 	if (rv < 0) | ||||||
| 		return rv; | 		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); | 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) | 	if (rv < 0) | ||||||
| 		return rv; | 		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); | 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) | 	if (rv < 0) | ||||||
| 		return rv; | 		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); | 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) | 	if (rv < 0) | ||||||
| 		return rv; | 		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, | 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[1] = attr->reg; | ||||||
| 	data.block[2] = 0x01; | 	data.block[2] = 0x01; | ||||||
| 
 | 
 | ||||||
|  | 	pmbus_wait(client); | ||||||
| 	rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, | 	rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, | ||||||
| 			    I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, | 			    I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, | ||||||
| 			    I2C_SMBUS_BLOCK_PROC_CALL, &data); | 			    I2C_SMBUS_BLOCK_PROC_CALL, &data); | ||||||
|  | 	pmbus_update_ts(client, true); | ||||||
| 
 | 
 | ||||||
| 	if (rv < 0) | 	if (rv < 0) | ||||||
| 		return rv; | 		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 */ | 	/* Enable PEC if the controller and bus supports it */ | ||||||
| 	if (!(data->flags & PMBUS_NO_CAPABILITY)) { | 	if (!(data->flags & PMBUS_NO_CAPABILITY)) { | ||||||
|  | 		pmbus_wait(client); | ||||||
| 		ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); | 		ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); | ||||||
|  | 		pmbus_update_ts(client, false); | ||||||
|  | 
 | ||||||
| 		if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { | 		if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { | ||||||
| 			if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) | 			if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) | ||||||
| 				client->flags |= I2C_CLIENT_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. | 	 * Bail out if both registers are not supported. | ||||||
| 	 */ | 	 */ | ||||||
| 	data->read_status = pmbus_read_status_word; | 	data->read_status = pmbus_read_status_word; | ||||||
|  | 	pmbus_wait(client); | ||||||
| 	ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); | 	ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); | ||||||
|  | 	pmbus_update_ts(client, false); | ||||||
|  | 
 | ||||||
| 	if (ret < 0 || ret == 0xffff) { | 	if (ret < 0 || ret == 0xffff) { | ||||||
| 		data->read_status = pmbus_read_status_byte; | 		data->read_status = pmbus_read_status_byte; | ||||||
|  | 		pmbus_wait(client); | ||||||
| 		ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); | 		ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); | ||||||
|  | 		pmbus_update_ts(client, false); | ||||||
|  | 
 | ||||||
| 		if (ret < 0 || ret == 0xff) { | 		if (ret < 0 || ret == 0xff) { | ||||||
| 			dev_err(dev, "PMBus status register not found\n"); | 			dev_err(dev, "PMBus status register not found\n"); | ||||||
| 			return -ENODEV; | 			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. | 	 * limit registers need to be disabled. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { | 	if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { | ||||||
|  | 		pmbus_wait(client); | ||||||
| 		ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); | 		ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); | ||||||
|  | 		pmbus_update_ts(client, false); | ||||||
|  | 
 | ||||||
| 		if (ret > 0 && (ret & PB_WP_ANY)) | 		if (ret > 0 && (ret & PB_WP_ANY)) | ||||||
| 			data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; | 			data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -67,7 +67,6 @@ struct ucd9000_data { | ||||||
| 	struct gpio_chip gpio; | 	struct gpio_chip gpio; | ||||||
| #endif | #endif | ||||||
| 	struct dentry *debugfs; | 	struct dentry *debugfs; | ||||||
| 	ktime_t write_time; |  | ||||||
| }; | }; | ||||||
| #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) | #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 | #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) | static int ucd9000_get_fan_config(struct i2c_client *client, int fan) | ||||||
| { | { | ||||||
| 	int fan_config = 0; | 	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 | 		info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | ||||||
| 		  | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; | 		  | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; | ||||||
| 	} else if (mid->driver_data == ucd90320) { | 	} else if (mid->driver_data == ucd90320) { | ||||||
| 		info->read_byte_data = ucd90320_read_byte_data; | 		/* Delay SMBus operations after a write */ | ||||||
| 		info->read_word_data = ucd90320_read_word_data; | 		info->write_delay = UCD90320_WAIT_DELAY_US; | ||||||
| 		info->write_byte = ucd90320_write_byte; |  | ||||||
| 		info->write_word_data = ucd90320_write_word_data; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ucd9000_probe_gpio(client, mid, data); | 	ucd9000_probe_gpio(client, mid, data); | ||||||
|  |  | ||||||
|  | @ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, | ||||||
| 
 | 
 | ||||||
| struct zl6100_data { | struct zl6100_data { | ||||||
| 	int id; | 	int id; | ||||||
| 	ktime_t access;		/* chip access time */ |  | ||||||
| 	int delay;		/* Delay between chip accesses in uS */ |  | ||||||
| 	struct pmbus_driver_info info; | 	struct pmbus_driver_info info; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -122,16 +120,6 @@ static u16 zl6100_d2l(long val) | ||||||
| 	return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); | 	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, | static int zl6100_read_word_data(struct i2c_client *client, int page, | ||||||
| 				 int phase, int reg) | 				 int phase, int reg) | ||||||
| { | { | ||||||
|  | @ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	zl6100_wait(data); |  | ||||||
| 	ret = pmbus_read_word_data(client, page, phase, vreg); | 	ret = pmbus_read_word_data(client, page, phase, vreg); | ||||||
| 	data->access = ktime_get(); |  | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		return ret; | 		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) | 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); | 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||||
| 	struct zl6100_data *data = to_zl6100_data(info); |  | ||||||
| 	int ret, status; | 	int ret, status; | ||||||
| 
 | 
 | ||||||
| 	if (page >= info->pages) | 	if (page >= info->pages) | ||||||
| 		return -ENXIO; | 		return -ENXIO; | ||||||
| 
 | 
 | ||||||
| 	zl6100_wait(data); |  | ||||||
| 
 |  | ||||||
| 	switch (reg) { | 	switch (reg) { | ||||||
| 	case PMBUS_VIRT_STATUS_VMON: | 	case PMBUS_VIRT_STATUS_VMON: | ||||||
| 		ret = pmbus_read_byte_data(client, 0, | 		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); | 		ret = pmbus_read_byte_data(client, page, reg); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	data->access = ktime_get(); |  | ||||||
| 
 | 
 | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -234,8 +216,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, | ||||||
| 				  u16 word) | 				  u16 word) | ||||||
| { | { | ||||||
| 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||||||
| 	struct zl6100_data *data = to_zl6100_data(info); | 	int vreg; | ||||||
| 	int ret, vreg; |  | ||||||
| 
 | 
 | ||||||
| 	if (page >= info->pages) | 	if (page >= info->pages) | ||||||
| 		return -ENXIO; | 		return -ENXIO; | ||||||
|  | @ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, | ||||||
| 		vreg = reg; | 		vreg = reg; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	zl6100_wait(data); | 	return pmbus_write_word_data(client, page, vreg, word); | ||||||
| 	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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct i2c_device_id zl6100_id[] = { | 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 | 	 * supported chips are known to require a wait time between I2C | ||||||
| 	 * accesses. | 	 * accesses. | ||||||
| 	 */ | 	 */ | ||||||
| 	data->delay = delay; | 	udelay(delay); | ||||||
| 
 |  | ||||||
| 	/*
 |  | ||||||
| 	 * Since there was a direct I2C device access above, wait before |  | ||||||
| 	 * accessing the chip again. |  | ||||||
| 	 */ |  | ||||||
| 	data->access = ktime_get(); |  | ||||||
| 	zl6100_wait(data); |  | ||||||
| 
 | 
 | ||||||
| 	info = &data->info; | 	info = &data->info; | ||||||
| 
 | 
 | ||||||
|  | @ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client) | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
| 
 | 
 | ||||||
| 		data->access = ktime_get(); | 		udelay(delay); | ||||||
| 		zl6100_wait(data); |  | ||||||
| 
 | 
 | ||||||
| 		if (ret & ZL8802_MFR_PHASES_MASK) | 		if (ret & ZL8802_MFR_PHASES_MASK) | ||||||
| 			info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; | 			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) | 			if (ret < 0) | ||||||
| 				return ret; | 				return ret; | ||||||
| 
 | 
 | ||||||
| 			data->access = ktime_get(); | 			udelay(delay); | ||||||
| 			zl6100_wait(data); |  | ||||||
| 
 | 
 | ||||||
| 			ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); | 			ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); | ||||||
| 			if (ret < 0) | 			if (ret < 0) | ||||||
|  | @ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client) | ||||||
| 			if (ret & ZL8802_MFR_XTEMP_ENABLE_2) | 			if (ret & ZL8802_MFR_XTEMP_ENABLE_2) | ||||||
| 				info->func[i] |= PMBUS_HAVE_TEMP2; | 				info->func[i] |= PMBUS_HAVE_TEMP2; | ||||||
| 
 | 
 | ||||||
| 			data->access = ktime_get(); | 			udelay(delay); | ||||||
| 			zl6100_wait(data); |  | ||||||
| 		} | 		} | ||||||
| 		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); | 		ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
|  | @ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client) | ||||||
| 			info->func[0] |= PMBUS_HAVE_TEMP2; | 			info->func[0] |= PMBUS_HAVE_TEMP2; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	data->access = ktime_get(); | 	udelay(delay); | ||||||
| 	zl6100_wait(data); |  | ||||||
| 
 | 
 | ||||||
|  | 	info->access_delay = delay; | ||||||
| 	info->read_word_data = zl6100_read_word_data; | 	info->read_word_data = zl6100_read_word_data; | ||||||
| 	info->read_byte_data = zl6100_read_byte_data; | 	info->read_byte_data = zl6100_read_byte_data; | ||||||
| 	info->write_word_data = zl6100_write_word_data; | 	info->write_word_data = zl6100_write_word_data; | ||||||
| 	info->write_byte = zl6100_write_byte; |  | ||||||
| 
 | 
 | ||||||
| 	return pmbus_do_probe(client, info); | 	return pmbus_do_probe(client, info); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -167,7 +167,7 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) | ||||||
| 	return ret; | 	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; | 	struct pwm_state *state = &ctx->pwm_state; | ||||||
| 	bool enable_regulator = false; | 	bool enable_regulator = false; | ||||||
|  | @ -180,7 +180,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) | ||||||
| 				    state, | 				    state, | ||||||
| 				    &enable_regulator); | 				    &enable_regulator); | ||||||
| 
 | 
 | ||||||
| 	state->enabled = false; | 	if (force_disable) | ||||||
|  | 		state->enabled = false; | ||||||
| 	state->duty_cycle = 0; | 	state->duty_cycle = 0; | ||||||
| 	ret = pwm_apply_might_sleep(ctx->pwm, state); | 	ret = pwm_apply_might_sleep(ctx->pwm, state); | ||||||
| 	if (ret) { | 	if (ret) { | ||||||
|  | @ -213,7 +214,7 @@ static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) | ||||||
| 			return ret; | 			return ret; | ||||||
| 		ret = pwm_fan_power_on(ctx); | 		ret = pwm_fan_power_on(ctx); | ||||||
| 	} else { | 	} else { | ||||||
| 		ret = pwm_fan_power_off(ctx); | 		ret = pwm_fan_power_off(ctx, false); | ||||||
| 	} | 	} | ||||||
| 	if (!ret) | 	if (!ret) | ||||||
| 		ctx->pwm_value = pwm; | 		ctx->pwm_value = pwm; | ||||||
|  | @ -468,7 +469,7 @@ static void pwm_fan_cleanup(void *__ctx) | ||||||
| 	del_timer_sync(&ctx->rpm_timer); | 	del_timer_sync(&ctx->rpm_timer); | ||||||
| 	/* Switch off everything */ | 	/* Switch off everything */ | ||||||
| 	ctx->enable_mode = pwm_disable_reg_disable; | 	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) | 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); | 	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) | static int pwm_fan_resume(struct device *dev) | ||||||
|  |  | ||||||
|  | @ -416,8 +416,7 @@ static int sch5636_probe(struct platform_device *pdev) | ||||||
| 	id[i] = '\0'; | 	id[i] = '\0'; | ||||||
| 
 | 
 | ||||||
| 	if (strcmp(id, "THS")) { | 	if (strcmp(id, "THS")) { | ||||||
| 		pr_err("Unknown Fujitsu id: %02x%02x%02x\n", | 		pr_err("Unknown Fujitsu id: %3pE (%3ph)\n", id, id); | ||||||
| 		       id[0], id[1], id[2]); |  | ||||||
| 		err = -ENODEV; | 		err = -ENODEV; | ||||||
| 		goto error; | 		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, | void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, | ||||||
| 			       struct mutex *io_lock, int check_enabled); | 			       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[6] = rx[0]; | ||||||
| 	eic[7] = rx[1]; | 	eic[7] = rx[1]; | ||||||
| 
 | 
 | ||||||
| 	ret = snprintf(sht21->eic, sizeof(sht21->eic), | 	ret = snprintf(sht21->eic, sizeof(sht21->eic), "%8phN\n", 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]); |  | ||||||
| out: | out: | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		sht21->eic[0] = 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[] = { | static const struct of_device_id __maybe_unused stts751_of_match[] = { | ||||||
| 	{ .compatible = "stts751" }, | 	{ .compatible = "st,stts751" }, | ||||||
| 	{ }, | 	{ }, | ||||||
| }; | }; | ||||||
| MODULE_DEVICE_TABLE(of, stts751_of_match); | 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 tmp401_data *data = dev_get_drvdata(dev); | ||||||
| 	struct regmap *regmap = data->regmap; | 	struct regmap *regmap = data->regmap; | ||||||
|  | 	unsigned int regs[2] = { TMP401_TEMP_MSB[3][channel], TMP401_TEMP_CRIT_HYST }; | ||||||
| 	unsigned int regval; | 	unsigned int regval; | ||||||
|  | 	u16 regvals[2]; | ||||||
| 	int reg, ret; | 	int reg, ret; | ||||||
| 
 | 
 | ||||||
| 	switch (attr) { | 	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); | 		*val = tmp401_register_to_temp(regval, data->extended_range); | ||||||
| 		break; | 		break; | ||||||
| 	case hwmon_temp_crit_hyst: | 	case hwmon_temp_crit_hyst: | ||||||
| 		mutex_lock(&data->update_lock); | 		ret = regmap_multi_reg_read(regmap, regs, regvals, 2); | ||||||
| 		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); |  | ||||||
| 		if (ret < 0) | 		if (ret < 0) | ||||||
| 			return ret; | 			return ret; | ||||||
|  | 		*val = tmp401_register_to_temp(regvals[0], data->extended_range) - | ||||||
|  | 							(regvals[1] * 1000); | ||||||
| 		break; | 		break; | ||||||
| 	case hwmon_temp_fault: | 	case hwmon_temp_fault: | ||||||
| 	case hwmon_temp_min_alarm: | 	case hwmon_temp_min_alarm: | ||||||
|  |  | ||||||
|  | @ -410,18 +410,15 @@ static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *d | ||||||
| { | { | ||||||
| 	struct device *dev = &client->dev; | 	struct device *dev = &client->dev; | ||||||
| 	const struct device_node *np = dev->of_node; | 	const struct device_node *np = dev->of_node; | ||||||
| 	struct device_node *child; |  | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	for_each_child_of_node(np, child) { | 	for_each_child_of_node_scoped(np, child) { | ||||||
| 		if (strcmp(child->name, "channel")) | 		if (strcmp(child->name, "channel")) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		err = tmp421_probe_child_from_dt(client, child, data); | 		err = tmp421_probe_child_from_dt(client, child, data); | ||||||
| 		if (err) { | 		if (err) | ||||||
| 			of_node_put(child); |  | ||||||
| 			return err; | 			return err; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	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 tmp464_data *data = dev_get_drvdata(dev); | ||||||
| 	struct regmap *regmap = data->regmap; | 	struct regmap *regmap = data->regmap; | ||||||
| 	unsigned int regval, regval2; | 	unsigned int regs[2]; | ||||||
|  | 	unsigned int regval; | ||||||
|  | 	u16 regvals[2]; | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&data->update_lock); |  | ||||||
| 
 |  | ||||||
| 	switch (attr) { | 	switch (attr) { | ||||||
| 	case hwmon_temp_max_alarm: | 	case hwmon_temp_max_alarm: | ||||||
| 		err = regmap_read(regmap, TMP464_THERM_STATUS_REG, ®val); | 		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 | 		 * complete. That means we have to cache the value internally | ||||||
| 		 * for one measurement cycle and report the cached value. | 		 * for one measurement cycle and report the cached value. | ||||||
| 		 */ | 		 */ | ||||||
|  | 		mutex_lock(&data->update_lock); | ||||||
| 		if (!data->valid || time_after(jiffies, data->last_updated + | 		if (!data->valid || time_after(jiffies, data->last_updated + | ||||||
| 					       msecs_to_jiffies(data->update_interval))) { | 					       msecs_to_jiffies(data->update_interval))) { | ||||||
| 			err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val); | 			err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val); | ||||||
| 			if (err < 0) | 			if (err < 0) | ||||||
| 				break; | 				goto unlock; | ||||||
| 			data->open_reg = regval; | 			data->open_reg = regval; | ||||||
| 			data->last_updated = jiffies; | 			data->last_updated = jiffies; | ||||||
| 			data->valid = true; | 			data->valid = true; | ||||||
| 		} | 		} | ||||||
| 		*val = !!(data->open_reg & BIT(channel + 7)); | 		*val = !!(data->open_reg & BIT(channel + 7)); | ||||||
|  | unlock: | ||||||
|  | 		mutex_unlock(&data->update_lock); | ||||||
| 		break; | 		break; | ||||||
| 	case hwmon_temp_max_hyst: | 	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) | 		if (err < 0) | ||||||
| 			break; | 			break; | ||||||
| 		err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); | 		*val = temp_from_reg(regvals[0] - regvals[1]); | ||||||
| 		if (err < 0) |  | ||||||
| 			break; |  | ||||||
| 		regval -= regval2; |  | ||||||
| 		*val = temp_from_reg(regval); |  | ||||||
| 		break; | 		break; | ||||||
| 	case hwmon_temp_max: | 	case hwmon_temp_max: | ||||||
| 		err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); | 		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); | 		*val = temp_from_reg(regval); | ||||||
| 		break; | 		break; | ||||||
| 	case hwmon_temp_crit_hyst: | 	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) | 		if (err < 0) | ||||||
| 			break; | 			break; | ||||||
| 		err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); | 		*val = temp_from_reg(regvals[0] - regvals[1]); | ||||||
| 		if (err < 0) |  | ||||||
| 			break; |  | ||||||
| 		regval -= regval2; |  | ||||||
| 		*val = temp_from_reg(regval); |  | ||||||
| 		break; | 		break; | ||||||
| 	case hwmon_temp_crit: | 	case hwmon_temp_crit: | ||||||
| 		err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); | 		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; | 		break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mutex_unlock(&data->update_lock); |  | ||||||
| 
 |  | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -565,18 +562,15 @@ static int tmp464_probe_child_from_dt(struct device *dev, | ||||||
| static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data) | static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data) | ||||||
| { | { | ||||||
| 	const struct device_node *np = dev->of_node; | 	const struct device_node *np = dev->of_node; | ||||||
| 	struct device_node *child; |  | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	for_each_child_of_node(np, child) { | 	for_each_child_of_node_scoped(np, child) { | ||||||
| 		if (strcmp(child->name, "channel")) | 		if (strcmp(child->name, "channel")) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		err = tmp464_probe_child_from_dt(dev, child, data); | 		err = tmp464_probe_child_from_dt(dev, child, data); | ||||||
| 		if (err) { | 		if (err) | ||||||
| 			of_node_put(child); |  | ||||||
| 			return err; | 			return err; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, | ||||||
| 				struct device_attribute, attr); | 				struct device_attribute, attr); | ||||||
| 
 | 
 | ||||||
| 	if (dev_attr->show == vexpress_hwmon_label_show && | 	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 0; | ||||||
| 
 | 
 | ||||||
| 	return attr->mode; | 	return attr->mode; | ||||||
|  |  | ||||||
|  | @ -481,7 +481,6 @@ devm_hwmon_device_register_with_info(struct device *dev, | ||||||
| 				const struct attribute_group **extra_groups); | 				const struct attribute_group **extra_groups); | ||||||
| 
 | 
 | ||||||
| void hwmon_device_unregister(struct device *dev); | 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, | int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, | ||||||
| 		       u32 attr, int channel); | 		       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