diff options
author | Kalle Valo <kvalo@codeaurora.org> | 2017-05-19 11:47:44 +0300 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2017-05-19 11:47:44 +0300 |
commit | fb53905cf2c3db6eb1ce37a37c5002456fa7ee20 (patch) | |
tree | 52c6f0bc2987803a10732a2acb772882d4189eb6 /drivers | |
parent | a1ad7198202ff9a2436bd24437730e78d90c5271 (diff) | |
parent | b01c9e327249e4995e5b5b6cebec14166db9c1e7 (diff) |
Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
ath.git patches for 4.13. Major changes:
ath10k
* add initial SDIO support (still work in progress)
Diffstat (limited to 'drivers')
22 files changed, 2782 insertions, 104 deletions
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index b4241cf9b7ed..412eb1380dcc 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -22,6 +22,13 @@ config ATH10K_AHB ---help--- This module adds support for AHB bus +config ATH10K_SDIO + tristate "Atheros ath10k SDIO support (EXPERIMENTAL)" + depends on ATH10K && MMC + ---help--- + This module adds experimental support for SDIO/MMC bus. Currently + work in progress and will not fully work. + config ATH10K_DEBUG bool "Atheros ath10k debugging" depends on ATH10K diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 930fadd940d8..b0b19a7eb98b 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -27,5 +27,8 @@ ath10k_pci-y += pci.o \ ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o +obj-$(CONFIG_ATH10K_SDIO) += ath10k_sdio.o +ath10k_sdio-y += sdio.o + # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index abeee200310b..2d3a2f31123d 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -97,6 +97,77 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, return 0; } +#define TARGET_VERSION_SENTINAL 0xffffffffu + +int ath10k_bmi_get_target_info_sdio(struct ath10k *ar, + struct bmi_target_info *target_info) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info); + u32 resplen, ver_len; + __le32 tmp; + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n"); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "BMI Get Target Info Command disallowed\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO); + + /* Step 1: Read 4 bytes of the target info and check if it is + * the special sentinal version word or the first word in the + * version response. + */ + resplen = sizeof(u32); + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen); + if (ret) { + ath10k_warn(ar, "unable to read from device\n"); + return ret; + } + + /* Some SDIO boards have a special sentinal byte before the real + * version response. + */ + if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) { + /* Step 1b: Read the version length */ + resplen = sizeof(u32); + ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp, + &resplen); + if (ret) { + ath10k_warn(ar, "unable to read from device\n"); + return ret; + } + } + + ver_len = __le32_to_cpu(tmp); + + /* Step 2: Check the target info length */ + if (ver_len != sizeof(resp.get_target_info)) { + ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n", + ver_len, sizeof(resp.get_target_info)); + return -EINVAL; + } + + /* Step 3: Read the rest of the version response */ + resplen = sizeof(resp.get_target_info) - sizeof(u32); + ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, + &resp.get_target_info.version, + &resplen); + if (ret) { + ath10k_warn(ar, "unable to read from device\n"); + return ret; + } + + target_info->version = __le32_to_cpu(resp.get_target_info.version); + target_info->type = __le32_to_cpu(resp.get_target_info.type); + + return 0; +} + int ath10k_bmi_read_memory(struct ath10k *ar, u32 address, void *buffer, u32 length) { diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index cc45b63ade15..0342073ed397 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -198,6 +198,8 @@ void ath10k_bmi_start(struct ath10k *ar); int ath10k_bmi_done(struct ath10k *ar); int ath10k_bmi_get_target_info(struct ath10k *ar, struct bmi_target_info *target_info); +int ath10k_bmi_get_target_info_sdio(struct ath10k *ar, + struct bmi_target_info *target_info); int ath10k_bmi_read_memory(struct ath10k *ar, u32 address, void *buffer, u32 length); int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 5a0638915874..eea111d704c5 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -389,6 +389,21 @@ static void ath10k_send_suspend_complete(struct ath10k *ar) complete(&ar->target_suspend); } +static void ath10k_init_sdio(struct ath10k *ar) +{ + u32 param = 0; + + ath10k_bmi_write32(ar, hi_mbox_io_block_sz, 256); + ath10k_bmi_write32(ar, hi_mbox_isr_yield_limit, 99); + ath10k_bmi_read32(ar, hi_acs_flags, ¶m); + + param |= (HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET | + HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET | + HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE); + + ath10k_bmi_write32(ar, hi_acs_flags, param); +} + static int ath10k_init_configure_target(struct ath10k *ar) { u32 param_host; @@ -1395,7 +1410,18 @@ err: static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name, size_t fw_name_len, int fw_api) { - scnprintf(fw_name, fw_name_len, "%s-%d.bin", ATH10K_FW_FILE_BASE, fw_api); + switch (ar->hif.bus) { + case ATH10K_BUS_SDIO: + scnprintf(fw_name, fw_name_len, "%s-%s-%d.bin", + ATH10K_FW_FILE_BASE, ath10k_bus_str(ar->hif.bus), + fw_api); + break; + case ATH10K_BUS_PCI: + case ATH10K_BUS_AHB: + scnprintf(fw_name, fw_name_len, "%s-%d.bin", + ATH10K_FW_FILE_BASE, fw_api); + break; + } } static int ath10k_core_fetch_firmware_files(struct ath10k *ar) @@ -1953,6 +1979,9 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, if (status) goto err; + if (ar->hif.bus == ATH10K_BUS_SDIO) + ath10k_init_sdio(ar); + ar->htc.htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; @@ -2200,7 +2229,10 @@ static int ath10k_core_probe_fw(struct ath10k *ar) } memset(&target_info, 0, sizeof(target_info)); - ret = ath10k_bmi_get_target_info(ar, &target_info); + if (ar->hif.bus == ATH10K_BUS_SDIO) + ret = ath10k_bmi_get_target_info_sdio(ar, &target_info); + else + ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index bf091514ecc6..8fc08a5043db 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -91,6 +91,7 @@ struct ath10k; enum ath10k_bus { ATH10K_BUS_PCI, ATH10K_BUS_AHB, + ATH10K_BUS_SDIO, }; static inline const char *ath10k_bus_str(enum ath10k_bus bus) @@ -100,6 +101,8 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) return "pci"; case ATH10K_BUS_AHB: return "ahb"; + case ATH10K_BUS_SDIO: + return "sdio"; } return "unknown"; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 4cd2a0fd49d6..389fcb7a9fd0 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -625,17 +625,21 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file, size_t count, loff_t *ppos) { struct ath10k *ar = file->private_data; - char buf[32]; + char buf[32] = {0}; + ssize_t rc; int ret; - simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; - /* make sure that buf is null terminated */ - buf[sizeof(buf) - 1] = 0; + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; /* drop the possible '\n' from the end */ - if (buf[count - 1] == '\n') - buf[count - 1] = 0; + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; mutex_lock(&ar->conf_mutex); diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 2368f47314ae..257d10985c6e 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -38,6 +38,8 @@ enum ath10k_debug_mask { ATH10K_DBG_WMI_PRINT = 0x00002000, ATH10K_DBG_PCI_PS = 0x00004000, ATH10K_DBG_AHB = 0x00008000, + ATH10K_DBG_SDIO = 0x00010000, + ATH10K_DBG_SDIO_DUMP = 0x00020000, ATH10K_DBG_ANY = 0xffffffff, }; diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index b7669b2e94aa..e5c80f582ff5 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -57,8 +57,8 @@ static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc, skb_pull(skb, sizeof(struct ath10k_htc_hdr)); } -static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, - struct sk_buff *skb) +void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, + struct sk_buff *skb) { struct ath10k *ar = ep->htc->ar; @@ -75,6 +75,7 @@ static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, ep->ep_ops.ep_tx_complete(ep->htc->ar, skb); } +EXPORT_SYMBOL(ath10k_htc_notify_tx_completion); static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, struct sk_buff *skb) @@ -230,12 +231,79 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc, spin_unlock_bh(&htc->tx_lock); } -static int ath10k_htc_process_trailer(struct ath10k_htc *htc, - u8 *buffer, - int length, - enum ath10k_htc_ep_id src_eid) +static int +ath10k_htc_process_lookahead(struct ath10k_htc *htc, + const struct ath10k_htc_lookahead_report *report, + int len, + enum ath10k_htc_ep_id eid, + void *next_lookaheads, + int *next_lookaheads_len) { struct ath10k *ar = htc->ar; + + /* Invalid lookahead flags are actually transmitted by + * the target in the HTC control message. + * Since this will happen at every boot we silently ignore + * the lookahead in this case + */ + if (report->pre_valid != ((~report->post_valid) & 0xFF)) + return 0; + + if (next_lookaheads && next_lookaheads_len) { + ath10k_dbg(ar, ATH10K_DBG_HTC, + "htc rx lookahead found pre_valid 0x%x post_valid 0x%x\n", + report->pre_valid, report->post_valid); + + /* look ahead bytes are valid, copy them over */ + memcpy((u8 *)next_lookaheads, report->lookahead, 4); + + *next_lookaheads_len = 1; + } + + return 0; +} + +static int +ath10k_htc_process_lookahead_bundle(struct ath10k_htc *htc, + const struct ath10k_htc_lookahead_bundle *report, + int len, + enum ath10k_htc_ep_id eid, + void *next_lookaheads, + int *next_lookaheads_len) +{ + struct ath10k *ar = htc->ar; + int bundle_cnt = len / sizeof(*report); + + if (!bundle_cnt || (bundle_cnt > HTC_HOST_MAX_MSG_PER_BUNDLE)) { + ath10k_warn(ar, "Invalid lookahead bundle count: %d\n", + bundle_cnt); + return -EINVAL; + } + + if (next_lookaheads && next_lookaheads_len) { + int i; + + for (i = 0; i < bundle_cnt; i++) { + memcpy(((u8 *)next_lookaheads) + 4 * i, + report->lookahead, 4); + report++; + } + + *next_lookaheads_len = bundle_cnt; + } + + return 0; +} + +int ath10k_htc_process_trailer(struct ath10k_htc *htc, + u8 *buffer, + int length, + enum ath10k_htc_ep_id src_eid, + void *next_lookaheads, + int *next_lookaheads_len) +{ + struct ath10k_htc_lookahead_bundle *bundle; + struct ath10k *ar = htc->ar; int status = 0; struct ath10k_htc_record *record; u8 *orig_buffer; @@ -274,6 +342,29 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, record->hdr.len, src_eid); break; + case ATH10K_HTC_RECORD_LOOKAHEAD: + len = sizeof(struct ath10k_htc_lookahead_report); + if (record->hdr.len < len) { + ath10k_warn(ar, "Lookahead report too long\n"); + status = -EINVAL; + break; + } + status = ath10k_htc_process_lookahead(htc, + record->lookahead_report, + record->hdr.len, + src_eid, + next_lookaheads, + next_lookaheads_len); + break; + case ATH10K_HTC_RECORD_LOOKAHEAD_BUNDLE: + bundle = record->lookahead_bundle; + status = ath10k_htc_process_lookahead_bundle(htc, + bundle, + record->hdr.len, + src_eid, + next_lookaheads, + next_lookaheads_len); + break; default: ath10k_warn(ar, "Unhandled record: id:%d length:%d\n", record->hdr.id, record->hdr.len); @@ -294,6 +385,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, return status; } +EXPORT_SYMBOL(ath10k_htc_process_trailer); void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb) { @@ -360,7 +452,8 @@ void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb) trailer += payload_len; trailer -= trailer_len; status = ath10k_htc_process_trailer(htc, trailer, - trailer_len, hdr->eid); + trailer_len, hdr->eid, + NULL, NULL); if (status) goto out; @@ -371,42 +464,6 @@ void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb) /* zero length packet with trailer data, just drop these */ goto out; - if (eid == ATH10K_HTC_EP_0) { - struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data; - - switch (__le16_to_cpu(msg->hdr.message_id)) { - case ATH10K_HTC_MSG_READY_ID: - case ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID: - /* handle HTC control message */ - if (completion_done(&htc->ctl_resp)) { - /* - * this is a fatal error, target should not be - * sending unsolicited messages on the ep 0 - */ - ath10k_warn(ar, "HTC rx ctrl still processing\n"); - complete(&htc->ctl_resp); - goto out; - } - - htc->control_resp_len = - min_t(int, skb->len, - ATH10K_HTC_MAX_CTRL_MSG_LEN); - - memcpy(htc->control_resp_buffer, skb->data, - htc->control_resp_len); - - complete(&htc->ctl_resp); - break; - case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE: - htc->htc_ops.target_send_suspend_complete(ar); - break; - default: - ath10k_warn(ar, "ignoring unsolicited htc ep0 event\n"); - break; - } - goto out; - } - ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %pK\n", eid, skb); ep->ep_ops.ep_rx_complete(ar, skb); @@ -421,10 +478,40 @@ EXPORT_SYMBOL(ath10k_htc_rx_completion_handler); static void ath10k_htc_control_rx_complete(struct ath10k *ar, struct sk_buff *skb) { - /* This is unexpected. FW is not supposed to send regular rx on this - * endpoint. - */ - ath10k_warn(ar, "unexpected htc rx\n"); + struct ath10k_htc *htc = &ar->htc; + struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data; + + switch (__le16_to_cpu(msg->hdr.message_id)) { + case ATH10K_HTC_MSG_READY_ID: + case ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID: + /* handle HTC control message */ + if (completion_done(&htc->ctl_resp)) { + /* this is a fatal error, target should not be + * sending unsolicited messages on the ep 0 + */ + ath10k_warn(ar, "HTC rx ctrl still processing\n"); + complete(&htc->ctl_resp); + goto out; + } + + htc->control_resp_len = + min_t(int, skb->len, + ATH10K_HTC_MAX_CTRL_MSG_LEN); + + memcpy(htc->control_resp_buffer, skb->data, + htc->control_resp_len); + + complete(&htc->ctl_resp); + break; + case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE: + htc->htc_ops.target_send_suspend_complete(ar); + break; + default: + ath10k_warn(ar, "ignoring unsolicited htc ep0 event\n"); + break; + } + +out: kfree_skb(skb); } @@ -497,12 +584,8 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) struct ath10k *ar = htc->ar; int i, status = 0; unsigned long time_left; - struct ath10k_htc_svc_conn_req conn_req; - struct ath10k_htc_svc_conn_resp conn_resp; struct ath10k_htc_msg *msg; u16 message_id; - u16 credit_count; - u16 credit_size; time_left = wait_for_completion_timeout(&htc->ctl_resp, ATH10K_HTC_WAIT_TIMEOUT_HZ); @@ -539,16 +622,14 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; message_id = __le16_to_cpu(msg->hdr.message_id); - credit_count = __le16_to_cpu(msg->ready.credit_count); - credit_size = __le16_to_cpu(msg->ready.credit_size); if (message_id != ATH10K_HTC_MSG_READY_ID) { ath10k_err(ar, "Invalid HTC ready msg: 0x%x\n", message_id); return -ECOMM; } - htc->total_transmit_credits = credit_count; - htc->target_credit_size = credit_size; + htc->total_transmit_credits = __le16_to_cpu(msg->ready.credit_count); + htc->target_credit_size = __le16_to_cpu(msg->ready.credit_size); ath10k_dbg(ar, ATH10K_DBG_HTC, "Target ready! transmit resources: %d size:%d\n", @@ -561,20 +642,17 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) return -ECOMM; } - /* setup our pseudo HTC control endpoint connection */ - memset(&conn_req, 0, sizeof(conn_req)); - memset(&conn_resp, 0, sizeof(conn_resp)); - conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete; - conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete; - conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS; - conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL; - - /* connect fake service */ - status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp); - if (status) { - ath10k_err(ar, "could not connect to htc service (%d)\n", - status); - return status; + /* The only way to determine if the ready message is an extended + * message is from the size. + */ + if (htc->control_resp_len >= + sizeof(msg->hdr) + sizeof(msg->ready_ext)) { + htc->max_msgs_per_htc_bundle = + min_t(u8, msg->ready_ext.max_msgs_per_htc_bundle, + HTC_HOST_MAX_MSG_PER_BUNDLE); + ath10k_dbg(ar, ATH10K_DBG_HTC, + "Extended ready message. RX bundle size: %d\n", + htc->max_msgs_per_htc_bundle); } return 0; @@ -772,6 +850,13 @@ int ath10k_htc_start(struct ath10k_htc *htc) msg->hdr.message_id = __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID); + if (ar->hif.bus == ATH10K_BUS_SDIO) { + /* Extra setup params used by SDIO */ + msg->setup_complete_ext.flags = + __cpu_to_le32(ATH10K_HTC_SETUP_COMPLETE_FLAGS_RX_BNDL_EN); + msg->setup_complete_ext.max_msgs_per_bundled_recv = + htc->max_msgs_per_htc_bundle; + } ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC is using TX credit flow control\n"); status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); @@ -786,8 +871,10 @@ int ath10k_htc_start(struct ath10k_htc *htc) /* registered target arrival callback from the HIF layer */ int ath10k_htc_init(struct ath10k *ar) { - struct ath10k_htc_ep *ep = NULL; + int status; struct ath10k_htc *htc = &ar->htc; + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; spin_lock_init(&htc->tx_lock); @@ -795,10 +882,21 @@ int ath10k_htc_init(struct ath10k *ar) htc->ar = ar; - /* Get HIF default pipe for HTC message exchange */ - ep = &htc->endpoint[ATH10K_HTC_EP_0]; + /* setup our pseudo HTC control endpoint connection */ + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete; + conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS; + conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL; - ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); + /* connect fake service */ + status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp); + if (status) { + ath10k_err(ar, "could not connect to htc service (%d)\n", + status); + return status; + } init_completion(&htc->ctl_resp); diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index 6ababa345e2b..24663b07eeac 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -50,6 +50,8 @@ struct ath10k; * 4-byte aligned. */ +#define HTC_HOST_MAX_MSG_PER_BUNDLE 8 + enum ath10k_htc_tx_flags { ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, ATH10K_HTC_FLAG_SEND_BUNDLE = 0x02 @@ -110,6 +112,10 @@ enum ath10k_htc_conn_svc_status { ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4 }; +enum ath10k_htc_setup_complete_flags { + ATH10K_HTC_SETUP_COMPLETE_FLAGS_RX_BNDL_EN = 1 +}; + struct ath10k_ath10k_htc_msg_hdr { __le16 message_id; /* @enum htc_message_id */ } __packed; @@ -174,8 +180,10 @@ struct ath10k_htc_msg { } __packed __aligned(4); enum ath10k_ath10k_htc_record_id { - ATH10K_HTC_RECORD_NULL = 0, - ATH10K_HTC_RECORD_CREDITS = 1 + ATH10K_HTC_RECORD_NULL = 0, + ATH10K_HTC_RECORD_CREDITS = 1, + ATH10K_HTC_RECORD_LOOKAHEAD = 2, + ATH10K_HTC_RECORD_LOOKAHEAD_BUNDLE = 3, }; struct ath10k_ath10k_htc_record_hdr { @@ -192,10 +200,28 @@ struct ath10k_htc_credit_report { u8 pad1; } __packed; +struct ath10k_htc_lookahead_report { + u8 pre_valid; + u8 pad0; + u8 pad1; + u8 pad2; + u8 lookahead[4]; + u8 post_valid; + u8 pad3; + u8 pad4; + u8 pad5; +} __packed; + +struct ath10k_htc_lookahead_bundle { + u8 lookahead[4]; +} __packed; + struct ath10k_htc_record { struct ath10k_ath10k_htc_record_hdr hdr; union { struct ath10k_htc_credit_report credit_report[0]; + struct ath10k_htc_lookahead_report lookahead_report[0]; + struct ath10k_htc_lookahead_bundle lookahead_bundle[0]; u8 pauload[0]; }; } __packed __aligned(4); @@ -338,6 +364,7 @@ struct ath10k_htc { int total_transmit_credits; int target_credit_size; + u8 max_msgs_per_htc_bundle; }; int ath10k_htc_init(struct ath10k *ar); @@ -351,5 +378,13 @@ int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size); void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb); void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb); +void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, + struct sk_buff *skb); +int ath10k_htc_process_trailer(struct ath10k_htc *htc, + u8 *buffer, + int length, + enum ath10k_htc_ep_id src_eid, + void *next_lookaheads, + int *next_lookaheads_len); #endif diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 5b1e90bb2a4d..d34272803fd7 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -863,6 +863,59 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define QCA9887_EEPROM_ADDR_LO_MASK 0x00ff0000 #define QCA9887_EEPROM_ADDR_LO_LSB 16 +#define MBOX_RESET_CONTROL_ADDRESS 0x00000000 +#define MBOX_HOST_INT_STATUS_ADDRESS 0x00000800 +#define MBOX_HOST_INT_STATUS_ERROR_LSB 7 +#define MBOX_HOST_INT_STATUS_ERROR_MASK 0x00000080 +#define MBOX_HOST_INT_STATUS_CPU_LSB 6 +#define MBOX_HOST_INT_STATUS_CPU_MASK 0x00000040 +#define MBOX_HOST_INT_STATUS_COUNTER_LSB 4 +#define MBOX_HOST_INT_STATUS_COUNTER_MASK 0x00000010 +#define MBOX_CPU_INT_STATUS_ADDRESS 0x00000801 +#define MBOX_ERROR_INT_STATUS_ADDRESS 0x00000802 +#define MBOX_ERROR_INT_STATUS_WAKEUP_LSB 2 +#define MBOX_ERROR_INT_STATUS_WAKEUP_MASK 0x00000004 +#define MBOX_ERROR_INT_STATUS_RX_UNDERFLOW_LSB 1 +#define MBOX_ERROR_INT_STATUS_RX_UNDERFLOW_MASK 0x00000002 +#define MBOX_ERROR_INT_STATUS_TX_OVERFLOW_LSB 0 +#define MBOX_ERROR_INT_STATUS_TX_OVERFLOW_MASK 0x00000001 +#define MBOX_COUNTER_INT_STATUS_ADDRESS 0x00000803 +#define MBOX_COUNTER_INT_STATUS_COUNTER_LSB 0 +#define MBOX_COUNTER_INT_STATUS_COUNTER_MASK 0x000000ff +#define MBOX_RX_LOOKAHEAD_VALID_ADDRESS 0x00000805 +#define MBOX_INT_STATUS_ENABLE_ADDRESS 0x00000828 +#define MBOX_INT_STATUS_ENABLE_ERROR_LSB 7 +#define MBOX_INT_STATUS_ENABLE_ERROR_MASK 0x00000080 +#define MBOX_INT_STATUS_ENABLE_CPU_LSB 6 +#define MBOX_INT_STATUS_ENABLE_CPU_MASK 0x00000040 +#define MBOX_INT_STATUS_ENABLE_INT_LSB 5 +#define MBOX_INT_STATUS_ENABLE_INT_MASK 0x00000020 +#define MBOX_INT_STATUS_ENABLE_COUNTER_LSB 4 +#define MBOX_INT_STATUS_ENABLE_COUNTER_MASK 0x00000010 +#define MBOX_INT_STATUS_ENABLE_MBOX_DATA_LSB 0 +#define MBOX_INT_STATUS_ENABLE_MBOX_DATA_MASK 0x0000000f +#define MBOX_CPU_INT_STATUS_ENABLE_ADDRESS 0x00000819 +#define MBOX_CPU_INT_STATUS_ENABLE_BIT_LSB 0 +#define MBOX_CPU_INT_STATUS_ENABLE_BIT_MASK 0x000000ff +#define MBOX_ERROR_STATUS_ENABLE_ADDRESS 0x0000081a +#define MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB 1 +#define MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK 0x00000002 +#define MBOX_ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB 0 +#define MBOX_ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK 0x00000001 +#define MBOX_COUNTER_INT_STATUS_ENABLE_ADDRESS 0x0000081b +#define MBOX_COUNTER_INT_STATUS_ENABLE_BIT_LSB 0 +#define MBOX_COUNTER_INT_STATUS_ENABLE_BIT_MASK 0x000000ff +#define MBOX_COUNT_ADDRESS 0x00000820 +#define MBOX_COUNT_DEC_ADDRESS 0x00000840 +#define MBOX_WINDOW_DATA_ADDRESS 0x00000874 +#define MBOX_WINDOW_WRITE_ADDR_ADDRESS 0x00000878 +#define MBOX_WINDOW_READ_ADDR_ADDRESS 0x0000087c +#define MBOX_CPU_DBG_SEL_ADDRESS 0x00000883 +#define MBOX_CPU_DBG_ADDRESS 0x00000884 +#define MBOX_RTC_BASE_ADDRESS 0x00000000 +#define MBOX_GPIO_BASE_ADDRESS 0x00005000 +#define MBOX_MBOX_BASE_ADDRESS 0x00008000 + #define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB) /* Register definitions for first generation ath10k cards. These cards include diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c new file mode 100644 index 000000000000..9e78fbae8413 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -0,0 +1,2113 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. + * Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sd.h> +#include <linux/bitfield.h> +#include "core.h" +#include "bmi.h" +#include "debug.h" +#include "hif.h" +#include "htc.h" +#include "targaddrs.h" +#include "trace.h" +#include "sdio.h" + +/* inlined helper functions */ + +static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio, + size_t len) +{ + return __ALIGN_MASK((len), ar_sdio->mbox_info.block_mask); +} + +static inline enum ath10k_htc_ep_id pipe_id_to_eid(u8 pipe_id) +{ + return (enum ath10k_htc_ep_id)pipe_id; +} + +static inline void ath10k_sdio_mbox_free_rx_pkt(struct ath10k_sdio_rx_data *pkt) +{ + dev_kfree_skb(pkt->skb); + pkt->skb = NULL; + pkt->alloc_len = 0; + pkt->act_len = 0; + pkt->trailer_only = false; +} + +static inline int ath10k_sdio_mbox_alloc_rx_pkt(struct ath10k_sdio_rx_data *pkt, + size_t act_len, size_t full_len, + bool part_of_bundle, + bool last_in_bundle) +{ + pkt->skb = dev_alloc_skb(full_len); + if (!pkt->skb) + return -ENOMEM; + + pkt->act_len = act_len; + pkt->alloc_len = full_len; + pkt->part_of_bundle = part_of_bundle; + pkt->last_in_bundle = last_in_bundle; + pkt->trailer_only = false; + + return 0; +} + +static inline bool is_trailer_only_msg(struct ath10k_sdio_rx_data *pkt) +{ + bool trailer_only = false; + struct ath10k_htc_hdr *htc_hdr = + (struct ath10k_htc_hdr *)pkt->skb->data; + u16 len = __le16_to_cpu(htc_hdr->len); |