Skip to content

Commit

Permalink
rt2x00: Move common firmware loading into rt2800lib
Browse files Browse the repository at this point in the history
Large parts of the firmware initialization are shared
between rt2800pci and rt2800usb. Move this code into
rt2800lib.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Ivo van Doorn authored and John W. Linville committed Jul 12, 2010
1 parent ab8966d commit f31c9a8
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 211 deletions.
155 changes: 155 additions & 0 deletions drivers/net/wireless/rt2x00/rt2800lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
Abstract: rt2800 generic device routines.
*/

#include <linux/crc-ccitt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
Expand Down Expand Up @@ -272,6 +273,160 @@ int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
}
EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready);

static bool rt2800_check_firmware_crc(const u8 *data, const size_t len)
{
u16 fw_crc;
u16 crc;

/*
* The last 2 bytes in the firmware array are the crc checksum itself,
* this means that we should never pass those 2 bytes to the crc
* algorithm.
*/
fw_crc = (data[len - 2] << 8 | data[len - 1]);

/*
* Use the crc ccitt algorithm.
* This will return the same value as the legacy driver which
* used bit ordering reversion on the both the firmware bytes
* before input input as well as on the final output.
* Obviously using crc ccitt directly is much more efficient.
*/
crc = crc_ccitt(~0, data, len - 2);

/*
* There is a small difference between the crc-itu-t + bitrev and
* the crc-ccitt crc calculation. In the latter method the 2 bytes
* will be swapped, use swab16 to convert the crc to the correct
* value.
*/
crc = swab16(crc);

return fw_crc == crc;
}

int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
size_t offset = 0;
size_t fw_len;
bool multiple;

/*
* PCI(e) & SOC devices require firmware with a length
* of 8kb. USB devices require firmware files with a length
* of 4kb. Certain USB chipsets however require different firmware,
* which Ralink only provides attached to the original firmware
* file. Thus for USB devices, firmware files have a length
* which is a multiple of 4kb.
*/
if (rt2x00_is_usb(rt2x00dev)) {
fw_len = 4096;
multiple = true;
} else {
fw_len = 8192;
multiple = true;
}

/*
* Validate the firmware length
*/
if (len != fw_len && (!multiple || (len % fw_len) != 0))
return FW_BAD_LENGTH;

/*
* Check if the chipset requires one of the upper parts
* of the firmware.
*/
if (rt2x00_is_usb(rt2x00dev) &&
!rt2x00_rt(rt2x00dev, RT2860) &&
!rt2x00_rt(rt2x00dev, RT2872) &&
!rt2x00_rt(rt2x00dev, RT3070) &&
((len / fw_len) == 1))
return FW_BAD_VERSION;

/*
* 8kb firmware files must be checked as if it were
* 2 separate firmware files.
*/
while (offset < len) {
if (!rt2800_check_firmware_crc(data + offset, fw_len))
return FW_BAD_CRC;

offset += fw_len;
}

return FW_OK;
}
EXPORT_SYMBOL_GPL(rt2800_check_firmware);

int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
unsigned int i;
u32 reg;

/*
* Wait for stable hardware.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg && reg != ~0)
break;
msleep(1);
}

if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "Unstable hardware.\n");
return -EBUSY;
}

if (rt2x00_is_pci(rt2x00dev))
rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);

/*
* Disable DMA, will be reenabled later when enabling
* the radio.
*/
rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);

/*
* Write firmware to the device.
*/
rt2800_drv_write_firmware(rt2x00dev, data, len);

/*
* Wait for device to stabilize.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
break;
msleep(1);
}

if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "PBF system register not ready.\n");
return -EBUSY;
}

/*
* Initialize firmware.
*/
rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
msleep(1);

return 0;
}
EXPORT_SYMBOL_GPL(rt2800_load_firmware);

void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
{
u32 word;
Expand Down
15 changes: 15 additions & 0 deletions drivers/net/wireless/rt2x00/rt2800lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ struct rt2800_ops {
const unsigned int offset,
const struct rt2x00_field32 field, u32 *reg);

int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
};

Expand Down Expand Up @@ -109,6 +111,14 @@ static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev,
return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg);
}

static inline int rt2800_drv_write_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
const struct rt2800_ops *rt2800ops = rt2x00dev->priv;

return rt2800ops->drv_write_firmware(rt2x00dev, data, len);
}

static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev)
{
const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
Expand All @@ -120,6 +130,11 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token,
const u8 arg0, const u8 arg1);

int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);

void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc);
void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);

Expand Down
101 changes: 5 additions & 96 deletions drivers/net/wireless/rt2x00/rt2800pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
Supported chipsets: RT2800E & RT2800ED.
*/

#include <linux/crc-ccitt.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
Expand Down Expand Up @@ -192,81 +191,13 @@ static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev)
return FIRMWARE_RT2860;
}

static int rt2800pci_check_firmware(struct rt2x00_dev *rt2x00dev,
static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
u16 fw_crc;
u16 crc;

/*
* Only support 8kb firmware files.
*/
if (len != 8192)
return FW_BAD_LENGTH;

/*
* The last 2 bytes in the firmware array are the crc checksum itself,
* this means that we should never pass those 2 bytes to the crc
* algorithm.
*/
fw_crc = (data[len - 2] << 8 | data[len - 1]);

/*
* Use the crc ccitt algorithm.
* This will return the same value as the legacy driver which
* used bit ordering reversion on the both the firmware bytes
* before input input as well as on the final output.
* Obviously using crc ccitt directly is much more efficient.
*/
crc = crc_ccitt(~0, data, len - 2);

/*
* There is a small difference between the crc-itu-t + bitrev and
* the crc-ccitt crc calculation. In the latter method the 2 bytes
* will be swapped, use swab16 to convert the crc to the correct
* value.
*/
crc = swab16(crc);

return (fw_crc == crc) ? FW_OK : FW_BAD_CRC;
}

static int rt2800pci_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
unsigned int i;
u32 reg;

/*
* Wait for stable hardware.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg && reg != ~0)
break;
msleep(1);
}

if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "Unstable hardware.\n");
return -EBUSY;
}

rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);

/*
* Disable DMA, will be reenabled later when enabling
* the radio.
*/
rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);

/*
* enable Host program ram write selection
*/
Expand All @@ -278,34 +209,11 @@ static int rt2800pci_load_firmware(struct rt2x00_dev *rt2x00dev,
* Write firmware to device.
*/
rt2800_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE,
data, len);
data, len);

rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000);
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001);

/*
* Wait for device to stabilize.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
break;
msleep(1);
}

if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "PBF system register not ready.\n");
return -EBUSY;
}

/*
* Disable interrupts
*/
rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF);

/*
* Initialize BBP R/W access agent
*/
rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);

Expand Down Expand Up @@ -1029,6 +937,7 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {

.regbusy_read = rt2x00pci_regbusy_read,

.drv_write_firmware = rt2800pci_write_firmware,
.drv_init_registers = rt2800pci_init_registers,
};

Expand Down Expand Up @@ -1114,8 +1023,8 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
.irq_handler_thread = rt2800pci_interrupt_thread,
.probe_hw = rt2800pci_probe_hw,
.get_firmware_name = rt2800pci_get_firmware_name,
.check_firmware = rt2800pci_check_firmware,
.load_firmware = rt2800pci_load_firmware,
.check_firmware = rt2800_check_firmware,
.load_firmware = rt2800_load_firmware,
.initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize,
.get_entry_state = rt2800pci_get_entry_state,
Expand Down
Loading

0 comments on commit f31c9a8

Please sign in to comment.