Loading drivers/net/wireless/ti/wl1251/spi.c +4 −26 Original line number Original line Diff line number Diff line Loading @@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl) memset(&t, 0, sizeof(t)); memset(&t, 0, sizeof(t)); spi_message_init(&m); spi_message_init(&m); /* /* Set WSPI_INIT_COMMAND * Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB * the data is being send from the MSB to LSB */ */ cmd[2] = 0xff; cmd[2] = 0xff; Loading Loading @@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi) wl->if_ops = &wl1251_spi_ops; wl->if_ops = &wl1251_spi_ops; /* This is the only SPI value that we need to set here, the rest /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ * comes from the board-peripherals file */ spi->bits_per_word = 32; spi->bits_per_word = 32; ret = spi_setup(spi); ret = spi_setup(spi); Loading Loading @@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = { .remove = wl1251_spi_remove, .remove = wl1251_spi_remove, }; }; static int __init wl1251_spi_init(void) module_spi_driver(wl1251_spi_driver); { int ret; ret = spi_register_driver(&wl1251_spi_driver); if (ret < 0) { wl1251_error("failed to register spi driver: %d", ret); goto out; } out: return ret; } static void __exit wl1251_spi_exit(void) { spi_unregister_driver(&wl1251_spi_driver); wl1251_notice("unloaded"); } module_init(wl1251_spi_init); module_exit(wl1251_spi_exit); MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); Loading drivers/net/wireless/ti/wl18xx/main.c +45 −2 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/platform_device.h> #include <linux/ip.h> #include <linux/ip.h> #include <linux/firmware.h> #include <linux/firmware.h> #include <linux/etherdevice.h> #include "../wlcore/wlcore.h" #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" #include "../wlcore/debug.h" Loading Loading @@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { .mem3 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, }, [PART_PHY_INIT] = { [PART_PHY_INIT] = { .mem = { .start = 0x80926000, .mem = { .start = WL18XX_PHY_INIT_MEM_ADDR, .size = sizeof(struct wl18xx_mac_and_phy_params) }, .size = WL18XX_PHY_INIT_MEM_SIZE }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, Loading Loading @@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl) u32 tmp; u32 tmp; int ret; int ret; BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) > WL18XX_PHY_INIT_MEM_SIZE); ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) if (ret < 0) goto out; goto out; Loading @@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); if (ret < 0) goto out; /* * Workaround for FDSP code RAM corruption (needed for PG2.1 * and newer; for older chips it's a NOP). Change FDSP clock * settings so that it's muxed to the ATGP clock instead of * its own clock. */ ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); if (ret < 0) goto out; /* disable FDSP clock */ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, MEM_FDSP_CLK_120_DISABLE); if (ret < 0) goto out; /* set ATPG clock toward FDSP Code RAM rather than its own clock */ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, MEM_FDSP_CODERAM_FUNC_CLK_SEL); if (ret < 0) goto out; /* re-enable FDSP clock */ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, MEM_FDSP_CLK_120_ENABLE); out: out: return ret; return ret; Loading Loading @@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl) ((mac1 & 0xff000000) >> 24); ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = (mac1 & 0xffffff); wl->fuse_nic_addr = (mac1 & 0xffffff); if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) { u8 mac[ETH_ALEN]; eth_random_addr(mac); wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2]; wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5]; wl1271_warning("MAC address from fuse not available, using random locally administered addresses."); } ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: out: Loading drivers/net/wireless/ti/wl18xx/reg.h +15 −0 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,9 @@ #define WL18XX_REG_BOOT_PART_SIZE 0x00014578 #define WL18XX_REG_BOOT_PART_SIZE 0x00014578 #define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 #define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 #define WL18XX_PHY_END_MEM_ADDR 0x8093CA44 #define WL18XX_PHY_INIT_MEM_SIZE \ (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR) #define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) #define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) #define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) #define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) Loading Loading @@ -217,4 +220,16 @@ static const char * const rdl_names[] = { [RDL_4_SP] = "1897 MIMO", [RDL_4_SP] = "1897 MIMO", }; }; /* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */ #define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40 /* command to disable FDSP clock */ #define MEM_FDSP_CLK_120_DISABLE 0x80000000 /* command to set ATPG clock toward FDSP Code RAM rather than its own clock */ #define MEM_FDSP_CODERAM_FUNC_CLK_SEL 0xC0000000 /* command to re-enable FDSP clock */ #define MEM_FDSP_CLK_120_ENABLE 0x40000000 #endif /* __REG_H__ */ #endif /* __REG_H__ */ drivers/net/wireless/ti/wlcore/Makefile +1 −1 Original line number Original line Diff line number Diff line wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ boot.o init.o debugfs.o scan.o boot.o init.o debugfs.o scan.o sysfs.o wlcore_spi-objs = spi.o wlcore_spi-objs = spi.o wlcore_sdio-objs = sdio.o wlcore_sdio-objs = sdio.o Loading drivers/net/wireless/ti/wlcore/main.c +55 −210 Original line number Original line Diff line number Diff line /* /* * This file is part of wl1271 * This file is part of wlcore * * * Copyright (C) 2008-2010 Nokia Corporation * Copyright (C) 2008-2010 Nokia Corporation * * Copyright (C) 2011-2013 Texas Instruments Inc. * Contact: Luciano Coelho <luciano.coelho@nokia.com> * * * This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License Loading @@ -24,34 +23,23 @@ #include <linux/module.h> #include <linux/module.h> #include <linux/firmware.h> #include <linux/firmware.h> #include <linux/delay.h> #include <linux/spi/spi.h> #include <linux/crc32.h> #include <linux/etherdevice.h> #include <linux/etherdevice.h> #include <linux/vmalloc.h> #include <linux/vmalloc.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/wl12xx.h> #include <linux/wl12xx.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/interrupt.h> #include "wlcore.h" #include "wlcore.h" #include "debug.h" #include "debug.h" #include "wl12xx_80211.h" #include "wl12xx_80211.h" #include "io.h" #include "io.h" #include "event.h" #include "tx.h" #include "tx.h" #include "rx.h" #include "ps.h" #include "ps.h" #include "init.h" #include "init.h" #include "debugfs.h" #include "debugfs.h" #include "cmd.h" #include "boot.h" #include "testmode.h" #include "testmode.h" #include "scan.h" #include "scan.h" #include "hw_ops.h" #include "hw_ops.h" #include "sysfs.h" #define WL1271_BOOT_RETRIES 3 #define WL1271_BOOT_RETRIES 3 #define WL1271_BOOT_RETRIES 3 Loading @@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, static void wlcore_op_stop_locked(struct wl1271 *wl); static void wlcore_op_stop_locked(struct wl1271 *wl); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); static int wl12xx_set_authorized(struct wl1271 *wl, static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) struct wl12xx_vif *wlvif) { { int ret; int ret; Loading Loading @@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) { { wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*wl->fw_status_2) + sizeof(*wl->fw_status_2) + wl->fw_status_priv_len, GFP_KERNEL); wl->fw_status_priv_len, GFP_KERNEL); if (!wl->fw_status_1) if (!wl->fw_status_1) Loading @@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl) (((u8 *) wl->fw_status_1) + (((u8 *) wl->fw_status_1) + WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); if (!wl->tx_res_if) { if (!wl->tx_res_if) { kfree(wl->fw_status_1); kfree(wl->fw_status_1); return -ENOMEM; return -ENOMEM; Loading Loading @@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl, return 0; return 0; } } static void wl1271_configure_resume(struct wl1271 *wl, static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) struct wl12xx_vif *wlvif) { { int ret = 0; int ret = 0; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; Loading Loading @@ -2603,6 +2589,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->channel_switch_work); mutex_lock(&wl->mutex); mutex_lock(&wl->mutex); } } Loading Loading @@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) if (ret < 0) return ret; return ret; /* the default WEP key needs to be configured at least once */ if (key_type == KEY_WEP) { ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key, wlvif->sta.hlid); if (ret < 0) return ret; } } } return 0; return 0; Loading Loading @@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, } } EXPORT_SYMBOL_GPL(wlcore_set_key); EXPORT_SYMBOL_GPL(wlcore_set_key); static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int key_idx) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d", key_idx); mutex_lock(&wl->mutex); if (unlikely(wl->state != WLCORE_STATE_ON)) { ret = -EAGAIN; goto out_unlock; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_unlock; wlvif->default_key = key_idx; /* the default WEP key needs to be configured at least once */ if (wlvif->encryption_type == KEY_WEP) { ret = wl12xx_cmd_set_default_wep_key(wl, key_idx, wlvif->sta.hlid); if (ret < 0) goto out_sleep; } out_sleep: wl1271_ps_elp_sleep(wl); out_unlock: mutex_unlock(&wl->mutex); } void wlcore_regdomain_config(struct wl1271 *wl) void wlcore_regdomain_config(struct wl1271 *wl) { { int ret; int ret; Loading Loading @@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr; u32 min_rate; u32 min_rate; int ret; int ret; int ieoffset = offsetof(struct ieee80211_mgmt, int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); u.beacon.variable); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); u16 tmpl_id; u16 tmpl_id; Loading Loading @@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } /* Handle new association with HT. Do this after join. */ /* Handle new association with HT. Do this after join. */ if (sta_exists && if (sta_exists) { (changed & BSS_CHANGED_HT)) { bool enabled = bool enabled = bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; Loading Loading @@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = { .ampdu_action = wl1271_op_ampdu_action, .ampdu_action = wl1271_op_ampdu_action, .tx_frames_pending = wl1271_tx_frames_pending, .tx_frames_pending = wl1271_tx_frames_pending, .set_bitrate_mask = wl12xx_set_bitrate_mask, .set_bitrate_mask = wl12xx_set_bitrate_mask, .set_default_unicast_key = wl1271_op_set_default_key_idx, .channel_switch = wl12xx_op_channel_switch, .channel_switch = wl12xx_op_channel_switch, .flush = wlcore_op_flush, .flush = wlcore_op_flush, .remain_on_channel = wlcore_op_remain_on_channel, .remain_on_channel = wlcore_op_remain_on_channel, Loading Loading @@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band) return idx; return idx; } } static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", wl->sg_enabled); mutex_unlock(&wl->mutex); return len; } static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct wl1271 *wl = dev_get_drvdata(dev); unsigned long res; int ret; ret = kstrtoul(buf, 10, &res); if (ret < 0) { wl1271_warning("incorrect value written to bt_coex_mode"); return count; } mutex_lock(&wl->mutex); res = !!res; if (res == wl->sg_enabled) goto out; wl->sg_enabled = res; if (unlikely(wl->state != WLCORE_STATE_ON)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_acx_sg_enable(wl, wl->sg_enabled); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, wl1271_sysfs_show_bt_coex_state, wl1271_sysfs_store_bt_coex_state); static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); if (wl->hw_pg_ver >= 0) len = snprintf(buf, len, "%d\n", wl->hw_pg_ver); else len = snprintf(buf, len, "n/a\n"); mutex_unlock(&wl->mutex); return len; } static DEVICE_ATTR(hw_pg_ver, S_IRUGO, wl1271_sysfs_show_hw_pg_ver, NULL); static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; int ret; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; /* Let only one thread read the log at a time, blocking others */ while (wl->fwlog_size == 0) { DEFINE_WAIT(wait); prepare_to_wait_exclusive(&wl->fwlog_waitq, &wait, TASK_INTERRUPTIBLE); if (wl->fwlog_size != 0) { finish_wait(&wl->fwlog_waitq, &wait); break; } mutex_unlock(&wl->mutex); schedule(); finish_wait(&wl->fwlog_waitq, &wait); if (signal_pending(current)) return -ERESTARTSYS; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; } /* Check if the fwlog is still valid */ if (wl->fwlog_size < 0) { mutex_unlock(&wl->mutex); return 0; } /* Seeking is not supported - old logs are not kept. Disregard pos. */ len = min(count, (size_t)wl->fwlog_size); wl->fwlog_size -= len; memcpy(buffer, wl->fwlog, len); /* Make room for new messages */ memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); mutex_unlock(&wl->mutex); return len; } static struct bin_attribute fwlog_attr = { .attr = {.name = "fwlog", .mode = S_IRUSR}, .read = wl1271_sysfs_read_fwlog, }; static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) { { int i; int i; Loading Loading @@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) return 0; return 0; } } #define WL1271_DEFAULT_CHANNEL 0 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, u32 mbox_size) u32 mbox_size) { { Loading Loading @@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, goto err_hw; goto err_hw; } } wl->channel = WL1271_DEFAULT_CHANNEL; wl->channel = 0; wl->rx_counter = 0; wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->band = IEEE80211_BAND_2GHZ; wl->band = IEEE80211_BAND_2GHZ; Loading Loading @@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl) wake_up_interruptible_all(&wl->fwlog_waitq); wake_up_interruptible_all(&wl->fwlog_waitq); mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex); device_remove_bin_file(wl->dev, &fwlog_attr); wlcore_sysfs_free(wl); device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); kfree(wl->buffer_32); kfree(wl->buffer_32); kfree(wl->mbox); kfree(wl->mbox); free_page((unsigned long)wl->fwlog); free_page((unsigned long)wl->fwlog); Loading Loading @@ -6104,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) if (ret) if (ret) goto out_irq; goto out_irq; /* Create sysfs file to control bt coex state */ ret = wlcore_sysfs_init(wl); ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); if (ret) if (ret < 0) { wl1271_error("failed to create sysfs file bt_coex_state"); goto out_unreg; goto out_unreg; } /* Create sysfs file to get HW PG version */ ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); if (ret < 0) { wl1271_error("failed to create sysfs file hw_pg_ver"); goto out_bt_coex_state; } /* Create sysfs file for the FW log */ ret = device_create_bin_file(wl->dev, &fwlog_attr); if (ret < 0) { wl1271_error("failed to create sysfs file fwlog"); goto out_hw_pg_ver; } wl->initialized = true; wl->initialized = true; goto out; goto out; out_hw_pg_ver: device_remove_file(wl->dev, &dev_attr_hw_pg_ver); out_bt_coex_state: device_remove_file(wl->dev, &dev_attr_bt_coex_state); out_unreg: out_unreg: wl1271_unregister_hw(wl); wl1271_unregister_hw(wl); Loading Loading
drivers/net/wireless/ti/wl1251/spi.c +4 −26 Original line number Original line Diff line number Diff line Loading @@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl) memset(&t, 0, sizeof(t)); memset(&t, 0, sizeof(t)); spi_message_init(&m); spi_message_init(&m); /* /* Set WSPI_INIT_COMMAND * Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB * the data is being send from the MSB to LSB */ */ cmd[2] = 0xff; cmd[2] = 0xff; Loading Loading @@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi) wl->if_ops = &wl1251_spi_ops; wl->if_ops = &wl1251_spi_ops; /* This is the only SPI value that we need to set here, the rest /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ * comes from the board-peripherals file */ spi->bits_per_word = 32; spi->bits_per_word = 32; ret = spi_setup(spi); ret = spi_setup(spi); Loading Loading @@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = { .remove = wl1251_spi_remove, .remove = wl1251_spi_remove, }; }; static int __init wl1251_spi_init(void) module_spi_driver(wl1251_spi_driver); { int ret; ret = spi_register_driver(&wl1251_spi_driver); if (ret < 0) { wl1251_error("failed to register spi driver: %d", ret); goto out; } out: return ret; } static void __exit wl1251_spi_exit(void) { spi_unregister_driver(&wl1251_spi_driver); wl1251_notice("unloaded"); } module_init(wl1251_spi_init); module_exit(wl1251_spi_exit); MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); Loading
drivers/net/wireless/ti/wl18xx/main.c +45 −2 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/platform_device.h> #include <linux/ip.h> #include <linux/ip.h> #include <linux/firmware.h> #include <linux/firmware.h> #include <linux/etherdevice.h> #include "../wlcore/wlcore.h" #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" #include "../wlcore/debug.h" Loading Loading @@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { .mem3 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, }, [PART_PHY_INIT] = { [PART_PHY_INIT] = { .mem = { .start = 0x80926000, .mem = { .start = WL18XX_PHY_INIT_MEM_ADDR, .size = sizeof(struct wl18xx_mac_and_phy_params) }, .size = WL18XX_PHY_INIT_MEM_SIZE }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, Loading Loading @@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl) u32 tmp; u32 tmp; int ret; int ret; BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) > WL18XX_PHY_INIT_MEM_SIZE); ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); if (ret < 0) if (ret < 0) goto out; goto out; Loading @@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); if (ret < 0) goto out; /* * Workaround for FDSP code RAM corruption (needed for PG2.1 * and newer; for older chips it's a NOP). Change FDSP clock * settings so that it's muxed to the ATGP clock instead of * its own clock. */ ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); if (ret < 0) goto out; /* disable FDSP clock */ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, MEM_FDSP_CLK_120_DISABLE); if (ret < 0) goto out; /* set ATPG clock toward FDSP Code RAM rather than its own clock */ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, MEM_FDSP_CODERAM_FUNC_CLK_SEL); if (ret < 0) goto out; /* re-enable FDSP clock */ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1, MEM_FDSP_CLK_120_ENABLE); out: out: return ret; return ret; Loading Loading @@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl) ((mac1 & 0xff000000) >> 24); ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = (mac1 & 0xffffff); wl->fuse_nic_addr = (mac1 & 0xffffff); if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) { u8 mac[ETH_ALEN]; eth_random_addr(mac); wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2]; wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5]; wl1271_warning("MAC address from fuse not available, using random locally administered addresses."); } ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: out: Loading
drivers/net/wireless/ti/wl18xx/reg.h +15 −0 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,9 @@ #define WL18XX_REG_BOOT_PART_SIZE 0x00014578 #define WL18XX_REG_BOOT_PART_SIZE 0x00014578 #define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 #define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 #define WL18XX_PHY_END_MEM_ADDR 0x8093CA44 #define WL18XX_PHY_INIT_MEM_SIZE \ (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR) #define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) #define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) #define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) #define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) Loading Loading @@ -217,4 +220,16 @@ static const char * const rdl_names[] = { [RDL_4_SP] = "1897 MIMO", [RDL_4_SP] = "1897 MIMO", }; }; /* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */ #define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40 /* command to disable FDSP clock */ #define MEM_FDSP_CLK_120_DISABLE 0x80000000 /* command to set ATPG clock toward FDSP Code RAM rather than its own clock */ #define MEM_FDSP_CODERAM_FUNC_CLK_SEL 0xC0000000 /* command to re-enable FDSP clock */ #define MEM_FDSP_CLK_120_ENABLE 0x40000000 #endif /* __REG_H__ */ #endif /* __REG_H__ */
drivers/net/wireless/ti/wlcore/Makefile +1 −1 Original line number Original line Diff line number Diff line wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ boot.o init.o debugfs.o scan.o boot.o init.o debugfs.o scan.o sysfs.o wlcore_spi-objs = spi.o wlcore_spi-objs = spi.o wlcore_sdio-objs = sdio.o wlcore_sdio-objs = sdio.o Loading
drivers/net/wireless/ti/wlcore/main.c +55 −210 Original line number Original line Diff line number Diff line /* /* * This file is part of wl1271 * This file is part of wlcore * * * Copyright (C) 2008-2010 Nokia Corporation * Copyright (C) 2008-2010 Nokia Corporation * * Copyright (C) 2011-2013 Texas Instruments Inc. * Contact: Luciano Coelho <luciano.coelho@nokia.com> * * * This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License Loading @@ -24,34 +23,23 @@ #include <linux/module.h> #include <linux/module.h> #include <linux/firmware.h> #include <linux/firmware.h> #include <linux/delay.h> #include <linux/spi/spi.h> #include <linux/crc32.h> #include <linux/etherdevice.h> #include <linux/etherdevice.h> #include <linux/vmalloc.h> #include <linux/vmalloc.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/wl12xx.h> #include <linux/wl12xx.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/interrupt.h> #include "wlcore.h" #include "wlcore.h" #include "debug.h" #include "debug.h" #include "wl12xx_80211.h" #include "wl12xx_80211.h" #include "io.h" #include "io.h" #include "event.h" #include "tx.h" #include "tx.h" #include "rx.h" #include "ps.h" #include "ps.h" #include "init.h" #include "init.h" #include "debugfs.h" #include "debugfs.h" #include "cmd.h" #include "boot.h" #include "testmode.h" #include "testmode.h" #include "scan.h" #include "scan.h" #include "hw_ops.h" #include "hw_ops.h" #include "sysfs.h" #define WL1271_BOOT_RETRIES 3 #define WL1271_BOOT_RETRIES 3 #define WL1271_BOOT_RETRIES 3 Loading @@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, static void wlcore_op_stop_locked(struct wl1271 *wl); static void wlcore_op_stop_locked(struct wl1271 *wl); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif); static int wl12xx_set_authorized(struct wl1271 *wl, static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) struct wl12xx_vif *wlvif) { { int ret; int ret; Loading Loading @@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) { { wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*wl->fw_status_2) + sizeof(*wl->fw_status_2) + wl->fw_status_priv_len, GFP_KERNEL); wl->fw_status_priv_len, GFP_KERNEL); if (!wl->fw_status_1) if (!wl->fw_status_1) Loading @@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl) (((u8 *) wl->fw_status_1) + (((u8 *) wl->fw_status_1) + WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); if (!wl->tx_res_if) { if (!wl->tx_res_if) { kfree(wl->fw_status_1); kfree(wl->fw_status_1); return -ENOMEM; return -ENOMEM; Loading Loading @@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl, return 0; return 0; } } static void wl1271_configure_resume(struct wl1271 *wl, static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) struct wl12xx_vif *wlvif) { { int ret = 0; int ret = 0; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; Loading Loading @@ -2603,6 +2589,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->channel_switch_work); mutex_lock(&wl->mutex); mutex_lock(&wl->mutex); } } Loading Loading @@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) if (ret < 0) return ret; return ret; /* the default WEP key needs to be configured at least once */ if (key_type == KEY_WEP) { ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key, wlvif->sta.hlid); if (ret < 0) return ret; } } } return 0; return 0; Loading Loading @@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, } } EXPORT_SYMBOL_GPL(wlcore_set_key); EXPORT_SYMBOL_GPL(wlcore_set_key); static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int key_idx) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d", key_idx); mutex_lock(&wl->mutex); if (unlikely(wl->state != WLCORE_STATE_ON)) { ret = -EAGAIN; goto out_unlock; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out_unlock; wlvif->default_key = key_idx; /* the default WEP key needs to be configured at least once */ if (wlvif->encryption_type == KEY_WEP) { ret = wl12xx_cmd_set_default_wep_key(wl, key_idx, wlvif->sta.hlid); if (ret < 0) goto out_sleep; } out_sleep: wl1271_ps_elp_sleep(wl); out_unlock: mutex_unlock(&wl->mutex); } void wlcore_regdomain_config(struct wl1271 *wl) void wlcore_regdomain_config(struct wl1271 *wl) { { int ret; int ret; Loading Loading @@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr; u32 min_rate; u32 min_rate; int ret; int ret; int ieoffset = offsetof(struct ieee80211_mgmt, int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); u.beacon.variable); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); u16 tmpl_id; u16 tmpl_id; Loading Loading @@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } /* Handle new association with HT. Do this after join. */ /* Handle new association with HT. Do this after join. */ if (sta_exists && if (sta_exists) { (changed & BSS_CHANGED_HT)) { bool enabled = bool enabled = bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; Loading Loading @@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = { .ampdu_action = wl1271_op_ampdu_action, .ampdu_action = wl1271_op_ampdu_action, .tx_frames_pending = wl1271_tx_frames_pending, .tx_frames_pending = wl1271_tx_frames_pending, .set_bitrate_mask = wl12xx_set_bitrate_mask, .set_bitrate_mask = wl12xx_set_bitrate_mask, .set_default_unicast_key = wl1271_op_set_default_key_idx, .channel_switch = wl12xx_op_channel_switch, .channel_switch = wl12xx_op_channel_switch, .flush = wlcore_op_flush, .flush = wlcore_op_flush, .remain_on_channel = wlcore_op_remain_on_channel, .remain_on_channel = wlcore_op_remain_on_channel, Loading Loading @@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band) return idx; return idx; } } static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", wl->sg_enabled); mutex_unlock(&wl->mutex); return len; } static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct wl1271 *wl = dev_get_drvdata(dev); unsigned long res; int ret; ret = kstrtoul(buf, 10, &res); if (ret < 0) { wl1271_warning("incorrect value written to bt_coex_mode"); return count; } mutex_lock(&wl->mutex); res = !!res; if (res == wl->sg_enabled) goto out; wl->sg_enabled = res; if (unlikely(wl->state != WLCORE_STATE_ON)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_acx_sg_enable(wl, wl->sg_enabled); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, wl1271_sysfs_show_bt_coex_state, wl1271_sysfs_store_bt_coex_state); static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); if (wl->hw_pg_ver >= 0) len = snprintf(buf, len, "%d\n", wl->hw_pg_ver); else len = snprintf(buf, len, "n/a\n"); mutex_unlock(&wl->mutex); return len; } static DEVICE_ATTR(hw_pg_ver, S_IRUGO, wl1271_sysfs_show_hw_pg_ver, NULL); static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; int ret; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; /* Let only one thread read the log at a time, blocking others */ while (wl->fwlog_size == 0) { DEFINE_WAIT(wait); prepare_to_wait_exclusive(&wl->fwlog_waitq, &wait, TASK_INTERRUPTIBLE); if (wl->fwlog_size != 0) { finish_wait(&wl->fwlog_waitq, &wait); break; } mutex_unlock(&wl->mutex); schedule(); finish_wait(&wl->fwlog_waitq, &wait); if (signal_pending(current)) return -ERESTARTSYS; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; } /* Check if the fwlog is still valid */ if (wl->fwlog_size < 0) { mutex_unlock(&wl->mutex); return 0; } /* Seeking is not supported - old logs are not kept. Disregard pos. */ len = min(count, (size_t)wl->fwlog_size); wl->fwlog_size -= len; memcpy(buffer, wl->fwlog, len); /* Make room for new messages */ memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); mutex_unlock(&wl->mutex); return len; } static struct bin_attribute fwlog_attr = { .attr = {.name = "fwlog", .mode = S_IRUSR}, .read = wl1271_sysfs_read_fwlog, }; static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) { { int i; int i; Loading Loading @@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) return 0; return 0; } } #define WL1271_DEFAULT_CHANNEL 0 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, u32 mbox_size) u32 mbox_size) { { Loading Loading @@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, goto err_hw; goto err_hw; } } wl->channel = WL1271_DEFAULT_CHANNEL; wl->channel = 0; wl->rx_counter = 0; wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->band = IEEE80211_BAND_2GHZ; wl->band = IEEE80211_BAND_2GHZ; Loading Loading @@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl) wake_up_interruptible_all(&wl->fwlog_waitq); wake_up_interruptible_all(&wl->fwlog_waitq); mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex); device_remove_bin_file(wl->dev, &fwlog_attr); wlcore_sysfs_free(wl); device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); kfree(wl->buffer_32); kfree(wl->buffer_32); kfree(wl->mbox); kfree(wl->mbox); free_page((unsigned long)wl->fwlog); free_page((unsigned long)wl->fwlog); Loading Loading @@ -6104,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) if (ret) if (ret) goto out_irq; goto out_irq; /* Create sysfs file to control bt coex state */ ret = wlcore_sysfs_init(wl); ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); if (ret) if (ret < 0) { wl1271_error("failed to create sysfs file bt_coex_state"); goto out_unreg; goto out_unreg; } /* Create sysfs file to get HW PG version */ ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); if (ret < 0) { wl1271_error("failed to create sysfs file hw_pg_ver"); goto out_bt_coex_state; } /* Create sysfs file for the FW log */ ret = device_create_bin_file(wl->dev, &fwlog_attr); if (ret < 0) { wl1271_error("failed to create sysfs file fwlog"); goto out_hw_pg_ver; } wl->initialized = true; wl->initialized = true; goto out; goto out; out_hw_pg_ver: device_remove_file(wl->dev, &dev_attr_hw_pg_ver); out_bt_coex_state: device_remove_file(wl->dev, &dev_attr_bt_coex_state); out_unreg: out_unreg: wl1271_unregister_hw(wl); wl1271_unregister_hw(wl); Loading