Intel MIC Host Driver Interrupt/SMPT support.
[deliverable/linux.git] / drivers / misc / mic / host / mic_x100.c
1 /*
2 * Intel MIC Platform Software Stack (MPSS)
3 *
4 * Copyright(c) 2013 Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * The full GNU General Public License is included in this distribution in
16 * the file called "COPYING".
17 *
18 * Intel MIC Host driver.
19 *
20 */
21 #include <linux/fs.h>
22 #include <linux/pci.h>
23
24 #include "../common/mic_device.h"
25 #include "mic_device.h"
26 #include "mic_x100.h"
27 #include "mic_smpt.h"
28
29 /**
30 * mic_x100_write_spad - write to the scratchpad register
31 * @mdev: pointer to mic_device instance
32 * @idx: index to the scratchpad register, 0 based
33 * @val: the data value to put into the register
34 *
35 * This function allows writing of a 32bit value to the indexed scratchpad
36 * register.
37 *
38 * RETURNS: none.
39 */
40 static void
41 mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val)
42 {
43 dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n",
44 val, idx);
45 mic_mmio_write(&mdev->mmio, val,
46 MIC_X100_SBOX_BASE_ADDRESS +
47 MIC_X100_SBOX_SPAD0 + idx * 4);
48 }
49
50 /**
51 * mic_x100_read_spad - read from the scratchpad register
52 * @mdev: pointer to mic_device instance
53 * @idx: index to scratchpad register, 0 based
54 *
55 * This function allows reading of the 32bit scratchpad register.
56 *
57 * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
58 */
59 static u32
60 mic_x100_read_spad(struct mic_device *mdev, unsigned int idx)
61 {
62 u32 val = mic_mmio_read(&mdev->mmio,
63 MIC_X100_SBOX_BASE_ADDRESS +
64 MIC_X100_SBOX_SPAD0 + idx * 4);
65
66 dev_dbg(mdev->sdev->parent,
67 "Reading 0x%x from scratch pad index %d\n", val, idx);
68 return val;
69 }
70
71 /**
72 * mic_x100_enable_interrupts - Enable interrupts.
73 * @mdev: pointer to mic_device instance
74 */
75 static void mic_x100_enable_interrupts(struct mic_device *mdev)
76 {
77 u32 reg;
78 struct mic_mw *mw = &mdev->mmio;
79 u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0;
80 u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0;
81
82 reg = mic_mmio_read(mw, sice0);
83 reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff);
84 mic_mmio_write(mw, reg, sice0);
85
86 /*
87 * Enable auto-clear when enabling interrupts. Applicable only for
88 * MSI-x. Legacy and MSI mode cannot have auto-clear enabled.
89 */
90 if (mdev->irq_info.num_vectors > 1) {
91 reg = mic_mmio_read(mw, siac0);
92 reg |= MIC_X100_SBOX_DBR_BITS(0xf) |
93 MIC_X100_SBOX_DMA_BITS(0xff);
94 mic_mmio_write(mw, reg, siac0);
95 }
96 }
97
98 /**
99 * mic_x100_disable_interrupts - Disable interrupts.
100 * @mdev: pointer to mic_device instance
101 */
102 static void mic_x100_disable_interrupts(struct mic_device *mdev)
103 {
104 u32 reg;
105 struct mic_mw *mw = &mdev->mmio;
106 u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0;
107 u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0;
108 u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0;
109
110 reg = mic_mmio_read(mw, sice0);
111 mic_mmio_write(mw, reg, sicc0);
112
113 if (mdev->irq_info.num_vectors > 1) {
114 reg = mic_mmio_read(mw, siac0);
115 reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) |
116 MIC_X100_SBOX_DMA_BITS(0xff));
117 mic_mmio_write(mw, reg, siac0);
118 }
119 }
120
121 /**
122 * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC.
123 * @mdev: pointer to mic_device instance
124 */
125 static void mic_x100_send_sbox_intr(struct mic_device *mdev,
126 int doorbell)
127 {
128 struct mic_mw *mw = &mdev->mmio;
129 u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8;
130 u32 apicicr_low = mic_mmio_read(mw,
131 MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset);
132
133 /* for MIC we need to make sure we "hit" the send_icr bit (13) */
134 apicicr_low = (apicicr_low | (1 << 13));
135
136 /* Ensure that the interrupt is ordered w.r.t. previous stores. */
137 wmb();
138 mic_mmio_write(mw, apicicr_low,
139 MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset);
140 }
141
142 /**
143 * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC.
144 * @mdev: pointer to mic_device instance
145 */
146 static void mic_x100_send_rdmasr_intr(struct mic_device *mdev,
147 int doorbell)
148 {
149 int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2);
150 /* Ensure that the interrupt is ordered w.r.t. previous stores. */
151 wmb();
152 mic_mmio_write(&mdev->mmio, 0,
153 MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset);
154 }
155
156 /**
157 * __mic_x100_send_intr - Send interrupt to MIC.
158 * @mdev: pointer to mic_device instance
159 * @doorbell: doorbell number.
160 */
161 static void mic_x100_send_intr(struct mic_device *mdev, int doorbell)
162 {
163 int rdmasr_db;
164 if (doorbell < MIC_X100_NUM_SBOX_IRQ) {
165 mic_x100_send_sbox_intr(mdev, doorbell);
166 } else {
167 rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ +
168 MIC_X100_RDMASR_IRQ_BASE;
169 mic_x100_send_rdmasr_intr(mdev, rdmasr_db);
170 }
171 }
172
173 /**
174 * mic_ack_interrupt - Device specific interrupt handling.
175 * @mdev: pointer to mic_device instance
176 *
177 * Returns: bitmask of doorbell events triggered.
178 */
179 static u32 mic_x100_ack_interrupt(struct mic_device *mdev)
180 {
181 u32 reg = 0;
182 struct mic_mw *mw = &mdev->mmio;
183 u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0;
184
185 /* Clear pending bit array. */
186 if (MIC_A0_STEP == mdev->stepping)
187 mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS +
188 MIC_X100_SBOX_MSIXPBACR);
189
190 if (mdev->irq_info.num_vectors <= 1) {
191 reg = mic_mmio_read(mw, sicr0);
192
193 if (unlikely(!reg))
194 goto done;
195
196 mic_mmio_write(mw, reg, sicr0);
197 }
198
199 if (mdev->stepping >= MIC_B0_STEP)
200 mdev->intr_ops->enable_interrupts(mdev);
201 done:
202 return reg;
203 }
204
205 /**
206 * mic_x100_hw_intr_init - Initialize h/w specific interrupt
207 * information.
208 * @mdev: pointer to mic_device instance
209 */
210 static void mic_x100_hw_intr_init(struct mic_device *mdev)
211 {
212 mdev->intr_info = (struct mic_intr_info *) mic_x100_intr_init;
213 }
214
215 /**
216 * mic_x100_read_msi_to_src_map - read from the MSI mapping registers
217 * @mdev: pointer to mic_device instance
218 * @idx: index to the mapping register, 0 based
219 *
220 * This function allows reading of the 32bit MSI mapping register.
221 *
222 * RETURNS: The value in the register.
223 */
224 static u32
225 mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx)
226 {
227 return mic_mmio_read(&mdev->mmio,
228 MIC_X100_SBOX_BASE_ADDRESS +
229 MIC_X100_SBOX_MXAR0 + idx * 4);
230 }
231
232 /**
233 * mic_x100_program_msi_to_src_map - program the MSI mapping registers
234 * @mdev: pointer to mic_device instance
235 * @idx: index to the mapping register, 0 based
236 * @offset: The bit offset in the register that needs to be updated.
237 * @set: boolean specifying if the bit in the specified offset needs
238 * to be set or cleared.
239 *
240 * RETURNS: None.
241 */
242 static void
243 mic_x100_program_msi_to_src_map(struct mic_device *mdev,
244 int idx, int offset, bool set)
245 {
246 unsigned long reg;
247 struct mic_mw *mw = &mdev->mmio;
248 u32 mxar = MIC_X100_SBOX_BASE_ADDRESS +
249 MIC_X100_SBOX_MXAR0 + idx * 4;
250
251 reg = mic_mmio_read(mw, mxar);
252 if (set)
253 __set_bit(offset, &reg);
254 else
255 __clear_bit(offset, &reg);
256 mic_mmio_write(mw, reg, mxar);
257 }
258
259 /**
260 * mic_x100_smpt_set - Update an SMPT entry with a DMA address.
261 * @mdev: pointer to mic_device instance
262 *
263 * RETURNS: none.
264 */
265 static void
266 mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index)
267 {
268 #define SNOOP_ON (0 << 0)
269 #define SNOOP_OFF (1 << 0)
270 /*
271 * Sbox Smpt Reg Bits:
272 * Bits 31:2 Host address
273 * Bits 1 RSVD
274 * Bits 0 No snoop
275 */
276 #define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \
277 (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01))
278
279 uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON,
280 dma_addr >> mdev->smpt->info.page_shift);
281 mic_mmio_write(&mdev->mmio, smpt_reg_val,
282 MIC_X100_SBOX_BASE_ADDRESS +
283 MIC_X100_SBOX_SMPT00 + (4 * index));
284 }
285
286 /**
287 * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields.
288 * @mdev: pointer to mic_device instance
289 *
290 * RETURNS: none.
291 */
292 static void mic_x100_smpt_hw_init(struct mic_device *mdev)
293 {
294 struct mic_smpt_hw_info *info = &mdev->smpt->info;
295
296 info->num_reg = 32;
297 info->page_shift = 34;
298 info->page_size = (1ULL << info->page_shift);
299 info->base = 0x8000000000ULL;
300 }
301
302 struct mic_smpt_ops mic_x100_smpt_ops = {
303 .init = mic_x100_smpt_hw_init,
304 .set = mic_x100_smpt_set,
305 };
306
307 struct mic_hw_ops mic_x100_ops = {
308 .aper_bar = MIC_X100_APER_BAR,
309 .mmio_bar = MIC_X100_MMIO_BAR,
310 .read_spad = mic_x100_read_spad,
311 .write_spad = mic_x100_write_spad,
312 .send_intr = mic_x100_send_intr,
313 .ack_interrupt = mic_x100_ack_interrupt,
314 };
315
316 struct mic_hw_intr_ops mic_x100_intr_ops = {
317 .intr_init = mic_x100_hw_intr_init,
318 .enable_interrupts = mic_x100_enable_interrupts,
319 .disable_interrupts = mic_x100_disable_interrupts,
320 .program_msi_to_src_map = mic_x100_program_msi_to_src_map,
321 .read_msi_to_src_map = mic_x100_read_msi_to_src_map,
322 };
This page took 0.03936 seconds and 5 git commands to generate.