| /* |
| * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved. |
| |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only 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. |
| */ |
| |
| #include <common.h> |
| #include <asm/arch-ipq40xx/clk.h> |
| #include <asm/arch-qca-common/iomap.h> |
| #include <asm/io.h> |
| #include <asm/errno.h> |
| |
| #define GCC_SDCC1_MISC 0x1818014 |
| #define GCC_SDCC1_APPS_CBCR 0x181800C |
| #define GCC_SDCC1_APPS_RCGR 0x1818008 |
| #define GCC_SDCC1_APPS_CMD_RCGR 0x1818004 |
| |
| void emmc_clock_config(int mode) |
| { |
| /* Select SDCC clock source as DDR_PLL_SDCC1_CLK 192MHz */ |
| writel(0x100, GCC_SDCC1_APPS_RCGR); |
| /* Update APPS_CMD_RCGR to reflect source selection */ |
| writel(0x1, GCC_SDCC1_APPS_CMD_RCGR); |
| udelay(10); |
| |
| if (mode == MMC_IDENTIFY_MODE) { |
| /* Set root clock generator to bypass mode */ |
| writel(0x0, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| /* Choose divider for 400KHz */ |
| writel(0x1e4 , GCC_SDCC1_MISC); |
| /* Enable root clock generator */ |
| writel(0x1, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| } |
| if (mode == MMC_DATA_TRANSFER_MODE) { |
| /* Set root clock generator to bypass mode */ |
| writel(0x0, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| /* Choose divider for 48MHz */ |
| writel(0x3, GCC_SDCC1_MISC); |
| /* Enable root clock generator */ |
| writel(0x1, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| } |
| if (mode == MMC_DATA_TRANSFER_SDHCI_MODE) { |
| /* Set root clock generator to bypass mode */ |
| writel(0x0, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| /* Choose divider for 192MHz */ |
| writel(0x0, GCC_SDCC1_MISC); |
| /* Enable root clock generator */ |
| writel(0x1, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| } |
| } |
| void emmc_clock_disable(void) |
| { |
| /* Clear divider */ |
| writel(0x0, GCC_SDCC1_MISC); |
| writel(0x0, GCC_SDCC1_APPS_CBCR); |
| udelay(10); |
| |
| } |
| |
| void uart2_configure_mux(void) |
| { |
| unsigned long cfg_rcgr; |
| |
| cfg_rcgr = readl(GCC_BLSP1_UART2_APPS_CFG_RCGR); |
| /* Clear mode, src sel, src div */ |
| cfg_rcgr &= ~(GCC_UART_CFG_RCGR_MODE_MASK | |
| GCC_UART_CFG_RCGR_SRCSEL_MASK | |
| GCC_UART_CFG_RCGR_SRCDIV_MASK); |
| |
| cfg_rcgr |= ((UART2_RCGR_SRC_SEL << GCC_UART_CFG_RCGR_SRCSEL_SHIFT) |
| & GCC_UART_CFG_RCGR_SRCSEL_MASK); |
| |
| cfg_rcgr |= ((UART2_RCGR_SRC_DIV << GCC_UART_CFG_RCGR_SRCDIV_SHIFT) |
| & GCC_UART_CFG_RCGR_SRCDIV_MASK); |
| |
| cfg_rcgr |= ((UART2_RCGR_MODE << GCC_UART_CFG_RCGR_MODE_SHIFT) |
| & GCC_UART_CFG_RCGR_MODE_MASK); |
| |
| writel(cfg_rcgr, GCC_BLSP1_UART2_APPS_CFG_RCGR); |
| } |
| |
| void uart2_set_rate_mnd(unsigned int m, |
| unsigned int n, unsigned int two_d) |
| { |
| writel(m, GCC_BLSP1_UART2_APPS_M); |
| writel(NOT_N_MINUS_M(n, m), GCC_BLSP1_UART2_APPS_N); |
| writel(NOT_2D(two_d), GCC_BLSP1_UART2_APPS_D); |
| } |
| |
| int uart2_trigger_update(void) |
| { |
| unsigned long cmd_rcgr; |
| int timeout = 0; |
| |
| cmd_rcgr = readl(GCC_BLSP1_UART2_APPS_CMD_RCGR); |
| cmd_rcgr |= UART2_CMD_RCGR_UPDATE; |
| writel(cmd_rcgr, GCC_BLSP1_UART2_APPS_CMD_RCGR); |
| |
| while (readl(GCC_BLSP1_UART2_APPS_CMD_RCGR) & UART2_CMD_RCGR_UPDATE) { |
| if (timeout++ >= CLOCK_UPDATE_TIMEOUT_US) { |
| printf("Timeout waiting for UART2 clock update\n"); |
| return -ETIMEDOUT; |
| } |
| udelay(1); |
| } |
| cmd_rcgr = readl(GCC_BLSP1_UART2_APPS_CMD_RCGR); |
| return 0; |
| } |
| |
| void uart2_toggle_clock(void) |
| { |
| unsigned long cbcr_val; |
| |
| cbcr_val = readl(GCC_BLSP1_UART2_APPS_CBCR); |
| cbcr_val |= UART2_CBCR_CLK_ENABLE; |
| writel(cbcr_val, GCC_BLSP1_UART2_APPS_CBCR); |
| } |
| |
| void uart2_clock_config(unsigned int m, |
| unsigned int n, unsigned int two_d) |
| { |
| uart2_configure_mux(); |
| uart2_set_rate_mnd(m, n, two_d); |
| uart2_trigger_update(); |
| uart2_toggle_clock(); |
| } |
| |
| #ifdef CONFIG_IPQ40XX_I2C |
| void i2c0_configure_mux(void) |
| { |
| unsigned long cfg_rcgr; |
| |
| cfg_rcgr = readl(GCC_BLSP1_QUP1_I2C_APPS_CFG_RCGR); |
| /* Clear mode, src sel, src div */ |
| cfg_rcgr &= ~(GCC_I2C_CFG_RCGR_SRCSEL_MASK | |
| GCC_I2C_CFG_RCGR_SRCDIV_MASK); |
| |
| cfg_rcgr |= ((I2C0_RCGR_SRC_SEL << GCC_I2C_CFG_RCGR_SRCSEL_SHIFT) |
| & GCC_UART_CFG_RCGR_SRCSEL_MASK); |
| |
| cfg_rcgr |= ((I2C0_RCGR_SRC_DIV << GCC_I2C_CFG_RCGR_SRCDIV_SHIFT) |
| & GCC_UART_CFG_RCGR_SRCDIV_MASK); |
| |
| writel(cfg_rcgr, GCC_BLSP1_QUP1_I2C_APPS_CFG_RCGR); |
| } |
| |
| int i2c0_trigger_update(void) |
| { |
| unsigned long cmd_rcgr; |
| int timeout = 0; |
| |
| cmd_rcgr = readl(GCC_BLSP1_QUP1_I2C_APPS_CMD_RCGR); |
| cmd_rcgr |= I2C0_CMD_RCGR_UPDATE; |
| writel(cmd_rcgr, GCC_BLSP1_QUP1_I2C_APPS_CMD_RCGR); |
| |
| while (readl(GCC_BLSP1_QUP1_I2C_APPS_CMD_RCGR) & I2C0_CMD_RCGR_UPDATE) { |
| if (timeout++ >= CLOCK_UPDATE_TIMEOUT_US) { |
| printf("Timeout waiting for I2C0 clock update\n"); |
| return -ETIMEDOUT; |
| } |
| udelay(1); |
| } |
| cmd_rcgr = readl(GCC_BLSP1_QUP1_I2C_APPS_CMD_RCGR); |
| return 0; |
| } |
| |
| void i2c0_toggle_clock(void) |
| { |
| unsigned long cbcr_val; |
| |
| cbcr_val = readl(GCC_BLSP1_QUP1_I2C_APPS_CBCR); |
| cbcr_val |= I2C0_CBCR_CLK_ENABLE; |
| writel(cbcr_val, GCC_BLSP1_QUP1_I2C_APPS_CBCR); |
| } |
| |
| void i2c_clock_config(void) |
| { |
| i2c0_configure_mux(); |
| i2c0_trigger_update(); |
| i2c0_toggle_clock(); |
| } |
| #endif |
| |
| int pcie_clock_enable(int clk_addr) |
| { |
| unsigned int count = PCIE_TIMEOUT_CNT; |
| int state, val; |
| |
| writel(ENABLE, clk_addr); |
| do { |
| val = readl(clk_addr); |
| count--; |
| if (count == 0) { |
| printf("Timeout waiting for %d enable \n", clk_addr); |
| return -ETIMEDOUT; |
| } |
| state = (val & BIT(31)); |
| udelay(10); |
| } while (state); |
| return 0; |
| } |
| |
| void pcie_clock_disable(int clk_addr) |
| { |
| writel(0, clk_addr); |
| } |