aboutsummaryrefslogtreecommitdiff
path: root/sys/arm/ti/omap4/omap4_prcm_clks.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/ti/omap4/omap4_prcm_clks.c')
-rw-r--r--sys/arm/ti/omap4/omap4_prcm_clks.c1504
1 files changed, 1504 insertions, 0 deletions
diff --git a/sys/arm/ti/omap4/omap4_prcm_clks.c b/sys/arm/ti/omap4/omap4_prcm_clks.c
new file mode 100644
index 000000000000..56368e44059e
--- /dev/null
+++ b/sys/arm/ti/omap4/omap4_prcm_clks.c
@@ -0,0 +1,1504 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray@gmail.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <arm/arm/mpcore_timervar.h>
+#include <arm/ti/tivar.h>
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/omap4/omap4_reg.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/*
+ * This file defines the clock configuration for the OMAP4xxx series of
+ * devices.
+ *
+ * How This is Suppose to Work
+ * ===========================
+ * - There is a top level omap_prcm module that defines all OMAP SoC drivers
+ * should use to enable/disable the system clocks regardless of the version
+ * of OMAP device they are running on. This top level PRCM module is just
+ * a thin shim to chip specific functions that perform the donkey work of
+ * configuring the clock - this file is the 'donkey' for OMAP44xx devices.
+ *
+ * - The key bit in this file is the omap_clk_devmap array, it's
+ * used by the omap_prcm driver to determine what clocks are valid and which
+ * functions to call to manipulate them.
+ *
+ * - In essence you just need to define some callbacks for each of the
+ * clocks and then you're done.
+ *
+ * - The other thing that is worth noting is that when the omap_prcm device
+ * is registered you typically pass in some memory ranges which are the
+ * SYS_MEMORY resources. These resources are in turn allocated using
+ * bus_allocate_resources(...) and the resource handles are passed to all
+ * individual clock callback handlers.
+ *
+ *
+ *
+ * OMAP4 devices are different from the previous OMAP3 devices in that there
+ * is no longer a separate functional and interface clock for each module,
+ * instead there is typically an interface clock that spans many modules.
+ */
+
+#define FREQ_96MHZ 96000000
+#define FREQ_64MHZ 64000000
+#define FREQ_48MHZ 48000000
+#define FREQ_32KHZ 32000
+
+#define PRM_INSTANCE 1
+#define CM1_INSTANCE 2
+#define CM2_INSTANCE 3
+
+/**
+ * Address offsets from the PRM memory region to the top level clock control
+ * registers.
+ */
+#define CKGEN_PRM_OFFSET 0x00000100UL
+#define MPU_PRM_OFFSET 0x00000300UL
+#define DSP_PRM_OFFSET 0x00000400UL
+#define ABE_PRM_OFFSET 0x00000500UL
+#define ALWAYS_ON_PRM_OFFSET 0x00000600UL
+#define CORE_PRM_OFFSET 0x00000700UL
+#define IVAHD_PRM_OFFSET 0x00000F00UL
+#define CAM_PRM_OFFSET 0x00001000UL
+#define DSS_PRM_OFFSET 0x00001100UL
+#define SGX_PRM_OFFSET 0x00001200UL
+#define L3INIT_PRM_OFFSET 0x00001300UL
+#define L4PER_PRM_OFFSET 0x00001400UL
+#define WKUP_PRM_OFFSET 0x00001700UL
+#define WKUP_CM_OFFSET 0x00001800UL
+#define EMU_PRM_OFFSET 0x00001900UL
+#define EMU_CM_OFFSET 0x00001A00UL
+#define DEVICE_PRM_OFFSET 0x00001B00UL
+#define INSTR_PRM_OFFSET 0x00001F00UL
+
+#define CM_ABE_DSS_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0000UL)
+#define CM_L4_WKUP_CLKSELL_OFFSET (CKGEN_PRM_OFFSET + 0x0008UL)
+#define CM_ABE_PLL_REF_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x000CUL)
+#define CM_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0010UL)
+
+/**
+ * Address offsets from the CM1 memory region to the top level clock control
+ * registers.
+ */
+#define CKGEN_CM1_OFFSET 0x00000100UL
+#define MPU_CM1_OFFSET 0x00000300UL
+#define DSP_CM1_OFFSET 0x00000400UL
+#define ABE_CM1_OFFSET 0x00000500UL
+#define RESTORE_CM1_OFFSET 0x00000E00UL
+#define INSTR_CM1_OFFSET 0x00000F00UL
+
+#define CM_CLKSEL_DPLL_MPU (CKGEN_CM1_OFFSET + 0x006CUL)
+
+/**
+ * Address offsets from the CM2 memory region to the top level clock control
+ * registers.
+ */
+#define INTRCONN_SOCKET_CM2_OFFSET 0x00000000UL
+#define CKGEN_CM2_OFFSET 0x00000100UL
+#define ALWAYS_ON_CM2_OFFSET 0x00000600UL
+#define CORE_CM2_OFFSET 0x00000700UL
+#define IVAHD_CM2_OFFSET 0x00000F00UL
+#define CAM_CM2_OFFSET 0x00001000UL
+#define DSS_CM2_OFFSET 0x00001100UL
+#define SGX_CM2_OFFSET 0x00001200UL
+#define L3INIT_CM2_OFFSET 0x00001300UL
+#define L4PER_CM2_OFFSET 0x00001400UL
+#define RESTORE_CM2_OFFSET 0x00001E00UL
+#define INSTR_CM2_OFFSET 0x00001F00UL
+
+#define CLKCTRL_MODULEMODE_MASK 0x00000003UL
+#define CLKCTRL_MODULEMODE_DISABLE 0x00000000UL
+#define CLKCTRL_MODULEMODE_AUTO 0x00000001UL
+#define CLKCTRL_MODULEMODE_ENABLE 0x00000001UL
+
+#define CLKCTRL_IDLEST_MASK 0x00030000UL
+#define CLKCTRL_IDLEST_ENABLED 0x00000000UL
+#define CLKCTRL_IDLEST_WAKING 0x00010000UL
+#define CLKCTRL_IDLEST_IDLE 0x00020000UL
+#define CLKCTRL_IDLEST_DISABLED 0x00030000UL
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,omap4-cm1", (uintptr_t)CM1_INSTANCE},
+ {"ti,omap4-cm2", (uintptr_t)CM2_INSTANCE},
+ {"ti,omap4-prm", (uintptr_t)PRM_INSTANCE},
+ {NULL, (uintptr_t)0},
+};
+
+struct omap4_prcm_softc {
+ struct resource *sc_res;
+ int sc_rid;
+ int sc_instance;
+ int attach_done;
+};
+
+static int omap4_clk_generic_activate(struct ti_clock_dev *clkdev);
+static int omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev);
+static int omap4_clk_generic_accessible(struct ti_clock_dev *clkdev);
+static int omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+static int omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+static int omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+static int omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev);
+static int omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev);
+static int omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev);
+
+static int omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+static int omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+
+/**
+ * omap_clk_devmap - Array of clock devices available on OMAP4xxx devices
+ *
+ * This map only defines which clocks are valid and the callback functions
+ * for clock activate, deactivate, etc. It is used by the top level omap_prcm
+ * driver.
+ *
+ * The actual details of the clocks (config registers, bit fields, sources,
+ * etc) are in the private g_omap3_clk_details array below.
+ *
+ */
+
+#define OMAP4_GENERIC_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_generic_activate, \
+ .clk_deactivate = omap4_clk_generic_deactivate, \
+ .clk_set_source = omap4_clk_generic_set_source, \
+ .clk_accessible = omap4_clk_generic_accessible, \
+ .clk_get_source_freq = omap4_clk_generic_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define OMAP4_GPTIMER_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_generic_activate, \
+ .clk_deactivate = omap4_clk_generic_deactivate, \
+ .clk_set_source = omap4_clk_gptimer_set_source, \
+ .clk_accessible = omap4_clk_generic_accessible, \
+ .clk_get_source_freq = omap4_clk_gptimer_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define OMAP4_HSMMC_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_generic_activate, \
+ .clk_deactivate = omap4_clk_generic_deactivate, \
+ .clk_set_source = omap4_clk_hsmmc_set_source, \
+ .clk_accessible = omap4_clk_generic_accessible, \
+ .clk_get_source_freq = omap4_clk_hsmmc_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define OMAP4_HSUSBHOST_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = omap4_clk_hsusbhost_activate, \
+ .clk_deactivate = omap4_clk_hsusbhost_deactivate, \
+ .clk_set_source = omap4_clk_hsusbhost_set_source, \
+ .clk_accessible = omap4_clk_hsusbhost_accessible, \
+ .clk_get_source_freq = NULL, \
+ .clk_set_source_freq = NULL \
+ }
+
+struct ti_clock_dev ti_omap4_clk_devmap[] = {
+ /* System clocks */
+ { .id = SYS_CLK,
+ .clk_activate = NULL,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = omap4_clk_get_sysclk_freq,
+ .clk_set_source_freq = NULL,
+ },
+ /* MPU (ARM) core clocks */
+ { .id = MPU_CLK,
+ .clk_activate = NULL,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = omap4_clk_get_arm_fclk_freq,
+ .clk_set_source_freq = NULL,
+ },
+
+ /* UART device clocks */
+ OMAP4_GENERIC_CLOCK_DEV(UART1_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(UART2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(UART3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(UART4_CLK),
+
+ /* Timer device source clocks */
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER1_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER2_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER3_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER4_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER5_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER6_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER7_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER8_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER9_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER10_CLK),
+ OMAP4_GPTIMER_CLOCK_DEV(TIMER11_CLK),
+
+ /* MMC device clocks (MMC1 and MMC2 can have different input clocks) */
+ OMAP4_HSMMC_CLOCK_DEV(MMC1_CLK),
+ OMAP4_HSMMC_CLOCK_DEV(MMC2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(MMC3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(MMC4_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(MMC5_CLK),
+
+ /* USB HS (high speed TLL, EHCI and OHCI) */
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBTLL_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBHSHOST_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBFSHOST_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_PHY_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_PHY_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_UTMI_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_UTMI_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_HSIC_CLK),
+ OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_HSIC_CLK),
+
+ /* GPIO */
+ OMAP4_GENERIC_CLOCK_DEV(GPIO1_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO4_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO5_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(GPIO6_CLK),
+
+ /* sDMA */
+ OMAP4_GENERIC_CLOCK_DEV(SDMA_CLK),
+
+ /* I2C */
+ OMAP4_GENERIC_CLOCK_DEV(I2C1_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(I2C2_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(I2C3_CLK),
+ OMAP4_GENERIC_CLOCK_DEV(I2C4_CLK),
+ { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL }
+};
+
+/**
+ * omap4_clk_details - Stores details for all the different clocks supported
+ *
+ * Whenever an operation on a clock is being performed (activated, deactivated,
+ * etc) this array is looked up to find the correct register and bit(s) we
+ * should be modifying.
+ *
+ */
+struct omap4_clk_details {
+ clk_ident_t id;
+
+ uint32_t instance;
+ uint32_t clksel_reg;
+
+ int32_t src_freq;
+
+ uint32_t enable_mode;
+};
+
+#define OMAP4_GENERIC_CLOCK_DETAILS(i, f, di, r, e) \
+ { .id = (i), \
+ .instance = (di), \
+ .clksel_reg = (r), \
+ .src_freq = (f), \
+ .enable_mode = (e), \
+ }
+
+static struct omap4_clk_details g_omap4_clk_details[] = {
+ /* UART */
+ OMAP4_GENERIC_CLOCK_DETAILS(UART1_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0140), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(UART2_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0148), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(UART3_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0150), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(UART4_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0158), CLKCTRL_MODULEMODE_ENABLE),
+
+ /* General purpose timers */
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER1_CLK, -1, PRM_INSTANCE,
+ (WKUP_CM_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER2_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x038), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER3_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER4_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x048), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER5_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x068), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER6_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x070), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER7_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x078), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER8_CLK, -1, CM1_INSTANCE,
+ (ABE_CM1_OFFSET + 0x080), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER9_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x050), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER10_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x028), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(TIMER11_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x030), CLKCTRL_MODULEMODE_ENABLE),
+
+ /* HSMMC (MMC1 and MMC2 can have different input clocks) */
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC1_CLK, -1, CM2_INSTANCE,
+ (L3INIT_CM2_OFFSET + 0x028), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC2_CLK, -1, CM2_INSTANCE,
+ (L3INIT_CM2_OFFSET + 0x030), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC3_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x120), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC4_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x128), /*CLKCTRL_MODULEMODE_ENABLE*/2),
+ OMAP4_GENERIC_CLOCK_DETAILS(MMC5_CLK, FREQ_48MHZ, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x160), /*CLKCTRL_MODULEMODE_ENABLE*/1),
+
+ /* GPIO modules */
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO1_CLK, -1, PRM_INSTANCE,
+ (WKUP_CM_OFFSET + 0x038), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO2_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x060), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO3_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x068), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO4_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x070), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO5_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x078), CLKCTRL_MODULEMODE_AUTO),
+ OMAP4_GENERIC_CLOCK_DETAILS(GPIO6_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x080), CLKCTRL_MODULEMODE_AUTO),
+
+ /* sDMA block */
+ OMAP4_GENERIC_CLOCK_DETAILS(SDMA_CLK, -1, CM2_INSTANCE,
+ (CORE_CM2_OFFSET + 0x300), CLKCTRL_MODULEMODE_AUTO),
+
+ /* I2C modules */
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C1_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0A0), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C2_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0A8), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C3_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0B0), CLKCTRL_MODULEMODE_ENABLE),
+ OMAP4_GENERIC_CLOCK_DETAILS(I2C4_CLK, -1, CM2_INSTANCE,
+ (L4PER_CM2_OFFSET + 0x0B8), CLKCTRL_MODULEMODE_ENABLE),
+
+ { INVALID_CLK_IDENT, 0, 0, 0, 0 },
+};
+
+/**
+ * MAX_MODULE_ENABLE_WAIT - the number of loops to wait for the module to come
+ * alive.
+ *
+ */
+#define MAX_MODULE_ENABLE_WAIT 100
+
+/**
+ * ARRAY_SIZE - Macro to return the number of elements in a static const array.
+ *
+ */
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/**
+ * omap4_clk_details - writes a 32-bit value to one of the timer registers
+ * @timer: Timer device context
+ * @off: The offset of a register from the timer register address range
+ * @val: The value to write into the register
+ *
+ *
+ * RETURNS:
+ * nothing
+ */
+static struct omap4_clk_details*
+omap4_clk_details(clk_ident_t id)
+{
+ struct omap4_clk_details *walker;
+
+ for (walker = g_omap4_clk_details; walker->id != INVALID_CLK_IDENT; walker++) {
+ if (id == walker->id)
+ return (walker);
+ }
+
+ return NULL;
+}
+
+static struct omap4_prcm_softc *
+omap4_prcm_get_instance_softc(int module_instance)
+{
+ int i, maxunit;
+ devclass_t prcm_devclass;
+ device_t dev;
+ struct omap4_prcm_softc *sc;
+
+ prcm_devclass = devclass_find("omap4_prcm");
+ maxunit = devclass_get_maxunit(prcm_devclass);
+
+ for (i = 0; i < maxunit; i++) {
+ dev = devclass_get_device(prcm_devclass, i);
+ sc = device_get_softc(dev);
+ if (sc->sc_instance == module_instance)
+ return (sc);
+ }
+
+ return (NULL);
+}
+
+/**
+ * omap4_clk_generic_activate - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_generic_activate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+ unsigned int i;
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* All the 'generic' clocks have a CLKCTRL register which is more or less
+ * generic - the have at least two fielda called MODULEMODE and IDLEST.
+ */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= clk_details->enable_mode;
+ bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel);
+
+ /* Now poll on the IDLEST register to tell us if the module has come up.
+ * TODO: We need to take into account the parent clocks.
+ */
+
+ /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
+ for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) {
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED)
+ break;
+ DELAY(10);
+ }
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) {
+ printf("Error: failed to enable module with clock %d\n", clkdev->id);
+ printf("Error: 0x%08x => 0x%08x\n", clk_details->clksel_reg, clksel);
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_deactivate - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* All the 'generic' clocks have a CLKCTRL register which is more or less
+ * generic - the have at least two fielda called MODULEMODE and IDLEST.
+ */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_DISABLE;
+ bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel);
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_set_source - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_generic_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_accessible - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_generic_accessible(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED)
+ return (0);
+
+ return (1);
+}
+
+/**
+ * omap4_clk_generic_get_source_freq - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq
+ )
+{
+ struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ /* Simply return the stored frequency */
+ if (freq)
+ *freq = (unsigned int)clk_details->src_freq;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_gptimer_set_source - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* TODO: Implement */
+
+ return (0);
+}
+
+/**
+ * omap4_clk_gptimer_get_source_freq - checks if a module is accessible
+ * @module: identifier for the module to check, see omap3_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq
+ )
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+ unsigned int src_freq;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* Need to read the CLKSEL field to determine the clock source */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ if (clksel & (0x1UL << 24))
+ src_freq = FREQ_32KHZ;
+ else
+ omap4_clk_get_sysclk_freq(NULL, &src_freq);
+
+ /* Return the frequency */
+ if (freq)
+ *freq = src_freq;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsmmc_set_source - sets the source clock (freq)
+ * @clkdev: pointer to the clockdev structure (id field will contain clock id)
+ *
+ * The MMC 1 and 2 clocks can be source from either a 64MHz or 96MHz clock.
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ /* For MMC modules 3, 4 & 5 you can't change the freq, it's always 48MHz */
+ if ((clkdev->id == MMC3_CLK) || (clkdev->id == MMC4_CLK) ||
+ (clkdev->id == MMC5_CLK)) {
+ if (clksrc != F48MHZ_CLK)
+ return (EINVAL);
+ return 0;
+ }
+
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+
+ /* Bit 24 is set if 96MHz clock or cleared for 64MHz clock */
+ if (clksrc == F64MHZ_CLK)
+ clksel &= ~(0x1UL << 24);
+ else if (clksrc == F96MHZ_CLK)
+ clksel |= (0x1UL << 24);
+ else
+ return (EINVAL);
+
+ bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel);
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsmmc_get_source_freq - checks if a module is accessible
+ * @clkdev: pointer to the clockdev structure (id field will contain clock id)
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int
+omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq
+ )
+{
+ struct omap4_prcm_softc *sc;
+ struct omap4_clk_details* clk_details;
+ struct resource* clk_mem_res;
+ uint32_t clksel;
+ unsigned int src_freq;
+
+ clk_details = omap4_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ sc = omap4_prcm_get_instance_softc(clk_details->instance);
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_mem_res = sc->sc_res;
+
+ if (clk_mem_res == NULL)
+ return (EINVAL);
+
+ switch (clkdev->id) {
+ case MMC1_CLK:
+ case MMC2_CLK:
+ /* Need to read the CLKSEL field to determine the clock source */
+ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg);
+ if (clksel & (0x1UL << 24))
+ src_freq = FREQ_96MHZ;
+ else
+ src_freq = FREQ_64MHZ;
+ break;
+ case MMC3_CLK:
+ case MMC4_CLK:
+ case MMC5_CLK:
+ src_freq = FREQ_48MHZ;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* Return the frequency */
+ if (freq)
+ *freq = src_freq;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_get_sysclk_freq - gets the sysclk frequency
+ * @sc: pointer to the clk module/device context
+ *
+ * Read the clocking information from the power-control/boot-strap registers,
+ * and stored in two global variables.
+ *
+ * RETURNS:
+ * nothing, values are saved in global variables
+ */
+static int
+omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq)
+{
+ uint32_t clksel;
+ uint32_t sysclk;
+ struct omap4_prcm_softc *sc;
+
+ sc = omap4_prcm_get_instance_softc(PRM_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ /* Read the input clock freq from the configuration register (CM_SYS_CLKSEL) */
+ clksel = bus_read_4(sc->sc_res, CM_SYS_CLKSEL_OFFSET);
+ switch (clksel & 0x7) {
+ case 0x1:
+ /* 12Mhz */
+ sysclk = 12000000;
+ break;
+ case 0x3:
+ /* 16.8Mhz */
+ sysclk = 16800000;
+ break;
+ case 0x4:
+ /* 19.2Mhz */
+ sysclk = 19200000;
+ break;
+ case 0x5:
+ /* 26Mhz */
+ sysclk = 26000000;
+ break;
+ case 0x7:
+ /* 38.4Mhz */
+ sysclk = 38400000;
+ break;
+ default:
+ panic("%s: Invalid clock freq", __func__);
+ }
+
+ /* Return the value */
+ if (freq)
+ *freq = sysclk;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_get_arm_fclk_freq - gets the MPU clock frequency
+ * @clkdev: ignored
+ * @freq: pointer which upon return will contain the freq in hz
+ * @mem_res: array of allocated memory resources
+ *
+ * Reads the frequency setting information registers and returns the value
+ * in the freq variable.
+ *
+ * RETURNS:
+ * returns 0 on success, a positive error code on failure.
+ */
+static int
+omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev,
+ unsigned int *freq)
+{
+ uint32_t clksel;
+ uint32_t pll_mult, pll_div;
+ uint32_t mpuclk, sysclk;
+ struct omap4_prcm_softc *sc;
+
+ sc = omap4_prcm_get_instance_softc(CM1_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ /* Read the clksel register which contains the DPLL multiple and divide
+ * values. These are applied to the sysclk.
+ */
+ clksel = bus_read_4(sc->sc_res, CM_CLKSEL_DPLL_MPU);
+
+ pll_mult = ((clksel >> 8) & 0x7ff);
+ pll_div = (clksel & 0x7f) + 1;
+
+ /* Get the system clock freq */
+ omap4_clk_get_sysclk_freq(NULL, &sysclk);
+
+ /* Calculate the MPU freq */
+ mpuclk = ((uint64_t)sysclk * pll_mult) / pll_div;
+
+ /* Return the value */
+ if (freq)
+ *freq = mpuclk;
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsusbhost_activate - activates the USB clocks for the given module
+ * @clkdev: pointer to the clock device structure.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ * The USB clocking setup seems to be a bit more tricky than the other modules,
+ * to start with the clocking diagram for the HS host module shows 13 different
+ * clocks. So to try and make it easier to follow the clocking activation
+ * and deactivation is handled in its own set of callbacks.
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+
+struct dpll_param {
+ unsigned int m;
+ unsigned int n;
+ unsigned int m2;
+ unsigned int m3;
+ unsigned int m4;
+ unsigned int m5;
+ unsigned int m6;
+ unsigned int m7;
+};
+/* USB parameters */
+struct dpll_param usb_dpll_param[7] = {
+ /* 12M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 13M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 16.8M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 19.2M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 26M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 27M values */
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ /* 38.4M values */
+#ifdef CONFIG_OMAP4_SDC
+ {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0},
+#else
+ {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0},
+#endif
+};
+static int
+omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+ unsigned int i;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ switch (clkdev->id) {
+ case USBTLL_CLK:
+ /* For the USBTLL module we need to enable the following clocks:
+ * - INIT_L4_ICLK (will be enabled by bootloader)
+ * - TLL_CH0_FCLK
+ * - TLL_CH1_FCLK
+ */
+
+ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x68;
+
+ /* Enable the module and also enable the optional func clocks for
+ * channels 0 & 1 (is this needed ?)
+ */
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_ENABLE;
+
+ clksel |= (0x1 << 8); /* USB-HOST optional clock: USB_CH0_CLK */
+ clksel |= (0x1 << 9); /* USB-HOST optional clock: USB_CH1_CLK */
+ break;
+
+ case USBHSHOST_CLK:
+ case USBP1_PHY_CLK:
+ case USBP2_PHY_CLK:
+ case USBP1_UTMI_CLK:
+ case USBP2_UTMI_CLK:
+ case USBP1_HSIC_CLK:
+ case USBP2_HSIC_CLK:
+ /* For the USB HS HOST module we need to enable the following clocks:
+ * - INIT_L4_ICLK (will be enabled by bootloader)
+ * - INIT_L3_ICLK (will be enabled by bootloader)
+ * - INIT_48MC_FCLK
+ * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?)
+ * - HSIC_P1_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P1_480 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_480 (HSIC only, create a new clock for that ?)
+ */
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ /* Enable the module and also enable the optional func clocks */
+ if (clkdev->id == USBHSHOST_CLK) {
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= /*CLKCTRL_MODULEMODE_ENABLE*/2;
+
+ clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
+ }
+
+ else if (clkdev->id == USBP1_UTMI_CLK)
+ clksel |= (0x1 << 8); /* UTMI_P1_CLK */
+ else if (clkdev->id == USBP2_UTMI_CLK)
+ clksel |= (0x1 << 9); /* UTMI_P2_CLK */
+
+ else if (clkdev->id == USBP1_HSIC_CLK)
+ clksel |= (0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */
+ else if (clkdev->id == USBP2_HSIC_CLK)
+ clksel |= (0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */
+
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ bus_write_4(clk_mem_res, clksel_reg_off, clksel);
+
+ /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
+ for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) {
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED)
+ break;
+ }
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) {
+ printf("Error: HERE failed to enable module with clock %d\n", clkdev->id);
+ printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel);
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+/**
+ * omap4_clk_generic_deactivate - checks if a module is accessible
+ * @clkdev: pointer to the clock device structure.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 on success or a positive error code on failure.
+ */
+static int
+omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ switch (clkdev->id) {
+ case USBTLL_CLK:
+ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x68;
+
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_DISABLE;
+ break;
+
+ case USBHSHOST_CLK:
+ case USBP1_PHY_CLK:
+ case USBP2_PHY_CLK:
+ case USBP1_UTMI_CLK:
+ case USBP2_UTMI_CLK:
+ case USBP1_HSIC_CLK:
+ case USBP2_HSIC_CLK:
+ /* For the USB HS HOST module we need to enable the following clocks:
+ * - INIT_L4_ICLK (will be enabled by bootloader)
+ * - INIT_L3_ICLK (will be enabled by bootloader)
+ * - INIT_48MC_FCLK
+ * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?)
+ * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?)
+ * - HSIC_P1_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P1_480 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_60 (HSIC only, create a new clock for that ?)
+ * - HSIC_P2_480 (HSIC only, create a new clock for that ?)
+ */
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+
+ /* Enable the module and also enable the optional func clocks */
+ if (clkdev->id == USBHSHOST_CLK) {
+ clksel &= ~CLKCTRL_MODULEMODE_MASK;
+ clksel |= CLKCTRL_MODULEMODE_DISABLE;
+
+ clksel &= ~(0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
+ }
+
+ else if (clkdev->id == USBP1_UTMI_CLK)
+ clksel &= ~(0x1 << 8); /* UTMI_P1_CLK */
+ else if (clkdev->id == USBP2_UTMI_CLK)
+ clksel &= ~(0x1 << 9); /* UTMI_P2_CLK */
+
+ else if (clkdev->id == USBP1_HSIC_CLK)
+ clksel &= ~(0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */
+ else if (clkdev->id == USBP2_HSIC_CLK)
+ clksel &= ~(0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */
+
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ bus_write_4(clk_mem_res, clksel_reg_off, clksel);
+
+ return (0);
+}
+
+/**
+ * omap4_clk_hsusbhost_accessible - checks if a module is accessible
+ * @clkdev: pointer to the clock device structure.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 if module is not enable, 1 if module is enabled or a negative
+ * error code on failure.
+ */
+static int
+omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ if (clkdev->id == USBTLL_CLK) {
+ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x68;
+ }
+ else if (clkdev->id == USBHSHOST_CLK) {
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ }
+ else {
+ return (EINVAL);
+ }
+
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+
+ /* Check the enabled state */
+ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED)
+ return (0);
+
+ return (1);
+}
+
+/**
+ * omap4_clk_hsusbhost_set_source - sets the source clocks
+ * @clkdev: pointer to the clock device structure.
+ * @clksrc: the clock source ID for the given clock.
+ * @mem_res: array of memory resources allocated by the top level PRCM driver.
+ *
+ *
+ *
+ * LOCKING:
+ * Inherits the locks from the omap_prcm driver, no internal locking.
+ *
+ * RETURNS:
+ * Returns 0 if successful otherwise a negative error code on failure.
+ */
+static int
+omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc)
+{
+ struct omap4_prcm_softc *sc;
+ struct resource* clk_mem_res;
+ uint32_t clksel_reg_off;
+ uint32_t clksel;
+ unsigned int bit;
+
+ sc = omap4_prcm_get_instance_softc(CM2_INSTANCE);
+ if (sc == NULL)
+ return ENXIO;
+
+ if (clkdev->id == USBP1_PHY_CLK)
+ bit = 24;
+ else if (clkdev->id != USBP2_PHY_CLK)
+ bit = 25;
+ else
+ return (EINVAL);
+
+ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
+ clk_mem_res = sc->sc_res;
+ clksel_reg_off = L3INIT_CM2_OFFSET + 0x58;
+ clksel = bus_read_4(clk_mem_res, clksel_reg_off);
+
+ /* Set the clock source to either external or internal */
+ if (clksrc == EXT_CLK)
+ clksel |= (0x1 << bit);
+ else
+ clksel &= ~(0x1 << bit);
+
+ bus_write_4(clk_mem_res, clksel_reg_off, clksel);
+
+ return (0);
+}
+
+#define PRM_RSTCTRL 0x1b00
+#define PRM_RSTCTRL_RESET 0x2
+
+static void
+omap4_prcm_reset(void)
+{
+ struct omap4_prcm_softc *sc;
+
+ sc = omap4_prcm_get_instance_softc(PRM_INSTANCE);
+ if (sc == NULL)
+ return;
+
+ bus_write_4(sc->sc_res, PRM_RSTCTRL,
+ bus_read_4(sc->sc_res, PRM_RSTCTRL) | PRM_RSTCTRL_RESET);
+ bus_read_4(sc->sc_res, PRM_RSTCTRL);
+}
+
+/**
+ * omap4_prcm_probe - probe function for the driver
+ * @dev: prcm device handle
+ *
+ * Simply sets the name of the driver module.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * Always returns 0
+ */
+static int
+omap4_prcm_probe(device_t dev)
+{
+ const struct ofw_compat_data *ocd;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ ocd = ofw_bus_search_compatible(dev, compat_data);
+ if ((int)ocd->ocd_data == 0)
+ return (ENXIO);
+
+ switch ((int)ocd->ocd_data) {
+ case PRM_INSTANCE:
+ device_set_desc(dev, "TI OMAP Power, Reset and Clock Management (PRM)");
+ break;
+ case CM1_INSTANCE:
+ device_set_desc(dev, "TI OMAP Power, Reset and Clock Management (C1)");
+ break;
+ case CM2_INSTANCE:
+ device_set_desc(dev, "TI OMAP Power, Reset and Clock Management (C2)");
+ break;
+ default:
+ device_printf(dev, "unknown instance type: %d\n", (int)ocd->ocd_data);
+ return (ENXIO);
+ }
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+/**
+ * omap_prcm_attach - attach function for the driver
+ * @dev: prcm device handle
+ *
+ * Allocates and sets up the driver context, this simply entails creating a
+ * bus mappings for the PRCM register set.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * Always returns 0
+ */
+
+extern uint32_t platform_arm_tmr_freq;
+
+static int
+omap4_prcm_attach(device_t dev)
+{
+ struct omap4_prcm_softc *sc;
+ const struct ofw_compat_data *ocd;
+
+ sc = device_get_softc(dev);
+ ocd = ofw_bus_search_compatible(dev, compat_data);
+ sc->sc_instance = (int)ocd->ocd_data;
+
+ sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
+ RF_ACTIVE);
+ if (sc->sc_res == NULL) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ ti_cpu_reset = omap4_prcm_reset;
+
+ return (0);
+}
+
+static void
+omap4_prcm_new_pass(device_t dev)
+{
+ struct omap4_prcm_softc *sc = device_get_softc(dev);
+ unsigned int freq;
+
+ if (sc->attach_done ||
+ bus_current_pass < (BUS_PASS_TIMER + BUS_PASS_ORDER_EARLY)) {
+ bus_generic_new_pass(dev);
+ return;
+ }
+ sc->attach_done = 1;
+
+ /*
+ * In order to determine ARM frequency we need both RPM and CM1
+ * instances up and running. So wait until all CRM devices are
+ * initialized. Should be replaced with proper clock framework
+ */
+ if (device_get_unit(dev) == 2) {
+ omap4_clk_get_arm_fclk_freq(NULL, &freq);
+ arm_tmr_change_frequency(freq / 2);
+ }
+
+ return;
+}
+
+static device_method_t omap4_prcm_methods[] = {
+ DEVMETHOD(device_probe, omap4_prcm_probe),
+ DEVMETHOD(device_attach, omap4_prcm_attach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_new_pass, omap4_prcm_new_pass),
+
+ {0, 0},
+};
+
+static driver_t omap4_prcm_driver = {
+ "omap4_prcm",
+ omap4_prcm_methods,
+ sizeof(struct omap4_prcm_softc),
+};
+
+static devclass_t omap4_prcm_devclass;
+
+EARLY_DRIVER_MODULE(omap4_prcm, simplebus, omap4_prcm_driver,
+ omap4_prcm_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(omap4_prcm, 1);