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