diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index cfb82b0..af5d8cd 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -88,6 +88,8 @@ config ARCH_MXS bool "Freescale i.MX23/28 (mxs) based" select GENERIC_GPIO + select COMMON_CLK + select CLKDEV_LOOKUP config ARCH_NETX bool "Hilscher NetX based" diff --git a/arch/arm/boards/chumby_falconwing/falconwing.c b/arch/arm/boards/chumby_falconwing/falconwing.c index cb934ee..b029261 100644 --- a/arch/arm/boards/chumby_falconwing/falconwing.c +++ b/arch/arm/boards/chumby_falconwing/falconwing.c @@ -290,9 +290,6 @@ for (i = 0; i < ARRAY_SIZE(pad_setup); i++) imx_gpio_mode(pad_setup[i]); - imx_set_ioclk(480000000); /* enable IOCLK to run at the PLL frequency */ - /* run the SSP unit clock at 100,000 kHz */ - imx_set_sspclk(0, 100000000, 1); add_generic_device("mxs_mci", 0, NULL, IMX_SSP1_BASE, 0x2000, IORESOURCE_MEM, &mci_pdata); add_generic_device("stmfb", 0, NULL, IMX_FB_BASE, 4096, diff --git a/arch/arm/boards/crystalfontz-cfa10036/cfa10036.c b/arch/arm/boards/crystalfontz-cfa10036/cfa10036.c index 9054afa..fc3bb9e 100644 --- a/arch/arm/boards/crystalfontz-cfa10036/cfa10036.c +++ b/arch/arm/boards/crystalfontz-cfa10036/cfa10036.c @@ -122,11 +122,6 @@ for (i = 0; i < ARRAY_SIZE(cfa10036_pads); i++) imx_gpio_mode(cfa10036_pads[i]); - /* enable IOCLK0 to run at the PLL frequency */ - imx_set_ioclk(0, 480000000); - /* run the SSP unit clock at 100 MHz */ - imx_set_sspclk(0, 100000000, 1); - armlinux_set_bootparams((void *)IMX_MEMORY_BASE + 0x100); armlinux_set_architecture(MACH_TYPE_CFA10036); diff --git a/arch/arm/boards/freescale-mx23-evk/mx23-evk.c b/arch/arm/boards/freescale-mx23-evk/mx23-evk.c index dddefb2..47f7031 100644 --- a/arch/arm/boards/freescale-mx23-evk/mx23-evk.c +++ b/arch/arm/boards/freescale-mx23-evk/mx23-evk.c @@ -109,9 +109,6 @@ armlinux_set_bootparams((void*)IMX_MEMORY_BASE + 0x100); armlinux_set_architecture(MACH_TYPE_MX23EVK); - imx_set_ioclk(480000000); /* enable IOCLK to run at the PLL frequency */ - imx_set_sspclk(0, 100000000, 1); - add_generic_device("mxs_mci", DEVICE_ID_DYNAMIC, NULL, IMX_SSP1_BASE, 0x8000, IORESOURCE_MEM, &mci_pdata); diff --git a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c index bb2dbcc..687d3f7 100644 --- a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c +++ b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c @@ -253,14 +253,6 @@ for (i = 0; i < ARRAY_SIZE(mx28evk_pads); i++) imx_gpio_mode(mx28evk_pads[i]); - /* enable IOCLK0 to run at the PLL frequency */ - imx_set_ioclk(0, 480000000); - imx_set_ioclk(1, 320000000); - /* run the SSP unit clock at 100 MHz */ - imx_set_sspclk(0, 100000000, 1); - /* run the SSP unit 2 clock at 160Mhz */ - imx_set_sspclk(2, 160000000, 1); - armlinux_set_bootparams((void *)IMX_MEMORY_BASE + 0x100); armlinux_set_architecture(MACH_TYPE_MX28EVK); @@ -274,7 +266,6 @@ IORESOURCE_MEM, NULL); mx28_evk_get_ethaddr(); /* must be after registering ocotp */ - imx_enable_enetclk(); mx28_evk_fec_reset(); add_generic_device("imx28-fec", 0, NULL, IMX_FEC0_BASE, 0x4000, IORESOURCE_MEM, &fec_info); diff --git a/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c b/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c index 116a9ea..ce59a2a 100644 --- a/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c +++ b/arch/arm/boards/imx233-olinuxino/imx23-olinuxino.c @@ -124,12 +124,6 @@ armlinux_set_bootparams((void *)IMX_MEMORY_BASE + 0x100); armlinux_set_architecture(MACH_TYPE_IMX233_OLINUXINO); - /* enable IOCLK to run at the PLL frequency */ - imx_set_ioclk(480000000); - - /* run the SSP unit clock at 100,000 kHz */ - imx_set_sspclk(0, 100000000, 1); - add_generic_device("mxs_mci", DEVICE_ID_DYNAMIC, NULL, IMX_SSP1_BASE, 0x8000, IORESOURCE_MEM, &mci_pdata); diff --git a/arch/arm/boards/karo-tx28/tx28-stk5.c b/arch/arm/boards/karo-tx28/tx28-stk5.c index 8ee8ac2..a1b161a 100644 --- a/arch/arm/boards/karo-tx28/tx28-stk5.c +++ b/arch/arm/boards/karo-tx28/tx28-stk5.c @@ -371,11 +371,6 @@ for (i = 0; i < ARRAY_SIZE(tx28_starterkit_pad_setup); i++) imx_gpio_mode(tx28_starterkit_pad_setup[i]); - /* enable IOCLK0 to run at the PLL frequency */ - imx_set_ioclk(0, 480000000); - /* run the SSP unit clock at 100 MHz */ - imx_set_sspclk(0, 100000000, 1); - add_generic_device("mxs_mci", 0, NULL, IMX_SSP0_BASE, 0x2000, IORESOURCE_MEM, &mci_pdata); @@ -392,7 +387,6 @@ tx28_get_ethaddr(); - imx_enable_enetclk(); add_generic_device("imx28-fec", 0, NULL, IMX_FEC0_BASE, 0x4000, IORESOURCE_MEM, &fec_info); diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile index fe93096..a183987 100644 --- a/arch/arm/mach-mxs/Makefile +++ b/arch/arm/mach-mxs/Makefile @@ -1,6 +1,5 @@ obj-y += imx.o iomux-imx.o power.o common.o -obj-$(CONFIG_DRIVER_VIDEO_STM) += imx_lcd_clk.o -obj-$(CONFIG_ARCH_IMX23) += speed-imx23.o clocksource-imx23.o usb-imx23.o soc-imx23.o -obj-$(CONFIG_ARCH_IMX28) += speed-imx28.o clocksource-imx28.o usb-imx28.o soc-imx28.o +obj-$(CONFIG_ARCH_IMX23) += clocksource-imx23.o usb-imx23.o soc-imx23.o +obj-$(CONFIG_ARCH_IMX28) += clocksource-imx28.o usb-imx28.o soc-imx28.o obj-$(CONFIG_MXS_OCOTP) += ocotp.o obj-$(CONFIG_MXS_CMD_BCB) += bcb.o diff --git a/arch/arm/mach-mxs/imx.c b/arch/arm/mach-mxs/imx.c index 5acce93..9f195e4 100644 --- a/arch/arm/mach-mxs/imx.c +++ b/arch/arm/mach-mxs/imx.c @@ -45,22 +45,6 @@ } device_initcall(imx_reset_usb_bootstrap); -extern void imx_dump_clocks(void); - -static int do_clocks(int argc, char *argv[]) -{ - imx_dump_clocks(); - - return 0; -} - -BAREBOX_CMD_START(dump_clocks) - .cmd = do_clocks, - .usage = "show clock frequencies", - BAREBOX_CMD_COMPLETE(empty_complete) -BAREBOX_CMD_END - - static int __silicon_revision = SILICON_REVISION_UNKNOWN; int silicon_revision_get(void) diff --git a/arch/arm/mach-mxs/imx_lcd_clk.c b/arch/arm/mach-mxs/imx_lcd_clk.c deleted file mode 100644 index 455dfcb..0000000 --- a/arch/arm/mach-mxs/imx_lcd_clk.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * (C) Copyright 2010 Juergen Beisert - 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. - */ - -#include -#include -#include -#include -#include - -#ifdef CONFIG_ARCH_IMX23 - -# define HW_CLKCTRL_DIS_LCDIF 0x060 -# define CLKCTRL_DIS_LCDIF_GATE (1 << 31) -# define CLKCTRL_DIS_LCDIF_BUSY (1 << 29) -# define MASK_DIS_LCDIF_DIV 0xfff -# define SET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV) -# define GET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV) - -# define HW_CLKCTRL_FRAC 0xf0 -# define MASK_PIXFRAC 0x3f -# define GET_PIXFRAC(x) (((x) >> 16) & MASK_PIXFRAC) -# define SET_PIXFRAC(x) (((x) & MASK_PIXFRAC) << 16) -# define CLKCTRL_FRAC_CLKGATEPIX (1 << 23) - -# define HW_CLKCTRL_CLKSEQ 0x110 -# define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 1) - -#endif - -#ifdef CONFIG_ARCH_IMX28 - -# define HW_CLKCTRL_DIS_LCDIF 0x120 -# define CLKCTRL_DIS_LCDIF_GATE (1 << 31) -# define CLKCTRL_DIS_LCDIF_BUSY (1 << 29) -# define MASK_DIS_LCDIF_DIV 0x1fff -# define SET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV) -# define GET_DIS_LCDIF_DIV(x) ((x) & MASK_DIS_LCDIF_DIV) - -/* note: On i.MX28 this is called 'FRAC1' */ -# define HW_CLKCTRL_FRAC 0x1c0 -# define MASK_PIXFRAC 0x3f -# define GET_PIXFRAC(x) ((x) & MASK_PIXFRAC) -# define SET_PIXFRAC(x) ((x) & MASK_PIXFRAC) -# define CLKCTRL_FRAC_CLKGATEPIX (1 << 7) - -# define HW_CLKCTRL_CLKSEQ 0x1d0 -# define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 14) - -#endif - -unsigned imx_get_lcdifclk(void) -{ - unsigned rate = (imx_get_mpllclk() / 1000) * 18U; - unsigned div; - - div = GET_PIXFRAC(readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC)); - if (div != 0U) { - rate /= div; - div = GET_DIS_LCDIF_DIV(readl(IMX_CCM_BASE + - HW_CLKCTRL_DIS_LCDIF)); - if (div != 0U) - rate /= div; - else - pr_debug("LCDIF clock has divisor 0!\n"); - } else - pr_debug("LCDIF clock has frac divisor 0!\n"); - - return rate * 1000; -} - -/* - * The source of the pixel clock can be the external 24 MHz crystal or the - * internal PLL running at 480 MHz. In order to support at least VGA sized - * displays/resolutions this routine forces the PLL as the clock source. - */ -unsigned imx_set_lcdifclk(unsigned nc) -{ - unsigned frac, best_frac = 0, div, best_div = 0, result; - int delta, best_delta = 0xffffff; - unsigned i, parent_rate = imx_get_mpllclk() / 1000; - uint32_t reg; - -#define SH_DIV(NOM, DEN, LSH) ((((NOM) / (DEN)) << (LSH)) + \ - DIV_ROUND_CLOSEST(((NOM) % (DEN)) << (LSH), DEN)) -#define SHIFT 4 - - nc /= 1000; - nc <<= SHIFT; - - for (frac = 18; frac <= 35; ++frac) { - for (div = 1; div <= 255; ++div) { - result = DIV_ROUND_CLOSEST(parent_rate * - SH_DIV(18U, frac, SHIFT), div); - delta = nc - result; - if (abs(delta) < abs(best_delta)) { - best_delta = delta; - best_frac = frac; - best_div = div; - } - } - } - - if (best_delta == 0xffffff) { - pr_debug("Unable to match the pixelclock\n"); - return 0; - } - - pr_debug("Programming PFD=%u,DIV=%u ref_pix=%u MHz PIXCLK=%u kHz\n", - best_frac, best_div, 480 * 18 / best_frac, - 480000 * 18 / best_frac / best_div); - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC); - reg &= ~SET_PIXFRAC(MASK_PIXFRAC); - reg |= SET_PIXFRAC(best_frac); - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_FRAC); - writel(reg & ~CLKCTRL_FRAC_CLKGATEPIX, IMX_CCM_BASE + HW_CLKCTRL_FRAC); - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF) & ~MASK_DIS_LCDIF_DIV; - reg &= ~CLKCTRL_DIS_LCDIF_GATE; - reg |= SET_DIS_LCDIF_DIV(best_div); - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF); - - /* Wait for divider update */ - for (i = 0; i < 10000; i++) { - if (!(readl(IMX_CCM_BASE + HW_CLKCTRL_DIS_LCDIF) & - CLKCTRL_DIS_LCDIF_BUSY)) - break; - } - - if (i >= 10000) { - pr_debug("Setting LCD clock failed\n"); - return 0; - } - - writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF, - IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_CLR); - - return imx_get_lcdifclk(); -} diff --git a/arch/arm/mach-mxs/include/mach/clock-imx23.h b/arch/arm/mach-mxs/include/mach/clock-imx23.h deleted file mode 100644 index 6507792..0000000 --- a/arch/arm/mach-mxs/include/mach/clock-imx23.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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. - */ - -#ifndef MACH_CLOCK_IMX23_H -# define MACH_CLOCK_IMX23_H - -unsigned imx_get_mpllclk(void); -unsigned imx_get_emiclk(void); -unsigned imx_get_ioclk(void); -unsigned imx_get_armclk(void); -unsigned imx_get_hclk(void); -unsigned imx_set_hclk(unsigned); -unsigned imx_get_xclk(void); -unsigned imx_get_sspclk(unsigned); -unsigned imx_set_sspclk(unsigned, unsigned, int); -unsigned imx_set_ioclk(unsigned); -unsigned imx_set_lcdifclk(unsigned); -unsigned imx_get_lcdifclk(void); -void imx_enable_nandclk(void); - -#endif /* MACH_CLOCK_IMX23_H */ diff --git a/arch/arm/mach-mxs/include/mach/clock-imx28.h b/arch/arm/mach-mxs/include/mach/clock-imx28.h deleted file mode 100644 index 0604f0a..0000000 --- a/arch/arm/mach-mxs/include/mach/clock-imx28.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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. - */ - -#ifndef MACH_CLOCK_IMX28_H -#define MACH_CLOCK_IMX28_H - -unsigned imx_get_mpllclk(void); -unsigned imx_get_emiclk(void); -unsigned imx_get_ioclk(unsigned); -unsigned imx_get_armclk(void); -unsigned imx_get_hclk(void); -unsigned imx_set_hclk(unsigned); -unsigned imx_get_xclk(void); -unsigned imx_get_sspclk(unsigned); -unsigned imx_set_sspclk(unsigned, unsigned, int); -unsigned imx_set_ioclk(unsigned, unsigned); -unsigned imx_set_lcdifclk(unsigned); -unsigned imx_get_lcdifclk(void); -unsigned imx_get_fecclk(void); -void imx_enable_enetclk(void); -void imx_enable_nandclk(void); - -#endif /* MACH_CLOCK_IMX28_H */ - diff --git a/arch/arm/mach-mxs/include/mach/clock.h b/arch/arm/mach-mxs/include/mach/clock.h index 367297b..adbc330 100644 --- a/arch/arm/mach-mxs/include/mach/clock.h +++ b/arch/arm/mach-mxs/include/mach/clock.h @@ -16,11 +16,4 @@ #ifndef __MACH_CLOCK_H # define __MACH_CLOCK_H -#if defined CONFIG_ARCH_IMX23 -# include -#endif -#if defined CONFIG_ARCH_IMX28 -# include -#endif - #endif /* __MACH_CLOCK_H */ diff --git a/arch/arm/mach-mxs/ocotp.c b/arch/arm/mach-mxs/ocotp.c index dd98446..6bfa3e2 100644 --- a/arch/arm/mach-mxs/ocotp.c +++ b/arch/arm/mach-mxs/ocotp.c @@ -21,11 +21,12 @@ #include #include #include +#include +#include #include #include #include -#include #include #define DRIVERNAME "ocotp" @@ -45,6 +46,7 @@ struct cdev cdev; void __iomem *base; unsigned int write_enable; + struct clk *clk; }; static int mxs_ocotp_wait_busy(struct ocotp_priv *priv) @@ -131,10 +133,10 @@ work_buf[offset - aligned_offset + i] |= ((u8 *)buf)[i]; /* prepare system for OTP write */ - old_hclk = imx_get_hclk(); + old_hclk = clk_get_rate(priv->clk); old_vddio = imx_get_vddio(); - imx_set_hclk(24000000); + clk_set_rate(priv->clk, 24000000); imx_set_vddio(2800000); writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_CLR); @@ -162,7 +164,7 @@ restore_system: imx_set_vddio(old_vddio); - imx_set_hclk(old_hclk); + clk_set_rate(priv->clk, old_hclk); free_mem: free(work_buf); @@ -180,6 +182,9 @@ struct ocotp_priv *priv = xzalloc(sizeof (*priv)); priv->base = dev_request_mem_region(dev, 0); + priv->clk = clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); priv->cdev.dev = dev; priv->cdev.ops = &mxs_ocotp_ops; priv->cdev.priv = priv; diff --git a/arch/arm/mach-mxs/soc-imx23.c b/arch/arm/mach-mxs/soc-imx23.c index 6819b3c..4e45064 100644 --- a/arch/arm/mach-mxs/soc-imx23.c +++ b/arch/arm/mach-mxs/soc-imx23.c @@ -35,3 +35,12 @@ /*NOTREACHED*/ } EXPORT_SYMBOL(reset_cpu); + +static int imx23_devices_init(void) +{ + + add_generic_device("imx23-clkctrl", 0, NULL, IMX_CCM_BASE, 0x100, IORESOURCE_MEM, NULL); + + return 0; +} +postcore_initcall(imx23_devices_init); diff --git a/arch/arm/mach-mxs/soc-imx28.c b/arch/arm/mach-mxs/soc-imx28.c index ed931af..426f8ac 100644 --- a/arch/arm/mach-mxs/soc-imx28.c +++ b/arch/arm/mach-mxs/soc-imx28.c @@ -53,3 +53,12 @@ return 0; } postcore_initcall(imx28_init); + +static int imx28_devices_init(void) +{ + + add_generic_device("imx28-clkctrl", 0, NULL, IMX_CCM_BASE, 0x100, IORESOURCE_MEM, NULL); + + return 0; +} +postcore_initcall(imx28_devices_init); diff --git a/arch/arm/mach-mxs/speed-imx23.c b/arch/arm/mach-mxs/speed-imx23.c deleted file mode 100644 index 14885d5..0000000 --- a/arch/arm/mach-mxs/speed-imx23.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * (C) Copyright 2010 Juergen Beisert - Pengutronix - * - * This code is based partially on code of: - * - * (c) 2008 Embedded Alley Solutions, Inc. - * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include - -#define HW_CLKCTRL_PLLCTRL0 0x000 -#define HW_CLKCTRL_PLLCTRL1 0x010 -#define HW_CLKCTRL_CPU 0x20 -# define GET_CPU_XTAL_DIV(x) (((x) >> 16) & 0x3ff) -# define GET_CPU_PLL_DIV(x) ((x) & 0x3f) -#define HW_CLKCTRL_HBUS 0x30 -#define HW_CLKCTRL_XBUS 0x40 -#define HW_CLKCTRL_XTAL 0x050 -#define HW_CLKCTRL_PIX 0x060 -/* note: no set/clear register! */ -#define HW_CLKCTRL_SSP 0x070 -/* note: no set/clear register! */ -# define CLKCTRL_SSP_CLKGATE (1 << 31) -# define CLKCTRL_SSP_BUSY (1 << 29) -# define CLKCTRL_SSP_DIV_MASK 0x1ff -# define GET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK) -# define SET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK) -#define HW_CLKCTRL_GPMI 0x080 -# define CLKCTRL_GPMI_CLKGATE (1 << 31) -# define CLKCTRL_GPMI_DIV_MASK 0x3ff -/* note: no set/clear register! */ -#define HW_CLKCTRL_SPDIF 0x090 -/* note: no set/clear register! */ -#define HW_CLKCTRL_EMI 0xa0 -/* note: no set/clear register! */ -# define CLKCTRL_EMI_CLKGATE (1 << 31) -# define GET_EMI_XTAL_DIV(x) (((x) >> 8) & 0xf) -# define GET_EMI_PLL_DIV(x) ((x) & 0x3f) -#define HW_CLKCTRL_SAIF 0x0c0 -#define HW_CLKCTRL_TV 0x0d0 -#define HW_CLKCTRL_ETM 0x0e0 -#define HW_CLKCTRL_FRAC 0xf0 -# define CLKCTRL_FRAC_CLKGATEIO (1 << 31) -# define GET_IOFRAC(x) (((x) >> 24) & 0x3f) -# define SET_IOFRAC(x) (((x) & 0x3f) << 24) -# define CLKCTRL_FRAC_CLKGATEPIX (1 << 23) -# define GET_PIXFRAC(x) (((x) >> 16) & 0x3f) -# define CLKCTRL_FRAC_CLKGATEEMI (1 << 15) -# define GET_EMIFRAC(x) (((x) >> 8) & 0x3f) -# define CLKCTRL_FRAC_CLKGATECPU (1 << 7) -# define GET_CPUFRAC(x) ((x) & 0x3f) -#define HW_CLKCTRL_FRAC1 0x100 -#define HW_CLKCTRL_CLKSEQ 0x110 -# define CLKCTRL_CLKSEQ_BYPASS_ETM (1 << 8) -# define CLKCTRL_CLKSEQ_BYPASS_CPU (1 << 7) -# define CLKCTRL_CLKSEQ_BYPASS_EMI (1 << 6) -# define CLKCTRL_CLKSEQ_BYPASS_SSP (1 << 5) -# define CLKCTRL_CLKSEQ_BYPASS_GPMI (1 << 4) -#define HW_CLKCTRL_RESET 0x120 -#define HW_CLKCTRL_STATUS 0x130 -#define HW_CLKCTRL_VERSION 0x140 - -unsigned imx_get_mpllclk(void) -{ - /* the main PLL runs at 480 MHz */ - return 480000000; -} - -unsigned imx_get_xtalclk(void) -{ - /* the external reference runs at 24 MHz */ - return 24000000; -} - -/* used for the SDRAM controller */ -unsigned imx_get_emiclk(void) -{ - uint32_t reg; - unsigned rate; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_EMI) & CLKCTRL_EMI_CLKGATE) - return 0U; /* clock is off */ - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_EMI) - return imx_get_xtalclk() / GET_EMI_XTAL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_EMI)); - - rate = imx_get_mpllclk() / 1000; - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC); - if (!(reg & CLKCTRL_FRAC_CLKGATEEMI)) { - rate *= 18U; - rate /= GET_EMIFRAC(reg); - } - - return (rate / GET_EMI_PLL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_EMI))) - * 1000; -} - -/* - * Source of ssp, gpmi, ir - */ -unsigned imx_get_ioclk(void) -{ - uint32_t reg; - unsigned rate = imx_get_mpllclk() / 1000; - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC); - if (reg & CLKCTRL_FRAC_CLKGATEIO) - return 0U; /* clock is off */ - - rate *= 18U; - rate /= GET_IOFRAC(reg); - return rate * 1000; -} - -/** - * Setup a new frequency to the IOCLK domain. - * @param nc New frequency in [Hz] - * - * The FRAC divider for the IOCLK must be between 18 (* 18/18) and 35 (* 18/35) - */ -unsigned imx_set_ioclk(unsigned nc) -{ - uint32_t reg; - unsigned div; - - nc /= 1000; - div = (imx_get_mpllclk() / 1000) * 18; - div = DIV_ROUND_CLOSEST(div, nc); - if (div > 0x3f) - div = 0x3f; - /* mask the current settings */ - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC) & ~(SET_IOFRAC(0x3f)); - writel(reg | SET_IOFRAC(div), IMX_CCM_BASE + HW_CLKCTRL_FRAC); - /* enable the IO clock at its new frequency */ - writel(CLKCTRL_FRAC_CLKGATEIO, IMX_CCM_BASE + HW_CLKCTRL_FRAC + 8); - - return imx_get_ioclk(); -} - -/* this is CPU core clock */ -unsigned imx_get_armclk(void) -{ - uint32_t reg; - unsigned rate; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_CPU) - return imx_get_xtalclk() / GET_CPU_XTAL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_CPU)); - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC); - if (reg & CLKCTRL_FRAC_CLKGATECPU) - return 0U; /* should not possible, shouldn't it? */ - - rate = imx_get_mpllclk() / 1000; - rate *= 18U; - rate /= GET_CPUFRAC(reg); - - return (rate / GET_CPU_PLL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_CPU))) - * 1000; -} - -/* this is the AHB and APBH bus clock */ -unsigned imx_get_hclk(void) -{ - unsigned rate = imx_get_armclk() / 1000; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x20) { - rate *= readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f; - rate = DIV_ROUND_UP(rate, 32); - } else - rate = DIV_ROUND_UP(rate, - readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f); - return rate * 1000; -} - -unsigned imx_set_hclk(unsigned nc) -{ - unsigned root_rate = imx_get_armclk(); - unsigned reg, div; - - div = DIV_ROUND_UP(root_rate, nc); - if ((div == 0) || (div >= 32)) - return 0; - - if ((root_rate < nc) && (root_rate == 64000000)) - div = 3; - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & ~0x3f; - writel(reg | div, IMX_CCM_BASE + HW_CLKCTRL_HBUS); - - while (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & (1 << 31)) - ; - - return imx_get_hclk(); -} - -/* - * Source of UART, debug UART, audio, PWM, dri, timer, digctl - */ -unsigned imx_get_xclk(void) -{ - unsigned rate = imx_get_xtalclk(); /* runs from the 24 MHz crystal reference */ - - return rate / (readl(IMX_CCM_BASE + HW_CLKCTRL_XBUS) & 0x3ff); -} - -/* 'index' gets ignored on i.MX23 */ -unsigned imx_get_sspclk(unsigned index) -{ - unsigned rate; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_SSP) & CLKCTRL_SSP_CLKGATE) - return 0U; /* clock is off */ - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_SSP) - rate = imx_get_xtalclk(); - else - rate = imx_get_ioclk(); - - return rate / GET_SSP_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_SSP)); -} - -/** - * @param index Unit index (ignored on i.MX23) - * @param nc New frequency in [Hz] - * @param high != 0 if ioclk should be the source - * @return The new possible frequency in [kHz] - */ -unsigned imx_set_sspclk(unsigned index, unsigned nc, int high) -{ - uint32_t reg; - unsigned ssp_div; - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_SSP) & ~CLKCTRL_SSP_CLKGATE; - /* Datasheet says: Do not change the DIV setting if the clock is off */ - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_SSP); - /* Wait while clock is gated */ - while (readl(IMX_CCM_BASE + HW_CLKCTRL_SSP) & CLKCTRL_SSP_CLKGATE) - ; - - if (high) - ssp_div = imx_get_ioclk(); - else - ssp_div = imx_get_xtalclk(); - - if (nc > ssp_div) { - printf("Cannot setup SSP unit clock to %u Hz, base clock is only %u Hz\n", nc, ssp_div); - ssp_div = 1U; - } else { - ssp_div = DIV_ROUND_UP(ssp_div, nc); - if (ssp_div > CLKCTRL_SSP_DIV_MASK) - ssp_div = CLKCTRL_SSP_DIV_MASK; - } - - /* Set new divider value */ - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_SSP) & ~CLKCTRL_SSP_DIV_MASK; - writel(reg | SET_SSP_DIV(ssp_div), IMX_CCM_BASE + HW_CLKCTRL_SSP); - - /* Wait until new divider value is set */ - while (readl(IMX_CCM_BASE + HW_CLKCTRL_SSP) & CLKCTRL_SSP_BUSY) - ; - - if (high) - /* switch to ioclock */ - writel(CLKCTRL_CLKSEQ_BYPASS_SSP, IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + 8); - else - /* switch to 24 MHz crystal */ - writel(CLKCTRL_CLKSEQ_BYPASS_SSP, IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + 4); - - return imx_get_sspclk(index); -} - -void imx_enable_nandclk(void) -{ - uint32_t reg; - - /* Clear bypass bit; refman says clear, but fsl-code does set. Hooray! */ - writel(CLKCTRL_CLKSEQ_BYPASS_GPMI, - IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_SET); - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_GPMI) & ~CLKCTRL_GPMI_CLKGATE; - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI); - udelay(1000); - /* Initialize DIV to 1 */ - reg &= ~CLKCTRL_GPMI_DIV_MASK; - reg |= 1; - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI); -} - -void imx_dump_clocks(void) -{ - printf("mpll: %10u kHz\n", imx_get_mpllclk() / 1000); - printf("arm: %10u kHz\n", imx_get_armclk() / 1000); - printf("ioclk: %10u kHz\n", imx_get_ioclk() / 1000); - printf("emiclk: %10u kHz\n", imx_get_emiclk() / 1000); - printf("hclk: %10u kHz\n", imx_get_hclk() / 1000); - printf("xclk: %10u kHz\n", imx_get_xclk() / 1000); - printf("ssp: %10u kHz\n", imx_get_sspclk(0) / 1000); -} diff --git a/arch/arm/mach-mxs/speed-imx28.c b/arch/arm/mach-mxs/speed-imx28.c deleted file mode 100644 index 2cab42d..0000000 --- a/arch/arm/mach-mxs/speed-imx28.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * (C) Copyright 2010 Juergen Beisert - Pengutronix - * - * This code is based partially on code that has: - * - * (c) 2008 Embedded Alley Solutions, Inc. - * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. - * - * 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. - */ -#include -#include -#include -#include -#include -#include - -#define HW_CLKCTRL_PLL0CTRL0 0x000 -#define HW_CLKCTRL_PLL0CTRL1 0x010 -#define HW_CLKCTRL_PLL1CTRL0 0x020 -#define HW_CLKCTRL_PLL1CTRL1 0x030 -#define HW_CLKCTRL_PLL2CTRL0 0x040 -# define CLKCTRL_PLL2CTRL0_CLKGATE (1 << 31) -# define CLKCTRL_PLL2CTRL0_POWER (1 << 23) -#define HW_CLKCTRL_CPU 0x50 -# define GET_CPU_XTAL_DIV(x) (((x) >> 16) & 0x3ff) -# define GET_CPU_PLL_DIV(x) ((x) & 0x3f) -#define HW_CLKCTRL_HBUS 0x60 -#define HW_CLKCTRL_XBUS 0x70 -#define HW_CLKCTRL_XTAL 0x080 -#define HW_CLKCTRL_SSP0 0x090 -#define HW_CLKCTRL_SSP1 0x0a0 -#define HW_CLKCTRL_SSP2 0x0b0 -#define HW_CLKCTRL_SSP3 0x0c0 -/* note: no set/clear register! */ -# define CLKCTRL_SSP_CLKGATE (1 << 31) -# define CLKCTRL_SSP_BUSY (1 << 29) -# define CLKCTRL_SSP_DIV_FRAC_EN (1 << 9) -# define CLKCTRL_SSP_DIV_MASK 0x1ff -# define GET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK) -# define SET_SSP_DIV(x) ((x) & CLKCTRL_SSP_DIV_MASK) -#define HW_CLKCTRL_GPMI 0x0d0 -# define CLKCTRL_GPMI_CLKGATE (1 << 31) -# define CLKCTRL_GPMI_DIV_MASK 0x3ff -/* note: no set/clear register! */ -#define HW_CLKCTRL_SPDIF 0x0e0 -/* note: no set/clear register! */ -#define HW_CLKCTRL_EMI 0xf0 -/* note: no set/clear register! */ -# define CLKCTRL_EMI_CLKGATE (1 << 31) -# define GET_EMI_XTAL_DIV(x) (((x) >> 8) & 0xf) -# define GET_EMI_PLL_DIV(x) ((x) & 0x3f) -#define HW_CLKCTRL_SAIF0 0x100 -#define HW_CLKCTRL_SAIF1 0x110 -#define HW_CLKCTRL_DIS_LCDIF 0x120 -# define CLKCTRL_DIS_LCDIF_GATE (1 << 31) -# define CLKCTRL_DIS_LCDIF_BUSY (1 << 29) -# define SET_DIS_LCDIF_DIV(x) ((x) & 0x1fff) -# define GET_DIS_LCDIF_DIV(x) ((x) & 0x1fff) -#define HW_CLKCTRL_ETM 0x130 -#define HW_CLKCTRL_ENET 0x140 -# define SET_CLKCTRL_ENET_DIV(x) (((x) & 0x3f) << 21) -# define SET_CLKCTRL_ENET_SEL(x) (((x) & 0x3) << 19) -# define CLKCTRL_ENET_CLK_OUT_EN (1 << 18) -#define HW_CLKCTRL_HSADC 0x150 -#define HW_CLKCTRL_FLEXCAN 0x160 -#define HW_CLKCTRL_FRAC0 0x1b0 -# define CLKCTRL_FRAC_CLKGATEIO0 (1 << 31) -# define GET_IO0FRAC(x) (((x) >> 24) & 0x3f) -# define SET_IO0FRAC(x) (((x) & 0x3f) << 24) -# define CLKCTRL_FRAC_CLKGATEIO1 (1 << 23) -# define GET_IO1FRAC(x) (((x) >> 16) & 0x3f) -# define SET_IO1FRAC(x) (((x) & 0x3f) << 16) -# define CLKCTRL_FRAC_CLKGATEEMI (1 << 15) -# define GET_EMIFRAC(x) (((x) >> 8) & 0x3f) -# define CLKCTRL_FRAC_CLKGATECPU (1 << 7) -# define GET_CPUFRAC(x) ((x) & 0x3f) -#define HW_CLKCTRL_FRAC1 0x1c0 -# define CLKCTRL_FRAC_CLKGATEGPMI (1 << 23) -# define GET_GPMIFRAC(x) (((x) >> 16) & 0x3f) -# define CLKCTRL_FRAC_CLKGATEHSADC (1 << 15) -# define GET_HSADCFRAC(x) (((x) >> 8) & 0x3f) -# define CLKCTRL_FRAC_CLKGATEPIX (1 << 7) -# define GET_PIXFRAC(x) ((x) & 0x3f) -# define SET_PIXFRAC(x) ((x) & 0x3f) -#define HW_CLKCTRL_CLKSEQ 0x1d0 -# define CLKCTRL_CLKSEQ_BYPASS_CPU (1 << 18) -# define CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF (1 << 14) -# define CLKCTRL_CLKSEQ_BYPASS_ETM (1 << 8) -# define CLKCTRL_CLKSEQ_BYPASS_EMI (1 << 7) -# define CLKCTRL_CLKSEQ_BYPASS_SSP3 (1 << 6) -# define CLKCTRL_CLKSEQ_BYPASS_SSP2 (1 << 5) -# define CLKCTRL_CLKSEQ_BYPASS_SSP1 (1 << 4) -# define CLKCTRL_CLKSEQ_BYPASS_SSP0 (1 << 3) -# define CLKCTRL_CLKSEQ_BYPASS_GPMI (1 << 2) -# define CLKCTRL_CLKSEQ_BYPASS_SAIF1 (1 << 1) -# define CLKCTRL_CLKSEQ_BYPASS_SAIF0 (1 << 0) -#define HW_CLKCTRL_RESET 0x1e0 -#define HW_CLKCTRL_STATUS 0x1f0 -#define HW_CLKCTRL_VERSION 0x200 - -unsigned imx_get_mpllclk(void) -{ - /* the main PLL runs at 480 MHz */ - return 480000000; -} - -unsigned imx_get_xtalclk(void) -{ - /* the external reference runs at 24 MHz */ - return 24000000; -} - -unsigned imx_get_fecclk(void) -{ - return imx_get_hclk(); -} - - -/* used for the SDRAM controller */ -unsigned imx_get_emiclk(void) -{ - uint32_t reg; - unsigned rate; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_EMI) & CLKCTRL_EMI_CLKGATE) - return 0; /* clock is off */ - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_EMI) - return imx_get_xtalclk() / - GET_EMI_XTAL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_EMI)); - - rate = imx_get_mpllclk() / 1000; - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0); - if (!(reg & CLKCTRL_FRAC_CLKGATEEMI)) { - rate *= 18; - rate /= GET_EMIFRAC(reg); - } - - return (rate / GET_EMI_PLL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_EMI))) - * 1000; -} - -/* - * Source of ssp, gpmi, ir - * @param index 0 or 1 for ioclk0 or ioclock1 - */ -unsigned imx_get_ioclk(unsigned index) -{ - uint32_t reg; - unsigned rate = imx_get_mpllclk() / 1000; - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0); - switch (index) { - case 0: - if (reg & CLKCTRL_FRAC_CLKGATEIO0) - return 0; /* clock is off */ - - rate *= 18; - rate /= GET_IO0FRAC(reg); - break; - case 1: - if (reg & CLKCTRL_FRAC_CLKGATEIO1) - return 0; /* clock is off */ - - rate *= 18; - rate /= GET_IO1FRAC(reg); - break; - } - - return rate * 1000; -} - -/** - * Setup a new frequency to the IOCLK domain. - * @param index 0 or 1 for ioclk0 or ioclock1 - * @param nc New frequency in [Hz] - * - * The FRAC divider for the IOCLK must be between 18 (* 18/18) and 35 (* 18/35) - * - * ioclock0 is the shared clock source of SSP0/SSP1, ioclock1 the shared clock - * source of SSP2/SSP3 - */ -unsigned imx_set_ioclk(unsigned index, unsigned nc) -{ - uint32_t reg; - unsigned div; - - nc /= 1000; - div = (imx_get_mpllclk() / 1000) * 18; - div = DIV_ROUND_CLOSEST(div, nc); - if (div > 0x3f) - div = 0x3f; - - switch (index) { - case 0: - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0) & - ~(SET_IO0FRAC(0x3f)); - /* mask the current settings */ - writel(reg | SET_IO0FRAC(div), IMX_CCM_BASE + HW_CLKCTRL_FRAC0); - /* enable the IO clock at its new frequency */ - writel(CLKCTRL_FRAC_CLKGATEIO0, - IMX_CCM_BASE + HW_CLKCTRL_FRAC0 + BIT_CLR); - break; - case 1: - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0) & - ~(SET_IO1FRAC(0x3f)); - /* mask the current settings */ - writel(reg | SET_IO1FRAC(div), IMX_CCM_BASE + HW_CLKCTRL_FRAC0); - /* enable the IO clock at its new frequency */ - writel(CLKCTRL_FRAC_CLKGATEIO1, - IMX_CCM_BASE + HW_CLKCTRL_FRAC0 + BIT_CLR); - break; - } - - return imx_get_ioclk(index); -} - -/* this is CPU core clock */ -unsigned imx_get_armclk(void) -{ - uint32_t reg; - unsigned rate; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & CLKCTRL_CLKSEQ_BYPASS_CPU) - return imx_get_xtalclk() / - GET_CPU_XTAL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_CPU)); - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_FRAC0); - if (reg & CLKCTRL_FRAC_CLKGATECPU) - return 0; /* should not possible, shouldn't it? */ - - rate = (imx_get_mpllclk() / 1000) * 18; - rate /= GET_CPUFRAC(reg); - - return (rate / GET_CPU_PLL_DIV(readl(IMX_CCM_BASE + HW_CLKCTRL_CPU))) - * 1000; -} - -/* this is the AHB and APBH bus clock */ -unsigned imx_get_hclk(void) -{ - unsigned rate = imx_get_armclk() / 1000; - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x20) { - rate *= readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f; - rate = DIV_ROUND_UP(rate, 32); - } else - rate = DIV_ROUND_UP(rate, - readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f); - return rate * 1000; -} - -unsigned imx_set_hclk(unsigned nc) -{ - unsigned root_rate = imx_get_armclk(); - unsigned reg, div; - - div = DIV_ROUND_UP(root_rate, nc); - if ((div == 0) || (div >= 32)) - return 0; - - if ((root_rate < nc) && (root_rate == 64000000)) - div = 3; - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & ~0x3f; - writel(reg | div, IMX_CCM_BASE + HW_CLKCTRL_HBUS); - - while (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & (1 << 31)) - ; - - return imx_get_hclk(); -} - -/* - * Source of UART, debug UART, audio, PWM, dri, timer, digctl - */ -unsigned imx_get_xclk(void) -{ - /* runs from the 24 MHz crystal reference */ - unsigned rate = imx_get_xtalclk(); - - return rate / (readl(IMX_CCM_BASE + HW_CLKCTRL_XBUS) & 0x3ff); -} - -/** - * @param index The SSP unit (0...3) - */ -unsigned imx_get_sspclk(unsigned index) -{ - unsigned rate, offset, shift, ioclk_index; - - if (index > 3) { - pr_debug("Unknown SSP unit: %u\n", index); - return 0; - } - - ioclk_index = index >> 1; - - offset = HW_CLKCTRL_SSP0 + (0x10 * index); - shift = CLKCTRL_CLKSEQ_BYPASS_SSP0 << index; - - if (readl(IMX_CCM_BASE + offset) & CLKCTRL_SSP_CLKGATE) - return 0; /* clock is off */ - - if (readl(IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ) & shift) - rate = imx_get_xtalclk(); - else - rate = imx_get_ioclk(ioclk_index); - - return rate / GET_SSP_DIV(readl(IMX_CCM_BASE + offset)); -} - -/** - * @param index The SSP unit (0...3) - * @param nc New frequency in [Hz] - * @param high != 0 if ioclk should be the source - * @return The new possible frequency - */ -unsigned imx_set_sspclk(unsigned index, unsigned nc, int high) -{ - uint32_t reg; - unsigned ssp_div, offset, shift, ioclk_index; - - if (index > 3) { - pr_debug("Unknown SSP unit: %u\n", index); - return 0; - } - - ioclk_index = index >> 1; - - offset = HW_CLKCTRL_SSP0 + (0x10 * index); - shift = CLKCTRL_CLKSEQ_BYPASS_SSP0 << index; - - reg = readl(IMX_CCM_BASE + offset) & ~CLKCTRL_SSP_CLKGATE; - /* Datasheet says: Do not change the DIV setting if the clock is off */ - writel(reg, IMX_CCM_BASE + offset); - /* Wait while clock is gated */ - while (readl(IMX_CCM_BASE + offset) & CLKCTRL_SSP_CLKGATE) - ; - - if (high) - ssp_div = imx_get_ioclk(ioclk_index); - else - ssp_div = imx_get_xtalclk(); - - if (nc > ssp_div) { - printf("Cannot setup SSP unit clock to %u kHz, base clock is " - "only %u kHz\n", nc, ssp_div); - ssp_div = 1; - } else { - ssp_div = DIV_ROUND_UP(ssp_div, nc); - if (ssp_div > CLKCTRL_SSP_DIV_MASK) - ssp_div = CLKCTRL_SSP_DIV_MASK; - } - - /* Set new divider value */ - reg = readl(IMX_CCM_BASE + offset) & ~CLKCTRL_SSP_DIV_MASK; - writel(reg | SET_SSP_DIV(ssp_div), IMX_CCM_BASE + offset); - - /* Wait until new divider value is set */ - while (readl(IMX_CCM_BASE + offset) & CLKCTRL_SSP_BUSY) - ; - - if (high) - /* switch to ioclock */ - writel(shift, IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_CLR); - else - /* switch to 24 MHz crystal */ - writel(shift, IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_SET); - - return imx_get_sspclk(index); -} - -void imx_enable_enetclk(void) -{ - uint32_t reg; - - /* wake up main enet PLL */ - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_PLL2CTRL0); - if (!(reg & CLKCTRL_PLL2CTRL0_POWER)) { - reg |= CLKCTRL_PLL2CTRL0_POWER; - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_PLL2CTRL0); - udelay(50); /* wait until this PLL locks */ - } - reg &= ~CLKCTRL_PLL2CTRL0_CLKGATE; - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_PLL2CTRL0); - - writel(SET_CLKCTRL_ENET_DIV(1) | SET_CLKCTRL_ENET_SEL(0) | - CLKCTRL_ENET_CLK_OUT_EN, /* FIXME may be platform specific */ - IMX_CCM_BASE + HW_CLKCTRL_ENET); -} - -void imx_enable_nandclk(void) -{ - uint32_t reg; - - /* Clear bypass bit; refman says clear, but fsl-code does set. Hooray! */ - writel(CLKCTRL_CLKSEQ_BYPASS_GPMI, - IMX_CCM_BASE + HW_CLKCTRL_CLKSEQ + BIT_SET); - - reg = readl(IMX_CCM_BASE + HW_CLKCTRL_GPMI) & ~CLKCTRL_GPMI_CLKGATE; - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI); - udelay(1000); - /* Initialize DIV to 1 */ - reg &= ~CLKCTRL_GPMI_DIV_MASK; - reg |= 1; - writel(reg, IMX_CCM_BASE + HW_CLKCTRL_GPMI); -} - -void imx_dump_clocks(void) -{ - printf("mpll: %10u kHz\n", imx_get_mpllclk() / 1000); - printf("arm: %10u kHz\n", imx_get_armclk() / 1000); - printf("ioclk0: %10u kHz\n", imx_get_ioclk(0) / 1000); - printf("ioclk1: %10u kHz\n", imx_get_ioclk(1) / 1000); - printf("emiclk: %10u kHz\n", imx_get_emiclk() / 1000); - printf("hclk: %10u kHz\n", imx_get_hclk() / 1000); - printf("xclk: %10u kHz\n", imx_get_xclk() / 1000); - printf("ssp0: %10u kHz\n", imx_get_sspclk(0) / 1000); - printf("ssp1: %10u kHz\n", imx_get_sspclk(1) / 1000); - printf("ssp2: %10u kHz\n", imx_get_sspclk(2) / 1000); - printf("ssp3: %10u kHz\n", imx_get_sspclk(3) / 1000); -} diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 656b859..d6c923d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \ clk-mux.o clk-gate.o clk-divider-table.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o + +obj-$(CONFIG_ARCH_MXS) += mxs/ diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 58a7ea5..3bf8105 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -20,13 +20,12 @@ #include #include -struct clk_divider { - struct clk clk; - u8 shift; - u8 width; - void __iomem *reg; - const char *parent; -}; +static unsigned int clk_divider_maxdiv(struct clk_divider *div) +{ + if (div->flags & CLK_DIVIDER_ONE_BASED) + return (1 << div->width) - 1; + return 1 << div->width; +} static int clk_divider_set_rate(struct clk *clk, unsigned long rate, unsigned long parent_rate) @@ -40,11 +39,11 @@ rate = 1; divval = DIV_ROUND_UP(parent_rate, rate); + if (divval > clk_divider_maxdiv(div)) + divval = clk_divider_maxdiv(div); - if (divval > (1 << div->width)) - divval = 1 << (div->width); - - divval--; + if (!(div->flags & CLK_DIVIDER_ONE_BASED)) + divval--; val = readl(div->reg); val &= ~(((1 << div->width) - 1) << div->shift); @@ -63,7 +62,12 @@ val = readl(div->reg) >> div->shift; val &= (1 << div->width) - 1; - val++; + if (div->flags & CLK_DIVIDER_ONE_BASED) { + if (!val) + val++; + } else { + val++; + } return parent_rate / val; } @@ -96,3 +100,19 @@ return &div->clk; } + +struct clk *clk_divider_one_based(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width) +{ + struct clk_divider *div; + struct clk *clk; + + clk = clk_divider(name, parent, reg, shift, width); + if (IS_ERR(clk)) + return clk; + + div = container_of(clk, struct clk_divider, clk); + div->flags |= CLK_DIVIDER_ONE_BASED; + + return clk; +} diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index a455094..f632d85 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -25,6 +25,8 @@ void __iomem *reg; int shift; const char *parent; +#define CLK_GATE_INVERTED (1 << 0) + unsigned flags; }; static int clk_gate_enable(struct clk *clk) @@ -33,7 +35,12 @@ u32 val; val = readl(g->reg); - val |= 1 << g->shift; + + if (g->flags & CLK_GATE_INVERTED) + val &= ~(1 << g->shift); + else + val |= 1 << g->shift; + writel(val, g->reg); return 0; @@ -45,7 +52,12 @@ u32 val; val = readl(g->reg); - val &= ~(1 << g->shift); + + if (g->flags & CLK_GATE_INVERTED) + val |= 1 << g->shift; + else + val &= ~(1 << g->shift); + writel(val, g->reg); } @@ -57,9 +69,9 @@ val = readl(g->reg); if (val & (1 << g->shift)) - return 1; + return g->flags & CLK_GATE_INVERTED ? 0 : 1; else - return 0; + return g->flags & CLK_GATE_INVERTED ? 1 : 0; } struct clk_ops clk_gate_ops = { @@ -90,3 +102,20 @@ return &g->clk; } + +struct clk *clk_gate_inverted(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + struct clk *clk; + struct clk_gate *g; + + clk = clk_gate(name, parent, reg, shift); + if (IS_ERR(clk)) + return clk; + + g = container_of(clk, struct clk_gate, clk); + + g->flags = CLK_GATE_INVERTED; + + return clk; +} diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index a3def53..690a0c6 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -208,7 +208,7 @@ return 0; } -static int clk_is_enabled(struct clk *clk) +int clk_is_enabled(struct clk *clk) { int enabled; diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile new file mode 100644 index 0000000..fb4e5db --- /dev/null +++ b/drivers/clk/mxs/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_ARCH_MXS) += clk-ref.o clk-pll.o clk-frac.o clk-div.o +obj-$(CONFIG_DRIVER_VIDEO_STM) += clk-lcdif.o + +obj-$(CONFIG_ARCH_IMX23) += clk-imx23.o +obj-$(CONFIG_ARCH_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 0000000..e8dae25 --- /dev/null +++ b/drivers/clk/mxs/clk-div.c @@ -0,0 +1,112 @@ +/* + * 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 +#include +#include +#include + +#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 char *parent; + const struct clk_ops *ops; + void __iomem *reg; + u8 busy; +}; + +static inline struct clk_div *to_clk_div(struct clk *clk) +{ + struct clk_divider *divider = container_of(clk, struct clk_divider, clk); + + return container_of(divider, struct clk_div, divider); +} + +static unsigned long clk_div_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_div *div = to_clk_div(clk); + + return div->ops->recalc_rate(&div->divider.clk, parent_rate); +} + +static long clk_div_round_rate(struct clk *clk, unsigned long rate, + unsigned long *prate) +{ + struct clk_div *div = to_clk_div(clk); + + return div->ops->round_rate(&div->divider.clk, rate, prate); +} + +static int clk_div_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_div *div = to_clk_div(clk); + int ret; + + ret = div->ops->set_rate(&div->divider.clk, rate, parent_rate); + if (ret) + return ret; + + if (clk_is_enabled(clk)) + while (readl(div->reg) & 1 << div->busy); + + return 0; +} + +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; + int ret; + + div = xzalloc(sizeof(*div)); + if (!div) + return ERR_PTR(-ENOMEM); + + div->parent = parent_name; + div->divider.clk.name = name; + div->divider.clk.ops = &clk_div_ops; + div->divider.clk.parent_names = &div->parent; + div->divider.clk.num_parents = 1; + + 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->ops = &clk_divider_ops; + + ret = clk_register(&div->divider.clk); + if (ret) + return ERR_PTR(ret); + + return &div->divider.clk; +} diff --git a/drivers/clk/mxs/clk-frac.c b/drivers/clk/mxs/clk-frac.c new file mode 100644 index 0000000..7aa8504 --- /dev/null +++ b/drivers/clk/mxs/clk-frac.c @@ -0,0 +1,136 @@ +/* + * 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 +#include +#include +#include +#include + +#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 clk; + const char *parent; + void __iomem *reg; + u8 shift; + u8 width; + u8 busy; +}; + +#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, clk) + +static unsigned long clk_frac_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(clk); + u32 div; + + div = readl(frac->reg) >> frac->shift; + div &= (1 << frac->width) - 1; + + return (parent_rate >> frac->width) * div; +} + +static long clk_frac_round_rate(struct clk *clk, unsigned long rate, + unsigned long *prate) +{ + struct clk_frac *frac = to_clk_frac(clk); + 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 *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(clk); + 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; + + val = readl(frac->reg); + val &= ~(((1 << frac->width) - 1) << frac->shift); + val |= div << frac->shift; + writel(val, frac->reg); + + if (clk_is_enabled(clk)) + while (readl(frac->reg) & 1 << frac->busy); + + return 0; +} + +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; + int ret; + + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) + return ERR_PTR(-ENOMEM); + + frac->parent = parent_name; + frac->clk.name = name; + frac->clk.ops = &clk_frac_ops; + frac->clk.parent_names = &frac->parent; + frac->clk.num_parents = 1; + + frac->reg = reg; + frac->shift = shift; + frac->width = width; + + ret = clk_register(&frac->clk); + if (ret) + return ERR_PTR(ret); + + return &frac->clk; +} diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c new file mode 100644 index 0000000..4b15350 --- /dev/null +++ b/drivers/clk/mxs/clk-imx23.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2008 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define PLLCTRL0 (regs + 0x0000) +#define CPU (regs + 0x0020) +#define HBUS (regs + 0x0030) +#define XBUS (regs + 0x0040) +#define XTAL (regs + 0x0050) +#define PIX (regs + 0x0060) +#define SSP (regs + 0x0070) +#define GPMI (regs + 0x0080) +#define SPDIF (regs + 0x0090) +#define EMI (regs + 0x00a0) +#define SAIF (regs + 0x00c0) +#define TV (regs + 0x00d0) +#define ETM (regs + 0x00e0) +#define FRAC (regs + 0x00f0) +#define CLKSEQ (regs + 0x0110) + +static const char *sel_pll[] = { "pll", "ref_xtal", }; +static const char *sel_cpu[] = { "ref_cpu", "ref_xtal", }; +static const char *sel_pix[] = { "ref_pix", "ref_xtal", }; +static const char *sel_io[] = { "ref_io", "ref_xtal", }; +static const char *cpu_sels[] = { "cpu_pll", "cpu_xtal", }; +static const char *emi_sels[] = { "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_phy, lcdif_comp, + clk_max +}; + +static struct clk *clks[clk_max]; + +int __init mx23_clocks_init(void __iomem *regs) +{ + 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[lcdif_comp] = mxs_clk_lcdif("lcdif_comp", clks[ref_pix], + clks[lcdif_div], clks[lcdif]); + + clk_set_rate(clks[ref_io], 480000000); + clk_set_parent(clks[ssp_sel], clks[ref_io]); + clk_set_rate(clks[ssp_div], 96000000); + clk_set_parent(clks[lcdif_sel], clks[ref_pix]); + + clkdev_add_physbase(clks[ssp], IMX_SSP1_BASE, NULL); + clkdev_add_physbase(clks[ssp], IMX_SSP2_BASE, NULL); + clkdev_add_physbase(clks[xbus], IMX_DBGUART_BASE, NULL); + clkdev_add_physbase(clks[hbus], IMX_OCOTP_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART1_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART2_BASE, NULL); + clkdev_add_physbase(clks[gpmi], MXS_GPMI_BASE, NULL); + if (IS_ENABLED(CONFIG_DRIVER_VIDEO_STM)) + clkdev_add_physbase(clks[lcdif_comp], IMX_FB_BASE, NULL); + + return 0; +} + +static int imx23_ccm_probe(struct device_d *dev) +{ + void __iomem *regs; + + regs = dev_request_mem_region(dev, 0); + + mx23_clocks_init(regs); + + return 0; +} + +static __maybe_unused struct of_device_id imx23_ccm_dt_ids[] = { + { + .compatible = "fsl,imx23-clkctrl", + }, { + /* sentinel */ + } +}; + +static struct driver_d imx23_ccm_driver = { + .probe = imx23_ccm_probe, + .name = "imx23-clkctrl", + .of_compatible = DRV_OF_COMPAT(imx23_ccm_dt_ids), +}; + +static int imx23_ccm_init(void) +{ + return platform_driver_register(&imx23_ccm_driver); +} +postcore_initcall(imx23_ccm_init); diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c new file mode 100644 index 0000000..0350aff --- /dev/null +++ b/drivers/clk/mxs/clk-imx28.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2008 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define PLL0CTRL0 (regs + 0x0000) +#define PLL1CTRL0 (regs + 0x0020) +#define PLL2CTRL0 (regs + 0x0040) +#define CPU (regs + 0x0050) +#define HBUS (regs + 0x0060) +#define XBUS (regs + 0x0070) +#define XTAL (regs + 0x0080) +#define SSP0 (regs + 0x0090) +#define SSP1 (regs + 0x00a0) +#define SSP2 (regs + 0x00b0) +#define SSP3 (regs + 0x00c0) +#define GPMI (regs + 0x00d0) +#define SPDIF (regs + 0x00e0) +#define EMI (regs + 0x00f0) +#define SAIF0 (regs + 0x0100) +#define SAIF1 (regs + 0x0110) +#define LCDIF (regs + 0x0120) +#define ETM (regs + 0x0130) +#define ENET (regs + 0x0140) +#define FLEXCAN (regs + 0x0160) +#define FRAC0 (regs + 0x01b0) +#define FRAC1 (regs + 0x01c0) +#define CLKSEQ (regs + 0x01d0) + +static const char *sel_cpu[] = { "ref_cpu", "ref_xtal", }; +static const char *sel_io0[] = { "ref_io0", "ref_xtal", }; +static const char *sel_io1[] = { "ref_io1", "ref_xtal", }; +static const char *sel_pix[] = { "ref_pix", "ref_xtal", }; +static const char *sel_gpmi[] = { "ref_gpmi", "ref_xtal", }; +static const char *cpu_sels[] = { "cpu_pll", "cpu_xtal", }; +static const char *emi_sels[] = { "emi_pll", "emi_xtal", }; +static const char *ptp_sels[] = { "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_phy, usb1_phy, enet_out, + lcdif_comp, clk_max +}; + +static struct clk *clks[clk_max]; + +int __init mx28_clocks_init(void __iomem *regs) +{ + clks[ref_xtal] = 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_io1] = mxs_clk_ref("ref_io1", "pll0", FRAC0, 2); + clks[ref_io0] = mxs_clk_ref("ref_io0", "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[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[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[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[emi] = mxs_clk_gate("emi", "emi_sel", EMI, 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[usb0_phy] = mxs_clk_gate("usb0_phy", "pll0", PLL0CTRL0, 18); + clks[usb1_phy] = mxs_clk_gate("usb1_phy", "pll1", PLL1CTRL0, 18); + clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18); + clks[lcdif_comp] = mxs_clk_lcdif("lcdif_comp", clks[ref_pix], + clks[lcdif_div], clks[lcdif]); + + clk_set_rate(clks[ref_io0], 480000000); + clk_set_rate(clks[ref_io1], 480000000); + clk_set_parent(clks[ssp0_sel], clks[ref_io0]); + clk_set_parent(clks[ssp1_sel], clks[ref_io0]); + clk_set_parent(clks[ssp2_sel], clks[ref_io1]); + clk_set_parent(clks[ssp3_sel], clks[ref_io1]); + clk_set_rate(clks[ssp0_div], 96000000); + clk_set_rate(clks[ssp1_div], 96000000); + clk_set_rate(clks[ssp2_div], 96000000); + clk_set_rate(clks[ssp3_div], 96000000); + clk_set_parent(clks[lcdif_sel], clks[ref_pix]); + clk_enable(clks[enet_out]); + + clkdev_add_physbase(clks[ssp0], IMX_SSP0_BASE, NULL); + clkdev_add_physbase(clks[ssp1], IMX_SSP1_BASE, NULL); + clkdev_add_physbase(clks[ssp2], IMX_SSP2_BASE, NULL); + clkdev_add_physbase(clks[ssp3], IMX_SSP3_BASE, NULL); + clkdev_add_physbase(clks[fec], IMX_FEC0_BASE, NULL); + clkdev_add_physbase(clks[xbus], IMX_DBGUART_BASE, NULL); + clkdev_add_physbase(clks[hbus], IMX_OCOTP_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART0_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART1_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART2_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART3_BASE, NULL); + clkdev_add_physbase(clks[uart], IMX_UART4_BASE, NULL); + clkdev_add_physbase(clks[gpmi], MXS_GPMI_BASE, NULL); + if (IS_ENABLED(CONFIG_DRIVER_VIDEO_STM)) + clkdev_add_physbase(clks[lcdif_comp], IMX_FB_BASE, NULL); + + return 0; +} + +static int imx28_ccm_probe(struct device_d *dev) +{ + void __iomem *regs; + + regs = dev_request_mem_region(dev, 0); + + mx28_clocks_init(regs); + + return 0; +} + +static __maybe_unused struct of_device_id imx28_ccm_dt_ids[] = { + { + .compatible = "fsl,imx28-clkctrl", + }, { + /* sentinel */ + } +}; + +static struct driver_d imx28_ccm_driver = { + .probe = imx28_ccm_probe, + .name = "imx28-clkctrl", + .of_compatible = DRV_OF_COMPAT(imx28_ccm_dt_ids), +}; + +static int imx28_ccm_init(void) +{ + return platform_driver_register(&imx28_ccm_driver); +} +postcore_initcall(imx28_ccm_init); diff --git a/drivers/clk/mxs/clk-lcdif.c b/drivers/clk/mxs/clk-lcdif.c new file mode 100644 index 0000000..86dfe89 --- /dev/null +++ b/drivers/clk/mxs/clk-lcdif.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include "clk.h" + +struct clk_lcdif { + struct clk clk; + + struct clk *frac, *div, *gate; + const char *parent; +}; + +#define to_clk_lcdif(_hw) container_of(_hw, struct clk_lcdif, clk) + +static int clk_lcdif_set_rate(struct clk *clk, unsigned long rate, + unsigned long unused) +{ + struct clk_lcdif *lcdif = to_clk_lcdif(clk); + unsigned long frac, div, best_div = 1; + int delta, best_delta = 0x7fffffff; + unsigned long frate, rrate, best_frate; + unsigned long parent_rate = clk_get_rate(clk_get_parent(lcdif->frac)); + + best_frate = parent_rate; + + for (frac = 18; frac < 35; frac++) { + frate = (parent_rate / frac) * 18; + div = frate / rate; + if (!div) + div = 1; + rrate = frate / div; + delta = rate - rrate; + if (abs(delta) < abs(best_delta)) { + best_frate = frate; + best_div = div; + best_delta = delta; + } + } + + clk_set_rate(lcdif->frac, best_frate); + best_frate = clk_get_rate(lcdif->frac); + clk_set_rate(lcdif->div, (best_frate + best_div) / best_div); + + return 0; +} + +static const struct clk_ops clk_lcdif_ops = { + .set_rate = clk_lcdif_set_rate, +}; + +struct clk *mxs_clk_lcdif(const char *name, struct clk *frac, struct clk *div, + struct clk *gate) +{ + struct clk_lcdif *lcdif; + int ret; + + lcdif = xzalloc(sizeof(*lcdif)); + + lcdif->parent = gate->name; + lcdif->frac = frac; + lcdif->div = div; + lcdif->gate = gate; + lcdif->clk.name = name; + lcdif->clk.ops = &clk_lcdif_ops; + lcdif->clk.parent_names = &lcdif->parent; + lcdif->clk.num_parents = 1; + + ret = clk_register(&lcdif->clk); + if (ret) + return ERR_PTR(ret); + + return &lcdif->clk; +} diff --git a/drivers/clk/mxs/clk-pll.c b/drivers/clk/mxs/clk-pll.c new file mode 100644 index 0000000..89fd6b5 --- /dev/null +++ b/drivers/clk/mxs/clk-pll.c @@ -0,0 +1,117 @@ +/* + * 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 +#include +#include +#include + +#include "clk.h" + +#define SET 0x4 +#define CLR 0x8 + +/** + * 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 clk; + const char *parent; + void __iomem *base; + u8 power; + unsigned long rate; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, clk) + +static int clk_pll_enable(struct clk *clk) +{ + struct clk_pll *pll = to_clk_pll(clk); + + writel(1 << pll->power, pll->base + SET); + + udelay(10); + + writel(1 << 31, pll->base + CLR); + + return 0; +} + +static void clk_pll_disable(struct clk *clk) +{ + struct clk_pll *pll = to_clk_pll(clk); + + writel(1 << 31, pll->base + SET); + + writel(1 << pll->power, pll->base + CLR); +} + +static int clk_pll_is_enabled(struct clk *clk) +{ + struct clk_pll *pll = to_clk_pll(clk); + u32 val; + + val = readl(pll->base); + + if (val & (1 << 31)) + return 0; + else + return 1; +} + +static unsigned long clk_pll_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(clk); + + return pll->rate; +} + +static const struct clk_ops clk_pll_ops = { + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .recalc_rate = clk_pll_recalc_rate, + .is_enabled = clk_pll_is_enabled, +}; + +struct clk *mxs_clk_pll(const char *name, const char *parent_name, + void __iomem *base, u8 power, unsigned long rate) +{ + struct clk_pll *pll; + int ret; + + pll = xzalloc(sizeof(*pll)); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->parent = parent_name; + pll->clk.name = name; + pll->clk.ops = &clk_pll_ops; + pll->clk.parent_names = &pll->parent; + pll->clk.num_parents = 1; + + pll->base = base; + pll->rate = rate; + pll->power = power; + + ret = clk_register(&pll->clk); + if (ret) + ERR_PTR(ret); + + return &pll->clk; +} diff --git a/drivers/clk/mxs/clk-ref.c b/drivers/clk/mxs/clk-ref.c new file mode 100644 index 0000000..7ff5527 --- /dev/null +++ b/drivers/clk/mxs/clk-ref.c @@ -0,0 +1,164 @@ +/* + * 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 +#include +#include +#include +#include + +#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 clk; + const char *parent; + void __iomem *reg; + u8 idx; +}; + +#define to_clk_ref(_hw) container_of(_hw, struct clk_ref, clk) + +#define SET 0x4 +#define CLR 0x8 + +static int clk_ref_is_enabled(struct clk *clk) +{ + struct clk_ref *ref = to_clk_ref(clk); + u32 reg = readl(ref->reg); + + if (reg & 1 << ((ref->idx + 1) * 8 - 1)) + return 0; + + return 1; +} + +static int clk_ref_enable(struct clk *clk) +{ + struct clk_ref *ref = to_clk_ref(clk); + + writel(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR); + + return 0; +} + +static void clk_ref_disable(struct clk *clk) +{ + struct clk_ref *ref = to_clk_ref(clk); + + writel(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET); +} + +static unsigned long clk_ref_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_ref *ref = to_clk_ref(clk); + u64 tmp = parent_rate; + u8 frac = (readl(ref->reg) >> (ref->idx * 8)) & 0x3f; + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static long clk_ref_round_rate(struct clk *clk, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + u64 tmp = parent_rate; + u32 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 *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_ref *ref = to_clk_ref(clk); + u64 tmp = parent_rate; + u32 val; + u32 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; + + val = readl(ref->reg); + val &= ~(0x3f << shift); + val |= frac << shift; + writel(val, ref->reg); + + return 0; +} + +static const struct clk_ops clk_ref_ops = { + .is_enabled = clk_ref_is_enabled, + .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; + int ret; + + ref = xzalloc(sizeof(*ref)); + if (!ref) + return ERR_PTR(-ENOMEM); + + ref->parent = parent_name; + ref->clk.name = name; + ref->clk.ops = &clk_ref_ops; + ref->clk.parent_names = &ref->parent; + ref->clk.num_parents = 1; + + ref->reg = reg; + ref->idx = idx; + + ret = clk_register(&ref->clk); + if (ret) + return ERR_PTR(ret); + + return &ref->clk; +} diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h new file mode 100644 index 0000000..b4fcfa0 --- /dev/null +++ b/drivers/clk/mxs/clk.h @@ -0,0 +1,52 @@ +#ifndef __MXS_CLK_H +#define __MXS_CLK_H + +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); + +#ifdef CONFIG_DRIVER_VIDEO_STM +struct clk *mxs_clk_lcdif(const char *name, struct clk *frac, struct clk *div, + struct clk *gate); +#else +static inline struct clk *mxs_clk_lcdif(const char *name, struct clk *frac, struct clk *div, + struct clk *gate) +{ + return ERR_PTR(-ENOSYS); +} +#endif + +static inline struct clk *mxs_clk_fixed(const char *name, int rate) +{ + return clk_fixed(name, rate); +} + +static inline struct clk *mxs_clk_gate(const char *name, + const char *parent_name, void __iomem *reg, u8 shift) +{ + return clk_gate_inverted(name, parent_name, reg, shift); +} + +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_mux(name, reg, shift, width, parent_names, num_parents); +} + +static inline struct clk *mxs_clk_fixed_factor(const char *name, + const char *parent_name, unsigned int mult, unsigned int div) +{ + return clk_fixed_factor(name, parent_name, mult, div); +} + +#endif /* __MXS_CLK_H */ diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c index e6f40eb..023f922 100644 --- a/drivers/mci/mxs.c +++ b/drivers/mci/mxs.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -49,8 +51,8 @@ struct mxs_mci_host { struct mci_host host; void __iomem *regs; + struct clk *clk; unsigned clock; /* current clock speed in Hz ("0" if disabled) */ - unsigned index; #ifdef CONFIG_MCI_INFO unsigned f_min; unsigned f_max; @@ -61,16 +63,6 @@ #define to_mxs_mci(mxs) container_of(mxs, struct mxs_mci_host, host) /** - * Get the SSP clock rate - * @param hw_dev Host interface device instance - * @return Unit's clock in [Hz] - */ -static unsigned mxs_mci_get_unit_clock(struct mxs_mci_host *mxs_mci) -{ - return imx_get_sspclk(mxs_mci->index); -} - -/** * Get MCI cards response if defined for the type of command * @param hw_dev Host interface device instance * @param cmd Command description @@ -116,22 +108,22 @@ * @param status HW_SSP_STATUS's content * @return 0 if no error, negative values else */ -static int mxs_mci_get_cmd_error(unsigned status) +static int mxs_mci_get_cmd_error(struct mxs_mci_host *mxs_mci, unsigned status) { if (status & SSP_STATUS_ERROR) - pr_debug("Status Reg reports %08X\n", status); + dev_dbg(mxs_mci->host.hw_dev, "Status Reg reports %08X\n", status); if (status & SSP_STATUS_TIMEOUT) { - pr_debug("CMD timeout\n"); + dev_dbg(mxs_mci->host.hw_dev, "CMD timeout\n"); return -ETIMEDOUT; } else if (status & SSP_STATUS_RESP_TIMEOUT) { - pr_debug("RESP timeout\n"); + dev_dbg(mxs_mci->host.hw_dev, "RESP timeout\n"); return -ETIMEDOUT; } else if (status & SSP_STATUS_RESP_CRC_ERR) { - pr_debug("CMD crc error\n"); + dev_dbg(mxs_mci->host.hw_dev, "CMD crc error\n"); return -EILSEQ; } else if (status & SSP_STATUS_RESP_ERR) { - pr_debug("RESP error\n"); + dev_dbg(mxs_mci->host.hw_dev, "RESP error\n"); return -EIO; } @@ -168,7 +160,8 @@ uint32_t *p = buffer; if (length & 0x3) { - pr_debug("Cannot read data sizes not multiple of 4 (request for %u detected)\n", + dev_dbg(mxs_mci->host.hw_dev, + "Cannot read data sizes not multiple of 4 (request for %u detected)\n", length); return -EINVAL; } @@ -206,7 +199,8 @@ const uint32_t *p = buffer; if (length & 0x3) { - pr_debug("Cannot write data sizes not multiple of 4 (request for %u detected)\n", + dev_dbg(mxs_mci->host.hw_dev, + "Cannot write data sizes not multiple of 4 (request for %u detected)\n", length); return -EINVAL; } @@ -320,7 +314,7 @@ if (cmd->resp_type & MMC_RSP_PRESENT) mxs_mci_get_cards_response(mxs_mci, cmd); - return mxs_mci_get_cmd_error(readl(mxs_mci->regs + HW_SSP_STATUS)); + return mxs_mci_get_cmd_error(mxs_mci, readl(mxs_mci->regs + HW_SSP_STATUS)); } /** @@ -385,7 +379,7 @@ err = mxs_mci_transfer_data(mxs_mci, data); if (err != 0) { - pr_debug(" Transfering data failed\n"); + dev_dbg(mxs_mci->host.hw_dev, "Transfering data failed with %d\n", err); return err; } @@ -428,7 +422,7 @@ return 0; } - ssp = mxs_mci_get_unit_clock(mxs_mci); + ssp = clk_get_rate(mxs_mci->clk); for (div = 2; div < 255; div += 2) { rate = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(ssp, nc), div); @@ -436,7 +430,7 @@ break; } if (div >= 255) { - pr_warning("Cannot set clock to %d Hz\n", nc); + dev_warn(mxs_mci->host.hw_dev, "Cannot set clock to %d Hz\n", nc); return 0; } @@ -531,7 +525,8 @@ } mxs_mci->clock = mxs_mci_setup_clock_speed(mxs_mci, ios->clock); - pr_debug("IO settings: frequency=%u Hz\n", mxs_mci->clock); + + dev_dbg(host->hw_dev, "IO settings: frequency=%u Hz\n", mxs_mci->clock); } /* ----------------------------------------------------------------------- */ @@ -555,9 +550,10 @@ struct mxs_mci_platform_data *pd = hw_dev->platform_data; struct mxs_mci_host *mxs_mci; struct mci_host *host; + unsigned long rate; if (hw_dev->platform_data == NULL) { - pr_err("Missing platform data\n"); + dev_err(hw_dev, "Missing platform data\n"); return -EINVAL; } @@ -575,44 +571,29 @@ host->voltages = pd->voltages; host->host_caps = pd->caps; -#ifdef CONFIG_ARCH_IMX23 - mxs_mci->index = 0; /* there is only one clock for all */ -#endif -#ifdef CONFIG_ARCH_IMX28 - /* one dedicated clock per unit */ - switch (hw_dev->resource[0].start) { - case IMX_SSP0_BASE: - mxs_mci->index = 0; - break; - case IMX_SSP1_BASE: - mxs_mci->index = 1; - break; - case IMX_SSP2_BASE: - mxs_mci->index = 2; - break; - case IMX_SSP3_BASE: - mxs_mci->index = 3; - break; - default: - pr_debug("Unknown SSP unit at address 0x%p\n", mxs_mci->regs); - return 0; - } -#endif + mxs_mci->clk = clk_get(hw_dev, NULL); + if (IS_ERR(mxs_mci->clk)) + return PTR_ERR(mxs_mci->clk); + + clk_enable(mxs_mci->clk); + + rate = clk_get_rate(mxs_mci->clk); + if (pd->f_min == 0) { - host->f_min = mxs_mci_get_unit_clock(mxs_mci) / 254 / 256; - pr_debug("Min. frequency is %u Hz\n", host->f_min); + host->f_min = rate / 254 / 256; + dev_dbg(hw_dev, "Min. frequency is %u Hz\n", host->f_min); } else { host->f_min = pd->f_min; - pr_debug("Min. frequency is %u Hz, could be %u Hz\n", - host->f_min, mxs_mci_get_unit_clock(mxs_mci) / 254 / 256); + dev_dbg(hw_dev, "Min. frequency is %u Hz, could be %lu Hz\n", + host->f_min, rate / 254 / 256); } if (pd->f_max == 0) { - host->f_max = mxs_mci_get_unit_clock(mxs_mci) / 2 / 1; - pr_debug("Max. frequency is %u Hz\n", host->f_max); + host->f_max = rate / 2 / 1; + dev_dbg(hw_dev, "Max. frequency is %u Hz\n", host->f_max); } else { host->f_max = pd->f_max; - pr_debug("Max. frequency is %u Hz, could be %u Hz\n", - host->f_max, mxs_mci_get_unit_clock(mxs_mci) / 2 / 1); + dev_dbg(hw_dev, "Max. frequency is %u Hz, could be %lu Hz\n", + host->f_max, rate / 2 / 1); } if (IS_ENABLED(CONFIG_MCI_INFO)) { diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c index dd43a95..56d5ecf 100644 --- a/drivers/mtd/nand/nand_mxs.c +++ b/drivers/mtd/nand/nand_mxs.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -140,6 +142,7 @@ struct mxs_nand_info { struct nand_chip nand_chip; void __iomem *io_base; + struct clk *clk; struct mtd_info mtd; u32 version; @@ -1147,8 +1150,6 @@ /* Init the DMA controller. */ mxs_dma_init(); - imx_enable_nandclk(); - /* Reset the GPMI block. */ ret = mxs_reset_block(gpmi_regs + GPMI_CTRL0, 0); if (ret) @@ -1200,6 +1201,12 @@ /* XXX: Remove u-boot specific access pointers and use io_base instead? */ nand_info->io_base = dev_request_mem_region(dev, 0); + nand_info->clk = clk_get(dev, NULL); + if (IS_ERR(nand_info->clk)) + return PTR_ERR(nand_info->clk); + + clk_enable(nand_info->clk); + err = mxs_nand_alloc_buffers(nand_info); if (err) goto err1; diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 1346c91..2f31352 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -45,19 +45,6 @@ uint8_t head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ }; -#ifdef CONFIG_COMMON_CLK -static inline unsigned long fec_clk_get_rate(struct fec_priv *fec) -{ - return clk_get_rate(fec->clk); -} -#else -static inline unsigned long fec_clk_get_rate(struct fec_priv *fec) -{ - return imx_get_fecclk(); -} -#endif - - /* * MII-interface related functions */ @@ -69,7 +56,7 @@ uint32_t phy; /* convenient holder for the PHY */ uint64_t start; - writel(((fec_clk_get_rate(fec) >> 20) / 5) << 1, + writel(((clk_get_rate(fec->clk) >> 20) / 5) << 1, fec->regs + FEC_MII_SPEED); /* * reading from any PHY's register is done by properly @@ -112,7 +99,7 @@ uint32_t phy; /* convenient holder for the PHY */ uint64_t start; - writel(((fec_clk_get_rate(fec) >> 20) / 5) << 1, + writel(((clk_get_rate(fec->clk) >> 20) / 5) << 1, fec->regs + FEC_MII_SPEED); reg = regAddr << FEC_MII_DATA_RA_SHIFT; @@ -305,7 +292,7 @@ * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock * and do not drop the Preamble. */ - writel(((fec_clk_get_rate(fec) >> 20) / 5) << 1, + writel(((clk_get_rate(fec->clk) >> 20) / 5) << 1, fec->regs + FEC_MII_SPEED); if (fec->interface == PHY_INTERFACE_MODE_RMII) { @@ -674,14 +661,14 @@ edev->set_ethaddr = fec_set_hwaddr; edev->parent = dev; - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - fec->clk = clk_get(dev, NULL); - if (IS_ERR(fec->clk)) { - ret = PTR_ERR(fec->clk); - goto err_free; - } + fec->clk = clk_get(dev, NULL); + if (IS_ERR(fec->clk)) { + ret = PTR_ERR(fec->clk); + goto err_free; } + clk_enable(fec->clk); + fec->regs = dev_request_mem_region(dev, 0); /* Reset chip. */ diff --git a/drivers/serial/serial_auart.c b/drivers/serial/serial_auart.c index a69e65b..98f7c75 100644 --- a/drivers/serial/serial_auart.c +++ b/drivers/serial/serial_auart.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -92,6 +94,7 @@ int baudrate; struct notifier_block notify; void __iomem *base; + struct clk *clk; }; static void auart_serial_putc(struct console_device *cdev, char c) @@ -143,7 +146,7 @@ writel(0x0, priv->base + HW_UARTAPP_CTRL2); /* Calculate and set baudrate */ - quot = (imx_get_xclk() * 32) / new_baudrate; + quot = (clk_get_rate(priv->clk) * 32) / new_baudrate; reg = BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(quot & 0x3F) | BF_UARTAPP_LINECTRL_BAUD_DIVINT(quot >> 6) | BF_UARTAPP_LINECTRL_WLEN(3) | @@ -194,6 +197,9 @@ dev->priv = priv; priv->base = dev_request_mem_region(dev, 0); + priv->clk = clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); auart_serial_init_port(priv); auart_serial_setbaudrate(cdev, CONFIG_BAUDRATE); diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c index a1bb733..0d7484f 100644 --- a/drivers/serial/stm-serial.c +++ b/drivers/serial/stm-serial.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -56,6 +58,7 @@ int baudrate; struct notifier_block notify; void __iomem *base; + struct clk *clk; }; static void stm_serial_putc(struct console_device *cdev, char c) @@ -107,7 +110,7 @@ writel(0, priv->base + UARTDBGCR); /* Calculate and set baudrate */ - quot = (imx_get_xclk() * 4) / new_baudrate; + quot = (clk_get_rate(priv->clk) * 4) / new_baudrate; writel(quot & 0x3f, priv->base + UARTDBGFBRD); writel(quot >> 6, priv->base + UARTDBGIBRD); @@ -160,6 +163,9 @@ dev->priv = priv; priv->base = dev_request_mem_region(dev, 0); + priv->clk = clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); stm_serial_init_port(priv); stm_serial_setbaudrate(cdev, CONFIG_BAUDRATE); diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index e15f2c2..8dfd6d5 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +53,7 @@ static void imx_set_ssp_busclock(struct spi_master *master, uint32_t freq) { struct mxs_spi *mxs = to_mxs(master); - const uint32_t sspclk = imx_get_sspclk(master->bus_num); + const uint32_t sspclk = clk_get_rate(mxs->clk); uint32_t val; uint32_t divide, rate, tgtclk; @@ -266,6 +267,9 @@ mxs->mode = SPI_CPOL | SPI_CPHA; mxs->regs = dev_request_mem_region(dev, 0); + mxs->clk = clk_get(dev, NULL); + if (IS_ERR(mxs->clk)) + return PTR_ERR(mxs->clk); spi_register_master(master); diff --git a/drivers/video/stm.c b/drivers/video/stm.c index 606e39a..d5212f8 100644 --- a/drivers/video/stm.c +++ b/drivers/video/stm.c @@ -24,8 +24,9 @@ #include #include #include +#include +#include #include -#include #include #define HW_LCDIF_CTRL 0x00 @@ -144,6 +145,7 @@ struct fb_info info; struct device_d *hw_dev; struct imx_fb_platformdata *pdata; + struct clk *clk; }; /* the RGB565 true colour mode */ @@ -327,7 +329,7 @@ /** @todo ensure HCLK is active at this point of time! */ - size = imx_set_lcdifclk(PICOS2KHZ(mode->pixclock) * 1000); + size = clk_set_rate(fbi->clk, PICOS2KHZ(mode->pixclock) * 1000); if (size == 0) { dev_dbg(fbi->hw_dev, "Unable to set a valid pixel clock\n"); return -EINVAL; @@ -490,6 +492,9 @@ fbi.hw_dev = hw_dev; fbi.base = dev_request_mem_region(hw_dev, 0); fbi.pdata = pdata; + fbi.clk = clk_get(hw_dev, NULL); + if (IS_ERR(fbi.clk)) + return PTR_ERR(fbi.clk); /* add runtime video info */ fbi.info.mode_list = pdata->mode_list; diff --git a/include/linux/clk.h b/include/linux/clk.h index 37a4813..38832ba 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -188,8 +188,23 @@ }; struct clk *clk_fixed(const char *name, int rate); + +struct clk_divider { + struct clk clk; + u8 shift; + u8 width; + void __iomem *reg; + const char *parent; +#define CLK_DIVIDER_ONE_BASED (1 << 0) + unsigned flags; +}; + +extern struct clk_ops clk_divider_ops; + struct clk *clk_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width); +struct clk *clk_divider_one_based(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width); struct clk *clk_divider_table(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, const struct clk_div_table *table); @@ -199,6 +214,9 @@ u8 shift, u8 width, const char **parents, u8 num_parents); struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift); +struct clk *clk_gate_inverted(const char *name, const char *parent, void __iomem *reg, + u8 shift); +int clk_is_enabled(struct clk *clk); int clk_is_enabled_always(struct clk *clk);