1a4b16dadSTom Zanussi // SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
2a4b16dadSTom Zanussi /* Copyright(c) 2022 Intel Corporation */
3a4b16dadSTom Zanussi #include <linux/bitfield.h>
4a4b16dadSTom Zanussi #include <linux/iopoll.h>
5a4b16dadSTom Zanussi #include "adf_accel_devices.h"
6a4b16dadSTom Zanussi #include "adf_common_drv.h"
7a4b16dadSTom Zanussi #include "adf_gen4_pm.h"
8a4b16dadSTom Zanussi #include "adf_cfg_strings.h"
9a4b16dadSTom Zanussi #include "icp_qat_fw_init_admin.h"
10a4b16dadSTom Zanussi #include "adf_gen4_hw_data.h"
11a4b16dadSTom Zanussi #include "adf_cfg.h"
12a4b16dadSTom Zanussi 
13a4b16dadSTom Zanussi enum qat_pm_host_msg {
14a4b16dadSTom Zanussi 	PM_NO_CHANGE = 0,
15a4b16dadSTom Zanussi 	PM_SET_MIN,
16a4b16dadSTom Zanussi };
17a4b16dadSTom Zanussi 
18a4b16dadSTom Zanussi struct adf_gen4_pm_data {
19a4b16dadSTom Zanussi 	struct work_struct pm_irq_work;
20a4b16dadSTom Zanussi 	struct adf_accel_dev *accel_dev;
21a4b16dadSTom Zanussi 	u32 pm_int_sts;
22a4b16dadSTom Zanussi };
23a4b16dadSTom Zanussi 
send_host_msg(struct adf_accel_dev * accel_dev)24a4b16dadSTom Zanussi static int send_host_msg(struct adf_accel_dev *accel_dev)
25a4b16dadSTom Zanussi {
26*2382b5aeSLucas Segarra Fernandez 	char pm_idle_support_cfg[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = {};
27a4b16dadSTom Zanussi 	void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
28*2382b5aeSLucas Segarra Fernandez 	bool pm_idle_support;
29a4b16dadSTom Zanussi 	u32 msg;
30*2382b5aeSLucas Segarra Fernandez 	int ret;
31a4b16dadSTom Zanussi 
32a4b16dadSTom Zanussi 	msg = ADF_CSR_RD(pmisc, ADF_GEN4_PM_HOST_MSG);
33a4b16dadSTom Zanussi 	if (msg & ADF_GEN4_PM_MSG_PENDING)
34a4b16dadSTom Zanussi 		return -EBUSY;
35a4b16dadSTom Zanussi 
36*2382b5aeSLucas Segarra Fernandez 	adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
37*2382b5aeSLucas Segarra Fernandez 				ADF_PM_IDLE_SUPPORT, pm_idle_support_cfg);
38*2382b5aeSLucas Segarra Fernandez 	ret = kstrtobool(pm_idle_support_cfg, &pm_idle_support);
39*2382b5aeSLucas Segarra Fernandez 	if (ret)
40*2382b5aeSLucas Segarra Fernandez 		pm_idle_support = true;
41*2382b5aeSLucas Segarra Fernandez 
42a4b16dadSTom Zanussi 	/* Send HOST_MSG */
43*2382b5aeSLucas Segarra Fernandez 	msg = FIELD_PREP(ADF_GEN4_PM_MSG_PAYLOAD_BIT_MASK,
44*2382b5aeSLucas Segarra Fernandez 			 pm_idle_support ? PM_SET_MIN : PM_NO_CHANGE);
45a4b16dadSTom Zanussi 	msg |= ADF_GEN4_PM_MSG_PENDING;
46a4b16dadSTom Zanussi 	ADF_CSR_WR(pmisc, ADF_GEN4_PM_HOST_MSG, msg);
47a4b16dadSTom Zanussi 
48a4b16dadSTom Zanussi 	/* Poll status register to make sure the HOST_MSG has been processed */
49a4b16dadSTom Zanussi 	return read_poll_timeout(ADF_CSR_RD, msg,
50a4b16dadSTom Zanussi 				!(msg & ADF_GEN4_PM_MSG_PENDING),
51a4b16dadSTom Zanussi 				ADF_GEN4_PM_MSG_POLL_DELAY_US,
52a4b16dadSTom Zanussi 				ADF_GEN4_PM_POLL_TIMEOUT_US, true, pmisc,
53a4b16dadSTom Zanussi 				ADF_GEN4_PM_HOST_MSG);
54a4b16dadSTom Zanussi }
55a4b16dadSTom Zanussi 
pm_bh_handler(struct work_struct * work)56a4b16dadSTom Zanussi static void pm_bh_handler(struct work_struct *work)
57a4b16dadSTom Zanussi {
58a4b16dadSTom Zanussi 	struct adf_gen4_pm_data *pm_data =
59a4b16dadSTom Zanussi 		container_of(work, struct adf_gen4_pm_data, pm_irq_work);
60a4b16dadSTom Zanussi 	struct adf_accel_dev *accel_dev = pm_data->accel_dev;
61a4b16dadSTom Zanussi 	void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
62a4b16dadSTom Zanussi 	u32 pm_int_sts = pm_data->pm_int_sts;
63a4b16dadSTom Zanussi 	u32 val;
64a4b16dadSTom Zanussi 
65a4b16dadSTom Zanussi 	/* PM Idle interrupt */
66a4b16dadSTom Zanussi 	if (pm_int_sts & ADF_GEN4_PM_IDLE_STS) {
67a4b16dadSTom Zanussi 		/* Issue host message to FW */
68a4b16dadSTom Zanussi 		if (send_host_msg(accel_dev))
69a4b16dadSTom Zanussi 			dev_warn_ratelimited(&GET_DEV(accel_dev),
70a4b16dadSTom Zanussi 					     "Failed to send host msg to FW\n");
71a4b16dadSTom Zanussi 	}
72a4b16dadSTom Zanussi 
73a4b16dadSTom Zanussi 	/* Clear interrupt status */
74a4b16dadSTom Zanussi 	ADF_CSR_WR(pmisc, ADF_GEN4_PM_INTERRUPT, pm_int_sts);
75a4b16dadSTom Zanussi 
76a4b16dadSTom Zanussi 	/* Reenable PM interrupt */
77a4b16dadSTom Zanussi 	val = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
78a4b16dadSTom Zanussi 	val &= ~ADF_GEN4_PM_SOU;
79a4b16dadSTom Zanussi 	ADF_CSR_WR(pmisc, ADF_GEN4_ERRMSK2, val);
80a4b16dadSTom Zanussi 
81a4b16dadSTom Zanussi 	kfree(pm_data);
82a4b16dadSTom Zanussi }
83a4b16dadSTom Zanussi 
adf_gen4_handle_pm_interrupt(struct adf_accel_dev * accel_dev)84a4b16dadSTom Zanussi bool adf_gen4_handle_pm_interrupt(struct adf_accel_dev *accel_dev)
85a4b16dadSTom Zanussi {
86a4b16dadSTom Zanussi 	void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
87a4b16dadSTom Zanussi 	struct adf_gen4_pm_data *pm_data = NULL;
88a4b16dadSTom Zanussi 	u32 errsou2;
89a4b16dadSTom Zanussi 	u32 errmsk2;
90a4b16dadSTom Zanussi 	u32 val;
91a4b16dadSTom Zanussi 
92a4b16dadSTom Zanussi 	/* Only handle the interrupt triggered by PM */
93a4b16dadSTom Zanussi 	errmsk2 = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
94a4b16dadSTom Zanussi 	if (errmsk2 & ADF_GEN4_PM_SOU)
95a4b16dadSTom Zanussi 		return false;
96a4b16dadSTom Zanussi 
97a4b16dadSTom Zanussi 	errsou2 = ADF_CSR_RD(pmisc, ADF_GEN4_ERRSOU2);
98a4b16dadSTom Zanussi 	if (!(errsou2 & ADF_GEN4_PM_SOU))
99a4b16dadSTom Zanussi 		return false;
100a4b16dadSTom Zanussi 
101a4b16dadSTom Zanussi 	/* Disable interrupt */
102a4b16dadSTom Zanussi 	val = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
103a4b16dadSTom Zanussi 	val |= ADF_GEN4_PM_SOU;
104a4b16dadSTom Zanussi 	ADF_CSR_WR(pmisc, ADF_GEN4_ERRMSK2, val);
105a4b16dadSTom Zanussi 
106a4b16dadSTom Zanussi 	val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_INTERRUPT);
107a4b16dadSTom Zanussi 
108a4b16dadSTom Zanussi 	pm_data = kzalloc(sizeof(*pm_data), GFP_ATOMIC);
109a4b16dadSTom Zanussi 	if (!pm_data)
110a4b16dadSTom Zanussi 		return false;
111a4b16dadSTom Zanussi 
112a4b16dadSTom Zanussi 	pm_data->pm_int_sts = val;
113a4b16dadSTom Zanussi 	pm_data->accel_dev = accel_dev;
114a4b16dadSTom Zanussi 
115a4b16dadSTom Zanussi 	INIT_WORK(&pm_data->pm_irq_work, pm_bh_handler);
116a4b16dadSTom Zanussi 	adf_misc_wq_queue_work(&pm_data->pm_irq_work);
117a4b16dadSTom Zanussi 
118a4b16dadSTom Zanussi 	return true;
119a4b16dadSTom Zanussi }
120a4b16dadSTom Zanussi EXPORT_SYMBOL_GPL(adf_gen4_handle_pm_interrupt);
121a4b16dadSTom Zanussi 
adf_gen4_enable_pm(struct adf_accel_dev * accel_dev)122a4b16dadSTom Zanussi int adf_gen4_enable_pm(struct adf_accel_dev *accel_dev)
123a4b16dadSTom Zanussi {
124a4b16dadSTom Zanussi 	void __iomem *pmisc = adf_get_pmisc_base(accel_dev);
125a4b16dadSTom Zanussi 	int ret;
126a4b16dadSTom Zanussi 	u32 val;
127a4b16dadSTom Zanussi 
128a4b16dadSTom Zanussi 	ret = adf_init_admin_pm(accel_dev, ADF_GEN4_PM_DEFAULT_IDLE_FILTER);
129a4b16dadSTom Zanussi 	if (ret)
130a4b16dadSTom Zanussi 		return ret;
131a4b16dadSTom Zanussi 
132a4b16dadSTom Zanussi 	/* Enable default PM interrupts: IDLE, THROTTLE */
133a4b16dadSTom Zanussi 	val = ADF_CSR_RD(pmisc, ADF_GEN4_PM_INTERRUPT);
134a4b16dadSTom Zanussi 	val |= ADF_GEN4_PM_INT_EN_DEFAULT;
135a4b16dadSTom Zanussi 
136a4b16dadSTom Zanussi 	/* Clear interrupt status */
137a4b16dadSTom Zanussi 	val |= ADF_GEN4_PM_INT_STS_MASK;
138a4b16dadSTom Zanussi 	ADF_CSR_WR(pmisc, ADF_GEN4_PM_INTERRUPT, val);
139a4b16dadSTom Zanussi 
140a4b16dadSTom Zanussi 	/* Unmask PM Interrupt */
141a4b16dadSTom Zanussi 	val = ADF_CSR_RD(pmisc, ADF_GEN4_ERRMSK2);
142a4b16dadSTom Zanussi 	val &= ~ADF_GEN4_PM_SOU;
143a4b16dadSTom Zanussi 	ADF_CSR_WR(pmisc, ADF_GEN4_ERRMSK2, val);
144a4b16dadSTom Zanussi 
145a4b16dadSTom Zanussi 	return 0;
146a4b16dadSTom Zanussi }
147a4b16dadSTom Zanussi EXPORT_SYMBOL_GPL(adf_gen4_enable_pm);
148