diff options
Diffstat (limited to 'package/kernel/lantiq/vrx518_ep/src/aca.c')
-rw-r--r-- | package/kernel/lantiq/vrx518_ep/src/aca.c | 1209 |
1 files changed, 1209 insertions, 0 deletions
diff --git a/package/kernel/lantiq/vrx518_ep/src/aca.c b/package/kernel/lantiq/vrx518_ep/src/aca.c new file mode 100644 index 0000000000..3fcf454884 --- /dev/null +++ b/package/kernel/lantiq/vrx518_ep/src/aca.c @@ -0,0 +1,1209 @@ +/******************************************************************************* + + Intel SmartPHY DSL PCIe Endpoint/ACA Linux driver + Copyright(c) 2016 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + +*******************************************************************************/ +#define DEBUG +#include <linux/init.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/firmware.h> + +#include <net/dc_ep.h> + +#include "regs.h" +#include "ep.h" +#include "misc.h" +#include "aca.h" + +#define ACA_FW_FILE "aca_fw.bin" + +#define set_mask_bit(val, set, mask, bits) \ + (val = (((val) & (~((mask) << (bits)))) \ + | (((set) & (mask)) << (bits)))) + +static char soc_str[128]; + +static const char *const aca_img_type_str[ACA_IMG_MAX] = { + "vrx518", + "vrx618", + "falcon-mx", + "pmua", +}; + +static void soc_type_to_str(u32 soc) +{ + memset(soc_str, 0, sizeof(soc_str)); + + if ((soc & ACA_SOC_XRX300)) + strcat(soc_str, "xrx300 "); + + if ((soc & ACA_SOC_XRX500)) + strcat(soc_str, "xrx500 "); + + if ((soc & ACA_SOC_PUMA)) + strcat(soc_str, "puma "); + + if ((soc & ACA_SOC_3RD_PARTY)) + strcat(soc_str, "third party SoC "); +} + +static const char *fw_id_to_str(u32 fw_id) +{ + switch (fw_id) { + case ACA_FW_TXIN: + return "txin"; + + case ACA_FW_TXOUT: + return "txout"; + + case ACA_FW_RXIN: + return "rxin"; + + case ACA_FW_RXOUT: + return "rxout"; + + case ACA_FW_GNRC: + return "Genrisc"; + + default: + return "unknow"; + } +} + +static const char * const sec_id_str[] = { + "Unknown", "HIF", "GenRisc", "MAC_HT", "TXIN", "TXIN_PDRING", "TXOUT", + "TXOUT_PDRING", "RXIN", "RXIN_PDRING", "RXOUT", "RXOUT_PDRING", "DMA", + "FW_INIT", +}; +static const char *sec_id_to_str(u32 sec_id) +{ + switch (sec_id) { + case ACA_SEC_HIF: + case ACA_SEC_GNR: + case ACA_SEC_MAC_HT: + case ACA_SEC_MEM_TXIN: + case ACA_SEC_MEM_TXIN_PDRING: + case ACA_SEC_MEM_TXOUT: + case ACA_SEC_MEM_TXOUT_PDRING: + case ACA_SEC_MEM_RXIN: + case ACA_SEC_MEM_RXIN_PDRING: + case ACA_SEC_MEM_RXOUT: + case ACA_SEC_MEM_RXOUT_PDRING: + case ACA_SEC_DMA: + case ACA_SEC_FW_INIT: + return sec_id_str[sec_id]; + case ACA_SEC_FW: + return "ACA FW"; + + default: + return "unknown"; + } +} + +static inline struct aca_fw_info *to_fw_info(struct dc_ep_priv *priv) +{ + return &priv->aca.fw_info; +} + +static inline struct aca_fw_dl_addr *to_fw_addr(struct dc_ep_priv *priv) +{ + return &priv->aca.fw_info.fw_dl; +} + +static inline struct aca_mem_layout *to_mem_layout(struct dc_ep_priv *priv) +{ + return &priv->aca.fw_info.mem_layout; +} + +static inline struct aca_pdmem_layout *to_pdmem_layout(struct dc_ep_priv *priv) +{ + return &priv->aca.fw_info.pdmem_layout; +} + +static inline struct aca_fw_param *to_aca_fw_param(struct dc_ep_priv *priv) +{ + return &priv->aca.fw_info.fw_param; +} + +static inline struct aca_hif_params *to_hif_params(struct dc_ep_priv *priv) +{ + return priv->aca.hif_params; +} + +static const struct firmware *aca_fetch_fw_file(struct dc_ep_priv *priv, + char *dir, const char *file) +{ + int ret; + char filename[100] = {0}; + const struct firmware *fw; + + if (file == NULL) + return ERR_PTR(-ENOENT); + + if (dir == NULL) + dir = "."; + + snprintf(filename, sizeof(filename), "%s/%s", dir, file); + ret = request_firmware(&fw, filename, priv->dev); + if (ret) + return ERR_PTR(ret); + + return fw; +} + +void dc_aca_free_fw_file(struct dc_ep_priv *priv) +{ + struct aca_fw_info *fw_info = to_fw_info(priv); + + if (fw_info->fw && !IS_ERR(fw_info->fw)) + release_firmware(fw_info->fw); + + fw_info->fw = NULL; + fw_info->fw_data = NULL; + fw_info->fw_len = 0; +} + +static void aca_dma_parse(struct dc_ep_priv *priv, const char *data, int chn) +{ + int i; + u32 cid, dbase; + struct aca_fw_dma *fw_dma; + struct aca_fw_info *fw_info = to_fw_info(priv); + + fw_info->chan_num = chn; + + for (i = 0; i < fw_info->chan_num; i++) { + fw_dma = (struct aca_fw_dma *)(data + i * sizeof(*fw_dma)); + cid = be32_to_cpu(fw_dma->cid); + dbase = be32_to_cpu(fw_dma->base); + fw_info->adma_desc_base[cid] = dbase; + dev_dbg(priv->dev, "dma channel %d desc base 0x%08x\n", + cid, dbase); + } +} + +static void aca_sram_desc_parse(struct dc_ep_priv *priv, const char *data, + u32 sid) +{ + u32 dbase, dnum; + struct aca_sram_desc *desc_base; + struct aca_mem_layout *mem_layout = to_mem_layout(priv); + struct aca_pdmem_layout *pdmem = to_pdmem_layout(priv); + + desc_base = (struct aca_sram_desc *)data; + dbase = be32_to_cpu(desc_base->dbase); + dnum = be32_to_cpu(desc_base->dnum); + + dev_dbg(priv->dev, "Sec %s desc base 0x%08x, des_num: %d\n", + sec_id_to_str(sid), dbase, dnum); + + switch (sid) { + case ACA_SEC_MEM_TXIN: + mem_layout->txin_host_desc_base = dbase; + mem_layout->txin_host_dnum = dnum; + break; + + case ACA_SEC_MEM_TXOUT: + mem_layout->txout_host_desc_base = dbase; + mem_layout->txout_host_dnum = dnum; + break; + + case ACA_SEC_MEM_RXIN: + mem_layout->rxin_host_desc_base = dbase; + mem_layout->rxin_host_dnum = dnum; + break; + + case ACA_SEC_MEM_RXOUT: + mem_layout->rxout_host_desc_base = dbase; + mem_layout->rxout_host_dnum = dnum; + break; + case ACA_SEC_MEM_TXIN_PDRING: + pdmem->txin_pd_desc_base = dbase; + pdmem->txin_pd_dnum = dnum; + break; + case ACA_SEC_MEM_TXOUT_PDRING: + pdmem->txout_pd_desc_base = dbase; + pdmem->txout_pd_dnum = dnum; + break; + case ACA_SEC_MEM_RXIN_PDRING: + pdmem->rxin_pd_desc_base = dbase; + pdmem->rxin_pd_dnum = dnum; + break; + case ACA_SEC_MEM_RXOUT_PDRING: + pdmem->rxin_pd_desc_base = dbase; + pdmem->rxin_pd_dnum = dnum; + break; + default: + dev_err(priv->dev, "Unknow aca sram section %d\n", sid); + break; + } +} + +static void aca_init_parse(struct dc_ep_priv *priv, const char *data, + u32 sid) +{ + struct aca_fw_param *fw_param = to_aca_fw_param(priv); + struct aca_fw_param *param; + u32 hdr_sz, hdr_addr; + + param = (struct aca_fw_param *)data; + hdr_sz = be32_to_cpu(param->st_sz); + hdr_addr = be32_to_cpu(param->init_addr); + + fw_param->init_addr = hdr_addr; + fw_param->st_sz = hdr_sz; + dev_dbg(priv->dev, "init st size: %d, addr: 0x%x\n", + hdr_sz, hdr_addr); +} + +static void aca_fw_parse(struct dc_ep_priv *priv, const char *data, + const char *fw_base, int fw_num) +{ + int i; + size_t size; + u32 id, offset, addr; + struct aca_int_hdr *hdr; + struct aca_fw_dl_addr *fw_dl = to_fw_addr(priv); + + fw_dl->fw_num = fw_num; + + for (i = 0; i < fw_dl->fw_num; i++) { + hdr = (struct aca_int_hdr *)(data + i * sizeof(*hdr)); + id = be32_to_cpu(hdr->id); + offset = be32_to_cpu(hdr->offset); + size = be32_to_cpu(hdr->size); + addr = be32_to_cpu(hdr->load_addr); + + fw_dl->fw_addr[i].fw_id = id; + fw_dl->fw_addr[i].fw_load_addr = addr; + fw_dl->fw_addr[i].fw_size = size; + fw_dl->fw_addr[i].fw_base = fw_base + offset; + dev_dbg(priv->dev, + "aca %s fw offset 0x%x size %zd loc 0x%x fw base %p\n", + fw_id_to_str(id), offset, size, addr, fw_base + offset); + } +} + +/* -------------------------------------------------------- + | Fixed header (20Bytes) | + --------------------------------------------------------- + | Variable header | + | ie / payload | + |-------------------------------------------------------| + | Actual ACA FW | + --------------------------------------------------------- +*/ +static int aca_section_parse(struct dc_ep_priv *priv, const char *fw_data) +{ + int ret = 0; + u32 fixed_hlen; + u32 var_hlen; + u32 ie_id; + size_t ie_len, ie_hlen, ie_dlen; + u32 fw_hlen; + struct aca_fw_f_hdr *fw_f_hdr; + struct aca_fw_ie *ie_hdr; + struct aca_int_hdr *aca_hdr; + const char *data = fw_data; + const char *aca_fw_data; + struct device *dev = priv->dev; + + fw_f_hdr = (struct aca_fw_f_hdr *)data; + + fw_hlen = be32_to_cpu(fw_f_hdr->hdr_size); + fixed_hlen = sizeof(*fw_f_hdr); + var_hlen = fw_hlen - fixed_hlen; + ie_hlen = sizeof(*ie_hdr); + + /* Record actual ACA fw data pointer */ + aca_fw_data = data + fw_hlen; + + /* Point to variable header and parse them */ + data += fixed_hlen; + + while (var_hlen > ie_hlen) { + /* Variable header information element */ + ie_hdr = (struct aca_fw_ie *)data; + ie_id = be32_to_cpu(ie_hdr->id); + ie_len = be32_to_cpu(ie_hdr->len); + dev_dbg(dev, "Section %s ie_len %zd\n", sec_id_to_str(ie_id), + ie_len); + + /* Variable header data conents */ + data += ie_hlen; + var_hlen -= ie_hlen; + + switch (ie_id) { + case ACA_SEC_HIF: + case ACA_SEC_GNR: + case ACA_SEC_MAC_HT: + ie_dlen = ie_len * sizeof(struct aca_fw_reg); + data += ie_dlen; + var_hlen -= ie_dlen; + + break; + + case ACA_SEC_MEM_TXIN: + case ACA_SEC_MEM_TXOUT: + case ACA_SEC_MEM_RXIN: + case ACA_SEC_MEM_RXOUT: + case ACA_SEC_MEM_TXIN_PDRING: + case ACA_SEC_MEM_TXOUT_PDRING: + case ACA_SEC_MEM_RXIN_PDRING: + case ACA_SEC_MEM_RXOUT_PDRING: + aca_sram_desc_parse(priv, data, ie_id); + ie_dlen = ie_len * sizeof(struct aca_sram_desc); + data += ie_dlen; + var_hlen -= ie_dlen; + break; + + case ACA_SEC_DMA: + if (ie_len > ACA_DMA_CHAN_MAX) { + dev_err(dev, "invalid dma channel %d\n", + ie_len); + ret = -EINVAL; + goto done; + } + aca_dma_parse(priv, data, ie_len); + ie_dlen = ie_len * sizeof(struct aca_fw_dma); + data += ie_dlen; + var_hlen -= ie_dlen; + break; + + case ACA_SEC_FW_INIT: + aca_init_parse(priv, data, ie_id); + ie_dlen = ie_len * sizeof(struct aca_fw_param); + data += ie_dlen; + var_hlen -= ie_dlen; + break; + + case ACA_SEC_FW: + if (ie_len > ACA_FW_MAX) { + dev_err(dev, "Too many aca fws %d\n", ie_len); + ret = -EINVAL; + goto done; + } + aca_fw_parse(priv, data, aca_fw_data, ie_len); + ie_dlen = ie_len * sizeof(*aca_hdr); + data += ie_dlen; + var_hlen -= ie_dlen; + break; + + default: + dev_warn(dev, "Unknown Sec id: %u\n", ie_id); + break; + } + } +done: + return ret; +} + +static int aca_fetch_fw_api(struct dc_ep_priv *priv, const char *name) +{ + int ret; + size_t hdr_len; + const u8 *fw_data; + size_t fw_len; + char dir[8] = {0}; + union fw_ver ver; + union img_soc_type type; + struct device *dev = priv->dev; + struct aca_fw_f_hdr *fw_f_hdr; + struct aca_fw_info *fw_info = to_fw_info(priv); + + sprintf(dir, "%04x", priv->pdev->device); + fw_info->fw = aca_fetch_fw_file(priv, dir, name); + if (IS_ERR(fw_info->fw)) { + dev_err(dev, "Could not fetch firmware file '%s': %ld\n", + name, PTR_ERR(fw_info->fw)); + return PTR_ERR(fw_info->fw); + } + + fw_data = fw_info->fw->data; + fw_len = fw_info->fw->size; + + /* Parse the fixed header part */ + fw_f_hdr = (struct aca_fw_f_hdr *)fw_data; + ver.all = be32_to_cpu(fw_f_hdr->ver); + + dev_info(dev, "ACA fw build %d branch %d major 0x%2x minor 0x%04x\n", + ver.field.build, ver.field.branch, + ver.field.major, ver.field.minor); + + type.all = be32_to_cpu(fw_f_hdr->type); + + if (type.field.img_type > (ACA_IMG_MAX - 1) + || ((type.field.soc_type & ACA_SOC_MASK) == 0)) { + dev_err(dev, "Invalid aca fw img %d soc %d\n", + type.field.img_type, type.field.soc_type); + ret = -EINVAL; + goto err; + } + + soc_type_to_str(type.field.soc_type); + + dev_info(priv->dev, "ACA fw for %s supported SoC type %s\n", + aca_img_type_str[type.field.img_type], soc_str); + + hdr_len = be32_to_cpu(fw_f_hdr->hdr_size); + /* Sanity Check */ + if (fw_len < hdr_len) { + dev_err(dev, "Invalid aca fw hdr len %zd fw len %zd\n", + hdr_len, fw_len); + ret = -EINVAL; + goto err; + } + dev_dbg(dev, "Header size 0x%08x fw size 0x%08x\n", + hdr_len, be32_to_cpu(fw_f_hdr->fw_size)); + dev_dbg(dev, "section number %d\n", + be32_to_cpu(fw_f_hdr->num_section)); + + aca_section_parse(priv, fw_data); + return 0; +err: + dc_aca_free_fw_file(priv); + return ret; +} + +static int aca_fetch_fw(struct dc_ep_priv *priv) +{ + return aca_fetch_fw_api(priv, ACA_FW_FILE); +} + +static int aca_fw_download(struct dc_ep_priv *priv) +{ + int i, j; + u32 val; + size_t size; + u32 id, load_addr; + const char *fw_base; + struct aca_fw_dl_addr *fw_dl = to_fw_addr(priv); + + for (i = 0; i < fw_dl->fw_num; i++) { + id = fw_dl->fw_addr[i].fw_id; + load_addr = fw_dl->fw_addr[i].fw_load_addr; + size = fw_dl->fw_addr[i].fw_size; + fw_base = fw_dl->fw_addr[i].fw_base; + + if (size % 4) { + dev_err(priv->dev, + "aca %s fw size is not a multiple of 4\n", + fw_id_to_str(id)); + return -EINVAL; + } + + for (j = 0; j < size; j += 4) { + val = *((u32 *)(fw_base + j)); + wr32(cpu_to_be32(val), load_addr + j); + } + /* Write flush */ + rd32(load_addr); + #ifdef DEBUG + { + u32 src, dst; + + for (j = 0; j < size; j += 4) { + dst = rd32(load_addr + j); + src = *((u32 *)(fw_base + j)); + if (dst != cpu_to_be32(src)) { + dev_info(priv->dev, + "dst 0x%08x != src 0x%08x\n", dst, src); + return -EIO; + } + } + } + #endif /* DEBUG */ + } + return 0; +} + +static void aca_dma_ctrl_init(struct dc_ep_priv *priv) +{ + u32 val; + struct dc_aca *aca = to_aca(priv); + + /* Global software reset CDMA */ + wr32_mask(0, BIT(CTRL_RST), ADMA_CTRL); + while ((rd32(ADMA_CTRL) & BIT(CTRL_RST))) + ; + + val = rd32(ADMA_ID); + /* Record max dma channels for later usage */ + aca->adma_chans = MS(val, ADMA_ID_CHNR); + val = rd32(ADMA_CTRL); + /* + * Enable Packet Arbitration + * Enable Meta data copy + * Enable Dedicated Descriptor port + */ + val |= BIT(CTRL_PKTARB) | BIT(CTRL_MDC) | BIT(CTRL_DSRAM); + set_mask_bit(val, 1, 1, CTRL_ENBE); /* Enable byte enable */ + set_mask_bit(val, 1, 1, CTRL_DCNF); /* 2DW descriptor format */ + set_mask_bit(val, 1, 1, CTRL_DDBR); /* Descriptor read back */ + set_mask_bit(val, 1, 1, CTRL_DRB); /* Dynamic burst read */ + wr32(val, ADMA_CTRL); + + /* Polling cnt cfg */ + wr32(ADMA_CPOLL_EN | SM(ADMA_DEFAULT_POLL, ADMA_CPOLL_CNT), + ADMA_CPOLL); +} + +static void aca_dma_port_init(struct dc_ep_priv *priv) +{ + u32 val; + + /* Only one port /port 0 */ + wr32(0, ADMA_PS); + val = rd32(ADMA_PCTRL); + set_mask_bit(val, 1, 1, PCTRL_RXBL16); + set_mask_bit(val, 1, 1, PCTRL_TXBL16); + set_mask_bit(val, 0, 3, PCTRL_RXBL); + set_mask_bit(val, 0, 3, PCTRL_TXBL); + + set_mask_bit(val, 0, 3, PCTRL_TXENDI); + set_mask_bit(val, 0, 3, PCTRL_RXENDI); + wr32(val, ADMA_PCTRL); +} + +static void aca_dma_ch_init(struct dc_ep_priv *priv, u32 cid, + u32 dbase, u32 dlen) +{ + /* Select channel */ + wr32(cid, ADMA_CS); + + /* Reset Channel */ + wr32_mask(0, BIT(CCTRL_RST), ADMA_CCTRL); + while ((rd32(ADMA_CCTRL) & BIT(CCTRL_RST))) + ; + + /* Set descriptor list base and length */ + wr32(dbase, ADMA_CDBA); + wr32(dlen, ADMA_CDLEN); + + /*Clear Intr */ + wr32(ADMA_CI_ALL, ADMA_CIS); + /* Enable Intr */ + wr32(ADMA_CI_ALL, ADMA_CIE); + + /* Enable Channel */ + wr32_mask(0, BIT(CCTRL_ONOFF), ADMA_CCTRL); + mb(); +} + +static void aca_dma_ch_off(struct dc_ep_priv *priv) +{ + int i; + struct dc_aca *aca = to_aca(priv); + + /* Shared between OS and ACA FW. Stop ACA first */ + for (i = 0; i < aca->adma_chans; i++) { + wr32(i, ADMA_CS); + wr32_mask(BIT(CCTRL_ONOFF), 0, ADMA_CCTRL); + while (rd32(ADMA_CCTRL) & BIT(CCTRL_ONOFF)) + ; + } + dev_dbg(priv->dev, "aca dma channel done\n"); +} + +static void aca_xbar_ia_reject_set(struct dc_ep_priv *priv, int ia_id) +{ + u32 val; + int timeout = 1000; + struct device *dev = priv->dev; + + /* Set reject bit */ + wr32(XBAR_CTRL_REJECT, ACA_AGENT_CTRL(ia_id)); + + /* Poll burst, readex, resp_waiting, req_active */ + val = XBAR_STAT_REQ_ACTIVE | XBAR_STAT_RESP_WAITING + | XBAR_STAT_BURST | XBAR_STAT_READEX; + while (--timeout && !!(rd32(ACA_AGENT_STATUS(ia_id)) & val)) + udelay(1); + + if (timeout <= 0) { + dev_dbg(dev, + "ACA XBAR IA: %d reset timeout, pending on 0x%x\n", + ia_id, rd32(ACA_AGENT_STATUS(ia_id))); + return; + } +} + +static void aca_xbar_ia_reject_clr(struct dc_ep_priv *priv, int ia_id) +{ + u32 val; + + /* Check reject bit */ + val = rd32(ACA_AGENT_CTRL(ia_id)); + if ((val & XBAR_CTRL_REJECT) == 0) + return; + + /* Clear reject bit */ + val &= ~XBAR_CTRL_REJECT; + wr32(val, ACA_AGENT_CTRL(ia_id)); + rd32(ACA_AGENT_CTRL(ia_id)); +} + +static void aca_xbar_ia_reset(struct dc_ep_priv *priv, int ia_id) +{ + /* ACA IA reset */ + wr32(XBAR_CTRL_CORE_RESET, ACA_AGENT_CTRL(ia_id)); + + /* Read till status become 1 */ + while ((rd32(ACA_AGENT_STATUS(ia_id)) & XBAR_STAT_CORE_RESET) == 0) + ; + + /* Clear the IA Reset signal */ + wr32(0, ACA_AGENT_CTRL(ia_id)); + + /* Read till status become 0 */ + while ((rd32(ACA_AGENT_STATUS(ia_id)) & XBAR_STAT_CORE_RESET) == 1) + ; + + dev_dbg(priv->dev, "ACA XBAR IA(%d) reset done\n", ia_id); +} + +void dc_aca_shutdown(struct dc_ep_priv *priv) +{ + struct dc_aca *aca = to_aca(priv); + + if (aca->initialized) { + aca_xbar_ia_reset(priv, ACA_ACC_IA04); + aca_xbar_ia_reset(priv, ACA_M_IA06); + } +} + +static void aca_dma_init(struct dc_ep_priv *priv) +{ + int i; + struct aca_fw_info *fw_info = to_fw_info(priv); + + aca_dma_ctrl_init(priv); + aca_dma_port_init(priv); + + for (i = 0; i < fw_info->chan_num; i++) { + aca_dma_ch_init(priv, i, + fw_info->adma_desc_base[i] | priv->phymem, + DESC_NUM_PER_CH); + } + + dev_dbg(priv->dev, "aca dma init done\n"); +} + +static void aca_basic_init(struct dc_ep_priv *priv) +{ + u32 addr, mask; + + /* Low 32 is RX, High 32 is TX */ + wr32(0x1, UMT_ORDER_CFG); + /* TXIN/TXOUT/RXIN/RXOUT All Controlled by Genrisc */ + wr32(0xF, HOST_TYPE); + /* Enable Host Gate CLK */ + wr32(0x4000, HT_GCLK_ENABLE); + /* Host Page/MASK */ + mask = ~priv->memsize + 1; + addr = mask | ((priv->phymem & mask) >> 16); + wr32(addr, AHB_ARB_HP_REG); + wr32(addr, OCP_ARB_ACC_PAGE_REG); + /* Stop all functions first */ + wr32(0, GNRC_EN_TASK_BITMAP); + + /* Enable XBAR */ + aca_xbar_ia_reject_clr(priv, ACA_ACC_IA04); + aca_xbar_ia_reject_clr(priv, ACA_M_IA06); + + dev_dbg(priv->dev, "aca basic config done\n"); +} + +static int aca_hif_param_init(struct dc_ep_priv *priv) +{ + struct dc_aca *aca = to_aca(priv); + + aca->hif_params = kzalloc(sizeof(struct aca_hif_params), GFP_KERNEL); + if (!aca->hif_params) + return -ENOMEM; + aca->hif_params->task_mask = 0x0000000F; + dev_dbg(priv->dev, "%s\n", __func__); + return 0; +} + +static void aca_hif_param_init_done(struct dc_ep_priv *priv) +{ + u32 addr; + struct aca_hif_params *hif_params = to_hif_params(priv); + struct aca_fw_param *fw_param = to_aca_fw_param(priv); + + /* wr32(ACA_HIF_PARAM_ADDR, ACA_HIF_LOC_POS);*/ + /* addr = rd32(ACA_HIF_LOC_POS);*/ + + addr = fw_param->init_addr; + dev_dbg(priv->dev, "init_addr: %x\n", addr); + memcpy_toio(priv->mem + addr, hif_params, sizeof(*hif_params)); + kzfree(hif_params); + dev_dbg(priv->dev, "%s\n", __func__); +} + +static bool aca_hif_param_init_check(struct dc_ep_priv *priv) +{ + u32 addr; + int timeout = ACA_LOOP_CNT; + u32 offset = offsetof(struct aca_hif_params, magic); + struct aca_fw_param *fw_param = to_aca_fw_param(priv); + + /* addr = rd32(ACA_HIF_LOC_POS);*/ + addr = fw_param->init_addr; + while (--timeout && (rd32(addr + offset) != ACA_MAGIC)) + udelay(1); + + if (timeout <= 0) { + dev_err(priv->dev, "aca hif params init failed\n"); + return false; + } + + return true; +} + +static void aca_txin_init(struct dc_ep_priv *priv, + struct aca_cfg_param *aca_txin) +{ + u32 val = 0; + struct aca_mem_layout *mem_layout = to_mem_layout(priv); + struct aca_hif_params *hif_params = to_hif_params(priv); + struct aca_hif_param *txin_param = &hif_params->txin; + + if (aca_txin->byteswap) + val = BYTE_SWAP_EN; + + val |= (aca_txin->hd_size_in_dw - 1) + | SM((aca_txin->pd_size_in_dw - 1), PD_DESC_IN_DW); + wr32(val, TXIN_CONV_CFG); + + /* SoC cumulative counter address */ + wr32(aca_txin->soc_cmlt_cnt_addr, GNRC_TXIN_CMLT_CNT_ADDR); + + + /* SoC descriptors */ + txin_param->soc_desc_base = aca_txin->soc_desc_base; + txin_param->soc_desc_num = aca_txin->soc_desc_num; + + /* Ping/pong buffer */ + txin_param->pp_buf_base = priv->phymem + + mem_layout->txin_host_desc_base; + + txin_param->pp_buf_num = mem_layout->txin_host_dnum; + + /* PD ring */ + txin_param->pd_desc_base = priv->phymem + + aca_txin->pd_desc_base; + txin_param->pd_desc_num = aca_txin->pd_desc_num; + + dev_dbg(priv->dev, "aca txin init done\n"); +} + +static void aca_txout_init(struct dc_ep_priv *priv, + struct aca_cfg_param *aca_txout) +{ + u32 val = 0; + struct aca_mem_layout *mem_layout = to_mem_layout(priv); + struct aca_hif_params *hif_params = to_hif_params(priv); + struct aca_hif_param *txout_param = &hif_params->txout; + + if (aca_txout->byteswap) + val = BYTE_SWAP_EN; + + val |= (aca_txout->hd_size_in_dw - 1) + | SM((aca_txout->pd_size_in_dw - 1), PD_DESC_IN_DW); + wr32(val, TXOUT_CONV_CFG); + + /* SoC Ring size */ + val = aca_txout->soc_desc_num; + wr32(val, TXOUT_RING_CFG); + + /* SoC cumulative counter address */ + wr32(aca_txout->soc_cmlt_cnt_addr, GNRC_TXOUT_CMLT_CNT_ADDR); + /* SoC descriptors */ + txout_param->soc_desc_base = aca_txout->soc_desc_base; + txout_param->soc_desc_num = aca_txout->soc_desc_num; + + /* Ping/pong buffer */ + txout_param->pp_buf_base = priv->phymem + +mem_layout->txout_host_desc_base; + + txout_param->pp_buf_num = mem_layout->txout_host_dnum; + + /* PD ring */ + txout_param->pd_desc_base = priv->phymem + + aca_txout->pd_desc_base; + txout_param->pd_desc_num = aca_txout->pd_desc_num; + + txout_param->pd_desc_threshold = aca_txout->pp_buf_desc_num; + + dev_dbg(priv->dev, "aca txout init done\n"); +} + +static void aca_rxin_init(struct dc_ep_priv *priv, + struct aca_cfg_param *aca_rxin) +{ + u32 val = 0; + struct aca_mem_layout *mem_layout = to_mem_layout(priv); + struct aca_hif_params *hif_params = to_hif_params(priv); + struct aca_hif_param *rxin_param = &hif_params->rxin; + + if (aca_rxin->byteswap) + val = BYTE_SWAP_EN; + + val |= (aca_rxin->hd_size_in_dw - 1) + | SM((aca_rxin->pd_size_in_dw - 1), PD_DESC_IN_DW); + wr32(val, RXIN_CONV_CFG); + + /* SoC cumulative counter address */ + wr32(aca_rxin->soc_cmlt_cnt_addr, GNRC_RXIN_CMLT_CNT_ADDR); + + /* RXIN may not be used */ + if (!(aca_rxin->soc_desc_base)) + goto __RXIN_DONE; + /* SoC descriptors */ + rxin_param->soc_desc_base = aca_rxin->soc_desc_base; + rxin_param->soc_desc_num = aca_rxin->soc_desc_num; + + /* Ping/pong buffer */ + rxin_param->pp_buf_base = (u32)priv->phymem + + mem_layout->rxin_host_desc_base; + + rxin_param->pp_buf_num = mem_layout->rxin_host_dnum; + + /* PD ring */ + rxin_param->pd_desc_base = (u32)priv->phymem + + aca_rxin->pd_desc_base; + rxin_param->pd_desc_num = aca_rxin->pd_desc_num; + + rxin_param->pd_desc_threshold = aca_rxin->pp_buf_desc_num; + +__RXIN_DONE: + dev_dbg(priv->dev, "aca rxin init done\n"); +} + +static void aca_rxout_init(struct dc_ep_priv *priv, + struct aca_cfg_param *aca_rxout) +{ + u32 val = 0; + struct aca_mem_layout *mem_layout = to_mem_layout(priv); + struct aca_hif_params *hif_params = to_hif_params(priv); + struct aca_hif_param *rxout_param = &hif_params->rxout; + + if (aca_rxout->byteswap) + val = BYTE_SWAP_EN; + + val |= (aca_rxout->hd_size_in_dw - 1) + | SM((aca_rxout->pd_size_in_dw - 1), PD_DESC_IN_DW); + wr32(val, RXOUT_CONV_CFG); + + /* SoC Ring size */ + val = aca_rxout->soc_desc_num; + wr32(val, RXOUT_RING_CFG); + + /* SoC cumulative counter address */ + wr32(aca_rxout->soc_cmlt_cnt_addr, GNRC_RXOUT_CMLT_CNT_ADDR); + /* SoC descriptors */ + rxout_param->soc_desc_base = aca_rxout->soc_desc_base; + rxout_param->soc_desc_num = aca_rxout->soc_desc_num; + + /* Ping/pong buffer */ + rxout_param->pp_buf_base = (u32)priv->phymem + + mem_layout->rxout_host_desc_base; + + rxout_param->pp_buf_num = mem_layout->rxout_host_dnum; + + /* PD ring */ + rxout_param->pd_desc_base = (u32)priv->phymem + + aca_rxout->pd_desc_base; + rxout_param->pd_desc_num = aca_rxout->pd_desc_num; + + rxout_param->pd_desc_threshold = aca_rxout->pp_buf_desc_num; + dev_dbg(priv->dev, "aca rxout init done\n"); +} + +static void aca_mdm_init(struct dc_ep_priv *priv, struct aca_modem_param *mdm) +{ + struct aca_proj_param *param; + + if (!mdm) + return; + + param = &mdm->mdm_txout; + wr32(param->stat | priv->phymem, GNRC_TXOUT_TGT_STAT); + wr32(param->pd | priv->phymem, GNRC_TXOUT_TGT_PD_OFF); + wr32(param->acc_cnt | priv->phymem, GNRC_TXOUT_TGT_ACCM_CNT); + + param = &mdm->mdm_rxin; + wr32(param->stat | priv->phymem, GNRC_RXIN_TGT_STAT); + wr32(param->pd | priv->phymem, GNRC_RXIN_TGT_PD_OFF); + wr32(param->acc_cnt | priv->phymem, GNRC_RXIN_TGT_ACCM_CNT); + + param = &mdm->mdm_rxout; + wr32(param->stat | priv->phymem, GNRC_RXOUT_TGT_STAT); + wr32(param->pd | priv->phymem, GNRC_RXOUT_TGT_PD_OFF); + wr32(param->acc_cnt | priv->phymem, GNRC_RXOUT_TGT_ACCM_CNT); + dev_dbg(priv->dev, "aca mdm init done\n"); +} + +static void dc_aca_clk_on(struct dc_ep_priv *priv) +{ + dc_ep_clk_on(priv, PMU_ADMA); +} + +static void dc_aca_clk_off(struct dc_ep_priv *priv) +{ + dc_ep_clk_off(priv, PMU_ADMA); +} + +static void dc_aca_reset(struct dc_ep_priv *priv) +{ + dc_ep_reset_device(priv, RST_ACA_DMA | RST_ACA_HOSTIF); +} + +static void aca_mem_clear(struct dc_ep_priv *priv) +{ + struct aca_fw_dl_addr *fw_dl = to_fw_addr(priv); + + memset_io(priv->mem + fw_dl->fw_addr[0].fw_load_addr, + 0, ACA_ACC_FW_SIZE); + memset_io(priv->mem + ACA_SRAM_BASE, 0, ACA_SRAM_SIZE); +} + +int dc_aca_start(struct dc_ep_priv *priv, u32 func, int start) +{ + if (!func) + return -EINVAL; + + wr32_mask(0, func, GNRC_EN_TASK_BITMAP); + + /* Only do if requested by caller */ + if (start) { + wr32(0x1, GNRC_START_OP); /* Any write will trigger */ + rd32(GNRC_START_OP); + if (!aca_hif_param_init_check(priv)) + return -EIO; + } + return 0; +} + +static void aca_sw_reset(struct dc_ep_priv *priv) +{ + u32 val = SW_RST_GENRISC | SW_RST_HOSTIF_REG | SW_RST_RXIN + | SW_RST_RXOUT | SW_RST_TXIN | SW_RST_TXOUT; + + wr32(val, HT_SW_RST_ASSRT); + udelay(1); + wr32(val, HT_SW_RST_RELEASE); + wmb(); +} + +int dc_aca_stop(struct dc_ep_priv *priv, u32 *func, int reset) +{ + u32 val = *func; + u32 reg; + + if (!val) + return 0; + + *func = 0; + + /* Only do it if reset is required. Otherwise, pending is fine */ + if (reset) { + if (val & ACA_TXIN_EN) { + reg = rd32(TXIN_COUNTERS); + if (MS(reg, ACA_PENDING_JOB) + || (MS(reg, ACA_AVAIL_BUF) != ACA_PP_BUFS)) { + *func = ACA_TXIN_EN; + return -EBUSY; + } + } + + if (val & ACA_TXOUT_EN) { + reg = rd32(TXOUT_COUNTERS); + if (MS(reg, ACA_PENDING_JOB) + || (MS(reg, ACA_AVAIL_BUF) != ACA_PP_BUFS)) { + *func = ACA_TXOUT_EN; + return -EBUSY; + } + } + + + if (val & ACA_RXIN_EN) { + reg = rd32(RXIN_COUNTERS); + if (MS(reg, ACA_PENDING_JOB) + || (MS(reg, ACA_AVAIL_BUF) != ACA_PP_BUFS)) { + *func = ACA_RXIN_EN; + return -EBUSY; + } + } + + if (val & ACA_RXOUT_EN) { + reg = rd32(RXOUT_COUNTERS); + if (MS(reg, ACA_PENDING_JOB) + || (MS(reg, ACA_AVAIL_BUF) != ACA_PP_BUFS)) { + *func = ACA_RXOUT_EN; + return -EBUSY; + } + } + } + + wr32_mask(val, 0, GNRC_EN_TASK_BITMAP); + + if (reset) { + aca_dma_ch_off(priv); + aca_xbar_ia_reject_set(priv, ACA_ACC_IA04); + aca_xbar_ia_reject_set(priv, ACA_M_IA06); + aca_sw_reset(priv); + } + return 0; +} + +#ifdef CONFIG_SOC_TYPE_XWAY +static void aca_grx330_init(struct dc_ep_priv *priv) +{ + wr32(0x0044001E, TXIN_CFG1); + wr32(0x0040041F, TXIN_CFG2); + wr32(0x007FE020, TXIN_CFG3); + + wr32(0x0044001F, TXOUT_CFG1); + wr32(0x0040041F, TXOUT_CFG2); + wr32(0x007BE020, TXOUT_CFG3); + + wr32(0x0044001F, RXOUT_CFG1); + wr32(0x0040041F, RXOUT_CFG2); + wr32(0x007BE020, RXOUT_CFG3); + + wr32(0x0044001E, RXIN_CFG1); + wr32(0x0040041F, RXIN_CFG2); + wr32(0x007FE020, RXIN_CFG3); + + wr32(0x1, TXIN_DST_OWWBIT_CFG4); + wr32(0x1, TXOUT_DST_OWWBIT_CFG4); + wr32(0x1, RXOUT_SRC_OWNBIT_CFG3); + wr32(0x1, RXIN_SRC_OWNBIT_CFG3); + + wr32(0x0, GNRC_TXIN_BUF_PREFILL); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0x4); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0x8); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0xc); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0x10); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0x14); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0x18); + wr32(0x0, GNRC_TXIN_BUF_PREFILL + 0x1c); +} +#endif + +int dc_aca_init(struct dc_ep_priv *priv, struct aca_param *param, + struct aca_modem_param *mdm) +{ + int ret; + struct dc_aca *aca = to_aca(priv); + + dc_aca_clk_on(priv); + dc_aca_reset(priv); + + ret = aca_fetch_fw(priv); + if (ret) { + dev_err(priv->dev, + "could not fetch firmware files %d\n", ret); + dc_aca_clk_off(priv); + return ret; + } + + aca_mem_clear(priv); + aca_dma_init(priv); + aca_basic_init(priv); + aca_fw_download(priv); + aca_hif_param_init(priv); + aca_txin_init(priv, ¶m->aca_txin); + aca_txout_init(priv, ¶m->aca_txout); + aca_rxout_init(priv, ¶m->aca_rxout); + aca_rxin_init(priv, ¶m->aca_rxin); + aca_hif_param_init_done(priv); + aca_mdm_init(priv, mdm); +#ifdef CONFIG_SOC_TYPE_XWAY + aca_grx330_init(priv); +#endif + aca->initialized = true; + dev_info(priv->dev, "aca init done\n"); + return 0; +} + +static int aca_max_gpio(struct dc_ep_priv *priv) +{ + return fls(rd32(PADC_AVAIL)); +} + +void dc_aca_info_init(struct dc_ep_priv *priv) +{ + struct dc_aca *aca = to_aca(priv); + + aca->initialized = false; + spin_lock_init(&aca->clk_lock); + spin_lock_init(&aca->rcu_lock); + mutex_init(&aca->pin_lock); + aca->max_gpio = aca_max_gpio(priv); +} + +#define ACA_ENDIAN_ADDR(addr, endian) \ +{ \ + if (endian == ACA_BIG_ENDIAN) \ + return addr##_BE; \ + else \ + return addr; \ +} + +u32 aca_umt_msg_addr(struct dc_ep_priv *priv, u32 endian, u32 type) +{ + switch (type) { + case ACA_TXIN: + ACA_ENDIAN_ADDR(TXIN_HD_ACCUM_ADD, endian); + case ACA_RXIN: + ACA_ENDIAN_ADDR(RXIN_HD_ACCUM_ADD, endian); + case ACA_TXOUT: + ACA_ENDIAN_ADDR(TXOUT_HD_ACCUM_SUB, endian); + case ACA_RXOUT: + ACA_ENDIAN_ADDR(RXOUT_HD_ACCUM_SUB, endian); + default: + ACA_ENDIAN_ADDR(RXIN_HD_ACCUM_ADD, endian); + }; +} + +void dc_aca_event_addr_get(struct dc_ep_priv *priv, + struct aca_event_reg_addr *regs) +{ + regs->txin_acc_sub = TXIN_ACA_ACCUM_SUB; + regs->txout_acc_add = TXOUT_ACA_ACCUM_ADD; + regs->rxin_acc_sub = RXIN_ACA_ACCUM_SUB; + regs->rxout_acc_add = RXOUT_ACA_ACCUM_ADD; +} + +void dc_aca_txin_sub_ack(struct dc_ep_priv *priv, u32 val) +{ + wr32(val, TXIN_ACA_ACCUM_SUB); +} + +u32 dc_aca_txin_hd_cnt(struct dc_ep_priv *priv) +{ + return rd32(TXIN_ACA_HD_ACC_CNT); +} + |