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>master
parent
120800df72
commit
085921368b
@ -0,0 +1,6 @@ |
||||
if ARCH_SNAPDRAGON |
||||
|
||||
config SYS_SOC |
||||
default "snapdragon" |
||||
|
||||
endif |
@ -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
|
@ -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, |
||||
}; |
@ -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+ |
||||
*/ |
@ -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 |
@ -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, |
||||
}; |
@ -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; |
Loading…
Reference in new issue