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