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