2008-12-16 03:31:40

by Colin McCabe

[permalink] [raw]
Subject: [PATCH 0/3] libertas: driver for GSPI devices


This patch series adds support for GSPI devices to libertas.
These devices are found in some embedded systems. We used a blackfin-based
board for development.

Since the blackfin has specific alignment requirements, this patch set includes
alignment fixes.

[PATCH 1/3] libertas: Fix alignment issues in libertas core
[PATCH 2/3] libertas: Update libertas core with GSPI constants
[PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Sincerely,
Colin


2008-12-19 16:10:07

by Harald Welte

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Hi Colin,

thanks a lot for your patch, it is greatly appreciated.

Stefan Schmidt (see Cc) and myself are currently working on using a 8686
attached t o SPI of a S3C2442 SoC.

Let me make some comments on the actual code:

On Mon, Dec 15, 2008 at 07:27:32PM -0800, Colin McCabe wrote:

> +struct libertas_spi_platform_data {
> + u16 host_irq_num;
> +
> + u16 gpio_cs;
> +};

> + gpio_set_value(card->gpio_cs, 0); /* assert CS */
> + gpio_set_value(card->gpio_cs, 1); /* drop CS */

Typically, in the Linux SPI framework, it is the SPI host controller
driver that exercises the chipselect GPIO, not the client (device) driver.

Also, the host_irq_num is typically passed by the '.irq' member of
spi_board_info structure, since it is board specific data (see e.g.
arch/arm/mach-pxa/spitz.c)

Unless there is a specific reason that I'm missing, it would probably be a
good idea to follow the general conventions on this.

Cheers,
--
- Harald Welte <[email protected]> http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
(ETSI EN 300 175-7 Ch. A6)


Attachments:
(No filename) (1.21 kB)
signature.asc (189.00 B)
Digital signature
Download all attachments

2008-12-17 11:12:26

by Peter Wippich

[permalink] [raw]
Subject: Re: [PATCH 0/3] libertas: driver for GSPI devices


Hi Andrey,

this is very recent. Good, so I can test it also here for our PXA
implementation. If it gets publicly available it will help many embedded
system implementers who want to use the 8686.

Chears,

Peter

On Tue, 16 Dec 2008, Andrey Yurovsky wrote:

> Hi Peter. The firmware we used is fw 9.70.3p37 and its cap mask is 0x303.
>
> We expect this firmware to be available for download soon. Thanks,
>
> On Tue, Dec 16, 2008 at 7:56 AM, Peter Wippich <[email protected]> wrote:
> >
> > Hi Colin,
> >
> >> This patch series adds support for GSPI devices to libertas.
> >> These devices are found in some embedded systems. We used a blackfin-based
> >> board for development.
> >>
> >> Since the blackfin has specific alignment requirements, this patch set
> >> includes alignment fixes.
> >
> > Thanks for this one. As you mentioned somewhere else SPI Firmware for 8686
> > is not publicly available. However, can you tell me with which firmware
> > Version you tested this ??
> >
> > Thank you and best regards,
> >
> > Peter
> >
> >
> > | Peter Wippich Voice: +49 30 46776411 |
> > | G&W Instruments GmbH fax: +49 30 46776419 |
> > | Gustav-Meyer-Allee 25, Geb. 12 Email: [email protected] |
> > | D-13355 Berlin / Germany |
> >
> >
>
>
>
> --
> Andrey Yurovsky
> cozybit Inc.
>
> _______________________________________________
> libertas-dev mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/libertas-dev
>


| Peter Wippich Voice: +49 30 46776411 |
| G&W Instruments GmbH fax: +49 30 46776419 |
| Gustav-Meyer-Allee 25, Geb. 12 Email: [email protected] |
| D-13355 Berlin / Germany |


2008-12-19 16:31:15

by Andrey Yurovsky

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Thanks Harald,

On Fri, Dec 19, 2008 at 8:08 AM, Harald Welte <[email protected]> wrote:
> Hi Colin,
>
> thanks a lot for your patch, it is greatly appreciated.
>
> Stefan Schmidt (see Cc) and myself are currently working on using a 8686
> attached t o SPI of a S3C2442 SoC.
>
> Let me make some comments on the actual code:
>
> On Mon, Dec 15, 2008 at 07:27:32PM -0800, Colin McCabe wrote:
>
>> +struct libertas_spi_platform_data {
>> + u16 host_irq_num;
>> +
>> + u16 gpio_cs;
>> +};
>
>> + gpio_set_value(card->gpio_cs, 0); /* assert CS */
>> + gpio_set_value(card->gpio_cs, 1); /* drop CS */
>
> Typically, in the Linux SPI framework, it is the SPI host controller
> driver that exercises the chipselect GPIO, not the client (device) driver.

The '8686 requires us to assert CS# and then hold it asserted for the
entire transaction. The transaction is an arbitrary number of SPI
transfers. Most host controllers cannot do this (they'll toggle CS#
between individual transactions) and therefore we chose to not use the
controller CS# line and use a GPIO. Can you suggest a different way
to do this given the current SPI framework?

> Also, the host_irq_num is typically passed by the '.irq' member of
> spi_board_info structure, since it is board specific data (see e.g.
> arch/arm/mach-pxa/spitz.c)

The IRQ in question is a GPIO line asserted by the '8686 and it is not
otherwise an SPI line, however I see now that it should be set in
spi_board_info as you mention, we'll fix that.

-Andrey

2008-12-23 20:25:43

by Colin McCabe

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Hi Peter,

If you look in include/linux/spi/spi.h, there is a comment that says:
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/

So the chip select features that our hardware needs (don't drop
chipselect after each word, busy-waits during the transaction) are not
yet part of the generic SPI layer. But they are "TBD."

I agree that we should move them into the generic SPI layer. Maybe
I'll come up with a patch to the SPI layer itself to do exactly that,
once I get back from vacation.

cheers,
Colin.


On Fri, Dec 19, 2008 at 8:57 AM, Peter Wippich <[email protected]> wrote:
>
> Hi Andrey,
>
>> The '8686 requires us to assert CS# and then hold it asserted for the
>> entire transaction. The transaction is an arbitrary number of SPI
>> transfers. Most host controllers cannot do this (they'll toggle CS#
>> between individual transactions) and therefore we chose to not use the
>> controller CS# line and use a GPIO. Can you suggest a different way
>> to do this given the current SPI framework?
>
> This can be handled by the SPI driver. See pxa2xx_spi.c as an example.
> Look for cs_control() to get the picture.....
> Perhaps this should go to the general driver and not be handled by the
> chip specific code. Seems a problem general enough.
>
> Chears,
>
> Peter
>

2008-12-19 17:00:29

by Peter Wippich

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices


Hi Andrey,

> The '8686 requires us to assert CS# and then hold it asserted for the
> entire transaction. The transaction is an arbitrary number of SPI
> transfers. Most host controllers cannot do this (they'll toggle CS#
> between individual transactions) and therefore we chose to not use the
> controller CS# line and use a GPIO. Can you suggest a different way
> to do this given the current SPI framework?

This can be handled by the SPI driver. See pxa2xx_spi.c as an example.
Look for cs_control() to get the picture.....
Perhaps this should go to the general driver and not be handled by the
chip specific code. Seems a problem general enough.

Chears,

Peter

2008-12-16 18:12:40

by Angel Roman

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices



Dan Williams wrote:
> On Mon, 2008-12-15 at 22:44 -0500, Angel Roman wrote:
>> Hi Colin,
>>
>> I have support for the gspi as well. I've been trying to get in contact
>> with Dan Williams in order to contribute it to the list.
>
> Sorry about that... it's in my queue and I'll try to get to do some
> review in the next few days. Doing the new interface isn't a ton of
> code, and I'd expect both yours and Colin's drivers to be quite similar
> as there's only a few ways this thing can be done :)
>
> The submission process is basically just like Colin did; generate a
> series of patches of your latest code (split into independent patches if
> possible) based on a kernel version (ideally the latest kernel version
> or better yet, wireless-testing.git) and then post it to linux-wireless
> and maybe cc libertas-dev as well.
Thanks, will do. I look forward to combining our efforts.
>
>> If you want, you can take a look at the code via:
>>
>> svn list -R
>> svn://svn.buglabs.net/bug/trunk/bug-linux-2.6.27.2/drivers/net/wireless/libertas
>>
>>
>>
>> This is currently working in the mx31 processor. Maybe we can work out a
>> way to merge the two drivers. The mx31 was a little tricky since there's
>> an error in the processor where one is not able to keep the chip select
>> signal active during multiple spi transfers as requried by the wifi
>> module. I've also abstracted the board interface from the GSPI code as
>> much as I could.
>
> Is there a generic SPI layer that could be used for the board-specific
> bits too, rather than putting that stuff in the libertas tree? I assume
> that the SPI bus is more or less generic on your hardware (ie you could
> put something else on the other end instead of the 8686), and thus it
> would be better if we could figure out way not to put some much board
> specific logic into the libertas driver itself.
Yes, the spi libertas core driver in buglabs' tree does not depend on
any particular spi implementation. The board specific code - if_bmi.c
and if_bmi.h - in my code registers a call back function with the gspi
core which performs the actual spi transfer. if_bmi.c and if_bmi.h
could go in another driver and does not need to go into libertas,
however, the core seems like a good fit for the libertas tree.

>
> Dan
>
>
> _______________________________________________
> libertas-dev mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/libertas-dev
>

2008-12-16 16:56:52

by Andrey Yurovsky

[permalink] [raw]
Subject: Re: [PATCH 0/3] libertas: driver for GSPI devices

Hi Peter. The firmware we used is fw 9.70.3p37 and its cap mask is 0x303.

We expect this firmware to be available for download soon. Thanks,

On Tue, Dec 16, 2008 at 7:56 AM, Peter Wippich <[email protected]> wrote:
>
> Hi Colin,
>
>> This patch series adds support for GSPI devices to libertas.
>> These devices are found in some embedded systems. We used a blackfin-based
>> board for development.
>>
>> Since the blackfin has specific alignment requirements, this patch set
>> includes alignment fixes.
>
> Thanks for this one. As you mentioned somewhere else SPI Firmware for 8686
> is not publicly available. However, can you tell me with which firmware
> Version you tested this ??
>
> Thank you and best regards,
>
> Peter
>
>
> | Peter Wippich Voice: +49 30 46776411 |
> | G&W Instruments GmbH fax: +49 30 46776419 |
> | Gustav-Meyer-Allee 25, Geb. 12 Email: [email protected] |
> | D-13355 Berlin / Germany |
>
>



--
Andrey Yurovsky
cozybit Inc.

2008-12-16 16:27:51

by Peter Wippich

[permalink] [raw]
Subject: Re: [PATCH 0/3] libertas: driver for GSPI devices


Hi Colin,

> This patch series adds support for GSPI devices to libertas.
> These devices are found in some embedded systems. We used a blackfin-based
> board for development.
>
> Since the blackfin has specific alignment requirements, this patch set
> includes alignment fixes.

Thanks for this one. As you mentioned somewhere else SPI Firmware for 8686
is not publicly available. However, can you tell me with which firmware
Version you tested this ??

Thank you and best regards,

Peter


| Peter Wippich Voice: +49 30 46776411 |
| G&W Instruments GmbH fax: +49 30 46776419 |
| Gustav-Meyer-Allee 25, Geb. 12 Email: [email protected] |
| D-13355 Berlin / Germany |


2008-12-16 03:31:47

by Colin McCabe

[permalink] [raw]
Subject: [PATCH 2/3] libertas: Update libertas core with GSPI constants

Add GSPI constants to libertas core.

Fix misleading comment in lbs_setup_firmware.

Signed-off-by: Colin McCabe <[email protected]>
Signed-off-by: Andrey Yurovsky <[email protected]>
---
drivers/net/wireless/libertas/host.h | 1 +
drivers/net/wireless/libertas/hostcmd.h | 8 ++++++++
drivers/net/wireless/libertas/main.c | 5 ++---
3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index a17b778..b2b617f 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -66,6 +66,7 @@
#define CMD_802_11_LED_GPIO_CTRL 0x004e
#define CMD_802_11_EEPROM_ACCESS 0x0059
#define CMD_802_11_BAND_CONFIG 0x0058
+#define CMD_GSPI_BUS_CONFIG 0x005a
#define CMD_802_11D_DOMAIN_INFO 0x005b
#define CMD_802_11_KEY_MATERIAL 0x005e
#define CMD_802_11_SLEEP_PARAMS 0x0066
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
index c6d5771..3c15611 100644
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -221,6 +221,14 @@ struct cmd_ds_mac_multicast_adr {
u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
} __attribute__ ((packed));

+struct cmd_ds_gspi_bus_config {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 bus_delay_mode;
+ __le16 host_time_delay_to_read_port;
+ __le16 host_time_delay_to_read_register;
+} __attribute__ ((packed));
+
struct cmd_ds_802_11_authenticate {
u8 macaddr[ETH_ALEN];
u8 authtype;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index a00d232..cc36a54 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -951,9 +951,8 @@ void lbs_resume(struct lbs_private *priv)
EXPORT_SYMBOL_GPL(lbs_resume);

/**
- * @brief This function downloads firmware image, gets
- * HW spec from firmware and set basic parameters to
- * firmware.
+ * @brief This function gets the HW spec from the firmware and sets
+ * some basic parameters.
*
* @param priv A pointer to struct lbs_private structure
* @return 0 or -1
--
1.5.6.3


2008-12-16 03:31:45

by Colin McCabe

[permalink] [raw]
Subject: [PATCH 1/3] libertas: Fix alignment issues in libertas core

When loading fields from packed structs, use get_unaligned_le16 and
get_unaligned_le32 instead of le16_to_cpu and le32_to_cpu.
This doesn't matter on x86, but it is important for some other CPUs.

Also, mark all packed structs as packed. Generally, any data structure
that comes over the wire from the WLAN firmware should be a packed struct.

Signed-off-by: Colin McCabe <[email protected]>
Signed-off-by: Andrey Yurovsky <[email protected]>
---
drivers/net/wireless/libertas/11d.c | 19 ++++---
drivers/net/wireless/libertas/assoc.c | 11 ++--
drivers/net/wireless/libertas/cmd.c | 87 +++++++++++++++------------
drivers/net/wireless/libertas/cmdresp.c | 54 +++++++++++------
drivers/net/wireless/libertas/debugfs.c | 7 +-
drivers/net/wireless/libertas/ethtool.c | 19 ++++---
drivers/net/wireless/libertas/hostcmd.h | 89 ++++++++++++++--------------
drivers/net/wireless/libertas/main.c | 12 +++-
drivers/net/wireless/libertas/persistcfg.c | 5 +-
drivers/net/wireless/libertas/rx.c | 1 +
drivers/net/wireless/libertas/scan.c | 11 ++--
drivers/net/wireless/libertas/tx.c | 7 ++-
drivers/net/wireless/libertas/wext.c | 7 +-
13 files changed, 186 insertions(+), 143 deletions(-)

diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c
index 4bc46a6..c0e4a3c 100644
--- a/drivers/net/wireless/libertas/11d.c
+++ b/drivers/net/wireless/libertas/11d.c
@@ -1,6 +1,7 @@
/**
* This file contains functions for 802.11D.
*/
+#include <asm/unaligned.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/wireless.h>
@@ -515,7 +516,7 @@ int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
cmd->size =
cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
- le16_to_cpu(cmd->size));
+ get_unaligned_le16(&cmd->size));
goto done;
}

@@ -532,15 +533,16 @@ int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
nr_subband * sizeof(struct ieeetypes_subbandset));

cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
- le16_to_cpu(domain->header.len) +
- sizeof(struct mrvlietypesheader) +
- S_DS_GEN);
+ get_unaligned_le16(&domain->header.len) +
+ sizeof(struct mrvlietypesheader) +
+ S_DS_GEN);
} else {
cmd->size =
cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
}

- lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size));
+ lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
+ get_unaligned_le16(&cmd->size));

done:
lbs_deb_enter(LBS_DEB_11D);
@@ -557,16 +559,17 @@ int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp)
{
struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp;
struct mrvlietypes_domainparamset *domain = &domaininfo->domain;
- u16 action = le16_to_cpu(domaininfo->action);
+ u16 action = get_unaligned_le16(&domaininfo->action);
s16 ret = 0;
u8 nr_subband = 0;

lbs_deb_enter(LBS_DEB_11D);

lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
- (int)le16_to_cpu(resp->size));
+ (int)get_unaligned_le16(&resp->size));

- nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
+ nr_subband = (get_unaligned_le16(&domain->header.len) -
+ COUNTRY_CODE_LEN) /
sizeof(struct ieeetypes_subbandset);

lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband);
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
index a9aedf3..c43a14f 100644
--- a/drivers/net/wireless/libertas/assoc.c
+++ b/drivers/net/wireless/libertas/assoc.c
@@ -1,5 +1,6 @@
/* Copyright (C) 2006, Red Hat, Inc. */

+#include <asm/unaligned.h>
#include <linux/types.h>
#include <linux/etherdevice.h>
#include <linux/ieee80211.h>
@@ -264,7 +265,7 @@ static int lbs_adhoc_join(struct lbs_private *priv,
cmd.bss.ssparamset.ibssparamset.atimwindow = cpu_to_le16(bss->atimwindow);

if (assoc_req->secinfo.wep_enabled) {
- u16 tmp = le16_to_cpu(cmd.bss.capability);
+ u16 tmp = get_unaligned_le16(&cmd.bss.capability);
tmp |= WLAN_CAPABILITY_PRIVACY;
cmd.bss.capability = cpu_to_le16(tmp);
}
@@ -1707,7 +1708,7 @@ int lbs_ret_80211_associate(struct lbs_private *priv,
* association response from the AP)
*/

- status_code = le16_to_cpu(passocrsp->statuscode);
+ status_code = get_unaligned_le16(&passocrsp->statuscode);
switch (status_code) {
case 0x00:
break;
@@ -1739,7 +1740,7 @@ int lbs_ret_80211_associate(struct lbs_private *priv,
}

lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params,
- le16_to_cpu(resp->size) - S_DS_GEN);
+ get_unaligned_le16(&resp->size) - S_DS_GEN);

/* Send a Media Connected event, according to the Spec */
priv->connect_status = LBS_CONNECTED;
@@ -1773,8 +1774,8 @@ done:
static int lbs_adhoc_post(struct lbs_private *priv, struct cmd_header *resp)
{
int ret = 0;
- u16 command = le16_to_cpu(resp->command);
- u16 result = le16_to_cpu(resp->result);
+ u16 command = get_unaligned_le16(&resp->command);
+ u16 result = get_unaligned_le16(&resp->result);
struct cmd_ds_802_11_ad_hoc_result *adhoc_resp;
union iwreq_data wrqu;
struct bss_descriptor *bss;
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index ac73050..7c77d00 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -3,6 +3,7 @@
* It prepares command and sends it to firmware when it is ready.
*/

+#include <asm/unaligned.h>
#include <net/iw_handler.h>
#include <net/lib80211.h>
#include <linux/kfifo.h>
@@ -34,7 +35,8 @@ int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
struct cmd_header *buf = (void *)extra;
uint16_t copy_len;

- copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
+ copy_len = min(get_unaligned_le16(&buf->size),
+ get_unaligned_le16(&resp->size));
memcpy(buf, resp, copy_len);
return 0;
}
@@ -98,11 +100,11 @@ int lbs_update_hw_spec(struct lbs_private *priv)
if (ret)
goto out;

- priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
+ priv->fwcapinfo = get_unaligned_le32(&cmd.fwcapinfo);

/* The firmware release is in an interesting format: the patch
* level is in the most significant nibble ... so fix that: */
- priv->fwrelease = le32_to_cpu(cmd.fwrelease);
+ priv->fwrelease = get_unaligned_le32(&cmd.fwrelease);
priv->fwrelease = (priv->fwrelease << 8) |
(priv->fwrelease >> 24 & 0xff);

@@ -124,7 +126,7 @@ int lbs_update_hw_spec(struct lbs_private *priv)
* only ever be 8-bit, even though the field size is 16-bit. Some firmware
* returns non-zero high 8 bits here.
*/
- priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
+ priv->regioncode = get_unaligned_le16(&cmd.regioncode) & 0xFF;

for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
/* use the region code to search for the index */
@@ -253,7 +255,7 @@ int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
ret = lbs_cmd_with_response(priv, CMD_802_11_INACTIVITY_TIMEOUT, &cmd);

if (!ret)
- *timeout = le16_to_cpu(cmd.timeout);
+ *timeout = get_unaligned_le16(&cmd.timeout);

lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return 0;
@@ -285,16 +287,17 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
if (!ret) {
lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, "
"calcontrol 0x%x extsleepclk 0x%x\n",
- le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset),
- le16_to_cpu(cmd.stabletime), cmd.calcontrol,
- cmd.externalsleepclk);
-
- sp->sp_error = le16_to_cpu(cmd.error);
- sp->sp_offset = le16_to_cpu(cmd.offset);
- sp->sp_stabletime = le16_to_cpu(cmd.stabletime);
+ get_unaligned_le16(&cmd.error),
+ get_unaligned_le16(&cmd.offset),
+ get_unaligned_le16(&cmd.stabletime),
+ cmd.calcontrol, cmd.externalsleepclk);
+
+ sp->sp_error = get_unaligned_le16(&cmd.error);
+ sp->sp_offset = get_unaligned_le16(&cmd.offset);
+ sp->sp_stabletime = get_unaligned_le16(&cmd.stabletime);
sp->sp_calcontrol = cmd.calcontrol;
sp->sp_extsleepclk = cmd.externalsleepclk;
- sp->sp_reserved = le16_to_cpu(cmd.reserved);
+ sp->sp_reserved = get_unaligned_le16(&cmd.reserved);
}

lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
@@ -385,7 +388,7 @@ int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,

ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
if (!ret && cmd_action == CMD_ACT_GET)
- *enable = le16_to_cpu(cmd.enable);
+ *enable = get_unaligned_le16(&cmd.enable);

lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
@@ -455,10 +458,14 @@ int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
while (buf_ptr < resp_end) {
struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
struct enc_key *key;
- uint16_t param_set_len = le16_to_cpu(keyparam->length);
- uint16_t key_len = le16_to_cpu(keyparam->keylen);
- uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
- uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
+ uint16_t param_set_len =
+ get_unaligned_le16(&keyparam->length);
+ uint16_t key_len =
+ get_unaligned_le16(&keyparam->keylen);
+ uint16_t key_flags =
+ get_unaligned_le16(&keyparam->keyinfo);
+ uint16_t key_type =
+ get_unaligned_le16(&keyparam->keytypeid);
void *end;

end = (void *)keyparam + sizeof(keyparam->type)
@@ -533,7 +540,8 @@ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
}

lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n",
- le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val);
+ get_unaligned_le16(&cmd.oid),
+ get_unaligned_le16(&cmd.bufsize), val);

ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);

@@ -567,7 +575,7 @@ int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
if (ret)
goto out;

- switch (le16_to_cpu(cmd.bufsize)) {
+ switch (get_unaligned_le16(&cmd.bufsize)) {
case sizeof(u8):
if (oid == SNMP_MIB_OID_BSS_TYPE) {
if (cmd.value[0] == 2)
@@ -582,7 +590,7 @@ int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
break;
default:
lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n",
- oid, le16_to_cpu(cmd.bufsize));
+ oid, get_unaligned_le16(&cmd.bufsize));
break;
}

@@ -615,7 +623,7 @@ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,

ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
if (ret == 0) {
- *curlevel = le16_to_cpu(cmd.curlevel);
+ *curlevel = get_unaligned_le16(&cmd.curlevel);
if (minlevel)
*minlevel = cmd.minlevel;
if (maxlevel)
@@ -719,8 +727,8 @@ int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto);
ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd);
if (!ret && cmd_action == CMD_ACT_GET) {
- priv->ratebitmap = le16_to_cpu(cmd.bitmap);
- priv->enablehwauto = le16_to_cpu(cmd.enablehwauto);
+ priv->ratebitmap = get_unaligned_le16(&cmd.bitmap);
+ priv->enablehwauto = get_unaligned_le16(&cmd.enablehwauto);
}

lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
@@ -800,7 +808,7 @@ int lbs_get_channel(struct lbs_private *priv)
if (ret)
goto out;

- ret = le16_to_cpu(cmd.channel);
+ ret = get_unaligned_le16(&cmd.channel);
lbs_deb_cmd("current radio channel is %d\n", ret);

out:
@@ -851,7 +859,7 @@ int lbs_set_channel(struct lbs_private *priv, u8 channel)
if (ret)
goto out;

- priv->curbssparams.channel = (uint8_t) le16_to_cpu(cmd.channel);
+ priv->curbssparams.channel = (uint8_t)get_unaligned_le16(&cmd.channel);
lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
priv->curbssparams.channel);

@@ -890,7 +898,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,

offval = (struct lbs_offset_value *)pdata_buf;

- switch (le16_to_cpu(cmdptr->command)) {
+ switch (get_unaligned_le16(&cmdptr->command)) {
case CMD_MAC_REG_ACCESS:
{
struct cmd_ds_mac_reg_access *macreg;
@@ -1151,7 +1159,8 @@ static void lbs_queue_cmd(struct lbs_private *priv,
cmdnode->result = 0;

/* Exit_PS command needs to be queued in the header always. */
- if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) {
+ if (get_unaligned_le16(&cmdnode->cmdbuf->command) ==
+ CMD_802_11_PS_MODE) {
struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf[1];

if (psm->action == cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
@@ -1170,7 +1179,7 @@ static void lbs_queue_cmd(struct lbs_private *priv,
spin_unlock_irqrestore(&priv->driver_lock, flags);

lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
- le16_to_cpu(cmdnode->cmdbuf->command));
+ get_unaligned_le16(&cmdnode->cmdbuf->command));

done:
lbs_deb_leave(LBS_DEB_HOST);
@@ -1195,8 +1204,8 @@ static void lbs_submit_command(struct lbs_private *priv,
priv->cur_cmd_retcode = 0;
spin_unlock_irqrestore(&priv->driver_lock, flags);

- cmdsize = le16_to_cpu(cmd->size);
- command = le16_to_cpu(cmd->command);
+ cmdsize = get_unaligned_le16(&cmd->size);
+ command = get_unaligned_le16(&cmd->command);

/* These commands take longer */
if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE ||
@@ -1204,7 +1213,7 @@ static void lbs_submit_command(struct lbs_private *priv,
timeo = 5 * HZ;

lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
- command, le16_to_cpu(cmd->seqnum), cmdsize);
+ command, get_unaligned_le16(&cmd->seqnum), cmdsize);
lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);

ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
@@ -1462,7 +1471,7 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,

#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8
cmdptr->size =
- cpu_to_le16(le16_to_cpu(gpio->header.len)
+ cpu_to_le16(get_unaligned_le16(&gpio->header.len)
+ S_DS_GEN
+ ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
gpio->header.len = gpio->header.len;
@@ -1687,19 +1696,21 @@ int lbs_execute_next_command(struct lbs_private *priv)
if (cmdnode) {
cmd = cmdnode->cmdbuf;

- if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) {
+ if (is_command_allowed_in_ps(
+ get_unaligned_le16(&cmd->command))) {
if ((priv->psstate == PS_STATE_SLEEP) ||
(priv->psstate == PS_STATE_PRE_SLEEP)) {
lbs_deb_host(
"EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n",
- le16_to_cpu(cmd->command),
+ get_unaligned_le16(&cmd->command),
priv->psstate);
ret = -1;
goto done;
}
lbs_deb_host("EXEC_NEXT_CMD: OK to send command "
- "0x%04x in psstate %d\n",
- le16_to_cpu(cmd->command), priv->psstate);
+ "0x%04x in psstate %d\n",
+ get_unaligned_le16(&cmd->command),
+ priv->psstate);
} else if (priv->psstate != PS_STATE_FULL_POWER) {
/*
* 1. Non-PS command:
@@ -1769,7 +1780,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
}
list_del(&cmdnode->list);
lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
- le16_to_cpu(cmd->command));
+ get_unaligned_le16(&cmd->command));
lbs_submit_command(priv, cmdnode);
} else {
/*
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index bcf2a97..14f54ad 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -2,6 +2,7 @@
* This file contains the handling of command
* responses as well as events generated by firmware.
*/
+#include <asm/unaligned.h>
#include <linux/delay.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
@@ -115,8 +116,10 @@ static int lbs_ret_reg_access(struct lbs_private *priv,
{
struct cmd_ds_mac_reg_access *reg = &resp->params.macreg;

- priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
- priv->offsetvalue.value = le32_to_cpu(reg->value);
+ priv->offsetvalue.offset =
+ (u32)get_unaligned_le16(&reg->offset);
+ priv->offsetvalue.value =
+ get_unaligned_le32(&reg->value);
break;
}

@@ -124,7 +127,8 @@ static int lbs_ret_reg_access(struct lbs_private *priv,
{
struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg;

- priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
+ priv->offsetvalue.offset =
+ (u32)get_unaligned_le16(&reg->offset);
priv->offsetvalue.value = reg->value;
break;
}
@@ -133,7 +137,8 @@ static int lbs_ret_reg_access(struct lbs_private *priv,
{
struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg;

- priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
+ priv->offsetvalue.offset =
+ (u32)get_unaligned_le16(&reg->offset);
priv->offsetvalue.value = reg->value;
break;
}
@@ -154,11 +159,15 @@ static int lbs_ret_802_11_rssi(struct lbs_private *priv,
lbs_deb_enter(LBS_DEB_CMD);

/* store the non average value */
- priv->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR);
- priv->NF[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->noisefloor);
+ priv->SNR[TYPE_BEACON][TYPE_NOAVG] =
+ get_unaligned_le16(&rssirsp->SNR);
+ priv->NF[TYPE_BEACON][TYPE_NOAVG] =
+ get_unaligned_le16(&rssirsp->noisefloor);

- priv->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR);
- priv->NF[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgnoisefloor);
+ priv->SNR[TYPE_BEACON][TYPE_AVG] =
+ get_unaligned_le16(&rssirsp->avgSNR);
+ priv->NF[TYPE_BEACON][TYPE_AVG] =
+ get_unaligned_le16(&rssirsp->avgnoisefloor);

priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
@@ -185,8 +194,10 @@ static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv,
lbs_deb_enter(LBS_DEB_CMD);

if (bcn_ctrl->action == CMD_ACT_GET) {
- priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
- priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
+ priv->beacon_enable =
+ (u8) get_unaligned_le16(&bcn_ctrl->beacon_enable);
+ priv->beacon_period =
+ get_unaligned_le16(&bcn_ctrl->beacon_period);
}

lbs_deb_enter(LBS_DEB_CMD);
@@ -199,7 +210,7 @@ static inline int handle_cmd_response(struct lbs_private *priv,
struct cmd_ds_command *resp = (struct cmd_ds_command *) cmd_response;
int ret = 0;
unsigned long flags;
- uint16_t respcmd = le16_to_cpu(resp->command);
+ uint16_t respcmd = get_unaligned_le16(&resp->command);

lbs_deb_enter(LBS_DEB_HOST);

@@ -276,7 +287,7 @@ static inline int handle_cmd_response(struct lbs_private *priv,

default:
lbs_pr_err("CMD_RESP: unknown cmd response 0x%04x\n",
- le16_to_cpu(resp->command));
+ get_unaligned_le16(&resp->command));
break;
}
lbs_deb_leave(LBS_DEB_HOST);
@@ -304,17 +315,19 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
}

resp = (void *)data;
- curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
- respcmd = le16_to_cpu(resp->command);
- result = le16_to_cpu(resp->result);
+ curcmd = get_unaligned_le16(&priv->cur_cmd->cmdbuf->command);
+ respcmd = get_unaligned_le16(&resp->command);
+ result = get_unaligned_le16(&resp->result);

lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n",
- respcmd, le16_to_cpu(resp->seqnum), len);
+ respcmd, get_unaligned_le16(&resp->seqnum), len);
lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);

if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
lbs_pr_info("Received CMD_RESP with invalid sequence %d (expected %d)\n",
- le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
+ get_unaligned_le16(&resp->seqnum),
+ get_unaligned_le16(&priv->cur_cmd->cmdbuf->
+ seqnum));
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
goto done;
@@ -330,8 +343,9 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
if (resp->result == cpu_to_le16(0x0004)) {
/* 0x0004 means -EAGAIN. Drop the response, let it time out
and be resubmitted */
- lbs_pr_info("Firmware returns DEFER to command %x. Will let it time out...\n",
- le16_to_cpu(resp->command));
+ lbs_pr_info("Firmware returns DEFER to command %x. "
+ "Will let it time out...\n",
+ get_unaligned_le16(&resp->command));
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
goto done;
@@ -351,7 +365,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)

if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
- u16 action = le16_to_cpu(psmode->action);
+ u16 action = get_unaligned_le16(&psmode->action);

lbs_deb_host(
"CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index de3c6ed..d1ed78c 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -1,3 +1,4 @@
+#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/dcache.h>
#include <linux/debugfs.h>
@@ -195,7 +196,7 @@ static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size)
return NULL;
if (tlv_h->type == cpu_to_le16(tlv_type))
return tlv_h;
- length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h);
+ length = get_unaligned_le16(&tlv_h->len) + sizeof(*tlv_h);
pos += length;
tlv += length;
}
@@ -238,7 +239,7 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
if (got) {
value = got->value;
freq = got->freq;
- events = le16_to_cpu(subscribed->events);
+ events = get_unaligned_le16(&subscribed->events);

pos += snprintf(buf, len, "%d %d %d\n", value, freq,
!!(events & event_mask));
@@ -296,7 +297,7 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
if (ret)
goto out_events;

- curr_mask = le16_to_cpu(events->events);
+ curr_mask = get_unaligned_le16(&events->events);

if (new_mask)
new_mask = curr_mask | event_mask;
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
index 73b414f..807f2ae 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -1,3 +1,4 @@
+#include <asm/unaligned.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
@@ -90,14 +91,16 @@ static void lbs_ethtool_get_stats(struct net_device *dev,
return;
}

- priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
- priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
- priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
- priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
- priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
- priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
- priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
- priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
+ priv->mstats.fwd_drop_rbt = get_unaligned_le32(&mesh_access.data[0]);
+ priv->mstats.fwd_drop_ttl = get_unaligned_le32(&mesh_access.data[1]);
+ priv->mstats.fwd_drop_noroute =
+ get_unaligned_le32(&mesh_access.data[2]);
+ priv->mstats.fwd_drop_nobuf = get_unaligned_le32(&mesh_access.data[3]);
+ priv->mstats.fwd_unicast_cnt =
+ get_unaligned_le32(&mesh_access.data[4]);
+ priv->mstats.fwd_bcast_cnt = get_unaligned_le32(&mesh_access.data[5]);
+ priv->mstats.drop_blind = get_unaligned_le32(&mesh_access.data[6]);
+ priv->mstats.tx_failed_cnt = get_unaligned_le32(&mesh_access.data[7]);

data[0] = priv->mstats.fwd_drop_rbt;
data[1] = priv->mstats.fwd_drop_ttl;
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
index e173b1b..c6d5771 100644
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -32,7 +32,7 @@ struct txpd {
u8 pktdelay_2ms;
/* reserved */
u8 reserved1;
-};
+} __attribute__ ((packed));

/* RxPD Descriptor */
struct rxpd {
@@ -63,7 +63,7 @@ struct rxpd {
/* Pkt Priority */
u8 priority;
u8 reserved[3];
-};
+} __attribute__ ((packed));

struct cmd_header {
__le16 command;
@@ -107,7 +107,7 @@ struct cmd_ds_gen {
__le16 seqnum;
__le16 result;
void *cmdresp[0];
-};
+} __attribute__ ((packed));

#define S_DS_GEN sizeof(struct cmd_ds_gen)

@@ -163,7 +163,7 @@ struct cmd_ds_802_11_subscribe_event {
* bump this up a bit.
*/
uint8_t tlv[128];
-};
+} __attribute__ ((packed));

/*
* This scan handle Country Information IE(802.11d compliant)
@@ -180,7 +180,7 @@ struct cmd_ds_802_11_scan {
mrvlietypes_chanlistparamset_t ChanListParamSet;
mrvlietypes_ratesparamset_t OpRateSet;
#endif
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_scan_rsp {
struct cmd_header hdr;
@@ -188,7 +188,7 @@ struct cmd_ds_802_11_scan_rsp {
__le16 bssdescriptsize;
uint8_t nr_sets;
uint8_t bssdesc_and_tlvbuffer[0];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_get_log {
struct cmd_header hdr;
@@ -206,33 +206,33 @@ struct cmd_ds_802_11_get_log {
__le32 fcserror;
__le32 txframe;
__le32 wepundecryptable;
-};
+} __attribute__ ((packed));

struct cmd_ds_mac_control {
struct cmd_header hdr;
__le16 action;
u16 reserved;
-};
+} __attribute__ ((packed));

struct cmd_ds_mac_multicast_adr {
struct cmd_header hdr;
__le16 action;
__le16 nr_of_adrs;
u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_authenticate {
u8 macaddr[ETH_ALEN];
u8 authtype;
u8 reserved[10];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_deauthenticate {
struct cmd_header hdr;

u8 macaddr[ETH_ALEN];
__le16 reasoncode;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_associate {
u8 peerstaaddr[6];
@@ -251,7 +251,7 @@ struct cmd_ds_802_11_associate {

struct cmd_ds_802_11_associate_rsp {
struct ieeetypes_assocrsp assocRsp;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_set_wep {
struct cmd_header hdr;
@@ -265,7 +265,7 @@ struct cmd_ds_802_11_set_wep {
/* 40, 128bit or TXWEP */
uint8_t keytype[4];
uint8_t keymaterial[4][16];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_3_get_stat {
__le32 xmitok;
@@ -274,7 +274,7 @@ struct cmd_ds_802_3_get_stat {
__le32 rcverror;
__le32 rcvnobuffer;
__le32 rcvcrcerror;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_get_stat {
__le32 txfragmentcnt;
@@ -294,7 +294,7 @@ struct cmd_ds_802_11_get_stat {
__le32 txbeacon;
__le32 rxbeacon;
__le32 wepundecryptable;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_snmp_mib {
struct cmd_header hdr;
@@ -303,58 +303,58 @@ struct cmd_ds_802_11_snmp_mib {
__le16 oid;
__le16 bufsize;
u8 value[128];
-};
+} __attribute__ ((packed));

struct cmd_ds_mac_reg_map {
__le16 buffersize;
u8 regmap[128];
__le16 reserved;
-};
+} __attribute__ ((packed));

struct cmd_ds_bbp_reg_map {
__le16 buffersize;
u8 regmap[128];
__le16 reserved;
-};
+} __attribute__ ((packed));

struct cmd_ds_rf_reg_map {
__le16 buffersize;
u8 regmap[64];
__le16 reserved;
-};
+} __attribute__ ((packed));

struct cmd_ds_mac_reg_access {
__le16 action;
__le16 offset;
__le32 value;
-};
+} __attribute__ ((packed));

struct cmd_ds_bbp_reg_access {
__le16 action;
__le16 offset;
u8 value;
u8 reserved[3];
-};
+} __attribute__ ((packed));

struct cmd_ds_rf_reg_access {
__le16 action;
__le16 offset;
u8 value;
u8 reserved[3];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_radio_control {
struct cmd_header hdr;

__le16 action;
__le16 control;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_beacon_control {
__le16 action;
__le16 beacon_enable;
__le16 beacon_period;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_sleep_params {
struct cmd_header hdr;
@@ -379,7 +379,7 @@ struct cmd_ds_802_11_sleep_params {

/* reserved field, should be set to zero */
__le16 reserved;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_inactivity_timeout {
struct cmd_header hdr;
@@ -389,7 +389,7 @@ struct cmd_ds_802_11_inactivity_timeout {

/* Inactivity timeout in msec */
__le16 timeout;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_rf_channel {
struct cmd_header hdr;
@@ -399,7 +399,7 @@ struct cmd_ds_802_11_rf_channel {
__le16 rftype; /* unused */
__le16 reserved; /* unused */
u8 channellist[32]; /* unused */
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_rssi {
/* weighting factor */
@@ -408,21 +408,21 @@ struct cmd_ds_802_11_rssi {
__le16 reserved_0;
__le16 reserved_1;
__le16 reserved_2;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_rssi_rsp {
__le16 SNR;
__le16 noisefloor;
__le16 avgSNR;
__le16 avgnoisefloor;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_mac_address {
struct cmd_header hdr;

__le16 action;
u8 macadd[ETH_ALEN];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_rf_tx_power {
struct cmd_header hdr;
@@ -431,7 +431,7 @@ struct cmd_ds_802_11_rf_tx_power {
__le16 curlevel;
s8 maxlevel;
s8 minlevel;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_rf_antenna {
__le16 action;
@@ -439,33 +439,33 @@ struct cmd_ds_802_11_rf_antenna {
/* Number of antennas or 0xffff(diversity) */
__le16 antennamode;

-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_monitor_mode {
__le16 action;
__le16 mode;
-};
+} __attribute__ ((packed));

struct cmd_ds_set_boot2_ver {
struct cmd_header hdr;

__le16 action;
__le16 version;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_fw_wake_method {
struct cmd_header hdr;

__le16 action;
__le16 method;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_sleep_period {
struct cmd_header hdr;

__le16 action;
__le16 period;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_ps_mode {
__le16 action;
@@ -473,7 +473,7 @@ struct cmd_ds_802_11_ps_mode {
__le16 multipledtim;
__le16 reserved;
__le16 locallisteninterval;
-};
+} __attribute__ ((packed));

struct cmd_confirm_sleep {
struct cmd_header hdr;
@@ -483,7 +483,7 @@ struct cmd_confirm_sleep {
__le16 multipledtim;
__le16 reserved;
__le16 locallisteninterval;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_data_rate {
struct cmd_header hdr;
@@ -491,14 +491,14 @@ struct cmd_ds_802_11_data_rate {
__le16 action;
__le16 reserved;
u8 rates[MAX_RATES];
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_rate_adapt_rateset {
struct cmd_header hdr;
__le16 action;
__le16 enablehwauto;
__le16 bitmap;
-};
+} __attribute__ ((packed));

struct cmd_ds_802_11_ad_hoc_start {
struct cmd_header hdr;
@@ -520,7 +520,7 @@ struct cmd_ds_802_11_ad_hoc_result {

u8 pad[3];
u8 bssid[ETH_ALEN];
-};
+} __attribute__ ((packed));

struct adhoc_bssdesc {
u8 bssid[ETH_ALEN];
@@ -578,7 +578,7 @@ struct MrvlIEtype_keyParamSet {

/* key material of size keylen */
u8 key[32];
-};
+} __attribute__ ((packed));

#define MAX_WOL_RULES 16

@@ -590,7 +590,7 @@ struct host_wol_rule {
__le16 reserve;
__be32 sig_mask;
__be32 signature;
-};
+} __attribute__ ((packed));

struct wol_config {
uint8_t action;
@@ -598,8 +598,7 @@ struct wol_config {
uint8_t no_rules_in_cmd;
uint8_t result;
struct host_wol_rule rule[MAX_WOL_RULES];
-};
-
+} __attribute__ ((packed));

struct cmd_ds_host_sleep {
struct cmd_header hdr;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index c320264..a00d232 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -4,6 +4,7 @@
* thread etc..
*/

+#include <asm/unaligned.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
@@ -232,7 +233,8 @@ static ssize_t lbs_anycast_get(struct device *dev,
if (ret)
return ret;

- return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
+ return snprintf(buf, 12, "0x%X\n",
+ get_unaligned_le32(&mesh_access.data[0]));
}

/**
@@ -784,7 +786,8 @@ static int lbs_thread(void *data)
if (++priv->nr_retries > 3) {
lbs_pr_info("Excessive timeouts submitting "
"command 0x%04x\n",
- le16_to_cpu(cmdnode->cmdbuf->command));
+ get_unaligned_le16(&cmdnode->cmdbuf->
+ command));
lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
priv->nr_retries = 0;
if (priv->reset_card)
@@ -794,7 +797,8 @@ static int lbs_thread(void *data)
priv->dnld_sent = DNLD_RES_RECEIVED;
lbs_pr_info("requeueing command 0x%04x due "
"to timeout (#%d)\n",
- le16_to_cpu(cmdnode->cmdbuf->command),
+ get_unaligned_le16(&cmdnode->cmdbuf->
+ command),
priv->nr_retries);

/* Stick it back at the _top_ of the pending queue
@@ -997,7 +1001,7 @@ static void command_timer_fn(unsigned long data)
goto out;

lbs_pr_info("command 0x%04x timed out\n",
- le16_to_cpu(priv->cur_cmd->cmdbuf->command));
+ get_unaligned_le16(&priv->cur_cmd->cmdbuf->command));

priv->cmd_timed_out = 1;
wake_up_interruptible(&priv->waitq);
diff --git a/drivers/net/wireless/libertas/persistcfg.c b/drivers/net/wireless/libertas/persistcfg.c
index 56e2401..a098ee5 100644
--- a/drivers/net/wireless/libertas/persistcfg.c
+++ b/drivers/net/wireless/libertas/persistcfg.c
@@ -1,3 +1,4 @@
+#include <asm/unaligned.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
@@ -48,7 +49,7 @@ static ssize_t bootflag_get(struct device *dev,
if (ret)
return ret;

- return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
+ return snprintf(buf, 12, "%d\n", get_unaligned_le32(&defs.bootflag));
}

/**
@@ -143,7 +144,7 @@ static ssize_t channel_get(struct device *dev,
if (ret)
return ret;

- return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
+ return snprintf(buf, 12, "%d\n", get_unaligned_le16(&defs.channel));
}

/**
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
index 079e6aa..5798373 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/libertas/rx.c
@@ -1,6 +1,7 @@
/**
* This file contains the handling of RX in wlan driver.
*/
+#include <asm/unaligned.h>
#include <linux/etherdevice.h>
#include <linux/types.h>

diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index b0a2bdd..78e6af4 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -4,10 +4,10 @@
* IOCTL handlers as well as command preperation and response routines
* for sending scan commands to the firmware.
*/
+#include <asm/unaligned.h>
#include <linux/types.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
-#include <asm/unaligned.h>
#include <net/lib80211.h>

#include "host.h"
@@ -331,7 +331,7 @@ static int lbs_do_scan(struct lbs_private *priv, uint8_t bsstype,
tlv - scan_cmd->tlvbuffer);

ret = __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr,
- le16_to_cpu(scan_cmd->hdr.size),
+ get_unaligned_le16(&scan_cmd->hdr.size),
lbs_ret_80211_scan, 0);

out:
@@ -641,7 +641,8 @@ static int lbs_process_bss(struct bss_descriptor *bss,

case WLAN_EID_IBSS_PARAMS:
pibss = (struct ieeetypes_ibssparamset *) pos;
- bss->atimwindow = le16_to_cpu(pibss->atimwindow);
+ bss->atimwindow =
+ get_unaligned_le16(&pibss->atimwindow);
memmove(&bss->ssparamset.ibssparamset, pibss,
sizeof(struct ieeetypes_ibssparamset));
lbs_deb_scan("got IBSS IE\n");
@@ -1132,10 +1133,10 @@ static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
goto done;
}

- bytesleft = le16_to_cpu(scanresp->bssdescriptsize);
+ bytesleft = get_unaligned_le16(&scanresp->bssdescriptsize);
lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft);

- scanrespsize = le16_to_cpu(resp->size);
+ scanrespsize = get_unaligned_le16(&resp->size);
lbs_deb_scan("SCAN_RESP: scan results %d\n", scanresp->nr_sets);

bssinfo = scanresp->bssdesc_and_tlvbuffer;
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
index a4972fe..213a1a7 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/libertas/tx.c
@@ -1,6 +1,7 @@
/**
* This file contains the handling of TX in wlan driver.
*/
+#include <asm/unaligned.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

@@ -137,9 +138,11 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)

lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd));

- lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length));
+ lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr,
+ get_unaligned_le16(&txpd->tx_packet_length));

- memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length));
+ memcpy(&txpd[1], p802x_hdr,
+ get_unaligned_le16(&txpd->tx_packet_length));

spin_lock_irqsave(&priv->driver_lock, flags);
priv->tx_pending_len = pkt_len + sizeof(struct txpd);
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
index 0d98343..e305a36 100644
--- a/drivers/net/wireless/libertas/wext.c
+++ b/drivers/net/wireless/libertas/wext.c
@@ -1,6 +1,7 @@
/**
* This file contains ioctl functions
*/
+#include <asm/unaligned.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/if.h>
@@ -836,7 +837,7 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
log.hdr.size = cpu_to_le16(sizeof(log));
lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);

- tx_retries = le32_to_cpu(log.retry);
+ tx_retries = get_unaligned_le32(&log.retry);

if (tx_retries > 75)
tx_qual = (90 - tx_retries) * POOR / 15;
@@ -852,9 +853,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
(PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
quality = min(quality, tx_qual);

- priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable);
+ priv->wstats.discard.code = get_unaligned_le32(&log.wepundecryptable);
priv->wstats.discard.retries = tx_retries;
- priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
+ priv->wstats.discard.misc = get_unaligned_le32(&log.ackfailure);

/* Calculate quality */
priv->wstats.qual.qual = min_t(u8, quality, 100);
--
1.5.6.3


2008-12-16 03:31:51

by Colin McCabe

[permalink] [raw]
Subject: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Add initial support for libertas devices using a GSPI interface. This has
been tested with the 8686. At this time, as far as we know, GSPI firmware for
this device is not publicly distributable, but we expect this to change.

GSPI is intended to be used on embedded systems. Board-specific parameters are
required (see libertas_spi.h).

Signed-off-by: Colin McCabe <[email protected]>
Signed-off-by: Andrey Yurovsky <[email protected]>
---
drivers/net/wireless/libertas/Makefile | 2 +
drivers/net/wireless/libertas/if_spi.c | 1181 ++++++++++++++++++++++++++++++++
drivers/net/wireless/libertas/if_spi.h | 207 ++++++
include/linux/spi/libertas_spi.h | 31 +
4 files changed, 1421 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/wireless/libertas/if_spi.c
create mode 100644 drivers/net/wireless/libertas/if_spi.h
create mode 100644 include/linux/spi/libertas_spi.h

diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
index 02080a3..0b69185 100644
--- a/drivers/net/wireless/libertas/Makefile
+++ b/drivers/net/wireless/libertas/Makefile
@@ -4,8 +4,10 @@ libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o \
usb8xxx-objs += if_usb.o
libertas_cs-objs += if_cs.o
libertas_sdio-objs += if_sdio.o
+libertas_spi-objs += if_spi.o

obj-$(CONFIG_LIBERTAS) += libertas.o
obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o
obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o
+obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
new file mode 100644
index 0000000..65219a5
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -0,0 +1,1181 @@
+/*
+ * linux/drivers/net/wireless/libertas/if_spi.c
+ *
+ * Driver for Marvell SPI WLAN cards.
+ *
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Authors:
+ * Andrey Yurovsky <[email protected]>
+ * Colin McCabe <[email protected]>
+ *
+ * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/spi/libertas_spi.h>
+#include <linux/spi/spi.h>
+
+#include "host.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "if_spi.h"
+
+struct if_spi_packet {
+ struct list_head list;
+ u16 blen;
+ u8 buffer[0] __attribute__((aligned(4)));
+};
+
+struct if_spi_card {
+ struct spi_device *spi;
+ struct lbs_private *priv;
+
+ char helper_fw_name[FIRMWARE_NAME_MAX];
+ char main_fw_name[FIRMWARE_NAME_MAX];
+
+ /* The card ID and card revision, as reported by the hardware. */
+ u16 card_id;
+ u8 card_rev;
+
+ /* Cache IRQ number for deregistration */
+ int host_irq_num;
+
+ /* Pin number for our GPIO chip-select. */
+ int gpio_cs;
+
+ /* The last time that we initiated an SPU operation */
+ unsigned long prev_xfer_time;
+
+ int use_dummy_writes;
+ unsigned long spu_port_delay;
+ unsigned long spu_reg_delay;
+
+ /* Handles all SPI communication (except for FW load) */
+ struct task_struct *spi_thread;
+ int run_thread;
+
+ /* Used to wake up the spi_thread */
+ struct semaphore spi_ready;
+ struct semaphore spi_thread_terminated;
+
+ u8 cmd_buffer[LBS_CMD_BUFFER_SIZE];
+
+ /* A buffer of incoming packets from libertas core.
+ * Since we can't sleep in hw_host_to_card, we have to buffer
+ * them. */
+ struct list_head cmd_packet_list;
+ struct list_head data_packet_list;
+
+ /* Protects cmd_packet_list and data_packet_list */
+ spinlock_t buffer_lock;
+};
+
+static void free_if_spi_card(struct if_spi_card *card)
+{
+ struct list_head *cursor, *next;
+ struct if_spi_packet *packet;
+
+ BUG_ON(card->run_thread);
+ list_for_each_safe(cursor, next, &card->cmd_packet_list) {
+ packet = container_of(cursor, struct if_spi_packet, list);
+ list_del(&packet->list);
+ kfree(packet);
+ }
+ list_for_each_safe(cursor, next, &card->data_packet_list) {
+ packet = container_of(cursor, struct if_spi_packet, list);
+ list_del(&packet->list);
+ kfree(packet);
+ }
+ spi_set_drvdata(card->spi, NULL);
+ kfree(card);
+}
+
+static struct chip_ident chip_id_to_device_name[] = {
+ { .chip_id = 0x04, .name = 8385 },
+ { .chip_id = 0x0b, .name = 8686 },
+};
+
+/*
+ * SPI Interface Unit Routines
+ *
+ * The SPU sits between the host and the WLAN module.
+ * All communication with the firmware is through SPU transactions.
+ *
+ * First we have to put a SPU register name on the bus. Then we can
+ * either read from or write to that register.
+ *
+ * For 16-bit transactions, byte order on the bus is big-endian.
+ * We don't have to worry about that here, though.
+ * The translation takes place in the SPI routines.
+ */
+
+static void spu_transaction_init(struct if_spi_card *card)
+{
+ if (!time_after(jiffies, card->prev_xfer_time + 1)) {
+ /* Unfortunately, the SPU requires a delay between successive
+ * transactions. If our last transaction was more than a jiffy
+ * ago, we have obviously already delayed enough.
+ * If not, we have to busy-wait to be on the safe side. */
+ ndelay(400);
+ }
+ gpio_set_value(card->gpio_cs, 0); /* assert CS */
+}
+
+static void spu_transaction_finish(struct if_spi_card *card)
+{
+ gpio_set_value(card->gpio_cs, 1); /* drop CS */
+ card->prev_xfer_time = jiffies;
+}
+
+/* Write out a byte buffer to an SPI register,
+ * using a series of 16-bit transfers. */
+static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
+{
+ int n;
+ int err = 0;
+ u16 reg_out = reg | IF_SPI_WRITE_OPERATION_MASK;
+
+ /* You must give an even number of bytes to the SPU, even if it
+ * doesn't care about the last one. */
+ BUG_ON(len & 0x1);
+
+ spu_transaction_init(card);
+
+ /* write SPU register index */
+ err = spi_write(card->spi, (u8 *)&reg_out, sizeof(u16));
+ if (err)
+ goto out;
+
+ for (n = 0; n < len; n += sizeof(u16)) {
+ err = spi_write(card->spi, buf + n, sizeof(u16));
+ if (err)
+ goto out;
+ }
+
+out:
+ spu_transaction_finish(card);
+ return err;
+}
+
+static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val)
+{
+ return spu_write(card, reg, (u8 *)&val, sizeof(u16));
+}
+
+static inline int spu_write_u32(struct if_spi_card *card, u16 reg, u32 val)
+{
+ /* The lower 16 bits are written first. */
+ u16 out[2];
+ out[0] = val & 0xffff;
+ out[1] = (val & 0xffff0000) >> 16;
+ return spu_write(card, reg, (u8 *)&out, sizeof(u32));
+}
+
+static inline int spu_reg_is_port_reg(u16 reg)
+{
+ switch (reg) {
+ case IF_SPI_IO_RDWRPORT_REG:
+ case IF_SPI_CMD_RDWRPORT_REG:
+ case IF_SPI_DATA_RDWRPORT_REG:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)
+{
+ unsigned int i, delay;
+ int n, err = 0;
+ u16 zero = 0;
+ u16 reg_out = reg | IF_SPI_READ_OPERATION_MASK;
+
+ /* You must take an even number of bytes from the SPU, even if you
+ * don't care about the last one. */
+ BUG_ON(len & 0x1);
+
+ spu_transaction_init(card);
+
+ /* write SPU register index */
+ err = spi_write(card->spi, (u8 *)&reg_out, sizeof(u16));
+ if (err)
+ return err;
+
+ delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay :
+ card->spu_reg_delay;
+ if (card->use_dummy_writes) {
+ /* Clock in dummy cycles while the SPU fills the FIFO */
+ for (i = 0; i < delay / 16; ++i) {
+ err = spi_write(card->spi, (u8 *)&zero, sizeof(u16));
+ if (err)
+ return err;
+ }
+ } else {
+ /* Busy-wait while the SPU fills the FIFO */
+ ndelay(100 + (delay * 10));
+ }
+
+ /* read in data */
+ for (n = 0; n < len; n += sizeof(u16)) {
+ err = spi_read(card->spi, buf + n, sizeof(u16));
+ if (err)
+ goto out;
+ }
+
+out:
+ spu_transaction_finish(card);
+
+ return err;
+}
+
+/* Read 16 bits from an SPI register */
+static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val)
+{
+ return spu_read(card, reg, (u8 *)val, sizeof(u16));
+}
+
+/* Read 32 bits from an SPI register.
+ * The low 16 bits are read first. */
+static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)
+{
+ u16 buf[2];
+ int err;
+ err = spu_read(card, reg, (u8 *)buf, sizeof(u32));
+ if (!err)
+ *val = buf[0] | (buf[1] << 16);
+ return err;
+}
+
+/* Keep reading 16 bits from an SPI register until you get the correct result.
+ *
+ * If mask = 0, the correct result is any non-zero number.
+ * If mask != 0, the correct result is any number where
+ * number & target_mask == target
+ *
+ * Returns -ETIMEDOUT if a second passes without the correct result. */
+int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
+ u16 target_mask, u16 target)
+{
+ int err;
+ unsigned long timeout = jiffies + 5*HZ;
+ while (1) {
+ u16 val;
+ err = spu_read_u16(card, reg, &val);
+ if (err)
+ return err;
+ if (target_mask) {
+ if ((val & target_mask) == target)
+ return 0;
+ } else {
+ if (val)
+ return 0;
+ }
+ udelay(100);
+ if (time_after(jiffies, timeout)) {
+ lbs_pr_err("%s: timeout with val=%02x, "
+ "target_mask=%02x, target=%02x\n",
+ __func__, val, target_mask, target);
+ return -ETIMEDOUT;
+ }
+ }
+}
+
+/* Read 16 bits from an SPI register until you receive a specific value.
+ * Returns -ETIMEDOUT if a 4 tries pass without success. */
+int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)
+{
+ int err, try;
+ for (try = 0; try < 4; ++try) {
+ u32 val = 0;
+ err = spu_read_u32(card, reg, &val);
+ if (err)
+ return err;
+ if (val == target)
+ return 0;
+ mdelay(100);
+ }
+ return -ETIMEDOUT;
+}
+
+int spu_set_interrupt_mode(struct if_spi_card *card,
+ int suppress_host_int,
+ int auto_int)
+{
+ int err = 0;
+
+ /* We can suppress a host interrupt by clearing the appropriate
+ * bit in the "host interrupt status mask" register */
+ if (suppress_host_int) {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
+ if (err)
+ return err;
+ } else {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
+ IF_SPI_HISM_TX_DOWNLOAD_RDY |
+ IF_SPI_HISM_RX_UPLOAD_RDY |
+ IF_SPI_HISM_CMD_DOWNLOAD_RDY |
+ IF_SPI_HISM_CARDEVENT |
+ IF_SPI_HISM_CMD_UPLOAD_RDY);
+ if (err)
+ return err;
+ }
+
+ /* If auto-interrupts are on, the completion of certain transactions
+ * will trigger an interrupt automatically. If auto-interrupts
+ * are off, we need to set the "Card Interrupt Cause" register to
+ * trigger a card interrupt. */
+ if (auto_int) {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
+ IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
+ IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
+ IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
+ IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO);
+ if (err)
+ return err;
+ } else {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
+ if (err)
+ return err;
+ }
+ return err;
+}
+
+static int spu_get_chip_revision(struct if_spi_card *card,
+ u16 *card_id, u8 *card_rev)
+{
+ int err = 0;
+ u32 dev_ctrl;
+ err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
+ if (err)
+ return err;
+ *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
+ *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
+ return err;
+}
+
+static int spu_set_bus_mode(struct if_spi_card *card, u16 mode)
+{
+ int err = 0;
+ u16 rval;
+ /* set bus mode */
+ err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode);
+ if (err)
+ return err;
+ /* Check that we were able to read back what we just wrote. */
+ err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
+ if (err)
+ return err;
+ if (rval != mode) {
+ lbs_pr_err("Can't read bus mode register.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int spu_init(struct if_spi_card *card, int use_dummy_writes)
+{
+ int err = 0;
+ u16 rval;
+ u32 delay;
+
+ /* We have to start up in timed delay mode so that we can safely
+ * read the Delay Read Register. */
+ card->use_dummy_writes = 0;
+ err = spu_set_bus_mode(card,
+ IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
+ IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
+ IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
+ if (err)
+ return err;
+ card->spu_port_delay = 100000;
+ card->spu_reg_delay = 100000;
+ err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay);
+ if (err)
+ return err;
+ card->spu_port_delay = delay & 0x0000ffff;
+ card->spu_reg_delay = (delay & 0xffff0000) >> 16;
+
+ /* If dummy clock delay mode has been requested, switch to it now */
+ if (use_dummy_writes) {
+ card->use_dummy_writes = 1;
+ err = spu_set_bus_mode(card,
+ IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
+ IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK |
+ IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
+ if (err)
+ return err;
+ }
+
+ /* Fetch interrupt status.
+ * This will cause the WLAN to de-assert the host-irq line. */
+ err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, &rval);
+ if (err)
+ return err;
+ lbs_deb_spi("IF_SPI_HOST_INT_STATUS_REG=0x%x\n", rval);
+
+ lbs_deb_spi("Initialized bootloader. "
+ "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n",
+ card->spu_port_delay, card->spu_reg_delay);
+ return err;
+}
+
+/*
+ * Firmware Loading
+ */
+
+static int if_spi_prog_helper_firmware(struct if_spi_card *card)
+{
+ int err = 0;
+ const struct firmware *firmware = NULL;
+ int bytes_remaining;
+ const u8 *fw;
+ u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
+ struct spi_device *spi = card->spi;
+
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ err = spu_set_interrupt_mode(card, 1, 0);
+ if (err)
+ goto out;
+ /* Get helper firmware image */
+ err = request_firmware(&firmware, card->helper_fw_name, &spi->dev);
+ if (err) {
+ lbs_pr_err("request_firmware failed with err = %d\n", err);
+ goto out;
+ }
+ bytes_remaining = firmware->size;
+ fw = firmware->data;
+
+ /* Load helper firmware image */
+ while (bytes_remaining > 0) {
+ /* Scratch pad 1 should contain the number of bytes we
+ * want to download to the firmware */
+ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
+ HELPER_FW_LOAD_CHUNK_SZ);
+ if (err)
+ goto release_firmware;
+
+ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY);
+ if (err)
+ goto release_firmware;
+
+ /* Feed the data into the command read/write port reg
+ * in chunks of 64 bytes */
+ memset(temp, 0, sizeof(temp));
+ memcpy(temp, fw,
+ min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ));
+ mdelay(10);
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
+ temp, HELPER_FW_LOAD_CHUNK_SZ);
+ if (err)
+ goto release_firmware;
+
+ /* Interrupt the boot code */
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto release_firmware;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ if (err)
+ goto release_firmware;
+ bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
+ fw += HELPER_FW_LOAD_CHUNK_SZ;
+ }
+
+ /* Once the helper / single stage firmware download is complete,
+ * write 0 to scratch pad 1 and interrupt the
+ * bootloader. This completes the helper download. */
+ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
+ if (err)
+ goto release_firmware;
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto release_firmware;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ goto release_firmware;
+
+ lbs_deb_spi("waiting for helper to boot...\n");
+
+release_firmware:
+ release_firmware(firmware);
+out:
+ if (err)
+ lbs_pr_err("failed to load helper firmware (err=%d)\n", err);
+ lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
+ return err;
+}
+
+/* Returns the length of the next packet the firmware expects us to send
+ * Sets crc_err if the previous transfer had a CRC error. */
+static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
+ int *crc_err)
+{
+ u16 len;
+ int err = 0;
+
+ /* wait until the host interrupt status register indicates
+ * that we are ready to download */
+ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY);
+ if (err) {
+ lbs_pr_err("timed out waiting for host_int_status\n");
+ return err;
+ }
+
+ /* Ask the device how many bytes of firmware it wants. */
+ err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
+ if (err)
+ return err;
+
+ if (len > LBS_CMD_BUFFER_SIZE) {
+ lbs_pr_err("firmware load device requested a larger "
+ "tranfer than we are prepared to "
+ "handle. (len = %d)\n", len);
+ return -EIO;
+ }
+ if (len & 0x1) {
+ lbs_deb_spi("%s: crc error\n", __func__);
+ len &= ~0x1;
+ *crc_err = 1;
+ } else
+ *crc_err = 0;
+
+ return len;
+}
+
+static int if_spi_prog_main_firmware(struct if_spi_card *card)
+{
+ int len, prev_len;
+ int bytes, crc_err = 0, err = 0;
+ const struct firmware *firmware = NULL;
+ const u8 *fw;
+ struct spi_device *spi = card->spi;
+ u16 num_crc_errs;
+
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ err = spu_set_interrupt_mode(card, 1, 0);
+ if (err)
+ goto out;
+
+ /* Get firmware image */
+ err = request_firmware(&firmware, card->main_fw_name, &spi->dev);
+ if (err) {
+ lbs_pr_err("%s: can't get firmware '%s' from kernel. "
+ "err = %d\n", __func__, card->main_fw_name, err);
+ goto out;
+ }
+
+ err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
+ if (err) {
+ lbs_pr_err("%s: timed out waiting for initial "
+ "scratch reg = 0\n", __func__);
+ goto release_firmware;
+ }
+
+ num_crc_errs = 0;
+ prev_len = 0;
+ bytes = firmware->size;
+ fw = firmware->data;
+ while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
+ if (len < 0) {
+ err = len;
+ goto release_firmware;
+ }
+ if (bytes < 0) {
+ /* If there are no more bytes left, we would normally
+ * expect to have terminated with len = 0 */
+ lbs_pr_err("Firmware load wants more bytes "
+ "than we have to offer.\n");
+ break;
+ }
+ if (crc_err) {
+ /* Previous transfer failed. */
+ if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) {
+ lbs_pr_err("Too many CRC errors encountered "
+ "in firmware load.\n");
+ err = -EIO;
+ goto release_firmware;
+ }
+ } else {
+ /* Previous transfer succeeded. Advance counters. */
+ bytes -= prev_len;
+ fw += prev_len;
+ }
+ if (bytes < len) {
+ memset(card->cmd_buffer, 0, len);
+ memcpy(card->cmd_buffer, fw, bytes);
+ } else
+ memcpy(card->cmd_buffer, fw, len);
+
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto release_firmware;
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
+ card->cmd_buffer, len);
+ if (err)
+ goto release_firmware;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ if (err)
+ goto release_firmware;
+ prev_len = len;
+ }
+ if (bytes > prev_len) {
+ lbs_pr_err("firmware load wants fewer bytes than "
+ "we have to offer.\n");
+ }
+
+ /* Confirm firmware download */
+ err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
+ SUCCESSFUL_FW_DOWNLOAD_MAGIC);
+ if (err) {
+ lbs_pr_err("failed to confirm the firmware download\n");
+ goto release_firmware;
+ }
+
+ err = spu_set_interrupt_mode(card, 0, 1);
+ if (err)
+ goto release_firmware;
+
+release_firmware:
+ release_firmware(firmware);
+
+out:
+ if (err)
+ lbs_pr_err("failed to load firmware (err=%d)\n", err);
+ lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
+ return err;
+}
+
+/*
+ * SPI Transfer Thread
+ *
+ * The SPI thread handles all SPI transfers, so there is no need for a lock.
+ */
+
+/* Move a command from the card to the host */
+static int if_spi_c2h_cmd(struct if_spi_card *card)
+{
+ struct lbs_private *priv = card->priv;
+ unsigned long flags;
+ int err = 0;
+ u16 len;
+ u8 i;
+
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ /* How many bytes are there to read? */
+ err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len);
+ if (err)
+ goto out;
+ if (!len) {
+ lbs_pr_err("%s: error: card has no data for host\n",
+ __func__);
+ err = -EINVAL;
+ goto out;
+ } else if (len > LBS_CMD_BUFFER_SIZE) {
+ lbs_pr_err("%s: error: response packet too large: "
+ "%d bytes, but maximum is %d\n",
+ __func__, len, LBS_CMD_BUFFER_SIZE);
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Read the data from the WLAN module into our command buffer */
+ err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG,
+ card->cmd_buffer, ALIGN(len, 2));
+ if (err)
+ goto out;
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ i = (priv->resp_idx == 0) ? 1 : 0;
+ BUG_ON(priv->resp_len[i]);
+ priv->resp_len[i] = len;
+ memcpy(priv->resp_buf[i], card->cmd_buffer, len);
+ lbs_notify_command_response(priv, i);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+out:
+ if (err)
+ lbs_pr_err("%s: err=%d\n", __func__, err);
+ lbs_deb_leave(LBS_DEB_SPI);
+ return err;
+}
+
+/* Move data from the card to the host */
+static int if_spi_c2h_data(struct if_spi_card *card)
+{
+ struct sk_buff *skb;
+ char *data;
+ u16 len;
+ int err = 0;
+
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ /* How many bytes are there to read? */
+ err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
+ if (err)
+ goto out;
+ if (!len) {
+ lbs_pr_err("%s: error: card has no data for host\n",
+ __func__);
+ err = -EINVAL;
+ goto out;
+ } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
+ lbs_pr_err("%s: error: card has %d bytes of data, but "
+ "our maximum skb size is %lu\n",
+ __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* TODO: should we allocate a smaller skb if we have less data? */
+ skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
+ if (!skb) {
+ err = -ENOBUFS;
+ goto out;
+ }
+ skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
+ data = skb_put(skb, len);
+
+ /* Read the data from the WLAN module into our skb... */
+
+ err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 2));
+ if (err)
+ goto free_skb;
+
+ /* pass the SKB to libertas */
+ err = lbs_process_rxed_packet(card->priv, skb);
+ if (err)
+ goto free_skb;
+
+ /* success */
+ goto out;
+
+free_skb:
+ dev_kfree_skb(skb);
+out:
+ if (err)
+ lbs_pr_err("%s: err=%d\n", __func__, err);
+ lbs_deb_leave(LBS_DEB_SPI);
+ return err;
+}
+
+/* Move data or a command from the host to the card. */
+static void if_spi_h2c(struct if_spi_card *card,
+ struct if_spi_packet *packet, int type)
+{
+ int err = 0;
+ u16 int_type, port_reg;
+
+ switch (type) {
+ case MVMS_DAT:
+ int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER;
+ port_reg = IF_SPI_DATA_RDWRPORT_REG;
+ break;
+ case MVMS_CMD:
+ int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER;
+ port_reg = IF_SPI_CMD_RDWRPORT_REG;
+ break;
+ default:
+ lbs_pr_err("can't transfer buffer of type %d\n", type);
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Write the data to the card */
+ err = spu_write(card, port_reg, packet->buffer, packet->blen);
+ if (err)
+ goto out;
+
+out:
+ kfree(packet);
+
+ if (err)
+ lbs_pr_err("%s: error %d\n", __func__, err);
+}
+
+static int lbs_spi_thread(void *data)
+{
+ int err;
+ struct if_spi_card *card = data;
+ u16 hiStatus;
+ unsigned long flags;
+ struct if_spi_packet *packet;
+
+ while (1) {
+ /* Wait to be woken up by one of two things. First, our ISR
+ * could tell us that something happened on the WLAN.
+ * Secondly, libertas could call hw_host_to_card with more
+ * data, which we might be able to send.
+ */
+ do {
+ err = down_interruptible(&card->spi_ready);
+ if (!card->run_thread) {
+ up(&card->spi_thread_terminated);
+ do_exit(0);
+ }
+ } while (err == EINTR);
+
+ /* Read the host interrupt status register to see what we
+ * can do. */
+ err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ &hiStatus);
+ if (err) {
+ lbs_pr_err("I/O error\n");
+ goto err;
+ }
+
+ if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY)
+ err = if_spi_c2h_cmd(card);
+ if (err)
+ goto err;
+ if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY)
+ err = if_spi_c2h_data(card);
+ if (err)
+ goto err;
+ if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY) {
+ /* This means two things. First of all,
+ * if there was a previous command sent, the card has
+ * successfully received it.
+ * Secondly, it is now ready to download another
+ * command.
+ */
+ lbs_host_to_card_done(card->priv);
+
+ /* Do we have any command packets from the host to
+ * send? */
+ packet = NULL;
+ spin_lock_irqsave(&card->buffer_lock, flags);
+ if (!list_empty(&card->cmd_packet_list)) {
+ packet = (struct if_spi_packet *)(card->
+ cmd_packet_list.next);
+ list_del(&packet->list);
+ }
+ spin_unlock_irqrestore(&card->buffer_lock, flags);
+
+ if (packet)
+ if_spi_h2c(card, packet, MVMS_CMD);
+ }
+ if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
+ /* Do we have any data packets from the host to
+ * send? */
+ packet = NULL;
+ spin_lock_irqsave(&card->buffer_lock, flags);
+ if (!list_empty(&card->data_packet_list)) {
+ packet = (struct if_spi_packet *)(card->
+ data_packet_list.next);
+ list_del(&packet->list);
+ }
+ spin_unlock_irqrestore(&card->buffer_lock, flags);
+
+ if (packet)
+ if_spi_h2c(card, packet, MVMS_DAT);
+ }
+ /* TODO: implement IF_SPI_HIST_CARD_EVENT
+ * Eventually this should feed into lbs_queue_event */
+
+err:
+ if (err)
+ lbs_pr_err("%s: got error %d\n", __func__, err);
+ }
+}
+
+/* Block until lbs_spi_thread thread has terminated */
+static void if_spi_terminate_spi_thread(struct if_spi_card *card)
+{
+ /* It would be nice to use kthread_stop here, but that function
+ * can't wake threads waiting for a semaphore. */
+ card->run_thread = 0;
+ up(&card->spi_ready);
+ down(&card->spi_thread_terminated);
+}
+
+/*
+ * Host to Card
+ *
+ * Called from Libertas to transfer some data to the WLAN device
+ * We can't sleep here. */
+static int if_spi_host_to_card(struct lbs_private *priv,
+ u8 type, u8 *buf, u16 nb)
+{
+ int err = 0;
+ unsigned long flags;
+ struct if_spi_card *card = priv->card;
+ struct if_spi_packet *packet;
+ u16 blen;
+
+ lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb);
+
+ if (nb == 0) {
+ lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb);
+ err = -EINVAL;
+ goto out;
+ }
+ blen = ALIGN(nb, 4);
+ packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC);
+ if (!packet) {
+ err = -ENOMEM;
+ goto out;
+ }
+ packet->blen = blen;
+ memcpy(packet->buffer, buf, nb);
+ memset(packet->buffer + nb, 0, blen - nb);
+
+ switch (type) {
+ case MVMS_CMD:
+ priv->dnld_sent = DNLD_CMD_SENT;
+ spin_lock_irqsave(&card->buffer_lock, flags);
+ list_add_tail(&packet->list, &card->cmd_packet_list);
+ spin_unlock_irqrestore(&card->buffer_lock, flags);
+ break;
+ case MVMS_DAT:
+ priv->dnld_sent = DNLD_DATA_SENT;
+ spin_lock_irqsave(&card->buffer_lock, flags);
+ list_add_tail(&packet->list, &card->data_packet_list);
+ spin_unlock_irqrestore(&card->buffer_lock, flags);
+ break;
+ default:
+ lbs_pr_err("can't transfer buffer of type %d", type);
+ err = -EINVAL;
+ break;
+ }
+
+ /* Wake up the spi thread */
+ up(&card->spi_ready);
+out:
+ lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err);
+ return err;
+}
+
+/*
+ * Host Interrupts
+ *
+ * Service incoming interrupts from the WLAN device. We can't sleep here, so
+ * don't try to talk on the SPI bus, just wake up the SPI thread.
+ */
+static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
+{
+ struct if_spi_card *card = dev_id;
+
+ up(&card->spi_ready);
+ return IRQ_HANDLED;
+}
+
+/*
+ * SPI callbacks
+ */
+
+static int if_spi_calculate_fw_names(u16 card_id,
+ char *helper_fw, char *main_fw)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(chip_id_to_device_name); ++i) {
+ if (card_id == chip_id_to_device_name[i].chip_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(chip_id_to_device_name)) {
+ lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id);
+ return -EAFNOSUPPORT;
+ }
+ snprintf(helper_fw, FIRMWARE_NAME_MAX, "spi%d_helper.bin",
+ chip_id_to_device_name[i].name);
+ snprintf(main_fw, FIRMWARE_NAME_MAX, "spi%d.bin",
+ chip_id_to_device_name[i].name);
+ return 0;
+}
+
+static int __devinit if_spi_probe(struct spi_device *spi)
+{
+ struct if_spi_card *card;
+ struct lbs_private *priv = NULL;
+ struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
+ int err = 0;
+
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ /* Allocate card structure to represent this specific device */
+ card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
+ if (!card) {
+ err = -ENOMEM;
+ goto out;
+ }
+ spi_set_drvdata(spi, card);
+ card->spi = spi;
+ card->host_irq_num = pdata->host_irq_num;
+ card->gpio_cs = pdata->gpio_cs;
+ card->prev_xfer_time = jiffies;
+
+ sema_init(&card->spi_ready, 0);
+ sema_init(&card->spi_thread_terminated, 0);
+ INIT_LIST_HEAD(&card->cmd_packet_list);
+ INIT_LIST_HEAD(&card->data_packet_list);
+ spin_lock_init(&card->buffer_lock);
+
+ /* set up GPIO CS line. TODO: use regular CS line */
+ err = gpio_request(card->gpio_cs, "if_spi_gpio_chip_select");
+ if (err)
+ goto free_card;
+ err = gpio_direction_output(card->gpio_cs, 1);
+ if (err)
+ goto free_gpio;
+
+ /* Initialize the SPI Interface Unit */
+ err = spu_init(card, pdata->use_dummy_writes);
+ if (err)
+ goto free_gpio;
+ err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
+ if (err)
+ goto free_gpio;
+
+ /* program the firmware */
+ err = if_spi_calculate_fw_names(card->card_id,
+ card->helper_fw_name, card->main_fw_name);
+ if (err)
+ goto free_gpio;
+ lbs_deb_spi("Initializing firmware for Marvell WLAN 802.11 adapter "
+ "(chip_id = 0x%04x, chip_rev = 0x%02x) "
+ "attached to SPI bus_num %d, chip_select %d. "
+ "spi->max_speed_hz=%d\n",
+ card->card_id, card->card_rev,
+ spi->master->bus_num, spi->chip_select,
+ spi->max_speed_hz);
+ err = if_spi_prog_helper_firmware(card);
+ if (err)
+ goto free_gpio;
+ err = if_spi_prog_main_firmware(card);
+ if (err)
+ goto free_gpio;
+ lbs_deb_spi("loaded firmware for Marvell WLAN 802.11 adapter\n");
+
+ /* Register our card with libertas.
+ * This will call alloc_etherdev */
+ priv = lbs_add_card(card, &spi->dev);
+ if (!priv) {
+ err = -ENOMEM;
+ goto free_gpio;
+ }
+ card->priv = priv;
+ priv->card = card;
+ priv->hw_host_to_card = if_spi_host_to_card;
+ priv->fw_ready = 1;
+
+ /* Initialize interrupt handling stuff. */
+ card->run_thread = 1;
+ card->spi_thread = kthread_run(lbs_spi_thread, card, "lbs_spi_thread");
+ if (IS_ERR(card->spi_thread)) {
+ card->run_thread = 0;
+ err = PTR_ERR(card->spi_thread);
+ lbs_pr_err("error creating SPI thread: err=%d\n", err);
+ goto remove_card;
+ }
+ err = request_irq(card->host_irq_num, if_spi_host_interrupt,
+ IRQF_TRIGGER_FALLING,
+ "libertas_spi", card);
+ if (err) {
+ lbs_pr_err("can't get host irq line-- request_irq failed\n");
+ goto terminate_thread;
+ }
+
+ /* Start the card.
+ * This will call register_netdev, and we'll start
+ * getting interrupts... */
+ err = lbs_start_card(priv);
+ if (err)
+ goto release_irq;
+
+ lbs_deb_spi("Finished initializing WLAN module.\n");
+
+ /* successful exit */
+ goto out;
+
+release_irq:
+ free_irq(card->host_irq_num, card);
+terminate_thread:
+ if_spi_terminate_spi_thread(card);
+remove_card:
+ lbs_remove_card(priv); /* will call free_netdev */
+free_gpio:
+ gpio_free(card->gpio_cs);
+free_card:
+ free_if_spi_card(card);
+out:
+ lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
+ return err;
+}
+
+static int __devexit libertas_spi_remove(struct spi_device *spi)
+{
+ struct if_spi_card *card = spi_get_drvdata(spi);
+ struct lbs_private *priv = card->priv;
+
+ lbs_deb_spi("libertas_spi_remove\n");
+ lbs_deb_enter(LBS_DEB_SPI);
+ priv->surpriseremoved = 1;
+
+ lbs_stop_card(priv);
+ free_irq(card->host_irq_num, card);
+ if_spi_terminate_spi_thread(card);
+ lbs_remove_card(priv); /* will call free_netdev */
+ gpio_free(card->gpio_cs);
+ free_if_spi_card(card);
+ lbs_deb_leave(LBS_DEB_SPI);
+ return 0;
+}
+
+static struct spi_driver libertas_spi_driver = {
+ .probe = if_spi_probe,
+ .remove = __devexit_p(libertas_spi_remove),
+ .driver = {
+ .name = "libertas_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * Module functions
+ */
+
+static int __init if_spi_init_module(void)
+{
+ int ret = 0;
+ lbs_deb_enter(LBS_DEB_SPI);
+ printk(KERN_INFO "libertas_spi: Libertas SPI driver\n");
+ ret = spi_register_driver(&libertas_spi_driver);
+ lbs_deb_leave(LBS_DEB_SPI);
+ return ret;
+}
+
+static void __exit if_spi_exit_module(void)
+{
+ lbs_deb_enter(LBS_DEB_SPI);
+ spi_unregister_driver(&libertas_spi_driver);
+ lbs_deb_leave(LBS_DEB_SPI);
+}
+
+module_init(if_spi_init_module);
+module_exit(if_spi_exit_module);
+
+MODULE_DESCRIPTION("Libertas SPI WLAN Driver");
+MODULE_AUTHOR("Andrey Yurovsky <[email protected]>, "
+ "Colin McCabe <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h
new file mode 100644
index 0000000..3e17a14
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_spi.h
@@ -0,0 +1,207 @@
+/*
+ * linux/drivers/net/wireless/libertas/if_spi.c
+ *
+ * Driver for Marvell SPI WLAN cards.
+ *
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Authors:
+ * Andrey Yurovsky <[email protected]>
+ * Colin McCabe <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef _LBS_IF_SPI_H_
+#define _LBS_IF_SPI_H_
+
+#define IPFIELD_ALIGN_OFFSET 2
+
+/***************** Firmware *****************/
+struct chip_ident {
+ u16 chip_id;
+ u16 name;
+};
+
+#define MAX_MAIN_FW_LOAD_CRC_ERR 10
+
+/* Chunk size when loading the helper firmware */
+#define HELPER_FW_LOAD_CHUNK_SZ 64
+
+/* Value to write to indicate end of helper firmware dnld */
+#define FIRMWARE_DNLD_OK 0x0000
+
+/* Value to check once the main firmware is downloaded */
+#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888
+
+/***************** SPI Interface Unit *****************/
+/* Masks used in SPI register read/write operations */
+#define IF_SPI_READ_OPERATION_MASK 0x0
+#define IF_SPI_WRITE_OPERATION_MASK 0x8000
+
+/* SPI register offsets. 4-byte aligned. */
+#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */
+#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */
+#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */
+#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */
+
+#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */
+#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */
+#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */
+
+#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */
+#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */
+#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */
+
+#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */
+#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */
+#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */
+#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */
+
+#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */
+#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */
+
+#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */
+
+#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */
+#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interupt status reg */
+#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */
+#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */
+
+#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */
+
+#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */
+#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */
+#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */
+#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */
+#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */
+
+#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */
+#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */
+
+/***************** IF_SPI_DEVICEID_CTRL_REG *****************/
+#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16)
+#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff)
+
+/***************** IF_SPI_HOST_INT_CTRL_REG *****************/
+/** Host Interrupt Control bit : Wake up */
+#define IF_SPI_HICT_WAKE_UP (1<<0)
+/** Host Interrupt Control bit : WLAN ready */
+#define IF_SPI_HICT_WLAN_READY (1<<1)
+/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */
+/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */
+/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */
+/** Host Interrupt Control bit : Tx auto download */
+#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5)
+/** Host Interrupt Control bit : Rx auto upload */
+#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6)
+/** Host Interrupt Control bit : Command auto download */
+#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7)
+/** Host Interrupt Control bit : Command auto upload */
+#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8)
+
+/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/
+/** Card Interrupt Case bit : Tx download over */
+#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0)
+/** Card Interrupt Case bit : Rx upload over */
+#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1)
+/** Card Interrupt Case bit : Command download over */
+#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2)
+/** Card Interrupt Case bit : Host event */
+#define IF_SPI_CIC_HOST_EVENT (1<<3)
+/** Card Interrupt Case bit : Command upload over */
+#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4)
+/** Card Interrupt Case bit : Power down */
+#define IF_SPI_CIC_POWER_DOWN (1<<5)
+
+/***************** IF_SPI_CARD_INT_STATUS_REG *****************/
+#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0)
+#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1)
+#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2)
+#define IF_SPI_CIS_HOST_EVENT (1<<3)
+#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4)
+#define IF_SPI_CIS_POWER_DOWN (1<<5)
+
+/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/
+#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0)
+#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1)
+#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2)
+#define IF_SPI_HICU_CARD_EVENT (1<<3)
+#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4)
+#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5)
+#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6)
+#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7)
+#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8)
+#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9)
+#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_HOST_INT_STATUS_REG *****************/
+/** Host Interrupt Status bit : Tx download ready */
+#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0)
+/** Host Interrupt Status bit : Rx upload ready */
+#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1)
+/** Host Interrupt Status bit : Command download ready */
+#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2)
+/** Host Interrupt Status bit : Card event */
+#define IF_SPI_HIST_CARD_EVENT (1<<3)
+/** Host Interrupt Status bit : Command upload ready */
+#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4)
+/** Host Interrupt Status bit : I/O write FIFO overflow */
+#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5)
+/** Host Interrupt Status bit : I/O read FIFO underflow */
+#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6)
+/** Host Interrupt Status bit : Data write FIFO overflow */
+#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7)
+/** Host Interrupt Status bit : Data read FIFO underflow */
+#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8)
+/** Host Interrupt Status bit : Command write FIFO overflow */
+#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9)
+/** Host Interrupt Status bit : Command read FIFO underflow */
+#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/
+/** Host Interrupt Status Mask bit : Tx download ready */
+#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0)
+/** Host Interrupt Status Mask bit : Rx upload ready */
+#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1)
+/** Host Interrupt Status Mask bit : Command download ready */
+#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2)
+/** Host Interrupt Status Mask bit : Card event */
+#define IF_SPI_HISM_CARDEVENT (1<<3)
+/** Host Interrupt Status Mask bit : Command upload ready */
+#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4)
+/** Host Interrupt Status Mask bit : I/O write FIFO overflow */
+#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5)
+/** Host Interrupt Status Mask bit : I/O read FIFO underflow */
+#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6)
+/** Host Interrupt Status Mask bit : Data write FIFO overflow */
+#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7)
+/** Host Interrupt Status Mask bit : Data write FIFO underflow */
+#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8)
+/** Host Interrupt Status Mask bit : Command write FIFO overflow */
+#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9)
+/** Host Interrupt Status Mask bit : Command write FIFO underflow */
+#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_SPU_BUS_MODE_REG *****************/
+/* SCK edge on which the WLAN module outputs data on MISO */
+#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8
+#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0
+
+/* In a SPIR read operation, there is a delay between writing the SPIR
+ * register name and getting back data from the WLAN module.
+ * This can be specified in terms of nanoseconds or in terms of dummy
+ * clock cycles which the master must output before receiving a response. */
+#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4
+#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0
+
+/* Some different modes of SPI operation */
+#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00
+#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01
+#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02
+#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03
+
+#endif
diff --git a/include/linux/spi/libertas_spi.h b/include/linux/spi/libertas_spi.h
new file mode 100644
index 0000000..a654261
--- /dev/null
+++ b/include/linux/spi/libertas_spi.h
@@ -0,0 +1,31 @@
+/*
+ * board-specific data for the libertas_spi driver.
+ *
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+#ifndef _LIBERTAS_SPI_H_
+#define _LIBERTAS_SPI_H_
+struct libertas_spi_platform_data {
+ /* There are two ways to read data from the WLAN module's SPI
+ * interface. Setting 0 or 1 here controls which one is used.
+ *
+ * Usually you want to set use_dummy_writes = 1.
+ * However, if that doesn't work or if you are using a slow SPI clock
+ * speed, you may want to use 0 here. */
+ u16 use_dummy_writes;
+
+ /* All SPI communications are initiated by the bus master. In order
+ * for the WLAN module to tell the kernel that it has received data
+ * from the network, it must use an out-of-band IRQ line. Here we
+ * specify which IRQ line to use. */
+ u16 host_irq_num;
+
+ /* GPIO number to use as chip select */
+ u16 gpio_cs;
+};
+#endif
--
1.5.6.3


2008-12-19 18:17:38

by Andrey Yurovsky

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Thanks Peter. The Blackfin SPI controller provides built-in Chip
Select signals and one can choose to assign one of several CS lines to
a device. However we need a GPIO-style CS rather than one generated
by the controller and while I see that there's certainly room to
implement that in the SPI controller driver, I don't see a way to have
both that and the automatically-generated CS as options. That is, if
I understand correctly, there's no way to configure spi_board_info
with, say, a flag that tells the driver "hey, use this specific GPIO
line", instead you can pick between 0 and num_chipselect for your
chip_select value and, in Blackfin's case, this would configure one of
the automatically-generated lines. We appreciate additional feedback
about this. Thanks,

On Fri, Dec 19, 2008 at 8:57 AM, Peter Wippich <[email protected]> wrote:
>
> Hi Andrey,
>
>> The '8686 requires us to assert CS# and then hold it asserted for the
>> entire transaction. The transaction is an arbitrary number of SPI
>> transfers. Most host controllers cannot do this (they'll toggle CS#
>> between individual transactions) and therefore we chose to not use the
>> controller CS# line and use a GPIO. Can you suggest a different way
>> to do this given the current SPI framework?
>
> This can be handled by the SPI driver. See pxa2xx_spi.c as an example.
> Look for cs_control() to get the picture.....
> Perhaps this should go to the general driver and not be handled by the
> chip specific code. Seems a problem general enough.
>
> Chears,
>
> Peter
>



--
Andrey Yurovsky
cozybit Inc.

2008-12-16 03:51:16

by Angel Roman

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

Hi Colin,

I have support for the gspi as well. I've been trying to get in contact
with Dan Williams in order to contribute it to the list.

If you want, you can take a look at the code via:

svn list -R
svn://svn.buglabs.net/bug/trunk/bug-linux-2.6.27.2/drivers/net/wireless/libertas


This is currently working in the mx31 processor. Maybe we can work out a
way to merge the two drivers. The mx31 was a little tricky since there's
an error in the processor where one is not able to keep the chip select
signal active during multiple spi transfers as requried by the wifi
module. I've also abstracted the board interface from the GSPI code as
much as I could.

Regards,
Angel Roman

Colin McCabe wrote:
> Add initial support for libertas devices using a GSPI interface. This has
> been tested with the 8686. At this time, as far as we know, GSPI
> firmware for
> this device is not publicly distributable, but we expect this to change.
>
> GSPI is intended to be used on embedded systems. Board-specific
> parameters are
> required (see libertas_spi.h).
>
> Signed-off-by: Colin McCabe <[email protected]>
> Signed-off-by: Andrey Yurovsky <[email protected]>
> ---
> drivers/net/wireless/libertas/Makefile | 2 +
> drivers/net/wireless/libertas/if_spi.c | 1181
> ++++++++++++++++++++++++++++++++
> drivers/net/wireless/libertas/if_spi.h | 207 ++++++
> include/linux/spi/libertas_spi.h | 31 +
> 4 files changed, 1421 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/wireless/libertas/if_spi.c
> create mode 100644 drivers/net/wireless/libertas/if_spi.h
> create mode 100644 include/linux/spi/libertas_spi.h
>
> diff --git a/drivers/net/wireless/libertas/Makefile
> b/drivers/net/wireless/libertas/Makefile
> index 02080a3..0b69185 100644
> --- a/drivers/net/wireless/libertas/Makefile
> +++ b/drivers/net/wireless/libertas/Makefile
> @@ -4,8 +4,10 @@ libertas-objs := main.o wext.o rx.o tx.o cmd.o
> cmdresp.o scan.o 11d.o \
> usb8xxx-objs += if_usb.o
> libertas_cs-objs += if_cs.o
> libertas_sdio-objs += if_sdio.o
> +libertas_spi-objs += if_spi.o
>
> obj-$(CONFIG_LIBERTAS) += libertas.o
> obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
> obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o
> obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o
> +obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o
> diff --git a/drivers/net/wireless/libertas/if_spi.c
> b/drivers/net/wireless/libertas/if_spi.c
> new file mode 100644
> index 0000000..65219a5
> --- /dev/null
> +++ b/drivers/net/wireless/libertas/if_spi.c
> @@ -0,0 +1,1181 @@
> +/*
> + * linux/drivers/net/wireless/libertas/if_spi.c
> + *
> + * Driver for Marvell SPI WLAN cards.
> + *
> + * Copyright 2008 Analog Devices Inc.
> + *
> + * Authors:
> + * Andrey Yurovsky <[email protected]>
> + * Colin McCabe <[email protected]>
> + *
> + * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/moduleparam.h>
> +#include <linux/firmware.h>
> +#include <linux/gpio.h>
> +#include <linux/jiffies.h>
> +#include <linux/kthread.h>
> +#include <linux/list.h>
> +#include <linux/netdevice.h>
> +#include <linux/spi/libertas_spi.h>
> +#include <linux/spi/spi.h>
> +
> +#include "host.h"
> +#include "decl.h"
> +#include "defs.h"
> +#include "dev.h"
> +#include "if_spi.h"
> +
> +struct if_spi_packet {
> + struct list_head list;
> + u16 blen;
> + u8 buffer[0] __attribute__((aligned(4)));
> +};
> +
> +struct if_spi_card {
> + struct spi_device *spi;
> + struct lbs_private *priv;
> +
> + char helper_fw_name[FIRMWARE_NAME_MAX];
> + char main_fw_name[FIRMWARE_NAME_MAX];
> +
> + /* The card ID and card revision, as reported by the hardware. */
> + u16 card_id;
> + u8 card_rev;
> +
> + /* Cache IRQ number for deregistration */
> + int host_irq_num;
> +
> + /* Pin number for our GPIO chip-select. */
> + int gpio_cs;
> +
> + /* The last time that we initiated an SPU operation */
> + unsigned long prev_xfer_time;
> +
> + int use_dummy_writes;
> + unsigned long spu_port_delay;
> + unsigned long spu_reg_delay;
> +
> + /* Handles all SPI communication (except for FW load) */
> + struct task_struct *spi_thread;
> + int run_thread;
> +
> + /* Used to wake up the spi_thread */
> + struct semaphore spi_ready;
> + struct semaphore spi_thread_terminated;
> +
> + u8 cmd_buffer[LBS_CMD_BUFFER_SIZE];
> +
> + /* A buffer of incoming packets from libertas core.
> + * Since we can't sleep in hw_host_to_card, we have to buffer
> + * them. */
> + struct list_head cmd_packet_list;
> + struct list_head data_packet_list;
> +
> + /* Protects cmd_packet_list and data_packet_list */
> + spinlock_t buffer_lock;
> +};
> +
> +static void free_if_spi_card(struct if_spi_card *card)
> +{
> + struct list_head *cursor, *next;
> + struct if_spi_packet *packet;
> +
> + BUG_ON(card->run_thread);
> + list_for_each_safe(cursor, next, &card->cmd_packet_list) {
> + packet = container_of(cursor, struct if_spi_packet, list);
> + list_del(&packet->list);
> + kfree(packet);
> + }
> + list_for_each_safe(cursor, next, &card->data_packet_list) {
> + packet = container_of(cursor, struct if_spi_packet, list);
> + list_del(&packet->list);
> + kfree(packet);
> + }
> + spi_set_drvdata(card->spi, NULL);
> + kfree(card);
> +}
> +
> +static struct chip_ident chip_id_to_device_name[] = {
> + { .chip_id = 0x04, .name = 8385 },
> + { .chip_id = 0x0b, .name = 8686 },
> +};
> +
> +/*
> + * SPI Interface Unit Routines
> + *
> + * The SPU sits between the host and the WLAN module.
> + * All communication with the firmware is through SPU transactions.
> + *
> + * First we have to put a SPU register name on the bus. Then we can
> + * either read from or write to that register.
> + *
> + * For 16-bit transactions, byte order on the bus is big-endian.
> + * We don't have to worry about that here, though.
> + * The translation takes place in the SPI routines.
> + */
> +
> +static void spu_transaction_init(struct if_spi_card *card)
> +{
> + if (!time_after(jiffies, card->prev_xfer_time + 1)) {
> + /* Unfortunately, the SPU requires a delay between successive
> + * transactions. If our last transaction was more than a jiffy
> + * ago, we have obviously already delayed enough.
> + * If not, we have to busy-wait to be on the safe side. */
> + ndelay(400);
> + }
> + gpio_set_value(card->gpio_cs, 0); /* assert CS */
> +}
> +
> +static void spu_transaction_finish(struct if_spi_card *card)
> +{
> + gpio_set_value(card->gpio_cs, 1); /* drop CS */
> + card->prev_xfer_time = jiffies;
> +}
> +
> +/* Write out a byte buffer to an SPI register,
> + * using a series of 16-bit transfers. */
> +static int spu_write(struct if_spi_card *card, u16 reg, const u8
> *buf, int len)
> +{
> + int n;
> + int err = 0;
> + u16 reg_out = reg | IF_SPI_WRITE_OPERATION_MASK;
> +
> + /* You must give an even number of bytes to the SPU, even if it
> + * doesn't care about the last one. */
> + BUG_ON(len & 0x1);
> +
> + spu_transaction_init(card);
> +
> + /* write SPU register index */
> + err = spi_write(card->spi, (u8 *)&reg_out, sizeof(u16));
> + if (err)
> + goto out;
> +
> + for (n = 0; n < len; n += sizeof(u16)) {
> + err = spi_write(card->spi, buf + n, sizeof(u16));
> + if (err)
> + goto out;
> + }
> +
> +out:
> + spu_transaction_finish(card);
> + return err;
> +}
> +
> +static inline int spu_write_u16(struct if_spi_card *card, u16 reg,
> u16 val)
> +{
> + return spu_write(card, reg, (u8 *)&val, sizeof(u16));
> +}
> +
> +static inline int spu_write_u32(struct if_spi_card *card, u16 reg,
> u32 val)
> +{
> + /* The lower 16 bits are written first. */
> + u16 out[2];
> + out[0] = val & 0xffff;
> + out[1] = (val & 0xffff0000) >> 16;
> + return spu_write(card, reg, (u8 *)&out, sizeof(u32));
> +}
> +
> +static inline int spu_reg_is_port_reg(u16 reg)
> +{
> + switch (reg) {
> + case IF_SPI_IO_RDWRPORT_REG:
> + case IF_SPI_CMD_RDWRPORT_REG:
> + case IF_SPI_DATA_RDWRPORT_REG:
> + return 1;
> + default:
> + return 0;
> + }
> +}
> +
> +static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)
> +{
> + unsigned int i, delay;
> + int n, err = 0;
> + u16 zero = 0;
> + u16 reg_out = reg | IF_SPI_READ_OPERATION_MASK;
> +
> + /* You must take an even number of bytes from the SPU, even if you
> + * don't care about the last one. */
> + BUG_ON(len & 0x1);
> +
> + spu_transaction_init(card);
> +
> + /* write SPU register index */
> + err = spi_write(card->spi, (u8 *)&reg_out, sizeof(u16));
> + if (err)
> + return err;
> +
> + delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay :
> + card->spu_reg_delay;
> + if (card->use_dummy_writes) {
> + /* Clock in dummy cycles while the SPU fills the FIFO */
> + for (i = 0; i < delay / 16; ++i) {
> + err = spi_write(card->spi, (u8 *)&zero, sizeof(u16));
> + if (err)
> + return err;
> + }
> + } else {
> + /* Busy-wait while the SPU fills the FIFO */
> + ndelay(100 + (delay * 10));
> + }
> +
> + /* read in data */
> + for (n = 0; n < len; n += sizeof(u16)) {
> + err = spi_read(card->spi, buf + n, sizeof(u16));
> + if (err)
> + goto out;
> + }
> +
> +out:
> + spu_transaction_finish(card);
> +
> + return err;
> +}
> +
> +/* Read 16 bits from an SPI register */
> +static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16
> *val)
> +{
> + return spu_read(card, reg, (u8 *)val, sizeof(u16));
> +}
> +
> +/* Read 32 bits from an SPI register.
> + * The low 16 bits are read first. */
> +static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)
> +{
> + u16 buf[2];
> + int err;
> + err = spu_read(card, reg, (u8 *)buf, sizeof(u32));
> + if (!err)
> + *val = buf[0] | (buf[1] << 16);
> + return err;
> +}
> +
> +/* Keep reading 16 bits from an SPI register until you get the
> correct result.
> + *
> + * If mask = 0, the correct result is any non-zero number.
> + * If mask != 0, the correct result is any number where
> + * number & target_mask == target
> + *
> + * Returns -ETIMEDOUT if a second passes without the correct result. */
> +int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
> + u16 target_mask, u16 target)
> +{
> + int err;
> + unsigned long timeout = jiffies + 5*HZ;
> + while (1) {
> + u16 val;
> + err = spu_read_u16(card, reg, &val);
> + if (err)
> + return err;
> + if (target_mask) {
> + if ((val & target_mask) == target)
> + return 0;
> + } else {
> + if (val)
> + return 0;
> + }
> + udelay(100);
> + if (time_after(jiffies, timeout)) {
> + lbs_pr_err("%s: timeout with val=%02x, "
> + "target_mask=%02x, target=%02x\n",
> + __func__, val, target_mask, target);
> + return -ETIMEDOUT;
> + }
> + }
> +}
> +
> +/* Read 16 bits from an SPI register until you receive a specific value.
> + * Returns -ETIMEDOUT if a 4 tries pass without success. */
> +int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)
> +{
> + int err, try;
> + for (try = 0; try < 4; ++try) {
> + u32 val = 0;
> + err = spu_read_u32(card, reg, &val);
> + if (err)
> + return err;
> + if (val == target)
> + return 0;
> + mdelay(100);
> + }
> + return -ETIMEDOUT;
> +}
> +
> +int spu_set_interrupt_mode(struct if_spi_card *card,
> + int suppress_host_int,
> + int auto_int)
> +{
> + int err = 0;
> +
> + /* We can suppress a host interrupt by clearing the appropriate
> + * bit in the "host interrupt status mask" register */
> + if (suppress_host_int) {
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
> + if (err)
> + return err;
> + } else {
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
> + IF_SPI_HISM_TX_DOWNLOAD_RDY |
> + IF_SPI_HISM_RX_UPLOAD_RDY |
> + IF_SPI_HISM_CMD_DOWNLOAD_RDY |
> + IF_SPI_HISM_CARDEVENT |
> + IF_SPI_HISM_CMD_UPLOAD_RDY);
> + if (err)
> + return err;
> + }
> +
> + /* If auto-interrupts are on, the completion of certain transactions
> + * will trigger an interrupt automatically. If auto-interrupts
> + * are off, we need to set the "Card Interrupt Cause" register to
> + * trigger a card interrupt. */
> + if (auto_int) {
> + err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
> + IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
> + IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
> + IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
> + IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO);
> + if (err)
> + return err;
> + } else {
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
> + if (err)
> + return err;
> + }
> + return err;
> +}
> +
> +static int spu_get_chip_revision(struct if_spi_card *card,
> + u16 *card_id, u8 *card_rev)
> +{
> + int err = 0;
> + u32 dev_ctrl;
> + err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
> + if (err)
> + return err;
> + *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
> + *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
> + return err;
> +}
> +
> +static int spu_set_bus_mode(struct if_spi_card *card, u16 mode)
> +{
> + int err = 0;
> + u16 rval;
> + /* set bus mode */
> + err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode);
> + if (err)
> + return err;
> + /* Check that we were able to read back what we just wrote. */
> + err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
> + if (err)
> + return err;
> + if (rval != mode) {
> + lbs_pr_err("Can't read bus mode register.\n");
> + return -EIO;
> + }
> + return 0;
> +}
> +
> +static int spu_init(struct if_spi_card *card, int use_dummy_writes)
> +{
> + int err = 0;
> + u16 rval;
> + u32 delay;
> +
> + /* We have to start up in timed delay mode so that we can safely
> + * read the Delay Read Register. */
> + card->use_dummy_writes = 0;
> + err = spu_set_bus_mode(card,
> + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
> + IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
> + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
> + if (err)
> + return err;
> + card->spu_port_delay = 100000;
> + card->spu_reg_delay = 100000;
> + err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay);
> + if (err)
> + return err;
> + card->spu_port_delay = delay & 0x0000ffff;
> + card->spu_reg_delay = (delay & 0xffff0000) >> 16;
> +
> + /* If dummy clock delay mode has been requested, switch to it now */
> + if (use_dummy_writes) {
> + card->use_dummy_writes = 1;
> + err = spu_set_bus_mode(card,
> + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
> + IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK |
> + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
> + if (err)
> + return err;
> + }
> +
> + /* Fetch interrupt status.
> + * This will cause the WLAN to de-assert the host-irq line. */
> + err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, &rval);
> + if (err)
> + return err;
> + lbs_deb_spi("IF_SPI_HOST_INT_STATUS_REG=0x%x\n", rval);
> +
> + lbs_deb_spi("Initialized bootloader. "
> + "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n",
> + card->spu_port_delay, card->spu_reg_delay);
> + return err;
> +}
> +
> +/*
> + * Firmware Loading
> + */
> +
> +static int if_spi_prog_helper_firmware(struct if_spi_card *card)
> +{
> + int err = 0;
> + const struct firmware *firmware = NULL;
> + int bytes_remaining;
> + const u8 *fw;
> + u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
> + struct spi_device *spi = card->spi;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + err = spu_set_interrupt_mode(card, 1, 0);
> + if (err)
> + goto out;
> + /* Get helper firmware image */
> + err = request_firmware(&firmware, card->helper_fw_name, &spi->dev);
> + if (err) {
> + lbs_pr_err("request_firmware failed with err = %d\n", err);
> + goto out;
> + }
> + bytes_remaining = firmware->size;
> + fw = firmware->data;
> +
> + /* Load helper firmware image */
> + while (bytes_remaining > 0) {
> + /* Scratch pad 1 should contain the number of bytes we
> + * want to download to the firmware */
> + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
> + HELPER_FW_LOAD_CHUNK_SZ);
> + if (err)
> + goto release_firmware;
> +
> + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
> + IF_SPI_HIST_CMD_DOWNLOAD_RDY,
> + IF_SPI_HIST_CMD_DOWNLOAD_RDY);
> + if (err)
> + goto release_firmware;
> +
> + /* Feed the data into the command read/write port reg
> + * in chunks of 64 bytes */
> + memset(temp, 0, sizeof(temp));
> + memcpy(temp, fw,
> + min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ));
> + mdelay(10);
> + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
> + temp, HELPER_FW_LOAD_CHUNK_SZ);
> + if (err)
> + goto release_firmware;
> +
> + /* Interrupt the boot code */
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
> + IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> + if (err)
> + goto release_firmware;
> + bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
> + fw += HELPER_FW_LOAD_CHUNK_SZ;
> + }
> +
> + /* Once the helper / single stage firmware download is complete,
> + * write 0 to scratch pad 1 and interrupt the
> + * bootloader. This completes the helper download. */
> + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
> + IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> + goto release_firmware;
> +
> + lbs_deb_spi("waiting for helper to boot...\n");
> +
> +release_firmware:
> + release_firmware(firmware);
> +out:
> + if (err)
> + lbs_pr_err("failed to load helper firmware (err=%d)\n", err);
> + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
> + return err;
> +}
> +
> +/* Returns the length of the next packet the firmware expects us to send
> + * Sets crc_err if the previous transfer had a CRC error. */
> +static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
> + int *crc_err)
> +{
> + u16 len;
> + int err = 0;
> +
> + /* wait until the host interrupt status register indicates
> +
> + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
> + IF_SPI_HIST_CMD_DOWNLOAD_RDY,
> + IF_SPI_HIST_CMD_DOWNLOAD_RDY);
> + if (err) {
> + lbs_pr_err("timed out waiting for host_int_status\n");
> + return err;
> + }
> +
> + /* Ask the device how many bytes of firmware it wants. */
> + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
> + if (err)
> + return err;
> +
> + if (len > LBS_CMD_BUFFER_SIZE) {
> + lbs_pr_err("firmware load device requested a larger "
> + "tranfer than we are prepared to "
> + "handle. (len = %d)\n", len);
> + return -EIO;
> + }
> + if (len & 0x1) {
> + lbs_deb_spi("%s: crc error\n", __func__);
> + len &= ~0x1;
> + *crc_err = 1;
> + } else
> + *crc_err = 0;
> +
> + return len;
> +}
> +
> +static int if_spi_prog_main_firmware(struct if_spi_card *card)
> +{
> + int len, prev_len;
> + int bytes, crc_err = 0, err = 0;
> + const struct firmware *firmware = NULL;
> + const u8 *fw;
> + struct spi_device *spi = card->spi;
> + u16 num_crc_errs;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + err = spu_set_interrupt_mode(card, 1, 0);
> + if (err)
> + goto out;
> +
> + /* Get firmware image */
> + err = request_firmware(&firmware, card->main_fw_name, &spi->dev);
> + if (err) {
> + lbs_pr_err("%s: can't get firmware '%s' from kernel. "
> + "err = %d\n", __func__, card->main_fw_name, err);
> + goto out;
> + }
> +
> + err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
> + if (err) {
> + lbs_pr_err("%s: timed out waiting for initial "
> + "scratch reg = 0\n", __func__);
> + goto release_firmware;
> + }
> +
> + num_crc_errs = 0;
> + prev_len = 0;
> + bytes = firmware->size;
> + fw = firmware->data;
> + while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
> + if (len < 0) {
> + err = len;
> + goto release_firmware;
> + }
> + if (bytes < 0) {
> + /* If there are no more bytes left, we would normally
> + * expect to have terminated with len = 0 */
> + lbs_pr_err("Firmware load wants more bytes "
> + "than we have to offer.\n");
> + break;
> + }
> + if (crc_err) {
> + /* Previous transfer failed. */
> + if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) {
> + lbs_pr_err("Too many CRC errors encountered "
> + "in firmware load.\n");
> + err = -EIO;
> + goto release_firmware;
> + }
> + } else {
> + /* Previous transfer succeeded. Advance counters. */
> + bytes -= prev_len;
> + fw += prev_len;
> + }
> + if (bytes < len) {
> + memset(card->cmd_buffer, 0, len);
> + memcpy(card->cmd_buffer, fw, bytes);
> + } else
> + memcpy(card->cmd_buffer, fw, len);
> +
> + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
> + if (err)
> + goto release_firmware;
> + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
> + card->cmd_buffer, len);
> + if (err)
> + goto release_firmware;
> + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
> + IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> + if (err)
> + goto release_firmware;
> + prev_len = len;
> + }
> + if (bytes > prev_len) {
> + lbs_pr_err("firmware load wants fewer bytes than "
> + "we have to offer.\n");
> + }
> +
> + /* Confirm firmware download */
> + err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
> + SUCCESSFUL_FW_DOWNLOAD_MAGIC);
> + if (err) {
> + lbs_pr_err("failed to confirm the firmware download\n");
> + goto release_firmware;
> + }
> +
> + err = spu_set_interrupt_mode(card, 0, 1);
> + if (err)
> + goto release_firmware;
> +
> +release_firmware:
> + release_firmware(firmware);
> +
> +out:
> + if (err)
> + lbs_pr_err("failed to load firmware (err=%d)\n", err);
> + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
> + return err;
> +}
> +
> +/*
> + * SPI Transfer Thread
> + *
> + * The SPI thread handles all SPI transfers, so there is no need for
> a lock.
> + */
> +
> +/* Move a command from the card to the host */
> +static int if_spi_c2h_cmd(struct if_spi_card *card)
> +{
> + struct lbs_private *priv = card->priv;
> + unsigned long flags;
> + int err = 0;
> + u16 len;
> + u8 i;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + /* How many bytes are there to read? */
> + err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len);
> + if (err)
> + goto out;
> + if (!len) {
> + lbs_pr_err("%s: error: card has no data for host\n",
> + __func__);
> + err = -EINVAL;
> + goto out;
> + } else if (len > LBS_CMD_BUFFER_SIZE) {
> + lbs_pr_err("%s: error: response packet too large: "
> + "%d bytes, but maximum is %d\n",
> + __func__, len, LBS_CMD_BUFFER_SIZE);
> + err = -EINVAL;
> + goto out;
> + }
> +
> + /* Read the data from the WLAN module into our command buffer */
> + err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG,
> + card->cmd_buffer, ALIGN(len, 2));
> + if (err)
> + goto out;
> +
> + spin_lock_irqsave(&priv->driver_lock, flags);
> + i = (priv->resp_idx == 0) ? 1 : 0;
> + BUG_ON(priv->resp_len[i]);
> + priv->resp_len[i] = len;
> + memcpy(priv->resp_buf[i], card->cmd_buffer, len);
> + lbs_notify_command_response(priv, i);
> + spin_unlock_irqrestore(&priv->driver_lock, flags);
> +
> +out:
> + if (err)
> + lbs_pr_err("%s: err=%d\n", __func__, err);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return err;
> +}
> +
> +/* Move data from the card to the host */
> +static int if_spi_c2h_data(struct if_spi_card *card)
> +{
> + struct sk_buff *skb;
> + char *data;
> + u16 len;
> + int err = 0;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + /* How many bytes are there to read? */
> + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
> + if (err)
> + goto out;
> + if (!len) {
> + lbs_pr_err("%s: error: card has no data for host\n",
> + __func__);
> + err = -EINVAL;
> + goto out;
> + } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
> + lbs_pr_err("%s: error: card has %d bytes of data, but "
> + "our maximum skb size is %lu\n",
> + __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
> + err = -EINVAL;
> + goto out;
> + }
> +
> + /* TODO: should we allocate a smaller skb if we have less data? */
> + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
> + if (!skb) {
> + err = -ENOBUFS;
> + goto out;
> + }
> + skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
> + data = skb_put(skb, len);
> +
> + /* Read the data from the WLAN module into our skb... */
> +
> + err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 2));
> + if (err)
> + goto free_skb;
> +
> + /* pass the SKB to libertas */
> + err = lbs_process_rxed_packet(card->priv, skb);
> + if (err)
> + goto free_skb;
> +
> + /* success */
> + goto out;
> +
> +free_skb:
> + dev_kfree_skb(skb);
> +out:
> + if (err)
> + lbs_pr_err("%s: err=%d\n", __func__, err);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return err;
> +}
> +
> +/* Move data or a command from the host to the card. */
> +static void if_spi_h2c(struct if_spi_card *card,
> + struct if_spi_packet *packet, int type)
> +{
> + int err = 0;
> + u16 int_type, port_reg;
> +
> + switch (type) {
> + case MVMS_DAT:
> + int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER;
> + port_reg = IF_SPI_DATA_RDWRPORT_REG;
> + break;
> + case MVMS_CMD:
> + int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER;
> + port_reg = IF_SPI_CMD_RDWRPORT_REG;
> + break;
> + default:
> + lbs_pr_err("can't transfer buffer of type %d\n", type);
> + err = -EINVAL;
> + goto out;
> + }
> +
> + /* Write the data to the card */
> + err = spu_write(card, port_reg, packet->buffer, packet->blen);
> + if (err)
> + goto out;
> +
> +out:
> + kfree(packet);
> +
> + if (err)
> + lbs_pr_err("%s: error %d\n", __func__, err);
> +}
> +
> +static int lbs_spi_thread(void *data)
> +{
> + int err;
> + struct if_spi_card *card = data;
> + u16 hiStatus;
> + unsigned long flags;
> + struct if_spi_packet *packet;
> +
> + while (1) {
> + /* Wait to be woken up by one of two things. First, our ISR
> + * could tell us that something happened on the WLAN.
> + * Secondly, libertas could call hw_host_to_card with more
> + * data, which we might be able to send.
> + */
> + do {
> + err = down_interruptible(&card->spi_ready);
> + if (!card->run_thread) {
> + up(&card->spi_thread_terminated);
> + do_exit(0);
> + }
> + } while (err == EINTR);
> +
> + /* Read the host interrupt status register to see what we
> + * can do. */
> + err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
> + &hiStatus);
> + if (err) {
> + lbs_pr_err("I/O error\n");
> + goto err;
> + }
> +
> + if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY)
> + err = if_spi_c2h_cmd(card);
> + if (err)
> + goto err;
> + if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY)
> + err = if_spi_c2h_data(card);
> + if (err)
> + goto err;
> + if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY) {
> + /* This means two things. First of all,
> + * if there was a previous command sent, the card has
> + * successfully received it.
> + * Secondly, it is now ready to download another
> + * command.
> + */
> + lbs_host_to_card_done(card->priv);
> +
> + /* Do we have any command packets from the host to
> + * send? */
> + packet = NULL;
> + spin_lock_irqsave(&card->buffer_lock, flags);
> + if (!list_empty(&card->cmd_packet_list)) {
> + packet = (struct if_spi_packet *)(card->
> + cmd_packet_list.next);
> + list_del(&packet->list);
> + }
> + spin_unlock_irqrestore(&card->buffer_lock, flags);
> +
> + if (packet)
> + if_spi_h2c(card, packet, MVMS_CMD);
> + }
> + if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
> + /* Do we have any data packets from the host to
> + * send? */
> + packet = NULL;
> + spin_lock_irqsave(&card->buffer_lock, flags);
> + if (!list_empty(&card->data_packet_list)) {
> + packet = (struct if_spi_packet *)(card->
> + data_packet_list.next);
> + list_del(&packet->list);
> + }
> + spin_unlock_irqrestore(&card->buffer_lock, flags);
> +
> + if (packet)
> + if_spi_h2c(card, packet, MVMS_DAT);
> + }
> + /* TODO: implement IF_SPI_HIST_CARD_EVENT
> + * Eventually this should feed into lbs_queue_event */
> +
> +err:
> + if (err)
> + lbs_pr_err("%s: got error %d\n", __func__, err);
> + }
> +}
> +
> +/* Block until lbs_spi_thread thread has terminated */
> +static void if_spi_terminate_spi_thread(struct if_spi_card *card)
> +{
> + /* It would be nice to use kthread_stop here, but that function
> + * can't wake threads waiting for a semaphore. */
> + card->run_thread = 0;
> + up(&card->spi_ready);
> + down(&card->spi_thread_terminated);
> +}
> +
> +/*
> + * Host to Card
> + *
> + * Called from Libertas to transfer some data to the WLAN device
> + * We can't sleep here. */
> +static int if_spi_host_to_card(struct lbs_private *priv,
> + u8 type, u8 *buf, u16 nb)
> +{
> + int err = 0;
> + unsigned long flags;
> + struct if_spi_card *card = priv->card;
> + struct if_spi_packet *packet;
> + u16 blen;
> +
> + lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb);
> +
> + if (nb == 0) {
> + lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb);
> + err = -EINVAL;
> + goto out;
> + }
> + blen = ALIGN(nb, 4);
> + packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC);
> + if (!packet) {
> + err = -ENOMEM;
> + goto out;
> + }
> + packet->blen = blen;
> + memcpy(packet->buffer, buf, nb);
> + memset(packet->buffer + nb, 0, blen - nb);
> +
> + switch (type) {
> + case MVMS_CMD:
> + priv->dnld_sent = DNLD_CMD_SENT;
> + spin_lock_irqsave(&card->buffer_lock, flags);
> + list_add_tail(&packet->list, &card->cmd_packet_list);
> + spin_unlock_irqrestore(&card->buffer_lock, flags);
> + break;
> + case MVMS_DAT:
> + priv->dnld_sent = DNLD_DATA_SENT;
> + spin_lock_irqsave(&card->buffer_lock, flags);
> + list_add_tail(&packet->list, &card->data_packet_list);
> + spin_unlock_irqrestore(&card->buffer_lock, flags);
> + break;
> + default:
> + lbs_pr_err("can't transfer buffer of type %d", type);
> + err = -EINVAL;
> + break;
> + }
> +
> + /* Wake up the spi thread */
> + up(&card->spi_ready);
> +out:
> + lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err);
> + return err;
> +}
> +
> +/*
> + * Host Interrupts
> + *
> + * Service incoming interrupts from the WLAN device. We can't sleep
> here, so
> + * don't try to talk on the SPI bus, just wake up the SPI thread.
> + */
> +static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
> +{
> + struct if_spi_card *card = dev_id;
> +
> + up(&card->spi_ready);
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * SPI callbacks
> + */
> +
> +static int if_spi_calculate_fw_names(u16 card_id,
> + char *helper_fw, char *main_fw)
> +{
> + int i;
> + for (i = 0; i < ARRAY_SIZE(chip_id_to_device_name); ++i) {
> + if (card_id == chip_id_to_device_name[i].chip_id)
> + break;
> + }
> + if (i == ARRAY_SIZE(chip_id_to_device_name)) {
> + lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id);
> + return -EAFNOSUPPORT;
> + }
> + snprintf(helper_fw, FIRMWARE_NAME_MAX, "spi%d_helper.bin",
> + chip_id_to_device_name[i].name);
> + snprintf(main_fw, FIRMWARE_NAME_MAX, "spi%d.bin",
> + chip_id_to_device_name[i].name);
> + return 0;
> +}
> +
> +static int __devinit if_spi_probe(struct spi_device *spi)
> +{
> + struct if_spi_card *card;
> + struct lbs_private *priv = NULL;
> + struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
> + int err = 0;
> +
> + lbs_deb_enter(LBS_DEB_SPI);
> +
> + /* Allocate card structure to represent this specific device */
> + card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
> + if (!card) {
> + err = -ENOMEM;
> + goto out;
> + }
> + spi_set_drvdata(spi, card);
> + card->spi = spi;
> + card->host_irq_num = pdata->host_irq_num;
> + card->gpio_cs = pdata->gpio_cs;
> + card->prev_xfer_time = jiffies;
> +
> + sema_init(&card->spi_ready, 0);
> + sema_init(&card->spi_thread_terminated, 0);
> + INIT_LIST_HEAD(&card->cmd_packet_list);
> + INIT_LIST_HEAD(&card->data_packet_list);
> + spin_lock_init(&card->buffer_lock);
> +
> + /* set up GPIO CS line. TODO: use regular CS line */
> + err = gpio_request(card->gpio_cs, "if_spi_gpio_chip_select");
> + if (err)
> + goto free_card;
> + err = gpio_direction_output(card->gpio_cs, 1);
> + if (err)
> + goto free_gpio;
> +
> + /* Initialize the SPI Interface Unit */
> + err = spu_init(card, pdata->use_dummy_writes);
> + if (err)
> + goto free_gpio;
> + err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
> + if (err)
> + goto free_gpio;
> +
> + /* program the firmware */
> + err = if_spi_calculate_fw_names(card->card_id,
> + card->helper_fw_name, card->main_fw_name);
> + if (err)
> + goto free_gpio;
> + lbs_deb_spi("Initializing firmware for Marvell WLAN 802.11 adapter "
> + "(chip_id = 0x%04x, chip_rev = 0x%02x) "
> + "attached to SPI bus_num %d, chip_select %d. "
> + "spi->max_speed_hz=%d\n",
> + card->card_id, card->card_rev,
> + spi->master->bus_num, spi->chip_select,
> + spi->max_speed_hz);
> + err = if_spi_prog_helper_firmware(card);
> + if (err)
> + goto free_gpio;
> + err = if_spi_prog_main_firmware(card);
> + if (err)
> + goto free_gpio;
> + lbs_deb_spi("loaded firmware for Marvell WLAN 802.11 adapter\n");
> +
> + /* Register our card with libertas.
> + * This will call alloc_etherdev */
> + priv = lbs_add_card(card, &spi->dev);
> + if (!priv) {
> + err = -ENOMEM;
> + goto free_gpio;
> + }
> + card->priv = priv;
> + priv->card = card;
> + priv->hw_host_to_card = if_spi_host_to_card;
> + priv->fw_ready = 1;
> +
> + /* Initialize interrupt handling stuff. */
> + card->run_thread = 1;
> + card->spi_thread = kthread_run(lbs_spi_thread, card, "lbs_spi_thread");
> + if (IS_ERR(card->spi_thread)) {
> + card->run_thread = 0;
> + err = PTR_ERR(card->spi_thread);
> + lbs_pr_err("error creating SPI thread: err=%d\n", err);
> + goto remove_card;
> + }
> + err = request_irq(card->host_irq_num, if_spi_host_interrupt,
> + IRQF_TRIGGER_FALLING,
> + "libertas_spi", card);
> + if (err) {
> + lbs_pr_err("can't get host irq line-- request_irq failed\n");
> + goto terminate_thread;
> + }
> +
> + /* Start the card.
> + * This will call register_netdev, and we'll start
> + * getting interrupts... */
> + err = lbs_start_card(priv);
> + if (err)
> + goto release_irq;
> +
> + lbs_deb_spi("Finished initializing WLAN module.\n");
> +
> + /* successful exit */
> + goto out;
> +
> +release_irq:
> + free_irq(card->host_irq_num, card);
> +terminate_thread:
> + if_spi_terminate_spi_thread(card);
> +remove_card:
> + lbs_remove_card(priv); /* will call free_netdev */
> +free_gpio:
> + gpio_free(card->gpio_cs);
> +free_card:
> + free_if_spi_card(card);
> +out:
> + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
> + return err;
> +}
> +
> +static int __devexit libertas_spi_remove(struct spi_device *spi)
> +{
> + struct if_spi_card *card = spi_get_drvdata(spi);
> + struct lbs_private *priv = card->priv;
> +
> + lbs_deb_spi("libertas_spi_remove\n");
> + lbs_deb_enter(LBS_DEB_SPI);
> + priv->surpriseremoved = 1;
> +
> + lbs_stop_card(priv);
> + free_irq(card->host_irq_num, card);
> + if_spi_terminate_spi_thread(card);
> + lbs_remove_card(priv); /* will call free_netdev */
> + gpio_free(card->gpio_cs);
> + free_if_spi_card(card);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return 0;
> +}
> +
> +static struct spi_driver libertas_spi_driver = {
> + .probe = if_spi_probe,
> + .remove = __devexit_p(libertas_spi_remove),
> + .driver = {
> + .name = "libertas_spi",
> + .bus = &spi_bus_type,
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +/*
> + * Module functions
> + */
> +
> +static int __init if_spi_init_module(void)
> +{
> + int ret = 0;
> + lbs_deb_enter(LBS_DEB_SPI);
> + printk(KERN_INFO "libertas_spi: Libertas SPI driver\n");
> + ret = spi_register_driver(&libertas_spi_driver);
> + lbs_deb_leave(LBS_DEB_SPI);
> + return ret;
> +}
> +
> +static void __exit if_spi_exit_module(void)
> +{
> + lbs_deb_enter(LBS_DEB_SPI);
> + spi_unregister_driver(&libertas_spi_driver);
> + lbs_deb_leave(LBS_DEB_SPI);
> +}
> +
> +module_init(if_spi_init_module);
> +module_exit(if_spi_exit_module);
> +
> +MODULE_DESCRIPTION("Libertas SPI WLAN Driver");
> +MODULE_AUTHOR("Andrey Yurovsky <[email protected]>, "
> + "Colin McCabe <[email protected]>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/wireless/libertas/if_spi.h
> b/drivers/net/wireless/libertas/if_spi.h
> new file mode 100644
> index 0000000..3e17a14
> --- /dev/null
> +++ b/drivers/net/wireless/libertas/if_spi.h
> @@ -0,0 +1,207 @@
> +/*
> + * linux/drivers/net/wireless/libertas/if_spi.c
> + *
> + * Driver for Marvell SPI WLAN cards.
> + *
> + * Copyright 2008 Analog Devices Inc.
> + *
> + * Authors:
> + * Andrey Yurovsky <[email protected]>
> + * Colin McCabe <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or (at
> + * your option) any later version.
> + */
> +
> +#ifndef _LBS_IF_SPI_H_
> +#define _LBS_IF_SPI_H_
> +
> +#define IPFIELD_ALIGN_OFFSET 2
> +
> +/***************** Firmware *****************/
> +struct chip_ident {
> + u16 chip_id;
> + u16 name;
> +};
> +
> +#define MAX_MAIN_FW_LOAD_CRC_ERR 10
> +
> +/* Chunk size when loading the helper firmware */
> +#define HELPER_FW_LOAD_CHUNK_SZ 64
> +
> +/* Value to write to indicate end of helper firmware dnld */
> +#define FIRMWARE_DNLD_OK 0x0000
> +
> +/* Value to check once the main firmware is downloaded */
> +#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888
> +
> +/***************** SPI Interface Unit *****************/
> +/* Masks used in SPI register read/write operations */
> +#define IF_SPI_READ_OPERATION_MASK 0x0
> +#define IF_SPI_WRITE_OPERATION_MASK 0x8000
> +
> +/* SPI register offsets. 4-byte aligned. */
> +#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */
> +#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */
> +#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */
> +#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */
> +
> +#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */
> +#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */
> +#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */
> +
> +#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */
> +#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */
> +#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */
> +
> +#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */
> +#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */
> +#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */
> +#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */
> +
> +#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number
> reg */
> +#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */
> +
> +#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */
> +
> +#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */
> +#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interupt status reg */
> +#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event
> mask */
> +#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status
> mask */
> +
> +#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset
> select */
> +
> +#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */
> +#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */
> +#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event
> mask */
> +#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status
> mask */
> +#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset
> select */
> +
> +#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */
> +#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */
> +
> +/***************** IF_SPI_DEVICEID_CTRL_REG *****************/
> +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16)
> +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff)
> +
> +/***************** IF_SPI_HOST_INT_CTRL_REG *****************/
> +/** Host Interrupt Control bit : Wake up */
> +#define IF_SPI_HICT_WAKE_UP (1<<0)
> +/** Host Interrupt Control bit : WLAN ready */
> +#define IF_SPI_HICT_WLAN_READY (1<<1)
> +/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */
> +/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */
> +/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */
> +/** Host Interrupt Control bit : Tx auto download */
> +#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5)
> +/** Host Interrupt Control bit : Rx auto upload */
> +#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6)
> +/** Host Interrupt Control bit : Command auto download */
> +#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7)
> +/** Host Interrupt Control bit : Command auto upload */
> +#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8)
> +
> +/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/
> +/** Card Interrupt Case bit : Tx download over */
> +#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0)
> +/** Card Interrupt Case bit : Rx upload over */
> +#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1)
> +/** Card Interrupt Case bit : Command download over */
> +#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2)
> +/** Card Interrupt Case bit : Host event */
> +#define IF_SPI_CIC_HOST_EVENT (1<<3)
> +/** Card Interrupt Case bit : Command upload over */
> +#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4)
> +/** Card Interrupt Case bit : Power down */
> +#define IF_SPI_CIC_POWER_DOWN (1<<5)
> +
> +/***************** IF_SPI_CARD_INT_STATUS_REG *****************/
> +#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0)
> +#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1)
> +#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2)
> +#define IF_SPI_CIS_HOST_EVENT (1<<3)
> +#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4)
> +#define IF_SPI_CIS_POWER_DOWN (1<<5)
> +
> +/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/
> +#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0)
> +#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1)
> +#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2)
> +#define IF_SPI_HICU_CARD_EVENT (1<<3)
> +#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4)
> +#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5)
> +#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6)
> +#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7)
> +#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8)
> +#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9)
> +#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10)
> +
> +/***************** IF_SPI_HOST_INT_STATUS_REG *****************/
> +/** Host Interrupt Status bit : Tx download ready */
> +#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0)
> +/** Host Interrupt Status bit : Rx upload ready */
> +#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1)
> +/** Host Interrupt Status bit : Command download ready */
> +#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2)
> +/** Host Interrupt Status bit : Card event */
> +#define IF_SPI_HIST_CARD_EVENT (1<<3)
> +/** Host Interrupt Status bit : Command upload ready */
> +#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4)
> +/** Host Interrupt Status bit : I/O write FIFO overflow */
> +#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5)
> +/** Host Interrupt Status bit : I/O read FIFO underflow */
> +#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6)
> +/** Host Interrupt Status bit : Data write FIFO overflow */
> +#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7)
> +/** Host Interrupt Status bit : Data read FIFO underflow */
> +#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8)
> +/** Host Interrupt Status bit : Command write FIFO overflow */
> +#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9)
> +/** Host Interrupt Status bit : Command read FIFO underflow */
> +#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10)
> +
> +/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/
> +/** Host Interrupt Status Mask bit : Tx download ready */
> +#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0)
> +/** Host Interrupt Status Mask bit : Rx upload ready */
> +#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1)
> +/** Host Interrupt Status Mask bit : Command download ready */
> +#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2)
> +/** Host Interrupt Status Mask bit : Card event */
> +#define IF_SPI_HISM_CARDEVENT (1<<3)
> +/** Host Interrupt Status Mask bit : Command upload ready */
> +#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4)
> +/** Host Interrupt Status Mask bit : I/O write FIFO overflow */
> +#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5)
> +/** Host Interrupt Status Mask bit : I/O read FIFO underflow */
> +#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6)
> +/** Host Interrupt Status Mask bit : Data write FIFO overflow */
> +#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7)
> +/** Host Interrupt Status Mask bit : Data write FIFO underflow */
> +#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8)
> +/** Host Interrupt Status Mask bit : Command write FIFO overflow */
> +#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9)
> +/** Host Interrupt Status Mask bit : Command write FIFO underflow */
> +#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10)
> +
> +/***************** IF_SPI_SPU_BUS_MODE_REG *****************/
> +/* SCK edge on which the WLAN module outputs data on MISO */
> +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8
> +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0
> +
> +/* In a SPIR read operation, there is a delay between writing the SPIR
> + * register name and getting back data from the WLAN module.
> + * This can be specified in terms of nanoseconds or in terms of dummy
> + * clock cycles which the master must output before receiving a
> response. */
> +#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4
> +#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0
> +
> +/* Some different modes of SPI operation */
> +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00
> +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01
> +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02
> +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03
> +
> +#endif
> diff --git a/include/linux/spi/libertas_spi.h
> b/include/linux/spi/libertas_spi.h
> new file mode 100644
> index 0000000..a654261
> --- /dev/null
> +++ b/include/linux/spi/libertas_spi.h
> @@ -0,0 +1,31 @@
> +/*
> + * board-specific data for the libertas_spi driver.
> + *
> + * Copyright 2008 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or (at
> + * your option) any later version.
> + */
> +#ifndef _LIBERTAS_SPI_H_
> +#define _LIBERTAS_SPI_H_
> +struct libertas_spi_platform_data {
> + /* There are two ways to read data from the WLAN module's SPI
> + * interface. Setting 0 or 1 here controls which one is used.
> + *
> + * Usually you want to set use_dummy_writes = 1.
> + * However, if that doesn't work or if you are using a slow SPI clock
> + * speed, you may want to use 0 here. */
> + u16 use_dummy_writes;
> +
> + /* All SPI communications are initiated by the bus master. In order
> + * for the WLAN module to tell the kernel that it has received data
> + * from the network, it must use an out-of-band IRQ line. Here we
> + * specify which IRQ line to use. */
> + u16 host_irq_num;
> +
> + /* GPIO number to use as chip select */
> + u16 gpio_cs;
> +};
> +#endif
> * that we are ready to download */

2008-12-16 18:14:07

by Andrey Yurovsky

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

On Tue, Dec 16, 2008 at 9:38 AM, Dan Williams <[email protected]> wrote:
> On Mon, 2008-12-15 at 22:44 -0500, Angel Roman wrote:
>> Hi Colin,
>>
>> I have support for the gspi as well. I've been trying to get in contact
>> with Dan Williams in order to contribute it to the list.
>
> Sorry about that... it's in my queue and I'll try to get to do some
> review in the next few days. Doing the new interface isn't a ton of
> code, and I'd expect both yours and Colin's drivers to be quite similar
> as there's only a few ways this thing can be done :)
>
> The submission process is basically just like Colin did; generate a
> series of patches of your latest code (split into independent patches if
> possible) based on a kernel version (ideally the latest kernel version
> or better yet, wireless-testing.git) and then post it to linux-wireless
> and maybe cc libertas-dev as well.

We've been working/testing against a kernel that's pretty close to
upstream and then also against wireless-testing to make this driver
upstream-friendly and hopefully easy to merge. If you have access to
a Blackfin development board, you can also clone our tree with the
driver integrated:
http://git.cozybit.com/
however it should be simple enough to build for other embedded platforms.

> Is there a generic SPI layer that could be used for the board-specific
> bits too, rather than putting that stuff in the libertas tree? I assume
> that the SPI bus is more or less generic on your hardware (ie you could
> put something else on the other end instead of the 8686), and thus it
> would be better if we could figure out way not to put some much board
> specific logic into the libertas driver itself.

The exception is the GPIO-based chip select signal that the '8686
requires, we otherwise use the generic SPI layer. The protocol
requires the host to assert the CS# line and then hold it low for the
entire transaction (which can be of arbitrary length). Most SPI host
controllers cannot do this and therefore a plain GPIO is used. The
board-specific config for this is not much different from configuring
your SPI chips in your mach-xxxx/board.c

--
Andrey Yurovsky
cozybit Inc.

2008-12-16 17:39:56

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH 3/3] libertas: if_spi, driver for libertas GSPI devices

On Mon, 2008-12-15 at 22:44 -0500, Angel Roman wrote:
> Hi Colin,
>
> I have support for the gspi as well. I've been trying to get in contact
> with Dan Williams in order to contribute it to the list.

Sorry about that... it's in my queue and I'll try to get to do some
review in the next few days. Doing the new interface isn't a ton of
code, and I'd expect both yours and Colin's drivers to be quite similar
as there's only a few ways this thing can be done :)

The submission process is basically just like Colin did; generate a
series of patches of your latest code (split into independent patches if
possible) based on a kernel version (ideally the latest kernel version
or better yet, wireless-testing.git) and then post it to linux-wireless
and maybe cc libertas-dev as well.

> If you want, you can take a look at the code via:
>
> svn list -R
> svn://svn.buglabs.net/bug/trunk/bug-linux-2.6.27.2/drivers/net/wireless/libertas
>
>
> This is currently working in the mx31 processor. Maybe we can work out a
> way to merge the two drivers. The mx31 was a little tricky since there's
> an error in the processor where one is not able to keep the chip select
> signal active during multiple spi transfers as requried by the wifi
> module. I've also abstracted the board interface from the GSPI code as
> much as I could.

Is there a generic SPI layer that could be used for the board-specific
bits too, rather than putting that stuff in the libertas tree? I assume
that the SPI bus is more or less generic on your hardware (ie you could
put something else on the other end instead of the 8686), and thus it
would be better if we could figure out way not to put some much board
specific logic into the libertas driver itself.

Dan