summaryrefslogtreecommitdiff
path: root/src/soc
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc')
-rw-r--r--src/soc/imgtec/pistachio/Makefile.inc1
-rw-r--r--src/soc/imgtec/pistachio/clocks.c344
-rw-r--r--src/soc/imgtec/pistachio/include/soc/clocks.h39
3 files changed, 384 insertions, 0 deletions
diff --git a/src/soc/imgtec/pistachio/Makefile.inc b/src/soc/imgtec/pistachio/Makefile.inc
index fdeba0cc01..6dc18f3b5e 100644
--- a/src/soc/imgtec/pistachio/Makefile.inc
+++ b/src/soc/imgtec/pistachio/Makefile.inc
@@ -20,6 +20,7 @@
#
# We enable CBFS_SPI_WRAPPER for Pistachio targets.
+bootblock-y += clocks.c
bootblock-y += spi.c
romstage-y += spi.c
ramstage-y += spi.c
diff --git a/src/soc/imgtec/pistachio/clocks.c b/src/soc/imgtec/pistachio/clocks.c
new file mode 100644
index 0000000000..ec77d6511d
--- /dev/null
+++ b/src/soc/imgtec/pistachio/clocks.c
@@ -0,0 +1,344 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Imagination Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <arch/io.h>
+#include <soc/clocks.h>
+#include <timer.h>
+#include <delay.h>
+#include <assert.h>
+
+/* Definitions for PLL enable */
+#define PISTACHIO_CLOCK_SWITCH 0xB8144200
+
+#define SYS_EXTERN_PLL_BYPASS_MASK 0x00002000
+#define SYS_PLL_CTRL4_ADDR 0xB8144048
+#define SYS_INTERNAL_PLL_BYPASS_MASK 0x10000000
+#define SYS_PLL_PD_CTRL_ADDR 0xB8144044
+#define SYS_PLL_PD_CTRL_PD_MASK 0x0000003F
+
+#define MIPS_EXTERN_PLL_BYPASS_MASK 0x00000002
+#define MIPS_PLL_CTRL2_ADDR 0xB8144008
+#define MIPS_INTERNAL_PLL_BYPASS_MASK 0x10000000
+#define MIPS_PLL_PD_CTRL_ADDR 0xB8144004
+#define MIPS_PLL_PD_CTRL_PD_MASK 0x0F000000
+
+/* Definitions for PLL dividers */
+#define SYS_PLL_POSTDIV_ADDR 0xB8144040
+#define SYS_PLL_POSTDIV1_MASK 0x07000000
+#define SYS_PLL_POSTDIV1_SHIFT 24
+#define SYS_PLL_POSTDIV2_MASK 0x38000000
+#define SYS_PLL_POSTDIV2_SHIFT 27
+#define SYS_PLL_STATUS_ADDR 0xB8144038
+#define SYS_PLL_STATUS_LOCK_MASK 0x00000001
+
+#define MIPS_PLL_POSTDIV_ADDR 0xB8144004
+#define MIPS_PLL_POSTDIV1_MASK 0x001C0000
+#define MIPS_PLL_POSTDIV1_SHIFT 18
+#define MIPS_PLL_POSTDIV2_MASK 0x00E00000
+#define MIPS_PLL_POSTDIV2_SHIFT 21
+#define MIPS_PLL_STATUS_ADDR 0xB8144000
+#define MIPS_PLL_STATUS_LOCK_MASK 0x00000001
+
+#define MIPS_REFDIV_ADDR 0xB8144004
+#define MIPS_REFDIV_MASK 0x0000003F
+#define MIPS_REFDIV_SHIFT 0
+#define MIPS_FEEDBACK_ADDR 0xB8144004
+#define MIPS_FEEDBACK_MASK 0x0003FFC0
+#define MIPS_FEEDBACK_SHIFT 6
+
+/* Definitions for system clock setup */
+#define SYSCLKINTERNAL_CTRL_ADDR 0xB8144244
+#define SYSCLKINTERNAL_MASK 0X00000007
+
+/* Definitions for USB clock setup */
+#define USBPHYCLKOUT_CTRL_ADDR 0xB814422C
+#define USBPHYCLKOUT_MASK 0X0000003F
+#define USBPHYCONTROL1_ADDR 0xB8149004
+#define USBPHYCONTROL1_FSEL_SHIFT 2
+#define USBPHYCONTROL1_FSEL_MASK 0x1C
+#define USBPHYSTRAPCTRL_ADDR 0xB8149010
+#define USBPHYSTRAPCTRL_REFCLKSEL_SHIFT 4
+#define USBPHYSTRAPCTRL_REFCLKSEL_MASK 0x30
+#define USBPHYSTATUS_ADDR 0xB8149014
+#define USBPHYSTATUS_RX_PHY_CLK_MASK 0x200
+#define USBPHYSTATUS_RX_UTMI_CLK_MASK 0x100
+#define USBPHYSTATUS_VBUS_FAULT_MASK 0x80
+
+/* Definitions for UART0/1 setup */
+#define UART0CLKINTERNAL_CTRL_ADDR 0xB8144234
+#define UART0CLKINTERNAL_MASK 0x00000007
+#define UART0CLKOUT_CTRL_ADDR 0xB8144238
+#define UART0CLKOUT_MASK 0x000003FF
+#define UART1CLKINTERNAL_CTRL_ADDR 0xB814423C
+#define UART1CLKINTERNAL_MASK 0x00000007
+#define UART1CLKOUT_CTRL_ADDR 0xB8144240
+#define UART1CLKOUT_MASK 0x000003FF
+
+/* Definitions for timeout values */
+#define PLL_TIMEOUT_VALUE_US 20000
+#define USB_TIMEOUT_VALUE_US 200000
+#define SYS_CLK_LOCK_DELAY 3
+
+struct pll_parameters {
+ u32 external_bypass_mask;
+ u32 ctrl_addr;
+ u32 internal_bypass_mask;
+ u32 power_down_ctrl_addr;
+ u32 power_down_ctrl_mask;
+ u32 postdiv_addr;
+ u32 postdiv1_shift;
+ u32 postdiv1_mask;
+ u32 postdiv2_shift;
+ u32 postdiv2_mask;
+ u32 status_addr;
+ u32 status_lock_mask;
+ u32 refdivider;
+ u32 refdiv_addr;
+ u32 refdiv_shift;
+ u32 refdiv_mask;
+ u32 feedback;
+ u32 feedback_addr;
+ u32 feedback_shift;
+ u32 feedback_mask;
+};
+
+enum plls {
+ SYS_PLL = 0,
+ MIPS_PLL = 1
+};
+
+static struct pll_parameters pll_params[] = {
+ [SYS_PLL] = {
+ .external_bypass_mask = SYS_EXTERN_PLL_BYPASS_MASK,
+ .ctrl_addr = SYS_PLL_CTRL4_ADDR,
+ .internal_bypass_mask = SYS_INTERNAL_PLL_BYPASS_MASK,
+ .power_down_ctrl_addr = SYS_PLL_PD_CTRL_ADDR,
+ .power_down_ctrl_mask = SYS_PLL_PD_CTRL_PD_MASK,
+ .postdiv_addr = SYS_PLL_POSTDIV_ADDR,
+ .postdiv1_shift = SYS_PLL_POSTDIV1_SHIFT,
+ .postdiv1_mask = SYS_PLL_POSTDIV1_MASK,
+ .postdiv2_shift = SYS_PLL_POSTDIV2_SHIFT,
+ .postdiv2_mask = SYS_PLL_POSTDIV2_MASK,
+ .status_addr = SYS_PLL_STATUS_ADDR,
+ .status_lock_mask = SYS_PLL_STATUS_LOCK_MASK,
+ .refdivider = 0, /* Not defined yet */
+ .refdiv_addr = 0, /* Not necessary */
+ .refdiv_shift = 0, /* Not necessary */
+ .refdiv_mask = 0, /* Not necessary */
+ .feedback = 0, /* Not necessary */
+ .feedback_addr = 0, /* Not necessary */
+ .feedback_shift = 0, /* Not necessary */
+ .feedback_mask = 0, /* Not necessary */
+ },
+
+ [MIPS_PLL] = {
+ .external_bypass_mask = MIPS_EXTERN_PLL_BYPASS_MASK,
+ .ctrl_addr = MIPS_PLL_CTRL2_ADDR,
+ .internal_bypass_mask = MIPS_INTERNAL_PLL_BYPASS_MASK,
+ .power_down_ctrl_addr = MIPS_PLL_PD_CTRL_ADDR,
+ .power_down_ctrl_mask = MIPS_PLL_PD_CTRL_PD_MASK,
+ .postdiv_addr = MIPS_PLL_POSTDIV_ADDR,
+ .postdiv1_shift = MIPS_PLL_POSTDIV1_SHIFT,
+ .postdiv1_mask = MIPS_PLL_POSTDIV1_MASK,
+ .postdiv2_shift = MIPS_PLL_POSTDIV2_SHIFT,
+ .postdiv2_mask = MIPS_PLL_POSTDIV2_MASK,
+ .status_addr = MIPS_PLL_STATUS_ADDR,
+ .status_lock_mask = MIPS_PLL_STATUS_LOCK_MASK,
+ .refdivider = 0, /* Not defined yet */
+ .refdiv_addr = MIPS_REFDIV_ADDR,
+ .refdiv_shift = MIPS_REFDIV_SHIFT,
+ .refdiv_mask = MIPS_REFDIV_MASK,
+ .feedback = 0, /* Not defined yet */
+ .feedback_addr = MIPS_FEEDBACK_ADDR,
+ .feedback_shift = MIPS_FEEDBACK_SHIFT,
+ .feedback_mask = MIPS_FEEDBACK_MASK
+ }
+};
+
+static int pll_setup(struct pll_parameters *param, u8 divider1, u8 divider2)
+{
+ u32 reg;
+ struct stopwatch sw;
+
+ /* Check input parameters */
+ assert(!(divider1 & ~(param->postdiv1_mask)));
+ assert(!(divider2 & ~(param->postdiv2_mask)));
+
+ /* Temporary bypass PLL (select XTAL as clock input) */
+ reg = read32(PISTACHIO_CLOCK_SWITCH);
+ reg &= ~(param->external_bypass_mask);
+ write32(PISTACHIO_CLOCK_SWITCH, reg);
+
+ /* Un-bypass PLL's internal bypass */
+ reg = read32(param->ctrl_addr);
+ reg &= ~(param->internal_bypass_mask);
+ write32(param->ctrl_addr, reg);
+
+ /* Disable power down */
+ reg = read32(param->power_down_ctrl_addr);
+ reg &= ~(param->power_down_ctrl_mask);
+ write32(param->power_down_ctrl_addr, reg);
+
+ if (param->feedback_addr) {
+ assert(!(param->feedback & ~(param->feedback_mask)));
+ reg = read32(param->feedback_addr);
+ reg &= ~(param->feedback_mask);
+ reg |= (param->feedback << param->feedback_shift) &
+ param->feedback_mask;
+ write32(param->feedback_addr, reg);
+ }
+
+ if (param->refdiv_addr) {
+ assert(!(param->refdivider & ~(param->refdiv_mask)));
+ reg = read32(param->refdiv_addr);
+ reg &= ~(param->refdiv_mask);
+ reg |= (param->refdivider << param->refdiv_shift) &
+ param->refdiv_mask;
+ write32(param->refdiv_addr, reg);
+ }
+
+ /* Read postdivider register value */
+ reg = read32(param->postdiv_addr);
+ /* Set divider 1 */
+ reg &= ~(param->postdiv1_mask);
+ reg |= (divider1 << param->postdiv1_shift) &
+ param->postdiv1_mask;
+ /* Set divider 2 */
+ reg &= ~(param->postdiv2_mask);
+ reg |= (divider2 << param->postdiv2_shift) &
+ param->postdiv2_mask;
+ /* Write back to register */
+ write32(param->postdiv_addr, reg);
+
+ /* Waiting for PLL to lock*/
+ stopwatch_init_usecs_expire(&sw, PLL_TIMEOUT_VALUE_US);
+ while (!(read32(param->status_addr) & param->status_lock_mask)) {
+ if (stopwatch_expired(&sw))
+ return PLL_TIMEOUT;
+ }
+
+ /* Start using PLL */
+ reg = read32(PISTACHIO_CLOCK_SWITCH);
+ reg |= param->external_bypass_mask;
+ write32(PISTACHIO_CLOCK_SWITCH, reg);
+
+ return CLOCKS_OK;
+}
+
+int sys_pll_setup(u8 divider1, u8 divider2)
+{
+ return pll_setup(&(pll_params[SYS_PLL]), divider1, divider2);
+}
+
+int mips_pll_setup(u8 divider1, u8 divider2, u8 refdivider, u32 feedback)
+{
+ pll_params[MIPS_PLL].refdivider = refdivider;
+ pll_params[MIPS_PLL].feedback = feedback;
+ return pll_setup(&(pll_params[MIPS_PLL]), divider1, divider2);
+}
+
+/*
+ * uart1_clk_setup: sets up clocks for UART1
+ * divider1: 3-bit divider value
+ * divider2: 10-bit divider value
+ */
+void uart1_clk_setup(u8 divider1, u16 divider2)
+{
+ u32 reg;
+
+ /* Check input parameters */
+ assert(!(divider1 & ~(UART1CLKINTERNAL_MASK)));
+ assert(!(divider2 & ~(UART1CLKOUT_MASK)));
+
+ /* Set divider 1 */
+ reg = read32(UART1CLKINTERNAL_CTRL_ADDR);
+ reg &= ~UART1CLKINTERNAL_MASK;
+ reg |= divider1 & UART1CLKINTERNAL_MASK;
+ write32(UART1CLKINTERNAL_CTRL_ADDR, reg);
+
+ /* Set divider 2 */
+ reg = read32(UART1CLKOUT_CTRL_ADDR);
+ reg &= ~UART1CLKOUT_MASK;
+ reg |= divider2 & UART1CLKOUT_MASK;
+ write32(UART1CLKOUT_CTRL_ADDR, reg);
+}
+
+/* system_clk_setup: sets up the system (peripheral) clock */
+void system_clk_setup(u8 divider)
+{
+ u32 reg;
+
+ /* Check input parameters */
+ assert(!(divider & ~(SYSCLKINTERNAL_MASK)));
+
+ /* Set system clock divider */
+ reg = read32(SYSCLKINTERNAL_CTRL_ADDR);
+ reg &= ~SYSCLKINTERNAL_MASK;
+ reg |= divider & SYSCLKINTERNAL_MASK;
+ write32(SYSCLKINTERNAL_CTRL_ADDR, reg);
+
+ /* Small delay to cover a maximum lock time of 1500 cycles */
+ udelay(SYS_CLK_LOCK_DELAY);
+}
+
+/* usb_clk_setup: sets up USB clock */
+int usb_clk_setup(u8 divider, u8 refclksel, u8 fsel)
+{
+ u32 reg;
+ struct stopwatch sw;
+
+ /* Check input parameters */
+ assert(!(divider & ~(USBPHYCLKOUT_MASK)));
+ assert(!(refclksel & ~(USBPHYSTRAPCTRL_REFCLKSEL_MASK)));
+ assert(!(fsel & ~(USBPHYCONTROL1_FSEL_MASK)));
+
+ /* Set USB divider */
+ reg = read32(USBPHYCLKOUT_CTRL_ADDR);
+ reg &= ~USBPHYCLKOUT_MASK;
+ reg |= divider & USBPHYCLKOUT_MASK;
+ write32(USBPHYCLKOUT_CTRL_ADDR, reg);
+
+ /* Set REFCLKSEL */
+ reg = read32(USBPHYSTRAPCTRL_ADDR);
+ reg &= ~USBPHYSTRAPCTRL_REFCLKSEL_MASK;
+ reg |= (refclksel << USBPHYSTRAPCTRL_REFCLKSEL_SHIFT) &
+ USBPHYSTRAPCTRL_REFCLKSEL_MASK;
+ write32(USBPHYSTRAPCTRL_ADDR, reg);
+
+ /* Set FSEL */
+ reg = read32(USBPHYCONTROL1_ADDR);
+ reg &= ~USBPHYCONTROL1_FSEL_MASK;
+ reg |= (fsel << USBPHYCONTROL1_FSEL_SHIFT) &
+ USBPHYCONTROL1_FSEL_MASK;
+ write32(USBPHYCONTROL1_ADDR, reg);
+
+ /* Waiting for USB clock status */
+ stopwatch_init_usecs_expire(&sw, USB_TIMEOUT_VALUE_US);
+ while (1) {
+ reg = read32(USBPHYSTATUS_ADDR);
+ if (reg & USBPHYSTATUS_VBUS_FAULT_MASK)
+ return USB_VBUS_FAULT;
+ if (stopwatch_expired(&sw))
+ return USB_TIMEOUT;
+ }
+
+ return CLOCKS_OK;
+}
diff --git a/src/soc/imgtec/pistachio/include/soc/clocks.h b/src/soc/imgtec/pistachio/include/soc/clocks.h
new file mode 100644
index 0000000000..95b35857a5
--- /dev/null
+++ b/src/soc/imgtec/pistachio/include/soc/clocks.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Imagination Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SOC_IMGTEC_PISTACHIO_CLOCKS_H__
+#define __SOC_IMGTEC_PISTACHIO_CLOCKS_H__
+
+#include <stdint.h>
+
+/* Functions for PLL setting */
+int sys_pll_setup(u8 divider1, u8 divider2);
+int mips_pll_setup(u8 divider1, u8 divider2i, u8 predivider, u32 feedback);
+
+/* Peripheral divider setting */
+void uart1_clk_setup(u8 divider1, u16 divider2);
+void system_clk_setup(u8 divider);
+int usb_clk_setup(u8 divider, u8 refclksel, u8 fsel);
+
+enum {
+ CLOCKS_OK = 0,
+ PLL_TIMEOUT = -1,
+ USB_TIMEOUT = -2,
+ USB_VBUS_FAULT = -3
+};
+
+#endif