12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b209e047SSebastian Reichel /* OMAP SSI port driver. 3b209e047SSebastian Reichel * 4b209e047SSebastian Reichel * Copyright (C) 2010 Nokia Corporation. All rights reserved. 5b209e047SSebastian Reichel * Copyright (C) 2014 Sebastian Reichel <sre@kernel.org> 6b209e047SSebastian Reichel * 7b209e047SSebastian Reichel * Contact: Carlos Chinea <carlos.chinea@nokia.com> 8b209e047SSebastian Reichel */ 9b209e047SSebastian Reichel 10ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 11b209e047SSebastian Reichel #include <linux/platform_device.h> 12b209e047SSebastian Reichel #include <linux/dma-mapping.h> 13b209e047SSebastian Reichel #include <linux/pm_runtime.h> 144bcf7414SSebastian Reichel #include <linux/delay.h> 15b209e047SSebastian Reichel 1673e6ce09SSebastian Reichel #include <linux/gpio/consumer.h> 17ac8e3ff3SArnd Bergmann #include <linux/pinctrl/consumer.h> 18b209e047SSebastian Reichel #include <linux/debugfs.h> 19b209e047SSebastian Reichel 20b209e047SSebastian Reichel #include "omap_ssi_regs.h" 21b209e047SSebastian Reichel #include "omap_ssi.h" 22b209e047SSebastian Reichel 23b209e047SSebastian Reichel static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused) 24b209e047SSebastian Reichel { 25b209e047SSebastian Reichel return 0; 26b209e047SSebastian Reichel } 27b209e047SSebastian Reichel 28b209e047SSebastian Reichel static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) 29b209e047SSebastian Reichel { 30b209e047SSebastian Reichel return 0; 31b209e047SSebastian Reichel } 32b209e047SSebastian Reichel 33b209e047SSebastian Reichel static inline unsigned int ssi_wakein(struct hsi_port *port) 34b209e047SSebastian Reichel { 35b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 3673e6ce09SSebastian Reichel return gpiod_get_value(omap_port->wake_gpio); 37b209e047SSebastian Reichel } 38b209e047SSebastian Reichel 39b209e047SSebastian Reichel #ifdef CONFIG_DEBUG_FS 40b209e047SSebastian Reichel static void ssi_debug_remove_port(struct hsi_port *port) 41b209e047SSebastian Reichel { 42b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 43b209e047SSebastian Reichel 44b209e047SSebastian Reichel debugfs_remove_recursive(omap_port->dir); 45b209e047SSebastian Reichel } 46b209e047SSebastian Reichel 473a658e09SYangtao Li static int ssi_port_regs_show(struct seq_file *m, void *p __maybe_unused) 48b209e047SSebastian Reichel { 49b209e047SSebastian Reichel struct hsi_port *port = m->private; 50b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 51b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 52b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 53b209e047SSebastian Reichel void __iomem *base = omap_ssi->sys; 54b209e047SSebastian Reichel unsigned int ch; 55b209e047SSebastian Reichel 56b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 57b209e047SSebastian Reichel if (omap_port->wake_irq > 0) 58b209e047SSebastian Reichel seq_printf(m, "CAWAKE\t\t: %d\n", ssi_wakein(port)); 59b209e047SSebastian Reichel seq_printf(m, "WAKE\t\t: 0x%08x\n", 60b209e047SSebastian Reichel readl(base + SSI_WAKE_REG(port->num))); 61b209e047SSebastian Reichel seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", 0, 62b209e047SSebastian Reichel readl(base + SSI_MPU_ENABLE_REG(port->num, 0))); 63b209e047SSebastian Reichel seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", 0, 64b209e047SSebastian Reichel readl(base + SSI_MPU_STATUS_REG(port->num, 0))); 65b209e047SSebastian Reichel /* SST */ 66b209e047SSebastian Reichel base = omap_port->sst_base; 67b209e047SSebastian Reichel seq_puts(m, "\nSST\n===\n"); 68b209e047SSebastian Reichel seq_printf(m, "ID SST\t\t: 0x%08x\n", 69b209e047SSebastian Reichel readl(base + SSI_SST_ID_REG)); 70b209e047SSebastian Reichel seq_printf(m, "MODE\t\t: 0x%08x\n", 71b209e047SSebastian Reichel readl(base + SSI_SST_MODE_REG)); 72b209e047SSebastian Reichel seq_printf(m, "FRAMESIZE\t: 0x%08x\n", 73b209e047SSebastian Reichel readl(base + SSI_SST_FRAMESIZE_REG)); 74b209e047SSebastian Reichel seq_printf(m, "DIVISOR\t\t: 0x%08x\n", 75b209e047SSebastian Reichel readl(base + SSI_SST_DIVISOR_REG)); 76b209e047SSebastian Reichel seq_printf(m, "CHANNELS\t: 0x%08x\n", 77b209e047SSebastian Reichel readl(base + SSI_SST_CHANNELS_REG)); 78b209e047SSebastian Reichel seq_printf(m, "ARBMODE\t\t: 0x%08x\n", 79b209e047SSebastian Reichel readl(base + SSI_SST_ARBMODE_REG)); 80b209e047SSebastian Reichel seq_printf(m, "TXSTATE\t\t: 0x%08x\n", 81b209e047SSebastian Reichel readl(base + SSI_SST_TXSTATE_REG)); 82b209e047SSebastian Reichel seq_printf(m, "BUFSTATE\t: 0x%08x\n", 83b209e047SSebastian Reichel readl(base + SSI_SST_BUFSTATE_REG)); 84b209e047SSebastian Reichel seq_printf(m, "BREAK\t\t: 0x%08x\n", 85b209e047SSebastian Reichel readl(base + SSI_SST_BREAK_REG)); 86b209e047SSebastian Reichel for (ch = 0; ch < omap_port->channels; ch++) { 87b209e047SSebastian Reichel seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, 88b209e047SSebastian Reichel readl(base + SSI_SST_BUFFER_CH_REG(ch))); 89b209e047SSebastian Reichel } 90b209e047SSebastian Reichel /* SSR */ 91b209e047SSebastian Reichel base = omap_port->ssr_base; 92b209e047SSebastian Reichel seq_puts(m, "\nSSR\n===\n"); 93b209e047SSebastian Reichel seq_printf(m, "ID SSR\t\t: 0x%08x\n", 94b209e047SSebastian Reichel readl(base + SSI_SSR_ID_REG)); 95b209e047SSebastian Reichel seq_printf(m, "MODE\t\t: 0x%08x\n", 96b209e047SSebastian Reichel readl(base + SSI_SSR_MODE_REG)); 97b209e047SSebastian Reichel seq_printf(m, "FRAMESIZE\t: 0x%08x\n", 98b209e047SSebastian Reichel readl(base + SSI_SSR_FRAMESIZE_REG)); 99b209e047SSebastian Reichel seq_printf(m, "CHANNELS\t: 0x%08x\n", 100b209e047SSebastian Reichel readl(base + SSI_SSR_CHANNELS_REG)); 101b209e047SSebastian Reichel seq_printf(m, "TIMEOUT\t\t: 0x%08x\n", 102b209e047SSebastian Reichel readl(base + SSI_SSR_TIMEOUT_REG)); 103b209e047SSebastian Reichel seq_printf(m, "RXSTATE\t\t: 0x%08x\n", 104b209e047SSebastian Reichel readl(base + SSI_SSR_RXSTATE_REG)); 105b209e047SSebastian Reichel seq_printf(m, "BUFSTATE\t: 0x%08x\n", 106b209e047SSebastian Reichel readl(base + SSI_SSR_BUFSTATE_REG)); 107b209e047SSebastian Reichel seq_printf(m, "BREAK\t\t: 0x%08x\n", 108b209e047SSebastian Reichel readl(base + SSI_SSR_BREAK_REG)); 109b209e047SSebastian Reichel seq_printf(m, "ERROR\t\t: 0x%08x\n", 110b209e047SSebastian Reichel readl(base + SSI_SSR_ERROR_REG)); 111b209e047SSebastian Reichel seq_printf(m, "ERRORACK\t: 0x%08x\n", 112b209e047SSebastian Reichel readl(base + SSI_SSR_ERRORACK_REG)); 113b209e047SSebastian Reichel for (ch = 0; ch < omap_port->channels; ch++) { 114b209e047SSebastian Reichel seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, 115b209e047SSebastian Reichel readl(base + SSI_SSR_BUFFER_CH_REG(ch))); 116b209e047SSebastian Reichel } 117ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 118b209e047SSebastian Reichel 119b209e047SSebastian Reichel return 0; 120b209e047SSebastian Reichel } 121b209e047SSebastian Reichel 1223a658e09SYangtao Li DEFINE_SHOW_ATTRIBUTE(ssi_port_regs); 123b209e047SSebastian Reichel 124b209e047SSebastian Reichel static int ssi_div_get(void *data, u64 *val) 125b209e047SSebastian Reichel { 126b209e047SSebastian Reichel struct hsi_port *port = data; 127b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 128b209e047SSebastian Reichel 129b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 130b209e047SSebastian Reichel *val = readl(omap_port->sst_base + SSI_SST_DIVISOR_REG); 131ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 132b209e047SSebastian Reichel 133b209e047SSebastian Reichel return 0; 134b209e047SSebastian Reichel } 135b209e047SSebastian Reichel 136b209e047SSebastian Reichel static int ssi_div_set(void *data, u64 val) 137b209e047SSebastian Reichel { 138b209e047SSebastian Reichel struct hsi_port *port = data; 139b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 140b209e047SSebastian Reichel 141b209e047SSebastian Reichel if (val > 127) 142b209e047SSebastian Reichel return -EINVAL; 143b209e047SSebastian Reichel 144b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 145b209e047SSebastian Reichel writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG); 146b209e047SSebastian Reichel omap_port->sst.divisor = val; 147ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 148b209e047SSebastian Reichel 149b209e047SSebastian Reichel return 0; 150b209e047SSebastian Reichel } 151b209e047SSebastian Reichel 1521ff85bfaSYueHaibing DEFINE_DEBUGFS_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n"); 153b209e047SSebastian Reichel 15442877c38SOsama Muhammad static void ssi_debug_add_port(struct omap_ssi_port *omap_port, 155b209e047SSebastian Reichel struct dentry *dir) 156b209e047SSebastian Reichel { 157b209e047SSebastian Reichel struct hsi_port *port = to_hsi_port(omap_port->dev); 158b209e047SSebastian Reichel 159b209e047SSebastian Reichel dir = debugfs_create_dir(dev_name(omap_port->dev), dir); 160b209e047SSebastian Reichel omap_port->dir = dir; 161b209e047SSebastian Reichel debugfs_create_file("regs", S_IRUGO, dir, port, &ssi_port_regs_fops); 162b209e047SSebastian Reichel dir = debugfs_create_dir("sst", dir); 1631ff85bfaSYueHaibing debugfs_create_file_unsafe("divisor", 0644, dir, port, 164b209e047SSebastian Reichel &ssi_sst_div_fops); 165b209e047SSebastian Reichel } 166b209e047SSebastian Reichel #endif 167b209e047SSebastian Reichel 1684e552310SSebastian Reichel static void ssi_process_errqueue(struct work_struct *work) 1694e552310SSebastian Reichel { 1704e552310SSebastian Reichel struct omap_ssi_port *omap_port; 1714e552310SSebastian Reichel struct list_head *head, *tmp; 1724e552310SSebastian Reichel struct hsi_msg *msg; 1734e552310SSebastian Reichel 1744e552310SSebastian Reichel omap_port = container_of(work, struct omap_ssi_port, errqueue_work.work); 1754e552310SSebastian Reichel 1764e552310SSebastian Reichel list_for_each_safe(head, tmp, &omap_port->errqueue) { 1774e552310SSebastian Reichel msg = list_entry(head, struct hsi_msg, link); 1784e552310SSebastian Reichel msg->complete(msg); 1794e552310SSebastian Reichel list_del(head); 1804e552310SSebastian Reichel } 1814e552310SSebastian Reichel } 1824e552310SSebastian Reichel 183b209e047SSebastian Reichel static int ssi_claim_lch(struct hsi_msg *msg) 184b209e047SSebastian Reichel { 185b209e047SSebastian Reichel 186b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(msg->cl); 187b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 188b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 189b209e047SSebastian Reichel int lch; 190b209e047SSebastian Reichel 191b209e047SSebastian Reichel for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) 192b209e047SSebastian Reichel if (!omap_ssi->gdd_trn[lch].msg) { 193b209e047SSebastian Reichel omap_ssi->gdd_trn[lch].msg = msg; 194b209e047SSebastian Reichel omap_ssi->gdd_trn[lch].sg = msg->sgt.sgl; 195b209e047SSebastian Reichel return lch; 196b209e047SSebastian Reichel } 197b209e047SSebastian Reichel 198b209e047SSebastian Reichel return -EBUSY; 199b209e047SSebastian Reichel } 200b209e047SSebastian Reichel 201b209e047SSebastian Reichel static int ssi_start_dma(struct hsi_msg *msg, int lch) 202b209e047SSebastian Reichel { 203b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(msg->cl); 204b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 205b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 206b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 207b209e047SSebastian Reichel void __iomem *gdd = omap_ssi->gdd; 208b209e047SSebastian Reichel int err; 209b209e047SSebastian Reichel u16 csdp; 210b209e047SSebastian Reichel u16 ccr; 211b209e047SSebastian Reichel u32 s_addr; 212b209e047SSebastian Reichel u32 d_addr; 213b209e047SSebastian Reichel u32 tmp; 214b209e047SSebastian Reichel 21562aa292bSSebastian Reichel /* Hold clocks during the transfer */ 21662aa292bSSebastian Reichel pm_runtime_get(omap_port->pdev); 21762aa292bSSebastian Reichel 21862aa292bSSebastian Reichel if (!pm_runtime_active(omap_port->pdev)) { 21962aa292bSSebastian Reichel dev_warn(&port->device, "ssi_start_dma called without runtime PM!\n"); 220ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 22162aa292bSSebastian Reichel return -EREMOTEIO; 22262aa292bSSebastian Reichel } 22362aa292bSSebastian Reichel 224b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_READ) { 225b209e047SSebastian Reichel err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, 226b209e047SSebastian Reichel DMA_FROM_DEVICE); 227551e325bSJack Wang if (!err) { 228b209e047SSebastian Reichel dev_dbg(&ssi->device, "DMA map SG failed !\n"); 229ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 230551e325bSJack Wang return -EIO; 231b209e047SSebastian Reichel } 232b209e047SSebastian Reichel csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT | 233b209e047SSebastian Reichel SSI_SRC_SINGLE_ACCESS0 | SSI_SRC_PERIPHERAL_PORT | 234b209e047SSebastian Reichel SSI_DATA_TYPE_S32; 235b209e047SSebastian Reichel ccr = msg->channel + 0x10 + (port->num * 8); /* Sync */ 236b209e047SSebastian Reichel ccr |= SSI_DST_AMODE_POSTINC | SSI_SRC_AMODE_CONST | 237b209e047SSebastian Reichel SSI_CCR_ENABLE; 238b209e047SSebastian Reichel s_addr = omap_port->ssr_dma + 239b209e047SSebastian Reichel SSI_SSR_BUFFER_CH_REG(msg->channel); 240b209e047SSebastian Reichel d_addr = sg_dma_address(msg->sgt.sgl); 241b209e047SSebastian Reichel } else { 242b209e047SSebastian Reichel err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, 243b209e047SSebastian Reichel DMA_TO_DEVICE); 244551e325bSJack Wang if (!err) { 245b209e047SSebastian Reichel dev_dbg(&ssi->device, "DMA map SG failed !\n"); 246ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 247551e325bSJack Wang return -EIO; 248b209e047SSebastian Reichel } 249b209e047SSebastian Reichel csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT | 250b209e047SSebastian Reichel SSI_DST_SINGLE_ACCESS0 | SSI_DST_PERIPHERAL_PORT | 251b209e047SSebastian Reichel SSI_DATA_TYPE_S32; 252b209e047SSebastian Reichel ccr = (msg->channel + 1 + (port->num * 8)) & 0xf; /* Sync */ 253b209e047SSebastian Reichel ccr |= SSI_SRC_AMODE_POSTINC | SSI_DST_AMODE_CONST | 254b209e047SSebastian Reichel SSI_CCR_ENABLE; 255b209e047SSebastian Reichel s_addr = sg_dma_address(msg->sgt.sgl); 256b209e047SSebastian Reichel d_addr = omap_port->sst_dma + 257b209e047SSebastian Reichel SSI_SST_BUFFER_CH_REG(msg->channel); 258b209e047SSebastian Reichel } 259b209e047SSebastian Reichel dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n", 260b209e047SSebastian Reichel lch, csdp, ccr, s_addr, d_addr); 261b209e047SSebastian Reichel 262b209e047SSebastian Reichel writew_relaxed(csdp, gdd + SSI_GDD_CSDP_REG(lch)); 263b209e047SSebastian Reichel writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch)); 264b209e047SSebastian Reichel writel_relaxed(d_addr, gdd + SSI_GDD_CDSA_REG(lch)); 265b209e047SSebastian Reichel writel_relaxed(s_addr, gdd + SSI_GDD_CSSA_REG(lch)); 266b209e047SSebastian Reichel writew_relaxed(SSI_BYTES_TO_FRAMES(msg->sgt.sgl->length), 267b209e047SSebastian Reichel gdd + SSI_GDD_CEN_REG(lch)); 268b209e047SSebastian Reichel 269b209e047SSebastian Reichel spin_lock_bh(&omap_ssi->lock); 270b209e047SSebastian Reichel tmp = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 271b209e047SSebastian Reichel tmp |= SSI_GDD_LCH(lch); 272b209e047SSebastian Reichel writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 273b209e047SSebastian Reichel spin_unlock_bh(&omap_ssi->lock); 274b209e047SSebastian Reichel writew(ccr, gdd + SSI_GDD_CCR_REG(lch)); 275b209e047SSebastian Reichel msg->status = HSI_STATUS_PROCEEDING; 276b209e047SSebastian Reichel 277b209e047SSebastian Reichel return 0; 278b209e047SSebastian Reichel } 279b209e047SSebastian Reichel 280b209e047SSebastian Reichel static int ssi_start_pio(struct hsi_msg *msg) 281b209e047SSebastian Reichel { 282b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(msg->cl); 283b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 284b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 285b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 286b209e047SSebastian Reichel u32 val; 287b209e047SSebastian Reichel 28862aa292bSSebastian Reichel pm_runtime_get(omap_port->pdev); 28962aa292bSSebastian Reichel 29062aa292bSSebastian Reichel if (!pm_runtime_active(omap_port->pdev)) { 29162aa292bSSebastian Reichel dev_warn(&port->device, "ssi_start_pio called without runtime PM!\n"); 292ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 29362aa292bSSebastian Reichel return -EREMOTEIO; 29462aa292bSSebastian Reichel } 29562aa292bSSebastian Reichel 296b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_WRITE) { 297b209e047SSebastian Reichel val = SSI_DATAACCEPT(msg->channel); 298b209e047SSebastian Reichel /* Hold clocks for pio writes */ 29962aa292bSSebastian Reichel pm_runtime_get(omap_port->pdev); 300b209e047SSebastian Reichel } else { 301b209e047SSebastian Reichel val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED; 302b209e047SSebastian Reichel } 303b209e047SSebastian Reichel dev_dbg(&port->device, "Single %s transfer\n", 304b209e047SSebastian Reichel msg->ttype ? "write" : "read"); 305b209e047SSebastian Reichel val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 306b209e047SSebastian Reichel writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 307ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 308b209e047SSebastian Reichel msg->actual_len = 0; 309b209e047SSebastian Reichel msg->status = HSI_STATUS_PROCEEDING; 310b209e047SSebastian Reichel 311b209e047SSebastian Reichel return 0; 312b209e047SSebastian Reichel } 313b209e047SSebastian Reichel 314b209e047SSebastian Reichel static int ssi_start_transfer(struct list_head *queue) 315b209e047SSebastian Reichel { 316b209e047SSebastian Reichel struct hsi_msg *msg; 317b209e047SSebastian Reichel int lch = -1; 318b209e047SSebastian Reichel 319b209e047SSebastian Reichel if (list_empty(queue)) 320b209e047SSebastian Reichel return 0; 321b209e047SSebastian Reichel msg = list_first_entry(queue, struct hsi_msg, link); 322b209e047SSebastian Reichel if (msg->status != HSI_STATUS_QUEUED) 323b209e047SSebastian Reichel return 0; 324b209e047SSebastian Reichel if ((msg->sgt.nents) && (msg->sgt.sgl->length > sizeof(u32))) 325b209e047SSebastian Reichel lch = ssi_claim_lch(msg); 326b209e047SSebastian Reichel if (lch >= 0) 327b209e047SSebastian Reichel return ssi_start_dma(msg, lch); 328b209e047SSebastian Reichel else 329b209e047SSebastian Reichel return ssi_start_pio(msg); 330b209e047SSebastian Reichel } 331b209e047SSebastian Reichel 332b209e047SSebastian Reichel static int ssi_async_break(struct hsi_msg *msg) 333b209e047SSebastian Reichel { 334b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(msg->cl); 335b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 336b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 337b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 338b209e047SSebastian Reichel int err = 0; 339b209e047SSebastian Reichel u32 tmp; 340b209e047SSebastian Reichel 341b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 342b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_WRITE) { 343b209e047SSebastian Reichel if (omap_port->sst.mode != SSI_MODE_FRAME) { 344b209e047SSebastian Reichel err = -EINVAL; 345b209e047SSebastian Reichel goto out; 346b209e047SSebastian Reichel } 347b209e047SSebastian Reichel writel(1, omap_port->sst_base + SSI_SST_BREAK_REG); 348b209e047SSebastian Reichel msg->status = HSI_STATUS_COMPLETED; 349b209e047SSebastian Reichel msg->complete(msg); 350b209e047SSebastian Reichel } else { 351b209e047SSebastian Reichel if (omap_port->ssr.mode != SSI_MODE_FRAME) { 352b209e047SSebastian Reichel err = -EINVAL; 353b209e047SSebastian Reichel goto out; 354b209e047SSebastian Reichel } 355b209e047SSebastian Reichel spin_lock_bh(&omap_port->lock); 356b209e047SSebastian Reichel tmp = readl(omap_ssi->sys + 357b209e047SSebastian Reichel SSI_MPU_ENABLE_REG(port->num, 0)); 358b209e047SSebastian Reichel writel(tmp | SSI_BREAKDETECTED, 359b209e047SSebastian Reichel omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 360b209e047SSebastian Reichel msg->status = HSI_STATUS_PROCEEDING; 361b209e047SSebastian Reichel list_add_tail(&msg->link, &omap_port->brkqueue); 362b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 363b209e047SSebastian Reichel } 364b209e047SSebastian Reichel out: 365ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 366ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 367b209e047SSebastian Reichel 368b209e047SSebastian Reichel return err; 369b209e047SSebastian Reichel } 370b209e047SSebastian Reichel 371b209e047SSebastian Reichel static int ssi_async(struct hsi_msg *msg) 372b209e047SSebastian Reichel { 373b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(msg->cl); 374b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 375b209e047SSebastian Reichel struct list_head *queue; 376b209e047SSebastian Reichel int err = 0; 377b209e047SSebastian Reichel 378b209e047SSebastian Reichel BUG_ON(!msg); 379b209e047SSebastian Reichel 380b209e047SSebastian Reichel if (msg->sgt.nents > 1) 381b209e047SSebastian Reichel return -ENOSYS; /* TODO: Add sg support */ 382b209e047SSebastian Reichel 383b209e047SSebastian Reichel if (msg->break_frame) 384b209e047SSebastian Reichel return ssi_async_break(msg); 385b209e047SSebastian Reichel 386b209e047SSebastian Reichel if (msg->ttype) { 387b209e047SSebastian Reichel BUG_ON(msg->channel >= omap_port->sst.channels); 388b209e047SSebastian Reichel queue = &omap_port->txqueue[msg->channel]; 389b209e047SSebastian Reichel } else { 390b209e047SSebastian Reichel BUG_ON(msg->channel >= omap_port->ssr.channels); 391b209e047SSebastian Reichel queue = &omap_port->rxqueue[msg->channel]; 392b209e047SSebastian Reichel } 393b209e047SSebastian Reichel msg->status = HSI_STATUS_QUEUED; 39462aa292bSSebastian Reichel 39562aa292bSSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 396b209e047SSebastian Reichel spin_lock_bh(&omap_port->lock); 397b209e047SSebastian Reichel list_add_tail(&msg->link, queue); 398b209e047SSebastian Reichel err = ssi_start_transfer(queue); 399b209e047SSebastian Reichel if (err < 0) { 400b209e047SSebastian Reichel list_del(&msg->link); 401b209e047SSebastian Reichel msg->status = HSI_STATUS_ERROR; 402b209e047SSebastian Reichel } 403b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 404ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 405ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 406b209e047SSebastian Reichel dev_dbg(&port->device, "msg status %d ttype %d ch %d\n", 407b209e047SSebastian Reichel msg->status, msg->ttype, msg->channel); 408b209e047SSebastian Reichel 409b209e047SSebastian Reichel return err; 410b209e047SSebastian Reichel } 411b209e047SSebastian Reichel 412b209e047SSebastian Reichel static u32 ssi_calculate_div(struct hsi_controller *ssi) 413b209e047SSebastian Reichel { 414b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 415b209e047SSebastian Reichel u32 tx_fckrate = (u32) omap_ssi->fck_rate; 416b209e047SSebastian Reichel 417b209e047SSebastian Reichel /* / 2 : SSI TX clock is always half of the SSI functional clock */ 418b209e047SSebastian Reichel tx_fckrate >>= 1; 419b209e047SSebastian Reichel /* Round down when tx_fckrate % omap_ssi->max_speed == 0 */ 420b209e047SSebastian Reichel tx_fckrate--; 421b209e047SSebastian Reichel dev_dbg(&ssi->device, "TX div %d for fck_rate %lu Khz speed %d Kb/s\n", 422b209e047SSebastian Reichel tx_fckrate / omap_ssi->max_speed, omap_ssi->fck_rate, 423b209e047SSebastian Reichel omap_ssi->max_speed); 424b209e047SSebastian Reichel 425b209e047SSebastian Reichel return tx_fckrate / omap_ssi->max_speed; 426b209e047SSebastian Reichel } 427b209e047SSebastian Reichel 428b209e047SSebastian Reichel static void ssi_flush_queue(struct list_head *queue, struct hsi_client *cl) 429b209e047SSebastian Reichel { 430b209e047SSebastian Reichel struct list_head *node, *tmp; 431b209e047SSebastian Reichel struct hsi_msg *msg; 432b209e047SSebastian Reichel 433b209e047SSebastian Reichel list_for_each_safe(node, tmp, queue) { 434b209e047SSebastian Reichel msg = list_entry(node, struct hsi_msg, link); 435b209e047SSebastian Reichel if ((cl) && (cl != msg->cl)) 436b209e047SSebastian Reichel continue; 437b209e047SSebastian Reichel list_del(node); 438b209e047SSebastian Reichel pr_debug("flush queue: ch %d, msg %p len %d type %d ctxt %p\n", 439b209e047SSebastian Reichel msg->channel, msg, msg->sgt.sgl->length, 440b209e047SSebastian Reichel msg->ttype, msg->context); 441b209e047SSebastian Reichel if (msg->destructor) 442b209e047SSebastian Reichel msg->destructor(msg); 443b209e047SSebastian Reichel else 444b209e047SSebastian Reichel hsi_free_msg(msg); 445b209e047SSebastian Reichel } 446b209e047SSebastian Reichel } 447b209e047SSebastian Reichel 448b209e047SSebastian Reichel static int ssi_setup(struct hsi_client *cl) 449b209e047SSebastian Reichel { 450b209e047SSebastian Reichel struct hsi_port *port = to_hsi_port(cl->device.parent); 451b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 452b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 453b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 454b209e047SSebastian Reichel void __iomem *sst = omap_port->sst_base; 455b209e047SSebastian Reichel void __iomem *ssr = omap_port->ssr_base; 456b209e047SSebastian Reichel u32 div; 457b209e047SSebastian Reichel u32 val; 458b209e047SSebastian Reichel int err = 0; 459b209e047SSebastian Reichel 460b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 461b209e047SSebastian Reichel spin_lock_bh(&omap_port->lock); 462b209e047SSebastian Reichel if (cl->tx_cfg.speed) 463b209e047SSebastian Reichel omap_ssi->max_speed = cl->tx_cfg.speed; 464b209e047SSebastian Reichel div = ssi_calculate_div(ssi); 465b209e047SSebastian Reichel if (div > SSI_MAX_DIVISOR) { 466b209e047SSebastian Reichel dev_err(&cl->device, "Invalid TX speed %d Mb/s (div %d)\n", 467b209e047SSebastian Reichel cl->tx_cfg.speed, div); 468b209e047SSebastian Reichel err = -EINVAL; 469b209e047SSebastian Reichel goto out; 470b209e047SSebastian Reichel } 471b209e047SSebastian Reichel /* Set TX/RX module to sleep to stop TX/RX during cfg update */ 472b209e047SSebastian Reichel writel_relaxed(SSI_MODE_SLEEP, sst + SSI_SST_MODE_REG); 473b209e047SSebastian Reichel writel_relaxed(SSI_MODE_SLEEP, ssr + SSI_SSR_MODE_REG); 474b209e047SSebastian Reichel /* Flush posted write */ 475b209e047SSebastian Reichel val = readl(ssr + SSI_SSR_MODE_REG); 476b209e047SSebastian Reichel /* TX */ 477b209e047SSebastian Reichel writel_relaxed(31, sst + SSI_SST_FRAMESIZE_REG); 478b209e047SSebastian Reichel writel_relaxed(div, sst + SSI_SST_DIVISOR_REG); 479b209e047SSebastian Reichel writel_relaxed(cl->tx_cfg.num_hw_channels, sst + SSI_SST_CHANNELS_REG); 480b209e047SSebastian Reichel writel_relaxed(cl->tx_cfg.arb_mode, sst + SSI_SST_ARBMODE_REG); 481b209e047SSebastian Reichel writel_relaxed(cl->tx_cfg.mode, sst + SSI_SST_MODE_REG); 482b209e047SSebastian Reichel /* RX */ 483b209e047SSebastian Reichel writel_relaxed(31, ssr + SSI_SSR_FRAMESIZE_REG); 484b209e047SSebastian Reichel writel_relaxed(cl->rx_cfg.num_hw_channels, ssr + SSI_SSR_CHANNELS_REG); 485b209e047SSebastian Reichel writel_relaxed(0, ssr + SSI_SSR_TIMEOUT_REG); 486b209e047SSebastian Reichel /* Cleanup the break queue if we leave FRAME mode */ 487b209e047SSebastian Reichel if ((omap_port->ssr.mode == SSI_MODE_FRAME) && 488b209e047SSebastian Reichel (cl->rx_cfg.mode != SSI_MODE_FRAME)) 489b209e047SSebastian Reichel ssi_flush_queue(&omap_port->brkqueue, cl); 490b209e047SSebastian Reichel writel_relaxed(cl->rx_cfg.mode, ssr + SSI_SSR_MODE_REG); 491b209e047SSebastian Reichel omap_port->channels = max(cl->rx_cfg.num_hw_channels, 492b209e047SSebastian Reichel cl->tx_cfg.num_hw_channels); 493b209e047SSebastian Reichel /* Shadow registering for OFF mode */ 494b209e047SSebastian Reichel /* SST */ 495b209e047SSebastian Reichel omap_port->sst.divisor = div; 496b209e047SSebastian Reichel omap_port->sst.frame_size = 31; 497b209e047SSebastian Reichel omap_port->sst.channels = cl->tx_cfg.num_hw_channels; 498b209e047SSebastian Reichel omap_port->sst.arb_mode = cl->tx_cfg.arb_mode; 499b209e047SSebastian Reichel omap_port->sst.mode = cl->tx_cfg.mode; 500b209e047SSebastian Reichel /* SSR */ 501b209e047SSebastian Reichel omap_port->ssr.frame_size = 31; 502b209e047SSebastian Reichel omap_port->ssr.timeout = 0; 503b209e047SSebastian Reichel omap_port->ssr.channels = cl->rx_cfg.num_hw_channels; 504b209e047SSebastian Reichel omap_port->ssr.mode = cl->rx_cfg.mode; 505b209e047SSebastian Reichel out: 506b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 507ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 508ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 509b209e047SSebastian Reichel 510b209e047SSebastian Reichel return err; 511b209e047SSebastian Reichel } 512b209e047SSebastian Reichel 513b209e047SSebastian Reichel static int ssi_flush(struct hsi_client *cl) 514b209e047SSebastian Reichel { 515b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(cl); 516b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 517b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 518b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 519b209e047SSebastian Reichel struct hsi_msg *msg; 520b209e047SSebastian Reichel void __iomem *sst = omap_port->sst_base; 521b209e047SSebastian Reichel void __iomem *ssr = omap_port->ssr_base; 522b209e047SSebastian Reichel unsigned int i; 523b209e047SSebastian Reichel u32 err; 524b209e047SSebastian Reichel 525b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 526b209e047SSebastian Reichel spin_lock_bh(&omap_port->lock); 5274bcf7414SSebastian Reichel 5284bcf7414SSebastian Reichel /* stop all ssi communication */ 5294bcf7414SSebastian Reichel pinctrl_pm_select_idle_state(omap_port->pdev); 5304bcf7414SSebastian Reichel udelay(1); /* wait for racing frames */ 5314bcf7414SSebastian Reichel 532b209e047SSebastian Reichel /* Stop all DMA transfers */ 533b209e047SSebastian Reichel for (i = 0; i < SSI_MAX_GDD_LCH; i++) { 534b209e047SSebastian Reichel msg = omap_ssi->gdd_trn[i].msg; 535b209e047SSebastian Reichel if (!msg || (port != hsi_get_port(msg->cl))) 536b209e047SSebastian Reichel continue; 537b209e047SSebastian Reichel writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); 538b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_READ) 539ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 540b209e047SSebastian Reichel omap_ssi->gdd_trn[i].msg = NULL; 541b209e047SSebastian Reichel } 542b209e047SSebastian Reichel /* Flush all SST buffers */ 543b209e047SSebastian Reichel writel_relaxed(0, sst + SSI_SST_BUFSTATE_REG); 544b209e047SSebastian Reichel writel_relaxed(0, sst + SSI_SST_TXSTATE_REG); 545b209e047SSebastian Reichel /* Flush all SSR buffers */ 546b209e047SSebastian Reichel writel_relaxed(0, ssr + SSI_SSR_RXSTATE_REG); 547b209e047SSebastian Reichel writel_relaxed(0, ssr + SSI_SSR_BUFSTATE_REG); 548b209e047SSebastian Reichel /* Flush all errors */ 549b209e047SSebastian Reichel err = readl(ssr + SSI_SSR_ERROR_REG); 550b209e047SSebastian Reichel writel_relaxed(err, ssr + SSI_SSR_ERRORACK_REG); 551b209e047SSebastian Reichel /* Flush break */ 552b209e047SSebastian Reichel writel_relaxed(0, ssr + SSI_SSR_BREAK_REG); 553b209e047SSebastian Reichel /* Clear interrupts */ 554b209e047SSebastian Reichel writel_relaxed(0, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 555b209e047SSebastian Reichel writel_relaxed(0xffffff00, 556b209e047SSebastian Reichel omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 557b209e047SSebastian Reichel writel_relaxed(0, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 558b209e047SSebastian Reichel writel(0xff, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG); 559b209e047SSebastian Reichel /* Dequeue all pending requests */ 560b209e047SSebastian Reichel for (i = 0; i < omap_port->channels; i++) { 561b209e047SSebastian Reichel /* Release write clocks */ 562b209e047SSebastian Reichel if (!list_empty(&omap_port->txqueue[i])) 563ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 564b209e047SSebastian Reichel ssi_flush_queue(&omap_port->txqueue[i], NULL); 565b209e047SSebastian Reichel ssi_flush_queue(&omap_port->rxqueue[i], NULL); 566b209e047SSebastian Reichel } 567b209e047SSebastian Reichel ssi_flush_queue(&omap_port->brkqueue, NULL); 5684bcf7414SSebastian Reichel 5694bcf7414SSebastian Reichel /* Resume SSI communication */ 5704bcf7414SSebastian Reichel pinctrl_pm_select_default_state(omap_port->pdev); 5714bcf7414SSebastian Reichel 572b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 573ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 574ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 575b209e047SSebastian Reichel 576b209e047SSebastian Reichel return 0; 577b209e047SSebastian Reichel } 578b209e047SSebastian Reichel 5797c5d8162SSebastian Reichel static void start_tx_work(struct work_struct *work) 5807c5d8162SSebastian Reichel { 5817c5d8162SSebastian Reichel struct omap_ssi_port *omap_port = 5827c5d8162SSebastian Reichel container_of(work, struct omap_ssi_port, work); 5837c5d8162SSebastian Reichel struct hsi_port *port = to_hsi_port(omap_port->dev); 5847c5d8162SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 5857c5d8162SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 5867c5d8162SSebastian Reichel 5877c5d8162SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */ 5887c5d8162SSebastian Reichel writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); 5897c5d8162SSebastian Reichel } 5907c5d8162SSebastian Reichel 591b209e047SSebastian Reichel static int ssi_start_tx(struct hsi_client *cl) 592b209e047SSebastian Reichel { 593b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(cl); 594b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 595b209e047SSebastian Reichel 596b209e047SSebastian Reichel dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount); 597b209e047SSebastian Reichel 598b209e047SSebastian Reichel spin_lock_bh(&omap_port->wk_lock); 599b209e047SSebastian Reichel if (omap_port->wk_refcount++) { 600b209e047SSebastian Reichel spin_unlock_bh(&omap_port->wk_lock); 601b209e047SSebastian Reichel return 0; 602b209e047SSebastian Reichel } 603b209e047SSebastian Reichel spin_unlock_bh(&omap_port->wk_lock); 604b209e047SSebastian Reichel 6057c5d8162SSebastian Reichel schedule_work(&omap_port->work); 6067c5d8162SSebastian Reichel 607b209e047SSebastian Reichel return 0; 608b209e047SSebastian Reichel } 609b209e047SSebastian Reichel 610b209e047SSebastian Reichel static int ssi_stop_tx(struct hsi_client *cl) 611b209e047SSebastian Reichel { 612b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(cl); 613b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 614b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 615b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 616b209e047SSebastian Reichel 617b209e047SSebastian Reichel dev_dbg(&port->device, "Wake out low %d\n", omap_port->wk_refcount); 618b209e047SSebastian Reichel 619b209e047SSebastian Reichel spin_lock_bh(&omap_port->wk_lock); 620b209e047SSebastian Reichel BUG_ON(!omap_port->wk_refcount); 621b209e047SSebastian Reichel if (--omap_port->wk_refcount) { 622b209e047SSebastian Reichel spin_unlock_bh(&omap_port->wk_lock); 623b209e047SSebastian Reichel return 0; 624b209e047SSebastian Reichel } 625b209e047SSebastian Reichel writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); 626b209e047SSebastian Reichel spin_unlock_bh(&omap_port->wk_lock); 627b209e047SSebastian Reichel 628ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 629ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); /* Release clocks */ 630ad60db2fSSebastian Reichel 6317c5d8162SSebastian Reichel 632b209e047SSebastian Reichel return 0; 633b209e047SSebastian Reichel } 634b209e047SSebastian Reichel 635b209e047SSebastian Reichel static void ssi_transfer(struct omap_ssi_port *omap_port, 636b209e047SSebastian Reichel struct list_head *queue) 637b209e047SSebastian Reichel { 638b209e047SSebastian Reichel struct hsi_msg *msg; 639b209e047SSebastian Reichel int err = -1; 640b209e047SSebastian Reichel 641604fdfa4SSebastian Reichel pm_runtime_get(omap_port->pdev); 642b209e047SSebastian Reichel spin_lock_bh(&omap_port->lock); 643b209e047SSebastian Reichel while (err < 0) { 644b209e047SSebastian Reichel err = ssi_start_transfer(queue); 645b209e047SSebastian Reichel if (err < 0) { 646b209e047SSebastian Reichel msg = list_first_entry(queue, struct hsi_msg, link); 647b209e047SSebastian Reichel msg->status = HSI_STATUS_ERROR; 648b209e047SSebastian Reichel msg->actual_len = 0; 649b209e047SSebastian Reichel list_del(&msg->link); 650b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 651b209e047SSebastian Reichel msg->complete(msg); 652b209e047SSebastian Reichel spin_lock_bh(&omap_port->lock); 653b209e047SSebastian Reichel } 654b209e047SSebastian Reichel } 655b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 656ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 657ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 658b209e047SSebastian Reichel } 659b209e047SSebastian Reichel 660b209e047SSebastian Reichel static void ssi_cleanup_queues(struct hsi_client *cl) 661b209e047SSebastian Reichel { 662b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(cl); 663b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 664b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 665b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 666b209e047SSebastian Reichel struct hsi_msg *msg; 667b209e047SSebastian Reichel unsigned int i; 668b209e047SSebastian Reichel u32 rxbufstate = 0; 669b209e047SSebastian Reichel u32 txbufstate = 0; 670b209e047SSebastian Reichel u32 status = SSI_ERROROCCURED; 671b209e047SSebastian Reichel u32 tmp; 672b209e047SSebastian Reichel 673b209e047SSebastian Reichel ssi_flush_queue(&omap_port->brkqueue, cl); 674b209e047SSebastian Reichel if (list_empty(&omap_port->brkqueue)) 675b209e047SSebastian Reichel status |= SSI_BREAKDETECTED; 676b209e047SSebastian Reichel 677b209e047SSebastian Reichel for (i = 0; i < omap_port->channels; i++) { 678b209e047SSebastian Reichel if (list_empty(&omap_port->txqueue[i])) 679b209e047SSebastian Reichel continue; 680b209e047SSebastian Reichel msg = list_first_entry(&omap_port->txqueue[i], struct hsi_msg, 681b209e047SSebastian Reichel link); 682b209e047SSebastian Reichel if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) { 683b209e047SSebastian Reichel txbufstate |= (1 << i); 684b209e047SSebastian Reichel status |= SSI_DATAACCEPT(i); 685b209e047SSebastian Reichel /* Release the clocks writes, also GDD ones */ 686ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 687ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 688b209e047SSebastian Reichel } 689b209e047SSebastian Reichel ssi_flush_queue(&omap_port->txqueue[i], cl); 690b209e047SSebastian Reichel } 691b209e047SSebastian Reichel for (i = 0; i < omap_port->channels; i++) { 692b209e047SSebastian Reichel if (list_empty(&omap_port->rxqueue[i])) 693b209e047SSebastian Reichel continue; 694b209e047SSebastian Reichel msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg, 695b209e047SSebastian Reichel link); 696b209e047SSebastian Reichel if ((msg->cl == cl) && (msg->status == HSI_STATUS_PROCEEDING)) { 697b209e047SSebastian Reichel rxbufstate |= (1 << i); 698b209e047SSebastian Reichel status |= SSI_DATAAVAILABLE(i); 699b209e047SSebastian Reichel } 700b209e047SSebastian Reichel ssi_flush_queue(&omap_port->rxqueue[i], cl); 701b209e047SSebastian Reichel /* Check if we keep the error detection interrupt armed */ 702b209e047SSebastian Reichel if (!list_empty(&omap_port->rxqueue[i])) 703b209e047SSebastian Reichel status &= ~SSI_ERROROCCURED; 704b209e047SSebastian Reichel } 705b209e047SSebastian Reichel /* Cleanup write buffers */ 706b209e047SSebastian Reichel tmp = readl(omap_port->sst_base + SSI_SST_BUFSTATE_REG); 707b209e047SSebastian Reichel tmp &= ~txbufstate; 708b209e047SSebastian Reichel writel_relaxed(tmp, omap_port->sst_base + SSI_SST_BUFSTATE_REG); 709b209e047SSebastian Reichel /* Cleanup read buffers */ 710b209e047SSebastian Reichel tmp = readl(omap_port->ssr_base + SSI_SSR_BUFSTATE_REG); 711b209e047SSebastian Reichel tmp &= ~rxbufstate; 712b209e047SSebastian Reichel writel_relaxed(tmp, omap_port->ssr_base + SSI_SSR_BUFSTATE_REG); 713b209e047SSebastian Reichel /* Disarm and ack pending interrupts */ 714b209e047SSebastian Reichel tmp = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 715b209e047SSebastian Reichel tmp &= ~status; 716b209e047SSebastian Reichel writel_relaxed(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 717b209e047SSebastian Reichel writel_relaxed(status, omap_ssi->sys + 718b209e047SSebastian Reichel SSI_MPU_STATUS_REG(port->num, 0)); 719b209e047SSebastian Reichel } 720b209e047SSebastian Reichel 721b209e047SSebastian Reichel static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl) 722b209e047SSebastian Reichel { 723b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 724b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(cl); 725b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 726b209e047SSebastian Reichel struct hsi_msg *msg; 727b209e047SSebastian Reichel unsigned int i; 728b209e047SSebastian Reichel u32 val = 0; 729b209e047SSebastian Reichel u32 tmp; 730b209e047SSebastian Reichel 731b209e047SSebastian Reichel for (i = 0; i < SSI_MAX_GDD_LCH; i++) { 732b209e047SSebastian Reichel msg = omap_ssi->gdd_trn[i].msg; 733b209e047SSebastian Reichel if ((!msg) || (msg->cl != cl)) 734b209e047SSebastian Reichel continue; 735b209e047SSebastian Reichel writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); 736b209e047SSebastian Reichel val |= (1 << i); 737b209e047SSebastian Reichel /* 738b209e047SSebastian Reichel * Clock references for write will be handled in 739b209e047SSebastian Reichel * ssi_cleanup_queues 740b209e047SSebastian Reichel */ 741ad60db2fSSebastian Reichel if (msg->ttype == HSI_MSG_READ) { 742ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 743ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 744ad60db2fSSebastian Reichel } 745b209e047SSebastian Reichel omap_ssi->gdd_trn[i].msg = NULL; 746b209e047SSebastian Reichel } 747b209e047SSebastian Reichel tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 748b209e047SSebastian Reichel tmp &= ~val; 749b209e047SSebastian Reichel writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 750b209e047SSebastian Reichel writel(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_STATUS_REG); 751b209e047SSebastian Reichel } 752b209e047SSebastian Reichel 753b209e047SSebastian Reichel static int ssi_set_port_mode(struct omap_ssi_port *omap_port, u32 mode) 754b209e047SSebastian Reichel { 755b209e047SSebastian Reichel writel(mode, omap_port->sst_base + SSI_SST_MODE_REG); 756b209e047SSebastian Reichel writel(mode, omap_port->ssr_base + SSI_SSR_MODE_REG); 757b209e047SSebastian Reichel /* OCP barrier */ 758b209e047SSebastian Reichel mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG); 759b209e047SSebastian Reichel 760b209e047SSebastian Reichel return 0; 761b209e047SSebastian Reichel } 762b209e047SSebastian Reichel 763b209e047SSebastian Reichel static int ssi_release(struct hsi_client *cl) 764b209e047SSebastian Reichel { 765b209e047SSebastian Reichel struct hsi_port *port = hsi_get_port(cl); 766b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 767b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 768b209e047SSebastian Reichel 769b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 770fa1572d9SSebastian Reichel spin_lock_bh(&omap_port->lock); 771b209e047SSebastian Reichel /* Stop all the pending DMA requests for that client */ 772b209e047SSebastian Reichel ssi_cleanup_gdd(ssi, cl); 773b209e047SSebastian Reichel /* Now cleanup all the queues */ 774b209e047SSebastian Reichel ssi_cleanup_queues(cl); 775b209e047SSebastian Reichel /* If it is the last client of the port, do extra checks and cleanup */ 776b209e047SSebastian Reichel if (port->claimed <= 1) { 777b209e047SSebastian Reichel /* 778b209e047SSebastian Reichel * Drop the clock reference for the incoming wake line 779b209e047SSebastian Reichel * if it is still kept high by the other side. 780b209e047SSebastian Reichel */ 7812083057aSSebastian Reichel if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) 782b209e047SSebastian Reichel pm_runtime_put_sync(omap_port->pdev); 783fa1572d9SSebastian Reichel pm_runtime_get(omap_port->pdev); 784b209e047SSebastian Reichel /* Stop any SSI TX/RX without a client */ 785b209e047SSebastian Reichel ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); 786b209e047SSebastian Reichel omap_port->sst.mode = SSI_MODE_SLEEP; 787b209e047SSebastian Reichel omap_port->ssr.mode = SSI_MODE_SLEEP; 788fa1572d9SSebastian Reichel pm_runtime_put(omap_port->pdev); 789b209e047SSebastian Reichel WARN_ON(omap_port->wk_refcount != 0); 790b209e047SSebastian Reichel } 791b209e047SSebastian Reichel spin_unlock_bh(&omap_port->lock); 792ad60db2fSSebastian Reichel pm_runtime_put_sync(omap_port->pdev); 793b209e047SSebastian Reichel 794b209e047SSebastian Reichel return 0; 795b209e047SSebastian Reichel } 796b209e047SSebastian Reichel 797b209e047SSebastian Reichel 798b209e047SSebastian Reichel 799b209e047SSebastian Reichel static void ssi_error(struct hsi_port *port) 800b209e047SSebastian Reichel { 801b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 802b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 803b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 804b209e047SSebastian Reichel struct hsi_msg *msg; 805b209e047SSebastian Reichel unsigned int i; 806b209e047SSebastian Reichel u32 err; 807b209e047SSebastian Reichel u32 val; 808b209e047SSebastian Reichel u32 tmp; 809b209e047SSebastian Reichel 810b209e047SSebastian Reichel /* ACK error */ 811b209e047SSebastian Reichel err = readl(omap_port->ssr_base + SSI_SSR_ERROR_REG); 812b209e047SSebastian Reichel dev_err(&port->device, "SSI error: 0x%02x\n", err); 813b209e047SSebastian Reichel if (!err) { 814b209e047SSebastian Reichel dev_dbg(&port->device, "spurious SSI error ignored!\n"); 815b209e047SSebastian Reichel return; 816b209e047SSebastian Reichel } 817b209e047SSebastian Reichel spin_lock(&omap_ssi->lock); 818b209e047SSebastian Reichel /* Cancel all GDD read transfers */ 819b209e047SSebastian Reichel for (i = 0, val = 0; i < SSI_MAX_GDD_LCH; i++) { 820b209e047SSebastian Reichel msg = omap_ssi->gdd_trn[i].msg; 821b209e047SSebastian Reichel if ((msg) && (msg->ttype == HSI_MSG_READ)) { 822b209e047SSebastian Reichel writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); 823b209e047SSebastian Reichel val |= (1 << i); 824b209e047SSebastian Reichel omap_ssi->gdd_trn[i].msg = NULL; 825b209e047SSebastian Reichel } 826b209e047SSebastian Reichel } 827b209e047SSebastian Reichel tmp = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 828b209e047SSebastian Reichel tmp &= ~val; 829b209e047SSebastian Reichel writel_relaxed(tmp, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 830b209e047SSebastian Reichel spin_unlock(&omap_ssi->lock); 831b209e047SSebastian Reichel /* Cancel all PIO read transfers */ 832b209e047SSebastian Reichel spin_lock(&omap_port->lock); 833b209e047SSebastian Reichel tmp = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 834b209e047SSebastian Reichel tmp &= 0xfeff00ff; /* Disable error & all dataavailable interrupts */ 835b209e047SSebastian Reichel writel_relaxed(tmp, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 836b209e047SSebastian Reichel /* ACK error */ 837b209e047SSebastian Reichel writel_relaxed(err, omap_port->ssr_base + SSI_SSR_ERRORACK_REG); 838b209e047SSebastian Reichel writel_relaxed(SSI_ERROROCCURED, 839b209e047SSebastian Reichel omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 840b209e047SSebastian Reichel /* Signal the error all current pending read requests */ 841b209e047SSebastian Reichel for (i = 0; i < omap_port->channels; i++) { 842b209e047SSebastian Reichel if (list_empty(&omap_port->rxqueue[i])) 843b209e047SSebastian Reichel continue; 844b209e047SSebastian Reichel msg = list_first_entry(&omap_port->rxqueue[i], struct hsi_msg, 845b209e047SSebastian Reichel link); 846b209e047SSebastian Reichel list_del(&msg->link); 847b209e047SSebastian Reichel msg->status = HSI_STATUS_ERROR; 848b209e047SSebastian Reichel spin_unlock(&omap_port->lock); 849b209e047SSebastian Reichel msg->complete(msg); 850b209e047SSebastian Reichel /* Now restart queued reads if any */ 851b209e047SSebastian Reichel ssi_transfer(omap_port, &omap_port->rxqueue[i]); 852b209e047SSebastian Reichel spin_lock(&omap_port->lock); 853b209e047SSebastian Reichel } 854b209e047SSebastian Reichel spin_unlock(&omap_port->lock); 855b209e047SSebastian Reichel } 856b209e047SSebastian Reichel 857b209e047SSebastian Reichel static void ssi_break_complete(struct hsi_port *port) 858b209e047SSebastian Reichel { 859b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 860b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 861b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 862b209e047SSebastian Reichel struct hsi_msg *msg; 863b209e047SSebastian Reichel struct hsi_msg *tmp; 864b209e047SSebastian Reichel u32 val; 865b209e047SSebastian Reichel 866b209e047SSebastian Reichel dev_dbg(&port->device, "HWBREAK received\n"); 867b209e047SSebastian Reichel 868b209e047SSebastian Reichel spin_lock(&omap_port->lock); 869b209e047SSebastian Reichel val = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 870b209e047SSebastian Reichel val &= ~SSI_BREAKDETECTED; 871b209e047SSebastian Reichel writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 872b209e047SSebastian Reichel writel_relaxed(0, omap_port->ssr_base + SSI_SSR_BREAK_REG); 873b209e047SSebastian Reichel writel(SSI_BREAKDETECTED, 874b209e047SSebastian Reichel omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 875b209e047SSebastian Reichel spin_unlock(&omap_port->lock); 876b209e047SSebastian Reichel 877b209e047SSebastian Reichel list_for_each_entry_safe(msg, tmp, &omap_port->brkqueue, link) { 878b209e047SSebastian Reichel msg->status = HSI_STATUS_COMPLETED; 879b209e047SSebastian Reichel spin_lock(&omap_port->lock); 880b209e047SSebastian Reichel list_del(&msg->link); 881b209e047SSebastian Reichel spin_unlock(&omap_port->lock); 882b209e047SSebastian Reichel msg->complete(msg); 883b209e047SSebastian Reichel } 884b209e047SSebastian Reichel 885b209e047SSebastian Reichel } 886b209e047SSebastian Reichel 887b209e047SSebastian Reichel static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) 888b209e047SSebastian Reichel { 889b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 890b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 891b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 892b209e047SSebastian Reichel struct hsi_msg *msg; 893b209e047SSebastian Reichel u32 *buf; 894b209e047SSebastian Reichel u32 reg; 895b209e047SSebastian Reichel u32 val; 896b209e047SSebastian Reichel 897c4a62573SSebastian Reichel spin_lock_bh(&omap_port->lock); 898b209e047SSebastian Reichel msg = list_first_entry(queue, struct hsi_msg, link); 899b209e047SSebastian Reichel if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) { 900b209e047SSebastian Reichel msg->actual_len = 0; 901b209e047SSebastian Reichel msg->status = HSI_STATUS_PENDING; 902b209e047SSebastian Reichel } 903b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_WRITE) 904b209e047SSebastian Reichel val = SSI_DATAACCEPT(msg->channel); 905b209e047SSebastian Reichel else 906b209e047SSebastian Reichel val = SSI_DATAAVAILABLE(msg->channel); 907b209e047SSebastian Reichel if (msg->status == HSI_STATUS_PROCEEDING) { 908b209e047SSebastian Reichel buf = sg_virt(msg->sgt.sgl) + msg->actual_len; 909b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_WRITE) 910b209e047SSebastian Reichel writel(*buf, omap_port->sst_base + 911b209e047SSebastian Reichel SSI_SST_BUFFER_CH_REG(msg->channel)); 912b209e047SSebastian Reichel else 913b209e047SSebastian Reichel *buf = readl(omap_port->ssr_base + 914b209e047SSebastian Reichel SSI_SSR_BUFFER_CH_REG(msg->channel)); 915b209e047SSebastian Reichel dev_dbg(&port->device, "ch %d ttype %d 0x%08x\n", msg->channel, 916b209e047SSebastian Reichel msg->ttype, *buf); 917b209e047SSebastian Reichel msg->actual_len += sizeof(*buf); 918b209e047SSebastian Reichel if (msg->actual_len >= msg->sgt.sgl->length) 919b209e047SSebastian Reichel msg->status = HSI_STATUS_COMPLETED; 920b209e047SSebastian Reichel /* 921b209e047SSebastian Reichel * Wait for the last written frame to be really sent before 922b209e047SSebastian Reichel * we call the complete callback 923b209e047SSebastian Reichel */ 924b209e047SSebastian Reichel if ((msg->status == HSI_STATUS_PROCEEDING) || 925b209e047SSebastian Reichel ((msg->status == HSI_STATUS_COMPLETED) && 926b209e047SSebastian Reichel (msg->ttype == HSI_MSG_WRITE))) { 927b209e047SSebastian Reichel writel(val, omap_ssi->sys + 928b209e047SSebastian Reichel SSI_MPU_STATUS_REG(port->num, 0)); 929c4a62573SSebastian Reichel spin_unlock_bh(&omap_port->lock); 930b209e047SSebastian Reichel 931b209e047SSebastian Reichel return; 932b209e047SSebastian Reichel } 933b209e047SSebastian Reichel 934b209e047SSebastian Reichel } 935b209e047SSebastian Reichel /* Transfer completed at this point */ 936b209e047SSebastian Reichel reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 937b209e047SSebastian Reichel if (msg->ttype == HSI_MSG_WRITE) { 938b209e047SSebastian Reichel /* Release clocks for write transfer */ 939ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 940ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 941b209e047SSebastian Reichel } 942b209e047SSebastian Reichel reg &= ~val; 943b209e047SSebastian Reichel writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 944b209e047SSebastian Reichel writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); 945b209e047SSebastian Reichel list_del(&msg->link); 946c4a62573SSebastian Reichel spin_unlock_bh(&omap_port->lock); 947b209e047SSebastian Reichel msg->complete(msg); 948b209e047SSebastian Reichel ssi_transfer(omap_port, queue); 949b209e047SSebastian Reichel } 950b209e047SSebastian Reichel 951c4a62573SSebastian Reichel static irqreturn_t ssi_pio_thread(int irq, void *ssi_port) 952b209e047SSebastian Reichel { 953b209e047SSebastian Reichel struct hsi_port *port = (struct hsi_port *)ssi_port; 954b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 955b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 956b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 957b209e047SSebastian Reichel void __iomem *sys = omap_ssi->sys; 958b209e047SSebastian Reichel unsigned int ch; 959b209e047SSebastian Reichel u32 status_reg; 960b209e047SSebastian Reichel 961b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 962c4a62573SSebastian Reichel 963c4a62573SSebastian Reichel do { 964b209e047SSebastian Reichel status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); 965b209e047SSebastian Reichel status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); 966b209e047SSebastian Reichel 967b209e047SSebastian Reichel for (ch = 0; ch < omap_port->channels; ch++) { 968b209e047SSebastian Reichel if (status_reg & SSI_DATAACCEPT(ch)) 969b209e047SSebastian Reichel ssi_pio_complete(port, &omap_port->txqueue[ch]); 970b209e047SSebastian Reichel if (status_reg & SSI_DATAAVAILABLE(ch)) 971b209e047SSebastian Reichel ssi_pio_complete(port, &omap_port->rxqueue[ch]); 972b209e047SSebastian Reichel } 973b209e047SSebastian Reichel if (status_reg & SSI_BREAKDETECTED) 974b209e047SSebastian Reichel ssi_break_complete(port); 975b209e047SSebastian Reichel if (status_reg & SSI_ERROROCCURED) 976b209e047SSebastian Reichel ssi_error(port); 977b209e047SSebastian Reichel 978b209e047SSebastian Reichel status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); 979b209e047SSebastian Reichel status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); 980b209e047SSebastian Reichel 981c4a62573SSebastian Reichel /* TODO: sleep if we retry? */ 982c4a62573SSebastian Reichel } while (status_reg); 983b209e047SSebastian Reichel 984ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 985ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 986ad60db2fSSebastian Reichel 987b209e047SSebastian Reichel return IRQ_HANDLED; 988b209e047SSebastian Reichel } 989b209e047SSebastian Reichel 990cb70e4c1SSebastian Reichel static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port) 991b209e047SSebastian Reichel { 992b209e047SSebastian Reichel struct hsi_port *port = (struct hsi_port *)ssi_port; 993b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 994b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 995b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 996b209e047SSebastian Reichel 997b209e047SSebastian Reichel if (ssi_wakein(port)) { 998b209e047SSebastian Reichel /** 999b209e047SSebastian Reichel * We can have a quick High-Low-High transition in the line. 1000b209e047SSebastian Reichel * In such a case if we have long interrupt latencies, 1001b209e047SSebastian Reichel * we can miss the low event or get twice a high event. 1002b209e047SSebastian Reichel * This workaround will avoid breaking the clock reference 1003b209e047SSebastian Reichel * count when such a situation ocurrs. 1004b209e047SSebastian Reichel */ 10052083057aSSebastian Reichel if (!test_and_set_bit(SSI_WAKE_EN, &omap_port->flags)) 1006b209e047SSebastian Reichel pm_runtime_get_sync(omap_port->pdev); 1007b209e047SSebastian Reichel dev_dbg(&ssi->device, "Wake in high\n"); 1008b209e047SSebastian Reichel if (omap_port->wktest) { /* FIXME: HACK ! To be removed */ 1009b209e047SSebastian Reichel writel(SSI_WAKE(0), 1010b209e047SSebastian Reichel omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); 1011b209e047SSebastian Reichel } 1012b209e047SSebastian Reichel hsi_event(port, HSI_EVENT_START_RX); 1013b209e047SSebastian Reichel } else { 1014b209e047SSebastian Reichel dev_dbg(&ssi->device, "Wake in low\n"); 1015b209e047SSebastian Reichel if (omap_port->wktest) { /* FIXME: HACK ! To be removed */ 1016b209e047SSebastian Reichel writel(SSI_WAKE(0), 1017b209e047SSebastian Reichel omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); 1018b209e047SSebastian Reichel } 1019b209e047SSebastian Reichel hsi_event(port, HSI_EVENT_STOP_RX); 1020ad60db2fSSebastian Reichel if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) { 1021ad60db2fSSebastian Reichel pm_runtime_mark_last_busy(omap_port->pdev); 1022ad60db2fSSebastian Reichel pm_runtime_put_autosuspend(omap_port->pdev); 1023ad60db2fSSebastian Reichel } 1024b209e047SSebastian Reichel } 1025b209e047SSebastian Reichel 1026b209e047SSebastian Reichel return IRQ_HANDLED; 1027b209e047SSebastian Reichel } 1028b209e047SSebastian Reichel 10298c009f1fSSebastian Reichel static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd) 1030b209e047SSebastian Reichel { 1031b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1032b209e047SSebastian Reichel int err; 1033b209e047SSebastian Reichel 1034b74d4954SAndrey Utkin err = platform_get_irq(pd, 0); 1035c1030cd4SStephen Boyd if (err < 0) 1036b74d4954SAndrey Utkin return err; 1037b74d4954SAndrey Utkin omap_port->irq = err; 1038c4a62573SSebastian Reichel err = devm_request_threaded_irq(&port->device, omap_port->irq, NULL, 1039c4a62573SSebastian Reichel ssi_pio_thread, IRQF_ONESHOT, "SSI PORT", port); 1040b209e047SSebastian Reichel if (err < 0) 1041b209e047SSebastian Reichel dev_err(&port->device, "Request IRQ %d failed (%d)\n", 1042b209e047SSebastian Reichel omap_port->irq, err); 1043b209e047SSebastian Reichel return err; 1044b209e047SSebastian Reichel } 1045b209e047SSebastian Reichel 10468c009f1fSSebastian Reichel static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd) 1047b209e047SSebastian Reichel { 1048b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1049b209e047SSebastian Reichel int cawake_irq; 1050b209e047SSebastian Reichel int err; 1051b209e047SSebastian Reichel 105273e6ce09SSebastian Reichel if (!omap_port->wake_gpio) { 1053b209e047SSebastian Reichel omap_port->wake_irq = -1; 1054b209e047SSebastian Reichel return 0; 1055b209e047SSebastian Reichel } 1056b209e047SSebastian Reichel 105773e6ce09SSebastian Reichel cawake_irq = gpiod_to_irq(omap_port->wake_gpio); 1058b209e047SSebastian Reichel omap_port->wake_irq = cawake_irq; 1059cb70e4c1SSebastian Reichel 1060cb70e4c1SSebastian Reichel err = devm_request_threaded_irq(&port->device, cawake_irq, NULL, 1061cb70e4c1SSebastian Reichel ssi_wake_thread, 1062cb70e4c1SSebastian Reichel IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 1063cb70e4c1SSebastian Reichel "SSI cawake", port); 1064b209e047SSebastian Reichel if (err < 0) 1065b209e047SSebastian Reichel dev_err(&port->device, "Request Wake in IRQ %d failed %d\n", 1066b209e047SSebastian Reichel cawake_irq, err); 1067b209e047SSebastian Reichel err = enable_irq_wake(cawake_irq); 1068b209e047SSebastian Reichel if (err < 0) 1069b209e047SSebastian Reichel dev_err(&port->device, "Enable wake on the wakeline in irq %d failed %d\n", 1070b209e047SSebastian Reichel cawake_irq, err); 1071b209e047SSebastian Reichel 1072b209e047SSebastian Reichel return err; 1073b209e047SSebastian Reichel } 1074b209e047SSebastian Reichel 10750845e1f2SSebastian Reichel static void ssi_queues_init(struct omap_ssi_port *omap_port) 1076b209e047SSebastian Reichel { 1077b209e047SSebastian Reichel unsigned int ch; 1078b209e047SSebastian Reichel 1079b209e047SSebastian Reichel for (ch = 0; ch < SSI_MAX_CHANNELS; ch++) { 1080b209e047SSebastian Reichel INIT_LIST_HEAD(&omap_port->txqueue[ch]); 1081b209e047SSebastian Reichel INIT_LIST_HEAD(&omap_port->rxqueue[ch]); 1082b209e047SSebastian Reichel } 1083b209e047SSebastian Reichel INIT_LIST_HEAD(&omap_port->brkqueue); 1084b209e047SSebastian Reichel } 1085b209e047SSebastian Reichel 10860845e1f2SSebastian Reichel static int ssi_port_get_iomem(struct platform_device *pd, 1087b209e047SSebastian Reichel const char *name, void __iomem **pbase, dma_addr_t *phy) 1088b209e047SSebastian Reichel { 1089b209e047SSebastian Reichel struct hsi_port *port = platform_get_drvdata(pd); 1090b209e047SSebastian Reichel struct resource *mem; 1091b209e047SSebastian Reichel struct resource *ioarea; 1092b209e047SSebastian Reichel void __iomem *base; 1093b209e047SSebastian Reichel 1094b209e047SSebastian Reichel mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name); 1095b209e047SSebastian Reichel if (!mem) { 1096b209e047SSebastian Reichel dev_err(&pd->dev, "IO memory region missing (%s)\n", name); 1097b209e047SSebastian Reichel return -ENXIO; 1098b209e047SSebastian Reichel } 1099b209e047SSebastian Reichel ioarea = devm_request_mem_region(&port->device, mem->start, 1100b209e047SSebastian Reichel resource_size(mem), dev_name(&pd->dev)); 1101b209e047SSebastian Reichel if (!ioarea) { 1102b209e047SSebastian Reichel dev_err(&pd->dev, "%s IO memory region request failed\n", 1103b209e047SSebastian Reichel mem->name); 1104b209e047SSebastian Reichel return -ENXIO; 1105b209e047SSebastian Reichel } 1106b209e047SSebastian Reichel base = devm_ioremap(&port->device, mem->start, resource_size(mem)); 1107b209e047SSebastian Reichel if (!base) { 1108b209e047SSebastian Reichel dev_err(&pd->dev, "%s IO remap failed\n", mem->name); 1109b209e047SSebastian Reichel return -ENXIO; 1110b209e047SSebastian Reichel } 1111b209e047SSebastian Reichel *pbase = base; 1112b209e047SSebastian Reichel 1113b209e047SSebastian Reichel if (phy) 1114b209e047SSebastian Reichel *phy = mem->start; 1115b209e047SSebastian Reichel 1116b209e047SSebastian Reichel return 0; 1117b209e047SSebastian Reichel } 1118b209e047SSebastian Reichel 11190845e1f2SSebastian Reichel static int ssi_port_probe(struct platform_device *pd) 1120b209e047SSebastian Reichel { 1121b209e047SSebastian Reichel struct device_node *np = pd->dev.of_node; 1122b209e047SSebastian Reichel struct hsi_port *port; 1123b209e047SSebastian Reichel struct omap_ssi_port *omap_port; 1124b209e047SSebastian Reichel struct hsi_controller *ssi = dev_get_drvdata(pd->dev.parent); 1125b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 112673e6ce09SSebastian Reichel struct gpio_desc *cawake_gpio = NULL; 1127b209e047SSebastian Reichel u32 port_id; 1128b209e047SSebastian Reichel int err; 1129b209e047SSebastian Reichel 1130b209e047SSebastian Reichel dev_dbg(&pd->dev, "init ssi port...\n"); 1131b209e047SSebastian Reichel 1132b209e047SSebastian Reichel if (!ssi->port || !omap_ssi->port) { 1133b209e047SSebastian Reichel dev_err(&pd->dev, "ssi controller not initialized!\n"); 1134b209e047SSebastian Reichel err = -ENODEV; 1135b209e047SSebastian Reichel goto error; 1136b209e047SSebastian Reichel } 1137b209e047SSebastian Reichel 1138b209e047SSebastian Reichel /* get id of first uninitialized port in controller */ 1139b209e047SSebastian Reichel for (port_id = 0; port_id < ssi->num_ports && omap_ssi->port[port_id]; 1140b209e047SSebastian Reichel port_id++) 1141b209e047SSebastian Reichel ; 1142b209e047SSebastian Reichel 1143b209e047SSebastian Reichel if (port_id >= ssi->num_ports) { 1144b209e047SSebastian Reichel dev_err(&pd->dev, "port id out of range!\n"); 1145b209e047SSebastian Reichel err = -ENODEV; 1146b209e047SSebastian Reichel goto error; 1147b209e047SSebastian Reichel } 1148b209e047SSebastian Reichel 1149b209e047SSebastian Reichel port = ssi->port[port_id]; 1150b209e047SSebastian Reichel 1151b209e047SSebastian Reichel if (!np) { 1152b209e047SSebastian Reichel dev_err(&pd->dev, "missing device tree data\n"); 1153b209e047SSebastian Reichel err = -EINVAL; 1154b209e047SSebastian Reichel goto error; 1155b209e047SSebastian Reichel } 1156b209e047SSebastian Reichel 115773e6ce09SSebastian Reichel cawake_gpio = devm_gpiod_get(&pd->dev, "ti,ssi-cawake", GPIOD_IN); 115873e6ce09SSebastian Reichel if (IS_ERR(cawake_gpio)) { 115973e6ce09SSebastian Reichel err = PTR_ERR(cawake_gpio); 116073e6ce09SSebastian Reichel dev_err(&pd->dev, "couldn't get cawake gpio (err=%d)!\n", err); 1161b209e047SSebastian Reichel goto error; 1162b209e047SSebastian Reichel } 1163b209e047SSebastian Reichel 1164b209e047SSebastian Reichel omap_port = devm_kzalloc(&port->device, sizeof(*omap_port), GFP_KERNEL); 1165b209e047SSebastian Reichel if (!omap_port) { 1166b209e047SSebastian Reichel err = -ENOMEM; 1167b209e047SSebastian Reichel goto error; 1168b209e047SSebastian Reichel } 1169b209e047SSebastian Reichel omap_port->wake_gpio = cawake_gpio; 1170b209e047SSebastian Reichel omap_port->pdev = &pd->dev; 1171b209e047SSebastian Reichel omap_port->port_id = port_id; 1172b209e047SSebastian Reichel 11734e552310SSebastian Reichel INIT_DEFERRABLE_WORK(&omap_port->errqueue_work, ssi_process_errqueue); 11747c5d8162SSebastian Reichel INIT_WORK(&omap_port->work, start_tx_work); 11757c5d8162SSebastian Reichel 1176b209e047SSebastian Reichel /* initialize HSI port */ 1177b209e047SSebastian Reichel port->async = ssi_async; 1178b209e047SSebastian Reichel port->setup = ssi_setup; 1179b209e047SSebastian Reichel port->flush = ssi_flush; 1180b209e047SSebastian Reichel port->start_tx = ssi_start_tx; 1181b209e047SSebastian Reichel port->stop_tx = ssi_stop_tx; 1182b209e047SSebastian Reichel port->release = ssi_release; 1183b209e047SSebastian Reichel hsi_port_set_drvdata(port, omap_port); 1184b209e047SSebastian Reichel omap_ssi->port[port_id] = omap_port; 1185b209e047SSebastian Reichel 1186b209e047SSebastian Reichel platform_set_drvdata(pd, port); 1187b209e047SSebastian Reichel 1188b209e047SSebastian Reichel err = ssi_port_get_iomem(pd, "tx", &omap_port->sst_base, 1189b209e047SSebastian Reichel &omap_port->sst_dma); 1190b209e047SSebastian Reichel if (err < 0) 1191b209e047SSebastian Reichel goto error; 1192b209e047SSebastian Reichel err = ssi_port_get_iomem(pd, "rx", &omap_port->ssr_base, 1193b209e047SSebastian Reichel &omap_port->ssr_dma); 1194b209e047SSebastian Reichel if (err < 0) 1195b209e047SSebastian Reichel goto error; 1196b209e047SSebastian Reichel 1197b209e047SSebastian Reichel err = ssi_port_irq(port, pd); 1198b209e047SSebastian Reichel if (err < 0) 1199b209e047SSebastian Reichel goto error; 1200b209e047SSebastian Reichel err = ssi_wake_irq(port, pd); 1201b209e047SSebastian Reichel if (err < 0) 1202b209e047SSebastian Reichel goto error; 1203b209e047SSebastian Reichel 1204b209e047SSebastian Reichel ssi_queues_init(omap_port); 1205b209e047SSebastian Reichel spin_lock_init(&omap_port->lock); 1206b209e047SSebastian Reichel spin_lock_init(&omap_port->wk_lock); 1207b209e047SSebastian Reichel omap_port->dev = &port->device; 1208b209e047SSebastian Reichel 1209ad60db2fSSebastian Reichel pm_runtime_use_autosuspend(omap_port->pdev); 1210ad60db2fSSebastian Reichel pm_runtime_set_autosuspend_delay(omap_port->pdev, 250); 1211b209e047SSebastian Reichel pm_runtime_enable(omap_port->pdev); 1212b209e047SSebastian Reichel 1213b209e047SSebastian Reichel #ifdef CONFIG_DEBUG_FS 121442877c38SOsama Muhammad ssi_debug_add_port(omap_port, omap_ssi->dir); 1215b209e047SSebastian Reichel #endif 1216b209e047SSebastian Reichel 1217b209e047SSebastian Reichel hsi_add_clients_from_dt(port, np); 1218b209e047SSebastian Reichel 121973e6ce09SSebastian Reichel dev_info(&pd->dev, "ssi port %u successfully initialized\n", port_id); 1220b209e047SSebastian Reichel 1221b209e047SSebastian Reichel return 0; 1222b209e047SSebastian Reichel 1223b209e047SSebastian Reichel error: 1224b209e047SSebastian Reichel return err; 1225b209e047SSebastian Reichel } 1226b209e047SSebastian Reichel 1227*c076486bSUwe Kleine-König static void ssi_port_remove(struct platform_device *pd) 1228b209e047SSebastian Reichel { 1229b209e047SSebastian Reichel struct hsi_port *port = platform_get_drvdata(pd); 1230b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1231b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 1232b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 1233b209e047SSebastian Reichel 1234b209e047SSebastian Reichel #ifdef CONFIG_DEBUG_FS 1235b209e047SSebastian Reichel ssi_debug_remove_port(port); 1236b209e047SSebastian Reichel #endif 1237b209e047SSebastian Reichel 12384e552310SSebastian Reichel cancel_delayed_work_sync(&omap_port->errqueue_work); 12394e552310SSebastian Reichel 1240b209e047SSebastian Reichel hsi_port_unregister_clients(port); 1241b209e047SSebastian Reichel 1242b209e047SSebastian Reichel port->async = hsi_dummy_msg; 1243b209e047SSebastian Reichel port->setup = hsi_dummy_cl; 1244b209e047SSebastian Reichel port->flush = hsi_dummy_cl; 1245b209e047SSebastian Reichel port->start_tx = hsi_dummy_cl; 1246b209e047SSebastian Reichel port->stop_tx = hsi_dummy_cl; 1247b209e047SSebastian Reichel port->release = hsi_dummy_cl; 1248b209e047SSebastian Reichel 1249b209e047SSebastian Reichel omap_ssi->port[omap_port->port_id] = NULL; 1250b209e047SSebastian Reichel platform_set_drvdata(pd, NULL); 1251ad60db2fSSebastian Reichel 1252ad60db2fSSebastian Reichel pm_runtime_dont_use_autosuspend(&pd->dev); 1253b209e047SSebastian Reichel pm_runtime_disable(&pd->dev); 1254b209e047SSebastian Reichel } 1255b209e047SSebastian Reichel 1256c2f90a46SArnd Bergmann static int ssi_restore_divisor(struct omap_ssi_port *omap_port) 1257c2f90a46SArnd Bergmann { 1258c2f90a46SArnd Bergmann writel_relaxed(omap_port->sst.divisor, 1259c2f90a46SArnd Bergmann omap_port->sst_base + SSI_SST_DIVISOR_REG); 1260c2f90a46SArnd Bergmann 1261c2f90a46SArnd Bergmann return 0; 1262c2f90a46SArnd Bergmann } 1263c2f90a46SArnd Bergmann 1264c2f90a46SArnd Bergmann void omap_ssi_port_update_fclk(struct hsi_controller *ssi, 1265c2f90a46SArnd Bergmann struct omap_ssi_port *omap_port) 1266c2f90a46SArnd Bergmann { 1267c2f90a46SArnd Bergmann /* update divisor */ 1268c2f90a46SArnd Bergmann u32 div = ssi_calculate_div(ssi); 1269c2f90a46SArnd Bergmann omap_port->sst.divisor = div; 1270c2f90a46SArnd Bergmann ssi_restore_divisor(omap_port); 1271c2f90a46SArnd Bergmann } 1272c2f90a46SArnd Bergmann 127396a1c18aSRafael J. Wysocki #ifdef CONFIG_PM 1274b209e047SSebastian Reichel static int ssi_save_port_ctx(struct omap_ssi_port *omap_port) 1275b209e047SSebastian Reichel { 1276b209e047SSebastian Reichel struct hsi_port *port = to_hsi_port(omap_port->dev); 1277b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 1278b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 1279b209e047SSebastian Reichel 1280b209e047SSebastian Reichel omap_port->sys_mpu_enable = readl(omap_ssi->sys + 1281b209e047SSebastian Reichel SSI_MPU_ENABLE_REG(port->num, 0)); 1282b209e047SSebastian Reichel 1283b209e047SSebastian Reichel return 0; 1284b209e047SSebastian Reichel } 1285b209e047SSebastian Reichel 1286b209e047SSebastian Reichel static int ssi_restore_port_ctx(struct omap_ssi_port *omap_port) 1287b209e047SSebastian Reichel { 1288b209e047SSebastian Reichel struct hsi_port *port = to_hsi_port(omap_port->dev); 1289b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 1290b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 1291b209e047SSebastian Reichel void __iomem *base; 1292b209e047SSebastian Reichel 1293b209e047SSebastian Reichel writel_relaxed(omap_port->sys_mpu_enable, 1294b209e047SSebastian Reichel omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 1295b209e047SSebastian Reichel 1296b209e047SSebastian Reichel /* SST context */ 1297b209e047SSebastian Reichel base = omap_port->sst_base; 1298b209e047SSebastian Reichel writel_relaxed(omap_port->sst.frame_size, base + SSI_SST_FRAMESIZE_REG); 1299b209e047SSebastian Reichel writel_relaxed(omap_port->sst.channels, base + SSI_SST_CHANNELS_REG); 1300b209e047SSebastian Reichel writel_relaxed(omap_port->sst.arb_mode, base + SSI_SST_ARBMODE_REG); 1301b209e047SSebastian Reichel 1302b209e047SSebastian Reichel /* SSR context */ 1303b209e047SSebastian Reichel base = omap_port->ssr_base; 1304b209e047SSebastian Reichel writel_relaxed(omap_port->ssr.frame_size, base + SSI_SSR_FRAMESIZE_REG); 1305b209e047SSebastian Reichel writel_relaxed(omap_port->ssr.channels, base + SSI_SSR_CHANNELS_REG); 1306b209e047SSebastian Reichel writel_relaxed(omap_port->ssr.timeout, base + SSI_SSR_TIMEOUT_REG); 1307b209e047SSebastian Reichel 1308b209e047SSebastian Reichel return 0; 1309b209e047SSebastian Reichel } 1310b209e047SSebastian Reichel 1311b209e047SSebastian Reichel static int ssi_restore_port_mode(struct omap_ssi_port *omap_port) 1312b209e047SSebastian Reichel { 1313b209e047SSebastian Reichel u32 mode; 1314b209e047SSebastian Reichel 1315b209e047SSebastian Reichel writel_relaxed(omap_port->sst.mode, 1316b209e047SSebastian Reichel omap_port->sst_base + SSI_SST_MODE_REG); 1317b209e047SSebastian Reichel writel_relaxed(omap_port->ssr.mode, 1318b209e047SSebastian Reichel omap_port->ssr_base + SSI_SSR_MODE_REG); 1319b209e047SSebastian Reichel /* OCP barrier */ 1320b209e047SSebastian Reichel mode = readl(omap_port->ssr_base + SSI_SSR_MODE_REG); 1321b209e047SSebastian Reichel 1322b209e047SSebastian Reichel return 0; 1323b209e047SSebastian Reichel } 1324b209e047SSebastian Reichel 1325b209e047SSebastian Reichel static int omap_ssi_port_runtime_suspend(struct device *dev) 1326b209e047SSebastian Reichel { 1327b209e047SSebastian Reichel struct hsi_port *port = dev_get_drvdata(dev); 1328b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1329b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 1330b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 1331b209e047SSebastian Reichel 1332b209e047SSebastian Reichel dev_dbg(dev, "port runtime suspend!\n"); 1333b209e047SSebastian Reichel 1334b209e047SSebastian Reichel ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); 1335b209e047SSebastian Reichel if (omap_ssi->get_loss) 1336b209e047SSebastian Reichel omap_port->loss_count = 1337b209e047SSebastian Reichel omap_ssi->get_loss(ssi->device.parent); 1338b209e047SSebastian Reichel ssi_save_port_ctx(omap_port); 1339b209e047SSebastian Reichel 1340b209e047SSebastian Reichel return 0; 1341b209e047SSebastian Reichel } 1342b209e047SSebastian Reichel 1343b209e047SSebastian Reichel static int omap_ssi_port_runtime_resume(struct device *dev) 1344b209e047SSebastian Reichel { 1345b209e047SSebastian Reichel struct hsi_port *port = dev_get_drvdata(dev); 1346b209e047SSebastian Reichel struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 1347b209e047SSebastian Reichel struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 1348b209e047SSebastian Reichel struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); 1349b209e047SSebastian Reichel 1350b209e047SSebastian Reichel dev_dbg(dev, "port runtime resume!\n"); 1351b209e047SSebastian Reichel 1352b209e047SSebastian Reichel if ((omap_ssi->get_loss) && (omap_port->loss_count == 1353b209e047SSebastian Reichel omap_ssi->get_loss(ssi->device.parent))) 1354b209e047SSebastian Reichel goto mode; /* We always need to restore the mode & TX divisor */ 1355b209e047SSebastian Reichel 1356b209e047SSebastian Reichel ssi_restore_port_ctx(omap_port); 1357b209e047SSebastian Reichel 1358b209e047SSebastian Reichel mode: 1359b209e047SSebastian Reichel ssi_restore_divisor(omap_port); 1360b209e047SSebastian Reichel ssi_restore_port_mode(omap_port); 1361b209e047SSebastian Reichel 1362b209e047SSebastian Reichel return 0; 1363b209e047SSebastian Reichel } 1364b209e047SSebastian Reichel 1365b209e047SSebastian Reichel static const struct dev_pm_ops omap_ssi_port_pm_ops = { 1366b209e047SSebastian Reichel SET_RUNTIME_PM_OPS(omap_ssi_port_runtime_suspend, 1367b209e047SSebastian Reichel omap_ssi_port_runtime_resume, NULL) 1368b209e047SSebastian Reichel }; 1369b209e047SSebastian Reichel 1370b209e047SSebastian Reichel #define DEV_PM_OPS (&omap_ssi_port_pm_ops) 1371b209e047SSebastian Reichel #else 1372b209e047SSebastian Reichel #define DEV_PM_OPS NULL 1373b209e047SSebastian Reichel #endif 1374b209e047SSebastian Reichel 1375b209e047SSebastian Reichel 1376b209e047SSebastian Reichel #ifdef CONFIG_OF 1377b209e047SSebastian Reichel static const struct of_device_id omap_ssi_port_of_match[] = { 1378b209e047SSebastian Reichel { .compatible = "ti,omap3-ssi-port", }, 1379b209e047SSebastian Reichel {}, 1380b209e047SSebastian Reichel }; 1381b209e047SSebastian Reichel MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match); 1382b209e047SSebastian Reichel #else 1383b209e047SSebastian Reichel #define omap_ssi_port_of_match NULL 1384b209e047SSebastian Reichel #endif 1385b209e047SSebastian Reichel 13860fae1989SSebastian Reichel struct platform_driver ssi_port_pdriver = { 13870845e1f2SSebastian Reichel .probe = ssi_port_probe, 1388*c076486bSUwe Kleine-König .remove_new = ssi_port_remove, 1389b209e047SSebastian Reichel .driver = { 1390b209e047SSebastian Reichel .name = "omap_ssi_port", 1391b209e047SSebastian Reichel .of_match_table = omap_ssi_port_of_match, 1392b209e047SSebastian Reichel .pm = DEV_PM_OPS, 1393b209e047SSebastian Reichel }, 1394b209e047SSebastian Reichel }; 1395