Commit 8286ae03 authored by James Hogan's avatar James Hogan Committed by Ralf Baechle

MIPS: Add CDMM bus support

Add MIPS Common Device Memory Map (CDMM) support in the form of a bus in
the standard Linux device model. Each device attached via CDMM is
discoverable via an 8-bit type identifier and may contain a number of
blocks of memory mapped registers in the CDMM region. IRQs are expected
to be handled separately.

Due to the per-cpu (per-VPE for MT cores) nature of the CDMM devices,
all the driver callbacks take place from workqueues which are run on the
right CPU for the device in question, so that the driver doesn't need to
be as concerned about which CPU it is running on. Callbacks also exist
for when CPUs are taken offline, so that any per-CPU resources used by
the driver can be disabled so they don't get forcefully migrated. CDMM
devices are created as children of the CPU device they are attached to.

Any existing CDMM configuration by the bootloader will be inherited,
however platforms wishing to enable CDMM should implement the weak
mips_cdmm_phys_base() function (see asm/cdmm.h) so that the bus driver
knows where it should put the CDMM region in the physical address space
if the bootloader hasn't already enabled it.

A mips_cdmm_early_probe() function is also provided to allow early boot
or particularly low level code to set up the CDMM region and probe for a
specific device type, for example early console or KGDB IO drivers for
the EJTAG Fast Debug Channel (FDC) CDMM device.
Signed-off-by: default avatarJames Hogan <james.hogan@imgtec.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/9599/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 9b3274bd
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2014 Imagination Technologies Ltd.
*/
#ifndef __ASM_CDMM_H
#define __ASM_CDMM_H
#include <linux/device.h>
#include <linux/mod_devicetable.h>
/**
* struct mips_cdmm_device - Represents a single device on a CDMM bus.
* @dev: Driver model device object.
* @cpu: CPU which can access this device.
* @res: MMIO resource.
* @type: Device type identifier.
* @rev: Device revision number.
*/
struct mips_cdmm_device {
struct device dev;
unsigned int cpu;
struct resource res;
unsigned int type;
unsigned int rev;
};
/**
* struct mips_cdmm_driver - Represents a driver for a CDMM device.
* @drv: Driver model driver object.
* @probe Callback for probing newly discovered devices.
* @remove: Callback to remove the device.
* @shutdown: Callback on system shutdown.
* @cpu_down: Callback when the parent CPU is going down.
* Any CPU pinned threads/timers should be disabled.
* @cpu_up: Callback when the parent CPU is coming back up again.
* CPU pinned threads/timers can be restarted.
* @id_table: Table for CDMM IDs to match against.
*/
struct mips_cdmm_driver {
struct device_driver drv;
int (*probe)(struct mips_cdmm_device *);
int (*remove)(struct mips_cdmm_device *);
void (*shutdown)(struct mips_cdmm_device *);
int (*cpu_down)(struct mips_cdmm_device *);
int (*cpu_up)(struct mips_cdmm_device *);
const struct mips_cdmm_device_id *id_table;
};
/**
* mips_cdmm_phys_base() - Choose a physical base address for CDMM region.
*
* Picking a suitable physical address at which to map the CDMM region is
* platform specific, so this weak function can be defined by platform code to
* pick a suitable value if none is configured by the bootloader.
*
* This address must be 32kB aligned, and the region occupies a maximum of 32kB
* of physical address space which must not be used for anything else.
*
* Returns: Physical base address for CDMM region, or 0 on failure.
*/
phys_addr_t __weak mips_cdmm_phys_base(void);
extern struct bus_type mips_cdmm_bustype;
void __iomem *mips_cdmm_early_probe(unsigned int dev_type);
#define to_mips_cdmm_device(d) container_of(d, struct mips_cdmm_device, dev)
#define mips_cdmm_get_drvdata(d) dev_get_drvdata(&d->dev)
#define mips_cdmm_set_drvdata(d, p) dev_set_drvdata(&d->dev, p)
int mips_cdmm_driver_register(struct mips_cdmm_driver *);
void mips_cdmm_driver_unregister(struct mips_cdmm_driver *);
/*
* module_mips_cdmm_driver() - Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*/
#define module_mips_cdmm_driver(__mips_cdmm_driver) \
module_driver(__mips_cdmm_driver, mips_cdmm_driver_register, \
mips_cdmm_driver_unregister)
#endif /* __ASM_CDMM_H */
......@@ -20,6 +20,19 @@ config IMX_WEIM
The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR, onenand.
config MIPS_CDMM
bool "MIPS Common Device Memory Map (CDMM) Driver"
depends on CPU_MIPSR2
help
Driver needed for the MIPS Common Device Memory Map bus in MIPS
cores. This bus is for per-CPU tightly coupled devices such as the
Fast Debug Channel (FDC).
For this to work, either your bootloader needs to enable the CDMM
region at an unused physical address on the boot CPU, or else your
platform code needs to implement mips_cdmm_phys_base() (see
asm/cdmm.h).
config MVEBU_MBUS
bool
depends on PLAT_ORION
......
......@@ -4,6 +4,7 @@
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
......
This diff is collapsed.
......@@ -546,6 +546,14 @@ struct amba_id {
void *data;
};
/**
* struct mips_cdmm_device_id - identifies devices in MIPS CDMM bus
* @type: Device type identifier.
*/
struct mips_cdmm_device_id {
__u8 type;
};
/*
* Match x86 CPUs for CPU specific drivers.
* See documentation of "x86_match_cpu" for details.
......
......@@ -168,6 +168,9 @@ int main(void)
DEVID_FIELD(amba_id, id);
DEVID_FIELD(amba_id, mask);
DEVID(mips_cdmm_device_id);
DEVID_FIELD(mips_cdmm_device_id, type);
DEVID(x86_cpu_id);
DEVID_FIELD(x86_cpu_id, feature);
DEVID_FIELD(x86_cpu_id, family);
......
......@@ -1109,6 +1109,22 @@ static int do_amba_entry(const char *filename,
}
ADD_TO_DEVTABLE("amba", amba_id, do_amba_entry);
/*
* looks like: "mipscdmm:tN"
*
* N is exactly 2 digits, where each is an upper-case hex digit, or
* a ? or [] pattern matching exactly one digit.
*/
static int do_mips_cdmm_entry(const char *filename,
void *symval, char *alias)
{
DEF_FIELD(symval, mips_cdmm_device_id, type);
sprintf(alias, "mipscdmm:t%02X*", type);
return 1;
}
ADD_TO_DEVTABLE("mipscdmm", mips_cdmm_device_id, do_mips_cdmm_entry);
/* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,*
* All fields are numbers. It would be nicer to use strings for vendor
* and feature, but getting those out of the build system here is too
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment