18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* OMAP SSI port driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Sebastian Reichel <sre@kernel.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Carlos Chinea <carlos.chinea@nokia.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "omap_ssi_regs.h" 218c2ecf20Sopenharmony_ci#include "omap_ssi.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci return 0; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return 0; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic inline unsigned int ssi_wakein(struct hsi_port *port) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 368c2ecf20Sopenharmony_ci return gpiod_get_value(omap_port->wake_gpio); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 408c2ecf20Sopenharmony_cistatic void ssi_debug_remove_port(struct hsi_port *port) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci debugfs_remove_recursive(omap_port->dir); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int ssi_port_regs_show(struct seq_file *m, void *p __maybe_unused) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct hsi_port *port = m->private; 508c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 518c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 528c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 538c2ecf20Sopenharmony_ci void __iomem *base = omap_ssi->sys; 548c2ecf20Sopenharmony_ci unsigned int ch; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 578c2ecf20Sopenharmony_ci if (omap_port->wake_irq > 0) 588c2ecf20Sopenharmony_ci seq_printf(m, "CAWAKE\t\t: %d\n", ssi_wakein(port)); 598c2ecf20Sopenharmony_ci seq_printf(m, "WAKE\t\t: 0x%08x\n", 608c2ecf20Sopenharmony_ci readl(base + SSI_WAKE_REG(port->num))); 618c2ecf20Sopenharmony_ci seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", 0, 628c2ecf20Sopenharmony_ci readl(base + SSI_MPU_ENABLE_REG(port->num, 0))); 638c2ecf20Sopenharmony_ci seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", 0, 648c2ecf20Sopenharmony_ci readl(base + SSI_MPU_STATUS_REG(port->num, 0))); 658c2ecf20Sopenharmony_ci /* SST */ 668c2ecf20Sopenharmony_ci base = omap_port->sst_base; 678c2ecf20Sopenharmony_ci seq_puts(m, "\nSST\n===\n"); 688c2ecf20Sopenharmony_ci seq_printf(m, "ID SST\t\t: 0x%08x\n", 698c2ecf20Sopenharmony_ci readl(base + SSI_SST_ID_REG)); 708c2ecf20Sopenharmony_ci seq_printf(m, "MODE\t\t: 0x%08x\n", 718c2ecf20Sopenharmony_ci readl(base + SSI_SST_MODE_REG)); 728c2ecf20Sopenharmony_ci seq_printf(m, "FRAMESIZE\t: 0x%08x\n", 738c2ecf20Sopenharmony_ci readl(base + SSI_SST_FRAMESIZE_REG)); 748c2ecf20Sopenharmony_ci seq_printf(m, "DIVISOR\t\t: 0x%08x\n", 758c2ecf20Sopenharmony_ci readl(base + SSI_SST_DIVISOR_REG)); 768c2ecf20Sopenharmony_ci seq_printf(m, "CHANNELS\t: 0x%08x\n", 778c2ecf20Sopenharmony_ci readl(base + SSI_SST_CHANNELS_REG)); 788c2ecf20Sopenharmony_ci seq_printf(m, "ARBMODE\t\t: 0x%08x\n", 798c2ecf20Sopenharmony_ci readl(base + SSI_SST_ARBMODE_REG)); 808c2ecf20Sopenharmony_ci seq_printf(m, "TXSTATE\t\t: 0x%08x\n", 818c2ecf20Sopenharmony_ci readl(base + SSI_SST_TXSTATE_REG)); 828c2ecf20Sopenharmony_ci seq_printf(m, "BUFSTATE\t: 0x%08x\n", 838c2ecf20Sopenharmony_ci readl(base + SSI_SST_BUFSTATE_REG)); 848c2ecf20Sopenharmony_ci seq_printf(m, "BREAK\t\t: 0x%08x\n", 858c2ecf20Sopenharmony_ci readl(base + SSI_SST_BREAK_REG)); 868c2ecf20Sopenharmony_ci for (ch = 0; ch < omap_port->channels; ch++) { 878c2ecf20Sopenharmony_ci seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, 888c2ecf20Sopenharmony_ci readl(base + SSI_SST_BUFFER_CH_REG(ch))); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci /* SSR */ 918c2ecf20Sopenharmony_ci base = omap_port->ssr_base; 928c2ecf20Sopenharmony_ci seq_puts(m, "\nSSR\n===\n"); 938c2ecf20Sopenharmony_ci seq_printf(m, "ID SSR\t\t: 0x%08x\n", 948c2ecf20Sopenharmony_ci readl(base + SSI_SSR_ID_REG)); 958c2ecf20Sopenharmony_ci seq_printf(m, "MODE\t\t: 0x%08x\n", 968c2ecf20Sopenharmony_ci readl(base + SSI_SSR_MODE_REG)); 978c2ecf20Sopenharmony_ci seq_printf(m, "FRAMESIZE\t: 0x%08x\n", 988c2ecf20Sopenharmony_ci readl(base + SSI_SSR_FRAMESIZE_REG)); 998c2ecf20Sopenharmony_ci seq_printf(m, "CHANNELS\t: 0x%08x\n", 1008c2ecf20Sopenharmony_ci readl(base + SSI_SSR_CHANNELS_REG)); 1018c2ecf20Sopenharmony_ci seq_printf(m, "TIMEOUT\t\t: 0x%08x\n", 1028c2ecf20Sopenharmony_ci readl(base + SSI_SSR_TIMEOUT_REG)); 1038c2ecf20Sopenharmony_ci seq_printf(m, "RXSTATE\t\t: 0x%08x\n", 1048c2ecf20Sopenharmony_ci readl(base + SSI_SSR_RXSTATE_REG)); 1058c2ecf20Sopenharmony_ci seq_printf(m, "BUFSTATE\t: 0x%08x\n", 1068c2ecf20Sopenharmony_ci readl(base + SSI_SSR_BUFSTATE_REG)); 1078c2ecf20Sopenharmony_ci seq_printf(m, "BREAK\t\t: 0x%08x\n", 1088c2ecf20Sopenharmony_ci readl(base + SSI_SSR_BREAK_REG)); 1098c2ecf20Sopenharmony_ci seq_printf(m, "ERROR\t\t: 0x%08x\n", 1108c2ecf20Sopenharmony_ci readl(base + SSI_SSR_ERROR_REG)); 1118c2ecf20Sopenharmony_ci seq_printf(m, "ERRORACK\t: 0x%08x\n", 1128c2ecf20Sopenharmony_ci readl(base + SSI_SSR_ERRORACK_REG)); 1138c2ecf20Sopenharmony_ci for (ch = 0; ch < omap_port->channels; ch++) { 1148c2ecf20Sopenharmony_ci seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, 1158c2ecf20Sopenharmony_ci readl(base + SSI_SSR_BUFFER_CH_REG(ch))); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ssi_port_regs); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int ssi_div_get(void *data, u64 *val) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct hsi_port *port = data; 1278c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 1308c2ecf20Sopenharmony_ci *val = readl(omap_port->sst_base + SSI_SST_DIVISOR_REG); 1318c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int ssi_div_set(void *data, u64 val) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct hsi_port *port = data; 1398c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (val > 127) 1428c2ecf20Sopenharmony_ci return -EINVAL; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 1458c2ecf20Sopenharmony_ci writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG); 1468c2ecf20Sopenharmony_ci omap_port->sst.divisor = val; 1478c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n"); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int ssi_debug_add_port(struct omap_ssi_port *omap_port, 1558c2ecf20Sopenharmony_ci struct dentry *dir) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct hsi_port *port = to_hsi_port(omap_port->dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci dir = debugfs_create_dir(dev_name(omap_port->dev), dir); 1608c2ecf20Sopenharmony_ci if (!dir) 1618c2ecf20Sopenharmony_ci return -ENOMEM; 1628c2ecf20Sopenharmony_ci omap_port->dir = dir; 1638c2ecf20Sopenharmony_ci debugfs_create_file("regs", S_IRUGO, dir, port, &ssi_port_regs_fops); 1648c2ecf20Sopenharmony_ci dir = debugfs_create_dir("sst", dir); 1658c2ecf20Sopenharmony_ci if (!dir) 1668c2ecf20Sopenharmony_ci return -ENOMEM; 1678c2ecf20Sopenharmony_ci debugfs_create_file_unsafe("divisor", 0644, dir, port, 1688c2ecf20Sopenharmony_ci &ssi_sst_div_fops); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci#endif 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void ssi_process_errqueue(struct work_struct *work) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port; 1778c2ecf20Sopenharmony_ci struct list_head *head, *tmp; 1788c2ecf20Sopenharmony_ci struct hsi_msg *msg; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci omap_port = container_of(work, struct omap_ssi_port, errqueue_work.work); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci list_for_each_safe(head, tmp, &omap_port->errqueue) { 1838c2ecf20Sopenharmony_ci msg = list_entry(head, struct hsi_msg, link); 1848c2ecf20Sopenharmony_ci msg->complete(msg); 1858c2ecf20Sopenharmony_ci list_del(head); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int ssi_claim_lch(struct hsi_msg *msg) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(msg->cl); 1938c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 1948c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 1958c2ecf20Sopenharmony_ci int lch; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) 1988c2ecf20Sopenharmony_ci if (!omap_ssi->gdd_trn[lch].msg) { 1998c2ecf20Sopenharmony_ci omap_ssi->gdd_trn[lch].msg = msg; 2008c2ecf20Sopenharmony_ci omap_ssi->gdd_trn[lch].sg = msg->sgt.sgl; 2018c2ecf20Sopenharmony_ci return lch; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return -EBUSY; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int ssi_start_dma(struct hsi_msg *msg, int lch) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(msg->cl); 2108c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 2118c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 2128c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 2138c2ecf20Sopenharmony_ci void __iomem *gdd = omap_ssi->gdd; 2148c2ecf20Sopenharmony_ci int err; 2158c2ecf20Sopenharmony_ci u16 csdp; 2168c2ecf20Sopenharmony_ci u16 ccr; 2178c2ecf20Sopenharmony_ci u32 s_addr; 2188c2ecf20Sopenharmony_ci u32 d_addr; 2198c2ecf20Sopenharmony_ci u32 tmp; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Hold clocks during the transfer */ 2228c2ecf20Sopenharmony_ci pm_runtime_get(omap_port->pdev); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (!pm_runtime_active(omap_port->pdev)) { 2258c2ecf20Sopenharmony_ci dev_warn(&port->device, "ssi_start_dma called without runtime PM!\n"); 2268c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 2278c2ecf20Sopenharmony_ci return -EREMOTEIO; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_READ) { 2318c2ecf20Sopenharmony_ci err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, 2328c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2338c2ecf20Sopenharmony_ci if (!err) { 2348c2ecf20Sopenharmony_ci dev_dbg(&ssi->device, "DMA map SG failed !\n"); 2358c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 2368c2ecf20Sopenharmony_ci return -EIO; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT | 2398c2ecf20Sopenharmony_ci SSI_SRC_SINGLE_ACCESS0 | SSI_SRC_PERIPHERAL_PORT | 2408c2ecf20Sopenharmony_ci SSI_DATA_TYPE_S32; 2418c2ecf20Sopenharmony_ci ccr = msg->channel + 0x10 + (port->num * 8); /* Sync */ 2428c2ecf20Sopenharmony_ci ccr |= SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST | 2438c2ecf20Sopenharmony_ci SSI_CCR_ENABLE; 2448c2ecf20Sopenharmony_ci s_addr = omap_port->ssr_dma + 2458c2ecf20Sopenharmony_ci SSI_SSR_BUFFER_CH_REG(msg->channel); 2468c2ecf20Sopenharmony_ci d_addr = sg_dma_address(msg->sgt.sgl); 2478c2ecf20Sopenharmony_ci } else { 2488c2ecf20Sopenharmony_ci err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, 2498c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2508c2ecf20Sopenharmony_ci if (!err) { 2518c2ecf20Sopenharmony_ci dev_dbg(&ssi->device, "DMA map SG failed !\n"); 2528c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 2538c2ecf20Sopenharmony_ci return -EIO; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT | 2568c2ecf20Sopenharmony_ci SSI_DST_SINGLE_ACCESS0 | SSI_DST_PERIPHERAL_PORT | 2578c2ecf20Sopenharmony_ci SSI_DATA_TYPE_S32; 2588c2ecf20Sopenharmony_ci ccr = (msg->channel + 1 + (port->num * 8)) & 0xf; /* Sync */ 2598c2ecf20Sopenharmony_ci ccr |= SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST | 2608c2ecf20Sopenharmony_ci SSI_CCR_ENABLE; 2618c2ecf20Sopenharmony_ci s_addr = sg_dma_address(msg->sgt.sgl); 2628c2ecf20Sopenharmony_ci d_addr = omap_port->sst_dma + 2638c2ecf20Sopenharmony_ci SSI_SST_BUFFER_CH_REG(msg->channel); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n", 2668c2ecf20Sopenharmony_ci lch, csdp, ccr, s_addr, d_addr); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci writew_relaxed(csdp, gdd + SSI_GDD_CSDP_REG(lch)); 2698c2ecf20Sopenharmony_ci writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch)); 2708c2ecf20Sopenharmony_ci writel_relaxed(d_addr, gdd + SSI_GDD_CDSA_REG(lch)); 2718c2ecf20Sopenharmony_ci writel_relaxed(s_addr, gdd + SSI_GDD_CSSA_REG(lch)); 2728c2ecf20Sopenharmony_ci writew_relaxed(SSI_BYTES_TO_FRAMES(msg->sgt.sgl->length), 2738c2ecf20Sopenharmony_ci gdd + SSI_GDD_CEN_REG(lch)); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci spin_lock_bh(&omap_ssi->lock); 2768c2ecf20Sopenharmony_ci tmp = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 2778c2ecf20Sopenharmony_ci tmp |= SSI_GDD_LCH(lch); 2788c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 2798c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_ssi->lock); 2808c2ecf20Sopenharmony_ci writew(ccr, gdd + SSI_GDD_CCR_REG(lch)); 2818c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_PROCEEDING; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int ssi_start_pio(struct hsi_msg *msg) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(msg->cl); 2898c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 2908c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 2918c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 2928c2ecf20Sopenharmony_ci u32 val; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci pm_runtime_get(omap_port->pdev); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!pm_runtime_active(omap_port->pdev)) { 2978c2ecf20Sopenharmony_ci dev_warn(&port->device, "ssi_start_pio called without runtime PM!\n"); 2988c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 2998c2ecf20Sopenharmony_ci return -EREMOTEIO; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_WRITE) { 3038c2ecf20Sopenharmony_ci val = SSI_DATAACCEPT(msg->channel); 3048c2ecf20Sopenharmony_ci /* Hold clocks for pio writes */ 3058c2ecf20Sopenharmony_ci pm_runtime_get(omap_port->pdev); 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci dev_dbg(&port->device, "Single %s transfer\n", 3108c2ecf20Sopenharmony_ci msg->ttype ? "write" : "read"); 3118c2ecf20Sopenharmony_ci val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 3128c2ecf20Sopenharmony_ci writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 3138c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 3148c2ecf20Sopenharmony_ci msg->actual_len = 0; 3158c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_PROCEEDING; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int ssi_start_transfer(struct list_head *queue) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct hsi_msg *msg; 3238c2ecf20Sopenharmony_ci int lch = -1; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (list_empty(queue)) 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci msg = list_first_entry(queue, struct hsi_msg, link); 3288c2ecf20Sopenharmony_ci if (msg->status != HSI_STATUS_QUEUED) 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci if ((msg->sgt.nents) && (msg->sgt.sgl->length > sizeof(u32))) 3318c2ecf20Sopenharmony_ci lch = ssi_claim_lch(msg); 3328c2ecf20Sopenharmony_ci if (lch >= 0) 3338c2ecf20Sopenharmony_ci return ssi_start_dma(msg, lch); 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci return ssi_start_pio(msg); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int ssi_async_break(struct hsi_msg *msg) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(msg->cl); 3418c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 3428c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 3438c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 3448c2ecf20Sopenharmony_ci int err = 0; 3458c2ecf20Sopenharmony_ci u32 tmp; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 3488c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_WRITE) { 3498c2ecf20Sopenharmony_ci if (omap_port->sst.mode != SSI_MODE_FRAME) { 3508c2ecf20Sopenharmony_ci err = -EINVAL; 3518c2ecf20Sopenharmony_ci goto out; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci writel(1, omap_port->sst_base + SSI_SST_BREAK_REG); 3548c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_COMPLETED; 3558c2ecf20Sopenharmony_ci msg->complete(msg); 3568c2ecf20Sopenharmony_ci } else { 3578c2ecf20Sopenharmony_ci if (omap_port->ssr.mode != SSI_MODE_FRAME) { 3588c2ecf20Sopenharmony_ci err = -EINVAL; 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 3628c2ecf20Sopenharmony_ci tmp = readl(omap_ssi->sys + 3638c2ecf20Sopenharmony_ci SSI_MPU_ENABLE_REG(port->num, 0)); 3648c2ecf20Sopenharmony_ci writel(tmp | SSI_BREAKDETECTED, 3658c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 3668c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_PROCEEDING; 3678c2ecf20Sopenharmony_ci list_add_tail(&msg->link, &omap_port->brkqueue); 3688c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ciout: 3718c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 3728c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return err; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int ssi_async(struct hsi_msg *msg) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(msg->cl); 3808c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 3818c2ecf20Sopenharmony_ci struct list_head *queue; 3828c2ecf20Sopenharmony_ci int err = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci BUG_ON(!msg); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (msg->sgt.nents > 1) 3878c2ecf20Sopenharmony_ci return -ENOSYS; /* TODO: Add sg support */ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (msg->break_frame) 3908c2ecf20Sopenharmony_ci return ssi_async_break(msg); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (msg->ttype) { 3938c2ecf20Sopenharmony_ci BUG_ON(msg->channel >= omap_port->sst.channels); 3948c2ecf20Sopenharmony_ci queue = &omap_port->txqueue[msg->channel]; 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci BUG_ON(msg->channel >= omap_port->ssr.channels); 3978c2ecf20Sopenharmony_ci queue = &omap_port->rxqueue[msg->channel]; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_QUEUED; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 4028c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 4038c2ecf20Sopenharmony_ci list_add_tail(&msg->link, queue); 4048c2ecf20Sopenharmony_ci err = ssi_start_transfer(queue); 4058c2ecf20Sopenharmony_ci if (err < 0) { 4068c2ecf20Sopenharmony_ci list_del(&msg->link); 4078c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_ERROR; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 4108c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 4118c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 4128c2ecf20Sopenharmony_ci dev_dbg(&port->device, "msg status %d ttype %d ch %d\n", 4138c2ecf20Sopenharmony_ci msg->status, msg->ttype, msg->channel); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return err; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic u32 ssi_calculate_div(struct hsi_controller *ssi) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 4218c2ecf20Sopenharmony_ci u32 tx_fckrate = (u32) omap_ssi->fck_rate; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* / 2 : SSI TX clock is always half of the SSI functional clock */ 4248c2ecf20Sopenharmony_ci tx_fckrate >>= 1; 4258c2ecf20Sopenharmony_ci /* Round down when tx_fckrate % omap_ssi->max_speed == 0 */ 4268c2ecf20Sopenharmony_ci tx_fckrate--; 4278c2ecf20Sopenharmony_ci dev_dbg(&ssi->device, "TX div %d for fck_rate %lu Khz speed %d Kb/s\n", 4288c2ecf20Sopenharmony_ci tx_fckrate / omap_ssi->max_speed, omap_ssi->fck_rate, 4298c2ecf20Sopenharmony_ci omap_ssi->max_speed); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return tx_fckrate / omap_ssi->max_speed; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void ssi_flush_queue(struct list_head *queue, struct hsi_client *cl) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct list_head *node, *tmp; 4378c2ecf20Sopenharmony_ci struct hsi_msg *msg; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci list_for_each_safe(node, tmp, queue) { 4408c2ecf20Sopenharmony_ci msg = list_entry(node, struct hsi_msg, link); 4418c2ecf20Sopenharmony_ci if ((cl) && (cl != msg->cl)) 4428c2ecf20Sopenharmony_ci continue; 4438c2ecf20Sopenharmony_ci list_del(node); 4448c2ecf20Sopenharmony_ci pr_debug("flush queue: ch %d, msg %p len %d type %d ctxt %p\n", 4458c2ecf20Sopenharmony_ci msg->channel, msg, msg->sgt.sgl->length, 4468c2ecf20Sopenharmony_ci msg->ttype, msg->context); 4478c2ecf20Sopenharmony_ci if (msg->destructor) 4488c2ecf20Sopenharmony_ci msg->destructor(msg); 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci hsi_free_msg(msg); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int ssi_setup(struct hsi_client *cl) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct hsi_port *port = to_hsi_port(cl->device.parent); 4578c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 4588c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 4598c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 4608c2ecf20Sopenharmony_ci void __iomem *sst = omap_port->sst_base; 4618c2ecf20Sopenharmony_ci void __iomem *ssr = omap_port->ssr_base; 4628c2ecf20Sopenharmony_ci u32 div; 4638c2ecf20Sopenharmony_ci u32 val; 4648c2ecf20Sopenharmony_ci int err = 0; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 4678c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 4688c2ecf20Sopenharmony_ci if (cl->tx_cfg.speed) 4698c2ecf20Sopenharmony_ci omap_ssi->max_speed = cl->tx_cfg.speed; 4708c2ecf20Sopenharmony_ci div = ssi_calculate_div(ssi); 4718c2ecf20Sopenharmony_ci if (div > SSI_MAX_DIVISOR) { 4728c2ecf20Sopenharmony_ci dev_err(&cl->device, "Invalid TX speed %d Mb/s (div %d)\n", 4738c2ecf20Sopenharmony_ci cl->tx_cfg.speed, div); 4748c2ecf20Sopenharmony_ci err = -EINVAL; 4758c2ecf20Sopenharmony_ci goto out; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci /* Set TX/RX module to sleep to stop TX/RX during cfg update */ 4788c2ecf20Sopenharmony_ci writel_relaxed(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG); 4798c2ecf20Sopenharmony_ci writel_relaxed(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG); 4808c2ecf20Sopenharmony_ci /* Flush posted write */ 4818c2ecf20Sopenharmony_ci val = readl(ssr + SSI_SSR_MODE_REG); 4828c2ecf20Sopenharmony_ci /* TX */ 4838c2ecf20Sopenharmony_ci writel_relaxed(31, sst + SSI_SST_FRAMESIZE_REG); 4848c2ecf20Sopenharmony_ci writel_relaxed(div, sst + SSI_SST_DIVISOR_REG); 4858c2ecf20Sopenharmony_ci writel_relaxed(cl->tx_cfg.num_hw_channels, sst + SSI_SST_CHANNELS_REG); 4868c2ecf20Sopenharmony_ci writel_relaxed(cl->tx_cfg.arb_mode, sst + SSI_SST_ARBMODE_REG); 4878c2ecf20Sopenharmony_ci writel_relaxed(cl->tx_cfg.mode, sst + SSI_SST_MODE_REG); 4888c2ecf20Sopenharmony_ci /* RX */ 4898c2ecf20Sopenharmony_ci writel_relaxed(31, ssr + SSI_SSR_FRAMESIZE_REG); 4908c2ecf20Sopenharmony_ci writel_relaxed(cl->rx_cfg.num_hw_channels, ssr + SSI_SSR_CHANNELS_REG); 4918c2ecf20Sopenharmony_ci writel_relaxed(0, ssr + SSI_SSR_TIMEOUT_REG); 4928c2ecf20Sopenharmony_ci /* Cleanup the break queue if we leave FRAME mode */ 4938c2ecf20Sopenharmony_ci if ((omap_port->ssr.mode == SSI_MODE_FRAME) && 4948c2ecf20Sopenharmony_ci (cl->rx_cfg.mode != SSI_MODE_FRAME)) 4958c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->brkqueue, cl); 4968c2ecf20Sopenharmony_ci writel_relaxed(cl->rx_cfg.mode, ssr + SSI_SSR_MODE_REG); 4978c2ecf20Sopenharmony_ci omap_port->channels = max(cl->rx_cfg.num_hw_channels, 4988c2ecf20Sopenharmony_ci cl->tx_cfg.num_hw_channels); 4998c2ecf20Sopenharmony_ci /* Shadow registering for OFF mode */ 5008c2ecf20Sopenharmony_ci /* SST */ 5018c2ecf20Sopenharmony_ci omap_port->sst.divisor = div; 5028c2ecf20Sopenharmony_ci omap_port->sst.frame_size = 31; 5038c2ecf20Sopenharmony_ci omap_port->sst.channels = cl->tx_cfg.num_hw_channels; 5048c2ecf20Sopenharmony_ci omap_port->sst.arb_mode = cl->tx_cfg.arb_mode; 5058c2ecf20Sopenharmony_ci omap_port->sst.mode = cl->tx_cfg.mode; 5068c2ecf20Sopenharmony_ci /* SSR */ 5078c2ecf20Sopenharmony_ci omap_port->ssr.frame_size = 31; 5088c2ecf20Sopenharmony_ci omap_port->ssr.timeout = 0; 5098c2ecf20Sopenharmony_ci omap_port->ssr.channels = cl->rx_cfg.num_hw_channels; 5108c2ecf20Sopenharmony_ci omap_port->ssr.mode = cl->rx_cfg.mode; 5118c2ecf20Sopenharmony_ciout: 5128c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 5138c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 5148c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return err; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic int ssi_flush(struct hsi_client *cl) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(cl); 5228c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 5238c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 5248c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 5258c2ecf20Sopenharmony_ci struct hsi_msg *msg; 5268c2ecf20Sopenharmony_ci void __iomem *sst = omap_port->sst_base; 5278c2ecf20Sopenharmony_ci void __iomem *ssr = omap_port->ssr_base; 5288c2ecf20Sopenharmony_ci unsigned int i; 5298c2ecf20Sopenharmony_ci u32 err; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 5328c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* stop all ssi communication */ 5358c2ecf20Sopenharmony_ci pinctrl_pm_select_idle_state(omap_port->pdev); 5368c2ecf20Sopenharmony_ci udelay(1); /* wait for racing frames */ 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Stop all DMA transfers */ 5398c2ecf20Sopenharmony_ci for (i = 0; i < SSI_MAX_GDD_LCH; i++) { 5408c2ecf20Sopenharmony_ci msg = omap_ssi->gdd_trn[i].msg; 5418c2ecf20Sopenharmony_ci if (!msg || (port != hsi_get_port(msg->cl))) 5428c2ecf20Sopenharmony_ci continue; 5438c2ecf20Sopenharmony_ci writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); 5448c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_READ) 5458c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 5468c2ecf20Sopenharmony_ci omap_ssi->gdd_trn[i].msg = NULL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci /* Flush all SST buffers */ 5498c2ecf20Sopenharmony_ci writel_relaxed(0, sst + SSI_SST_BUFSTATE_REG); 5508c2ecf20Sopenharmony_ci writel_relaxed(0, sst + SSI_SST_TXSTATE_REG); 5518c2ecf20Sopenharmony_ci /* Flush all SSR buffers */ 5528c2ecf20Sopenharmony_ci writel_relaxed(0, ssr + SSI_SSR_RXSTATE_REG); 5538c2ecf20Sopenharmony_ci writel_relaxed(0, ssr + SSI_SSR_BUFSTATE_REG); 5548c2ecf20Sopenharmony_ci /* Flush all errors */ 5558c2ecf20Sopenharmony_ci err = readl(ssr + SSI_SSR_ERROR_REG); 5568c2ecf20Sopenharmony_ci writel_relaxed(err, ssr + SSI_SSR_ERRORACK_REG); 5578c2ecf20Sopenharmony_ci /* Flush break */ 5588c2ecf20Sopenharmony_ci writel_relaxed(0, ssr + SSI_SSR_BREAK_REG); 5598c2ecf20Sopenharmony_ci /* Clear interrupts */ 5608c2ecf20Sopenharmony_ci writel_relaxed(0, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 5618c2ecf20Sopenharmony_ci writel_relaxed(0xffffff00, 5628c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 5638c2ecf20Sopenharmony_ci writel_relaxed(0, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 5648c2ecf20Sopenharmony_ci writel(0xff, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG); 5658c2ecf20Sopenharmony_ci /* Dequeue all pending requests */ 5668c2ecf20Sopenharmony_ci for (i = 0; i < omap_port->channels; i++) { 5678c2ecf20Sopenharmony_ci /* Release write clocks */ 5688c2ecf20Sopenharmony_ci if (!list_empty(&omap_port->txqueue[i])) 5698c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 5708c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->txqueue[i], NULL); 5718c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->rxqueue[i], NULL); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->brkqueue, NULL); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Resume SSI communication */ 5768c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(omap_port->pdev); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 5798c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 5808c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic void start_tx_work(struct work_struct *work) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = 5888c2ecf20Sopenharmony_ci container_of(work, struct omap_ssi_port, work); 5898c2ecf20Sopenharmony_ci struct hsi_port *port = to_hsi_port(omap_port->dev); 5908c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 5918c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */ 5948c2ecf20Sopenharmony_ci writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int ssi_start_tx(struct hsi_client *cl) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(cl); 6008c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->wk_lock); 6058c2ecf20Sopenharmony_ci if (omap_port->wk_refcount++) { 6068c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->wk_lock); 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->wk_lock); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci schedule_work(&omap_port->work); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int ssi_stop_tx(struct hsi_client *cl) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(cl); 6198c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 6208c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 6218c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci dev_dbg(&port->device, "Wake out low %d\n", omap_port->wk_refcount); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->wk_lock); 6268c2ecf20Sopenharmony_ci BUG_ON(!omap_port->wk_refcount); 6278c2ecf20Sopenharmony_ci if (--omap_port->wk_refcount) { 6288c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->wk_lock); 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); 6328c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->wk_lock); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 6358c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); /* Release clocks */ 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic void ssi_transfer(struct omap_ssi_port *omap_port, 6428c2ecf20Sopenharmony_ci struct list_head *queue) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct hsi_msg *msg; 6458c2ecf20Sopenharmony_ci int err = -1; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci pm_runtime_get(omap_port->pdev); 6488c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 6498c2ecf20Sopenharmony_ci while (err < 0) { 6508c2ecf20Sopenharmony_ci err = ssi_start_transfer(queue); 6518c2ecf20Sopenharmony_ci if (err < 0) { 6528c2ecf20Sopenharmony_ci msg = list_first_entry(queue, struct hsi_msg, link); 6538c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_ERROR; 6548c2ecf20Sopenharmony_ci msg->actual_len = 0; 6558c2ecf20Sopenharmony_ci list_del(&msg->link); 6568c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 6578c2ecf20Sopenharmony_ci msg->complete(msg); 6588c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 6628c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 6638c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic void ssi_cleanup_queues(struct hsi_client *cl) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(cl); 6698c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 6708c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 6718c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 6728c2ecf20Sopenharmony_ci struct hsi_msg *msg; 6738c2ecf20Sopenharmony_ci unsigned int i; 6748c2ecf20Sopenharmony_ci u32 rxbufstate = 0; 6758c2ecf20Sopenharmony_ci u32 txbufstate = 0; 6768c2ecf20Sopenharmony_ci u32 status = SSI_ERROROCCURED; 6778c2ecf20Sopenharmony_ci u32 tmp; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->brkqueue, cl); 6808c2ecf20Sopenharmony_ci if (list_empty(&omap_port->brkqueue)) 6818c2ecf20Sopenharmony_ci status |= SSI_BREAKDETECTED; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci for (i = 0; i < omap_port->channels; i++) { 6848c2ecf20Sopenharmony_ci if (list_empty(&omap_port->txqueue[i])) 6858c2ecf20Sopenharmony_ci continue; 6868c2ecf20Sopenharmony_ci msg = list_first_entry(&omap_port->txqueue[i], struct hsi_msg, 6878c2ecf20Sopenharmony_ci link); 6888c2ecf20Sopenharmony_ci if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) { 6898c2ecf20Sopenharmony_ci txbufstate |= (1 << i); 6908c2ecf20Sopenharmony_ci status |= SSI_DATAACCEPT(i); 6918c2ecf20Sopenharmony_ci /* Release the clocks writes, also GDD ones */ 6928c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 6938c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->txqueue[i], cl); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci for (i = 0; i < omap_port->channels; i++) { 6988c2ecf20Sopenharmony_ci if (list_empty(&omap_port->rxqueue[i])) 6998c2ecf20Sopenharmony_ci continue; 7008c2ecf20Sopenharmony_ci msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg, 7018c2ecf20Sopenharmony_ci link); 7028c2ecf20Sopenharmony_ci if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) { 7038c2ecf20Sopenharmony_ci rxbufstate |= (1 << i); 7048c2ecf20Sopenharmony_ci status |= SSI_DATAAVAILABLE(i); 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci ssi_flush_queue(&omap_port->rxqueue[i], cl); 7078c2ecf20Sopenharmony_ci /* Check if we keep the error detection interrupt armed */ 7088c2ecf20Sopenharmony_ci if (!list_empty(&omap_port->rxqueue[i])) 7098c2ecf20Sopenharmony_ci status &= ~SSI_ERROROCCURED; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci /* Cleanup write buffers */ 7128c2ecf20Sopenharmony_ci tmp = readl(omap_port->sst_base + SSI_SST_BUFSTATE_REG); 7138c2ecf20Sopenharmony_ci tmp &= ~txbufstate; 7148c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_port->sst_base + SSI_SST_BUFSTATE_REG); 7158c2ecf20Sopenharmony_ci /* Cleanup read buffers */ 7168c2ecf20Sopenharmony_ci tmp = readl(omap_port->ssr_base + SSI_SSR_BUFSTATE_REG); 7178c2ecf20Sopenharmony_ci tmp &= ~rxbufstate; 7188c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_port->ssr_base + SSI_SSR_BUFSTATE_REG); 7198c2ecf20Sopenharmony_ci /* Disarm and ack pending interrupts */ 7208c2ecf20Sopenharmony_ci tmp = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 7218c2ecf20Sopenharmony_ci tmp &= ~status; 7228c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 7238c2ecf20Sopenharmony_ci writel_relaxed(status, omap_ssi->sys + 7248c2ecf20Sopenharmony_ci SSI_MPU_STATUS_REG(port->num, 0)); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 7308c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(cl); 7318c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 7328c2ecf20Sopenharmony_ci struct hsi_msg *msg; 7338c2ecf20Sopenharmony_ci unsigned int i; 7348c2ecf20Sopenharmony_ci u32 val = 0; 7358c2ecf20Sopenharmony_ci u32 tmp; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci for (i = 0; i < SSI_MAX_GDD_LCH; i++) { 7388c2ecf20Sopenharmony_ci msg = omap_ssi->gdd_trn[i].msg; 7398c2ecf20Sopenharmony_ci if ((!msg) || (msg->cl != cl)) 7408c2ecf20Sopenharmony_ci continue; 7418c2ecf20Sopenharmony_ci writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); 7428c2ecf20Sopenharmony_ci val |= (1 << i); 7438c2ecf20Sopenharmony_ci /* 7448c2ecf20Sopenharmony_ci * Clock references for write will be handled in 7458c2ecf20Sopenharmony_ci * ssi_cleanup_queues 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_READ) { 7488c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 7498c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci omap_ssi->gdd_trn[i].msg = NULL; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 7548c2ecf20Sopenharmony_ci tmp &= ~val; 7558c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 7568c2ecf20Sopenharmony_ci writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int ssi_set_port_mode(struct omap_ssi_port *omap_port, u32 mode) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci writel(mode, omap_port->sst_base + SSI_SST_MODE_REG); 7628c2ecf20Sopenharmony_ci writel(mode, omap_port->ssr_base + SSI_SSR_MODE_REG); 7638c2ecf20Sopenharmony_ci /* OCP barrier */ 7648c2ecf20Sopenharmony_ci mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return 0; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int ssi_release(struct hsi_client *cl) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct hsi_port *port = hsi_get_port(cl); 7728c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 7738c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 7768c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 7778c2ecf20Sopenharmony_ci /* Stop all the pending DMA requests for that client */ 7788c2ecf20Sopenharmony_ci ssi_cleanup_gdd(ssi, cl); 7798c2ecf20Sopenharmony_ci /* Now cleanup all the queues */ 7808c2ecf20Sopenharmony_ci ssi_cleanup_queues(cl); 7818c2ecf20Sopenharmony_ci /* If it is the last client of the port, do extra checks and cleanup */ 7828c2ecf20Sopenharmony_ci if (port->claimed <= 1) { 7838c2ecf20Sopenharmony_ci /* 7848c2ecf20Sopenharmony_ci * Drop the clock reference for the incoming wake line 7858c2ecf20Sopenharmony_ci * if it is still kept high by the other side. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_ci if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) 7888c2ecf20Sopenharmony_ci pm_runtime_put_sync(omap_port->pdev); 7898c2ecf20Sopenharmony_ci pm_runtime_get(omap_port->pdev); 7908c2ecf20Sopenharmony_ci /* Stop any SSI TX/RX without a client */ 7918c2ecf20Sopenharmony_ci ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); 7928c2ecf20Sopenharmony_ci omap_port->sst.mode = SSI_MODE_SLEEP; 7938c2ecf20Sopenharmony_ci omap_port->ssr.mode = SSI_MODE_SLEEP; 7948c2ecf20Sopenharmony_ci pm_runtime_put(omap_port->pdev); 7958c2ecf20Sopenharmony_ci WARN_ON(omap_port->wk_refcount != 0); 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 7988c2ecf20Sopenharmony_ci pm_runtime_put_sync(omap_port->pdev); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic void ssi_error(struct hsi_port *port) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 8088c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 8098c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 8108c2ecf20Sopenharmony_ci struct hsi_msg *msg; 8118c2ecf20Sopenharmony_ci unsigned int i; 8128c2ecf20Sopenharmony_ci u32 err; 8138c2ecf20Sopenharmony_ci u32 val; 8148c2ecf20Sopenharmony_ci u32 tmp; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* ACK error */ 8178c2ecf20Sopenharmony_ci err = readl(omap_port->ssr_base + SSI_SSR_ERROR_REG); 8188c2ecf20Sopenharmony_ci dev_err(&port->device, "SSI error: 0x%02x\n", err); 8198c2ecf20Sopenharmony_ci if (!err) { 8208c2ecf20Sopenharmony_ci dev_dbg(&port->device, "spurious SSI error ignored!\n"); 8218c2ecf20Sopenharmony_ci return; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci spin_lock(&omap_ssi->lock); 8248c2ecf20Sopenharmony_ci /* Cancel all GDD read transfers */ 8258c2ecf20Sopenharmony_ci for (i = 0, val = 0; i < SSI_MAX_GDD_LCH; i++) { 8268c2ecf20Sopenharmony_ci msg = omap_ssi->gdd_trn[i].msg; 8278c2ecf20Sopenharmony_ci if ((msg) && (msg->ttype == HSI_MSG_READ)) { 8288c2ecf20Sopenharmony_ci writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); 8298c2ecf20Sopenharmony_ci val |= (1 << i); 8308c2ecf20Sopenharmony_ci omap_ssi->gdd_trn[i].msg = NULL; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci tmp = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 8348c2ecf20Sopenharmony_ci tmp &= ~val; 8358c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 8368c2ecf20Sopenharmony_ci spin_unlock(&omap_ssi->lock); 8378c2ecf20Sopenharmony_ci /* Cancel all PIO read transfers */ 8388c2ecf20Sopenharmony_ci spin_lock(&omap_port->lock); 8398c2ecf20Sopenharmony_ci tmp = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 8408c2ecf20Sopenharmony_ci tmp &= 0xfeff00ff; /* Disable error & all dataavailable interrupts */ 8418c2ecf20Sopenharmony_ci writel_relaxed(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 8428c2ecf20Sopenharmony_ci /* ACK error */ 8438c2ecf20Sopenharmony_ci writel_relaxed(err, omap_port->ssr_base + SSI_SSR_ERRORACK_REG); 8448c2ecf20Sopenharmony_ci writel_relaxed(SSI_ERROROCCURED, 8458c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 8468c2ecf20Sopenharmony_ci /* Signal the error all current pending read requests */ 8478c2ecf20Sopenharmony_ci for (i = 0; i < omap_port->channels; i++) { 8488c2ecf20Sopenharmony_ci if (list_empty(&omap_port->rxqueue[i])) 8498c2ecf20Sopenharmony_ci continue; 8508c2ecf20Sopenharmony_ci msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg, 8518c2ecf20Sopenharmony_ci link); 8528c2ecf20Sopenharmony_ci list_del(&msg->link); 8538c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_ERROR; 8548c2ecf20Sopenharmony_ci spin_unlock(&omap_port->lock); 8558c2ecf20Sopenharmony_ci msg->complete(msg); 8568c2ecf20Sopenharmony_ci /* Now restart queued reads if any */ 8578c2ecf20Sopenharmony_ci ssi_transfer(omap_port, &omap_port->rxqueue[i]); 8588c2ecf20Sopenharmony_ci spin_lock(&omap_port->lock); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci spin_unlock(&omap_port->lock); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic void ssi_break_complete(struct hsi_port *port) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 8668c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 8678c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 8688c2ecf20Sopenharmony_ci struct hsi_msg *msg; 8698c2ecf20Sopenharmony_ci struct hsi_msg *tmp; 8708c2ecf20Sopenharmony_ci u32 val; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci dev_dbg(&port->device, "HWBREAK received\n"); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci spin_lock(&omap_port->lock); 8758c2ecf20Sopenharmony_ci val = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 8768c2ecf20Sopenharmony_ci val &= ~SSI_BREAKDETECTED; 8778c2ecf20Sopenharmony_ci writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 8788c2ecf20Sopenharmony_ci writel_relaxed(0, omap_port->ssr_base + SSI_SSR_BREAK_REG); 8798c2ecf20Sopenharmony_ci writel(SSI_BREAKDETECTED, 8808c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 8818c2ecf20Sopenharmony_ci spin_unlock(&omap_port->lock); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci list_for_each_entry_safe(msg, tmp, &omap_port->brkqueue, link) { 8848c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_COMPLETED; 8858c2ecf20Sopenharmony_ci spin_lock(&omap_port->lock); 8868c2ecf20Sopenharmony_ci list_del(&msg->link); 8878c2ecf20Sopenharmony_ci spin_unlock(&omap_port->lock); 8888c2ecf20Sopenharmony_ci msg->complete(msg); 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 8968c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 8978c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 8988c2ecf20Sopenharmony_ci struct hsi_msg *msg; 8998c2ecf20Sopenharmony_ci u32 *buf; 9008c2ecf20Sopenharmony_ci u32 reg; 9018c2ecf20Sopenharmony_ci u32 val; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci spin_lock_bh(&omap_port->lock); 9048c2ecf20Sopenharmony_ci msg = list_first_entry(queue, struct hsi_msg, link); 9058c2ecf20Sopenharmony_ci if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) { 9068c2ecf20Sopenharmony_ci msg->actual_len = 0; 9078c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_PENDING; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_WRITE) 9108c2ecf20Sopenharmony_ci val = SSI_DATAACCEPT(msg->channel); 9118c2ecf20Sopenharmony_ci else 9128c2ecf20Sopenharmony_ci val = SSI_DATAAVAILABLE(msg->channel); 9138c2ecf20Sopenharmony_ci if (msg->status == HSI_STATUS_PROCEEDING) { 9148c2ecf20Sopenharmony_ci buf = sg_virt(msg->sgt.sgl) + msg->actual_len; 9158c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_WRITE) 9168c2ecf20Sopenharmony_ci writel(*buf, omap_port->sst_base + 9178c2ecf20Sopenharmony_ci SSI_SST_BUFFER_CH_REG(msg->channel)); 9188c2ecf20Sopenharmony_ci else 9198c2ecf20Sopenharmony_ci *buf = readl(omap_port->ssr_base + 9208c2ecf20Sopenharmony_ci SSI_SSR_BUFFER_CH_REG(msg->channel)); 9218c2ecf20Sopenharmony_ci dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel, 9228c2ecf20Sopenharmony_ci msg->ttype, *buf); 9238c2ecf20Sopenharmony_ci msg->actual_len += sizeof(*buf); 9248c2ecf20Sopenharmony_ci if (msg->actual_len >= msg->sgt.sgl->length) 9258c2ecf20Sopenharmony_ci msg->status = HSI_STATUS_COMPLETED; 9268c2ecf20Sopenharmony_ci /* 9278c2ecf20Sopenharmony_ci * Wait for the last written frame to be really sent before 9288c2ecf20Sopenharmony_ci * we call the complete callback 9298c2ecf20Sopenharmony_ci */ 9308c2ecf20Sopenharmony_ci if ((msg->status == HSI_STATUS_PROCEEDING) || 9318c2ecf20Sopenharmony_ci ((msg->status == HSI_STATUS_COMPLETED) && 9328c2ecf20Sopenharmony_ci (msg->ttype == HSI_MSG_WRITE))) { 9338c2ecf20Sopenharmony_ci writel(val, omap_ssi->sys + 9348c2ecf20Sopenharmony_ci SSI_MPU_STATUS_REG(port->num, 0)); 9358c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci return; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci /* Transfer completed at this point */ 9428c2ecf20Sopenharmony_ci reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 9438c2ecf20Sopenharmony_ci if (msg->ttype == HSI_MSG_WRITE) { 9448c2ecf20Sopenharmony_ci /* Release clocks for write transfer */ 9458c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 9468c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci reg &= ~val; 9498c2ecf20Sopenharmony_ci writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 9508c2ecf20Sopenharmony_ci writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 9518c2ecf20Sopenharmony_ci list_del(&msg->link); 9528c2ecf20Sopenharmony_ci spin_unlock_bh(&omap_port->lock); 9538c2ecf20Sopenharmony_ci msg->complete(msg); 9548c2ecf20Sopenharmony_ci ssi_transfer(omap_port, queue); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic irqreturn_t ssi_pio_thread(int irq, void *ssi_port) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct hsi_port *port = (struct hsi_port *)ssi_port; 9608c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 9618c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 9628c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 9638c2ecf20Sopenharmony_ci void __iomem *sys = omap_ssi->sys; 9648c2ecf20Sopenharmony_ci unsigned int ch; 9658c2ecf20Sopenharmony_ci u32 status_reg; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci do { 9708c2ecf20Sopenharmony_ci status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); 9718c2ecf20Sopenharmony_ci status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for (ch = 0; ch < omap_port->channels; ch++) { 9748c2ecf20Sopenharmony_ci if (status_reg & SSI_DATAACCEPT(ch)) 9758c2ecf20Sopenharmony_ci ssi_pio_complete(port, &omap_port->txqueue[ch]); 9768c2ecf20Sopenharmony_ci if (status_reg & SSI_DATAAVAILABLE(ch)) 9778c2ecf20Sopenharmony_ci ssi_pio_complete(port, &omap_port->rxqueue[ch]); 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci if (status_reg & SSI_BREAKDETECTED) 9808c2ecf20Sopenharmony_ci ssi_break_complete(port); 9818c2ecf20Sopenharmony_ci if (status_reg & SSI_ERROROCCURED) 9828c2ecf20Sopenharmony_ci ssi_error(port); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); 9858c2ecf20Sopenharmony_ci status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* TODO: sleep if we retry? */ 9888c2ecf20Sopenharmony_ci } while (status_reg); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 9918c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct hsi_port *port = (struct hsi_port *)ssi_port; 9998c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 10008c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 10018c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (ssi_wakein(port)) { 10048c2ecf20Sopenharmony_ci /** 10058c2ecf20Sopenharmony_ci * We can have a quick High-Low-High transition in the line. 10068c2ecf20Sopenharmony_ci * In such a case if we have long interrupt latencies, 10078c2ecf20Sopenharmony_ci * we can miss the low event or get twice a high event. 10088c2ecf20Sopenharmony_ci * This workaround will avoid breaking the clock reference 10098c2ecf20Sopenharmony_ci * count when such a situation ocurrs. 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_ci if (!test_and_set_bit(SSI_WAKE_EN, &omap_port->flags)) 10128c2ecf20Sopenharmony_ci pm_runtime_get_sync(omap_port->pdev); 10138c2ecf20Sopenharmony_ci dev_dbg(&ssi->device, "Wake in high\n"); 10148c2ecf20Sopenharmony_ci if (omap_port->wktest) { /* FIXME: HACK ! To be removed */ 10158c2ecf20Sopenharmony_ci writel(SSI_WAKE(0), 10168c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci hsi_event(port, HSI_EVENT_START_RX); 10198c2ecf20Sopenharmony_ci } else { 10208c2ecf20Sopenharmony_ci dev_dbg(&ssi->device, "Wake in low\n"); 10218c2ecf20Sopenharmony_ci if (omap_port->wktest) { /* FIXME: HACK ! To be removed */ 10228c2ecf20Sopenharmony_ci writel(SSI_WAKE(0), 10238c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci hsi_event(port, HSI_EVENT_STOP_RX); 10268c2ecf20Sopenharmony_ci if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) { 10278c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(omap_port->pdev); 10288c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(omap_port->pdev); 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic int ssi_port_irq(struct hsi_port *port, struct platform_device *pd) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 10388c2ecf20Sopenharmony_ci int err; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci err = platform_get_irq(pd, 0); 10418c2ecf20Sopenharmony_ci if (err < 0) 10428c2ecf20Sopenharmony_ci return err; 10438c2ecf20Sopenharmony_ci omap_port->irq = err; 10448c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(&port->device, omap_port->irq, NULL, 10458c2ecf20Sopenharmony_ci ssi_pio_thread, IRQF_ONESHOT, "SSI PORT", port); 10468c2ecf20Sopenharmony_ci if (err < 0) 10478c2ecf20Sopenharmony_ci dev_err(&port->device, "Request IRQ %d failed (%d)\n", 10488c2ecf20Sopenharmony_ci omap_port->irq, err); 10498c2ecf20Sopenharmony_ci return err; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 10558c2ecf20Sopenharmony_ci int cawake_irq; 10568c2ecf20Sopenharmony_ci int err; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (!omap_port->wake_gpio) { 10598c2ecf20Sopenharmony_ci omap_port->wake_irq = -1; 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci cawake_irq = gpiod_to_irq(omap_port->wake_gpio); 10648c2ecf20Sopenharmony_ci omap_port->wake_irq = cawake_irq; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(&port->device, cawake_irq, NULL, 10678c2ecf20Sopenharmony_ci ssi_wake_thread, 10688c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 10698c2ecf20Sopenharmony_ci "SSI cawake", port); 10708c2ecf20Sopenharmony_ci if (err < 0) 10718c2ecf20Sopenharmony_ci dev_err(&port->device, "Request Wake in IRQ %d failed %d\n", 10728c2ecf20Sopenharmony_ci cawake_irq, err); 10738c2ecf20Sopenharmony_ci err = enable_irq_wake(cawake_irq); 10748c2ecf20Sopenharmony_ci if (err < 0) 10758c2ecf20Sopenharmony_ci dev_err(&port->device, "Enable wake on the wakeline in irq %d failed %d\n", 10768c2ecf20Sopenharmony_ci cawake_irq, err); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci return err; 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic void ssi_queues_init(struct omap_ssi_port *omap_port) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci unsigned int ch; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci for (ch = 0; ch < SSI_MAX_CHANNELS; ch++) { 10868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&omap_port->txqueue[ch]); 10878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&omap_port->rxqueue[ch]); 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&omap_port->brkqueue); 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic int ssi_port_get_iomem(struct platform_device *pd, 10938c2ecf20Sopenharmony_ci const char *name, void __iomem **pbase, dma_addr_t *phy) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct hsi_port *port = platform_get_drvdata(pd); 10968c2ecf20Sopenharmony_ci struct resource *mem; 10978c2ecf20Sopenharmony_ci struct resource *ioarea; 10988c2ecf20Sopenharmony_ci void __iomem *base; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name); 11018c2ecf20Sopenharmony_ci if (!mem) { 11028c2ecf20Sopenharmony_ci dev_err(&pd->dev, "IO memory region missing (%s)\n", name); 11038c2ecf20Sopenharmony_ci return -ENXIO; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci ioarea = devm_request_mem_region(&port->device, mem->start, 11068c2ecf20Sopenharmony_ci resource_size(mem), dev_name(&pd->dev)); 11078c2ecf20Sopenharmony_ci if (!ioarea) { 11088c2ecf20Sopenharmony_ci dev_err(&pd->dev, "%s IO memory region request failed\n", 11098c2ecf20Sopenharmony_ci mem->name); 11108c2ecf20Sopenharmony_ci return -ENXIO; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci base = devm_ioremap(&port->device, mem->start, resource_size(mem)); 11138c2ecf20Sopenharmony_ci if (!base) { 11148c2ecf20Sopenharmony_ci dev_err(&pd->dev, "%s IO remap failed\n", mem->name); 11158c2ecf20Sopenharmony_ci return -ENXIO; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci *pbase = base; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (phy) 11208c2ecf20Sopenharmony_ci *phy = mem->start; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci return 0; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic int ssi_port_probe(struct platform_device *pd) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct device_node *np = pd->dev.of_node; 11288c2ecf20Sopenharmony_ci struct hsi_port *port; 11298c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port; 11308c2ecf20Sopenharmony_ci struct hsi_controller *ssi = dev_get_drvdata(pd->dev.parent); 11318c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 11328c2ecf20Sopenharmony_ci struct gpio_desc *cawake_gpio = NULL; 11338c2ecf20Sopenharmony_ci u32 port_id; 11348c2ecf20Sopenharmony_ci int err; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci dev_dbg(&pd->dev, "init ssi port...\n"); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (!ssi->port || !omap_ssi->port) { 11398c2ecf20Sopenharmony_ci dev_err(&pd->dev, "ssi controller not initialized!\n"); 11408c2ecf20Sopenharmony_ci err = -ENODEV; 11418c2ecf20Sopenharmony_ci goto error; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* get id of first uninitialized port in controller */ 11458c2ecf20Sopenharmony_ci for (port_id = 0; port_id < ssi->num_ports && omap_ssi->port[port_id]; 11468c2ecf20Sopenharmony_ci port_id++) 11478c2ecf20Sopenharmony_ci ; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (port_id >= ssi->num_ports) { 11508c2ecf20Sopenharmony_ci dev_err(&pd->dev, "port id out of range!\n"); 11518c2ecf20Sopenharmony_ci err = -ENODEV; 11528c2ecf20Sopenharmony_ci goto error; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci port = ssi->port[port_id]; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (!np) { 11588c2ecf20Sopenharmony_ci dev_err(&pd->dev, "missing device tree data\n"); 11598c2ecf20Sopenharmony_ci err = -EINVAL; 11608c2ecf20Sopenharmony_ci goto error; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci cawake_gpio = devm_gpiod_get(&pd->dev, "ti,ssi-cawake", GPIOD_IN); 11648c2ecf20Sopenharmony_ci if (IS_ERR(cawake_gpio)) { 11658c2ecf20Sopenharmony_ci err = PTR_ERR(cawake_gpio); 11668c2ecf20Sopenharmony_ci dev_err(&pd->dev, "couldn't get cawake gpio (err=%d)!\n", err); 11678c2ecf20Sopenharmony_ci goto error; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci omap_port = devm_kzalloc(&port->device, sizeof(*omap_port), GFP_KERNEL); 11718c2ecf20Sopenharmony_ci if (!omap_port) { 11728c2ecf20Sopenharmony_ci err = -ENOMEM; 11738c2ecf20Sopenharmony_ci goto error; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci omap_port->wake_gpio = cawake_gpio; 11768c2ecf20Sopenharmony_ci omap_port->pdev = &pd->dev; 11778c2ecf20Sopenharmony_ci omap_port->port_id = port_id; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&omap_port->errqueue_work, ssi_process_errqueue); 11808c2ecf20Sopenharmony_ci INIT_WORK(&omap_port->work, start_tx_work); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* initialize HSI port */ 11838c2ecf20Sopenharmony_ci port->async = ssi_async; 11848c2ecf20Sopenharmony_ci port->setup = ssi_setup; 11858c2ecf20Sopenharmony_ci port->flush = ssi_flush; 11868c2ecf20Sopenharmony_ci port->start_tx = ssi_start_tx; 11878c2ecf20Sopenharmony_ci port->stop_tx = ssi_stop_tx; 11888c2ecf20Sopenharmony_ci port->release = ssi_release; 11898c2ecf20Sopenharmony_ci hsi_port_set_drvdata(port, omap_port); 11908c2ecf20Sopenharmony_ci omap_ssi->port[port_id] = omap_port; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci platform_set_drvdata(pd, port); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci err = ssi_port_get_iomem(pd, "tx", &omap_port->sst_base, 11958c2ecf20Sopenharmony_ci &omap_port->sst_dma); 11968c2ecf20Sopenharmony_ci if (err < 0) 11978c2ecf20Sopenharmony_ci goto error; 11988c2ecf20Sopenharmony_ci err = ssi_port_get_iomem(pd, "rx", &omap_port->ssr_base, 11998c2ecf20Sopenharmony_ci &omap_port->ssr_dma); 12008c2ecf20Sopenharmony_ci if (err < 0) 12018c2ecf20Sopenharmony_ci goto error; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci err = ssi_port_irq(port, pd); 12048c2ecf20Sopenharmony_ci if (err < 0) 12058c2ecf20Sopenharmony_ci goto error; 12068c2ecf20Sopenharmony_ci err = ssi_wake_irq(port, pd); 12078c2ecf20Sopenharmony_ci if (err < 0) 12088c2ecf20Sopenharmony_ci goto error; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci ssi_queues_init(omap_port); 12118c2ecf20Sopenharmony_ci spin_lock_init(&omap_port->lock); 12128c2ecf20Sopenharmony_ci spin_lock_init(&omap_port->wk_lock); 12138c2ecf20Sopenharmony_ci omap_port->dev = &port->device; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(omap_port->pdev); 12168c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(omap_port->pdev, 250); 12178c2ecf20Sopenharmony_ci pm_runtime_enable(omap_port->pdev); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 12208c2ecf20Sopenharmony_ci err = ssi_debug_add_port(omap_port, omap_ssi->dir); 12218c2ecf20Sopenharmony_ci if (err < 0) { 12228c2ecf20Sopenharmony_ci pm_runtime_disable(omap_port->pdev); 12238c2ecf20Sopenharmony_ci goto error; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci#endif 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci hsi_add_clients_from_dt(port, np); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci dev_info(&pd->dev, "ssi port %u successfully initialized\n", port_id); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return 0; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_cierror: 12348c2ecf20Sopenharmony_ci return err; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int ssi_port_remove(struct platform_device *pd) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct hsi_port *port = platform_get_drvdata(pd); 12408c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 12418c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 12428c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 12458c2ecf20Sopenharmony_ci ssi_debug_remove_port(port); 12468c2ecf20Sopenharmony_ci#endif 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&omap_port->errqueue_work); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci hsi_port_unregister_clients(port); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci port->async = hsi_dummy_msg; 12538c2ecf20Sopenharmony_ci port->setup = hsi_dummy_cl; 12548c2ecf20Sopenharmony_ci port->flush = hsi_dummy_cl; 12558c2ecf20Sopenharmony_ci port->start_tx = hsi_dummy_cl; 12568c2ecf20Sopenharmony_ci port->stop_tx = hsi_dummy_cl; 12578c2ecf20Sopenharmony_ci port->release = hsi_dummy_cl; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci omap_ssi->port[omap_port->port_id] = NULL; 12608c2ecf20Sopenharmony_ci platform_set_drvdata(pd, NULL); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pd->dev); 12638c2ecf20Sopenharmony_ci pm_runtime_disable(&pd->dev); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic int ssi_restore_divisor(struct omap_ssi_port *omap_port) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci writel_relaxed(omap_port->sst.divisor, 12718c2ecf20Sopenharmony_ci omap_port->sst_base + SSI_SST_DIVISOR_REG); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci return 0; 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_civoid omap_ssi_port_update_fclk(struct hsi_controller *ssi, 12778c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci /* update divisor */ 12808c2ecf20Sopenharmony_ci u32 div = ssi_calculate_div(ssi); 12818c2ecf20Sopenharmony_ci omap_port->sst.divisor = div; 12828c2ecf20Sopenharmony_ci ssi_restore_divisor(omap_port); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12868c2ecf20Sopenharmony_cistatic int ssi_save_port_ctx(struct omap_ssi_port *omap_port) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci struct hsi_port *port = to_hsi_port(omap_port->dev); 12898c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 12908c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci omap_port->sys_mpu_enable = readl(omap_ssi->sys + 12938c2ecf20Sopenharmony_ci SSI_MPU_ENABLE_REG(port->num, 0)); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return 0; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic int ssi_restore_port_ctx(struct omap_ssi_port *omap_port) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct hsi_port *port = to_hsi_port(omap_port->dev); 13018c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 13028c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 13038c2ecf20Sopenharmony_ci void __iomem *base; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci writel_relaxed(omap_port->sys_mpu_enable, 13068c2ecf20Sopenharmony_ci omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* SST context */ 13098c2ecf20Sopenharmony_ci base = omap_port->sst_base; 13108c2ecf20Sopenharmony_ci writel_relaxed(omap_port->sst.frame_size, base + SSI_SST_FRAMESIZE_REG); 13118c2ecf20Sopenharmony_ci writel_relaxed(omap_port->sst.channels, base + SSI_SST_CHANNELS_REG); 13128c2ecf20Sopenharmony_ci writel_relaxed(omap_port->sst.arb_mode, base + SSI_SST_ARBMODE_REG); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* SSR context */ 13158c2ecf20Sopenharmony_ci base = omap_port->ssr_base; 13168c2ecf20Sopenharmony_ci writel_relaxed(omap_port->ssr.frame_size, base + SSI_SSR_FRAMESIZE_REG); 13178c2ecf20Sopenharmony_ci writel_relaxed(omap_port->ssr.channels, base + SSI_SSR_CHANNELS_REG); 13188c2ecf20Sopenharmony_ci writel_relaxed(omap_port->ssr.timeout, base + SSI_SSR_TIMEOUT_REG); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci return 0; 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic int ssi_restore_port_mode(struct omap_ssi_port *omap_port) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci u32 mode; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci writel_relaxed(omap_port->sst.mode, 13288c2ecf20Sopenharmony_ci omap_port->sst_base + SSI_SST_MODE_REG); 13298c2ecf20Sopenharmony_ci writel_relaxed(omap_port->ssr.mode, 13308c2ecf20Sopenharmony_ci omap_port->ssr_base + SSI_SSR_MODE_REG); 13318c2ecf20Sopenharmony_ci /* OCP barrier */ 13328c2ecf20Sopenharmony_ci mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci return 0; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic int omap_ssi_port_runtime_suspend(struct device *dev) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct hsi_port *port = dev_get_drvdata(dev); 13408c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 13418c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 13428c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci dev_dbg(dev, "port runtime suspend!\n"); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); 13478c2ecf20Sopenharmony_ci if (omap_ssi->get_loss) 13488c2ecf20Sopenharmony_ci omap_port->loss_count = 13498c2ecf20Sopenharmony_ci omap_ssi->get_loss(ssi->device.parent); 13508c2ecf20Sopenharmony_ci ssi_save_port_ctx(omap_port); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci return 0; 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic int omap_ssi_port_runtime_resume(struct device *dev) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct hsi_port *port = dev_get_drvdata(dev); 13588c2ecf20Sopenharmony_ci struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 13598c2ecf20Sopenharmony_ci struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 13608c2ecf20Sopenharmony_ci struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci dev_dbg(dev, "port runtime resume!\n"); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci if ((omap_ssi->get_loss) && (omap_port->loss_count == 13658c2ecf20Sopenharmony_ci omap_ssi->get_loss(ssi->device.parent))) 13668c2ecf20Sopenharmony_ci goto mode; /* We always need to restore the mode & TX divisor */ 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci ssi_restore_port_ctx(omap_port); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cimode: 13718c2ecf20Sopenharmony_ci ssi_restore_divisor(omap_port); 13728c2ecf20Sopenharmony_ci ssi_restore_port_mode(omap_port); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci return 0; 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap_ssi_port_pm_ops = { 13788c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(omap_ssi_port_runtime_suspend, 13798c2ecf20Sopenharmony_ci omap_ssi_port_runtime_resume, NULL) 13808c2ecf20Sopenharmony_ci}; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci#define DEV_PM_OPS (&omap_ssi_port_pm_ops) 13838c2ecf20Sopenharmony_ci#else 13848c2ecf20Sopenharmony_ci#define DEV_PM_OPS NULL 13858c2ecf20Sopenharmony_ci#endif 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 13898c2ecf20Sopenharmony_cistatic const struct of_device_id omap_ssi_port_of_match[] = { 13908c2ecf20Sopenharmony_ci { .compatible = "ti,omap3-ssi-port", }, 13918c2ecf20Sopenharmony_ci {}, 13928c2ecf20Sopenharmony_ci}; 13938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_ssi_port_of_match); 13948c2ecf20Sopenharmony_ci#else 13958c2ecf20Sopenharmony_ci#define omap_ssi_port_of_match NULL 13968c2ecf20Sopenharmony_ci#endif 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_cistruct platform_driver ssi_port_pdriver = { 13998c2ecf20Sopenharmony_ci .probe = ssi_port_probe, 14008c2ecf20Sopenharmony_ci .remove = ssi_port_remove, 14018c2ecf20Sopenharmony_ci .driver = { 14028c2ecf20Sopenharmony_ci .name = "omap_ssi_port", 14038c2ecf20Sopenharmony_ci .of_match_table = omap_ssi_port_of_match, 14048c2ecf20Sopenharmony_ci .pm = DEV_PM_OPS, 14058c2ecf20Sopenharmony_ci }, 14068c2ecf20Sopenharmony_ci}; 1407