aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiam Girdwood <liam.r.girdwood@linux.intel.com>2014-02-20 16:48:42 -0500
committerMark Brown <broonie@linaro.org>2014-02-21 21:22:21 -0500
commit1e42c3e426b3f7bc61ba338dd6507a293108117c (patch)
tree88f1c28ea170654c0a85afef81889e280827fe0b
parentafd954900af84bf66a0dd72b2648a2c15fb68f25 (diff)
ASoC: Intel: Add support for Haswell/Broadwell DSP
Add support for low level differentiation functions for Haswell and Broadwell SST DSPs. This includes suppoprt for DSP boot and reset, DSP firmware module parsing and DSP memory block map initialisation. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--sound/soc/intel/sst-dsp.c60
-rw-r--r--sound/soc/intel/sst-dsp.h10
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c517
3 files changed, 587 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c
index 6e22c1201959..0c129fd85ecf 100644
--- a/sound/soc/intel/sst-dsp.c
+++ b/sound/soc/intel/sst-dsp.c
@@ -27,6 +27,66 @@
27#define CREATE_TRACE_POINTS 27#define CREATE_TRACE_POINTS
28#include <trace/events/intel-sst.h> 28#include <trace/events/intel-sst.h>
29 29
30/* Internal generic low-level SST IO functions - can be overidden */
31void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
32{
33 writel(value, addr + offset);
34}
35EXPORT_SYMBOL_GPL(sst_shim32_write);
36
37u32 sst_shim32_read(void __iomem *addr, u32 offset)
38{
39 return readl(addr + offset);
40}
41EXPORT_SYMBOL_GPL(sst_shim32_read);
42
43void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
44{
45 memcpy_toio(addr + offset, &value, sizeof(value));
46}
47EXPORT_SYMBOL_GPL(sst_shim32_write64);
48
49u64 sst_shim32_read64(void __iomem *addr, u32 offset)
50{
51 u64 val;
52
53 memcpy_fromio(&val, addr + offset, sizeof(val));
54 return val;
55}
56EXPORT_SYMBOL_GPL(sst_shim32_read64);
57
58static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
59 u32 *src, size_t bytes)
60{
61 int i, words = bytes >> 2;
62
63 for (i = 0; i < words; i++)
64 writel(src[i], dest + i);
65}
66
67static inline void _sst_memcpy_fromio_32(u32 *dest,
68 const volatile __iomem u32 *src, size_t bytes)
69{
70 int i, words = bytes >> 2;
71
72 for (i = 0; i < words; i++)
73 dest[i] = readl(src + i);
74}
75
76void sst_memcpy_toio_32(struct sst_dsp *sst,
77 void __iomem *dest, void *src, size_t bytes)
78{
79 _sst_memcpy_toio_32(dest, src, bytes);
80}
81EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
82
83void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
84 void __iomem *src, size_t bytes)
85{
86 _sst_memcpy_fromio_32(dest, src, bytes);
87}
88EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
89
30/* Public API */ 90/* Public API */
31void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) 91void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
32{ 92{
diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h
index 3730fd324455..608418c1181a 100644
--- a/sound/soc/intel/sst-dsp.h
+++ b/sound/soc/intel/sst-dsp.h
@@ -189,6 +189,16 @@ u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset);
189int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, 189int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
190 u64 mask, u64 value); 190 u64 mask, u64 value);
191 191
192/* Internal generic low-level SST IO functions - can be overidden */
193void sst_shim32_write(void __iomem *addr, u32 offset, u32 value);
194u32 sst_shim32_read(void __iomem *addr, u32 offset);
195void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value);
196u64 sst_shim32_read64(void __iomem *addr, u32 offset);
197void sst_memcpy_toio_32(struct sst_dsp *sst,
198 void __iomem *dest, void *src, size_t bytes);
199void sst_memcpy_fromio_32(struct sst_dsp *sst,
200 void *dest, void __iomem *src, size_t bytes);
201
192/* DSP reset & boot */ 202/* DSP reset & boot */
193void sst_dsp_reset(struct sst_dsp *sst); 203void sst_dsp_reset(struct sst_dsp *sst);
194int sst_dsp_boot(struct sst_dsp *sst); 204int sst_dsp_boot(struct sst_dsp *sst);
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
new file mode 100644
index 000000000000..12f73173a792
--- /dev/null
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -0,0 +1,517 @@
1/*
2 * Intel Haswell SST DSP driver
3 *
4 * Copyright (C) 2013, Intel Corporation. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/delay.h>
18#include <linux/fs.h>
19#include <linux/slab.h>
20#include <linux/device.h>
21#include <linux/sched.h>
22#include <linux/export.h>
23#include <linux/interrupt.h>
24#include <linux/module.h>
25#include <linux/dma-mapping.h>
26#include <linux/platform_device.h>
27#include <linux/pci.h>
28#include <linux/firmware.h>
29#include <linux/pm_runtime.h>
30
31#include <linux/acpi.h>
32#include <acpi/acpi_bus.h>
33
34#include "sst-dsp.h"
35#include "sst-dsp-priv.h"
36#include "sst-haswell-ipc.h"
37
38#include <trace/events/hswadsp.h>
39
40#define SST_HSW_FW_SIGNATURE_SIZE 4
41#define SST_HSW_FW_SIGN "$SST"
42#define SST_HSW_FW_LIB_SIGN "$LIB"
43
44#define SST_WPT_SHIM_OFFSET 0xFB000
45#define SST_LP_SHIM_OFFSET 0xE7000
46#define SST_WPT_IRAM_OFFSET 0xA0000
47#define SST_LP_IRAM_OFFSET 0x80000
48
49#define SST_SHIM_PM_REG 0x84
50
51#define SST_HSW_IRAM 1
52#define SST_HSW_DRAM 2
53#define SST_HSW_REGS 3
54
55struct dma_block_info {
56 __le32 type; /* IRAM/DRAM */
57 __le32 size; /* Bytes */
58 __le32 ram_offset; /* Offset in I/DRAM */
59 __le32 rsvd; /* Reserved field */
60} __attribute__((packed));
61
62struct fw_module_info {
63 __le32 persistent_size;
64 __le32 scratch_size;
65} __attribute__((packed));
66
67struct fw_header {
68 unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */
69 __le32 file_size; /* size of fw minus this header */
70 __le32 modules; /* # of modules */
71 __le32 file_format; /* version of header format */
72 __le32 reserved[4];
73} __attribute__((packed));
74
75struct fw_module_header {
76 unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */
77 __le32 mod_size; /* size of module */
78 __le32 blocks; /* # of blocks */
79 __le16 padding;
80 __le16 type; /* codec type, pp lib */
81 __le32 entry_point;
82 struct fw_module_info info;
83} __attribute__((packed));
84
85static void hsw_free(struct sst_dsp *sst);
86
87static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
88 struct fw_module_header *module)
89{
90 struct dma_block_info *block;
91 struct sst_module *mod;
92 struct sst_module_data block_data;
93 struct sst_module_template template;
94 int count;
95 void __iomem *ram;
96
97 /* TODO: allowed module types need to be configurable */
98 if (module->type != SST_HSW_MODULE_BASE_FW
99 && module->type != SST_HSW_MODULE_PCM_SYSTEM
100 && module->type != SST_HSW_MODULE_PCM
101 && module->type != SST_HSW_MODULE_PCM_REFERENCE
102 && module->type != SST_HSW_MODULE_PCM_CAPTURE
103 && module->type != SST_HSW_MODULE_LPAL)
104 return 0;
105
106 dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n",
107 module->signature, module->mod_size,
108 module->blocks, module->type);
109 dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point);
110 dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n",
111 module->info.persistent_size, module->info.scratch_size);
112
113 memset(&template, 0, sizeof(template));
114 template.id = module->type;
115 template.entry = module->entry_point;
116 template.p.size = module->info.persistent_size;
117 template.p.type = SST_MEM_DRAM;
118 template.p.data_type = SST_DATA_P;
119 template.s.size = module->info.scratch_size;
120 template.s.type = SST_MEM_DRAM;
121 template.s.data_type = SST_DATA_S;
122
123 mod = sst_module_new(fw, &template, NULL);
124 if (mod == NULL)
125 return -ENOMEM;
126
127 block = (void *)module + sizeof(*module);
128
129 for (count = 0; count < module->blocks; count++) {
130
131 if (block->size <= 0) {
132 dev_err(dsp->dev,
133 "error: block %d size invalid\n", count);
134 sst_module_free(mod);
135 return -EINVAL;
136 }
137
138 switch (block->type) {
139 case SST_HSW_IRAM:
140 ram = dsp->addr.lpe;
141 block_data.offset =
142 block->ram_offset + dsp->addr.iram_offset;
143 block_data.type = SST_MEM_IRAM;
144 break;
145 case SST_HSW_DRAM:
146 ram = dsp->addr.lpe;
147 block_data.offset = block->ram_offset;
148 block_data.type = SST_MEM_DRAM;
149 break;
150 default:
151 dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
152 block->type, count);
153 sst_module_free(mod);
154 return -EINVAL;
155 }
156
157 block_data.size = block->size;
158 block_data.data_type = SST_DATA_M;
159 block_data.data = (void *)block + sizeof(*block);
160 block_data.data_offset = block_data.data - fw->dma_buf;
161
162 dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
163 "size 0x%x ==> ram %p offset 0x%x\n",
164 count, block->type, block->size, ram,
165 block->ram_offset);
166
167 sst_module_insert_fixed_block(mod, &block_data);
168
169 block = (void *)block + sizeof(*block) + block->size;
170 }
171 return 0;
172}
173
174static int hsw_parse_fw_image(struct sst_fw *sst_fw)
175{
176 struct fw_header *header;
177 struct sst_module *scratch;
178 struct fw_module_header *module;
179 struct sst_dsp *dsp = sst_fw->dsp;
180 struct sst_hsw *hsw = sst_fw->private;
181 int ret, count;
182
183 /* Read the header information from the data pointer */
184 header = (struct fw_header *)sst_fw->dma_buf;
185
186 /* verify FW */
187 if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) ||
188 (sst_fw->size != header->file_size + sizeof(*header))) {
189 dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n");
190 return -EINVAL;
191 }
192
193 dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
194 header->file_size, header->modules,
195 header->file_format, sizeof(*header));
196
197 /* parse each module */
198 module = (void *)sst_fw->dma_buf + sizeof(*header);
199 for (count = 0; count < header->modules; count++) {
200
201 /* module */
202 ret = hsw_parse_module(dsp, sst_fw, module);
203 if (ret < 0) {
204 dev_err(dsp->dev, "error: invalid module %d\n", count);
205 return ret;
206 }
207 module = (void *)module + sizeof(*module) + module->mod_size;
208 }
209
210 /* allocate persistent/scratch mem regions */
211 scratch = sst_mem_block_alloc_scratch(dsp);
212 if (scratch == NULL)
213 return -ENOMEM;
214
215 sst_hsw_set_scratch_module(hsw, scratch);
216
217 return 0;
218}
219
220static irqreturn_t hsw_irq(int irq, void *context)
221{
222 struct sst_dsp *sst = (struct sst_dsp *) context;
223 u32 isr;
224 int ret = IRQ_NONE;
225
226 spin_lock(&sst->spinlock);
227
228 /* Interrupt arrived, check src */
229 isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
230 if (isr & SST_ISRX_DONE) {
231 trace_sst_irq_done(isr,
232 sst_dsp_shim_read_unlocked(sst, SST_IMRX));
233
234 /* Mask Done interrupt before return */
235 sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
236 SST_IMRX_DONE, SST_IMRX_DONE);
237 ret = IRQ_WAKE_THREAD;
238 }
239
240 if (isr & SST_ISRX_BUSY) {
241 trace_sst_irq_busy(isr,
242 sst_dsp_shim_read_unlocked(sst, SST_IMRX));
243
244 /* Mask Busy interrupt before return */
245 sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
246 SST_IMRX_BUSY, SST_IMRX_BUSY);
247 ret = IRQ_WAKE_THREAD;
248 }
249
250 spin_unlock(&sst->spinlock);
251 return ret;
252}
253
254static void hsw_boot(struct sst_dsp *sst)
255{
256 /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
257 sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
258 SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
259
260 /* stall DSP core, set clk to 192/96Mhz */
261 sst_dsp_shim_update_bits_unlocked(sst,
262 SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
263 SST_CSR_STALL | SST_CSR_DCS(4));
264
265 /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
266 sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
267 SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
268 SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
269
270 /* disable DMA finish function for SSP0 & SSP1 */
271 sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
272 SST_CSR2_SDFD_SSP1);
273
274 /* enable DMA engine 0,1 all channels to access host memory */
275 sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC,
276 SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff),
277 SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff));
278
279 /* disable all clock gating */
280 writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
281
282 /* set DSP to RUN */
283 sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
284}
285
286static void hsw_reset(struct sst_dsp *sst)
287{
288 /* put DSP into reset and stall */
289 sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
290 SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
291
292 /* keep in reset for 10ms */
293 mdelay(10);
294
295 /* take DSP out of reset and keep stalled for FW loading */
296 sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
297 SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
298}
299
300struct sst_adsp_memregion {
301 u32 start;
302 u32 end;
303 int blocks;
304 enum sst_mem_type type;
305};
306
307/* lynx point ADSP mem regions */
308static const struct sst_adsp_memregion lp_region[] = {
309 {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
310 {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
311 {0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */
312};
313
314/* wild cat point ADSP mem regions */
315static const struct sst_adsp_memregion wpt_region[] = {
316 {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
317 {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
318 {0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */
319 {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
320};
321
322static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
323{
324 /* ADSP DRAM & IRAM */
325 sst->addr.lpe_base = pdata->lpe_base;
326 sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
327 if (!sst->addr.lpe)
328 return -ENODEV;
329
330 /* ADSP PCI MMIO config space */
331 sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
332 if (!sst->addr.pci_cfg) {
333 iounmap(sst->addr.lpe);
334 return -ENODEV;
335 }
336
337 /* SST Shim */
338 sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
339 return 0;
340}
341
342static u32 hsw_block_get_bit(struct sst_mem_block *block)
343{
344 u32 bit = 0, shift = 0;
345
346 switch (block->type) {
347 case SST_MEM_DRAM:
348 shift = 16;
349 break;
350 case SST_MEM_IRAM:
351 shift = 6;
352 break;
353 default:
354 return 0;
355 }
356
357 bit = 1 << (block->index + shift);
358
359 return bit;
360}
361
362/* enable 32kB memory block - locks held by caller */
363static int hsw_block_enable(struct sst_mem_block *block)
364{
365 struct sst_dsp *sst = block->dsp;
366 u32 bit, val;
367
368 if (block->users++ > 0)
369 return 0;
370
371 dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
372 block->type, block->index, block->offset);
373
374 val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
375 bit = hsw_block_get_bit(block);
376 writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
377
378 /* wait 18 DSP clock ticks */
379 udelay(10);
380
381 return 0;
382}
383
384/* disable 32kB memory block - locks held by caller */
385static int hsw_block_disable(struct sst_mem_block *block)
386{
387 struct sst_dsp *sst = block->dsp;
388 u32 bit, val;
389
390 if (--block->users > 0)
391 return 0;
392
393 dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
394 block->type, block->index, block->offset);
395
396 val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
397 bit = hsw_block_get_bit(block);
398 writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
399
400 return 0;
401}
402
403static struct sst_block_ops sst_hsw_ops = {
404 .enable = hsw_block_enable,
405 .disable = hsw_block_disable,
406};
407
408static int hsw_enable_shim(struct sst_dsp *sst)
409{
410 int tries = 10;
411 u32 reg;
412
413 /* enable shim */
414 reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
415 writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
416
417 /* check that ADSP shim is enabled */
418 while (tries--) {
419 reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
420 if (reg != 0xffffffff)
421 return 0;
422
423 msleep(1);
424 }
425
426 return -ENODEV;
427}
428
429static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
430{
431 const struct sst_adsp_memregion *region;
432 struct device *dev;
433 int ret = -ENODEV, i, j, region_count;
434 u32 offset, size;
435
436 dev = sst->dev;
437
438 switch (sst->id) {
439 case SST_DEV_ID_LYNX_POINT:
440 region = lp_region;
441 region_count = ARRAY_SIZE(lp_region);
442 sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
443 sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
444 break;
445 case SST_DEV_ID_WILDCAT_POINT:
446 region = wpt_region;
447 region_count = ARRAY_SIZE(wpt_region);
448 sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
449 sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
450 break;
451 default:
452 dev_err(dev, "error: failed to get mem resources\n");
453 return ret;
454 }
455
456 ret = hsw_acpi_resource_map(sst, pdata);
457 if (ret < 0) {
458 dev_err(dev, "error: failed to map resources\n");
459 return ret;
460 }
461
462 /* enable the DSP SHIM */
463 ret = hsw_enable_shim(sst);
464 if (ret < 0) {
465 dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
466 return ret;
467 }
468
469 ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
470 if (ret)
471 return ret;
472
473 /* Enable Interrupt from both sides */
474 sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
475 sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
476 (0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
477
478 /* register DSP memory blocks - ideally we should get this from ACPI */
479 for (i = 0; i < region_count; i++) {
480 offset = region[i].start;
481 size = (region[i].end - region[i].start) / region[i].blocks;
482
483 /* register individual memory blocks */
484 for (j = 0; j < region[i].blocks; j++) {
485 sst_mem_block_register(sst, offset, size,
486 region[i].type, &sst_hsw_ops, j, sst);
487 offset += size;
488 }
489 }
490
491 /* set default power gating mask */
492 writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0);
493
494 return 0;
495}
496
497static void hsw_free(struct sst_dsp *sst)
498{
499 sst_mem_block_unregister_all(sst);
500 iounmap(sst->addr.lpe);
501 iounmap(sst->addr.pci_cfg);
502}
503
504struct sst_ops haswell_ops = {
505 .reset = hsw_reset,
506 .boot = hsw_boot,
507 .write = sst_shim32_write,
508 .read = sst_shim32_read,
509 .write64 = sst_shim32_write64,
510 .read64 = sst_shim32_read64,
511 .ram_read = sst_memcpy_fromio_32,
512 .ram_write = sst_memcpy_toio_32,
513 .irq_handler = hsw_irq,
514 .init = hsw_init,
515 .free = hsw_free,
516 .parse_fw = hsw_parse_fw_image,
517};