mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	The OCC driver limits the rate of sending poll commands to the OCC. If a user reads a hwmon entry after a poll response resulted in an error and is rate-limited, the error is invisible to the user. Fix this by storing the last error and returning that in the rate-limited case. Signed-off-by: Eddie James <eajames@linux.ibm.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
		
			
				
	
	
		
			130 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0+ */
 | 
						|
/* Copyright IBM Corp 2019 */
 | 
						|
 | 
						|
#ifndef OCC_COMMON_H
 | 
						|
#define OCC_COMMON_H
 | 
						|
 | 
						|
#include <linux/hwmon-sysfs.h>
 | 
						|
#include <linux/mutex.h>
 | 
						|
#include <linux/sysfs.h>
 | 
						|
 | 
						|
struct device;
 | 
						|
 | 
						|
#define OCC_RESP_DATA_BYTES		4089
 | 
						|
 | 
						|
/*
 | 
						|
 * Same response format for all OCC versions.
 | 
						|
 * Allocate the largest possible response.
 | 
						|
 */
 | 
						|
struct occ_response {
 | 
						|
	u8 seq_no;
 | 
						|
	u8 cmd_type;
 | 
						|
	u8 return_status;
 | 
						|
	__be16 data_length;
 | 
						|
	u8 data[OCC_RESP_DATA_BYTES];
 | 
						|
	__be16 checksum;
 | 
						|
} __packed;
 | 
						|
 | 
						|
struct occ_sensor_data_block_header {
 | 
						|
	u8 eye_catcher[4];
 | 
						|
	u8 reserved;
 | 
						|
	u8 sensor_format;
 | 
						|
	u8 sensor_length;
 | 
						|
	u8 num_sensors;
 | 
						|
} __packed;
 | 
						|
 | 
						|
struct occ_sensor_data_block {
 | 
						|
	struct occ_sensor_data_block_header header;
 | 
						|
	u32 data;
 | 
						|
} __packed;
 | 
						|
 | 
						|
struct occ_poll_response_header {
 | 
						|
	u8 status;
 | 
						|
	u8 ext_status;
 | 
						|
	u8 occs_present;
 | 
						|
	u8 config_data;
 | 
						|
	u8 occ_state;
 | 
						|
	u8 mode;
 | 
						|
	u8 ips_status;
 | 
						|
	u8 error_log_id;
 | 
						|
	__be32 error_log_start_address;
 | 
						|
	__be16 error_log_length;
 | 
						|
	u16 reserved;
 | 
						|
	u8 occ_code_level[16];
 | 
						|
	u8 eye_catcher[6];
 | 
						|
	u8 num_sensor_data_blocks;
 | 
						|
	u8 sensor_data_block_header_version;
 | 
						|
} __packed;
 | 
						|
 | 
						|
struct occ_poll_response {
 | 
						|
	struct occ_poll_response_header header;
 | 
						|
	struct occ_sensor_data_block block;
 | 
						|
} __packed;
 | 
						|
 | 
						|
struct occ_sensor {
 | 
						|
	u8 num_sensors;
 | 
						|
	u8 version;
 | 
						|
	void *data;	/* pointer to sensor data start within response */
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * OCC only provides one sensor data block of each type, but any number of
 | 
						|
 * sensors within that block.
 | 
						|
 */
 | 
						|
struct occ_sensors {
 | 
						|
	struct occ_sensor temp;
 | 
						|
	struct occ_sensor freq;
 | 
						|
	struct occ_sensor power;
 | 
						|
	struct occ_sensor caps;
 | 
						|
	struct occ_sensor extended;
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Use our own attribute struct so we can dynamically allocate space for the
 | 
						|
 * name.
 | 
						|
 */
 | 
						|
struct occ_attribute {
 | 
						|
	char name[32];
 | 
						|
	struct sensor_device_attribute_2 sensor;
 | 
						|
};
 | 
						|
 | 
						|
struct occ {
 | 
						|
	struct device *bus_dev;
 | 
						|
 | 
						|
	struct occ_response resp;
 | 
						|
	struct occ_sensors sensors;
 | 
						|
 | 
						|
	int powr_sample_time_us;	/* average power sample time */
 | 
						|
	u8 poll_cmd_data;		/* to perform OCC poll command */
 | 
						|
	int (*send_cmd)(struct occ *occ, u8 *cmd);
 | 
						|
 | 
						|
	unsigned long last_update;
 | 
						|
	struct mutex lock;		/* lock OCC access */
 | 
						|
 | 
						|
	struct device *hwmon;
 | 
						|
	struct occ_attribute *attrs;
 | 
						|
	struct attribute_group group;
 | 
						|
	const struct attribute_group *groups[2];
 | 
						|
 | 
						|
	int error;                      /* final transfer error after retry */
 | 
						|
	int last_error;			/* latest transfer error */
 | 
						|
	unsigned int error_count;       /* number of xfr errors observed */
 | 
						|
	unsigned long last_safe;        /* time OCC entered "safe" state */
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Store the previous state data for comparison in order to notify
 | 
						|
	 * sysfs readers of state changes.
 | 
						|
	 */
 | 
						|
	int prev_error;
 | 
						|
	u8 prev_stat;
 | 
						|
	u8 prev_ext_stat;
 | 
						|
	u8 prev_occs_present;
 | 
						|
};
 | 
						|
 | 
						|
int occ_setup(struct occ *occ, const char *name);
 | 
						|
int occ_setup_sysfs(struct occ *occ);
 | 
						|
void occ_shutdown(struct occ *occ);
 | 
						|
void occ_sysfs_poll_done(struct occ *occ);
 | 
						|
int occ_update_response(struct occ *occ);
 | 
						|
 | 
						|
#endif /* OCC_COMMON_H */
 |