162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2008 Christian Lamparter <chunkeey@web.de>
462306a36Sopenharmony_ci * Copyright 2008       Johannes Berg <johannes@sipsolutions.net>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This driver is a port from stlc45xx:
762306a36Sopenharmony_ci *	Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/firmware.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/spi/spi.h>
1762306a36Sopenharmony_ci#include <linux/etherdevice.h>
1862306a36Sopenharmony_ci#include <linux/gpio.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "p54spi.h"
2262306a36Sopenharmony_ci#include "p54.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "lmac.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
2762306a36Sopenharmony_ci#include "p54spi_eeprom.h"
2862306a36Sopenharmony_ci#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciMODULE_FIRMWARE("3826.arm");
3162306a36Sopenharmony_ciMODULE_FIRMWARE("3826.eeprom");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* gpios should be handled in board files and provided via platform data,
3462306a36Sopenharmony_ci * but because it's currently impossible for p54spi to have a header file
3562306a36Sopenharmony_ci * in include/linux, let's use module paramaters for now
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int p54spi_gpio_power = 97;
3962306a36Sopenharmony_cimodule_param(p54spi_gpio_power, int, 0444);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int p54spi_gpio_irq = 87;
4362306a36Sopenharmony_cimodule_param(p54spi_gpio_irq, int, 0444);
4462306a36Sopenharmony_ciMODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void p54spi_spi_read(struct p54s_priv *priv, u8 address,
4762306a36Sopenharmony_ci			      void *buf, size_t len)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct spi_transfer t[2];
5062306a36Sopenharmony_ci	struct spi_message m;
5162306a36Sopenharmony_ci	__le16 addr;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* We first push the address */
5462306a36Sopenharmony_ci	addr = cpu_to_le16(address << 8 | SPI_ADRS_READ_BIT_15);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	spi_message_init(&m);
5762306a36Sopenharmony_ci	memset(t, 0, sizeof(t));
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	t[0].tx_buf = &addr;
6062306a36Sopenharmony_ci	t[0].len = sizeof(addr);
6162306a36Sopenharmony_ci	spi_message_add_tail(&t[0], &m);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	t[1].rx_buf = buf;
6462306a36Sopenharmony_ci	t[1].len = len;
6562306a36Sopenharmony_ci	spi_message_add_tail(&t[1], &m);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	spi_sync(priv->spi, &m);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void p54spi_spi_write(struct p54s_priv *priv, u8 address,
7262306a36Sopenharmony_ci			     const void *buf, size_t len)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct spi_transfer t[3];
7562306a36Sopenharmony_ci	struct spi_message m;
7662306a36Sopenharmony_ci	__le16 addr;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* We first push the address */
7962306a36Sopenharmony_ci	addr = cpu_to_le16(address << 8);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	spi_message_init(&m);
8262306a36Sopenharmony_ci	memset(t, 0, sizeof(t));
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	t[0].tx_buf = &addr;
8562306a36Sopenharmony_ci	t[0].len = sizeof(addr);
8662306a36Sopenharmony_ci	spi_message_add_tail(&t[0], &m);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	t[1].tx_buf = buf;
8962306a36Sopenharmony_ci	t[1].len = len & ~1;
9062306a36Sopenharmony_ci	spi_message_add_tail(&t[1], &m);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (len % 2) {
9362306a36Sopenharmony_ci		__le16 last_word;
9462306a36Sopenharmony_ci		last_word = cpu_to_le16(((u8 *)buf)[len - 1]);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		t[2].tx_buf = &last_word;
9762306a36Sopenharmony_ci		t[2].len = sizeof(last_word);
9862306a36Sopenharmony_ci		spi_message_add_tail(&t[2], &m);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	spi_sync(priv->spi, &m);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic u32 p54spi_read32(struct p54s_priv *priv, u8 addr)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	__le32 val;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	p54spi_spi_read(priv, addr, &val, sizeof(val));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return le32_to_cpu(val);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline void p54spi_write16(struct p54s_priv *priv, u8 addr, __le16 val)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	p54spi_spi_write(priv, addr, &val, sizeof(val));
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	p54spi_spi_write(priv, addr, &val, sizeof(val));
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int i;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	for (i = 0; i < 2000; i++) {
12862306a36Sopenharmony_ci		u32 buffer = p54spi_read32(priv, reg);
12962306a36Sopenharmony_ci		if ((buffer & bits) == bits)
13062306a36Sopenharmony_ci			return 1;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
13662306a36Sopenharmony_ci				const void *buf, size_t len)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) {
13962306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "spi_write_dma not allowed "
14062306a36Sopenharmony_ci			"to DMA write.\n");
14162306a36Sopenharmony_ci		return -EAGAIN;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
14562306a36Sopenharmony_ci		       cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len));
14862306a36Sopenharmony_ci	p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base);
14962306a36Sopenharmony_ci	p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len);
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int p54spi_request_firmware(struct ieee80211_hw *dev)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct p54s_priv *priv = dev->priv;
15662306a36Sopenharmony_ci	int ret;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* FIXME: should driver use it's own struct device? */
15962306a36Sopenharmony_ci	ret = request_firmware(&priv->firmware, "3826.arm", &priv->spi->dev);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (ret < 0) {
16262306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "request_firmware() failed: %d", ret);
16362306a36Sopenharmony_ci		return ret;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ret = p54_parse_firmware(dev, priv->firmware);
16762306a36Sopenharmony_ci	if (ret) {
16862306a36Sopenharmony_ci		/* the firmware is released by the caller */
16962306a36Sopenharmony_ci		return ret;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int p54spi_request_eeprom(struct ieee80211_hw *dev)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct p54s_priv *priv = dev->priv;
17862306a36Sopenharmony_ci	const struct firmware *eeprom;
17962306a36Sopenharmony_ci	int ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* allow users to customize their eeprom.
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev);
18562306a36Sopenharmony_ci	if (ret < 0) {
18662306a36Sopenharmony_ci#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
18762306a36Sopenharmony_ci		dev_info(&priv->spi->dev, "loading default eeprom...\n");
18862306a36Sopenharmony_ci		ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom,
18962306a36Sopenharmony_ci				       sizeof(p54spi_eeprom));
19062306a36Sopenharmony_ci#else
19162306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "Failed to request user eeprom\n");
19262306a36Sopenharmony_ci#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */
19362306a36Sopenharmony_ci	} else {
19462306a36Sopenharmony_ci		dev_info(&priv->spi->dev, "loading user eeprom...\n");
19562306a36Sopenharmony_ci		ret = p54_parse_eeprom(dev, (void *) eeprom->data,
19662306a36Sopenharmony_ci				       (int)eeprom->size);
19762306a36Sopenharmony_ci		release_firmware(eeprom);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	return ret;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int p54spi_upload_firmware(struct ieee80211_hw *dev)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct p54s_priv *priv = dev->priv;
20562306a36Sopenharmony_ci	unsigned long fw_len, _fw_len;
20662306a36Sopenharmony_ci	unsigned int offset = 0;
20762306a36Sopenharmony_ci	int err = 0;
20862306a36Sopenharmony_ci	u8 *fw;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	fw_len = priv->firmware->size;
21162306a36Sopenharmony_ci	fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL);
21262306a36Sopenharmony_ci	if (!fw)
21362306a36Sopenharmony_ci		return -ENOMEM;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* stop the device */
21662306a36Sopenharmony_ci	p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
21762306a36Sopenharmony_ci		       SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET |
21862306a36Sopenharmony_ci		       SPI_CTRL_STAT_START_HALTED));
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	msleep(TARGET_BOOT_SLEEP);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
22362306a36Sopenharmony_ci		       SPI_CTRL_STAT_HOST_OVERRIDE |
22462306a36Sopenharmony_ci		       SPI_CTRL_STAT_START_HALTED));
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	msleep(TARGET_BOOT_SLEEP);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	while (fw_len > 0) {
22962306a36Sopenharmony_ci		_fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		err = p54spi_spi_write_dma(priv, cpu_to_le32(
23262306a36Sopenharmony_ci					   ISL38XX_DEV_FIRMWARE_ADDR + offset),
23362306a36Sopenharmony_ci					   (fw + offset), _fw_len);
23462306a36Sopenharmony_ci		if (err < 0)
23562306a36Sopenharmony_ci			goto out;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		fw_len -= _fw_len;
23862306a36Sopenharmony_ci		offset += _fw_len;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	BUG_ON(fw_len != 0);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* enable host interrupts */
24462306a36Sopenharmony_ci	p54spi_write32(priv, SPI_ADRS_HOST_INT_EN,
24562306a36Sopenharmony_ci		       cpu_to_le32(SPI_HOST_INTS_DEFAULT));
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* boot the device */
24862306a36Sopenharmony_ci	p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
24962306a36Sopenharmony_ci		       SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET |
25062306a36Sopenharmony_ci		       SPI_CTRL_STAT_RAM_BOOT));
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	msleep(TARGET_BOOT_SLEEP);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
25562306a36Sopenharmony_ci		       SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT));
25662306a36Sopenharmony_ci	msleep(TARGET_BOOT_SLEEP);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciout:
25962306a36Sopenharmony_ci	kfree(fw);
26062306a36Sopenharmony_ci	return err;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void p54spi_power_off(struct p54s_priv *priv)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	disable_irq(gpio_to_irq(p54spi_gpio_irq));
26662306a36Sopenharmony_ci	gpio_set_value(p54spi_gpio_power, 0);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void p54spi_power_on(struct p54s_priv *priv)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	gpio_set_value(p54spi_gpio_power, 1);
27262306a36Sopenharmony_ci	enable_irq(gpio_to_irq(p54spi_gpio_irq));
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* need to wait a while before device can be accessed, the length
27562306a36Sopenharmony_ci	 * is just a guess
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	msleep(10);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic inline void p54spi_int_ack(struct p54s_priv *priv, u32 val)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val));
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int p54spi_wakeup(struct p54s_priv *priv)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	/* wake the chip */
28862306a36Sopenharmony_ci	p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
28962306a36Sopenharmony_ci		       cpu_to_le32(SPI_TARGET_INT_WAKEUP));
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* And wait for the READY interrupt */
29262306a36Sopenharmony_ci	if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
29362306a36Sopenharmony_ci			     SPI_HOST_INT_READY)) {
29462306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "INT_READY timeout\n");
29562306a36Sopenharmony_ci		return -EBUSY;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	p54spi_int_ack(priv, SPI_HOST_INT_READY);
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic inline void p54spi_sleep(struct p54s_priv *priv)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
30562306a36Sopenharmony_ci		       cpu_to_le32(SPI_TARGET_INT_SLEEP));
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void p54spi_int_ready(struct p54s_priv *priv)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32(
31162306a36Sopenharmony_ci		       SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE));
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	switch (priv->fw_state) {
31462306a36Sopenharmony_ci	case FW_STATE_BOOTING:
31562306a36Sopenharmony_ci		priv->fw_state = FW_STATE_READY;
31662306a36Sopenharmony_ci		complete(&priv->fw_comp);
31762306a36Sopenharmony_ci		break;
31862306a36Sopenharmony_ci	case FW_STATE_RESETTING:
31962306a36Sopenharmony_ci		priv->fw_state = FW_STATE_READY;
32062306a36Sopenharmony_ci		/* TODO: reinitialize state */
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	default:
32362306a36Sopenharmony_ci		break;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int p54spi_rx(struct p54s_priv *priv)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct sk_buff *skb;
33062306a36Sopenharmony_ci	u16 len;
33162306a36Sopenharmony_ci	u16 rx_head[2];
33262306a36Sopenharmony_ci#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (p54spi_wakeup(priv) < 0)
33562306a36Sopenharmony_ci		return -EBUSY;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Read data size and first data word in one SPI transaction
33862306a36Sopenharmony_ci	 * This is workaround for firmware/DMA bug,
33962306a36Sopenharmony_ci	 * when first data word gets lost under high load.
34062306a36Sopenharmony_ci	 */
34162306a36Sopenharmony_ci	p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
34262306a36Sopenharmony_ci	len = rx_head[0];
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (len == 0) {
34562306a36Sopenharmony_ci		p54spi_sleep(priv);
34662306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "rx request of zero bytes\n");
34762306a36Sopenharmony_ci		return 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Firmware may insert up to 4 padding bytes after the lmac header,
35162306a36Sopenharmony_ci	 * but it does not amend the size of SPI data transfer.
35262306a36Sopenharmony_ci	 * Such packets has correct data size in header, thus referencing
35362306a36Sopenharmony_ci	 * past the end of allocated skb. Reserve extra 4 bytes for this case
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	skb = dev_alloc_skb(len + 4);
35662306a36Sopenharmony_ci	if (!skb) {
35762306a36Sopenharmony_ci		p54spi_sleep(priv);
35862306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "could not alloc skb");
35962306a36Sopenharmony_ci		return -ENOMEM;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (len <= READAHEAD_SZ) {
36362306a36Sopenharmony_ci		skb_put_data(skb, rx_head + 1, len);
36462306a36Sopenharmony_ci	} else {
36562306a36Sopenharmony_ci		skb_put_data(skb, rx_head + 1, READAHEAD_SZ);
36662306a36Sopenharmony_ci		p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
36762306a36Sopenharmony_ci				skb_put(skb, len - READAHEAD_SZ),
36862306a36Sopenharmony_ci				len - READAHEAD_SZ);
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci	p54spi_sleep(priv);
37162306a36Sopenharmony_ci	/* Put additional bytes to compensate for the possible
37262306a36Sopenharmony_ci	 * alignment-caused truncation
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci	skb_put(skb, 4);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (p54_rx(priv->hw, skb) == 0)
37762306a36Sopenharmony_ci		dev_kfree_skb(skb);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic irqreturn_t p54spi_interrupt(int irq, void *config)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct spi_device *spi = config;
38662306a36Sopenharmony_ci	struct p54s_priv *priv = spi_get_drvdata(spi);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	ieee80211_queue_work(priv->hw, &priv->work);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return IRQ_HANDLED;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
39662306a36Sopenharmony_ci	int ret = 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (p54spi_wakeup(priv) < 0)
39962306a36Sopenharmony_ci		return -EBUSY;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len);
40262306a36Sopenharmony_ci	if (ret < 0)
40362306a36Sopenharmony_ci		goto out;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
40662306a36Sopenharmony_ci			     SPI_HOST_INT_WR_READY)) {
40762306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "WR_READY timeout\n");
40862306a36Sopenharmony_ci		ret = -EAGAIN;
40962306a36Sopenharmony_ci		goto out;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	p54spi_int_ack(priv, SPI_HOST_INT_WR_READY);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (FREE_AFTER_TX(skb))
41562306a36Sopenharmony_ci		p54_free_skb(priv->hw, skb);
41662306a36Sopenharmony_ciout:
41762306a36Sopenharmony_ci	p54spi_sleep(priv);
41862306a36Sopenharmony_ci	return ret;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int p54spi_wq_tx(struct p54s_priv *priv)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct p54s_tx_info *entry;
42462306a36Sopenharmony_ci	struct sk_buff *skb;
42562306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
42662306a36Sopenharmony_ci	struct p54_tx_info *minfo;
42762306a36Sopenharmony_ci	struct p54s_tx_info *dinfo;
42862306a36Sopenharmony_ci	unsigned long flags;
42962306a36Sopenharmony_ci	int ret = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	spin_lock_irqsave(&priv->tx_lock, flags);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	while (!list_empty(&priv->tx_pending)) {
43462306a36Sopenharmony_ci		entry = list_entry(priv->tx_pending.next,
43562306a36Sopenharmony_ci				   struct p54s_tx_info, tx_list);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		list_del_init(&entry->tx_list);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->tx_lock, flags);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		dinfo = container_of((void *) entry, struct p54s_tx_info,
44262306a36Sopenharmony_ci				     tx_list);
44362306a36Sopenharmony_ci		minfo = container_of((void *) dinfo, struct p54_tx_info,
44462306a36Sopenharmony_ci				     data);
44562306a36Sopenharmony_ci		info = container_of((void *) minfo, struct ieee80211_tx_info,
44662306a36Sopenharmony_ci				    rate_driver_data);
44762306a36Sopenharmony_ci		skb = container_of((void *) info, struct sk_buff, cb);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		ret = p54spi_tx_frame(priv, skb);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		if (ret < 0) {
45262306a36Sopenharmony_ci			p54_free_skb(priv->hw, skb);
45362306a36Sopenharmony_ci			return ret;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		spin_lock_irqsave(&priv->tx_lock, flags);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->tx_lock, flags);
45962306a36Sopenharmony_ci	return ret;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct p54s_priv *priv = dev->priv;
46562306a36Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
46662306a36Sopenharmony_ci	struct p54_tx_info *mi = (struct p54_tx_info *) info->rate_driver_data;
46762306a36Sopenharmony_ci	struct p54s_tx_info *di = (struct p54s_tx_info *) mi->data;
46862306a36Sopenharmony_ci	unsigned long flags;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(*di) > sizeof((mi->data)));
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	spin_lock_irqsave(&priv->tx_lock, flags);
47362306a36Sopenharmony_ci	list_add_tail(&di->tx_list, &priv->tx_pending);
47462306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->tx_lock, flags);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ieee80211_queue_work(priv->hw, &priv->work);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void p54spi_work(struct work_struct *work)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct p54s_priv *priv = container_of(work, struct p54s_priv, work);
48262306a36Sopenharmony_ci	u32 ints;
48362306a36Sopenharmony_ci	int ret;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (priv->fw_state == FW_STATE_OFF)
48862306a36Sopenharmony_ci		goto out;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (ints & SPI_HOST_INT_READY) {
49362306a36Sopenharmony_ci		p54spi_int_ready(priv);
49462306a36Sopenharmony_ci		p54spi_int_ack(priv, SPI_HOST_INT_READY);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (priv->fw_state != FW_STATE_READY)
49862306a36Sopenharmony_ci		goto out;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (ints & SPI_HOST_INT_UPDATE) {
50162306a36Sopenharmony_ci		p54spi_int_ack(priv, SPI_HOST_INT_UPDATE);
50262306a36Sopenharmony_ci		ret = p54spi_rx(priv);
50362306a36Sopenharmony_ci		if (ret < 0)
50462306a36Sopenharmony_ci			goto out;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci	if (ints & SPI_HOST_INT_SW_UPDATE) {
50762306a36Sopenharmony_ci		p54spi_int_ack(priv, SPI_HOST_INT_SW_UPDATE);
50862306a36Sopenharmony_ci		ret = p54spi_rx(priv);
50962306a36Sopenharmony_ci		if (ret < 0)
51062306a36Sopenharmony_ci			goto out;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	ret = p54spi_wq_tx(priv);
51462306a36Sopenharmony_ciout:
51562306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int p54spi_op_start(struct ieee80211_hw *dev)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct p54s_priv *priv = dev->priv;
52162306a36Sopenharmony_ci	unsigned long timeout;
52262306a36Sopenharmony_ci	int ret = 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (mutex_lock_interruptible(&priv->mutex)) {
52562306a36Sopenharmony_ci		ret = -EINTR;
52662306a36Sopenharmony_ci		goto out;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	priv->fw_state = FW_STATE_BOOTING;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	p54spi_power_on(priv);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	ret = p54spi_upload_firmware(dev);
53462306a36Sopenharmony_ci	if (ret < 0) {
53562306a36Sopenharmony_ci		p54spi_power_off(priv);
53662306a36Sopenharmony_ci		goto out_unlock;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	timeout = msecs_to_jiffies(2000);
54262306a36Sopenharmony_ci	timeout = wait_for_completion_interruptible_timeout(&priv->fw_comp,
54362306a36Sopenharmony_ci							    timeout);
54462306a36Sopenharmony_ci	if (!timeout) {
54562306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "firmware boot failed");
54662306a36Sopenharmony_ci		p54spi_power_off(priv);
54762306a36Sopenharmony_ci		ret = -1;
54862306a36Sopenharmony_ci		goto out;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&priv->mutex)) {
55262306a36Sopenharmony_ci		ret = -EINTR;
55362306a36Sopenharmony_ci		p54spi_power_off(priv);
55462306a36Sopenharmony_ci		goto out;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	WARN_ON(priv->fw_state != FW_STATE_READY);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ciout_unlock:
56062306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ciout:
56362306a36Sopenharmony_ci	return ret;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void p54spi_op_stop(struct ieee80211_hw *dev)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct p54s_priv *priv = dev->priv;
56962306a36Sopenharmony_ci	unsigned long flags;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
57262306a36Sopenharmony_ci	WARN_ON(priv->fw_state != FW_STATE_READY);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	p54spi_power_off(priv);
57562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->tx_lock, flags);
57662306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_pending);
57762306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->tx_lock, flags);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	priv->fw_state = FW_STATE_OFF;
58062306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	cancel_work_sync(&priv->work);
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int p54spi_probe(struct spi_device *spi)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct p54s_priv *priv = NULL;
58862306a36Sopenharmony_ci	struct ieee80211_hw *hw;
58962306a36Sopenharmony_ci	int ret = -EINVAL;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	hw = p54_init_common(sizeof(*priv));
59262306a36Sopenharmony_ci	if (!hw) {
59362306a36Sopenharmony_ci		dev_err(&spi->dev, "could not alloc ieee80211_hw");
59462306a36Sopenharmony_ci		return -ENOMEM;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	priv = hw->priv;
59862306a36Sopenharmony_ci	priv->hw = hw;
59962306a36Sopenharmony_ci	spi_set_drvdata(spi, priv);
60062306a36Sopenharmony_ci	priv->spi = spi;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	spi->bits_per_word = 16;
60362306a36Sopenharmony_ci	spi->max_speed_hz = 24000000;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	ret = spi_setup(spi);
60662306a36Sopenharmony_ci	if (ret < 0) {
60762306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "spi_setup failed");
60862306a36Sopenharmony_ci		goto err_free;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	ret = gpio_request(p54spi_gpio_power, "p54spi power");
61262306a36Sopenharmony_ci	if (ret < 0) {
61362306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret);
61462306a36Sopenharmony_ci		goto err_free;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ret = gpio_request(p54spi_gpio_irq, "p54spi irq");
61862306a36Sopenharmony_ci	if (ret < 0) {
61962306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret);
62062306a36Sopenharmony_ci		goto err_free_gpio_power;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	gpio_direction_output(p54spi_gpio_power, 0);
62462306a36Sopenharmony_ci	gpio_direction_input(p54spi_gpio_irq);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
62762306a36Sopenharmony_ci			  p54spi_interrupt, 0, "p54spi",
62862306a36Sopenharmony_ci			  priv->spi);
62962306a36Sopenharmony_ci	if (ret < 0) {
63062306a36Sopenharmony_ci		dev_err(&priv->spi->dev, "request_irq() failed");
63162306a36Sopenharmony_ci		goto err_free_gpio_irq;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	disable_irq(gpio_to_irq(p54spi_gpio_irq));
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	INIT_WORK(&priv->work, p54spi_work);
63962306a36Sopenharmony_ci	init_completion(&priv->fw_comp);
64062306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->tx_pending);
64162306a36Sopenharmony_ci	mutex_init(&priv->mutex);
64262306a36Sopenharmony_ci	spin_lock_init(&priv->tx_lock);
64362306a36Sopenharmony_ci	SET_IEEE80211_DEV(hw, &spi->dev);
64462306a36Sopenharmony_ci	priv->common.open = p54spi_op_start;
64562306a36Sopenharmony_ci	priv->common.stop = p54spi_op_stop;
64662306a36Sopenharmony_ci	priv->common.tx = p54spi_op_tx;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ret = p54spi_request_firmware(hw);
64962306a36Sopenharmony_ci	if (ret < 0)
65062306a36Sopenharmony_ci		goto err_free_common;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	ret = p54spi_request_eeprom(hw);
65362306a36Sopenharmony_ci	if (ret)
65462306a36Sopenharmony_ci		goto err_free_common;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	ret = p54_register_common(hw, &priv->spi->dev);
65762306a36Sopenharmony_ci	if (ret)
65862306a36Sopenharmony_ci		goto err_free_common;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cierr_free_common:
66362306a36Sopenharmony_ci	release_firmware(priv->firmware);
66462306a36Sopenharmony_ci	free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
66562306a36Sopenharmony_cierr_free_gpio_irq:
66662306a36Sopenharmony_ci	gpio_free(p54spi_gpio_irq);
66762306a36Sopenharmony_cierr_free_gpio_power:
66862306a36Sopenharmony_ci	gpio_free(p54spi_gpio_power);
66962306a36Sopenharmony_cierr_free:
67062306a36Sopenharmony_ci	p54_free_common(priv->hw);
67162306a36Sopenharmony_ci	return ret;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void p54spi_remove(struct spi_device *spi)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct p54s_priv *priv = spi_get_drvdata(spi);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	p54_unregister_common(priv->hw);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	gpio_free(p54spi_gpio_power);
68362306a36Sopenharmony_ci	gpio_free(p54spi_gpio_irq);
68462306a36Sopenharmony_ci	release_firmware(priv->firmware);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	mutex_destroy(&priv->mutex);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	p54_free_common(priv->hw);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic struct spi_driver p54spi_driver = {
69362306a36Sopenharmony_ci	.driver = {
69462306a36Sopenharmony_ci		.name		= "p54spi",
69562306a36Sopenharmony_ci	},
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	.probe		= p54spi_probe,
69862306a36Sopenharmony_ci	.remove		= p54spi_remove,
69962306a36Sopenharmony_ci};
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cimodule_spi_driver(p54spi_driver);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
70462306a36Sopenharmony_ciMODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
70562306a36Sopenharmony_ciMODULE_ALIAS("spi:cx3110x");
70662306a36Sopenharmony_ciMODULE_ALIAS("spi:p54spi");
70762306a36Sopenharmony_ciMODULE_ALIAS("spi:stlc45xx");
708