[PATCH 21/24] C6X: specific SoC support

Mark Salter
2011-08-22T20:11:11Z

This patch provides the code for the four currently supported SoCs. Each SoC
provides a struct soc_ops which contains hooks for miscellaneous functionality
which does not fit well in any drivers. At setup_arch time, the device tree is
probed to find the correct soc_ops struct to use.

Most SoCs provide one or more sets of MMIO registers for various SoC-specific
low-level device setup and control. the device tree is parsed at setup_arch
time to find the base address of these registers.

Signed-off-by: Mark Salter <msalter@redhat.com>
---
arch/c6x/platforms/soc-6455.c | 389 +++++++++++++++++++++++++++++++++++++
arch/c6x/platforms/soc-6457.c | 187 ++++++++++++++++++
arch/c6x/platforms/soc-6472.c | 428 +++++++++++++++++++++++++++++++++++++++++
arch/c6x/platforms/soc-6474.c | 244 +++++++++++++++++++++++
4 files changed, 1248 insertions(+), 0 deletions(-)
create mode 100644 arch/c6x/platforms/soc-6455.c
create mode 100644 arch/c6x/platforms/soc-6457.c
create mode 100644 arch/c6x/platforms/soc-6472.c
create mode 100644 arch/c6x/platforms/soc-6474.c

diff --git a/arch/c6x/platforms/soc-6455.c b/arch/c6x/platforms/soc-6455.c
new file mode 100644
index 0000000..03fb618
--- /dev/null
+++ b/arch/c6x/platforms/soc-6455.c
@@ -0,0 +1,389 @@
+/*
+ * Miscellaneous SoC specific code
+ *
+ * Port on Texas Instruments TMS320C6x architecture
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/clock.h>
+#include <asm/soc.h>
+#include "megamod-pic.h"
+#include "timer64.h"
+
+static struct clk_lookup c6455_clks[] = {
+ CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]),
+ CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]),
+ CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]),
+ CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]),
+ CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]),
+ CLK(NULL, "core", &c6x_core_clk),
+ CLK("i2c_davinci.1", NULL, &c6x_i2c_clk),
+ CLK("watchdog", NULL, &c6x_watchdog_clk),
+ CLK("2c81800.mdio", NULL, &c6x_mdio_clk),
+ CLK("", NULL, NULL)
+};
+
+
+/* assumptions used for delay loop calculations */
+#define MIN_CLKIN1_KHz 10000
+#define MAX_CORE_KHz 1200000
+#define MIN_PLLOUT_KHz (MIN_CLKIN1_KHz/3)
+
+static void __init __setup_clocks(void)
+{
+ struct pll_data *pll = &c6x_soc_pll1;
+ struct clk *sysclks = pll->sysclks;
+
+ pll->flags = PLL_HAS_PRE | PLL_HAS_MUL;
+
+ pll->bypass_delay = 4 * (MAX_CORE_KHz/MIN_PLLOUT_KHz);
+ pll->reset_delay = 128 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+ pll->lock_delay = 2000 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+
+ sysclks[2].flags |= FIXED_DIV_PLL;
+ sysclks[2].div = 3;
+ sysclks[3].flags |= FIXED_DIV_PLL;
+ sysclks[3].div = 6;
+ sysclks[4].div = PLLDIV4;
+ sysclks[5].div = PLLDIV5;
+
+ c6x_core_clk.parent = &sysclks[0];
+ c6x_i2c_clk.parent = &sysclks[3];
+ c6x_watchdog_clk.parent = &sysclks[3];
+ c6x_mdio_clk.parent = &sysclks[3];
+
+ c6x_clks_init(c6455_clks);
+}
+
+/*
+ * Chip Level (SoC) Registers
+ */
+#define CHIP_DEVSTAT 0x00
+#define CHIP_PRI_ALLOC 0x04
+#define CHIP_JTAGID 0x08
+
+/*
+ * Device State Controller Registers
+ */
+#define DSCR_PERLOCK 0x04
+#define DSCR_PERCFG0 0x08
+#define DSCR_PERSTAT0 0x14
+#define DSCR_PERSTAT1 0x18
+#define DSCR_EMACCFG 0x20
+#define DSCR_PERCFG1 0x2C
+#define DSCR_EMUBUFPD 0x54
+
+#define DSCR_LOCKVAL 0x0f0a0b00
+
+/* device IDs passed to dscr_enable() and dscr_disable() */
+#define DSCR_TCP 0
+#define DSCR_VCP 1
+#define DSCR_EMAC 2
+#define DSCR_TIMER0 3
+#define DSCR_TIMER1 4
+#define DSCR_GPIO 5
+#define DSCR_I2C 6
+#define DSCR_BSP0 7
+#define DSCR_BSP1 8
+#define DSCR_HPI 9
+#define DSCR_PCI 10
+#define DSCR_UTOPIA 11
+#define DSCR_SRIO 15
+#define DSCR_EMIFA 16
+#define DSCR_DDR2 17
+
+#define PERSTAT_DISABLED 0
+#define PERSTAT_ENABLED 1
+#define PERSTAT_PWRDOWN 3
+#define PERSTAT_INPROGRESS 5
+
+static void __iomem *chip_base;
+static void __iomem *dscr_base;
+
+static inline void dscr_write(int offset, unsigned int val)
+{
+ soc_writel(val, dscr_base + offset);
+}
+
+static inline unsigned int dscr_read(int offset)
+{
+ return soc_readl(dscr_base + offset);
+}
+
+static inline void chip_write(int offset, unsigned int val)
+{
+ soc_writel(val, chip_base + offset);
+}
+
+static inline unsigned int chip_read(int offset)
+{
+ return soc_readl(chip_base + offset);
+}
+
+static inline void __write_percfg0(unsigned int val)
+{
+ void __iomem *_base = dscr_base;
+ int _key = DSCR_LOCKVAL;
+
+ /*
+ * We need to ensure write to lock register and write to PERCFG0 are
+ * in same fetch packet and that an interrupt doesn't occur between
+ * them. The .align makes sure they're in the same packet and putting
+ * them in the delay slots of the second branch ensures they are not
+ * interrupted.
+ */
+ asm volatile ("b .s2 0f\n"
+ "nop 5\n"
+ " .align 5\n"
+ "0:\n"
+ "b .s2 0f\n"
+ "stw .D1T1 %2,*+%1(%4)\n"
+ "stw .D1T1 %0,*+%1(%3)\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "0:\n"
+ :
+ : "a"(val), "a"(_base), "a"(_key),
+ "Iu5"(DSCR_PERCFG0), "Iu5"(DSCR_PERLOCK)
+ );
+}
+
+/*
+ * Enable a peripheral through PERCFG registers.
+ */
+static void dscr_enable(int id)
+{
+ unsigned int val;
+ int shift;
+
+ if (id < 16) {
+ int status_reg;
+
+ shift = id * 2;
+
+ val = dscr_read(DSCR_PERCFG0);
+ val &= ~(3 << shift);
+ if (id == DSCR_SRIO) {
+ /* SRIO is handled differently than the others */
+ val |= (3 << shift);
+ __write_percfg0(val);
+ return;
+ }
+ val |= (1 << shift);
+ __write_percfg0(val);
+ /* wait for completion */
+ if (id < 10) {
+ status_reg = DSCR_PERSTAT0;
+ shift = id * 3;
+ } else {
+ status_reg = DSCR_PERSTAT1;
+ shift = (id - 10) * 3;
+ }
+ do {
+ val = dscr_read(status_reg);
+ val >>= shift;
+ val &= 7;
+ } while (val != PERSTAT_ENABLED);
+ } else {
+ shift = id - 16;
+ val = dscr_read(DSCR_PERCFG1);
+ dscr_write(DSCR_PERCFG1, val | (1 << shift));
+ __delay(100);
+ }
+}
+
+/*
+ * Disable a peripheral through PERCFG registers.
+ */
+static void dscr_disable(int id)
+{
+ unsigned int val;
+ int shift;
+
+ if (id < 16) {
+ shift = id * 2;
+
+ val = dscr_read(DSCR_PERCFG0);
+ val &= ~(3 << shift);
+ __write_percfg0(val);
+ }
+}
+
+static void __dev_enable(int id, unsigned int index, int enable)
+{
+ switch (id) {
+ case DSCR_TIMER0:
+ if (index == 1)
+ id = DSCR_TIMER1;
+ else if (index)
+ return;
+ break;
+ case DSCR_BSP0:
+ if (index == 1)
+ id = DSCR_BSP1;
+ else if (index)
+ return;
+ break;
+ default:
+ if (index)
+ return;
+ break;
+ }
+ if (enable)
+ dscr_enable(id);
+ else
+ dscr_disable(id);
+}
+
+static void c6455_dev_enable(enum soc_device_id id, int index)
+{
+ __dev_enable(id, index, 1);
+}
+
+static void c6455_dev_disable(enum soc_device_id id, int index)
+{
+ __dev_enable(id, index, 0);
+}
+
+static void __init __setup_dscr(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,tms320c6455-dscr");
+ if (!node)
+ return;
+
+ dscr_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!dscr_base)
+ return;
+
+ node = of_find_node_by_type(NULL, "soc");
+ if (node) {
+ chip_base = of_iomap(node, 0);
+ of_node_put(node);
+
+ pr_info("DEVSTAT=%08x PERCFG0=%08x PERCFG1=%08x\n",
+ chip_read(CHIP_DEVSTAT), dscr_read(DSCR_PERCFG0),
+ dscr_read(DSCR_PERCFG1));
+ }
+
+ SOC_DEVCONFIG_SUPPORT(TCP, DSCR_TCP);
+ SOC_DEVCONFIG_SUPPORT(VCP, DSCR_VCP);
+ SOC_DEVCONFIG_SUPPORT(EMAC, DSCR_EMAC);
+ SOC_DEVCONFIG_SUPPORT(TIMER, DSCR_TIMER0);
+ SOC_DEVCONFIG_SUPPORT(GPIO, DSCR_GPIO);
+ SOC_DEVCONFIG_SUPPORT(I2C, DSCR_I2C);
+ SOC_DEVCONFIG_SUPPORT(MCBSP, DSCR_BSP0);
+ SOC_DEVCONFIG_SUPPORT(HPI, DSCR_HPI);
+ SOC_DEVCONFIG_SUPPORT(PCI, DSCR_PCI);
+ SOC_DEVCONFIG_SUPPORT(UTOPIA, DSCR_UTOPIA);
+ SOC_DEVCONFIG_SUPPORT(SRIO, DSCR_SRIO);
+ SOC_DEVCONFIG_SUPPORT(EMIF, DSCR_EMIFA);
+ SOC_DEVCONFIG_SUPPORT(DDR2, DSCR_DDR2);
+
+ c6455_dev_enable(SOC_DEV_TIMER, 0);
+ c6455_dev_enable(SOC_DEV_TIMER, 1);
+
+#ifdef CONFIG_I2C
+ c6455_dev_enable(SOC_DEV_I2C, 0);
+#endif
+#ifdef CONFIG_GENERIC_GPIO
+ c6455_dev_enable(SOC_DEV_GPIO, 0);
+#endif
+#ifdef CONFIG_TMS320C64X_GEMAC
+ c6455_dev_enable(SOC_DEV_EMAC, 0);
+#endif
+}
+
+static unsigned int c6455_silicon_rev(char **strp)
+{
+ u32 jtagid = chip_read(CHIP_JTAGID);
+ u32 silicon_rev = (jtagid >> 28) & 0xf;
+
+ if (strp) {
+ switch (silicon_rev) {
+ case 0:
+ *strp = "1.1";
+ break;
+ case 1:
+ *strp = "2.0";
+ break;
+ case 2:
+ *strp = "2.1";
+ break;
+ case 4:
+ *strp = "3.1";
+ break;
+ default:
+ *strp = "unknown";
+ break;
+ }
+ }
+ return silicon_rev;
+}
+
+static int macsel_ids[] = {
+ [0] = MACSEL_MII,
+ [1] = MACSEL_RMII,
+ [2] = MACSEL_GMII,
+ [3] = MACSEL_RGMII,
+};
+
+static int c6455_macsel(unsigned int index)
+{
+ u32 devstat = chip_read(CHIP_DEVSTAT);
+ if (index > 0)
+ return MACSEL_UNKNOWN;
+
+ return macsel_ids[(devstat >> 9) & 3];
+}
+
+static void c6455_rmii_reset_ctl(int index, int assert)
+{
+ u32 val;
+
+ if (index)
+ return;
+
+ val = dscr_read(DSCR_EMACCFG);
+ if (assert)
+ dscr_write(DSCR_EMACCFG, val | (1 << 18));
+ else
+ dscr_write(DSCR_EMACCFG, val & ~(1 << 18));
+}
+
+static void __init c6455_setup_arch(void)
+{
+ /* so we won't get called twice */
+ soc_ops.setup_arch = NULL;
+
+ c6x_num_cores = 1;
+ __setup_dscr();
+ __setup_clocks();
+}
+
+define_soc(tms320c6455) {
+ .compat = "ti,tms320c6455",
+ .name = "TMS320C6455",
+ .setup_arch = c6455_setup_arch,
+ .silicon_rev = c6455_silicon_rev,
+ .init_IRQ = megamod_pic_init,
+ .time_init = timer64_init,
+ .macsel = c6455_macsel,
+ .dev_enable = c6455_dev_enable,
+ .dev_disable = c6455_dev_disable,
+ .rmii_reset_ctl = c6455_rmii_reset_ctl,
+};
diff --git a/arch/c6x/platforms/soc-6457.c b/arch/c6x/platforms/soc-6457.c
new file mode 100644
index 0000000..b6b5f7b
--- /dev/null
+++ b/arch/c6x/platforms/soc-6457.c
@@ -0,0 +1,187 @@
+/*
+ * Miscellaneous SoC specific code
+ *
+ * Port on Texas Instruments TMS320C6x architecture
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/clock.h>
+#include <asm/soc.h>
+#include "psc.h"
+#include "megamod-pic.h"
+#include "timer64.h"
+
+static struct clk_lookup c6457_clks[] = {
+ CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]),
+ CLK(NULL, "pll1_sysclk1", &c6x_soc_pll1.sysclks[1]),
+ CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]),
+ CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]),
+ CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]),
+ CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]),
+ CLK(NULL, "core", &c6x_core_clk),
+ CLK("i2c_davinci.1", NULL, &c6x_i2c_clk),
+ CLK("watchdog", NULL, &c6x_watchdog_clk),
+ CLK("2c81800.mdio", NULL, &c6x_mdio_clk),
+ CLK("", NULL, NULL)
+};
+
+/* assumptions used for delay loop calculations */
+#define MIN_CLKIN1_KHz 50000
+#define MAX_CORE_KHz 1200000
+#define MIN_PLLOUT_KHz (MIN_CLKIN1_KHz/3)
+
+static void __init __setup_clocks(void)
+{
+ struct pll_data *pll = &c6x_soc_pll1;
+ struct clk *sysclks = pll->sysclks;
+
+ pll->flags = PLL_HAS_MUL | PLL_HAS_POST;
+
+ pll->bypass_delay = 4 * (MAX_CORE_KHz/MIN_PLLOUT_KHz);
+ pll->reset_delay = 1000 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+ pll->lock_delay = 2000 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+
+ sysclks[1].flags |= FIXED_DIV_PLL;
+ sysclks[1].div = 1;
+ sysclks[2].flags |= FIXED_DIV_PLL;
+ sysclks[2].div = 3;
+ sysclks[3].flags |= FIXED_DIV_PLL;
+ sysclks[3].div = 6;
+ sysclks[4].div = PLLDIV4;
+ sysclks[5].div = PLLDIV5;
+
+ c6x_core_clk.parent = &sysclks[1];
+ c6x_i2c_clk.parent = &sysclks[3];
+ c6x_watchdog_clk.parent = &sysclks[5];
+ c6x_mdio_clk.parent = &sysclks[5];
+
+ c6x_clks_init(c6457_clks);
+}
+
+#define DSCR_JTAGID 0x018
+#define DSCR_DEVSTAT 0x020
+#define DSCR_KICK0 0x038
+#define DSCR_KICK1 0x03c
+#define DSCR_BOOTADDR 0x040
+#define DSCR_DEVCFG 0x110
+#define DSCR_MACID1 0x114
+#define DSCR_MACID2 0x118
+#define DSCR_PRI_ALLOC 0x11c
+#define DSCR_WDRSTSEL 0x120
+
+#define DSCR_KICK0_KEY 0x83E70B13
+#define DSCR_KICK1_KEY 0x95A4F1E0
+
+static void __iomem *dscr_base;
+
+static inline void dscr_write(int offset, unsigned int val)
+{
+ soc_writel(val, dscr_base + offset);
+}
+
+static inline unsigned int dscr_read(int offset)
+{
+ return soc_readl(dscr_base + offset);
+}
+
+static void __init __setup_dscr(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,tms320c6457-dscr");
+ if (!node)
+ return;
+
+ dscr_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!dscr_base)
+ return;
+
+ printk(KERN_INFO "DEVSTAT=%08x DEVCFG=%08x\n",
+ dscr_read(DSCR_DEVSTAT), dscr_read(DSCR_DEVCFG));
+}
+
+static unsigned int c6457_silicon_rev(char **strp)
+{
+ u32 jtagid = dscr_read(DSCR_JTAGID);
+ u32 silicon_rev = (jtagid >> 28) & 0xf;
+
+ if (strp) {
+ switch (silicon_rev) {
+ case 0:
+ *strp = "1.1";
+ break;
+ case 1:
+ *strp = "1.2";
+ break;
+ case 2:
+ *strp = "1.3";
+ break;
+ case 3:
+ *strp = "1.4";
+ break;
+ default:
+ *strp = "unknown";
+ break;
+ }
+ }
+ return silicon_rev;
+}
+
+static int c6457_macsel(unsigned int index)
+{
+ /* all sgmii, all the time */
+ return MACSEL_SGMII;
+}
+
+static int c6457_mac_addr(unsigned int index, u8 *addr)
+{
+ unsigned int fuse0, fuse1;
+
+ if (index)
+ return 0;
+
+ fuse0 = dscr_read(DSCR_MACID1);
+ fuse1 = dscr_read(DSCR_MACID2);
+
+ addr[0] = fuse1 >> 8;
+ addr[1] = fuse1 >> 0;
+ addr[2] = fuse0 >> 24;
+ addr[3] = fuse0 >> 16;
+ addr[4] = fuse0 >> 8;
+ addr[5] = fuse0 >> 0;
+
+ return 1;
+}
+
+static void __init c6457_setup_arch(void)
+{
+ /* so we won't get called twice */
+ soc_ops.setup_arch = NULL;
+
+ c6x_num_cores = 1;
+ psc_init();
+ __setup_dscr();
+ __setup_clocks();
+}
+
+define_soc(tms320c6457) {
+ .compat = "ti,tms320c6457",
+ .name = "TMS320C6457",
+ .setup_arch = c6457_setup_arch,
+ .silicon_rev = c6457_silicon_rev,
+ .init_IRQ = megamod_pic_init,
+ .time_init = timer64_init,
+ .macsel = c6457_macsel,
+ .mac_addr = c6457_mac_addr,
+};
diff --git a/arch/c6x/platforms/soc-6472.c b/arch/c6x/platforms/soc-6472.c
new file mode 100644
index 0000000..0fce934
--- /dev/null
+++ b/arch/c6x/platforms/soc-6472.c
@@ -0,0 +1,428 @@
+/*
+ * Miscellaneous SoC specific code
+ *
+ * Port on Texas Instruments TMS320C6x architecture
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/clock.h>
+#include <asm/soc.h>
+#include "psc.h"
+#include "megamod-pic.h"
+#include "timer64.h"
+
+static struct clk_lookup c6472_clks[] = {
+ CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]),
+ CLK(NULL, "pll1_sysclk1", &c6x_soc_pll1.sysclks[1]),
+ CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]),
+ CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]),
+ CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]),
+ CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]),
+ CLK(NULL, "pll1_sysclk6", &c6x_soc_pll1.sysclks[6]),
+ CLK(NULL, "pll1_sysclk7", &c6x_soc_pll1.sysclks[7]),
+ CLK(NULL, "pll1_sysclk8", &c6x_soc_pll1.sysclks[8]),
+ CLK(NULL, "pll1_sysclk9", &c6x_soc_pll1.sysclks[9]),
+ CLK(NULL, "pll1_sysclk10", &c6x_soc_pll1.sysclks[10]),
+ CLK(NULL, "core", &c6x_core_clk),
+ CLK("i2c_davinci.1", NULL, &c6x_i2c_clk),
+ CLK("watchdog", NULL, &c6x_watchdog_clk),
+ CLK("2c81800.mdio", NULL, &c6x_mdio_clk),
+ CLK("", NULL, NULL)
+};
+
+/* assumptions used for delay loop calculations */
+#define MIN_CLKIN1_KHz 15625
+#define MAX_CORE_KHz 700000
+#define MIN_PLLOUT_KHz MIN_CLKIN1_KHz
+
+static void __init __setup_clocks(void)
+{
+ struct pll_data *pll = &c6x_soc_pll1;
+ struct clk *sysclks = pll->sysclks;
+ int i;
+
+ pll->flags = PLL_HAS_MUL;
+
+ pll->bypass_delay = 4 * (MAX_CORE_KHz/MIN_PLLOUT_KHz);
+ pll->reset_delay = 256 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+ pll->lock_delay = 2000 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+
+ for (i = 1; i <= 6; i++) {
+ sysclks[i].flags |= FIXED_DIV_PLL;
+ sysclks[i].div = 1;
+ }
+
+ sysclks[7].flags |= FIXED_DIV_PLL;
+ sysclks[7].div = 3;
+ sysclks[8].flags |= FIXED_DIV_PLL;
+ sysclks[8].div = 6;
+ sysclks[9].flags |= FIXED_DIV_PLL;
+ sysclks[9].div = 2;
+ sysclks[10].div = PLLDIV10;
+
+ c6x_core_clk.parent = &sysclks[get_coreid() + 1];
+ c6x_i2c_clk.parent = &sysclks[8];
+ c6x_watchdog_clk.parent = &sysclks[8];
+ c6x_mdio_clk.parent = &sysclks[5];
+
+ c6x_clks_init(c6472_clks);
+}
+
+#define DSCR_BASE_ADDR 0x2a80000
+#define DSCR_SIZE 0x1000
+
+#define DSCR_DEVSTAT 0x000
+#define DSCR_PRIALLOC 0x004
+#define DSCR_DEVID 0x008
+#define DSCR_DEVCTL 0x200
+#define DSCR_DEVCTL_KEY 0x204
+#define DSCR_RMIIRESET0 0x208
+#define DSCR_RMIIRESET1 0x20c
+
+#define DSCR_HOSTPRIV 0x40c
+#define DSCR_PRIVPERM 0x41c
+#define DSCR_PRIVKEY 0x420
+
+#define DSCR_NMIGR0 0x500
+#define DSCR_NMIGR1 0x504
+#define DSCR_NMIGR2 0x508
+#define DSCR_NMIGR3 0x50c
+#define DSCR_NMIGR4 0x510
+#define DSCR_NMIGR5 0x514
+
+#define DSCR_IPCGR0 0x540
+#define DSCR_IPCGR1 0x544
+#define DSCR_IPCGR2 0x548
+#define DSCR_IPCGR3 0x54c
+#define DSCR_IPCGR4 0x550
+#define DSCR_IPCGR5 0x554
+#define DSCR_IPCGRH 0x57c
+
+#define DSCR_IPCAR0 0x580
+#define DSCR_IPCAR1 0x584
+#define DSCR_IPCAR2 0x588
+#define DSCR_IPCAR3 0x58c
+#define DSCR_IPCAR4 0x590
+#define DSCR_IPCAR5 0x594
+#define DSCR_IPCARH 0x5bc
+
+#define DSCR_EFUSE0 0x700
+#define DSCR_EFUSE1 0x704
+#define DSCR_SILICON_ID 0x70c
+
+#define DSCR_TPMGR 0x714
+
+#define DSCR_RSTMUX0 0x718
+#define DSCR_RSTMUX1 0x71c
+#define DSCR_RSTMUX2 0x720
+#define DSCR_RSTMUX3 0x724
+#define DSCR_RSTMUX4 0x728
+#define DSCR_RSTMUX5 0x72c
+
+#define DSCR_LOCKVAL 0xa1e183a
+
+/* device IDs (actually bit masks) passed to dscr_enable() and dscr_disable() */
+#define DSCR_HPI BIT(0)
+#define DSCR_UTOPIA (BIT(1) | BIT(2))
+#define DSCR_TSIP0 (BIT(3) | BIT(4) | BIT(5))
+#define DSCR_TSIP1 (BIT(6) | BIT(7) | BIT(8))
+#define DSCR_TSIP2 (BIT(9) | BIT(10) | BIT(11))
+#define DSCR_EMAC1 BIT(12)
+
+static void __iomem *dscr_base;
+
+static inline void dscr_write(int offset, unsigned int val)
+{
+ soc_writel(val, dscr_base + offset);
+}
+
+static inline unsigned int dscr_read(int offset)
+{
+ return soc_readl(dscr_base + offset);
+}
+
+static inline void __write_devctl(unsigned int val)
+{
+ int _base = DSCR_BASE_ADDR + DSCR_DEVCTL;
+ int _key = DSCR_LOCKVAL;
+
+ /*
+ * We need to ensure write to lock register and write to DEVCTL are
+ * not interrupted. Putting them both in the delay slots of a branch
+ * ensures they are not interrupted.
+ */
+ asm volatile ("b .s2 0f\n"
+ "stw .D1T1 %2,*+%1(%3)\n"
+ "stw .D1T1 %0,*%1\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "0:\n"
+ :
+ : "a"(val), "a"(_base), "a"(_key),
+ "Iu5"(DSCR_DEVCTL_KEY - DSCR_DEVCTL)
+ );
+}
+
+/*
+ * Enable a peripheral through PERCFG registers.
+ */
+void dscr_enable(int mask)
+{
+ unsigned int val;
+
+ val = dscr_read(DSCR_DEVCTL);
+
+ /* only need to enable ones that are currently disabled */
+ mask &= ~val;
+ if (mask == 0)
+ return;
+
+ __write_devctl(val | mask);
+}
+
+/*
+ * Disable a peripheral through PERCFG registers.
+ */
+static void dscr_disable(int mask)
+{
+ u32 val;
+
+ val = dscr_read(DSCR_DEVCTL);
+
+ /* only need to disable ones that are currently enabled */
+ mask &= val;
+ if (mask == 0)
+ return;
+
+ __write_devctl(val & ~mask);
+}
+
+static void __dev_enable(int id, unsigned int index, int enable)
+{
+ switch (id) {
+ case DSCR_EMAC1:
+ if (index != 1)
+ return;
+ break;
+ case DSCR_TSIP0:
+ if (index == 1)
+ id = DSCR_TSIP1;
+ else if (index == 2)
+ id = DSCR_TSIP2;
+ else if (index)
+ return;
+ break;
+ default:
+ if (index)
+ return;
+ break;
+ }
+ if (enable)
+ dscr_enable(id);
+ else
+ dscr_disable(id);
+}
+
+static void c6472_dev_enable(enum soc_device_id id, int index)
+{
+ __dev_enable(id, index, 1);
+}
+
+static void c6472_dev_disable(enum soc_device_id id, int index)
+{
+ __dev_enable(id, index, 0);
+}
+
+static void __init __setup_dscr(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,tms320c6472-dscr");
+ if (!node)
+ return;
+
+ dscr_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!dscr_base)
+ return;
+
+ SOC_DEVCONFIG_SUPPORT(HPI, DSCR_HPI);
+ SOC_DEVCONFIG_SUPPORT(UTOPIA, DSCR_UTOPIA);
+ SOC_DEVCONFIG_SUPPORT(EMAC, DSCR_EMAC1);
+ SOC_DEVCONFIG_SUPPORT(TSIP, DSCR_TSIP0);
+
+ pr_info("DEVSTAT=%08x DEVCTL=%08x\n",
+ dscr_read(DSCR_DEVSTAT), dscr_read(DSCR_DEVCTL));
+
+ /* Do not allow user mode to access SoC device I/O */
+ dscr_write(DSCR_PRIVKEY, 0xbea7);
+ dscr_write(DSCR_PRIVPERM, 0xaaaaaaaa);
+ dscr_write(DSCR_PRIVKEY, 0);
+}
+
+static unsigned int c6472_silicon_rev(char **strp)
+{
+ u32 silicon_rev = dscr_read(DSCR_SILICON_ID);
+ int major, minor;
+ static char __str[6];
+
+ major = (silicon_rev >> 20) & 0xf;
+ minor = (silicon_rev >> 16) & 0xf;
+ if (strp) {
+ sprintf(__str, "%d.%d", major, minor);
+ *strp = __str;
+ }
+ return (major << 4) | minor;
+}
+
+static int macsel0_ids[] = {
+ [0] = MACSEL_MII,
+ [1] = MACSEL_RMII,
+ [2] = MACSEL_GMII,
+ [3] = MACSEL_RGMII,
+ [4] = MACSEL_UNKNOWN,
+ [5] = MACSEL_S3MII,
+ [6] = MACSEL_UNKNOWN,
+ [7] = MACSEL_DISABLED,
+};
+
+static int macsel1_ids[] = {
+ [0] = MACSEL_UNKNOWN,
+ [1] = MACSEL_S3MII,
+ [2] = MACSEL_RGMII,
+ [3] = MACSEL_RMII,
+};
+
+
+#define RESET_STATUS 0x0000
+#define BOOT_COMPLETE 0x0004
+#define BOOT_PROGRESS 0x0008
+
+static void __iomem *boot_base;
+
+static inline void boot_write(int offset, unsigned int val)
+{
+ soc_writel(val, boot_base + offset);
+}
+
+void __setup_boot_control(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL,
+ "ti,tms320c6472-boot-controller");
+ if (!node)
+ return;
+
+ boot_base = of_iomap(node, 0);
+ of_node_put(node);
+}
+
+static int c6472_macsel(unsigned int index)
+{
+ u32 devstat = dscr_read(DSCR_DEVSTAT);
+
+ if (index == 0)
+ return macsel0_ids[(devstat >> 8) & 7];
+ if (index == 1)
+ return macsel1_ids[(devstat >> 22) & 3];
+ return MACSEL_UNKNOWN;
+}
+
+static int c6472_mac_addr(unsigned int index, u8 *addr)
+{
+ unsigned int fuse0, fuse1;
+
+ if (index)
+ return 0;
+
+ fuse0 = dscr_read(DSCR_EFUSE0);
+ fuse1 = dscr_read(DSCR_EFUSE1);
+
+ addr[0] = fuse0 >> 24;
+ addr[1] = fuse0 >> 16;
+ addr[2] = fuse0 >> 8;
+ addr[3] = fuse0 >> 0;
+ addr[4] = fuse1 >> 24;
+ addr[5] = fuse1 >> 16;
+
+ return 1;
+}
+
+static void c6472_boot_core(int corenum)
+{
+ psc_module_reset_release(corenum);
+ boot_write(BOOT_COMPLETE, 0x3f);
+}
+
+static void c6472_reset_core(int corenum)
+{
+ psc_module_reset(corenum);
+}
+
+static void c6472_rmii_reset_ctl(int index, int assert)
+{
+ u32 reg;
+
+ if (index == 0)
+ reg = DSCR_RMIIRESET0;
+ else if (index == 1)
+ reg = DSCR_RMIIRESET1;
+ else
+ return;
+
+ dscr_write(reg, assert ? 1 : 0);
+}
+
+static void __init c6472_setup_arch(void)
+{
+ /* so we won't get called twice */
+ soc_ops.setup_arch = NULL;
+
+ c6x_num_cores = 6;
+ __setup_dscr();
+
+ psc_init();
+
+ /* EMAC 0 */
+ psc_module_reset_release(7);
+ psc_module_on(7);
+
+ /* EMAC 1 */
+ psc_module_reset_release(8);
+ psc_module_on(8);
+
+ psc_transition_domains(1);
+
+ dscr_enable(DSCR_EMAC1);
+
+ __setup_clocks();
+}
+
+define_soc(tms320c6472) {
+ .compat = "ti,tms320c6472",
+ .name = "TMS320C6472",
+ .setup_arch = c6472_setup_arch,
+ .init_IRQ = megamod_pic_init,
+ .time_init = timer64_init,
+ .silicon_rev = c6472_silicon_rev,
+ .macsel = c6472_macsel,
+ .mac_addr = c6472_mac_addr,
+ .boot_core = c6472_boot_core,
+ .reset_core = c6472_reset_core,
+ .dev_enable = c6472_dev_enable,
+ .dev_disable = c6472_dev_disable,
+ .rmii_reset_ctl = c6472_rmii_reset_ctl,
+};
diff --git a/arch/c6x/platforms/soc-6474.c b/arch/c6x/platforms/soc-6474.c
new file mode 100644
index 0000000..e3d0f9a
--- /dev/null
+++ b/arch/c6x/platforms/soc-6474.c
@@ -0,0 +1,244 @@
+/*
+ * Miscellaneous SoC specific code
+ *
+ * Port on Texas Instruments TMS320C6x architecture
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/clock.h>
+#include <asm/soc.h>
+#include "megamod-pic.h"
+#include "timer64.h"
+#include "psc.h"
+
+static struct clk_lookup c6474_clks[] = {
+ CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]),
+ CLK(NULL, "pll1_sysclk7", &c6x_soc_pll1.sysclks[7]),
+ CLK(NULL, "pll1_sysclk9", &c6x_soc_pll1.sysclks[9]),
+ CLK(NULL, "pll1_sysclk10", &c6x_soc_pll1.sysclks[10]),
+ CLK(NULL, "pll1_sysclk11", &c6x_soc_pll1.sysclks[11]),
+ CLK(NULL, "pll1_sysclk12", &c6x_soc_pll1.sysclks[12]),
+ CLK(NULL, "pll1_sysclk13", &c6x_soc_pll1.sysclks[13]),
+ CLK(NULL, "core", &c6x_core_clk),
+ CLK("i2c_davinci.1", NULL, &c6x_i2c_clk),
+ CLK("mcbsp.1", NULL, &c6x_mcbsp1_clk),
+ CLK("mcbsp.2", NULL, &c6x_mcbsp2_clk),
+ CLK("watchdog", NULL, &c6x_watchdog_clk),
+ CLK("2c81800.mdio", NULL, &c6x_mdio_clk),
+ CLK("", NULL, NULL)
+};
+
+/* assumptions used for delay loop calculations */
+#define MIN_CLKIN1_KHz 40000
+#define MAX_CORE_KHz 1200000
+#define MIN_PLLOUT_KHz MIN_CLKIN1_KHz
+
+static void __init __setup_clocks(void)
+{
+ struct pll_data *pll = &c6x_soc_pll1;
+ struct clk *sysclks = pll->sysclks;
+
+ pll->flags = PLL_HAS_MUL;
+
+ pll->bypass_delay = 4 * (MAX_CORE_KHz/MIN_PLLOUT_KHz);
+ pll->reset_delay = 1000 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+ pll->lock_delay = 2000 * (MAX_CORE_KHz/MIN_CLKIN1_KHz);
+
+ sysclks[7].flags |= FIXED_DIV_PLL;
+ sysclks[7].div = 1;
+ sysclks[9].flags |= FIXED_DIV_PLL;
+ sysclks[9].div = 3;
+ sysclks[10].flags |= FIXED_DIV_PLL;
+ sysclks[10].div = 6;
+
+ sysclks[11].div = PLLDIV11;
+
+ sysclks[12].flags |= FIXED_DIV_PLL;
+ sysclks[12].div = 2;
+
+ sysclks[13].div = PLLDIV13;
+
+ c6x_core_clk.parent = &sysclks[7];
+ c6x_i2c_clk.parent = &sysclks[10];
+ c6x_watchdog_clk.parent = &sysclks[10];
+ c6x_mcbsp1_clk.parent = &sysclks[10];
+ c6x_mcbsp2_clk.parent = &sysclks[10];
+
+ c6x_clks_init(c6474_clks);
+}
+
+#define DSCR_DEVCFG1 0x000
+#define DSCR_DEVSTAT 0x004
+#define DSCR_BOOTADDR0 0x008
+#define DSCR_BOOTADDR1 0x00c
+#define DSCR_BOOTADDR2 0x010
+#define DSCR_JTAGID 0x014
+#define DSCR_EFUSE0 0x034
+#define DSCR_EFUSE1 0x038
+#define DSCR_PRI_ALLOC 0x03c
+#define DSCR_IPCGR0 0x100
+#define DSCR_IPCGR1 0x104
+#define DSCR_IPCGR2 0x108
+#define DSCR_IPCAR0 0x140
+#define DSCR_IPCAR1 0x144
+#define DSCR_IPCAR2 0x148
+
+static void __iomem *dscr_base;
+
+static inline void dscr_write(int offset, unsigned int val)
+{
+ soc_writel(val, dscr_base + offset);
+}
+
+static inline unsigned int dscr_read(int offset)
+{
+ return soc_readl(dscr_base + offset);
+}
+
+static void __init __setup_dscr(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "ti,tms320c6474-dscr");
+ if (!node)
+ return;
+
+ dscr_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!dscr_base)
+ return;
+
+ printk(KERN_INFO "DEVSTAT=%08x DEVCFG=%08x\n",
+ dscr_read(DSCR_DEVSTAT), dscr_read(DSCR_DEVCFG1));
+}
+
+static unsigned int c6474_silicon_rev(char **strp)
+{
+ u32 jtagid_val = dscr_read(DSCR_JTAGID);
+ u32 silicon_rev = (jtagid_val >> 28) & 0xf;
+ char *str;
+
+ if (strp) {
+ switch (silicon_rev) {
+ case 0x1:
+ str = "1.2";
+ break;
+ case 0x2:
+ str = "1.3";
+ break;
+ case 0x3:
+ str = "2.0";
+ break;
+ case 0x4:
+ str = "2.1";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+ *strp = str;
+ }
+ return silicon_rev;
+}
+
+static int c6474_macsel(unsigned int index)
+{
+ /* all sgmii, all the time */
+ return MACSEL_SGMII;
+}
+
+static int c6474_mac_addr(unsigned int index, u8 *addr)
+{
+ unsigned int fuse0, fuse1;
+
+ if (index)
+ return 0;
+
+ fuse0 = dscr_read(DSCR_EFUSE0);
+ fuse1 = dscr_read(DSCR_EFUSE1);
+
+ addr[0] = fuse1 >> 8;
+ addr[1] = fuse1 >> 0;
+ addr[2] = fuse0 >> 24;
+ addr[3] = fuse0 >> 16;
+ addr[4] = fuse0 >> 8;
+ addr[5] = fuse0 >> 0;
+
+ return 1;
+}
+
+static void c6474_boot_core(int corenum)
+{
+ static int first_time = 1;
+
+ if (first_time) {
+ soc_assert_event(1 << 4);
+ first_time = 0;
+ }
+ psc_module_reset_release(3 + corenum);
+}
+
+static void c6474_reset_core(int corenum)
+{
+ psc_module_reset(3 + corenum);
+}
+
+static void __init c6474_setup_arch(void)
+{
+ /* so we won't get called twice */
+ soc_ops.setup_arch = NULL;
+
+ c6x_num_cores = 3;
+
+ /* setup power registers */
+ psc_init();
+ __setup_dscr();
+
+ /* AIF */
+ psc_domain_on(1);
+ psc_module_on(6);
+
+ /* SRIO */
+ psc_domain_on(2);
+ psc_module_on(7);
+
+ /* reserved */
+ psc_domain_on(3);
+ psc_module_on(8);
+
+ /* TCP */
+ psc_domain_on(4);
+ psc_module_on(9);
+
+ /* VCP */
+ psc_domain_on(5);
+ psc_module_on(10);
+
+ psc_transition_domains(0x3f);
+
+ __setup_clocks();
+}
+
+define_soc(tms320c6474) {
+ .compat = "ti,tms320c6474",
+ .name = "TMS320C6474",
+ .setup_arch = c6474_setup_arch,
+ .init_IRQ = megamod_pic_init,
+ .time_init = timer64_init,
+ .silicon_rev = c6474_silicon_rev,
+ .macsel = c6474_macsel,
+ .mac_addr = c6474_mac_addr,
+ .boot_core = c6474_boot_core,
+ .reset_core = c6474_reset_core,
+};
--
1.7.6