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