From patchwork Sun Jan 18 05:16:45 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Green X-Patchwork-Id: 5652921 X-Patchwork-Delegate: kvalo@adurom.com Return-Path: X-Original-To: patchwork-linux-wireless@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 5B89D9F333 for ; Sun, 18 Jan 2015 05:17:01 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0CF6D2037E for ; Sun, 18 Jan 2015 05:17:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 97E622035B for ; Sun, 18 Jan 2015 05:16:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751237AbbARFQx (ORCPT ); Sun, 18 Jan 2015 00:16:53 -0500 Received: from mail-pd0-f177.google.com ([209.85.192.177]:54770 "EHLO mail-pd0-f177.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751104AbbARFQv (ORCPT ); Sun, 18 Jan 2015 00:16:51 -0500 Received: by mail-pd0-f177.google.com with SMTP id y13so5389279pdi.8; Sat, 17 Jan 2015 21:16:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20120113; h=sender:subject:from:to:cc:date:message-id:in-reply-to:references :user-agent:mime-version:content-type:content-transfer-encoding; bh=EiEoz+7aXIzvAO5SAFKb0aVC0S+PLvQgL7tY8tZht3I=; b=qX7/euyWvJC3Kc8w6ca9lhOcEIzxGy7axSUp78UjdSD84z8mwZi3gXm2uJIYUZOtPa yzWSV1NOpbep+amKzP2N7trmG+Khznb8L6CqkpN3LKaTMmsFObZRK0N66WX77Cl9DmNe BUWZNQlT2VJQsubpyhGFpd19VyBKHr0Xs1RjEY8K0V2w2GcJzhU3KQ9ugM541Cibp7AE 5htoixoz8BdscKenxtx/ToGACwmiaxJYyHBSgY4lr8vnz3/1KaZxkLkrXJ/LAL+70V7U td4HUSqqExJsd1Yd4eTiIusaKetkTP2SQ1etP8m8BSp3ZxgBhimCo6tgwaI/pqny3hms IN2w== X-Received: by 10.66.249.39 with SMTP id yr7mr7644448pac.43.1421558210539; Sat, 17 Jan 2015 21:16:50 -0800 (PST) Received: from warmcat.com (114-36-241-182.dynamic.hinet.net. [114.36.241.182]) by mx.google.com with ESMTPSA id fu17sm8090686pdb.5.2015.01.17.21.16.48 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 17 Jan 2015 21:16:49 -0800 (PST) Subject: [PATCH 1/2] net wireless wcn36xx add wcnss platform code From: Andy Green To: Kalle Valo , Eugene Krasnikov Cc: wcn36xx@lists.infradead.org, linux-wireless@vger.kernel.org, netdev@vger.kernel.org Date: Sun, 18 Jan 2015 13:16:45 +0800 Message-ID: <20150118051644.32019.65489.stgit@114-36-241-182.dynamic.hinet.net> In-Reply-To: <20150118051222.32019.32719.stgit@114-36-241-182.dynamic.hinet.net> References: <20150118051222.32019.32719.stgit@114-36-241-182.dynamic.hinet.net> User-Agent: StGit/0.17-dirty MIME-Version: 1.0 Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID,T_RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Eugene Krasnikov AG modified to remove regulator handling not needed on msm8916-qrd Signed-off-by: Eugene Krasnikov Signed-off-by: Andy Green --- drivers/net/wireless/ath/wcn36xx/Makefile | 2 drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c | 357 ++++++++++++++++++++++++ 2 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/net/wireless/ath/wcn36xx/Makefile b/drivers/net/wireless/ath/wcn36xx/Makefile index 50c43b4..e889f2c 100644 --- a/drivers/net/wireless/ath/wcn36xx/Makefile +++ b/drivers/net/wireless/ath/wcn36xx/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_WCN36XX) := wcn36xx.o +obj-$(CONFIG_WCN36XX) := wcn36xx.o wcn36xx-msm.o wcn36xx-y += main.o \ dxe.o \ txrx.o \ diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c b/drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c new file mode 100644 index 0000000..f6f6c83 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include "wcn36xx.h" + +#include +#include + +#define MAC_ADDR_0 "wlan/macaddr0" + +static void *pil; + +struct wcn36xx_msm { + struct wcn36xx_platform_ctrl_ops ctrl_ops; + struct platform_device *core; + void *drv_priv; + void (*rsp_cb)(void *drv_priv, void *buf, size_t len); + /* SMD related */ + struct workqueue_struct *wq; + struct work_struct smd_work; + struct completion smd_compl; + smd_channel_t *smd_ch; + struct pinctrl *pinctrl; +} wmsm; + +static int wcn36xx_msm_smsm_change_state(u32 clear_mask, u32 set_mask) +{ + return smsm_change_state(SMSM_APPS_STATE, clear_mask, set_mask); +} + +static int wcn36xx_msm_get_hw_mac(u8 *addr) +{ + const struct firmware *addr_file = NULL; + int status; + u8 tmp[18]; + static const u8 qcom_oui[3] = {0x00, 0x0A, 0xF5}; + static const char *files = {MAC_ADDR_0}; + + status = request_firmware(&addr_file, files, &wmsm.core->dev); + + if (status < 0) { + /* Assign a random mac with Qualcomm oui */ + dev_err(&wmsm.core->dev, "Failed (%d) to read macaddress file %s, using a random address instead", status, + files); + memcpy(addr, qcom_oui, 3); + get_random_bytes(addr + 3, 3); + } else { + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, addr_file->data, sizeof(tmp) - 1); + sscanf(tmp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &addr[0], + &addr[1], + &addr[2], + &addr[3], + &addr[4], + &addr[5]); + + release_firmware(addr_file); + } + + return 0; +} + +static int wcn36xx_msm_smd_send_and_wait(char *buf, size_t len) +{ + int avail; + int ret = 0; + + avail = smd_write_avail(wmsm.smd_ch); + + if (avail >= len) { + avail = smd_write(wmsm.smd_ch, buf, len); + if (avail != len) { + dev_err(&wmsm.core->dev, + "Cannot write to SMD channel\n"); + ret = -EAGAIN; + goto out; + } + } else { + dev_err(&wmsm.core->dev, + "SMD channel can accept only %d bytes\n", avail); + ret = -ENOMEM; + goto out; + } + +out: + return ret; +} + +static void wcn36xx_msm_smd_notify(void *data, unsigned event) +{ + struct wcn36xx_msm *wmsm_priv = (struct wcn36xx_msm *)data; + + switch (event) { + case SMD_EVENT_OPEN: + complete(&wmsm_priv->smd_compl); + break; + case SMD_EVENT_DATA: + queue_work(wmsm_priv->wq, &wmsm_priv->smd_work); + break; + case SMD_EVENT_CLOSE: + break; + case SMD_EVENT_STATUS: + break; + case SMD_EVENT_REOPEN_READY: + break; + default: + dev_err(&wmsm_priv->core->dev, + "%s: SMD_EVENT (%d) not supported\n", __func__, event); + break; + } +} + +static void wcn36xx_msm_smd_work(struct work_struct *work) +{ + int avail; + int msg_len; + void *msg; + int ret; + struct wcn36xx_msm *wmsm_priv = + container_of(work, struct wcn36xx_msm, smd_work); + + while (1) { + msg_len = smd_cur_packet_size(wmsm_priv->smd_ch); + if (0 == msg_len) { + return; + } + avail = smd_read_avail(wmsm_priv->smd_ch); + if (avail < msg_len) { + return; + } + msg = kmalloc(msg_len, GFP_KERNEL); + if (NULL == msg) { + return; + } + + ret = smd_read(wmsm_priv->smd_ch, msg, msg_len); + if (ret != msg_len) { + return; + } + wmsm_priv->rsp_cb(wmsm_priv->drv_priv, msg, msg_len); + kfree(msg); + } +} + +int wcn36xx_msm_smd_open(void *drv_priv, void *rsp_cb) +{ + int ret, left; + wmsm.drv_priv = drv_priv; + wmsm.rsp_cb = rsp_cb; + INIT_WORK(&wmsm.smd_work, wcn36xx_msm_smd_work); + init_completion(&wmsm.smd_compl); + + wmsm.wq = create_workqueue("wcn36xx_msm_smd_wq"); + if (!wmsm.wq) { + dev_err(&wmsm.core->dev, "failed to allocate wq"); + ret = -ENOMEM; + return ret; + } + + ret = smd_named_open_on_edge("WLAN_CTRL", SMD_APPS_WCNSS, + &wmsm.smd_ch, &wmsm, wcn36xx_msm_smd_notify); + if (ret) { + dev_err(&wmsm.core->dev, + "smd_named_open_on_edge failed: %d\n", ret); + return ret; + } + + left = wait_for_completion_interruptible_timeout(&wmsm.smd_compl, + msecs_to_jiffies(HAL_MSG_TIMEOUT)); + if (left <= 0) { + dev_err(&wmsm.core->dev, + "timeout waiting for smd open: %d\n", ret); + return left; + } + + /* Not to receive INT until the whole buf from SMD is read */ + smd_disable_read_intr(wmsm.smd_ch); + + return 0; +} + +void wcn36xx_msm_smd_close(void) +{ + smd_close(wmsm.smd_ch); + flush_workqueue(wmsm.wq); + destroy_workqueue(wmsm.wq); +} + +int wcn36xx_msm_shutdown(const struct subsys_desc *desc, bool force_stop) +{ + return 0; +} +int wcn36xx_msm_powerup(const struct subsys_desc *desc) +{ + return 0; +} + +static int wcn36xx_msm_probe(struct platform_device *pdev) +{ + int ret; + struct resource *wcnss_memory; + struct resource *tx_irq; + struct resource *rx_irq; + struct resource res[3]; + struct pinctrl_state *ps; + + wmsm.pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(wmsm.pinctrl)) + return PTR_ERR(wmsm.pinctrl); + + ps = pinctrl_lookup_state(wmsm.pinctrl, "wcnss_default"); + if (IS_ERR_OR_NULL(ps)) + return PTR_ERR(ps); + + ret = pinctrl_select_state(wmsm.pinctrl, ps); + if (ret) + return ret; + + if (IS_ERR_OR_NULL(pil)) + pil = subsystem_get("wcnss"); + if (IS_ERR_OR_NULL(pil)) + return PTR_ERR(pil); + + wmsm.core = platform_device_alloc("wcn36xx", -1); + + //dev_err(&pdev->dev, "%s starting\n", __func__); + + memset(res, 0x00, sizeof(res)); + wmsm.ctrl_ops.open = wcn36xx_msm_smd_open; + wmsm.ctrl_ops.close = wcn36xx_msm_smd_close; + wmsm.ctrl_ops.tx = wcn36xx_msm_smd_send_and_wait; + wmsm.ctrl_ops.get_hw_mac = wcn36xx_msm_get_hw_mac; + wmsm.ctrl_ops.smsm_change_state = wcn36xx_msm_smsm_change_state; + wcnss_memory = + platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "wcnss_mmio"); + if (wcnss_memory == NULL) { + dev_err(&wmsm.core->dev, + "Failed to get wcnss wlan memory map.\n"); + ret = -ENOMEM; + return ret; + } + memcpy(&res[0], wcnss_memory, sizeof(*wcnss_memory)); + + tx_irq = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, + "wcnss_wlantx_irq"); + if (tx_irq == NULL) { + dev_err(&wmsm.core->dev, "Failed to get wcnss tx_irq"); + ret = -ENOMEM; + return ret; + } + memcpy(&res[1], tx_irq, sizeof(*tx_irq)); + + rx_irq = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, + "wcnss_wlanrx_irq"); + if (rx_irq == NULL) { + dev_err(&wmsm.core->dev, "Failed to get wcnss rx_irq"); + ret = -ENOMEM; + return ret; + } + memcpy(&res[2], rx_irq, sizeof(*rx_irq)); + + platform_device_add_resources(wmsm.core, res, ARRAY_SIZE(res)); + + ret = platform_device_add_data(wmsm.core, &wmsm.ctrl_ops, + sizeof(wmsm.ctrl_ops)); + if (ret) { + dev_err(&wmsm.core->dev, "Can't add platform data\n"); + ret = -ENOMEM; + return ret; + } + + platform_device_add(wmsm.core); + + dev_info(&pdev->dev, "%s initialized\n", __func__); + + return 0; +} +static int wcn36xx_msm_remove(struct platform_device *pdev) +{ + struct pinctrl_state *ps; + + platform_device_del(wmsm.core); + platform_device_put(wmsm.core); + + if (wmsm.pinctrl) { + ps = pinctrl_lookup_state(wmsm.pinctrl, "wcnss_sleep"); + if (IS_ERR_OR_NULL(ps)) + return PTR_ERR(ps); + + pinctrl_select_state(wmsm.pinctrl, ps); + } + + return 0; +} + +static const struct of_device_id wcn36xx_msm_match_table[] = { + { .compatible = "qcom,wcn36xx" }, + { } +}; +MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table); + +static struct platform_driver wcn36xx_msm_driver = { + .probe = wcn36xx_msm_probe, + .remove = wcn36xx_msm_remove, + .driver = { + .name = "wcn36xx-msm", + .owner = THIS_MODULE, + .of_match_table = wcn36xx_msm_match_table, + }, +}; + +static int __init wcn36xx_msm_init(void) +{ + return platform_driver_register(&wcn36xx_msm_driver); +} +module_init(wcn36xx_msm_init); + +static void __exit wcn36xx_msm_exit(void) +{ + platform_driver_unregister(&wcn36xx_msm_driver); + if (pil) + subsystem_put(pil); + + +} +module_exit(wcn36xx_msm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); +MODULE_FIRMWARE(MAC_ADDR_0); +