diff options
author | Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | 2016-08-07 05:25:35 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2016-08-17 05:13:07 -0400 |
commit | ae02e5d40d5f829c589412c6253f925e35cf7a22 (patch) | |
tree | 602344892039bff5ad5479faa3155081369455f9 /drivers/hid/intel-ish-hid/ipc | |
parent | 3703f53b99e4a7c373ce3568dd3f91f175ebb626 (diff) |
HID: intel-ish-hid: ipc layer
This layer is responsible for
- Enumerating over PCI bus
- Inform FW about host readiness
- Provide HW interface to transport layer for control and messages
- Interrupt handling and routing
Original-author: Daniel Drubin <daniel.drubin@intel.com>
Reviewed-and-tested-by: Ooi, Joyce <joyce.ooi@intel.com>
Tested-by: Grant Likely <grant.likely@secretlab.ca>
Tested-by: Rann Bar-On <rb6@duke.edu>
Tested-by: Atri Bhattacharya <badshah400@aim.com>
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/intel-ish-hid/ipc')
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h | 220 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/hw-ish.h | 71 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/ipc.c | 882 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/pci-ish.c | 333 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/utils.h | 64 |
5 files changed, 1570 insertions, 0 deletions
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h new file mode 100644 index 000000000000..ab68afcba2a2 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h | |||
@@ -0,0 +1,220 @@ | |||
1 | /* | ||
2 | * ISH registers definitions | ||
3 | * | ||
4 | * Copyright (c) 2012-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #ifndef _ISHTP_ISH_REGS_H_ | ||
17 | #define _ISHTP_ISH_REGS_H_ | ||
18 | |||
19 | |||
20 | /*** IPC PCI Offsets and sizes ***/ | ||
21 | /* ISH IPC Base Address */ | ||
22 | #define IPC_REG_BASE 0x0000 | ||
23 | /* Peripheral Interrupt Status Register */ | ||
24 | #define IPC_REG_PISR_CHV_AB (IPC_REG_BASE + 0x00) | ||
25 | /* Peripheral Interrupt Mask Register */ | ||
26 | #define IPC_REG_PIMR_CHV_AB (IPC_REG_BASE + 0x04) | ||
27 | /*BXT, CHV_K0*/ | ||
28 | /*Peripheral Interrupt Status Register */ | ||
29 | #define IPC_REG_PISR_BXT (IPC_REG_BASE + 0x0C) | ||
30 | /*Peripheral Interrupt Mask Register */ | ||
31 | #define IPC_REG_PIMR_BXT (IPC_REG_BASE + 0x08) | ||
32 | /***********************************/ | ||
33 | /* ISH Host Firmware status Register */ | ||
34 | #define IPC_REG_ISH_HOST_FWSTS (IPC_REG_BASE + 0x34) | ||
35 | /* Host Communication Register */ | ||
36 | #define IPC_REG_HOST_COMM (IPC_REG_BASE + 0x38) | ||
37 | /* Reset register */ | ||
38 | #define IPC_REG_ISH_RST (IPC_REG_BASE + 0x44) | ||
39 | |||
40 | /* Inbound doorbell register Host to ISH */ | ||
41 | #define IPC_REG_HOST2ISH_DRBL (IPC_REG_BASE + 0x48) | ||
42 | /* Outbound doorbell register ISH to Host */ | ||
43 | #define IPC_REG_ISH2HOST_DRBL (IPC_REG_BASE + 0x54) | ||
44 | /* ISH to HOST message registers */ | ||
45 | #define IPC_REG_ISH2HOST_MSG (IPC_REG_BASE + 0x60) | ||
46 | /* HOST to ISH message registers */ | ||
47 | #define IPC_REG_HOST2ISH_MSG (IPC_REG_BASE + 0xE0) | ||
48 | /* REMAP2 to enable DMA (D3 RCR) */ | ||
49 | #define IPC_REG_ISH_RMP2 (IPC_REG_BASE + 0x368) | ||
50 | |||
51 | #define IPC_REG_MAX (IPC_REG_BASE + 0x400) | ||
52 | |||
53 | /*** register bits - HISR ***/ | ||
54 | /* bit corresponds HOST2ISH interrupt in PISR and PIMR registers */ | ||
55 | #define IPC_INT_HOST2ISH_BIT (1<<0) | ||
56 | /***********************************/ | ||
57 | /*CHV_A0, CHV_B0*/ | ||
58 | /* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */ | ||
59 | #define IPC_INT_ISH2HOST_BIT_CHV_AB (1<<3) | ||
60 | /*BXT, CHV_K0*/ | ||
61 | /* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */ | ||
62 | #define IPC_INT_ISH2HOST_BIT_BXT (1<<0) | ||
63 | /***********************************/ | ||
64 | |||
65 | /* bit corresponds ISH2HOST busy clear interrupt in PIMR register */ | ||
66 | #define IPC_INT_ISH2HOST_CLR_MASK_BIT (1<<11) | ||
67 | |||
68 | /* offset of ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */ | ||
69 | #define IPC_INT_ISH2HOST_CLR_OFFS (0) | ||
70 | |||
71 | /* bit corresponds ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */ | ||
72 | #define IPC_INT_ISH2HOST_CLR_BIT (1<<IPC_INT_ISH2HOST_CLR_OFFS) | ||
73 | |||
74 | /* bit corresponds busy bit in doorbell registers */ | ||
75 | #define IPC_DRBL_BUSY_OFFS (31) | ||
76 | #define IPC_DRBL_BUSY_BIT (1<<IPC_DRBL_BUSY_OFFS) | ||
77 | |||
78 | #define IPC_HOST_OWNS_MSG_OFFS (30) | ||
79 | |||
80 | /* | ||
81 | * A0: bit means that host owns MSGnn registers and is reading them. | ||
82 | * ISH FW may not write to them | ||
83 | */ | ||
84 | #define IPC_HOST_OWNS_MSG_BIT (1<<IPC_HOST_OWNS_MSG_OFFS) | ||
85 | |||
86 | /* | ||
87 | * Host status bits (HOSTCOMM) | ||
88 | */ | ||
89 | /* bit corresponds host ready bit in Host Status Register (HOST_COMM) */ | ||
90 | #define IPC_HOSTCOMM_READY_OFFS (7) | ||
91 | #define IPC_HOSTCOMM_READY_BIT (1<<IPC_HOSTCOMM_READY_OFFS) | ||
92 | |||
93 | /***********************************/ | ||
94 | /*CHV_A0, CHV_B0*/ | ||
95 | #define IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB (31) | ||
96 | #define IPC_HOSTCOMM_INT_EN_BIT_CHV_AB \ | ||
97 | (1<<IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB) | ||
98 | /*BXT, CHV_K0*/ | ||
99 | #define IPC_PIMR_INT_EN_OFFS_BXT (0) | ||
100 | #define IPC_PIMR_INT_EN_BIT_BXT (1<<IPC_PIMR_INT_EN_OFFS_BXT) | ||
101 | |||
102 | #define IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT (8) | ||
103 | #define IPC_HOST2ISH_BUSYCLEAR_MASK_BIT \ | ||
104 | (1<<IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT) | ||
105 | /***********************************/ | ||
106 | /* | ||
107 | * both Host and ISH have ILUP at bit 0 | ||
108 | * bit corresponds host ready bit in both status registers | ||
109 | */ | ||
110 | #define IPC_ILUP_OFFS (0) | ||
111 | #define IPC_ILUP_BIT (1<<IPC_ILUP_OFFS) | ||
112 | |||
113 | /* | ||
114 | * FW status bits (relevant) | ||
115 | */ | ||
116 | #define IPC_FWSTS_ILUP 0x1 | ||
117 | #define IPC_FWSTS_ISHTP_UP (1<<1) | ||
118 | #define IPC_FWSTS_DMA0 (1<<16) | ||
119 | #define IPC_FWSTS_DMA1 (1<<17) | ||
120 | #define IPC_FWSTS_DMA2 (1<<18) | ||
121 | #define IPC_FWSTS_DMA3 (1<<19) | ||
122 | |||
123 | #define IPC_ISH_IN_DMA \ | ||
124 | (IPC_FWSTS_DMA0 | IPC_FWSTS_DMA1 | IPC_FWSTS_DMA2 | IPC_FWSTS_DMA3) | ||
125 | |||
126 | /* bit corresponds host ready bit in ISH FW Status Register */ | ||
127 | #define IPC_ISH_ISHTP_READY_OFFS (1) | ||
128 | #define IPC_ISH_ISHTP_READY_BIT (1<<IPC_ISH_ISHTP_READY_OFFS) | ||
129 | |||
130 | #define IPC_RMP2_DMA_ENABLED 0x1 /* Value to enable DMA, per D3 RCR */ | ||
131 | |||
132 | #define IPC_MSG_MAX_SIZE 0x80 | ||
133 | |||
134 | |||
135 | #define IPC_HEADER_LENGTH_MASK 0x03FF | ||
136 | #define IPC_HEADER_PROTOCOL_MASK 0x0F | ||
137 | #define IPC_HEADER_MNG_CMD_MASK 0x0F | ||
138 | |||
139 | #define IPC_HEADER_LENGTH_OFFSET 0 | ||
140 | #define IPC_HEADER_PROTOCOL_OFFSET 10 | ||
141 | #define IPC_HEADER_MNG_CMD_OFFSET 16 | ||
142 | |||
143 | #define IPC_HEADER_GET_LENGTH(drbl_reg) \ | ||
144 | (((drbl_reg) >> IPC_HEADER_LENGTH_OFFSET)&IPC_HEADER_LENGTH_MASK) | ||
145 | #define IPC_HEADER_GET_PROTOCOL(drbl_reg) \ | ||
146 | (((drbl_reg) >> IPC_HEADER_PROTOCOL_OFFSET)&IPC_HEADER_PROTOCOL_MASK) | ||
147 | #define IPC_HEADER_GET_MNG_CMD(drbl_reg) \ | ||
148 | (((drbl_reg) >> IPC_HEADER_MNG_CMD_OFFSET)&IPC_HEADER_MNG_CMD_MASK) | ||
149 | |||
150 | #define IPC_IS_BUSY(drbl_reg) \ | ||
151 | (((drbl_reg)&IPC_DRBL_BUSY_BIT) == ((uint32_t)IPC_DRBL_BUSY_BIT)) | ||
152 | |||
153 | /***********************************/ | ||
154 | /*CHV_A0, CHV_B0*/ | ||
155 | #define IPC_INT_FROM_ISH_TO_HOST_CHV_AB(drbl_reg) \ | ||
156 | (((drbl_reg)&IPC_INT_ISH2HOST_BIT_CHV_AB) == \ | ||
157 | ((u32)IPC_INT_ISH2HOST_BIT_CHV_AB)) | ||
158 | /*BXT, CHV_K0*/ | ||
159 | #define IPC_INT_FROM_ISH_TO_HOST_BXT(drbl_reg) \ | ||
160 | (((drbl_reg)&IPC_INT_ISH2HOST_BIT_BXT) == \ | ||
161 | ((u32)IPC_INT_ISH2HOST_BIT_BXT)) | ||
162 | /***********************************/ | ||
163 | |||
164 | #define IPC_BUILD_HEADER(length, protocol, busy) \ | ||
165 | (((busy)<<IPC_DRBL_BUSY_OFFS) | \ | ||
166 | ((protocol) << IPC_HEADER_PROTOCOL_OFFSET) | \ | ||
167 | ((length)<<IPC_HEADER_LENGTH_OFFSET)) | ||
168 | |||
169 | #define IPC_BUILD_MNG_MSG(cmd, length) \ | ||
170 | (((1)<<IPC_DRBL_BUSY_OFFS)| \ | ||
171 | ((IPC_PROTOCOL_MNG)<<IPC_HEADER_PROTOCOL_OFFSET)| \ | ||
172 | ((cmd)<<IPC_HEADER_MNG_CMD_OFFSET)| \ | ||
173 | ((length)<<IPC_HEADER_LENGTH_OFFSET)) | ||
174 | |||
175 | |||
176 | #define IPC_SET_HOST_READY(host_status) \ | ||
177 | ((host_status) |= (IPC_HOSTCOMM_READY_BIT)) | ||
178 | |||
179 | #define IPC_SET_HOST_ILUP(host_status) \ | ||
180 | ((host_status) |= (IPC_ILUP_BIT)) | ||
181 | |||
182 | #define IPC_CLEAR_HOST_READY(host_status) \ | ||
183 | ((host_status) ^= (IPC_HOSTCOMM_READY_BIT)) | ||
184 | |||
185 | #define IPC_CLEAR_HOST_ILUP(host_status) \ | ||
186 | ((host_status) ^= (IPC_ILUP_BIT)) | ||
187 | |||
188 | /* todo - temp until PIMR HW ready */ | ||
189 | #define IPC_HOST_BUSY_READING_OFFS 6 | ||
190 | |||
191 | /* bit corresponds host ready bit in Host Status Register (HOST_COMM) */ | ||
192 | #define IPC_HOST_BUSY_READING_BIT (1<<IPC_HOST_BUSY_READING_OFFS) | ||
193 | |||
194 | #define IPC_SET_HOST_BUSY_READING(host_status) \ | ||
195 | ((host_status) |= (IPC_HOST_BUSY_READING_BIT)) | ||
196 | |||
197 | #define IPC_CLEAR_HOST_BUSY_READING(host_status)\ | ||
198 | ((host_status) ^= (IPC_HOST_BUSY_READING_BIT)) | ||
199 | |||
200 | |||
201 | #define IPC_IS_ISH_ISHTP_READY(ish_status) \ | ||
202 | (((ish_status) & IPC_ISH_ISHTP_READY_BIT) == \ | ||
203 | ((uint32_t)IPC_ISH_ISHTP_READY_BIT)) | ||
204 | |||
205 | #define IPC_IS_ISH_ILUP(ish_status) \ | ||
206 | (((ish_status) & IPC_ILUP_BIT) == ((uint32_t)IPC_ILUP_BIT)) | ||
207 | |||
208 | |||
209 | #define IPC_PROTOCOL_ISHTP 1 | ||
210 | #define IPC_PROTOCOL_MNG 3 | ||
211 | |||
212 | #define MNG_RX_CMPL_ENABLE 0 | ||
213 | #define MNG_RX_CMPL_DISABLE 1 | ||
214 | #define MNG_RX_CMPL_INDICATION 2 | ||
215 | #define MNG_RESET_NOTIFY 3 | ||
216 | #define MNG_RESET_NOTIFY_ACK 4 | ||
217 | #define MNG_SYNC_FW_CLOCK 5 | ||
218 | #define MNG_ILLEGAL_CMD 0xFF | ||
219 | |||
220 | #endif /* _ISHTP_ISH_REGS_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h new file mode 100644 index 000000000000..46615a03e78f --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * H/W layer of ISHTP provider device (ISH) | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #ifndef _ISHTP_HW_ISH_H_ | ||
17 | #define _ISHTP_HW_ISH_H_ | ||
18 | |||
19 | #include <linux/pci.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include "hw-ish-regs.h" | ||
22 | #include "ishtp-dev.h" | ||
23 | |||
24 | #define CHV_DEVICE_ID 0x22D8 | ||
25 | #define BXT_Ax_DEVICE_ID 0x0AA2 | ||
26 | #define BXT_Bx_DEVICE_ID 0x1AA2 | ||
27 | #define APL_Ax_DEVICE_ID 0x5AA2 | ||
28 | #define SPT_Ax_DEVICE_ID 0x9D35 | ||
29 | |||
30 | #define REVISION_ID_CHT_A0 0x6 | ||
31 | #define REVISION_ID_CHT_Ax_SI 0x0 | ||
32 | #define REVISION_ID_CHT_Bx_SI 0x10 | ||
33 | #define REVISION_ID_CHT_Kx_SI 0x20 | ||
34 | #define REVISION_ID_CHT_Dx_SI 0x30 | ||
35 | #define REVISION_ID_CHT_B0 0xB0 | ||
36 | #define REVISION_ID_SI_MASK 0x70 | ||
37 | |||
38 | struct ipc_rst_payload_type { | ||
39 | uint16_t reset_id; | ||
40 | uint16_t reserved; | ||
41 | }; | ||
42 | |||
43 | struct time_sync_format { | ||
44 | uint8_t ts1_source; | ||
45 | uint8_t ts2_source; | ||
46 | uint16_t reserved; | ||
47 | } __packed; | ||
48 | |||
49 | struct ipc_time_update_msg { | ||
50 | uint64_t primary_host_time; | ||
51 | struct time_sync_format sync_info; | ||
52 | uint64_t secondary_host_time; | ||
53 | } __packed; | ||
54 | |||
55 | enum { | ||
56 | HOST_UTC_TIME_USEC = 0, | ||
57 | HOST_SYSTEM_TIME_USEC = 1 | ||
58 | }; | ||
59 | |||
60 | struct ish_hw { | ||
61 | void __iomem *mem_addr; | ||
62 | }; | ||
63 | |||
64 | #define to_ish_hw(dev) (struct ish_hw *)((dev)->hw) | ||
65 | |||
66 | irqreturn_t ish_irq_handler(int irq, void *dev_id); | ||
67 | struct ishtp_device *ish_dev_init(struct pci_dev *pdev); | ||
68 | int ish_hw_start(struct ishtp_device *dev); | ||
69 | void ish_device_disable(struct ishtp_device *dev); | ||
70 | |||
71 | #endif /* _ISHTP_HW_ISH_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c new file mode 100644 index 000000000000..851029bf17bc --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c | |||
@@ -0,0 +1,882 @@ | |||
1 | /* | ||
2 | * H/W layer of ISHTP provider device (ISH) | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/sched.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/jiffies.h> | ||
20 | #include "client.h" | ||
21 | #include "hw-ish.h" | ||
22 | #include "utils.h" | ||
23 | #include "hbm.h" | ||
24 | |||
25 | /* For FW reset flow */ | ||
26 | static struct work_struct fw_reset_work; | ||
27 | static struct ishtp_device *ishtp_dev; | ||
28 | |||
29 | /** | ||
30 | * ish_reg_read() - Read register | ||
31 | * @dev: ISHTP device pointer | ||
32 | * @offset: Register offset | ||
33 | * | ||
34 | * Read 32 bit register at a given offset | ||
35 | * | ||
36 | * Return: Read register value | ||
37 | */ | ||
38 | static inline uint32_t ish_reg_read(const struct ishtp_device *dev, | ||
39 | unsigned long offset) | ||
40 | { | ||
41 | struct ish_hw *hw = to_ish_hw(dev); | ||
42 | |||
43 | return readl(hw->mem_addr + offset); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * ish_reg_write() - Write register | ||
48 | * @dev: ISHTP device pointer | ||
49 | * @offset: Register offset | ||
50 | * @value: Value to write | ||
51 | * | ||
52 | * Writes 32 bit register at a give offset | ||
53 | */ | ||
54 | static inline void ish_reg_write(struct ishtp_device *dev, | ||
55 | unsigned long offset, | ||
56 | uint32_t value) | ||
57 | { | ||
58 | struct ish_hw *hw = to_ish_hw(dev); | ||
59 | |||
60 | writel(value, hw->mem_addr + offset); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * _ish_read_fw_sts_reg() - Read FW status register | ||
65 | * @dev: ISHTP device pointer | ||
66 | * | ||
67 | * Read FW status register | ||
68 | * | ||
69 | * Return: Read register value | ||
70 | */ | ||
71 | static inline uint32_t _ish_read_fw_sts_reg(struct ishtp_device *dev) | ||
72 | { | ||
73 | return ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * check_generated_interrupt() - Check if ISH interrupt | ||
78 | * @dev: ISHTP device pointer | ||
79 | * | ||
80 | * Check if an interrupt was generated for ISH | ||
81 | * | ||
82 | * Return: Read true or false | ||
83 | */ | ||
84 | static bool check_generated_interrupt(struct ishtp_device *dev) | ||
85 | { | ||
86 | bool interrupt_generated = true; | ||
87 | uint32_t pisr_val = 0; | ||
88 | |||
89 | if (dev->pdev->device == CHV_DEVICE_ID) { | ||
90 | pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB); | ||
91 | interrupt_generated = | ||
92 | IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val); | ||
93 | } else { | ||
94 | pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT); | ||
95 | interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_BXT(pisr_val); | ||
96 | } | ||
97 | |||
98 | return interrupt_generated; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * ish_is_input_ready() - Check if FW ready for RX | ||
103 | * @dev: ISHTP device pointer | ||
104 | * | ||
105 | * Check if ISH FW is ready for receiving data | ||
106 | * | ||
107 | * Return: Read true or false | ||
108 | */ | ||
109 | static bool ish_is_input_ready(struct ishtp_device *dev) | ||
110 | { | ||
111 | uint32_t doorbell_val; | ||
112 | |||
113 | doorbell_val = ish_reg_read(dev, IPC_REG_HOST2ISH_DRBL); | ||
114 | return !IPC_IS_BUSY(doorbell_val); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * set_host_ready() - Indicate host ready | ||
119 | * @dev: ISHTP device pointer | ||
120 | * | ||
121 | * Set host ready indication to FW | ||
122 | */ | ||
123 | static void set_host_ready(struct ishtp_device *dev) | ||
124 | { | ||
125 | if (dev->pdev->device == CHV_DEVICE_ID) { | ||
126 | if (dev->pdev->revision == REVISION_ID_CHT_A0 || | ||
127 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
128 | REVISION_ID_CHT_Ax_SI) | ||
129 | ish_reg_write(dev, IPC_REG_HOST_COMM, 0x81); | ||
130 | else if (dev->pdev->revision == REVISION_ID_CHT_B0 || | ||
131 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
132 | REVISION_ID_CHT_Bx_SI || | ||
133 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
134 | REVISION_ID_CHT_Kx_SI || | ||
135 | (dev->pdev->revision & REVISION_ID_SI_MASK) == | ||
136 | REVISION_ID_CHT_Dx_SI) { | ||
137 | uint32_t host_comm_val; | ||
138 | |||
139 | host_comm_val = ish_reg_read(dev, IPC_REG_HOST_COMM); | ||
140 | host_comm_val |= IPC_HOSTCOMM_INT_EN_BIT_CHV_AB | 0x81; | ||
141 | ish_reg_write(dev, IPC_REG_HOST_COMM, host_comm_val); | ||
142 | } | ||
143 | } else { | ||
144 | uint32_t host_pimr_val; | ||
145 | |||
146 | host_pimr_val = ish_reg_read(dev, IPC_REG_PIMR_BXT); | ||
147 | host_pimr_val |= IPC_PIMR_INT_EN_BIT_BXT; | ||
148 | /* | ||
149 | * disable interrupt generated instead of | ||
150 | * RX_complete_msg | ||
151 | */ | ||
152 | host_pimr_val &= ~IPC_HOST2ISH_BUSYCLEAR_MASK_BIT; | ||
153 | |||
154 | ish_reg_write(dev, IPC_REG_PIMR_BXT, host_pimr_val); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * ishtp_fw_is_ready() - Check if FW ready | ||
160 | * @dev: ISHTP device pointer | ||
161 | * | ||
162 | * Check if ISH FW is ready | ||
163 | * | ||
164 | * Return: Read true or false | ||
165 | */ | ||
166 | static bool ishtp_fw_is_ready(struct ishtp_device *dev) | ||
167 | { | ||
168 | uint32_t ish_status = _ish_read_fw_sts_reg(dev); | ||
169 | |||
170 | return IPC_IS_ISH_ILUP(ish_status) && | ||
171 | IPC_IS_ISH_ISHTP_READY(ish_status); | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * ish_set_host_rdy() - Indicate host ready | ||
176 | * @dev: ISHTP device pointer | ||
177 | * | ||
178 | * Set host ready indication to FW | ||
179 | */ | ||
180 | static void ish_set_host_rdy(struct ishtp_device *dev) | ||
181 | { | ||
182 | uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM); | ||
183 | |||
184 | IPC_SET_HOST_READY(host_status); | ||
185 | ish_reg_write(dev, IPC_REG_HOST_COMM, host_status); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * ish_clr_host_rdy() - Indicate host not ready | ||
190 | * @dev: ISHTP device pointer | ||
191 | * | ||
192 | * Send host not ready indication to FW | ||
193 | */ | ||
194 | static void ish_clr_host_rdy(struct ishtp_device *dev) | ||
195 | { | ||
196 | uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM); | ||
197 | |||
198 | IPC_CLEAR_HOST_READY(host_status); | ||
199 | ish_reg_write(dev, IPC_REG_HOST_COMM, host_status); | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * _ishtp_read_hdr() - Read message header | ||
204 | * @dev: ISHTP device pointer | ||
205 | * | ||
206 | * Read header of 32bit length | ||
207 | * | ||
208 | * Return: Read register value | ||
209 | */ | ||
210 | static uint32_t _ishtp_read_hdr(const struct ishtp_device *dev) | ||
211 | { | ||
212 | return ish_reg_read(dev, IPC_REG_ISH2HOST_MSG); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * _ishtp_read - Read message | ||
217 | * @dev: ISHTP device pointer | ||
218 | * @buffer: message buffer | ||
219 | * @buffer_length: length of message buffer | ||
220 | * | ||
221 | * Read message from FW | ||
222 | * | ||
223 | * Return: Always 0 | ||
224 | */ | ||
225 | static int _ishtp_read(struct ishtp_device *dev, unsigned char *buffer, | ||
226 | unsigned long buffer_length) | ||
227 | { | ||
228 | uint32_t i; | ||
229 | uint32_t *r_buf = (uint32_t *)buffer; | ||
230 | uint32_t msg_offs; | ||
231 | |||
232 | msg_offs = IPC_REG_ISH2HOST_MSG + sizeof(struct ishtp_msg_hdr); | ||
233 | for (i = 0; i < buffer_length; i += sizeof(uint32_t)) | ||
234 | *r_buf++ = ish_reg_read(dev, msg_offs + i); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * write_ipc_from_queue() - try to write ipc msg from Tx queue to device | ||
241 | * @dev: ishtp device pointer | ||
242 | * | ||
243 | * Check if DRBL is cleared. if it is - write the first IPC msg, then call | ||
244 | * the callback function (unless it's NULL) | ||
245 | * | ||
246 | * Return: 0 for success else failure code | ||
247 | */ | ||
248 | static int write_ipc_from_queue(struct ishtp_device *dev) | ||
249 | { | ||
250 | struct wr_msg_ctl_info *ipc_link; | ||
251 | unsigned long length; | ||
252 | unsigned long rem; | ||
253 | unsigned long flags; | ||
254 | uint32_t doorbell_val; | ||
255 | uint32_t *r_buf; | ||
256 | uint32_t reg_addr; | ||
257 | int i; | ||
258 | void (*ipc_send_compl)(void *); | ||
259 | void *ipc_send_compl_prm; | ||
260 | static int out_ipc_locked; | ||
261 | unsigned long out_ipc_flags; | ||
262 | |||
263 | if (dev->dev_state == ISHTP_DEV_DISABLED) | ||
264 | return -EINVAL; | ||
265 | |||
266 | spin_lock_irqsave(&dev->out_ipc_spinlock, out_ipc_flags); | ||
267 | if (out_ipc_locked) { | ||
268 | spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); | ||
269 | return -EBUSY; | ||
270 | } | ||
271 | out_ipc_locked = 1; | ||
272 | if (!ish_is_input_ready(dev)) { | ||
273 | out_ipc_locked = 0; | ||
274 | spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); | ||
275 | return -EBUSY; | ||
276 | } | ||
277 | spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); | ||
278 | |||
279 | spin_lock_irqsave(&dev->wr_processing_spinlock, flags); | ||
280 | /* | ||
281 | * if tx send list is empty - return 0; | ||
282 | * may happen, as RX_COMPLETE handler doesn't check list emptiness. | ||
283 | */ | ||
284 | if (list_empty(&dev->wr_processing_list_head.link)) { | ||
285 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
286 | out_ipc_locked = 0; | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | ipc_link = list_entry(dev->wr_processing_list_head.link.next, | ||
291 | struct wr_msg_ctl_info, link); | ||
292 | /* first 4 bytes of the data is the doorbell value (IPC header) */ | ||
293 | length = ipc_link->length - sizeof(uint32_t); | ||
294 | doorbell_val = *(uint32_t *)ipc_link->inline_data; | ||
295 | r_buf = (uint32_t *)(ipc_link->inline_data + sizeof(uint32_t)); | ||
296 | |||
297 | /* If sending MNG_SYNC_FW_CLOCK, update clock again */ | ||
298 | if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG && | ||
299 | IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) { | ||
300 | struct timespec ts_system; | ||
301 | struct timeval tv_utc; | ||
302 | uint64_t usec_system, usec_utc; | ||
303 | struct ipc_time_update_msg time_update; | ||
304 | struct time_sync_format ts_format; | ||
305 | |||
306 | get_monotonic_boottime(&ts_system); | ||
307 | do_gettimeofday(&tv_utc); | ||
308 | usec_system = (timespec_to_ns(&ts_system)) / NSEC_PER_USEC; | ||
309 | usec_utc = (uint64_t)tv_utc.tv_sec * 1000000 + | ||
310 | ((uint32_t)tv_utc.tv_usec); | ||
311 | ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; | ||
312 | ts_format.ts2_source = HOST_UTC_TIME_USEC; | ||
313 | |||
314 | time_update.primary_host_time = usec_system; | ||
315 | time_update.secondary_host_time = usec_utc; | ||
316 | time_update.sync_info = ts_format; | ||
317 | |||
318 | memcpy(r_buf, &time_update, | ||
319 | sizeof(struct ipc_time_update_msg)); | ||
320 | } | ||
321 | |||
322 | for (i = 0, reg_addr = IPC_REG_HOST2ISH_MSG; i < length >> 2; i++, | ||
323 | reg_addr += 4) | ||
324 | ish_reg_write(dev, reg_addr, r_buf[i]); | ||
325 | |||
326 | rem = length & 0x3; | ||
327 | if (rem > 0) { | ||
328 | uint32_t reg = 0; | ||
329 | |||
330 | memcpy(®, &r_buf[length >> 2], rem); | ||
331 | ish_reg_write(dev, reg_addr, reg); | ||
332 | } | ||
333 | /* Flush writes to msg registers and doorbell */ | ||
334 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
335 | |||
336 | /* Update IPC counters */ | ||
337 | ++dev->ipc_tx_cnt; | ||
338 | dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); | ||
339 | |||
340 | ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val); | ||
341 | out_ipc_locked = 0; | ||
342 | |||
343 | ipc_send_compl = ipc_link->ipc_send_compl; | ||
344 | ipc_send_compl_prm = ipc_link->ipc_send_compl_prm; | ||
345 | list_del_init(&ipc_link->link); | ||
346 | list_add_tail(&ipc_link->link, &dev->wr_free_list_head.link); | ||
347 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
348 | |||
349 | /* | ||
350 | * callback will be called out of spinlock, | ||
351 | * after ipc_link returned to free list | ||
352 | */ | ||
353 | if (ipc_send_compl) | ||
354 | ipc_send_compl(ipc_send_compl_prm); | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | /** | ||
360 | * write_ipc_to_queue() - write ipc msg to Tx queue | ||
361 | * @dev: ishtp device instance | ||
362 | * @ipc_send_compl: Send complete callback | ||
363 | * @ipc_send_compl_prm: Parameter to send in complete callback | ||
364 | * @msg: Pointer to message | ||
365 | * @length: Length of message | ||
366 | * | ||
367 | * Recived msg with IPC (and upper protocol) header and add it to the device | ||
368 | * Tx-to-write list then try to send the first IPC waiting msg | ||
369 | * (if DRBL is cleared) | ||
370 | * This function returns negative value for failure (means free list | ||
371 | * is empty, or msg too long) and 0 for success. | ||
372 | * | ||
373 | * Return: 0 for success else failure code | ||
374 | */ | ||
375 | static int write_ipc_to_queue(struct ishtp_device *dev, | ||
376 | void (*ipc_send_compl)(void *), void *ipc_send_compl_prm, | ||
377 | unsigned char *msg, int length) | ||
378 | { | ||
379 | struct wr_msg_ctl_info *ipc_link; | ||
380 | unsigned long flags; | ||
381 | |||
382 | if (length > IPC_FULL_MSG_SIZE) | ||
383 | return -EMSGSIZE; | ||
384 | |||
385 | spin_lock_irqsave(&dev->wr_processing_spinlock, flags); | ||
386 | if (list_empty(&dev->wr_free_list_head.link)) { | ||
387 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
388 | return -ENOMEM; | ||
389 | } | ||
390 | ipc_link = list_entry(dev->wr_free_list_head.link.next, | ||
391 | struct wr_msg_ctl_info, link); | ||
392 | list_del_init(&ipc_link->link); | ||
393 | |||
394 | ipc_link->ipc_send_compl = ipc_send_compl; | ||
395 | ipc_link->ipc_send_compl_prm = ipc_send_compl_prm; | ||
396 | ipc_link->length = length; | ||
397 | memcpy(ipc_link->inline_data, msg, length); | ||
398 | |||
399 | list_add_tail(&ipc_link->link, &dev->wr_processing_list_head.link); | ||
400 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
401 | |||
402 | write_ipc_from_queue(dev); | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /** | ||
408 | * ipc_send_mng_msg() - Send management message | ||
409 | * @dev: ishtp device instance | ||
410 | * @msg_code: Message code | ||
411 | * @msg: Pointer to message | ||
412 | * @size: Length of message | ||
413 | * | ||
414 | * Send management message to FW | ||
415 | * | ||
416 | * Return: 0 for success else failure code | ||
417 | */ | ||
418 | static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code, | ||
419 | void *msg, size_t size) | ||
420 | { | ||
421 | unsigned char ipc_msg[IPC_FULL_MSG_SIZE]; | ||
422 | uint32_t drbl_val = IPC_BUILD_MNG_MSG(msg_code, size); | ||
423 | |||
424 | memcpy(ipc_msg, &drbl_val, sizeof(uint32_t)); | ||
425 | memcpy(ipc_msg + sizeof(uint32_t), msg, size); | ||
426 | return write_ipc_to_queue(dev, NULL, NULL, ipc_msg, | ||
427 | sizeof(uint32_t) + size); | ||
428 | } | ||
429 | |||
430 | /** | ||
431 | * ish_fw_reset_handler() - FW reset handler | ||
432 | * @dev: ishtp device pointer | ||
433 | * | ||
434 | * Handle FW reset | ||
435 | * | ||
436 | * Return: 0 for success else failure code | ||
437 | */ | ||
438 | static int ish_fw_reset_handler(struct ishtp_device *dev) | ||
439 | { | ||
440 | uint32_t reset_id; | ||
441 | unsigned long flags; | ||
442 | struct wr_msg_ctl_info *processing, *next; | ||
443 | |||
444 | /* Read reset ID */ | ||
445 | reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF; | ||
446 | |||
447 | /* Clear IPC output queue */ | ||
448 | spin_lock_irqsave(&dev->wr_processing_spinlock, flags); | ||
449 | list_for_each_entry_safe(processing, next, | ||
450 | &dev->wr_processing_list_head.link, link) { | ||
451 | list_del(&processing->link); | ||
452 | list_add_tail(&processing->link, &dev->wr_free_list_head.link); | ||
453 | } | ||
454 | spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); | ||
455 | |||
456 | /* ISHTP notification in IPC_RESET */ | ||
457 | ishtp_reset_handler(dev); | ||
458 | |||
459 | if (!ish_is_input_ready(dev)) | ||
460 | timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, | ||
461 | ish_is_input_ready(dev), (2 * HZ)); | ||
462 | |||
463 | /* ISH FW is dead */ | ||
464 | if (!ish_is_input_ready(dev)) | ||
465 | return -EPIPE; | ||
466 | /* | ||
467 | * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending | ||
468 | * RESET_NOTIFY_ACK - FW will be checking for it | ||
469 | */ | ||
470 | ish_set_host_rdy(dev); | ||
471 | /* Send RESET_NOTIFY_ACK (with reset_id) */ | ||
472 | ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id, | ||
473 | sizeof(uint32_t)); | ||
474 | |||
475 | /* Wait for ISH FW'es ILUP and ISHTP_READY */ | ||
476 | timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, ishtp_fw_is_ready(dev), | ||
477 | (2 * HZ)); | ||
478 | if (!ishtp_fw_is_ready(dev)) { | ||
479 | /* ISH FW is dead */ | ||
480 | uint32_t ish_status; | ||
481 | |||
482 | ish_status = _ish_read_fw_sts_reg(dev); | ||
483 | dev_err(dev->devc, | ||
484 | "[ishtp-ish]: completed reset, ISH is dead (FWSTS = %08X)\n", | ||
485 | ish_status); | ||
486 | return -ENODEV; | ||
487 | } | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | /** | ||
492 | * ish_fw_reset_work_fn() - FW reset worker function | ||
493 | * @unused: not used | ||
494 | * | ||
495 | * Call ish_fw_reset_handler to complete FW reset | ||
496 | */ | ||
497 | static void fw_reset_work_fn(struct work_struct *unused) | ||
498 | { | ||
499 | int rv; | ||
500 | |||
501 | rv = ish_fw_reset_handler(ishtp_dev); | ||
502 | if (!rv) { | ||
503 | /* ISH is ILUP & ISHTP-ready. Restart ISHTP */ | ||
504 | schedule_timeout(HZ / 3); | ||
505 | ishtp_dev->recvd_hw_ready = 1; | ||
506 | wake_up_interruptible(&ishtp_dev->wait_hw_ready); | ||
507 | |||
508 | /* ISHTP notification in IPC_RESET sequence completion */ | ||
509 | ishtp_reset_compl_handler(ishtp_dev); | ||
510 | } else | ||
511 | dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n", | ||
512 | rv); | ||
513 | } | ||
514 | |||
515 | /** | ||
516 | * _ish_sync_fw_clock() -Sync FW clock with the OS clock | ||
517 | * @dev: ishtp device pointer | ||
518 | * | ||
519 | * Sync FW and OS time | ||
520 | */ | ||
521 | static void _ish_sync_fw_clock(struct ishtp_device *dev) | ||
522 | { | ||
523 | static unsigned long prev_sync; | ||
524 | struct timespec ts; | ||
525 | uint64_t usec; | ||
526 | |||
527 | if (prev_sync && jiffies - prev_sync < 20 * HZ) | ||
528 | return; | ||
529 | |||
530 | prev_sync = jiffies; | ||
531 | get_monotonic_boottime(&ts); | ||
532 | usec = (timespec_to_ns(&ts)) / NSEC_PER_USEC; | ||
533 | ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t)); | ||
534 | } | ||
535 | |||
536 | /** | ||
537 | * recv_ipc() - Receive and process IPC management messages | ||
538 | * @dev: ishtp device instance | ||
539 | * @doorbell_val: doorbell value | ||
540 | * | ||
541 | * This function runs in ISR context. | ||
542 | * NOTE: Any other mng command than reset_notify and reset_notify_ack | ||
543 | * won't wake BH handler | ||
544 | */ | ||
545 | static void recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val) | ||
546 | { | ||
547 | uint32_t mng_cmd; | ||
548 | |||
549 | mng_cmd = IPC_HEADER_GET_MNG_CMD(doorbell_val); | ||
550 | |||
551 | switch (mng_cmd) { | ||
552 | default: | ||
553 | break; | ||
554 | |||
555 | case MNG_RX_CMPL_INDICATION: | ||
556 | if (dev->suspend_flag) { | ||
557 | dev->suspend_flag = 0; | ||
558 | wake_up_interruptible(&dev->suspend_wait); | ||
559 | } | ||
560 | if (dev->resume_flag) { | ||
561 | dev->resume_flag = 0; | ||
562 | wake_up_interruptible(&dev->resume_wait); | ||
563 | } | ||
564 | |||
565 | write_ipc_from_queue(dev); | ||
566 | break; | ||
567 | |||
568 | case MNG_RESET_NOTIFY: | ||
569 | if (!ishtp_dev) { | ||
570 | ishtp_dev = dev; | ||
571 | INIT_WORK(&fw_reset_work, fw_reset_work_fn); | ||
572 | } | ||
573 | schedule_work(&fw_reset_work); | ||
574 | break; | ||
575 | |||
576 | case MNG_RESET_NOTIFY_ACK: | ||
577 | dev->recvd_hw_ready = 1; | ||
578 | wake_up_interruptible(&dev->wait_hw_ready); | ||
579 | break; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | /** | ||
584 | * ish_irq_handler() - ISH IRQ handler | ||
585 | * @irq: irq number | ||
586 | * @dev_id: ishtp device pointer | ||
587 | * | ||
588 | * ISH IRQ handler. If interrupt is generated and is for ISH it will process | ||
589 | * the interrupt. | ||
590 | */ | ||
591 | irqreturn_t ish_irq_handler(int irq, void *dev_id) | ||
592 | { | ||
593 | struct ishtp_device *dev = dev_id; | ||
594 | uint32_t doorbell_val; | ||
595 | bool interrupt_generated; | ||
596 | |||
597 | /* Check that it's interrupt from ISH (may be shared) */ | ||
598 | interrupt_generated = check_generated_interrupt(dev); | ||
599 | |||
600 | if (!interrupt_generated) | ||
601 | return IRQ_NONE; | ||
602 | |||
603 | doorbell_val = ish_reg_read(dev, IPC_REG_ISH2HOST_DRBL); | ||
604 | if (!IPC_IS_BUSY(doorbell_val)) | ||
605 | return IRQ_HANDLED; | ||
606 | |||
607 | if (dev->dev_state == ISHTP_DEV_DISABLED) | ||
608 | return IRQ_HANDLED; | ||
609 | |||
610 | /* Sanity check: IPC dgram length in header */ | ||
611 | if (IPC_HEADER_GET_LENGTH(doorbell_val) > IPC_PAYLOAD_SIZE) { | ||
612 | dev_err(dev->devc, | ||
613 | "IPC hdr - bad length: %u; dropped\n", | ||
614 | (unsigned int)IPC_HEADER_GET_LENGTH(doorbell_val)); | ||
615 | goto eoi; | ||
616 | } | ||
617 | |||
618 | switch (IPC_HEADER_GET_PROTOCOL(doorbell_val)) { | ||
619 | default: | ||
620 | break; | ||
621 | case IPC_PROTOCOL_MNG: | ||
622 | recv_ipc(dev, doorbell_val); | ||
623 | break; | ||
624 | case IPC_PROTOCOL_ISHTP: | ||
625 | ishtp_recv(dev); | ||
626 | break; | ||
627 | } | ||
628 | |||
629 | eoi: | ||
630 | /* Update IPC counters */ | ||
631 | ++dev->ipc_rx_cnt; | ||
632 | dev->ipc_rx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); | ||
633 | |||
634 | ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0); | ||
635 | /* Flush write to doorbell */ | ||
636 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
637 | |||
638 | return IRQ_HANDLED; | ||
639 | } | ||
640 | |||
641 | /** | ||
642 | * _ish_hw_reset() - HW reset | ||
643 | * @dev: ishtp device pointer | ||
644 | * | ||
645 | * Reset ISH HW to recover if any error | ||
646 | * | ||
647 | * Return: 0 for success else error fault code | ||
648 | */ | ||
649 | static int _ish_hw_reset(struct ishtp_device *dev) | ||
650 | { | ||
651 | struct pci_dev *pdev = dev->pdev; | ||
652 | int rv; | ||
653 | unsigned int dma_delay; | ||
654 | uint16_t csr; | ||
655 | |||
656 | if (!pdev) | ||
657 | return -ENODEV; | ||
658 | |||
659 | rv = pci_reset_function(pdev); | ||
660 | if (!rv) | ||
661 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
662 | |||
663 | if (!pdev->pm_cap) { | ||
664 | dev_err(&pdev->dev, "Can't reset - no PM caps\n"); | ||
665 | return -EINVAL; | ||
666 | } | ||
667 | |||
668 | /* Now trigger reset to FW */ | ||
669 | ish_reg_write(dev, IPC_REG_ISH_RMP2, 0); | ||
670 | |||
671 | for (dma_delay = 0; dma_delay < MAX_DMA_DELAY && | ||
672 | _ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA); | ||
673 | dma_delay += 5) | ||
674 | mdelay(5); | ||
675 | |||
676 | if (dma_delay >= MAX_DMA_DELAY) { | ||
677 | dev_err(&pdev->dev, | ||
678 | "Can't reset - stuck with DMA in-progress\n"); | ||
679 | return -EBUSY; | ||
680 | } | ||
681 | |||
682 | pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &csr); | ||
683 | |||
684 | csr &= ~PCI_PM_CTRL_STATE_MASK; | ||
685 | csr |= PCI_D3hot; | ||
686 | pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); | ||
687 | |||
688 | mdelay(pdev->d3_delay); | ||
689 | |||
690 | csr &= ~PCI_PM_CTRL_STATE_MASK; | ||
691 | csr |= PCI_D0; | ||
692 | pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr); | ||
693 | |||
694 | ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); | ||
695 | |||
696 | /* | ||
697 | * Send 0 IPC message so that ISH FW wakes up if it was already | ||
698 | * asleep | ||
699 | */ | ||
700 | ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); | ||
701 | |||
702 | /* Flush writes to doorbell and REMAP2 */ | ||
703 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
704 | |||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | /** | ||
709 | * _ish_ipc_reset() - IPC reset | ||
710 | * @dev: ishtp device pointer | ||
711 | * | ||
712 | * Resets host and fw IPC and upper layers | ||
713 | * | ||
714 | * Return: 0 for success else error fault code | ||
715 | */ | ||
716 | static int _ish_ipc_reset(struct ishtp_device *dev) | ||
717 | { | ||
718 | struct ipc_rst_payload_type ipc_mng_msg; | ||
719 | int rv = 0; | ||
720 | |||
721 | ipc_mng_msg.reset_id = 1; | ||
722 | ipc_mng_msg.reserved = 0; | ||
723 | |||
724 | set_host_ready(dev); | ||
725 | |||
726 | /* Clear the incoming doorbell */ | ||
727 | ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0); | ||
728 | /* Flush write to doorbell */ | ||
729 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
730 | |||
731 | dev->recvd_hw_ready = 0; | ||
732 | |||
733 | /* send message */ | ||
734 | rv = ipc_send_mng_msg(dev, MNG_RESET_NOTIFY, &ipc_mng_msg, | ||
735 | sizeof(struct ipc_rst_payload_type)); | ||
736 | if (rv) { | ||
737 | dev_err(dev->devc, "Failed to send IPC MNG_RESET_NOTIFY\n"); | ||
738 | return rv; | ||
739 | } | ||
740 | |||
741 | wait_event_interruptible_timeout(dev->wait_hw_ready, | ||
742 | dev->recvd_hw_ready, 2 * HZ); | ||
743 | if (!dev->recvd_hw_ready) { | ||
744 | dev_err(dev->devc, "Timed out waiting for HW ready\n"); | ||
745 | rv = -ENODEV; | ||
746 | } | ||
747 | |||
748 | return rv; | ||
749 | } | ||
750 | |||
751 | /** | ||
752 | * ish_hw_start() -Start ISH HW | ||
753 | * @dev: ishtp device pointer | ||
754 | * | ||
755 | * Set host to ready state and wait for FW reset | ||
756 | * | ||
757 | * Return: 0 for success else error fault code | ||
758 | */ | ||
759 | int ish_hw_start(struct ishtp_device *dev) | ||
760 | { | ||
761 | ish_set_host_rdy(dev); | ||
762 | /* After that we can enable ISH DMA operation */ | ||
763 | ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED); | ||
764 | |||
765 | /* | ||
766 | * Send 0 IPC message so that ISH FW wakes up if it was already | ||
767 | * asleep | ||
768 | */ | ||
769 | ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT); | ||
770 | /* Flush write to doorbell */ | ||
771 | ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); | ||
772 | |||
773 | set_host_ready(dev); | ||
774 | |||
775 | /* wait for FW-initiated reset flow */ | ||
776 | if (!dev->recvd_hw_ready) | ||
777 | wait_event_interruptible_timeout(dev->wait_hw_ready, | ||
778 | dev->recvd_hw_ready, | ||
779 | 10 * HZ); | ||
780 | |||
781 | if (!dev->recvd_hw_ready) { | ||
782 | dev_err(dev->devc, | ||
783 | "[ishtp-ish]: Timed out waiting for FW-initiated reset\n"); | ||
784 | return -ENODEV; | ||
785 | } | ||
786 | |||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | /** | ||
791 | * ish_ipc_get_header() -Get doorbell value | ||
792 | * @dev: ishtp device pointer | ||
793 | * @length: length of message | ||
794 | * @busy: busy status | ||
795 | * | ||
796 | * Get door bell value from message header | ||
797 | * | ||
798 | * Return: door bell value | ||
799 | */ | ||
800 | static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length, | ||
801 | int busy) | ||
802 | { | ||
803 | uint32_t drbl_val; | ||
804 | |||
805 | drbl_val = IPC_BUILD_HEADER(length, IPC_PROTOCOL_ISHTP, busy); | ||
806 | |||
807 | return drbl_val; | ||
808 | } | ||
809 | |||
810 | static const struct ishtp_hw_ops ish_hw_ops = { | ||
811 | .hw_reset = _ish_hw_reset, | ||
812 | .ipc_reset = _ish_ipc_reset, | ||
813 | .ipc_get_header = ish_ipc_get_header, | ||
814 | .ishtp_read = _ishtp_read, | ||
815 | .write = write_ipc_to_queue, | ||
816 | .get_fw_status = _ish_read_fw_sts_reg, | ||
817 | .sync_fw_clock = _ish_sync_fw_clock, | ||
818 | .ishtp_read_hdr = _ishtp_read_hdr | ||
819 | }; | ||
820 | |||
821 | /** | ||
822 | * ish_dev_init() -Initialize ISH devoce | ||
823 | * @pdev: PCI device | ||
824 | * | ||
825 | * Allocate ISHTP device and initialize IPC processing | ||
826 | * | ||
827 | * Return: ISHTP device instance on success else NULL | ||
828 | */ | ||
829 | struct ishtp_device *ish_dev_init(struct pci_dev *pdev) | ||
830 | { | ||
831 | struct ishtp_device *dev; | ||
832 | int i; | ||
833 | |||
834 | dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw), | ||
835 | GFP_KERNEL); | ||
836 | if (!dev) | ||
837 | return NULL; | ||
838 | |||
839 | ishtp_device_init(dev); | ||
840 | |||
841 | init_waitqueue_head(&dev->wait_hw_ready); | ||
842 | |||
843 | spin_lock_init(&dev->wr_processing_spinlock); | ||
844 | spin_lock_init(&dev->out_ipc_spinlock); | ||
845 | |||
846 | /* Init IPC processing and free lists */ | ||
847 | INIT_LIST_HEAD(&dev->wr_processing_list_head.link); | ||
848 | INIT_LIST_HEAD(&dev->wr_free_list_head.link); | ||
849 | for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) { | ||
850 | struct wr_msg_ctl_info *tx_buf; | ||
851 | |||
852 | tx_buf = kzalloc(sizeof(struct wr_msg_ctl_info), GFP_KERNEL); | ||
853 | if (!tx_buf) { | ||
854 | /* | ||
855 | * IPC buffers may be limited or not available | ||
856 | * at all - although this shouldn't happen | ||
857 | */ | ||
858 | dev_err(dev->devc, | ||
859 | "[ishtp-ish]: failure in Tx FIFO allocations (%d)\n", | ||
860 | i); | ||
861 | break; | ||
862 | } | ||
863 | list_add_tail(&tx_buf->link, &dev->wr_free_list_head.link); | ||
864 | } | ||
865 | |||
866 | dev->ops = &ish_hw_ops; | ||
867 | dev->devc = &pdev->dev; | ||
868 | dev->mtu = IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr); | ||
869 | return dev; | ||
870 | } | ||
871 | |||
872 | /** | ||
873 | * ish_device_disable() - Disable ISH device | ||
874 | * @dev: ISHTP device pointer | ||
875 | * | ||
876 | * Disable ISH by clearing host ready to inform firmware. | ||
877 | */ | ||
878 | void ish_device_disable(struct ishtp_device *dev) | ||
879 | { | ||
880 | dev->dev_state = ISHTP_DEV_DISABLED; | ||
881 | ish_clr_host_rdy(dev); | ||
882 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c new file mode 100644 index 000000000000..b14d6f4774b9 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c | |||
@@ -0,0 +1,333 @@ | |||
1 | /* | ||
2 | * PCI glue for ISHTP provider device (ISH) driver | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/workqueue.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #define CREATE_TRACE_POINTS | ||
29 | #include <trace/events/intel_ish.h> | ||
30 | #include "ishtp-dev.h" | ||
31 | #include "hw-ish.h" | ||
32 | |||
33 | static const struct pci_device_id ish_pci_tbl[] = { | ||
34 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)}, | ||
35 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)}, | ||
36 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, | ||
37 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, | ||
38 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, | ||
39 | {0, } | ||
40 | }; | ||
41 | MODULE_DEVICE_TABLE(pci, ish_pci_tbl); | ||
42 | |||
43 | /** | ||
44 | * ish_event_tracer() - Callback function to dump trace messages | ||
45 | * @dev: ishtp device | ||
46 | * @format: printf style format | ||
47 | * | ||
48 | * Callback to direct log messages to Linux trace buffers | ||
49 | */ | ||
50 | static void ish_event_tracer(struct ishtp_device *dev, char *format, ...) | ||
51 | { | ||
52 | if (trace_ishtp_dump_enabled()) { | ||
53 | va_list args; | ||
54 | char tmp_buf[100]; | ||
55 | |||
56 | va_start(args, format); | ||
57 | vsnprintf(tmp_buf, sizeof(tmp_buf), format, args); | ||
58 | va_end(args); | ||
59 | |||
60 | trace_ishtp_dump(tmp_buf); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * ish_init() - Init function | ||
66 | * @dev: ishtp device | ||
67 | * | ||
68 | * This function initialize wait queues for suspend/resume and call | ||
69 | * calls hadware initialization function. This will initiate | ||
70 | * startup sequence | ||
71 | * | ||
72 | * Return: 0 for success or error code for failure | ||
73 | */ | ||
74 | static int ish_init(struct ishtp_device *dev) | ||
75 | { | ||
76 | int ret; | ||
77 | |||
78 | /* Set the state of ISH HW to start */ | ||
79 | ret = ish_hw_start(dev); | ||
80 | if (ret) { | ||
81 | dev_err(dev->devc, "ISH: hw start failed.\n"); | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | /* Start the inter process communication to ISH processor */ | ||
86 | ret = ishtp_start(dev); | ||
87 | if (ret) { | ||
88 | dev_err(dev->devc, "ISHTP: Protocol init failed.\n"); | ||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * ish_probe() - PCI driver probe callback | ||
97 | * @pdev: pci device | ||
98 | * @ent: pci device id | ||
99 | * | ||
100 | * Initialize PCI function, setup interrupt and call for ISH initialization | ||
101 | * | ||
102 | * Return: 0 for success or error code for failure | ||
103 | */ | ||
104 | static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
105 | { | ||
106 | struct ishtp_device *dev; | ||
107 | struct ish_hw *hw; | ||
108 | int ret; | ||
109 | |||
110 | /* enable pci dev */ | ||
111 | ret = pci_enable_device(pdev); | ||
112 | if (ret) { | ||
113 | dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n"); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | /* set PCI host mastering */ | ||
118 | pci_set_master(pdev); | ||
119 | |||
120 | /* pci request regions for ISH driver */ | ||
121 | ret = pci_request_regions(pdev, KBUILD_MODNAME); | ||
122 | if (ret) { | ||
123 | dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n"); | ||
124 | goto disable_device; | ||
125 | } | ||
126 | |||
127 | /* allocates and initializes the ISH dev structure */ | ||
128 | dev = ish_dev_init(pdev); | ||
129 | if (!dev) { | ||
130 | ret = -ENOMEM; | ||
131 | goto release_regions; | ||
132 | } | ||
133 | hw = to_ish_hw(dev); | ||
134 | dev->print_log = ish_event_tracer; | ||
135 | |||
136 | /* mapping IO device memory */ | ||
137 | hw->mem_addr = pci_iomap(pdev, 0, 0); | ||
138 | if (!hw->mem_addr) { | ||
139 | dev_err(&pdev->dev, "ISH: mapping I/O range failure\n"); | ||
140 | ret = -ENOMEM; | ||
141 | goto free_device; | ||
142 | } | ||
143 | |||
144 | dev->pdev = pdev; | ||
145 | |||
146 | pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; | ||
147 | |||
148 | /* request and enable interrupt */ | ||
149 | ret = request_irq(pdev->irq, ish_irq_handler, IRQF_NO_SUSPEND, | ||
150 | KBUILD_MODNAME, dev); | ||
151 | if (ret) { | ||
152 | dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n", | ||
153 | pdev->irq); | ||
154 | goto free_device; | ||
155 | } | ||
156 | |||
157 | dev_set_drvdata(dev->devc, dev); | ||
158 | |||
159 | init_waitqueue_head(&dev->suspend_wait); | ||
160 | init_waitqueue_head(&dev->resume_wait); | ||
161 | |||
162 | ret = ish_init(dev); | ||
163 | if (ret) | ||
164 | goto free_irq; | ||
165 | |||
166 | return 0; | ||
167 | |||
168 | free_irq: | ||
169 | free_irq(pdev->irq, dev); | ||
170 | free_device: | ||
171 | pci_iounmap(pdev, hw->mem_addr); | ||
172 | kfree(dev); | ||
173 | release_regions: | ||
174 | pci_release_regions(pdev); | ||
175 | disable_device: | ||
176 | pci_clear_master(pdev); | ||
177 | pci_disable_device(pdev); | ||
178 | dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n"); | ||
179 | |||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * ish_remove() - PCI driver remove callback | ||
185 | * @pdev: pci device | ||
186 | * | ||
187 | * This function does cleanup of ISH on pci remove callback | ||
188 | */ | ||
189 | static void ish_remove(struct pci_dev *pdev) | ||
190 | { | ||
191 | struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev); | ||
192 | struct ish_hw *hw = to_ish_hw(ishtp_dev); | ||
193 | |||
194 | ishtp_bus_remove_all_clients(ishtp_dev, false); | ||
195 | ish_device_disable(ishtp_dev); | ||
196 | |||
197 | free_irq(pdev->irq, ishtp_dev); | ||
198 | pci_iounmap(pdev, hw->mem_addr); | ||
199 | pci_release_regions(pdev); | ||
200 | pci_clear_master(pdev); | ||
201 | pci_disable_device(pdev); | ||
202 | kfree(ishtp_dev); | ||
203 | } | ||
204 | |||
205 | static struct device *ish_resume_device; | ||
206 | |||
207 | /** | ||
208 | * ish_resume_handler() - Work function to complete resume | ||
209 | * @work: work struct | ||
210 | * | ||
211 | * The resume work function to complete resume function asynchronously. | ||
212 | * There are two types of platforms, one where ISH is not powered off, | ||
213 | * in that case a simple resume message is enough, others we need | ||
214 | * a reset sequence. | ||
215 | */ | ||
216 | static void ish_resume_handler(struct work_struct *work) | ||
217 | { | ||
218 | struct pci_dev *pdev = to_pci_dev(ish_resume_device); | ||
219 | struct ishtp_device *dev = pci_get_drvdata(pdev); | ||
220 | int ret; | ||
221 | |||
222 | ishtp_send_resume(dev); | ||
223 | |||
224 | /* 50 ms to get resume response */ | ||
225 | if (dev->resume_flag) | ||
226 | ret = wait_event_interruptible_timeout(dev->resume_wait, | ||
227 | !dev->resume_flag, | ||
228 | msecs_to_jiffies(50)); | ||
229 | |||
230 | /* | ||
231 | * If no resume response. This platform is not S0ix compatible | ||
232 | * So on resume full reboot of ISH processor will happen, so | ||
233 | * need to go through init sequence again | ||
234 | */ | ||
235 | if (dev->resume_flag) | ||
236 | ish_init(dev); | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * ish_suspend() - ISH suspend callback | ||
241 | * @device: device pointer | ||
242 | * | ||
243 | * ISH suspend callback | ||
244 | * | ||
245 | * Return: 0 to the pm core | ||
246 | */ | ||
247 | static int ish_suspend(struct device *device) | ||
248 | { | ||
249 | struct pci_dev *pdev = to_pci_dev(device); | ||
250 | struct ishtp_device *dev = pci_get_drvdata(pdev); | ||
251 | |||
252 | enable_irq_wake(pdev->irq); | ||
253 | /* | ||
254 | * If previous suspend hasn't been asnwered then ISH is likely dead, | ||
255 | * don't attempt nested notification | ||
256 | */ | ||
257 | if (dev->suspend_flag) | ||
258 | return 0; | ||
259 | |||
260 | dev->resume_flag = 0; | ||
261 | dev->suspend_flag = 1; | ||
262 | ishtp_send_suspend(dev); | ||
263 | |||
264 | /* 25 ms should be enough for live ISH to flush all IPC buf */ | ||
265 | if (dev->suspend_flag) | ||
266 | wait_event_interruptible_timeout(dev->suspend_wait, | ||
267 | !dev->suspend_flag, | ||
268 | msecs_to_jiffies(25)); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static DECLARE_WORK(resume_work, ish_resume_handler); | ||
274 | /** | ||
275 | * ish_resume() - ISH resume callback | ||
276 | * @device: device pointer | ||
277 | * | ||
278 | * ISH resume callback | ||
279 | * | ||
280 | * Return: 0 to the pm core | ||
281 | */ | ||
282 | static int ish_resume(struct device *device) | ||
283 | { | ||
284 | struct pci_dev *pdev = to_pci_dev(device); | ||
285 | struct ishtp_device *dev = pci_get_drvdata(pdev); | ||
286 | |||
287 | ish_resume_device = device; | ||
288 | dev->resume_flag = 1; | ||
289 | |||
290 | disable_irq_wake(pdev->irq); | ||
291 | schedule_work(&resume_work); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | #ifdef CONFIG_PM | ||
297 | static const struct dev_pm_ops ish_pm_ops = { | ||
298 | .suspend = ish_suspend, | ||
299 | .resume = ish_resume, | ||
300 | }; | ||
301 | #define ISHTP_ISH_PM_OPS (&ish_pm_ops) | ||
302 | #else | ||
303 | #define ISHTP_ISH_PM_OPS NULL | ||
304 | #endif | ||
305 | |||
306 | static struct pci_driver ish_driver = { | ||
307 | .name = KBUILD_MODNAME, | ||
308 | .id_table = ish_pci_tbl, | ||
309 | .probe = ish_probe, | ||
310 | .remove = ish_remove, | ||
311 | .driver.pm = ISHTP_ISH_PM_OPS, | ||
312 | }; | ||
313 | |||
314 | static int __init ish_driver_init(void) | ||
315 | { | ||
316 | return pci_register_driver(&ish_driver); | ||
317 | } | ||
318 | |||
319 | static void __exit ish_driver_exit(void) | ||
320 | { | ||
321 | pci_unregister_driver(&ish_driver); | ||
322 | } | ||
323 | |||
324 | module_init(ish_driver_init); | ||
325 | module_exit(ish_driver_exit); | ||
326 | |||
327 | /* Original author */ | ||
328 | MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>"); | ||
329 | /* Adoption to upstream Linux kernel */ | ||
330 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
331 | |||
332 | MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver"); | ||
333 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/intel-ish-hid/ipc/utils.h b/drivers/hid/intel-ish-hid/ipc/utils.h new file mode 100644 index 000000000000..5a82123dc7b4 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ipc/utils.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * Utility macros of ISH | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | #ifndef UTILS__H | ||
16 | #define UTILS__H | ||
17 | |||
18 | #define WAIT_FOR_SEND_SLICE (HZ / 10) | ||
19 | #define WAIT_FOR_CONNECT_SLICE (HZ / 10) | ||
20 | |||
21 | /* | ||
22 | * Waits for specified event when a thread that triggers event can't signal | ||
23 | * Also, waits *at_least* `timeinc` after condition is satisfied | ||
24 | */ | ||
25 | #define timed_wait_for(timeinc, condition) \ | ||
26 | do { \ | ||
27 | int completed = 0; \ | ||
28 | do { \ | ||
29 | unsigned long j; \ | ||
30 | int done = 0; \ | ||
31 | \ | ||
32 | completed = (condition); \ | ||
33 | for (j = jiffies, done = 0; !done; ) { \ | ||
34 | schedule_timeout(timeinc); \ | ||
35 | if (time_is_before_eq_jiffies(j + timeinc)) \ | ||
36 | done = 1; \ | ||
37 | } \ | ||
38 | } while (!(completed)); \ | ||
39 | } while (0) | ||
40 | |||
41 | |||
42 | /* | ||
43 | * Waits for specified event when a thread that triggers event | ||
44 | * can't signal with timeout (use whenever we may hang) | ||
45 | */ | ||
46 | #define timed_wait_for_timeout(timeinc, condition, timeout) \ | ||
47 | do { \ | ||
48 | int t = timeout; \ | ||
49 | do { \ | ||
50 | unsigned long j; \ | ||
51 | int done = 0; \ | ||
52 | \ | ||
53 | for (j = jiffies, done = 0; !done; ) { \ | ||
54 | schedule_timeout(timeinc); \ | ||
55 | if (time_is_before_eq_jiffies(j + timeinc)) \ | ||
56 | done = 1; \ | ||
57 | } \ | ||
58 | t -= timeinc; \ | ||
59 | if (t <= 0) \ | ||
60 | break; \ | ||
61 | } while (!(condition)); \ | ||
62 | } while (0) | ||
63 | |||
64 | #endif /* UTILS__H */ | ||