1*90921014SLuciano Coelho /* 2*90921014SLuciano Coelho * This file is part of wl1251 3*90921014SLuciano Coelho * 4*90921014SLuciano Coelho * Copyright (C) 2008 Nokia Corporation 5*90921014SLuciano Coelho * 6*90921014SLuciano Coelho * This program is free software; you can redistribute it and/or 7*90921014SLuciano Coelho * modify it under the terms of the GNU General Public License 8*90921014SLuciano Coelho * version 2 as published by the Free Software Foundation. 9*90921014SLuciano Coelho * 10*90921014SLuciano Coelho * This program is distributed in the hope that it will be useful, but 11*90921014SLuciano Coelho * WITHOUT ANY WARRANTY; without even the implied warranty of 12*90921014SLuciano Coelho * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13*90921014SLuciano Coelho * General Public License for more details. 14*90921014SLuciano Coelho * 15*90921014SLuciano Coelho * You should have received a copy of the GNU General Public License 16*90921014SLuciano Coelho * along with this program; if not, write to the Free Software 17*90921014SLuciano Coelho * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 18*90921014SLuciano Coelho * 02110-1301 USA 19*90921014SLuciano Coelho * 20*90921014SLuciano Coelho */ 21*90921014SLuciano Coelho 22*90921014SLuciano Coelho #include <linux/interrupt.h> 23*90921014SLuciano Coelho #include <linux/irq.h> 24*90921014SLuciano Coelho #include <linux/module.h> 25*90921014SLuciano Coelho #include <linux/slab.h> 26*90921014SLuciano Coelho #include <linux/crc7.h> 27*90921014SLuciano Coelho #include <linux/spi/spi.h> 28*90921014SLuciano Coelho #include <linux/wl12xx.h> 29*90921014SLuciano Coelho 30*90921014SLuciano Coelho #include "wl1251.h" 31*90921014SLuciano Coelho #include "reg.h" 32*90921014SLuciano Coelho #include "spi.h" 33*90921014SLuciano Coelho 34*90921014SLuciano Coelho static irqreturn_t wl1251_irq(int irq, void *cookie) 35*90921014SLuciano Coelho { 36*90921014SLuciano Coelho struct wl1251 *wl; 37*90921014SLuciano Coelho 38*90921014SLuciano Coelho wl1251_debug(DEBUG_IRQ, "IRQ"); 39*90921014SLuciano Coelho 40*90921014SLuciano Coelho wl = cookie; 41*90921014SLuciano Coelho 42*90921014SLuciano Coelho ieee80211_queue_work(wl->hw, &wl->irq_work); 43*90921014SLuciano Coelho 44*90921014SLuciano Coelho return IRQ_HANDLED; 45*90921014SLuciano Coelho } 46*90921014SLuciano Coelho 47*90921014SLuciano Coelho static struct spi_device *wl_to_spi(struct wl1251 *wl) 48*90921014SLuciano Coelho { 49*90921014SLuciano Coelho return wl->if_priv; 50*90921014SLuciano Coelho } 51*90921014SLuciano Coelho 52*90921014SLuciano Coelho static void wl1251_spi_reset(struct wl1251 *wl) 53*90921014SLuciano Coelho { 54*90921014SLuciano Coelho u8 *cmd; 55*90921014SLuciano Coelho struct spi_transfer t; 56*90921014SLuciano Coelho struct spi_message m; 57*90921014SLuciano Coelho 58*90921014SLuciano Coelho cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); 59*90921014SLuciano Coelho if (!cmd) { 60*90921014SLuciano Coelho wl1251_error("could not allocate cmd for spi reset"); 61*90921014SLuciano Coelho return; 62*90921014SLuciano Coelho } 63*90921014SLuciano Coelho 64*90921014SLuciano Coelho memset(&t, 0, sizeof(t)); 65*90921014SLuciano Coelho spi_message_init(&m); 66*90921014SLuciano Coelho 67*90921014SLuciano Coelho memset(cmd, 0xff, WSPI_INIT_CMD_LEN); 68*90921014SLuciano Coelho 69*90921014SLuciano Coelho t.tx_buf = cmd; 70*90921014SLuciano Coelho t.len = WSPI_INIT_CMD_LEN; 71*90921014SLuciano Coelho spi_message_add_tail(&t, &m); 72*90921014SLuciano Coelho 73*90921014SLuciano Coelho spi_sync(wl_to_spi(wl), &m); 74*90921014SLuciano Coelho 75*90921014SLuciano Coelho wl1251_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); 76*90921014SLuciano Coelho } 77*90921014SLuciano Coelho 78*90921014SLuciano Coelho static void wl1251_spi_wake(struct wl1251 *wl) 79*90921014SLuciano Coelho { 80*90921014SLuciano Coelho u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; 81*90921014SLuciano Coelho struct spi_transfer t; 82*90921014SLuciano Coelho struct spi_message m; 83*90921014SLuciano Coelho 84*90921014SLuciano Coelho cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); 85*90921014SLuciano Coelho if (!cmd) { 86*90921014SLuciano Coelho wl1251_error("could not allocate cmd for spi init"); 87*90921014SLuciano Coelho return; 88*90921014SLuciano Coelho } 89*90921014SLuciano Coelho 90*90921014SLuciano Coelho memset(crc, 0, sizeof(crc)); 91*90921014SLuciano Coelho memset(&t, 0, sizeof(t)); 92*90921014SLuciano Coelho spi_message_init(&m); 93*90921014SLuciano Coelho 94*90921014SLuciano Coelho /* 95*90921014SLuciano Coelho * Set WSPI_INIT_COMMAND 96*90921014SLuciano Coelho * the data is being send from the MSB to LSB 97*90921014SLuciano Coelho */ 98*90921014SLuciano Coelho cmd[2] = 0xff; 99*90921014SLuciano Coelho cmd[3] = 0xff; 100*90921014SLuciano Coelho cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; 101*90921014SLuciano Coelho cmd[0] = 0; 102*90921014SLuciano Coelho cmd[7] = 0; 103*90921014SLuciano Coelho cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; 104*90921014SLuciano Coelho cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; 105*90921014SLuciano Coelho 106*90921014SLuciano Coelho if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) 107*90921014SLuciano Coelho cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; 108*90921014SLuciano Coelho else 109*90921014SLuciano Coelho cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; 110*90921014SLuciano Coelho 111*90921014SLuciano Coelho cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS 112*90921014SLuciano Coelho | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; 113*90921014SLuciano Coelho 114*90921014SLuciano Coelho crc[0] = cmd[1]; 115*90921014SLuciano Coelho crc[1] = cmd[0]; 116*90921014SLuciano Coelho crc[2] = cmd[7]; 117*90921014SLuciano Coelho crc[3] = cmd[6]; 118*90921014SLuciano Coelho crc[4] = cmd[5]; 119*90921014SLuciano Coelho 120*90921014SLuciano Coelho cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; 121*90921014SLuciano Coelho cmd[4] |= WSPI_INIT_CMD_END; 122*90921014SLuciano Coelho 123*90921014SLuciano Coelho t.tx_buf = cmd; 124*90921014SLuciano Coelho t.len = WSPI_INIT_CMD_LEN; 125*90921014SLuciano Coelho spi_message_add_tail(&t, &m); 126*90921014SLuciano Coelho 127*90921014SLuciano Coelho spi_sync(wl_to_spi(wl), &m); 128*90921014SLuciano Coelho 129*90921014SLuciano Coelho wl1251_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); 130*90921014SLuciano Coelho } 131*90921014SLuciano Coelho 132*90921014SLuciano Coelho static void wl1251_spi_reset_wake(struct wl1251 *wl) 133*90921014SLuciano Coelho { 134*90921014SLuciano Coelho wl1251_spi_reset(wl); 135*90921014SLuciano Coelho wl1251_spi_wake(wl); 136*90921014SLuciano Coelho } 137*90921014SLuciano Coelho 138*90921014SLuciano Coelho static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, 139*90921014SLuciano Coelho size_t len) 140*90921014SLuciano Coelho { 141*90921014SLuciano Coelho struct spi_transfer t[3]; 142*90921014SLuciano Coelho struct spi_message m; 143*90921014SLuciano Coelho u8 *busy_buf; 144*90921014SLuciano Coelho u32 *cmd; 145*90921014SLuciano Coelho 146*90921014SLuciano Coelho cmd = &wl->buffer_cmd; 147*90921014SLuciano Coelho busy_buf = wl->buffer_busyword; 148*90921014SLuciano Coelho 149*90921014SLuciano Coelho *cmd = 0; 150*90921014SLuciano Coelho *cmd |= WSPI_CMD_READ; 151*90921014SLuciano Coelho *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; 152*90921014SLuciano Coelho *cmd |= addr & WSPI_CMD_BYTE_ADDR; 153*90921014SLuciano Coelho 154*90921014SLuciano Coelho spi_message_init(&m); 155*90921014SLuciano Coelho memset(t, 0, sizeof(t)); 156*90921014SLuciano Coelho 157*90921014SLuciano Coelho t[0].tx_buf = cmd; 158*90921014SLuciano Coelho t[0].len = 4; 159*90921014SLuciano Coelho spi_message_add_tail(&t[0], &m); 160*90921014SLuciano Coelho 161*90921014SLuciano Coelho /* Busy and non busy words read */ 162*90921014SLuciano Coelho t[1].rx_buf = busy_buf; 163*90921014SLuciano Coelho t[1].len = WL1251_BUSY_WORD_LEN; 164*90921014SLuciano Coelho spi_message_add_tail(&t[1], &m); 165*90921014SLuciano Coelho 166*90921014SLuciano Coelho t[2].rx_buf = buf; 167*90921014SLuciano Coelho t[2].len = len; 168*90921014SLuciano Coelho spi_message_add_tail(&t[2], &m); 169*90921014SLuciano Coelho 170*90921014SLuciano Coelho spi_sync(wl_to_spi(wl), &m); 171*90921014SLuciano Coelho 172*90921014SLuciano Coelho /* FIXME: check busy words */ 173*90921014SLuciano Coelho 174*90921014SLuciano Coelho wl1251_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); 175*90921014SLuciano Coelho wl1251_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); 176*90921014SLuciano Coelho } 177*90921014SLuciano Coelho 178*90921014SLuciano Coelho static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, 179*90921014SLuciano Coelho size_t len) 180*90921014SLuciano Coelho { 181*90921014SLuciano Coelho struct spi_transfer t[2]; 182*90921014SLuciano Coelho struct spi_message m; 183*90921014SLuciano Coelho u32 *cmd; 184*90921014SLuciano Coelho 185*90921014SLuciano Coelho cmd = &wl->buffer_cmd; 186*90921014SLuciano Coelho 187*90921014SLuciano Coelho *cmd = 0; 188*90921014SLuciano Coelho *cmd |= WSPI_CMD_WRITE; 189*90921014SLuciano Coelho *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; 190*90921014SLuciano Coelho *cmd |= addr & WSPI_CMD_BYTE_ADDR; 191*90921014SLuciano Coelho 192*90921014SLuciano Coelho spi_message_init(&m); 193*90921014SLuciano Coelho memset(t, 0, sizeof(t)); 194*90921014SLuciano Coelho 195*90921014SLuciano Coelho t[0].tx_buf = cmd; 196*90921014SLuciano Coelho t[0].len = sizeof(*cmd); 197*90921014SLuciano Coelho spi_message_add_tail(&t[0], &m); 198*90921014SLuciano Coelho 199*90921014SLuciano Coelho t[1].tx_buf = buf; 200*90921014SLuciano Coelho t[1].len = len; 201*90921014SLuciano Coelho spi_message_add_tail(&t[1], &m); 202*90921014SLuciano Coelho 203*90921014SLuciano Coelho spi_sync(wl_to_spi(wl), &m); 204*90921014SLuciano Coelho 205*90921014SLuciano Coelho wl1251_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); 206*90921014SLuciano Coelho wl1251_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); 207*90921014SLuciano Coelho } 208*90921014SLuciano Coelho 209*90921014SLuciano Coelho static void wl1251_spi_enable_irq(struct wl1251 *wl) 210*90921014SLuciano Coelho { 211*90921014SLuciano Coelho return enable_irq(wl->irq); 212*90921014SLuciano Coelho } 213*90921014SLuciano Coelho 214*90921014SLuciano Coelho static void wl1251_spi_disable_irq(struct wl1251 *wl) 215*90921014SLuciano Coelho { 216*90921014SLuciano Coelho return disable_irq(wl->irq); 217*90921014SLuciano Coelho } 218*90921014SLuciano Coelho 219*90921014SLuciano Coelho static int wl1251_spi_set_power(struct wl1251 *wl, bool enable) 220*90921014SLuciano Coelho { 221*90921014SLuciano Coelho if (wl->set_power) 222*90921014SLuciano Coelho wl->set_power(enable); 223*90921014SLuciano Coelho 224*90921014SLuciano Coelho return 0; 225*90921014SLuciano Coelho } 226*90921014SLuciano Coelho 227*90921014SLuciano Coelho static const struct wl1251_if_operations wl1251_spi_ops = { 228*90921014SLuciano Coelho .read = wl1251_spi_read, 229*90921014SLuciano Coelho .write = wl1251_spi_write, 230*90921014SLuciano Coelho .reset = wl1251_spi_reset_wake, 231*90921014SLuciano Coelho .enable_irq = wl1251_spi_enable_irq, 232*90921014SLuciano Coelho .disable_irq = wl1251_spi_disable_irq, 233*90921014SLuciano Coelho .power = wl1251_spi_set_power, 234*90921014SLuciano Coelho }; 235*90921014SLuciano Coelho 236*90921014SLuciano Coelho static int __devinit wl1251_spi_probe(struct spi_device *spi) 237*90921014SLuciano Coelho { 238*90921014SLuciano Coelho struct wl12xx_platform_data *pdata; 239*90921014SLuciano Coelho struct ieee80211_hw *hw; 240*90921014SLuciano Coelho struct wl1251 *wl; 241*90921014SLuciano Coelho int ret; 242*90921014SLuciano Coelho 243*90921014SLuciano Coelho pdata = spi->dev.platform_data; 244*90921014SLuciano Coelho if (!pdata) { 245*90921014SLuciano Coelho wl1251_error("no platform data"); 246*90921014SLuciano Coelho return -ENODEV; 247*90921014SLuciano Coelho } 248*90921014SLuciano Coelho 249*90921014SLuciano Coelho hw = wl1251_alloc_hw(); 250*90921014SLuciano Coelho if (IS_ERR(hw)) 251*90921014SLuciano Coelho return PTR_ERR(hw); 252*90921014SLuciano Coelho 253*90921014SLuciano Coelho wl = hw->priv; 254*90921014SLuciano Coelho 255*90921014SLuciano Coelho SET_IEEE80211_DEV(hw, &spi->dev); 256*90921014SLuciano Coelho dev_set_drvdata(&spi->dev, wl); 257*90921014SLuciano Coelho wl->if_priv = spi; 258*90921014SLuciano Coelho wl->if_ops = &wl1251_spi_ops; 259*90921014SLuciano Coelho 260*90921014SLuciano Coelho /* This is the only SPI value that we need to set here, the rest 261*90921014SLuciano Coelho * comes from the board-peripherals file */ 262*90921014SLuciano Coelho spi->bits_per_word = 32; 263*90921014SLuciano Coelho 264*90921014SLuciano Coelho ret = spi_setup(spi); 265*90921014SLuciano Coelho if (ret < 0) { 266*90921014SLuciano Coelho wl1251_error("spi_setup failed"); 267*90921014SLuciano Coelho goto out_free; 268*90921014SLuciano Coelho } 269*90921014SLuciano Coelho 270*90921014SLuciano Coelho wl->set_power = pdata->set_power; 271*90921014SLuciano Coelho if (!wl->set_power) { 272*90921014SLuciano Coelho wl1251_error("set power function missing in platform data"); 273*90921014SLuciano Coelho return -ENODEV; 274*90921014SLuciano Coelho } 275*90921014SLuciano Coelho 276*90921014SLuciano Coelho wl->irq = spi->irq; 277*90921014SLuciano Coelho if (wl->irq < 0) { 278*90921014SLuciano Coelho wl1251_error("irq missing in platform data"); 279*90921014SLuciano Coelho return -ENODEV; 280*90921014SLuciano Coelho } 281*90921014SLuciano Coelho 282*90921014SLuciano Coelho wl->use_eeprom = pdata->use_eeprom; 283*90921014SLuciano Coelho 284*90921014SLuciano Coelho ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); 285*90921014SLuciano Coelho if (ret < 0) { 286*90921014SLuciano Coelho wl1251_error("request_irq() failed: %d", ret); 287*90921014SLuciano Coelho goto out_free; 288*90921014SLuciano Coelho } 289*90921014SLuciano Coelho 290*90921014SLuciano Coelho irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); 291*90921014SLuciano Coelho 292*90921014SLuciano Coelho disable_irq(wl->irq); 293*90921014SLuciano Coelho 294*90921014SLuciano Coelho ret = wl1251_init_ieee80211(wl); 295*90921014SLuciano Coelho if (ret) 296*90921014SLuciano Coelho goto out_irq; 297*90921014SLuciano Coelho 298*90921014SLuciano Coelho return 0; 299*90921014SLuciano Coelho 300*90921014SLuciano Coelho out_irq: 301*90921014SLuciano Coelho free_irq(wl->irq, wl); 302*90921014SLuciano Coelho 303*90921014SLuciano Coelho out_free: 304*90921014SLuciano Coelho ieee80211_free_hw(hw); 305*90921014SLuciano Coelho 306*90921014SLuciano Coelho return ret; 307*90921014SLuciano Coelho } 308*90921014SLuciano Coelho 309*90921014SLuciano Coelho static int __devexit wl1251_spi_remove(struct spi_device *spi) 310*90921014SLuciano Coelho { 311*90921014SLuciano Coelho struct wl1251 *wl = dev_get_drvdata(&spi->dev); 312*90921014SLuciano Coelho 313*90921014SLuciano Coelho free_irq(wl->irq, wl); 314*90921014SLuciano Coelho wl1251_free_hw(wl); 315*90921014SLuciano Coelho 316*90921014SLuciano Coelho return 0; 317*90921014SLuciano Coelho } 318*90921014SLuciano Coelho 319*90921014SLuciano Coelho static struct spi_driver wl1251_spi_driver = { 320*90921014SLuciano Coelho .driver = { 321*90921014SLuciano Coelho .name = DRIVER_NAME, 322*90921014SLuciano Coelho .owner = THIS_MODULE, 323*90921014SLuciano Coelho }, 324*90921014SLuciano Coelho 325*90921014SLuciano Coelho .probe = wl1251_spi_probe, 326*90921014SLuciano Coelho .remove = __devexit_p(wl1251_spi_remove), 327*90921014SLuciano Coelho }; 328*90921014SLuciano Coelho 329*90921014SLuciano Coelho static int __init wl1251_spi_init(void) 330*90921014SLuciano Coelho { 331*90921014SLuciano Coelho int ret; 332*90921014SLuciano Coelho 333*90921014SLuciano Coelho ret = spi_register_driver(&wl1251_spi_driver); 334*90921014SLuciano Coelho if (ret < 0) { 335*90921014SLuciano Coelho wl1251_error("failed to register spi driver: %d", ret); 336*90921014SLuciano Coelho goto out; 337*90921014SLuciano Coelho } 338*90921014SLuciano Coelho 339*90921014SLuciano Coelho out: 340*90921014SLuciano Coelho return ret; 341*90921014SLuciano Coelho } 342*90921014SLuciano Coelho 343*90921014SLuciano Coelho static void __exit wl1251_spi_exit(void) 344*90921014SLuciano Coelho { 345*90921014SLuciano Coelho spi_unregister_driver(&wl1251_spi_driver); 346*90921014SLuciano Coelho 347*90921014SLuciano Coelho wl1251_notice("unloaded"); 348*90921014SLuciano Coelho } 349*90921014SLuciano Coelho 350*90921014SLuciano Coelho module_init(wl1251_spi_init); 351*90921014SLuciano Coelho module_exit(wl1251_spi_exit); 352*90921014SLuciano Coelho 353*90921014SLuciano Coelho MODULE_LICENSE("GPL"); 354*90921014SLuciano Coelho MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); 355*90921014SLuciano Coelho MODULE_ALIAS("spi:wl1251"); 356