IMX: introduce clock API
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>
This commit is contained in:
parent
dbff4e9ea2
commit
38a41fdf94
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
# Object file lists.
|
# 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
|
obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
|
|
||||||
#include <asm/hardware.h>
|
#include <asm/hardware.h>
|
||||||
|
@ -52,6 +54,8 @@
|
||||||
static u32 mpctl0_at_boot;
|
static u32 mpctl0_at_boot;
|
||||||
static u32 bclk_div_at_boot;
|
static u32 bclk_div_at_boot;
|
||||||
|
|
||||||
|
static struct clk *system_clk, *mcu_clk;
|
||||||
|
|
||||||
static void imx_set_async_mode(void)
|
static void imx_set_async_mode(void)
|
||||||
{
|
{
|
||||||
adjust_cr(CR_920T_CLOCK_MODE, CR_920T_ASYNC_MODE);
|
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();
|
cr = get_cr();
|
||||||
|
|
||||||
if((cr & CR_920T_CLOCK_MODE) == CR_920T_FASTBUS_MODE) {
|
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;
|
freq = (freq + bclk_div/2) / bclk_div;
|
||||||
} else {
|
} else {
|
||||||
freq = imx_get_mcu_clk();
|
freq = clk_get_rate(mcu_clk);
|
||||||
if (cscr & CSCR_MPU_PRESC)
|
if (cscr & CSCR_MPU_PRESC)
|
||||||
freq /= 2;
|
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",
|
pr_debug(KERN_DEBUG "imx: requested frequency %ld Hz, mpctl0 at boot 0x%08x\n",
|
||||||
freq, mpctl0_at_boot);
|
freq, mpctl0_at_boot);
|
||||||
|
|
||||||
sysclk = imx_get_system_clk();
|
sysclk = clk_get_rate(system_clk);
|
||||||
|
|
||||||
if (freq > sysclk / bclk_div_at_boot + 1000000) {
|
if (freq > sysclk / bclk_div_at_boot + 1000000) {
|
||||||
freq = imx_compute_mpctl(&mpctl0, mpctl0_at_boot, CLK32 * 512, freq, relation);
|
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;
|
bclk_div_at_boot = __mfld2val(CSCR_BCLK_DIV, CSCR) + 1;
|
||||||
mpctl0_at_boot = 0;
|
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) &&
|
if((CSCR & CSCR_MPEN) &&
|
||||||
((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE))
|
((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE))
|
||||||
mpctl0_at_boot = MPCTL0;
|
mpctl0_at_boot = MPCTL0;
|
||||||
|
|
|
@ -214,82 +214,6 @@ int imx_irq_to_gpio(unsigned irq)
|
||||||
|
|
||||||
EXPORT_SYMBOL(imx_irq_to_gpio);
|
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[] = {
|
static struct resource imx_mmc_resources[] = {
|
||||||
[0] = {
|
[0] = {
|
||||||
.start = 0x00214000,
|
.start = 0x00214000,
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <linux/clocksource.h>
|
#include <linux/clocksource.h>
|
||||||
#include <linux/clockchips.h>
|
#include <linux/clockchips.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#include <asm/hardware.h>
|
#include <asm/hardware.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
@ -86,10 +87,10 @@ static struct clocksource clocksource_imx = {
|
||||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
.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_imx.mult =
|
||||||
clocksource_hz2mult(imx_get_perclk1(), clocksource_imx.shift);
|
clocksource_hz2mult(rate, clocksource_imx.shift);
|
||||||
clocksource_register(&clocksource_imx);
|
clocksource_register(&clocksource_imx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -174,9 +175,9 @@ static struct clock_event_device clockevent_imx = {
|
||||||
.rating = 200,
|
.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.shift);
|
||||||
clockevent_imx.max_delta_ns =
|
clockevent_imx.max_delta_ns =
|
||||||
clockevent_delta2ns(0xfffffffe, &clockevent_imx);
|
clockevent_delta2ns(0xfffffffe, &clockevent_imx);
|
||||||
|
@ -190,13 +191,23 @@ static int __init imx_clockevent_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int imx_clocks_init(void);
|
||||||
|
|
||||||
static void __init imx_timer_init(void)
|
static void __init imx_timer_init(void)
|
||||||
{
|
{
|
||||||
imx_timer_hardware_init();
|
struct clk *clk;
|
||||||
imx_clocksource_init();
|
unsigned long rate;
|
||||||
|
|
||||||
imx_clockevent_init();
|
imx_clocks_init();
|
||||||
|
|
||||||
|
clk = clk_get(NULL, "perclk1");
|
||||||
|
clk_enable(clk);
|
||||||
|
rate = clk_get_rate(clk);
|
||||||
|
|
||||||
|
imx_timer_hardware_init();
|
||||||
|
imx_clocksource_init(rate);
|
||||||
|
|
||||||
|
imx_clockevent_init(rate);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make irqs happen for the system timer
|
* Make irqs happen for the system timer
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#include <asm/dma.h>
|
#include <asm/dma.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
@ -92,6 +93,8 @@ struct imxmci_host {
|
||||||
unsigned char actual_bus_width;
|
unsigned char actual_bus_width;
|
||||||
|
|
||||||
int prev_cmd_code;
|
int prev_cmd_code;
|
||||||
|
|
||||||
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IMXMCI_PEND_IRQ_b 0
|
#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
|
/* The prescaler is 5 for PERCLK2 equal to 96MHz
|
||||||
* then 96MHz / 5 = 19.2 MHz
|
* then 96MHz / 5 = 19.2 MHz
|
||||||
*/
|
*/
|
||||||
clk=imx_get_perclk2();
|
clk = clk_get_rate(host->clk);
|
||||||
prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
|
prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
|
||||||
switch(prescaler) {
|
switch(prescaler) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -994,6 +997,13 @@ static int imxmci_probe(struct platform_device *pdev)
|
||||||
host->res = r;
|
host->res = r;
|
||||||
host->irq = irq;
|
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(PB8_PF_SD_DAT0);
|
||||||
imx_gpio_mode(PB9_PF_SD_DAT1);
|
imx_gpio_mode(PB9_PF_SD_DAT1);
|
||||||
imx_gpio_mode(PB10_PF_SD_DAT2);
|
imx_gpio_mode(PB10_PF_SD_DAT2);
|
||||||
|
@ -1053,6 +1063,10 @@ out:
|
||||||
imx_dma_free(host->dma);
|
imx_dma_free(host->dma);
|
||||||
host->dma_allocated=0;
|
host->dma_allocated=0;
|
||||||
}
|
}
|
||||||
|
if (host->clk) {
|
||||||
|
clk_disable(host->clk);
|
||||||
|
clk_put(host->clk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mmc)
|
if (mmc)
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
@ -1082,6 +1096,9 @@ static int imxmci_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
tasklet_kill(&host->tasklet);
|
tasklet_kill(&host->tasklet);
|
||||||
|
|
||||||
|
clk_disable(host->clk);
|
||||||
|
clk_put(host->clk);
|
||||||
|
|
||||||
release_resource(host->res);
|
release_resource(host->res);
|
||||||
|
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include <linux/tty_flip.h>
|
#include <linux/tty_flip.h>
|
||||||
#include <linux/serial_core.h>
|
#include <linux/serial_core.h>
|
||||||
#include <linux/serial.h>
|
#include <linux/serial.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
@ -184,6 +185,7 @@ struct imx_port {
|
||||||
unsigned int old_status;
|
unsigned int old_status;
|
||||||
int txirq,rxirq,rtsirq;
|
int txirq,rxirq,rtsirq;
|
||||||
int have_rtscts:1;
|
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
|
* RFDIV is set such way to satisfy requested uartclk value
|
||||||
*/
|
*/
|
||||||
val = TXTL << 10 | RXTL;
|
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)
|
if(!ufcr_rfdiv)
|
||||||
ufcr_rfdiv = 1;
|
ufcr_rfdiv = 1;
|
||||||
|
@ -916,7 +919,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
|
||||||
else
|
else
|
||||||
ucfr_rfdiv = 6 - ucfr_rfdiv;
|
ucfr_rfdiv = 6 - ucfr_rfdiv;
|
||||||
|
|
||||||
uartclk = imx_get_perclk1();
|
uartclk = clk_get_rate(sport->clk);
|
||||||
uartclk /= ucfr_rfdiv;
|
uartclk /= ucfr_rfdiv;
|
||||||
|
|
||||||
{ /*
|
{ /*
|
||||||
|
@ -1054,7 +1057,15 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||||
init_timer(&sport->timer);
|
init_timer(&sport->timer);
|
||||||
sport->timer.function = imx_timeout;
|
sport->timer.function = imx_timeout;
|
||||||
sport->timer.data = (unsigned long)sport;
|
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;
|
imx_ports[pdev->id] = sport;
|
||||||
|
|
||||||
|
@ -1069,6 +1080,8 @@ static int serial_imx_probe(struct platform_device *pdev)
|
||||||
platform_set_drvdata(pdev, &sport->port);
|
platform_set_drvdata(pdev, &sport->port);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
unmap:
|
||||||
|
iounmap(sport->port.membase);
|
||||||
free:
|
free:
|
||||||
kfree(sport);
|
kfree(sport);
|
||||||
|
|
||||||
|
@ -1084,8 +1097,12 @@ static int serial_imx_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
if (sport)
|
if (sport) {
|
||||||
uart_remove_one_port(&imx_reg, &sport->port);
|
uart_remove_one_port(&imx_reg, &sport->port);
|
||||||
|
clk_put(sport->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_disable(sport->clk);
|
||||||
|
|
||||||
if (pdata->exit)
|
if (pdata->exit)
|
||||||
pdata->exit(pdev);
|
pdata->exit(pdev);
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
@ -250,6 +251,8 @@ struct driver_data {
|
||||||
int tx_dma_needs_unmap;
|
int tx_dma_needs_unmap;
|
||||||
size_t tx_map_len;
|
size_t tx_map_len;
|
||||||
u32 dummy_dma_buf ____cacheline_aligned;
|
u32 dummy_dma_buf ____cacheline_aligned;
|
||||||
|
|
||||||
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Runtime state */
|
/* Runtime state */
|
||||||
|
@ -855,15 +858,15 @@ static irqreturn_t spi_int(int irq, void *dev_id)
|
||||||
return drv_data->transfer_handler(drv_data);
|
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 div;
|
||||||
u32 quantized_hz = imx_get_perclk2() >> 2;
|
u32 quantized_hz = clk_get_rate(drv_data->clk) >> 2;
|
||||||
|
|
||||||
for (div = SPI_PERCLK2_DIV_MIN;
|
for (div = SPI_PERCLK2_DIV_MIN;
|
||||||
div <= SPI_PERCLK2_DIV_MAX;
|
div <= SPI_PERCLK2_DIV_MAX;
|
||||||
|
@ -947,7 +950,7 @@ static void pump_transfers(unsigned long data)
|
||||||
tmp = transfer->speed_hz;
|
tmp = transfer->speed_hz;
|
||||||
if (tmp == 0)
|
if (tmp == 0)
|
||||||
tmp = chip->max_speed_hz;
|
tmp = chip->max_speed_hz;
|
||||||
tmp = spi_data_rate(tmp);
|
tmp = spi_data_rate(drv_data, tmp);
|
||||||
u32_EDIT(control, SPI_CONTROL_DATARATE, tmp);
|
u32_EDIT(control, SPI_CONTROL_DATARATE, tmp);
|
||||||
|
|
||||||
writel(control, regs + SPI_CONTROL);
|
writel(control, regs + SPI_CONTROL);
|
||||||
|
@ -1109,7 +1112,7 @@ static int transfer(struct spi_device *spi, struct spi_message *msg)
|
||||||
msg->actual_length = 0;
|
msg->actual_length = 0;
|
||||||
|
|
||||||
/* Per transfer setup check */
|
/* 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;
|
max_speed_hz = spi->max_speed_hz;
|
||||||
list_for_each_entry(trans, &msg->transfers, transfer_list) {
|
list_for_each_entry(trans, &msg->transfers, transfer_list) {
|
||||||
tmp = trans->bits_per_word;
|
tmp = trans->bits_per_word;
|
||||||
|
@ -1176,6 +1179,7 @@ msg_rejected:
|
||||||
applied and notified to the calling driver. */
|
applied and notified to the calling driver. */
|
||||||
static int setup(struct spi_device *spi)
|
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 spi_imx_chip *chip_info;
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
int first_setup = 0;
|
int first_setup = 0;
|
||||||
|
@ -1304,14 +1308,14 @@ static int setup(struct spi_device *spi)
|
||||||
chip->n_bytes = (tmp <= 8) ? 1 : 2;
|
chip->n_bytes = (tmp <= 8) ? 1 : 2;
|
||||||
|
|
||||||
/* SPI datarate */
|
/* 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) {
|
if (tmp == SPI_CONTROL_DATARATE_BAD) {
|
||||||
status = -EINVAL;
|
status = -EINVAL;
|
||||||
dev_err(&spi->dev,
|
dev_err(&spi->dev,
|
||||||
"setup - "
|
"setup - "
|
||||||
"HW min speed (%d Hz) exceeds required "
|
"HW min speed (%d Hz) exceeds required "
|
||||||
"max speed (%d Hz)\n",
|
"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);
|
spi->max_speed_hz);
|
||||||
if (first_setup)
|
if (first_setup)
|
||||||
goto err_first_setup;
|
goto err_first_setup;
|
||||||
|
@ -1321,7 +1325,7 @@ static int setup(struct spi_device *spi)
|
||||||
} else {
|
} else {
|
||||||
u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp);
|
u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp);
|
||||||
/* Actual rounded max_speed_hz */
|
/* Actual rounded max_speed_hz */
|
||||||
tmp = spi_speed_hz(tmp);
|
tmp = spi_speed_hz(drv_data, tmp);
|
||||||
spi->max_speed_hz = tmp;
|
spi->max_speed_hz = tmp;
|
||||||
chip->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,
|
chip->period & SPI_PERIOD_WAIT,
|
||||||
spi->mode,
|
spi->mode,
|
||||||
spi->bits_per_word,
|
spi->bits_per_word,
|
||||||
spi_speed_hz(SPI_CONTROL_DATARATE_MIN),
|
spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN),
|
||||||
spi->max_speed_hz);
|
spi->max_speed_hz);
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
@ -1465,6 +1469,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
|
||||||
goto err_no_pdata;
|
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 */
|
/* Allocate master with space for drv_data */
|
||||||
master = spi_alloc_master(dev, sizeof(struct driver_data));
|
master = spi_alloc_master(dev, sizeof(struct driver_data));
|
||||||
if (!master) {
|
if (!master) {
|
||||||
|
@ -1623,6 +1635,9 @@ err_no_iores:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
|
||||||
err_no_pdata:
|
err_no_pdata:
|
||||||
|
clk_disable(drv_data->clk);
|
||||||
|
clk_put(drv_data->clk);
|
||||||
|
err_no_clk:
|
||||||
err_no_mem:
|
err_no_mem:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -1662,6 +1677,9 @@ static int __exit spi_imx_remove(struct platform_device *pdev)
|
||||||
if (irq >= 0)
|
if (irq >= 0)
|
||||||
free_irq(irq, drv_data);
|
free_irq(irq, drv_data);
|
||||||
|
|
||||||
|
clk_disable(drv_data->clk);
|
||||||
|
clk_put(drv_data->clk);
|
||||||
|
|
||||||
/* Release map resources */
|
/* Release map resources */
|
||||||
iounmap(drv_data->regs);
|
iounmap(drv_data->regs);
|
||||||
release_resource(drv_data->ioarea);
|
release_resource(drv_data->ioarea);
|
||||||
|
|
|
@ -73,14 +73,6 @@
|
||||||
*/
|
*/
|
||||||
extern void imx_gpio_mode( int gpio_mode );
|
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
|
#endif
|
||||||
|
|
||||||
#define MAXIRQNUM 62
|
#define MAXIRQNUM 62
|
||||||
|
|
Loading…
Reference in New Issue