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