From 600f7cfebeef74a6ba961c507079e3629da5dd7a Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Sep 2008 11:04:14 -0700 Subject: [PATCH] [ARM] msm: clock: provide clk_*() api support for Makes use of the proc_comm interface to provide clock control on MSM7X01A family SoCs. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-halibut.c | 1 + arch/arm/mach-msm/clock-7x01a.c | 126 ++++++++++++++ arch/arm/mach-msm/clock.c | 218 +++++++++++++++++++++++++ arch/arm/mach-msm/clock.h | 48 ++++++ arch/arm/mach-msm/include/mach/board.h | 1 + 6 files changed, 395 insertions(+) create mode 100644 arch/arm/mach-msm/clock-7x01a.c create mode 100644 arch/arm/mach-msm/clock.c create mode 100644 arch/arm/mach-msm/clock.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index d4d1deabce46..bfcb2518ba53 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,6 +1,7 @@ obj-y += io.o idle.o irq.o timer.o dma.o obj-y += devices.o obj-y += proc_comm.o +obj-y += clock.o clock-7x01a.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index b263783dadb8..c2a96e3965a6 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -79,6 +79,7 @@ static void __init halibut_init(void) static void __init halibut_map_io(void) { msm_map_common_io(); + msm_clock_init(); } MACHINE_START(HALIBUT, "Halibut Board (QCT SURF7200A)") diff --git a/arch/arm/mach-msm/clock-7x01a.c b/arch/arm/mach-msm/clock-7x01a.c new file mode 100644 index 000000000000..62230a3428ee --- /dev/null +++ b/arch/arm/mach-msm/clock-7x01a.c @@ -0,0 +1,126 @@ +/* arch/arm/mach-msm/clock-7x01a.c + * + * Clock tables for MSM7X01A + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "clock.h" +#include "devices.h" + +/* clock IDs used by the modem processor */ + +#define ACPU_CLK 0 /* Applications processor clock */ +#define ADM_CLK 1 /* Applications data mover clock */ +#define ADSP_CLK 2 /* ADSP clock */ +#define EBI1_CLK 3 /* External bus interface 1 clock */ +#define EBI2_CLK 4 /* External bus interface 2 clock */ +#define ECODEC_CLK 5 /* External CODEC clock */ +#define EMDH_CLK 6 /* External MDDI host clock */ +#define GP_CLK 7 /* General purpose clock */ +#define GRP_CLK 8 /* Graphics clock */ +#define I2C_CLK 9 /* I2C clock */ +#define ICODEC_RX_CLK 10 /* Internal CODEX RX clock */ +#define ICODEC_TX_CLK 11 /* Internal CODEX TX clock */ +#define IMEM_CLK 12 /* Internal graphics memory clock */ +#define MDC_CLK 13 /* MDDI client clock */ +#define MDP_CLK 14 /* Mobile display processor clock */ +#define PBUS_CLK 15 /* Peripheral bus clock */ +#define PCM_CLK 16 /* PCM clock */ +#define PMDH_CLK 17 /* Primary MDDI host clock */ +#define SDAC_CLK 18 /* Stereo DAC clock */ +#define SDC1_CLK 19 /* Secure Digital Card clocks */ +#define SDC1_PCLK 20 +#define SDC2_CLK 21 +#define SDC2_PCLK 22 +#define SDC3_CLK 23 +#define SDC3_PCLK 24 +#define SDC4_CLK 25 +#define SDC4_PCLK 26 +#define TSIF_CLK 27 /* Transport Stream Interface clocks */ +#define TSIF_REF_CLK 28 +#define TV_DAC_CLK 29 /* TV clocks */ +#define TV_ENC_CLK 30 +#define UART1_CLK 31 /* UART clocks */ +#define UART2_CLK 32 +#define UART3_CLK 33 +#define UART1DM_CLK 34 +#define UART2DM_CLK 35 +#define USB_HS_CLK 36 /* High speed USB core clock */ +#define USB_HS_PCLK 37 /* High speed USB pbus clock */ +#define USB_OTG_CLK 38 /* Full speed USB clock */ +#define VDC_CLK 39 /* Video controller clock */ +#define VFE_CLK 40 /* Camera / Video Front End clock */ +#define VFE_MDC_CLK 41 /* VFE MDDI client clock */ + +#define NR_CLKS 42 + +#define CLOCK(clk_name, clk_id, clk_dev, clk_flags) { \ + .name = clk_name, \ + .id = clk_id, \ + .flags = clk_flags, \ + .dev = clk_dev, \ + } + +#define OFF CLKFLAG_AUTO_OFF +#define MINMAX CLKFLAG_USE_MIN_MAX_TO_SET + +struct clk msm_clocks[] = { + CLOCK("adm_clk", ADM_CLK, NULL, 0), + CLOCK("adsp_clk", ADSP_CLK, NULL, 0), + CLOCK("ebi1_clk", EBI1_CLK, NULL, 0), + CLOCK("ebi2_clk", EBI2_CLK, NULL, 0), + CLOCK("ecodec_clk", ECODEC_CLK, NULL, 0), + CLOCK("emdh_clk", EMDH_CLK, NULL, OFF), + CLOCK("gp_clk", GP_CLK, NULL, 0), + CLOCK("grp_clk", GRP_CLK, NULL, OFF), + CLOCK("i2c_clk", I2C_CLK, &msm_device_i2c.dev, 0), + CLOCK("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), + CLOCK("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), + CLOCK("imem_clk", IMEM_CLK, NULL, OFF), + CLOCK("mdc_clk", MDC_CLK, NULL, 0), + CLOCK("mdp_clk", MDP_CLK, NULL, OFF), + CLOCK("pbus_clk", PBUS_CLK, NULL, 0), + CLOCK("pcm_clk", PCM_CLK, NULL, 0), + CLOCK("pmdh_clk", PMDH_CLK, NULL, OFF | MINMAX), + CLOCK("sdac_clk", SDAC_CLK, NULL, OFF), + CLOCK("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLOCK("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), + CLOCK("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLOCK("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), + CLOCK("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLOCK("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), + CLOCK("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLOCK("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), + CLOCK("tsif_clk", TSIF_CLK, NULL, 0), + CLOCK("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), + CLOCK("tv_dac_clk", TV_DAC_CLK, NULL, 0), + CLOCK("tv_enc_clk", TV_ENC_CLK, NULL, 0), + CLOCK("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLOCK("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLOCK("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLOCK("uart1dm_clk", UART1DM_CLK, NULL, OFF), + CLOCK("uart2dm_clk", UART2DM_CLK, NULL, 0), + CLOCK("usb_hs_clk", USB_HS_CLK, &msm_device_hsusb.dev, OFF), + CLOCK("usb_hs_pclk", USB_HS_PCLK, &msm_device_hsusb.dev, OFF), + CLOCK("usb_otg_clk", USB_OTG_CLK, NULL, 0), + CLOCK("vdc_clk", VDC_CLK, NULL, OFF | MINMAX), + CLOCK("vfe_clk", VFE_CLK, NULL, OFF), + CLOCK("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), +}; + +unsigned msm_num_clocks = ARRAY_SIZE(msm_clocks); diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c new file mode 100644 index 000000000000..3b1ce36f1032 --- /dev/null +++ b/arch/arm/mach-msm/clock.c @@ -0,0 +1,218 @@ +/* arch/arm/mach-msm/clock.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include + +#include "clock.h" +#include "proc_comm.h" + +static DEFINE_MUTEX(clocks_mutex); +static DEFINE_SPINLOCK(clocks_lock); +static LIST_HEAD(clocks); + +/* + * glue for the proc_comm interface + */ +static inline int pc_clk_enable(unsigned id) +{ + return msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); +} + +static inline void pc_clk_disable(unsigned id) +{ + msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); +} + +static inline int pc_clk_set_rate(unsigned id, unsigned rate) +{ + return msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); +} + +static inline int pc_clk_set_min_rate(unsigned id, unsigned rate) +{ + return msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); +} + +static inline int pc_clk_set_max_rate(unsigned id, unsigned rate) +{ + return msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate); +} + +static inline int pc_clk_set_flags(unsigned id, unsigned flags) +{ + return msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags); +} + +static inline unsigned pc_clk_get_rate(unsigned id) +{ + if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) + return 0; + else + return id; +} + +static inline unsigned pc_clk_is_enabled(unsigned id) +{ + if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) + return 0; + else + return id; +} + +static inline int pc_pll_request(unsigned id, unsigned on) +{ + on = !!on; + return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on); +} + +/* + * Standard clock functions defined in include/linux/clk.h + */ +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *clk; + + mutex_lock(&clocks_mutex); + + list_for_each_entry(clk, &clocks, list) + if (!strcmp(id, clk->name) && clk->dev == dev) + goto found_it; + + list_for_each_entry(clk, &clocks, list) + if (!strcmp(id, clk->name) && clk->dev == NULL) + goto found_it; + + clk = ERR_PTR(-ENOENT); +found_it: + mutex_unlock(&clocks_mutex); + return clk; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL(clk_put); + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + spin_lock_irqsave(&clocks_lock, flags); + clk->count++; + if (clk->count == 1) + pc_clk_enable(clk->id); + spin_unlock_irqrestore(&clocks_lock, flags); + return 0; +} +EXPORT_SYMBOL(clk_enable); + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + spin_lock_irqsave(&clocks_lock, flags); + BUG_ON(clk->count == 0); + clk->count--; + if (clk->count == 0) + pc_clk_disable(clk->id); + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + return pc_clk_get_rate(clk->id); +} +EXPORT_SYMBOL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + if (clk->flags & CLKFLAG_USE_MIN_MAX_TO_SET) { + ret = pc_clk_set_max_rate(clk->id, rate); + if (ret) + return ret; + return pc_clk_set_min_rate(clk->id, rate); + } + return pc_clk_set_rate(clk->id, rate); +} +EXPORT_SYMBOL(clk_set_rate); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + return -ENOSYS; +} +EXPORT_SYMBOL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + return ERR_PTR(-ENOSYS); +} +EXPORT_SYMBOL(clk_get_parent); + +int clk_set_flags(struct clk *clk, unsigned long flags) +{ + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + return pc_clk_set_flags(clk->id, flags); +} +EXPORT_SYMBOL(clk_set_flags); + + +void __init msm_clock_init(void) +{ + unsigned n; + + spin_lock_init(&clocks_lock); + mutex_lock(&clocks_mutex); + for (n = 0; n < msm_num_clocks; n++) + list_add_tail(&msm_clocks[n].list, &clocks); + mutex_unlock(&clocks_mutex); +} + +/* The bootloader and/or AMSS may have left various clocks enabled. + * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have + * not been explicitly enabled by a clk_enable() call. + */ +static int __init clock_late_init(void) +{ + unsigned long flags; + struct clk *clk; + unsigned count = 0; + + mutex_lock(&clocks_mutex); + list_for_each_entry(clk, &clocks, list) { + if (clk->flags & CLKFLAG_AUTO_OFF) { + spin_lock_irqsave(&clocks_lock, flags); + if (!clk->count) { + count++; + pc_clk_disable(clk->id); + } + spin_unlock_irqrestore(&clocks_lock, flags); + } + } + mutex_unlock(&clocks_mutex); + pr_info("clock_late_init() disabled %d unused clocks\n", count); + return 0; +} + +late_initcall(clock_late_init); diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h new file mode 100644 index 000000000000..f875e1544e5f --- /dev/null +++ b/arch/arm/mach-msm/clock.h @@ -0,0 +1,48 @@ +/* arch/arm/mach-msm/clock.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ARCH_ARM_MACH_MSM_CLOCK_H +#define __ARCH_ARM_MACH_MSM_CLOCK_H + +#include + +#define CLKFLAG_INVERT 0x00000001 +#define CLKFLAG_NOINVERT 0x00000002 +#define CLKFLAG_NONEST 0x00000004 +#define CLKFLAG_NORESET 0x00000008 + +#define CLK_FIRST_AVAILABLE_FLAG 0x00000100 +#define CLKFLAG_USE_MIN_MAX_TO_SET 0x00000200 +#define CLKFLAG_AUTO_OFF 0x00000400 + +struct clk { + uint32_t id; + uint32_t count; + uint32_t flags; + const char *name; + struct list_head list; + struct device *dev; +}; + +#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) +#define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) +#define A11S_VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124) + +extern struct clk msm_clocks[]; +extern unsigned msm_num_clocks; + +#endif + diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index a7639493c095..264d62e519f3 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -33,5 +33,6 @@ void __init msm_add_devices(void); void __init msm_map_common_io(void); void __init msm_init_irq(void); void __init msm_init_gpio(void); +void __init msm_clock_init(void); #endif