IMX: introduce clock API
authorSascha Hauer <s.hauer@pengutronix.de>
Sat, 5 Jul 2008 08:02:46 +0000 (10:02 +0200)
committerRobert Schwebel <r.schwebel@pengutronix.de>
Sat, 5 Jul 2008 08:02:46 +0000 (10:02 +0200)
This patch introduces the clock API for i.MX and converts all
in-Kernel drivers to use it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/clock.c [new file with mode: 0644]
arch/arm/mach-imx/cpufreq.c
arch/arm/mach-imx/generic.c
arch/arm/mach-imx/time.c
drivers/mmc/host/imxmmc.c
drivers/serial/imx.c
drivers/spi/spi_imx.c
include/asm-arm/arch-imx/hardware.h

index 88d5e61..b047c7e 100644 (file)
@@ -4,7 +4,7 @@
 
 # Object file lists.
 
-obj-y                  += irq.o time.o dma.o generic.o
+obj-y                  += irq.o time.o dma.o generic.o clock.o
 
 obj-$(CONFIG_CPU_FREQ_IMX)     += cpufreq.o
 
diff --git a/arch/arm/mach-imx/clock.c b/arch/arm/mach-imx/clock.c
new file mode 100644 (file)
index 0000000..6a90fe5
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ *  Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/arch/imx-regs.h>
+
+/*
+ * Very simple approach: We can't disable clocks, so we do
+ * not need refcounting
+ */
+
+struct clk {
+       struct list_head node;
+       const char *name;
+       unsigned long (*get_rate)(void);
+};
+
+/*
+ *  get the system pll clock in Hz
+ *
+ *                  mfi + mfn / (mfd +1)
+ *  f = 2 * f_ref * --------------------
+ *                        pd + 1
+ */
+static unsigned long imx_decode_pll(unsigned int pll, u32 f_ref)
+{
+       unsigned long long ll;
+       unsigned long quot;
+
+       u32 mfi = (pll >> 10) & 0xf;
+       u32 mfn = pll & 0x3ff;
+       u32 mfd = (pll >> 16) & 0x3ff;
+       u32 pd =  (pll >> 26) & 0xf;
+
+       mfi = mfi <= 5 ? 5 : mfi;
+
+       ll = 2 * (unsigned long long)f_ref *
+               ((mfi << 16) + (mfn << 16) / (mfd + 1));
+       quot = (pd + 1) * (1 << 16);
+       ll += quot / 2;
+       do_div(ll, quot);
+       return (unsigned long)ll;
+}
+
+static unsigned long imx_get_system_clk(void)
+{
+       u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512);
+
+       return imx_decode_pll(SPCTL0, f_ref);
+}
+
+static unsigned long imx_get_mcu_clk(void)
+{
+       return imx_decode_pll(MPCTL0, CLK32 * 512);
+}
+
+/*
+ *  get peripheral clock 1 ( UART[12], Timer[12], PWM )
+ */
+static unsigned long imx_get_perclk1(void)
+{
+       return imx_get_system_clk() / (((PCDR) & 0xf)+1);
+}
+
+/*
+ *  get peripheral clock 2 ( LCD, SD, SPI[12] )
+ */
+static unsigned long imx_get_perclk2(void)
+{
+       return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1);
+}
+
+/*
+ *  get peripheral clock 3 ( SSI )
+ */
+static unsigned long imx_get_perclk3(void)
+{
+       return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1);
+}
+
+/*
+ *  get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
+ */
+static unsigned long imx_get_hclk(void)
+{
+       return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1);
+}
+
+static struct clk clk_system_clk = {
+       .name = "system_clk",
+       .get_rate = imx_get_system_clk,
+};
+
+static struct clk clk_hclk = {
+       .name = "hclk",
+       .get_rate = imx_get_hclk,
+};
+
+static struct clk clk_mcu_clk = {
+       .name = "mcu_clk",
+       .get_rate = imx_get_mcu_clk,
+};
+
+static struct clk clk_perclk1 = {
+       .name = "perclk1",
+       .get_rate = imx_get_perclk1,
+};
+
+static struct clk clk_uart_clk = {
+       .name = "uart_clk",
+       .get_rate = imx_get_perclk1,
+};
+
+static struct clk clk_perclk2 = {
+       .name = "perclk2",
+       .get_rate = imx_get_perclk2,
+};
+
+static struct clk clk_perclk3 = {
+       .name = "perclk3",
+       .get_rate = imx_get_perclk3,
+};
+
+static struct clk *clks[] = {
+       &clk_perclk1,
+       &clk_perclk2,
+       &clk_perclk3,
+       &clk_system_clk,
+       &clk_hclk,
+       &clk_mcu_clk,
+       &clk_uart_clk,
+};
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+       struct clk *p, *clk = ERR_PTR(-ENOENT);
+
+       mutex_lock(&clocks_mutex);
+       list_for_each_entry(p, &clocks, node) {
+               if (!strcmp(p->name, id)) {
+                       clk = p;
+                       goto found;
+               }
+       }
+
+found:
+       mutex_unlock(&clocks_mutex);
+
+       return clk;
+}
+
+void clk_put(struct clk *clk)
+{
+}
+
+int clk_enable(struct clk *clk)
+{
+       return 0;
+}
+
+void clk_disable(struct clk *clk)
+{
+}
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+       return clk->get_rate();
+}
+
+int imx_clocks_init(void)
+{
+       int i;
+
+       mutex_lock(&clocks_mutex);
+       for (i = 0; i < ARRAY_SIZE(clks); i++)
+               list_add(&clks[i]->node, &clocks);
+       mutex_unlock(&clocks_mutex);
+
+       return 0;
+}
+
index e548ba7..be0809b 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 #include <asm/system.h>
 
 #include <asm/hardware.h>
@@ -52,6 +54,8 @@
 static u32 mpctl0_at_boot;
 static u32 bclk_div_at_boot;
 
+static struct clk *system_clk, *mcu_clk;
+
 static void imx_set_async_mode(void)
 {
        adjust_cr(CR_920T_CLOCK_MODE, CR_920T_ASYNC_MODE);
@@ -160,10 +164,10 @@ static unsigned int imx_get_speed(unsigned int cpu)
        cr = get_cr();
 
        if((cr & CR_920T_CLOCK_MODE) == CR_920T_FASTBUS_MODE) {
-               freq = imx_get_system_clk();
+               freq = clk_get_rate(system_clk);
                freq = (freq + bclk_div/2) / bclk_div;
        } else {
-               freq = imx_get_mcu_clk();
+               freq = clk_get_rate(mcu_clk);
                if (cscr & CSCR_MPU_PRESC)
                        freq /= 2;
        }
@@ -201,7 +205,7 @@ static int imx_set_target(struct cpufreq_policy *policy,
        pr_debug(KERN_DEBUG "imx: requested frequency %ld Hz, mpctl0 at boot 0x%08x\n",
                        freq, mpctl0_at_boot);
 
-       sysclk = imx_get_system_clk();
+       sysclk = clk_get_rate(system_clk);
 
        if (freq > sysclk / bclk_div_at_boot + 1000000) {
                freq = imx_compute_mpctl(&mpctl0, mpctl0_at_boot, CLK32 * 512, freq, relation);
@@ -290,6 +294,16 @@ static int __init imx_cpufreq_init(void)
        bclk_div_at_boot = __mfld2val(CSCR_BCLK_DIV, CSCR) + 1;
        mpctl0_at_boot = 0;
 
+       system_clk = clk_get(NULL, "system_clk");
+       if (IS_ERR(system_clk))
+               return PTR_ERR(system_clk);
+
+       mcu_clk = clk_get(NULL, "mcu_clk");
+       if (IS_ERR(mcu_clk)) {
+               clk_put(system_clk);
+               return PTR_ERR(mcu_clk);
+       }
+
        if((CSCR & CSCR_MPEN) &&
           ((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE))
                mpctl0_at_boot = MPCTL0;
index 4cfc9d3..98ddd8a 100644 (file)
@@ -214,82 +214,6 @@ int imx_irq_to_gpio(unsigned irq)
 
 EXPORT_SYMBOL(imx_irq_to_gpio);
 
-/*
- *  get the system pll clock in Hz
- *
- *                  mfi + mfn / (mfd +1)
- *  f = 2 * f_ref * --------------------
- *                        pd + 1
- */
-static unsigned int imx_decode_pll(unsigned int pll, u32 f_ref)
-{
-       unsigned long long ll;
-       unsigned long quot;
-
-       u32 mfi = (pll >> 10) & 0xf;
-       u32 mfn = pll & 0x3ff;
-       u32 mfd = (pll >> 16) & 0x3ff;
-       u32 pd =  (pll >> 26) & 0xf;
-
-       mfi = mfi <= 5 ? 5 : mfi;
-
-       ll = 2 * (unsigned long long)f_ref * ( (mfi<<16) + (mfn<<16) / (mfd+1) );
-       quot = (pd+1) * (1<<16);
-       ll += quot / 2;
-       do_div(ll, quot);
-       return (unsigned int) ll;
-}
-
-unsigned int imx_get_system_clk(void)
-{
-       u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512);
-
-       return imx_decode_pll(SPCTL0, f_ref);
-}
-EXPORT_SYMBOL(imx_get_system_clk);
-
-unsigned int imx_get_mcu_clk(void)
-{
-       return imx_decode_pll(MPCTL0, CLK32 * 512);
-}
-EXPORT_SYMBOL(imx_get_mcu_clk);
-
-/*
- *  get peripheral clock 1 ( UART[12], Timer[12], PWM )
- */
-unsigned int imx_get_perclk1(void)
-{
-       return imx_get_system_clk() / (((PCDR) & 0xf)+1);
-}
-EXPORT_SYMBOL(imx_get_perclk1);
-
-/*
- *  get peripheral clock 2 ( LCD, SD, SPI[12] )
- */
-unsigned int imx_get_perclk2(void)
-{
-       return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1);
-}
-EXPORT_SYMBOL(imx_get_perclk2);
-
-/*
- *  get peripheral clock 3 ( SSI )
- */
-unsigned int imx_get_perclk3(void)
-{
-       return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1);
-}
-EXPORT_SYMBOL(imx_get_perclk3);
-
-/*
- *  get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
- */
-unsigned int imx_get_hclk(void)
-{
-       return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1);
-}
-EXPORT_SYMBOL(imx_get_hclk);
-
 static struct resource imx_mmc_resources[] = {
        [0] = {
                .start  = 0x00214000,
index d86d124..5a41e96 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/time.h>
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
+#include <linux/clk.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
@@ -86,10 +87,10 @@ static struct clocksource clocksource_imx = {
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-static int __init imx_clocksource_init(void)
+static int __init imx_clocksource_init(unsigned long rate)
 {
        clocksource_imx.mult =
-               clocksource_hz2mult(imx_get_perclk1(), clocksource_imx.shift);
+               clocksource_hz2mult(rate, clocksource_imx.shift);
        clocksource_register(&clocksource_imx);
 
        return 0;
@@ -174,9 +175,9 @@ static struct clock_event_device clockevent_imx = {
        .rating         = 200,
 };
 
-static int __init imx_clockevent_init(void)
+static int __init imx_clockevent_init(unsigned long rate)
 {
-       clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC,
+       clockevent_imx.mult = div_sc(rate, NSEC_PER_SEC,
                                        clockevent_imx.shift);
        clockevent_imx.max_delta_ns =
                clockevent_delta2ns(0xfffffffe, &clockevent_imx);
@@ -190,13 +191,23 @@ static int __init imx_clockevent_init(void)
        return 0;
 }
 
+extern int imx_clocks_init(void);
 
 static void __init imx_timer_init(void)
 {
+       struct clk *clk;
+       unsigned long rate;
+
+       imx_clocks_init();
+
+       clk = clk_get(NULL, "perclk1");
+       clk_enable(clk);
+       rate = clk_get_rate(clk);
+
        imx_timer_hardware_init();
-       imx_clocksource_init();
+       imx_clocksource_init(rate);
 
-       imx_clockevent_init();
+       imx_clockevent_init(rate);
 
        /*
         * Make irqs happen for the system timer
index 95f33e8..ef2f6fc 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
 #include <linux/delay.h>
+#include <linux/clk.h>
 
 #include <asm/dma.h>
 #include <asm/io.h>
@@ -92,6 +93,8 @@ struct imxmci_host {
        unsigned char           actual_bus_width;
 
        int                     prev_cmd_code;
+
+       struct clk              *clk;
 };
 
 #define IMXMCI_PEND_IRQ_b      0
@@ -841,7 +844,7 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                /* The prescaler is 5 for PERCLK2 equal to 96MHz
                 * then 96MHz / 5 = 19.2 MHz
                 */
-               clk=imx_get_perclk2();
+               clk = clk_get_rate(host->clk);
                prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
                switch(prescaler) {
                case 0:
@@ -994,6 +997,13 @@ static int imxmci_probe(struct platform_device *pdev)
        host->res = r;
        host->irq = irq;
 
+       host->clk = clk_get(&pdev->dev, "perclk2");
+       if (IS_ERR(host->clk)) {
+               ret = PTR_ERR(host->clk);
+               goto out;
+       }
+       clk_enable(host->clk);
+
        imx_gpio_mode(PB8_PF_SD_DAT0);
        imx_gpio_mode(PB9_PF_SD_DAT1);
        imx_gpio_mode(PB10_PF_SD_DAT2);
@@ -1053,6 +1063,10 @@ out:
                        imx_dma_free(host->dma);
                        host->dma_allocated=0;
                }
+               if (host->clk) {
+                       clk_disable(host->clk);
+                       clk_put(host->clk);
+               }
        }
        if (mmc)
                mmc_free_host(mmc);
@@ -1082,6 +1096,9 @@ static int imxmci_remove(struct platform_device *pdev)
 
                tasklet_kill(&host->tasklet);
 
+               clk_disable(host->clk);
+               clk_put(host->clk);
+
                release_resource(host->res);
 
                mmc_free_host(mmc);
index 8d6cb74..9e2162e 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
+#include <linux/clk.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -184,6 +185,7 @@ struct imx_port {
        unsigned int            old_status;
        int                     txirq,rxirq,rtsirq;
        int                     have_rtscts:1;
+       struct clk              *clk;
 };
 
 /*
@@ -479,7 +481,8 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
         * RFDIV is set such way to satisfy requested uartclk value
         */
        val = TXTL << 10 | RXTL;
-       ufcr_rfdiv = (imx_get_perclk1() + sport->port.uartclk / 2) / sport->port.uartclk;
+       ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2)
+                       / sport->port.uartclk;
 
        if(!ufcr_rfdiv)
                ufcr_rfdiv = 1;
@@ -916,7 +919,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
                else
                        ucfr_rfdiv = 6 - ucfr_rfdiv;
 
-               uartclk = imx_get_perclk1();
+               uartclk = clk_get_rate(sport->clk);
                uartclk /= ucfr_rfdiv;
 
                {       /*
@@ -1054,7 +1057,15 @@ static int serial_imx_probe(struct platform_device *pdev)
        init_timer(&sport->timer);
        sport->timer.function = imx_timeout;
        sport->timer.data     = (unsigned long)sport;
-       sport->port.uartclk = imx_get_perclk1();
+
+       sport->clk = clk_get(&pdev->dev, "uart_clk");
+       if (IS_ERR(sport->clk)) {
+               ret = PTR_ERR(sport->clk);
+               goto unmap;
+       }
+       clk_enable(sport->clk);
+
+       sport->port.uartclk = clk_get_rate(sport->clk);
 
        imx_ports[pdev->id] = sport;
 
@@ -1069,6 +1080,8 @@ static int serial_imx_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, &sport->port);
 
        return 0;
+unmap:
+       iounmap(sport->port.membase);
 free:
        kfree(sport);
 
@@ -1084,8 +1097,12 @@ static int serial_imx_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
-       if (sport)
+       if (sport) {
                uart_remove_one_port(&imx_reg, &sport->port);
+               clk_put(sport->clk);
+       }
+
+       clk_disable(sport->clk);
 
        if (pdata->exit)
                pdata->exit(pdev);
index c730d05..bd0729b 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/spi/spi.h>
 #include <linux/workqueue.h>
 #include <linux/delay.h>
+#include <linux/clk.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -250,6 +251,8 @@ struct driver_data {
        int tx_dma_needs_unmap;
        size_t tx_map_len;
        u32 dummy_dma_buf ____cacheline_aligned;
+
+       struct clk *clk;
 };
 
 /* Runtime state */
@@ -855,15 +858,15 @@ static irqreturn_t spi_int(int irq, void *dev_id)
        return drv_data->transfer_handler(drv_data);
 }
 
-static inline u32 spi_speed_hz(u32 data_rate)
+static inline u32 spi_speed_hz(struct driver_data *drv_data, u32 data_rate)
 {
-       return imx_get_perclk2() / (4 << ((data_rate) >> 13));
+       return clk_get_rate(drv_data->clk) / (4 << ((data_rate) >> 13));
 }
 
-static u32 spi_data_rate(u32 speed_hz)
+static u32 spi_data_rate(struct driver_data *drv_data, u32 speed_hz)
 {
        u32 div;
-       u32 quantized_hz = imx_get_perclk2() >> 2;
+       u32 quantized_hz = clk_get_rate(drv_data->clk) >> 2;
 
        for (div = SPI_PERCLK2_DIV_MIN;
                div <= SPI_PERCLK2_DIV_MAX;
@@ -947,7 +950,7 @@ static void pump_transfers(unsigned long data)
        tmp = transfer->speed_hz;
        if (tmp == 0)
                tmp = chip->max_speed_hz;
-       tmp = spi_data_rate(tmp);
+       tmp = spi_data_rate(drv_data, tmp);
        u32_EDIT(control, SPI_CONTROL_DATARATE, tmp);
 
        writel(control, regs + SPI_CONTROL);
@@ -1109,7 +1112,7 @@ static int transfer(struct spi_device *spi, struct spi_message *msg)
        msg->actual_length = 0;
 
        /* Per transfer setup check */
-       min_speed_hz = spi_speed_hz(SPI_CONTROL_DATARATE_MIN);
+       min_speed_hz = spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN);
        max_speed_hz = spi->max_speed_hz;
        list_for_each_entry(trans, &msg->transfers, transfer_list) {
                tmp = trans->bits_per_word;
@@ -1176,6 +1179,7 @@ msg_rejected:
    applied and notified to the calling driver. */
 static int setup(struct spi_device *spi)
 {
+       struct driver_data *drv_data = spi_master_get_devdata(spi->master);
        struct spi_imx_chip *chip_info;
        struct chip_data *chip;
        int first_setup = 0;
@@ -1304,14 +1308,14 @@ static int setup(struct spi_device *spi)
        chip->n_bytes = (tmp <= 8) ? 1 : 2;
 
        /* SPI datarate */
-       tmp = spi_data_rate(spi->max_speed_hz);
+       tmp = spi_data_rate(drv_data, spi->max_speed_hz);
        if (tmp == SPI_CONTROL_DATARATE_BAD) {
                status = -EINVAL;
                dev_err(&spi->dev,
                        "setup - "
                        "HW min speed (%d Hz) exceeds required "
                        "max speed (%d Hz)\n",
-                       spi_speed_hz(SPI_CONTROL_DATARATE_MIN),
+                       spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN),
                        spi->max_speed_hz);
                if (first_setup)
                        goto err_first_setup;
@@ -1321,7 +1325,7 @@ static int setup(struct spi_device *spi)
        } else {
                u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp);
                /* Actual rounded max_speed_hz */
-               tmp = spi_speed_hz(tmp);
+               tmp = spi_speed_hz(drv_data, tmp);
                spi->max_speed_hz = tmp;
                chip->max_speed_hz = tmp;
        }
@@ -1352,7 +1356,7 @@ static int setup(struct spi_device *spi)
                chip->period & SPI_PERIOD_WAIT,
                spi->mode,
                spi->bits_per_word,
-               spi_speed_hz(SPI_CONTROL_DATARATE_MIN),
+               spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN),
                spi->max_speed_hz);
        return status;
 
@@ -1465,6 +1469,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
                goto err_no_pdata;
        }
 
+       drv_data->clk = clk_get(&pdev->dev, "perclk2");
+       if (IS_ERR(drv_data->clk)) {
+               dev_err(&pdev->dev, "probe - cannot get get\n");
+               status = PTR_ERR(drv_data->clk);
+               goto err_no_clk;
+       }
+       clk_enable(drv_data->clk);
+
        /* Allocate master with space for drv_data */
        master = spi_alloc_master(dev, sizeof(struct driver_data));
        if (!master) {
@@ -1623,6 +1635,9 @@ err_no_iores:
        spi_master_put(master);
 
 err_no_pdata:
+       clk_disable(drv_data->clk);
+       clk_put(drv_data->clk);
+err_no_clk:
 err_no_mem:
        return status;
 }
@@ -1662,6 +1677,9 @@ static int __exit spi_imx_remove(struct platform_device *pdev)
        if (irq >= 0)
                free_irq(irq, drv_data);
 
+       clk_disable(drv_data->clk);
+       clk_put(drv_data->clk);
+
        /* Release map resources */
        iounmap(drv_data->regs);
        release_resource(drv_data->ioarea);
index adffb6a..6542ca5 100644 (file)
  */
 extern void imx_gpio_mode( int gpio_mode );
 
-/* get frequencies in Hz */
-extern unsigned int imx_get_system_clk(void);
-extern unsigned int imx_get_mcu_clk(void);
-extern unsigned int imx_get_perclk1(void); /* UART[12], Timer[12], PWM */
-extern unsigned int imx_get_perclk2(void); /* LCD, SD, SPI[12]         */
-extern unsigned int imx_get_perclk3(void); /* SSI                      */
-extern unsigned int imx_get_hclk(void);    /* SDRAM, CSI, Memory Stick,*/
-                                           /* I2C, DMA                 */
 #endif
 
 #define MAXIRQNUM                       62