18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * APM X-Gene SoC Hardware Monitoring Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016, Applied Micro Circuits Corporation
68c2ecf20Sopenharmony_ci * Author: Loc Ho <lho@apm.com>
78c2ecf20Sopenharmony_ci *         Hoan Tran <hotran@apm.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This driver provides the following features:
108c2ecf20Sopenharmony_ci *  - Retrieve CPU total power (uW)
118c2ecf20Sopenharmony_ci *  - Retrieve IO total power (uW)
128c2ecf20Sopenharmony_ci *  - Retrieve SoC temperature (milli-degree C) and alarm
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <linux/acpi.h>
158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
168c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
178c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
218c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h>
228c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/of.h>
258c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <acpi/pcc.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* SLIMpro message defines */
308c2ecf20Sopenharmony_ci#define MSG_TYPE_DBG			0
318c2ecf20Sopenharmony_ci#define MSG_TYPE_ERR			7
328c2ecf20Sopenharmony_ci#define MSG_TYPE_PWRMGMT		9
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define MSG_TYPE(v)			(((v) & 0xF0000000) >> 28)
358c2ecf20Sopenharmony_ci#define MSG_TYPE_SET(v)			(((v) << 28) & 0xF0000000)
368c2ecf20Sopenharmony_ci#define MSG_SUBTYPE(v)			(((v) & 0x0F000000) >> 24)
378c2ecf20Sopenharmony_ci#define MSG_SUBTYPE_SET(v)		(((v) << 24) & 0x0F000000)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define DBG_SUBTYPE_SENSOR_READ		4
408c2ecf20Sopenharmony_ci#define SENSOR_RD_MSG			0x04FFE902
418c2ecf20Sopenharmony_ci#define SENSOR_RD_EN_ADDR(a)		((a) & 0x000FFFFF)
428c2ecf20Sopenharmony_ci#define PMD_PWR_REG			0x20
438c2ecf20Sopenharmony_ci#define PMD_PWR_MW_REG			0x26
448c2ecf20Sopenharmony_ci#define SOC_PWR_REG			0x21
458c2ecf20Sopenharmony_ci#define SOC_PWR_MW_REG			0x27
468c2ecf20Sopenharmony_ci#define SOC_TEMP_REG			0x10
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define TEMP_NEGATIVE_BIT		8
498c2ecf20Sopenharmony_ci#define SENSOR_INVALID_DATA		BIT(15)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define PWRMGMT_SUBTYPE_TPC		1
528c2ecf20Sopenharmony_ci#define TPC_ALARM			2
538c2ecf20Sopenharmony_ci#define TPC_GET_ALARM			3
548c2ecf20Sopenharmony_ci#define TPC_CMD(v)			(((v) & 0x00FF0000) >> 16)
558c2ecf20Sopenharmony_ci#define TPC_CMD_SET(v)			(((v) << 16) & 0x00FF0000)
568c2ecf20Sopenharmony_ci#define TPC_EN_MSG(hndl, cmd, type) \
578c2ecf20Sopenharmony_ci	(MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \
588c2ecf20Sopenharmony_ci	MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type)
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* PCC defines */
618c2ecf20Sopenharmony_ci#define PCC_SIGNATURE_MASK		0x50424300
628c2ecf20Sopenharmony_ci#define PCCC_GENERATE_DB_INT		BIT(15)
638c2ecf20Sopenharmony_ci#define PCCS_CMD_COMPLETE		BIT(0)
648c2ecf20Sopenharmony_ci#define PCCS_SCI_DOORBEL		BIT(1)
658c2ecf20Sopenharmony_ci#define PCCS_PLATFORM_NOTIFICATION	BIT(3)
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * Arbitrary retries in case the remote processor is slow to respond
688c2ecf20Sopenharmony_ci * to PCC commands
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ci#define PCC_NUM_RETRIES			500
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define ASYNC_MSG_FIFO_SIZE		16
738c2ecf20Sopenharmony_ci#define MBOX_OP_TIMEOUTMS		1000
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define WATT_TO_mWATT(x)		((x) * 1000)
768c2ecf20Sopenharmony_ci#define mWATT_TO_uWATT(x)		((x) * 1000)
778c2ecf20Sopenharmony_ci#define CELSIUS_TO_mCELSIUS(x)		((x) * 1000)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define to_xgene_hwmon_dev(cl)		\
808c2ecf20Sopenharmony_ci	container_of(cl, struct xgene_hwmon_dev, mbox_client)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cienum xgene_hwmon_version {
838c2ecf20Sopenharmony_ci	XGENE_HWMON_V1 = 0,
848c2ecf20Sopenharmony_ci	XGENE_HWMON_V2 = 1,
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistruct slimpro_resp_msg {
888c2ecf20Sopenharmony_ci	u32 msg;
898c2ecf20Sopenharmony_ci	u32 param1;
908c2ecf20Sopenharmony_ci	u32 param2;
918c2ecf20Sopenharmony_ci} __packed;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistruct xgene_hwmon_dev {
948c2ecf20Sopenharmony_ci	struct device		*dev;
958c2ecf20Sopenharmony_ci	struct mbox_chan	*mbox_chan;
968c2ecf20Sopenharmony_ci	struct mbox_client	mbox_client;
978c2ecf20Sopenharmony_ci	int			mbox_idx;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	spinlock_t		kfifo_lock;
1008c2ecf20Sopenharmony_ci	struct mutex		rd_mutex;
1018c2ecf20Sopenharmony_ci	struct completion	rd_complete;
1028c2ecf20Sopenharmony_ci	int			resp_pending;
1038c2ecf20Sopenharmony_ci	struct slimpro_resp_msg sync_msg;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	struct work_struct	workq;
1068c2ecf20Sopenharmony_ci	struct kfifo_rec_ptr_1	async_msg_fifo;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	struct device		*hwmon_dev;
1098c2ecf20Sopenharmony_ci	bool			temp_critical_alarm;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	phys_addr_t		comm_base_addr;
1128c2ecf20Sopenharmony_ci	void			*pcc_comm_addr;
1138c2ecf20Sopenharmony_ci	u64			usecs_lat;
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * This function tests and clears a bitmask then returns its old value
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u16 ret, val;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	val = le16_to_cpu(READ_ONCE(*addr));
1248c2ecf20Sopenharmony_ci	ret = val & mask;
1258c2ecf20Sopenharmony_ci	val &= ~mask;
1268c2ecf20Sopenharmony_ci	WRITE_ONCE(*addr, cpu_to_le16(val));
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return ret;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
1348c2ecf20Sopenharmony_ci	u32 *ptr = (void *)(generic_comm_base + 1);
1358c2ecf20Sopenharmony_ci	int rc, i;
1368c2ecf20Sopenharmony_ci	u16 val;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	mutex_lock(&ctx->rd_mutex);
1398c2ecf20Sopenharmony_ci	init_completion(&ctx->rd_complete);
1408c2ecf20Sopenharmony_ci	ctx->resp_pending = true;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Write signature for subspace */
1438c2ecf20Sopenharmony_ci	WRITE_ONCE(generic_comm_base->signature,
1448c2ecf20Sopenharmony_ci		   cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* Write to the shared command region */
1478c2ecf20Sopenharmony_ci	WRITE_ONCE(generic_comm_base->command,
1488c2ecf20Sopenharmony_ci		   cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* Flip CMD COMPLETE bit */
1518c2ecf20Sopenharmony_ci	val = le16_to_cpu(READ_ONCE(generic_comm_base->status));
1528c2ecf20Sopenharmony_ci	val &= ~PCCS_CMD_COMPLETE;
1538c2ecf20Sopenharmony_ci	WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Copy the message to the PCC comm space */
1568c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)
1578c2ecf20Sopenharmony_ci		WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* Ring the doorbell */
1608c2ecf20Sopenharmony_ci	rc = mbox_send_message(ctx->mbox_chan, msg);
1618c2ecf20Sopenharmony_ci	if (rc < 0) {
1628c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Mailbox send error %d\n", rc);
1638c2ecf20Sopenharmony_ci		goto err;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&ctx->rd_complete,
1668c2ecf20Sopenharmony_ci					 usecs_to_jiffies(ctx->usecs_lat))) {
1678c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Mailbox operation timed out\n");
1688c2ecf20Sopenharmony_ci		rc = -ETIMEDOUT;
1698c2ecf20Sopenharmony_ci		goto err;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* Check for error message */
1738c2ecf20Sopenharmony_ci	if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
1748c2ecf20Sopenharmony_ci		rc = -EINVAL;
1758c2ecf20Sopenharmony_ci		goto err;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	msg[0] = ctx->sync_msg.msg;
1798c2ecf20Sopenharmony_ci	msg[1] = ctx->sync_msg.param1;
1808c2ecf20Sopenharmony_ci	msg[2] = ctx->sync_msg.param2;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cierr:
1838c2ecf20Sopenharmony_ci	mbox_chan_txdone(ctx->mbox_chan, 0);
1848c2ecf20Sopenharmony_ci	ctx->resp_pending = false;
1858c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->rd_mutex);
1868c2ecf20Sopenharmony_ci	return rc;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	int rc;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	mutex_lock(&ctx->rd_mutex);
1948c2ecf20Sopenharmony_ci	init_completion(&ctx->rd_complete);
1958c2ecf20Sopenharmony_ci	ctx->resp_pending = true;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	rc = mbox_send_message(ctx->mbox_chan, msg);
1988c2ecf20Sopenharmony_ci	if (rc < 0) {
1998c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Mailbox send error %d\n", rc);
2008c2ecf20Sopenharmony_ci		goto err;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&ctx->rd_complete,
2048c2ecf20Sopenharmony_ci					 msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) {
2058c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Mailbox operation timed out\n");
2068c2ecf20Sopenharmony_ci		rc = -ETIMEDOUT;
2078c2ecf20Sopenharmony_ci		goto err;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* Check for error message */
2118c2ecf20Sopenharmony_ci	if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
2128c2ecf20Sopenharmony_ci		rc = -EINVAL;
2138c2ecf20Sopenharmony_ci		goto err;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	msg[0] = ctx->sync_msg.msg;
2178c2ecf20Sopenharmony_ci	msg[1] = ctx->sync_msg.param1;
2188c2ecf20Sopenharmony_ci	msg[2] = ctx->sync_msg.param2;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cierr:
2218c2ecf20Sopenharmony_ci	ctx->resp_pending = false;
2228c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->rd_mutex);
2238c2ecf20Sopenharmony_ci	return rc;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
2278c2ecf20Sopenharmony_ci				  u32 *data)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	u32 msg[3];
2308c2ecf20Sopenharmony_ci	int rc;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	msg[0] = SENSOR_RD_MSG;
2338c2ecf20Sopenharmony_ci	msg[1] = SENSOR_RD_EN_ADDR(addr);
2348c2ecf20Sopenharmony_ci	msg[2] = 0;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (acpi_disabled)
2378c2ecf20Sopenharmony_ci		rc = xgene_hwmon_rd(ctx, msg);
2388c2ecf20Sopenharmony_ci	else
2398c2ecf20Sopenharmony_ci		rc = xgene_hwmon_pcc_rd(ctx, msg);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (rc < 0)
2428c2ecf20Sopenharmony_ci		return rc;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/*
2458c2ecf20Sopenharmony_ci	 * Check if sensor data is valid.
2468c2ecf20Sopenharmony_ci	 */
2478c2ecf20Sopenharmony_ci	if (msg[1] & SENSOR_INVALID_DATA)
2488c2ecf20Sopenharmony_ci		return -ENODATA;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	*data = msg[1];
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return rc;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx,
2568c2ecf20Sopenharmony_ci					    u32 *amsg)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	u32 msg[3];
2598c2ecf20Sopenharmony_ci	int rc;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0);
2628c2ecf20Sopenharmony_ci	msg[1] = 0;
2638c2ecf20Sopenharmony_ci	msg[2] = 0;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	rc = xgene_hwmon_pcc_rd(ctx, msg);
2668c2ecf20Sopenharmony_ci	if (rc < 0)
2678c2ecf20Sopenharmony_ci		return rc;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	amsg[0] = msg[0];
2708c2ecf20Sopenharmony_ci	amsg[1] = msg[1];
2718c2ecf20Sopenharmony_ci	amsg[2] = msg[2];
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return rc;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	u32 watt, mwatt;
2798c2ecf20Sopenharmony_ci	int rc;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);
2828c2ecf20Sopenharmony_ci	if (rc < 0)
2838c2ecf20Sopenharmony_ci		return rc;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);
2868c2ecf20Sopenharmony_ci	if (rc < 0)
2878c2ecf20Sopenharmony_ci		return rc;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	*val = WATT_TO_mWATT(watt) + mwatt;
2908c2ecf20Sopenharmony_ci	return 0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	u32 watt, mwatt;
2968c2ecf20Sopenharmony_ci	int rc;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt);
2998c2ecf20Sopenharmony_ci	if (rc < 0)
3008c2ecf20Sopenharmony_ci		return rc;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt);
3038c2ecf20Sopenharmony_ci	if (rc < 0)
3048c2ecf20Sopenharmony_ci		return rc;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	*val = WATT_TO_mWATT(watt) + mwatt;
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/*
3168c2ecf20Sopenharmony_ci * Sensor temperature/power functions
3178c2ecf20Sopenharmony_ci */
3188c2ecf20Sopenharmony_cistatic ssize_t temp1_input_show(struct device *dev,
3198c2ecf20Sopenharmony_ci				struct device_attribute *attr,
3208c2ecf20Sopenharmony_ci				char *buf)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
3238c2ecf20Sopenharmony_ci	int rc, temp;
3248c2ecf20Sopenharmony_ci	u32 val;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	rc = xgene_hwmon_get_temp(ctx, &val);
3278c2ecf20Sopenharmony_ci	if (rc < 0)
3288c2ecf20Sopenharmony_ci		return rc;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp));
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic ssize_t temp1_label_show(struct device *dev,
3368c2ecf20Sopenharmony_ci				struct device_attribute *attr,
3378c2ecf20Sopenharmony_ci				char *buf)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "SoC Temperature\n");
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic ssize_t temp1_critical_alarm_show(struct device *dev,
3438c2ecf20Sopenharmony_ci					 struct device_attribute *devattr,
3448c2ecf20Sopenharmony_ci					 char *buf)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic ssize_t power1_label_show(struct device *dev,
3528c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3538c2ecf20Sopenharmony_ci				 char *buf)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "CPU power\n");
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic ssize_t power2_label_show(struct device *dev,
3598c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3608c2ecf20Sopenharmony_ci				 char *buf)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "IO power\n");
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic ssize_t power1_input_show(struct device *dev,
3668c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3678c2ecf20Sopenharmony_ci				 char *buf)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
3708c2ecf20Sopenharmony_ci	u32 val;
3718c2ecf20Sopenharmony_ci	int rc;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	rc = xgene_hwmon_get_cpu_pwr(ctx, &val);
3748c2ecf20Sopenharmony_ci	if (rc < 0)
3758c2ecf20Sopenharmony_ci		return rc;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic ssize_t power2_input_show(struct device *dev,
3818c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3828c2ecf20Sopenharmony_ci				 char *buf)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
3858c2ecf20Sopenharmony_ci	u32 val;
3868c2ecf20Sopenharmony_ci	int rc;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	rc = xgene_hwmon_get_io_pwr(ctx, &val);
3898c2ecf20Sopenharmony_ci	if (rc < 0)
3908c2ecf20Sopenharmony_ci		return rc;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_label);
3968c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_input);
3978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_critical_alarm);
3988c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_label);
3998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power1_input);
4008c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power2_label);
4018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(power2_input);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic struct attribute *xgene_hwmon_attrs[] = {
4048c2ecf20Sopenharmony_ci	&dev_attr_temp1_label.attr,
4058c2ecf20Sopenharmony_ci	&dev_attr_temp1_input.attr,
4068c2ecf20Sopenharmony_ci	&dev_attr_temp1_critical_alarm.attr,
4078c2ecf20Sopenharmony_ci	&dev_attr_power1_label.attr,
4088c2ecf20Sopenharmony_ci	&dev_attr_power1_input.attr,
4098c2ecf20Sopenharmony_ci	&dev_attr_power2_label.attr,
4108c2ecf20Sopenharmony_ci	&dev_attr_power2_input.attr,
4118c2ecf20Sopenharmony_ci	NULL,
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(xgene_hwmon);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx,
4178c2ecf20Sopenharmony_ci				 struct slimpro_resp_msg *amsg)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	ctx->temp_critical_alarm = !!amsg->param2;
4208c2ecf20Sopenharmony_ci	sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm");
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx,
4268c2ecf20Sopenharmony_ci				       struct slimpro_resp_msg *amsg)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) &&
4298c2ecf20Sopenharmony_ci	    (TPC_CMD(amsg->msg) == TPC_ALARM))
4308c2ecf20Sopenharmony_ci		xgene_hwmon_tpc_alarm(ctx, amsg);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/*
4348c2ecf20Sopenharmony_ci * This function is called to process async work queue
4358c2ecf20Sopenharmony_ci */
4368c2ecf20Sopenharmony_cistatic void xgene_hwmon_evt_work(struct work_struct *work)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct slimpro_resp_msg amsg;
4398c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx;
4408c2ecf20Sopenharmony_ci	int ret;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	ctx = container_of(work, struct xgene_hwmon_dev, workq);
4438c2ecf20Sopenharmony_ci	while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg,
4448c2ecf20Sopenharmony_ci				    sizeof(struct slimpro_resp_msg),
4458c2ecf20Sopenharmony_ci				    &ctx->kfifo_lock)) {
4468c2ecf20Sopenharmony_ci		/*
4478c2ecf20Sopenharmony_ci		 * If PCC, send a consumer command to Platform to get info
4488c2ecf20Sopenharmony_ci		 * If Slimpro Mailbox, get message from specific FIFO
4498c2ecf20Sopenharmony_ci		 */
4508c2ecf20Sopenharmony_ci		if (!acpi_disabled) {
4518c2ecf20Sopenharmony_ci			ret = xgene_hwmon_get_notification_msg(ctx,
4528c2ecf20Sopenharmony_ci							       (u32 *)&amsg);
4538c2ecf20Sopenharmony_ci			if (ret < 0)
4548c2ecf20Sopenharmony_ci				continue;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT)
4588c2ecf20Sopenharmony_ci			xgene_hwmon_process_pwrmsg(ctx, &amsg);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) {
4658c2ecf20Sopenharmony_ci		/* Enqueue to the FIFO */
4668c2ecf20Sopenharmony_ci		kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
4678c2ecf20Sopenharmony_ci				    sizeof(struct slimpro_resp_msg),
4688c2ecf20Sopenharmony_ci				    &ctx->kfifo_lock);
4698c2ecf20Sopenharmony_ci		return -ENODEV;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/*
4768c2ecf20Sopenharmony_ci * This function is called when the SLIMpro Mailbox received a message
4778c2ecf20Sopenharmony_ci */
4788c2ecf20Sopenharmony_cistatic void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * While the driver registers with the mailbox framework, an interrupt
4848c2ecf20Sopenharmony_ci	 * can be pending before the probe function completes its
4858c2ecf20Sopenharmony_ci	 * initialization. If such condition occurs, just queue up the message
4868c2ecf20Sopenharmony_ci	 * as the driver is not ready for servicing the callback.
4878c2ecf20Sopenharmony_ci	 */
4888c2ecf20Sopenharmony_ci	if (xgene_hwmon_rx_ready(ctx, msg) < 0)
4898c2ecf20Sopenharmony_ci		return;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/*
4928c2ecf20Sopenharmony_ci	 * Response message format:
4938c2ecf20Sopenharmony_ci	 * msg[0] is the return code of the operation
4948c2ecf20Sopenharmony_ci	 * msg[1] is the first parameter word
4958c2ecf20Sopenharmony_ci	 * msg[2] is the second parameter word
4968c2ecf20Sopenharmony_ci	 *
4978c2ecf20Sopenharmony_ci	 * As message only supports dword size, just assign it.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/* Check for sync query */
5018c2ecf20Sopenharmony_ci	if (ctx->resp_pending &&
5028c2ecf20Sopenharmony_ci	    ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
5038c2ecf20Sopenharmony_ci	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
5048c2ecf20Sopenharmony_ci	      MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
5058c2ecf20Sopenharmony_ci	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
5068c2ecf20Sopenharmony_ci	      MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
5078c2ecf20Sopenharmony_ci	      TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
5088c2ecf20Sopenharmony_ci		ctx->sync_msg.msg = ((u32 *)msg)[0];
5098c2ecf20Sopenharmony_ci		ctx->sync_msg.param1 = ((u32 *)msg)[1];
5108c2ecf20Sopenharmony_ci		ctx->sync_msg.param2 = ((u32 *)msg)[2];
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		/* Operation waiting for response */
5138c2ecf20Sopenharmony_ci		complete(&ctx->rd_complete);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		return;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Enqueue to the FIFO */
5198c2ecf20Sopenharmony_ci	kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
5208c2ecf20Sopenharmony_ci			    sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
5218c2ecf20Sopenharmony_ci	/* Schedule the bottom handler */
5228c2ecf20Sopenharmony_ci	schedule_work(&ctx->workq);
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/*
5268c2ecf20Sopenharmony_ci * This function is called when the PCC Mailbox received a message
5278c2ecf20Sopenharmony_ci */
5288c2ecf20Sopenharmony_cistatic void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
5318c2ecf20Sopenharmony_ci	struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
5328c2ecf20Sopenharmony_ci	struct slimpro_resp_msg amsg;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/*
5358c2ecf20Sopenharmony_ci	 * While the driver registers with the mailbox framework, an interrupt
5368c2ecf20Sopenharmony_ci	 * can be pending before the probe function completes its
5378c2ecf20Sopenharmony_ci	 * initialization. If such condition occurs, just queue up the message
5388c2ecf20Sopenharmony_ci	 * as the driver is not ready for servicing the callback.
5398c2ecf20Sopenharmony_ci	 */
5408c2ecf20Sopenharmony_ci	if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)
5418c2ecf20Sopenharmony_ci		return;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	msg = generic_comm_base + 1;
5448c2ecf20Sopenharmony_ci	/* Check if platform sends interrupt */
5458c2ecf20Sopenharmony_ci	if (!xgene_word_tst_and_clr(&generic_comm_base->status,
5468c2ecf20Sopenharmony_ci				    PCCS_SCI_DOORBEL))
5478c2ecf20Sopenharmony_ci		return;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/*
5508c2ecf20Sopenharmony_ci	 * Response message format:
5518c2ecf20Sopenharmony_ci	 * msg[0] is the return code of the operation
5528c2ecf20Sopenharmony_ci	 * msg[1] is the first parameter word
5538c2ecf20Sopenharmony_ci	 * msg[2] is the second parameter word
5548c2ecf20Sopenharmony_ci	 *
5558c2ecf20Sopenharmony_ci	 * As message only supports dword size, just assign it.
5568c2ecf20Sopenharmony_ci	 */
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* Check for sync query */
5598c2ecf20Sopenharmony_ci	if (ctx->resp_pending &&
5608c2ecf20Sopenharmony_ci	    ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
5618c2ecf20Sopenharmony_ci	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
5628c2ecf20Sopenharmony_ci	      MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
5638c2ecf20Sopenharmony_ci	     (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
5648c2ecf20Sopenharmony_ci	      MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
5658c2ecf20Sopenharmony_ci	      TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
5668c2ecf20Sopenharmony_ci		/* Check if platform completes command */
5678c2ecf20Sopenharmony_ci		if (xgene_word_tst_and_clr(&generic_comm_base->status,
5688c2ecf20Sopenharmony_ci					   PCCS_CMD_COMPLETE)) {
5698c2ecf20Sopenharmony_ci			ctx->sync_msg.msg = ((u32 *)msg)[0];
5708c2ecf20Sopenharmony_ci			ctx->sync_msg.param1 = ((u32 *)msg)[1];
5718c2ecf20Sopenharmony_ci			ctx->sync_msg.param2 = ((u32 *)msg)[2];
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci			/* Operation waiting for response */
5748c2ecf20Sopenharmony_ci			complete(&ctx->rd_complete);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci			return;
5778c2ecf20Sopenharmony_ci		}
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/*
5818c2ecf20Sopenharmony_ci	 * Platform notifies interrupt to OSPM.
5828c2ecf20Sopenharmony_ci	 * OPSM schedules a consumer command to get this information
5838c2ecf20Sopenharmony_ci	 * in a workqueue. Platform must wait until OSPM has issued
5848c2ecf20Sopenharmony_ci	 * a consumer command that serves this notification.
5858c2ecf20Sopenharmony_ci	 */
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/* Enqueue to the FIFO */
5888c2ecf20Sopenharmony_ci	kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,
5898c2ecf20Sopenharmony_ci			    sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
5908c2ecf20Sopenharmony_ci	/* Schedule the bottom handler */
5918c2ecf20Sopenharmony_ci	schedule_work(&ctx->workq);
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	if (ret) {
5978c2ecf20Sopenharmony_ci		dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
5988c2ecf20Sopenharmony_ci			*(u16 *)msg, ret);
5998c2ecf20Sopenharmony_ci	} else {
6008c2ecf20Sopenharmony_ci		dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
6018c2ecf20Sopenharmony_ci			*(u16 *)msg, ret);
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
6068c2ecf20Sopenharmony_cistatic const struct acpi_device_id xgene_hwmon_acpi_match[] = {
6078c2ecf20Sopenharmony_ci	{"APMC0D29", XGENE_HWMON_V1},
6088c2ecf20Sopenharmony_ci	{"APMC0D8A", XGENE_HWMON_V2},
6098c2ecf20Sopenharmony_ci	{},
6108c2ecf20Sopenharmony_ci};
6118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
6128c2ecf20Sopenharmony_ci#endif
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic int xgene_hwmon_probe(struct platform_device *pdev)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx;
6178c2ecf20Sopenharmony_ci	struct mbox_client *cl;
6188c2ecf20Sopenharmony_ci	int rc;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
6218c2ecf20Sopenharmony_ci	if (!ctx)
6228c2ecf20Sopenharmony_ci		return -ENOMEM;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	ctx->dev = &pdev->dev;
6258c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ctx);
6268c2ecf20Sopenharmony_ci	cl = &ctx->mbox_client;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	spin_lock_init(&ctx->kfifo_lock);
6298c2ecf20Sopenharmony_ci	mutex_init(&ctx->rd_mutex);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	rc = kfifo_alloc(&ctx->async_msg_fifo,
6328c2ecf20Sopenharmony_ci			 sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE,
6338c2ecf20Sopenharmony_ci			 GFP_KERNEL);
6348c2ecf20Sopenharmony_ci	if (rc)
6358c2ecf20Sopenharmony_ci		return -ENOMEM;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/* Request mailbox channel */
6408c2ecf20Sopenharmony_ci	cl->dev = &pdev->dev;
6418c2ecf20Sopenharmony_ci	cl->tx_done = xgene_hwmon_tx_done;
6428c2ecf20Sopenharmony_ci	cl->tx_block = false;
6438c2ecf20Sopenharmony_ci	cl->tx_tout = MBOX_OP_TIMEOUTMS;
6448c2ecf20Sopenharmony_ci	cl->knows_txdone = false;
6458c2ecf20Sopenharmony_ci	if (acpi_disabled) {
6468c2ecf20Sopenharmony_ci		cl->rx_callback = xgene_hwmon_rx_cb;
6478c2ecf20Sopenharmony_ci		ctx->mbox_chan = mbox_request_channel(cl, 0);
6488c2ecf20Sopenharmony_ci		if (IS_ERR(ctx->mbox_chan)) {
6498c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
6508c2ecf20Sopenharmony_ci				"SLIMpro mailbox channel request failed\n");
6518c2ecf20Sopenharmony_ci			rc = -ENODEV;
6528c2ecf20Sopenharmony_ci			goto out_mbox_free;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci	} else {
6558c2ecf20Sopenharmony_ci		struct acpi_pcct_hw_reduced *cppc_ss;
6568c2ecf20Sopenharmony_ci		const struct acpi_device_id *acpi_id;
6578c2ecf20Sopenharmony_ci		int version;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci		acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
6608c2ecf20Sopenharmony_ci					    &pdev->dev);
6618c2ecf20Sopenharmony_ci		if (!acpi_id)
6628c2ecf20Sopenharmony_ci			return -EINVAL;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci		version = (int)acpi_id->driver_data;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (device_property_read_u32(&pdev->dev, "pcc-channel",
6678c2ecf20Sopenharmony_ci					     &ctx->mbox_idx)) {
6688c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "no pcc-channel property\n");
6698c2ecf20Sopenharmony_ci			rc = -ENODEV;
6708c2ecf20Sopenharmony_ci			goto out_mbox_free;
6718c2ecf20Sopenharmony_ci		}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		cl->rx_callback = xgene_hwmon_pcc_rx_cb;
6748c2ecf20Sopenharmony_ci		ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
6758c2ecf20Sopenharmony_ci		if (IS_ERR(ctx->mbox_chan)) {
6768c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
6778c2ecf20Sopenharmony_ci				"PPC channel request failed\n");
6788c2ecf20Sopenharmony_ci			rc = -ENODEV;
6798c2ecf20Sopenharmony_ci			goto out_mbox_free;
6808c2ecf20Sopenharmony_ci		}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		/*
6838c2ecf20Sopenharmony_ci		 * The PCC mailbox controller driver should
6848c2ecf20Sopenharmony_ci		 * have parsed the PCCT (global table of all
6858c2ecf20Sopenharmony_ci		 * PCC channels) and stored pointers to the
6868c2ecf20Sopenharmony_ci		 * subspace communication region in con_priv.
6878c2ecf20Sopenharmony_ci		 */
6888c2ecf20Sopenharmony_ci		cppc_ss = ctx->mbox_chan->con_priv;
6898c2ecf20Sopenharmony_ci		if (!cppc_ss) {
6908c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "PPC subspace not found\n");
6918c2ecf20Sopenharmony_ci			rc = -ENODEV;
6928c2ecf20Sopenharmony_ci			goto out;
6938c2ecf20Sopenharmony_ci		}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		if (!ctx->mbox_chan->mbox->txdone_irq) {
6968c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "PCC IRQ not supported\n");
6978c2ecf20Sopenharmony_ci			rc = -ENODEV;
6988c2ecf20Sopenharmony_ci			goto out;
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		/*
7028c2ecf20Sopenharmony_ci		 * This is the shared communication region
7038c2ecf20Sopenharmony_ci		 * for the OS and Platform to communicate over.
7048c2ecf20Sopenharmony_ci		 */
7058c2ecf20Sopenharmony_ci		ctx->comm_base_addr = cppc_ss->base_address;
7068c2ecf20Sopenharmony_ci		if (ctx->comm_base_addr) {
7078c2ecf20Sopenharmony_ci			if (version == XGENE_HWMON_V2)
7088c2ecf20Sopenharmony_ci				ctx->pcc_comm_addr = (void __force *)ioremap(
7098c2ecf20Sopenharmony_ci							ctx->comm_base_addr,
7108c2ecf20Sopenharmony_ci							cppc_ss->length);
7118c2ecf20Sopenharmony_ci			else
7128c2ecf20Sopenharmony_ci				ctx->pcc_comm_addr = memremap(
7138c2ecf20Sopenharmony_ci							ctx->comm_base_addr,
7148c2ecf20Sopenharmony_ci							cppc_ss->length,
7158c2ecf20Sopenharmony_ci							MEMREMAP_WB);
7168c2ecf20Sopenharmony_ci		} else {
7178c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to get PCC comm region\n");
7188c2ecf20Sopenharmony_ci			rc = -ENODEV;
7198c2ecf20Sopenharmony_ci			goto out;
7208c2ecf20Sopenharmony_ci		}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		if (!ctx->pcc_comm_addr) {
7238c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
7248c2ecf20Sopenharmony_ci				"Failed to ioremap PCC comm region\n");
7258c2ecf20Sopenharmony_ci			rc = -ENOMEM;
7268c2ecf20Sopenharmony_ci			goto out;
7278c2ecf20Sopenharmony_ci		}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		/*
7308c2ecf20Sopenharmony_ci		 * cppc_ss->latency is just a Nominal value. In reality
7318c2ecf20Sopenharmony_ci		 * the remote processor could be much slower to reply.
7328c2ecf20Sopenharmony_ci		 * So add an arbitrary amount of wait on top of Nominal.
7338c2ecf20Sopenharmony_ci		 */
7348c2ecf20Sopenharmony_ci		ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
7388c2ecf20Sopenharmony_ci							   "apm_xgene",
7398c2ecf20Sopenharmony_ci							   ctx,
7408c2ecf20Sopenharmony_ci							   xgene_hwmon_groups);
7418c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->hwmon_dev)) {
7428c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register HW monitor device\n");
7438c2ecf20Sopenharmony_ci		rc = PTR_ERR(ctx->hwmon_dev);
7448c2ecf20Sopenharmony_ci		goto out;
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	/*
7488c2ecf20Sopenharmony_ci	 * Schedule the bottom handler if there is a pending message.
7498c2ecf20Sopenharmony_ci	 */
7508c2ecf20Sopenharmony_ci	schedule_work(&ctx->workq);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return 0;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ciout:
7578c2ecf20Sopenharmony_ci	if (acpi_disabled)
7588c2ecf20Sopenharmony_ci		mbox_free_channel(ctx->mbox_chan);
7598c2ecf20Sopenharmony_ci	else
7608c2ecf20Sopenharmony_ci		pcc_mbox_free_channel(ctx->mbox_chan);
7618c2ecf20Sopenharmony_ciout_mbox_free:
7628c2ecf20Sopenharmony_ci	kfifo_free(&ctx->async_msg_fifo);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	return rc;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic int xgene_hwmon_remove(struct platform_device *pdev)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	cancel_work_sync(&ctx->workq);
7728c2ecf20Sopenharmony_ci	hwmon_device_unregister(ctx->hwmon_dev);
7738c2ecf20Sopenharmony_ci	kfifo_free(&ctx->async_msg_fifo);
7748c2ecf20Sopenharmony_ci	if (acpi_disabled)
7758c2ecf20Sopenharmony_ci		mbox_free_channel(ctx->mbox_chan);
7768c2ecf20Sopenharmony_ci	else
7778c2ecf20Sopenharmony_ci		pcc_mbox_free_channel(ctx->mbox_chan);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	return 0;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic const struct of_device_id xgene_hwmon_of_match[] = {
7838c2ecf20Sopenharmony_ci	{.compatible = "apm,xgene-slimpro-hwmon"},
7848c2ecf20Sopenharmony_ci	{}
7858c2ecf20Sopenharmony_ci};
7868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic struct platform_driver xgene_hwmon_driver __refdata = {
7898c2ecf20Sopenharmony_ci	.probe = xgene_hwmon_probe,
7908c2ecf20Sopenharmony_ci	.remove = xgene_hwmon_remove,
7918c2ecf20Sopenharmony_ci	.driver = {
7928c2ecf20Sopenharmony_ci		.name = "xgene-slimpro-hwmon",
7938c2ecf20Sopenharmony_ci		.of_match_table = xgene_hwmon_of_match,
7948c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
7958c2ecf20Sopenharmony_ci	},
7968c2ecf20Sopenharmony_ci};
7978c2ecf20Sopenharmony_cimodule_platform_driver(xgene_hwmon_driver);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
8008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
801