提交 4d230d4d 编写于 作者: L Linus Torvalds

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
...@@ -5,15 +5,11 @@ comment "HSI controllers" ...@@ -5,15 +5,11 @@ comment "HSI controllers"
config OMAP_SSI config OMAP_SSI
tristate "OMAP SSI hardware driver" 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--- ---help---
SSI is a legacy version of HSI. It is usually used to connect SSI is a legacy version of HSI. It is usually used to connect
an application engine with a cellular modem. an application engine with a cellular modem.
If you say Y here, you will enable the OMAP SSI hardware driver. If you say Y here, you will enable the OMAP SSI hardware driver.
If unsure, say N. If unsure, say N.
config OMAP_SSI_PORT
tristate
default m if OMAP_SSI=m
default y if OMAP_SSI=y
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
# Makefile for HSI controllers drivers # Makefile for HSI controllers drivers
# #
obj-$(CONFIG_OMAP_SSI) += omap_ssi.o omap_ssi-objs += omap_ssi_core.o omap_ssi_port.o
obj-$(CONFIG_OMAP_SSI_PORT) += omap_ssi_port.o obj-$(CONFIG_OMAP_SSI) += omap_ssi.o
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/hsi/hsi.h> #include <linux/hsi/hsi.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -97,7 +97,7 @@ struct omap_ssi_port { ...@@ -97,7 +97,7 @@ struct omap_ssi_port {
struct list_head brkqueue; struct list_head brkqueue;
unsigned int irq; unsigned int irq;
int wake_irq; int wake_irq;
int wake_gpio; struct gpio_desc *wake_gpio;
struct tasklet_struct pio_tasklet; struct tasklet_struct pio_tasklet;
struct tasklet_struct wake_tasklet; struct tasklet_struct wake_tasklet;
bool wktest:1; /* FIXME: HACK to be removed */ bool wktest:1; /* FIXME: HACK to be removed */
...@@ -134,6 +134,8 @@ struct gdd_trn { ...@@ -134,6 +134,8 @@ struct gdd_trn {
* @gdd_tasklet: bottom half for DMA transfers * @gdd_tasklet: bottom half for DMA transfers
* @gdd_trn: Array of GDD transaction data for ongoing GDD transfers * @gdd_trn: Array of GDD transaction data for ongoing GDD transfers
* @lock: lock to serialize access to GDD * @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 * @loss_count: To follow if we need to restore context or not
* @max_speed: Maximum TX speed (Kb/s) set by the clients. * @max_speed: Maximum TX speed (Kb/s) set by the clients.
* @sysconfig: SSI controller saved context * @sysconfig: SSI controller saved context
...@@ -151,6 +153,7 @@ struct omap_ssi_controller { ...@@ -151,6 +153,7 @@ struct omap_ssi_controller {
struct tasklet_struct gdd_tasklet; struct tasklet_struct gdd_tasklet;
struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH]; struct gdd_trn gdd_trn[SSI_MAX_GDD_LCH];
spinlock_t lock; spinlock_t lock;
struct notifier_block fck_nb;
unsigned long fck_rate; unsigned long fck_rate;
u32 loss_count; u32 loss_count;
u32 max_speed; u32 max_speed;
...@@ -164,4 +167,9 @@ struct omap_ssi_controller { ...@@ -164,4 +167,9 @@ struct omap_ssi_controller {
#endif #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__ */ #endif /* __LINUX_HSI_OMAP_SSI_H__ */
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -36,6 +35,7 @@ ...@@ -36,6 +35,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/hsi/hsi.h> #include <linux/hsi/hsi.h>
...@@ -141,7 +141,7 @@ static const struct file_operations ssi_gdd_regs_fops = { ...@@ -141,7 +141,7 @@ static const struct file_operations ssi_gdd_regs_fops = {
.release = single_release, .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 omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
struct dentry *dir; struct dentry *dir;
...@@ -291,7 +291,65 @@ static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi) ...@@ -291,7 +291,65 @@ static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi)
return rate; 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) const char *name, void __iomem **pbase, dma_addr_t *phy)
{ {
struct resource *mem; struct resource *mem;
...@@ -311,7 +369,7 @@ static int __init ssi_get_iomem(struct platform_device *pd, ...@@ -311,7 +369,7 @@ static int __init ssi_get_iomem(struct platform_device *pd,
return 0; 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 platform_device *pd)
{ {
struct omap_ssi_controller *omap_ssi; struct omap_ssi_controller *omap_ssi;
...@@ -370,6 +428,10 @@ static int __init ssi_add_controller(struct hsi_controller *ssi, ...@@ -370,6 +428,10 @@ static int __init ssi_add_controller(struct hsi_controller *ssi,
goto out_err; 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 */ /* TODO: find register, which can be used to detect context loss */
omap_ssi->get_loss = NULL; omap_ssi->get_loss = NULL;
...@@ -387,7 +449,7 @@ static int __init ssi_add_controller(struct hsi_controller *ssi, ...@@ -387,7 +449,7 @@ static int __init ssi_add_controller(struct hsi_controller *ssi,
return 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); struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
unsigned int i; unsigned int i;
...@@ -433,6 +495,7 @@ static void ssi_remove_controller(struct hsi_controller *ssi) ...@@ -433,6 +495,7 @@ static void ssi_remove_controller(struct hsi_controller *ssi)
int id = ssi->id; int id = ssi->id;
tasklet_kill(&omap_ssi->gdd_tasklet); tasklet_kill(&omap_ssi->gdd_tasklet);
hsi_unregister_controller(ssi); hsi_unregister_controller(ssi);
clk_notifier_unregister(omap_ssi->fck, &omap_ssi->fck_nb);
ida_simple_remove(&platform_omap_ssi_ida, id); ida_simple_remove(&platform_omap_ssi_ida, id);
} }
...@@ -452,12 +515,16 @@ static int ssi_remove_ports(struct device *dev, void *c) ...@@ -452,12 +515,16 @@ static int ssi_remove_ports(struct device *dev, void *c)
{ {
struct platform_device *pdev = to_platform_device(dev); 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); of_device_unregister(pdev);
return 0; return 0;
} }
static int __init ssi_probe(struct platform_device *pd) static int ssi_probe(struct platform_device *pd)
{ {
struct platform_device *childpdev; struct platform_device *childpdev;
struct device_node *np = pd->dev.of_node; struct device_node *np = pd->dev.of_node;
...@@ -523,10 +590,13 @@ static int __init ssi_probe(struct platform_device *pd) ...@@ -523,10 +590,13 @@ static int __init ssi_probe(struct platform_device *pd)
return err; 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); 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 #ifdef CONFIG_DEBUG_FS
ssi_debug_remove_ctrl(ssi); ssi_debug_remove_ctrl(ssi);
#endif #endif
...@@ -535,9 +605,6 @@ static int __exit ssi_remove(struct platform_device *pd) ...@@ -535,9 +605,6 @@ static int __exit ssi_remove(struct platform_device *pd)
pm_runtime_disable(&pd->dev); pm_runtime_disable(&pd->dev);
/* cleanup of of_platform_populate() call */
device_for_each_child(&pd->dev, NULL, ssi_remove_ports);
return 0; return 0;
} }
...@@ -593,7 +660,8 @@ MODULE_DEVICE_TABLE(of, omap_ssi_of_match); ...@@ -593,7 +660,8 @@ MODULE_DEVICE_TABLE(of, omap_ssi_of_match);
#endif #endif
static struct platform_driver ssi_pdriver = { static struct platform_driver ssi_pdriver = {
.remove = __exit_p(ssi_remove), .probe = ssi_probe,
.remove = ssi_remove,
.driver = { .driver = {
.name = "omap_ssi", .name = "omap_ssi",
.pm = DEV_PM_OPS, .pm = DEV_PM_OPS,
...@@ -601,7 +669,22 @@ static struct platform_driver ssi_pdriver = { ...@@ -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_ALIAS("platform:omap_ssi");
MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>"); MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
......
...@@ -23,8 +23,10 @@ ...@@ -23,8 +23,10 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/pm_runtime.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 <linux/debugfs.h>
#include "omap_ssi_regs.h" #include "omap_ssi_regs.h"
...@@ -43,7 +45,7 @@ static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) ...@@ -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) static inline unsigned int ssi_wakein(struct hsi_port *port)
{ {
struct omap_ssi_port *omap_port = hsi_port_drvdata(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 #ifdef CONFIG_DEBUG_FS
...@@ -171,7 +173,7 @@ static int ssi_div_set(void *data, u64 val) ...@@ -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"); 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 dentry *dir)
{ {
struct hsi_port *port = to_hsi_port(omap_port->dev); struct hsi_port *port = to_hsi_port(omap_port->dev);
...@@ -514,6 +516,11 @@ static int ssi_flush(struct hsi_client *cl) ...@@ -514,6 +516,11 @@ static int ssi_flush(struct hsi_client *cl)
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get_sync(omap_port->pdev);
spin_lock_bh(&omap_port->lock); 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 */ /* Stop all DMA transfers */
for (i = 0; i < SSI_MAX_GDD_LCH; i++) { for (i = 0; i < SSI_MAX_GDD_LCH; i++) {
msg = omap_ssi->gdd_trn[i].msg; msg = omap_ssi->gdd_trn[i].msg;
...@@ -550,6 +557,10 @@ static int ssi_flush(struct hsi_client *cl) ...@@ -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->rxqueue[i], NULL);
} }
ssi_flush_queue(&omap_port->brkqueue, 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); spin_unlock_bh(&omap_port->lock);
pm_runtime_put_sync(omap_port->pdev); pm_runtime_put_sync(omap_port->pdev);
...@@ -1007,7 +1018,7 @@ static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port) ...@@ -1007,7 +1018,7 @@ static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port)
return IRQ_HANDLED; 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 platform_device *pd)
{ {
struct omap_ssi_port *omap_port = hsi_port_drvdata(port); struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
...@@ -1029,19 +1040,19 @@ static int __init ssi_port_irq(struct hsi_port *port, ...@@ -1029,19 +1040,19 @@ static int __init ssi_port_irq(struct hsi_port *port,
return err; 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 platform_device *pd)
{ {
struct omap_ssi_port *omap_port = hsi_port_drvdata(port); struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
int cawake_irq; int cawake_irq;
int err; int err;
if (omap_port->wake_gpio == -1) { if (!omap_port->wake_gpio) {
omap_port->wake_irq = -1; omap_port->wake_irq = -1;
return 0; 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; omap_port->wake_irq = cawake_irq;
tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet, tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
...@@ -1060,7 +1071,7 @@ static int __init ssi_wake_irq(struct hsi_port *port, ...@@ -1060,7 +1071,7 @@ static int __init ssi_wake_irq(struct hsi_port *port,
return err; 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; unsigned int ch;
...@@ -1071,7 +1082,7 @@ static void __init ssi_queues_init(struct omap_ssi_port *omap_port) ...@@ -1071,7 +1082,7 @@ static void __init ssi_queues_init(struct omap_ssi_port *omap_port)
INIT_LIST_HEAD(&omap_port->brkqueue); 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) const char *name, void __iomem **pbase, dma_addr_t *phy)
{ {
struct hsi_port *port = platform_get_drvdata(pd); struct hsi_port *port = platform_get_drvdata(pd);
...@@ -1104,24 +1115,19 @@ static int __init ssi_port_get_iomem(struct platform_device *pd, ...@@ -1104,24 +1115,19 @@ static int __init ssi_port_get_iomem(struct platform_device *pd,
return 0; 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 device_node *np = pd->dev.of_node;
struct hsi_port *port; struct hsi_port *port;
struct omap_ssi_port *omap_port; struct omap_ssi_port *omap_port;
struct hsi_controller *ssi = dev_get_drvdata(pd->dev.parent); struct hsi_controller *ssi = dev_get_drvdata(pd->dev.parent);
struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
int cawake_gpio = 0; struct gpio_desc *cawake_gpio = NULL;
u32 port_id; u32 port_id;
int err; int err;
dev_dbg(&pd->dev, "init ssi port...\n"); 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) { if (!ssi->port || !omap_ssi->port) {
dev_err(&pd->dev, "ssi controller not initialized!\n"); dev_err(&pd->dev, "ssi controller not initialized!\n");
err = -ENODEV; err = -ENODEV;
...@@ -1147,20 +1153,10 @@ static int __init ssi_port_probe(struct platform_device *pd) ...@@ -1147,20 +1153,10 @@ static int __init ssi_port_probe(struct platform_device *pd)
goto error; goto error;
} }
err = of_get_named_gpio(np, "ti,ssi-cawake-gpio", 0); cawake_gpio = devm_gpiod_get(&pd->dev, "ti,ssi-cawake", GPIOD_IN);
if (err < 0) { if (IS_ERR(cawake_gpio)) {
dev_err(&pd->dev, "DT data is missing cawake gpio (err=%d)\n", err = PTR_ERR(cawake_gpio);
err); dev_err(&pd->dev, "couldn't get 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;
goto error; goto error;
} }
...@@ -1219,8 +1215,7 @@ static int __init ssi_port_probe(struct platform_device *pd) ...@@ -1219,8 +1215,7 @@ static int __init ssi_port_probe(struct platform_device *pd)
hsi_add_clients_from_dt(port, np); hsi_add_clients_from_dt(port, np);
dev_info(&pd->dev, "ssi port %u successfully initialized (cawake=%d)\n", dev_info(&pd->dev, "ssi port %u successfully initialized\n", port_id);
port_id, cawake_gpio);
return 0; return 0;
...@@ -1228,7 +1223,7 @@ static int __init ssi_port_probe(struct platform_device *pd) ...@@ -1228,7 +1223,7 @@ static int __init ssi_port_probe(struct platform_device *pd)
return err; 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 hsi_port *port = platform_get_drvdata(pd);
struct omap_ssi_port *omap_port = hsi_port_drvdata(port); struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
...@@ -1253,12 +1248,28 @@ static int __exit ssi_port_remove(struct platform_device *pd) ...@@ -1253,12 +1248,28 @@ static int __exit ssi_port_remove(struct platform_device *pd)
omap_ssi->port[omap_port->port_id] = NULL; omap_ssi->port[omap_port->port_id] = NULL;
platform_set_drvdata(pd, NULL); platform_set_drvdata(pd, NULL);
module_put(ssi->owner);
pm_runtime_disable(&pd->dev); pm_runtime_disable(&pd->dev);
return 0; 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 #ifdef CONFIG_PM
static int ssi_save_port_ctx(struct omap_ssi_port *omap_port) 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) ...@@ -1311,14 +1322,6 @@ static int ssi_restore_port_mode(struct omap_ssi_port *omap_port)
return 0; 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) static int omap_ssi_port_runtime_suspend(struct device *dev)
{ {
struct hsi_port *port = dev_get_drvdata(dev); struct hsi_port *port = dev_get_drvdata(dev);
...@@ -1380,19 +1383,12 @@ MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match); ...@@ -1380,19 +1383,12 @@ MODULE_DEVICE_TABLE(of, omap_ssi_port_of_match);
#define omap_ssi_port_of_match NULL #define omap_ssi_port_of_match NULL
#endif #endif
static struct platform_driver ssi_port_pdriver = { struct platform_driver ssi_port_pdriver = {
.remove = __exit_p(ssi_port_remove), .probe = ssi_port_probe,
.remove = ssi_port_remove,
.driver = { .driver = {
.name = "omap_ssi_port", .name = "omap_ssi_port",
.of_match_table = omap_ssi_port_of_match, .of_match_table = omap_ssi_port_of_match,
.pm = DEV_PM_OPS, .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");
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册