137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy/* Firmware file reading and download helpers
237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy *
337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * See copyright notice in main.c
437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy */
537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#include <linux/kernel.h>
65a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#include <linux/firmware.h>
844d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy#include <linux/device.h>
99d9779e723a5d23b94abbe5bb7d1197921f6f3ddPaul Gortmaker#include <linux/module.h>
1037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
1137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#include "hermes.h"
1237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#include "hermes_dld.h"
1337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#include "orinoco.h"
1437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
1537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#include "fw.h"
1637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
1737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy/* End markers (for Symbol firmware only) */
1837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy#define TEXT_END	0x1A		/* End of text header */
1937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
2037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroystruct fw_info {
2137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	char *pri_fw;
2237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	char *sta_fw;
2337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	char *ap_fw;
2437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	u32 pda_addr;
2537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	u16 pda_size;
2637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy};
2737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
28f733ded107ff15022e3f0f8204f11ab2e83a2aa4Tobias Klauserstatic const struct fw_info orinoco_fw[] = {
2937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	{ NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
3037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	{ NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
3137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	{ "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 }
3237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy};
336f48d0e981c026572eac643ed43ebfb883048c14Ben HutchingsMODULE_FIRMWARE("agere_sta_fw.bin");
346f48d0e981c026572eac643ed43ebfb883048c14Ben HutchingsMODULE_FIRMWARE("agere_ap_fw.bin");
356f48d0e981c026572eac643ed43ebfb883048c14Ben HutchingsMODULE_FIRMWARE("prism_sta_fw.bin");
366f48d0e981c026572eac643ed43ebfb883048c14Ben HutchingsMODULE_FIRMWARE("prism_ap_fw.bin");
376f48d0e981c026572eac643ed43ebfb883048c14Ben HutchingsMODULE_FIRMWARE("symbol_sp24t_prim_fw");
386f48d0e981c026572eac643ed43ebfb883048c14Ben HutchingsMODULE_FIRMWARE("symbol_sp24t_sec_fw");
3937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
4037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy/* Structure used to access fields in FW
4137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * Make sure LE decoding macros are used
4237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy */
4337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroystruct orinoco_fw_header {
4437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	char hdr_vers[6];       /* ASCII string for header version */
4537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le16 headersize;      /* Total length of header */
4637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le32 entry_point;     /* NIC entry point */
4737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le32 blocks;          /* Number of blocks to program */
4837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le32 block_offset;    /* Offset of block data from eof header */
4937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le32 pdr_offset;      /* Offset to PDR data from eof header */
5037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le32 pri_offset;      /* Offset to primary plug data */
5137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le32 compat_offset;   /* Offset to compatibility data*/
5237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	char signature[0];      /* FW signature length headersize-20 */
53ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
5437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
557e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy/* Check the range of various header entries. Return a pointer to a
567e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy * description of the problem, or NULL if everything checks out. */
577e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroystatic const char *validate_fw(const struct orinoco_fw_header *hdr, size_t len)
587e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy{
597e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	u16 hdrsize;
607e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy
617e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if (len < sizeof(*hdr))
627e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "image too small";
637e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if (memcmp(hdr->hdr_vers, "HFW", 3) != 0)
647e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "format not recognised";
657e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy
667e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	hdrsize = le16_to_cpu(hdr->headersize);
677e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if (hdrsize > len)
687e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "bad headersize";
697e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if ((hdrsize + le32_to_cpu(hdr->block_offset)) > len)
707e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "bad block offset";
717e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if ((hdrsize + le32_to_cpu(hdr->pdr_offset)) > len)
727e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "bad PDR offset";
737e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if ((hdrsize + le32_to_cpu(hdr->pri_offset)) > len)
747e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "bad PRI offset";
757e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if ((hdrsize + le32_to_cpu(hdr->compat_offset)) > len)
767e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		return "bad compat offset";
777e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy
787e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	/* TODO: consider adding a checksum or CRC to the firmware format */
797e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	return NULL;
807e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy}
817e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy
822bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
832bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkovstatic inline const struct firmware *
842bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkovorinoco_cached_fw_get(struct orinoco_private *priv, bool primary)
852bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov{
862bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (primary)
872bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov		return priv->cached_pri_fw;
882bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	else
892bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov		return priv->cached_fw;
902bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov}
912bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov#else
922bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov#define orinoco_cached_fw_get(priv, primary) (NULL)
932bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov#endif
942bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov
9537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy/* Download either STA or AP firmware into the card. */
9637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroystatic int
9737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyorinoco_dl_firmware(struct orinoco_private *priv,
9837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		    const struct fw_info *fw,
9937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		    int ap)
10037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy{
10137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Plug Data Area (PDA) */
10237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le16 *pda;
10337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
104933d594313a5928ffc5325d7bbb6e2383d79622ePavel Roskin	struct hermes *hw = &priv->hw;
10537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const struct firmware *fw_entry;
10637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const struct orinoco_fw_header *hdr;
10737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const unsigned char *first_block;
1083faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy	const void *end;
10937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const char *firmware;
1107e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	const char *fw_err;
11144d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	struct device *dev = priv->dev;
11237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	int err = 0;
11337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
11437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	pda = kzalloc(fw->pda_size, GFP_KERNEL);
11537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (!pda)
11637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		return -ENOMEM;
11737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
11837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (ap)
11937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		firmware = fw->ap_fw;
12037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	else
12137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		firmware = fw->sta_fw;
12237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
12344d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "Attempting to download firmware %s\n", firmware);
12437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
12537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Read current plug data */
12607cefe7ac983374ee4c369f1d4aee3093bf3b44fDavid Kilroy	err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
12744d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "Read PDA returned %d\n", err);
12837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (err)
12937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		goto free;
13037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
1312bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (!orinoco_cached_fw_get(priv, false)) {
13237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		err = request_firmware(&fw_entry, firmware, priv->dev);
13337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
13437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (err) {
13544d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy			dev_err(dev, "Cannot find firmware %s\n", firmware);
13637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			err = -ENOENT;
13737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			goto free;
13837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		}
13937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	} else
1402bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov		fw_entry = orinoco_cached_fw_get(priv, false);
14137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
14237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	hdr = (const struct orinoco_fw_header *) fw_entry->data;
14337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
1447e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	fw_err = validate_fw(hdr, fw_entry->size);
1457e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	if (fw_err) {
14644d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy		dev_warn(dev, "Invalid firmware image detected (%s). "
14744d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy			 "Aborting download\n", fw_err);
1487e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		err = -EINVAL;
1497e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy		goto abort;
1507e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy	}
1517e57811ac5b595bdb53f2aef3bcb2b3d72663fa4David Kilroy
15237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Enable aux port to allow programming */
15307cefe7ac983374ee4c369f1d4aee3093bf3b44fDavid Kilroy	err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point));
15444d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "Program init returned %d\n", err);
15537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (err != 0)
15637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		goto abort;
15737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
15837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Program data */
15937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	first_block = (fw_entry->data +
16037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		       le16_to_cpu(hdr->headersize) +
16137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		       le32_to_cpu(hdr->block_offset));
16237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	end = fw_entry->data + fw_entry->size;
16337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
16437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	err = hermes_program(hw, first_block, end);
16544d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "Program returned %d\n", err);
16637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (err != 0)
16737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		goto abort;
16837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
16937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Update production data */
17037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	first_block = (fw_entry->data +
17137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		       le16_to_cpu(hdr->headersize) +
17237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		       le32_to_cpu(hdr->pdr_offset));
17337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
1743faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy	err = hermes_apply_pda_with_defaults(hw, first_block, end, pda,
1753faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy					     &pda[fw->pda_size / sizeof(*pda)]);
17644d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "Apply PDA returned %d\n", err);
17737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (err)
17837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		goto abort;
17937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
18037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Tell card we've finished */
18107cefe7ac983374ee4c369f1d4aee3093bf3b44fDavid Kilroy	err = hw->ops->program_end(hw);
18244d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "Program end returned %d\n", err);
18337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (err != 0)
18437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		goto abort;
18537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
18637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Check if we're running */
18744d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw));
18837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
18937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyabort:
19037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* If we requested the firmware, release it. */
1912bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (!orinoco_cached_fw_get(priv, false))
19237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		release_firmware(fw_entry);
19337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
19437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyfree:
19537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	kfree(pda);
19637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	return err;
19737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy}
19837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
19937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy/*
20037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * Process a firmware image - stop the card, load the firmware, reset
20137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * the card and make sure it responds.  For the secondary firmware take
20237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * care of the PDA - read it and then write it on top of the firmware.
20337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy */
20437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroystatic int
20537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroysymbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
2063faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy		const unsigned char *image, const void *end,
20737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		int secondary)
20837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy{
209933d594313a5928ffc5325d7bbb6e2383d79622ePavel Roskin	struct hermes *hw = &priv->hw;
21037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	int ret = 0;
21137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const unsigned char *ptr;
21237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const unsigned char *first_block;
21337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
21437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Plug Data Area (PDA) */
21537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	__le16 *pda = NULL;
21637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
21737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Binary block begins after the 0x1A marker */
21837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	ptr = image;
21937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	while (*ptr++ != TEXT_END);
22037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	first_block = ptr;
22137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
22237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Read the PDA from EEPROM */
22337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (secondary) {
22437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		pda = kzalloc(fw->pda_size, GFP_KERNEL);
22537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (!pda)
22637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			return -ENOMEM;
22737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
22807cefe7ac983374ee4c369f1d4aee3093bf3b44fDavid Kilroy		ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
22937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (ret)
23037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			goto free;
23137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
23237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
23337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Stop the firmware, so that it can be safely rewritten */
23437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (priv->stop_fw) {
23537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		ret = priv->stop_fw(priv, 1);
23637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (ret)
23737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			goto free;
23837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
23937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
24037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Program the adapter with new firmware */
24137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	ret = hermes_program(hw, first_block, end);
24237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (ret)
24337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		goto free;
24437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
24537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Write the PDA to the adapter */
24637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (secondary) {
2473faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy		size_t len = hermes_blocks_length(first_block, end);
24837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		ptr = first_block + len;
2493faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy		ret = hermes_apply_pda(hw, ptr, end, pda,
2503faa19cd9dfac30aa08bc311ddbd62ee5ccc0d85David Kilroy				       &pda[fw->pda_size / sizeof(*pda)]);
25137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		kfree(pda);
25237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (ret)
25337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			return ret;
25437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
25537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
25637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Run the firmware */
25737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (priv->stop_fw) {
25837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		ret = priv->stop_fw(priv, 0);
25937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (ret)
26037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			return ret;
26137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
26237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
26337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Reset hermes chip and make sure it responds */
264b42f2074dedef559ecf72dce61a6501f9f9b273aDavid Kilroy	ret = hw->ops->init(hw);
26537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
26637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* hermes_reset() should return 0 with the secondary firmware */
26737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (secondary && ret != 0)
26837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		return -ENODEV;
26937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
27037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* And this should work with any firmware */
27137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (!hermes_present(hw))
27237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		return -ENODEV;
27337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
27437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	return 0;
27537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
27637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyfree:
27737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	kfree(pda);
27837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	return ret;
27937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy}
28037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
28137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
28237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy/*
28337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * Download the firmware into the card, this also does a PCMCIA soft
28437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy * reset on the card, to make sure it's in a sane state.
28537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy */
28637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroystatic int
28737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroysymbol_dl_firmware(struct orinoco_private *priv,
28837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		   const struct fw_info *fw)
28937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy{
29044d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy	struct device *dev = priv->dev;
29137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	int ret;
29237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const struct firmware *fw_entry;
29337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
2942bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (!orinoco_cached_fw_get(priv, true)) {
29537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) {
29644d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy			dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw);
29737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			return -ENOENT;
29837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		}
29937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	} else
3002bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov		fw_entry = orinoco_cached_fw_get(priv, true);
30137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
30237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Load primary firmware */
30337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	ret = symbol_dl_image(priv, fw, fw_entry->data,
30437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			      fw_entry->data + fw_entry->size, 0);
30537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
3062bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (!orinoco_cached_fw_get(priv, true))
30737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		release_firmware(fw_entry);
30837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (ret) {
30944d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy		dev_err(dev, "Primary firmware download failed\n");
31037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		return ret;
31137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
31237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
3132bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (!orinoco_cached_fw_get(priv, false)) {
31437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) {
31544d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy			dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw);
31637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			return -ENOENT;
31737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		}
31837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	} else
3192bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov		fw_entry = orinoco_cached_fw_get(priv, false);
32037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
32137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Load secondary firmware */
32237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	ret = symbol_dl_image(priv, fw, fw_entry->data,
32337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			      fw_entry->data + fw_entry->size, 1);
3242bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov	if (!orinoco_cached_fw_get(priv, false))
32537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		release_firmware(fw_entry);
326933d594313a5928ffc5325d7bbb6e2383d79622ePavel Roskin	if (ret)
32744d8dade8f12ffe5c9b7eddd0512c1548c027a4cDavid Kilroy		dev_err(dev, "Secondary firmware download failed\n");
32837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
32937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	return ret;
33037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy}
33137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
33237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyint orinoco_download(struct orinoco_private *priv)
33337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy{
33437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	int err = 0;
33537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* Reload firmware */
33637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	switch (priv->firmware_type) {
33737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	case FIRMWARE_TYPE_AGERE:
33837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		/* case FIRMWARE_TYPE_INTERSIL: */
33937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		err = orinoco_dl_firmware(priv,
34037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy					  &orinoco_fw[priv->firmware_type], 0);
34137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		break;
34237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
34337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	case FIRMWARE_TYPE_SYMBOL:
34437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		err = symbol_dl_firmware(priv,
34537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy					 &orinoco_fw[priv->firmware_type]);
34637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		break;
34737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	case FIRMWARE_TYPE_INTERSIL:
34837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		break;
34937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
35037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	/* TODO: if we fail we probably need to reinitialise
35137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	 * the driver */
35237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
35337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	return err;
35437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy}
35537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
3562bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
35737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyvoid orinoco_cache_fw(struct orinoco_private *priv, int ap)
35837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy{
35937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const struct firmware *fw_entry = NULL;
36037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const char *pri_fw;
36137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	const char *fw;
36237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
36337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	pri_fw = orinoco_fw[priv->firmware_type].pri_fw;
36437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (ap)
36537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		fw = orinoco_fw[priv->firmware_type].ap_fw;
36637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	else
36737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		fw = orinoco_fw[priv->firmware_type].sta_fw;
36837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
36937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (pri_fw) {
37037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0)
37137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			priv->cached_pri_fw = fw_entry;
37237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
37337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
37437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (fw) {
37537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		if (request_firmware(&fw_entry, fw, priv->dev) == 0)
37637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy			priv->cached_fw = fw_entry;
37737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	}
37837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy}
37937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
38037a2e566f82de9a88fe119479162f9984af2180dDavid Kilroyvoid orinoco_uncache_fw(struct orinoco_private *priv)
38137a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy{
38237a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (priv->cached_pri_fw)
38337a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		release_firmware(priv->cached_pri_fw);
38437a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	if (priv->cached_fw)
38537a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy		release_firmware(priv->cached_fw);
38637a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy
38737a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	priv->cached_pri_fw = NULL;
38837a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy	priv->cached_fw = NULL;
38937a2e566f82de9a88fe119479162f9984af2180dDavid Kilroy}
3902bfc5cb57b55ed2204bca7668e082f7bf485760aAndrey Borzenkov#endif
391