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