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