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