Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

29
drivers/bus/Kconfig Normal file
View File

@@ -0,0 +1,29 @@
#
# Bus Devices
#
menu "Bus devices"
config MVEBU_MBUS
bool
depends on PLAT_ORION
help
Driver needed for the MBus configuration on Marvell EBU SoCs
(Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP).
config OMAP_OCP2SCP
tristate "OMAP OCP2SCP DRIVER"
depends on ARCH_OMAP2PLUS
help
Driver to enable ocp2scp module which transforms ocp interface
protocol to scp protocol. In OMAP4, USB PHY is connected via
OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via
OCP2SCP.
config OMAP_INTERCONNECT
tristate "OMAP INTERCONNECT DRIVER"
depends on ARCH_OMAP2PLUS
help
Driver to enable OMAP interconnect error handling driver.
endmenu

9
drivers/bus/Makefile Normal file
View File

@@ -0,0 +1,9 @@
#
# Makefile for the bus drivers.
#
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
# Interconnect bus driver for OMAP SoCs.
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o

870
drivers/bus/mvebu-mbus.c Normal file
View File

@@ -0,0 +1,870 @@
/*
* Address map functions for Marvell EBU SoCs (Kirkwood, Armada
* 370/XP, Dove, Orion5x and MV78xx0)
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* The Marvell EBU SoCs have a configurable physical address space:
* the physical address at which certain devices (PCIe, NOR, NAND,
* etc.) sit can be configured. The configuration takes place through
* two sets of registers:
*
* - One to configure the access of the CPU to the devices. Depending
* on the families, there are between 8 and 20 configurable windows,
* each can be use to create a physical memory window that maps to a
* specific device. Devices are identified by a tuple (target,
* attribute).
*
* - One to configure the access to the CPU to the SDRAM. There are
* either 2 (for Dove) or 4 (for other families) windows to map the
* SDRAM into the physical address space.
*
* This driver:
*
* - Reads out the SDRAM address decoding windows at initialization
* time, and fills the mvebu_mbus_dram_info structure with these
* informations. The exported function mv_mbus_dram_info() allow
* device drivers to get those informations related to the SDRAM
* address decoding windows. This is because devices also have their
* own windows (configured through registers that are part of each
* device register space), and therefore the drivers for Marvell
* devices have to configure those device -> SDRAM windows to ensure
* that DMA works properly.
*
* - Provides an API for platform code or device drivers to
* dynamically add or remove address decoding windows for the CPU ->
* device accesses. This API is mvebu_mbus_add_window(),
* mvebu_mbus_add_window_remap_flags() and
* mvebu_mbus_del_window(). Since the (target, attribute) values
* differ from one SoC family to another, the API uses a 'const char
* *' string to identify devices, and this driver is responsible for
* knowing the mapping between the name of a device and its
* corresponding (target, attribute) in the current SoC family.
*
* - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to
* see the list of CPU -> SDRAM windows and their configuration
* (file 'sdram') and the list of CPU -> devices windows and their
* configuration (file 'devices').
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mbus.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/debugfs.h>
/*
* DDR target is the same on all platforms.
*/
#define TARGET_DDR 0
/*
* CPU Address Decode Windows registers
*/
#define WIN_CTRL_OFF 0x0000
#define WIN_CTRL_ENABLE BIT(0)
#define WIN_CTRL_TGT_MASK 0xf0
#define WIN_CTRL_TGT_SHIFT 4
#define WIN_CTRL_ATTR_MASK 0xff00
#define WIN_CTRL_ATTR_SHIFT 8
#define WIN_CTRL_SIZE_MASK 0xffff0000
#define WIN_CTRL_SIZE_SHIFT 16
#define WIN_BASE_OFF 0x0004
#define WIN_BASE_LOW 0xffff0000
#define WIN_BASE_HIGH 0xf
#define WIN_REMAP_LO_OFF 0x0008
#define WIN_REMAP_LOW 0xffff0000
#define WIN_REMAP_HI_OFF 0x000c
#define ATTR_HW_COHERENCY (0x1 << 4)
#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3))
#define DDR_BASE_CS_HIGH_MASK 0xf
#define DDR_BASE_CS_LOW_MASK 0xff000000
#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3))
#define DDR_SIZE_ENABLED BIT(0)
#define DDR_SIZE_CS_MASK 0x1c
#define DDR_SIZE_CS_SHIFT 2
#define DDR_SIZE_MASK 0xff000000
#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
struct mvebu_mbus_mapping {
const char *name;
u8 target;
u8 attr;
u8 attrmask;
};
/*
* Masks used for the 'attrmask' field of mvebu_mbus_mapping. They
* allow to get the real attribute value, discarding the special bits
* used to select a PCI MEM region or a PCI WA region. This allows the
* debugfs code to reverse-match the name of a device from its
* target/attr values.
*
* For all devices except PCI, all bits of 'attr' must be
* considered. For most SoCs, only bit 3 should be ignored (it allows
* to select between PCI MEM and PCI I/O). On Orion5x however, there
* is the special bit 5 to select a PCI WA region.
*/
#define MAPDEF_NOMASK 0xff
#define MAPDEF_PCIMASK 0xf7
#define MAPDEF_ORIONPCIMASK 0xd7
/* Macro used to define one mvebu_mbus_mapping entry */
#define MAPDEF(__n, __t, __a, __m) \
{ .name = __n, .target = __t, .attr = __a, .attrmask = __m }
struct mvebu_mbus_state;
struct mvebu_mbus_soc_data {
unsigned int num_wins;
unsigned int num_remappable_wins;
unsigned int (*win_cfg_offset)(const int win);
void (*setup_cpu_target)(struct mvebu_mbus_state *s);
int (*show_cpu_target)(struct mvebu_mbus_state *s,
struct seq_file *seq, void *v);
const struct mvebu_mbus_mapping *map;
};
struct mvebu_mbus_state {
void __iomem *mbuswins_base;
void __iomem *sdramwins_base;
struct dentry *debugfs_root;
struct dentry *debugfs_sdram;
struct dentry *debugfs_devs;
const struct mvebu_mbus_soc_data *soc;
int hw_io_coherency;
};
static struct mvebu_mbus_state mbus_state;
static struct mbus_dram_target_info mvebu_mbus_dram_info;
const struct mbus_dram_target_info *mv_mbus_dram_info(void)
{
return &mvebu_mbus_dram_info;
}
EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
/*
* Functions to manipulate the address decoding windows
*/
static void mvebu_mbus_read_window(struct mvebu_mbus_state *mbus,
int win, int *enabled, u64 *base,
u32 *size, u8 *target, u8 *attr,
u64 *remap)
{
void __iomem *addr = mbus->mbuswins_base +
mbus->soc->win_cfg_offset(win);
u32 basereg = readl(addr + WIN_BASE_OFF);
u32 ctrlreg = readl(addr + WIN_CTRL_OFF);
if (!(ctrlreg & WIN_CTRL_ENABLE)) {
*enabled = 0;
return;
}
*enabled = 1;
*base = ((u64)basereg & WIN_BASE_HIGH) << 32;
*base |= (basereg & WIN_BASE_LOW);
*size = (ctrlreg | ~WIN_CTRL_SIZE_MASK) + 1;
if (target)
*target = (ctrlreg & WIN_CTRL_TGT_MASK) >> WIN_CTRL_TGT_SHIFT;
if (attr)
*attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT;
if (remap) {
if (win < mbus->soc->num_remappable_wins) {
u32 remap_low = readl(addr + WIN_REMAP_LO_OFF);
u32 remap_hi = readl(addr + WIN_REMAP_HI_OFF);
*remap = ((u64)remap_hi << 32) | remap_low;
} else
*remap = 0;
}
}
static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus,
int win)
{
void __iomem *addr;
addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win);
writel(0, addr + WIN_BASE_OFF);
writel(0, addr + WIN_CTRL_OFF);
if (win < mbus->soc->num_remappable_wins) {
writel(0, addr + WIN_REMAP_LO_OFF);
writel(0, addr + WIN_REMAP_HI_OFF);
}
}
/* Checks whether the given window number is available */
static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus,
const int win)
{
void __iomem *addr = mbus->mbuswins_base +
mbus->soc->win_cfg_offset(win);
u32 ctrl = readl(addr + WIN_CTRL_OFF);
return !(ctrl & WIN_CTRL_ENABLE);
}
/*
* Checks whether the given (base, base+size) area doesn't overlap an
* existing region
*/
static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus,
phys_addr_t base, size_t size,
u8 target, u8 attr)
{
u64 end = (u64)base + size;
int win;
for (win = 0; win < mbus->soc->num_wins; win++) {
u64 wbase, wend;
u32 wsize;
u8 wtarget, wattr;
int enabled;
mvebu_mbus_read_window(mbus, win,
&enabled, &wbase, &wsize,
&wtarget, &wattr, NULL);
if (!enabled)
continue;
wend = wbase + wsize;
/*
* Check if the current window overlaps with the
* proposed physical range
*/
if ((u64)base < wend && end > wbase)
return 0;
/*
* Check if target/attribute conflicts
*/
if (target == wtarget && attr == wattr)
return 0;
}
return 1;
}
static int mvebu_mbus_find_window(struct mvebu_mbus_state *mbus,
phys_addr_t base, size_t size)
{
int win;
for (win = 0; win < mbus->soc->num_wins; win++) {
u64 wbase;
u32 wsize;
int enabled;
mvebu_mbus_read_window(mbus, win,
&enabled, &wbase, &wsize,
NULL, NULL, NULL);
if (!enabled)
continue;
if (base == wbase && size == wsize)
return win;
}
return -ENODEV;
}
static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
int win, phys_addr_t base, size_t size,
phys_addr_t remap, u8 target,
u8 attr)
{
void __iomem *addr = mbus->mbuswins_base +
mbus->soc->win_cfg_offset(win);
u32 ctrl, remap_addr;
ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
(attr << WIN_CTRL_ATTR_SHIFT) |
(target << WIN_CTRL_TGT_SHIFT) |
WIN_CTRL_ENABLE;
writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
writel(ctrl, addr + WIN_CTRL_OFF);
if (win < mbus->soc->num_remappable_wins) {
if (remap == MVEBU_MBUS_NO_REMAP)
remap_addr = base;
else
remap_addr = remap;
writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF);
writel(0, addr + WIN_REMAP_HI_OFF);
}
return 0;
}
static int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus,
phys_addr_t base, size_t size,
phys_addr_t remap, u8 target,
u8 attr)
{
int win;
if (remap == MVEBU_MBUS_NO_REMAP) {
for (win = mbus->soc->num_remappable_wins;
win < mbus->soc->num_wins; win++)
if (mvebu_mbus_window_is_free(mbus, win))
return mvebu_mbus_setup_window(mbus, win, base,
size, remap,
target, attr);
}
for (win = 0; win < mbus->soc->num_wins; win++)
if (mvebu_mbus_window_is_free(mbus, win))
return mvebu_mbus_setup_window(mbus, win, base, size,
remap, target, attr);
return -ENOMEM;
}
/*
* Debugfs debugging
*/
/* Common function used for Dove, Kirkwood, Armada 370/XP and Orion 5x */
static int mvebu_sdram_debug_show_orion(struct mvebu_mbus_state *mbus,
struct seq_file *seq, void *v)
{
int i;
for (i = 0; i < 4; i++) {
u32 basereg = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
u32 sizereg = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
u64 base;
u32 size;
if (!(sizereg & DDR_SIZE_ENABLED)) {
seq_printf(seq, "[%d] disabled\n", i);
continue;
}
base = ((u64)basereg & DDR_BASE_CS_HIGH_MASK) << 32;
base |= basereg & DDR_BASE_CS_LOW_MASK;
size = (sizereg | ~DDR_SIZE_MASK);
seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n",
i, (unsigned long long)base,
(unsigned long long)base + size + 1,
(sizereg & DDR_SIZE_CS_MASK) >> DDR_SIZE_CS_SHIFT);
}
return 0;
}
/* Special function for Dove */
static int mvebu_sdram_debug_show_dove(struct mvebu_mbus_state *mbus,
struct seq_file *seq, void *v)
{
int i;
for (i = 0; i < 2; i++) {
u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
u64 base;
u32 size;
if (!(map & 1)) {
seq_printf(seq, "[%d] disabled\n", i);
continue;
}
base = map & 0xff800000;
size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n",
i, (unsigned long long)base,
(unsigned long long)base + size, i);
}
return 0;
}
static int mvebu_sdram_debug_show(struct seq_file *seq, void *v)
{
struct mvebu_mbus_state *mbus = &mbus_state;
return mbus->soc->show_cpu_target(mbus, seq, v);
}
static int mvebu_sdram_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, mvebu_sdram_debug_show, inode->i_private);
}
static const struct file_operations mvebu_sdram_debug_fops = {
.open = mvebu_sdram_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
{
struct mvebu_mbus_state *mbus = &mbus_state;
int win;
for (win = 0; win < mbus->soc->num_wins; win++) {
u64 wbase, wremap;
u32 wsize;
u8 wtarget, wattr;
int enabled, i;
const char *name;
mvebu_mbus_read_window(mbus, win,
&enabled, &wbase, &wsize,
&wtarget, &wattr, &wremap);
if (!enabled) {
seq_printf(seq, "[%02d] disabled\n", win);
continue;
}
for (i = 0; mbus->soc->map[i].name; i++)
if (mbus->soc->map[i].target == wtarget &&
mbus->soc->map[i].attr ==
(wattr & mbus->soc->map[i].attrmask))
break;
name = mbus->soc->map[i].name ?: "unknown";
seq_printf(seq, "[%02d] %016llx - %016llx : %s",
win, (unsigned long long)wbase,
(unsigned long long)(wbase + wsize), name);
if (win < mbus->soc->num_remappable_wins) {
seq_printf(seq, " (remap %016llx)\n",
(unsigned long long)wremap);
} else
seq_printf(seq, "\n");
}
return 0;
}
static int mvebu_devs_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, mvebu_devs_debug_show, inode->i_private);
}
static const struct file_operations mvebu_devs_debug_fops = {
.open = mvebu_devs_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*
* SoC-specific functions and definitions
*/
static unsigned int orion_mbus_win_offset(int win)
{
return win << 4;
}
static unsigned int armada_370_xp_mbus_win_offset(int win)
{
/* The register layout is a bit annoying and the below code
* tries to cope with it.
* - At offset 0x0, there are the registers for the first 8
* windows, with 4 registers of 32 bits per window (ctrl,
* base, remap low, remap high)
* - Then at offset 0x80, there is a hole of 0x10 bytes for
* the internal registers base address and internal units
* sync barrier register.
* - Then at offset 0x90, there the registers for 12
* windows, with only 2 registers of 32 bits per window
* (ctrl, base).
*/
if (win < 8)
return win << 4;
else
return 0x90 + ((win - 8) << 3);
}
static unsigned int mv78xx0_mbus_win_offset(int win)
{
if (win < 8)
return win << 4;
else
return 0x900 + ((win - 8) << 4);
}
static void __init
mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
{
int i;
int cs;
mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
for (i = 0, cs = 0; i < 4; i++) {
u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
/*
* We only take care of entries for which the chip
* select is enabled, and that don't have high base
* address bits set (devices can only access the first
* 32 bits of the memory).
*/
if ((size & DDR_SIZE_ENABLED) &&
!(base & DDR_BASE_CS_HIGH_MASK)) {
struct mbus_dram_window *w;
w = &mvebu_mbus_dram_info.cs[cs++];
w->cs_index = i;
w->mbus_attr = 0xf & ~(1 << i);
if (mbus->hw_io_coherency)
w->mbus_attr |= ATTR_HW_COHERENCY;
w->base = base & DDR_BASE_CS_LOW_MASK;
w->size = (size | ~DDR_SIZE_MASK) + 1;
}
}
mvebu_mbus_dram_info.num_cs = cs;
}
static void __init
mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
{
int i;
int cs;
mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
for (i = 0, cs = 0; i < 2; i++) {
u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
/*
* Chip select enabled?
*/
if (map & 1) {
struct mbus_dram_window *w;
w = &mvebu_mbus_dram_info.cs[cs++];
w->cs_index = i;
w->mbus_attr = 0; /* CS address decoding done inside */
/* the DDR controller, no need to */
/* provide attributes */
w->base = map & 0xff800000;
w->size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
}
}
mvebu_mbus_dram_info.num_cs = cs;
}
static const struct mvebu_mbus_mapping armada_370_map[] = {
MAPDEF("bootrom", 1, 0xe0, MAPDEF_NOMASK),
MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
{},
};
static const struct mvebu_mbus_soc_data armada_370_mbus_data = {
.num_wins = 20,
.num_remappable_wins = 8,
.win_cfg_offset = armada_370_xp_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
.map = armada_370_map,
};
static const struct mvebu_mbus_mapping armada_xp_map[] = {
MAPDEF("bootrom", 1, 0x1d, MAPDEF_NOMASK),
MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
{},
};
static const struct mvebu_mbus_soc_data armada_xp_mbus_data = {
.num_wins = 20,
.num_remappable_wins = 8,
.win_cfg_offset = armada_370_xp_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
.map = armada_xp_map,
};
static const struct mvebu_mbus_mapping kirkwood_map[] = {
MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie1.0", 4, 0xd0, MAPDEF_PCIMASK),
MAPDEF("sram", 3, 0x01, MAPDEF_NOMASK),
MAPDEF("nand", 1, 0x2f, MAPDEF_NOMASK),
{},
};
static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
.map = kirkwood_map,
};
static const struct mvebu_mbus_mapping dove_map[] = {
MAPDEF("pcie0.0", 0x4, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie1.0", 0x8, 0xe0, MAPDEF_PCIMASK),
MAPDEF("cesa", 0x3, 0x01, MAPDEF_NOMASK),
MAPDEF("bootrom", 0x1, 0xfd, MAPDEF_NOMASK),
MAPDEF("scratchpad", 0xd, 0x0, MAPDEF_NOMASK),
{},
};
static const struct mvebu_mbus_soc_data dove_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_dove,
.map = dove_map,
};
static const struct mvebu_mbus_mapping orion5x_map[] = {
MAPDEF("pcie0.0", 4, 0x51, MAPDEF_ORIONPCIMASK),
MAPDEF("pci0.0", 3, 0x51, MAPDEF_ORIONPCIMASK),
MAPDEF("devbus-boot", 1, 0x0f, MAPDEF_NOMASK),
MAPDEF("devbus-cs0", 1, 0x1e, MAPDEF_NOMASK),
MAPDEF("devbus-cs1", 1, 0x1d, MAPDEF_NOMASK),
MAPDEF("devbus-cs2", 1, 0x1b, MAPDEF_NOMASK),
MAPDEF("sram", 0, 0x00, MAPDEF_NOMASK),
{},
};
/*
* Some variants of Orion5x have 4 remappable windows, some other have
* only two of them.
*/
static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
.map = orion5x_map,
};
static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 2,
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
.map = orion5x_map,
};
static const struct mvebu_mbus_mapping mv78xx0_map[] = {
MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
{},
};
static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
.num_wins = 14,
.num_remappable_wins = 8,
.win_cfg_offset = mv78xx0_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
.map = mv78xx0_map,
};
/*
* The driver doesn't yet have a DT binding because the details of
* this DT binding still need to be sorted out. However, as a
* preparation, we already use of_device_id to match a SoC description
* string against the SoC specific details of this driver.
*/
static const struct of_device_id of_mvebu_mbus_ids[] = {
{ .compatible = "marvell,armada370-mbus",
.data = &armada_370_mbus_data, },
{ .compatible = "marvell,armadaxp-mbus",
.data = &armada_xp_mbus_data, },
{ .compatible = "marvell,kirkwood-mbus",
.data = &kirkwood_mbus_data, },
{ .compatible = "marvell,dove-mbus",
.data = &dove_mbus_data, },
{ .compatible = "marvell,orion5x-88f5281-mbus",
.data = &orion5x_4win_mbus_data, },
{ .compatible = "marvell,orion5x-88f5182-mbus",
.data = &orion5x_2win_mbus_data, },
{ .compatible = "marvell,orion5x-88f5181-mbus",
.data = &orion5x_2win_mbus_data, },
{ .compatible = "marvell,orion5x-88f6183-mbus",
.data = &orion5x_4win_mbus_data, },
{ .compatible = "marvell,mv78xx0-mbus",
.data = &mv78xx0_mbus_data, },
{ },
};
/*
* Public API of the driver
*/
int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base,
size_t size, phys_addr_t remap,
unsigned int flags)
{
struct mvebu_mbus_state *s = &mbus_state;
u8 target, attr;
int i;
if (!s->soc->map)
return -ENODEV;
for (i = 0; s->soc->map[i].name; i++)
if (!strcmp(s->soc->map[i].name, devname))
break;
if (!s->soc->map[i].name) {
pr_err("mvebu-mbus: unknown device '%s'\n", devname);
return -ENODEV;
}
target = s->soc->map[i].target;
attr = s->soc->map[i].attr;
if (flags == MVEBU_MBUS_PCI_MEM)
attr |= 0x8;
else if (flags == MVEBU_MBUS_PCI_WA)
attr |= 0x28;
if (!mvebu_mbus_window_conflicts(s, base, size, target, attr)) {
pr_err("mvebu-mbus: cannot add window '%s', conflicts with another window\n",
devname);
return -EINVAL;
}
return mvebu_mbus_alloc_window(s, base, size, remap, target, attr);
}
int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size)
{
return mvebu_mbus_add_window_remap_flags(devname, base, size,
MVEBU_MBUS_NO_REMAP, 0);
}
int mvebu_mbus_del_window(phys_addr_t base, size_t size)
{
int win;
win = mvebu_mbus_find_window(&mbus_state, base, size);
if (win < 0)
return win;
mvebu_mbus_disable_window(&mbus_state, win);
return 0;
}
static __init int mvebu_mbus_debugfs_init(void)
{
struct mvebu_mbus_state *s = &mbus_state;
/*
* If no base has been initialized, doesn't make sense to
* register the debugfs entries. We may be on a multiplatform
* kernel that isn't running a Marvell EBU SoC.
*/
if (!s->mbuswins_base)
return 0;
s->debugfs_root = debugfs_create_dir("mvebu-mbus", NULL);
if (s->debugfs_root) {
s->debugfs_sdram = debugfs_create_file("sdram", S_IRUGO,
s->debugfs_root, NULL,
&mvebu_sdram_debug_fops);
s->debugfs_devs = debugfs_create_file("devices", S_IRUGO,
s->debugfs_root, NULL,
&mvebu_devs_debug_fops);
}
return 0;
}
fs_initcall(mvebu_mbus_debugfs_init);
int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
size_t mbuswins_size,
phys_addr_t sdramwins_phys_base,
size_t sdramwins_size)
{
struct mvebu_mbus_state *mbus = &mbus_state;
const struct of_device_id *of_id;
int win;
for (of_id = of_mvebu_mbus_ids; of_id->compatible; of_id++)
if (!strcmp(of_id->compatible, soc))
break;
if (!of_id->compatible) {
pr_err("mvebu-mbus: could not find a matching SoC family\n");
return -ENODEV;
}
mbus->soc = of_id->data;
mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
if (!mbus->mbuswins_base)
return -ENOMEM;
mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size);
if (!mbus->sdramwins_base) {
iounmap(mbus_state.mbuswins_base);
return -ENOMEM;
}
if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
mbus->hw_io_coherency = 1;
for (win = 0; win < mbus->soc->num_wins; win++)
mvebu_mbus_disable_window(mbus, win);
mbus->soc->setup_cpu_target(mbus);
return 0;
}

150
drivers/bus/omap-ocp2scp.c Normal file
View File

@@ -0,0 +1,150 @@
/*
* omap-ocp2scp.c - transform ocp interface protocol to scp protocol
*
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_data/omap_ocp2scp.h>
/**
* _count_resources - count for the number of resources
* @res: struct resource *
*
* Count and return the number of resources populated for the device that is
* connected to ocp2scp.
*/
static unsigned _count_resources(struct resource *res)
{
int cnt = 0;
while (res->start != res->end) {
cnt++;
res++;
}
return cnt;
}
static int ocp2scp_remove_devices(struct device *dev, void *c)
{
struct platform_device *pdev = to_platform_device(dev);
platform_device_unregister(pdev);
return 0;
}
static int omap_ocp2scp_probe(struct platform_device *pdev)
{
int ret;
unsigned res_cnt, i;
struct device_node *np = pdev->dev.of_node;
struct platform_device *pdev_child;
struct omap_ocp2scp_platform_data *pdata = pdev->dev.platform_data;
struct omap_ocp2scp_dev *dev;
if (np) {
ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
if (ret) {
dev_err(&pdev->dev,
"failed to add resources for ocp2scp child\n");
goto err0;
}
} else if (pdata) {
for (i = 0, dev = *pdata->devices; i < pdata->dev_cnt; i++,
dev++) {
res_cnt = _count_resources(dev->res);
pdev_child = platform_device_alloc(dev->drv_name,
PLATFORM_DEVID_AUTO);
if (!pdev_child) {
dev_err(&pdev->dev,
"failed to allocate mem for ocp2scp child\n");
goto err0;
}
ret = platform_device_add_resources(pdev_child,
dev->res, res_cnt);
if (ret) {
dev_err(&pdev->dev,
"failed to add resources for ocp2scp child\n");
goto err1;
}
pdev_child->dev.parent = &pdev->dev;
ret = platform_device_add(pdev_child);
if (ret) {
dev_err(&pdev->dev,
"failed to register ocp2scp child device\n");
goto err1;
}
}
} else {
dev_err(&pdev->dev, "OCP2SCP initialized without plat data\n");
return -EINVAL;
}
pm_runtime_enable(&pdev->dev);
return 0;
err1:
platform_device_put(pdev_child);
err0:
device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
return ret;
}
static int omap_ocp2scp_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id omap_ocp2scp_id_table[] = {
{ .compatible = "ti,omap-ocp2scp" },
{}
};
MODULE_DEVICE_TABLE(of, omap_ocp2scp_id_table);
#endif
static struct platform_driver omap_ocp2scp_driver = {
.probe = omap_ocp2scp_probe,
.remove = omap_ocp2scp_remove,
.driver = {
.name = "omap-ocp2scp",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(omap_ocp2scp_id_table),
},
};
module_platform_driver(omap_ocp2scp_driver);
MODULE_ALIAS("platform: omap-ocp2scp");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("OMAP OCP2SCP driver");
MODULE_LICENSE("GPL v2");

266
drivers/bus/omap_l3_noc.c Normal file
View File

@@ -0,0 +1,266 @@
/*
* OMAP4XXX L3 Interconnect error handling driver
*
* Copyright (C) 2011 Texas Corporation
* Santosh Shilimkar <santosh.shilimkar@ti.com>
* Sricharan <r.sricharan@ti.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "omap_l3_noc.h"
/*
* Interrupt Handler for L3 error detection.
* 1) Identify the L3 clockdomain partition to which the error belongs to.
* 2) Identify the slave where the error information is logged
* 3) Print the logged information.
* 4) Add dump stack to provide kernel trace.
*
* Two Types of errors :
* 1) Custom errors in L3 :
* Target like DMM/FW/EMIF generates SRESP=ERR error
* 2) Standard L3 error:
* - Unsupported CMD.
* L3 tries to access target while it is idle
* - OCP disconnect.
* - Address hole error:
* If DSS/ISS/FDIF/USBHOSTFS access a target where they
* do not have connectivity, the error is logged in
* their default target which is DMM2.
*
* On High Secure devices, firewall errors are possible and those
* can be trapped as well. But the trapping is implemented as part
* secure software and hence need not be implemented here.
*/
static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
{
struct omap4_l3 *l3 = _l3;
int inttype, i, k;
int err_src = 0;
u32 std_err_main, err_reg, clear, masterid;
void __iomem *base, *l3_targ_base;
char *target_name, *master_name = "UN IDENTIFIED";
/* Get the Type of interrupt */
inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
for (i = 0; i < L3_MODULES; i++) {
/*
* Read the regerr register of the clock domain
* to determine the source
*/
base = l3->l3_base[i];
err_reg = __raw_readl(base + l3_flagmux[i] +
+ L3_FLAGMUX_REGERR0 + (inttype << 3));
/* Get the corresponding error and analyse */
if (err_reg) {
/* Identify the source from control status register */
err_src = __ffs(err_reg);
/* Read the stderrlog_main_source from clk domain */
l3_targ_base = base + *(l3_targ[i] + err_src);
std_err_main = __raw_readl(l3_targ_base +
L3_TARG_STDERRLOG_MAIN);
masterid = __raw_readl(l3_targ_base +
L3_TARG_STDERRLOG_MSTADDR);
switch (std_err_main & CUSTOM_ERROR) {
case STANDARD_ERROR:
target_name =
l3_targ_inst_name[i][err_src];
WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n",
target_name,
__raw_readl(l3_targ_base +
L3_TARG_STDERRLOG_SLVOFSLSB));
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG;
writel(clear, l3_targ_base +
L3_TARG_STDERRLOG_MAIN);
break;
case CUSTOM_ERROR:
target_name =
l3_targ_inst_name[i][err_src];
for (k = 0; k < NUM_OF_L3_MASTERS; k++) {
if (masterid == l3_masters[k].id)
master_name =
l3_masters[k].name;
}
WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n",
master_name, target_name);
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG;
writel(clear, l3_targ_base +
L3_TARG_STDERRLOG_MAIN);
break;
default:
/* Nothing to be handled here as of now */
break;
}
/* Error found so break the for loop */
break;
}
}
return IRQ_HANDLED;
}
static int omap4_l3_probe(struct platform_device *pdev)
{
static struct omap4_l3 *l3;
struct resource *res;
int ret;
l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
if (!l3)
return -ENOMEM;
platform_set_drvdata(pdev, l3);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource 0\n");
ret = -ENODEV;
goto err0;
}
l3->l3_base[0] = ioremap(res->start, resource_size(res));
if (!l3->l3_base[0]) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err0;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource 1\n");
ret = -ENODEV;
goto err1;
}
l3->l3_base[1] = ioremap(res->start, resource_size(res));
if (!l3->l3_base[1]) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource 2\n");
ret = -ENODEV;
goto err2;
}
l3->l3_base[2] = ioremap(res->start, resource_size(res));
if (!l3->l3_base[2]) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err2;
}
/*
* Setup interrupt Handlers
*/
l3->debug_irq = platform_get_irq(pdev, 0);
ret = request_irq(l3->debug_irq,
l3_interrupt_handler,
IRQF_DISABLED, "l3-dbg-irq", l3);
if (ret) {
pr_crit("L3: request_irq failed to register for 0x%x\n",
l3->debug_irq);
goto err3;
}
l3->app_irq = platform_get_irq(pdev, 1);
ret = request_irq(l3->app_irq,
l3_interrupt_handler,
IRQF_DISABLED, "l3-app-irq", l3);
if (ret) {
pr_crit("L3: request_irq failed to register for 0x%x\n",
l3->app_irq);
goto err4;
}
return 0;
err4:
free_irq(l3->debug_irq, l3);
err3:
iounmap(l3->l3_base[2]);
err2:
iounmap(l3->l3_base[1]);
err1:
iounmap(l3->l3_base[0]);
err0:
kfree(l3);
return ret;
}
static int omap4_l3_remove(struct platform_device *pdev)
{
struct omap4_l3 *l3 = platform_get_drvdata(pdev);
free_irq(l3->app_irq, l3);
free_irq(l3->debug_irq, l3);
iounmap(l3->l3_base[0]);
iounmap(l3->l3_base[1]);
iounmap(l3->l3_base[2]);
kfree(l3);
return 0;
}
#if defined(CONFIG_OF)
static const struct of_device_id l3_noc_match[] = {
{.compatible = "ti,omap4-l3-noc", },
{},
};
MODULE_DEVICE_TABLE(of, l3_noc_match);
#else
#define l3_noc_match NULL
#endif
static struct platform_driver omap4_l3_driver = {
.probe = omap4_l3_probe,
.remove = omap4_l3_remove,
.driver = {
.name = "omap_l3_noc",
.owner = THIS_MODULE,
.of_match_table = l3_noc_match,
},
};
static int __init omap4_l3_init(void)
{
return platform_driver_register(&omap4_l3_driver);
}
postcore_initcall_sync(omap4_l3_init);
static void __exit omap4_l3_exit(void)
{
platform_driver_unregister(&omap4_l3_driver);
}
module_exit(omap4_l3_exit);

176
drivers/bus/omap_l3_noc.h Normal file
View File

@@ -0,0 +1,176 @@
/*
* OMAP4XXX L3 Interconnect error handling driver header
*
* Copyright (C) 2011 Texas Corporation
* Santosh Shilimkar <santosh.shilimkar@ti.com>
* sricharan <r.sricharan@ti.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#ifndef __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
#define __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
#define L3_MODULES 3
#define CLEAR_STDERR_LOG (1 << 31)
#define CUSTOM_ERROR 0x2
#define STANDARD_ERROR 0x0
#define INBAND_ERROR 0x0
#define L3_APPLICATION_ERROR 0x0
#define L3_DEBUG_ERROR 0x1
/* L3 TARG register offsets */
#define L3_TARG_STDERRLOG_MAIN 0x48
#define L3_TARG_STDERRLOG_SLVOFSLSB 0x5c
#define L3_TARG_STDERRLOG_MSTADDR 0x68
#define L3_FLAGMUX_REGERR0 0xc
#define NUM_OF_L3_MASTERS (sizeof(l3_masters)/sizeof(l3_masters[0]))
static u32 l3_flagmux[L3_MODULES] = {
0x500,
0x1000,
0X0200
};
/* L3 Target standard Error register offsets */
static u32 l3_targ_inst_clk1[] = {
0x100, /* DMM1 */
0x200, /* DMM2 */
0x300, /* ABE */
0x400, /* L4CFG */
0x600, /* CLK2 PWR DISC */
0x0, /* Host CLK1 */
0x900 /* L4 Wakeup */
};
static u32 l3_targ_inst_clk2[] = {
0x500, /* CORTEX M3 */
0x300, /* DSS */
0x100, /* GPMC */
0x400, /* ISS */
0x700, /* IVAHD */
0xD00, /* missing in TRM corresponds to AES1*/
0x900, /* L4 PER0*/
0x200, /* OCMRAM */
0x100, /* missing in TRM corresponds to GPMC sERROR*/
0x600, /* SGX */
0x800, /* SL2 */
0x1600, /* C2C */
0x1100, /* missing in TRM corresponds PWR DISC CLK1*/
0xF00, /* missing in TRM corrsponds to SHA1*/
0xE00, /* missing in TRM corresponds to AES2*/
0xC00, /* L4 PER3 */
0xA00, /* L4 PER1*/
0xB00, /* L4 PER2*/
0x0, /* HOST CLK2 */
0x1800, /* CAL */
0x1700 /* LLI */
};
static u32 l3_targ_inst_clk3[] = {
0x0100 /* EMUSS */,
0x0300, /* DEBUGSS_CT_TBR */
0x0 /* HOST CLK3 */
};
static struct l3_masters_data {
u32 id;
char name[10];
} l3_masters[] = {
{ 0x0 , "MPU"},
{ 0x10, "CS_ADP"},
{ 0x14, "xxx"},
{ 0x20, "DSP"},
{ 0x30, "IVAHD"},
{ 0x40, "ISS"},
{ 0x44, "DucatiM3"},
{ 0x48, "FaceDetect"},
{ 0x50, "SDMA_Rd"},
{ 0x54, "SDMA_Wr"},
{ 0x58, "xxx"},
{ 0x5C, "xxx"},
{ 0x60, "SGX"},
{ 0x70, "DSS"},
{ 0x80, "C2C"},
{ 0x88, "xxx"},
{ 0x8C, "xxx"},
{ 0x90, "HSI"},
{ 0xA0, "MMC1"},
{ 0xA4, "MMC2"},
{ 0xA8, "MMC6"},
{ 0xB0, "UNIPRO1"},
{ 0xC0, "USBHOSTHS"},
{ 0xC4, "USBOTGHS"},
{ 0xC8, "USBHOSTFS"}
};
static char *l3_targ_inst_name[L3_MODULES][21] = {
{
"DMM1",
"DMM2",
"ABE",
"L4CFG",
"CLK2 PWR DISC",
"HOST CLK1",
"L4 WAKEUP"
},
{
"CORTEX M3" ,
"DSS ",
"GPMC ",
"ISS ",
"IVAHD ",
"AES1",
"L4 PER0",
"OCMRAM ",
"GPMC sERROR",
"SGX ",
"SL2 ",
"C2C ",
"PWR DISC CLK1",
"SHA1",
"AES2",
"L4 PER3",
"L4 PER1",
"L4 PER2",
"HOST CLK2",
"CAL",
"LLI"
},
{
"EMUSS",
"DEBUG SOURCE",
"HOST CLK3"
},
};
static u32 *l3_targ[L3_MODULES] = {
l3_targ_inst_clk1,
l3_targ_inst_clk2,
l3_targ_inst_clk3,
};
struct omap4_l3 {
struct device *dev;
struct clk *ick;
/* memory base */
void __iomem *l3_base[L3_MODULES];
int debug_irq;
int app_irq;
};
#endif

297
drivers/bus/omap_l3_smx.c Normal file
View File

@@ -0,0 +1,297 @@
/*
* OMAP3XXX L3 Interconnect Driver
*
* Copyright (C) 2011 Texas Corporation
* Felipe Balbi <balbi@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
* Sricharan <r.sricharan@ti.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include "omap_l3_smx.h"
static inline u64 omap3_l3_readll(void __iomem *base, u16 reg)
{
return __raw_readll(base + reg);
}
static inline void omap3_l3_writell(void __iomem *base, u16 reg, u64 value)
{
__raw_writell(value, base + reg);
}
static inline enum omap3_l3_code omap3_l3_decode_error_code(u64 error)
{
return (error & 0x0f000000) >> L3_ERROR_LOG_CODE;
}
static inline u32 omap3_l3_decode_addr(u64 error_addr)
{
return error_addr & 0xffffffff;
}
static inline unsigned omap3_l3_decode_cmd(u64 error)
{
return (error & 0x07) >> L3_ERROR_LOG_CMD;
}
static inline enum omap3_l3_initiator_id omap3_l3_decode_initid(u64 error)
{
return (error & 0xff00) >> L3_ERROR_LOG_INITID;
}
static inline unsigned omap3_l3_decode_req_info(u64 error)
{
return (error >> 32) & 0xffff;
}
static char *omap3_l3_code_string(u8 code)
{
switch (code) {
case OMAP_L3_CODE_NOERROR:
return "No Error";
case OMAP_L3_CODE_UNSUP_CMD:
return "Unsupported Command";
case OMAP_L3_CODE_ADDR_HOLE:
return "Address Hole";
case OMAP_L3_CODE_PROTECT_VIOLATION:
return "Protection Violation";
case OMAP_L3_CODE_IN_BAND_ERR:
return "In-band Error";
case OMAP_L3_CODE_REQ_TOUT_NOT_ACCEPT:
return "Request Timeout Not Accepted";
case OMAP_L3_CODE_REQ_TOUT_NO_RESP:
return "Request Timeout, no response";
default:
return "UNKNOWN error";
}
}
static char *omap3_l3_initiator_string(u8 initid)
{
switch (initid) {
case OMAP_L3_LCD:
return "LCD";
case OMAP_L3_SAD2D:
return "SAD2D";
case OMAP_L3_IA_MPU_SS_1:
case OMAP_L3_IA_MPU_SS_2:
case OMAP_L3_IA_MPU_SS_3:
case OMAP_L3_IA_MPU_SS_4:
case OMAP_L3_IA_MPU_SS_5:
return "MPU";
case OMAP_L3_IA_IVA_SS_1:
case OMAP_L3_IA_IVA_SS_2:
case OMAP_L3_IA_IVA_SS_3:
return "IVA_SS";
case OMAP_L3_IA_IVA_SS_DMA_1:
case OMAP_L3_IA_IVA_SS_DMA_2:
case OMAP_L3_IA_IVA_SS_DMA_3:
case OMAP_L3_IA_IVA_SS_DMA_4:
case OMAP_L3_IA_IVA_SS_DMA_5:
case OMAP_L3_IA_IVA_SS_DMA_6:
return "IVA_SS_DMA";
case OMAP_L3_IA_SGX:
return "SGX";
case OMAP_L3_IA_CAM_1:
case OMAP_L3_IA_CAM_2:
case OMAP_L3_IA_CAM_3:
return "CAM";
case OMAP_L3_IA_DAP:
return "DAP";
case OMAP_L3_SDMA_WR_1:
case OMAP_L3_SDMA_WR_2:
return "SDMA_WR";
case OMAP_L3_SDMA_RD_1:
case OMAP_L3_SDMA_RD_2:
case OMAP_L3_SDMA_RD_3:
case OMAP_L3_SDMA_RD_4:
return "SDMA_RD";
case OMAP_L3_USBOTG:
return "USB_OTG";
case OMAP_L3_USBHOST:
return "USB_HOST";
default:
return "UNKNOWN Initiator";
}
}
/*
* omap3_l3_block_irq - handles a register block's irq
* @l3: struct omap3_l3 *
* @base: register block base address
* @error: L3_ERROR_LOG register of our block
*
* Called in hard-irq context. Caller should take care of locking
*
* OMAP36xx TRM gives, on page 2001, Figure 9-10, the Typical Error
* Analysis Sequence, we are following that sequence here, please
* refer to that Figure for more information on the subject.
*/
static irqreturn_t omap3_l3_block_irq(struct omap3_l3 *l3,
u64 error, int error_addr)
{
u8 code = omap3_l3_decode_error_code(error);
u8 initid = omap3_l3_decode_initid(error);
u8 multi = error & L3_ERROR_LOG_MULTI;
u32 address = omap3_l3_decode_addr(error_addr);
pr_err("%s seen by %s %s at address %x\n",
omap3_l3_code_string(code),
omap3_l3_initiator_string(initid),
multi ? "Multiple Errors" : "", address);
WARN_ON(1);
return IRQ_HANDLED;
}
static irqreturn_t omap3_l3_app_irq(int irq, void *_l3)
{
struct omap3_l3 *l3 = _l3;
u64 status, clear;
u64 error;
u64 error_addr;
u64 err_source = 0;
void __iomem *base;
int int_type;
irqreturn_t ret = IRQ_NONE;
int_type = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
if (!int_type) {
status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_0);
/*
* if we have a timeout error, there's nothing we can
* do besides rebooting the board. So let's BUG on any
* of such errors and handle the others. timeout error
* is severe and not expected to occur.
*/
BUG_ON(status & L3_STATUS_0_TIMEOUT_MASK);
} else {
status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_1);
/* No timeout error for debug sources */
}
/* identify the error source */
err_source = __ffs(status);
base = l3->rt + omap3_l3_bases[int_type][err_source];
error = omap3_l3_readll(base, L3_ERROR_LOG);
if (error) {
error_addr = omap3_l3_readll(base, L3_ERROR_LOG_ADDR);
ret |= omap3_l3_block_irq(l3, error, error_addr);
}
/* Clear the status register */
clear = (L3_AGENT_STATUS_CLEAR_IA << int_type) |
L3_AGENT_STATUS_CLEAR_TA;
omap3_l3_writell(base, L3_AGENT_STATUS, clear);
/* clear the error log register */
omap3_l3_writell(base, L3_ERROR_LOG, error);
return ret;
}
static int __init omap3_l3_probe(struct platform_device *pdev)
{
struct omap3_l3 *l3;
struct resource *res;
int ret;
l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
if (!l3)
return -ENOMEM;
platform_set_drvdata(pdev, l3);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource\n");
ret = -ENODEV;
goto err0;
}
l3->rt = ioremap(res->start, resource_size(res));
if (!l3->rt) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err0;
}
l3->debug_irq = platform_get_irq(pdev, 0);
ret = request_irq(l3->debug_irq, omap3_l3_app_irq,
IRQF_DISABLED | IRQF_TRIGGER_RISING,
"l3-debug-irq", l3);
if (ret) {
dev_err(&pdev->dev, "couldn't request debug irq\n");
goto err1;
}
l3->app_irq = platform_get_irq(pdev, 1);
ret = request_irq(l3->app_irq, omap3_l3_app_irq,
IRQF_DISABLED | IRQF_TRIGGER_RISING,
"l3-app-irq", l3);
if (ret) {
dev_err(&pdev->dev, "couldn't request app irq\n");
goto err2;
}
return 0;
err2:
free_irq(l3->debug_irq, l3);
err1:
iounmap(l3->rt);
err0:
kfree(l3);
return ret;
}
static int __exit omap3_l3_remove(struct platform_device *pdev)
{
struct omap3_l3 *l3 = platform_get_drvdata(pdev);
free_irq(l3->app_irq, l3);
free_irq(l3->debug_irq, l3);
iounmap(l3->rt);
kfree(l3);
return 0;
}
static struct platform_driver omap3_l3_driver = {
.remove = __exit_p(omap3_l3_remove),
.driver = {
.name = "omap_l3_smx",
},
};
static int __init omap3_l3_init(void)
{
return platform_driver_probe(&omap3_l3_driver, omap3_l3_probe);
}
postcore_initcall_sync(omap3_l3_init);
static void __exit omap3_l3_exit(void)
{
platform_driver_unregister(&omap3_l3_driver);
}
module_exit(omap3_l3_exit);

338
drivers/bus/omap_l3_smx.h Normal file
View File

@@ -0,0 +1,338 @@
/*
* OMAP3XXX L3 Interconnect Driver header
*
* Copyright (C) 2011 Texas Corporation
* Felipe Balbi <balbi@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
* sricharan <r.sricharan@ti.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#ifndef __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
#define __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
/* Register definitions. All 64-bit wide */
#define L3_COMPONENT 0x000
#define L3_CORE 0x018
#define L3_AGENT_CONTROL 0x020
#define L3_AGENT_STATUS 0x028
#define L3_ERROR_LOG 0x058
#define L3_ERROR_LOG_MULTI (1 << 31)
#define L3_ERROR_LOG_SECONDARY (1 << 30)
#define L3_ERROR_LOG_ADDR 0x060
/* Register definitions for Sideband Interconnect */
#define L3_SI_CONTROL 0x020
#define L3_SI_FLAG_STATUS_0 0x510
static const u64 shift = 1;
#define L3_STATUS_0_MPUIA_BRST (shift << 0)
#define L3_STATUS_0_MPUIA_RSP (shift << 1)
#define L3_STATUS_0_MPUIA_INBAND (shift << 2)
#define L3_STATUS_0_IVAIA_BRST (shift << 6)
#define L3_STATUS_0_IVAIA_RSP (shift << 7)
#define L3_STATUS_0_IVAIA_INBAND (shift << 8)
#define L3_STATUS_0_SGXIA_BRST (shift << 9)
#define L3_STATUS_0_SGXIA_RSP (shift << 10)
#define L3_STATUS_0_SGXIA_MERROR (shift << 11)
#define L3_STATUS_0_CAMIA_BRST (shift << 12)
#define L3_STATUS_0_CAMIA_RSP (shift << 13)
#define L3_STATUS_0_CAMIA_INBAND (shift << 14)
#define L3_STATUS_0_DISPIA_BRST (shift << 15)
#define L3_STATUS_0_DISPIA_RSP (shift << 16)
#define L3_STATUS_0_DMARDIA_BRST (shift << 18)
#define L3_STATUS_0_DMARDIA_RSP (shift << 19)
#define L3_STATUS_0_DMAWRIA_BRST (shift << 21)
#define L3_STATUS_0_DMAWRIA_RSP (shift << 22)
#define L3_STATUS_0_USBOTGIA_BRST (shift << 24)
#define L3_STATUS_0_USBOTGIA_RSP (shift << 25)
#define L3_STATUS_0_USBOTGIA_INBAND (shift << 26)
#define L3_STATUS_0_USBHOSTIA_BRST (shift << 27)
#define L3_STATUS_0_USBHOSTIA_INBAND (shift << 28)
#define L3_STATUS_0_SMSTA_REQ (shift << 48)
#define L3_STATUS_0_GPMCTA_REQ (shift << 49)
#define L3_STATUS_0_OCMRAMTA_REQ (shift << 50)
#define L3_STATUS_0_OCMROMTA_REQ (shift << 51)
#define L3_STATUS_0_IVATA_REQ (shift << 54)
#define L3_STATUS_0_SGXTA_REQ (shift << 55)
#define L3_STATUS_0_SGXTA_SERROR (shift << 56)
#define L3_STATUS_0_GPMCTA_SERROR (shift << 57)
#define L3_STATUS_0_L4CORETA_REQ (shift << 58)
#define L3_STATUS_0_L4PERTA_REQ (shift << 59)
#define L3_STATUS_0_L4EMUTA_REQ (shift << 60)
#define L3_STATUS_0_MAD2DTA_REQ (shift << 61)
#define L3_STATUS_0_TIMEOUT_MASK (L3_STATUS_0_MPUIA_BRST \
| L3_STATUS_0_MPUIA_RSP \
| L3_STATUS_0_IVAIA_BRST \
| L3_STATUS_0_IVAIA_RSP \
| L3_STATUS_0_SGXIA_BRST \
| L3_STATUS_0_SGXIA_RSP \
| L3_STATUS_0_CAMIA_BRST \
| L3_STATUS_0_CAMIA_RSP \
| L3_STATUS_0_DISPIA_BRST \
| L3_STATUS_0_DISPIA_RSP \
| L3_STATUS_0_DMARDIA_BRST \
| L3_STATUS_0_DMARDIA_RSP \
| L3_STATUS_0_DMAWRIA_BRST \
| L3_STATUS_0_DMAWRIA_RSP \
| L3_STATUS_0_USBOTGIA_BRST \
| L3_STATUS_0_USBOTGIA_RSP \
| L3_STATUS_0_USBHOSTIA_BRST \
| L3_STATUS_0_SMSTA_REQ \
| L3_STATUS_0_GPMCTA_REQ \
| L3_STATUS_0_OCMRAMTA_REQ \
| L3_STATUS_0_OCMROMTA_REQ \
| L3_STATUS_0_IVATA_REQ \
| L3_STATUS_0_SGXTA_REQ \
| L3_STATUS_0_L4CORETA_REQ \
| L3_STATUS_0_L4PERTA_REQ \
| L3_STATUS_0_L4EMUTA_REQ \
| L3_STATUS_0_MAD2DTA_REQ)
#define L3_SI_FLAG_STATUS_1 0x530
#define L3_STATUS_1_MPU_DATAIA (1 << 0)
#define L3_STATUS_1_DAPIA0 (1 << 3)
#define L3_STATUS_1_DAPIA1 (1 << 4)
#define L3_STATUS_1_IVAIA (1 << 6)
#define L3_PM_ERROR_LOG 0x020
#define L3_PM_CONTROL 0x028
#define L3_PM_ERROR_CLEAR_SINGLE 0x030
#define L3_PM_ERROR_CLEAR_MULTI 0x038
#define L3_PM_REQ_INFO_PERMISSION(n) (0x048 + (0x020 * n))
#define L3_PM_READ_PERMISSION(n) (0x050 + (0x020 * n))
#define L3_PM_WRITE_PERMISSION(n) (0x058 + (0x020 * n))
#define L3_PM_ADDR_MATCH(n) (0x060 + (0x020 * n))
/* L3 error log bit fields. Common for IA and TA */
#define L3_ERROR_LOG_CODE 24
#define L3_ERROR_LOG_INITID 8
#define L3_ERROR_LOG_CMD 0
/* L3 agent status bit fields. */
#define L3_AGENT_STATUS_CLEAR_IA 0x10000000
#define L3_AGENT_STATUS_CLEAR_TA 0x01000000
#define OMAP34xx_IRQ_L3_APP 10
#define L3_APPLICATION_ERROR 0x0
#define L3_DEBUG_ERROR 0x1
enum omap3_l3_initiator_id {
/* LCD has 1 ID */
OMAP_L3_LCD = 29,
/* SAD2D has 1 ID */
OMAP_L3_SAD2D = 28,
/* MPU has 5 IDs */
OMAP_L3_IA_MPU_SS_1 = 27,
OMAP_L3_IA_MPU_SS_2 = 26,
OMAP_L3_IA_MPU_SS_3 = 25,
OMAP_L3_IA_MPU_SS_4 = 24,
OMAP_L3_IA_MPU_SS_5 = 23,
/* IVA2.2 SS has 3 IDs*/
OMAP_L3_IA_IVA_SS_1 = 22,
OMAP_L3_IA_IVA_SS_2 = 21,
OMAP_L3_IA_IVA_SS_3 = 20,
/* IVA 2.2 SS DMA has 6 IDS */
OMAP_L3_IA_IVA_SS_DMA_1 = 19,
OMAP_L3_IA_IVA_SS_DMA_2 = 18,
OMAP_L3_IA_IVA_SS_DMA_3 = 17,
OMAP_L3_IA_IVA_SS_DMA_4 = 16,
OMAP_L3_IA_IVA_SS_DMA_5 = 15,
OMAP_L3_IA_IVA_SS_DMA_6 = 14,
/* SGX has 1 ID */
OMAP_L3_IA_SGX = 13,
/* CAM has 3 ID */
OMAP_L3_IA_CAM_1 = 12,
OMAP_L3_IA_CAM_2 = 11,
OMAP_L3_IA_CAM_3 = 10,
/* DAP has 1 ID */
OMAP_L3_IA_DAP = 9,
/* SDMA WR has 2 IDs */
OMAP_L3_SDMA_WR_1 = 8,
OMAP_L3_SDMA_WR_2 = 7,
/* SDMA RD has 4 IDs */
OMAP_L3_SDMA_RD_1 = 6,
OMAP_L3_SDMA_RD_2 = 5,
OMAP_L3_SDMA_RD_3 = 4,
OMAP_L3_SDMA_RD_4 = 3,
/* HSUSB OTG has 1 ID */
OMAP_L3_USBOTG = 2,
/* HSUSB HOST has 1 ID */
OMAP_L3_USBHOST = 1,
};
enum omap3_l3_code {
OMAP_L3_CODE_NOERROR = 0,
OMAP_L3_CODE_UNSUP_CMD = 1,
OMAP_L3_CODE_ADDR_HOLE = 2,
OMAP_L3_CODE_PROTECT_VIOLATION = 3,
OMAP_L3_CODE_IN_BAND_ERR = 4,
/* codes 5 and 6 are reserved */
OMAP_L3_CODE_REQ_TOUT_NOT_ACCEPT = 7,
OMAP_L3_CODE_REQ_TOUT_NO_RESP = 8,
/* codes 9 - 15 are also reserved */
};
struct omap3_l3 {
struct device *dev;
struct clk *ick;
/* memory base*/
void __iomem *rt;
int debug_irq;
int app_irq;
/* true when and inband functional error occurs */
unsigned inband:1;
};
/* offsets for l3 agents in order with the Flag status register */
static unsigned int omap3_l3_app_bases[] = {
/* MPU IA */
0x1400,
0x1400,
0x1400,
/* RESERVED */
0,
0,
0,
/* IVA 2.2 IA */
0x1800,
0x1800,
0x1800,
/* SGX IA */
0x1c00,
0x1c00,
/* RESERVED */
0,
/* CAMERA IA */
0x5800,
0x5800,
0x5800,
/* DISPLAY IA */
0x5400,
0x5400,
/* RESERVED */
0,
/*SDMA RD IA */
0x4c00,
0x4c00,
/* RESERVED */
0,
/* SDMA WR IA */
0x5000,
0x5000,
/* RESERVED */
0,
/* USB OTG IA */
0x4400,
0x4400,
0x4400,
/* USB HOST IA */
0x4000,
0x4000,
/* RESERVED */
0,
0,
0,
0,
/* SAD2D IA */
0x3000,
0x3000,
0x3000,
/* RESERVED */
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
/* SMA TA */
0x2000,
/* GPMC TA */
0x2400,
/* OCM RAM TA */
0x2800,
/* OCM ROM TA */
0x2C00,
/* L4 CORE TA */
0x6800,
/* L4 PER TA */
0x6c00,
/* IVA 2.2 TA */
0x6000,
/* SGX TA */
0x6400,
/* L4 EMU TA */
0x7000,
/* GPMC TA */
0x2400,
/* L4 CORE TA */
0x6800,
/* L4 PER TA */
0x6c00,
/* L4 EMU TA */
0x7000,
/* MAD2D TA */
0x3400,
/* RESERVED */
0,
0,
};
static unsigned int omap3_l3_debug_bases[] = {
/* MPU DATA IA */
0x1400,
/* RESERVED */
0,
0,
/* DAP IA */
0x5c00,
0x5c00,
/* RESERVED */
0,
/* IVA 2.2 IA */
0x1800,
/* REST RESERVED */
};
static u32 *omap3_l3_bases[] = {
omap3_l3_app_bases,
omap3_l3_debug_bases,
};
/*
* REVISIT define __raw_readll/__raw_writell here, but move them to
* <asm/io.h> at some point
*/
#define __raw_writell(v, a) (__chk_io_ptr(a), \
*(volatile u64 __force *)(a) = (v))
#define __raw_readll(a) (__chk_io_ptr(a), \
*(volatile u64 __force *)(a))
#endif