diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hsi/controllers/Kconfig | 8 | ||||
-rw-r--r-- | drivers/hsi/controllers/Makefile | 4 | ||||
-rw-r--r-- | drivers/hsi/controllers/omap_ssi.h | 12 | ||||
-rw-r--r-- | drivers/hsi/controllers/omap_ssi_core.c (renamed from drivers/hsi/controllers/omap_ssi.c) | 107 | ||||
-rw-r--r-- | drivers/hsi/controllers/omap_ssi_port.c | 100 |
5 files changed, 157 insertions, 74 deletions
diff --git a/drivers/hsi/controllers/Kconfig b/drivers/hsi/controllers/Kconfig index 6aba27808172..48e4eda186cc 100644 --- a/drivers/hsi/controllers/Kconfig +++ b/drivers/hsi/controllers/Kconfig @@ -5,15 +5,11 @@ 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. 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 f9aaf37262be..7b4dec2c69ff 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -27,7 +27,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/hsi/hsi.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -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 */ @@ -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,4 +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.c b/drivers/hsi/controllers/omap_ssi_core.c index 27b91f14ba7a..a3e0febfb64a 100644 --- a/drivers/hsi/controllers/omap_ssi.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -24,7 +24,6 @@ #include <linux/err.h> #include <linux/ioport.h> #include <linux/io.h> -#include <linux/gpio.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/platform_device.h> @@ -36,6 +35,7 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/debugfs.h> +#include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> #include <linux/of_platform.h> #include <linux/hsi/hsi.h> @@ -141,7 +141,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; @@ -291,7 +291,65 @@ 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_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) { struct resource *mem; @@ -311,7 +369,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; @@ -370,6 +428,10 @@ static int __init 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; @@ -387,7 +449,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; @@ -433,6 +495,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); } @@ -452,12 +515,16 @@ 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 __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; @@ -523,10 +590,13 @@ 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); + /* 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 @@ -535,9 +605,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; } @@ -593,7 +660,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, @@ -601,7 +669,22 @@ static struct platform_driver ssi_pdriver = { }, }; -module_platform_driver_probe(ssi_pdriver, ssi_probe); +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 <carlos.chinea@nokia.com>"); diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index e80a66e20998..6b8f7739768a 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -23,8 +23,10 @@ #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> +#include <linux/delay.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/pinctrl/consumer.h> #include <linux/debugfs.h> #include "omap_ssi_regs.h" @@ -43,7 +45,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 @@ -171,7 +173,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); @@ -514,6 +516,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 +557,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); @@ -1007,7 +1018,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,19 +1040,19 @@ 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); 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, @@ -1060,7 +1071,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 +1082,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,24 +1115,19 @@ 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; 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; 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; @@ -1147,20 +1153,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 +1215,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; @@ -1228,7 +1223,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); @@ -1253,12 +1248,28 @@ static int __exit 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; } +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) { @@ -1311,14 +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; -} - static int omap_ssi_port_runtime_suspend(struct device *dev) { struct hsi_port *port = dev_get_drvdata(dev); @@ -1380,19 +1383,12 @@ MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match); #define omap_ssi_port_of_match NULL #endif -static struct platform_driver ssi_port_pdriver = { - .remove = __exit_p(ssi_port_remove), +struct platform_driver ssi_port_pdriver = { + .probe = ssi_port_probe, + .remove = ssi_port_remove, .driver = { .name = "omap_ssi_port", .of_match_table = omap_ssi_port_of_match, .pm = DEV_PM_OPS, }, }; - -module_platform_driver_probe(ssi_port_pdriver, ssi_port_probe); - -MODULE_ALIAS("platform:omap_ssi_port"); -MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>"); -MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); -MODULE_DESCRIPTION("Synchronous Serial Interface Port Driver"); -MODULE_LICENSE("GPL v2"); |