18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Low-level device IO routines for ST-Ericsson CW1200 drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 68c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on: 98c2ecf20Sopenharmony_ci * ST-Ericsson UMAC CW1200 driver, which is 108c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 118c2ecf20Sopenharmony_ci * Author: Ajitpal Singh <ajitpal.singh@lockless.no> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "cw1200.h" 178c2ecf20Sopenharmony_ci#include "hwio.h" 188c2ecf20Sopenharmony_ci#include "hwbus.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci /* Sdio addr is 4*spi_addr */ 218c2ecf20Sopenharmony_ci#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) 228c2ecf20Sopenharmony_ci#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ 238c2ecf20Sopenharmony_ci ((((buf_id) & 0x1F) << 7) \ 248c2ecf20Sopenharmony_ci | (((mpf) & 1) << 6) \ 258c2ecf20Sopenharmony_ci | (((rfu) & 1) << 5) \ 268c2ecf20Sopenharmony_ci | (((reg_id_ofs) & 0x1F) << 0)) 278c2ecf20Sopenharmony_ci#define MAX_RETRY 3 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, 318c2ecf20Sopenharmony_ci void *buf, size_t buf_len, int buf_id) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci u16 addr_sdio; 348c2ecf20Sopenharmony_ci u32 sdio_reg_addr_17bit; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* Check if buffer is aligned to 4 byte boundary */ 378c2ecf20Sopenharmony_ci if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { 388c2ecf20Sopenharmony_ci pr_err("buffer is not aligned.\n"); 398c2ecf20Sopenharmony_ci return -EINVAL; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Convert to SDIO Register Address */ 438c2ecf20Sopenharmony_ci addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); 448c2ecf20Sopenharmony_ci sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv, 478c2ecf20Sopenharmony_ci sdio_reg_addr_17bit, 488c2ecf20Sopenharmony_ci buf, buf_len); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, 528c2ecf20Sopenharmony_ci const void *buf, size_t buf_len, int buf_id) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci u16 addr_sdio; 558c2ecf20Sopenharmony_ci u32 sdio_reg_addr_17bit; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Convert to SDIO Register Address */ 588c2ecf20Sopenharmony_ci addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); 598c2ecf20Sopenharmony_ci sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv, 628c2ecf20Sopenharmony_ci sdio_reg_addr_17bit, 638c2ecf20Sopenharmony_ci buf, buf_len); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic inline int __cw1200_reg_read_32(struct cw1200_common *priv, 678c2ecf20Sopenharmony_ci u16 addr, u32 *val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci __le32 tmp; 708c2ecf20Sopenharmony_ci int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); 718c2ecf20Sopenharmony_ci *val = le32_to_cpu(tmp); 728c2ecf20Sopenharmony_ci return i; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline int __cw1200_reg_write_32(struct cw1200_common *priv, 768c2ecf20Sopenharmony_ci u16 addr, u32 val) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci __le32 tmp = cpu_to_le32(val); 798c2ecf20Sopenharmony_ci return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline int __cw1200_reg_read_16(struct cw1200_common *priv, 838c2ecf20Sopenharmony_ci u16 addr, u16 *val) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci __le16 tmp; 868c2ecf20Sopenharmony_ci int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); 878c2ecf20Sopenharmony_ci *val = le16_to_cpu(tmp); 888c2ecf20Sopenharmony_ci return i; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline int __cw1200_reg_write_16(struct cw1200_common *priv, 928c2ecf20Sopenharmony_ci u16 addr, u16 val) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci __le16 tmp = cpu_to_le16(val); 958c2ecf20Sopenharmony_ci return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ciint cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, 998c2ecf20Sopenharmony_ci size_t buf_len) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 1038c2ecf20Sopenharmony_ci ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); 1048c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 1058c2ecf20Sopenharmony_ci return ret; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ciint cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, 1098c2ecf20Sopenharmony_ci size_t buf_len) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int ret; 1128c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 1138c2ecf20Sopenharmony_ci ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); 1148c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciint cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci int ret, retry = 1; 1218c2ecf20Sopenharmony_ci int buf_id_rx = priv->buf_id_rx; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci while (retry <= MAX_RETRY) { 1268c2ecf20Sopenharmony_ci ret = __cw1200_reg_read(priv, 1278c2ecf20Sopenharmony_ci ST90TDS_IN_OUT_QUEUE_REG_ID, buf, 1288c2ecf20Sopenharmony_ci buf_len, buf_id_rx + 1); 1298c2ecf20Sopenharmony_ci if (!ret) { 1308c2ecf20Sopenharmony_ci buf_id_rx = (buf_id_rx + 1) & 3; 1318c2ecf20Sopenharmony_ci priv->buf_id_rx = buf_id_rx; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } else { 1348c2ecf20Sopenharmony_ci retry++; 1358c2ecf20Sopenharmony_ci mdelay(1); 1368c2ecf20Sopenharmony_ci pr_err("error :[%d]\n", ret); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciint cw1200_data_write(struct cw1200_common *priv, const void *buf, 1458c2ecf20Sopenharmony_ci size_t buf_len) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int ret, retry = 1; 1488c2ecf20Sopenharmony_ci int buf_id_tx = priv->buf_id_tx; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci while (retry <= MAX_RETRY) { 1538c2ecf20Sopenharmony_ci ret = __cw1200_reg_write(priv, 1548c2ecf20Sopenharmony_ci ST90TDS_IN_OUT_QUEUE_REG_ID, buf, 1558c2ecf20Sopenharmony_ci buf_len, buf_id_tx); 1568c2ecf20Sopenharmony_ci if (!ret) { 1578c2ecf20Sopenharmony_ci buf_id_tx = (buf_id_tx + 1) & 31; 1588c2ecf20Sopenharmony_ci priv->buf_id_tx = buf_id_tx; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci } else { 1618c2ecf20Sopenharmony_ci retry++; 1628c2ecf20Sopenharmony_ci mdelay(1); 1638c2ecf20Sopenharmony_ci pr_err("error :[%d]\n", ret); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciint cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, 1728c2ecf20Sopenharmony_ci size_t buf_len, u32 prefetch, u16 port_addr) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u32 val32 = 0; 1758c2ecf20Sopenharmony_ci int i, ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if ((buf_len / 2) >= 0x1000) { 1788c2ecf20Sopenharmony_ci pr_err("Can't read more than 0xfff words.\n"); 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 1838c2ecf20Sopenharmony_ci /* Write address */ 1848c2ecf20Sopenharmony_ci ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); 1858c2ecf20Sopenharmony_ci if (ret < 0) { 1868c2ecf20Sopenharmony_ci pr_err("Can't write address register.\n"); 1878c2ecf20Sopenharmony_ci goto out; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Read CONFIG Register Value - We will read 32 bits */ 1918c2ecf20Sopenharmony_ci ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); 1928c2ecf20Sopenharmony_ci if (ret < 0) { 1938c2ecf20Sopenharmony_ci pr_err("Can't read config register.\n"); 1948c2ecf20Sopenharmony_ci goto out; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Set PREFETCH bit */ 1988c2ecf20Sopenharmony_ci ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, 1998c2ecf20Sopenharmony_ci val32 | prefetch); 2008c2ecf20Sopenharmony_ci if (ret < 0) { 2018c2ecf20Sopenharmony_ci pr_err("Can't write prefetch bit.\n"); 2028c2ecf20Sopenharmony_ci goto out; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Check for PRE-FETCH bit to be cleared */ 2068c2ecf20Sopenharmony_ci for (i = 0; i < 20; i++) { 2078c2ecf20Sopenharmony_ci ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); 2088c2ecf20Sopenharmony_ci if (ret < 0) { 2098c2ecf20Sopenharmony_ci pr_err("Can't check prefetch bit.\n"); 2108c2ecf20Sopenharmony_ci goto out; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci if (!(val32 & prefetch)) 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci mdelay(i); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (val32 & prefetch) { 2198c2ecf20Sopenharmony_ci pr_err("Prefetch bit is not cleared.\n"); 2208c2ecf20Sopenharmony_ci goto out; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Read data port */ 2248c2ecf20Sopenharmony_ci ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); 2258c2ecf20Sopenharmony_ci if (ret < 0) { 2268c2ecf20Sopenharmony_ci pr_err("Can't read data port.\n"); 2278c2ecf20Sopenharmony_ci goto out; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciout: 2318c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ciint cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, 2368c2ecf20Sopenharmony_ci size_t buf_len) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if ((buf_len / 2) >= 0x1000) { 2418c2ecf20Sopenharmony_ci pr_err("Can't write more than 0xfff words.\n"); 2428c2ecf20Sopenharmony_ci return -EINVAL; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci priv->hwbus_ops->lock(priv->hwbus_priv); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Write address */ 2488c2ecf20Sopenharmony_ci ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); 2498c2ecf20Sopenharmony_ci if (ret < 0) { 2508c2ecf20Sopenharmony_ci pr_err("Can't write address register.\n"); 2518c2ecf20Sopenharmony_ci goto out; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Write data port */ 2558c2ecf20Sopenharmony_ci ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, 2568c2ecf20Sopenharmony_ci buf, buf_len, 0); 2578c2ecf20Sopenharmony_ci if (ret < 0) { 2588c2ecf20Sopenharmony_ci pr_err("Can't write data port.\n"); 2598c2ecf20Sopenharmony_ci goto out; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciout: 2638c2ecf20Sopenharmony_ci priv->hwbus_ops->unlock(priv->hwbus_priv); 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciint __cw1200_irq_enable(struct cw1200_common *priv, int enable) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci u32 val32; 2708c2ecf20Sopenharmony_ci u16 val16; 2718c2ecf20Sopenharmony_ci int ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (HIF_8601_SILICON == priv->hw_type) { 2748c2ecf20Sopenharmony_ci ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); 2758c2ecf20Sopenharmony_ci if (ret < 0) { 2768c2ecf20Sopenharmony_ci pr_err("Can't read config register.\n"); 2778c2ecf20Sopenharmony_ci return ret; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (enable) 2818c2ecf20Sopenharmony_ci val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE; 2828c2ecf20Sopenharmony_ci else 2838c2ecf20Sopenharmony_ci val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32); 2868c2ecf20Sopenharmony_ci if (ret < 0) { 2878c2ecf20Sopenharmony_ci pr_err("Can't write config register.\n"); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); 2928c2ecf20Sopenharmony_ci if (ret < 0) { 2938c2ecf20Sopenharmony_ci pr_err("Can't read control register.\n"); 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (enable) 2988c2ecf20Sopenharmony_ci val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE; 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16); 3038c2ecf20Sopenharmony_ci if (ret < 0) { 3048c2ecf20Sopenharmony_ci pr_err("Can't write control register.\n"); 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 310