--- /dev/null
+/*
+ * Qualcomm/Atheros system clocks related functions
+ *
+ * Copyright (C) 2015 Piotr Dymacz <piotr@dymacz.pl>
+ *
+ * Partially based on:
+ * Linux/arch/mips/ath79/clock.c
+ *
+ * SPDX-License-Identifier:GPL-2.0
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <soc/qca_soc_common.h>
+
+/* QCA system clocks */
+static u32 qca_cpu_clk;
+static u32 qca_ddr_clk;
+static u32 qca_ahb_clk;
+static u32 qca_spi_clk;
+static u32 qca_ref_clk;
+
+/*
+ * Calculates and returns PLL value
+ * TODO: check for overflow!
+ */
+static u32 qca_get_pll(u32 ref_clk,
+ u32 refdiv,
+ u32 nfrac,
+ u32 nfracdiv,
+ u32 nint,
+ u32 outdiv)
+{
+ u64 pll_mul;
+ u64 pll_div;
+
+ pll_mul = ref_clk;
+ pll_div = refdiv;
+
+ if (pll_div == 0)
+ pll_div = 1;
+
+ if (nfrac > 0) {
+ pll_mul = pll_mul * ((nint * nfracdiv) + nfrac);
+ pll_div = pll_div * nfracdiv;
+ } else {
+ pll_mul = pll_mul * nint;
+ }
+
+ pll_div = pll_div << outdiv;
+
+ return (u32)(pll_mul / pll_div);
+}
+
+/*
+ * Get CPU, RAM, AHB and SPI clocks
+ * TODO: confirm nfracdiv values
+ */
+void qca_sys_clocks(u32 *cpu_clk,
+ u32 *ddr_clk,
+ u32 *ahb_clk,
+ u32 *spi_clk,
+ u32 *ref_clk)
+{
+ u32 cpu_pll;
+#if (SOC_TYPE != QCA_AR933X_SOC)
+ u32 ddr_pll;
+#endif
+ u32 outdiv;
+ u32 refdiv;
+ u32 reg_val;
+ u32 temp;
+ u32 nfrac;
+ u32 nfracdiv;
+ u32 nint;
+
+ if (qca_xtal_is_40mhz() == 1) {
+ qca_ref_clk = VAL_40MHz;
+ } else {
+ qca_ref_clk = VAL_25MHz;
+ }
+
+#if (SOC_TYPE == QCA_AR933X_SOC)
+ /*
+ * Main AR933x CPU PLL clock calculation:
+ *
+ * 1. If CPU PLL DITHER is disabled:
+ * VCO_OUT = (REF_CLK / REF_DIV) * (NINT + (NFRAC_MIN / 1024))
+ * CPU_PLL_OUT = VCO_OUT / (2^OUT_DIV)
+ *
+ * 2. If CPU PLL DITHER is enabled:
+ * VCO_OUT = (REF_CLK / REF_DIV) * (NINT + (NFRAC / 1024))
+ * CPU_PLL_OUT = VCO_OUT / (2^OUT_DIV)
+ *
+ * TODO: NFRAC does not exist in AR9331 datasheet,
+ * but exist in many other QC/A WiSOC datasheets,
+ * we should somehow (scope?) check and confirm it
+ */
+
+ /* Read CPU CLock Control Register (CLOCK_CONTROL) value */
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_CLK_CTRL_REG);
+
+ if (reg_val & QCA_PLL_CPU_CLK_CTRL_BYPASS_MASK) {
+ /* PLL is bypassed, so all clocks are == reference clock */
+ *cpu_clk = qca_ref_clk;
+ *ddr_clk = qca_ref_clk;
+ *ahb_clk = qca_ref_clk;
+ } else {
+ reg_val = qca_soc_reg_read(QCA_PLL_PLL_DITHER_REG);
+
+ if (reg_val & QCA_PLL_PLL_DITHER_DITHER_EN_MASK) {
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_PLL_CFG_REG);
+ nfrac = (reg_val & QCA_PLL_CPU_PLL_CFG_NFRAC_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_NFRAC_SHIFT;
+ } else {
+ /* NFRAC = NFRAC_MIN if DITHER_EN is 0 */
+ reg_val = qca_soc_reg_read(QCA_PLL_PLL_DITHER_FRAC_REG);
+ nfrac = (reg_val & QCA_PLL_PLL_DITHER_FRAC_NFRAC_MIN_MASK)
+ >> QCA_PLL_PLL_DITHER_FRAC_NFRAC_MIN_SHIFT;
+ }
+
+ nfracdiv = 1 << 10;
+
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_PLL_CFG_REG);
+
+ nint = (reg_val & QCA_PLL_CPU_PLL_CFG_NINT_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_NINT_SHIFT;
+
+ refdiv = (reg_val & QCA_PLL_CPU_PLL_CFG_REFDIV_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_REFDIV_SHIFT;
+
+ outdiv = (reg_val & QCA_PLL_CPU_PLL_CFG_OUTDIV_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_OUTDIV_SHIFT;
+
+ /* TODO: need confirmation that OUTDIV == 0 is not supported for AR933x */
+ if (outdiv == 0)
+ outdiv = 1;
+
+ /* Final CPU PLL value */
+ cpu_pll = qca_get_pll(qca_ref_clk, refdiv,
+ nfrac, nfracdiv, nint, outdiv);
+
+ /* CPU, DDR and AHB clock dividers */
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_CLK_CTRL_REG);
+
+ temp = ((reg_val & QCA_PLL_CPU_CLK_CTRL_CPU_POST_DIV_MASK)
+ >> QCA_PLL_CPU_CLK_CTRL_CPU_POST_DIV_SHIFT) + 1;
+ qca_cpu_clk = cpu_pll / temp;
+
+ temp = ((reg_val & QCA_PLL_CPU_CLK_CTRL_DDR_POST_DIV_MASK)
+ >> QCA_PLL_CPU_CLK_CTRL_DDR_POST_DIV_SHIFT) + 1;
+ qca_ddr_clk = cpu_pll / temp;
+
+ temp = ((reg_val & QCA_PLL_CPU_CLK_CTRL_AHB_POST_DIV_MASK)
+ >> QCA_PLL_CPU_CLK_CTRL_AHB_POST_DIV_SHIFT) + 1;
+ qca_ahb_clk = cpu_pll / temp;
+ }
+#else
+ /*
+ * Main AR934x/QCA95xx CPU/DDR PLL clock calculation
+ */
+
+ /*
+ * CPU PLL
+ */
+ reg_val = qca_soc_reg_read(QCA_PLL_SRIF_CPU_DPLL2_REG);
+
+ /* CPU PLL settings from SRIF CPU DPLL2? */
+ if (reg_val & QCA_PLL_SRIF_DPLL2_LOCAL_PLL_MASK) {
+ outdiv = (reg_val & QCA_PLL_SRIF_DPLL2_OUTDIV_MASK)
+ >> QCA_PLL_SRIF_DPLL2_OUTDIV_SHIFT;
+
+ reg_val = qca_soc_reg_read(QCA_PLL_SRIF_CPU_DPLL1_REG);
+
+ nfrac = (reg_val & QCA_PLL_SRIF_DPLL1_NFRAC_MASK)
+ >> QCA_PLL_SRIF_DPLL1_NFRAC_SHIFT;
+
+ nfracdiv = 1 << 18;
+
+ nint = (reg_val & QCA_PLL_SRIF_DPLL1_NINT_MASK)
+ >> QCA_PLL_SRIF_DPLL1_NINT_SHIFT;
+
+ refdiv = (reg_val & QCA_PLL_SRIF_DPLL1_REFDIV_MASK)
+ >> QCA_PLL_SRIF_DPLL1_REFDIV_SHIFT;
+ } else {
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_PLL_DITHER_REG);
+
+ if (reg_val & QCA_PLL_CPU_PLL_DITHER_DITHER_EN_MASK) {
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_PLL_CFG_REG);
+ nfrac = (reg_val & QCA_PLL_CPU_PLL_CFG_NFRAC_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_NFRAC_SHIFT;
+ } else {
+ /* NFRAC = NFRAC_MIN if DITHER_EN is 0 */
+ nfrac = (reg_val & QCA_PLL_CPU_PLL_DITHER_NFRAC_MIN_MASK)
+ >> QCA_PLL_CPU_PLL_DITHER_NFRAC_MIN_SHIFT;
+ }
+
+ nfracdiv = 1 << 6;
+
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_PLL_CFG_REG);
+
+ nint = (reg_val & QCA_PLL_CPU_PLL_CFG_NINT_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_NINT_SHIFT;
+
+ refdiv = (reg_val & QCA_PLL_CPU_PLL_CFG_REFDIV_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_REFDIV_SHIFT;
+
+ outdiv = (reg_val & QCA_PLL_CPU_PLL_CFG_OUTDIV_MASK)
+ >> QCA_PLL_CPU_PLL_CFG_OUTDIV_SHIFT;
+ }
+
+ /* Final CPU PLL value */
+ cpu_pll = qca_get_pll(qca_ref_clk, refdiv,
+ nfrac, nfracdiv, nint, outdiv);
+
+ /*
+ * DDR PLL
+ */
+ reg_val = qca_soc_reg_read(QCA_PLL_SRIF_DDR_DPLL2_REG);
+
+ /* DDR PLL settings from SRIF DDR DPLL2? */
+ if (reg_val & QCA_PLL_SRIF_DPLL2_LOCAL_PLL_MASK) {
+ outdiv = (reg_val & QCA_PLL_SRIF_DPLL2_OUTDIV_MASK)
+ >> QCA_PLL_SRIF_DPLL2_OUTDIV_SHIFT;
+
+ reg_val = qca_soc_reg_read(QCA_PLL_SRIF_DDR_DPLL1_REG);
+
+ nfrac = (reg_val & QCA_PLL_SRIF_DPLL1_NFRAC_MASK)
+ >> QCA_PLL_SRIF_DPLL1_NFRAC_SHIFT;
+
+ nfracdiv = 1 << 18;
+
+ nint = (reg_val & QCA_PLL_SRIF_DPLL1_NINT_MASK)
+ >> QCA_PLL_SRIF_DPLL1_NINT_SHIFT;
+
+ refdiv = (reg_val & QCA_PLL_SRIF_DPLL1_REFDIV_MASK)
+ >> QCA_PLL_SRIF_DPLL1_REFDIV_SHIFT;
+ } else {
+ reg_val = qca_soc_reg_read(QCA_PLL_DDR_PLL_DITHER_REG);
+
+ if (reg_val & QCA_PLL_DDR_PLL_DITHER_DITHER_EN_MASK) {
+ reg_val = qca_soc_reg_read(QCA_PLL_DDR_PLL_CFG_REG);
+ nfrac = (reg_val & QCA_PLL_DDR_PLL_CFG_NFRAC_MASK)
+ >> QCA_PLL_DDR_PLL_CFG_NFRAC_SHIFT;
+ } else {
+ /* NFRAC = NFRAC_MIN if DITHER_EN is 0 */
+ nfrac = (reg_val & QCA_PLL_DDR_PLL_DITHER_NFRAC_MIN_MASK)
+ >> QCA_PLL_DDR_PLL_DITHER_NFRAC_MIN_SHIFT;
+ }
+
+ nfracdiv = 1 << 10;
+
+ reg_val = qca_soc_reg_read(QCA_PLL_DDR_PLL_CFG_REG);
+
+ nint = (reg_val & QCA_PLL_DDR_PLL_CFG_NINT_MASK)
+ >> QCA_PLL_DDR_PLL_CFG_NINT_SHIFT;
+
+ refdiv = (reg_val & QCA_PLL_DDR_PLL_CFG_REFDIV_MASK)
+ >> QCA_PLL_DDR_PLL_CFG_REFDIV_SHIFT;
+
+ outdiv = (reg_val & QCA_PLL_DDR_PLL_CFG_OUTDIV_MASK)
+ >> QCA_PLL_DDR_PLL_CFG_OUTDIV_SHIFT;
+ }
+
+ /* Final DDR PLL value */
+ ddr_pll = qca_get_pll(qca_ref_clk, refdiv,
+ nfrac, nfracdiv, nint, outdiv);
+
+ /* CPU clock divider */
+ reg_val = qca_soc_reg_read(QCA_PLL_CPU_DDR_CLK_CTRL_REG);
+
+ temp = ((reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_MASK)
+ >> QCA_PLL_CPU_DDR_CLK_CTRL_CPU_POST_DIV_SHIFT) + 1;
+
+ if (reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_CPU_PLL_BYPASS_MASK) {
+ qca_cpu_clk = qca_ref_clk;
+ } else if (reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_CPUCLK_FROM_CPUPLL_MASK) {
+ qca_cpu_clk = cpu_pll / temp;
+ } else {
+ qca_cpu_clk = ddr_pll / temp;
+ }
+
+ /* DDR clock divider */
+ temp = ((reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_MASK)
+ >> QCA_PLL_CPU_DDR_CLK_CTRL_DDR_POST_DIV_SHIFT) + 1;
+
+ if (reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_DDR_PLL_BYPASS_MASK) {
+ qca_ddr_clk = qca_ref_clk;
+ } else if (reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL_MASK) {
+ qca_ddr_clk = ddr_pll / temp;
+ } else {
+ qca_ddr_clk = cpu_pll / temp;
+ }
+
+ /* AHB clock divider */
+ temp = ((reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_MASK)
+ >> QCA_PLL_CPU_DDR_CLK_CTRL_AHB_POST_DIV_SHIFT) + 1;
+
+ if (reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_AHB_PLL_BYPASS_MASK) {
+ qca_ahb_clk = qca_ref_clk;
+ } else if (reg_val & QCA_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL_MASK) {
+ qca_ahb_clk = ddr_pll / temp;
+ } else {
+ qca_ahb_clk = cpu_pll / temp;
+ }
+#endif
+ /* Calculate SPI FLASH clock - first disable SPI */
+ qca_soc_reg_read_set(QCA_SPI_FUNC_SEL_REG,
+ QCA_SPI_FUNC_SEL_FUNC_SEL_MASK);
+
+ /* SPI clock = AHB clock / ((SPI clock divider + 1) * 2) */
+ reg_val = (qca_soc_reg_read(QCA_SPI_CTRL_REG) & QCA_SPI_CTRL_CLK_DIV_MASK)
+ >> QCA_SPI_CTRL_CLK_DIV_SHIFT;
+
+ qca_spi_clk = qca_ahb_clk / ((reg_val + 1) * 2);
+
+ /* Re-enable SPI */
+ qca_soc_reg_read_clear(QCA_SPI_FUNC_SEL_REG,
+ QCA_SPI_FUNC_SEL_FUNC_SEL_MASK);
+
+ /* Return values */
+ if (cpu_clk != NULL)
+ *cpu_clk = qca_cpu_clk;
+
+ if (ddr_clk != NULL)
+ *ddr_clk = qca_ddr_clk;
+
+ if (ahb_clk != NULL)
+ *ahb_clk = qca_ahb_clk;
+
+ if (spi_clk != NULL)
+ *spi_clk = qca_spi_clk;
+
+ if (ref_clk != NULL)
+ *ref_clk = qca_ref_clk;
+}
--- /dev/null
+/*
+ * Qualcomm/Atheros common/helper functions
+ *
+ * Copyright (C) 2015 Piotr Dymacz <piotr@dymacz.pl>
+ *
+ * Partially based on:
+ * Linux/arch/mips/ath79/setup.c
+ *
+ * SPDX-License-Identifier:GPL-2.0
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/addrspace.h>
+#include <soc/qca_soc_common.h>
+
+/*
+ * Returns 1 if reference clock is 40 MHz
+ */
+inline u32 qca_xtal_is_40mhz(void)
+{
+ return ((qca_soc_reg_read(QCA_RST_BOOTSTRAP_REG) &
+ QCA_RST_BOOTSTRAP_REF_CLK_MASK) >> QCA_RST_BOOTSTRAP_REF_CLK_SHIFT);
+}
+
+/*
+ * Return memory type value from BOOT_STRAP register
+ */
+inline u32 qca_mem_type(void)
+{
+ return ((qca_soc_reg_read(QCA_RST_BOOTSTRAP_REG) &
+ QCA_RST_BOOTSTRAP_MEM_TYPE_MASK) >> QCA_RST_BOOTSTRAP_MEM_TYPE_SHIFT);
+}
+
+/*
+ * Put QCA SOC name, version and revision in buffer
+ */
+void qca_soc_name_rev(char *buf)
+{
+ u32 id;
+ u32 major;
+ u32 rev = 0;
+
+ /* Get revision ID value */
+ id = qca_soc_reg_read(QCA_RST_REVISION_ID_REG);
+
+ major = id & QCA_RST_REVISION_ID_MAJOR_MASK;
+ rev = id & QCA_RST_REVISION_ID_REV_MASK;
+
+ switch (major) {
+#if (SOC_TYPE == QCA_AR933X_SOC)
+ case QCA_RST_REVISION_ID_MAJOR_AR9330_VAL:
+ sprintf(buf, "AR9330 rev. %d", rev);
+ break;
+ case QCA_RST_REVISION_ID_MAJOR_AR9331_VAL:
+ sprintf(buf, "AR9331 rev. %d", rev);
+ break;
+#endif
+#if (SOC_TYPE == QCA_AR9341_SOC)
+ case QCA_RST_REVISION_ID_MAJOR_AR9341_VAL:
+ sprintf(buf, "AR9341 rev. %d", rev);
+ break;
+#endif
+#if (SOC_TYPE == QCA_AR9344_SOC)
+ case QCA_RST_REVISION_ID_MAJOR_AR9344_VAL:
+ sprintf(buf, "AR9344 rev. %d", rev);
+ break;
+#endif
+#if (SOC_TYPE == QCA_QCA9531_SOC || SOC_TYPE == QCA_QCA9533_SOC)
+ case QCA_RST_REVISION_ID_MAJOR_QCA9531_VAL:
+ case QCA_RST_REVISION_ID_MAJOR_QCA9533_VAL:
+ sprintf(buf, "QCA953x ver. 1 rev. %d", rev);
+ break;
+ case QCA_RST_REVISION_ID_MAJOR_QCA9533_V2_VAL:
+ sprintf(buf, "QCA953x ver. 2 rev. %d", rev);
+ break;
+#endif
+#if (SOC_TYPE == QCA_QCA9558_SOC)
+ case QCA_RST_REVISION_ID_MAJOR_QCA9558_VAL:
+ sprintf(buf, "QCA9558 rev. %d", rev);
+ break;
+#endif
+ default:
+ sprintf(buf, "Unknown");
+ break;
+ }
+}
--- /dev/null
+/*
+ * Qualcomm/Atheros High-Speed UART driver
+ *
+ * Copyright (C) 2015 Piotr Dymacz <piotr@dymacz.pl>
+ * Copyright (C) 2014 Mantas Pucka <mantas@8devices.com>
+ * Copyright (C) 2008-2010 Atheros Communications Inc.
+ *
+ * Values for UART_SCALE and UART_STEP:
+ * https://www.mail-archive.com/openwrt-devel@lists.openwrt.org/msg22371.html
+ *
+ * Partially based on:
+ * Linux/drivers/tty/serial/ar933x_uart.c
+ *
+ * SPDX-License-Identifier:GPL-2.0
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/addrspace.h>
+#include <soc/qca_soc_common.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* HS UART baudrate = (REF_CLK / (CLOCK_SCALE + 1)) * (CLOCK_STEP * (1 / 2^17)) */
+static u32 qca_hsuart_get_baud(u32 ref_clk, u32 uart_scale, u32 uart_step)
+{
+ u64 baudrate;
+ u32 div;
+
+ div = (uart_scale + 1) * (2 << 16);
+
+ baudrate = (ref_clk * uart_step) + (div / 2);
+ baudrate = baudrate / div;
+
+ return (u32)baudrate;
+}
+
+static void qca_hsuart_get_scale_step(u32 baudrate,
+ u32 *uart_scale,
+ u32 *uart_step)
+{
+ s32 diff;
+ u32 ref_clk;
+ u32 tscale;
+ u64 tstep;
+ s32 min_diff;
+
+ *uart_scale = 0;
+ *uart_step = 0;
+
+ min_diff = baudrate;
+
+ if (qca_xtal_is_40mhz() == 1) {
+ ref_clk = VAL_40MHz;
+ } else {
+ ref_clk = VAL_25MHz;
+ }
+
+ for (tscale = 0; tscale < QCA_HSUART_CLK_SCALE_MAX_VAL; tscale++) {
+ tstep = baudrate * (tscale + 1);
+ tstep = tstep * (2 << 16);
+ tstep = tstep / ref_clk;
+
+ if (tstep > QCA_HSUART_CLK_STEP_MAX_VAL)
+ break;
+
+ diff = qca_hsuart_get_baud(ref_clk, tscale, tstep) - baudrate;
+
+ if (diff < 0)
+ diff = -1 * diff;
+
+ if (diff < min_diff) {
+ min_diff = diff;
+ *uart_scale = tscale;
+ *uart_step = tstep;
+ }
+ }
+}
+
+void serial_setbrg(void)
+{
+ u32 uart_clock;
+ u32 uart_scale;
+ u32 uart_step;
+
+ qca_hsuart_get_scale_step(gd->baudrate, &uart_scale, &uart_step);
+
+ uart_clock = (uart_scale << QCA_HSUART_CLK_SCALE_SHIFT);
+ uart_clock |= (uart_step << QCA_HSUART_CLK_STEP_SHIFT);
+
+ qca_soc_reg_write(QCA_HSUART_CLK_REG, uart_clock);
+}
+
+int serial_init(void)
+{
+ u32 uart_cs;
+
+#if (SOC_TYPE == QCA_AR933X_SOC)
+ /*
+ * Set GPIO10 (UART_SO) as output and enable UART,
+ * BIT(15) in GPIO_FUNCTION_1 register must be written with 1
+ */
+ qca_soc_reg_read_set(QCA_GPIO_OE_REG, GPIO10);
+
+ qca_soc_reg_read_set(QCA_GPIO_FUNC_1_REG,
+ QCA_GPIO_FUNC_1_UART_EN_MASK | BIT(15));
+#else
+ #error "Missing GPIO configuration for HS UART"
+#endif
+
+ /*
+ * High-Speed UART controller configuration:
+ * - no DMA
+ * - no interrupt
+ * - no parity
+ * - DCE mode
+ * - no flow control
+ * - set RX ready oride
+ * - set TX ready oride
+ */
+ uart_cs = (0 << QCA_HSUART_CS_DMA_EN_SHIFT) |
+ (0 << QCA_HSUART_CS_HOST_INT_EN_SHIFT) |
+ (1 << QCA_HSUART_CS_RX_READY_ORIDE_SHIFT) |
+ (1 << QCA_HSUART_CS_TX_READY_ORIDE_SHIFT) |
+ (QCA_HSUART_CS_PAR_MODE_NO_VAL << QCA_HSUART_CS_PAR_MODE_SHIFT) |
+ (QCA_HSUART_CS_IFACE_MODE_DCE_VAL << QCA_HSUART_CS_IFACE_MODE_SHIFT) |
+ (QCA_HSUART_CS_FLOW_MODE_NO_VAL << QCA_HSUART_CS_FLOW_MODE_SHIFT);
+
+ qca_soc_reg_write(QCA_HSUART_CS_REG, uart_cs);
+
+ serial_setbrg();
+
+ return 0;
+}
+
+void serial_putc(const char c)
+{
+ u32 uart_data;
+
+ if (c == '\n')
+ serial_putc('\r');
+
+ /* Wait for FIFO */
+ do {
+ uart_data = qca_soc_reg_read(QCA_HSUART_DATA_REG);
+ } while (((uart_data & QCA_HSUART_DATA_TX_CSR_MASK)
+ >> QCA_HSUART_DATA_TX_CSR_SHIFT) == 0);
+
+ /* Put data in buffer and set CSR bit */
+ uart_data = (u32)c | (1 << QCA_HSUART_DATA_TX_CSR_SHIFT);
+
+ qca_soc_reg_write(QCA_HSUART_DATA_REG, uart_data);
+}
+
+int serial_getc(void)
+{
+ u32 uart_data;
+
+ while (!serial_tstc())
+ ;
+
+ uart_data = qca_soc_reg_read(QCA_HSUART_DATA_REG);
+
+ qca_soc_reg_write(QCA_HSUART_DATA_REG,
+ (1 << QCA_HSUART_DATA_RX_CSR_SHIFT));
+
+ return (uart_data & QCA_HSUART_DATA_TX_RX_DATA_MASK);
+}
+
+int serial_tstc(void)
+{
+ u32 uart_data = qca_soc_reg_read(QCA_HSUART_DATA_REG);
+
+ return ((uart_data & QCA_HSUART_DATA_RX_CSR_MASK)
+ >> QCA_HSUART_DATA_RX_CSR_SHIFT);
+}
+
+void serial_puts(const char *s)
+{
+ while (*s)
+ serial_putc(*s++);
+}
--- /dev/null
+/*
+ * Qualcomm/Atheros Low-Speed UART driver
+ *
+ * Copyright (C) 2015 Piotr Dymacz <piotr@dymacz.pl>
+ * Copyright (C) 2008-2010 Atheros Communications Inc.
+ *
+ * SPDX-License-Identifier:GPL-2.0
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/addrspace.h>
+#include <soc/qca_soc_common.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void serial_setbrg(void)
+{
+ u32 div;
+
+ /*
+ * TODO: prepare list of supported range of baudrate values
+ * For 40 MHz ref_clk, successfully tested up to 1152000 on AR9344
+ *
+ * TODO: support 100 MHz reference clocks on AR934x and QCA955x
+ */
+
+ /* Round to closest, final baudrate = ref_clk / (16 * div) */
+ if (qca_xtal_is_40mhz() == 1) {
+ div = (VAL_40MHz + (8 * gd->baudrate)) / (16 * gd->baudrate);
+ } else {
+ div = (VAL_25MHz + (8 * gd->baudrate)) / (16 * gd->baudrate);
+ }
+
+ /* Set DLAB bit in LCR register unlocks DLL/DLH registers */
+ qca_soc_reg_read_set(QCA_LSUART_LCR_REG, QCA_LSUART_LCR_DLAB_MASK);
+
+ /* Write div into DLL and DLH registers */
+ qca_soc_reg_write(QCA_LSUART_DLL_REG, (div & 0xFF));
+ qca_soc_reg_write(QCA_LSUART_DLH_REG, ((div >> 8) & 0xFF));
+
+ /* Clear DLAB bit in LCR register */
+ qca_soc_reg_read_clear(QCA_LSUART_LCR_REG, QCA_LSUART_LCR_DLAB_MASK);
+}
+
+int serial_init(void)
+{
+ u32 uart_lcr;
+
+ serial_setbrg();
+
+ /* No interrupt */
+ qca_soc_reg_write(QCA_LSUART_IER_REG, 0x0);
+
+ /* No FIFO/DMA */
+ qca_soc_reg_write(QCA_LSUART_FCR_REG, 0x0);
+
+ /*
+ * Low-Speed UART controller configuration:
+ * - data: 8bits
+ * - stop: 1bit
+ * - parity: no
+ */
+ uart_lcr = (QCA_LSUART_LCR_CLS_8BIT_VAL << QCA_LSUART_LCR_CLS_SHIFT)
+ | (0 << QCA_LSUART_LCR_STOP_SHIFT)
+ | (0 << QCA_LSUART_LCR_PEN_SHIFT);
+
+ qca_soc_reg_write(QCA_LSUART_LCR_REG, uart_lcr);
+
+ return 0;
+}
+
+void serial_putc(const char c)
+{
+ u32 line_status;
+
+ if (c == '\n')
+ serial_putc('\r');
+
+ /* Wait for empty THR */
+ do {
+ line_status = qca_soc_reg_read(QCA_LSUART_LSR_REG);
+ } while (((line_status & QCA_LSUART_LSR_THRE_MASK)
+ >> QCA_LSUART_LSR_THRE_SHIFT) == 0);
+
+ /* Put data in THR */
+ qca_soc_reg_write(QCA_LSUART_THR_REG, (u32)c);
+}
+
+int serial_getc(void)
+{
+ while (!serial_tstc())
+ ;
+
+ /* Get data from RBR */
+ return (qca_soc_reg_read(QCA_LSUART_RBR_REG)
+ & QCA_LSUART_RBR_RBR_MASK);
+}
+
+int serial_tstc(void)
+{
+ u32 uart_data = qca_soc_reg_read(QCA_LSUART_LSR_REG);
+
+ /* Check data ready bit */
+ return ((uart_data & QCA_LSUART_LSR_DR_MASK)
+ >> QCA_LSUART_LSR_DR_SHIFT);
+}
+
+void serial_puts(const char *s)
+{
+ while (*s)
+ serial_putc(*s++);
+}