diff options
author | Olof Johansson <olof@lixom.net> | 2012-05-12 22:33:24 -0700 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2012-05-12 22:33:24 -0700 |
commit | e29402edf848359d619ce06af86d61e62c292c87 (patch) | |
tree | 94451c1d400d478654e0d0e78564e882081b806c /drivers | |
parent | bf98a6eaa9964fef49f186834713bfc57d16ede1 (diff) | |
parent | 530f1d416091212243b341e0022b2967886b30e4 (diff) | |
download | linux-e29402edf848359d619ce06af86d61e62c292c87.tar.gz linux-e29402edf848359d619ce06af86d61e62c292c87.tar.bz2 linux-e29402edf848359d619ce06af86d61e62c292c87.zip |
Merge branch 'mxs/dt/for-3.5' of git://git.linaro.org/people/shawnguo/linux-2.6 into next/dt2
* 'mxs/dt/for-3.5' of git://git.linaro.org/people/shawnguo/linux-2.6: (51 commits)
ARM: dts: enable audio support for imx28-evk
ARM: dts: enable i2c device for imx28-evk
i2c: mxs: add device tree probe support
ARM: dts: enable mmc for imx28-evk
ARM: dts: enable mmc for imx23-evk
mmc: mxs-mmc: add device tree support
mmc: mxs-mmc: copy wp_gpio in struct mxs_mmc_host
mmc: mxs-mmc: have dma_channel than dma_res in mxs_mmc_host
mmc: mxs-mmc: use devm_* helper to make cleanup simpler
mmc: mxs-mmc: move header from mach into linux folder
mmc: mxs-mmc: get rid of the use of cpu_is_xxx
mmc: mxs-mmc: let ssp_is_old take host as parameter
mmc: mxs-mmc: use global stmp_device functionality
ARM: mxs: add gpio support for device tree boot
gpio/mxs: add device tree probe
gpio/mxs: get rid of the use of cpu_is_xxx
gpio/mxs: use devm_* helpers to make error handling simple
ARM: mxs: add mxs-dma dt support
ARM: mxs: do not add dma device by default
dma: mxs-dma: add device tree probe support
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/mxs/Makefile | 8 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-div.c | 110 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-frac.c | 139 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx23.c | 205 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx28.c | 338 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-pll.c | 116 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-ref.c | 154 | ||||
-rw-r--r-- | drivers/clk/mxs/clk.c | 28 | ||||
-rw-r--r-- | drivers/clk/mxs/clk.h | 66 | ||||
-rw-r--r-- | drivers/dma/Kconfig | 1 | ||||
-rw-r--r-- | drivers/dma/mxs-dma.c | 188 | ||||
-rw-r--r-- | drivers/gpio/gpio-mxs.c | 156 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 8 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mxs.c | 19 | ||||
-rw-r--r-- | drivers/mmc/host/mxs-mmc.c | 203 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 9 | ||||
-rw-r--r-- | drivers/net/can/flexcan.c | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec.c | 9 | ||||
-rw-r--r-- | drivers/spi/spi-imx.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/imx.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/mxs-auart.c | 8 | ||||
-rw-r--r-- | drivers/video/mxsfb.c | 9 |
25 files changed, 1607 insertions, 208 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 1f736bc11c4b..a576f5447d38 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ clk-mux.o clk-divider.o + +obj-$(CONFIG_ARCH_MXS) += mxs/ diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile new file mode 100644 index 000000000000..7bedeec08524 --- /dev/null +++ b/drivers/clk/mxs/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for mxs specific clk +# + +obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o + +obj-$(CONFIG_SOC_IMX23) += clk-imx23.o +obj-$(CONFIG_SOC_IMX28) += clk-imx28.o diff --git a/drivers/clk/mxs/clk-div.c b/drivers/clk/mxs/clk-div.c new file mode 100644 index 000000000000..90e1da93877e --- /dev/null +++ b/drivers/clk/mxs/clk-div.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_div - mxs integer divider clock + * @divider: the parent class + * @ops: pointer to clk_ops of parent class + * @reg: register address + * @busy: busy bit shift + * + * The mxs divider clock is a subclass of basic clk_divider with an + * addtional busy bit. + */ +struct clk_div { + struct clk_divider divider; + const struct clk_ops *ops; + void __iomem *reg; + u8 busy; +}; + +static inline struct clk_div *to_clk_div(struct clk_hw *hw) +{ + struct clk_divider *divider = container_of(hw, struct clk_divider, hw); + + return container_of(divider, struct clk_div, divider); +} + +static unsigned long clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_div *div = to_clk_div(hw); + + return div->ops->recalc_rate(&div->divider.hw, parent_rate); +} + +static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_div *div = to_clk_div(hw); + + return div->ops->round_rate(&div->divider.hw, rate, prate); +} + +static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_div *div = to_clk_div(hw); + int ret; + + ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate); + if (!ret) + ret = mxs_clk_wait(div->reg, div->busy); + + return ret; +} + +static struct clk_ops clk_div_ops = { + .recalc_rate = clk_div_recalc_rate, + .round_rate = clk_div_round_rate, + .set_rate = clk_div_set_rate, +}; + +struct clk *mxs_clk_div(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy) +{ + struct clk_div *div; + struct clk *clk; + struct clk_init_data init; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_div_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + div->reg = reg; + div->busy = busy; + + div->divider.reg = reg; + div->divider.shift = shift; + div->divider.width = width; + div->divider.flags = CLK_DIVIDER_ONE_BASED; + div->divider.lock = &mxs_lock; + div->divider.hw.init = &init; + div->ops = &clk_divider_ops; + + clk = clk_register(NULL, &div->divider.hw); + if (IS_ERR(clk)) + kfree(div); + + return clk; +} diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c new file mode 100644 index 000000000000..e6aa6b567d68 --- /dev/null +++ b/drivers/clk/mxs/clk-frac.c @@ -0,0 +1,139 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_frac - mxs fractional divider clock + * @hw: clk_hw for the fractional divider clock + * @reg: register address + * @shift: the divider bit shift + * @width: the divider bit width + * @busy: busy bit shift + * + * The clock is an adjustable fractional divider with a busy bit to wait + * when the divider is adjusted. + */ +struct clk_frac { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u8 busy; +}; + +#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) + +static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + u32 div; + + div = readl_relaxed(frac->reg) >> frac->shift; + div &= (1 << frac->width) - 1; + + return (parent_rate >> frac->width) * div; +} + +static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long parent_rate = *prate; + u32 div; + u64 tmp; + + if (rate > parent_rate) + return -EINVAL; + + tmp = rate; + tmp <<= frac->width; + do_div(tmp, parent_rate); + div = tmp; + + if (!div) + return -EINVAL; + + return (parent_rate >> frac->width) * div; +} + +static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long flags; + u32 div, val; + u64 tmp; + + if (rate > parent_rate) + return -EINVAL; + + tmp = rate; + tmp <<= frac->width; + do_div(tmp, parent_rate); + div = tmp; + + if (!div) + return -EINVAL; + + spin_lock_irqsave(&mxs_lock, flags); + + val = readl_relaxed(frac->reg); + val &= ~(((1 << frac->width) - 1) << frac->shift); + val |= div << frac->shift; + writel_relaxed(val, frac->reg); + + spin_unlock_irqrestore(&mxs_lock, flags); + + return mxs_clk_wait(frac->reg, frac->busy); +} + +static struct clk_ops clk_frac_ops = { + .recalc_rate = clk_frac_recalc_rate, + .round_rate = clk_frac_round_rate, + .set_rate = clk_frac_set_rate, +}; + +struct clk *mxs_clk_frac(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy) +{ + struct clk_frac *frac; + struct clk *clk; + struct clk_init_data init; + + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_frac_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + frac->reg = reg; + frac->shift = shift; + frac->width = width; + frac->busy = busy; + frac->hw.init = &init; + + clk = clk_register(NULL, &frac->hw); + if (IS_ERR(clk)) + kfree(frac); + + return clk; +} diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c new file mode 100644 index 000000000000..f7be225f544c --- /dev/null +++ b/drivers/clk/mxs/clk-imx23.c @@ -0,0 +1,205 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <mach/common.h> +#include <mach/mx23.h> +#include "clk.h" + +#define DIGCTRL MX23_IO_ADDRESS(MX23_DIGCTL_BASE_ADDR) +#define CLKCTRL MX23_IO_ADDRESS(MX23_CLKCTRL_BASE_ADDR) +#define PLLCTRL0 (CLKCTRL + 0x0000) +#define CPU (CLKCTRL + 0x0020) +#define HBUS (CLKCTRL + 0x0030) +#define XBUS (CLKCTRL + 0x0040) +#define XTAL (CLKCTRL + 0x0050) +#define PIX (CLKCTRL + 0x0060) +#define SSP (CLKCTRL + 0x0070) +#define GPMI (CLKCTRL + 0x0080) +#define SPDIF (CLKCTRL + 0x0090) +#define EMI (CLKCTRL + 0x00a0) +#define SAIF (CLKCTRL + 0x00c0) +#define TV (CLKCTRL + 0x00d0) +#define ETM (CLKCTRL + 0x00e0) +#define FRAC (CLKCTRL + 0x00f0) +#define CLKSEQ (CLKCTRL + 0x0110) + +#define BP_CPU_INTERRUPT_WAIT 12 +#define BP_CLKSEQ_BYPASS_SAIF 0 +#define BP_CLKSEQ_BYPASS_SSP 5 +#define BP_SAIF_DIV_FRAC_EN 16 +#define BP_FRAC_IOFRAC 24 + +static void __init clk_misc_init(void) +{ + u32 val; + + /* Gate off cpu clock in WFI for power saving */ + __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + + /* Clear BYPASS for SAIF */ + __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ); + + /* SAIF has to use frac div for functional operation */ + val = readl_relaxed(SAIF); + val |= 1 << BP_SAIF_DIV_FRAC_EN; + writel_relaxed(val, SAIF); + + /* + * Source ssp clock from ref_io than ref_xtal, + * as ref_xtal only provides 24 MHz as maximum. + */ + __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ); + + /* + * 480 MHz seems too high to be ssp clock source directly, + * so set frac to get a 288 MHz ref_io. + */ + __mxs_clrl(0x3f << BP_FRAC_IOFRAC, FRAC); + __mxs_setl(30 << BP_FRAC_IOFRAC, FRAC); +} + +static struct clk_lookup uart_lookups[] __initdata = { + { .dev_id = "duart", }, + { .dev_id = "mxs-auart.0", }, + { .dev_id = "mxs-auart.1", }, + { .dev_id = "8006c000.serial", }, + { .dev_id = "8006e000.serial", }, + { .dev_id = "80070000.serial", }, +}; + +static struct clk_lookup hbus_lookups[] __initdata = { + { .dev_id = "imx23-dma-apbh", }, + { .dev_id = "80004000.dma-apbh", }, +}; + +static struct clk_lookup xbus_lookups[] __initdata = { + { .dev_id = "duart", .con_id = "apb_pclk"}, + { .dev_id = "80070000.serial", .con_id = "apb_pclk"}, + { .dev_id = "imx23-dma-apbx", }, + { .dev_id = "80024000.dma-apbx", }, +}; + +static struct clk_lookup ssp_lookups[] __initdata = { + { .dev_id = "imx23-mmc.0", }, + { .dev_id = "imx23-mmc.1", }, + { .dev_id = "80010000.ssp", }, + { .dev_id = "80034000.ssp", }, +}; + +static struct clk_lookup lcdif_lookups[] __initdata = { + { .dev_id = "imx23-fb", }, + { .dev_id = "80030000.lcdif", }, +}; + +static struct clk_lookup gpmi_lookups[] __initdata = { + { .dev_id = "imx23-gpmi-nand", }, + { .dev_id = "8000c000.gpmi", }, +}; + +static const char *sel_pll[] __initconst = { "pll", "ref_xtal", }; +static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *sel_io[] __initconst = { "ref_io", "ref_xtal", }; +static const char *cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; + +enum imx23_clk { + ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel, + lcdif_sel, gpmi_sel, ssp_sel, emi_sel, cpu, etm_sel, cpu_pll, + cpu_xtal, hbus, xbus, lcdif_div, ssp_div, gpmi_div, emi_pll, + emi_xtal, etm_div, saif_div, clk32k_div, rtc, adc, spdif_div, + clk32k, dri, pwm, filt, uart, ssp, gpmi, spdif, emi, saif, + lcdif, etm, usb, usb_pwr, + clk_max +}; + +static struct clk *clks[clk_max]; + +static enum imx23_clk clks_init_on[] __initdata = { + cpu, hbus, xbus, emi, uart, +}; + +int __init mx23_clocks_init(void) +{ + int i; + + clk_misc_init(); + + clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); + clks[pll] = mxs_clk_pll("pll", "ref_xtal", PLLCTRL0, 16, 480000000); + clks[ref_cpu] = mxs_clk_ref("ref_cpu", "pll", FRAC, 0); + clks[ref_emi] = mxs_clk_ref("ref_emi", "pll", FRAC, 1); + clks[ref_pix] = mxs_clk_ref("ref_pix", "pll", FRAC, 2); + clks[ref_io] = mxs_clk_ref("ref_io", "pll", FRAC, 3); + clks[saif_sel] = mxs_clk_mux("saif_sel", CLKSEQ, 0, 1, sel_pll, ARRAY_SIZE(sel_pll)); + clks[lcdif_sel] = mxs_clk_mux("lcdif_sel", CLKSEQ, 1, 1, sel_pix, ARRAY_SIZE(sel_pix)); + clks[gpmi_sel] = mxs_clk_mux("gpmi_sel", CLKSEQ, 4, 1, sel_io, ARRAY_SIZE(sel_io)); + clks[ssp_sel] = mxs_clk_mux("ssp_sel", CLKSEQ, 5, 1, sel_io, ARRAY_SIZE(sel_io)); + clks[emi_sel] = mxs_clk_mux("emi_sel", CLKSEQ, 6, 1, emi_sels, ARRAY_SIZE(emi_sels)); + clks[cpu] = mxs_clk_mux("cpu", CLKSEQ, 7, 1, cpu_sels, ARRAY_SIZE(cpu_sels)); + clks[etm_sel] = mxs_clk_mux("etm_sel", CLKSEQ, 8, 1, sel_cpu, ARRAY_SIZE(sel_cpu)); + clks[cpu_pll] = mxs_clk_div("cpu_pll", "ref_cpu", CPU, 0, 6, 28); + clks[cpu_xtal] = mxs_clk_div("cpu_xtal", "ref_xtal", CPU, 16, 10, 29); + clks[hbus] = mxs_clk_div("hbus", "cpu", HBUS, 0, 5, 29); + clks[xbus] = mxs_clk_div("xbus", "ref_xtal", XBUS, 0, 10, 31); + clks[lcdif_div] = mxs_clk_div("lcdif_div", "lcdif_sel", PIX, 0, 12, 29); + clks[ssp_div] = mxs_clk_div("ssp_div", "ssp_sel", SSP, 0, 9, 29); + clks[gpmi_div] = mxs_clk_div("gpmi_div", "gpmi_sel", GPMI, 0, 10, 29); + clks[emi_pll] = mxs_clk_div("emi_pll", "ref_emi", EMI, 0, 6, 28); + clks[emi_xtal] = mxs_clk_div("emi_xtal", "ref_xtal", EMI, 8, 4, 29); + clks[etm_div] = mxs_clk_div("etm_div", "etm_sel", ETM, 0, 6, 29); + clks[saif_div] = mxs_clk_frac("saif_div", "saif_sel", SAIF, 0, 16, 29); + clks[clk32k_div] = mxs_clk_fixed_factor("clk32k_div", "ref_xtal", 1, 750); + clks[rtc] = mxs_clk_fixed_factor("rtc", "ref_xtal", 1, 768); + clks[adc] = mxs_clk_fixed_factor("adc", "clk32k", 1, 16); + clks[spdif_div] = mxs_clk_fixed_factor("spdif_div", "pll", 1, 4); + clks[clk32k] = mxs_clk_gate("clk32k", "clk32k_div", XTAL, 26); + clks[dri] = mxs_clk_gate("dri", "ref_xtal", XTAL, 28); + clks[pwm] = mxs_clk_gate("pwm", "ref_xtal", XTAL, 29); + clks[filt] = mxs_clk_gate("filt", "ref_xtal", XTAL, 30); + clks[uart] = mxs_clk_gate("uart", "ref_xtal", XTAL, 31); + clks[ssp] = mxs_clk_gate("ssp", "ssp_div", SSP, 31); + clks[gpmi] = mxs_clk_gate("gpmi", "gpmi_div", GPMI, 31); + clks[spdif] = mxs_clk_gate("spdif", "spdif_div", SPDIF, 31); + clks[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 31); + clks[saif] = mxs_clk_gate("saif", "saif_div", SAIF, 31); + clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", PIX, 31); + clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31); + clks[usb] = mxs_clk_gate("usb", "usb_pwr", DIGCTRL, 2); + clks[usb_pwr] = clk_register_gate(NULL, "usb_pwr", "pll", 0, PLLCTRL0, 18, 0, &mxs_lock); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) { + pr_err("i.MX23 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + return PTR_ERR(clks[i]); + } + + clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups)); + clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups)); + clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups)); + clk_register_clkdevs(clks[ssp], ssp_lookups, ARRAY_SIZE(ssp_lookups)); + clk_register_clkdevs(clks[gpmi], gpmi_lookups, ARRAY_SIZE(gpmi_lookups)); + clk_register_clkdevs(clks[lcdif], lcdif_lookups, ARRAY_SIZE(lcdif_lookups)); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clks[clks_init_on[i]]); + + mxs_timer_init(MX23_INT_TIMER0); + + return 0; +} diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c new file mode 100644 index 000000000000..2826a2606a29 --- /dev/null +++ b/drivers/clk/mxs/clk-imx28.c @@ -0,0 +1,338 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <mach/common.h> +#include <mach/mx28.h> +#include "clk.h" + +#define CLKCTRL MX28_IO_ADDRESS(MX28_CLKCTRL_BASE_ADDR) +#define PLL0CTRL0 (CLKCTRL + 0x0000) +#define PLL1CTRL0 (CLKCTRL + 0x0020) +#define PLL2CTRL0 (CLKCTRL + 0x0040) +#define CPU (CLKCTRL + 0x0050) +#define HBUS (CLKCTRL + 0x0060) +#define XBUS (CLKCTRL + 0x0070) +#define XTAL (CLKCTRL + 0x0080) +#define SSP0 (CLKCTRL + 0x0090) +#define SSP1 (CLKCTRL + 0x00a0) +#define SSP2 (CLKCTRL + 0x00b0) +#define SSP3 (CLKCTRL + 0x00c0) +#define GPMI (CLKCTRL + 0x00d0) +#define SPDIF (CLKCTRL + 0x00e0) +#define EMI (CLKCTRL + 0x00f0) +#define SAIF0 (CLKCTRL + 0x0100) +#define SAIF1 (CLKCTRL + 0x0110) +#define LCDIF (CLKCTRL + 0x0120) +#define ETM (CLKCTRL + 0x0130) +#define ENET (CLKCTRL + 0x0140) +#define FLEXCAN (CLKCTRL + 0x0160) +#define FRAC0 (CLKCTRL + 0x01b0) +#define FRAC1 (CLKCTRL + 0x01c0) +#define CLKSEQ (CLKCTRL + 0x01d0) + +#define BP_CPU_INTERRUPT_WAIT 12 +#define BP_SAIF_DIV_FRAC_EN 16 +#define BP_ENET_DIV_TIME 21 +#define BP_ENET_SLEEP 31 +#define BP_CLKSEQ_BYPASS_SAIF0 0 +#define BP_CLKSEQ_BYPASS_SSP0 3 +#define BP_FRAC0_IO1FRAC 16 +#define BP_FRAC0_IO0FRAC 24 + +#define DIGCTRL MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR) +#define BP_SAIF_CLKMUX 10 + +/* + * HW_SAIF_CLKMUX_SEL: + * DIRECT(0x0): SAIF0 clock pins selected for SAIF0 input clocks, and SAIF1 + * clock pins selected for SAIF1 input clocks. + * CROSSINPUT(0x1): SAIF1 clock inputs selected for SAIF0 input clocks, and + * SAIF0 clock inputs selected for SAIF1 input clocks. + * EXTMSTR0(0x2): SAIF0 clock pin selected for both SAIF0 and SAIF1 input + * clocks. + * EXTMSTR1(0x3): SAIF1 clock pin selected for both SAIF0 and SAIF1 input + * clocks. + */ +int mxs_saif_clkmux_select(unsigned int clkmux) +{ + if (clkmux > 0x3) + return -EINVAL; + + __mxs_clrl(0x3 << BP_SAIF_CLKMUX, DIGCTRL); + __mxs_setl(clkmux << BP_SAIF_CLKMUX, DIGCTRL); + + return 0; +} + +static void __init clk_misc_init(void) +{ + u32 val; + + /* Gate off cpu clock in WFI for power saving */ + __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + + /* 0 is a bad default value for a divider */ + __mxs_setl(1 << BP_ENET_DIV_TIME, ENET); + + /* Clear BYPASS for SAIF */ + __mxs_clrl(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ); + + /* SAIF has to use frac div for functional operation */ + val = readl_relaxed(SAIF0); + val |= 1 << BP_SAIF_DIV_FRAC_EN; + writel_relaxed(val, SAIF0); + + val = readl_relaxed(SAIF1); + val |= 1 << BP_SAIF_DIV_FRAC_EN; + writel_relaxed(val, SAIF1); + + /* Extra fec clock setting */ + val = readl_relaxed(ENET); + val &= ~(1 << BP_ENET_SLEEP); + writel_relaxed(val, ENET); + + /* + * Source ssp clock from ref_io than ref_xtal, + * as ref_xtal only provides 24 MHz as maximum. + */ + __mxs_clrl(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ); + + /* + * 480 MHz seems too high to be ssp clock source directly, + * so set frac0 to get a 288 MHz ref_io0. + */ + val = readl_relaxed(FRAC0); + val &= ~(0x3f << BP_FRAC0_IO0FRAC); + val |= 30 << BP_FRAC0_IO0FRAC; + writel_relaxed(val, FRAC0); +} + +static struct clk_lookup uart_lookups[] __initdata = { + { .dev_id = "duart", }, + { .dev_id = "mxs-auart.0", }, + { .dev_id = "mxs-auart.1", }, + { .dev_id = "mxs-auart.2", }, + { .dev_id = "mxs-auart.3", }, + { .dev_id = "mxs-auart.4", }, + { .dev_id = "8006a000.serial", }, + { .dev_id = "8006c000.serial", }, + { .dev_id = "8006e000.serial", }, + { .dev_id = "80070000.serial", }, + { .dev_id = "80072000.serial", }, + { .dev_id = "80074000.serial", }, +}; + +static struct clk_lookup hbus_lookups[] __initdata = { + { .dev_id = "imx28-dma-apbh", }, + { .dev_id = "80004000.dma-apbh", }, +}; + +static struct clk_lookup xbus_lookups[] __initdata = { + { .dev_id = "duart", .con_id = "apb_pclk"}, + { .dev_id = "80074000.serial", .con_id = "apb_pclk"}, + { .dev_id = "imx28-dma-apbx", }, + { .dev_id = "80024000.dma-apbx", }, +}; + +static struct clk_lookup ssp0_lookups[] __initdata = { + { .dev_id = "imx28-mmc.0", }, + { .dev_id = "80010000.ssp", }, +}; + +static struct clk_lookup ssp1_lookups[] __initdata = { + { .dev_id = "imx28-mmc.1", }, + { .dev_id = "80012000.ssp", }, +}; + +static struct clk_lookup ssp2_lookups[] __initdata = { + { .dev_id = "imx28-mmc.2", }, + { .dev_id = "80014000.ssp", }, +}; + +static struct clk_lookup ssp3_lookups[] __initdata = { + { .dev_id = "imx28-mmc.3", }, + { .dev_id = "80016000.ssp", }, +}; + +static struct clk_lookup lcdif_lookups[] __initdata = { + { .dev_id = "imx28-fb", }, + { .dev_id = "80030000.lcdif", }, +}; + +static struct clk_lookup gpmi_lookups[] __initdata = { + { .dev_id = "imx28-gpmi-nand", }, + { .dev_id = "8000c000.gpmi", }, +}; + +static struct clk_lookup fec_lookups[] __initdata = { + { .dev_id = "imx28-fec.0", }, + { .dev_id = "imx28-fec.1", }, + { .dev_id = "800f0000.ethernet", }, + { .dev_id = "800f4000.ethernet", }, +}; + +static struct clk_lookup can0_lookups[] __initdata = { + { .dev_id = "flexcan.0", }, + { .dev_id = "80032000.can", }, +}; + +static struct clk_lookup can1_lookups[] __initdata = { + { .dev_id = "flexcan.1", }, + { .dev_id = "80034000.can", }, +}; + +static struct clk_lookup saif0_lookups[] __initdata = { + { .dev_id = "mxs-saif.0", }, + { .dev_id = "80042000.saif", }, +}; + +static struct clk_lookup saif1_lookups[] __initdata = { + { .dev_id = "mxs-saif.1", }, + { .dev_id = "80046000.saif", }, +}; + +static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *sel_io0[] __initconst = { "ref_io0", "ref_xtal", }; +static const char *sel_io1[] __initconst = { "ref_io1", "ref_xtal", }; +static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", }; +static const char *sel_pll0[] __initconst = { "pll0", "ref_xtal", }; +static const char *cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; +static const char *ptp_sels[] __initconst = { "ref_xtal", "pll0", }; + +enum imx28_clk { + ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1, + ref_pix, ref_hsadc, ref_gpmi, saif0_sel, saif1_sel, gpmi_sel, + ssp0_sel, ssp1_sel, ssp2_sel, ssp3_sel, emi_sel, etm_sel, + lcdif_sel, cpu, ptp_sel, cpu_pll, cpu_xtal, hbus, xbus, + ssp0_div, ssp1_div, ssp2_div, ssp3_div, gpmi_div, emi_pll, + emi_xtal, lcdif_div, etm_div, ptp, saif0_div, saif1_div, + clk32k_div, rtc, lradc, spdif_div, clk32k, pwm, uart, ssp0, + ssp1, ssp2, ssp3, gpmi, spdif, emi, saif0, saif1, lcdif, etm, + fec, can0, can1, usb0, usb1, usb0_pwr, usb1_pwr, enet_out, + clk_max +}; + +static struct clk *clks[clk_max]; + +static enum imx28_clk clks_init_on[] __initdata = { + cpu, hbus, xbus, emi, uart, +}; + +int __init mx28_clocks_init(void) +{ + int i; + + clk_misc_init(); + + clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); + clks[pll0] = mxs_clk_pll("pll0", "ref_xtal", PLL0CTRL0, 17, 480000000); + clks[pll1] = mxs_clk_pll("pll1", "ref_xtal", PLL1CTRL0, 17, 480000000); + clks[pll2] = mxs_clk_pll("pll2", "ref_xtal", PLL2CTRL0, 23, 50000000); + clks[ref_cpu] = mxs_clk_ref("ref_cpu", "pll0", FRAC0, 0); + clks[ref_emi] = mxs_clk_ref("ref_emi", "pll0", FRAC0, 1); + clks[ref_io0] = mxs_clk_ref("ref_io0", "pll0", FRAC0, 2); + clks[ref_io1] = mxs_clk_ref("ref_io1", "pll0", FRAC0, 3); + clks[ref_pix] = mxs_clk_ref("ref_pix", "pll0", FRAC1, 0); + clks[ref_hsadc] = mxs_clk_ref("ref_hsadc", "pll0", FRAC1, 1); + clks[ref_gpmi] = mxs_clk_ref("ref_gpmi", "pll0", FRAC1, 2); + clks[saif0_sel] = mxs_clk_mux("saif0_sel", CLKSEQ, 0, 1, sel_pll0, ARRAY_SIZE(sel_pll0)); + clks[saif1_sel] = mxs_clk_mux("saif1_sel", CLKSEQ, 1, 1, sel_pll0, ARRAY_SIZE(sel_pll0)); + clks[gpmi_sel] = mxs_clk_mux("gpmi_sel", CLKSEQ, 2, 1, sel_gpmi, ARRAY_SIZE(sel_gpmi)); + clks[ssp0_sel] = mxs_clk_mux("ssp0_sel", CLKSEQ, 3, 1, sel_io0, ARRAY_SIZE(sel_io0)); + clks[ssp1_sel] = mxs_clk_mux("ssp1_sel", CLKSEQ, 4, 1, sel_io0, ARRAY_SIZE(sel_io0)); + clks[ssp2_sel] = mxs_clk_mux("ssp2_sel", CLKSEQ, 5, 1, sel_io1, ARRAY_SIZE(sel_io1)); + clks[ssp3_sel] = mxs_clk_mux("ssp3_sel", CLKSEQ, 6, 1, sel_io1, ARRAY_SIZE(sel_io1)); + clks[emi_sel] = mxs_clk_mux("emi_sel", CLKSEQ, 7, 1, emi_sels, ARRAY_SIZE(emi_sels)); + clks[etm_sel] = mxs_clk_mux("etm_sel", CLKSEQ, 8, 1, sel_cpu, ARRAY_SIZE(sel_cpu)); + clks[lcdif_sel] = mxs_clk_mux("lcdif_sel", CLKSEQ, 14, 1, sel_pix, ARRAY_SIZE(sel_pix)); + clks[cpu] = mxs_clk_mux("cpu", CLKSEQ, 18, 1, cpu_sels, ARRAY_SIZE(cpu_sels)); + clks[ptp_sel] = mxs_clk_mux("ptp_sel", ENET, 19, 1, ptp_sels, ARRAY_SIZE(ptp_sels)); + clks[cpu_pll] = mxs_clk_div("cpu_pll", "ref_cpu", CPU, 0, 6, 28); + clks[cpu_xtal] = mxs_clk_div("cpu_xtal", "ref_xtal", CPU, 16, 10, 29); + clks[hbus] = mxs_clk_div("hbus", "cpu", HBUS, 0, 5, 31); + clks[xbus] = mxs_clk_div("xbus", "ref_xtal", XBUS, 0, 10, 31); + clks[ssp0_div] = mxs_clk_div("ssp0_div", "ssp0_sel", SSP0, 0, 9, 29); + clks[ssp1_div] = mxs_clk_div("ssp1_div", "ssp1_sel", SSP1, 0, 9, 29); + clks[ssp2_div] = mxs_clk_div("ssp2_div", "ssp2_sel", SSP2, 0, 9, 29); + clks[ssp3_div] = mxs_clk_div("ssp3_div", "ssp3_sel", SSP3, 0, 9, 29); + clks[gpmi_div] = mxs_clk_div("gpmi_div", "gpmi_sel", GPMI, 0, 10, 29); + clks[emi_pll] = mxs_clk_div("emi_pll", "ref_emi", EMI, 0, 6, 28); + clks[emi_xtal] = mxs_clk_div("emi_xtal", "ref_xtal", EMI, 8, 4, 29); + clks[lcdif_div] = mxs_clk_div("lcdif_div", "lcdif_sel", LCDIF, 0, 13, 29); + clks[etm_div] = mxs_clk_div("etm_div", "etm_sel", ETM, 0, 7, 29); + clks[ptp] = mxs_clk_div("ptp", "ptp_sel", ENET, 21, 6, 27); + clks[saif0_div] = mxs_clk_frac("saif0_div", "saif0_sel", SAIF0, 0, 16, 29); + clks[saif1_div] = mxs_clk_frac("saif1_div", "saif1_sel", SAIF1, 0, 16, 29); + clks[clk32k_div] = mxs_clk_fixed_factor("clk32k_div", "ref_xtal", 1, 750); + clks[rtc] = mxs_clk_fixed_factor("rtc", "ref_xtal", 1, 768); + clks[lradc] = mxs_clk_fixed_factor("lradc", "clk32k", 1, 16); + clks[spdif_div] = mxs_clk_fixed_factor("spdif_div", "pll0", 1, 4); + clks[clk32k] = mxs_clk_gate("clk32k", "clk32k_div", XTAL, 26); + clks[pwm] = mxs_clk_gate("pwm", "ref_xtal", XTAL, 29); + clks[uart] = mxs_clk_gate("uart", "ref_xtal", XTAL, 31); + clks[ssp0] = mxs_clk_gate("ssp0", "ssp0_div", SSP0, 31); + clks[ssp1] = mxs_clk_gate("ssp1", "ssp1_div", SSP1, 31); + clks[ssp2] = mxs_clk_gate("ssp2", "ssp2_div", SSP2, 31); + clks[ssp3] = mxs_clk_gate("ssp3", "ssp3_div", SSP3, 31); + clks[gpmi] = mxs_clk_gate("gpmi", "gpmi_div", GPMI, 31); + clks[spdif] = mxs_clk_gate("spdif", "spdif_div", SPDIF, 31); + clks[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 31); + clks[saif0] = mxs_clk_gate("saif0", "saif0_div", SAIF0, 31); + clks[saif1] = mxs_clk_gate("saif1", "saif1_div", SAIF1, 31); + clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", LCDIF, 31); + clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31); + clks[fec] = mxs_clk_gate("fec", "hbus", ENET, 30); + clks[can0] = mxs_clk_gate("can0", "ref_xtal", FLEXCAN, 30); + clks[can1] = mxs_clk_gate("can1", "ref_xtal", FLEXCAN, 28); + clks[usb0] = mxs_clk_gate("usb0", "usb0_pwr", DIGCTRL, 2); + clks[usb1] = mxs_clk_gate("usb1", "usb1_pwr", DIGCTRL, 16); + clks[usb0_pwr] = clk_register_gate(NULL, "usb0_pwr", "pll0", 0, PLL0CTRL0, 18, 0, &mxs_lock); + clks[usb1_pwr] = clk_register_gate(NULL, "usb1_pwr", "pll1", 0, PLL1CTRL0, 18, 0, &mxs_lock); + clks[enet_out] = clk_register_gate(NULL, "enet_out", "pll2", 0, ENET, 18, 0, &mxs_lock); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) { + pr_err("i.MX28 clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + return PTR_ERR(clks[i]); + } + + clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_register_clkdev(clks[enet_out], NULL, "enet_out"); + clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups)); + clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups)); + clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups)); + clk_register_clkdevs(clks[ssp0], ssp0_lookups, ARRAY_SIZE(ssp0_lookups)); + clk_register_clkdevs(clks[ssp1], ssp1_lookups, ARRAY_SIZE(ssp1_lookups)); + clk_register_clkdevs(clks[ssp2], ssp2_lookups, ARRAY_SIZE(ssp2_lookups)); + clk_register_clkdevs(clks[ssp3], ssp3_lookups, ARRAY_SIZE(ssp3_lookups)); + clk_register_clkdevs(clks[gpmi], gpmi_lookups, ARRAY_SIZE(gpmi_lookups)); + clk_register_clkdevs(clks[saif0], saif0_lookups, ARRAY_SIZE(saif0_lookups)); + clk_register_clkdevs(clks[saif1], saif1_lookups, ARRAY_SIZE(saif1_lookups)); + clk_register_clkdevs(clks[lcdif], lcdif_lookups, ARRAY_SIZE(lcdif_lookups)); + clk_register_clkdevs(clks[fec], fec_lookups, ARRAY_SIZE(fec_lookups)); + clk_register_clkdevs(clks[can0], can0_lookups, ARRAY_SIZE(can0_lookups)); + clk_register_clkdevs(clks[can1], can1_lookups, ARRAY_SIZE(can1_lookups)); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clks[clks_init_on[i]]); + + mxs_timer_init(MX28_INT_TIMER0); + + return 0; +} diff --git a/drivers/clk/mxs/clk-pll.c b/drivers/clk/mxs/clk-pll.c new file mode 100644 index 000000000000..fadae41833ec --- /dev/null +++ b/drivers/clk/mxs/clk-pll.c @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_pll - mxs pll clock + * @hw: clk_hw for the pll + * @base: base address of the pll + * @power: the shift of power bit + * @rate: the clock rate of the pll + * + * The mxs pll is a fixed rate clock with power and gate control, + * and the shift of gate bit is always 31. + */ +struct clk_pll { + struct clk_hw hw; + void __iomem *base; + u8 power; + unsigned long rate; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +static int clk_pll_prepare(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << pll->power, pll->base + SET); + + udelay(10); + + return 0; +} + +static void clk_pll_unprepare(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << pll->power, pll->base + CLR); +} + +static int clk_pll_enable(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << 31, pll->base + CLR); + + return 0; +} + +static void clk_pll_disable(struct clk_hw *hw) +{ + struct clk_pll *pll = to_clk_pll(hw); + + writel_relaxed(1 << 31, pll->base + SET); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + + return pll->rate; +} + +static const struct clk_ops clk_pll_ops = { + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .recalc_rate = clk_pll_recalc_rate, +}; + +struct clk *mxs_clk_pll(const char *name, const char *parent_name, + void __iomem *base, u8 power, unsigned long rate) +{ + struct clk_pll *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_pll_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + pll->base = base; + pll->rate = rate; + pll->power = power; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} diff --git a/drivers/clk/mxs/clk-ref.c b/drivers/clk/mxs/clk-ref.c new file mode 100644 index 000000000000..4adeed6c2f94 --- /dev/null +++ b/drivers/clk/mxs/clk-ref.c @@ -0,0 +1,154 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_ref - mxs reference clock + * @hw: clk_hw for the reference clock + * @reg: register address + * @idx: the index of the reference clock within the same register + * + * The mxs reference clock sources from pll. Every 4 reference clocks share + * one register space, and @idx is used to identify them. Each reference + * clock has a gate control and a fractional * divider. The rate is calculated + * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35. + */ +struct clk_ref { + struct clk_hw hw; + void __iomem *reg; + u8 idx; +}; + +#define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw) + +static int clk_ref_enable(struct clk_hw *hw) +{ + struct clk_ref *ref = to_clk_ref(hw); + + writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR); + + return 0; +} + +static void clk_ref_disable(struct clk_hw *hw) +{ + struct clk_ref *ref = to_clk_ref(hw); + + writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET); +} + +static unsigned long clk_ref_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_ref *ref = to_clk_ref(hw); + u64 tmp = parent_rate; + u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f; + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + u64 tmp = parent_rate; + u8 frac; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 18) + frac = 18; + else if (frac > 35) + frac = 35; + + tmp = parent_rate; + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_ref *ref = to_clk_ref(hw); + unsigned long flags; + u64 tmp = parent_rate; + u32 val; + u8 frac, shift = ref->idx * 8; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 18) + frac = 18; + else if (frac > 35) + frac = 35; + + spin_lock_irqsave(&mxs_lock, flags); + + val = readl_relaxed(ref->reg); + val &= ~(0x3f << shift); + val |= frac << shift; + writel_relaxed(val, ref->reg); + + spin_unlock_irqrestore(&mxs_lock, flags); + + return 0; +} + +static const struct clk_ops clk_ref_ops = { + .enable = clk_ref_enable, + .disable = clk_ref_disable, + .recalc_rate = clk_ref_recalc_rate, + .round_rate = clk_ref_round_rate, + .set_rate = clk_ref_set_rate, +}; + +struct clk *mxs_clk_ref(const char *name, const char *parent_name, + void __iomem *reg, u8 idx) +{ + struct clk_ref *ref; + struct clk *clk; + struct clk_init_data init; + + ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!ref) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_ref_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name: NULL); + init.num_parents = (parent_name ? 1 : 0); + + ref->reg = reg; + ref->idx = idx; + ref->hw.init = &init; + + clk = clk_register(NULL, &ref->hw); + if (IS_ERR(clk)) + kfree(ref); + + return clk; +} diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c new file mode 100644 index 000000000000..b24d56067c80 --- /dev/null +++ b/drivers/clk/mxs/clk.c @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/spinlock.h> + +DEFINE_SPINLOCK(mxs_lock); + +int mxs_clk_wait(void __iomem *reg, u8 shift) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + while (readl_relaxed(reg) & (1 << shift)) + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + return 0; +} diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h new file mode 100644 index 000000000000..81421e28e69c --- /dev/null +++ b/drivers/clk/mxs/clk.h @@ -0,0 +1,66 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __MXS_CLK_H +#define __MXS_CLK_H + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/spinlock.h> + +#define SET 0x4 +#define CLR 0x8 + +extern spinlock_t mxs_lock; + +int mxs_clk_wait(void __iomem *reg, u8 shift); + +struct clk *mxs_clk_pll(const char *name, const char *parent_name, + void __iomem *base, u8 power, unsigned long rate); + +struct clk *mxs_clk_ref(const char *name, const char *parent_name, + void __iomem *reg, u8 idx); + +struct clk *mxs_clk_div(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy); + +struct clk *mxs_clk_frac(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, u8 busy); + +static inline struct clk *mxs_clk_fixed(const char *name, int rate) +{ + return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); +} + +static inline struct clk *mxs_clk_gate(const char *name, + const char *parent_name, void __iomem *reg, u8 shift) +{ + return clk_register_gate(NULL, name, parent_name, CLK_SET_RATE_PARENT, + reg, shift, CLK_GATE_SET_TO_DISABLE, + &mxs_lock); +} + +static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parent_names, int num_parents) +{ + return clk_register_mux(NULL, name, parent_names, num_parents, + CLK_SET_RATE_PARENT, reg, shift, width, + 0, &mxs_lock); +} + +static inline struct clk *mxs_clk_fixed_factor(const char *name, + const char *parent_name, unsigned int mult, unsigned int div) +{ + return clk_register_fixed_factor(NULL, name, parent_name, + CLK_SET_RATE_PARENT, mult, div); +} + +#endif /* __MXS_CLK_H */ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index ef378b5b17e4..aadeb5be9dba 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -238,6 +238,7 @@ config IMX_DMA config MXS_DMA bool "MXS DMA support" depends on SOC_IMX23 || SOC_IMX28 + select STMP_DEVICE select DMA_ENGINE help Support the MXS DMA engine. This engine including APBH-DMA diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 655d4ce6ed0d..1cb9b974493f 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -22,11 +22,14 @@ #include <linux/platform_device.h> #include <linux/dmaengine.h> #include <linux/delay.h> +#include <linux/module.h> #include <linux/fsl/mxs-dma.h> +#include <linux/stmp_device.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/irq.h> #include <mach/mxs.h> -#include <mach/common.h> #include "dmaengine.h" @@ -36,12 +39,8 @@ * dma can program the controller registers of peripheral devices. */ -#define MXS_DMA_APBH 0 -#define MXS_DMA_APBX 1 -#define dma_is_apbh() (mxs_dma->dev_id == MXS_DMA_APBH) - -#define APBH_VERSION_LATEST 3 -#define apbh_is_old() (mxs_dma->version < APBH_VERSION_LATEST) +#define dma_is_apbh(mxs_dma) ((mxs_dma)->type == MXS_DMA_APBH) +#define apbh_is_old(mxs_dma) ((mxs_dma)->dev_id == IMX23_DMA) #define HW_APBHX_CTRL0 0x000 #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) @@ -51,13 +50,14 @@ #define HW_APBHX_CTRL2 0x020 #define HW_APBHX_CHANNEL_CTRL 0x030 #define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16 -#define HW_APBH_VERSION (cpu_is_mx23() ? 0x3f0 : 0x800) -#define HW_APBX_VERSION 0x800 -#define BP_APBHX_VERSION_MAJOR 24 -#define HW_APBHX_CHn_NXTCMDAR(n) \ - (((dma_is_apbh() && apbh_is_old()) ? 0x050 : 0x110) + (n) * 0x70) -#define HW_APBHX_CHn_SEMA(n) \ - (((dma_is_apbh() && apbh_is_old()) ? 0x080 : 0x140) + (n) * 0x70) +/* + * The offset of NXTCMDAR register is different per both dma type and version, + * while stride for each channel is all the same 0x70. + */ +#define HW_APBHX_CHn_NXTCMDAR(d, n) \ + (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x050 : 0x110) + (n) * 0x70) +#define HW_APBHX_CHn_SEMA(d, n) \ + (((dma_is_apbh(d) && apbh_is_old(d)) ? 0x080 : 0x140) + (n) * 0x70) /* * ccw bits definitions @@ -121,9 +121,19 @@ struct mxs_dma_chan { #define MXS_DMA_CHANNELS 16 #define MXS_DMA_CHANNELS_MASK 0xffff +enum mxs_dma_devtype { + MXS_DMA_APBH, + MXS_DMA_APBX, +}; + +enum mxs_dma_id { + IMX23_DMA, + IMX28_DMA, +}; + struct mxs_dma_engine { - int dev_id; - unsigned int version; + enum mxs_dma_id dev_id; + enum mxs_dma_devtype type; void __iomem *base; struct clk *clk; struct dma_device dma_device; @@ -131,17 +141,86 @@ struct mxs_dma_engine { struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; }; +struct mxs_dma_type { + enum mxs_dma_id id; + enum mxs_dma_devtype type; +}; + +static struct mxs_dma_type mxs_dma_types[] = { + { + .id = IMX23_DMA, + .type = MXS_DMA_APBH, + }, { + .id = IMX23_DMA, + .type = MXS_DMA_APBX, + }, { + .id = IMX28_DMA, + .type = MXS_DMA_APBH, + }, { + .id = IMX28_DMA, + .type = MXS_DMA_APBX, + } +}; + +static struct platform_device_id mxs_dma_ids[] = { + { + .name = "imx23-dma-apbh", + .driver_data = (kernel_ulong_t) &mxs_dma_types[0], + }, { + .name = "imx23-dma-apbx", + .driver_data = (kernel_ulong_t) &mxs_dma_types[1], + }, { + .name = "imx28-dma-apbh", + .driver_data = (kernel_ulong_t) &mxs_dma_types[2], + }, { + .name = "imx28-dma-apbx", + .driver_data = (kernel_ulong_t) &mxs_dma_types[3], + }, { + /* end of list */ + } +}; + +static const struct of_device_id mxs_dma_dt_ids[] = { + { .compatible = "fsl,imx23-dma-apbh", .data = &mxs_dma_ids[0], }, + { .compatible = "fsl,imx23-dma-apbx", .data = &mxs_dma_ids[1], }, + { .compatible = "fsl,imx28-dma-apbh", .data = &mxs_dma_ids[2], }, + { .compatible = "fsl,imx28-dma-apbx", .data = &mxs_dma_ids[3], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_dma_dt_ids); + +static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct mxs_dma_chan, chan); +} + +int mxs_dma_is_apbh(struct dma_chan *chan) +{ + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; + + return dma_is_apbh(mxs_dma); +} + +int mxs_dma_is_apbx(struct dma_chan *chan) +{ + struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; + + return !dma_is_apbh(mxs_dma); +} + static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) { struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; - if (dma_is_apbh() && apbh_is_old()) + if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) writel(1 << (chan_id + BP_APBH_CTRL0_RESET_CHANNEL), - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); else writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL), - mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET); } static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) @@ -151,10 +230,10 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) /* set cmd_addr up */ writel(mxs_chan->ccw_phys, - mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); + mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(mxs_dma, chan_id)); /* write 1 to SEMA to kick off the channel */ - writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id)); + writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(mxs_dma, chan_id)); } static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) @@ -168,12 +247,12 @@ static void mxs_dma_pause_chan(struct mxs_dma_chan *mxs_chan) int chan_id = mxs_chan->chan.chan_id; /* freeze the channel */ - if (dma_is_apbh() && apbh_is_old()) + if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); else writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_SET); mxs_chan->status = DMA_PAUSED; } @@ -184,21 +263,16 @@ static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan) int chan_id = mxs_chan->chan.chan_id; /* unfreeze the channel */ - if (dma_is_apbh() && apbh_is_old()) + if (dma_is_apbh(mxs_dma) && apbh_is_old(mxs_dma)) writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR); + mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR); else writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CHANNEL_CTRL + MXS_CLR_ADDR); + mxs_dma->base + HW_APBHX_CHANNEL_CTRL + STMP_OFFSET_REG_CLR); mxs_chan->status = DMA_IN_PROGRESS; } -static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) -{ - return container_of(chan, struct mxs_dma_chan, chan); -} - static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) { return dma_cookie_assign(tx); @@ -220,11 +294,11 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) /* completion status */ stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1); stat1 &= MXS_DMA_CHANNELS_MASK; - writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + MXS_CLR_ADDR); + writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR); /* error status */ stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2); - writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + MXS_CLR_ADDR); + writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR); /* * When both completion and error of termination bits set at the @@ -567,27 +641,21 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) if (ret) return ret; - ret = mxs_reset_block(mxs_dma->base); + ret = stmp_reset_block(mxs_dma->base); if (ret) goto err_out; - /* only major version matters */ - mxs_dma->version = readl(mxs_dma->base + - ((mxs_dma->dev_id == MXS_DMA_APBX) ? - HW_APBX_VERSION : HW_APBH_VERSION)) >> - BP_APBHX_VERSION_MAJOR; - /* enable apbh burst */ - if (dma_is_apbh()) { + if (dma_is_apbh(mxs_dma)) { writel(BM_APBH_CTRL0_APB_BURST_EN, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); writel(BM_APBH_CTRL0_APB_BURST8_EN, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); } /* enable irq for all the channels */ writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS, - mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR); + mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET); err_out: clk_disable_unprepare(mxs_dma->clk); @@ -596,8 +664,9 @@ err_out: static int __init mxs_dma_probe(struct platform_device *pdev) { - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); + const struct platform_device_id *id_entry; + const struct of_device_id *of_id; + const struct mxs_dma_type *dma_type; struct mxs_dma_engine *mxs_dma; struct resource *iores; int ret, i; @@ -606,7 +675,15 @@ static int __init mxs_dma_probe(struct platform_device *pdev) if (!mxs_dma) return -ENOMEM; - mxs_dma->dev_id = id_entry->driver_data; + of_id = of_match_device(mxs_dma_dt_ids, &pdev->dev); + if (of_id) + id_entry = of_id->data; + else + id_entry = platform_get_device_id(pdev); + + dma_type = (struct mxs_dma_type *)id_entry->driver_data; + mxs_dma->type = dma_type->type; + mxs_dma->dev_id = dma_type->id; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -689,23 +766,12 @@ err_request_region: return ret; } -static struct platform_device_id mxs_dma_type[] = { - { - .name = "mxs-dma-apbh", - .driver_data = MXS_DMA_APBH, - }, { - .name = "mxs-dma-apbx", - .driver_data = MXS_DMA_APBX, - }, { - /* end of list */ - } -}; - static struct platform_driver mxs_dma_driver = { .driver = { .name = "mxs-dma", + .of_match_table = mxs_dma_dt_ids, }, - .id_table = mxs_dma_type, + .id_table = mxs_dma_ids, }; static int __init mxs_dma_module_init(void) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 385c58e8405b..429228b52acd 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -25,23 +25,25 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/basic_mmio_gpio.h> #include <linux/module.h> -#include <mach/mxs.h> #define MXS_SET 0x4 #define MXS_CLR 0x8 -#define PINCTRL_DOUT(n) ((cpu_is_mx23() ? 0x0500 : 0x0700) + (n) * 0x10) -#define PINCTRL_DIN(n) ((cpu_is_mx23() ? 0x0600 : 0x0900) + (n) * 0x10) -#define PINCTRL_DOE(n) ((cpu_is_mx23() ? 0x0700 : 0x0b00) + (n) * 0x10) -#define PINCTRL_PIN2IRQ(n) ((cpu_is_mx23() ? 0x0800 : 0x1000) + (n) * 0x10) -#define PINCTRL_IRQEN(n) ((cpu_is_mx23() ? 0x0900 : 0x1100) + (n) * 0x10) -#define PINCTRL_IRQLEV(n) ((cpu_is_mx23() ? 0x0a00 : 0x1200) + (n) * 0x10) -#define PINCTRL_IRQPOL(n) ((cpu_is_mx23() ? 0x0b00 : 0x1300) + (n) * 0x10) -#define PINCTRL_IRQSTAT(n) ((cpu_is_mx23() ? 0x0c00 : 0x1400) + (n) * 0x10) +#define PINCTRL_DOUT(p) ((is_imx23_gpio(p) ? 0x0500 : 0x0700) + (p->id) * 0x10) +#define PINCTRL_DIN(p) ((is_imx23_gpio(p) ? 0x0600 : 0x0900) + (p->id) * 0x10) +#define PINCTRL_DOE(p) ((is_imx23_gpio(p) ? 0x0700 : 0x0b00) + (p->id) * 0x10) +#define PINCTRL_PIN2IRQ(p) ((is_imx23_gpio(p) ? 0x0800 : 0x1000) + (p->id) * 0x10) +#define PINCTRL_IRQEN(p) ((is_imx23_gpio(p) ? 0x0900 : 0x1100) + (p->id) * 0x10) +#define PINCTRL_IRQLEV(p) ((is_imx23_gpio(p) ? 0x0a00 : 0x1200) + (p->id) * 0x10) +#define PINCTRL_IRQPOL(p) ((is_imx23_gpio(p) ? 0x0b00 : 0x1300) + (p->id) * 0x10) +#define PINCTRL_IRQSTAT(p) ((is_imx23_gpio(p) ? 0x0c00 : 0x1400) + (p->id) * 0x10) #define GPIO_INT_FALL_EDGE 0x0 #define GPIO_INT_LOW_LEV 0x1 @@ -52,14 +54,30 @@ #define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START) +enum mxs_gpio_id { + IMX23_GPIO, + IMX28_GPIO, +}; + struct mxs_gpio_port { void __iomem *base; int id; int irq; int virtual_irq_start; struct bgpio_chip bgc; + enum mxs_gpio_id devid; }; +static inline int is_imx23_gpio(struct mxs_gpio_port *port) +{ + return port->devid == IMX23_GPIO; +} + +static inline int is_imx28_gpio(struct mxs_gpio_port *port) +{ + return port->devid == IMX28_GPIO; +} + /* Note: This driver assumes 32 GPIOs are handled in one register */ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) @@ -89,21 +107,21 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type) } /* set level or edge */ - pin_addr = port->base + PINCTRL_IRQLEV(port->id); + pin_addr = port->base + PINCTRL_IRQLEV(port); if (edge & GPIO_INT_LEV_MASK) writel(pin_mask, pin_addr + MXS_SET); else writel(pin_mask, pin_addr + MXS_CLR); /* set polarity */ - pin_addr = port->base + PINCTRL_IRQPOL(port->id); + pin_addr = port->base + PINCTRL_IRQPOL(port); if (edge & GPIO_INT_POL_MASK) writel(pin_mask, pin_addr + MXS_SET); else writel(pin_mask, pin_addr + MXS_CLR); writel(1 << (gpio & 0x1f), - port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); + port->base + PINCTRL_IRQSTAT(port) + MXS_CLR); return 0; } @@ -117,8 +135,8 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc) desc->irq_data.chip->irq_ack(&desc->irq_data); - irq_stat = readl(port->base + PINCTRL_IRQSTAT(port->id)) & - readl(port->base + PINCTRL_IRQEN(port->id)); + irq_stat = readl(port->base + PINCTRL_IRQSTAT(port)) & + readl(port->base + PINCTRL_IRQEN(port)); while (irq_stat != 0) { int irqoffset = fls(irq_stat) - 1; @@ -164,8 +182,8 @@ static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port) ct->chip.irq_unmask = irq_gc_mask_set_bit; ct->chip.irq_set_type = mxs_gpio_set_irq_type; ct->chip.irq_set_wake = mxs_gpio_set_wake_irq; - ct->regs.ack = PINCTRL_IRQSTAT(port->id) + MXS_CLR; - ct->regs.mask = PINCTRL_IRQEN(port->id); + ct->regs.ack = PINCTRL_IRQSTAT(port) + MXS_CLR; + ct->regs.mask = PINCTRL_IRQEN(port); irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0); } @@ -179,60 +197,83 @@ static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return port->virtual_irq_start + offset; } +static struct platform_device_id mxs_gpio_ids[] = { + { + .name = "imx23-gpio", + .driver_data = IMX23_GPIO, + }, { + .name = "imx28-gpio", + .driver_data = IMX28_GPIO, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mxs_gpio_ids); + +static const struct of_device_id mxs_gpio_dt_ids[] = { + { .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, }, + { .compatible = "fsl,imx28-gpio", .data = (void *) IMX28_GPIO, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids); + static int __devinit mxs_gpio_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(mxs_gpio_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; + struct device_node *parent; static void __iomem *base; struct mxs_gpio_port *port; struct resource *iores = NULL; int err; - port = kzalloc(sizeof(struct mxs_gpio_port), GFP_KERNEL); + port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; - port->id = pdev->id; + if (np) { + port->id = of_alias_get_id(np, "gpio"); + if (port->id < 0) + return port->id; + port->devid = (enum mxs_gpio_id) of_id->data; + } else { + port->id = pdev->id; + port->devid = pdev->id_entry->driver_data; + } port->virtual_irq_start = MXS_GPIO_IRQ_START + port->id * 32; + port->irq = platform_get_irq(pdev, 0); + if (port->irq < 0) + return port->irq; + /* * map memory region only once, as all the gpio ports * share the same one */ if (!base) { - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) { - err = -ENODEV; - goto out_kfree; - } - - if (!request_mem_region(iores->start, resource_size(iores), - pdev->name)) { - err = -EBUSY; - goto out_kfree; - } - - base = ioremap(iores->start, resource_size(iores)); - if (!base) { - err = -ENOMEM; - goto out_release_mem; + if (np) { + parent = of_get_parent(np); + base = of_iomap(parent, 0); + of_node_put(parent); + } else { + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_request_and_ioremap(&pdev->dev, iores); } + if (!base) + return -EADDRNOTAVAIL; } port->base = base; - port->irq = platform_get_irq(pdev, 0); - if (port->irq < 0) { - err = -EINVAL; - goto out_iounmap; - } - /* * select the pin interrupt functionality but initially * disable the interrupts */ - writel(~0U, port->base + PINCTRL_PIN2IRQ(port->id)); - writel(0, port->base + PINCTRL_IRQEN(port->id)); + writel(~0U, port->base + PINCTRL_PIN2IRQ(port)); + writel(0, port->base + PINCTRL_IRQEN(port)); /* clear address has to be used to clear IRQSTAT bits */ - writel(~0U, port->base + PINCTRL_IRQSTAT(port->id) + MXS_CLR); + writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR); /* gpio-mxs can be a generic irq chip */ mxs_gpio_init_gc(port); @@ -242,41 +283,32 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) irq_set_handler_data(port->irq, port); err = bgpio_init(&port->bgc, &pdev->dev, 4, - port->base + PINCTRL_DIN(port->id), - port->base + PINCTRL_DOUT(port->id), NULL, - port->base + PINCTRL_DOE(port->id), NULL, false); + port->base + PINCTRL_DIN(port), + port->base + PINCTRL_DOUT(port), NULL, + port->base + PINCTRL_DOE(port), NULL, false); if (err) - goto out_iounmap; + return err; port->bgc.gc.to_irq = mxs_gpio_to_irq; port->bgc.gc.base = port->id * 32; err = gpiochip_add(&port->bgc.gc); - if (err) - goto out_bgpio_remove; + if (err) { + bgpio_remove(&port->bgc); + return err; + } return 0; - -out_bgpio_remove: - bgpio_remove(&port->bgc); -out_iounmap: - if (iores) - iounmap(port->base); -out_release_mem: - if (iores) - release_mem_region(iores->start, resource_size(iores)); -out_kfree: - kfree(port); - dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); - return err; } static struct platform_driver mxs_gpio_driver = { .driver = { .name = "gpio-mxs", .owner = THIS_MODULE, + .of_match_table = mxs_gpio_dt_ids, }, .probe = mxs_gpio_probe, + .id_table = mxs_gpio_ids, }; static int __init mxs_gpio_init(void) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index dfb84b7ee550..56bce9a8bcbb 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -51,6 +51,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_i2c.h> +#include <linux/pinctrl/consumer.h> #include <mach/irqs.h> #include <mach/hardware.h> @@ -470,6 +471,7 @@ static int __init i2c_imx_probe(struct platform_device *pdev) struct imx_i2c_struct *i2c_imx; struct resource *res; struct imxi2c_platform_data *pdata = pdev->dev.platform_data; + struct pinctrl *pinctrl; void __iomem *base; resource_size_t res_size; int irq, bitrate; @@ -520,6 +522,12 @@ static int __init i2c_imx_probe(struct platform_device *pdev) i2c_imx->base = base; i2c_imx->res = res; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto fail3; + } + /* Get I2C clock */ i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk"); if (IS_ERR(i2c_imx->clk)) { diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 76b8af44f634..7dca58b0e746 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -26,6 +26,10 @@ #include <linux/platform_device.h> #include <linux/jiffies.h> #include <linux/io.h> +#include <linux/pinctrl/consumer.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_i2c.h> #include <mach/common.h> @@ -325,10 +329,15 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mxs_i2c_dev *i2c; struct i2c_adapter *adap; + struct pinctrl *pinctrl; struct resource *res; resource_size_t res_size; int err, irq; + pinctrl = devm_pinctrl_get_select_default(dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL); if (!i2c) return -ENOMEM; @@ -365,6 +374,7 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev) adap->algo = &mxs_i2c_algo; adap->dev.parent = dev; adap->nr = pdev->id; + adap->dev.of_node = pdev->dev.of_node; i2c_set_adapdata(adap, i2c); err = i2c_add_numbered_adapter(adap); if (err) { @@ -374,6 +384,8 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev) return err; } + of_i2c_register_devices(adap); + return 0; } @@ -393,10 +405,17 @@ static int __devexit mxs_i2c_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxs_i2c_dt_ids[] = { + { .compatible = "fsl,imx28-i2c", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_i2c_dt_ids); + static struct platform_driver mxs_i2c_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = mxs_i2c_dt_ids, }, .remove = __devexit_p(mxs_i2c_remove), }; diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e3f5af96ab87..34a90266ab11 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -23,6 +23,9 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/ioport.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -39,18 +42,16 @@ #include <linux/regulator/consumer.h> #include <linux/module.h> #include <linux/fsl/mxs-dma.h> - -#include <mach/mxs.h> -#include <mach/common.h> -#include <mach/mmc.h> +#include <linux/pinctrl/consumer.h> +#include <linux/stmp_device.h> +#include <linux/mmc/mxs-mmc.h> #define DRIVER_NAME "mxs-mmc" /* card detect polling timeout */ #define MXS_MMC_DETECT_TIMEOUT (HZ/2) -#define SSP_VERSION_LATEST 4 -#define ssp_is_old() (host->version < SSP_VERSION_LATEST) +#define ssp_is_old(host) ((host)->devid == IMX23_MMC) /* SSP registers */ #define HW_SSP_CTRL0 0x000 @@ -85,14 +86,14 @@ #define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) #define BP_SSP_BLOCK_SIZE_BLOCK_SIZE (0) #define BM_SSP_BLOCK_SIZE_BLOCK_SIZE (0xf) -#define HW_SSP_TIMING (ssp_is_old() ? 0x050 : 0x070) +#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070) #define BP_SSP_TIMING_TIMEOUT (16) #define BM_SSP_TIMING_TIMEOUT (0xffff << 16) #define BP_SSP_TIMING_CLOCK_DIVIDE (8) #define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) #define BP_SSP_TIMING_CLOCK_RATE (0) #define BM_SSP_TIMING_CLOCK_RATE (0xff) -#define HW_SSP_CTRL1 (ssp_is_old() ? 0x060 : 0x080) +#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) #define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) #define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) #define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) @@ -115,15 +116,13 @@ #define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) #define BP_SSP_CTRL1_SSP_MODE (0) #define BM_SSP_CTRL1_SSP_MODE (0xf) -#define HW_SSP_SDRESP0 (ssp_is_old() ? 0x080 : 0x0a0) -#define HW_SSP_SDRESP1 (ssp_is_old() ? 0x090 : 0x0b0) -#define HW_SSP_SDRESP2 (ssp_is_old() ? 0x0a0 : 0x0c0) -#define HW_SSP_SDRESP3 (ssp_is_old() ? 0x0b0 : 0x0d0) -#define HW_SSP_STATUS (ssp_is_old() ? 0x0c0 : 0x100) +#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) +#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) +#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) +#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0) +#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) #define BM_SSP_STATUS_CARD_DETECT (1 << 28) #define BM_SSP_STATUS_SDIO_IRQ (1 << 17) -#define HW_SSP_VERSION (cpu_is_mx23() ? 0x110 : 0x130) -#define BP_SSP_VERSION_MAJOR (24) #define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) @@ -138,6 +137,11 @@ #define SSP_PIO_NUM 3 +enum mxs_mmc_id { + IMX23_MMC, + IMX28_MMC, +}; + struct mxs_mmc_host { struct mmc_host *mmc; struct mmc_request *mrq; @@ -145,9 +149,7 @@ struct mxs_mmc_host { struct mmc_data *data; void __iomem *base; - int irq; - struct resource *res; - struct resource *dma_res; + int dma_channel; struct clk *clk; unsigned int clk_rate; @@ -157,32 +159,28 @@ struct mxs_mmc_host { enum dma_transfer_direction slave_dirn; u32 ssp_pio_words[SSP_PIO_NUM]; - unsigned int version; + enum mxs_mmc_id devid; unsigned char bus_width; spinlock_t lock; int sdio_irq_en; + int wp_gpio; }; static int mxs_mmc_get_ro(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); - struct mxs_mmc_platform_data *pdata = - mmc_dev(host->mmc)->platform_data; - - if (!pdata) - return -EFAULT; - if (!gpio_is_valid(pdata->wp_gpio)) + if (!gpio_is_valid(host->wp_gpio)) return -EINVAL; - return gpio_get_value(pdata->wp_gpio); + return gpio_get_value(host->wp_gpio); } static int mxs_mmc_get_cd(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); - return !(readl(host->base + HW_SSP_STATUS) & + return !(readl(host->base + HW_SSP_STATUS(host)) & BM_SSP_STATUS_CARD_DETECT); } @@ -190,7 +188,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host) { u32 ctrl0, ctrl1; - mxs_reset_block(host->base); + stmp_reset_block(host->base); ctrl0 = BM_SSP_CTRL0_IGNORE_CRC; ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) | @@ -206,7 +204,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host) writel(BF_SSP(0xffff, TIMING_TIMEOUT) | BF_SSP(2, TIMING_CLOCK_DIVIDE) | BF_SSP(0, TIMING_CLOCK_RATE), - host->base + HW_SSP_TIMING); + host->base + HW_SSP_TIMING(host)); if (host->sdio_irq_en) { ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; @@ -214,7 +212,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host) } writel(ctrl0, host->base + HW_SSP_CTRL0); - writel(ctrl1, host->base + HW_SSP_CTRL1); + writel(ctrl1, host->base + HW_SSP_CTRL1(host)); } static void mxs_mmc_start_cmd(struct mxs_mmc_host *host, @@ -228,12 +226,12 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) { if (mmc_resp_type(cmd) & MMC_RSP_136) { - cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0); - cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1); - cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2); - cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3); + cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host)); + cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host)); + cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host)); + cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host)); } else { - cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0); + cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host)); } } @@ -276,9 +274,9 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) spin_lock(&host->lock); - stat = readl(host->base + HW_SSP_CTRL1); + stat = readl(host->base + HW_SSP_CTRL1(host)); writel(stat & MXS_MMC_IRQ_BITS, - host->base + HW_SSP_CTRL1 + MXS_CLR_ADDR); + host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN)) mmc_signal_sdio_irq(host->mmc); @@ -484,7 +482,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) blocks = 1; /* xfer count, block size and count need to be set differently */ - if (ssp_is_old()) { + if (ssp_is_old(host)) { ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT); cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) | BF_SSP(blocks - 1, CMD0_BLOCK_COUNT); @@ -508,10 +506,10 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) /* set the timeout count */ timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns); - val = readl(host->base + HW_SSP_TIMING); + val = readl(host->base + HW_SSP_TIMING(host)); val &= ~(BM_SSP_TIMING_TIMEOUT); val |= BF_SSP(timeout, TIMING_TIMEOUT); - writel(val, host->base + HW_SSP_TIMING); + writel(val, host->base + HW_SSP_TIMING(host)); /* pio */ host->ssp_pio_words[0] = ctrl0; @@ -597,11 +595,11 @@ static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); - val = readl(host->base + HW_SSP_TIMING); + val = readl(host->base + HW_SSP_TIMING(host)); val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); - writel(val, host->base + HW_SSP_TIMING); + writel(val, host->base + HW_SSP_TIMING(host)); host->clk_rate = ssp_sck; @@ -636,18 +634,19 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) if (enable) { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, - host->base + HW_SSP_CTRL0 + MXS_SET_ADDR); + host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1 + MXS_SET_ADDR); + host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET); - if (readl(host->base + HW_SSP_STATUS) & BM_SSP_STATUS_SDIO_IRQ) + if (readl(host->base + HW_SSP_STATUS(host)) & + BM_SSP_STATUS_SDIO_IRQ) mmc_signal_sdio_irq(host->mmc); } else { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, - host->base + HW_SSP_CTRL0 + MXS_CLR_ADDR); + host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1 + MXS_CLR_ADDR); + host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); } spin_unlock_irqrestore(&host->lock, flags); @@ -668,7 +667,7 @@ static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param) if (!mxs_dma_is_apbh(chan)) return false; - if (chan->chan_id != host->dma_res->start) + if (chan->chan_id != host->dma_channel) return false; chan->private = &host->dma_data; @@ -676,12 +675,36 @@ static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param) return true; } +static struct platform_device_id mxs_mmc_ids[] = { + { + .name = "imx23-mmc", + .driver_data = IMX23_MMC, + }, { + .name = "imx28-mmc", + .driver_data = IMX28_MMC, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mxs_mmc_ids); + +static const struct of_device_id mxs_mmc_dt_ids[] = { + { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, }, + { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids); + static int mxs_mmc_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(mxs_mmc_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; struct mxs_mmc_host *host; struct mmc_host *mmc; - struct resource *iores, *dmares, *r; + struct resource *iores, *dmares; struct mxs_mmc_platform_data *pdata; + struct pinctrl *pinctrl; int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; @@ -689,40 +712,51 @@ static int mxs_mmc_probe(struct platform_device *pdev) dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); irq_err = platform_get_irq(pdev, 0); irq_dma = platform_get_irq(pdev, 1); - if (!iores || !dmares || irq_err < 0 || irq_dma < 0) + if (!iores || irq_err < 0 || irq_dma < 0) return -EINVAL; - r = request_mem_region(iores->start, resource_size(iores), pdev->name); - if (!r) - return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out_release_mem; - } + if (!mmc) + return -ENOMEM; host = mmc_priv(mmc); - host->base = ioremap(r->start, resource_size(r)); + host->base = devm_request_and_ioremap(&pdev->dev, iores); if (!host->base) { - ret = -ENOMEM; + ret = -EADDRNOTAVAIL; goto out_mmc_free; } - /* only major verion does matter */ - host->version = readl(host->base + HW_SSP_VERSION) >> - BP_SSP_VERSION_MAJOR; + if (np) { + host->devid = (enum mxs_mmc_id) of_id->data; + /* + * TODO: This is a temporary solution and should be changed + * to use generic DMA binding later when the helpers get in. + */ + ret = of_property_read_u32(np, "fsl,ssp-dma-channel", + &host->dma_channel); + if (ret) { + dev_err(mmc_dev(host->mmc), + "failed to get dma channel\n"); + goto out_mmc_free; + } + } else { + host->devid = pdev->id_entry->driver_data; + host->dma_channel = dmares->start; + } host->mmc = mmc; - host->res = r; - host->dma_res = dmares; - host->irq = irq_err; host->sdio_irq_en = 0; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto out_mmc_free; + } + host->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); - goto out_iounmap; + goto out_mmc_free; } clk_prepare_enable(host->clk); @@ -744,11 +778,20 @@ static int mxs_mmc_probe(struct platform_device *pdev) MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL; pdata = mmc_dev(host->mmc)->platform_data; - if (pdata) { + if (!pdata) { + u32 bus_width = 0; + of_property_read_u32(np, "bus-width", &bus_width); + if (bus_width == 4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + else if (bus_width == 8) + mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); + } else { if (pdata->flags & SLOTF_8_BIT_CAPABLE) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; if (pdata->flags & SLOTF_4_BIT_CAPABLE) mmc->caps |= MMC_CAP_4_BIT_DATA; + host->wp_gpio = pdata->wp_gpio; } mmc->f_min = 400000; @@ -757,13 +800,14 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->max_segs = 52; mmc->max_blk_size = 1 << 0xf; - mmc->max_blk_count = (ssp_is_old()) ? 0xff : 0xffffff; - mmc->max_req_size = (ssp_is_old()) ? 0xffff : 0xffffffff; + mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff; + mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff; mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev); platform_set_drvdata(pdev, mmc); - ret = request_irq(host->irq, mxs_mmc_irq_handler, 0, DRIVER_NAME, host); + ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0, + DRIVER_NAME, host); if (ret) goto out_free_dma; @@ -771,26 +815,20 @@ static int mxs_mmc_probe(struct platform_device *pdev) ret = mmc_add_host(mmc); if (ret) - goto out_free_irq; + goto out_free_dma; dev_info(mmc_dev(host->mmc), "initialized\n"); return 0; -out_free_irq: - free_irq(host->irq, host); out_free_dma: if (host->dmach) dma_release_channel(host->dmach); out_clk_put: clk_disable_unprepare(host->clk); clk_put(host->clk); -out_iounmap: - iounmap(host->base); out_mmc_free: mmc_free_host(mmc); -out_release_mem: - release_mem_region(iores->start, resource_size(iores)); return ret; } @@ -798,12 +836,9 @@ static int mxs_mmc_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct mxs_mmc_host *host = mmc_priv(mmc); - struct resource *res = host->res; mmc_remove_host(mmc); - free_irq(host->irq, host); - platform_set_drvdata(pdev, NULL); if (host->dmach) @@ -812,12 +847,8 @@ static int mxs_mmc_remove(struct platform_device *pdev) clk_disable_unprepare(host->clk); clk_put(host->clk); - iounmap(host->base); - mmc_free_host(mmc); - release_mem_region(res->start, resource_size(res)); - return 0; } @@ -857,11 +888,13 @@ static const struct dev_pm_ops mxs_mmc_pm_ops = { static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, .remove = mxs_mmc_remove, + .id_table = mxs_mmc_ids, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &mxs_mmc_pm_ops, + .of_match_table = mxs_mmc_dt_ids, #endif }, }; diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 8abdaf6697a8..d190d04636a7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -24,6 +24,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> #include <mach/esdhc.h> #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" @@ -68,6 +69,7 @@ struct pltfm_imx_data { int flags; u32 scratchpad; enum imx_esdhc_type devtype; + struct pinctrl *pinctrl; struct esdhc_platform_data boarddata; }; @@ -467,6 +469,12 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) clk_prepare_enable(clk); pltfm_host->clk = clk; + imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(imx_data->pinctrl)) { + err = PTR_ERR(imx_data->pinctrl); + goto pin_err; + } + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) @@ -558,6 +566,7 @@ no_card_detect_irq: gpio_free(boarddata->wp_gpio); no_card_detect_pin: no_board_data: +pin_err: clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); err_clk_get: diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 9ec51cec2e14..8478fd9701a3 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/mtd/gpmi-nand.h> #include <linux/mtd/partitions.h> +#include <linux/pinctrl/consumer.h> #include "gpmi-nand.h" /* add our owner bbt descriptor */ @@ -476,6 +477,7 @@ acquire_err: static int __devinit acquire_resources(struct gpmi_nand_data *this) { struct resources *res = &this->resources; + struct pinctrl *pinctrl; int ret; ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); @@ -494,6 +496,12 @@ static int __devinit acquire_resources(struct gpmi_nand_data *this) if (ret) goto exit_dma_channels; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto exit_pin; + } + res->clock = clk_get(&this->pdev->dev, NULL); if (IS_ERR(res->clock)) { pr_err("can not get the clock\n"); @@ -503,6 +511,7 @@ static int __devinit acquire_resources(struct gpmi_nand_data *this) return 0; exit_clock: +exit_pin: release_dma_channels(this); exit_dma_channels: release_bch_irq(this); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 1efb08386c61..38c0690df5c8 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -35,6 +35,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> #define DRV_NAME "flexcan" @@ -927,11 +928,16 @@ static int __devinit flexcan_probe(struct platform_device *pdev) struct flexcan_priv *priv; struct resource *mem; struct clk *clk = NULL; + struct pinctrl *pinctrl; void __iomem *base; resource_size_t mem_size; int err, irq; u32 clock_freq = 0; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + if (pdev->dev.of_node) { const u32 *clock_freq_p; diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index a12b3f5bc025..500c106af86e 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -48,6 +48,7 @@ #include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/of_net.h> +#include <linux/pinctrl/consumer.h> #include <asm/cacheflush.h> @@ -1542,6 +1543,7 @@ fec_probe(struct platform_device *pdev) struct resource *r; const struct of_device_id *of_id; static int dev_id; + struct pinctrl *pinctrl; of_id = of_match_device(fec_dt_ids, &pdev->dev); if (of_id) @@ -1609,6 +1611,12 @@ fec_probe(struct platform_device *pdev) } } + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto failed_pin; + } + fep->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(fep->clk)) { ret = PTR_ERR(fep->clk); @@ -1639,6 +1647,7 @@ failed_mii_init: failed_init: clk_disable_unprepare(fep->clk); clk_put(fep->clk); +failed_pin: failed_clk: for (i = 0; i < FEC_IRQ_NUM; i++) { irq = platform_get_irq(pdev, i); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 570f22053be8..69c9a6601f45 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -37,6 +37,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> #include <mach/spi.h> @@ -758,6 +759,7 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; + struct pinctrl *pinctrl; int i, ret, num_cs; if (!np && !mxc_platform_info) { @@ -845,6 +847,12 @@ static int __devinit spi_imx_probe(struct platform_device *pdev) goto out_iounmap; } + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto out_free_irq; + } + spi_imx->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(spi_imx->clk)) { dev_err(&pdev->dev, "unable to get clock\n"); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3d569cd68f58..062ef8c2b3cb 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -52,6 +52,7 @@ #include <linux/scatterlist.h> #include <linux/delay.h> #include <linux/types.h> +#include <linux/pinctrl/consumer.h> #include <asm/io.h> #include <asm/sizes.h> @@ -1916,6 +1917,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) { struct uart_amba_port *uap; struct vendor_data *vendor = id->data; + struct pinctrl *pinctrl; void __iomem *base; int i, ret; @@ -1940,6 +1942,12 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto free; } + pinctrl = devm_pinctrl_get_select_default(&dev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto unmap; + } + uap->clk = clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e7feceeebc2f..ec206732f68c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -47,6 +47,7 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> #include <asm/io.h> #include <asm/irq.h> @@ -1464,6 +1465,7 @@ static int serial_imx_probe(struct platform_device *pdev) void __iomem *base; int ret = 0; struct resource *res; + struct pinctrl *pinctrl; sport = kzalloc(sizeof(*sport), GFP_KERNEL); if (!sport) @@ -1503,6 +1505,12 @@ static int serial_imx_probe(struct platform_device *pdev) sport->timer.function = imx_timeout; sport->timer.data = (unsigned long)sport; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto unmap; + } + sport->clk = clk_get(&pdev->dev, "uart"); if (IS_ERR(sport->clk)) { ret = PTR_ERR(sport->clk); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 55fd362b9879..7081600bede4 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -32,6 +32,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/pinctrl/consumer.h> #include <asm/cacheflush.h> @@ -678,6 +679,7 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) u32 version; int ret = 0; struct resource *r; + struct pinctrl *pinctrl; s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); if (!s) { @@ -685,6 +687,12 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) goto out; } + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto out_free; + } + s->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(s->clk)) { ret = PTR_ERR(s->clk); diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 4a89f889852d..6c6bc578d0fc 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -45,6 +45,7 @@ #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/io.h> +#include <linux/pinctrl/consumer.h> #include <mach/mxsfb.h> #define REG_SET 4 @@ -756,6 +757,7 @@ static int __devinit mxsfb_probe(struct platform_device *pdev) struct mxsfb_info *host; struct fb_info *fb_info; struct fb_modelist *modelist; + struct pinctrl *pinctrl; int i, ret; if (!pdata) { @@ -793,6 +795,12 @@ static int __devinit mxsfb_probe(struct platform_device *pdev) host->devdata = &mxsfb_devdata[pdev->id_entry->driver_data]; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + goto error_getpin; + } + host->clk = clk_get(&host->pdev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); @@ -848,6 +856,7 @@ error_init_fb: error_pseudo_pallette: clk_put(host->clk); error_getclock: +error_getpin: iounmap(host->base); error_ioremap: framebuffer_release(fb_info); |