xref: /linux/drivers/net/wireless/ti/wlcore/spi.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27b3115f2SLuciano Coelho /*
37b3115f2SLuciano Coelho  * This file is part of wl1271
47b3115f2SLuciano Coelho  *
57b3115f2SLuciano Coelho  * Copyright (C) 2008-2009 Nokia Corporation
67b3115f2SLuciano Coelho  *
77b3115f2SLuciano Coelho  * Contact: Luciano Coelho <luciano.coelho@nokia.com>
87b3115f2SLuciano Coelho  */
97b3115f2SLuciano Coelho 
107b3115f2SLuciano Coelho #include <linux/interrupt.h>
117b3115f2SLuciano Coelho #include <linux/irq.h>
127b3115f2SLuciano Coelho #include <linux/module.h>
13e757201bSGeorge Spelvin #include <linux/slab.h>
14e757201bSGeorge Spelvin #include <linux/swab.h>
157b3115f2SLuciano Coelho #include <linux/crc7.h>
167b3115f2SLuciano Coelho #include <linux/spi/spi.h>
177b3115f2SLuciano Coelho #include <linux/platform_device.h>
1804654c38SUri Mashiach #include <linux/of_irq.h>
194c1ce07bSUri Mashiach #include <linux/regulator/consumer.h>
207b3115f2SLuciano Coelho 
21c31be25aSLuciano Coelho #include "wlcore.h"
227b3115f2SLuciano Coelho #include "wl12xx_80211.h"
237b3115f2SLuciano Coelho #include "io.h"
247b3115f2SLuciano Coelho 
257b3115f2SLuciano Coelho #define WSPI_CMD_READ                 0x40000000
267b3115f2SLuciano Coelho #define WSPI_CMD_WRITE                0x00000000
277b3115f2SLuciano Coelho #define WSPI_CMD_FIXED                0x20000000
287b3115f2SLuciano Coelho #define WSPI_CMD_BYTE_LENGTH          0x1FFE0000
297b3115f2SLuciano Coelho #define WSPI_CMD_BYTE_LENGTH_OFFSET   17
307b3115f2SLuciano Coelho #define WSPI_CMD_BYTE_ADDR            0x0001FFFF
317b3115f2SLuciano Coelho 
327b3115f2SLuciano Coelho #define WSPI_INIT_CMD_CRC_LEN       5
337b3115f2SLuciano Coelho 
347b3115f2SLuciano Coelho #define WSPI_INIT_CMD_START         0x00
357b3115f2SLuciano Coelho #define WSPI_INIT_CMD_TX            0x40
367b3115f2SLuciano Coelho /* the extra bypass bit is sampled by the TNET as '1' */
377b3115f2SLuciano Coelho #define WSPI_INIT_CMD_BYPASS_BIT    0x80
387b3115f2SLuciano Coelho #define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07
397b3115f2SLuciano Coelho #define WSPI_INIT_CMD_EN_FIXEDBUSY  0x80
407b3115f2SLuciano Coelho #define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00
417b3115f2SLuciano Coelho #define WSPI_INIT_CMD_IOD           0x40
427b3115f2SLuciano Coelho #define WSPI_INIT_CMD_IP            0x20
437b3115f2SLuciano Coelho #define WSPI_INIT_CMD_CS            0x10
447b3115f2SLuciano Coelho #define WSPI_INIT_CMD_WS            0x08
457b3115f2SLuciano Coelho #define WSPI_INIT_CMD_WSPI          0x01
467b3115f2SLuciano Coelho #define WSPI_INIT_CMD_END           0x01
477b3115f2SLuciano Coelho 
487b3115f2SLuciano Coelho #define WSPI_INIT_CMD_LEN           8
497b3115f2SLuciano Coelho 
507b3115f2SLuciano Coelho #define HW_ACCESS_WSPI_FIXED_BUSY_LEN \
517b3115f2SLuciano Coelho 		((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32))
527b3115f2SLuciano Coelho #define HW_ACCESS_WSPI_INIT_CMD_MASK  0
537b3115f2SLuciano Coelho 
547b3115f2SLuciano Coelho /* HW limitation: maximum possible chunk size is 4095 bytes */
557b3115f2SLuciano Coelho #define WSPI_MAX_CHUNK_SIZE    4092
567b3115f2SLuciano Coelho 
5761932ba5SArik Nemtsov /*
584a4274bfSArnd Bergmann  * wl18xx driver aggregation buffer size is (13 * 4K) compared to
594a4274bfSArnd Bergmann  * (4 * 4K) for wl12xx, so use the larger buffer needed for wl18xx
6061932ba5SArik Nemtsov  */
614a4274bfSArnd Bergmann #define SPI_AGGR_BUFFER_SIZE (13 * SZ_4K)
6261932ba5SArik Nemtsov 
639b2761cbSUri Mashiach /* Maximum number of SPI write chunks */
649b2761cbSUri Mashiach #define WSPI_MAX_NUM_OF_CHUNKS \
659b2761cbSUri Mashiach 	((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
669b2761cbSUri Mashiach 
67c815fdebSTony Lindgren static const struct wilink_family_data wl127x_data = {
68c815fdebSTony Lindgren 	.name = "wl127x",
69c815fdebSTony Lindgren 	.nvs_name = "ti-connectivity/wl127x-nvs.bin",
70c815fdebSTony Lindgren };
717b3115f2SLuciano Coelho 
72c815fdebSTony Lindgren static const struct wilink_family_data wl128x_data = {
73c815fdebSTony Lindgren 	.name = "wl128x",
74c815fdebSTony Lindgren 	.nvs_name = "ti-connectivity/wl128x-nvs.bin",
75c815fdebSTony Lindgren };
7601efe65aSEyal Reizer 
77a762bb8eSTony Lindgren static const struct wilink_family_data wl18xx_data = {
7801efe65aSEyal Reizer 	.name = "wl18xx",
79c815fdebSTony Lindgren 	.cfg_name = "ti-connectivity/wl18xx-conf.bin",
80d382b9c0SReizer, Eyal 	.nvs_name = "ti-connectivity/wl1271-nvs.bin",
8101efe65aSEyal Reizer };
8201efe65aSEyal Reizer 
837b3115f2SLuciano Coelho struct wl12xx_spi_glue {
847b3115f2SLuciano Coelho 	struct device *dev;
857b3115f2SLuciano Coelho 	struct platform_device *core;
864c1ce07bSUri Mashiach 	struct regulator *reg; /* Power regulator */
877b3115f2SLuciano Coelho };
887b3115f2SLuciano Coelho 
wl12xx_spi_reset(struct device * child)897b3115f2SLuciano Coelho static void wl12xx_spi_reset(struct device *child)
907b3115f2SLuciano Coelho {
917b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
927b3115f2SLuciano Coelho 	u8 *cmd;
937b3115f2SLuciano Coelho 	struct spi_transfer t;
947b3115f2SLuciano Coelho 	struct spi_message m;
957b3115f2SLuciano Coelho 
967b3115f2SLuciano Coelho 	cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
977b3115f2SLuciano Coelho 	if (!cmd) {
987b3115f2SLuciano Coelho 		dev_err(child->parent,
997b3115f2SLuciano Coelho 			"could not allocate cmd for spi reset\n");
1007b3115f2SLuciano Coelho 		return;
1017b3115f2SLuciano Coelho 	}
1027b3115f2SLuciano Coelho 
1037b3115f2SLuciano Coelho 	memset(&t, 0, sizeof(t));
1047b3115f2SLuciano Coelho 	spi_message_init(&m);
1057b3115f2SLuciano Coelho 
1067b3115f2SLuciano Coelho 	memset(cmd, 0xff, WSPI_INIT_CMD_LEN);
1077b3115f2SLuciano Coelho 
1087b3115f2SLuciano Coelho 	t.tx_buf = cmd;
1097b3115f2SLuciano Coelho 	t.len = WSPI_INIT_CMD_LEN;
1107b3115f2SLuciano Coelho 	spi_message_add_tail(&t, &m);
1117b3115f2SLuciano Coelho 
1127b3115f2SLuciano Coelho 	spi_sync(to_spi_device(glue->dev), &m);
1137b3115f2SLuciano Coelho 
1147b3115f2SLuciano Coelho 	kfree(cmd);
1157b3115f2SLuciano Coelho }
1167b3115f2SLuciano Coelho 
wl12xx_spi_init(struct device * child)1177b3115f2SLuciano Coelho static void wl12xx_spi_init(struct device *child)
1187b3115f2SLuciano Coelho {
1197b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
1207b3115f2SLuciano Coelho 	struct spi_transfer t;
1217b3115f2SLuciano Coelho 	struct spi_message m;
12201efe65aSEyal Reizer 	struct spi_device *spi = to_spi_device(glue->dev);
123e757201bSGeorge Spelvin 	u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
1247b3115f2SLuciano Coelho 
1257b3115f2SLuciano Coelho 	if (!cmd) {
1267b3115f2SLuciano Coelho 		dev_err(child->parent,
1277b3115f2SLuciano Coelho 			"could not allocate cmd for spi init\n");
1287b3115f2SLuciano Coelho 		return;
1297b3115f2SLuciano Coelho 	}
1307b3115f2SLuciano Coelho 
1317b3115f2SLuciano Coelho 	memset(&t, 0, sizeof(t));
1327b3115f2SLuciano Coelho 	spi_message_init(&m);
1337b3115f2SLuciano Coelho 
1347b3115f2SLuciano Coelho 	/*
1357b3115f2SLuciano Coelho 	 * Set WSPI_INIT_COMMAND
1367b3115f2SLuciano Coelho 	 * the data is being send from the MSB to LSB
1377b3115f2SLuciano Coelho 	 */
138e757201bSGeorge Spelvin 	cmd[0] = 0xff;
139e757201bSGeorge Spelvin 	cmd[1] = 0xff;
140e757201bSGeorge Spelvin 	cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
141e757201bSGeorge Spelvin 	cmd[3] = 0;
142e757201bSGeorge Spelvin 	cmd[4] = 0;
143e757201bSGeorge Spelvin 	cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
144e757201bSGeorge Spelvin 	cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
1457b3115f2SLuciano Coelho 
146e757201bSGeorge Spelvin 	cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
1477b3115f2SLuciano Coelho 		| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
1487b3115f2SLuciano Coelho 
149e757201bSGeorge Spelvin 	if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
150e757201bSGeorge Spelvin 		cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
151e757201bSGeorge Spelvin 	else
152e757201bSGeorge Spelvin 		cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
1537b3115f2SLuciano Coelho 
154e757201bSGeorge Spelvin 	cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
15501efe65aSEyal Reizer 
156e757201bSGeorge Spelvin 	/*
157e757201bSGeorge Spelvin 	 * The above is the logical order; it must actually be stored
158e757201bSGeorge Spelvin 	 * in the buffer byte-swapped.
159e757201bSGeorge Spelvin 	 */
160e757201bSGeorge Spelvin 	__swab32s((u32 *)cmd);
161e757201bSGeorge Spelvin 	__swab32s((u32 *)cmd+1);
1627b3115f2SLuciano Coelho 
1637b3115f2SLuciano Coelho 	t.tx_buf = cmd;
1647b3115f2SLuciano Coelho 	t.len = WSPI_INIT_CMD_LEN;
1657b3115f2SLuciano Coelho 	spi_message_add_tail(&t, &m);
1667b3115f2SLuciano Coelho 
1677b3115f2SLuciano Coelho 	spi_sync(to_spi_device(glue->dev), &m);
16801efe65aSEyal Reizer 
16901efe65aSEyal Reizer 	/* Send extra clocks with inverted CS (high). this is required
17001efe65aSEyal Reizer 	 * by the wilink family in order to successfully enter WSPI mode.
17101efe65aSEyal Reizer 	 */
17201efe65aSEyal Reizer 	spi->mode ^= SPI_CS_HIGH;
17301efe65aSEyal Reizer 	memset(&m, 0, sizeof(m));
17401efe65aSEyal Reizer 	spi_message_init(&m);
17501efe65aSEyal Reizer 
17601efe65aSEyal Reizer 	cmd[0] = 0xff;
17701efe65aSEyal Reizer 	cmd[1] = 0xff;
17801efe65aSEyal Reizer 	cmd[2] = 0xff;
17901efe65aSEyal Reizer 	cmd[3] = 0xff;
18001efe65aSEyal Reizer 	__swab32s((u32 *)cmd);
18101efe65aSEyal Reizer 
18201efe65aSEyal Reizer 	t.tx_buf = cmd;
18301efe65aSEyal Reizer 	t.len = 4;
18401efe65aSEyal Reizer 	spi_message_add_tail(&t, &m);
18501efe65aSEyal Reizer 
18601efe65aSEyal Reizer 	spi_sync(to_spi_device(glue->dev), &m);
18701efe65aSEyal Reizer 
188c199ce4fSGeert Uytterhoeven 	/* Restore chip select configuration to normal */
18901efe65aSEyal Reizer 	spi->mode ^= SPI_CS_HIGH;
1907b3115f2SLuciano Coelho 	kfree(cmd);
1917b3115f2SLuciano Coelho }
1927b3115f2SLuciano Coelho 
1937b3115f2SLuciano Coelho #define WL1271_BUSY_WORD_TIMEOUT 1000
1947b3115f2SLuciano Coelho 
wl12xx_spi_read_busy(struct device * child)1957b3115f2SLuciano Coelho static int wl12xx_spi_read_busy(struct device *child)
1967b3115f2SLuciano Coelho {
1977b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
1987b3115f2SLuciano Coelho 	struct wl1271 *wl = dev_get_drvdata(child);
1997b3115f2SLuciano Coelho 	struct spi_transfer t[1];
2007b3115f2SLuciano Coelho 	struct spi_message m;
2017b3115f2SLuciano Coelho 	u32 *busy_buf;
2027b3115f2SLuciano Coelho 	int num_busy_bytes = 0;
2037b3115f2SLuciano Coelho 
2047b3115f2SLuciano Coelho 	/*
2057b3115f2SLuciano Coelho 	 * Read further busy words from SPI until a non-busy word is
2067b3115f2SLuciano Coelho 	 * encountered, then read the data itself into the buffer.
2077b3115f2SLuciano Coelho 	 */
2087b3115f2SLuciano Coelho 
2097b3115f2SLuciano Coelho 	num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT;
2107b3115f2SLuciano Coelho 	busy_buf = wl->buffer_busyword;
2117b3115f2SLuciano Coelho 	while (num_busy_bytes) {
2127b3115f2SLuciano Coelho 		num_busy_bytes--;
2137b3115f2SLuciano Coelho 		spi_message_init(&m);
2147b3115f2SLuciano Coelho 		memset(t, 0, sizeof(t));
2157b3115f2SLuciano Coelho 		t[0].rx_buf = busy_buf;
2167b3115f2SLuciano Coelho 		t[0].len = sizeof(u32);
2177b3115f2SLuciano Coelho 		t[0].cs_change = true;
2187b3115f2SLuciano Coelho 		spi_message_add_tail(&t[0], &m);
2197b3115f2SLuciano Coelho 		spi_sync(to_spi_device(glue->dev), &m);
2207b3115f2SLuciano Coelho 
2217b3115f2SLuciano Coelho 		if (*busy_buf & 0x1)
2227b3115f2SLuciano Coelho 			return 0;
2237b3115f2SLuciano Coelho 	}
2247b3115f2SLuciano Coelho 
2257b3115f2SLuciano Coelho 	/* The SPI bus is unresponsive, the read failed. */
2267b3115f2SLuciano Coelho 	dev_err(child->parent, "SPI read busy-word timeout!\n");
2277b3115f2SLuciano Coelho 	return -ETIMEDOUT;
2287b3115f2SLuciano Coelho }
2297b3115f2SLuciano Coelho 
wl12xx_spi_raw_read(struct device * child,int addr,void * buf,size_t len,bool fixed)230f1a26e63SIdo Yariv static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
231f1a26e63SIdo Yariv 					    void *buf, size_t len, bool fixed)
2327b3115f2SLuciano Coelho {
2337b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
2347b3115f2SLuciano Coelho 	struct wl1271 *wl = dev_get_drvdata(child);
2357b3115f2SLuciano Coelho 	struct spi_transfer t[2];
2367b3115f2SLuciano Coelho 	struct spi_message m;
2377b3115f2SLuciano Coelho 	u32 *busy_buf;
2387b3115f2SLuciano Coelho 	u32 *cmd;
2397b3115f2SLuciano Coelho 	u32 chunk_len;
2407b3115f2SLuciano Coelho 
2417b3115f2SLuciano Coelho 	while (len > 0) {
242c8e49556SSilvan Jegen 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
2437b3115f2SLuciano Coelho 
2447b3115f2SLuciano Coelho 		cmd = &wl->buffer_cmd;
2457b3115f2SLuciano Coelho 		busy_buf = wl->buffer_busyword;
2467b3115f2SLuciano Coelho 
2477b3115f2SLuciano Coelho 		*cmd = 0;
2487b3115f2SLuciano Coelho 		*cmd |= WSPI_CMD_READ;
2497b3115f2SLuciano Coelho 		*cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) &
2507b3115f2SLuciano Coelho 			WSPI_CMD_BYTE_LENGTH;
2517b3115f2SLuciano Coelho 		*cmd |= addr & WSPI_CMD_BYTE_ADDR;
2527b3115f2SLuciano Coelho 
2537b3115f2SLuciano Coelho 		if (fixed)
2547b3115f2SLuciano Coelho 			*cmd |= WSPI_CMD_FIXED;
2557b3115f2SLuciano Coelho 
2567b3115f2SLuciano Coelho 		spi_message_init(&m);
2577b3115f2SLuciano Coelho 		memset(t, 0, sizeof(t));
2587b3115f2SLuciano Coelho 
2597b3115f2SLuciano Coelho 		t[0].tx_buf = cmd;
2607b3115f2SLuciano Coelho 		t[0].len = 4;
2617b3115f2SLuciano Coelho 		t[0].cs_change = true;
2627b3115f2SLuciano Coelho 		spi_message_add_tail(&t[0], &m);
2637b3115f2SLuciano Coelho 
2647b3115f2SLuciano Coelho 		/* Busy and non busy words read */
2657b3115f2SLuciano Coelho 		t[1].rx_buf = busy_buf;
2667b3115f2SLuciano Coelho 		t[1].len = WL1271_BUSY_WORD_LEN;
2677b3115f2SLuciano Coelho 		t[1].cs_change = true;
2687b3115f2SLuciano Coelho 		spi_message_add_tail(&t[1], &m);
2697b3115f2SLuciano Coelho 
2707b3115f2SLuciano Coelho 		spi_sync(to_spi_device(glue->dev), &m);
2717b3115f2SLuciano Coelho 
2727b3115f2SLuciano Coelho 		if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) &&
2737b3115f2SLuciano Coelho 		    wl12xx_spi_read_busy(child)) {
2747b3115f2SLuciano Coelho 			memset(buf, 0, chunk_len);
27502eb1d9dSIdo Yariv 			return 0;
2767b3115f2SLuciano Coelho 		}
2777b3115f2SLuciano Coelho 
2787b3115f2SLuciano Coelho 		spi_message_init(&m);
2797b3115f2SLuciano Coelho 		memset(t, 0, sizeof(t));
2807b3115f2SLuciano Coelho 
2817b3115f2SLuciano Coelho 		t[0].rx_buf = buf;
2827b3115f2SLuciano Coelho 		t[0].len = chunk_len;
2837b3115f2SLuciano Coelho 		t[0].cs_change = true;
2847b3115f2SLuciano Coelho 		spi_message_add_tail(&t[0], &m);
2857b3115f2SLuciano Coelho 
2867b3115f2SLuciano Coelho 		spi_sync(to_spi_device(glue->dev), &m);
2877b3115f2SLuciano Coelho 
2887b3115f2SLuciano Coelho 		if (!fixed)
2897b3115f2SLuciano Coelho 			addr += chunk_len;
2907b3115f2SLuciano Coelho 		buf += chunk_len;
2917b3115f2SLuciano Coelho 		len -= chunk_len;
2927b3115f2SLuciano Coelho 	}
29302eb1d9dSIdo Yariv 
29402eb1d9dSIdo Yariv 	return 0;
2957b3115f2SLuciano Coelho }
2967b3115f2SLuciano Coelho 
__wl12xx_spi_raw_write(struct device * child,int addr,void * buf,size_t len,bool fixed)29701efe65aSEyal Reizer static int __wl12xx_spi_raw_write(struct device *child, int addr,
298f1a26e63SIdo Yariv 				  void *buf, size_t len, bool fixed)
2997b3115f2SLuciano Coelho {
3007b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
30101efe65aSEyal Reizer 	struct spi_transfer *t;
3027b3115f2SLuciano Coelho 	struct spi_message m;
3039b2761cbSUri Mashiach 	u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
3047b3115f2SLuciano Coelho 	u32 *cmd;
3057b3115f2SLuciano Coelho 	u32 chunk_len;
3067b3115f2SLuciano Coelho 	int i;
3077b3115f2SLuciano Coelho 
30801efe65aSEyal Reizer 	/* SPI write buffers - 2 for each chunk */
30901efe65aSEyal Reizer 	t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
31001efe65aSEyal Reizer 	if (!t)
31101efe65aSEyal Reizer 		return -ENOMEM;
31201efe65aSEyal Reizer 
31361932ba5SArik Nemtsov 	WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
3147b3115f2SLuciano Coelho 
3157b3115f2SLuciano Coelho 	spi_message_init(&m);
3167b3115f2SLuciano Coelho 
3177b3115f2SLuciano Coelho 	cmd = &commands[0];
3187b3115f2SLuciano Coelho 	i = 0;
3197b3115f2SLuciano Coelho 	while (len > 0) {
320c8e49556SSilvan Jegen 		chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
3217b3115f2SLuciano Coelho 
3227b3115f2SLuciano Coelho 		*cmd = 0;
3237b3115f2SLuciano Coelho 		*cmd |= WSPI_CMD_WRITE;
3247b3115f2SLuciano Coelho 		*cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) &
3257b3115f2SLuciano Coelho 			WSPI_CMD_BYTE_LENGTH;
3267b3115f2SLuciano Coelho 		*cmd |= addr & WSPI_CMD_BYTE_ADDR;
3277b3115f2SLuciano Coelho 
3287b3115f2SLuciano Coelho 		if (fixed)
3297b3115f2SLuciano Coelho 			*cmd |= WSPI_CMD_FIXED;
3307b3115f2SLuciano Coelho 
3317b3115f2SLuciano Coelho 		t[i].tx_buf = cmd;
3327b3115f2SLuciano Coelho 		t[i].len = sizeof(*cmd);
3337b3115f2SLuciano Coelho 		spi_message_add_tail(&t[i++], &m);
3347b3115f2SLuciano Coelho 
3357b3115f2SLuciano Coelho 		t[i].tx_buf = buf;
3367b3115f2SLuciano Coelho 		t[i].len = chunk_len;
3377b3115f2SLuciano Coelho 		spi_message_add_tail(&t[i++], &m);
3387b3115f2SLuciano Coelho 
3397b3115f2SLuciano Coelho 		if (!fixed)
3407b3115f2SLuciano Coelho 			addr += chunk_len;
3417b3115f2SLuciano Coelho 		buf += chunk_len;
3427b3115f2SLuciano Coelho 		len -= chunk_len;
3437b3115f2SLuciano Coelho 		cmd++;
3447b3115f2SLuciano Coelho 	}
3457b3115f2SLuciano Coelho 
3467b3115f2SLuciano Coelho 	spi_sync(to_spi_device(glue->dev), &m);
34702eb1d9dSIdo Yariv 
34801efe65aSEyal Reizer 	kfree(t);
34902eb1d9dSIdo Yariv 	return 0;
3507b3115f2SLuciano Coelho }
3517b3115f2SLuciano Coelho 
wl12xx_spi_raw_write(struct device * child,int addr,void * buf,size_t len,bool fixed)35201efe65aSEyal Reizer static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
35301efe65aSEyal Reizer 					     void *buf, size_t len, bool fixed)
35401efe65aSEyal Reizer {
35501efe65aSEyal Reizer 	/* The ELP wakeup write may fail the first time due to internal
35601efe65aSEyal Reizer 	 * hardware latency. It is safer to send the wakeup command twice to
35701efe65aSEyal Reizer 	 * avoid unexpected failures.
35801efe65aSEyal Reizer 	 */
35901efe65aSEyal Reizer 	if (addr == HW_ACCESS_ELP_CTRL_REG)
360c48c281eSGustavo A. R. Silva 		__wl12xx_spi_raw_write(child, addr, buf, len, fixed);
36101efe65aSEyal Reizer 
362c48c281eSGustavo A. R. Silva 	return __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
36301efe65aSEyal Reizer }
36401efe65aSEyal Reizer 
3654c1ce07bSUri Mashiach /**
3664c1ce07bSUri Mashiach  * wl12xx_spi_set_power - power on/off the wl12xx unit
3674c1ce07bSUri Mashiach  * @child: wl12xx device handle.
3684c1ce07bSUri Mashiach  * @enable: true/false to power on/off the unit.
3694c1ce07bSUri Mashiach  *
3704c1ce07bSUri Mashiach  * use the WiFi enable regulator to enable/disable the WiFi unit.
3714c1ce07bSUri Mashiach  */
wl12xx_spi_set_power(struct device * child,bool enable)3724c1ce07bSUri Mashiach static int wl12xx_spi_set_power(struct device *child, bool enable)
3734c1ce07bSUri Mashiach {
3744c1ce07bSUri Mashiach 	int ret = 0;
3754c1ce07bSUri Mashiach 	struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
3764c1ce07bSUri Mashiach 
3774c1ce07bSUri Mashiach 	WARN_ON(!glue->reg);
3784c1ce07bSUri Mashiach 
3794c1ce07bSUri Mashiach 	/* Update regulator state */
3804c1ce07bSUri Mashiach 	if (enable) {
3814c1ce07bSUri Mashiach 		ret = regulator_enable(glue->reg);
3824c1ce07bSUri Mashiach 		if (ret)
3834c1ce07bSUri Mashiach 			dev_err(child, "Power enable failure\n");
3844c1ce07bSUri Mashiach 	} else {
3854c1ce07bSUri Mashiach 		ret =  regulator_disable(glue->reg);
3864c1ce07bSUri Mashiach 		if (ret)
3874c1ce07bSUri Mashiach 			dev_err(child, "Power disable failure\n");
3884c1ce07bSUri Mashiach 	}
3894c1ce07bSUri Mashiach 
3904c1ce07bSUri Mashiach 	return ret;
3914c1ce07bSUri Mashiach }
3924c1ce07bSUri Mashiach 
393409d1c82SLee Jones /*
39401efe65aSEyal Reizer  * wl12xx_spi_set_block_size
39501efe65aSEyal Reizer  *
39601efe65aSEyal Reizer  * This function is not needed for spi mode, but need to be present.
39701efe65aSEyal Reizer  * Without it defined the wlcore fallback to use the wrong packet
39801efe65aSEyal Reizer  * allignment on tx.
39901efe65aSEyal Reizer  */
wl12xx_spi_set_block_size(struct device * child,unsigned int blksz)40001efe65aSEyal Reizer static void wl12xx_spi_set_block_size(struct device *child,
40101efe65aSEyal Reizer 				      unsigned int blksz)
40201efe65aSEyal Reizer {
40301efe65aSEyal Reizer }
40401efe65aSEyal Reizer 
4057b3115f2SLuciano Coelho static struct wl1271_if_operations spi_ops = {
4067b3115f2SLuciano Coelho 	.read		= wl12xx_spi_raw_read,
4077b3115f2SLuciano Coelho 	.write		= wl12xx_spi_raw_write,
4087b3115f2SLuciano Coelho 	.reset		= wl12xx_spi_reset,
4097b3115f2SLuciano Coelho 	.init		= wl12xx_spi_init,
4104c1ce07bSUri Mashiach 	.power		= wl12xx_spi_set_power,
41101efe65aSEyal Reizer 	.set_block_size = wl12xx_spi_set_block_size,
4127b3115f2SLuciano Coelho };
4137b3115f2SLuciano Coelho 
41404654c38SUri Mashiach static const struct of_device_id wlcore_spi_of_match_table[] = {
415c815fdebSTony Lindgren 	{ .compatible = "ti,wl1271", .data = &wl127x_data},
416c815fdebSTony Lindgren 	{ .compatible = "ti,wl1273", .data = &wl127x_data},
417c815fdebSTony Lindgren 	{ .compatible = "ti,wl1281", .data = &wl128x_data},
418c815fdebSTony Lindgren 	{ .compatible = "ti,wl1283", .data = &wl128x_data},
419078b30daSSebastian Reichel 	{ .compatible = "ti,wl1285", .data = &wl128x_data},
42001efe65aSEyal Reizer 	{ .compatible = "ti,wl1801", .data = &wl18xx_data},
42101efe65aSEyal Reizer 	{ .compatible = "ti,wl1805", .data = &wl18xx_data},
42201efe65aSEyal Reizer 	{ .compatible = "ti,wl1807", .data = &wl18xx_data},
42301efe65aSEyal Reizer 	{ .compatible = "ti,wl1831", .data = &wl18xx_data},
42401efe65aSEyal Reizer 	{ .compatible = "ti,wl1835", .data = &wl18xx_data},
42501efe65aSEyal Reizer 	{ .compatible = "ti,wl1837", .data = &wl18xx_data},
42604654c38SUri Mashiach 	{ }
42704654c38SUri Mashiach };
42804654c38SUri Mashiach MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
42904654c38SUri Mashiach 
43004654c38SUri Mashiach /**
43104654c38SUri Mashiach  * wlcore_probe_of - DT node parsing.
43204654c38SUri Mashiach  * @spi: SPI slave device parameters.
43304654c38SUri Mashiach  * @glue: wl12xx SPI bus to slave device glue parameters.
43404654c38SUri Mashiach  * @pdev_data: wlcore device parameters
43504654c38SUri Mashiach  */
wlcore_probe_of(struct spi_device * spi,struct wl12xx_spi_glue * glue,struct wlcore_platdev_data * pdev_data)43604654c38SUri Mashiach static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
43704654c38SUri Mashiach 			   struct wlcore_platdev_data *pdev_data)
43804654c38SUri Mashiach {
43904654c38SUri Mashiach 	struct device_node *dt_node = spi->dev.of_node;
44001efe65aSEyal Reizer 	const struct of_device_id *of_id;
44101efe65aSEyal Reizer 
44201efe65aSEyal Reizer 	of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
44301efe65aSEyal Reizer 	if (!of_id)
44401efe65aSEyal Reizer 		return -ENODEV;
44501efe65aSEyal Reizer 
446c815fdebSTony Lindgren 	pdev_data->family = of_id->data;
447a762bb8eSTony Lindgren 	dev_info(&spi->dev, "selected chip family is %s\n",
448c815fdebSTony Lindgren 		 pdev_data->family->name);
44904654c38SUri Mashiach 
4501a87e641SRob Herring 	pdev_data->ref_clock_xtal = of_property_read_bool(dt_node, "clock-xtal");
45104654c38SUri Mashiach 
45201efe65aSEyal Reizer 	/* optional clock frequency params */
45301efe65aSEyal Reizer 	of_property_read_u32(dt_node, "ref-clock-frequency",
45404654c38SUri Mashiach 			     &pdev_data->ref_clock_freq);
45501efe65aSEyal Reizer 	of_property_read_u32(dt_node, "tcxo-clock-frequency",
45601efe65aSEyal Reizer 			     &pdev_data->tcxo_clock_freq);
45704654c38SUri Mashiach 
45804654c38SUri Mashiach 	return 0;
45904654c38SUri Mashiach }
46004654c38SUri Mashiach 
wl1271_probe(struct spi_device * spi)461b74324d1SBill Pemberton static int wl1271_probe(struct spi_device *spi)
4627b3115f2SLuciano Coelho {
4637b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue;
464c815fdebSTony Lindgren 	struct wlcore_platdev_data *pdev_data;
4657b3115f2SLuciano Coelho 	struct resource res[1];
466372e3a84SHimangi Saraogi 	int ret;
4677b3115f2SLuciano Coelho 
468c815fdebSTony Lindgren 	pdev_data = devm_kzalloc(&spi->dev, sizeof(*pdev_data), GFP_KERNEL);
469c815fdebSTony Lindgren 	if (!pdev_data)
470c815fdebSTony Lindgren 		return -ENOMEM;
471afb43e6dSLuciano Coelho 
472c815fdebSTony Lindgren 	pdev_data->if_ops = &spi_ops;
4737b3115f2SLuciano Coelho 
474372e3a84SHimangi Saraogi 	glue = devm_kzalloc(&spi->dev, sizeof(*glue), GFP_KERNEL);
4757b3115f2SLuciano Coelho 	if (!glue) {
4767b3115f2SLuciano Coelho 		dev_err(&spi->dev, "can't allocate glue\n");
477372e3a84SHimangi Saraogi 		return -ENOMEM;
4787b3115f2SLuciano Coelho 	}
4797b3115f2SLuciano Coelho 
4807b3115f2SLuciano Coelho 	glue->dev = &spi->dev;
4817b3115f2SLuciano Coelho 
4827b3115f2SLuciano Coelho 	spi_set_drvdata(spi, glue);
4837b3115f2SLuciano Coelho 
4847b3115f2SLuciano Coelho 	/* This is the only SPI value that we need to set here, the rest
4857b3115f2SLuciano Coelho 	 * comes from the board-peripherals file */
4867b3115f2SLuciano Coelho 	spi->bits_per_word = 32;
4877b3115f2SLuciano Coelho 
4884c1ce07bSUri Mashiach 	glue->reg = devm_regulator_get(&spi->dev, "vwlan");
4891aa3367cSGeert Uytterhoeven 	if (IS_ERR(glue->reg))
4901aa3367cSGeert Uytterhoeven 		return dev_err_probe(glue->dev, PTR_ERR(glue->reg),
4911aa3367cSGeert Uytterhoeven 				     "can't get regulator\n");
4924c1ce07bSUri Mashiach 
493c815fdebSTony Lindgren 	ret = wlcore_probe_of(spi, glue, pdev_data);
494287980e4SArnd Bergmann 	if (ret) {
49504654c38SUri Mashiach 		dev_err(glue->dev,
49604654c38SUri Mashiach 			"can't get device tree parameters (%d)\n", ret);
49704654c38SUri Mashiach 		return ret;
49804654c38SUri Mashiach 	}
49904654c38SUri Mashiach 
5007b3115f2SLuciano Coelho 	ret = spi_setup(spi);
5017b3115f2SLuciano Coelho 	if (ret < 0) {
5027b3115f2SLuciano Coelho 		dev_err(glue->dev, "spi_setup failed\n");
503372e3a84SHimangi Saraogi 		return ret;
5047b3115f2SLuciano Coelho 	}
5057b3115f2SLuciano Coelho 
506c815fdebSTony Lindgren 	glue->core = platform_device_alloc(pdev_data->family->name,
50701efe65aSEyal Reizer 					   PLATFORM_DEVID_AUTO);
5087b3115f2SLuciano Coelho 	if (!glue->core) {
5097b3115f2SLuciano Coelho 		dev_err(glue->dev, "can't allocate platform_device\n");
510372e3a84SHimangi Saraogi 		return -ENOMEM;
5117b3115f2SLuciano Coelho 	}
5127b3115f2SLuciano Coelho 
5137b3115f2SLuciano Coelho 	glue->core->dev.parent = &spi->dev;
5147b3115f2SLuciano Coelho 
5157b3115f2SLuciano Coelho 	memset(res, 0x00, sizeof(res));
5167b3115f2SLuciano Coelho 
5177b3115f2SLuciano Coelho 	res[0].start = spi->irq;
51804654c38SUri Mashiach 	res[0].flags = IORESOURCE_IRQ | irq_get_trigger_type(spi->irq);
5197b3115f2SLuciano Coelho 	res[0].name = "irq";
5207b3115f2SLuciano Coelho 
5217b3115f2SLuciano Coelho 	ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
5227b3115f2SLuciano Coelho 	if (ret) {
5237b3115f2SLuciano Coelho 		dev_err(glue->dev, "can't add resources\n");
5247b3115f2SLuciano Coelho 		goto out_dev_put;
5257b3115f2SLuciano Coelho 	}
5267b3115f2SLuciano Coelho 
527c815fdebSTony Lindgren 	ret = platform_device_add_data(glue->core, pdev_data,
528c815fdebSTony Lindgren 				       sizeof(*pdev_data));
5297b3115f2SLuciano Coelho 	if (ret) {
5307b3115f2SLuciano Coelho 		dev_err(glue->dev, "can't add platform data\n");
5317b3115f2SLuciano Coelho 		goto out_dev_put;
5327b3115f2SLuciano Coelho 	}
5337b3115f2SLuciano Coelho 
5347b3115f2SLuciano Coelho 	ret = platform_device_add(glue->core);
5357b3115f2SLuciano Coelho 	if (ret) {
5367b3115f2SLuciano Coelho 		dev_err(glue->dev, "can't register platform device\n");
5377b3115f2SLuciano Coelho 		goto out_dev_put;
5387b3115f2SLuciano Coelho 	}
5397b3115f2SLuciano Coelho 
5407b3115f2SLuciano Coelho 	return 0;
5417b3115f2SLuciano Coelho 
5427b3115f2SLuciano Coelho out_dev_put:
5437b3115f2SLuciano Coelho 	platform_device_put(glue->core);
5447b3115f2SLuciano Coelho 	return ret;
5457b3115f2SLuciano Coelho }
5467b3115f2SLuciano Coelho 
wl1271_remove(struct spi_device * spi)547a0386bbaSUwe Kleine-König static void wl1271_remove(struct spi_device *spi)
5487b3115f2SLuciano Coelho {
5497b3115f2SLuciano Coelho 	struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
5507b3115f2SLuciano Coelho 
551ca6dc103SWei Yongjun 	platform_device_unregister(glue->core);
5527b3115f2SLuciano Coelho }
5537b3115f2SLuciano Coelho 
5547b3115f2SLuciano Coelho static struct spi_driver wl1271_spi_driver = {
5557b3115f2SLuciano Coelho 	.driver = {
5567b3115f2SLuciano Coelho 		.name		= "wl1271_spi",
557cf2abd87SRuan Jinjie 		.of_match_table = wlcore_spi_of_match_table,
5587b3115f2SLuciano Coelho 	},
5597b3115f2SLuciano Coelho 
5607b3115f2SLuciano Coelho 	.probe		= wl1271_probe,
561b74324d1SBill Pemberton 	.remove		= wl1271_remove,
5627b3115f2SLuciano Coelho };
5637b3115f2SLuciano Coelho 
564d5a49178SPeter Huewe module_spi_driver(wl1271_spi_driver);
565*5b778e1cSBreno Leitao MODULE_DESCRIPTION("TI WLAN SPI helpers");
5667b3115f2SLuciano Coelho MODULE_LICENSE("GPL");
5677b3115f2SLuciano Coelho MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
5687b3115f2SLuciano Coelho MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
5697b3115f2SLuciano Coelho MODULE_ALIAS("spi:wl1271");
570