18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/net/wireless/libertas/if_spi.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for Marvell SPI WLAN cards. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2008 Analog Devices Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Authors: 108c2ecf20Sopenharmony_ci * Andrey Yurovsky <andrey@cozybit.com> 118c2ecf20Sopenharmony_ci * Colin McCabe <colin@cozybit.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/firmware.h> 228c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 238c2ecf20Sopenharmony_ci#include <linux/list.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/spi/libertas_spi.h> 278c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "host.h" 308c2ecf20Sopenharmony_ci#include "decl.h" 318c2ecf20Sopenharmony_ci#include "defs.h" 328c2ecf20Sopenharmony_ci#include "dev.h" 338c2ecf20Sopenharmony_ci#include "if_spi.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct if_spi_packet { 368c2ecf20Sopenharmony_ci struct list_head list; 378c2ecf20Sopenharmony_ci u16 blen; 388c2ecf20Sopenharmony_ci u8 buffer[] __aligned(4); 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct if_spi_card { 428c2ecf20Sopenharmony_ci struct spi_device *spi; 438c2ecf20Sopenharmony_ci struct lbs_private *priv; 448c2ecf20Sopenharmony_ci struct libertas_spi_platform_data *pdata; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* The card ID and card revision, as reported by the hardware. */ 478c2ecf20Sopenharmony_ci u16 card_id; 488c2ecf20Sopenharmony_ci u8 card_rev; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* The last time that we initiated an SPU operation */ 518c2ecf20Sopenharmony_ci unsigned long prev_xfer_time; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci int use_dummy_writes; 548c2ecf20Sopenharmony_ci unsigned long spu_port_delay; 558c2ecf20Sopenharmony_ci unsigned long spu_reg_delay; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Handles all SPI communication (except for FW load) */ 588c2ecf20Sopenharmony_ci struct workqueue_struct *workqueue; 598c2ecf20Sopenharmony_ci struct work_struct packet_work; 608c2ecf20Sopenharmony_ci struct work_struct resume_work; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* A buffer of incoming packets from libertas core. 658c2ecf20Sopenharmony_ci * Since we can't sleep in hw_host_to_card, we have to buffer 668c2ecf20Sopenharmony_ci * them. */ 678c2ecf20Sopenharmony_ci struct list_head cmd_packet_list; 688c2ecf20Sopenharmony_ci struct list_head data_packet_list; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Protects cmd_packet_list and data_packet_list */ 718c2ecf20Sopenharmony_ci spinlock_t buffer_lock; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* True is card suspended */ 748c2ecf20Sopenharmony_ci u8 suspended; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void free_if_spi_card(struct if_spi_card *card) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct list_head *cursor, *next; 808c2ecf20Sopenharmony_ci struct if_spi_packet *packet; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci list_for_each_safe(cursor, next, &card->cmd_packet_list) { 838c2ecf20Sopenharmony_ci packet = container_of(cursor, struct if_spi_packet, list); 848c2ecf20Sopenharmony_ci list_del(&packet->list); 858c2ecf20Sopenharmony_ci kfree(packet); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci list_for_each_safe(cursor, next, &card->data_packet_list) { 888c2ecf20Sopenharmony_ci packet = container_of(cursor, struct if_spi_packet, list); 898c2ecf20Sopenharmony_ci list_del(&packet->list); 908c2ecf20Sopenharmony_ci kfree(packet); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci kfree(card); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define MODEL_8385 0x04 968c2ecf20Sopenharmony_ci#define MODEL_8686 0x0b 978c2ecf20Sopenharmony_ci#define MODEL_8688 0x10 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = { 1008c2ecf20Sopenharmony_ci { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" }, 1018c2ecf20Sopenharmony_ci { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" }, 1028c2ecf20Sopenharmony_ci { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" }, 1038c2ecf20Sopenharmony_ci { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" }, 1048c2ecf20Sopenharmony_ci { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" }, 1058c2ecf20Sopenharmony_ci { 0, NULL, NULL } 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385_helper.bin"); 1088c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); 1098c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385.bin"); 1108c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin"); 1118c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_v9.bin"); 1128c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); 1138c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686.bin"); 1148c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8688_helper.bin"); 1158c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8688.bin"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* 1198c2ecf20Sopenharmony_ci * SPI Interface Unit Routines 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * The SPU sits between the host and the WLAN module. 1228c2ecf20Sopenharmony_ci * All communication with the firmware is through SPU transactions. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * First we have to put a SPU register name on the bus. Then we can 1258c2ecf20Sopenharmony_ci * either read from or write to that register. 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void spu_transaction_init(struct if_spi_card *card) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci if (!time_after(jiffies, card->prev_xfer_time + 1)) { 1328c2ecf20Sopenharmony_ci /* Unfortunately, the SPU requires a delay between successive 1338c2ecf20Sopenharmony_ci * transactions. If our last transaction was more than a jiffy 1348c2ecf20Sopenharmony_ci * ago, we have obviously already delayed enough. 1358c2ecf20Sopenharmony_ci * If not, we have to busy-wait to be on the safe side. */ 1368c2ecf20Sopenharmony_ci ndelay(400); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void spu_transaction_finish(struct if_spi_card *card) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci card->prev_xfer_time = jiffies; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * Write out a byte buffer to an SPI register, 1478c2ecf20Sopenharmony_ci * using a series of 16-bit transfers. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci int err = 0; 1528c2ecf20Sopenharmony_ci __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); 1538c2ecf20Sopenharmony_ci struct spi_message m; 1548c2ecf20Sopenharmony_ci struct spi_transfer reg_trans; 1558c2ecf20Sopenharmony_ci struct spi_transfer data_trans; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci spi_message_init(&m); 1588c2ecf20Sopenharmony_ci memset(®_trans, 0, sizeof(reg_trans)); 1598c2ecf20Sopenharmony_ci memset(&data_trans, 0, sizeof(data_trans)); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* You must give an even number of bytes to the SPU, even if it 1628c2ecf20Sopenharmony_ci * doesn't care about the last one. */ 1638c2ecf20Sopenharmony_ci BUG_ON(len & 0x1); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci spu_transaction_init(card); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* write SPU register index */ 1688c2ecf20Sopenharmony_ci reg_trans.tx_buf = ®_out; 1698c2ecf20Sopenharmony_ci reg_trans.len = sizeof(reg_out); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci data_trans.tx_buf = buf; 1728c2ecf20Sopenharmony_ci data_trans.len = len; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci spi_message_add_tail(®_trans, &m); 1758c2ecf20Sopenharmony_ci spi_message_add_tail(&data_trans, &m); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci err = spi_sync(card->spi, &m); 1788c2ecf20Sopenharmony_ci spu_transaction_finish(card); 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci __le16 buff; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci buff = cpu_to_le16(val); 1878c2ecf20Sopenharmony_ci return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic inline int spu_reg_is_port_reg(u16 reg) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci switch (reg) { 1938c2ecf20Sopenharmony_ci case IF_SPI_IO_RDWRPORT_REG: 1948c2ecf20Sopenharmony_ci case IF_SPI_CMD_RDWRPORT_REG: 1958c2ecf20Sopenharmony_ci case IF_SPI_DATA_RDWRPORT_REG: 1968c2ecf20Sopenharmony_ci return 1; 1978c2ecf20Sopenharmony_ci default: 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci unsigned int delay; 2058c2ecf20Sopenharmony_ci int err = 0; 2068c2ecf20Sopenharmony_ci __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); 2078c2ecf20Sopenharmony_ci struct spi_message m; 2088c2ecf20Sopenharmony_ci struct spi_transfer reg_trans; 2098c2ecf20Sopenharmony_ci struct spi_transfer dummy_trans; 2108c2ecf20Sopenharmony_ci struct spi_transfer data_trans; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * You must take an even number of bytes from the SPU, even if you 2148c2ecf20Sopenharmony_ci * don't care about the last one. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci BUG_ON(len & 0x1); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci spu_transaction_init(card); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spi_message_init(&m); 2218c2ecf20Sopenharmony_ci memset(®_trans, 0, sizeof(reg_trans)); 2228c2ecf20Sopenharmony_ci memset(&dummy_trans, 0, sizeof(dummy_trans)); 2238c2ecf20Sopenharmony_ci memset(&data_trans, 0, sizeof(data_trans)); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* write SPU register index */ 2268c2ecf20Sopenharmony_ci reg_trans.tx_buf = ®_out; 2278c2ecf20Sopenharmony_ci reg_trans.len = sizeof(reg_out); 2288c2ecf20Sopenharmony_ci spi_message_add_tail(®_trans, &m); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : 2318c2ecf20Sopenharmony_ci card->spu_reg_delay; 2328c2ecf20Sopenharmony_ci if (card->use_dummy_writes) { 2338c2ecf20Sopenharmony_ci /* Clock in dummy cycles while the SPU fills the FIFO */ 2348c2ecf20Sopenharmony_ci dummy_trans.len = delay / 8; 2358c2ecf20Sopenharmony_ci spi_message_add_tail(&dummy_trans, &m); 2368c2ecf20Sopenharmony_ci } else { 2378c2ecf20Sopenharmony_ci /* Busy-wait while the SPU fills the FIFO */ 2388c2ecf20Sopenharmony_ci reg_trans.delay.value = 2398c2ecf20Sopenharmony_ci DIV_ROUND_UP((100 + (delay * 10)), 1000); 2408c2ecf20Sopenharmony_ci reg_trans.delay.unit = SPI_DELAY_UNIT_USECS; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* read in data */ 2448c2ecf20Sopenharmony_ci data_trans.rx_buf = buf; 2458c2ecf20Sopenharmony_ci data_trans.len = len; 2468c2ecf20Sopenharmony_ci spi_message_add_tail(&data_trans, &m); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci err = spi_sync(card->spi, &m); 2498c2ecf20Sopenharmony_ci spu_transaction_finish(card); 2508c2ecf20Sopenharmony_ci return err; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* Read 16 bits from an SPI register */ 2548c2ecf20Sopenharmony_cistatic inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci __le16 buf; 2578c2ecf20Sopenharmony_ci int ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); 2608c2ecf20Sopenharmony_ci if (ret == 0) 2618c2ecf20Sopenharmony_ci *val = le16_to_cpup(&buf); 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * Read 32 bits from an SPI register. 2678c2ecf20Sopenharmony_ci * The low 16 bits are read first. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci __le32 buf; 2728c2ecf20Sopenharmony_ci int err; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); 2758c2ecf20Sopenharmony_ci if (!err) 2768c2ecf20Sopenharmony_ci *val = le32_to_cpup(&buf); 2778c2ecf20Sopenharmony_ci return err; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * Keep reading 16 bits from an SPI register until you get the correct result. 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * If mask = 0, the correct result is any non-zero number. 2848c2ecf20Sopenharmony_ci * If mask != 0, the correct result is any number where 2858c2ecf20Sopenharmony_ci * number & target_mask == target 2868c2ecf20Sopenharmony_ci * 2878c2ecf20Sopenharmony_ci * Returns -ETIMEDOUT if a second passes without the correct result. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_cistatic int spu_wait_for_u16(struct if_spi_card *card, u16 reg, 2908c2ecf20Sopenharmony_ci u16 target_mask, u16 target) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int err; 2938c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + 5*HZ; 2948c2ecf20Sopenharmony_ci while (1) { 2958c2ecf20Sopenharmony_ci u16 val; 2968c2ecf20Sopenharmony_ci err = spu_read_u16(card, reg, &val); 2978c2ecf20Sopenharmony_ci if (err) 2988c2ecf20Sopenharmony_ci return err; 2998c2ecf20Sopenharmony_ci if (target_mask) { 3008c2ecf20Sopenharmony_ci if ((val & target_mask) == target) 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci if (val) 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci udelay(100); 3078c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 3088c2ecf20Sopenharmony_ci pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n", 3098c2ecf20Sopenharmony_ci __func__, val, target_mask, target); 3108c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* 3168c2ecf20Sopenharmony_ci * Read 16 bits from an SPI register until you receive a specific value. 3178c2ecf20Sopenharmony_ci * Returns -ETIMEDOUT if a 4 tries pass without success. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_cistatic int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int err, try; 3228c2ecf20Sopenharmony_ci for (try = 0; try < 4; ++try) { 3238c2ecf20Sopenharmony_ci u32 val = 0; 3248c2ecf20Sopenharmony_ci err = spu_read_u32(card, reg, &val); 3258c2ecf20Sopenharmony_ci if (err) 3268c2ecf20Sopenharmony_ci return err; 3278c2ecf20Sopenharmony_ci if (val == target) 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci mdelay(100); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int spu_set_interrupt_mode(struct if_spi_card *card, 3358c2ecf20Sopenharmony_ci int suppress_host_int, 3368c2ecf20Sopenharmony_ci int auto_int) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci int err = 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * We can suppress a host interrupt by clearing the appropriate 3428c2ecf20Sopenharmony_ci * bit in the "host interrupt status mask" register 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci if (suppress_host_int) { 3458c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); 3468c2ecf20Sopenharmony_ci if (err) 3478c2ecf20Sopenharmony_ci return err; 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 3508c2ecf20Sopenharmony_ci IF_SPI_HISM_TX_DOWNLOAD_RDY | 3518c2ecf20Sopenharmony_ci IF_SPI_HISM_RX_UPLOAD_RDY | 3528c2ecf20Sopenharmony_ci IF_SPI_HISM_CMD_DOWNLOAD_RDY | 3538c2ecf20Sopenharmony_ci IF_SPI_HISM_CARDEVENT | 3548c2ecf20Sopenharmony_ci IF_SPI_HISM_CMD_UPLOAD_RDY); 3558c2ecf20Sopenharmony_ci if (err) 3568c2ecf20Sopenharmony_ci return err; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * If auto-interrupts are on, the completion of certain transactions 3618c2ecf20Sopenharmony_ci * will trigger an interrupt automatically. If auto-interrupts 3628c2ecf20Sopenharmony_ci * are off, we need to set the "Card Interrupt Cause" register to 3638c2ecf20Sopenharmony_ci * trigger a card interrupt. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci if (auto_int) { 3668c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, 3678c2ecf20Sopenharmony_ci IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | 3688c2ecf20Sopenharmony_ci IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | 3698c2ecf20Sopenharmony_ci IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | 3708c2ecf20Sopenharmony_ci IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); 3718c2ecf20Sopenharmony_ci if (err) 3728c2ecf20Sopenharmony_ci return err; 3738c2ecf20Sopenharmony_ci } else { 3748c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); 3758c2ecf20Sopenharmony_ci if (err) 3768c2ecf20Sopenharmony_ci return err; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci return err; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int spu_get_chip_revision(struct if_spi_card *card, 3828c2ecf20Sopenharmony_ci u16 *card_id, u8 *card_rev) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int err = 0; 3858c2ecf20Sopenharmony_ci u32 dev_ctrl; 3868c2ecf20Sopenharmony_ci err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); 3878c2ecf20Sopenharmony_ci if (err) 3888c2ecf20Sopenharmony_ci return err; 3898c2ecf20Sopenharmony_ci *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); 3908c2ecf20Sopenharmony_ci *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); 3918c2ecf20Sopenharmony_ci return err; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int spu_set_bus_mode(struct if_spi_card *card, u16 mode) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci int err = 0; 3978c2ecf20Sopenharmony_ci u16 rval; 3988c2ecf20Sopenharmony_ci /* set bus mode */ 3998c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); 4008c2ecf20Sopenharmony_ci if (err) 4018c2ecf20Sopenharmony_ci return err; 4028c2ecf20Sopenharmony_ci /* Check that we were able to read back what we just wrote. */ 4038c2ecf20Sopenharmony_ci err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); 4048c2ecf20Sopenharmony_ci if (err) 4058c2ecf20Sopenharmony_ci return err; 4068c2ecf20Sopenharmony_ci if ((rval & 0xF) != mode) { 4078c2ecf20Sopenharmony_ci pr_err("Can't read bus mode register\n"); 4088c2ecf20Sopenharmony_ci return -EIO; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int spu_init(struct if_spi_card *card, int use_dummy_writes) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci int err = 0; 4168c2ecf20Sopenharmony_ci u32 delay; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* 4198c2ecf20Sopenharmony_ci * We have to start up in timed delay mode so that we can safely 4208c2ecf20Sopenharmony_ci * read the Delay Read Register. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci card->use_dummy_writes = 0; 4238c2ecf20Sopenharmony_ci err = spu_set_bus_mode(card, 4248c2ecf20Sopenharmony_ci IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | 4258c2ecf20Sopenharmony_ci IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | 4268c2ecf20Sopenharmony_ci IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); 4278c2ecf20Sopenharmony_ci if (err) 4288c2ecf20Sopenharmony_ci return err; 4298c2ecf20Sopenharmony_ci card->spu_port_delay = 1000; 4308c2ecf20Sopenharmony_ci card->spu_reg_delay = 1000; 4318c2ecf20Sopenharmony_ci err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); 4328c2ecf20Sopenharmony_ci if (err) 4338c2ecf20Sopenharmony_ci return err; 4348c2ecf20Sopenharmony_ci card->spu_port_delay = delay & 0x0000ffff; 4358c2ecf20Sopenharmony_ci card->spu_reg_delay = (delay & 0xffff0000) >> 16; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* If dummy clock delay mode has been requested, switch to it now */ 4388c2ecf20Sopenharmony_ci if (use_dummy_writes) { 4398c2ecf20Sopenharmony_ci card->use_dummy_writes = 1; 4408c2ecf20Sopenharmony_ci err = spu_set_bus_mode(card, 4418c2ecf20Sopenharmony_ci IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | 4428c2ecf20Sopenharmony_ci IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | 4438c2ecf20Sopenharmony_ci IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); 4448c2ecf20Sopenharmony_ci if (err) 4458c2ecf20Sopenharmony_ci return err; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci lbs_deb_spi("Initialized SPU unit. " 4498c2ecf20Sopenharmony_ci "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", 4508c2ecf20Sopenharmony_ci card->spu_port_delay, card->spu_reg_delay); 4518c2ecf20Sopenharmony_ci return err; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* 4558c2ecf20Sopenharmony_ci * Firmware Loading 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int if_spi_prog_helper_firmware(struct if_spi_card *card, 4598c2ecf20Sopenharmony_ci const struct firmware *firmware) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci int err = 0; 4628c2ecf20Sopenharmony_ci int bytes_remaining; 4638c2ecf20Sopenharmony_ci const u8 *fw; 4648c2ecf20Sopenharmony_ci u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci err = spu_set_interrupt_mode(card, 1, 0); 4678c2ecf20Sopenharmony_ci if (err) 4688c2ecf20Sopenharmony_ci goto out; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci bytes_remaining = firmware->size; 4718c2ecf20Sopenharmony_ci fw = firmware->data; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Load helper firmware image */ 4748c2ecf20Sopenharmony_ci while (bytes_remaining > 0) { 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * Scratch pad 1 should contain the number of bytes we 4778c2ecf20Sopenharmony_ci * want to download to the firmware 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, 4808c2ecf20Sopenharmony_ci HELPER_FW_LOAD_CHUNK_SZ); 4818c2ecf20Sopenharmony_ci if (err) 4828c2ecf20Sopenharmony_ci goto out; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, 4858c2ecf20Sopenharmony_ci IF_SPI_HIST_CMD_DOWNLOAD_RDY, 4868c2ecf20Sopenharmony_ci IF_SPI_HIST_CMD_DOWNLOAD_RDY); 4878c2ecf20Sopenharmony_ci if (err) 4888c2ecf20Sopenharmony_ci goto out; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * Feed the data into the command read/write port reg 4928c2ecf20Sopenharmony_ci * in chunks of 64 bytes 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci memset(temp, 0, sizeof(temp)); 4958c2ecf20Sopenharmony_ci memcpy(temp, fw, 4968c2ecf20Sopenharmony_ci min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); 4978c2ecf20Sopenharmony_ci mdelay(10); 4988c2ecf20Sopenharmony_ci err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, 4998c2ecf20Sopenharmony_ci temp, HELPER_FW_LOAD_CHUNK_SZ); 5008c2ecf20Sopenharmony_ci if (err) 5018c2ecf20Sopenharmony_ci goto out; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Interrupt the boot code */ 5048c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); 5058c2ecf20Sopenharmony_ci if (err) 5068c2ecf20Sopenharmony_ci goto out; 5078c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, 5088c2ecf20Sopenharmony_ci IF_SPI_CIC_CMD_DOWNLOAD_OVER); 5098c2ecf20Sopenharmony_ci if (err) 5108c2ecf20Sopenharmony_ci goto out; 5118c2ecf20Sopenharmony_ci bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; 5128c2ecf20Sopenharmony_ci fw += HELPER_FW_LOAD_CHUNK_SZ; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 5168c2ecf20Sopenharmony_ci * Once the helper / single stage firmware download is complete, 5178c2ecf20Sopenharmony_ci * write 0 to scratch pad 1 and interrupt the 5188c2ecf20Sopenharmony_ci * bootloader. This completes the helper download. 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); 5218c2ecf20Sopenharmony_ci if (err) 5228c2ecf20Sopenharmony_ci goto out; 5238c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); 5248c2ecf20Sopenharmony_ci if (err) 5258c2ecf20Sopenharmony_ci goto out; 5268c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, 5278c2ecf20Sopenharmony_ci IF_SPI_CIC_CMD_DOWNLOAD_OVER); 5288c2ecf20Sopenharmony_ciout: 5298c2ecf20Sopenharmony_ci if (err) 5308c2ecf20Sopenharmony_ci pr_err("failed to load helper firmware (err=%d)\n", err); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/* 5368c2ecf20Sopenharmony_ci * Returns the length of the next packet the firmware expects us to send. 5378c2ecf20Sopenharmony_ci * Sets crc_err if the previous transfer had a CRC error. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_cistatic int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, 5408c2ecf20Sopenharmony_ci int *crc_err) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci u16 len; 5438c2ecf20Sopenharmony_ci int err = 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * wait until the host interrupt status register indicates 5478c2ecf20Sopenharmony_ci * that we are ready to download 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, 5508c2ecf20Sopenharmony_ci IF_SPI_HIST_CMD_DOWNLOAD_RDY, 5518c2ecf20Sopenharmony_ci IF_SPI_HIST_CMD_DOWNLOAD_RDY); 5528c2ecf20Sopenharmony_ci if (err) { 5538c2ecf20Sopenharmony_ci pr_err("timed out waiting for host_int_status\n"); 5548c2ecf20Sopenharmony_ci return err; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Ask the device how many bytes of firmware it wants. */ 5588c2ecf20Sopenharmony_ci err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); 5598c2ecf20Sopenharmony_ci if (err) 5608c2ecf20Sopenharmony_ci return err; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (len > IF_SPI_CMD_BUF_SIZE) { 5638c2ecf20Sopenharmony_ci pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", 5648c2ecf20Sopenharmony_ci len); 5658c2ecf20Sopenharmony_ci return -EIO; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci if (len & 0x1) { 5688c2ecf20Sopenharmony_ci lbs_deb_spi("%s: crc error\n", __func__); 5698c2ecf20Sopenharmony_ci len &= ~0x1; 5708c2ecf20Sopenharmony_ci *crc_err = 1; 5718c2ecf20Sopenharmony_ci } else 5728c2ecf20Sopenharmony_ci *crc_err = 0; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return len; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int if_spi_prog_main_firmware(struct if_spi_card *card, 5788c2ecf20Sopenharmony_ci const struct firmware *firmware) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 5818c2ecf20Sopenharmony_ci int len, prev_len; 5828c2ecf20Sopenharmony_ci int bytes, crc_err = 0, err = 0; 5838c2ecf20Sopenharmony_ci const u8 *fw; 5848c2ecf20Sopenharmony_ci u16 num_crc_errs; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci err = spu_set_interrupt_mode(card, 1, 0); 5878c2ecf20Sopenharmony_ci if (err) 5888c2ecf20Sopenharmony_ci goto out; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); 5918c2ecf20Sopenharmony_ci if (err) { 5928c2ecf20Sopenharmony_ci netdev_err(priv->dev, 5938c2ecf20Sopenharmony_ci "%s: timed out waiting for initial scratch reg = 0\n", 5948c2ecf20Sopenharmony_ci __func__); 5958c2ecf20Sopenharmony_ci goto out; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci num_crc_errs = 0; 5998c2ecf20Sopenharmony_ci prev_len = 0; 6008c2ecf20Sopenharmony_ci bytes = firmware->size; 6018c2ecf20Sopenharmony_ci fw = firmware->data; 6028c2ecf20Sopenharmony_ci while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { 6038c2ecf20Sopenharmony_ci if (len < 0) { 6048c2ecf20Sopenharmony_ci err = len; 6058c2ecf20Sopenharmony_ci goto out; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci if (bytes < 0) { 6088c2ecf20Sopenharmony_ci /* 6098c2ecf20Sopenharmony_ci * If there are no more bytes left, we would normally 6108c2ecf20Sopenharmony_ci * expect to have terminated with len = 0 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci netdev_err(priv->dev, 6138c2ecf20Sopenharmony_ci "Firmware load wants more bytes than we have to offer.\n"); 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci if (crc_err) { 6178c2ecf20Sopenharmony_ci /* Previous transfer failed. */ 6188c2ecf20Sopenharmony_ci if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { 6198c2ecf20Sopenharmony_ci pr_err("Too many CRC errors encountered in firmware load.\n"); 6208c2ecf20Sopenharmony_ci err = -EIO; 6218c2ecf20Sopenharmony_ci goto out; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci } else { 6248c2ecf20Sopenharmony_ci /* Previous transfer succeeded. Advance counters. */ 6258c2ecf20Sopenharmony_ci bytes -= prev_len; 6268c2ecf20Sopenharmony_ci fw += prev_len; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci if (bytes < len) { 6298c2ecf20Sopenharmony_ci memset(card->cmd_buffer, 0, len); 6308c2ecf20Sopenharmony_ci memcpy(card->cmd_buffer, fw, bytes); 6318c2ecf20Sopenharmony_ci } else 6328c2ecf20Sopenharmony_ci memcpy(card->cmd_buffer, fw, len); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); 6358c2ecf20Sopenharmony_ci if (err) 6368c2ecf20Sopenharmony_ci goto out; 6378c2ecf20Sopenharmony_ci err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, 6388c2ecf20Sopenharmony_ci card->cmd_buffer, len); 6398c2ecf20Sopenharmony_ci if (err) 6408c2ecf20Sopenharmony_ci goto out; 6418c2ecf20Sopenharmony_ci err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , 6428c2ecf20Sopenharmony_ci IF_SPI_CIC_CMD_DOWNLOAD_OVER); 6438c2ecf20Sopenharmony_ci if (err) 6448c2ecf20Sopenharmony_ci goto out; 6458c2ecf20Sopenharmony_ci prev_len = len; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci if (bytes > prev_len) { 6488c2ecf20Sopenharmony_ci pr_err("firmware load wants fewer bytes than we have to offer\n"); 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* Confirm firmware download */ 6528c2ecf20Sopenharmony_ci err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, 6538c2ecf20Sopenharmony_ci SUCCESSFUL_FW_DOWNLOAD_MAGIC); 6548c2ecf20Sopenharmony_ci if (err) { 6558c2ecf20Sopenharmony_ci pr_err("failed to confirm the firmware download\n"); 6568c2ecf20Sopenharmony_ci goto out; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ciout: 6608c2ecf20Sopenharmony_ci if (err) 6618c2ecf20Sopenharmony_ci pr_err("failed to load firmware (err=%d)\n", err); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return err; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* 6678c2ecf20Sopenharmony_ci * SPI Transfer Thread 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * The SPI worker handles all SPI transfers, so there is no need for a lock. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/* Move a command from the card to the host */ 6738c2ecf20Sopenharmony_cistatic int if_spi_c2h_cmd(struct if_spi_card *card) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 6768c2ecf20Sopenharmony_ci unsigned long flags; 6778c2ecf20Sopenharmony_ci int err = 0; 6788c2ecf20Sopenharmony_ci u16 len; 6798c2ecf20Sopenharmony_ci u8 i; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* 6828c2ecf20Sopenharmony_ci * We need a buffer big enough to handle whatever people send to 6838c2ecf20Sopenharmony_ci * hw_host_to_card 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_ci BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); 6868c2ecf20Sopenharmony_ci BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* 6898c2ecf20Sopenharmony_ci * It's just annoying if the buffer size isn't a multiple of 4, because 6908c2ecf20Sopenharmony_ci * then we might have len < IF_SPI_CMD_BUF_SIZE but 6918c2ecf20Sopenharmony_ci * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* How many bytes are there to read? */ 6968c2ecf20Sopenharmony_ci err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); 6978c2ecf20Sopenharmony_ci if (err) 6988c2ecf20Sopenharmony_ci goto out; 6998c2ecf20Sopenharmony_ci if (!len) { 7008c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: error: card has no data for host\n", 7018c2ecf20Sopenharmony_ci __func__); 7028c2ecf20Sopenharmony_ci err = -EINVAL; 7038c2ecf20Sopenharmony_ci goto out; 7048c2ecf20Sopenharmony_ci } else if (len > IF_SPI_CMD_BUF_SIZE) { 7058c2ecf20Sopenharmony_ci netdev_err(priv->dev, 7068c2ecf20Sopenharmony_ci "%s: error: response packet too large: %d bytes, but maximum is %d\n", 7078c2ecf20Sopenharmony_ci __func__, len, IF_SPI_CMD_BUF_SIZE); 7088c2ecf20Sopenharmony_ci err = -EINVAL; 7098c2ecf20Sopenharmony_ci goto out; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* Read the data from the WLAN module into our command buffer */ 7138c2ecf20Sopenharmony_ci err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, 7148c2ecf20Sopenharmony_ci card->cmd_buffer, ALIGN(len, 4)); 7158c2ecf20Sopenharmony_ci if (err) 7168c2ecf20Sopenharmony_ci goto out; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 7198c2ecf20Sopenharmony_ci i = (priv->resp_idx == 0) ? 1 : 0; 7208c2ecf20Sopenharmony_ci BUG_ON(priv->resp_len[i]); 7218c2ecf20Sopenharmony_ci priv->resp_len[i] = len; 7228c2ecf20Sopenharmony_ci memcpy(priv->resp_buf[i], card->cmd_buffer, len); 7238c2ecf20Sopenharmony_ci lbs_notify_command_response(priv, i); 7248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ciout: 7278c2ecf20Sopenharmony_ci if (err) 7288c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: err=%d\n", __func__, err); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return err; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* Move data from the card to the host */ 7348c2ecf20Sopenharmony_cistatic int if_spi_c2h_data(struct if_spi_card *card) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 7378c2ecf20Sopenharmony_ci struct sk_buff *skb; 7388c2ecf20Sopenharmony_ci char *data; 7398c2ecf20Sopenharmony_ci u16 len; 7408c2ecf20Sopenharmony_ci int err = 0; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* How many bytes are there to read? */ 7438c2ecf20Sopenharmony_ci err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); 7448c2ecf20Sopenharmony_ci if (err) 7458c2ecf20Sopenharmony_ci goto out; 7468c2ecf20Sopenharmony_ci if (!len) { 7478c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: error: card has no data for host\n", 7488c2ecf20Sopenharmony_ci __func__); 7498c2ecf20Sopenharmony_ci err = -EINVAL; 7508c2ecf20Sopenharmony_ci goto out; 7518c2ecf20Sopenharmony_ci } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { 7528c2ecf20Sopenharmony_ci netdev_err(priv->dev, 7538c2ecf20Sopenharmony_ci "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n", 7548c2ecf20Sopenharmony_ci __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); 7558c2ecf20Sopenharmony_ci err = -EINVAL; 7568c2ecf20Sopenharmony_ci goto out; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* TODO: should we allocate a smaller skb if we have less data? */ 7608c2ecf20Sopenharmony_ci skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); 7618c2ecf20Sopenharmony_ci if (!skb) { 7628c2ecf20Sopenharmony_ci err = -ENOBUFS; 7638c2ecf20Sopenharmony_ci goto out; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci skb_reserve(skb, IPFIELD_ALIGN_OFFSET); 7668c2ecf20Sopenharmony_ci data = skb_put(skb, len); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* Read the data from the WLAN module into our skb... */ 7698c2ecf20Sopenharmony_ci err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); 7708c2ecf20Sopenharmony_ci if (err) { 7718c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 7728c2ecf20Sopenharmony_ci goto out; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* pass the SKB to libertas */ 7768c2ecf20Sopenharmony_ci err = lbs_process_rxed_packet(card->priv, skb); 7778c2ecf20Sopenharmony_ci /* lbs_process_rxed_packet() consumes the skb */ 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ciout: 7808c2ecf20Sopenharmony_ci if (err) 7818c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: err=%d\n", __func__, err); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return err; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci/* Move data or a command from the host to the card. */ 7878c2ecf20Sopenharmony_cistatic void if_spi_h2c(struct if_spi_card *card, 7888c2ecf20Sopenharmony_ci struct if_spi_packet *packet, int type) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 7918c2ecf20Sopenharmony_ci int err = 0; 7928c2ecf20Sopenharmony_ci u16 port_reg; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci switch (type) { 7958c2ecf20Sopenharmony_ci case MVMS_DAT: 7968c2ecf20Sopenharmony_ci port_reg = IF_SPI_DATA_RDWRPORT_REG; 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci case MVMS_CMD: 7998c2ecf20Sopenharmony_ci port_reg = IF_SPI_CMD_RDWRPORT_REG; 8008c2ecf20Sopenharmony_ci break; 8018c2ecf20Sopenharmony_ci default: 8028c2ecf20Sopenharmony_ci netdev_err(priv->dev, "can't transfer buffer of type %d\n", 8038c2ecf20Sopenharmony_ci type); 8048c2ecf20Sopenharmony_ci err = -EINVAL; 8058c2ecf20Sopenharmony_ci goto out; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* Write the data to the card */ 8098c2ecf20Sopenharmony_ci err = spu_write(card, port_reg, packet->buffer, packet->blen); 8108c2ecf20Sopenharmony_ci if (err) 8118c2ecf20Sopenharmony_ci goto out; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ciout: 8148c2ecf20Sopenharmony_ci kfree(packet); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (err) 8178c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: error %d\n", __func__, err); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci/* Inform the host about a card event */ 8218c2ecf20Sopenharmony_cistatic void if_spi_e2h(struct if_spi_card *card) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci int err = 0; 8248c2ecf20Sopenharmony_ci u32 cause; 8258c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); 8288c2ecf20Sopenharmony_ci if (err) 8298c2ecf20Sopenharmony_ci goto out; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* re-enable the card event interrupt */ 8328c2ecf20Sopenharmony_ci spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 8338c2ecf20Sopenharmony_ci ~IF_SPI_HICU_CARD_EVENT); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* generate a card interrupt */ 8368c2ecf20Sopenharmony_ci spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci lbs_queue_event(priv, cause & 0xff); 8398c2ecf20Sopenharmony_ciout: 8408c2ecf20Sopenharmony_ci if (err) 8418c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: error %d\n", __func__, err); 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void if_spi_host_to_card_worker(struct work_struct *work) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci int err; 8478c2ecf20Sopenharmony_ci struct if_spi_card *card; 8488c2ecf20Sopenharmony_ci u16 hiStatus; 8498c2ecf20Sopenharmony_ci unsigned long flags; 8508c2ecf20Sopenharmony_ci struct if_spi_packet *packet; 8518c2ecf20Sopenharmony_ci struct lbs_private *priv; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci card = container_of(work, struct if_spi_card, packet_work); 8548c2ecf20Sopenharmony_ci priv = card->priv; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* 8578c2ecf20Sopenharmony_ci * Read the host interrupt status register to see what we 8588c2ecf20Sopenharmony_ci * can do. 8598c2ecf20Sopenharmony_ci */ 8608c2ecf20Sopenharmony_ci err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, 8618c2ecf20Sopenharmony_ci &hiStatus); 8628c2ecf20Sopenharmony_ci if (err) { 8638c2ecf20Sopenharmony_ci netdev_err(priv->dev, "I/O error\n"); 8648c2ecf20Sopenharmony_ci goto err; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { 8688c2ecf20Sopenharmony_ci err = if_spi_c2h_cmd(card); 8698c2ecf20Sopenharmony_ci if (err) 8708c2ecf20Sopenharmony_ci goto err; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { 8738c2ecf20Sopenharmony_ci err = if_spi_c2h_data(card); 8748c2ecf20Sopenharmony_ci if (err) 8758c2ecf20Sopenharmony_ci goto err; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* 8798c2ecf20Sopenharmony_ci * workaround: in PS mode, the card does not set the Command 8808c2ecf20Sopenharmony_ci * Download Ready bit, but it sets TX Download Ready. 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || 8838c2ecf20Sopenharmony_ci (card->priv->psstate != PS_STATE_FULL_POWER && 8848c2ecf20Sopenharmony_ci (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { 8858c2ecf20Sopenharmony_ci /* 8868c2ecf20Sopenharmony_ci * This means two things. First of all, 8878c2ecf20Sopenharmony_ci * if there was a previous command sent, the card has 8888c2ecf20Sopenharmony_ci * successfully received it. 8898c2ecf20Sopenharmony_ci * Secondly, it is now ready to download another 8908c2ecf20Sopenharmony_ci * command. 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_ci lbs_host_to_card_done(card->priv); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Do we have any command packets from the host to send? */ 8958c2ecf20Sopenharmony_ci packet = NULL; 8968c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->buffer_lock, flags); 8978c2ecf20Sopenharmony_ci if (!list_empty(&card->cmd_packet_list)) { 8988c2ecf20Sopenharmony_ci packet = (struct if_spi_packet *)(card-> 8998c2ecf20Sopenharmony_ci cmd_packet_list.next); 9008c2ecf20Sopenharmony_ci list_del(&packet->list); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->buffer_lock, flags); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (packet) 9058c2ecf20Sopenharmony_ci if_spi_h2c(card, packet, MVMS_CMD); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { 9088c2ecf20Sopenharmony_ci /* Do we have any data packets from the host to send? */ 9098c2ecf20Sopenharmony_ci packet = NULL; 9108c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->buffer_lock, flags); 9118c2ecf20Sopenharmony_ci if (!list_empty(&card->data_packet_list)) { 9128c2ecf20Sopenharmony_ci packet = (struct if_spi_packet *)(card-> 9138c2ecf20Sopenharmony_ci data_packet_list.next); 9148c2ecf20Sopenharmony_ci list_del(&packet->list); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->buffer_lock, flags); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (packet) 9198c2ecf20Sopenharmony_ci if_spi_h2c(card, packet, MVMS_DAT); 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci if (hiStatus & IF_SPI_HIST_CARD_EVENT) 9228c2ecf20Sopenharmony_ci if_spi_e2h(card); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cierr: 9258c2ecf20Sopenharmony_ci if (err) 9268c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: got error %d\n", __func__, err); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/* 9308c2ecf20Sopenharmony_ci * Host to Card 9318c2ecf20Sopenharmony_ci * 9328c2ecf20Sopenharmony_ci * Called from Libertas to transfer some data to the WLAN device 9338c2ecf20Sopenharmony_ci * We can't sleep here. 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_cistatic int if_spi_host_to_card(struct lbs_private *priv, 9368c2ecf20Sopenharmony_ci u8 type, u8 *buf, u16 nb) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci int err = 0; 9398c2ecf20Sopenharmony_ci unsigned long flags; 9408c2ecf20Sopenharmony_ci struct if_spi_card *card = priv->card; 9418c2ecf20Sopenharmony_ci struct if_spi_packet *packet; 9428c2ecf20Sopenharmony_ci u16 blen; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (nb == 0) { 9458c2ecf20Sopenharmony_ci netdev_err(priv->dev, "%s: invalid size requested: %d\n", 9468c2ecf20Sopenharmony_ci __func__, nb); 9478c2ecf20Sopenharmony_ci err = -EINVAL; 9488c2ecf20Sopenharmony_ci goto out; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci blen = ALIGN(nb, 4); 9518c2ecf20Sopenharmony_ci packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); 9528c2ecf20Sopenharmony_ci if (!packet) { 9538c2ecf20Sopenharmony_ci err = -ENOMEM; 9548c2ecf20Sopenharmony_ci goto out; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci packet->blen = blen; 9578c2ecf20Sopenharmony_ci memcpy(packet->buffer, buf, nb); 9588c2ecf20Sopenharmony_ci memset(packet->buffer + nb, 0, blen - nb); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci switch (type) { 9618c2ecf20Sopenharmony_ci case MVMS_CMD: 9628c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_CMD_SENT; 9638c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->buffer_lock, flags); 9648c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &card->cmd_packet_list); 9658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->buffer_lock, flags); 9668c2ecf20Sopenharmony_ci break; 9678c2ecf20Sopenharmony_ci case MVMS_DAT: 9688c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_DATA_SENT; 9698c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->buffer_lock, flags); 9708c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &card->data_packet_list); 9718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->buffer_lock, flags); 9728c2ecf20Sopenharmony_ci break; 9738c2ecf20Sopenharmony_ci default: 9748c2ecf20Sopenharmony_ci kfree(packet); 9758c2ecf20Sopenharmony_ci netdev_err(priv->dev, "can't transfer buffer of type %d\n", 9768c2ecf20Sopenharmony_ci type); 9778c2ecf20Sopenharmony_ci err = -EINVAL; 9788c2ecf20Sopenharmony_ci break; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* Queue spi xfer work */ 9828c2ecf20Sopenharmony_ci queue_work(card->workqueue, &card->packet_work); 9838c2ecf20Sopenharmony_ciout: 9848c2ecf20Sopenharmony_ci return err; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci/* 9888c2ecf20Sopenharmony_ci * Host Interrupts 9898c2ecf20Sopenharmony_ci * 9908c2ecf20Sopenharmony_ci * Service incoming interrupts from the WLAN device. We can't sleep here, so 9918c2ecf20Sopenharmony_ci * don't try to talk on the SPI bus, just queue the SPI xfer work. 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_cistatic irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct if_spi_card *card = dev_id; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci queue_work(card->workqueue, &card->packet_work); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci/* 10038c2ecf20Sopenharmony_ci * SPI callbacks 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int if_spi_init_card(struct if_spi_card *card) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 10098c2ecf20Sopenharmony_ci int err, i; 10108c2ecf20Sopenharmony_ci u32 scratch; 10118c2ecf20Sopenharmony_ci const struct firmware *helper = NULL; 10128c2ecf20Sopenharmony_ci const struct firmware *mainfw = NULL; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci err = spu_init(card, card->pdata->use_dummy_writes); 10158c2ecf20Sopenharmony_ci if (err) 10168c2ecf20Sopenharmony_ci goto out; 10178c2ecf20Sopenharmony_ci err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); 10188c2ecf20Sopenharmony_ci if (err) 10198c2ecf20Sopenharmony_ci goto out; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); 10228c2ecf20Sopenharmony_ci if (err) 10238c2ecf20Sopenharmony_ci goto out; 10248c2ecf20Sopenharmony_ci if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) 10258c2ecf20Sopenharmony_ci lbs_deb_spi("Firmware is already loaded for " 10268c2ecf20Sopenharmony_ci "Marvell WLAN 802.11 adapter\n"); 10278c2ecf20Sopenharmony_ci else { 10288c2ecf20Sopenharmony_ci /* Check if we support this card */ 10298c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_table); i++) { 10308c2ecf20Sopenharmony_ci if (card->card_id == fw_table[i].model) 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(fw_table)) { 10348c2ecf20Sopenharmony_ci netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", 10358c2ecf20Sopenharmony_ci card->card_id); 10368c2ecf20Sopenharmony_ci err = -ENODEV; 10378c2ecf20Sopenharmony_ci goto out; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci err = lbs_get_firmware(&card->spi->dev, card->card_id, 10418c2ecf20Sopenharmony_ci &fw_table[0], &helper, &mainfw); 10428c2ecf20Sopenharmony_ci if (err) { 10438c2ecf20Sopenharmony_ci netdev_err(priv->dev, "failed to find firmware (%d)\n", 10448c2ecf20Sopenharmony_ci err); 10458c2ecf20Sopenharmony_ci goto out; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " 10498c2ecf20Sopenharmony_ci "(chip_id = 0x%04x, chip_rev = 0x%02x) " 10508c2ecf20Sopenharmony_ci "attached to SPI bus_num %d, chip_select %d. " 10518c2ecf20Sopenharmony_ci "spi->max_speed_hz=%d\n", 10528c2ecf20Sopenharmony_ci card->card_id, card->card_rev, 10538c2ecf20Sopenharmony_ci card->spi->master->bus_num, 10548c2ecf20Sopenharmony_ci card->spi->chip_select, 10558c2ecf20Sopenharmony_ci card->spi->max_speed_hz); 10568c2ecf20Sopenharmony_ci err = if_spi_prog_helper_firmware(card, helper); 10578c2ecf20Sopenharmony_ci if (err) 10588c2ecf20Sopenharmony_ci goto out; 10598c2ecf20Sopenharmony_ci err = if_spi_prog_main_firmware(card, mainfw); 10608c2ecf20Sopenharmony_ci if (err) 10618c2ecf20Sopenharmony_ci goto out; 10628c2ecf20Sopenharmony_ci lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci err = spu_set_interrupt_mode(card, 0, 1); 10668c2ecf20Sopenharmony_ci if (err) 10678c2ecf20Sopenharmony_ci goto out; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ciout: 10708c2ecf20Sopenharmony_ci return err; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void if_spi_resume_worker(struct work_struct *work) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct if_spi_card *card; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci card = container_of(work, struct if_spi_card, resume_work); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (card->suspended) { 10808c2ecf20Sopenharmony_ci if (card->pdata->setup) 10818c2ecf20Sopenharmony_ci card->pdata->setup(card->spi); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Init card ... */ 10848c2ecf20Sopenharmony_ci if_spi_init_card(card); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci enable_irq(card->spi->irq); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* And resume it ... */ 10898c2ecf20Sopenharmony_ci lbs_resume(card->priv); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci card->suspended = 0; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int if_spi_probe(struct spi_device *spi) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct if_spi_card *card; 10988c2ecf20Sopenharmony_ci struct lbs_private *priv = NULL; 10998c2ecf20Sopenharmony_ci struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); 11008c2ecf20Sopenharmony_ci int err = 0; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (!pdata) { 11038c2ecf20Sopenharmony_ci err = -EINVAL; 11048c2ecf20Sopenharmony_ci goto out; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (pdata->setup) { 11088c2ecf20Sopenharmony_ci err = pdata->setup(spi); 11098c2ecf20Sopenharmony_ci if (err) 11108c2ecf20Sopenharmony_ci goto out; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* Allocate card structure to represent this specific device */ 11148c2ecf20Sopenharmony_ci card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); 11158c2ecf20Sopenharmony_ci if (!card) { 11168c2ecf20Sopenharmony_ci err = -ENOMEM; 11178c2ecf20Sopenharmony_ci goto teardown; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci spi_set_drvdata(spi, card); 11208c2ecf20Sopenharmony_ci card->pdata = pdata; 11218c2ecf20Sopenharmony_ci card->spi = spi; 11228c2ecf20Sopenharmony_ci card->prev_xfer_time = jiffies; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->cmd_packet_list); 11258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->data_packet_list); 11268c2ecf20Sopenharmony_ci spin_lock_init(&card->buffer_lock); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* Initialize the SPI Interface Unit */ 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Firmware load */ 11318c2ecf20Sopenharmony_ci err = if_spi_init_card(card); 11328c2ecf20Sopenharmony_ci if (err) 11338c2ecf20Sopenharmony_ci goto free_card; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * Register our card with libertas. 11378c2ecf20Sopenharmony_ci * This will call alloc_etherdev. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci priv = lbs_add_card(card, &spi->dev); 11408c2ecf20Sopenharmony_ci if (IS_ERR(priv)) { 11418c2ecf20Sopenharmony_ci err = PTR_ERR(priv); 11428c2ecf20Sopenharmony_ci goto free_card; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci card->priv = priv; 11458c2ecf20Sopenharmony_ci priv->setup_fw_on_resume = 1; 11468c2ecf20Sopenharmony_ci priv->card = card; 11478c2ecf20Sopenharmony_ci priv->hw_host_to_card = if_spi_host_to_card; 11488c2ecf20Sopenharmony_ci priv->enter_deep_sleep = NULL; 11498c2ecf20Sopenharmony_ci priv->exit_deep_sleep = NULL; 11508c2ecf20Sopenharmony_ci priv->reset_deep_sleep_wakeup = NULL; 11518c2ecf20Sopenharmony_ci priv->fw_ready = 1; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* Initialize interrupt handling stuff. */ 11548c2ecf20Sopenharmony_ci card->workqueue = alloc_workqueue("libertas_spi", WQ_MEM_RECLAIM, 0); 11558c2ecf20Sopenharmony_ci if (!card->workqueue) { 11568c2ecf20Sopenharmony_ci err = -ENOMEM; 11578c2ecf20Sopenharmony_ci goto remove_card; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); 11608c2ecf20Sopenharmony_ci INIT_WORK(&card->resume_work, if_spi_resume_worker); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci err = request_irq(spi->irq, if_spi_host_interrupt, 11638c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, "libertas_spi", card); 11648c2ecf20Sopenharmony_ci if (err) { 11658c2ecf20Sopenharmony_ci pr_err("can't get host irq line-- request_irq failed\n"); 11668c2ecf20Sopenharmony_ci goto terminate_workqueue; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* 11708c2ecf20Sopenharmony_ci * Start the card. 11718c2ecf20Sopenharmony_ci * This will call register_netdev, and we'll start 11728c2ecf20Sopenharmony_ci * getting interrupts... 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_ci err = lbs_start_card(priv); 11758c2ecf20Sopenharmony_ci if (err) 11768c2ecf20Sopenharmony_ci goto release_irq; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci lbs_deb_spi("Finished initializing WLAN module.\n"); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* successful exit */ 11818c2ecf20Sopenharmony_ci goto out; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cirelease_irq: 11848c2ecf20Sopenharmony_ci free_irq(spi->irq, card); 11858c2ecf20Sopenharmony_citerminate_workqueue: 11868c2ecf20Sopenharmony_ci destroy_workqueue(card->workqueue); 11878c2ecf20Sopenharmony_ciremove_card: 11888c2ecf20Sopenharmony_ci lbs_remove_card(priv); /* will call free_netdev */ 11898c2ecf20Sopenharmony_cifree_card: 11908c2ecf20Sopenharmony_ci free_if_spi_card(card); 11918c2ecf20Sopenharmony_citeardown: 11928c2ecf20Sopenharmony_ci if (pdata->teardown) 11938c2ecf20Sopenharmony_ci pdata->teardown(spi); 11948c2ecf20Sopenharmony_ciout: 11958c2ecf20Sopenharmony_ci return err; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic int libertas_spi_remove(struct spi_device *spi) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct if_spi_card *card = spi_get_drvdata(spi); 12018c2ecf20Sopenharmony_ci struct lbs_private *priv = card->priv; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci lbs_deb_spi("libertas_spi_remove\n"); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci cancel_work_sync(&card->resume_work); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci lbs_stop_card(priv); 12088c2ecf20Sopenharmony_ci lbs_remove_card(priv); /* will call free_netdev */ 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci free_irq(spi->irq, card); 12118c2ecf20Sopenharmony_ci destroy_workqueue(card->workqueue); 12128c2ecf20Sopenharmony_ci if (card->pdata->teardown) 12138c2ecf20Sopenharmony_ci card->pdata->teardown(spi); 12148c2ecf20Sopenharmony_ci free_if_spi_card(card); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int if_spi_suspend(struct device *dev) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 12228c2ecf20Sopenharmony_ci struct if_spi_card *card = spi_get_drvdata(spi); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (!card->suspended) { 12258c2ecf20Sopenharmony_ci lbs_suspend(card->priv); 12268c2ecf20Sopenharmony_ci flush_workqueue(card->workqueue); 12278c2ecf20Sopenharmony_ci disable_irq(spi->irq); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (card->pdata->teardown) 12308c2ecf20Sopenharmony_ci card->pdata->teardown(spi); 12318c2ecf20Sopenharmony_ci card->suspended = 1; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return 0; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int if_spi_resume(struct device *dev) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct spi_device *spi = to_spi_device(dev); 12408c2ecf20Sopenharmony_ci struct if_spi_card *card = spi_get_drvdata(spi); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci /* Schedule delayed work */ 12438c2ecf20Sopenharmony_ci schedule_work(&card->resume_work); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci return 0; 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_cistatic const struct dev_pm_ops if_spi_pm_ops = { 12498c2ecf20Sopenharmony_ci .suspend = if_spi_suspend, 12508c2ecf20Sopenharmony_ci .resume = if_spi_resume, 12518c2ecf20Sopenharmony_ci}; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic struct spi_driver libertas_spi_driver = { 12548c2ecf20Sopenharmony_ci .probe = if_spi_probe, 12558c2ecf20Sopenharmony_ci .remove = libertas_spi_remove, 12568c2ecf20Sopenharmony_ci .driver = { 12578c2ecf20Sopenharmony_ci .name = "libertas_spi", 12588c2ecf20Sopenharmony_ci .pm = &if_spi_pm_ops, 12598c2ecf20Sopenharmony_ci }, 12608c2ecf20Sopenharmony_ci}; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci/* 12638c2ecf20Sopenharmony_ci * Module functions 12648c2ecf20Sopenharmony_ci */ 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int __init if_spi_init_module(void) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci int ret = 0; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); 12718c2ecf20Sopenharmony_ci ret = spi_register_driver(&libertas_spi_driver); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci return ret; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic void __exit if_spi_exit_module(void) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci spi_unregister_driver(&libertas_spi_driver); 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cimodule_init(if_spi_init_module); 12828c2ecf20Sopenharmony_cimodule_exit(if_spi_exit_module); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Libertas SPI WLAN Driver"); 12858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Yurovsky <andrey@cozybit.com>, " 12868c2ecf20Sopenharmony_ci "Colin McCabe <colin@cozybit.com>"); 12878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12888c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:libertas_spi"); 1289