From 3cfb6a066b6801186b716a4f789b69655e1e420e Mon Sep 17 00:00:00 2001 From: Vadim Bendebury Date: Wed, 11 Feb 2015 15:13:04 -0800 Subject: ipq806x: add LPASS clock control driver Add a clock control driver to initialize the clock tree inside the low-power audio subsystem. Depthcharge builds up on this to enable audio function on storm. The clock is hardcoded for 48KHz frame rate, two 16 bit channels. BRANCH=storm BUG=chrome-os-partner:35247 TEST=with depthcharge patches applied and Using depthcharge CLI audio test program verified that the target generates sensible sounds audio 100 100 audio 1000 5000 Change-Id: I56513fc782657ade99b6e43b2d5d3141d27ecc4e Signed-off-by: Patrick Georgi Original-Commit-Id: 0d4f408408aa38b2f0ee19b83ed490de39074760 Original-Change-Id: If8ffc326698fcea17e05d536930d927ca553481f Original-Signed-off-by: Kenneth Westfield Original-Signed-off-by: Vadim Bendebury Original-Reviewed-on: https://chromium-review.googlesource.com/248830 Original-Reviewed-by: Dylan Reid Reviewed-on: http://review.coreboot.org/9758 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/mainboard/google/storm/mainboard.c | 8 + src/soc/qualcomm/ipq806x/Makefile.inc | 1 + src/soc/qualcomm/ipq806x/include/soc/clock.h | 1 + src/soc/qualcomm/ipq806x/include/soc/lcc-reg.h | 341 +++++++++++++++++++++++++ src/soc/qualcomm/ipq806x/lcc.c | 316 +++++++++++++++++++++++ 5 files changed, 667 insertions(+) create mode 100644 src/soc/qualcomm/ipq806x/include/soc/lcc-reg.h create mode 100644 src/soc/qualcomm/ipq806x/lcc.c diff --git a/src/mainboard/google/storm/mainboard.c b/src/mainboard/google/storm/mainboard.c index f83cfe99ad..db743cfa2e 100644 --- a/src/mainboard/google/storm/mainboard.c +++ b/src/mainboard/google/storm/mainboard.c @@ -98,6 +98,14 @@ static void mainboard_init(device_t dev) /* Copy WIFI calibration data into CBMEM. */ cbmem_add_vpd_calibration_data(); #endif + + /* + * Make sure bootloader can issue sounds The frequency is calculated + * as " * * * 4", i.e. + * + * 48000 * 2 * 16 * 4 = 6144000 + */ + audio_clock_config(6144000); } static void mainboard_enable(device_t dev) diff --git a/src/soc/qualcomm/ipq806x/Makefile.inc b/src/soc/qualcomm/ipq806x/Makefile.inc index e0f014af35..80bd0587b1 100644 --- a/src/soc/qualcomm/ipq806x/Makefile.inc +++ b/src/soc/qualcomm/ipq806x/Makefile.inc @@ -44,6 +44,7 @@ ramstage-y += blobs_init.c ramstage-y += cbmem.c ramstage-y += clock.c ramstage-y += gpio.c +ramstage-y += lcc.c ramstage-y += soc.c ramstage-$(CONFIG_SPI_FLASH) += spi.c ramstage-y += timer.c diff --git a/src/soc/qualcomm/ipq806x/include/soc/clock.h b/src/soc/qualcomm/ipq806x/include/soc/clock.h index 837b831680..ab50d3f32f 100644 --- a/src/soc/qualcomm/ipq806x/include/soc/clock.h +++ b/src/soc/qualcomm/ipq806x/include/soc/clock.h @@ -192,5 +192,6 @@ void uart_clock_config(unsigned int gsbi_port, unsigned int m, unsigned int n, unsigned int d, unsigned int clk_dummy); void nand_clock_config(void); void usb_clock_config(void); +int audio_clock_config(unsigned frequency); #endif /* __PLATFORM_IPQ860X_CLOCK_H_ */ diff --git a/src/soc/qualcomm/ipq806x/include/soc/lcc-reg.h b/src/soc/qualcomm/ipq806x/include/soc/lcc-reg.h new file mode 100644 index 0000000000..2827ac94ed --- /dev/null +++ b/src/soc/qualcomm/ipq806x/include/soc/lcc-reg.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * 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. + */ + +#ifndef __DRIVERS_CLOCK_IPQ806X_LCC_REG_H__ +#define __DRIVERS_CLOCK_IPQ806X_LCC_REG_H__ + +#define MSM_GCC_BASE 0x00900000 +#define MSM_LPASS_LCC_BASE 0x28000000 + +/* GCC APCS Configuration/Control */ + +#define GCC_PLL_APCS_REG 0x34C0 + +#define GCC_PLL_APCS_PLL4_MASK 0x10 +#define GCC_PLL_APCS_PLL4_SHIFT 4 +#define GCC_PLL_APCS_PLL4_ENABLE (1 << GCC_PLL_APCS_PLL4_SHIFT) + +/* LCC PLL0 Configuration/Control */ + +#define LCC_PLL0_MODE_REG 0x00 +#define LCC_PLL0_L_REG 0x04 +#define LCC_PLL0_M_REG 0x08 +#define LCC_PLL0_N_REG 0x0C +#define LCC_PLL0_CFG_REG 0x14 +#define LCC_PLL0_STAT_REG 0x18 + +#define LCC_PLL0_MODE_FSM_RESET_MASK 0x200000 +#define LCC_PLL0_MODE_FSM_RESET_SHIFT 21 +#define LCC_PLL0_MODE_FSM_RESET_ASSERT (1 << LCC_PLL0_MODE_FSM_RESET_SHIFT) + +#define LCC_PLL0_MODE_FSM_VOTE_MASK 0x100000 +#define LCC_PLL0_MODE_FSM_VOTE_SHIFT 20 +#define LCC_PLL0_MODE_FSM_VOTE_ENABLE (1 << LCC_PLL0_MODE_FSM_VOTE_SHIFT) + +#define LCC_PLL0_MODE_BIAS_CNT_MASK 0xFC000 +#define LCC_PLL0_MODE_BIAS_CNT_SHIFT 14 + +#define LCC_PLL0_MODE_LOCK_CNT_MASK 0x3F00 +#define LCC_PLL0_MODE_LOCK_CNT_SHIFT 8 + +#define LCC_PLL0_MODE_XO_SEL_MASK 0x30 +#define LCC_PLL0_MODE_XO_SEL_SHIFT 4 +#define LCC_PLL0_MODE_XO_SEL_PXO (0 << LCC_PLL0_MODE_XO_SEL_SHIFT) +#define LCC_PLL0_MODE_XO_SEL_MXO (1 << LCC_PLL0_MODE_XO_SEL_SHIFT) +#define LCC_PLL0_MODE_XO_SEL_CXO (2 << LCC_PLL0_MODE_XO_SEL_SHIFT) + +#define LCC_PLL0_MODE_TEST_MASK 0x8 +#define LCC_PLL0_MODE_TEST_SHIFT 3 +#define LCC_PLL0_MODE_TEST_ENABLE (1 << LCC_PLL0_MODE_TEST_SHIFT) + +#define LCC_PLL0_MODE_RESET_MASK 0x4 +#define LCC_PLL0_MODE_RESET_SHIFT 2 +#define LCC_PLL0_MODE_RESET_DEASSERT (1 << LCC_PLL0_MODE_RESET_SHIFT) + +#define LCC_PLL0_MODE_BYPASS_MASK 0x2 +#define LCC_PLL0_MODE_BYPASS_SHIFT 1 +#define LCC_PLL0_MODE_BYPASS_DISABLE (1 << LCC_PLL0_MODE_BYPASS_SHIFT) + +#define LCC_PLL0_MODE_OUTPUT_MASK 0x1 +#define LCC_PLL0_MODE_OUTPUT_SHIFT 0 +#define LCC_PLL0_MODE_OUTPUT_ENABLE (1 << LCC_PLL0_MODE_OUTPUT_SHIFT) + +#define LCC_PLL0_L_MASK 0x3FF +#define LCC_PLL0_L_SHIFT 0 + +#define LCC_PLL0_M_MASK 0x7FFFF +#define LCC_PLL0_M_SHIFT 0 + +#define LCC_PLL0_N_MASK 0x7FFFF +#define LCC_PLL0_N_SHIFT 0 + +#define LCC_PLL0_CFG_LV_MAIN_MASK 0x800000 +#define LCC_PLL0_CFG_LV_MAIN_SHIFT 23 +#define LCC_PLL0_CFG_LV_MAIN_ENABLE (1 << LCC_PLL0_CFG_LV_MAIN_SHIFT) + +#define LCC_PLL0_CFG_FRAC_MASK 0x400000 +#define LCC_PLL0_CFG_FRAC_SHIFT 22 +#define LCC_PLL0_CFG_FRAC_ENABLE (1 << LCC_PLL0_CFG_FRAC_SHIFT) + +#define LCC_PLL0_CFG_POSTDIV_MASK 0x300000 +#define LCC_PLL0_CFG_POSTDIV_SHIFT 20 +#define LCC_PLL0_CFG_POSTDIV_DIV1 (0 << LCC_PLL0_CFG_POSTDIV_SHIFT) +#define LCC_PLL0_CFG_POSTDIV_DIV2 (1 << LCC_PLL0_CFG_POSTDIV_SHIFT) +#define LCC_PLL0_CFG_POSTDIV_DIV4 (2 << LCC_PLL0_CFG_POSTDIV_SHIFT) + +#define LCC_PLL0_CFG_PREDIV_MASK 0x80000 +#define LCC_PLL0_CFG_PREDIV_SHIFT 19 +#define LCC_PLL0_CFG_PREDIV_DIV1 (0 << LCC_PLL0_CFG_PREDIV_SHIFT) +#define LCC_PLL0_CFG_PREDIV_DIV2 (1 << LCC_PLL0_CFG_PREDIV_SHIFT) + +#define LCC_PLL0_CFG_VCO_SEL_MASK 0x30000 +#define LCC_PLL0_CFG_VCO_SEL_SHIFT 16 +#define LCC_PLL0_CFG_VCO_SEL_LOW (0 << LCC_PLL0_CFG_VCO_SEL_SHIFT) +#define LCC_PLL0_CFG_VCO_SEL_MED (1 << LCC_PLL0_CFG_VCO_SEL_SHIFT) +#define LCC_PLL0_CFG_VCO_SEL_HIGH (2 << LCC_PLL0_CFG_VCO_SEL_SHIFT) + +#define LCC_PLL0_STAT_ACTIVE_MASK 0x10000 +#define LCC_PLL0_STAT_ACTIVE_SHIFT 16 +#define LCC_PLL0_STAT_ACTIVE_SET (1 << LCC_PLL0_STAT_ACTIVE_SHIFT) + +#define LCC_PLL0_STAT_NOCLK_MASK 0x1 +#define LCC_PLL0_STAT_NOCLK_SHIFT 0 +#define LCC_PLL0_STAT_NOCLK_SET (1 << LCC_PLL0_STAT_NOCLK_SHIFT) + +/* LCC AHBIX Configuration/Control */ + +#define LCC_AHBIX_NS_REG 0x38 +#define LCC_AHBIX_MD_REG 0x3C +#define LCC_AHBIX_STAT_REG 0x44 + +#define LCC_AHBIX_NS_N_VAL_MASK 0xFF000000 +#define LCC_AHBIX_NS_N_VAL_SHIFT 24 + +#define LCC_AHBIX_NS_CRC_MASK 0x800 +#define LCC_AHBIX_NS_CRC_SHIFT 11 +#define LCC_AHBIX_NS_CRC_ENABLE (1 << LCC_AHBIX_NS_CRC_SHIFT) + +#define LCC_AHBIX_NS_GFM_SEL_MASK 0x400 +#define LCC_AHBIX_NS_GFM_SEL_SHIFT 10 +#define LCC_AHBIX_NS_GFM_SEL_PXO (0 << LCC_AHBIX_NS_GFM_SEL_SHIFT) +#define LCC_AHBIX_NS_GFM_SEL_MNC (1 << LCC_AHBIX_NS_GFM_SEL_SHIFT) + +#define LCC_AHBIX_NS_MNC_CLK_MASK 0x200 +#define LCC_AHBIX_NS_MNC_CLK_SHIFT 9 +#define LCC_AHBIX_NS_MNC_CLK_ENABLE (1 << LCC_AHBIX_NS_MNC_CLK_SHIFT) + +#define LCC_AHBIX_NS_MNC_MASK 0x100 +#define LCC_AHBIX_NS_MNC_SHIFT 8 +#define LCC_AHBIX_NS_MNC_ENABLE (1 << LCC_AHBIX_NS_MNC_SHIFT) + +#define LCC_AHBIX_NS_MNC_RESET_MASK 0x80 +#define LCC_AHBIX_NS_MNC_RESET_SHIFT 7 +#define LCC_AHBIX_NS_MNC_RESET_ASSERT (1 << LCC_AHBIX_NS_MNC_RESET_SHIFT) + +#define LCC_AHBIX_NS_MNC_MODE_MASK 0x60 +#define LCC_AHBIX_NS_MNC_MODE_SHIFT 5 +#define LCC_AHBIX_NS_MNC_MODE_BYPASS (0 << LCC_AHBIX_NS_MNC_MODE_SHIFT) +#define LCC_AHBIX_NS_MNC_MODE_SWALLOW (1 << LCC_AHBIX_NS_MNC_MODE_SHIFT) +#define LCC_AHBIX_NS_MNC_MODE_DUAL (2 << LCC_AHBIX_NS_MNC_MODE_SHIFT) +#define LCC_AHBIX_NS_MNC_MODE_SINGLE (3 << LCC_AHBIX_NS_MNC_MODE_SHIFT) + +#define LCC_AHBIX_NS_PREDIV_MASK 0x18 +#define LCC_AHBIX_NS_PREDIV_SHIFT 3 +#define LCC_AHBIX_NS_PREDIV_BYPASS (0 << LCC_AHBIX_NS_PREDIV_SHIFT) +#define LCC_AHBIX_NS_PREDIV_DIV2 (1 << LCC_AHBIX_NS_PREDIV_SHIFT) +#define LCC_AHBIX_NS_PREDIV_DIV4 (3 << LCC_AHBIX_NS_PREDIV_SHIFT) + +#define LCC_AHBIX_NS_MN_SRC_MASK 0x7 +#define LCC_AHBIX_NS_MN_SRC_SHIFT 0 +#define LCC_AHBIX_NS_MN_SRC_PXO (0 << LCC_AHBIX_NS_MN_SRC_SHIFT) +#define LCC_AHBIX_NS_MN_SRC_CXO (1 << LCC_AHBIX_NS_MN_SRC_SHIFT) +#define LCC_AHBIX_NS_MN_SRC_LPA (2 << LCC_AHBIX_NS_MN_SRC_SHIFT) +#define LCC_AHBIX_NS_MN_SRC_SEC (3 << LCC_AHBIX_NS_MN_SRC_SHIFT) +#define LCC_AHBIX_NS_MN_SRC_CTEST (6 << LCC_AHBIX_NS_MN_SRC_SHIFT) +#define LCC_AHBIX_NS_MN_SRC_PTEST (7 << LCC_AHBIX_NS_MN_SRC_SHIFT) + +#define LCC_AHBIX_MD_M_VAL_MASK 0xFF00 +#define LCC_AHBIX_MD_M_VAL_SHIFT 8 + +#define LCC_AHBIX_MD_NOT_2D_VAL_MASK 0xFF +#define LCC_AHBIX_MD_NOT_2D_VAL_SHIFT 0 + +#define LCC_AHBIX_STAT_AHB_CLK_MASK 0x400 +#define LCC_AHBIX_STAT_AHB_CLK_SHIFT 10 +#define LCC_AHBIX_STAT_AHB_CLK_ON (1 << LCC_AHBIX_STAT_AHB_CLK_SHIFT) + +#define LCC_AHBIX_STAT_AIF_CLK_MASK 0x200 +#define LCC_AHBIX_STAT_AIF_CLK_SHIFT 9 +#define LCC_AHBIX_STAT_AIF_CLK_ON (1 << LCC_AHBIX_STAT_AIF_CLK_SHIFT) + +#define LCC_AHBIX_STAT_FAB2_CLK_MASK 0x40 +#define LCC_AHBIX_STAT_FAB2_CLK_SHIFT 6 +#define LCC_AHBIX_STAT_FAB2_CLK_ON (1 << LCC_AHBIX_STAT_FAB2_CLK_SHIFT) + +#define LCC_AHBIX_STAT_2FAB_CLK_MASK 0x20 +#define LCC_AHBIX_STAT_2FAB_CLK_SHIFT 5 +#define LCC_AHBIX_STAT_2FAB_CLK_ON (1 << LCC_AHBIX_STAT_2FAB_CLK_SHIFT) + +/* LCC MI2S Configuration/Control */ + +#define LCC_MI2S_NS_REG 0x48 +#define LCC_MI2S_MD_REG 0x4C +#define LCC_MI2S_STAT_REG 0x50 + +#define LCC_MI2S_NS_N_VAL_MASK 0xFF000000 +#define LCC_MI2S_NS_N_VAL_SHIFT 24 + +#define LCC_MI2S_NS_RESET_MASK 0x80000 +#define LCC_MI2S_NS_RESET_SHIFT 19 +#define LCC_MI2S_NS_RESET_ASSERT (1 << LCC_MI2S_NS_RESET_SHIFT) + +#define LCC_MI2S_NS_OSR_INV_MASK 0x40000 +#define LCC_MI2S_NS_OSR_INV_SHIFT 18 +#define LCC_MI2S_NS_OSR_INV_ENABLE (1 << LCC_MI2S_NS_OSR_INV_SHIFT) + +#define LCC_MI2S_NS_OSR_CXC_MASK 0x20000 +#define LCC_MI2S_NS_OSR_CXC_SHIFT 17 +#define LCC_MI2S_NS_OSR_CXC_ENABLE (1 << LCC_MI2S_NS_OSR_CXC_SHIFT) + +#define LCC_MI2S_NS_BIT_INV_MASK 0x10000 +#define LCC_MI2S_NS_BIT_INV_SHIFT 16 +#define LCC_MI2S_NS_BIT_INV_ENABLE (1 << LCC_MI2S_NS_BIT_INV_SHIFT) + +#define LCC_MI2S_NS_BIT_CXC_MASK 0x8000 +#define LCC_MI2S_NS_BIT_CXC_SHIFT 15 +#define LCC_MI2S_NS_BIT_CXC_ENABLE (1 << LCC_MI2S_NS_BIT_CXC_SHIFT) + +#define LCC_MI2S_NS_BIT_SRC_MASK 0x4000 +#define LCC_MI2S_NS_BIT_SRC_SHIFT 14 +#define LCC_MI2S_NS_BIT_SRC_MASTER (0 << LCC_MI2S_NS_BIT_SRC_SHIFT) +#define LCC_MI2S_NS_BIT_SRC_SLAVE (1 << LCC_MI2S_NS_BIT_SRC_SHIFT) + +#define LCC_MI2S_NS_BIT_DIV_MASK 0x3C00 +#define LCC_MI2S_NS_BIT_DIV_SHIFT 10 +#define LCC_MI2S_NS_BIT_DIV_BYPASS (0 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV2 (1 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV3 (2 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV4 (3 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV5 (4 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV6 (5 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV7 (6 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV8 (7 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV9 (8 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV10 (9 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV11 (10 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV12 (11 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV13 (12 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV14 (13 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV15 (14 << LCC_MI2S_NS_BIT_DIV_SHIFT) +#define LCC_MI2S_NS_BIT_DIV_DIV16 (15 << LCC_MI2S_NS_BIT_DIV_SHIFT) + +#define LCC_MI2S_NS_MNC_CLK_MASK 0x200 +#define LCC_MI2S_NS_MNC_CLK_SHIFT 9 +#define LCC_MI2S_NS_MNC_CLK_ENABLE (1 << LCC_MI2S_NS_MNC_CLK_SHIFT) + +#define LCC_MI2S_NS_MNC_MASK 0x100 +#define LCC_MI2S_NS_MNC_SHIFT 8 +#define LCC_MI2S_NS_MNC_ENABLE (1 << LCC_MI2S_NS_MNC_SHIFT) + +#define LCC_MI2S_NS_MNC_RESET_MASK 0x80 +#define LCC_MI2S_NS_MNC_RESET_SHIFT 7 +#define LCC_MI2S_NS_MNC_RESET_ASSERT (1 << LCC_MI2S_NS_MNC_RESET_SHIFT) + +#define LCC_MI2S_NS_MNC_MODE_MASK 0x60 +#define LCC_MI2S_NS_MNC_MODE_SHIFT 5 +#define LCC_MI2S_NS_MNC_MODE_BYPASS (0 << LCC_MI2S_NS_MNC_MODE_SHIFT) +#define LCC_MI2S_NS_MNC_MODE_SWALLOW (1 << LCC_MI2S_NS_MNC_MODE_SHIFT) +#define LCC_MI2S_NS_MNC_MODE_DUAL (2 << LCC_MI2S_NS_MNC_MODE_SHIFT) +#define LCC_MI2S_NS_MNC_MODE_SINGLE (3 << LCC_MI2S_NS_MNC_MODE_SHIFT) + +#define LCC_MI2S_NS_PREDIV_MASK 0x18 +#define LCC_MI2S_NS_PREDIV_SHIFT 3 +#define LCC_MI2S_NS_PREDIV_BYPASS (0 << LCC_MI2S_NS_PREDIV_SHIFT) +#define LCC_MI2S_NS_PREDIV_DIV2 (1 << LCC_MI2S_NS_PREDIV_SHIFT) +#define LCC_MI2S_NS_PREDIV_DIV4 (3 << LCC_MI2S_NS_PREDIV_SHIFT) + +#define LCC_MI2S_NS_MN_SRC_MASK 0x7 +#define LCC_MI2S_NS_MN_SRC_SHIFT 0 +#define LCC_MI2S_NS_MN_SRC_PXO (0 << LCC_MI2S_NS_MN_SRC_SHIFT) +#define LCC_MI2S_NS_MN_SRC_CXO (1 << LCC_MI2S_NS_MN_SRC_SHIFT) +#define LCC_MI2S_NS_MN_SRC_LPA (2 << LCC_MI2S_NS_MN_SRC_SHIFT) +#define LCC_MI2S_NS_MN_SRC_SEC (3 << LCC_MI2S_NS_MN_SRC_SHIFT) +#define LCC_MI2S_NS_MN_SRC_CTEST (6 << LCC_MI2S_NS_MN_SRC_SHIFT) +#define LCC_MI2S_NS_MN_SRC_PTEST (7 << LCC_MI2S_NS_MN_SRC_SHIFT) + +#define LCC_MI2S_MD_M_VAL_MASK 0xFF00 +#define LCC_MI2S_MD_M_VAL_SHIFT 8 + +#define LCC_MI2S_MD_NOT_2D_VAL_MASK 0xFF +#define LCC_MI2S_MD_NOT_2D_VAL_SHIFT 0 + +#define LCC_MI2S_STAT_OSR_CLK_MASK 0x2 +#define LCC_MI2S_STAT_OSR_CLK_SHIFT 1 +#define LCC_MI2S_STAT_OSR_CLK_ON (1 << LCC_MI2S_STAT_OSR_CLK_SHIFT) + +#define LCC_MI2S_STAT_BIT_CLK_MASK 0x1 +#define LCC_MI2S_STAT_BIT_CLK_SHIFT 0 +#define LCC_MI2S_STAT_BIT_CLK_ON (1 << LCC_MI2S_STAT_BIT_CLK_SHIFT) + +/* LCC PLL Configuration/Control */ + +#define LCC_PLL_PCLK_REG 0xC4 +#define LCC_PLL_SCLK_REG 0xC8 + +#define LCC_PLL_PCLK_RESET_MASK 0x2 +#define LCC_PLL_PCLK_RESET_SHIFT 1 +#define LCC_PLL_PCLK_RESET_ASSERT (1 << LCC_PLL_PCLK_RESET_SHIFT) + +#define LCC_PLL_PCLK_SRC_MASK 0x1 +#define LCC_PLL_PCLK_SRC_SHIFT 0 +#define LCC_PLL_PCLK_SRC_PXO (0 << LCC_PLL_PCLK_SRC_SHIFT) +#define LCC_PLL_PCLK_SRC_PRI (1 << LCC_PLL_PCLK_SRC_SHIFT) + +#define LCC_PLL_SCLK_RESET_MASK 0x10 +#define LCC_PLL_SCLK_RESET_SHIFT 4 +#define LCC_PLL_SCLK_RESET_ASSERT (1 << LCC_PLL_SCLK_RESET_SHIFT) + +#define LCC_PLL_SCLK_DIV_MASK 0xC +#define LCC_PLL_SCLK_DIV_SHIFT 2 +#define LCC_PLL_SCLK_DIV_BYPASS (0 << LCC_PLL_SCLK_DIV_SHIFT) +#define LCC_PLL_SCLK_DIV_DIV2 (1 << LCC_PLL_SCLK_DIV_SHIFT) +#define LCC_PLL_SCLK_DIV_DIV3 (2 << LCC_PLL_SCLK_DIV_SHIFT) +#define LCC_PLL_SCLK_DIV_DIV4 (3 << LCC_PLL_SCLK_DIV_SHIFT) + +#define LCC_PLL_SCLK_XO_MASK 0x2 +#define LCC_PLL_SCLK_XO_SHIFT 1 +#define LCC_PLL_SCLK_XO_PXO (0 << LCC_PLL_SCLK_XO_SHIFT) +#define LCC_PLL_SCLK_XO_SEC (1 << LCC_PLL_SCLK_XO_SHIFT) + +#define LCC_PLL_SCLK_MUX_MASK 0x1 +#define LCC_PLL_SCLK_MUX_SHIFT 0 +#define LCC_PLL_SCLK_MUX_PLL1 (0 << LCC_PLL_SCLK_MUX_SHIFT) +#define LCC_PLL_SCLK_MUX_PLL0 (1 << LCC_PLL_SCLK_MUX_SHIFT) + +#endif /* __DRIVERS_CLOCK_IPQ806X_LCC_REG_H__ */ diff --git a/src/soc/qualcomm/ipq806x/lcc.c b/src/soc/qualcomm/ipq806x/lcc.c new file mode 100644 index 0000000000..978526d8e7 --- /dev/null +++ b/src/soc/qualcomm/ipq806x/lcc.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * 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 +#include +#include +#include +#include +#include +#include + +typedef struct { + void *gcc_apcs_regs; + void *lcc_pll0_regs; + void *lcc_ahbix_regs; + void *lcc_mi2s_regs; + void *lcc_pll_regs; +} Ipq806xLccClocks; + +typedef struct __attribute__((packed)) { + uint32_t apcs; +} Ipq806xLccGccRegs; + +typedef struct __attribute__((packed)) { + uint32_t mode; + uint32_t l_val; + uint32_t m_val; + uint32_t n_val; + uint32_t UNUSED; + uint32_t config; + uint32_t status; +} Ipq806xLccPll0Regs; + +typedef struct __attribute__((packed)) { + uint32_t ns; + uint32_t md; + uint32_t UNUSED; + uint32_t status; +} Ipq806xLccAhbixRegs; + +typedef struct __attribute__((packed)) { + uint32_t ns; + uint32_t md; + uint32_t status; +} Ipq806xLccMi2sRegs; + +typedef struct __attribute__((packed)) { + uint32_t pri; + uint32_t sec; +} Ipq806xLccPllRegs; + +struct lcc_freq_tbl { + unsigned freq; + unsigned pd; + unsigned m; + unsigned n; + unsigned d; +}; + +static const struct lcc_freq_tbl lcc_mi2s_freq_tbl[] = { + { 1024000, 4, 1, 96, 8 }, + { 1411200, 4, 2, 139, 8 }, + { 1536000, 4, 1, 64, 8 }, + { 2048000, 4, 1, 48, 8 }, + { 2116800, 4, 2, 93, 8 }, + { 2304000, 4, 2, 85, 8 }, + { 2822400, 4, 6, 209, 8 }, + { 3072000, 4, 1, 32, 8 }, + { 3175200, 4, 1, 31, 8 }, + { 4096000, 4, 1, 24, 8 }, + { 4233600, 4, 9, 209, 8 }, + { 4608000, 4, 3, 64, 8 }, + { 5644800, 4, 12, 209, 8 }, + { 6144000, 4, 1, 16, 8 }, + { 6350400, 4, 2, 31, 8 }, + { 8192000, 4, 1, 12, 8 }, + { 8467200, 4, 18, 209, 8 }, + { 9216000, 4, 3, 32, 8 }, + { 11289600, 4, 24, 209, 8 }, + { 12288000, 4, 1, 8, 8 }, + { 12700800, 4, 27, 209, 8 }, + { 13824000, 4, 9, 64, 8 }, + { 16384000, 4, 1, 6, 8 }, + { 16934400, 4, 41, 238, 8 }, + { 18432000, 4, 3, 16, 8 }, + { 22579200, 2, 24, 209, 8 }, + { 24576000, 4, 1, 4, 8 }, + { 27648000, 4, 9, 32, 8 }, + { 33868800, 4, 41, 119, 8 }, + { 36864000, 4, 3, 8, 8 }, + { 45158400, 1, 24, 209, 8 }, + { 49152000, 4, 1, 2, 8 }, + { 50803200, 1, 27, 209, 8 }, + { } +}; + +static int lcc_init_enable_pll0(Ipq806xLccClocks *bus) +{ + Ipq806xLccGccRegs *gcc_regs = bus->gcc_apcs_regs; + Ipq806xLccPll0Regs *pll0_regs = bus->lcc_pll0_regs; + Ipq806xLccPllRegs *pll_regs = bus->lcc_pll_regs; + uint32_t regval; + + regval = 0; + regval = 15 << LCC_PLL0_L_SHIFT & LCC_PLL0_L_MASK; + writel(regval, &pll0_regs->l_val); + + regval = 0; + regval = 145 << LCC_PLL0_M_SHIFT & LCC_PLL0_M_MASK; + writel(regval, &pll0_regs->m_val); + + regval = 0; + regval = 199 << LCC_PLL0_N_SHIFT & LCC_PLL0_N_MASK; + writel(regval, &pll0_regs->n_val); + + regval = 0; + regval |= LCC_PLL0_CFG_LV_MAIN_ENABLE; + regval |= LCC_PLL0_CFG_FRAC_ENABLE; + writel(regval, &pll0_regs->config); + + regval = 0; + regval |= LCC_PLL_PCLK_SRC_PRI; + writel(regval, &pll_regs->pri); + + regval = 0; + regval |= 1 << LCC_PLL0_MODE_BIAS_CNT_SHIFT & + LCC_PLL0_MODE_BIAS_CNT_MASK; + regval |= 8 << LCC_PLL0_MODE_LOCK_CNT_SHIFT & + LCC_PLL0_MODE_LOCK_CNT_MASK; + writel(regval, &pll0_regs->mode); + + regval = readl(&gcc_regs->apcs); + regval |= GCC_PLL_APCS_PLL4_ENABLE; + writel(regval, &gcc_regs->apcs); + + regval = readl(&pll0_regs->mode); + regval |= LCC_PLL0_MODE_FSM_VOTE_ENABLE; + writel(regval, &pll0_regs->mode); + + mdelay(1); + + regval = readl(&pll0_regs->status); + if (regval & LCC_PLL0_STAT_ACTIVE_MASK) + return 0; + + printk(BIOS_ERR, "%s: error enabling PLL4 clock\n", __func__); + return 1; +} + +static int lcc_init_enable_ahbix(Ipq806xLccClocks *bus) +{ + Ipq806xLccAhbixRegs *ahbix_regs = bus->lcc_ahbix_regs; + uint32_t regval; + + regval = 0; + regval |= 1 << LCC_AHBIX_MD_M_VAL_SHIFT & LCC_AHBIX_MD_M_VAL_MASK; + regval |= 252 << LCC_AHBIX_MD_NOT_2D_VAL_SHIFT & + LCC_AHBIX_MD_NOT_2D_VAL_MASK; + writel(regval, &ahbix_regs->md); + + regval = 0; + regval |= 253 << LCC_AHBIX_NS_N_VAL_SHIFT & LCC_AHBIX_NS_N_VAL_MASK; + regval |= LCC_AHBIX_NS_CRC_ENABLE; + regval |= LCC_AHBIX_NS_GFM_SEL_MNC; + regval |= LCC_AHBIX_NS_MNC_CLK_ENABLE; + regval |= LCC_AHBIX_NS_MNC_ENABLE; + regval |= LCC_AHBIX_NS_MNC_MODE_DUAL; + regval |= LCC_AHBIX_NS_PREDIV_BYPASS; + regval |= LCC_AHBIX_NS_MN_SRC_LPA; + writel(regval, &ahbix_regs->ns); + + mdelay(1); + + regval = readl(&ahbix_regs->status); + if (regval & LCC_AHBIX_STAT_AIF_CLK_MASK) + return 0; + + printk(BIOS_ERR, "%s: error enabling AHBIX clock\n", __func__); + return 1; +} + +static int lcc_init_mi2s(Ipq806xLccClocks *bus, unsigned freq) +{ + Ipq806xLccMi2sRegs *mi2s_regs = bus->lcc_mi2s_regs; + uint32_t regval; + uint8_t pd, m, n, d; + unsigned i; + + i = 0; + while (lcc_mi2s_freq_tbl[i].freq != 0) { + if (lcc_mi2s_freq_tbl[i].freq == freq) + break; + ++i; + } + if (lcc_mi2s_freq_tbl[i].freq == 0) { + printk(BIOS_ERR, "%s: invalid frequency given: %u\n", + __func__, freq); + return 1; + } + + switch (lcc_mi2s_freq_tbl[i].pd) { + case 1: + pd = LCC_MI2S_NS_PREDIV_BYPASS; + break; + case 2: + pd = LCC_MI2S_NS_PREDIV_DIV2; + break; + case 4: + pd = LCC_MI2S_NS_PREDIV_DIV4; + break; + default: + printk(BIOS_ERR, "%s: invalid prediv found: %u\n", __func__, + lcc_mi2s_freq_tbl[i].pd); + return 1; + } + + m = lcc_mi2s_freq_tbl[i].m; + n = ~(lcc_mi2s_freq_tbl[i].n - m); + d = ~(lcc_mi2s_freq_tbl[i].d * 2); + + regval = 0; + regval |= m << LCC_MI2S_MD_M_VAL_SHIFT & LCC_MI2S_MD_M_VAL_MASK; + regval |= d << LCC_MI2S_MD_NOT_2D_VAL_SHIFT & + LCC_MI2S_MD_NOT_2D_VAL_MASK; + writel(regval, &mi2s_regs->md); + + regval = 0; + regval |= n << LCC_MI2S_NS_N_VAL_SHIFT & LCC_MI2S_NS_N_VAL_MASK; + regval |= LCC_MI2S_NS_BIT_DIV_DIV4; + regval |= LCC_MI2S_NS_MNC_CLK_ENABLE; + regval |= LCC_MI2S_NS_MNC_ENABLE; + regval |= LCC_MI2S_NS_MNC_MODE_DUAL; + regval |= pd; + regval |= LCC_MI2S_NS_MN_SRC_LPA; + writel(regval, &mi2s_regs->ns); + + return 0; +} + +static int lcc_enable_mi2s(Ipq806xLccClocks *bus) +{ + Ipq806xLccMi2sRegs *mi2s_regs = bus->lcc_mi2s_regs; + uint32_t regval; + + regval = readl(&mi2s_regs->ns); + regval |= LCC_MI2S_NS_OSR_CXC_ENABLE; + regval |= LCC_MI2S_NS_BIT_CXC_ENABLE; + writel(regval, &mi2s_regs->ns); + + udelay(10); + + regval = readl(&mi2s_regs->status); + if (regval & LCC_MI2S_STAT_OSR_CLK_MASK) + if (regval & LCC_MI2S_STAT_BIT_CLK_MASK) + return 0; + + printk(BIOS_ERR, "%s: error enabling MI2S clocks: %u\n", + __func__, regval); + return 1; +} + +int audio_clock_config(unsigned frequency) +{ + Ipq806xLccClocks *bus = malloc(sizeof(*bus)); + + if (!bus) { + printk(BIOS_ERR, "%s: failed to allocate bus structure\n", + __func__); + return 1; + } + + bus->gcc_apcs_regs = (void *)(MSM_GCC_BASE + GCC_PLL_APCS_REG); + bus->lcc_pll0_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_PLL0_MODE_REG); + bus->lcc_ahbix_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_AHBIX_NS_REG); + bus->lcc_mi2s_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_MI2S_NS_REG); + bus->lcc_pll_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_PLL_PCLK_REG); + + + if (lcc_init_enable_pll0(bus)) + return 1; + if (lcc_init_enable_ahbix(bus)) + return 1; + if (lcc_init_mi2s(bus, frequency)) + return 1; + + if (lcc_enable_mi2s(bus)) + return 1; + + return 0; +} -- cgit v1.2.3