linux/drivers/staging/rtl8188eu/hal/fw.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/******************************************************************************
   3 *
   4 * Copyright(c) 2009-2013  Realtek Corporation.
   5 *
   6 * Contact Information:
   7 * wlanfae <wlanfae@realtek.com>
   8 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
   9 * Hsinchu 300, Taiwan.
  10 *
  11 * Larry Finger <Larry.Finger@lwfinger.net>
  12 *
  13 *****************************************************************************/
  14
  15#include "fw.h"
  16#include "drv_types.h"
  17#include "usb_ops_linux.h"
  18#include "rtl8188e_spec.h"
  19#include "rtl8188e_hal.h"
  20
  21#include <linux/firmware.h>
  22#include <linux/slab.h>
  23
  24static void _rtl88e_enable_fw_download(struct adapter *adapt, bool enable)
  25{
  26        u8 tmp;
  27
  28        if (enable) {
  29                tmp = usb_read8(adapt, REG_MCUFWDL);
  30                usb_write8(adapt, REG_MCUFWDL, tmp | 0x01);
  31
  32                tmp = usb_read8(adapt, REG_MCUFWDL + 2);
  33                usb_write8(adapt, REG_MCUFWDL + 2, tmp & 0xf7);
  34        } else {
  35                tmp = usb_read8(adapt, REG_MCUFWDL);
  36                usb_write8(adapt, REG_MCUFWDL, tmp & 0xfe);
  37
  38                usb_write8(adapt, REG_MCUFWDL + 1, 0x00);
  39        }
  40}
  41
  42static void _rtl88e_fw_block_write(struct adapter *adapt,
  43                                   const u8 *buffer, u32 size)
  44{
  45        u32 blk_sz = sizeof(u32);
  46        const u8 *byte_buffer;
  47        const u32 *dword_buffer = (u32 *)buffer;
  48        u32 i, write_address, blk_cnt, remain;
  49
  50        blk_cnt = size / blk_sz;
  51        remain = size % blk_sz;
  52
  53        write_address = FW_8192C_START_ADDRESS;
  54
  55        for (i = 0; i < blk_cnt; i++, write_address += blk_sz)
  56                usb_write32(adapt, write_address, dword_buffer[i]);
  57
  58        byte_buffer = buffer + blk_cnt * blk_sz;
  59        for (i = 0; i < remain; i++, write_address++)
  60                usb_write8(adapt, write_address, byte_buffer[i]);
  61}
  62
  63static void _rtl88e_fw_page_write(struct adapter *adapt,
  64                                  u32 page, const u8 *buffer, u32 size)
  65{
  66        u8 value8;
  67        u8 u8page = (u8)(page & 0x07);
  68
  69        value8 = (usb_read8(adapt, REG_MCUFWDL + 2) & 0xF8) | u8page;
  70
  71        usb_write8(adapt, (REG_MCUFWDL + 2), value8);
  72        _rtl88e_fw_block_write(adapt, buffer, size);
  73}
  74
  75static void _rtl88e_write_fw(struct adapter *adapt, u8 *buffer, u32 size)
  76{
  77        u8 *buf_ptr = buffer;
  78        u32 page_no, remain;
  79        u32 page, offset;
  80
  81        page_no = size / FW_8192C_PAGE_SIZE;
  82        remain = size % FW_8192C_PAGE_SIZE;
  83
  84        for (page = 0; page < page_no; page++) {
  85                offset = page * FW_8192C_PAGE_SIZE;
  86                _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset),
  87                                      FW_8192C_PAGE_SIZE);
  88        }
  89
  90        if (remain) {
  91                offset = page_no * FW_8192C_PAGE_SIZE;
  92                page = page_no;
  93                _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset), remain);
  94        }
  95}
  96
  97static void rtl88e_firmware_selfreset(struct adapter *adapt)
  98{
  99        u8 u1b_tmp;
 100
 101        u1b_tmp = usb_read8(adapt, REG_SYS_FUNC_EN + 1);
 102        usb_write8(adapt, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2))));
 103        usb_write8(adapt, REG_SYS_FUNC_EN + 1, (u1b_tmp | BIT(2)));
 104}
 105
 106static int _rtl88e_fw_free_to_go(struct adapter *adapt)
 107{
 108        int err = -EIO;
 109        u32 counter = 0;
 110        u32 value32;
 111
 112        do {
 113                value32 = usb_read32(adapt, REG_MCUFWDL);
 114                if (value32 & FWDL_ChkSum_rpt)
 115                        break;
 116        } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
 117
 118        if (counter >= POLLING_READY_TIMEOUT_COUNT)
 119                goto exit;
 120
 121        value32 = usb_read32(adapt, REG_MCUFWDL);
 122        value32 |= MCUFWDL_RDY;
 123        value32 &= ~WINTINI_RDY;
 124        usb_write32(adapt, REG_MCUFWDL, value32);
 125
 126        rtl88e_firmware_selfreset(adapt);
 127        counter = 0;
 128
 129        do {
 130                value32 = usb_read32(adapt, REG_MCUFWDL);
 131                if (value32 & WINTINI_RDY) {
 132                        err = 0;
 133                        goto exit;
 134                }
 135
 136                udelay(FW_8192C_POLLING_DELAY);
 137
 138        } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
 139
 140exit:
 141        return err;
 142}
 143
 144int rtl88eu_download_fw(struct adapter *adapt)
 145{
 146        struct dvobj_priv *dvobj = adapter_to_dvobj(adapt);
 147        struct device *device = dvobj_to_dev(dvobj);
 148        const struct firmware *fw;
 149        const char fw_name[] = "rtlwifi/rtl8188eufw.bin";
 150        struct rtl92c_firmware_header *pfwheader = NULL;
 151        u8 *download_data, *fw_data;
 152        size_t download_size;
 153        unsigned int trailing_zeros_length;
 154
 155        if (request_firmware(&fw, fw_name, device)) {
 156                dev_err(device, "Firmware %s not available\n", fw_name);
 157                return -ENOENT;
 158        }
 159
 160        if (fw->size > FW_8188E_SIZE) {
 161                dev_err(device, "Firmware size exceed 0x%X. Check it.\n",
 162                        FW_8188E_SIZE);
 163                release_firmware(fw);
 164                return -1;
 165        }
 166
 167        trailing_zeros_length = (4 - fw->size % 4) % 4;
 168
 169        fw_data = kmalloc(fw->size + trailing_zeros_length, GFP_KERNEL);
 170        if (!fw_data) {
 171                release_firmware(fw);
 172                return -ENOMEM;
 173        }
 174
 175        memcpy(fw_data, fw->data, fw->size);
 176        memset(fw_data + fw->size, 0, trailing_zeros_length);
 177
 178        pfwheader = (struct rtl92c_firmware_header *)fw_data;
 179
 180        if (IS_FW_HEADER_EXIST(pfwheader)) {
 181                download_data = fw_data + 32;
 182                download_size = fw->size + trailing_zeros_length - 32;
 183        } else {
 184                download_data = fw_data;
 185                download_size = fw->size + trailing_zeros_length;
 186        }
 187
 188        release_firmware(fw);
 189
 190        if (usb_read8(adapt, REG_MCUFWDL) & RAM_DL_SEL) {
 191                usb_write8(adapt, REG_MCUFWDL, 0);
 192                rtl88e_firmware_selfreset(adapt);
 193        }
 194        _rtl88e_enable_fw_download(adapt, true);
 195        usb_write8(adapt, REG_MCUFWDL, usb_read8(adapt, REG_MCUFWDL) | FWDL_ChkSum_rpt);
 196        _rtl88e_write_fw(adapt, download_data, download_size);
 197        _rtl88e_enable_fw_download(adapt, false);
 198
 199        kfree(fw_data);
 200        return _rtl88e_fw_free_to_go(adapt);
 201}
 202