xref: /openbmc/linux/drivers/hwmon/xgene-hwmon.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ed42cfa8Shotran /*
3ed42cfa8Shotran  * APM X-Gene SoC Hardware Monitoring Driver
4ed42cfa8Shotran  *
5ed42cfa8Shotran  * Copyright (c) 2016, Applied Micro Circuits Corporation
6ed42cfa8Shotran  * Author: Loc Ho <lho@apm.com>
7ed42cfa8Shotran  *         Hoan Tran <hotran@apm.com>
8ed42cfa8Shotran  *
9ed42cfa8Shotran  * This driver provides the following features:
10ed42cfa8Shotran  *  - Retrieve CPU total power (uW)
11ed42cfa8Shotran  *  - Retrieve IO total power (uW)
12ed42cfa8Shotran  *  - Retrieve SoC temperature (milli-degree C) and alarm
13ed42cfa8Shotran  */
14ed42cfa8Shotran #include <linux/acpi.h>
15ed42cfa8Shotran #include <linux/dma-mapping.h>
16ed42cfa8Shotran #include <linux/hwmon.h>
17ed42cfa8Shotran #include <linux/hwmon-sysfs.h>
18c7cefce0SArnd Bergmann #include <linux/io.h>
19ed42cfa8Shotran #include <linux/interrupt.h>
20ed42cfa8Shotran #include <linux/kfifo.h>
21ed42cfa8Shotran #include <linux/mailbox_controller.h>
22ed42cfa8Shotran #include <linux/mailbox_client.h>
23ed42cfa8Shotran #include <linux/module.h>
24ed42cfa8Shotran #include <linux/of.h>
25ed42cfa8Shotran #include <linux/platform_device.h>
26c7cefce0SArnd Bergmann 
27ed42cfa8Shotran #include <acpi/pcc.h>
28ed42cfa8Shotran 
29ed42cfa8Shotran /* SLIMpro message defines */
30ed42cfa8Shotran #define MSG_TYPE_DBG			0
31ed42cfa8Shotran #define MSG_TYPE_ERR			7
32ed42cfa8Shotran #define MSG_TYPE_PWRMGMT		9
33ed42cfa8Shotran 
34ed42cfa8Shotran #define MSG_TYPE(v)			(((v) & 0xF0000000) >> 28)
35ed42cfa8Shotran #define MSG_TYPE_SET(v)			(((v) << 28) & 0xF0000000)
36ed42cfa8Shotran #define MSG_SUBTYPE(v)			(((v) & 0x0F000000) >> 24)
37ed42cfa8Shotran #define MSG_SUBTYPE_SET(v)		(((v) << 24) & 0x0F000000)
38ed42cfa8Shotran 
39ed42cfa8Shotran #define DBG_SUBTYPE_SENSOR_READ		4
40ed42cfa8Shotran #define SENSOR_RD_MSG			0x04FFE902
41ed42cfa8Shotran #define SENSOR_RD_EN_ADDR(a)		((a) & 0x000FFFFF)
42ed42cfa8Shotran #define PMD_PWR_REG			0x20
43ed42cfa8Shotran #define PMD_PWR_MW_REG			0x26
44ed42cfa8Shotran #define SOC_PWR_REG			0x21
45ed42cfa8Shotran #define SOC_PWR_MW_REG			0x27
46ed42cfa8Shotran #define SOC_TEMP_REG			0x10
47ed42cfa8Shotran 
48ed42cfa8Shotran #define TEMP_NEGATIVE_BIT		8
49ed42cfa8Shotran #define SENSOR_INVALID_DATA		BIT(15)
50ed42cfa8Shotran 
51ed42cfa8Shotran #define PWRMGMT_SUBTYPE_TPC		1
52ed42cfa8Shotran #define TPC_ALARM			2
53ed42cfa8Shotran #define TPC_GET_ALARM			3
54ed42cfa8Shotran #define TPC_CMD(v)			(((v) & 0x00FF0000) >> 16)
55ed42cfa8Shotran #define TPC_CMD_SET(v)			(((v) << 16) & 0x00FF0000)
56ed42cfa8Shotran #define TPC_EN_MSG(hndl, cmd, type) \
57ed42cfa8Shotran 	(MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \
58ed42cfa8Shotran 	MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type)
59ed42cfa8Shotran 
60ed42cfa8Shotran /* PCC defines */
61ed42cfa8Shotran #define PCC_SIGNATURE_MASK		0x50424300
62ed42cfa8Shotran #define PCCC_GENERATE_DB_INT		BIT(15)
63ed42cfa8Shotran #define PCCS_CMD_COMPLETE		BIT(0)
64ed42cfa8Shotran #define PCCS_SCI_DOORBEL		BIT(1)
65ed42cfa8Shotran #define PCCS_PLATFORM_NOTIFICATION	BIT(3)
66ed42cfa8Shotran /*
67ed42cfa8Shotran  * Arbitrary retries in case the remote processor is slow to respond
68ed42cfa8Shotran  * to PCC commands
69ed42cfa8Shotran  */
70ed42cfa8Shotran #define PCC_NUM_RETRIES			500
71ed42cfa8Shotran 
72ed42cfa8Shotran #define ASYNC_MSG_FIFO_SIZE		16
73ed42cfa8Shotran #define MBOX_OP_TIMEOUTMS		1000
74ed42cfa8Shotran 
75ed42cfa8Shotran #define WATT_TO_mWATT(x)		((x) * 1000)
76ed42cfa8Shotran #define mWATT_TO_uWATT(x)		((x) * 1000)
77ed42cfa8Shotran #define CELSIUS_TO_mCELSIUS(x)		((x) * 1000)
78ed42cfa8Shotran 
79ed42cfa8Shotran #define to_xgene_hwmon_dev(cl)		\
80ed42cfa8Shotran 	container_of(cl, struct xgene_hwmon_dev, mbox_client)
81ed42cfa8Shotran 
82749d782dShotran enum xgene_hwmon_version {
83749d782dShotran 	XGENE_HWMON_V1 = 0,
84749d782dShotran 	XGENE_HWMON_V2 = 1,
85749d782dShotran };
86749d782dShotran 
87ed42cfa8Shotran struct slimpro_resp_msg {
88ed42cfa8Shotran 	u32 msg;
89ed42cfa8Shotran 	u32 param1;
90ed42cfa8Shotran 	u32 param2;
91ed42cfa8Shotran } __packed;
92ed42cfa8Shotran 
93ed42cfa8Shotran struct xgene_hwmon_dev {
94ed42cfa8Shotran 	struct device		*dev;
95ed42cfa8Shotran 	struct mbox_chan	*mbox_chan;
967b6da7feSSudeep Holla 	struct pcc_mbox_chan	*pcc_chan;
97ed42cfa8Shotran 	struct mbox_client	mbox_client;
98ed42cfa8Shotran 	int			mbox_idx;
99ed42cfa8Shotran 
100ed42cfa8Shotran 	spinlock_t		kfifo_lock;
101ed42cfa8Shotran 	struct mutex		rd_mutex;
102ed42cfa8Shotran 	struct completion	rd_complete;
103ed42cfa8Shotran 	int			resp_pending;
104ed42cfa8Shotran 	struct slimpro_resp_msg sync_msg;
105ed42cfa8Shotran 
106ed42cfa8Shotran 	struct work_struct	workq;
107ed42cfa8Shotran 	struct kfifo_rec_ptr_1	async_msg_fifo;
108ed42cfa8Shotran 
109ed42cfa8Shotran 	struct device		*hwmon_dev;
110ed42cfa8Shotran 	bool			temp_critical_alarm;
111ed42cfa8Shotran 
112ed42cfa8Shotran 	phys_addr_t		comm_base_addr;
113ed42cfa8Shotran 	void			*pcc_comm_addr;
114ed42cfa8Shotran 	u64			usecs_lat;
115ed42cfa8Shotran };
116ed42cfa8Shotran 
117ed42cfa8Shotran /*
118ed42cfa8Shotran  * This function tests and clears a bitmask then returns its old value
119ed42cfa8Shotran  */
xgene_word_tst_and_clr(u16 * addr,u16 mask)120ed42cfa8Shotran static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
121ed42cfa8Shotran {
122ed42cfa8Shotran 	u16 ret, val;
123ed42cfa8Shotran 
124c7cefce0SArnd Bergmann 	val = le16_to_cpu(READ_ONCE(*addr));
125ed42cfa8Shotran 	ret = val & mask;
126ed42cfa8Shotran 	val &= ~mask;
127c7cefce0SArnd Bergmann 	WRITE_ONCE(*addr, cpu_to_le16(val));
128ed42cfa8Shotran 
129ed42cfa8Shotran 	return ret;
130ed42cfa8Shotran }
131ed42cfa8Shotran 
xgene_hwmon_pcc_rd(struct xgene_hwmon_dev * ctx,u32 * msg)132ed42cfa8Shotran static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
133ed42cfa8Shotran {
134ed42cfa8Shotran 	struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
135c7cefce0SArnd Bergmann 	u32 *ptr = (void *)(generic_comm_base + 1);
136ed42cfa8Shotran 	int rc, i;
137ed42cfa8Shotran 	u16 val;
138ed42cfa8Shotran 
139ed42cfa8Shotran 	mutex_lock(&ctx->rd_mutex);
140ed42cfa8Shotran 	init_completion(&ctx->rd_complete);
141ed42cfa8Shotran 	ctx->resp_pending = true;
142ed42cfa8Shotran 
143ed42cfa8Shotran 	/* Write signature for subspace */
144c7cefce0SArnd Bergmann 	WRITE_ONCE(generic_comm_base->signature,
145c7cefce0SArnd Bergmann 		   cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));
146ed42cfa8Shotran 
147ed42cfa8Shotran 	/* Write to the shared command region */
148c7cefce0SArnd Bergmann 	WRITE_ONCE(generic_comm_base->command,
149c7cefce0SArnd Bergmann 		   cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));
150ed42cfa8Shotran 
151ed42cfa8Shotran 	/* Flip CMD COMPLETE bit */
152c7cefce0SArnd Bergmann 	val = le16_to_cpu(READ_ONCE(generic_comm_base->status));
153ed42cfa8Shotran 	val &= ~PCCS_CMD_COMPLETE;
154c7cefce0SArnd Bergmann 	WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));
155ed42cfa8Shotran 
156ed42cfa8Shotran 	/* Copy the message to the PCC comm space */
157ed42cfa8Shotran 	for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)
158c7cefce0SArnd Bergmann 		WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
159ed42cfa8Shotran 
160ed42cfa8Shotran 	/* Ring the doorbell */
161ed42cfa8Shotran 	rc = mbox_send_message(ctx->mbox_chan, msg);
162ed42cfa8Shotran 	if (rc < 0) {
163ed42cfa8Shotran 		dev_err(ctx->dev, "Mailbox send error %d\n", rc);
164ed42cfa8Shotran 		goto err;
165ed42cfa8Shotran 	}
166ed42cfa8Shotran 	if (!wait_for_completion_timeout(&ctx->rd_complete,
167ed42cfa8Shotran 					 usecs_to_jiffies(ctx->usecs_lat))) {
168ed42cfa8Shotran 		dev_err(ctx->dev, "Mailbox operation timed out\n");
169ed42cfa8Shotran 		rc = -ETIMEDOUT;
170ed42cfa8Shotran 		goto err;
171ed42cfa8Shotran 	}
172ed42cfa8Shotran 
173ed42cfa8Shotran 	/* Check for error message */
174ed42cfa8Shotran 	if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
175ed42cfa8Shotran 		rc = -EINVAL;
176ed42cfa8Shotran 		goto err;
177ed42cfa8Shotran 	}
178ed42cfa8Shotran 
179ed42cfa8Shotran 	msg[0] = ctx->sync_msg.msg;
180ed42cfa8Shotran 	msg[1] = ctx->sync_msg.param1;
181ed42cfa8Shotran 	msg[2] = ctx->sync_msg.param2;
182ed42cfa8Shotran 
183ed42cfa8Shotran err:
184ed42cfa8Shotran 	mbox_chan_txdone(ctx->mbox_chan, 0);
185ed42cfa8Shotran 	ctx->resp_pending = false;
186ed42cfa8Shotran 	mutex_unlock(&ctx->rd_mutex);
187ed42cfa8Shotran 	return rc;
188ed42cfa8Shotran }
189ed42cfa8Shotran 
xgene_hwmon_rd(struct xgene_hwmon_dev * ctx,u32 * msg)190ed42cfa8Shotran static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
191ed42cfa8Shotran {
192ed42cfa8Shotran 	int rc;
193ed42cfa8Shotran 
194ed42cfa8Shotran 	mutex_lock(&ctx->rd_mutex);
195ed42cfa8Shotran 	init_completion(&ctx->rd_complete);
196ed42cfa8Shotran 	ctx->resp_pending = true;
197ed42cfa8Shotran 
198ed42cfa8Shotran 	rc = mbox_send_message(ctx->mbox_chan, msg);
199ed42cfa8Shotran 	if (rc < 0) {
200ed42cfa8Shotran 		dev_err(ctx->dev, "Mailbox send error %d\n", rc);
201ed42cfa8Shotran 		goto err;
202ed42cfa8Shotran 	}
203ed42cfa8Shotran 
204ed42cfa8Shotran 	if (!wait_for_completion_timeout(&ctx->rd_complete,
205ed42cfa8Shotran 					 msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) {
206ed42cfa8Shotran 		dev_err(ctx->dev, "Mailbox operation timed out\n");
207ed42cfa8Shotran 		rc = -ETIMEDOUT;
208ed42cfa8Shotran 		goto err;
209ed42cfa8Shotran 	}
210ed42cfa8Shotran 
211ed42cfa8Shotran 	/* Check for error message */
212ed42cfa8Shotran 	if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
213ed42cfa8Shotran 		rc = -EINVAL;
214ed42cfa8Shotran 		goto err;
215ed42cfa8Shotran 	}
216ed42cfa8Shotran 
217ed42cfa8Shotran 	msg[0] = ctx->sync_msg.msg;
218ed42cfa8Shotran 	msg[1] = ctx->sync_msg.param1;
219ed42cfa8Shotran 	msg[2] = ctx->sync_msg.param2;
220ed42cfa8Shotran 
221ed42cfa8Shotran err:
222ed42cfa8Shotran 	ctx->resp_pending = false;
223ed42cfa8Shotran 	mutex_unlock(&ctx->rd_mutex);
224ed42cfa8Shotran 	return rc;
225ed42cfa8Shotran }
226ed42cfa8Shotran 
xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev * ctx,u32 addr,u32 * data)227ed42cfa8Shotran static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
228ed42cfa8Shotran 				  u32 *data)
229ed42cfa8Shotran {
230ed42cfa8Shotran 	u32 msg[3];
231ed42cfa8Shotran 	int rc;
232ed42cfa8Shotran 
233ed42cfa8Shotran 	msg[0] = SENSOR_RD_MSG;
234ed42cfa8Shotran 	msg[1] = SENSOR_RD_EN_ADDR(addr);
235ed42cfa8Shotran 	msg[2] = 0;
236ed42cfa8Shotran 
237ed42cfa8Shotran 	if (acpi_disabled)
238ed42cfa8Shotran 		rc = xgene_hwmon_rd(ctx, msg);
239ed42cfa8Shotran 	else
240ed42cfa8Shotran 		rc = xgene_hwmon_pcc_rd(ctx, msg);
241ed42cfa8Shotran 
242ed42cfa8Shotran 	if (rc < 0)
243ed42cfa8Shotran 		return rc;
244ed42cfa8Shotran 
245ed42cfa8Shotran 	/*
246ed42cfa8Shotran 	 * Check if sensor data is valid.
247ed42cfa8Shotran 	 */
248ed42cfa8Shotran 	if (msg[1] & SENSOR_INVALID_DATA)
249ed42cfa8Shotran 		return -ENODATA;
250ed42cfa8Shotran 
251ed42cfa8Shotran 	*data = msg[1];
252ed42cfa8Shotran 
253ed42cfa8Shotran 	return rc;
254ed42cfa8Shotran }
255ed42cfa8Shotran 
xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev * ctx,u32 * amsg)256ed42cfa8Shotran static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx,
257ed42cfa8Shotran 					    u32 *amsg)
258ed42cfa8Shotran {
259ed42cfa8Shotran 	u32 msg[3];
260ed42cfa8Shotran 	int rc;
261ed42cfa8Shotran 
262ed42cfa8Shotran 	msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0);
263ed42cfa8Shotran 	msg[1] = 0;
264ed42cfa8Shotran 	msg[2] = 0;
265ed42cfa8Shotran 
266ed42cfa8Shotran 	rc = xgene_hwmon_pcc_rd(ctx, msg);
267ed42cfa8Shotran 	if (rc < 0)
268ed42cfa8Shotran 		return rc;
269ed42cfa8Shotran 
270ed42cfa8Shotran 	amsg[0] = msg[0];
271ed42cfa8Shotran 	amsg[1] = msg[1];
272ed42cfa8Shotran 	amsg[2] = msg[2];
273ed42cfa8Shotran 
274ed42cfa8Shotran 	return rc;
275ed42cfa8Shotran }
276ed42cfa8Shotran 
xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev * ctx,u32 * val)277ed42cfa8Shotran static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
278ed42cfa8Shotran {
279ed42cfa8Shotran 	u32 watt, mwatt;
280ed42cfa8Shotran 	int rc;
281ed42cfa8Shotran 
282ed42cfa8Shotran 	rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);
283ed42cfa8Shotran 	if (rc < 0)
284ed42cfa8Shotran 		return rc;
285ed42cfa8Shotran 
286ed42cfa8Shotran 	rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);
287ed42cfa8Shotran 	if (rc < 0)
288ed42cfa8Shotran 		return rc;
289ed42cfa8Shotran 
290ed42cfa8Shotran 	*val = WATT_TO_mWATT(watt) + mwatt;
291ed42cfa8Shotran 	return 0;
292ed42cfa8Shotran }
293ed42cfa8Shotran 
xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev * ctx,u32 * val)294ed42cfa8Shotran static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
295ed42cfa8Shotran {
296ed42cfa8Shotran 	u32 watt, mwatt;
297ed42cfa8Shotran 	int rc;
298ed42cfa8Shotran 
299ed42cfa8Shotran 	rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt);
300ed42cfa8Shotran 	if (rc < 0)
301ed42cfa8Shotran 		return rc;
302ed42cfa8Shotran 
303ed42cfa8Shotran 	rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt);
304ed42cfa8Shotran 	if (rc < 0)
305ed42cfa8Shotran 		return rc;
306ed42cfa8Shotran 
307ed42cfa8Shotran 	*val = WATT_TO_mWATT(watt) + mwatt;
308ed42cfa8Shotran 	return 0;
309ed42cfa8Shotran }
310ed42cfa8Shotran 
xgene_hwmon_get_temp(struct xgene_hwmon_dev * ctx,u32 * val)311ed42cfa8Shotran static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val)
312ed42cfa8Shotran {
313ed42cfa8Shotran 	return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val);
314ed42cfa8Shotran }
315ed42cfa8Shotran 
316ed42cfa8Shotran /*
317ed42cfa8Shotran  * Sensor temperature/power functions
318ed42cfa8Shotran  */
temp1_input_show(struct device * dev,struct device_attribute * attr,char * buf)319ed42cfa8Shotran static ssize_t temp1_input_show(struct device *dev,
320ed42cfa8Shotran 				struct device_attribute *attr,
321ed42cfa8Shotran 				char *buf)
322ed42cfa8Shotran {
323ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
324ed42cfa8Shotran 	int rc, temp;
325ed42cfa8Shotran 	u32 val;
326ed42cfa8Shotran 
327ed42cfa8Shotran 	rc = xgene_hwmon_get_temp(ctx, &val);
328ed42cfa8Shotran 	if (rc < 0)
329ed42cfa8Shotran 		return rc;
330ed42cfa8Shotran 
331ed42cfa8Shotran 	temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
332ed42cfa8Shotran 
3331f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%d\n", CELSIUS_TO_mCELSIUS(temp));
334ed42cfa8Shotran }
335ed42cfa8Shotran 
temp1_label_show(struct device * dev,struct device_attribute * attr,char * buf)336ed42cfa8Shotran static ssize_t temp1_label_show(struct device *dev,
337ed42cfa8Shotran 				struct device_attribute *attr,
338ed42cfa8Shotran 				char *buf)
339ed42cfa8Shotran {
3401f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "SoC Temperature\n");
341ed42cfa8Shotran }
342ed42cfa8Shotran 
temp1_critical_alarm_show(struct device * dev,struct device_attribute * devattr,char * buf)343ed42cfa8Shotran static ssize_t temp1_critical_alarm_show(struct device *dev,
344ed42cfa8Shotran 					 struct device_attribute *devattr,
345ed42cfa8Shotran 					 char *buf)
346ed42cfa8Shotran {
347ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
348ed42cfa8Shotran 
3491f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%d\n", ctx->temp_critical_alarm);
350ed42cfa8Shotran }
351ed42cfa8Shotran 
power1_label_show(struct device * dev,struct device_attribute * attr,char * buf)352ed42cfa8Shotran static ssize_t power1_label_show(struct device *dev,
353ed42cfa8Shotran 				 struct device_attribute *attr,
354ed42cfa8Shotran 				 char *buf)
355ed42cfa8Shotran {
3561f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "CPU power\n");
357ed42cfa8Shotran }
358ed42cfa8Shotran 
power2_label_show(struct device * dev,struct device_attribute * attr,char * buf)359ed42cfa8Shotran static ssize_t power2_label_show(struct device *dev,
360ed42cfa8Shotran 				 struct device_attribute *attr,
361ed42cfa8Shotran 				 char *buf)
362ed42cfa8Shotran {
3631f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "IO power\n");
364ed42cfa8Shotran }
365ed42cfa8Shotran 
power1_input_show(struct device * dev,struct device_attribute * attr,char * buf)366ed42cfa8Shotran static ssize_t power1_input_show(struct device *dev,
367ed42cfa8Shotran 				 struct device_attribute *attr,
368ed42cfa8Shotran 				 char *buf)
369ed42cfa8Shotran {
370ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
371ed42cfa8Shotran 	u32 val;
372ed42cfa8Shotran 	int rc;
373ed42cfa8Shotran 
374ed42cfa8Shotran 	rc = xgene_hwmon_get_cpu_pwr(ctx, &val);
375ed42cfa8Shotran 	if (rc < 0)
376ed42cfa8Shotran 		return rc;
377ed42cfa8Shotran 
3781f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
379ed42cfa8Shotran }
380ed42cfa8Shotran 
power2_input_show(struct device * dev,struct device_attribute * attr,char * buf)381ed42cfa8Shotran static ssize_t power2_input_show(struct device *dev,
382ed42cfa8Shotran 				 struct device_attribute *attr,
383ed42cfa8Shotran 				 char *buf)
384ed42cfa8Shotran {
385ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
386ed42cfa8Shotran 	u32 val;
387ed42cfa8Shotran 	int rc;
388ed42cfa8Shotran 
389ed42cfa8Shotran 	rc = xgene_hwmon_get_io_pwr(ctx, &val);
390ed42cfa8Shotran 	if (rc < 0)
391ed42cfa8Shotran 		return rc;
392ed42cfa8Shotran 
3931f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
394ed42cfa8Shotran }
395ed42cfa8Shotran 
396ed42cfa8Shotran static DEVICE_ATTR_RO(temp1_label);
397ed42cfa8Shotran static DEVICE_ATTR_RO(temp1_input);
398ed42cfa8Shotran static DEVICE_ATTR_RO(temp1_critical_alarm);
399ed42cfa8Shotran static DEVICE_ATTR_RO(power1_label);
400ed42cfa8Shotran static DEVICE_ATTR_RO(power1_input);
401ed42cfa8Shotran static DEVICE_ATTR_RO(power2_label);
402ed42cfa8Shotran static DEVICE_ATTR_RO(power2_input);
403ed42cfa8Shotran 
404ed42cfa8Shotran static struct attribute *xgene_hwmon_attrs[] = {
405ed42cfa8Shotran 	&dev_attr_temp1_label.attr,
406ed42cfa8Shotran 	&dev_attr_temp1_input.attr,
407ed42cfa8Shotran 	&dev_attr_temp1_critical_alarm.attr,
408ed42cfa8Shotran 	&dev_attr_power1_label.attr,
409ed42cfa8Shotran 	&dev_attr_power1_input.attr,
410ed42cfa8Shotran 	&dev_attr_power2_label.attr,
411ed42cfa8Shotran 	&dev_attr_power2_input.attr,
412ed42cfa8Shotran 	NULL,
413ed42cfa8Shotran };
414ed42cfa8Shotran 
415ed42cfa8Shotran ATTRIBUTE_GROUPS(xgene_hwmon);
416ed42cfa8Shotran 
xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev * ctx,struct slimpro_resp_msg * amsg)417ed42cfa8Shotran static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx,
418ed42cfa8Shotran 				 struct slimpro_resp_msg *amsg)
419ed42cfa8Shotran {
420ed42cfa8Shotran 	ctx->temp_critical_alarm = !!amsg->param2;
421ed42cfa8Shotran 	sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm");
422ed42cfa8Shotran 
423ed42cfa8Shotran 	return 0;
424ed42cfa8Shotran }
425ed42cfa8Shotran 
xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev * ctx,struct slimpro_resp_msg * amsg)426ed42cfa8Shotran static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx,
427ed42cfa8Shotran 				       struct slimpro_resp_msg *amsg)
428ed42cfa8Shotran {
429ed42cfa8Shotran 	if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) &&
430ed42cfa8Shotran 	    (TPC_CMD(amsg->msg) == TPC_ALARM))
431ed42cfa8Shotran 		xgene_hwmon_tpc_alarm(ctx, amsg);
432ed42cfa8Shotran }
433ed42cfa8Shotran 
434ed42cfa8Shotran /*
435ed42cfa8Shotran  * This function is called to process async work queue
436ed42cfa8Shotran  */
xgene_hwmon_evt_work(struct work_struct * work)437ed42cfa8Shotran static void xgene_hwmon_evt_work(struct work_struct *work)
438ed42cfa8Shotran {
439ed42cfa8Shotran 	struct slimpro_resp_msg amsg;
440ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx;
441ed42cfa8Shotran 	int ret;
442ed42cfa8Shotran 
443ed42cfa8Shotran 	ctx = container_of(work, struct xgene_hwmon_dev, workq);
444ed42cfa8Shotran 	while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg,
445ed42cfa8Shotran 				    sizeof(struct slimpro_resp_msg),
446ed42cfa8Shotran 				    &ctx->kfifo_lock)) {
447ed42cfa8Shotran 		/*
448ed42cfa8Shotran 		 * If PCC, send a consumer command to Platform to get info
449ed42cfa8Shotran 		 * If Slimpro Mailbox, get message from specific FIFO
450ed42cfa8Shotran 		 */
451ed42cfa8Shotran 		if (!acpi_disabled) {
452ed42cfa8Shotran 			ret = xgene_hwmon_get_notification_msg(ctx,
453ed42cfa8Shotran 							       (u32 *)&amsg);
454ed42cfa8Shotran 			if (ret < 0)
455ed42cfa8Shotran 				continue;
456ed42cfa8Shotran 		}
457ed42cfa8Shotran 
458ed42cfa8Shotran 		if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT)
459ed42cfa8Shotran 			xgene_hwmon_process_pwrmsg(ctx, &amsg);
460ed42cfa8Shotran 	}
461ed42cfa8Shotran }
462ed42cfa8Shotran 
xgene_hwmon_rx_ready(struct xgene_hwmon_dev * ctx,void * msg)4632ca492e2Shotran static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg)
4642ca492e2Shotran {
4652ca492e2Shotran 	if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) {
4662ca492e2Shotran 		/* Enqueue to the FIFO */
4672ca492e2Shotran 		kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
4682ca492e2Shotran 				    sizeof(struct slimpro_resp_msg),
4692ca492e2Shotran 				    &ctx->kfifo_lock);
4702ca492e2Shotran 		return -ENODEV;
4712ca492e2Shotran 	}
4722ca492e2Shotran 
4732ca492e2Shotran 	return 0;
4742ca492e2Shotran }
4752ca492e2Shotran 
476ed42cfa8Shotran /*
477ed42cfa8Shotran  * This function is called when the SLIMpro Mailbox received a message
478ed42cfa8Shotran  */
xgene_hwmon_rx_cb(struct mbox_client * cl,void * msg)479ed42cfa8Shotran static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
480ed42cfa8Shotran {
481ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
4822ca492e2Shotran 
4832ca492e2Shotran 	/*
4842ca492e2Shotran 	 * While the driver registers with the mailbox framework, an interrupt
4852ca492e2Shotran 	 * can be pending before the probe function completes its
4862ca492e2Shotran 	 * initialization. If such condition occurs, just queue up the message
4872ca492e2Shotran 	 * as the driver is not ready for servicing the callback.
4882ca492e2Shotran 	 */
4892ca492e2Shotran 	if (xgene_hwmon_rx_ready(ctx, msg) < 0)
4902ca492e2Shotran 		return;
491ed42cfa8Shotran 
492ed42cfa8Shotran 	/*
493ed42cfa8Shotran 	 * Response message format:
494ed42cfa8Shotran 	 * msg[0] is the return code of the operation
495ed42cfa8Shotran 	 * msg[1] is the first parameter word
496ed42cfa8Shotran 	 * msg[2] is the second parameter word
497ed42cfa8Shotran 	 *
498ed42cfa8Shotran 	 * As message only supports dword size, just assign it.
499ed42cfa8Shotran 	 */
500ed42cfa8Shotran 
501ed42cfa8Shotran 	/* Check for sync query */
502ed42cfa8Shotran 	if (ctx->resp_pending &&
503ed42cfa8Shotran 	    ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
504ed42cfa8Shotran 	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
505ed42cfa8Shotran 	      MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
506ed42cfa8Shotran 	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
507ed42cfa8Shotran 	      MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
508ed42cfa8Shotran 	      TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
509ed42cfa8Shotran 		ctx->sync_msg.msg = ((u32 *)msg)[0];
510ed42cfa8Shotran 		ctx->sync_msg.param1 = ((u32 *)msg)[1];
511ed42cfa8Shotran 		ctx->sync_msg.param2 = ((u32 *)msg)[2];
512ed42cfa8Shotran 
513ed42cfa8Shotran 		/* Operation waiting for response */
514ed42cfa8Shotran 		complete(&ctx->rd_complete);
515ed42cfa8Shotran 
516ed42cfa8Shotran 		return;
517ed42cfa8Shotran 	}
518ed42cfa8Shotran 
519ed42cfa8Shotran 	/* Enqueue to the FIFO */
5202ca492e2Shotran 	kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
521ed42cfa8Shotran 			    sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
522ed42cfa8Shotran 	/* Schedule the bottom handler */
523ed42cfa8Shotran 	schedule_work(&ctx->workq);
524ed42cfa8Shotran }
525ed42cfa8Shotran 
526ed42cfa8Shotran /*
527ed42cfa8Shotran  * This function is called when the PCC Mailbox received a message
528ed42cfa8Shotran  */
xgene_hwmon_pcc_rx_cb(struct mbox_client * cl,void * msg)529ed42cfa8Shotran static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
530ed42cfa8Shotran {
531ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
532ed42cfa8Shotran 	struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
533ed42cfa8Shotran 	struct slimpro_resp_msg amsg;
534ed42cfa8Shotran 
5352ca492e2Shotran 	/*
5362ca492e2Shotran 	 * While the driver registers with the mailbox framework, an interrupt
5372ca492e2Shotran 	 * can be pending before the probe function completes its
5382ca492e2Shotran 	 * initialization. If such condition occurs, just queue up the message
5392ca492e2Shotran 	 * as the driver is not ready for servicing the callback.
5402ca492e2Shotran 	 */
5412ca492e2Shotran 	if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)
5422ca492e2Shotran 		return;
5432ca492e2Shotran 
544ed42cfa8Shotran 	msg = generic_comm_base + 1;
545ed42cfa8Shotran 	/* Check if platform sends interrupt */
546ed42cfa8Shotran 	if (!xgene_word_tst_and_clr(&generic_comm_base->status,
547ed42cfa8Shotran 				    PCCS_SCI_DOORBEL))
548ed42cfa8Shotran 		return;
549ed42cfa8Shotran 
550ed42cfa8Shotran 	/*
551ed42cfa8Shotran 	 * Response message format:
552ed42cfa8Shotran 	 * msg[0] is the return code of the operation
553ed42cfa8Shotran 	 * msg[1] is the first parameter word
554ed42cfa8Shotran 	 * msg[2] is the second parameter word
555ed42cfa8Shotran 	 *
556ed42cfa8Shotran 	 * As message only supports dword size, just assign it.
557ed42cfa8Shotran 	 */
558ed42cfa8Shotran 
559ed42cfa8Shotran 	/* Check for sync query */
560ed42cfa8Shotran 	if (ctx->resp_pending &&
561ed42cfa8Shotran 	    ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
562ed42cfa8Shotran 	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
563ed42cfa8Shotran 	      MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
564ed42cfa8Shotran 	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
565ed42cfa8Shotran 	      MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
566ed42cfa8Shotran 	      TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
567ed42cfa8Shotran 		/* Check if platform completes command */
568ed42cfa8Shotran 		if (xgene_word_tst_and_clr(&generic_comm_base->status,
569ed42cfa8Shotran 					   PCCS_CMD_COMPLETE)) {
570ed42cfa8Shotran 			ctx->sync_msg.msg = ((u32 *)msg)[0];
571ed42cfa8Shotran 			ctx->sync_msg.param1 = ((u32 *)msg)[1];
572ed42cfa8Shotran 			ctx->sync_msg.param2 = ((u32 *)msg)[2];
573ed42cfa8Shotran 
574ed42cfa8Shotran 			/* Operation waiting for response */
575ed42cfa8Shotran 			complete(&ctx->rd_complete);
576ed42cfa8Shotran 
577ed42cfa8Shotran 			return;
578ed42cfa8Shotran 		}
579ed42cfa8Shotran 	}
580ed42cfa8Shotran 
581ed42cfa8Shotran 	/*
582ed42cfa8Shotran 	 * Platform notifies interrupt to OSPM.
583ed42cfa8Shotran 	 * OPSM schedules a consumer command to get this information
584ed42cfa8Shotran 	 * in a workqueue. Platform must wait until OSPM has issued
585ed42cfa8Shotran 	 * a consumer command that serves this notification.
586ed42cfa8Shotran 	 */
587ed42cfa8Shotran 
588ed42cfa8Shotran 	/* Enqueue to the FIFO */
589ed42cfa8Shotran 	kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,
590ed42cfa8Shotran 			    sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
591ed42cfa8Shotran 	/* Schedule the bottom handler */
592ed42cfa8Shotran 	schedule_work(&ctx->workq);
593ed42cfa8Shotran }
594ed42cfa8Shotran 
xgene_hwmon_tx_done(struct mbox_client * cl,void * msg,int ret)595ed42cfa8Shotran static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret)
596ed42cfa8Shotran {
597ed42cfa8Shotran 	if (ret) {
598ed42cfa8Shotran 		dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
599ed42cfa8Shotran 			*(u16 *)msg, ret);
600ed42cfa8Shotran 	} else {
601ed42cfa8Shotran 		dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
602ed42cfa8Shotran 			*(u16 *)msg, ret);
603ed42cfa8Shotran 	}
604ed42cfa8Shotran }
605ed42cfa8Shotran 
606749d782dShotran #ifdef CONFIG_ACPI
607749d782dShotran static const struct acpi_device_id xgene_hwmon_acpi_match[] = {
608749d782dShotran 	{"APMC0D29", XGENE_HWMON_V1},
609749d782dShotran 	{"APMC0D8A", XGENE_HWMON_V2},
610749d782dShotran 	{},
611749d782dShotran };
612749d782dShotran MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
613749d782dShotran #endif
614749d782dShotran 
xgene_hwmon_probe(struct platform_device * pdev)615ed42cfa8Shotran static int xgene_hwmon_probe(struct platform_device *pdev)
616ed42cfa8Shotran {
617ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx;
618ed42cfa8Shotran 	struct mbox_client *cl;
619ed42cfa8Shotran 	int rc;
620ed42cfa8Shotran 
621ed42cfa8Shotran 	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
622ed42cfa8Shotran 	if (!ctx)
623ed42cfa8Shotran 		return -ENOMEM;
624ed42cfa8Shotran 
625ed42cfa8Shotran 	ctx->dev = &pdev->dev;
626ed42cfa8Shotran 	platform_set_drvdata(pdev, ctx);
627ed42cfa8Shotran 	cl = &ctx->mbox_client;
628ed42cfa8Shotran 
6292ca492e2Shotran 	spin_lock_init(&ctx->kfifo_lock);
6302ca492e2Shotran 	mutex_init(&ctx->rd_mutex);
6312ca492e2Shotran 
6322ca492e2Shotran 	rc = kfifo_alloc(&ctx->async_msg_fifo,
6332ca492e2Shotran 			 sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE,
6342ca492e2Shotran 			 GFP_KERNEL);
6352ca492e2Shotran 	if (rc)
63674007ae6SChristophe Jaillet 		return -ENOMEM;
6372ca492e2Shotran 
6382ca492e2Shotran 	INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);
6392ca492e2Shotran 
640ed42cfa8Shotran 	/* Request mailbox channel */
641ed42cfa8Shotran 	cl->dev = &pdev->dev;
642ed42cfa8Shotran 	cl->tx_done = xgene_hwmon_tx_done;
643ed42cfa8Shotran 	cl->tx_block = false;
644ed42cfa8Shotran 	cl->tx_tout = MBOX_OP_TIMEOUTMS;
645ed42cfa8Shotran 	cl->knows_txdone = false;
646ed42cfa8Shotran 	if (acpi_disabled) {
647ed42cfa8Shotran 		cl->rx_callback = xgene_hwmon_rx_cb;
648ed42cfa8Shotran 		ctx->mbox_chan = mbox_request_channel(cl, 0);
649ed42cfa8Shotran 		if (IS_ERR(ctx->mbox_chan)) {
650ed42cfa8Shotran 			dev_err(&pdev->dev,
651ed42cfa8Shotran 				"SLIMpro mailbox channel request failed\n");
65274007ae6SChristophe Jaillet 			rc = -ENODEV;
65374007ae6SChristophe Jaillet 			goto out_mbox_free;
654ed42cfa8Shotran 		}
655ed42cfa8Shotran 	} else {
6567b6da7feSSudeep Holla 		struct pcc_mbox_chan *pcc_chan;
657749d782dShotran 		const struct acpi_device_id *acpi_id;
6582305a18bShotran 		int version;
659749d782dShotran 
6602305a18bShotran 		acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
6612305a18bShotran 					    &pdev->dev);
662660d1878SPeiwei Hu 		if (!acpi_id) {
663660d1878SPeiwei Hu 			rc = -EINVAL;
664660d1878SPeiwei Hu 			goto out_mbox_free;
665660d1878SPeiwei Hu 		}
666749d782dShotran 
667749d782dShotran 		version = (int)acpi_id->driver_data;
668ed42cfa8Shotran 
669ed42cfa8Shotran 		if (device_property_read_u32(&pdev->dev, "pcc-channel",
670ed42cfa8Shotran 					     &ctx->mbox_idx)) {
671ed42cfa8Shotran 			dev_err(&pdev->dev, "no pcc-channel property\n");
67274007ae6SChristophe Jaillet 			rc = -ENODEV;
67374007ae6SChristophe Jaillet 			goto out_mbox_free;
674ed42cfa8Shotran 		}
675ed42cfa8Shotran 
676ed42cfa8Shotran 		cl->rx_callback = xgene_hwmon_pcc_rx_cb;
6777b6da7feSSudeep Holla 		pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
6787b6da7feSSudeep Holla 		if (IS_ERR(pcc_chan)) {
679ed42cfa8Shotran 			dev_err(&pdev->dev,
680ed42cfa8Shotran 				"PPC channel request failed\n");
68174007ae6SChristophe Jaillet 			rc = -ENODEV;
68274007ae6SChristophe Jaillet 			goto out_mbox_free;
683ed42cfa8Shotran 		}
684ed42cfa8Shotran 
6857b6da7feSSudeep Holla 		ctx->pcc_chan = pcc_chan;
6867b6da7feSSudeep Holla 		ctx->mbox_chan = pcc_chan->mchan;
687ed42cfa8Shotran 
688ed42cfa8Shotran 		if (!ctx->mbox_chan->mbox->txdone_irq) {
689ed42cfa8Shotran 			dev_err(&pdev->dev, "PCC IRQ not supported\n");
690ed42cfa8Shotran 			rc = -ENODEV;
69174007ae6SChristophe Jaillet 			goto out;
692ed42cfa8Shotran 		}
693ed42cfa8Shotran 
694ed42cfa8Shotran 		/*
695ed42cfa8Shotran 		 * This is the shared communication region
696ed42cfa8Shotran 		 * for the OS and Platform to communicate over.
697ed42cfa8Shotran 		 */
6987b6da7feSSudeep Holla 		ctx->comm_base_addr = pcc_chan->shmem_base_addr;
699ed42cfa8Shotran 		if (ctx->comm_base_addr) {
700749d782dShotran 			if (version == XGENE_HWMON_V2)
701813cc94cSTianyi Jing 				ctx->pcc_comm_addr = (void __force *)devm_ioremap(&pdev->dev,
702749d782dShotran 								  ctx->comm_base_addr,
7037b6da7feSSudeep Holla 								  pcc_chan->shmem_size);
704749d782dShotran 			else
705813cc94cSTianyi Jing 				ctx->pcc_comm_addr = devm_memremap(&pdev->dev,
706749d782dShotran 								   ctx->comm_base_addr,
7077b6da7feSSudeep Holla 								   pcc_chan->shmem_size,
708c7cefce0SArnd Bergmann 								   MEMREMAP_WB);
709ed42cfa8Shotran 		} else {
710ed42cfa8Shotran 			dev_err(&pdev->dev, "Failed to get PCC comm region\n");
711ed42cfa8Shotran 			rc = -ENODEV;
71274007ae6SChristophe Jaillet 			goto out;
713ed42cfa8Shotran 		}
714ed42cfa8Shotran 
715*b1e17ebdSXinghuo Chen 		if (IS_ERR_OR_NULL(ctx->pcc_comm_addr)) {
716ed42cfa8Shotran 			dev_err(&pdev->dev,
717ed42cfa8Shotran 				"Failed to ioremap PCC comm region\n");
718ed42cfa8Shotran 			rc = -ENOMEM;
71974007ae6SChristophe Jaillet 			goto out;
720ed42cfa8Shotran 		}
721ed42cfa8Shotran 
722ed42cfa8Shotran 		/*
7237b6da7feSSudeep Holla 		 * pcc_chan->latency is just a Nominal value. In reality
724ed42cfa8Shotran 		 * the remote processor could be much slower to reply.
725ed42cfa8Shotran 		 * So add an arbitrary amount of wait on top of Nominal.
726ed42cfa8Shotran 		 */
7277b6da7feSSudeep Holla 		ctx->usecs_lat = PCC_NUM_RETRIES * pcc_chan->latency;
728ed42cfa8Shotran 	}
729ed42cfa8Shotran 
730ed42cfa8Shotran 	ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
731ed42cfa8Shotran 							   "apm_xgene",
732ed42cfa8Shotran 							   ctx,
733ed42cfa8Shotran 							   xgene_hwmon_groups);
734ed42cfa8Shotran 	if (IS_ERR(ctx->hwmon_dev)) {
735ed42cfa8Shotran 		dev_err(&pdev->dev, "Failed to register HW monitor device\n");
736ed42cfa8Shotran 		rc = PTR_ERR(ctx->hwmon_dev);
737ed42cfa8Shotran 		goto out;
738ed42cfa8Shotran 	}
739ed42cfa8Shotran 
7402ca492e2Shotran 	/*
7412ca492e2Shotran 	 * Schedule the bottom handler if there is a pending message.
7422ca492e2Shotran 	 */
7432ca492e2Shotran 	schedule_work(&ctx->workq);
7442ca492e2Shotran 
745ed42cfa8Shotran 	dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");
746ed42cfa8Shotran 
747ed42cfa8Shotran 	return 0;
748ed42cfa8Shotran 
749ed42cfa8Shotran out:
750ed42cfa8Shotran 	if (acpi_disabled)
751ed42cfa8Shotran 		mbox_free_channel(ctx->mbox_chan);
752ed42cfa8Shotran 	else
7537b6da7feSSudeep Holla 		pcc_mbox_free_channel(ctx->pcc_chan);
7542ca492e2Shotran out_mbox_free:
7552ca492e2Shotran 	kfifo_free(&ctx->async_msg_fifo);
756ed42cfa8Shotran 
757ed42cfa8Shotran 	return rc;
758ed42cfa8Shotran }
759ed42cfa8Shotran 
xgene_hwmon_remove(struct platform_device * pdev)760ed42cfa8Shotran static int xgene_hwmon_remove(struct platform_device *pdev)
761ed42cfa8Shotran {
762ed42cfa8Shotran 	struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
763ed42cfa8Shotran 
764cb090e64SZheng Wang 	cancel_work_sync(&ctx->workq);
765ed42cfa8Shotran 	hwmon_device_unregister(ctx->hwmon_dev);
766ed42cfa8Shotran 	kfifo_free(&ctx->async_msg_fifo);
767ed42cfa8Shotran 	if (acpi_disabled)
768ed42cfa8Shotran 		mbox_free_channel(ctx->mbox_chan);
769ed42cfa8Shotran 	else
7707b6da7feSSudeep Holla 		pcc_mbox_free_channel(ctx->pcc_chan);
771ed42cfa8Shotran 
772ed42cfa8Shotran 	return 0;
773ed42cfa8Shotran }
774ed42cfa8Shotran 
775ed42cfa8Shotran static const struct of_device_id xgene_hwmon_of_match[] = {
776ed42cfa8Shotran 	{.compatible = "apm,xgene-slimpro-hwmon"},
777ed42cfa8Shotran 	{}
778ed42cfa8Shotran };
779ed42cfa8Shotran MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
780ed42cfa8Shotran 
781fb42dce4SGeert Uytterhoeven static struct platform_driver xgene_hwmon_driver = {
782ed42cfa8Shotran 	.probe = xgene_hwmon_probe,
783ed42cfa8Shotran 	.remove = xgene_hwmon_remove,
784ed42cfa8Shotran 	.driver = {
785ed42cfa8Shotran 		.name = "xgene-slimpro-hwmon",
786ed42cfa8Shotran 		.of_match_table = xgene_hwmon_of_match,
787ed42cfa8Shotran 		.acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
788ed42cfa8Shotran 	},
789ed42cfa8Shotran };
790ed42cfa8Shotran module_platform_driver(xgene_hwmon_driver);
791ed42cfa8Shotran 
792ed42cfa8Shotran MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
793ed42cfa8Shotran MODULE_LICENSE("GPL");
794