Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d230d4d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 410b4297 c2f90a46
Loading
Loading
Loading
Loading
+2 −6
Original line number Diff line number Diff line
@@ -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
+2 −2
Original line number Diff line number Diff line
@@ -2,5 +2,5 @@
# Makefile for HSI controllers drivers
#

omap_ssi-objs		+= omap_ssi_core.o omap_ssi_port.o
obj-$(CONFIG_OMAP_SSI)	+= omap_ssi.o
obj-$(CONFIG_OMAP_SSI_PORT)	+= omap_ssi_port.o
+10 −2
Original line number Diff line number Diff line
@@ -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__ */
+95 −12
Original line number Diff line number Diff line
@@ -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 @@ static int __init ssi_add_controller(struct hsi_controller *ssi,
	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 @@ static int __init ssi_probe(struct platform_device *pd)
	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>");
+48 −52
Original line number Diff line number Diff line
@@ -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 @@ static int __init ssi_port_probe(struct platform_device *pd)
	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");