3  * Copyright (C) 2010 Nokia Corporation. All rights reserved.
 
   4  * Copyright (C) 2014 Sebastian Reichel <sre@kernel.org>
 
   6  * Contact: Carlos Chinea <carlos.chinea@nokia.com>
 
   8  * This program is free software; you can redistribute it and/or
 
   9  * modify it under the terms of the GNU General Public License
 
  10  * version 2 as published by the Free Software Foundation.
 
  12  * This program is distributed in the hope that it will be useful, but
 
  13  * WITHOUT ANY WARRANTY; without even the implied warranty of
 
  14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
  15  * General Public License for more details.
 
  17  * You should have received a copy of the GNU General Public License
 
  18  * along with this program; if not, write to the Free Software
 
  19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
  23 #include <linux/compiler.h>
 
  24 #include <linux/err.h>
 
  25 #include <linux/ioport.h>
 
  27 #include <linux/clk.h>
 
  28 #include <linux/device.h>
 
  29 #include <linux/platform_device.h>
 
  30 #include <linux/dma-mapping.h>
 
  31 #include <linux/dmaengine.h>
 
  32 #include <linux/delay.h>
 
  33 #include <linux/seq_file.h>
 
  34 #include <linux/scatterlist.h>
 
  35 #include <linux/interrupt.h>
 
  36 #include <linux/spinlock.h>
 
  37 #include <linux/debugfs.h>
 
  38 #include <linux/pinctrl/consumer.h>
 
  39 #include <linux/pm_runtime.h>
 
  40 #include <linux/of_platform.h>
 
  41 #include <linux/hsi/hsi.h>
 
  42 #include <linux/idr.h>
 
  44 #include "omap_ssi_regs.h"
 
  47 /* For automatically allocated device IDs */
 
  48 static DEFINE_IDA(platform_omap_ssi_ida);
 
  50 #ifdef CONFIG_DEBUG_FS
 
  51 static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused)
 
  53         struct hsi_controller *ssi = m->private;
 
  54         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
  55         void __iomem *sys = omap_ssi->sys;
 
  57         pm_runtime_get_sync(ssi->device.parent);
 
  58         seq_printf(m, "REVISION\t: 0x%08x\n",  readl(sys + SSI_REVISION_REG));
 
  59         seq_printf(m, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG));
 
  60         seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_REG));
 
  61         pm_runtime_put(ssi->device.parent);
 
  66 static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused)
 
  68         struct hsi_controller *ssi = m->private;
 
  69         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
  70         void __iomem *gdd = omap_ssi->gdd;
 
  71         void __iomem *sys = omap_ssi->sys;
 
  74         pm_runtime_get_sync(ssi->device.parent);
 
  76         seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n",
 
  77                 readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG));
 
  78         seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n",
 
  79                 readl(sys + SSI_GDD_MPU_IRQ_ENABLE_REG));
 
  80         seq_printf(m, "HW_ID\t\t: 0x%08x\n",
 
  81                                 readl(gdd + SSI_GDD_HW_ID_REG));
 
  82         seq_printf(m, "PPORT_ID\t: 0x%08x\n",
 
  83                                 readl(gdd + SSI_GDD_PPORT_ID_REG));
 
  84         seq_printf(m, "MPORT_ID\t: 0x%08x\n",
 
  85                                 readl(gdd + SSI_GDD_MPORT_ID_REG));
 
  86         seq_printf(m, "TEST\t\t: 0x%08x\n",
 
  87                                 readl(gdd + SSI_GDD_TEST_REG));
 
  88         seq_printf(m, "GCR\t\t: 0x%08x\n",
 
  89                                 readl(gdd + SSI_GDD_GCR_REG));
 
  91         for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
 
  92                 seq_printf(m, "\nGDD LCH %d\n=========\n", lch);
 
  93                 seq_printf(m, "CSDP\t\t: 0x%04x\n",
 
  94                                 readw(gdd + SSI_GDD_CSDP_REG(lch)));
 
  95                 seq_printf(m, "CCR\t\t: 0x%04x\n",
 
  96                                 readw(gdd + SSI_GDD_CCR_REG(lch)));
 
  97                 seq_printf(m, "CICR\t\t: 0x%04x\n",
 
  98                                 readw(gdd + SSI_GDD_CICR_REG(lch)));
 
  99                 seq_printf(m, "CSR\t\t: 0x%04x\n",
 
 100                                 readw(gdd + SSI_GDD_CSR_REG(lch)));
 
 101                 seq_printf(m, "CSSA\t\t: 0x%08x\n",
 
 102                                 readl(gdd + SSI_GDD_CSSA_REG(lch)));
 
 103                 seq_printf(m, "CDSA\t\t: 0x%08x\n",
 
 104                                 readl(gdd + SSI_GDD_CDSA_REG(lch)));
 
 105                 seq_printf(m, "CEN\t\t: 0x%04x\n",
 
 106                                 readw(gdd + SSI_GDD_CEN_REG(lch)));
 
 107                 seq_printf(m, "CSAC\t\t: 0x%04x\n",
 
 108                                 readw(gdd + SSI_GDD_CSAC_REG(lch)));
 
 109                 seq_printf(m, "CDAC\t\t: 0x%04x\n",
 
 110                                 readw(gdd + SSI_GDD_CDAC_REG(lch)));
 
 111                 seq_printf(m, "CLNK_CTRL\t: 0x%04x\n",
 
 112                                 readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch)));
 
 115         pm_runtime_put(ssi->device.parent);
 
 120 static int ssi_regs_open(struct inode *inode, struct file *file)
 
 122         return single_open(file, ssi_debug_show, inode->i_private);
 
 125 static int ssi_gdd_regs_open(struct inode *inode, struct file *file)
 
 127         return single_open(file, ssi_debug_gdd_show, inode->i_private);
 
 130 static const struct file_operations ssi_regs_fops = {
 
 131         .open           = ssi_regs_open,
 
 134         .release        = single_release,
 
 137 static const struct file_operations ssi_gdd_regs_fops = {
 
 138         .open           = ssi_gdd_regs_open,
 
 141         .release        = single_release,
 
 144 static int ssi_debug_add_ctrl(struct hsi_controller *ssi)
 
 146         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 150         omap_ssi->dir = debugfs_create_dir(dev_name(&ssi->device), NULL);
 
 154         debugfs_create_file("regs", S_IRUGO, omap_ssi->dir, ssi,
 
 157         dir = debugfs_create_dir("gdd", omap_ssi->dir);
 
 160         debugfs_create_file("regs", S_IRUGO, dir, ssi, &ssi_gdd_regs_fops);
 
 164         debugfs_remove_recursive(omap_ssi->dir);
 
 169 static void ssi_debug_remove_ctrl(struct hsi_controller *ssi)
 
 171         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 173         debugfs_remove_recursive(omap_ssi->dir);
 
 175 #endif /* CONFIG_DEBUG_FS */
 
 178  * FIXME: Horrible HACK needed until we remove the useless wakeline test
 
 179  * in the CMT. To be removed !!!!
 
 181 void ssi_waketest(struct hsi_client *cl, unsigned int enable)
 
 183         struct hsi_port *port = hsi_get_port(cl);
 
 184         struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
 
 185         struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
 
 186         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 188         omap_port->wktest = !!enable;
 
 189         if (omap_port->wktest) {
 
 190                 pm_runtime_get_sync(ssi->device.parent);
 
 191                 writel_relaxed(SSI_WAKE(0),
 
 192                                 omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
 
 194                 writel_relaxed(SSI_WAKE(0),
 
 195                                 omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
 
 196                 pm_runtime_put(ssi->device.parent);
 
 199 EXPORT_SYMBOL_GPL(ssi_waketest);
 
 201 static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
 
 203         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 204         struct hsi_msg *msg = omap_ssi->gdd_trn[lch].msg;
 
 205         struct hsi_port *port = to_hsi_port(msg->cl->device.parent);
 
 206         struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
 
 211         spin_lock(&omap_ssi->lock);
 
 213         val = readl(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
 
 214         val &= ~SSI_GDD_LCH(lch);
 
 215         writel_relaxed(val, omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
 
 217         if (msg->ttype == HSI_MSG_READ) {
 
 218                 dir = DMA_FROM_DEVICE;
 
 219                 val = SSI_DATAAVAILABLE(msg->channel);
 
 220                 pm_runtime_put(omap_port->pdev);
 
 223                 val = SSI_DATAACCEPT(msg->channel);
 
 224                 /* Keep clocks reference for write pio event */
 
 226         dma_unmap_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, dir);
 
 227         csr = readw(omap_ssi->gdd + SSI_GDD_CSR_REG(lch));
 
 228         omap_ssi->gdd_trn[lch].msg = NULL; /* release GDD lch */
 
 229         dev_dbg(&port->device, "DMA completed ch %d ttype %d\n",
 
 230                                 msg->channel, msg->ttype);
 
 231         spin_unlock(&omap_ssi->lock);
 
 232         if (csr & SSI_CSR_TOUR) { /* Timeout error */
 
 233                 msg->status = HSI_STATUS_ERROR;
 
 235                 spin_lock(&omap_port->lock);
 
 236                 list_del(&msg->link); /* Dequeue msg */
 
 237                 spin_unlock(&omap_port->lock);
 
 239                 list_add_tail(&msg->link, &omap_port->errqueue);
 
 240                 schedule_delayed_work(&omap_port->errqueue_work, 0);
 
 243         spin_lock(&omap_port->lock);
 
 244         val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
 
 245         writel_relaxed(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
 
 246         spin_unlock(&omap_port->lock);
 
 248         msg->status = HSI_STATUS_COMPLETED;
 
 249         msg->actual_len = sg_dma_len(msg->sgt.sgl);
 
 252 static void ssi_gdd_tasklet(unsigned long dev)
 
 254         struct hsi_controller *ssi = (struct hsi_controller *)dev;
 
 255         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 256         void __iomem *sys = omap_ssi->sys;
 
 260         pm_runtime_get(ssi->device.parent);
 
 262         if (!pm_runtime_active(ssi->device.parent)) {
 
 263                 dev_warn(ssi->device.parent, "ssi_gdd_tasklet called without runtime PM!\n");
 
 264                 pm_runtime_put(ssi->device.parent);
 
 268         status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
 
 269         for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
 
 270                 if (status_reg & SSI_GDD_LCH(lch))
 
 271                         ssi_gdd_complete(ssi, lch);
 
 273         writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG);
 
 274         status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
 
 276         pm_runtime_put(ssi->device.parent);
 
 279                 tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
 
 281                 enable_irq(omap_ssi->gdd_irq);
 
 285 static irqreturn_t ssi_gdd_isr(int irq, void *ssi)
 
 287         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 289         tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
 
 290         disable_irq_nosync(irq);
 
 295 static unsigned long ssi_get_clk_rate(struct hsi_controller *ssi)
 
 297         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 298         unsigned long rate = clk_get_rate(omap_ssi->fck);
 
 302 static int ssi_clk_event(struct notifier_block *nb, unsigned long event,
 
 305         struct omap_ssi_controller *omap_ssi = container_of(nb,
 
 306                                         struct omap_ssi_controller, fck_nb);
 
 307         struct hsi_controller *ssi = to_hsi_controller(omap_ssi->dev);
 
 308         struct clk_notifier_data *clk_data = data;
 
 309         struct omap_ssi_port *omap_port;
 
 313         case PRE_RATE_CHANGE:
 
 314                 dev_dbg(&ssi->device, "pre rate change\n");
 
 316                 for (i = 0; i < ssi->num_ports; i++) {
 
 317                         omap_port = omap_ssi->port[i];
 
 322                         /* Workaround for SWBREAK + CAwake down race in CMT */
 
 323                         disable_irq(omap_port->wake_irq);
 
 325                         /* stop all ssi communication */
 
 326                         pinctrl_pm_select_idle_state(omap_port->pdev);
 
 327                         udelay(1); /* wait for racing frames */
 
 331         case ABORT_RATE_CHANGE:
 
 332                 dev_dbg(&ssi->device, "abort rate change\n");
 
 334         case POST_RATE_CHANGE:
 
 335                 dev_dbg(&ssi->device, "post rate change (%lu -> %lu)\n",
 
 336                         clk_data->old_rate, clk_data->new_rate);
 
 337                 omap_ssi->fck_rate = DIV_ROUND_CLOSEST(clk_data->new_rate, 1000); /* kHz */
 
 339                 for (i = 0; i < ssi->num_ports; i++) {
 
 340                         omap_port = omap_ssi->port[i];
 
 345                         omap_ssi_port_update_fclk(ssi, omap_port);
 
 347                         /* resume ssi communication */
 
 348                         pinctrl_pm_select_default_state(omap_port->pdev);
 
 349                         enable_irq(omap_port->wake_irq);
 
 360 static int ssi_get_iomem(struct platform_device *pd,
 
 361                 const char *name, void __iomem **pbase, dma_addr_t *phy)
 
 363         struct resource *mem;
 
 365         struct hsi_controller *ssi = platform_get_drvdata(pd);
 
 367         mem = platform_get_resource_byname(pd, IORESOURCE_MEM, name);
 
 368         base = devm_ioremap_resource(&ssi->device, mem);
 
 370                 return PTR_ERR(base);
 
 380 static int ssi_add_controller(struct hsi_controller *ssi,
 
 381                                                 struct platform_device *pd)
 
 383         struct omap_ssi_controller *omap_ssi;
 
 386         omap_ssi = devm_kzalloc(&ssi->device, sizeof(*omap_ssi), GFP_KERNEL);
 
 390         err = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL);
 
 395         ssi->owner = THIS_MODULE;
 
 396         ssi->device.parent = &pd->dev;
 
 397         dev_set_name(&ssi->device, "ssi%d", ssi->id);
 
 398         hsi_controller_set_drvdata(ssi, omap_ssi);
 
 399         omap_ssi->dev = &ssi->device;
 
 400         err = ssi_get_iomem(pd, "sys", &omap_ssi->sys, NULL);
 
 403         err = ssi_get_iomem(pd, "gdd", &omap_ssi->gdd, NULL);
 
 406         err = platform_get_irq_byname(pd, "gdd_mpu");
 
 408                 dev_err(&pd->dev, "GDD IRQ resource missing\n");
 
 411         omap_ssi->gdd_irq = err;
 
 412         tasklet_init(&omap_ssi->gdd_tasklet, ssi_gdd_tasklet,
 
 414         err = devm_request_irq(&ssi->device, omap_ssi->gdd_irq, ssi_gdd_isr,
 
 417                 dev_err(&ssi->device, "Request GDD IRQ %d failed (%d)",
 
 418                                                         omap_ssi->gdd_irq, err);
 
 422         omap_ssi->port = devm_kcalloc(&ssi->device, ssi->num_ports,
 
 423                                       sizeof(*omap_ssi->port), GFP_KERNEL);
 
 424         if (!omap_ssi->port) {
 
 429         omap_ssi->fck = devm_clk_get(&ssi->device, "ssi_ssr_fck");
 
 430         if (IS_ERR(omap_ssi->fck)) {
 
 431                 dev_err(&pd->dev, "Could not acquire clock \"ssi_ssr_fck\": %li\n",
 
 432                         PTR_ERR(omap_ssi->fck));
 
 437         omap_ssi->fck_nb.notifier_call = ssi_clk_event;
 
 438         omap_ssi->fck_nb.priority = INT_MAX;
 
 439         clk_notifier_register(omap_ssi->fck, &omap_ssi->fck_nb);
 
 441         /* TODO: find register, which can be used to detect context loss */
 
 442         omap_ssi->get_loss = NULL;
 
 444         omap_ssi->max_speed = UINT_MAX;
 
 445         spin_lock_init(&omap_ssi->lock);
 
 446         err = hsi_register_controller(ssi);
 
 454         ida_simple_remove(&platform_omap_ssi_ida, ssi->id);
 
 458 static int ssi_hw_init(struct hsi_controller *ssi)
 
 460         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 463         err = pm_runtime_get_sync(ssi->device.parent);
 
 465                 dev_err(&ssi->device, "runtime PM failed %d\n", err);
 
 469         writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
 
 470         /* Get FCK rate in kHz */
 
 471         omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000);
 
 472         dev_dbg(&ssi->device, "SSI fck rate %lu kHz\n", omap_ssi->fck_rate);
 
 474         writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG);
 
 475         omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON;
 
 476         pm_runtime_put_sync(ssi->device.parent);
 
 481 static void ssi_remove_controller(struct hsi_controller *ssi)
 
 483         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 485         tasklet_kill(&omap_ssi->gdd_tasklet);
 
 486         hsi_unregister_controller(ssi);
 
 487         clk_notifier_unregister(omap_ssi->fck, &omap_ssi->fck_nb);
 
 488         ida_simple_remove(&platform_omap_ssi_ida, id);
 
 491 static inline int ssi_of_get_available_ports_count(const struct device_node *np)
 
 493         struct device_node *child;
 
 496         for_each_available_child_of_node(np, child)
 
 497                 if (of_device_is_compatible(child, "ti,omap3-ssi-port"))
 
 503 static int ssi_remove_ports(struct device *dev, void *c)
 
 505         struct platform_device *pdev = to_platform_device(dev);
 
 510         of_node_clear_flag(dev->of_node, OF_POPULATED);
 
 511         of_device_unregister(pdev);
 
 516 static int ssi_probe(struct platform_device *pd)
 
 518         struct platform_device *childpdev;
 
 519         struct device_node *np = pd->dev.of_node;
 
 520         struct device_node *child;
 
 521         struct hsi_controller *ssi;
 
 526                 dev_err(&pd->dev, "missing device tree data\n");
 
 530         num_ports = ssi_of_get_available_ports_count(np);
 
 532         ssi = hsi_alloc_controller(num_ports, GFP_KERNEL);
 
 534                 dev_err(&pd->dev, "No memory for controller\n");
 
 538         platform_set_drvdata(pd, ssi);
 
 540         err = ssi_add_controller(ssi, pd);
 
 544         pm_runtime_enable(&pd->dev);
 
 546         err = ssi_hw_init(ssi);
 
 549 #ifdef CONFIG_DEBUG_FS
 
 550         err = ssi_debug_add_ctrl(ssi);
 
 555         for_each_available_child_of_node(np, child) {
 
 556                 if (!of_device_is_compatible(child, "ti,omap3-ssi-port"))
 
 559                 childpdev = of_platform_device_create(child, NULL, &pd->dev);
 
 562                         dev_err(&pd->dev, "failed to create ssi controller port\n");
 
 567         dev_info(&pd->dev, "ssi controller %d initialized (%d ports)!\n",
 
 571         device_for_each_child(&pd->dev, NULL, ssi_remove_ports);
 
 573         ssi_remove_controller(ssi);
 
 575         platform_set_drvdata(pd, NULL);
 
 576         pm_runtime_disable(&pd->dev);
 
 581 static int ssi_remove(struct platform_device *pd)
 
 583         struct hsi_controller *ssi = platform_get_drvdata(pd);
 
 585         /* cleanup of of_platform_populate() call */
 
 586         device_for_each_child(&pd->dev, NULL, ssi_remove_ports);
 
 588 #ifdef CONFIG_DEBUG_FS
 
 589         ssi_debug_remove_ctrl(ssi);
 
 591         ssi_remove_controller(ssi);
 
 592         platform_set_drvdata(pd, NULL);
 
 594         pm_runtime_disable(&pd->dev);
 
 600 static int omap_ssi_runtime_suspend(struct device *dev)
 
 602         struct hsi_controller *ssi = dev_get_drvdata(dev);
 
 603         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 605         dev_dbg(dev, "runtime suspend!\n");
 
 607         if (omap_ssi->get_loss)
 
 608                 omap_ssi->loss_count =
 
 609                                 omap_ssi->get_loss(ssi->device.parent);
 
 614 static int omap_ssi_runtime_resume(struct device *dev)
 
 616         struct hsi_controller *ssi = dev_get_drvdata(dev);
 
 617         struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
 
 619         dev_dbg(dev, "runtime resume!\n");
 
 621         if ((omap_ssi->get_loss) && (omap_ssi->loss_count ==
 
 622                                 omap_ssi->get_loss(ssi->device.parent)))
 
 625         writel_relaxed(omap_ssi->gdd_gcr, omap_ssi->gdd + SSI_GDD_GCR_REG);
 
 630 static const struct dev_pm_ops omap_ssi_pm_ops = {
 
 631         SET_RUNTIME_PM_OPS(omap_ssi_runtime_suspend, omap_ssi_runtime_resume,
 
 635 #define DEV_PM_OPS     (&omap_ssi_pm_ops)
 
 637 #define DEV_PM_OPS     NULL
 
 641 static const struct of_device_id omap_ssi_of_match[] = {
 
 642         { .compatible = "ti,omap3-ssi", },
 
 645 MODULE_DEVICE_TABLE(of, omap_ssi_of_match);
 
 647 #define omap_ssi_of_match NULL
 
 650 static struct platform_driver ssi_pdriver = {
 
 652         .remove = ssi_remove,
 
 656                 .of_match_table = omap_ssi_of_match,
 
 660 static int __init ssi_init(void) {
 
 663         ret = platform_driver_register(&ssi_pdriver);
 
 667         return platform_driver_register(&ssi_port_pdriver);
 
 669 module_init(ssi_init);
 
 671 static void __exit ssi_exit(void) {
 
 672         platform_driver_unregister(&ssi_port_pdriver);
 
 673         platform_driver_unregister(&ssi_pdriver);
 
 675 module_exit(ssi_exit);
 
 677 MODULE_ALIAS("platform:omap_ssi");
 
 678 MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
 
 679 MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
 
 680 MODULE_DESCRIPTION("Synchronous Serial Interface Driver");
 
 681 MODULE_LICENSE("GPL v2");