diff options
| author | Mateusz Kulikowski <mateusz.kulikowski@gmail.com> | 2016-03-31 23:12:32 +0200 | 
|---|---|---|
| committer | Tom Rini <trini@konsulko.com> | 2016-04-01 17:18:26 -0400 | 
| commit | 085921368b7d1aa30e031cb7b54e5ea707a2155f (patch) | |
| tree | 8384344c4fcaceaee5737729653be2ffec7436fa | |
| parent | 120800df720095a3c60c72a6bdcf6904dc3c4b20 (diff) | |
arm: Add support for Qualcomm Snapdragon family
First supported chip is APQ8016 (that is compatible with MSM8916).
Drivers in SoC code:
- Reset controller (PSHOLD)
- Clock controller (very simple clock configuration for MMC and UART)
Signed-off-by: Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
| -rw-r--r-- | arch/arm/Kconfig | 12 | ||||
| -rw-r--r-- | arch/arm/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/Kconfig | 6 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/Makefile | 9 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/clock-apq8016.c | 262 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/include/mach/gpio.h | 9 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h | 14 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/reset.c | 40 | ||||
| -rw-r--r-- | arch/arm/mach-snapdragon/sysmap-apq8016.c | 30 | 
9 files changed, 383 insertions, 0 deletions
| diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b82ec18445c..bf5e18aba26 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -511,6 +511,16 @@ config RMOBILE  	bool "Renesas ARM SoCs"  	select CPU_V7 +config ARCH_SNAPDRAGON +	bool "Qualcomm Snapdragon SoCs" +	select ARM64 +	select DM +	select DM_GPIO +	select DM_SERIAL +	select SPMI +	select OF_CONTROL +	select OF_SEPARATE +  config ARCH_SOCFPGA  	bool "Altera SOCFPGA family"  	select CPU_V7 @@ -774,6 +784,8 @@ source "arch/arm/mach-rockchip/Kconfig"  source "arch/arm/mach-s5pc1xx/Kconfig" +source "arch/arm/mach-snapdragon/Kconfig" +  source "arch/arm/mach-socfpga/Kconfig"  source "arch/arm/mach-stm32/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 09ba027cff4..d51634584bf 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -56,6 +56,7 @@ machine-$(CONFIG_ARCH_MVEBU)		+= mvebu  machine-$(CONFIG_ORION5X)		+= orion5x  machine-$(CONFIG_ARCH_S5PC1XX)		+= s5pc1xx  machine-$(CONFIG_ARCH_SUNXI)		+= sunxi +machine-$(CONFIG_ARCH_SNAPDRAGON)	+= snapdragon  machine-$(CONFIG_ARCH_SOCFPGA)		+= socfpga  machine-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip  machine-$(CONFIG_STM32)			+= stm32 diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig new file mode 100644 index 00000000000..156e733cd69 --- /dev/null +++ b/arch/arm/mach-snapdragon/Kconfig @@ -0,0 +1,6 @@ +if ARCH_SNAPDRAGON + +config SYS_SOC +	default "snapdragon" + +endif diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile new file mode 100644 index 00000000000..4735844b752 --- /dev/null +++ b/arch/arm/mach-snapdragon/Makefile @@ -0,0 +1,9 @@ +# +# (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +obj-y += clock-apq8016.o +obj-y += sysmap-apq8016.o +obj-y += reset.o diff --git a/arch/arm/mach-snapdragon/clock-apq8016.c b/arch/arm/mach-snapdragon/clock-apq8016.c new file mode 100644 index 00000000000..d548d757d37 --- /dev/null +++ b/arch/arm/mach-snapdragon/clock-apq8016.c @@ -0,0 +1,262 @@ +/* + * Clock drivers for Qualcomm APQ8016 + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * Based on Little Kernel driver, simplified + * + * SPDX-License-Identifier:	BSD-3-Clause + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <linux/bitops.h> + +/* GPLL0 clock control registers */ +#define GPLL0_STATUS        0x2101C +#define GPLL0_STATUS_ACTIVE BIT(17) + +#define APCS_GPLL_ENA_VOTE  0x45000 +#define APCS_GPLL_ENA_VOTE_GPLL0 BIT(0) + +/* vote reg for blsp1 clock */ +#define APCS_CLOCK_BRANCH_ENA_VOTE  0x45004 +#define APCS_CLOCK_BRANCH_ENA_VOTE_BLSP1 BIT(10) + +/* SDC(n) clock control registers; n=1,2 */ + +/* block control register */ +#define SDCC_BCR(n)                 ((n * 0x1000) + 0x41000) +/* cmd */ +#define SDCC_CMD_RCGR(n)            ((n * 0x1000) + 0x41004) +/* cfg */ +#define SDCC_CFG_RCGR(n)            ((n * 0x1000) + 0x41008) +/* m */ +#define SDCC_M(n)                   ((n * 0x1000) + 0x4100C) +/* n */ +#define SDCC_N(n)                   ((n * 0x1000) + 0x41010) +/* d */ +#define SDCC_D(n)                   ((n * 0x1000) + 0x41014) +/* branch control */ +#define SDCC_APPS_CBCR(n)           ((n * 0x1000) + 0x41018) +#define SDCC_AHB_CBCR(n)            ((n * 0x1000) + 0x4101C) + +/* BLSP1 AHB clock (root clock for BLSP) */ +#define BLSP1_AHB_CBCR              0x1008 + +/* Uart clock control registers */ +#define BLSP1_UART2_BCR             0x3028 +#define BLSP1_UART2_APPS_CBCR       0x302C +#define BLSP1_UART2_APPS_CMD_RCGR   0x3034 +#define BLSP1_UART2_APPS_CFG_RCGR   0x3038 +#define BLSP1_UART2_APPS_M          0x303C +#define BLSP1_UART2_APPS_N          0x3040 +#define BLSP1_UART2_APPS_D          0x3044 + +/* CBCR register fields */ +#define CBCR_BRANCH_ENABLE_BIT  BIT(0) +#define CBCR_BRANCH_OFF_BIT     BIT(31) + +struct msm_clk_priv { +	phys_addr_t base; +}; + +/* Enable clock controlled by CBC soft macro */ +static void clk_enable_cbc(phys_addr_t cbcr) +{ +	setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT); + +	while (readl(cbcr) & CBCR_BRANCH_OFF_BIT) +		; +} + +/* clock has 800MHz */ +static void clk_enable_gpll0(phys_addr_t base) +{ +	if (readl(base + GPLL0_STATUS) & GPLL0_STATUS_ACTIVE) +		return; /* clock already enabled */ + +	setbits_le32(base + APCS_GPLL_ENA_VOTE, APCS_GPLL_ENA_VOTE_GPLL0); + +	while ((readl(base + GPLL0_STATUS) & GPLL0_STATUS_ACTIVE) == 0) +		; +} + +#define APPS_CMD_RGCR_UPDATE BIT(0) + +/* Update clock command via CMD_RGCR */ +static void clk_bcr_update(phys_addr_t apps_cmd_rgcr) +{ +	setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE); + +	/* Wait for frequency to be updated. */ +	while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE) +		; +} + +struct bcr_regs { +	uintptr_t cfg_rcgr; +	uintptr_t cmd_rcgr; +	uintptr_t M; +	uintptr_t N; +	uintptr_t D; +}; + +/* RCGR_CFG register fields */ +#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */ + +/* sources */ +#define CFG_CLK_SRC_CXO   (0 << 8) +#define CFG_CLK_SRC_GPLL0 (1 << 8) +#define CFG_CLK_SRC_MASK  (7 << 8) + +/* Mask for supported fields */ +#define CFG_MASK 0x3FFF + +#define CFG_DIVIDER_MASK 0x1F + +/* root set rate for clocks with half integer and MND divider */ +static void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs, +				 int div, int m, int n, int source) +{ +	uint32_t cfg; +	/* M value for MND divider. */ +	uint32_t m_val = m; +	/* NOT(N-M) value for MND divider. */ +	uint32_t n_val = ~((n)-(m)) * !!(n); +	/* NOT 2D value for MND divider. */ +	uint32_t d_val = ~(n); + +	/* Program MND values */ +	writel(m_val, base + regs->M); +	writel(n_val, base + regs->N); +	writel(d_val, base + regs->D); + +	/* setup src select and divider */ +	cfg  = readl(base + regs->cfg_rcgr); +	cfg &= ~CFG_MASK; +	cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */ + +	/* Set the divider; HW permits fraction dividers (+0.5), but +	   for simplicity, we will support integers only */ +	if (div) +		cfg |= (2 * div - 1) & CFG_DIVIDER_MASK; + +	if (n_val) +		cfg |= CFG_MODE_DUAL_EDGE; + +	writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */ + +	/* Inform h/w to start using the new config. */ +	clk_bcr_update(base + regs->cmd_rcgr); +} + +static const struct bcr_regs sdc_regs[] = { +	{ +	.cfg_rcgr = SDCC_CFG_RCGR(1), +	.cmd_rcgr = SDCC_CMD_RCGR(1), +	.M = SDCC_M(1), +	.N = SDCC_N(1), +	.D = SDCC_D(1), +	}, +	{ +	.cfg_rcgr = SDCC_CFG_RCGR(2), +	.cmd_rcgr = SDCC_CMD_RCGR(2), +	.M = SDCC_M(2), +	.N = SDCC_N(2), +	.D = SDCC_D(2), +	} +}; + +/* Init clock for SDHCI controller */ +static int clk_init_sdc(struct msm_clk_priv *priv, int slot, uint rate) +{ +	int div = 8; /* 100MHz default */ + +	if (rate == 200000000) +		div = 4; + +	clk_enable_cbc(priv->base + SDCC_AHB_CBCR(slot)); +	/* 800Mhz/div, gpll0 */ +	clk_rcg_set_rate_mnd(priv->base, &sdc_regs[slot], div, 0, 0, +			     CFG_CLK_SRC_GPLL0); +	clk_enable_gpll0(priv->base); +	clk_enable_cbc(priv->base + SDCC_APPS_CBCR(slot)); + +	return rate; +} + +static const struct bcr_regs uart2_regs = { +	.cfg_rcgr = BLSP1_UART2_APPS_CFG_RCGR, +	.cmd_rcgr = BLSP1_UART2_APPS_CMD_RCGR, +	.M = BLSP1_UART2_APPS_M, +	.N = BLSP1_UART2_APPS_N, +	.D = BLSP1_UART2_APPS_D, +}; + +/* Init UART clock, 115200 */ +static int clk_init_uart(struct msm_clk_priv *priv) +{ +	/* Enable iface clk */ +	clk_enable_cbc(priv->base + BLSP1_AHB_CBCR); +	/* 7372800 uart block clock @ GPLL0 */ +	clk_rcg_set_rate_mnd(priv->base, &uart2_regs, 1, 144, 15625, +			     CFG_CLK_SRC_GPLL0); +	clk_enable_gpll0(priv->base); +	/* Enable core clk */ +	clk_enable_cbc(priv->base + BLSP1_UART2_APPS_CBCR); + +	return 0; +} + +ulong msm_set_periph_rate(struct udevice *dev, int periph, ulong rate) +{ +	struct msm_clk_priv *priv = dev_get_priv(dev); + +	switch (periph) { +	case 0: /* SDC1 */ +		return clk_init_sdc(priv, 0, rate); +		break; +	case 1: /* SDC2 */ +		return clk_init_sdc(priv, 1, rate); +		break; +	case 4: /* UART2 */ +		return clk_init_uart(priv); +		break; +	default: +		return 0; +	} +} + +static int msm_clk_probe(struct udevice *dev) +{ +	struct msm_clk_priv *priv = dev_get_priv(dev); + +	priv->base = dev_get_addr(dev); +	if (priv->base == FDT_ADDR_T_NONE) +		return -EINVAL; + +	return 0; +} + +static struct clk_ops msm_clk_ops = { +	.set_periph_rate = msm_set_periph_rate, +}; + +static const struct udevice_id msm_clk_ids[] = { +	{ .compatible = "qcom,gcc-msm8916" }, +	{ .compatible = "qcom,gcc-apq8016" }, +	{ } +}; + +U_BOOT_DRIVER(clk_msm) = { +	.name		= "clk_msm", +	.id		= UCLASS_CLK, +	.of_match	= msm_clk_ids, +	.ops		= &msm_clk_ops, +	.priv_auto_alloc_size = sizeof(struct msm_clk_priv), +	.probe		= msm_clk_probe, +}; diff --git a/arch/arm/mach-snapdragon/include/mach/gpio.h b/arch/arm/mach-snapdragon/include/mach/gpio.h new file mode 100644 index 00000000000..ff949b2133a --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/gpio.h @@ -0,0 +1,9 @@ +/* + * Empty gpio.h + * + * This file must stay as arch/arm/include/asm/gpio.h requires it. + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ diff --git a/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h b/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h new file mode 100644 index 00000000000..cdbfad0defe --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h @@ -0,0 +1,14 @@ +/* + * Qualcomm APQ8916 sysmap + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ +#ifndef _MACH_SYSMAP_APQ8016_H +#define _MACH_SYSMAP_APQ8016_H + +#define GICD_BASE 0x0b000000 +#define GICC_BASE 0x0a20c000 + +#endif diff --git a/arch/arm/mach-snapdragon/reset.c b/arch/arm/mach-snapdragon/reset.c new file mode 100644 index 00000000000..2627eec1815 --- /dev/null +++ b/arch/arm/mach-snapdragon/reset.c @@ -0,0 +1,40 @@ +/* + * Qualcomm APQ8016 reset controller driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <reset.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int msm_reset_request(struct udevice *dev, enum reset_t type) +{ +	phys_addr_t addr = dev_get_addr(dev); +	if (!addr) +		return -EINVAL; +	writel(0, addr); +	return -EINPROGRESS; +} + +static struct reset_ops msm_reset_ops = { +	.request	= msm_reset_request, +}; + +static const struct udevice_id msm_reset_ids[] = { +	{ .compatible = "qcom,pshold" }, +	{ } +}; + +U_BOOT_DRIVER(msm_reset) = { +	.name		= "msm_reset", +	.id		= UCLASS_RESET, +	.of_match	= msm_reset_ids, +	.ops		= &msm_reset_ops, +}; diff --git a/arch/arm/mach-snapdragon/sysmap-apq8016.c b/arch/arm/mach-snapdragon/sysmap-apq8016.c new file mode 100644 index 00000000000..ef0db2ab5f4 --- /dev/null +++ b/arch/arm/mach-snapdragon/sysmap-apq8016.c @@ -0,0 +1,30 @@ +/* + * Qualcomm APQ8016 memory map + * + * (C) Copyright 2016 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/armv8/mmu.h> + +static struct mm_region apq8016_mem_map[] = { +	{ +		.base = 0x0UL, /* Peripheral block */ +		.size = 0x8000000UL, +		.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | +			 PTE_BLOCK_NON_SHARE | +			 PTE_BLOCK_PXN | PTE_BLOCK_UXN +	}, { +		.base = 0x80000000UL, /* DDR */ +		.size = 0x80000000UL, +		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | +			 PTE_BLOCK_INNER_SHARE +	}, { +		/* List terminator */ +		0, +	} +}; + +struct mm_region *mem_map = apq8016_mem_map; | 
