Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

70
drivers/bus/Kconfig Normal file
View file

@ -0,0 +1,70 @@
#
# Bus Devices
#
menu "Bus devices"
config BRCMSTB_GISB_ARB
bool "Broadcom STB GISB bus arbiter"
depends on ARM
help
Driver for the Broadcom Set Top Box System-on-a-chip internal bus
arbiter. This driver provides timeout and target abort error handling
and internal bus master decoding.
config IMX_WEIM
bool "Freescale EIM DRIVER"
depends on ARCH_MXC
help
Driver for i.MX WEIM controller.
The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR, onenand.
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.
config ARM_CCI
bool "ARM CCI driver support"
depends on ARM && OF && CPU_V7
help
Driver supporting the CCI cache coherent interconnect for ARM
platforms.
config ARM_CCN
bool "ARM CCN driver support"
depends on ARM || ARM64
depends on PERF_EVENTS
help
PMU (perf) driver supporting the ARM CCN (Cache Coherent Network)
interconnect.
config VEXPRESS_CONFIG
bool "Versatile Express configuration bus"
default y if ARCH_VEXPRESS
depends on ARM || ARM64
depends on OF
select REGMAP
help
Platform configuration infrastructure for the ARM Ltd.
Versatile Express.
endmenu

17
drivers/bus/Makefile Normal file
View file

@ -0,0 +1,17 @@
#
# Makefile for the bus drivers.
#
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
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
# Interconnect bus drivers for ARM platforms
obj-$(CONFIG_ARM_CCI) += arm-cci.o
obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o

1133
drivers/bus/arm-cci.c Normal file

File diff suppressed because it is too large Load diff

1393
drivers/bus/arm-ccn.c Normal file

File diff suppressed because it is too large Load diff

289
drivers/bus/brcmstb_gisb.c Normal file
View file

@ -0,0 +1,289 @@
/*
* Copyright (C) 2014 Broadcom Corporation
*
* 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.
*
* 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/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <asm/bug.h>
#include <asm/signal.h>
#define ARB_TIMER 0x008
#define ARB_ERR_CAP_CLR 0x7e4
#define ARB_ERR_CAP_CLEAR (1 << 0)
#define ARB_ERR_CAP_HI_ADDR 0x7e8
#define ARB_ERR_CAP_ADDR 0x7ec
#define ARB_ERR_CAP_DATA 0x7f0
#define ARB_ERR_CAP_STATUS 0x7f4
#define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12)
#define ARB_ERR_CAP_STATUS_TEA (1 << 11)
#define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2)
#define ARB_ERR_CAP_STATUS_BS_MASK 0x3c
#define ARB_ERR_CAP_STATUS_WRITE (1 << 1)
#define ARB_ERR_CAP_STATUS_VALID (1 << 0)
#define ARB_ERR_CAP_MASTER 0x7f8
struct brcmstb_gisb_arb_device {
void __iomem *base;
struct mutex lock;
struct list_head next;
u32 valid_mask;
const char *master_names[sizeof(u32) * BITS_PER_BYTE];
};
static LIST_HEAD(brcmstb_gisb_arb_device_list);
static ssize_t gisb_arb_get_timeout(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
u32 timeout;
mutex_lock(&gdev->lock);
timeout = ioread32(gdev->base + ARB_TIMER);
mutex_unlock(&gdev->lock);
return sprintf(buf, "%d", timeout);
}
static ssize_t gisb_arb_set_timeout(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev);
int val, ret;
ret = kstrtoint(buf, 10, &val);
if (ret < 0)
return ret;
if (val == 0 || val >= 0xffffffff)
return -EINVAL;
mutex_lock(&gdev->lock);
iowrite32(val, gdev->base + ARB_TIMER);
mutex_unlock(&gdev->lock);
return count;
}
static const char *
brcmstb_gisb_master_to_str(struct brcmstb_gisb_arb_device *gdev,
u32 masters)
{
u32 mask = gdev->valid_mask & masters;
if (hweight_long(mask) != 1)
return NULL;
return gdev->master_names[ffs(mask) - 1];
}
static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
const char *reason)
{
u32 cap_status;
unsigned long arb_addr;
u32 master;
const char *m_name;
char m_fmt[11];
cap_status = ioread32(gdev->base + ARB_ERR_CAP_STATUS);
/* Invalid captured address, bail out */
if (!(cap_status & ARB_ERR_CAP_STATUS_VALID))
return 1;
/* Read the address and master */
arb_addr = ioread32(gdev->base + ARB_ERR_CAP_ADDR) & 0xffffffff;
#if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
arb_addr |= (u64)ioread32(gdev->base + ARB_ERR_CAP_HI_ADDR) << 32;
#endif
master = ioread32(gdev->base + ARB_ERR_CAP_MASTER);
m_name = brcmstb_gisb_master_to_str(gdev, master);
if (!m_name) {
snprintf(m_fmt, sizeof(m_fmt), "0x%08x", master);
m_name = m_fmt;
}
pr_crit("%s: %s at 0x%lx [%c %s], core: %s\n",
__func__, reason, arb_addr,
cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R',
cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "",
m_name);
/* clear the GISB error */
iowrite32(ARB_ERR_CAP_CLEAR, gdev->base + ARB_ERR_CAP_CLR);
return 0;
}
static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
int ret = 0;
struct brcmstb_gisb_arb_device *gdev;
/* iterate over each GISB arb registered handlers */
list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next)
ret |= brcmstb_gisb_arb_decode_addr(gdev, "bus error");
/*
* If it was an imprecise abort, then we need to correct the
* return address to be _after_ the instruction.
*/
if (fsr & (1 << 10))
regs->ARM_pc += 4;
return ret;
}
void __init brcmstb_hook_fault_code(void)
{
hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0,
"imprecise external abort");
}
static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id)
{
brcmstb_gisb_arb_decode_addr(dev_id, "timeout");
return IRQ_HANDLED;
}
static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id)
{
brcmstb_gisb_arb_decode_addr(dev_id, "target abort");
return IRQ_HANDLED;
}
static DEVICE_ATTR(gisb_arb_timeout, S_IWUSR | S_IRUGO,
gisb_arb_get_timeout, gisb_arb_set_timeout);
static struct attribute *gisb_arb_sysfs_attrs[] = {
&dev_attr_gisb_arb_timeout.attr,
NULL,
};
static struct attribute_group gisb_arb_sysfs_attr_group = {
.attrs = gisb_arb_sysfs_attrs,
};
static int brcmstb_gisb_arb_probe(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
struct brcmstb_gisb_arb_device *gdev;
struct resource *r;
int err, timeout_irq, tea_irq;
unsigned int num_masters, j = 0;
int i, first, last;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
timeout_irq = platform_get_irq(pdev, 0);
tea_irq = platform_get_irq(pdev, 1);
gdev = devm_kzalloc(&pdev->dev, sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
mutex_init(&gdev->lock);
INIT_LIST_HEAD(&gdev->next);
gdev->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(gdev->base))
return PTR_ERR(gdev->base);
err = devm_request_irq(&pdev->dev, timeout_irq,
brcmstb_gisb_timeout_handler, 0, pdev->name,
gdev);
if (err < 0)
return err;
err = devm_request_irq(&pdev->dev, tea_irq,
brcmstb_gisb_tea_handler, 0, pdev->name,
gdev);
if (err < 0)
return err;
/* If we do not have a valid mask, assume all masters are enabled */
if (of_property_read_u32(dn, "brcm,gisb-arb-master-mask",
&gdev->valid_mask))
gdev->valid_mask = 0xffffffff;
/* Proceed with reading the litteral names if we agree on the
* number of masters
*/
num_masters = of_property_count_strings(dn,
"brcm,gisb-arb-master-names");
if (hweight_long(gdev->valid_mask) == num_masters) {
first = ffs(gdev->valid_mask) - 1;
last = fls(gdev->valid_mask) - 1;
for (i = first; i < last; i++) {
if (!(gdev->valid_mask & BIT(i)))
continue;
of_property_read_string_index(dn,
"brcm,gisb-arb-master-names", j,
&gdev->master_names[i]);
j++;
}
}
err = sysfs_create_group(&pdev->dev.kobj, &gisb_arb_sysfs_attr_group);
if (err)
return err;
platform_set_drvdata(pdev, gdev);
list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n",
gdev->base, timeout_irq, tea_irq);
return 0;
}
static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,gisb-arb" },
{ },
};
static struct platform_driver brcmstb_gisb_arb_driver = {
.probe = brcmstb_gisb_arb_probe,
.driver = {
.name = "brcm-gisb-arb",
.owner = THIS_MODULE,
.of_match_table = brcmstb_gisb_arb_of_match,
},
};
static int __init brcm_gisb_driver_init(void)
{
return platform_driver_register(&brcmstb_gisb_arb_driver);
}
module_init(brcm_gisb_driver_init);

217
drivers/bus/imx-weim.c Normal file
View file

@ -0,0 +1,217 @@
/*
* EIM driver for Freescale's i.MX chips
*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
* 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.
*/
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/regmap.h>
struct imx_weim_devtype {
unsigned int cs_count;
unsigned int cs_regs_count;
unsigned int cs_stride;
};
static const struct imx_weim_devtype imx1_weim_devtype = {
.cs_count = 6,
.cs_regs_count = 2,
.cs_stride = 0x08,
};
static const struct imx_weim_devtype imx27_weim_devtype = {
.cs_count = 6,
.cs_regs_count = 3,
.cs_stride = 0x10,
};
static const struct imx_weim_devtype imx50_weim_devtype = {
.cs_count = 4,
.cs_regs_count = 6,
.cs_stride = 0x18,
};
static const struct imx_weim_devtype imx51_weim_devtype = {
.cs_count = 6,
.cs_regs_count = 6,
.cs_stride = 0x18,
};
static const struct of_device_id weim_id_table[] = {
/* i.MX1/21 */
{ .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
/* i.MX25/27/31/35 */
{ .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, },
/* i.MX50/53/6Q */
{ .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, },
{ .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, },
/* i.MX51 */
{ .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, },
{ }
};
MODULE_DEVICE_TABLE(of, weim_id_table);
static int __init imx_weim_gpr_setup(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct property *prop;
const __be32 *p;
struct regmap *gpr;
u32 gprvals[4] = {
05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */
033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */
0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */
01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */
};
u32 gprval = 0;
u32 val;
int cs = 0;
int i = 0;
gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
if (IS_ERR(gpr)) {
dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
return 0;
}
of_property_for_each_u32(np, "ranges", prop, p, val) {
if (i % 4 == 0) {
cs = val;
} else if (i % 4 == 3 && val) {
val = (val / SZ_32M) | 1;
gprval |= val << cs * 3;
}
i++;
}
if (i == 0 || i % 4)
goto err;
for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
if (gprval == gprvals[i]) {
/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
return 0;
}
}
err:
dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
return -EINVAL;
}
/* Parse and set the timing for this device. */
static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
const struct imx_weim_devtype *devtype)
{
u32 cs_idx, value[devtype->cs_regs_count];
int i, ret;
/* get the CS index from this child node's "reg" property. */
ret = of_property_read_u32(np, "reg", &cs_idx);
if (ret)
return ret;
if (cs_idx >= devtype->cs_count)
return -EINVAL;
ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
value, devtype->cs_regs_count);
if (ret)
return ret;
/* set the timing for WEIM */
for (i = 0; i < devtype->cs_regs_count; i++)
writel(value[i], base + cs_idx * devtype->cs_stride + i * 4);
return 0;
}
static int __init weim_parse_dt(struct platform_device *pdev,
void __iomem *base)
{
const struct of_device_id *of_id = of_match_device(weim_id_table,
&pdev->dev);
const struct imx_weim_devtype *devtype = of_id->data;
struct device_node *child;
int ret;
if (devtype == &imx50_weim_devtype) {
ret = imx_weim_gpr_setup(pdev);
if (ret)
return ret;
}
for_each_child_of_node(pdev->dev.of_node, child) {
if (!child->name)
continue;
ret = weim_timing_setup(child, base, devtype);
if (ret) {
dev_err(&pdev->dev, "%s set timing failed.\n",
child->full_name);
return ret;
}
}
ret = of_platform_populate(pdev->dev.of_node,
of_default_bus_match_table,
NULL, &pdev->dev);
if (ret)
dev_err(&pdev->dev, "%s fail to create devices.\n",
pdev->dev.of_node->full_name);
return ret;
}
static int __init weim_probe(struct platform_device *pdev)
{
struct resource *res;
struct clk *clk;
void __iomem *base;
int ret;
/* get the resource */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
/* get the clock */
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_prepare_enable(clk);
if (ret)
return ret;
/* parse the device node */
ret = weim_parse_dt(pdev, base);
if (ret)
clk_disable_unprepare(clk);
else
dev_info(&pdev->dev, "Driver registered.\n");
return ret;
}
static struct platform_driver weim_driver = {
.driver = {
.name = "imx-weim",
.owner = THIS_MODULE,
.of_match_table = weim_id_table,
},
};
module_platform_driver_probe(weim_driver, weim_probe);
MODULE_AUTHOR("Freescale Semiconductor Inc.");
MODULE_DESCRIPTION("i.MX EIM Controller Driver");
MODULE_LICENSE("GPL");

956
drivers/bus/mvebu-mbus.c Normal file
View file

@ -0,0 +1,956 @@
/*
* 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_by_id(),
* mvebu_mbus_add_window_remap_by_id() and
* mvebu_mbus_del_window().
*
* - 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').
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#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>
#include <linux/log2.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_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);
};
struct mvebu_mbus_state {
void __iomem *mbuswins_base;
void __iomem *sdramwins_base;
struct dentry *debugfs_root;
struct dentry *debugfs_sdram;
struct dentry *debugfs_devs;
struct resource pcie_mem_aperture;
struct resource pcie_io_aperture;
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 */
/* On Armada XP, 375 and 38x the MBus window 13 has the remap
* capability, like windows 0 to 7. However, the mvebu-mbus driver
* isn't currently taking into account this special case, which means
* that when window 13 is actually used, the remap registers are left
* to 0, making the device using this MBus window unavailable. The
* quick fix for stable is to not use window 13. A follow up patch
* will correctly handle this window.
*/
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);
if (win == 13)
return false;
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;
}
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;
if (!is_power_of_2(size)) {
WARN(true, "Invalid MBus window size: 0x%zx\n", size);
return -EINVAL;
}
if ((base & (phys_addr_t)(size - 1)) != 0) {
WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base,
size);
return -EINVAL;
}
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;
mvebu_mbus_read_window(mbus, win,
&enabled, &wbase, &wsize,
&wtarget, &wattr, &wremap);
if (!enabled) {
seq_printf(seq, "[%02d] disabled\n", win);
continue;
}
seq_printf(seq, "[%02d] %016llx - %016llx : %04x:%04x",
win, (unsigned long long)wbase,
(unsigned long long)(wbase + wsize), wtarget, wattr);
if (!is_power_of_2(wsize) ||
((wbase & (u64)(wsize - 1)) != 0))
seq_puts(seq, " (Invalid base/size!!)");
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_soc_data armada_370_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,
};
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,
};
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,
};
/*
* 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,
};
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,
};
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,
};
static const struct of_device_id of_mvebu_mbus_ids[] = {
{ .compatible = "marvell,armada370-mbus",
.data = &armada_370_xp_mbus_data, },
{ .compatible = "marvell,armadaxp-mbus",
.data = &armada_370_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_by_id(unsigned int target,
unsigned int attribute,
phys_addr_t base, size_t size,
phys_addr_t remap)
{
struct mvebu_mbus_state *s = &mbus_state;
if (!mvebu_mbus_window_conflicts(s, base, size, target, attribute)) {
pr_err("cannot add window '%x:%x', conflicts with another window\n",
target, attribute);
return -EINVAL;
}
return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute);
}
int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute,
phys_addr_t base, size_t size)
{
return mvebu_mbus_add_window_remap_by_id(target, attribute, base,
size, MVEBU_MBUS_NO_REMAP);
}
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;
}
void mvebu_mbus_get_pcie_mem_aperture(struct resource *res)
{
if (!res)
return;
*res = mbus_state.pcie_mem_aperture;
}
void mvebu_mbus_get_pcie_io_aperture(struct resource *res)
{
if (!res)
return;
*res = mbus_state.pcie_io_aperture;
}
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);
static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
phys_addr_t mbuswins_phys_base,
size_t mbuswins_size,
phys_addr_t sdramwins_phys_base,
size_t sdramwins_size)
{
int win;
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;
}
for (win = 0; win < mbus->soc->num_wins; win++)
mvebu_mbus_disable_window(mbus, win);
mbus->soc->setup_cpu_target(mbus);
return 0;
}
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)
{
const struct of_device_id *of_id;
for (of_id = of_mvebu_mbus_ids; of_id->compatible[0]; of_id++)
if (!strcmp(of_id->compatible, soc))
break;
if (!of_id->compatible[0]) {
pr_err("could not find a matching SoC family\n");
return -ENODEV;
}
mbus_state.soc = of_id->data;
return mvebu_mbus_common_init(&mbus_state,
mbuswins_phys_base,
mbuswins_size,
sdramwins_phys_base,
sdramwins_size);
}
#ifdef CONFIG_OF
/*
* The window IDs in the ranges DT property have the following format:
* - bits 28 to 31: MBus custom field
* - bits 24 to 27: window target ID
* - bits 16 to 23: window attribute ID
* - bits 0 to 15: unused
*/
#define CUSTOM(id) (((id) & 0xF0000000) >> 24)
#define TARGET(id) (((id) & 0x0F000000) >> 24)
#define ATTR(id) (((id) & 0x00FF0000) >> 16)
static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
u32 base, u32 size,
u8 target, u8 attr)
{
if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) {
pr_err("cannot add window '%04x:%04x', conflicts with another window\n",
target, attr);
return -EBUSY;
}
if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP,
target, attr)) {
pr_err("cannot add window '%04x:%04x', too many windows\n",
target, attr);
return -ENOMEM;
}
return 0;
}
static int __init
mbus_parse_ranges(struct device_node *node,
int *addr_cells, int *c_addr_cells, int *c_size_cells,
int *cell_count, const __be32 **ranges_start,
const __be32 **ranges_end)
{
const __be32 *prop;
int ranges_len, tuple_len;
/* Allow a node with no 'ranges' property */
*ranges_start = of_get_property(node, "ranges", &ranges_len);
if (*ranges_start == NULL) {
*addr_cells = *c_addr_cells = *c_size_cells = *cell_count = 0;
*ranges_start = *ranges_end = NULL;
return 0;
}
*ranges_end = *ranges_start + ranges_len / sizeof(__be32);
*addr_cells = of_n_addr_cells(node);
prop = of_get_property(node, "#address-cells", NULL);
*c_addr_cells = be32_to_cpup(prop);
prop = of_get_property(node, "#size-cells", NULL);
*c_size_cells = be32_to_cpup(prop);
*cell_count = *addr_cells + *c_addr_cells + *c_size_cells;
tuple_len = (*cell_count) * sizeof(__be32);
if (ranges_len % tuple_len) {
pr_warn("malformed ranges entry '%s'\n", node->name);
return -EINVAL;
}
return 0;
}
static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus,
struct device_node *np)
{
int addr_cells, c_addr_cells, c_size_cells;
int i, ret, cell_count;
const __be32 *r, *ranges_start, *ranges_end;
ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells,
&c_size_cells, &cell_count,
&ranges_start, &ranges_end);
if (ret < 0)
return ret;
for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) {
u32 windowid, base, size;
u8 target, attr;
/*
* An entry with a non-zero custom field do not
* correspond to a static window, so skip it.
*/
windowid = of_read_number(r, 1);
if (CUSTOM(windowid))
continue;
target = TARGET(windowid);
attr = ATTR(windowid);
base = of_read_number(r + c_addr_cells, addr_cells);
size = of_read_number(r + c_addr_cells + addr_cells,
c_size_cells);
ret = mbus_dt_setup_win(mbus, base, size, target, attr);
if (ret < 0)
return ret;
}
return 0;
}
static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
struct resource *mem,
struct resource *io)
{
u32 reg[2];
int ret;
/*
* These are optional, so we make sure that resource_size(x) will
* return 0.
*/
memset(mem, 0, sizeof(struct resource));
mem->end = -1;
memset(io, 0, sizeof(struct resource));
io->end = -1;
ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg));
if (!ret) {
mem->start = reg[0];
mem->end = mem->start + reg[1] - 1;
mem->flags = IORESOURCE_MEM;
}
ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg));
if (!ret) {
io->start = reg[0];
io->end = io->start + reg[1] - 1;
io->flags = IORESOURCE_IO;
}
}
int __init mvebu_mbus_dt_init(bool is_coherent)
{
struct resource mbuswins_res, sdramwins_res;
struct device_node *np, *controller;
const struct of_device_id *of_id;
const __be32 *prop;
int ret;
np = of_find_matching_node_and_match(NULL, of_mvebu_mbus_ids, &of_id);
if (!np) {
pr_err("could not find a matching SoC family\n");
return -ENODEV;
}
mbus_state.soc = of_id->data;
prop = of_get_property(np, "controller", NULL);
if (!prop) {
pr_err("required 'controller' property missing\n");
return -EINVAL;
}
controller = of_find_node_by_phandle(be32_to_cpup(prop));
if (!controller) {
pr_err("could not find an 'mbus-controller' node\n");
return -ENODEV;
}
if (of_address_to_resource(controller, 0, &mbuswins_res)) {
pr_err("cannot get MBUS register address\n");
return -EINVAL;
}
if (of_address_to_resource(controller, 1, &sdramwins_res)) {
pr_err("cannot get SDRAM register address\n");
return -EINVAL;
}
mbus_state.hw_io_coherency = is_coherent;
/* Get optional pcie-{mem,io}-aperture properties */
mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture,
&mbus_state.pcie_io_aperture);
ret = mvebu_mbus_common_init(&mbus_state,
mbuswins_res.start,
resource_size(&mbuswins_res),
sdramwins_res.start,
resource_size(&sdramwins_res));
if (ret)
return ret;
/* Setup statically declared windows in the DT */
return mbus_dt_setup(&mbus_state, np);
}
#endif

View file

@ -0,0 +1,90 @@
/*
* 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>
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;
struct device_node *np = pdev->dev.of_node;
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;
}
}
pm_runtime_enable(&pdev->dev);
return 0;
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");

377
drivers/bus/omap_l3_noc.c Normal file
View file

@ -0,0 +1,377 @@
/*
* OMAP L3 Interconnect error handling driver
*
* Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.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 version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "omap_l3_noc.h"
/**
* l3_handle_target() - Handle Target specific parse and reporting
* @l3: pointer to l3 struct
* @base: base address of clkdm
* @flag_mux: flagmux corresponding to the event
* @err_src: error source index of the slave (target)
*
* This does the second part of the error interrupt handling:
* 3) Parse in the slave information
* 4) Print the logged information.
* 5) Add dump stack to provide kernel trace.
* 6) Clear the source if known.
*
* This handles 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 int l3_handle_target(struct omap_l3 *l3, void __iomem *base,
struct l3_flagmux_data *flag_mux, int err_src)
{
int k;
u32 std_err_main, clear, masterid;
u8 op_code, m_req_info;
void __iomem *l3_targ_base;
void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr;
void __iomem *l3_targ_hdr, *l3_targ_info;
struct l3_target_data *l3_targ_inst;
struct l3_masters_data *master;
char *target_name, *master_name = "UN IDENTIFIED";
char *err_description;
char err_string[30] = { 0 };
char info_string[60] = { 0 };
/* We DONOT expect err_src to go out of bounds */
BUG_ON(err_src > MAX_CLKDM_TARGETS);
if (err_src < flag_mux->num_targ_data) {
l3_targ_inst = &flag_mux->l3_targ[err_src];
target_name = l3_targ_inst->name;
l3_targ_base = base + l3_targ_inst->offset;
} else {
target_name = L3_TARGET_NOT_SUPPORTED;
}
if (target_name == L3_TARGET_NOT_SUPPORTED)
return -ENODEV;
/* Read the stderrlog_main_source from clk domain */
l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN;
l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB;
std_err_main = readl_relaxed(l3_targ_stderr);
switch (std_err_main & CUSTOM_ERROR) {
case STANDARD_ERROR:
err_description = "Standard";
snprintf(err_string, sizeof(err_string),
": At Address: 0x%08X ",
readl_relaxed(l3_targ_slvofslsb));
l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_MSTADDR;
l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_HDR;
l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_INFO;
break;
case CUSTOM_ERROR:
err_description = "Custom";
l3_targ_mstaddr = l3_targ_base +
L3_TARG_STDERRLOG_CINFO_MSTADDR;
l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_OPCODE;
l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_CINFO_INFO;
break;
default:
/* Nothing to be handled here as of now */
return 0;
}
/* STDERRLOG_MSTADDR Stores the NTTP master address. */
masterid = (readl_relaxed(l3_targ_mstaddr) &
l3->mst_addr_mask) >> __ffs(l3->mst_addr_mask);
for (k = 0, master = l3->l3_masters; k < l3->num_masters;
k++, master++) {
if (masterid == master->id) {
master_name = master->name;
break;
}
}
op_code = readl_relaxed(l3_targ_hdr) & 0x7;
m_req_info = readl_relaxed(l3_targ_info) & 0xF;
snprintf(info_string, sizeof(info_string),
": %s in %s mode during %s access",
(m_req_info & BIT(0)) ? "Opcode Fetch" : "Data Access",
(m_req_info & BIT(1)) ? "Supervisor" : "User",
(m_req_info & BIT(3)) ? "Debug" : "Functional");
WARN(true,
"%s:L3 %s Error: MASTER %s TARGET %s (%s)%s%s\n",
dev_name(l3->dev),
err_description,
master_name, target_name,
l3_transaction_type[op_code],
err_string, info_string);
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG;
writel_relaxed(clear, l3_targ_stderr);
return 0;
}
/**
* l3_interrupt_handler() - interrupt handler for l3 events
* @irq: irq number
* @_l3: pointer to l3 structure
*
* 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
* ... handle the slave event..
* 7) if the slave is unknown, mask out the slave.
*/
static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
{
struct omap_l3 *l3 = _l3;
int inttype, i, ret;
int err_src = 0;
u32 err_reg, mask_val;
void __iomem *base, *mask_reg;
struct l3_flagmux_data *flag_mux;
/* Get the Type of interrupt */
inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
for (i = 0; i < l3->num_modules; i++) {
/*
* Read the regerr register of the clock domain
* to determine the source
*/
base = l3->l3_base[i];
flag_mux = l3->l3_flagmux[i];
err_reg = readl_relaxed(base + flag_mux->offset +
L3_FLAGMUX_REGERR0 + (inttype << 3));
err_reg &= ~(inttype ? flag_mux->mask_app_bits :
flag_mux->mask_dbg_bits);
/* Get the corresponding error and analyse */
if (err_reg) {
/* Identify the source from control status register */
err_src = __ffs(err_reg);
ret = l3_handle_target(l3, base, flag_mux, err_src);
/*
* Certain plaforms may have "undocumented" status
* pending on boot. So dont generate a severe warning
* here. Just mask it off to prevent the error from
* reoccuring and locking up the system.
*/
if (ret) {
dev_err(l3->dev,
"L3 %s error: target %d mod:%d %s\n",
inttype ? "debug" : "application",
err_src, i, "(unclearable)");
mask_reg = base + flag_mux->offset +
L3_FLAGMUX_MASK0 + (inttype << 3);
mask_val = readl_relaxed(mask_reg);
mask_val &= ~(1 << err_src);
writel_relaxed(mask_val, mask_reg);
/* Mark these bits as to be ignored */
if (inttype)
flag_mux->mask_app_bits |= 1 << err_src;
else
flag_mux->mask_dbg_bits |= 1 << err_src;
}
/* Error found so break the for loop */
return IRQ_HANDLED;
}
}
dev_err(l3->dev, "L3 %s IRQ not handled!!\n",
inttype ? "debug" : "application");
return IRQ_NONE;
}
static const struct of_device_id l3_noc_match[] = {
{.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data},
{.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data},
{.compatible = "ti,am4372-l3-noc", .data = &am4372_l3_data},
{},
};
MODULE_DEVICE_TABLE(of, l3_noc_match);
static int omap_l3_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id;
static struct omap_l3 *l3;
int ret, i, res_idx;
of_id = of_match_device(l3_noc_match, &pdev->dev);
if (!of_id) {
dev_err(&pdev->dev, "OF data missing\n");
return -EINVAL;
}
l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL);
if (!l3)
return -ENOMEM;
memcpy(l3, of_id->data, sizeof(*l3));
l3->dev = &pdev->dev;
platform_set_drvdata(pdev, l3);
/* Get mem resources */
for (i = 0, res_idx = 0; i < l3->num_modules; i++) {
struct resource *res;
if (l3->l3_base[i] == L3_BASE_IS_SUBMODULE) {
/* First entry cannot be submodule */
BUG_ON(i == 0);
l3->l3_base[i] = l3->l3_base[i - 1];
continue;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, res_idx);
l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(l3->l3_base[i])) {
dev_err(l3->dev, "ioremap %d failed\n", i);
return PTR_ERR(l3->l3_base[i]);
}
res_idx++;
}
/*
* Setup interrupt Handlers
*/
l3->debug_irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler,
IRQF_DISABLED, "l3-dbg-irq", l3);
if (ret) {
dev_err(l3->dev, "request_irq failed for %d\n",
l3->debug_irq);
return ret;
}
l3->app_irq = platform_get_irq(pdev, 1);
ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler,
IRQF_DISABLED, "l3-app-irq", l3);
if (ret)
dev_err(l3->dev, "request_irq failed for %d\n", l3->app_irq);
return ret;
}
#ifdef CONFIG_PM
/**
* l3_resume_noirq() - resume function for l3_noc
* @dev: pointer to l3_noc device structure
*
* We only have the resume handler only since we
* have already maintained the delta register
* configuration as part of configuring the system
*/
static int l3_resume_noirq(struct device *dev)
{
struct omap_l3 *l3 = dev_get_drvdata(dev);
int i;
struct l3_flagmux_data *flag_mux;
void __iomem *base, *mask_regx = NULL;
u32 mask_val;
for (i = 0; i < l3->num_modules; i++) {
base = l3->l3_base[i];
flag_mux = l3->l3_flagmux[i];
if (!flag_mux->mask_app_bits && !flag_mux->mask_dbg_bits)
continue;
mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 +
(L3_APPLICATION_ERROR << 3);
mask_val = readl_relaxed(mask_regx);
mask_val &= ~(flag_mux->mask_app_bits);
writel_relaxed(mask_val, mask_regx);
mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 +
(L3_DEBUG_ERROR << 3);
mask_val = readl_relaxed(mask_regx);
mask_val &= ~(flag_mux->mask_dbg_bits);
writel_relaxed(mask_val, mask_regx);
}
/* Dummy read to force OCP barrier */
if (mask_regx)
(void)readl(mask_regx);
return 0;
}
static const struct dev_pm_ops l3_dev_pm_ops = {
.resume_noirq = l3_resume_noirq,
};
#define L3_DEV_PM_OPS (&l3_dev_pm_ops)
#else
#define L3_DEV_PM_OPS NULL
#endif
static struct platform_driver omap_l3_driver = {
.probe = omap_l3_probe,
.driver = {
.name = "omap_l3_noc",
.owner = THIS_MODULE,
.pm = L3_DEV_PM_OPS,
.of_match_table = of_match_ptr(l3_noc_match),
},
};
static int __init omap_l3_init(void)
{
return platform_driver_register(&omap_l3_driver);
}
postcore_initcall_sync(omap_l3_init);
static void __exit omap_l3_exit(void)
{
platform_driver_unregister(&omap_l3_driver);
}
module_exit(omap_l3_exit);

475
drivers/bus/omap_l3_noc.h Normal file
View file

@ -0,0 +1,475 @@
/*
* OMAP L3 Interconnect error handling driver header
*
* Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.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 version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __OMAP_L3_NOC_H
#define __OMAP_L3_NOC_H
#define MAX_L3_MODULES 3
#define MAX_CLKDM_TARGETS 31
#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_HDR 0x4c
#define L3_TARG_STDERRLOG_MSTADDR 0x50
#define L3_TARG_STDERRLOG_INFO 0x58
#define L3_TARG_STDERRLOG_SLVOFSLSB 0x5c
#define L3_TARG_STDERRLOG_CINFO_INFO 0x64
#define L3_TARG_STDERRLOG_CINFO_MSTADDR 0x68
#define L3_TARG_STDERRLOG_CINFO_OPCODE 0x6c
#define L3_FLAGMUX_REGERR0 0xc
#define L3_FLAGMUX_MASK0 0x8
#define L3_TARGET_NOT_SUPPORTED NULL
#define L3_BASE_IS_SUBMODULE ((void __iomem *)(1 << 0))
static const char * const l3_transaction_type[] = {
/* 0 0 0 */ "Idle",
/* 0 0 1 */ "Write",
/* 0 1 0 */ "Read",
/* 0 1 1 */ "ReadEx",
/* 1 0 0 */ "Read Link",
/* 1 0 1 */ "Write Non-Posted",
/* 1 1 0 */ "Write Conditional",
/* 1 1 1 */ "Write Broadcast",
};
/**
* struct l3_masters_data - L3 Master information
* @id: ID of the L3 Master
* @name: master name
*/
struct l3_masters_data {
u32 id;
char *name;
};
/**
* struct l3_target_data - L3 Target information
* @offset: Offset from base for L3 Target
* @name: Target name
*
* Target information is organized indexed by bit field definitions.
*/
struct l3_target_data {
u32 offset;
char *name;
};
/**
* struct l3_flagmux_data - Flag Mux information
* @offset: offset from base for flagmux register
* @l3_targ: array indexed by flagmux index (bit offset) pointing to the
* target data. unsupported ones are marked with
* L3_TARGET_NOT_SUPPORTED
* @num_targ_data: number of entries in target data
* @mask_app_bits: ignore these from raw application irq status
* @mask_dbg_bits: ignore these from raw debug irq status
*/
struct l3_flagmux_data {
u32 offset;
struct l3_target_data *l3_targ;
u8 num_targ_data;
u32 mask_app_bits;
u32 mask_dbg_bits;
};
/**
* struct omap_l3 - Description of data relevant for L3 bus.
* @dev: device representing the bus (populated runtime)
* @l3_base: base addresses of modules (populated runtime if 0)
* if set to L3_BASE_IS_SUBMODULE, then uses previous
* module index as the base address
* @l3_flag_mux: array containing flag mux data per module
* offset from corresponding module base indexed per
* module.
* @num_modules: number of clock domains / modules.
* @l3_masters: array pointing to master data containing name and register
* offset for the master.
* @num_master: number of masters
* @mst_addr_mask: Mask representing MSTADDR information of NTTP packet
* @debug_irq: irq number of the debug interrupt (populated runtime)
* @app_irq: irq number of the application interrupt (populated runtime)
*/
struct omap_l3 {
struct device *dev;
void __iomem *l3_base[MAX_L3_MODULES];
struct l3_flagmux_data **l3_flagmux;
int num_modules;
struct l3_masters_data *l3_masters;
int num_masters;
u32 mst_addr_mask;
int debug_irq;
int app_irq;
};
static struct l3_target_data omap_l3_target_data_clk1[] = {
{0x100, "DMM1",},
{0x200, "DMM2",},
{0x300, "ABE",},
{0x400, "L4CFG",},
{0x600, "CLK2PWRDISC",},
{0x0, "HOSTCLK1",},
{0x900, "L4WAKEUP",},
};
static struct l3_flagmux_data omap_l3_flagmux_clk1 = {
.offset = 0x500,
.l3_targ = omap_l3_target_data_clk1,
.num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk1),
};
static struct l3_target_data omap_l3_target_data_clk2[] = {
{0x500, "CORTEXM3",},
{0x300, "DSS",},
{0x100, "GPMC",},
{0x400, "ISS",},
{0x700, "IVAHD",},
{0xD00, "AES1",},
{0x900, "L4PER0",},
{0x200, "OCMRAM",},
{0x100, "GPMCsERROR",},
{0x600, "SGX",},
{0x800, "SL2",},
{0x1600, "C2C",},
{0x1100, "PWRDISCCLK1",},
{0xF00, "SHA1",},
{0xE00, "AES2",},
{0xC00, "L4PER3",},
{0xA00, "L4PER1",},
{0xB00, "L4PER2",},
{0x0, "HOSTCLK2",},
{0x1800, "CAL",},
{0x1700, "LLI",},
};
static struct l3_flagmux_data omap_l3_flagmux_clk2 = {
.offset = 0x1000,
.l3_targ = omap_l3_target_data_clk2,
.num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk2),
};
static struct l3_target_data omap_l3_target_data_clk3[] = {
{0x0100, "EMUSS",},
{0x0300, "DEBUG SOURCE",},
{0x0, "HOST CLK3",},
};
static struct l3_flagmux_data omap_l3_flagmux_clk3 = {
.offset = 0x0200,
.l3_targ = omap_l3_target_data_clk3,
.num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk3),
};
static struct l3_masters_data omap_l3_masters[] = {
{ 0x00, "MPU"},
{ 0x04, "CS_ADP"},
{ 0x05, "xxx"},
{ 0x08, "DSP"},
{ 0x0C, "IVAHD"},
{ 0x10, "ISS"},
{ 0x11, "DucatiM3"},
{ 0x12, "FaceDetect"},
{ 0x14, "SDMA_Rd"},
{ 0x15, "SDMA_Wr"},
{ 0x16, "xxx"},
{ 0x17, "xxx"},
{ 0x18, "SGX"},
{ 0x1C, "DSS"},
{ 0x20, "C2C"},
{ 0x22, "xxx"},
{ 0x23, "xxx"},
{ 0x24, "HSI"},
{ 0x28, "MMC1"},
{ 0x29, "MMC2"},
{ 0x2A, "MMC6"},
{ 0x2C, "UNIPRO1"},
{ 0x30, "USBHOSTHS"},
{ 0x31, "USBOTGHS"},
{ 0x32, "USBHOSTFS"}
};
static struct l3_flagmux_data *omap_l3_flagmux[] = {
&omap_l3_flagmux_clk1,
&omap_l3_flagmux_clk2,
&omap_l3_flagmux_clk3,
};
static const struct omap_l3 omap_l3_data = {
.l3_flagmux = omap_l3_flagmux,
.num_modules = ARRAY_SIZE(omap_l3_flagmux),
.l3_masters = omap_l3_masters,
.num_masters = ARRAY_SIZE(omap_l3_masters),
/* The 6 MSBs of register field used to distinguish initiator */
.mst_addr_mask = 0xFC,
};
/* DRA7 data */
static struct l3_target_data dra_l3_target_data_clk1[] = {
{0x2a00, "AES1",},
{0x0200, "DMM_P1",},
{0x0600, "DSP2_SDMA",},
{0x0b00, "EVE2",},
{0x1300, "DMM_P2",},
{0x2c00, "AES2",},
{0x0300, "DSP1_SDMA",},
{0x0a00, "EVE1",},
{0x0c00, "EVE3",},
{0x0d00, "EVE4",},
{0x2900, "DSS",},
{0x0100, "GPMC",},
{0x3700, "PCIE1",},
{0x1600, "IVA_CONFIG",},
{0x1800, "IVA_SL2IF",},
{0x0500, "L4_CFG",},
{0x1d00, "L4_WKUP",},
{0x3800, "PCIE2",},
{0x3300, "SHA2_1",},
{0x1200, "GPU",},
{0x1000, "IPU1",},
{0x1100, "IPU2",},
{0x2000, "TPCC_EDMA",},
{0x2e00, "TPTC1_EDMA",},
{0x2b00, "TPTC2_EDMA",},
{0x0700, "VCP1",},
{0x2500, "L4_PER2_P3",},
{0x0e00, "L4_PER3_P3",},
{0x2200, "MMU1",},
{0x1400, "PRUSS1",},
{0x1500, "PRUSS2"},
{0x0800, "VCP1",},
};
static struct l3_flagmux_data dra_l3_flagmux_clk1 = {
.offset = 0x803500,
.l3_targ = dra_l3_target_data_clk1,
.num_targ_data = ARRAY_SIZE(dra_l3_target_data_clk1),
};
static struct l3_target_data dra_l3_target_data_clk2[] = {
{0x0, "HOST CLK1",},
{0x0, "HOST CLK2",},
{0xdead, L3_TARGET_NOT_SUPPORTED,},
{0x3400, "SHA2_2",},
{0x0900, "BB2D",},
{0xdead, L3_TARGET_NOT_SUPPORTED,},
{0x2100, "L4_PER1_P3",},
{0x1c00, "L4_PER1_P1",},
{0x1f00, "L4_PER1_P2",},
{0x2300, "L4_PER2_P1",},
{0x2400, "L4_PER2_P2",},
{0x2600, "L4_PER3_P1",},
{0x2700, "L4_PER3_P2",},
{0x2f00, "MCASP1",},
{0x3000, "MCASP2",},
{0x3100, "MCASP3",},
{0x2800, "MMU2",},
{0x0f00, "OCMC_RAM1",},
{0x1700, "OCMC_RAM2",},
{0x1900, "OCMC_RAM3",},
{0x1e00, "OCMC_ROM",},
{0x3900, "QSPI",},
};
static struct l3_flagmux_data dra_l3_flagmux_clk2 = {
.offset = 0x803600,
.l3_targ = dra_l3_target_data_clk2,
.num_targ_data = ARRAY_SIZE(dra_l3_target_data_clk2),
};
static struct l3_target_data dra_l3_target_data_clk3[] = {
{0x0100, "L3_INSTR"},
{0x0300, "DEBUGSS_CT_TBR"},
{0x0, "HOST CLK3"},
};
static struct l3_flagmux_data dra_l3_flagmux_clk3 = {
.offset = 0x200,
.l3_targ = dra_l3_target_data_clk3,
.num_targ_data = ARRAY_SIZE(dra_l3_target_data_clk3),
};
static struct l3_masters_data dra_l3_masters[] = {
{ 0x0, "MPU" },
{ 0x4, "CS_DAP" },
{ 0x5, "IEEE1500_2_OCP" },
{ 0x8, "DSP1_MDMA" },
{ 0x9, "DSP1_CFG" },
{ 0xA, "DSP1_DMA" },
{ 0xB, "DSP2_MDMA" },
{ 0xC, "DSP2_CFG" },
{ 0xD, "DSP2_DMA" },
{ 0xE, "IVA" },
{ 0x10, "EVE1_P1" },
{ 0x11, "EVE2_P1" },
{ 0x12, "EVE3_P1" },
{ 0x13, "EVE4_P1" },
{ 0x14, "PRUSS1 PRU1" },
{ 0x15, "PRUSS1 PRU2" },
{ 0x16, "PRUSS2 PRU1" },
{ 0x17, "PRUSS2 PRU2" },
{ 0x18, "IPU1" },
{ 0x19, "IPU2" },
{ 0x1A, "SDMA" },
{ 0x1B, "CDMA" },
{ 0x1C, "TC1_EDMA" },
{ 0x1D, "TC2_EDMA" },
{ 0x20, "DSS" },
{ 0x21, "MMU1" },
{ 0x22, "PCIE1" },
{ 0x23, "MMU2" },
{ 0x24, "VIP1" },
{ 0x25, "VIP2" },
{ 0x26, "VIP3" },
{ 0x27, "VPE" },
{ 0x28, "GPU_P1" },
{ 0x29, "BB2D" },
{ 0x29, "GPU_P2" },
{ 0x2B, "GMAC_SW" },
{ 0x2C, "USB3" },
{ 0x2D, "USB2_SS" },
{ 0x2E, "USB2_ULPI_SS1" },
{ 0x2F, "USB2_ULPI_SS2" },
{ 0x30, "CSI2_1" },
{ 0x31, "CSI2_2" },
{ 0x33, "SATA" },
{ 0x34, "EVE1_P2" },
{ 0x35, "EVE2_P2" },
{ 0x36, "EVE3_P2" },
{ 0x37, "EVE4_P2" }
};
static struct l3_flagmux_data *dra_l3_flagmux[] = {
&dra_l3_flagmux_clk1,
&dra_l3_flagmux_clk2,
&dra_l3_flagmux_clk3,
};
static const struct omap_l3 dra_l3_data = {
.l3_base = { [1] = L3_BASE_IS_SUBMODULE },
.l3_flagmux = dra_l3_flagmux,
.num_modules = ARRAY_SIZE(dra_l3_flagmux),
.l3_masters = dra_l3_masters,
.num_masters = ARRAY_SIZE(dra_l3_masters),
/* The 6 MSBs of register field used to distinguish initiator */
.mst_addr_mask = 0xFC,
};
/* AM4372 data */
static struct l3_target_data am4372_l3_target_data_200f[] = {
{0xf00, "EMIF",},
{0x1200, "DES",},
{0x400, "OCMCRAM",},
{0x700, "TPTC0",},
{0x800, "TPTC1",},
{0x900, "TPTC2"},
{0xb00, "TPCC",},
{0xd00, "DEBUGSS",},
{0xdead, L3_TARGET_NOT_SUPPORTED,},
{0x200, "SHA",},
{0xc00, "SGX530",},
{0x500, "AES0",},
{0xa00, "L4_FAST",},
{0x300, "MPUSS_L2_RAM",},
{0x100, "ICSS",},
};
static struct l3_flagmux_data am4372_l3_flagmux_200f = {
.offset = 0x1000,
.l3_targ = am4372_l3_target_data_200f,
.num_targ_data = ARRAY_SIZE(am4372_l3_target_data_200f),
};
static struct l3_target_data am4372_l3_target_data_100s[] = {
{0x100, "L4_PER_0",},
{0x200, "L4_PER_1",},
{0x300, "L4_PER_2",},
{0x400, "L4_PER_3",},
{0x800, "McASP0",},
{0x900, "McASP1",},
{0xC00, "MMCHS2",},
{0x700, "GPMC",},
{0xD00, "L4_FW",},
{0xdead, L3_TARGET_NOT_SUPPORTED,},
{0x500, "ADCTSC",},
{0xE00, "L4_WKUP",},
{0xA00, "MAG_CARD",},
};
static struct l3_flagmux_data am4372_l3_flagmux_100s = {
.offset = 0x600,
.l3_targ = am4372_l3_target_data_100s,
.num_targ_data = ARRAY_SIZE(am4372_l3_target_data_100s),
};
static struct l3_masters_data am4372_l3_masters[] = {
{ 0x0, "M1 (128-bit)"},
{ 0x1, "M2 (64-bit)"},
{ 0x4, "DAP"},
{ 0x5, "P1500"},
{ 0xC, "ICSS0"},
{ 0xD, "ICSS1"},
{ 0x14, "Wakeup Processor"},
{ 0x18, "TPTC0 Read"},
{ 0x19, "TPTC0 Write"},
{ 0x1A, "TPTC1 Read"},
{ 0x1B, "TPTC1 Write"},
{ 0x1C, "TPTC2 Read"},
{ 0x1D, "TPTC2 Write"},
{ 0x20, "SGX530"},
{ 0x21, "OCP WP Traffic Probe"},
{ 0x22, "OCP WP DMA Profiling"},
{ 0x23, "OCP WP Event Trace"},
{ 0x25, "DSS"},
{ 0x28, "Crypto DMA RD"},
{ 0x29, "Crypto DMA WR"},
{ 0x2C, "VPFE0"},
{ 0x2D, "VPFE1"},
{ 0x30, "GEMAC"},
{ 0x34, "USB0 RD"},
{ 0x35, "USB0 WR"},
{ 0x36, "USB1 RD"},
{ 0x37, "USB1 WR"},
};
static struct l3_flagmux_data *am4372_l3_flagmux[] = {
&am4372_l3_flagmux_200f,
&am4372_l3_flagmux_100s,
};
static const struct omap_l3 am4372_l3_data = {
.l3_flagmux = am4372_l3_flagmux,
.num_modules = ARRAY_SIZE(am4372_l3_flagmux),
.l3_masters = am4372_l3_masters,
.num_masters = ARRAY_SIZE(am4372_l3_masters),
/* All 6 bits of register field used to distinguish initiator */
.mst_addr_mask = 0x3F,
};
#endif /* __OMAP_L3_NOC_H */

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

View file

@ -0,0 +1,202 @@
/*
* 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.
*
* 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.
*
* Copyright (C) 2014 ARM Limited
*/
#include <linux/err.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/vexpress.h>
struct vexpress_config_bridge {
struct vexpress_config_bridge_ops *ops;
void *context;
};
static DEFINE_MUTEX(vexpress_config_mutex);
static struct class *vexpress_config_class;
static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
void vexpress_config_set_master(u32 site)
{
vexpress_config_site_master = site;
}
u32 vexpress_config_get_master(void)
{
return vexpress_config_site_master;
}
void vexpress_config_lock(void *arg)
{
mutex_lock(&vexpress_config_mutex);
}
void vexpress_config_unlock(void *arg)
{
mutex_unlock(&vexpress_config_mutex);
}
static void vexpress_config_find_prop(struct device_node *node,
const char *name, u32 *val)
{
/* Default value */
*val = 0;
of_node_get(node);
while (node) {
if (of_property_read_u32(node, name, val) == 0) {
of_node_put(node);
return;
}
node = of_get_next_parent(node);
}
}
int vexpress_config_get_topo(struct device_node *node, u32 *site,
u32 *position, u32 *dcc)
{
vexpress_config_find_prop(node, "arm,vexpress,site", site);
if (*site == VEXPRESS_SITE_MASTER)
*site = vexpress_config_site_master;
if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER))
return -EINVAL;
vexpress_config_find_prop(node, "arm,vexpress,position", position);
vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc);
return 0;
}
static void vexpress_config_devres_release(struct device *dev, void *res)
{
struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent);
struct regmap *regmap = res;
bridge->ops->regmap_exit(regmap, bridge->context);
}
struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
{
struct vexpress_config_bridge *bridge;
struct regmap *regmap;
struct regmap **res;
if (WARN_ON(dev->parent->class != vexpress_config_class))
return ERR_PTR(-ENODEV);
bridge = dev_get_drvdata(dev->parent);
if (WARN_ON(!bridge))
return ERR_PTR(-EINVAL);
res = devres_alloc(vexpress_config_devres_release, sizeof(*res),
GFP_KERNEL);
if (!res)
return ERR_PTR(-ENOMEM);
regmap = bridge->ops->regmap_init(dev, bridge->context);
if (IS_ERR(regmap)) {
devres_free(res);
return regmap;
}
*res = regmap;
devres_add(dev, res);
return regmap;
}
EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
struct device *vexpress_config_bridge_register(struct device *parent,
struct vexpress_config_bridge_ops *ops, void *context)
{
struct device *dev;
struct vexpress_config_bridge *bridge;
if (!vexpress_config_class) {
vexpress_config_class = class_create(THIS_MODULE,
"vexpress-config");
if (IS_ERR(vexpress_config_class))
return (void *)vexpress_config_class;
}
dev = device_create(vexpress_config_class, parent, 0,
NULL, "%s.bridge", dev_name(parent));
if (IS_ERR(dev))
return dev;
bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
put_device(dev);
device_unregister(dev);
return ERR_PTR(-ENOMEM);
}
bridge->ops = ops;
bridge->context = context;
dev_set_drvdata(dev, bridge);
dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
dev_name(dev), parent->of_node);
return dev;
}
static int vexpress_config_node_match(struct device *dev, const void *data)
{
const struct device_node *node = data;
dev_dbg(dev, "Parent node %p, looking for %p\n",
dev->parent->of_node, node);
return dev->parent->of_node == node;
}
static int vexpress_config_populate(struct device_node *node)
{
struct device_node *bridge;
struct device *parent;
bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
if (!bridge)
return -EINVAL;
parent = class_find_device(vexpress_config_class, NULL, bridge,
vexpress_config_node_match);
if (WARN_ON(!parent))
return -ENODEV;
return of_platform_populate(node, NULL, NULL, parent);
}
static int __init vexpress_config_init(void)
{
int err = 0;
struct device_node *node;
/* Need the config devices early, before the "normal" devices... */
for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
err = vexpress_config_populate(node);
if (err)
break;
}
return err;
}
postcore_initcall(vexpress_config_init);