Exemple #1
0
/* uploads NVS and firmware */
int wl1271_load_firmware(struct wl1271 *wl)
{
    int ret = 0;
    u32 tmp, clk;
    int selected_clock = -1;

    ret = wl12xx_init_pll_clock(wl, &selected_clock);
    if (ret < 0)
        goto out;

    ret = wl1271_set_partition(wl, &wl12xx_part_table[PART_DRPW]);
    if (ret < 0)
        goto out;

    /* Read-modify-write DRPW_SCRATCH_START register (see next state)
       to be used by DRPw FW. The RTRIM value will be added by the FW
       before taking DRPw out of reset */

    wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START);
    ret = wl1271_read32(wl, DRPW_SCRATCH_START, &clk);
    if (ret < 0)
        goto out;

    wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);

    if (wl->chip.id == CHIP_ID_1283_PG20) {
        clk |= ((selected_clock & 0x3) << 1) << 4;
    } else {
        clk |= (wl->ref_clock << 1) << 4;
    }

    ret = wl1271_write32(wl, DRPW_SCRATCH_START, clk);
    if (ret < 0)
        goto out;

    ret = wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
    if (ret < 0)
        goto out;

    /* Disable interrupts */
    ret = wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
    if (ret < 0)
        goto out;

    ret = wl1271_boot_soft_reset(wl);
    if (ret < 0)
        goto out;

    /* 2. start processing NVS file */
    ret = wl1271_boot_upload_nvs(wl);
    if (ret < 0)
        goto out;

    /* write firmware's last address (ie. it's length) to
     * ACX_EEPROMLESS_IND_REG */
    wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");

    ret = wl1271_write32(wl, ACX_EEPROMLESS_IND_REG,
                         ACX_EEPROMLESS_IND_REG);
    if (ret < 0)
        goto out;

    ret = wl1271_read32(wl, CHIP_ID_B, &tmp);
    if (ret < 0)
        goto out;

    wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);

    /* 6. read the EEPROM parameters */
    ret = wl1271_read32(wl, SCR_PAD2, &tmp);
    if (ret < 0)
        goto out;

    /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
     * to upload_fw) */

    if (wl->chip.id == CHIP_ID_1283_PG20) {
        ret = wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds);
        if (ret < 0)
            goto out;
    }

    ret = wl1271_boot_upload_firmware(wl);
    if (ret < 0)
        goto out;

    /* update loaded fw type */
    wl->fw_type = wl->saved_fw_type;
out:
    return ret;
}
Exemple #2
0
static int wl1271_chip_wakeup(struct wl1271 *wl)
{
	int ret = 0;

	wl1271_power_on(wl);
	msleep(WL1271_POWER_ON_SLEEP);
	wl1271_spi_reset(wl);
	wl1271_spi_init(wl);

	
	wl1271_set_partition(wl,
			     0x00000000,
			     0x00000000,
			     REGISTERS_BASE,
			     REGISTERS_DOWN_SIZE);

	
	wl1271_fw_wakeup(wl);

	

	
	wl->chip.id = wl1271_reg_read32(wl, CHIP_ID_B);

	

	switch (wl->chip.id) {
	case CHIP_ID_1271_PG10:
		wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
			       wl->chip.id);

		ret = wl1271_setup(wl);
		if (ret < 0)
			goto out;
		break;
	case CHIP_ID_1271_PG20:
		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
			     wl->chip.id);

		ret = wl1271_setup(wl);
		if (ret < 0)
			goto out;
		break;
	default:
		wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
		ret = -ENODEV;
		goto out;
	}

	if (wl->fw == NULL) {
		ret = wl1271_fetch_firmware(wl);
		if (ret < 0)
			goto out;
	}

	
	if (wl->nvs == NULL) {
		ret = wl1271_fetch_nvs(wl);
		if (ret < 0)
			goto out;
	}

out:
	return ret;
}
Exemple #3
0
static int wl1271_boot_upload_nvs(struct wl1271 *wl)
{
    size_t nvs_len, burst_len;
    int i;
    u32 dest_addr, val;
    u8 *nvs_ptr, *nvs_aligned;
    int ret;

    if (wl->nvs == NULL)
        return -ENODEV;

    if (wl->chip.id == CHIP_ID_1283_PG20) {
        struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;

        if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) {
            if (nvs->general_params.dual_mode_select)
                wl->enable_11a = true;
        } else {
            wl1271_error("nvs size is not as expected: %zu != %zu",
                         wl->nvs_len,
                         sizeof(struct wl128x_nvs_file));
            kfree(wl->nvs);
            wl->nvs = NULL;
            wl->nvs_len = 0;
            return -EILSEQ;
        }

        /* only the first part of the NVS needs to be uploaded */
        nvs_len = sizeof(nvs->nvs);
        nvs_ptr = (u8 *)nvs->nvs;

    } else {
        struct wl1271_nvs_file *nvs =
            (struct wl1271_nvs_file *)wl->nvs;
        /*
         * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz
         * band configurations) can be removed when those NVS files stop
         * floating around.
         */
        if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
                wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
            if (nvs->general_params.dual_mode_select)
                wl->enable_11a = true;
        }

        if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
                (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
                 wl->enable_11a)) {
            wl1271_error("nvs size is not as expected: %zu != %zu",
                         wl->nvs_len, sizeof(struct wl1271_nvs_file));
            kfree(wl->nvs);
            wl->nvs = NULL;
            wl->nvs_len = 0;
            return -EILSEQ;
        }

        /* only the first part of the NVS needs to be uploaded */
        nvs_len = sizeof(nvs->nvs);
        nvs_ptr = (u8 *) nvs->nvs;
    }

    /* update current MAC address to NVS */
    nvs_ptr[11] = wl->addresses[0].addr[0];
    nvs_ptr[10] = wl->addresses[0].addr[1];
    nvs_ptr[6] = wl->addresses[0].addr[2];
    nvs_ptr[5] = wl->addresses[0].addr[3];
    nvs_ptr[4] = wl->addresses[0].addr[4];
    nvs_ptr[3] = wl->addresses[0].addr[5];

    /*
     * Layout before the actual NVS tables:
     * 1 byte : burst length.
     * 2 bytes: destination address.
     * n bytes: data to burst copy.
     *
     * This is ended by a 0 length, then the NVS tables.
     */

    /* FIXME: Do we need to check here whether the LSB is 1? */
    while (nvs_ptr[0]) {
        burst_len = nvs_ptr[0];
        dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));

        /*
         * Due to our new wl1271_translate_reg_addr function,
         * we need to add the REGISTER_BASE to the destination
         */
        dest_addr += REGISTERS_BASE;

        /* We move our pointer to the data */
        nvs_ptr += 3;

        for (i = 0; i < burst_len; i++) {
            if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len)
                goto out_badnvs;

            val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
                   | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));

            wl1271_debug(DEBUG_BOOT,
                         "nvs burst write 0x%x: 0x%x",
                         dest_addr, val);
            ret = wl1271_write32(wl, dest_addr, val);
            if (ret < 0)
                return ret;

            nvs_ptr += 4;
            dest_addr += 4;
        }

        if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
            goto out_badnvs;
    }

    /*
     * We've reached the first zero length, the first NVS table
     * is located at an aligned offset which is at least 7 bytes further.
     * NOTE: The wl->nvs->nvs element must be first, in order to
     * simplify the casting, we assume it is at the beginning of
     * the wl->nvs structure.
     */
    nvs_ptr = (u8 *)wl->nvs +
              ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4);

    if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
        goto out_badnvs;

    nvs_len -= nvs_ptr - (u8 *)wl->nvs;

    /* Now we must set the partition correctly */
    ret = wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
    if (ret < 0)
        return ret;

    /* Copy the NVS tables to a new block to ensure alignment */
    nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
    if (!nvs_aligned)
        return -ENOMEM;

    /* And finally we upload the NVS tables */
    ret = wl1271_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);
    if (ret < 0)
        return ret;

    kfree(nvs_aligned);
    return 0;

out_badnvs:
    wl1271_error("nvs data is malformed");
    return -EILSEQ;
}
Exemple #4
0
static int wl1271_boot_run_firmware(struct wl1271 *wl)
{
    int loop, ret;
    u32 chip_id, intr;

    ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
    if (ret < 0)
        goto out;

    ret = wl1271_read32(wl, CHIP_ID_B, &chip_id);
    if (ret < 0)
        goto out;

    wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);

    if (chip_id != wl->chip.id) {
        wl1271_error("chip id doesn't match after firmware boot");
        ret = -EIO;
        goto out;
    }

    /* wait for init to complete */
    loop = 0;
    while (loop++ < INIT_LOOP) {
        udelay(INIT_LOOP_DELAY);
        ret = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR, &intr);
        if (ret < 0)
            goto out;

        if (intr == 0xffffffff) {
            wl1271_error("error reading hardware complete "
                         "init indication");
            ret = -EIO;
            goto out;
        }
        /* check that ACX_INTR_INIT_COMPLETE is enabled */
        else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) {
            ret = wl1271_write32(wl, ACX_REG_INTERRUPT_ACK,
                                 WL1271_ACX_INTR_INIT_COMPLETE);
            if (ret < 0)
                goto out;
            break;
        }
    }

    if (loop > INIT_LOOP) {
        wl1271_error("timeout waiting for the hardware to "
                     "complete initialization");
        ret = -EIO;
        goto out;
    }

    /* get hardware config command mail box */
    ret = wl1271_read32(wl, REG_COMMAND_MAILBOX_PTR, &wl->cmd_box_addr);
    if (ret < 0)
        goto out;

    /* get hardware config event mail box */
    ret = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR, &wl->event_box_addr);
    if (ret < 0)
        goto out;

    /* set the working partition to its "running" mode offset */
    ret = wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
    if (ret < 0)
        goto out;

    wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
                 wl->cmd_box_addr, wl->event_box_addr);

    ret = wl1271_boot_fw_version(wl);
    if (ret < 0)
        goto out;

    /*
     * in case of full asynchronous mode the firmware event must be
     * ready to receive event from the command mailbox
     */

    /* unmask required mbox events  */
    wl->event_mask = BSS_LOSE_EVENT_ID |
                     SCAN_COMPLETE_EVENT_ID |
                     ROLE_STOP_COMPLETE_EVENT_ID |
                     RSSI_SNR_TRIGGER_0_EVENT_ID |
                     PSPOLL_DELIVERY_FAILURE_EVENT_ID |
                     SOFT_GEMINI_SENSE_EVENT_ID |
                     PERIODIC_SCAN_REPORT_EVENT_ID |
                     PERIODIC_SCAN_COMPLETE_EVENT_ID |
                     DUMMY_PACKET_EVENT_ID |
                     PEER_REMOVE_COMPLETE_EVENT_ID |
                     BA_SESSION_RX_CONSTRAINT_EVENT_ID |
                     REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                     INACTIVE_STA_EVENT_ID |
                     MAX_TX_RETRY_EVENT_ID |
                     CHANNEL_SWITCH_COMPLETE_EVENT_ID;

    ret = wl1271_event_unmask(wl);
    if (ret < 0) {
        wl1271_error("EVENT mask setting failed");
        goto out;
    }

    ret = wl1271_event_mbox_config(wl);

    /* firmware startup completed */
out:
    return ret;
}
int wl1271_boot(struct wl1271 *wl)
{
	int ret = 0;
	u32 tmp, clk, pause;

	if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4)
		/* ref clk: 19.2/38.4/38.4-XTAL */
		clk = 0x3;
	else if (REF_CLOCK == 1 || REF_CLOCK == 3)
		/* ref clk: 26/52 */
		clk = 0x5;

	if (REF_CLOCK != 0) {
		u16 val;
		/* Set clock type */
		val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
		val &= FREF_CLK_TYPE_BITS;
		val |= CLK_REQ_PRCM;
		wl1271_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
	} else {
		u16 val;
		/* Set clock polarity */
		val = wl1271_top_reg_read(wl, OCP_REG_CLK_POLARITY);
		val &= FREF_CLK_POLARITY_BITS;
		val |= CLK_REQ_OUTN_SEL;
		wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
	}

	wl1271_spi_write32(wl, PLL_PARAMETERS, clk);

	pause = wl1271_spi_read32(wl, PLL_PARAMETERS);

	wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);

	pause &= ~(WU_COUNTER_PAUSE_VAL); /* FIXME: This should probably be
					   * WU_COUNTER_PAUSE_VAL instead of
					   * 0x3ff (magic number ).  How does
					   * this work?! */
	pause |= WU_COUNTER_PAUSE_VAL;
	wl1271_spi_write32(wl, WU_COUNTER_PAUSE, pause);

	/* Continue the ELP wake up sequence */
	wl1271_spi_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
	udelay(500);

	wl1271_set_partition(wl, &part_table[PART_DRPW]);

	/* Read-modify-write DRPW_SCRATCH_START register (see next state)
	   to be used by DRPw FW. The RTRIM value will be added by the FW
	   before taking DRPw out of reset */

	wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START);
	clk = wl1271_spi_read32(wl, DRPW_SCRATCH_START);

	wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);

	/* 2 */
	clk |= (REF_CLOCK << 1) << 4;
	wl1271_spi_write32(wl, DRPW_SCRATCH_START, clk);

	wl1271_set_partition(wl, &part_table[PART_WORK]);

	/* Disable interrupts */
	wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);

	ret = wl1271_boot_soft_reset(wl);
	if (ret < 0)
		goto out;

	/* 2. start processing NVS file */
	ret = wl1271_boot_upload_nvs(wl);
	if (ret < 0)
		goto out;

	/* write firmware's last address (ie. it's length) to
	 * ACX_EEPROMLESS_IND_REG */
	wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");

	wl1271_spi_write32(wl, ACX_EEPROMLESS_IND_REG,
			   ACX_EEPROMLESS_IND_REG);

	tmp = wl1271_spi_read32(wl, CHIP_ID_B);

	wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);

	/* 6. read the EEPROM parameters */
	tmp = wl1271_spi_read32(wl, SCR_PAD2);

	ret = wl1271_boot_write_irq_polarity(wl);
	if (ret < 0)
		goto out;

	/* FIXME: Need to check whether this is really what we want */
	wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
			   WL1271_ACX_ALL_EVENTS_VECTOR);

	/* WL1271: The reference driver skips steps 7 to 10 (jumps directly
	 * to upload_fw) */

	ret = wl1271_boot_upload_firmware(wl);
	if (ret < 0)
		goto out;

	/* 10.5 start firmware */
	ret = wl1271_boot_run_firmware(wl);
	if (ret < 0)
		goto out;

	/* Enable firmware interrupts now */
	wl1271_boot_enable_interrupts(wl);

	/* set the wl1271 default filters */
	wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
	wl->rx_filter = WL1271_DEFAULT_RX_FILTER;

	wl1271_event_mbox_config(wl);

out:
	return ret;
}
Exemple #6
0
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
        size_t fw_data_len, u32 dest)
{
    struct wl1271_partition_set partition;
    int addr, chunk_num, partition_limit;
    u8 *p, *chunk;
    int ret;

    /* whal_FwCtrl_LoadFwImageSm() */

    wl1271_debug(DEBUG_BOOT, "starting firmware upload");

    wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d",
                 fw_data_len, CHUNK_SIZE);

    if ((fw_data_len % 4) != 0) {
        wl1271_error("firmware length not multiple of four");
        return -EIO;
    }

    chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL);
    if (!chunk) {
        wl1271_error("allocation for firmware upload chunk failed");
        return -ENOMEM;
    }

    memcpy(&partition, &wl12xx_part_table[PART_DOWN], sizeof(partition));
    partition.mem.start = dest;
    ret = wl1271_set_partition(wl, &partition);
    if (ret < 0)
        goto out;

    /* 10.1 set partition limit and chunk num */
    chunk_num = 0;
    partition_limit = wl12xx_part_table[PART_DOWN].mem.size;

    while (chunk_num < fw_data_len / CHUNK_SIZE) {
        /* 10.2 update partition, if needed */
        addr = dest + (chunk_num + 2) * CHUNK_SIZE;
        if (addr > partition_limit) {
            addr = dest + chunk_num * CHUNK_SIZE;
            partition_limit = chunk_num * CHUNK_SIZE +
                              wl12xx_part_table[PART_DOWN].mem.size;
            partition.mem.start = addr;
            ret = wl1271_set_partition(wl, &partition);
            if (ret < 0)
                goto out;
        }

        /* 10.3 upload the chunk */
        addr = dest + chunk_num * CHUNK_SIZE;
        p = buf + chunk_num * CHUNK_SIZE;
        memcpy(chunk, p, CHUNK_SIZE);
        wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
                     p, addr);
        ret = wl1271_write(wl, addr, chunk, CHUNK_SIZE, false);
        if (ret < 0)
            goto out;

        chunk_num++;
    }

    /* 10.4 upload the last chunk */
    addr = dest + chunk_num * CHUNK_SIZE;
    p = buf + chunk_num * CHUNK_SIZE;
    memcpy(chunk, p, fw_data_len % CHUNK_SIZE);
    wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x",
                 fw_data_len % CHUNK_SIZE, p, addr);
    ret = wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);

out:
    kfree(chunk);
    return ret;
}
static int wl1271_boot_run_firmware(struct wl1271 *wl)
{
	int loop, ret;
	u32 chip_id, interrupt;

	wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);

	chip_id = wl1271_spi_read32(wl, CHIP_ID_B);

	wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);

	if (chip_id != wl->chip.id) {
		wl1271_error("chip id doesn't match after firmware boot");
		return -EIO;
	}

	/* wait for init to complete */
	loop = 0;
	while (loop++ < INIT_LOOP) {
		udelay(INIT_LOOP_DELAY);
		interrupt = wl1271_spi_read32(wl,
					      ACX_REG_INTERRUPT_NO_CLEAR);

		if (interrupt == 0xffffffff) {
			wl1271_error("error reading hardware complete "
				     "init indication");
			return -EIO;
		}
		/* check that ACX_INTR_INIT_COMPLETE is enabled */
		else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) {
			wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK,
					   WL1271_ACX_INTR_INIT_COMPLETE);
			break;
		}
	}

	if (loop > INIT_LOOP) {
		wl1271_error("timeout waiting for the hardware to "
			     "complete initialization");
		return -EIO;
	}

	/* get hardware config command mail box */
	wl->cmd_box_addr = wl1271_spi_read32(wl, REG_COMMAND_MAILBOX_PTR);

	/* get hardware config event mail box */
	wl->event_box_addr = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR);

	/* set the working partition to its "running" mode offset */
	wl1271_set_partition(wl, &part_table[PART_WORK]);

	wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
		     wl->cmd_box_addr, wl->event_box_addr);

	wl1271_boot_fw_version(wl);

	/*
	 * in case of full asynchronous mode the firmware event must be
	 * ready to receive event from the command mailbox
	 */

	/* unmask required mbox events  */
	wl->event_mask = BSS_LOSE_EVENT_ID |
		SCAN_COMPLETE_EVENT_ID |
		PS_REPORT_EVENT_ID;

	ret = wl1271_event_unmask(wl);
	if (ret < 0) {
		wl1271_error("EVENT mask setting failed");
		return ret;
	}

	wl1271_event_mbox_config(wl);

	/* firmware startup completed */
	return 0;
}
static int wl1271_boot_upload_nvs(struct wl1271 *wl)
{
	size_t nvs_len, burst_len;
	int i;
	u32 dest_addr, val;
	u8 *nvs_ptr, *nvs, *nvs_aligned;

	nvs = wl->nvs;
	if (nvs == NULL)
		return -ENODEV;

	nvs_ptr = nvs;

	nvs_len = wl->nvs_len;

	/* Update the device MAC address into the nvs */
	nvs[11] = wl->mac_addr[0];
	nvs[10] = wl->mac_addr[1];
	nvs[6] = wl->mac_addr[2];
	nvs[5] = wl->mac_addr[3];
	nvs[4] = wl->mac_addr[4];
	nvs[3] = wl->mac_addr[5];

	/*
	 * Layout before the actual NVS tables:
	 * 1 byte : burst length.
	 * 2 bytes: destination address.
	 * n bytes: data to burst copy.
	 *
	 * This is ended by a 0 length, then the NVS tables.
	 */

	/* FIXME: Do we need to check here whether the LSB is 1? */
	while (nvs_ptr[0]) {
		burst_len = nvs_ptr[0];
		dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));

		/* FIXME: Due to our new wl1271_translate_reg_addr function,
		   we need to add the REGISTER_BASE to the destination */
		dest_addr += REGISTERS_BASE;

		/* We move our pointer to the data */
		nvs_ptr += 3;

		for (i = 0; i < burst_len; i++) {
			val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
			       | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));

			wl1271_debug(DEBUG_BOOT,
				     "nvs burst write 0x%x: 0x%x",
				     dest_addr, val);
			wl1271_spi_write32(wl, dest_addr, val);

			nvs_ptr += 4;
			dest_addr += 4;
		}
	}

	/*
	 * We've reached the first zero length, the first NVS table
	 * is 7 bytes further.
	 */
	nvs_ptr += 7;
	nvs_len -= nvs_ptr - nvs;
	nvs_len = ALIGN(nvs_len, 4);

	/* FIXME: The driver sets the partition here, but this is not needed,
	   since it sets to the same one as currently in use */
	/* Now we must set the partition correctly */
	wl1271_set_partition(wl, &part_table[PART_WORK]);

	/* Copy the NVS tables to a new block to ensure alignment */
	nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
	if (!nvs_aligned)
		return -ENOMEM;

	/* And finally we upload the NVS tables */
	/* FIXME: In wl1271, we upload everything at once.
	   No endianness handling needed here?! The ref driver doesn't do
	   anything about it at this point */
	wl1271_spi_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);

	kfree(nvs_aligned);
	return 0;
}
Exemple #9
0
static int tester(void *data)
{
	struct wl1271 *wl = data;
	struct sdio_func *func = wl_to_func(wl);
	struct device *pdev = &func->dev;
	int ret = 0;
	bool rx_started = 0;
	bool tx_started = 0;
	uint8_t *tx_buf, *rx_buf;
	int test_size = PAGE_SIZE;
	u32 addr = 0;
	struct wl1271_partition_set partition;

	/* We assume chip is powered up and firmware fetched */

	memcpy(&partition, &part_down, sizeof(partition));
	partition.mem.start = addr;
	wl1271_set_partition(wl, &partition);

	tx_buf = kmalloc(test_size, GFP_KERNEL);
	rx_buf = kmalloc(test_size, GFP_KERNEL);
	if (!tx_buf || !rx_buf) {
		dev_err(pdev,
			"Could not allocate memory. Test will not run.\n");
		ret = -ENOMEM;
		goto free;
	}

	memset(tx_buf, 0x5a, test_size);

	/* write something in data area so we can read it back */
	wl1271_write(wl, addr, tx_buf, test_size, false);

	while (!kthread_should_stop()) {
		if (rx && !rx_started) {
			dev_info(pdev, "starting rx test\n");
			rx_started = 1;
		} else if (!rx && rx_started) {
			dev_info(pdev, "stopping rx test\n");
			rx_started = 0;
		}

		if (tx && !tx_started) {
			dev_info(pdev, "starting tx test\n");
			tx_started = 1;
		} else if (!tx && tx_started) {
			dev_info(pdev, "stopping tx test\n");
			tx_started = 0;
		}

		if (rx_started)
			wl1271_read(wl, addr, rx_buf, test_size, false);

		if (tx_started)
			wl1271_write(wl, addr, tx_buf, test_size, false);

		if (!rx_started && !tx_started)
			msleep(100);
	}

free:
	kfree(tx_buf);
	kfree(rx_buf);
	return ret;
}
static int wl1271_chip_wakeup(struct wl1271 *wl)
{
	int ret = 0;

	wl1271_power_on(wl);
	msleep(WL1271_POWER_ON_SLEEP);
	wl1271_spi_reset(wl);
	wl1271_spi_init(wl);

	/* We don't need a real memory partition here, because we only want
	 * to use the registers at this point. */
	wl1271_set_partition(wl,
			     0x00000000,
			     0x00000000,
			     REGISTERS_BASE,
			     REGISTERS_DOWN_SIZE);

	/* ELP module wake up */
	wl1271_fw_wakeup(wl);

	/* whal_FwCtrl_BootSm() */

	/* 0. read chip id from CHIP_ID */
	wl->chip.id = wl1271_reg_read32(wl, CHIP_ID_B);

	/* 1. check if chip id is valid */

	switch (wl->chip.id) {
	case CHIP_ID_1271_PG10:
		wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
			       wl->chip.id);

		ret = wl1271_setup(wl);
		if (ret < 0)
			goto out;
		break;
	case CHIP_ID_1271_PG20:
		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
			     wl->chip.id);

		ret = wl1271_setup(wl);
		if (ret < 0)
			goto out;
		break;
	default:
		wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
		ret = -ENODEV;
		goto out;
	}

	if (wl->fw == NULL) {
		ret = wl1271_fetch_firmware(wl);
		if (ret < 0)
			goto out;
	}

	/* No NVS from netlink, try to get it from the filesystem */
	if (wl->nvs == NULL) {
		ret = wl1271_fetch_nvs(wl);
		if (ret < 0)
			goto out;
	}

out:
	return ret;
}
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
					     size_t fw_data_len, u32 dest)
{
	int addr, chunk_num, partition_limit;
	u8 *p;

	/* whal_FwCtrl_LoadFwImageSm() */

	wl1271_debug(DEBUG_BOOT, "starting firmware upload");

	wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d",
		     fw_data_len, CHUNK_SIZE);


	if ((fw_data_len % 4) != 0) {
		wl1271_error("firmware length not multiple of four");
		return -EIO;
	}

	wl1271_set_partition(wl, dest,
			     part_table[PART_DOWN].mem.size,
			     part_table[PART_DOWN].reg.start,
			     part_table[PART_DOWN].reg.size);

	/* 10.1 set partition limit and chunk num */
	chunk_num = 0;
	partition_limit = part_table[PART_DOWN].mem.size;

	while (chunk_num < fw_data_len / CHUNK_SIZE) {
		/* 10.2 update partition, if needed */
		addr = dest + (chunk_num + 2) * CHUNK_SIZE;
		if (addr > partition_limit) {
			addr = dest + chunk_num * CHUNK_SIZE;
			partition_limit = chunk_num * CHUNK_SIZE +
				part_table[PART_DOWN].mem.size;

			/* FIXME: Over 80 chars! */
			wl1271_set_partition(wl,
					     addr,
					     part_table[PART_DOWN].mem.size,
					     part_table[PART_DOWN].reg.start,
					     part_table[PART_DOWN].reg.size);
		}

		/* 10.3 upload the chunk */
		addr = dest + chunk_num * CHUNK_SIZE;
		p = buf + chunk_num * CHUNK_SIZE;
		wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
			     p, addr);
		wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE);

		chunk_num++;
	}

	/* 10.4 upload the last chunk */
	addr = dest + chunk_num * CHUNK_SIZE;
	p = buf + chunk_num * CHUNK_SIZE;
	wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x",
		     fw_data_len % CHUNK_SIZE, p, addr);
	wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE);

	return 0;
}