xref: /openbmc/linux/drivers/crypto/intel/qat/qat_common/adf_gen4_pm.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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