pmc.o \
debug.o
-wcn36xx-platform-y += wcn36xx-msm.o\
- wcnss_core.o
+wcn36xx-platform-y += wcn36xx-msm.o
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <linux/of_irq.h>
#include "wcn36xx.h"
unsigned int wcn36xx_dbg_mask;
static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
struct platform_device *pdev)
{
- struct resource *res;
+ u32 mmio[2];
+ int ret;
+
/* Set TX IRQ */
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "wcnss_wlantx_irq");
- if (!res) {
+ wcn->tx_irq = irq_of_parse_and_map(pdev->dev.parent->of_node, 0);
+ if (!wcn->tx_irq) {
wcn36xx_err("failed to get tx_irq\n");
return -ENOENT;
}
- wcn->tx_irq = res->start;
/* Set RX IRQ */
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- "wcnss_wlanrx_irq");
- if (!res) {
+ wcn->rx_irq = irq_of_parse_and_map(pdev->dev.parent->of_node, 1);
+ if (!wcn->rx_irq) {
wcn36xx_err("failed to get rx_irq\n");
return -ENOENT;
}
- wcn->rx_irq = res->start;
/* Map the memory */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "wcnss_mmio");
- if (!res) {
- wcn36xx_err("failed to get mmio\n");
+ ret = of_property_read_u32_array(pdev->dev.parent->of_node, "qcom,wcnss-mmio", mmio, 2);
+ if (ret) {
+ wcn36xx_err("failed to get qcom,wcnss-mmio\n");
return -ENOENT;
}
- wcn->mmio = ioremap(res->start, resource_size(res));
+
+ wcn->mmio = ioremap(mmio[0], mmio[1]);
if (!wcn->mmio) {
wcn36xx_err("failed to map io memory\n");
return -ENOMEM;
#include <linux/remoteproc.h>
#include <linux/soc/qcom/smd.h>
#include "wcn36xx.h"
-#include "wcnss_core.h"
#define MAC_ADDR_0 "wlan/macaddr0"
dev_set_drvdata(&sdev->dev, &wcn36xx_data);
wcn36xx_data.wlan_ctrl_channel = sdev->channel;
+ wcn36xx_data.chip_type = (enum wcn36xx_chip_type)of_device_get_match_data(&sdev->dev);
+ wcn36xx_data.core = platform_device_alloc("wcn36xx", -1);
+ wcn36xx_data.core->dev.parent = &sdev->dev;
+ wcn36xx_data.core->dev.platform_data = &wcn36xx_data;
+
+ platform_device_add(wcn36xx_data.core);
+
of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev);
return 0;
static void qcom_smd_wlan_ctrl_remove(struct qcom_smd_device *sdev)
{
of_platform_depopulate(&sdev->dev);
+ platform_device_del(wcn36xx_data.core);
+ platform_device_put(wcn36xx_data.core);
}
static int qcom_smd_wlan_ctrl_callback(struct qcom_smd_device *qsdev,
return 0;
}
-static const struct qcom_smd_id qcom_smd_wlan_ctrl_match[] = {
- { .name = "WLAN_CTRL" },
- {}
+static const struct of_device_id wcn36xx_msm_match_table[] = {
+ { .compatible = "qcom,wcn3660-wlan", .data = (void *)WCN36XX_CHIP_3660 },
+ { .compatible = "qcom,wcn3680-wlan", .data = (void *)WCN36XX_CHIP_3680 },
+ { .compatible = "qcom,wcn3620-wlan", .data = (void *)WCN36XX_CHIP_3620 },
+ { }
};
+MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table);
static struct qcom_smd_driver qcom_smd_wlan_ctrl_driver = {
.probe = qcom_smd_wlan_ctrl_probe,
.remove = qcom_smd_wlan_ctrl_remove,
.callback = qcom_smd_wlan_ctrl_callback,
- .smd_match_table = qcom_smd_wlan_ctrl_match,
.driver = {
.name = "qcom_smd_wlan_ctrl",
.owner = THIS_MODULE,
- },
-};
-
-static const struct of_device_id wcn36xx_msm_match_table[] = {
- { .compatible = "qcom,wcn3660", .data = (void *)WCN36XX_CHIP_3660 },
- { .compatible = "qcom,wcn3680", .data = (void *)WCN36XX_CHIP_3680 },
- { .compatible = "qcom,wcn3620", .data = (void *)WCN36XX_CHIP_3620 },
- { }
-};
-MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table);
-
-static int wcn36xx_msm_probe(struct platform_device *pdev)
-{
- int ret;
- const struct of_device_id *of_id;
- struct resource *r;
- struct resource res[3];
- static const char const *rnames[] = {
- "wcnss_mmio", "wcnss_wlantx_irq", "wcnss_wlanrx_irq" };
- static const int rtype[] = {
- IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_IRQ };
- struct device_node *dn;
- int n;
-
- wcnss_core_prepare(pdev);
-
- dn = of_parse_phandle(pdev->dev.of_node, "rproc", 0);
- if (!dn) {
- dev_err(&pdev->dev, "No rproc specified in DT\n");
- } else {
- struct rproc *rproc= rproc_get_by_phandle(dn->phandle);
- if (rproc)
- rproc_boot(rproc);
- else {
- dev_err(&pdev->dev, "No rproc registered\n");
- }
- }
-
- qcom_smd_driver_register(&qcom_smd_wlan_ctrl_driver);
- wcnss_core_init();
-
- of_id = of_match_node(wcn36xx_msm_match_table, pdev->dev.of_node);
- if (!of_id)
- return -EINVAL;
-
- wcn36xx_data.chip_type = (enum wcn36xx_chip_type)of_id->data;
-
- wcn36xx_data.core = platform_device_alloc("wcn36xx", -1);
-
- for (n = 0; n < ARRAY_SIZE(rnames); n++) {
- r = platform_get_resource_byname(pdev, rtype[n], rnames[n]);
- if (!r) {
- dev_err(&wcn36xx_data.core->dev,
- "Missing resource %s'\n", rnames[n]);
- ret = -ENOMEM;
- return ret;
- }
- res[n] = *r;
- }
-
- platform_device_add_resources(wcn36xx_data.core, res, n);
- wcn36xx_data.core->dev.platform_data = &wcn36xx_data;
-
- platform_device_add(wcn36xx_data.core);
-
- dev_info(&pdev->dev, "%s initialized\n", __func__);
-
- return 0;
-}
-
-static int wcn36xx_msm_remove(struct platform_device *pdev)
-{
- platform_device_del(wcn36xx_data.core);
- platform_device_put(wcn36xx_data.core);
- return 0;
-}
-
-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);
+ return qcom_smd_driver_register(&qcom_smd_wlan_ctrl_driver);
}
module_init(wcn36xx_msm_init);
static void __exit wcn36xx_msm_exit(void)
{
- platform_driver_unregister(&wcn36xx_msm_driver);
}
module_exit(wcn36xx_msm_exit);
+++ /dev/null
-#include <linux/completion.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <linux/soc/qcom/smd.h>
-#include "wcn36xx.h"
-#include "wcnss_core.h"
-
-#define WCNSS_CTRL_TIMEOUT (msecs_to_jiffies(500))
-
-static int wcnss_core_config(struct platform_device *pdev, void __iomem *base)
-{
- int ret = 0;
- u32 value, iris_read_v = INVALID_IRIS_REG;
- int clk_48m = 0;
-
- value = readl_relaxed(base + SPARE_OFFSET);
- value |= WCNSS_FW_DOWNLOAD_ENABLE;
- writel_relaxed(value, base + SPARE_OFFSET);
-
- writel_relaxed(0, base + PMU_OFFSET);
- value = readl_relaxed(base + PMU_OFFSET);
- value |= WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
- WCNSS_PMU_CFG_IRIS_XO_EN;
- writel_relaxed(value, base + PMU_OFFSET);
-
- iris_read_v = readl_relaxed(base + IRIS_REG_OFFSET);
- pr_info("iris_read_v: 0x%x\n", iris_read_v);
-
- iris_read_v &= 0xffff;
- iris_read_v |= 0x04;
- writel_relaxed(iris_read_v, base + IRIS_REG_OFFSET);
-
- value = readl_relaxed(base + PMU_OFFSET);
- value |= WCNSS_PMU_CFG_IRIS_XO_READ;
- writel_relaxed(value, base + PMU_OFFSET);
-
- while (readl_relaxed(base + PMU_OFFSET) &
- WCNSS_PMU_CFG_IRIS_XO_READ_STS)
- cpu_relax();
-
- iris_read_v = readl_relaxed(base + 0x1134);
- pr_info("wcnss: IRIS Reg: 0x%08x\n", iris_read_v);
- clk_48m = (iris_read_v >> 30) ? 0 : 1;
- value &= ~WCNSS_PMU_CFG_IRIS_XO_READ;
-
- /* XO_MODE[b2:b1]. Clear implies 19.2MHz */
- value &= ~WCNSS_PMU_CFG_IRIS_XO_MODE;
-
- if (clk_48m)
- value |= WCNSS_PMU_CFG_IRIS_XO_MODE_48;
-
- writel_relaxed(value, base + PMU_OFFSET);
-
- /* Reset IRIS */
- value |= WCNSS_PMU_CFG_IRIS_RESET;
- writel_relaxed(value, base + PMU_OFFSET);
-
- while (readl_relaxed(base + PMU_OFFSET) &
- WCNSS_PMU_CFG_IRIS_RESET_STS)
- cpu_relax();
-
- /* reset IRIS reset bit */
- value &= ~WCNSS_PMU_CFG_IRIS_RESET;
- writel_relaxed(value, base + PMU_OFFSET);
-
- /* start IRIS XO configuration */
- value |= WCNSS_PMU_CFG_IRIS_XO_CFG;
- writel_relaxed(value, base + PMU_OFFSET);
-
- /* Wait for XO configuration to finish */
- while (readl_relaxed(base + PMU_OFFSET) &
- WCNSS_PMU_CFG_IRIS_XO_CFG_STS)
- cpu_relax();
-
- /* Stop IRIS XO configuration */
- value &= ~(WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP |
- WCNSS_PMU_CFG_IRIS_XO_CFG);
- writel_relaxed(value, base + PMU_OFFSET);
-
- msleep(200);
-
- return ret;
-}
-
-int wcnss_core_prepare(struct platform_device *pdev)
-{
- int ret = 0;
- struct resource *res;
- void __iomem *wcnss_base;
-
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "pronto_phy_base");
- if (!res) {
- ret = -EIO;
- dev_err(&pdev->dev, "resource pronto_phy_base failed\n");
- return ret;
- }
-
- wcnss_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(wcnss_base)) {
- dev_err(&pdev->dev, "pronto_phy_base map failed\n");
- return PTR_ERR(wcnss_base);
- }
-
- ret = wcnss_core_config(pdev, wcnss_base);
- return ret;
-}
-
-struct wcnss_platform_data {
- struct qcom_smd_channel *channel;
- struct completion ack;
- struct mutex lock;
-
- struct work_struct rx_work;
- struct work_struct download_work;
-
- struct qcom_smd_device *sdev;
-};
-
-static struct completion fw_ready_compl;
-#define NV_FILE_NAME "wlan/prima/WCNSS_qcom_wlan_nv.bin"
-static void wcn36xx_nv_download_work(struct work_struct *worker)
-{
- int ret = 0, i;
- const struct firmware *nv = NULL;
- struct wcnss_platform_data *wcnss_data =
- container_of(worker, struct wcnss_platform_data, download_work);
- struct device *dev = &wcnss_data->sdev->dev;
-
- struct nvbin_dnld_req_msg *msg;
- const void *nv_blob_start;
- char *pkt = NULL;
- int nv_blob_size = 0, fragments;
-
- ret = request_firmware(&nv, NV_FILE_NAME, dev);
- if (ret || !nv || !nv->data || !nv->size) {
- dev_err(dev, "request firmware for %s (ret = %d)\n",
- NV_FILE_NAME, ret);
- return;
- }
-
- nv_blob_start = nv->data + 4;
- nv_blob_size = nv->size -4;
-
- fragments = (nv_blob_size + NV_FRAGMENT_SIZE - 1)/NV_FRAGMENT_SIZE;
-
- pkt = kzalloc(sizeof(struct nvbin_dnld_req_msg) + NV_FRAGMENT_SIZE,
- GFP_KERNEL);
- if (!pkt) {
- dev_err(dev, "allocation packet for nv download failed\n");
- release_firmware(nv);
- }
-
- msg = (struct nvbin_dnld_req_msg *)pkt;
- msg->hdr.msg_type = WCNSS_NV_DOWNLOAD_REQ;
- msg->dnld_req_params.msg_flags = 0;
-
- i = 0;
- do {
- int pkt_len = 0;
-
- msg->dnld_req_params.frag_number = i;
- if (nv_blob_size > NV_FRAGMENT_SIZE) {
- msg->dnld_req_params.msg_flags &=
- ~LAST_FRAGMENT;
- pkt_len = NV_FRAGMENT_SIZE;
- } else {
- pkt_len = nv_blob_size;
- msg->dnld_req_params.msg_flags |=
- LAST_FRAGMENT | CAN_RECEIVE_CALDATA;
- }
-
- msg->dnld_req_params.nvbin_buffer_size = pkt_len;
- msg->hdr.msg_len =
- sizeof(struct nvbin_dnld_req_msg) + pkt_len;
-
- memcpy(pkt + sizeof(struct nvbin_dnld_req_msg),
- nv_blob_start + i * NV_FRAGMENT_SIZE, pkt_len);
-
- ret = qcom_smd_send(wcnss_data->channel, pkt, msg->hdr.msg_len);
- if (ret) {
- dev_err(dev, "nv download failed\n");
- goto out;
- }
-
- i++;
- nv_blob_size -= NV_FRAGMENT_SIZE;
- msleep(100);
- } while (nv_blob_size > 0);
-
-out:
- kfree(pkt);
- release_firmware(nv);
- return;
-}
-
-static int qcom_smd_wcnss_ctrl_callback(struct qcom_smd_device *qsdev,
- const void *data,
- size_t count)
-{
- struct wcnss_platform_data *wcnss_data = dev_get_drvdata(&qsdev->dev);
- struct smd_msg_hdr phdr;
- const unsigned char *tmp = data;
-
- memcpy_fromio(&phdr, data, sizeof(struct smd_msg_hdr));
-
- switch (phdr.msg_type) {
- /* CBC COMPLETE means firmware ready for go */
- case WCNSS_CBC_COMPLETE_IND:
- complete(&fw_ready_compl);
- pr_info("wcnss: received WCNSS_CBC_COMPLETE_IND from FW\n");
- break;
-
- case WCNSS_NV_DOWNLOAD_RSP:
- pr_info("fw_status: %d\n", tmp[sizeof(struct smd_msg_hdr)]);
- break;
- }
-
- complete(&wcnss_data->ack);
- return 0;
-}
-
-static int qcom_smd_wcnss_ctrl_probe(struct qcom_smd_device *sdev)
-{
- struct wcnss_platform_data *wcnss_data;
-
- wcnss_data = devm_kzalloc(&sdev->dev, sizeof(*wcnss_data), GFP_KERNEL);
- if (!wcnss_data)
- return -ENOMEM;
-
- mutex_init(&wcnss_data->lock);
- init_completion(&wcnss_data->ack);
-
- wcnss_data->sdev = sdev;
-
- dev_set_drvdata(&sdev->dev, wcnss_data);
- wcnss_data->channel = sdev->channel;
-
- INIT_WORK(&wcnss_data->download_work, wcn36xx_nv_download_work);
-
- of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev);
-
- /* We are ready for download here */
- schedule_work(&wcnss_data->download_work);
- return 0;
-}
-
-static void qcom_smd_wcnss_ctrl_remove(struct qcom_smd_device *sdev)
-{
- of_platform_depopulate(&sdev->dev);
-}
-
-static const struct qcom_smd_id qcom_smd_wcnss_ctrl_match[] = {
- { .name = "WCNSS_CTRL" },
- {}
-};
-
-static struct qcom_smd_driver qcom_smd_wcnss_ctrl_driver = {
- .probe = qcom_smd_wcnss_ctrl_probe,
- .remove = qcom_smd_wcnss_ctrl_remove,
- .callback = qcom_smd_wcnss_ctrl_callback,
- .smd_match_table = qcom_smd_wcnss_ctrl_match,
- .driver = {
- .name = "qcom_smd_wcnss_ctrl",
- .owner = THIS_MODULE,
- },
-};
-
-void wcnss_core_init(void)
-{
- int ret = 0;
-
- init_completion(&fw_ready_compl);
- qcom_smd_driver_register(&qcom_smd_wcnss_ctrl_driver);
-
- ret = wait_for_completion_interruptible_timeout(
- &fw_ready_compl, msecs_to_jiffies(FW_READY_TIMEOUT));
- if (ret <= 0) {
- pr_err("timeout waiting for wcnss firmware ready indicator\n");
- return;
- }
-
- return;
-}
-
-void wcnss_core_deinit(void)
-{
- qcom_smd_driver_unregister(&qcom_smd_wcnss_ctrl_driver);
-}
+++ /dev/null
-#ifndef _WCNSS_CORE_H_
-#define _WCNSS_CORE_H_
-
-#define PMU_OFFSET 0x1004
-#define SPARE_OFFSET 0x1088
-#define IRIS_REG_OFFSET 0x1134
-
-#define INVALID_IRIS_REG 0xbaadbaad
-
-#define WCNSS_PMU_CFG_IRIS_XO_CFG BIT(3)
-#define WCNSS_PMU_CFG_IRIS_XO_EN BIT(4)
-#define WCNSS_PMU_CFG_GC_BUS_MUX_SEL_TOP BIT(5)
-#define WCNSS_PMU_CFG_IRIS_XO_CFG_STS BIT(6) /* 1: in progress, 0: done */
-
-#define WCNSS_PMU_CFG_IRIS_RESET BIT(7)
-#define WCNSS_PMU_CFG_IRIS_RESET_STS BIT(8) /* 1: in progress, 0: done */
-#define WCNSS_PMU_CFG_IRIS_XO_READ BIT(9)
-#define WCNSS_PMU_CFG_IRIS_XO_READ_STS BIT(10)
-#define WCNSS_FW_DOWNLOAD_ENABLE BIT(25)
-
-#define WCNSS_PMU_CFG_IRIS_XO_MODE 0x6
-#define WCNSS_PMU_CFG_IRIS_XO_MODE_48 (3 << 1)
-
-#define NV_DOWNLOAD_TIMEOUT 500
-#define NV_FRAGMENT_SIZE 3072
-#define MAX_CALIBRATED_DATA_SIZE (64*1024)
-#define LAST_FRAGMENT (1 << 0)
-#define MESSAGE_TO_FOLLOW (1 << 1)
-#define CAN_RECEIVE_CALDATA (1 << 15)
-#define WCNSS_RESP_SUCCESS 1
-#define WCNSS_RESP_FAIL 0
-
-
-#define WCNSS_NV_DOWNLOAD_REQ 0x01000002
-#define WCNSS_NV_DOWNLOAD_RSP 0x01000003
-#define WCNSS_CBC_COMPLETE_IND 0x0100000C
-
-/*time out 10s for the firmware status ready indicator */
-#define FW_READY_TIMEOUT (10000)
-
-
-struct smd_msg_hdr {
- unsigned int msg_type;
- unsigned int msg_len;
-};
-
-struct nvbin_dnld_req_params {
- /* Fragment sequence number of the NV bin Image. NV Bin Image
- * might not fit into one message due to size limitation of
- * the SMD channel FIFO so entire NV blob is chopped into
- * multiple fragments starting with seqeunce number 0. The
- * last fragment is indicated by marking is_last_fragment field
- * to 1. At receiving side, NV blobs would be concatenated
- * together without any padding bytes in between.
- */
- unsigned short frag_number;
-
- /* bit 0: When set to 1 it indicates that no more fragments will
- * be sent.
- * bit 1: When set, a new message will be followed by this message
- * bit 2- bit 14: Reserved
- * bit 15: when set, it indicates that the sender is capable of
- * receiving Calibrated data.
- */
- unsigned short msg_flags;
-
- /* NV Image size (number of bytes) */
- unsigned int nvbin_buffer_size;
-
- /* Following the 'nvbin_buffer_size', there should be
- * nvbin_buffer_size bytes of NV bin Image i.e.
- * uint8[nvbin_buffer_size].
- */
-};
-
-struct nvbin_dnld_req_msg {
- /* Note: The length specified in nvbin_dnld_req_msg messages
- * should be hdr.msg_len = sizeof(nvbin_dnld_req_msg) +
- * nvbin_buffer_size.
- */
- struct smd_msg_hdr hdr;
- struct nvbin_dnld_req_params dnld_req_params;
-};
-
-struct wcnss_version {
- struct smd_msg_hdr hdr;
- unsigned char major;
- unsigned char minor;
- unsigned char version;
- unsigned char revision;
-};
-
-
-int wcnss_core_prepare(struct platform_device *pdev);
-void wcnss_core_init(void);
-void wcnss_core_deinit(void);
-
-#endif
-