1#!/usr/bin/env python3
2#
3# Copyright (c) 2019 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import time
8import logging
9import array
10
11from lib.stream_desc import StreamDescList
12from lib.device import Device
13from lib.ipc import Ipc
14import lib.registers as regs_def
15import lib.platforms as plat_def
16
17
18class FirmwareStatus():
19    """ Data structure for Firmware Status register """
20
21    def __init__(self, value=None):
22        self.value = None
23        self.boot_state = None
24        self.wait_state = None
25        self.moudle = None
26        self.error = None
27
28        if value:
29            self.set(value)
30
31    def set(self, value):
32        self.value = value
33        self.boot_state = self.value & plat_def.FW_STATUS_BOOT_STATE
34        self.wait_state = ((self.value & plat_def.FW_STATUS_WAIT_STATE)
35                            >> plat_def.FW_STATUS_WAIT_STATE_OFFSET)
36        self.moudle = ((self.value & plat_def.FW_STATUS_MODULE)
37                        >> plat_def.FW_STATUS_MODULE_OFFSET)
38        self.error = ((self.value & plat_def.FW_STATUS_ERROR)
39                       >> plat_def.FW_STATUS_ERROR_OFFSET)
40
41    def __str__(self):
42        return "0x%08X" % self.value
43
44    def print(self):
45        output = ("Firmware Status Register (%s)\n"
46                  "   Boot:   0x%06X (%s)\n"
47                  "   Wait:   0x%02X (%s)\n"
48                  "   Module: 0x%02X\n"
49                  "   Error:  0x%02X" %
50                  (self,
51                   self.boot_state, plat_def.BOOT_STATUS_STR(self.boot_state),
52                   self.wait_state, plat_def.WAIT_STATUS_STR(self.wait_state),
53                   self.moudle, self.error))
54        logging.info(output)
55
56class FirmwareLoader():
57
58    def __init__(self):
59        self._init_done = False
60        self.dma_id = None
61        self.dev = None
62        self.sdl = None
63
64    def init(self, dma_id):
65        if self._init_done:
66            logging.warning("Already initialized! Skip init")
67            return
68
69        self.dma_id = dma_id
70        self.dev = Device()
71        self.dev.open_device()
72        self.sdl = StreamDescList(self.dev)
73        self.ipc = Ipc(self.dev)
74        self._init_done = True
75
76    def close(self):
77        if not self._init_done:
78            logging.warning("Not initialized! Skip closing.")
79            return
80
81        self.sdl.close()
82        self.dev.close()
83        self._init_done = False
84
85    def reset_dsp(self):
86        logging.debug(">>> FirmwareLoader.reset_dsp()")
87        logging.debug("Recycling controller power...")
88        self.dev.power_cycle()
89
90        # This should be enabled prior to power down the cores.
91        self.dev.enable_proc_pipe_ctl()
92
93        logging.debug("Power down cores...")
94        self.dev.core_stall_reset(plat_def.CORE_MASK)
95        self.dev.core_power_down(plat_def.CORE_MASK)
96        logging.debug("<<< FirmwareLoader.reset_dsp()")
97
98    def boot_dsp(self):
99        logging.debug(">>> FirmwareLoader.boot_dsp()")
100        self.dev.enable_proc_pipe_ctl()
101        self.sdl.reset_all()
102        self.dev.core_power_up(0x1)
103        self.dev.dsp_hipct.value = self.dev.dsp_hipct.value
104
105        logging.debug("Purging pending FW request")
106        boot_config = plat_def.FW_IPC_PURGE | regs_def.ADSP_IPC_HIPCI_BUSY
107        boot_config = boot_config | ((self.dma_id - 7) << 9)
108        self.dev.dsp_hipci.value = boot_config
109        time.sleep(0.1)
110        logging.debug("   DSP_HIPCI=%s" % self.dev.dsp_hipci)
111
112        self.dev.core_power_up(plat_def.CORE_MASK)
113        self.dev.core_run(plat_def.CORE_0)
114        self.dev.core_run(plat_def.CORE_1)
115        logging.debug("Wait for IPC DONE bit from ROM")
116        while True:
117            ipc_ack = self.dev.dsp_hipcie.value
118            if (ipc_ack & (1 << regs_def.ADSP_IPC_HIPCIE_DONE_OFFSET)) != 0:
119                break
120            time.sleep(0.1)
121        logging.debug("<<< FirmwareLoader.boot_dsp()")
122
123    def check_fw_boot_status(self, expected):
124        logging.debug(">>> FirmwareLoader.check_fw_boot_status(0x%08X)" % expected)
125        raw_status = self.dev.fw_status.value
126        FirmwareStatus(raw_status).print()
127
128        if (raw_status & plat_def.FW_STATUS_ERROR) != 0:
129            output = ("Firmware Status error:"
130                      "   Status:     0x%08X\n"
131                      "   Error Code  0x%08X" %
132                      (raw_status, self.dev.fw_err_code.value))
133            raise RuntimeError(output)
134        status = raw_status & plat_def.FW_STATUS_BOOT_STATE
135        logging.debug("<<< FirmwareLoader.check_fw_boot_status()")
136        return status == expected
137
138    def wait_for_fw_boot_status(self, boot_status):
139        logging.debug("Waiting for FW Boot Status: 0x%08X (%s)"
140                      % (boot_status,
141                         plat_def.BOOT_STATUS_STR(boot_status)))
142
143        for _ in range(10):
144            reg = self.dev.fw_status.value
145            bs = reg & plat_def.FW_STATUS_BOOT_STATE
146            if bs == boot_status:
147                logging.debug("Received Expected Boot Status")
148                return True
149            time.sleep(0.01)
150        logging.error("Failed to receive expected status")
151        return False
152
153    def wait_for_fw_wait_status(self, wait_status):
154        logging.debug("Waiting for FW Wait Status: 0x%08X (%s)"
155                      % (wait_status,
156                         plat_def.WAIT_STATUS_STR(wait_status)))
157        for _ in range(10):
158            reg = self.dev.fw_status.value
159            ws = reg & plat_def.FW_STATUS_WAIT_STATE
160            if ws == (wait_status << plat_def.FW_STATUS_WAIT_STATE_OFFSET):
161                logging.debug("Received Expected Wait Status")
162                return True
163            time.sleep(0.01)
164        logging.error("Failed to receive expected status")
165        return False
166
167    def load_firmware(self, fw_file):
168        logging.debug(">>> FirmwareLoader.load_firmware()")
169        with open(fw_file, "rb") as fd:
170            data = array.array('B', fd.read())
171            sd = self.sdl.get_sd(self.dma_id)
172            sd.enable = True
173            sd.alloc_memory(len(data))
174            sd.buf.copy(data, len(data))
175            sd.config()
176            sd.set_stream_id(1)
177            sd.set_traffic_priority(1)
178            sd.set_bitrate(0x4)
179            time.sleep(0.1)
180            logging.debug("<<< FirmwareLoader.load_firmware()")
181            return sd
182
183    def download_firmware(self, fw_file):
184        logging.debug(">>> FirmwareLoader.download_firmware(fw_file=%s)" % fw_file)
185
186        # Load firmware to DMA buffer and update SD and SDL
187        sd = self.load_firmware(fw_file)
188        try:
189            self.dev.hda_spibe.value |= (1 << self.dma_id)
190            self.wait_for_fw_wait_status(plat_def.WAIT_STATUS_DMA_BUFFER_FULL)
191
192            logging.info("Start firmware downloading...")
193            sd.start()
194            time.sleep(0.5)
195            self.wait_for_fw_boot_status(plat_def.BOOT_STATUS_FW_ENTERED)
196        finally:
197            sd.pause()
198            sd.reset()
199            self.sdl.release_sd(sd.idx)
200            self.dev.hda_spibe.value = 0
201
202        logging.debug("<<< FirmwareLoader.download_firmware()")
203