summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/occ/common.h
blob: e6df719770e81298d026df5050b962f8054e7871 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* 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 seq_no;
	u8 poll_cmd_data;		/* to perform OCC poll command */
	int (*send_cmd)(struct occ *occ, u8 *cmd);

	unsigned long next_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 */