From 73e6ce09c068d42d627874019899f1138740a6c5 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 00:04:29 +0200 Subject: HSI: omap_ssi_port: switch to gpiod API Simplify driver by switching to new gpio descriptor based API. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi.c | 1 - drivers/hsi/controllers/omap_ssi.h | 4 ++-- drivers/hsi/controllers/omap_ssi_port.c | 31 ++++++++++--------------------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c index 27b91f14ba7a..c582229d1cd2 100644 --- a/drivers/hsi/controllers/omap_ssi.c +++ b/drivers/hsi/controllers/omap_ssi.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index f9aaf37262be..1fa028078a3c 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -97,7 +97,7 @@ struct omap_ssi_port { struct list_head brkqueue; unsigned int irq; int wake_irq; - int wake_gpio; + struct gpio_desc *wake_gpio; struct tasklet_struct pio_tasklet; struct tasklet_struct wake_tasklet; bool wktest:1; /* FIXME: HACK to be removed */ diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index e80a66e20998..948bdc7946fb 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include "omap_ssi_regs.h" @@ -43,7 +43,7 @@ static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) static inline unsigned int ssi_wakein(struct hsi_port *port) { struct omap_ssi_port *omap_port = hsi_port_drvdata(port); - return gpio_get_value(omap_port->wake_gpio); + return gpiod_get_value(omap_port->wake_gpio); } #ifdef CONFIG_DEBUG_FS @@ -1036,12 +1036,12 @@ static int __init ssi_wake_irq(struct hsi_port *port, int cawake_irq; int err; - if (omap_port->wake_gpio == -1) { + if (!omap_port->wake_gpio) { omap_port->wake_irq = -1; return 0; } - cawake_irq = gpio_to_irq(omap_port->wake_gpio); + cawake_irq = gpiod_to_irq(omap_port->wake_gpio); omap_port->wake_irq = cawake_irq; tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet, @@ -1111,7 +1111,7 @@ static int __init ssi_port_probe(struct platform_device *pd) struct omap_ssi_port *omap_port; struct hsi_controller *ssi = dev_get_drvdata(pd->dev.parent); struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - int cawake_gpio = 0; + struct gpio_desc *cawake_gpio = NULL; u32 port_id; int err; @@ -1147,20 +1147,10 @@ static int __init ssi_port_probe(struct platform_device *pd) goto error; } - err = of_get_named_gpio(np, "ti,ssi-cawake-gpio", 0); - if (err < 0) { - dev_err(&pd->dev, "DT data is missing cawake gpio (err=%d)\n", - err); - goto error; - } - cawake_gpio = err; - - err = devm_gpio_request_one(&port->device, cawake_gpio, GPIOF_DIR_IN, - "cawake"); - if (err) { - dev_err(&pd->dev, "could not request cawake gpio (err=%d)!\n", - err); - err = -ENXIO; + cawake_gpio = devm_gpiod_get(&pd->dev, "ti,ssi-cawake", GPIOD_IN); + if (IS_ERR(cawake_gpio)) { + err = PTR_ERR(cawake_gpio); + dev_err(&pd->dev, "couldn't get cawake gpio (err=%d)!\n", err); goto error; } @@ -1219,8 +1209,7 @@ static int __init ssi_port_probe(struct platform_device *pd) hsi_add_clients_from_dt(port, np); - dev_info(&pd->dev, "ssi port %u successfully initialized (cawake=%d)\n", - port_id, cawake_gpio); + dev_info(&pd->dev, "ssi port %u successfully initialized\n", port_id); return 0; -- cgit v1.2.3 From f704e1103e05a65c83b85c66d947e57bc20d6edd Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 00:07:35 +0200 Subject: HSI: omap_ssi: fix module unloading Removal of ssi controller debugfs directory must happen after the clients have been removed from it. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c index c582229d1cd2..2dd46b219af2 100644 --- a/drivers/hsi/controllers/omap_ssi.c +++ b/drivers/hsi/controllers/omap_ssi.c @@ -526,6 +526,9 @@ static int __exit ssi_remove(struct platform_device *pd) { struct hsi_controller *ssi = platform_get_drvdata(pd); + /* cleanup of of_platform_populate() call */ + device_for_each_child(&pd->dev, NULL, ssi_remove_ports); + #ifdef CONFIG_DEBUG_FS ssi_debug_remove_ctrl(ssi); #endif @@ -534,9 +537,6 @@ static int __exit ssi_remove(struct platform_device *pd) pm_runtime_disable(&pd->dev); - /* cleanup of of_platform_populate() call */ - device_for_each_child(&pd->dev, NULL, ssi_remove_ports); - return 0; } -- cgit v1.2.3 From 0845e1f20af100d1d4ac7cc111a8dfb790f94a16 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 01:01:06 +0200 Subject: HSI: omap_ssi: make sure probe stays available device can be unbind/rebind, so probe should stay available. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi.c | 17 +++++++++-------- drivers/hsi/controllers/omap_ssi_port.c | 19 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c index 2dd46b219af2..ffb921482e76 100644 --- a/drivers/hsi/controllers/omap_ssi.c +++ b/drivers/hsi/controllers/omap_ssi.c @@ -140,7 +140,7 @@ static const struct file_operations ssi_gdd_regs_fops = { .release = single_release, }; -static int __init ssi_debug_add_ctrl(struct hsi_controller *ssi) +static int ssi_debug_add_ctrl(struct hsi_controller *ssi) { struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); struct dentry *dir; @@ -290,7 +290,7 @@ static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) return rate; } -static int __init ssi_get_iomem(struct platform_device *pd, +static int ssi_get_iomem(struct platform_device *pd, const char *name, void __iomem **pbase, dma_addr_t *phy) { struct resource *mem; @@ -310,7 +310,7 @@ static int __init ssi_get_iomem(struct platform_device *pd, return 0; } -static int __init ssi_add_controller(struct hsi_controller *ssi, +static int ssi_add_controller(struct hsi_controller *ssi, struct platform_device *pd) { struct omap_ssi_controller *omap_ssi; @@ -386,7 +386,7 @@ out_err: return err; } -static int __init ssi_hw_init(struct hsi_controller *ssi) +static int ssi_hw_init(struct hsi_controller *ssi) { struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); unsigned int i; @@ -456,7 +456,7 @@ static int ssi_remove_ports(struct device *dev, void *c) return 0; } -static int __init ssi_probe(struct platform_device *pd) +static int ssi_probe(struct platform_device *pd) { struct platform_device *childpdev; struct device_node *np = pd->dev.of_node; @@ -522,7 +522,7 @@ out1: return err; } -static int __exit ssi_remove(struct platform_device *pd) +static int ssi_remove(struct platform_device *pd) { struct hsi_controller *ssi = platform_get_drvdata(pd); @@ -592,7 +592,8 @@ MODULE_DEVICE_TABLE(of, omap_ssi_of_match); #endif static struct platform_driver ssi_pdriver = { - .remove = __exit_p(ssi_remove), + .probe = ssi_probe, + .remove = ssi_remove, .driver = { .name = "omap_ssi", .pm = DEV_PM_OPS, @@ -600,7 +601,7 @@ static struct platform_driver ssi_pdriver = { }, }; -module_platform_driver_probe(ssi_pdriver, ssi_probe); +module_platform_driver(ssi_pdriver); MODULE_ALIAS("platform:omap_ssi"); MODULE_AUTHOR("Carlos Chinea "); diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 948bdc7946fb..530095ed39e7 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -171,7 +171,7 @@ static int ssi_div_set(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(ssi_sst_div_fops, ssi_div_get, ssi_div_set, "%llu\n"); -static int __init ssi_debug_add_port(struct omap_ssi_port *omap_port, +static int ssi_debug_add_port(struct omap_ssi_port *omap_port, struct dentry *dir) { struct hsi_port *port = to_hsi_port(omap_port->dev); @@ -1007,7 +1007,7 @@ static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port) return IRQ_HANDLED; } -static int __init ssi_port_irq(struct hsi_port *port, +static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd) { struct omap_ssi_port *omap_port = hsi_port_drvdata(port); @@ -1029,7 +1029,7 @@ static int __init ssi_port_irq(struct hsi_port *port, return err; } -static int __init ssi_wake_irq(struct hsi_port *port, +static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd) { struct omap_ssi_port *omap_port = hsi_port_drvdata(port); @@ -1060,7 +1060,7 @@ static int __init ssi_wake_irq(struct hsi_port *port, return err; } -static void __init ssi_queues_init(struct omap_ssi_port *omap_port) +static void ssi_queues_init(struct omap_ssi_port *omap_port) { unsigned int ch; @@ -1071,7 +1071,7 @@ static void __init ssi_queues_init(struct omap_ssi_port *omap_port) INIT_LIST_HEAD(&omap_port->brkqueue); } -static int __init ssi_port_get_iomem(struct platform_device *pd, +static int ssi_port_get_iomem(struct platform_device *pd, const char *name, void __iomem **pbase, dma_addr_t *phy) { struct hsi_port *port = platform_get_drvdata(pd); @@ -1104,7 +1104,7 @@ static int __init ssi_port_get_iomem(struct platform_device *pd, return 0; } -static int __init ssi_port_probe(struct platform_device *pd) +static int ssi_port_probe(struct platform_device *pd) { struct device_node *np = pd->dev.of_node; struct hsi_port *port; @@ -1217,7 +1217,7 @@ error: return err; } -static int __exit ssi_port_remove(struct platform_device *pd) +static int ssi_port_remove(struct platform_device *pd) { struct hsi_port *port = platform_get_drvdata(pd); struct omap_ssi_port *omap_port = hsi_port_drvdata(port); @@ -1370,7 +1370,8 @@ MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match); #endif static struct platform_driver ssi_port_pdriver = { - .remove = __exit_p(ssi_port_remove), + .probe = ssi_port_probe, + .remove = ssi_port_remove, .driver = { .name = "omap_ssi_port", .of_match_table = omap_ssi_port_of_match, @@ -1378,7 +1379,7 @@ static struct platform_driver ssi_port_pdriver = { }, }; -module_platform_driver_probe(ssi_port_pdriver, ssi_port_probe); +module_platform_driver(ssi_port_pdriver); MODULE_ALIAS("platform:omap_ssi_port"); MODULE_AUTHOR("Carlos Chinea "); -- cgit v1.2.3 From 2a57aba8503d5694ee113016cb1a107831b8236f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 03:23:27 +0200 Subject: HSI: omap_ssi: fix removal of port platform device This avoids removal of the HSI port device when only the platform port device should be removed and clears the POPULATED bit in the DT node, so that a new platform device is created when the driver is probed again. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c index ffb921482e76..68dfdaa19938 100644 --- a/drivers/hsi/controllers/omap_ssi.c +++ b/drivers/hsi/controllers/omap_ssi.c @@ -451,6 +451,10 @@ static int ssi_remove_ports(struct device *dev, void *c) { struct platform_device *pdev = to_platform_device(dev); + if (!dev->of_node) + return 0; + + of_node_clear_flag(dev->of_node, OF_POPULATED); of_device_unregister(pdev); return 0; -- cgit v1.2.3 From 0fae198988b873d30fe9ecb6a6271afb36df97e9 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 03:24:09 +0200 Subject: HSI: omap_ssi: built omap_ssi and omap_ssi_port into one module Merge omap_ssi and omap_ssi_port into one module. This fixes problems with module cycle dependencies introduced by future patches. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/Kconfig | 5 - drivers/hsi/controllers/Makefile | 4 +- drivers/hsi/controllers/omap_ssi.c | 614 ------------------------------- drivers/hsi/controllers/omap_ssi.h | 2 + drivers/hsi/controllers/omap_ssi_core.c | 629 ++++++++++++++++++++++++++++++++ drivers/hsi/controllers/omap_ssi_port.c | 16 +- 6 files changed, 634 insertions(+), 636 deletions(-) delete mode 100644 drivers/hsi/controllers/omap_ssi.c create mode 100644 drivers/hsi/controllers/omap_ssi_core.c diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig index 6aba27808172..084ec97eec64 100644 --- a/drivers/hsi/controllers/Kconfig +++ b/drivers/hsi/controllers/Kconfig @@ -12,8 +12,3 @@ config OMAP_SSI If you say Y here, you will enable the OMAP SSI hardware driver. If unsure, say N. - -config OMAP_SSI_PORT - tristate - default m if OMAP_SSI=m - default y if OMAP_SSI=y diff --git a/drivers/hsi/controllers/Makefile b/drivers/hsi/controllers/Makefile index d2665cf9c545..7aba9c7f71bb 100644 --- a/drivers/hsi/controllers/Makefile +++ b/drivers/hsi/controllers/Makefile @@ -2,5 +2,5 @@ # Makefile for HSI controllers drivers # -obj-$(CONFIG_OMAP_SSI) += omap_ssi.o -obj-$(CONFIG_OMAP_SSI_PORT) += omap_ssi_port.o +omap_ssi-objs += omap_ssi_core.o omap_ssi_port.o +obj-$(CONFIG_OMAP_SSI) += omap_ssi.o diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c deleted file mode 100644 index 68dfdaa19938..000000000000 --- a/drivers/hsi/controllers/omap_ssi.c +++ /dev/null @@ -1,614 +0,0 @@ -/* OMAP SSI driver. - * - * Copyright (C) 2010 Nokia Corporation. All rights reserved. - * Copyright (C) 2014 Sebastian Reichel - * - * Contact: Carlos Chinea - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "omap_ssi_regs.h" -#include "omap_ssi.h" - -/* For automatically allocated device IDs */ -static DEFINE_IDA(platform_omap_ssi_ida); - -#ifdef CONFIG_DEBUG_FS -static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused) -{ - struct hsi_controller *ssi = m->private; - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - void __iomem *sys = omap_ssi->sys; - - pm_runtime_get_sync(ssi->device.parent); - seq_printf(m, "REVISION\t: 0x%08x\n", readl(sys + SSI_REVISION_REG)); - seq_printf(m, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG)); - seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_REG)); - pm_runtime_put_sync(ssi->device.parent); - - return 0; -} - -static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused) -{ - struct hsi_controller *ssi = m->private; - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - void __iomem *gdd = omap_ssi->gdd; - void __iomem *sys = omap_ssi->sys; - int lch; - - pm_runtime_get_sync(ssi->device.parent); - - seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n", - readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG)); - seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n", - readl(sys + SSI_GDD_MPU_IRQ_ENABLE_REG)); - seq_printf(m, "HW_ID\t\t: 0x%08x\n", - readl(gdd + SSI_GDD_HW_ID_REG)); - seq_printf(m, "PPORT_ID\t: 0x%08x\n", - readl(gdd + SSI_GDD_PPORT_ID_REG)); - seq_printf(m, "MPORT_ID\t: 0x%08x\n", - readl(gdd + SSI_GDD_MPORT_ID_REG)); - seq_printf(m, "TEST\t\t: 0x%08x\n", - readl(gdd + SSI_GDD_TEST_REG)); - seq_printf(m, "GCR\t\t: 0x%08x\n", - readl(gdd + SSI_GDD_GCR_REG)); - - for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) { - seq_printf(m, "\nGDD LCH %d\n=========\n", lch); - seq_printf(m, "CSDP\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CSDP_REG(lch))); - seq_printf(m, "CCR\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CCR_REG(lch))); - seq_printf(m, "CICR\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CICR_REG(lch))); - seq_printf(m, "CSR\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CSR_REG(lch))); - seq_printf(m, "CSSA\t\t: 0x%08x\n", - readl(gdd + SSI_GDD_CSSA_REG(lch))); - seq_printf(m, "CDSA\t\t: 0x%08x\n", - readl(gdd + SSI_GDD_CDSA_REG(lch))); - seq_printf(m, "CEN\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CEN_REG(lch))); - seq_printf(m, "CSAC\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CSAC_REG(lch))); - seq_printf(m, "CDAC\t\t: 0x%04x\n", - readw(gdd + SSI_GDD_CDAC_REG(lch))); - seq_printf(m, "CLNK_CTRL\t: 0x%04x\n", - readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch))); - } - - pm_runtime_put_sync(ssi->device.parent); - - return 0; -} - -static int ssi_regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, ssi_debug_show, inode->i_private); -} - -static int ssi_gdd_regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, ssi_debug_gdd_show, inode->i_private); -} - -static const struct file_operations ssi_regs_fops = { - .open = ssi_regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations ssi_gdd_regs_fops = { - .open = ssi_gdd_regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int ssi_debug_add_ctrl(struct hsi_controller *ssi) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - struct dentry *dir; - - /* SSI controller */ - omap_ssi->dir = debugfs_create_dir(dev_name(&ssi->device), NULL); - if (!omap_ssi->dir) - return -ENOMEM; - - debugfs_create_file("regs", S_IRUGO, omap_ssi->dir, ssi, - &ssi_regs_fops); - /* SSI GDD (DMA) */ - dir = debugfs_create_dir("gdd", omap_ssi->dir); - if (!dir) - goto rback; - debugfs_create_file("regs", S_IRUGO, dir, ssi, &ssi_gdd_regs_fops); - - return 0; -rback: - debugfs_remove_recursive(omap_ssi->dir); - - return -ENOMEM; -} - -static void ssi_debug_remove_ctrl(struct hsi_controller *ssi) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - - debugfs_remove_recursive(omap_ssi->dir); -} -#endif /* CONFIG_DEBUG_FS */ - -/* - * FIXME: Horrible HACK needed until we remove the useless wakeline test - * in the CMT. To be removed !!!! - */ -void ssi_waketest(struct hsi_client *cl, unsigned int enable) -{ - struct hsi_port *port = hsi_get_port(cl); - struct omap_ssi_port *omap_port = hsi_port_drvdata(port); - struct hsi_controller *ssi = to_hsi_controller(port->device.parent); - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - - omap_port->wktest = !!enable; - if (omap_port->wktest) { - pm_runtime_get_sync(ssi->device.parent); - writel_relaxed(SSI_WAKE(0), - omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); - } else { - writel_relaxed(SSI_WAKE(0), - omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); - pm_runtime_put_sync(ssi->device.parent); - } -} -EXPORT_SYMBOL_GPL(ssi_waketest); - -static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg; - struct hsi_port *port = to_hsi_port(msg->cl->device.parent); - struct omap_ssi_port *omap_port = hsi_port_drvdata(port); - unsigned int dir; - u32 csr; - u32 val; - - spin_lock(&omap_ssi->lock); - - val = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); - val &= ~SSI_GDD_LCH(lch); - writel_relaxed(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); - - if (msg->ttype == HSI_MSG_READ) { - dir = DMA_FROM_DEVICE; - val = SSI_DATAAVAILABLE(msg->channel); - pm_runtime_put_sync(ssi->device.parent); - } else { - dir = DMA_TO_DEVICE; - val = SSI_DATAACCEPT(msg->channel); - /* Keep clocks reference for write pio event */ - } - dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir); - csr = readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch)); - omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */ - dev_dbg(&port->device, "DMA completed ch %d ttype %d\n", - msg->channel, msg->ttype); - spin_unlock(&omap_ssi->lock); - if (csr & SSI_CSR_TOUR) { /* Timeout error */ - msg->status = HSI_STATUS_ERROR; - msg->actual_len = 0; - spin_lock(&omap_port->lock); - list_del(&msg->link); /* Dequeue msg */ - spin_unlock(&omap_port->lock); - msg->complete(msg); - return; - } - spin_lock(&omap_port->lock); - val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); - writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); - spin_unlock(&omap_port->lock); - - msg->status = HSI_STATUS_COMPLETED; - msg->actual_len = sg_dma_len(msg->sgt.sgl); -} - -static void ssi_gdd_tasklet(unsigned long dev) -{ - struct hsi_controller *ssi = (struct hsi_controller *)dev; - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - void __iomem *sys = omap_ssi->sys; - unsigned int lch; - u32 status_reg; - - pm_runtime_get_sync(ssi->device.parent); - - status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG); - for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) { - if (status_reg & SSI_GDD_LCH(lch)) - ssi_gdd_complete(ssi, lch); - } - writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG); - status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG); - - pm_runtime_put_sync(ssi->device.parent); - - if (status_reg) - tasklet_hi_schedule(&omap_ssi->gdd_tasklet); - else - enable_irq(omap_ssi->gdd_irq); - -} - -static irqreturn_t ssi_gdd_isr(int irq, void *ssi) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - - tasklet_hi_schedule(&omap_ssi->gdd_tasklet); - disable_irq_nosync(irq); - - return IRQ_HANDLED; -} - -static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - unsigned long rate = clk_get_rate(omap_ssi->fck); - return rate; -} - -static int ssi_get_iomem(struct platform_device *pd, - const char *name, void __iomem **pbase, dma_addr_t *phy) -{ - struct resource *mem; - void __iomem *base; - struct hsi_controller *ssi = platform_get_drvdata(pd); - - mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name); - base = devm_ioremap_resource(&ssi->device, mem); - if (IS_ERR(base)) - return PTR_ERR(base); - - *pbase = base; - - if (phy) - *phy = mem->start; - - return 0; -} - -static int ssi_add_controller(struct hsi_controller *ssi, - struct platform_device *pd) -{ - struct omap_ssi_controller *omap_ssi; - int err; - - omap_ssi = devm_kzalloc(&ssi->device, sizeof(*omap_ssi), GFP_KERNEL); - if (!omap_ssi) { - dev_err(&pd->dev, "not enough memory for omap ssi\n"); - return -ENOMEM; - } - - err = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL); - if (err < 0) - goto out_err; - ssi->id = err; - - ssi->owner = THIS_MODULE; - ssi->device.parent = &pd->dev; - dev_set_name(&ssi->device, "ssi%d", ssi->id); - hsi_controller_set_drvdata(ssi, omap_ssi); - omap_ssi->dev = &ssi->device; - err = ssi_get_iomem(pd, "sys", &omap_ssi->sys, NULL); - if (err < 0) - goto out_err; - err = ssi_get_iomem(pd, "gdd", &omap_ssi->gdd, NULL); - if (err < 0) - goto out_err; - err = platform_get_irq_byname(pd, "gdd_mpu"); - if (err < 0) { - dev_err(&pd->dev, "GDD IRQ resource missing\n"); - goto out_err; - } - omap_ssi->gdd_irq = err; - tasklet_init(&omap_ssi->gdd_tasklet, ssi_gdd_tasklet, - (unsigned long)ssi); - err = devm_request_irq(&ssi->device, omap_ssi->gdd_irq, ssi_gdd_isr, - 0, "gdd_mpu", ssi); - if (err < 0) { - dev_err(&ssi->device, "Request GDD IRQ %d failed (%d)", - omap_ssi->gdd_irq, err); - goto out_err; - } - - omap_ssi->port = devm_kzalloc(&ssi->device, - sizeof(struct omap_ssi_port *) * ssi->num_ports, GFP_KERNEL); - if (!omap_ssi->port) { - err = -ENOMEM; - goto out_err; - } - - omap_ssi->fck = devm_clk_get(&ssi->device, "ssi_ssr_fck"); - if (IS_ERR(omap_ssi->fck)) { - dev_err(&pd->dev, "Could not acquire clock \"ssi_ssr_fck\": %li\n", - PTR_ERR(omap_ssi->fck)); - err = -ENODEV; - goto out_err; - } - - /* TODO: find register, which can be used to detect context loss */ - omap_ssi->get_loss = NULL; - - omap_ssi->max_speed = UINT_MAX; - spin_lock_init(&omap_ssi->lock); - err = hsi_register_controller(ssi); - - if (err < 0) - goto out_err; - - return 0; - -out_err: - ida_simple_remove(&platform_omap_ssi_ida, ssi->id); - return err; -} - -static int ssi_hw_init(struct hsi_controller *ssi) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - unsigned int i; - u32 val; - int err; - - err = pm_runtime_get_sync(ssi->device.parent); - if (err < 0) { - dev_err(&ssi->device, "runtime PM failed %d\n", err); - return err; - } - /* Reseting SSI controller */ - writel_relaxed(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG); - val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG); - for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) { - msleep(20); - val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG); - } - if (!(val & SSI_RESETDONE)) { - dev_err(&ssi->device, "SSI HW reset failed\n"); - pm_runtime_put_sync(ssi->device.parent); - return -EIO; - } - /* Reseting GDD */ - writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG); - /* Get FCK rate in KHz */ - omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000); - dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate); - /* Set default PM settings */ - val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART; - writel_relaxed(val, omap_ssi->sys + SSI_SYSCONFIG_REG); - omap_ssi->sysconfig = val; - writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG); - omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON; - pm_runtime_put_sync(ssi->device.parent); - - return 0; -} - -static void ssi_remove_controller(struct hsi_controller *ssi) -{ - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - int id = ssi->id; - tasklet_kill(&omap_ssi->gdd_tasklet); - hsi_unregister_controller(ssi); - ida_simple_remove(&platform_omap_ssi_ida, id); -} - -static inline int ssi_of_get_available_ports_count(const struct device_node *np) -{ - struct device_node *child; - int num = 0; - - for_each_available_child_of_node(np, child) - if (of_device_is_compatible(child, "ti,omap3-ssi-port")) - num++; - - return num; -} - -static int ssi_remove_ports(struct device *dev, void *c) -{ - struct platform_device *pdev = to_platform_device(dev); - - if (!dev->of_node) - return 0; - - of_node_clear_flag(dev->of_node, OF_POPULATED); - of_device_unregister(pdev); - - return 0; -} - -static int ssi_probe(struct platform_device *pd) -{ - struct platform_device *childpdev; - struct device_node *np = pd->dev.of_node; - struct device_node *child; - struct hsi_controller *ssi; - int err; - int num_ports; - - if (!np) { - dev_err(&pd->dev, "missing device tree data\n"); - return -EINVAL; - } - - num_ports = ssi_of_get_available_ports_count(np); - - ssi = hsi_alloc_controller(num_ports, GFP_KERNEL); - if (!ssi) { - dev_err(&pd->dev, "No memory for controller\n"); - return -ENOMEM; - } - - platform_set_drvdata(pd, ssi); - - err = ssi_add_controller(ssi, pd); - if (err < 0) - goto out1; - - pm_runtime_irq_safe(&pd->dev); - pm_runtime_enable(&pd->dev); - - err = ssi_hw_init(ssi); - if (err < 0) - goto out2; -#ifdef CONFIG_DEBUG_FS - err = ssi_debug_add_ctrl(ssi); - if (err < 0) - goto out2; -#endif - - for_each_available_child_of_node(np, child) { - if (!of_device_is_compatible(child, "ti,omap3-ssi-port")) - continue; - - childpdev = of_platform_device_create(child, NULL, &pd->dev); - if (!childpdev) { - err = -ENODEV; - dev_err(&pd->dev, "failed to create ssi controller port\n"); - goto out3; - } - } - - dev_info(&pd->dev, "ssi controller %d initialized (%d ports)!\n", - ssi->id, num_ports); - return err; -out3: - device_for_each_child(&pd->dev, NULL, ssi_remove_ports); -out2: - ssi_remove_controller(ssi); -out1: - platform_set_drvdata(pd, NULL); - pm_runtime_disable(&pd->dev); - - return err; -} - -static int ssi_remove(struct platform_device *pd) -{ - struct hsi_controller *ssi = platform_get_drvdata(pd); - - /* cleanup of of_platform_populate() call */ - device_for_each_child(&pd->dev, NULL, ssi_remove_ports); - -#ifdef CONFIG_DEBUG_FS - ssi_debug_remove_ctrl(ssi); -#endif - ssi_remove_controller(ssi); - platform_set_drvdata(pd, NULL); - - pm_runtime_disable(&pd->dev); - - return 0; -} - -#ifdef CONFIG_PM -static int omap_ssi_runtime_suspend(struct device *dev) -{ - struct hsi_controller *ssi = dev_get_drvdata(dev); - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - - dev_dbg(dev, "runtime suspend!\n"); - - if (omap_ssi->get_loss) - omap_ssi->loss_count = - omap_ssi->get_loss(ssi->device.parent); - - return 0; -} - -static int omap_ssi_runtime_resume(struct device *dev) -{ - struct hsi_controller *ssi = dev_get_drvdata(dev); - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - - dev_dbg(dev, "runtime resume!\n"); - - if ((omap_ssi->get_loss) && (omap_ssi->loss_count == - omap_ssi->get_loss(ssi->device.parent))) - return 0; - - writel_relaxed(omap_ssi->gdd_gcr, omap_ssi->gdd + SSI_GDD_GCR_REG); - - return 0; -} - -static const struct dev_pm_ops omap_ssi_pm_ops = { - SET_RUNTIME_PM_OPS(omap_ssi_runtime_suspend, omap_ssi_runtime_resume, - NULL) -}; - -#define DEV_PM_OPS (&omap_ssi_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - -#ifdef CONFIG_OF -static const struct of_device_id omap_ssi_of_match[] = { - { .compatible = "ti,omap3-ssi", }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_ssi_of_match); -#else -#define omap_ssi_of_match NULL -#endif - -static struct platform_driver ssi_pdriver = { - .probe = ssi_probe, - .remove = ssi_remove, - .driver = { - .name = "omap_ssi", - .pm = DEV_PM_OPS, - .of_match_table = omap_ssi_of_match, - }, -}; - -module_platform_driver(ssi_pdriver); - -MODULE_ALIAS("platform:omap_ssi"); -MODULE_AUTHOR("Carlos Chinea "); -MODULE_AUTHOR("Sebastian Reichel "); -MODULE_DESCRIPTION("Synchronous Serial Interface Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index 1fa028078a3c..e493321cb0c3 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -164,4 +164,6 @@ struct omap_ssi_controller { #endif }; +extern struct platform_driver ssi_port_pdriver; + #endif /* __LINUX_HSI_OMAP_SSI_H__ */ diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c new file mode 100644 index 000000000000..535c76038288 --- /dev/null +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -0,0 +1,629 @@ +/* OMAP SSI driver. + * + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * Copyright (C) 2014 Sebastian Reichel + * + * Contact: Carlos Chinea + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "omap_ssi_regs.h" +#include "omap_ssi.h" + +/* For automatically allocated device IDs */ +static DEFINE_IDA(platform_omap_ssi_ida); + +#ifdef CONFIG_DEBUG_FS +static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused) +{ + struct hsi_controller *ssi = m->private; + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + void __iomem *sys = omap_ssi->sys; + + pm_runtime_get_sync(ssi->device.parent); + seq_printf(m, "REVISION\t: 0x%08x\n", readl(sys + SSI_REVISION_REG)); + seq_printf(m, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG)); + seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_REG)); + pm_runtime_put_sync(ssi->device.parent); + + return 0; +} + +static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused) +{ + struct hsi_controller *ssi = m->private; + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + void __iomem *gdd = omap_ssi->gdd; + void __iomem *sys = omap_ssi->sys; + int lch; + + pm_runtime_get_sync(ssi->device.parent); + + seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n", + readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG)); + seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n", + readl(sys + SSI_GDD_MPU_IRQ_ENABLE_REG)); + seq_printf(m, "HW_ID\t\t: 0x%08x\n", + readl(gdd + SSI_GDD_HW_ID_REG)); + seq_printf(m, "PPORT_ID\t: 0x%08x\n", + readl(gdd + SSI_GDD_PPORT_ID_REG)); + seq_printf(m, "MPORT_ID\t: 0x%08x\n", + readl(gdd + SSI_GDD_MPORT_ID_REG)); + seq_printf(m, "TEST\t\t: 0x%08x\n", + readl(gdd + SSI_GDD_TEST_REG)); + seq_printf(m, "GCR\t\t: 0x%08x\n", + readl(gdd + SSI_GDD_GCR_REG)); + + for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) { + seq_printf(m, "\nGDD LCH %d\n=========\n", lch); + seq_printf(m, "CSDP\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CSDP_REG(lch))); + seq_printf(m, "CCR\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CCR_REG(lch))); + seq_printf(m, "CICR\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CICR_REG(lch))); + seq_printf(m, "CSR\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CSR_REG(lch))); + seq_printf(m, "CSSA\t\t: 0x%08x\n", + readl(gdd + SSI_GDD_CSSA_REG(lch))); + seq_printf(m, "CDSA\t\t: 0x%08x\n", + readl(gdd + SSI_GDD_CDSA_REG(lch))); + seq_printf(m, "CEN\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CEN_REG(lch))); + seq_printf(m, "CSAC\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CSAC_REG(lch))); + seq_printf(m, "CDAC\t\t: 0x%04x\n", + readw(gdd + SSI_GDD_CDAC_REG(lch))); + seq_printf(m, "CLNK_CTRL\t: 0x%04x\n", + readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch))); + } + + pm_runtime_put_sync(ssi->device.parent); + + return 0; +} + +static int ssi_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, ssi_debug_show, inode->i_private); +} + +static int ssi_gdd_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, ssi_debug_gdd_show, inode->i_private); +} + +static const struct file_operations ssi_regs_fops = { + .open = ssi_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations ssi_gdd_regs_fops = { + .open = ssi_gdd_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ssi_debug_add_ctrl(struct hsi_controller *ssi) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + struct dentry *dir; + + /* SSI controller */ + omap_ssi->dir = debugfs_create_dir(dev_name(&ssi->device), NULL); + if (!omap_ssi->dir) + return -ENOMEM; + + debugfs_create_file("regs", S_IRUGO, omap_ssi->dir, ssi, + &ssi_regs_fops); + /* SSI GDD (DMA) */ + dir = debugfs_create_dir("gdd", omap_ssi->dir); + if (!dir) + goto rback; + debugfs_create_file("regs", S_IRUGO, dir, ssi, &ssi_gdd_regs_fops); + + return 0; +rback: + debugfs_remove_recursive(omap_ssi->dir); + + return -ENOMEM; +} + +static void ssi_debug_remove_ctrl(struct hsi_controller *ssi) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + + debugfs_remove_recursive(omap_ssi->dir); +} +#endif /* CONFIG_DEBUG_FS */ + +/* + * FIXME: Horrible HACK needed until we remove the useless wakeline test + * in the CMT. To be removed !!!! + */ +void ssi_waketest(struct hsi_client *cl, unsigned int enable) +{ + struct hsi_port *port = hsi_get_port(cl); + struct omap_ssi_port *omap_port = hsi_port_drvdata(port); + struct hsi_controller *ssi = to_hsi_controller(port->device.parent); + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + + omap_port->wktest = !!enable; + if (omap_port->wktest) { + pm_runtime_get_sync(ssi->device.parent); + writel_relaxed(SSI_WAKE(0), + omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); + } else { + writel_relaxed(SSI_WAKE(0), + omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); + pm_runtime_put_sync(ssi->device.parent); + } +} +EXPORT_SYMBOL_GPL(ssi_waketest); + +static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg; + struct hsi_port *port = to_hsi_port(msg->cl->device.parent); + struct omap_ssi_port *omap_port = hsi_port_drvdata(port); + unsigned int dir; + u32 csr; + u32 val; + + spin_lock(&omap_ssi->lock); + + val = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); + val &= ~SSI_GDD_LCH(lch); + writel_relaxed(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); + + if (msg->ttype == HSI_MSG_READ) { + dir = DMA_FROM_DEVICE; + val = SSI_DATAAVAILABLE(msg->channel); + pm_runtime_put_sync(ssi->device.parent); + } else { + dir = DMA_TO_DEVICE; + val = SSI_DATAACCEPT(msg->channel); + /* Keep clocks reference for write pio event */ + } + dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir); + csr = readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch)); + omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */ + dev_dbg(&port->device, "DMA completed ch %d ttype %d\n", + msg->channel, msg->ttype); + spin_unlock(&omap_ssi->lock); + if (csr & SSI_CSR_TOUR) { /* Timeout error */ + msg->status = HSI_STATUS_ERROR; + msg->actual_len = 0; + spin_lock(&omap_port->lock); + list_del(&msg->link); /* Dequeue msg */ + spin_unlock(&omap_port->lock); + msg->complete(msg); + return; + } + spin_lock(&omap_port->lock); + val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); + writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); + spin_unlock(&omap_port->lock); + + msg->status = HSI_STATUS_COMPLETED; + msg->actual_len = sg_dma_len(msg->sgt.sgl); +} + +static void ssi_gdd_tasklet(unsigned long dev) +{ + struct hsi_controller *ssi = (struct hsi_controller *)dev; + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + void __iomem *sys = omap_ssi->sys; + unsigned int lch; + u32 status_reg; + + pm_runtime_get_sync(ssi->device.parent); + + status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG); + for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) { + if (status_reg & SSI_GDD_LCH(lch)) + ssi_gdd_complete(ssi, lch); + } + writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG); + status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG); + + pm_runtime_put_sync(ssi->device.parent); + + if (status_reg) + tasklet_hi_schedule(&omap_ssi->gdd_tasklet); + else + enable_irq(omap_ssi->gdd_irq); + +} + +static irqreturn_t ssi_gdd_isr(int irq, void *ssi) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + + tasklet_hi_schedule(&omap_ssi->gdd_tasklet); + disable_irq_nosync(irq); + + return IRQ_HANDLED; +} + +static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + unsigned long rate = clk_get_rate(omap_ssi->fck); + return rate; +} + +static int ssi_get_iomem(struct platform_device *pd, + const char *name, void __iomem **pbase, dma_addr_t *phy) +{ + struct resource *mem; + void __iomem *base; + struct hsi_controller *ssi = platform_get_drvdata(pd); + + mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name); + base = devm_ioremap_resource(&ssi->device, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + *pbase = base; + + if (phy) + *phy = mem->start; + + return 0; +} + +static int ssi_add_controller(struct hsi_controller *ssi, + struct platform_device *pd) +{ + struct omap_ssi_controller *omap_ssi; + int err; + + omap_ssi = devm_kzalloc(&ssi->device, sizeof(*omap_ssi), GFP_KERNEL); + if (!omap_ssi) { + dev_err(&pd->dev, "not enough memory for omap ssi\n"); + return -ENOMEM; + } + + err = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL); + if (err < 0) + goto out_err; + ssi->id = err; + + ssi->owner = THIS_MODULE; + ssi->device.parent = &pd->dev; + dev_set_name(&ssi->device, "ssi%d", ssi->id); + hsi_controller_set_drvdata(ssi, omap_ssi); + omap_ssi->dev = &ssi->device; + err = ssi_get_iomem(pd, "sys", &omap_ssi->sys, NULL); + if (err < 0) + goto out_err; + err = ssi_get_iomem(pd, "gdd", &omap_ssi->gdd, NULL); + if (err < 0) + goto out_err; + err = platform_get_irq_byname(pd, "gdd_mpu"); + if (err < 0) { + dev_err(&pd->dev, "GDD IRQ resource missing\n"); + goto out_err; + } + omap_ssi->gdd_irq = err; + tasklet_init(&omap_ssi->gdd_tasklet, ssi_gdd_tasklet, + (unsigned long)ssi); + err = devm_request_irq(&ssi->device, omap_ssi->gdd_irq, ssi_gdd_isr, + 0, "gdd_mpu", ssi); + if (err < 0) { + dev_err(&ssi->device, "Request GDD IRQ %d failed (%d)", + omap_ssi->gdd_irq, err); + goto out_err; + } + + omap_ssi->port = devm_kzalloc(&ssi->device, + sizeof(struct omap_ssi_port *) * ssi->num_ports, GFP_KERNEL); + if (!omap_ssi->port) { + err = -ENOMEM; + goto out_err; + } + + omap_ssi->fck = devm_clk_get(&ssi->device, "ssi_ssr_fck"); + if (IS_ERR(omap_ssi->fck)) { + dev_err(&pd->dev, "Could not acquire clock \"ssi_ssr_fck\": %li\n", + PTR_ERR(omap_ssi->fck)); + err = -ENODEV; + goto out_err; + } + + /* TODO: find register, which can be used to detect context loss */ + omap_ssi->get_loss = NULL; + + omap_ssi->max_speed = UINT_MAX; + spin_lock_init(&omap_ssi->lock); + err = hsi_register_controller(ssi); + + if (err < 0) + goto out_err; + + return 0; + +out_err: + ida_simple_remove(&platform_omap_ssi_ida, ssi->id); + return err; +} + +static int ssi_hw_init(struct hsi_controller *ssi) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + unsigned int i; + u32 val; + int err; + + err = pm_runtime_get_sync(ssi->device.parent); + if (err < 0) { + dev_err(&ssi->device, "runtime PM failed %d\n", err); + return err; + } + /* Reseting SSI controller */ + writel_relaxed(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG); + val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG); + for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) { + msleep(20); + val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG); + } + if (!(val & SSI_RESETDONE)) { + dev_err(&ssi->device, "SSI HW reset failed\n"); + pm_runtime_put_sync(ssi->device.parent); + return -EIO; + } + /* Reseting GDD */ + writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG); + /* Get FCK rate in KHz */ + omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000); + dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate); + /* Set default PM settings */ + val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART; + writel_relaxed(val, omap_ssi->sys + SSI_SYSCONFIG_REG); + omap_ssi->sysconfig = val; + writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG); + omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON; + pm_runtime_put_sync(ssi->device.parent); + + return 0; +} + +static void ssi_remove_controller(struct hsi_controller *ssi) +{ + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + int id = ssi->id; + tasklet_kill(&omap_ssi->gdd_tasklet); + hsi_unregister_controller(ssi); + ida_simple_remove(&platform_omap_ssi_ida, id); +} + +static inline int ssi_of_get_available_ports_count(const struct device_node *np) +{ + struct device_node *child; + int num = 0; + + for_each_available_child_of_node(np, child) + if (of_device_is_compatible(child, "ti,omap3-ssi-port")) + num++; + + return num; +} + +static int ssi_remove_ports(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (!dev->of_node) + return 0; + + of_node_clear_flag(dev->of_node, OF_POPULATED); + of_device_unregister(pdev); + + return 0; +} + +static int ssi_probe(struct platform_device *pd) +{ + struct platform_device *childpdev; + struct device_node *np = pd->dev.of_node; + struct device_node *child; + struct hsi_controller *ssi; + int err; + int num_ports; + + if (!np) { + dev_err(&pd->dev, "missing device tree data\n"); + return -EINVAL; + } + + num_ports = ssi_of_get_available_ports_count(np); + + ssi = hsi_alloc_controller(num_ports, GFP_KERNEL); + if (!ssi) { + dev_err(&pd->dev, "No memory for controller\n"); + return -ENOMEM; + } + + platform_set_drvdata(pd, ssi); + + err = ssi_add_controller(ssi, pd); + if (err < 0) + goto out1; + + pm_runtime_irq_safe(&pd->dev); + pm_runtime_enable(&pd->dev); + + err = ssi_hw_init(ssi); + if (err < 0) + goto out2; +#ifdef CONFIG_DEBUG_FS + err = ssi_debug_add_ctrl(ssi); + if (err < 0) + goto out2; +#endif + + for_each_available_child_of_node(np, child) { + if (!of_device_is_compatible(child, "ti,omap3-ssi-port")) + continue; + + childpdev = of_platform_device_create(child, NULL, &pd->dev); + if (!childpdev) { + err = -ENODEV; + dev_err(&pd->dev, "failed to create ssi controller port\n"); + goto out3; + } + } + + dev_info(&pd->dev, "ssi controller %d initialized (%d ports)!\n", + ssi->id, num_ports); + return err; +out3: + device_for_each_child(&pd->dev, NULL, ssi_remove_ports); +out2: + ssi_remove_controller(ssi); +out1: + platform_set_drvdata(pd, NULL); + pm_runtime_disable(&pd->dev); + + return err; +} + +static int ssi_remove(struct platform_device *pd) +{ + struct hsi_controller *ssi = platform_get_drvdata(pd); + + /* cleanup of of_platform_populate() call */ + device_for_each_child(&pd->dev, NULL, ssi_remove_ports); + +#ifdef CONFIG_DEBUG_FS + ssi_debug_remove_ctrl(ssi); +#endif + ssi_remove_controller(ssi); + platform_set_drvdata(pd, NULL); + + pm_runtime_disable(&pd->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int omap_ssi_runtime_suspend(struct device *dev) +{ + struct hsi_controller *ssi = dev_get_drvdata(dev); + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + + dev_dbg(dev, "runtime suspend!\n"); + + if (omap_ssi->get_loss) + omap_ssi->loss_count = + omap_ssi->get_loss(ssi->device.parent); + + return 0; +} + +static int omap_ssi_runtime_resume(struct device *dev) +{ + struct hsi_controller *ssi = dev_get_drvdata(dev); + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + + dev_dbg(dev, "runtime resume!\n"); + + if ((omap_ssi->get_loss) && (omap_ssi->loss_count == + omap_ssi->get_loss(ssi->device.parent))) + return 0; + + writel_relaxed(omap_ssi->gdd_gcr, omap_ssi->gdd + SSI_GDD_GCR_REG); + + return 0; +} + +static const struct dev_pm_ops omap_ssi_pm_ops = { + SET_RUNTIME_PM_OPS(omap_ssi_runtime_suspend, omap_ssi_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&omap_ssi_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id omap_ssi_of_match[] = { + { .compatible = "ti,omap3-ssi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_ssi_of_match); +#else +#define omap_ssi_of_match NULL +#endif + +static struct platform_driver ssi_pdriver = { + .probe = ssi_probe, + .remove = ssi_remove, + .driver = { + .name = "omap_ssi", + .pm = DEV_PM_OPS, + .of_match_table = omap_ssi_of_match, + }, +}; + +static int __init ssi_init(void) { + int ret; + + ret = platform_driver_register(&ssi_pdriver); + if (ret) + return ret; + + return platform_driver_register(&ssi_port_pdriver); +} +module_init(ssi_init); + +static void __exit ssi_exit(void) { + platform_driver_unregister(&ssi_port_pdriver); + platform_driver_unregister(&ssi_pdriver); +} +module_exit(ssi_exit); + +MODULE_ALIAS("platform:omap_ssi"); +MODULE_AUTHOR("Carlos Chinea "); +MODULE_AUTHOR("Sebastian Reichel "); +MODULE_DESCRIPTION("Synchronous Serial Interface Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 530095ed39e7..1569bbb53ee8 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -1117,11 +1117,6 @@ static int ssi_port_probe(struct platform_device *pd) dev_dbg(&pd->dev, "init ssi port...\n"); - if (!try_module_get(ssi->owner)) { - dev_err(&pd->dev, "could not increment parent module refcount\n"); - return -ENODEV; - } - if (!ssi->port || !omap_ssi->port) { dev_err(&pd->dev, "ssi controller not initialized!\n"); err = -ENODEV; @@ -1242,7 +1237,6 @@ static int ssi_port_remove(struct platform_device *pd) omap_ssi->port[omap_port->port_id] = NULL; platform_set_drvdata(pd, NULL); - module_put(ssi->owner); pm_runtime_disable(&pd->dev); return 0; @@ -1369,7 +1363,7 @@ MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match); #define omap_ssi_port_of_match NULL #endif -static struct platform_driver ssi_port_pdriver = { +struct platform_driver ssi_port_pdriver = { .probe = ssi_port_probe, .remove = ssi_port_remove, .driver = { @@ -1378,11 +1372,3 @@ static struct platform_driver ssi_port_pdriver = { .pm = DEV_PM_OPS, }, }; - -module_platform_driver(ssi_port_pdriver); - -MODULE_ALIAS("platform:omap_ssi_port"); -MODULE_AUTHOR("Carlos Chinea "); -MODULE_AUTHOR("Sebastian Reichel "); -MODULE_DESCRIPTION("Synchronous Serial Interface Port Driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4bcf7414528a6b7ca52d28953a732a4cf36063e8 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 31 Jan 2016 01:52:38 +0100 Subject: HSI: omap-ssi: add clk change support This adds support for frequency changes of the SSI functional clock, which may occur due to DVFS. Acked-by: Pavel Machek Signed-off-By: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi.h | 6 ++++ drivers/hsi/controllers/omap_ssi_core.c | 63 +++++++++++++++++++++++++++++++++ drivers/hsi/controllers/omap_ssi_port.c | 20 +++++++++++ 3 files changed, 89 insertions(+) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index e493321cb0c3..7b4dec2c69ff 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -134,6 +134,8 @@ struct gdd_trn { * @gdd_tasklet: bottom half for DMA transfers * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers * @lock: lock to serialize access to GDD + * @fck_nb: DVFS notfifier block + * @fck_rate: clock rate * @loss_count: To follow if we need to restore context or not * @max_speed: Maximum TX speed (Kb/s) set by the clients. * @sysconfig: SSI controller saved context @@ -151,6 +153,7 @@ struct omap_ssi_controller { struct tasklet_struct gdd_tasklet; struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH]; spinlock_t lock; + struct notifier_block fck_nb; unsigned long fck_rate; u32 loss_count; u32 max_speed; @@ -164,6 +167,9 @@ struct omap_ssi_controller { #endif }; +void omap_ssi_port_update_fclk(struct hsi_controller *ssi, + struct omap_ssi_port *omap_port); + extern struct platform_driver ssi_port_pdriver; #endif /* __LINUX_HSI_OMAP_SSI_H__ */ diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 535c76038288..15b2a600d77b 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -290,6 +290,64 @@ static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) return rate; } +static int ssi_clk_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct omap_ssi_controller *omap_ssi = container_of(nb, + struct omap_ssi_controller, fck_nb); + struct hsi_controller *ssi = to_hsi_controller(omap_ssi->dev); + struct clk_notifier_data *clk_data = data; + struct omap_ssi_port *omap_port; + int i; + + switch (event) { + case PRE_RATE_CHANGE: + dev_dbg(&ssi->device, "pre rate change\n"); + + for (i = 0; i < ssi->num_ports; i++) { + omap_port = omap_ssi->port[i]; + + if (!omap_port) + continue; + + /* Workaround for SWBREAK + CAwake down race in CMT */ + tasklet_disable(&omap_port->wake_tasklet); + + /* stop all ssi communication */ + pinctrl_pm_select_idle_state(omap_port->pdev); + udelay(1); /* wait for racing frames */ + } + + break; + case ABORT_RATE_CHANGE: + dev_dbg(&ssi->device, "abort rate change\n"); + /* Fall through */ + case POST_RATE_CHANGE: + dev_dbg(&ssi->device, "post rate change (%lu -> %lu)\n", + clk_data->old_rate, clk_data->new_rate); + omap_ssi->fck_rate = DIV_ROUND_CLOSEST(clk_data->new_rate, 1000); /* KHz */ + + for (i = 0; i < ssi->num_ports; i++) { + omap_port = omap_ssi->port[i]; + + if (!omap_port) + continue; + + omap_ssi_port_update_fclk(ssi, omap_port); + + /* resume ssi communication */ + pinctrl_pm_select_default_state(omap_port->pdev); + tasklet_enable(&omap_port->wake_tasklet); + } + + break; + default: + break; + } + + return NOTIFY_DONE; +} + static int ssi_get_iomem(struct platform_device *pd, const char *name, void __iomem **pbase, dma_addr_t *phy) { @@ -369,6 +427,10 @@ static int ssi_add_controller(struct hsi_controller *ssi, goto out_err; } + omap_ssi->fck_nb.notifier_call = ssi_clk_event; + omap_ssi->fck_nb.priority = INT_MAX; + clk_notifier_register(omap_ssi->fck, &omap_ssi->fck_nb); + /* TODO: find register, which can be used to detect context loss */ omap_ssi->get_loss = NULL; @@ -432,6 +494,7 @@ static void ssi_remove_controller(struct hsi_controller *ssi) int id = ssi->id; tasklet_kill(&omap_ssi->gdd_tasklet); hsi_unregister_controller(ssi); + clk_notifier_unregister(omap_ssi->fck, &omap_ssi->fck_nb); ida_simple_remove(&platform_omap_ssi_ida, id); } diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 1569bbb53ee8..98b22e88085c 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -514,6 +515,11 @@ static int ssi_flush(struct hsi_client *cl) pm_runtime_get_sync(omap_port->pdev); spin_lock_bh(&omap_port->lock); + + /* stop all ssi communication */ + pinctrl_pm_select_idle_state(omap_port->pdev); + udelay(1); /* wait for racing frames */ + /* Stop all DMA transfers */ for (i = 0; i < SSI_MAX_GDD_LCH; i++) { msg = omap_ssi->gdd_trn[i].msg; @@ -550,6 +556,10 @@ static int ssi_flush(struct hsi_client *cl) ssi_flush_queue(&omap_port->rxqueue[i], NULL); } ssi_flush_queue(&omap_port->brkqueue, NULL); + + /* Resume SSI communication */ + pinctrl_pm_select_default_state(omap_port->pdev); + spin_unlock_bh(&omap_port->lock); pm_runtime_put_sync(omap_port->pdev); @@ -1302,6 +1312,16 @@ static int ssi_restore_divisor(struct omap_ssi_port *omap_port) return 0; } +void omap_ssi_port_update_fclk(struct hsi_controller *ssi, + struct omap_ssi_port *omap_port) +{ + /* update divisor */ + u32 div = ssi_calculate_div(ssi); + omap_port->sst.divisor = div; + ssi_restore_divisor(omap_port); +} +EXPORT_SYMBOL_GPL(omap_ssi_port_update_fclk); + static int omap_ssi_port_runtime_suspend(struct device *dev) { struct hsi_port *port = dev_get_drvdata(dev); -- cgit v1.2.3 From 53c703501e3c10283315f1c846d4ebea7d371c4d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 May 2016 17:16:20 +0200 Subject: HSI: omap-ssi: add COMMON_CLK dependency Enabling the omap ssi driver without COMMON_CLK results in a build failure: drivers/hsi/controllers/omap_ssi_core.c: In function 'ssi_clk_event': drivers/hsi/controllers/omap_ssi_core.c:304:7: error: 'PRE_RATE_CHANGE' undeclared (first use in this function) This adds a Kconfig dependency to avoid the invalid configuration. Signed-off-by: Arnd Bergmann Fixes: 4bcf7414528a ("HSI: omap-ssi: add clk change support") Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig index 084ec97eec64..48e4eda186cc 100644 --- a/drivers/hsi/controllers/Kconfig +++ b/drivers/hsi/controllers/Kconfig @@ -5,7 +5,8 @@ comment "HSI controllers" config OMAP_SSI tristate "OMAP SSI hardware driver" - depends on HSI && OF && (ARCH_OMAP3 || (ARM && COMPILE_TEST)) + depends on HSI && OF && ARM && COMMON_CLK + depends on ARCH_OMAP3 || COMPILE_TEST ---help--- SSI is a legacy version of HSI. It is usually used to connect an application engine with a cellular modem. -- cgit v1.2.3 From ac8e3ff3a07cb8bf7a5ce4627c39a50e71dcd394 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 May 2016 17:16:21 +0200 Subject: HSI: omap-ssi: include pinctrl header files The driver now uses some pinctrl functions, but fails to build if PINCTRL is disabled because the respective header files are only included indirectly: drivers/hsi/controllers/omap_ssi_core.c: In function 'ssi_clk_event': drivers/hsi/controllers/omap_ssi_core.c:317:4: error: implicit declaration of function 'pinctrl_pm_select_idle_state' [-Werror=implicit-function-declaration] drivers/hsi/controllers/omap_ssi_core.c:339:4: error: implicit declaration of function 'pinctrl_pm_select_default_state' [-Werror=implicit-function-declaration] drivers/hsi/controllers/omap_ssi_port.c: In function 'ssi_flush': drivers/hsi/controllers/omap_ssi_port.c:520:2: error: implicit declaration of function 'pinctrl_pm_select_idle_state' [-Werror=implicit-function-declaration] This includes the headers from the files that call the functions, which works even if pinctrl is turned off. Signed-off-by: Arnd Bergmann Fixes: 4bcf7414528a ("HSI: omap-ssi: add clk change support") Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi_core.c | 1 + drivers/hsi/controllers/omap_ssi_port.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 15b2a600d77b..a3e0febfb64a 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 98b22e88085c..ca7139eaaa1d 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -26,6 +26,7 @@ #include #include +#include #include #include "omap_ssi_regs.h" -- cgit v1.2.3 From c2f90a465df75254fb41bf6e7975464929c21e26 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 May 2016 17:16:22 +0200 Subject: HSI: omap-ssi: move omap_ssi_port_update_fclk After the clk change support, the ssi omap ssi core driver now calls into the port driver to change fclk. This function was previously inside of an #ifdef, because it was only used when CONFIG_PM is enabled. Now it also gets used without power management support: drivers/hsi/built-in.o: In function `ssi_clk_event': omap_ssi_port.c:(.text+0x1bf8): undefined reference to `omap_ssi_port_update_fclk' This moves the function outside of the CONFIG_PM guard. Signed-off-by: Arnd Bergmann Fixes: 4bcf7414528a ("HSI: omap-ssi: add clk change support") Signed-off-by: Sebastian Reichel --- drivers/hsi/controllers/omap_ssi_port.c | 35 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index ca7139eaaa1d..6b8f7739768a 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -1253,6 +1253,23 @@ static int ssi_port_remove(struct platform_device *pd) return 0; } +static int ssi_restore_divisor(struct omap_ssi_port *omap_port) +{ + writel_relaxed(omap_port->sst.divisor, + omap_port->sst_base + SSI_SST_DIVISOR_REG); + + return 0; +} + +void omap_ssi_port_update_fclk(struct hsi_controller *ssi, + struct omap_ssi_port *omap_port) +{ + /* update divisor */ + u32 div = ssi_calculate_div(ssi); + omap_port->sst.divisor = div; + ssi_restore_divisor(omap_port); +} + #ifdef CONFIG_PM static int ssi_save_port_ctx(struct omap_ssi_port *omap_port) { @@ -1305,24 +1322,6 @@ static int ssi_restore_port_mode(struct omap_ssi_port *omap_port) return 0; } -static int ssi_restore_divisor(struct omap_ssi_port *omap_port) -{ - writel_relaxed(omap_port->sst.divisor, - omap_port->sst_base + SSI_SST_DIVISOR_REG); - - return 0; -} - -void omap_ssi_port_update_fclk(struct hsi_controller *ssi, - struct omap_ssi_port *omap_port) -{ - /* update divisor */ - u32 div = ssi_calculate_div(ssi); - omap_port->sst.divisor = div; - ssi_restore_divisor(omap_port); -} -EXPORT_SYMBOL_GPL(omap_ssi_port_update_fclk); - static int omap_ssi_port_runtime_suspend(struct device *dev) { struct hsi_port *port = dev_get_drvdata(dev); -- cgit v1.2.3