HSI changes for the v4.7 series

* merge omap-ssi and omap-ssi-port modules
  * fix omap-ssi module reloading
  * add DVFS support to omap-ssi
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCgAGBQJXPpJpAAoJENju1/PIO/qaRWQP/ia/qFECRFiObARtny5zUtMS
 NX/QIXz96XozP0BhP58yS/qXP2N+RUZzXuft8NnlIgM7xcOScUu07WL3nPZ7sQO/
 QI7NL3NJtuh2UWKFa6mjEZiHkprM/qvcGPu2cY4SLGGczNbKBESBdnl0wUUHftqg
 vSoRtr8mK1zVPFCpoKOFcZkVq2mhzU6re5CxXwwRCsM55qwtaaQkBk2qzn9ZkQvm
 AVB39zh8QitpIZbJtM45ZHkoVzq12MuqYXvLXO7wUUxrS2A9aZn//J+D7UEc5e4l
 QArC+N71wLCioYqGQXvKDUWsPybjtIT0CLljaXVyRn+3ZHoCrWrbNRVz7bU28HQo
 gXM0BhDuexdZsWE6aWvlmSBr1jFi1Hwp7POyYHLwTKpqUmJTbEd+F/HP9UxkVMpo
 d9j0wiIziTeRYvQ/o0maFkkARQkirJGv+zL1nZr5Q7q4WqdXNd7MDwN6qsi2QAD5
 EkJpsetllH8e5JZqT4drFMtAOypgstSpzuU5EuyL5EO6ES7apzGQhNGW+cWlVtCb
 8J0HYpa3T39ekNPIWbuHcGatKJYdPZVlLnDAZiQRjdhGAahS1i52MwjIlB68AWoc
 hYGAccDdwgarjp+ce5QFu6cGiX2jOt7zgO1PhQAXY8Yzt9q7PJPaR8mxdxUNiCvv
 KZ2DzE1GRlBD1TP+NMQc
 =4zdt
 -----END PGP SIGNATURE-----

Merge tag 'hsi-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi

Pull HSI updates from Sebastian Reichel:

 - merge omap-ssi and omap-ssi-port modules

 - fix omap-ssi module reloading

 - add DVFS support to omap-ssi

* tag 'hsi-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi:
  HSI: omap-ssi: move omap_ssi_port_update_fclk
  HSI: omap-ssi: include pinctrl header files
  HSI: omap-ssi: add COMMON_CLK dependency
  HSI: omap-ssi: add clk change support
  HSI: omap_ssi: built omap_ssi and omap_ssi_port into one module
  HSI: omap_ssi: fix removal of port platform device
  HSI: omap_ssi: make sure probe stays available
  HSI: omap_ssi: fix module unloading
  HSI: omap_ssi_port: switch to gpiod API
This commit is contained in:
Linus Torvalds 2016-05-20 11:05:40 -07:00
commit 4d230d4d03
5 changed files with 157 additions and 74 deletions

View File

@ -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

View File

@ -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

View File

@ -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__ */

View File

@ -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>");

View File

@ -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");