From 73e6ce09c068d42d627874019899f1138740a6c5 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 00:04:29 +0200 Subject: [PATCH 1/9] 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; From f704e1103e05a65c83b85c66d947e57bc20d6edd Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 00:07:35 +0200 Subject: [PATCH 2/9] 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; } From 0845e1f20af100d1d4ac7cc111a8dfb790f94a16 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 01:01:06 +0200 Subject: [PATCH 3/9] 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 "); From 2a57aba8503d5694ee113016cb1a107831b8236f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 03:23:27 +0200 Subject: [PATCH 4/9] 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; From 0fae198988b873d30fe9ecb6a6271afb36df97e9 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 03:24:09 +0200 Subject: [PATCH 5/9] 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.h | 2 ++ .../controllers/{omap_ssi.c => omap_ssi_core.c} | 17 ++++++++++++++++- drivers/hsi/controllers/omap_ssi_port.c | 16 +--------------- 5 files changed, 21 insertions(+), 23 deletions(-) rename drivers/hsi/controllers/{omap_ssi.c => omap_ssi_core.c} (97%) 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.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.c b/drivers/hsi/controllers/omap_ssi_core.c similarity index 97% rename from drivers/hsi/controllers/omap_ssi.c rename to drivers/hsi/controllers/omap_ssi_core.c index 68dfdaa19938..535c76038288 100644 --- a/drivers/hsi/controllers/omap_ssi.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -605,7 +605,22 @@ static struct platform_driver ssi_pdriver = { }, }; -module_platform_driver(ssi_pdriver); +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 "); 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"); From 4bcf7414528a6b7ca52d28953a732a4cf36063e8 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sun, 31 Jan 2016 01:52:38 +0100 Subject: [PATCH 6/9] 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); From 53c703501e3c10283315f1c846d4ebea7d371c4d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 May 2016 17:16:20 +0200 Subject: [PATCH 7/9] 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. From ac8e3ff3a07cb8bf7a5ce4627c39a50e71dcd394 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 May 2016 17:16:21 +0200 Subject: [PATCH 8/9] 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" From c2f90a465df75254fb41bf6e7975464929c21e26 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 May 2016 17:16:22 +0200 Subject: [PATCH 9/9] 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);