|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. | 
|  | * All rights reserved. | 
|  | */ | 
|  |  | 
|  | #include <linux/clk.h> | 
|  | #include <linux/spi/spi.h> | 
|  | #include <linux/crc7.h> | 
|  | #include <linux/crc-itu-t.h> | 
|  | #include <linux/gpio/consumer.h> | 
|  |  | 
|  | #include "netdev.h" | 
|  | #include "cfg80211.h" | 
|  |  | 
|  | #define SPI_MODALIAS		"wilc1000_spi" | 
|  |  | 
|  | static bool enable_crc7;	/* protect SPI commands with CRC7 */ | 
|  | module_param(enable_crc7, bool, 0644); | 
|  | MODULE_PARM_DESC(enable_crc7, | 
|  | "Enable CRC7 checksum to protect command transfers\n" | 
|  | "\t\t\tagainst corruption during the SPI transfer.\n" | 
|  | "\t\t\tCommand transfers are short and the CPU-cycle cost\n" | 
|  | "\t\t\tof enabling this is small."); | 
|  |  | 
|  | static bool enable_crc16;	/* protect SPI data with CRC16 */ | 
|  | module_param(enable_crc16, bool, 0644); | 
|  | MODULE_PARM_DESC(enable_crc16, | 
|  | "Enable CRC16 checksum to protect data transfers\n" | 
|  | "\t\t\tagainst corruption during the SPI transfer.\n" | 
|  | "\t\t\tData transfers can be large and the CPU-cycle cost\n" | 
|  | "\t\t\tof enabling this may be substantial."); | 
|  |  | 
|  | /* | 
|  | * For CMD_SINGLE_READ and CMD_INTERNAL_READ, WILC may insert one or | 
|  | * more zero bytes between the command response and the DATA Start tag | 
|  | * (0xf3).  This behavior appears to be undocumented in "ATWILC1000 | 
|  | * USER GUIDE" (https://tinyurl.com/4hhshdts) but we have observed 1-4 | 
|  | * zero bytes when the SPI bus operates at 48MHz and none when it | 
|  | * operates at 1MHz. | 
|  | */ | 
|  | #define WILC_SPI_RSP_HDR_EXTRA_DATA	8 | 
|  |  | 
|  | struct wilc_spi { | 
|  | bool isinit;		/* true if SPI protocol has been configured */ | 
|  | bool probing_crc;	/* true if we're probing chip's CRC config */ | 
|  | bool crc7_enabled;	/* true if crc7 is currently enabled */ | 
|  | bool crc16_enabled;	/* true if crc16 is currently enabled */ | 
|  | struct wilc_gpios { | 
|  | struct gpio_desc *enable;	/* ENABLE GPIO or NULL */ | 
|  | struct gpio_desc *reset;	/* RESET GPIO or NULL */ | 
|  | } gpios; | 
|  | }; | 
|  |  | 
|  | static const struct wilc_hif_func wilc_hif_spi; | 
|  |  | 
|  | static int wilc_spi_reset(struct wilc *wilc); | 
|  |  | 
|  | /******************************************** | 
|  | * | 
|  | *      Spi protocol Function | 
|  | * | 
|  | ********************************************/ | 
|  |  | 
|  | #define CMD_DMA_WRITE				0xc1 | 
|  | #define CMD_DMA_READ				0xc2 | 
|  | #define CMD_INTERNAL_WRITE			0xc3 | 
|  | #define CMD_INTERNAL_READ			0xc4 | 
|  | #define CMD_TERMINATE				0xc5 | 
|  | #define CMD_REPEAT				0xc6 | 
|  | #define CMD_DMA_EXT_WRITE			0xc7 | 
|  | #define CMD_DMA_EXT_READ			0xc8 | 
|  | #define CMD_SINGLE_WRITE			0xc9 | 
|  | #define CMD_SINGLE_READ				0xca | 
|  | #define CMD_RESET				0xcf | 
|  |  | 
|  | #define SPI_ENABLE_VMM_RETRY_LIMIT		2 | 
|  |  | 
|  | /* SPI response fields (section 11.1.2 in ATWILC1000 User Guide): */ | 
|  | #define RSP_START_FIELD				GENMASK(7, 4) | 
|  | #define RSP_TYPE_FIELD				GENMASK(3, 0) | 
|  |  | 
|  | /* SPI response values for the response fields: */ | 
|  | #define RSP_START_TAG				0xc | 
|  | #define RSP_TYPE_FIRST_PACKET			0x1 | 
|  | #define RSP_TYPE_INNER_PACKET			0x2 | 
|  | #define RSP_TYPE_LAST_PACKET			0x3 | 
|  | #define RSP_STATE_NO_ERROR			0x00 | 
|  |  | 
|  | #define PROTOCOL_REG_PKT_SZ_MASK		GENMASK(6, 4) | 
|  | #define PROTOCOL_REG_CRC16_MASK			GENMASK(3, 3) | 
|  | #define PROTOCOL_REG_CRC7_MASK			GENMASK(2, 2) | 
|  |  | 
|  | /* | 
|  | * The SPI data packet size may be any integer power of two in the | 
|  | * range from 256 to 8192 bytes. | 
|  | */ | 
|  | #define DATA_PKT_LOG_SZ_MIN			8	/* 256 B */ | 
|  | #define DATA_PKT_LOG_SZ_MAX			13	/* 8 KiB */ | 
|  |  | 
|  | /* | 
|  | * Select the data packet size (log2 of number of bytes): Use the | 
|  | * maximum data packet size.  We only retransmit complete packets, so | 
|  | * there is no benefit from using smaller data packets. | 
|  | */ | 
|  | #define DATA_PKT_LOG_SZ				DATA_PKT_LOG_SZ_MAX | 
|  | #define DATA_PKT_SZ				(1 << DATA_PKT_LOG_SZ) | 
|  |  | 
|  | #define WILC_SPI_COMMAND_STAT_SUCCESS		0 | 
|  | #define WILC_GET_RESP_HDR_START(h)		(((h) >> 4) & 0xf) | 
|  |  | 
|  | struct wilc_spi_cmd { | 
|  | u8 cmd_type; | 
|  | union { | 
|  | struct { | 
|  | u8 addr[3]; | 
|  | u8 crc[]; | 
|  | } __packed simple_cmd; | 
|  | struct { | 
|  | u8 addr[3]; | 
|  | u8 size[2]; | 
|  | u8 crc[]; | 
|  | } __packed dma_cmd; | 
|  | struct { | 
|  | u8 addr[3]; | 
|  | u8 size[3]; | 
|  | u8 crc[]; | 
|  | } __packed dma_cmd_ext; | 
|  | struct { | 
|  | u8 addr[2]; | 
|  | __be32 data; | 
|  | u8 crc[]; | 
|  | } __packed internal_w_cmd; | 
|  | struct { | 
|  | u8 addr[3]; | 
|  | __be32 data; | 
|  | u8 crc[]; | 
|  | } __packed w_cmd; | 
|  | } u; | 
|  | } __packed; | 
|  |  | 
|  | struct wilc_spi_read_rsp_data { | 
|  | u8 header; | 
|  | u8 data[4]; | 
|  | u8 crc[]; | 
|  | } __packed; | 
|  |  | 
|  | struct wilc_spi_rsp_data { | 
|  | u8 rsp_cmd_type; | 
|  | u8 status; | 
|  | u8 data[]; | 
|  | } __packed; | 
|  |  | 
|  | struct wilc_spi_special_cmd_rsp { | 
|  | u8 skip_byte; | 
|  | u8 rsp_cmd_type; | 
|  | u8 status; | 
|  | } __packed; | 
|  |  | 
|  | static int wilc_parse_gpios(struct wilc *wilc) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | struct wilc_gpios *gpios = &spi_priv->gpios; | 
|  |  | 
|  | /* get ENABLE pin and deassert it (if it is defined): */ | 
|  | gpios->enable = devm_gpiod_get_optional(&spi->dev, | 
|  | "enable", GPIOD_OUT_LOW); | 
|  | /* get RESET pin and assert it (if it is defined): */ | 
|  | if (gpios->enable) { | 
|  | /* if enable pin exists, reset must exist as well */ | 
|  | gpios->reset = devm_gpiod_get(&spi->dev, | 
|  | "reset", GPIOD_OUT_HIGH); | 
|  | if (IS_ERR(gpios->reset)) { | 
|  | dev_err(&spi->dev, "missing reset gpio.\n"); | 
|  | return PTR_ERR(gpios->reset); | 
|  | } | 
|  | } else { | 
|  | gpios->reset = devm_gpiod_get_optional(&spi->dev, | 
|  | "reset", GPIOD_OUT_HIGH); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void wilc_wlan_power(struct wilc *wilc, bool on) | 
|  | { | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | struct wilc_gpios *gpios = &spi_priv->gpios; | 
|  |  | 
|  | if (on) { | 
|  | /* assert ENABLE: */ | 
|  | gpiod_set_value(gpios->enable, 1); | 
|  | mdelay(5); | 
|  | /* assert RESET: */ | 
|  | gpiod_set_value(gpios->reset, 1); | 
|  | } else { | 
|  | /* deassert RESET: */ | 
|  | gpiod_set_value(gpios->reset, 0); | 
|  | /* deassert ENABLE: */ | 
|  | gpiod_set_value(gpios->enable, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int wilc_bus_probe(struct spi_device *spi) | 
|  | { | 
|  | int ret; | 
|  | struct wilc *wilc; | 
|  | struct wilc_spi *spi_priv; | 
|  |  | 
|  | spi_priv = kzalloc(sizeof(*spi_priv), GFP_KERNEL); | 
|  | if (!spi_priv) | 
|  | return -ENOMEM; | 
|  |  | 
|  | ret = wilc_cfg80211_init(&wilc, &spi->dev, WILC_HIF_SPI, &wilc_hif_spi); | 
|  | if (ret) | 
|  | goto free; | 
|  |  | 
|  | spi_set_drvdata(spi, wilc); | 
|  | wilc->dev = &spi->dev; | 
|  | wilc->bus_data = spi_priv; | 
|  | wilc->dev_irq_num = spi->irq; | 
|  |  | 
|  | ret = wilc_parse_gpios(wilc); | 
|  | if (ret < 0) | 
|  | goto netdev_cleanup; | 
|  |  | 
|  | wilc->rtc_clk = devm_clk_get_optional(&spi->dev, "rtc"); | 
|  | if (IS_ERR(wilc->rtc_clk)) { | 
|  | ret = PTR_ERR(wilc->rtc_clk); | 
|  | goto netdev_cleanup; | 
|  | } | 
|  | clk_prepare_enable(wilc->rtc_clk); | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | netdev_cleanup: | 
|  | wilc_netdev_cleanup(wilc); | 
|  | free: | 
|  | kfree(spi_priv); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void wilc_bus_remove(struct spi_device *spi) | 
|  | { | 
|  | struct wilc *wilc = spi_get_drvdata(spi); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  |  | 
|  | clk_disable_unprepare(wilc->rtc_clk); | 
|  | wilc_netdev_cleanup(wilc); | 
|  | kfree(spi_priv); | 
|  | } | 
|  |  | 
|  | static const struct of_device_id wilc_of_match[] = { | 
|  | { .compatible = "microchip,wilc1000", }, | 
|  | { /* sentinel */ } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, wilc_of_match); | 
|  |  | 
|  | static const struct spi_device_id wilc_spi_id[] = { | 
|  | { "wilc1000", 0 }, | 
|  | { /* sentinel */ } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(spi, wilc_spi_id); | 
|  |  | 
|  | static struct spi_driver wilc_spi_driver = { | 
|  | .driver = { | 
|  | .name = SPI_MODALIAS, | 
|  | .of_match_table = wilc_of_match, | 
|  | }, | 
|  | .id_table = wilc_spi_id, | 
|  | .probe =  wilc_bus_probe, | 
|  | .remove = wilc_bus_remove, | 
|  | }; | 
|  | module_spi_driver(wilc_spi_driver); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int ret; | 
|  | struct spi_message msg; | 
|  |  | 
|  | if (len > 0 && b) { | 
|  | struct spi_transfer tr = { | 
|  | .tx_buf = b, | 
|  | .len = len, | 
|  | .delay = { | 
|  | .value = 0, | 
|  | .unit = SPI_DELAY_UNIT_USECS | 
|  | }, | 
|  | }; | 
|  | char *r_buffer = kzalloc(len, GFP_KERNEL); | 
|  |  | 
|  | if (!r_buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | tr.rx_buf = r_buffer; | 
|  | dev_dbg(&spi->dev, "Request writing %d bytes\n", len); | 
|  |  | 
|  | memset(&msg, 0, sizeof(msg)); | 
|  | spi_message_init(&msg); | 
|  | msg.spi = spi; | 
|  | spi_message_add_tail(&tr, &msg); | 
|  |  | 
|  | ret = spi_sync(spi, &msg); | 
|  | if (ret < 0) | 
|  | dev_err(&spi->dev, "SPI transaction failed\n"); | 
|  |  | 
|  | kfree(r_buffer); | 
|  | } else { | 
|  | dev_err(&spi->dev, | 
|  | "can't write data with the following length: %d\n", | 
|  | len); | 
|  | ret = -EINVAL; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int ret; | 
|  |  | 
|  | if (rlen > 0) { | 
|  | struct spi_message msg; | 
|  | struct spi_transfer tr = { | 
|  | .rx_buf = rb, | 
|  | .len = rlen, | 
|  | .delay = { | 
|  | .value = 0, | 
|  | .unit = SPI_DELAY_UNIT_USECS | 
|  | }, | 
|  |  | 
|  | }; | 
|  | char *t_buffer = kzalloc(rlen, GFP_KERNEL); | 
|  |  | 
|  | if (!t_buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | tr.tx_buf = t_buffer; | 
|  |  | 
|  | memset(&msg, 0, sizeof(msg)); | 
|  | spi_message_init(&msg); | 
|  | msg.spi = spi; | 
|  | spi_message_add_tail(&tr, &msg); | 
|  |  | 
|  | ret = spi_sync(spi, &msg); | 
|  | if (ret < 0) | 
|  | dev_err(&spi->dev, "SPI transaction failed\n"); | 
|  | kfree(t_buffer); | 
|  | } else { | 
|  | dev_err(&spi->dev, | 
|  | "can't read data with the following length: %u\n", | 
|  | rlen); | 
|  | ret = -EINVAL; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int ret; | 
|  |  | 
|  | if (rlen > 0) { | 
|  | struct spi_message msg; | 
|  | struct spi_transfer tr = { | 
|  | .rx_buf = rb, | 
|  | .tx_buf = wb, | 
|  | .len = rlen, | 
|  | .bits_per_word = 8, | 
|  | .delay = { | 
|  | .value = 0, | 
|  | .unit = SPI_DELAY_UNIT_USECS | 
|  | }, | 
|  |  | 
|  | }; | 
|  |  | 
|  | memset(&msg, 0, sizeof(msg)); | 
|  | spi_message_init(&msg); | 
|  | msg.spi = spi; | 
|  |  | 
|  | spi_message_add_tail(&tr, &msg); | 
|  | ret = spi_sync(spi, &msg); | 
|  | if (ret < 0) | 
|  | dev_err(&spi->dev, "SPI transaction failed\n"); | 
|  | } else { | 
|  | dev_err(&spi->dev, | 
|  | "can't read data with the following length: %u\n", | 
|  | rlen); | 
|  | ret = -EINVAL; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | int ix, nbytes; | 
|  | int result = 0; | 
|  | u8 cmd, order, crc[2]; | 
|  | u16 crc_calc; | 
|  |  | 
|  | /* | 
|  | * Data | 
|  | */ | 
|  | ix = 0; | 
|  | do { | 
|  | if (sz <= DATA_PKT_SZ) { | 
|  | nbytes = sz; | 
|  | order = 0x3; | 
|  | } else { | 
|  | nbytes = DATA_PKT_SZ; | 
|  | if (ix == 0) | 
|  | order = 0x1; | 
|  | else | 
|  | order = 0x02; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write command | 
|  | */ | 
|  | cmd = 0xf0; | 
|  | cmd |= order; | 
|  |  | 
|  | if (wilc_spi_tx(wilc, &cmd, 1)) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed data block cmd write, bus error...\n"); | 
|  | result = -EINVAL; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write data | 
|  | */ | 
|  | if (wilc_spi_tx(wilc, &b[ix], nbytes)) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed data block write, bus error...\n"); | 
|  | result = -EINVAL; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write CRC | 
|  | */ | 
|  | if (spi_priv->crc16_enabled) { | 
|  | crc_calc = crc_itu_t(0xffff, &b[ix], nbytes); | 
|  | crc[0] = crc_calc >> 8; | 
|  | crc[1] = crc_calc; | 
|  | if (wilc_spi_tx(wilc, crc, 2)) { | 
|  | dev_err(&spi->dev, "Failed data block crc write, bus error...\n"); | 
|  | result = -EINVAL; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * No need to wait for response | 
|  | */ | 
|  | ix += nbytes; | 
|  | sz -= nbytes; | 
|  | } while (sz); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /******************************************** | 
|  | * | 
|  | *      Spi Internal Read/Write Function | 
|  | * | 
|  | ********************************************/ | 
|  | static u8 wilc_get_crc7(u8 *buffer, u32 len) | 
|  | { | 
|  | return crc7_be(0xfe, buffer, len); | 
|  | } | 
|  |  | 
|  | static int wilc_spi_single_read(struct wilc *wilc, u8 cmd, u32 adr, void *b, | 
|  | u8 clockless) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | u8 wb[32], rb[32]; | 
|  | int cmd_len, resp_len, i; | 
|  | u16 crc_calc, crc_recv; | 
|  | struct wilc_spi_cmd *c; | 
|  | struct wilc_spi_rsp_data *r; | 
|  | struct wilc_spi_read_rsp_data *r_data; | 
|  |  | 
|  | memset(wb, 0x0, sizeof(wb)); | 
|  | memset(rb, 0x0, sizeof(rb)); | 
|  | c = (struct wilc_spi_cmd *)wb; | 
|  | c->cmd_type = cmd; | 
|  | if (cmd == CMD_SINGLE_READ) { | 
|  | c->u.simple_cmd.addr[0] = adr >> 16; | 
|  | c->u.simple_cmd.addr[1] = adr >> 8; | 
|  | c->u.simple_cmd.addr[2] = adr; | 
|  | } else if (cmd == CMD_INTERNAL_READ) { | 
|  | c->u.simple_cmd.addr[0] = adr >> 8; | 
|  | if (clockless == 1) | 
|  | c->u.simple_cmd.addr[0] |= BIT(7); | 
|  | c->u.simple_cmd.addr[1] = adr; | 
|  | c->u.simple_cmd.addr[2] = 0x0; | 
|  | } else { | 
|  | dev_err(&spi->dev, "cmd [%x] not supported\n", cmd); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc); | 
|  | resp_len = sizeof(*r) + sizeof(*r_data) + WILC_SPI_RSP_HDR_EXTRA_DATA; | 
|  |  | 
|  | if (spi_priv->crc7_enabled) { | 
|  | c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); | 
|  | cmd_len += 1; | 
|  | resp_len += 2; | 
|  | } | 
|  |  | 
|  | if (cmd_len + resp_len > ARRAY_SIZE(wb)) { | 
|  | dev_err(&spi->dev, | 
|  | "spi buffer size too small (%d) (%d) (%zu)\n", | 
|  | cmd_len, resp_len, ARRAY_SIZE(wb)); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { | 
|  | dev_err(&spi->dev, "Failed cmd write, bus error...\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | r = (struct wilc_spi_rsp_data *)&rb[cmd_len]; | 
|  | if (r->rsp_cmd_type != cmd && !clockless) { | 
|  | if (!spi_priv->probing_crc) | 
|  | dev_err(&spi->dev, | 
|  | "Failed cmd, cmd (%02x), resp (%02x)\n", | 
|  | cmd, r->rsp_cmd_type); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS && !clockless) { | 
|  | dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", | 
|  | r->status); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < WILC_SPI_RSP_HDR_EXTRA_DATA; ++i) | 
|  | if (WILC_GET_RESP_HDR_START(r->data[i]) == 0xf) | 
|  | break; | 
|  |  | 
|  | if (i >= WILC_SPI_RSP_HDR_EXTRA_DATA) { | 
|  | dev_err(&spi->dev, "Error, data start missing\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | r_data = (struct wilc_spi_read_rsp_data *)&r->data[i]; | 
|  |  | 
|  | if (b) | 
|  | memcpy(b, r_data->data, 4); | 
|  |  | 
|  | if (!clockless && spi_priv->crc16_enabled) { | 
|  | crc_recv = (r_data->crc[0] << 8) | r_data->crc[1]; | 
|  | crc_calc = crc_itu_t(0xffff, r_data->data, 4); | 
|  | if (crc_recv != crc_calc) { | 
|  | dev_err(&spi->dev, "%s: bad CRC 0x%04x " | 
|  | "(calculated 0x%04x)\n", __func__, | 
|  | crc_recv, crc_calc); | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_write_cmd(struct wilc *wilc, u8 cmd, u32 adr, u32 data, | 
|  | u8 clockless) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | u8 wb[32], rb[32]; | 
|  | int cmd_len, resp_len; | 
|  | struct wilc_spi_cmd *c; | 
|  | struct wilc_spi_rsp_data *r; | 
|  |  | 
|  | memset(wb, 0x0, sizeof(wb)); | 
|  | memset(rb, 0x0, sizeof(rb)); | 
|  | c = (struct wilc_spi_cmd *)wb; | 
|  | c->cmd_type = cmd; | 
|  | if (cmd == CMD_INTERNAL_WRITE) { | 
|  | c->u.internal_w_cmd.addr[0] = adr >> 8; | 
|  | if (clockless == 1) | 
|  | c->u.internal_w_cmd.addr[0] |= BIT(7); | 
|  |  | 
|  | c->u.internal_w_cmd.addr[1] = adr; | 
|  | c->u.internal_w_cmd.data = cpu_to_be32(data); | 
|  | cmd_len = offsetof(struct wilc_spi_cmd, u.internal_w_cmd.crc); | 
|  | if (spi_priv->crc7_enabled) | 
|  | c->u.internal_w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); | 
|  | } else if (cmd == CMD_SINGLE_WRITE) { | 
|  | c->u.w_cmd.addr[0] = adr >> 16; | 
|  | c->u.w_cmd.addr[1] = adr >> 8; | 
|  | c->u.w_cmd.addr[2] = adr; | 
|  | c->u.w_cmd.data = cpu_to_be32(data); | 
|  | cmd_len = offsetof(struct wilc_spi_cmd, u.w_cmd.crc); | 
|  | if (spi_priv->crc7_enabled) | 
|  | c->u.w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); | 
|  | } else { | 
|  | dev_err(&spi->dev, "write cmd [%x] not supported\n", cmd); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (spi_priv->crc7_enabled) | 
|  | cmd_len += 1; | 
|  |  | 
|  | resp_len = sizeof(*r); | 
|  |  | 
|  | if (cmd_len + resp_len > ARRAY_SIZE(wb)) { | 
|  | dev_err(&spi->dev, | 
|  | "spi buffer size too small (%d) (%d) (%zu)\n", | 
|  | cmd_len, resp_len, ARRAY_SIZE(wb)); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { | 
|  | dev_err(&spi->dev, "Failed cmd write, bus error...\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | r = (struct wilc_spi_rsp_data *)&rb[cmd_len]; | 
|  | /* | 
|  | * Clockless registers operations might return unexptected responses, | 
|  | * even if successful. | 
|  | */ | 
|  | if (r->rsp_cmd_type != cmd && !clockless) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed cmd response, cmd (%02x), resp (%02x)\n", | 
|  | cmd, r->rsp_cmd_type); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS && !clockless) { | 
|  | dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", | 
|  | r->status); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_dma_rw(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | u16 crc_recv, crc_calc; | 
|  | u8 wb[32], rb[32]; | 
|  | int cmd_len, resp_len; | 
|  | int retry, ix = 0; | 
|  | u8 crc[2]; | 
|  | struct wilc_spi_cmd *c; | 
|  | struct wilc_spi_rsp_data *r; | 
|  |  | 
|  | memset(wb, 0x0, sizeof(wb)); | 
|  | memset(rb, 0x0, sizeof(rb)); | 
|  | c = (struct wilc_spi_cmd *)wb; | 
|  | c->cmd_type = cmd; | 
|  | if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_READ) { | 
|  | c->u.dma_cmd.addr[0] = adr >> 16; | 
|  | c->u.dma_cmd.addr[1] = adr >> 8; | 
|  | c->u.dma_cmd.addr[2] = adr; | 
|  | c->u.dma_cmd.size[0] = sz >> 8; | 
|  | c->u.dma_cmd.size[1] = sz; | 
|  | cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd.crc); | 
|  | if (spi_priv->crc7_enabled) | 
|  | c->u.dma_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); | 
|  | } else if (cmd == CMD_DMA_EXT_WRITE || cmd == CMD_DMA_EXT_READ) { | 
|  | c->u.dma_cmd_ext.addr[0] = adr >> 16; | 
|  | c->u.dma_cmd_ext.addr[1] = adr >> 8; | 
|  | c->u.dma_cmd_ext.addr[2] = adr; | 
|  | c->u.dma_cmd_ext.size[0] = sz >> 16; | 
|  | c->u.dma_cmd_ext.size[1] = sz >> 8; | 
|  | c->u.dma_cmd_ext.size[2] = sz; | 
|  | cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd_ext.crc); | 
|  | if (spi_priv->crc7_enabled) | 
|  | c->u.dma_cmd_ext.crc[0] = wilc_get_crc7(wb, cmd_len); | 
|  | } else { | 
|  | dev_err(&spi->dev, "dma read write cmd [%x] not supported\n", | 
|  | cmd); | 
|  | return -EINVAL; | 
|  | } | 
|  | if (spi_priv->crc7_enabled) | 
|  | cmd_len += 1; | 
|  |  | 
|  | resp_len = sizeof(*r); | 
|  |  | 
|  | if (cmd_len + resp_len > ARRAY_SIZE(wb)) { | 
|  | dev_err(&spi->dev, "spi buffer size too small (%d)(%d) (%zu)\n", | 
|  | cmd_len, resp_len, ARRAY_SIZE(wb)); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { | 
|  | dev_err(&spi->dev, "Failed cmd write, bus error...\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | r = (struct wilc_spi_rsp_data *)&rb[cmd_len]; | 
|  | if (r->rsp_cmd_type != cmd) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed cmd response, cmd (%02x), resp (%02x)\n", | 
|  | cmd, r->rsp_cmd_type); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) { | 
|  | dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", | 
|  | r->status); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_EXT_WRITE) | 
|  | return 0; | 
|  |  | 
|  | while (sz > 0) { | 
|  | int nbytes; | 
|  | u8 rsp; | 
|  |  | 
|  | nbytes = min_t(u32, sz, DATA_PKT_SZ); | 
|  |  | 
|  | /* | 
|  | * Data Response header | 
|  | */ | 
|  | retry = 100; | 
|  | do { | 
|  | if (wilc_spi_rx(wilc, &rsp, 1)) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed resp read, bus err\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  | if (WILC_GET_RESP_HDR_START(rsp) == 0xf) | 
|  | break; | 
|  | } while (retry--); | 
|  |  | 
|  | /* | 
|  | * Read bytes | 
|  | */ | 
|  | if (wilc_spi_rx(wilc, &b[ix], nbytes)) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed block read, bus err\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read CRC | 
|  | */ | 
|  | if (spi_priv->crc16_enabled) { | 
|  | if (wilc_spi_rx(wilc, crc, 2)) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed block CRC read, bus err\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  | crc_recv = (crc[0] << 8) | crc[1]; | 
|  | crc_calc = crc_itu_t(0xffff, &b[ix], nbytes); | 
|  | if (crc_recv != crc_calc) { | 
|  | dev_err(&spi->dev, "%s: bad CRC 0x%04x " | 
|  | "(calculated 0x%04x)\n", __func__, | 
|  | crc_recv, crc_calc); | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | ix += nbytes; | 
|  | sz -= nbytes; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_special_cmd(struct wilc *wilc, u8 cmd) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | u8 wb[32], rb[32]; | 
|  | int cmd_len, resp_len = 0; | 
|  | struct wilc_spi_cmd *c; | 
|  | struct wilc_spi_special_cmd_rsp *r; | 
|  |  | 
|  | if (cmd != CMD_TERMINATE && cmd != CMD_REPEAT && cmd != CMD_RESET) | 
|  | return -EINVAL; | 
|  |  | 
|  | memset(wb, 0x0, sizeof(wb)); | 
|  | memset(rb, 0x0, sizeof(rb)); | 
|  | c = (struct wilc_spi_cmd *)wb; | 
|  | c->cmd_type = cmd; | 
|  |  | 
|  | if (cmd == CMD_RESET) | 
|  | memset(c->u.simple_cmd.addr, 0xFF, 3); | 
|  |  | 
|  | cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc); | 
|  | resp_len = sizeof(*r); | 
|  |  | 
|  | if (spi_priv->crc7_enabled) { | 
|  | c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); | 
|  | cmd_len += 1; | 
|  | } | 
|  | if (cmd_len + resp_len > ARRAY_SIZE(wb)) { | 
|  | dev_err(&spi->dev, "spi buffer size too small (%d) (%d) (%zu)\n", | 
|  | cmd_len, resp_len, ARRAY_SIZE(wb)); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { | 
|  | dev_err(&spi->dev, "Failed cmd write, bus error...\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | r = (struct wilc_spi_special_cmd_rsp *)&rb[cmd_len]; | 
|  | if (r->rsp_cmd_type != cmd) { | 
|  | if (!spi_priv->probing_crc) | 
|  | dev_err(&spi->dev, | 
|  | "Failed cmd response, cmd (%02x), resp (%02x)\n", | 
|  | cmd, r->rsp_cmd_type); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) { | 
|  | dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", | 
|  | r->status); | 
|  | return -EINVAL; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int result; | 
|  | u8 cmd = CMD_SINGLE_READ; | 
|  | u8 clockless = 0; | 
|  |  | 
|  | if (addr < WILC_SPI_CLOCKLESS_ADDR_LIMIT) { | 
|  | /* Clockless register */ | 
|  | cmd = CMD_INTERNAL_READ; | 
|  | clockless = 1; | 
|  | } | 
|  |  | 
|  | result = wilc_spi_single_read(wilc, cmd, addr, data, clockless); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | le32_to_cpus(data); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int result; | 
|  |  | 
|  | if (size <= 4) | 
|  | return -EINVAL; | 
|  |  | 
|  | result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_READ, addr, buf, size); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int result; | 
|  |  | 
|  | result = wilc_spi_write_cmd(wilc, CMD_INTERNAL_WRITE, adr, dat, 0); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, "Failed internal write cmd...\n"); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | int result; | 
|  |  | 
|  | result = wilc_spi_single_read(wilc, CMD_INTERNAL_READ, adr, data, 0); | 
|  | if (result) { | 
|  | if (!spi_priv->probing_crc) | 
|  | dev_err(&spi->dev, "Failed internal read cmd...\n"); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | le32_to_cpus(data); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /******************************************** | 
|  | * | 
|  | *      Spi interfaces | 
|  | * | 
|  | ********************************************/ | 
|  |  | 
|  | static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int result; | 
|  | u8 cmd = CMD_SINGLE_WRITE; | 
|  | u8 clockless = 0; | 
|  |  | 
|  | if (addr < WILC_SPI_CLOCKLESS_ADDR_LIMIT) { | 
|  | /* Clockless register */ | 
|  | cmd = CMD_INTERNAL_WRITE; | 
|  | clockless = 1; | 
|  | } | 
|  |  | 
|  | result = wilc_spi_write_cmd(wilc, cmd, addr, data, clockless); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spi_data_rsp(struct wilc *wilc, u8 cmd) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int result, i; | 
|  | u8 rsp[4]; | 
|  |  | 
|  | /* | 
|  | * The response to data packets is two bytes long.  For | 
|  | * efficiency's sake, wilc_spi_write() wisely ignores the | 
|  | * responses for all packets but the final one.  The downside | 
|  | * of that optimization is that when the final data packet is | 
|  | * short, we may receive (part of) the response to the | 
|  | * second-to-last packet before the one for the final packet. | 
|  | * To handle this, we always read 4 bytes and then search for | 
|  | * the last byte that contains the "Response Start" code (0xc | 
|  | * in the top 4 bits).  We then know that this byte is the | 
|  | * first response byte of the final data packet. | 
|  | */ | 
|  | result = wilc_spi_rx(wilc, rsp, sizeof(rsp)); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, "Failed bus error...\n"); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | for (i = sizeof(rsp) - 2; i >= 0; --i) | 
|  | if (FIELD_GET(RSP_START_FIELD, rsp[i]) == RSP_START_TAG) | 
|  | break; | 
|  |  | 
|  | if (i < 0) { | 
|  | dev_err(&spi->dev, | 
|  | "Data packet response missing (%02x %02x %02x %02x)\n", | 
|  | rsp[0], rsp[1], rsp[2], rsp[3]); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* rsp[i] is the last response start byte */ | 
|  |  | 
|  | if (FIELD_GET(RSP_TYPE_FIELD, rsp[i]) != RSP_TYPE_LAST_PACKET | 
|  | || rsp[i + 1] != RSP_STATE_NO_ERROR) { | 
|  | dev_err(&spi->dev, "Data response error (%02x %02x)\n", | 
|  | rsp[i], rsp[i + 1]); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | int result; | 
|  |  | 
|  | /* | 
|  | * has to be greated than 4 | 
|  | */ | 
|  | if (size <= 4) | 
|  | return -EINVAL; | 
|  |  | 
|  | result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_WRITE, addr, NULL, size); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, | 
|  | "Failed cmd, write block (%08x)...\n", addr); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Data | 
|  | */ | 
|  | result = spi_data_write(wilc, buf, size); | 
|  | if (result) { | 
|  | dev_err(&spi->dev, "Failed block data write...\n"); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Data response | 
|  | */ | 
|  | return spi_data_rsp(wilc, CMD_DMA_EXT_WRITE); | 
|  | } | 
|  |  | 
|  | /******************************************** | 
|  | * | 
|  | *      Bus interfaces | 
|  | * | 
|  | ********************************************/ | 
|  |  | 
|  | static int wilc_spi_reset(struct wilc *wilc) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | int result; | 
|  |  | 
|  | result = wilc_spi_special_cmd(wilc, CMD_RESET); | 
|  | if (result && !spi_priv->probing_crc) | 
|  | dev_err(&spi->dev, "Failed cmd reset\n"); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static bool wilc_spi_is_init(struct wilc *wilc) | 
|  | { | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  |  | 
|  | return spi_priv->isinit; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_deinit(struct wilc *wilc) | 
|  | { | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  |  | 
|  | spi_priv->isinit = false; | 
|  | wilc_wlan_power(wilc, false); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_init(struct wilc *wilc, bool resume) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | struct wilc_spi *spi_priv = wilc->bus_data; | 
|  | u32 reg; | 
|  | u32 chipid; | 
|  | int ret, i; | 
|  |  | 
|  | if (spi_priv->isinit) { | 
|  | /* Confirm we can read chipid register without error: */ | 
|  | ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid); | 
|  | if (ret == 0) | 
|  | return 0; | 
|  |  | 
|  | dev_err(&spi->dev, "Fail cmd read chip id...\n"); | 
|  | } | 
|  |  | 
|  | wilc_wlan_power(wilc, true); | 
|  |  | 
|  | /* | 
|  | * configure protocol | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Infer the CRC settings that are currently in effect.  This | 
|  | * is necessary because we can't be sure that the chip has | 
|  | * been RESET (e.g, after module unload and reload). | 
|  | */ | 
|  | spi_priv->probing_crc = true; | 
|  | spi_priv->crc7_enabled = enable_crc7; | 
|  | spi_priv->crc16_enabled = false; /* don't check CRC16 during probing */ | 
|  | for (i = 0; i < 2; ++i) { | 
|  | ret = spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, ®); | 
|  | if (ret == 0) | 
|  | break; | 
|  | spi_priv->crc7_enabled = !enable_crc7; | 
|  | } | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed with CRC7 on and off.\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* set up the desired CRC configuration: */ | 
|  | reg &= ~(PROTOCOL_REG_CRC7_MASK | PROTOCOL_REG_CRC16_MASK); | 
|  | if (enable_crc7) | 
|  | reg |= PROTOCOL_REG_CRC7_MASK; | 
|  | if (enable_crc16) | 
|  | reg |= PROTOCOL_REG_CRC16_MASK; | 
|  |  | 
|  | /* set up the data packet size: */ | 
|  | BUILD_BUG_ON(DATA_PKT_LOG_SZ < DATA_PKT_LOG_SZ_MIN | 
|  | || DATA_PKT_LOG_SZ > DATA_PKT_LOG_SZ_MAX); | 
|  | reg &= ~PROTOCOL_REG_PKT_SZ_MASK; | 
|  | reg |= FIELD_PREP(PROTOCOL_REG_PKT_SZ_MASK, | 
|  | DATA_PKT_LOG_SZ - DATA_PKT_LOG_SZ_MIN); | 
|  |  | 
|  | /* establish the new setup: */ | 
|  | ret = spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, | 
|  | "[wilc spi %d]: Failed internal write reg\n", | 
|  | __LINE__); | 
|  | return ret; | 
|  | } | 
|  | /* update our state to match new protocol settings: */ | 
|  | spi_priv->crc7_enabled = enable_crc7; | 
|  | spi_priv->crc16_enabled = enable_crc16; | 
|  |  | 
|  | /* re-read to make sure new settings are in effect: */ | 
|  | spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, ®); | 
|  |  | 
|  | spi_priv->probing_crc = false; | 
|  |  | 
|  | /* | 
|  | * make sure can read chip id without protocol error | 
|  | */ | 
|  | ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Fail cmd read chip id...\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | spi_priv->isinit = true; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_read_size(struct wilc *wilc, u32 *size) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = spi_internal_read(wilc, | 
|  | WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE, size); | 
|  | *size = FIELD_GET(IRQ_DMA_WD_CNT_MASK, *size); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status) | 
|  | { | 
|  | return spi_internal_read(wilc, WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE, | 
|  | int_status); | 
|  | } | 
|  |  | 
|  | static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val) | 
|  | { | 
|  | int ret; | 
|  | int retry = SPI_ENABLE_VMM_RETRY_LIMIT; | 
|  | u32 check; | 
|  |  | 
|  | while (retry) { | 
|  | ret = spi_internal_write(wilc, | 
|  | WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE, | 
|  | val); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | ret = spi_internal_read(wilc, | 
|  | WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE, | 
|  | &check); | 
|  | if (ret || ((check & EN_VMM) == (val & EN_VMM))) | 
|  | break; | 
|  |  | 
|  | retry--; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int wilc_spi_sync_ext(struct wilc *wilc, int nint) | 
|  | { | 
|  | struct spi_device *spi = to_spi_device(wilc->dev); | 
|  | u32 reg; | 
|  | int ret, i; | 
|  |  | 
|  | if (nint > MAX_NUM_INT) { | 
|  | dev_err(&spi->dev, "Too many interrupts (%d)...\n", nint); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * interrupt pin mux select | 
|  | */ | 
|  | ret = wilc_spi_read_reg(wilc, WILC_PIN_MUX_0, ®); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed read reg (%08x)...\n", | 
|  | WILC_PIN_MUX_0); | 
|  | return ret; | 
|  | } | 
|  | reg |= BIT(8); | 
|  | ret = wilc_spi_write_reg(wilc, WILC_PIN_MUX_0, reg); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed write reg (%08x)...\n", | 
|  | WILC_PIN_MUX_0); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * interrupt enable | 
|  | */ | 
|  | ret = wilc_spi_read_reg(wilc, WILC_INTR_ENABLE, ®); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed read reg (%08x)...\n", | 
|  | WILC_INTR_ENABLE); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | for (i = 0; (i < 5) && (nint > 0); i++, nint--) | 
|  | reg |= (BIT((27 + i))); | 
|  |  | 
|  | ret = wilc_spi_write_reg(wilc, WILC_INTR_ENABLE, reg); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed write reg (%08x)...\n", | 
|  | WILC_INTR_ENABLE); | 
|  | return ret; | 
|  | } | 
|  | if (nint) { | 
|  | ret = wilc_spi_read_reg(wilc, WILC_INTR2_ENABLE, ®); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed read reg (%08x)...\n", | 
|  | WILC_INTR2_ENABLE); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | for (i = 0; (i < 3) && (nint > 0); i++, nint--) | 
|  | reg |= BIT(i); | 
|  |  | 
|  | ret = wilc_spi_write_reg(wilc, WILC_INTR2_ENABLE, reg); | 
|  | if (ret) { | 
|  | dev_err(&spi->dev, "Failed write reg (%08x)...\n", | 
|  | WILC_INTR2_ENABLE); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Global spi HIF function table */ | 
|  | static const struct wilc_hif_func wilc_hif_spi = { | 
|  | .hif_init = wilc_spi_init, | 
|  | .hif_deinit = wilc_spi_deinit, | 
|  | .hif_read_reg = wilc_spi_read_reg, | 
|  | .hif_write_reg = wilc_spi_write_reg, | 
|  | .hif_block_rx = wilc_spi_read, | 
|  | .hif_block_tx = wilc_spi_write, | 
|  | .hif_read_int = wilc_spi_read_int, | 
|  | .hif_clear_int_ext = wilc_spi_clear_int_ext, | 
|  | .hif_read_size = wilc_spi_read_size, | 
|  | .hif_block_tx_ext = wilc_spi_write, | 
|  | .hif_block_rx_ext = wilc_spi_read, | 
|  | .hif_sync_ext = wilc_spi_sync_ext, | 
|  | .hif_reset = wilc_spi_reset, | 
|  | .hif_is_init = wilc_spi_is_init, | 
|  | }; |