162306a36Sopenharmony_ci/* hermes.c
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Driver core for the "Hermes" wireless MAC controller, as used in
462306a36Sopenharmony_ci * the Lucent Orinoco and Cabletron RoamAbout cards. It should also
562306a36Sopenharmony_ci * work on the hfa3841 and hfa3842 MAC controller chips used in the
662306a36Sopenharmony_ci * Prism II chipsets.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This is not a complete driver, just low-level access routines for
962306a36Sopenharmony_ci * the MAC controller itself.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Based on the prism2 driver from Absolute Value Systems' linux-wlan
1262306a36Sopenharmony_ci * project, the Linux wvlan_cs driver, Lucent's HCF-Light
1362306a36Sopenharmony_ci * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no
1462306a36Sopenharmony_ci * particular order).
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Copyright (C) 2000, David Gibson, Linuxcare Australia.
1762306a36Sopenharmony_ci * (C) Copyright David Gibson, IBM Corp. 2001-2003.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * The contents of this file are subject to the Mozilla Public License
2062306a36Sopenharmony_ci * Version 1.1 (the "License"); you may not use this file except in
2162306a36Sopenharmony_ci * compliance with the License. You may obtain a copy of the License
2262306a36Sopenharmony_ci * at http://www.mozilla.org/MPL/
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Software distributed under the License is distributed on an "AS IS"
2562306a36Sopenharmony_ci * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
2662306a36Sopenharmony_ci * the License for the specific language governing rights and
2762306a36Sopenharmony_ci * limitations under the License.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Alternatively, the contents of this file may be used under the
3062306a36Sopenharmony_ci * terms of the GNU General Public License version 2 (the "GPL"), in
3162306a36Sopenharmony_ci * which case the provisions of the GPL are applicable instead of the
3262306a36Sopenharmony_ci * above.  If you wish to allow the use of your version of this file
3362306a36Sopenharmony_ci * only under the terms of the GPL and not to allow others to use your
3462306a36Sopenharmony_ci * version of this file under the MPL, indicate your decision by
3562306a36Sopenharmony_ci * deleting the provisions above and replace them with the notice and
3662306a36Sopenharmony_ci * other provisions required by the GPL.  If you do not delete the
3762306a36Sopenharmony_ci * provisions above, a recipient may use your version of this file
3862306a36Sopenharmony_ci * under either the MPL or the GPL.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <linux/net.h>
4262306a36Sopenharmony_ci#include <linux/module.h>
4362306a36Sopenharmony_ci#include <linux/kernel.h>
4462306a36Sopenharmony_ci#include <linux/delay.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include "hermes.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* These are maximum timeouts. Most often, card wil react much faster */
4962306a36Sopenharmony_ci#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */
5062306a36Sopenharmony_ci#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */
5162306a36Sopenharmony_ci#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */
5262306a36Sopenharmony_ci#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * AUX port access.  To unlock the AUX port write the access keys to the
5662306a36Sopenharmony_ci * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
5762306a36Sopenharmony_ci * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci#define HERMES_AUX_ENABLE	0x8000	/* Enable auxiliary port access */
6062306a36Sopenharmony_ci#define HERMES_AUX_DISABLE	0x4000	/* Disable to auxiliary port access */
6162306a36Sopenharmony_ci#define HERMES_AUX_ENABLED	0xC000	/* Auxiliary port is open */
6262306a36Sopenharmony_ci#define HERMES_AUX_DISABLED	0x0000	/* Auxiliary port is closed */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define HERMES_AUX_PW0	0xFE01
6562306a36Sopenharmony_ci#define HERMES_AUX_PW1	0xDC23
6662306a36Sopenharmony_ci#define HERMES_AUX_PW2	0xBA45
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* HERMES_CMD_DOWNLD */
6962306a36Sopenharmony_ci#define HERMES_PROGRAM_DISABLE             (0x0000 | HERMES_CMD_DOWNLD)
7062306a36Sopenharmony_ci#define HERMES_PROGRAM_ENABLE_VOLATILE     (0x0100 | HERMES_CMD_DOWNLD)
7162306a36Sopenharmony_ci#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
7262306a36Sopenharmony_ci#define HERMES_PROGRAM_NON_VOLATILE        (0x0300 | HERMES_CMD_DOWNLD)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Debugging helpers
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \
7962306a36Sopenharmony_ci			printk(stuff); } while (0)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#undef HERMES_DEBUG
8262306a36Sopenharmony_ci#ifdef HERMES_DEBUG
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define DEBUG(lvl, stuff...) if ((lvl) <= HERMES_DEBUG) DMSG(stuff)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#else /* ! HERMES_DEBUG */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define DEBUG(lvl, stuff...) do { } while (0)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#endif /* ! HERMES_DEBUG */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic const struct hermes_ops hermes_ops_local;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * Internal functions
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Issue a command to the chip. Waiting for it to complete is the caller's
9962306a36Sopenharmony_ci   problem.
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci   Returns -EBUSY if the command register is busy, 0 on success.
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci   Callable from any context.
10462306a36Sopenharmony_ci*/
10562306a36Sopenharmony_cistatic int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0,
10662306a36Sopenharmony_ci			    u16 param1, u16 param2)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int k = CMD_BUSY_TIMEOUT;
10962306a36Sopenharmony_ci	u16 reg;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* First wait for the command register to unbusy */
11262306a36Sopenharmony_ci	reg = hermes_read_regn(hw, CMD);
11362306a36Sopenharmony_ci	while ((reg & HERMES_CMD_BUSY) && k) {
11462306a36Sopenharmony_ci		k--;
11562306a36Sopenharmony_ci		udelay(1);
11662306a36Sopenharmony_ci		reg = hermes_read_regn(hw, CMD);
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	if (reg & HERMES_CMD_BUSY)
11962306a36Sopenharmony_ci		return -EBUSY;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	hermes_write_regn(hw, PARAM2, param2);
12262306a36Sopenharmony_ci	hermes_write_regn(hw, PARAM1, param1);
12362306a36Sopenharmony_ci	hermes_write_regn(hw, PARAM0, param0);
12462306a36Sopenharmony_ci	hermes_write_regn(hw, CMD, cmd);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Function definitions
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/* For doing cmds that wipe the magic constant in SWSUPPORT0 */
13462306a36Sopenharmony_cistatic int hermes_doicmd_wait(struct hermes *hw, u16 cmd,
13562306a36Sopenharmony_ci			      u16 parm0, u16 parm1, u16 parm2,
13662306a36Sopenharmony_ci			      struct hermes_response *resp)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	int err = 0;
13962306a36Sopenharmony_ci	int k;
14062306a36Sopenharmony_ci	u16 status, reg;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2);
14362306a36Sopenharmony_ci	if (err)
14462306a36Sopenharmony_ci		return err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	reg = hermes_read_regn(hw, EVSTAT);
14762306a36Sopenharmony_ci	k = CMD_INIT_TIMEOUT;
14862306a36Sopenharmony_ci	while ((!(reg & HERMES_EV_CMD)) && k) {
14962306a36Sopenharmony_ci		k--;
15062306a36Sopenharmony_ci		udelay(10);
15162306a36Sopenharmony_ci		reg = hermes_read_regn(hw, EVSTAT);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!hermes_present(hw)) {
15762306a36Sopenharmony_ci		DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n",
15862306a36Sopenharmony_ci		       hw->iobase);
15962306a36Sopenharmony_ci		err = -ENODEV;
16062306a36Sopenharmony_ci		goto out;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (!(reg & HERMES_EV_CMD)) {
16462306a36Sopenharmony_ci		printk(KERN_ERR "hermes @ %p: "
16562306a36Sopenharmony_ci		       "Timeout waiting for card to reset (reg=0x%04x)!\n",
16662306a36Sopenharmony_ci		       hw->iobase, reg);
16762306a36Sopenharmony_ci		err = -ETIMEDOUT;
16862306a36Sopenharmony_ci		goto out;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	status = hermes_read_regn(hw, STATUS);
17262306a36Sopenharmony_ci	if (resp) {
17362306a36Sopenharmony_ci		resp->status = status;
17462306a36Sopenharmony_ci		resp->resp0 = hermes_read_regn(hw, RESP0);
17562306a36Sopenharmony_ci		resp->resp1 = hermes_read_regn(hw, RESP1);
17662306a36Sopenharmony_ci		resp->resp2 = hermes_read_regn(hw, RESP2);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (status & HERMES_STATUS_RESULT)
18262306a36Sopenharmony_ci		err = -EIO;
18362306a36Sopenharmony_ciout:
18462306a36Sopenharmony_ci	return err;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid hermes_struct_init(struct hermes *hw, void __iomem *address,
18862306a36Sopenharmony_ci			int reg_spacing)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	hw->iobase = address;
19162306a36Sopenharmony_ci	hw->reg_spacing = reg_spacing;
19262306a36Sopenharmony_ci	hw->inten = 0x0;
19362306a36Sopenharmony_ci	hw->eeprom_pda = false;
19462306a36Sopenharmony_ci	hw->ops = &hermes_ops_local;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ciEXPORT_SYMBOL(hermes_struct_init);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int hermes_init(struct hermes *hw)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u16 reg;
20162306a36Sopenharmony_ci	int err = 0;
20262306a36Sopenharmony_ci	int k;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* We don't want to be interrupted while resetting the chipset */
20562306a36Sopenharmony_ci	hw->inten = 0x0;
20662306a36Sopenharmony_ci	hermes_write_regn(hw, INTEN, 0);
20762306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, 0xffff);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Normally it's a "can't happen" for the command register to
21062306a36Sopenharmony_ci	   be busy when we go to issue a command because we are
21162306a36Sopenharmony_ci	   serializing all commands.  However we want to have some
21262306a36Sopenharmony_ci	   chance of resetting the card even if it gets into a stupid
21362306a36Sopenharmony_ci	   state, so we actually wait to see if the command register
21462306a36Sopenharmony_ci	   will unbusy itself here. */
21562306a36Sopenharmony_ci	k = CMD_BUSY_TIMEOUT;
21662306a36Sopenharmony_ci	reg = hermes_read_regn(hw, CMD);
21762306a36Sopenharmony_ci	while (k && (reg & HERMES_CMD_BUSY)) {
21862306a36Sopenharmony_ci		if (reg == 0xffff) /* Special case - the card has probably been
21962306a36Sopenharmony_ci				      removed, so don't wait for the timeout */
22062306a36Sopenharmony_ci			return -ENODEV;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		k--;
22362306a36Sopenharmony_ci		udelay(1);
22462306a36Sopenharmony_ci		reg = hermes_read_regn(hw, CMD);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* No need to explicitly handle the timeout - if we've timed
22862306a36Sopenharmony_ci	   out hermes_issue_cmd() will probably return -EBUSY below */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* According to the documentation, EVSTAT may contain
23162306a36Sopenharmony_ci	   obsolete event occurrence information.  We have to acknowledge
23262306a36Sopenharmony_ci	   it by writing EVACK. */
23362306a36Sopenharmony_ci	reg = hermes_read_regn(hw, EVSTAT);
23462306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, reg);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* We don't use hermes_docmd_wait here, because the reset wipes
23762306a36Sopenharmony_ci	   the magic constant in SWSUPPORT0 away, and it gets confused */
23862306a36Sopenharmony_ci	err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return err;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/* Issue a command to the chip, and (busy!) wait for it to
24462306a36Sopenharmony_ci * complete.
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * Returns:
24762306a36Sopenharmony_ci *     < 0 on internal error
24862306a36Sopenharmony_ci *       0 on success
24962306a36Sopenharmony_ci *     > 0 on error returned by the firmware
25062306a36Sopenharmony_ci *
25162306a36Sopenharmony_ci * Callable from any context, but locking is your problem. */
25262306a36Sopenharmony_cistatic int hermes_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0,
25362306a36Sopenharmony_ci			     struct hermes_response *resp)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int err;
25662306a36Sopenharmony_ci	int k;
25762306a36Sopenharmony_ci	u16 reg;
25862306a36Sopenharmony_ci	u16 status;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	err = hermes_issue_cmd(hw, cmd, parm0, 0, 0);
26162306a36Sopenharmony_ci	if (err) {
26262306a36Sopenharmony_ci		if (!hermes_present(hw)) {
26362306a36Sopenharmony_ci			if (net_ratelimit())
26462306a36Sopenharmony_ci				printk(KERN_WARNING "hermes @ %p: "
26562306a36Sopenharmony_ci				       "Card removed while issuing command "
26662306a36Sopenharmony_ci				       "0x%04x.\n", hw->iobase, cmd);
26762306a36Sopenharmony_ci			err = -ENODEV;
26862306a36Sopenharmony_ci		} else
26962306a36Sopenharmony_ci			if (net_ratelimit())
27062306a36Sopenharmony_ci				printk(KERN_ERR "hermes @ %p: "
27162306a36Sopenharmony_ci				       "Error %d issuing command 0x%04x.\n",
27262306a36Sopenharmony_ci				       hw->iobase, err, cmd);
27362306a36Sopenharmony_ci		goto out;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	reg = hermes_read_regn(hw, EVSTAT);
27762306a36Sopenharmony_ci	k = CMD_COMPL_TIMEOUT;
27862306a36Sopenharmony_ci	while ((!(reg & HERMES_EV_CMD)) && k) {
27962306a36Sopenharmony_ci		k--;
28062306a36Sopenharmony_ci		udelay(10);
28162306a36Sopenharmony_ci		reg = hermes_read_regn(hw, EVSTAT);
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (!hermes_present(hw)) {
28562306a36Sopenharmony_ci		printk(KERN_WARNING "hermes @ %p: Card removed "
28662306a36Sopenharmony_ci		       "while waiting for command 0x%04x completion.\n",
28762306a36Sopenharmony_ci		       hw->iobase, cmd);
28862306a36Sopenharmony_ci		err = -ENODEV;
28962306a36Sopenharmony_ci		goto out;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (!(reg & HERMES_EV_CMD)) {
29362306a36Sopenharmony_ci		printk(KERN_ERR "hermes @ %p: Timeout waiting for "
29462306a36Sopenharmony_ci		       "command 0x%04x completion.\n", hw->iobase, cmd);
29562306a36Sopenharmony_ci		err = -ETIMEDOUT;
29662306a36Sopenharmony_ci		goto out;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	status = hermes_read_regn(hw, STATUS);
30062306a36Sopenharmony_ci	if (resp) {
30162306a36Sopenharmony_ci		resp->status = status;
30262306a36Sopenharmony_ci		resp->resp0 = hermes_read_regn(hw, RESP0);
30362306a36Sopenharmony_ci		resp->resp1 = hermes_read_regn(hw, RESP1);
30462306a36Sopenharmony_ci		resp->resp2 = hermes_read_regn(hw, RESP2);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (status & HERMES_STATUS_RESULT)
31062306a36Sopenharmony_ci		err = -EIO;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci out:
31362306a36Sopenharmony_ci	return err;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int hermes_allocate(struct hermes *hw, u16 size, u16 *fid)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	int err = 0;
31962306a36Sopenharmony_ci	int k;
32062306a36Sopenharmony_ci	u16 reg;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if ((size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX))
32362306a36Sopenharmony_ci		return -EINVAL;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL);
32662306a36Sopenharmony_ci	if (err)
32762306a36Sopenharmony_ci		return err;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	reg = hermes_read_regn(hw, EVSTAT);
33062306a36Sopenharmony_ci	k = ALLOC_COMPL_TIMEOUT;
33162306a36Sopenharmony_ci	while ((!(reg & HERMES_EV_ALLOC)) && k) {
33262306a36Sopenharmony_ci		k--;
33362306a36Sopenharmony_ci		udelay(10);
33462306a36Sopenharmony_ci		reg = hermes_read_regn(hw, EVSTAT);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (!hermes_present(hw)) {
33862306a36Sopenharmony_ci		printk(KERN_WARNING "hermes @ %p: "
33962306a36Sopenharmony_ci		       "Card removed waiting for frame allocation.\n",
34062306a36Sopenharmony_ci		       hw->iobase);
34162306a36Sopenharmony_ci		return -ENODEV;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (!(reg & HERMES_EV_ALLOC)) {
34562306a36Sopenharmony_ci		printk(KERN_ERR "hermes @ %p: "
34662306a36Sopenharmony_ci		       "Timeout waiting for frame allocation\n",
34762306a36Sopenharmony_ci		       hw->iobase);
34862306a36Sopenharmony_ci		return -ETIMEDOUT;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	*fid = hermes_read_regn(hw, ALLOCFID);
35262306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/* Set up a BAP to read a particular chunk of data from card's internal buffer.
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci * Returns:
36062306a36Sopenharmony_ci *     < 0 on internal failure (errno)
36162306a36Sopenharmony_ci *       0 on success
36262306a36Sopenharmony_ci *     > 0 on error
36362306a36Sopenharmony_ci * from firmware
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * Callable from any context */
36662306a36Sopenharmony_cistatic int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;
36962306a36Sopenharmony_ci	int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
37062306a36Sopenharmony_ci	int k;
37162306a36Sopenharmony_ci	u16 reg;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* Paranoia.. */
37462306a36Sopenharmony_ci	if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2))
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	k = HERMES_BAP_BUSY_TIMEOUT;
37862306a36Sopenharmony_ci	reg = hermes_read_reg(hw, oreg);
37962306a36Sopenharmony_ci	while ((reg & HERMES_OFFSET_BUSY) && k) {
38062306a36Sopenharmony_ci		k--;
38162306a36Sopenharmony_ci		udelay(1);
38262306a36Sopenharmony_ci		reg = hermes_read_reg(hw, oreg);
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (reg & HERMES_OFFSET_BUSY)
38662306a36Sopenharmony_ci		return -ETIMEDOUT;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Now we actually set up the transfer */
38962306a36Sopenharmony_ci	hermes_write_reg(hw, sreg, id);
39062306a36Sopenharmony_ci	hermes_write_reg(hw, oreg, offset);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Wait for the BAP to be ready */
39362306a36Sopenharmony_ci	k = HERMES_BAP_BUSY_TIMEOUT;
39462306a36Sopenharmony_ci	reg = hermes_read_reg(hw, oreg);
39562306a36Sopenharmony_ci	while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {
39662306a36Sopenharmony_ci		k--;
39762306a36Sopenharmony_ci		udelay(1);
39862306a36Sopenharmony_ci		reg = hermes_read_reg(hw, oreg);
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (reg != offset) {
40262306a36Sopenharmony_ci		printk(KERN_ERR "hermes @ %p: BAP%d offset %s: "
40362306a36Sopenharmony_ci		       "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap,
40462306a36Sopenharmony_ci		       (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error",
40562306a36Sopenharmony_ci		       reg, id, offset);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		if (reg & HERMES_OFFSET_BUSY)
40862306a36Sopenharmony_ci			return -ETIMEDOUT;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		return -EIO;		/* error or wrong offset */
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return 0;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/* Read a block of data from the chip's buffer, via the
41762306a36Sopenharmony_ci * BAP. Synchronization/serialization is the caller's problem.  len
41862306a36Sopenharmony_ci * must be even.
41962306a36Sopenharmony_ci *
42062306a36Sopenharmony_ci * Returns:
42162306a36Sopenharmony_ci *     < 0 on internal failure (errno)
42262306a36Sopenharmony_ci *       0 on success
42362306a36Sopenharmony_ci *     > 0 on error from firmware
42462306a36Sopenharmony_ci */
42562306a36Sopenharmony_cistatic int hermes_bap_pread(struct hermes *hw, int bap, void *buf, int len,
42662306a36Sopenharmony_ci			    u16 id, u16 offset)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
42962306a36Sopenharmony_ci	int err = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if ((len < 0) || (len % 2))
43262306a36Sopenharmony_ci		return -EINVAL;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	err = hermes_bap_seek(hw, bap, id, offset);
43562306a36Sopenharmony_ci	if (err)
43662306a36Sopenharmony_ci		goto out;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* Actually do the transfer */
43962306a36Sopenharmony_ci	hermes_read_words(hw, dreg, buf, len / 2);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci out:
44262306a36Sopenharmony_ci	return err;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci/* Write a block of data to the chip's buffer, via the
44662306a36Sopenharmony_ci * BAP. Synchronization/serialization is the caller's problem.
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * Returns:
44962306a36Sopenharmony_ci *     < 0 on internal failure (errno)
45062306a36Sopenharmony_ci *       0 on success
45162306a36Sopenharmony_ci *     > 0 on error from firmware
45262306a36Sopenharmony_ci */
45362306a36Sopenharmony_cistatic int hermes_bap_pwrite(struct hermes *hw, int bap, const void *buf,
45462306a36Sopenharmony_ci			     int len, u16 id, u16 offset)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
45762306a36Sopenharmony_ci	int err = 0;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (len < 0)
46062306a36Sopenharmony_ci		return -EINVAL;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	err = hermes_bap_seek(hw, bap, id, offset);
46362306a36Sopenharmony_ci	if (err)
46462306a36Sopenharmony_ci		goto out;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Actually do the transfer */
46762306a36Sopenharmony_ci	hermes_write_bytes(hw, dreg, buf, len);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci out:
47062306a36Sopenharmony_ci	return err;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/* Read a Length-Type-Value record from the card.
47462306a36Sopenharmony_ci *
47562306a36Sopenharmony_ci * If length is NULL, we ignore the length read from the card, and
47662306a36Sopenharmony_ci * read the entire buffer regardless. This is useful because some of
47762306a36Sopenharmony_ci * the configuration records appear to have incorrect lengths in
47862306a36Sopenharmony_ci * practice.
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * Callable from user or bh context.  */
48162306a36Sopenharmony_cistatic int hermes_read_ltv(struct hermes *hw, int bap, u16 rid,
48262306a36Sopenharmony_ci			   unsigned bufsize, u16 *length, void *buf)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	int err = 0;
48562306a36Sopenharmony_ci	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
48662306a36Sopenharmony_ci	u16 rlength, rtype;
48762306a36Sopenharmony_ci	unsigned nwords;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (bufsize % 2)
49062306a36Sopenharmony_ci		return -EINVAL;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL);
49362306a36Sopenharmony_ci	if (err)
49462306a36Sopenharmony_ci		return err;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	err = hermes_bap_seek(hw, bap, rid, 0);
49762306a36Sopenharmony_ci	if (err)
49862306a36Sopenharmony_ci		return err;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	rlength = hermes_read_reg(hw, dreg);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!rlength)
50362306a36Sopenharmony_ci		return -ENODATA;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	rtype = hermes_read_reg(hw, dreg);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (length)
50862306a36Sopenharmony_ci		*length = rlength;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (rtype != rid)
51162306a36Sopenharmony_ci		printk(KERN_WARNING "hermes @ %p: %s(): "
51262306a36Sopenharmony_ci		       "rid (0x%04x) does not match type (0x%04x)\n",
51362306a36Sopenharmony_ci		       hw->iobase, __func__, rid, rtype);
51462306a36Sopenharmony_ci	if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize)
51562306a36Sopenharmony_ci		printk(KERN_WARNING "hermes @ %p: "
51662306a36Sopenharmony_ci		       "Truncating LTV record from %d to %d bytes. "
51762306a36Sopenharmony_ci		       "(rid=0x%04x, len=0x%04x)\n", hw->iobase,
51862306a36Sopenharmony_ci		       HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	nwords = min((unsigned)rlength - 1, bufsize / 2);
52162306a36Sopenharmony_ci	hermes_read_words(hw, dreg, buf, nwords);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int hermes_write_ltv(struct hermes *hw, int bap, u16 rid,
52762306a36Sopenharmony_ci			    u16 length, const void *value)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
53062306a36Sopenharmony_ci	int err = 0;
53162306a36Sopenharmony_ci	unsigned count;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (length == 0)
53462306a36Sopenharmony_ci		return -EINVAL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	err = hermes_bap_seek(hw, bap, rid, 0);
53762306a36Sopenharmony_ci	if (err)
53862306a36Sopenharmony_ci		return err;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	hermes_write_reg(hw, dreg, length);
54162306a36Sopenharmony_ci	hermes_write_reg(hw, dreg, rid);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	count = length - 1;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	hermes_write_bytes(hw, dreg, value, count << 1);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
54862306a36Sopenharmony_ci				rid, NULL);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return err;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/*** Hermes AUX control ***/
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic inline void
55662306a36Sopenharmony_cihermes_aux_setaddr(struct hermes *hw, u32 addr)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
55962306a36Sopenharmony_ci	hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic inline int
56362306a36Sopenharmony_cihermes_aux_control(struct hermes *hw, int enabled)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
56662306a36Sopenharmony_ci	int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
56762306a36Sopenharmony_ci	int i;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* Already open? */
57062306a36Sopenharmony_ci	if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
57162306a36Sopenharmony_ci		return 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
57462306a36Sopenharmony_ci	hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
57562306a36Sopenharmony_ci	hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
57662306a36Sopenharmony_ci	hermes_write_reg(hw, HERMES_CONTROL, action);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	for (i = 0; i < 20; i++) {
57962306a36Sopenharmony_ci		udelay(10);
58062306a36Sopenharmony_ci		if (hermes_read_reg(hw, HERMES_CONTROL) ==
58162306a36Sopenharmony_ci		    desired_state)
58262306a36Sopenharmony_ci			return 0;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return -EBUSY;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/*** Hermes programming ***/
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/* About to start programming data (Hermes I)
59162306a36Sopenharmony_ci * offset is the entry point
59262306a36Sopenharmony_ci *
59362306a36Sopenharmony_ci * Spectrum_cs' Symbol fw does not require this
59462306a36Sopenharmony_ci * wl_lkm Agere fw does
59562306a36Sopenharmony_ci * Don't know about intersil
59662306a36Sopenharmony_ci */
59762306a36Sopenharmony_cistatic int hermesi_program_init(struct hermes *hw, u32 offset)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	int err;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Disable interrupts?*/
60262306a36Sopenharmony_ci	/*hw->inten = 0x0;*/
60362306a36Sopenharmony_ci	/*hermes_write_regn(hw, INTEN, 0);*/
60462306a36Sopenharmony_ci	/*hermes_set_irqmask(hw, 0);*/
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* Acknowledge any outstanding command */
60762306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, 0xFFFF);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	/* Using init_cmd_wait rather than cmd_wait */
61062306a36Sopenharmony_ci	err = hw->ops->init_cmd_wait(hw,
61162306a36Sopenharmony_ci				     0x0100 | HERMES_CMD_INIT,
61262306a36Sopenharmony_ci				     0, 0, 0, NULL);
61362306a36Sopenharmony_ci	if (err)
61462306a36Sopenharmony_ci		return err;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	err = hw->ops->init_cmd_wait(hw,
61762306a36Sopenharmony_ci				     0x0000 | HERMES_CMD_INIT,
61862306a36Sopenharmony_ci				     0, 0, 0, NULL);
61962306a36Sopenharmony_ci	if (err)
62062306a36Sopenharmony_ci		return err;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	err = hermes_aux_control(hw, 1);
62362306a36Sopenharmony_ci	pr_debug("AUX enable returned %d\n", err);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (err)
62662306a36Sopenharmony_ci		return err;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	pr_debug("Enabling volatile, EP 0x%08x\n", offset);
62962306a36Sopenharmony_ci	err = hw->ops->init_cmd_wait(hw,
63062306a36Sopenharmony_ci				     HERMES_PROGRAM_ENABLE_VOLATILE,
63162306a36Sopenharmony_ci				     offset & 0xFFFFu,
63262306a36Sopenharmony_ci				     offset >> 16,
63362306a36Sopenharmony_ci				     0,
63462306a36Sopenharmony_ci				     NULL);
63562306a36Sopenharmony_ci	pr_debug("PROGRAM_ENABLE returned %d\n", err);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return err;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/* Done programming data (Hermes I)
64162306a36Sopenharmony_ci *
64262306a36Sopenharmony_ci * Spectrum_cs' Symbol fw does not require this
64362306a36Sopenharmony_ci * wl_lkm Agere fw does
64462306a36Sopenharmony_ci * Don't know about intersil
64562306a36Sopenharmony_ci */
64662306a36Sopenharmony_cistatic int hermesi_program_end(struct hermes *hw)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct hermes_response resp;
64962306a36Sopenharmony_ci	int rc = 0;
65062306a36Sopenharmony_ci	int err;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	pr_debug("PROGRAM_DISABLE returned %d, "
65562306a36Sopenharmony_ci		 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
65662306a36Sopenharmony_ci		 rc, resp.resp0, resp.resp1, resp.resp2);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if ((rc == 0) &&
65962306a36Sopenharmony_ci	    ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
66062306a36Sopenharmony_ci		rc = -EIO;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	err = hermes_aux_control(hw, 0);
66362306a36Sopenharmony_ci	pr_debug("AUX disable returned %d\n", err);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* Acknowledge any outstanding command */
66662306a36Sopenharmony_ci	hermes_write_regn(hw, EVACK, 0xFFFF);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Reinitialise, ignoring return */
66962306a36Sopenharmony_ci	(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
67062306a36Sopenharmony_ci				      0, 0, 0, NULL);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return rc ? rc : err;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int hermes_program_bytes(struct hermes *hw, const char *data,
67662306a36Sopenharmony_ci				u32 addr, u32 len)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	/* wl lkm splits the programming into chunks of 2000 bytes.
67962306a36Sopenharmony_ci	 * This restriction appears to come from USB. The PCMCIA
68062306a36Sopenharmony_ci	 * adapters can program the whole lot in one go */
68162306a36Sopenharmony_ci	hermes_aux_setaddr(hw, addr);
68262306a36Sopenharmony_ci	hermes_write_bytes(hw, HERMES_AUXDATA, data, len);
68362306a36Sopenharmony_ci	return 0;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci/* Read PDA from the adapter */
68762306a36Sopenharmony_cistatic int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr,
68862306a36Sopenharmony_ci			   u16 pda_len)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	int ret;
69162306a36Sopenharmony_ci	u16 pda_size;
69262306a36Sopenharmony_ci	u16 data_len = pda_len;
69362306a36Sopenharmony_ci	__le16 *data = pda;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (hw->eeprom_pda) {
69662306a36Sopenharmony_ci		/* PDA of spectrum symbol is in eeprom */
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		/* Issue command to read EEPROM */
69962306a36Sopenharmony_ci		ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
70062306a36Sopenharmony_ci		if (ret)
70162306a36Sopenharmony_ci			return ret;
70262306a36Sopenharmony_ci	} else {
70362306a36Sopenharmony_ci		/* wl_lkm does not include PDA size in the PDA area.
70462306a36Sopenharmony_ci		 * We will pad the information into pda, so other routines
70562306a36Sopenharmony_ci		 * don't have to be modified */
70662306a36Sopenharmony_ci		pda[0] = cpu_to_le16(pda_len - 2);
70762306a36Sopenharmony_ci			/* Includes CFG_PROD_DATA but not itself */
70862306a36Sopenharmony_ci		pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
70962306a36Sopenharmony_ci		data_len = pda_len - 4;
71062306a36Sopenharmony_ci		data = pda + 2;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/* Open auxiliary port */
71462306a36Sopenharmony_ci	ret = hermes_aux_control(hw, 1);
71562306a36Sopenharmony_ci	pr_debug("AUX enable returned %d\n", ret);
71662306a36Sopenharmony_ci	if (ret)
71762306a36Sopenharmony_ci		return ret;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Read PDA */
72062306a36Sopenharmony_ci	hermes_aux_setaddr(hw, pda_addr);
72162306a36Sopenharmony_ci	hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Close aux port */
72462306a36Sopenharmony_ci	ret = hermes_aux_control(hw, 0);
72562306a36Sopenharmony_ci	pr_debug("AUX disable returned %d\n", ret);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* Check PDA length */
72862306a36Sopenharmony_ci	pda_size = le16_to_cpu(pda[0]);
72962306a36Sopenharmony_ci	pr_debug("Actual PDA length %d, Max allowed %d\n",
73062306a36Sopenharmony_ci		 pda_size, pda_len);
73162306a36Sopenharmony_ci	if (pda_size > pda_len)
73262306a36Sopenharmony_ci		return -EINVAL;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return 0;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic void hermes_lock_irqsave(spinlock_t *lock,
73862306a36Sopenharmony_ci				unsigned long *flags) __acquires(lock)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	spin_lock_irqsave(lock, *flags);
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic void hermes_unlock_irqrestore(spinlock_t *lock,
74462306a36Sopenharmony_ci				     unsigned long *flags) __releases(lock)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, *flags);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic void hermes_lock_irq(spinlock_t *lock) __acquires(lock)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	spin_lock_irq(lock);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void hermes_unlock_irq(spinlock_t *lock) __releases(lock)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	spin_unlock_irq(lock);
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci/* Hermes operations for local buses */
76062306a36Sopenharmony_cistatic const struct hermes_ops hermes_ops_local = {
76162306a36Sopenharmony_ci	.init = hermes_init,
76262306a36Sopenharmony_ci	.cmd_wait = hermes_docmd_wait,
76362306a36Sopenharmony_ci	.init_cmd_wait = hermes_doicmd_wait,
76462306a36Sopenharmony_ci	.allocate = hermes_allocate,
76562306a36Sopenharmony_ci	.read_ltv = hermes_read_ltv,
76662306a36Sopenharmony_ci	.read_ltv_pr = hermes_read_ltv,
76762306a36Sopenharmony_ci	.write_ltv = hermes_write_ltv,
76862306a36Sopenharmony_ci	.bap_pread = hermes_bap_pread,
76962306a36Sopenharmony_ci	.bap_pwrite = hermes_bap_pwrite,
77062306a36Sopenharmony_ci	.read_pda = hermes_read_pda,
77162306a36Sopenharmony_ci	.program_init = hermesi_program_init,
77262306a36Sopenharmony_ci	.program_end = hermesi_program_end,
77362306a36Sopenharmony_ci	.program = hermes_program_bytes,
77462306a36Sopenharmony_ci	.lock_irqsave = hermes_lock_irqsave,
77562306a36Sopenharmony_ci	.unlock_irqrestore = hermes_unlock_irqrestore,
77662306a36Sopenharmony_ci	.lock_irq = hermes_lock_irq,
77762306a36Sopenharmony_ci	.unlock_irq = hermes_unlock_irq,
77862306a36Sopenharmony_ci};
779