162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Bus driver for MIPS Common Device Memory Map (CDMM).
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2014-2015 Imagination Technologies Ltd.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
762306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
862306a36Sopenharmony_ci * for more details.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/atomic.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/cpu.h>
1462306a36Sopenharmony_ci#include <linux/cpumask.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/of_address.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/smp.h>
2162306a36Sopenharmony_ci#include <asm/cdmm.h>
2262306a36Sopenharmony_ci#include <asm/hazards.h>
2362306a36Sopenharmony_ci#include <asm/mipsregs.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* Access control and status register fields */
2662306a36Sopenharmony_ci#define CDMM_ACSR_DEVTYPE_SHIFT	24
2762306a36Sopenharmony_ci#define CDMM_ACSR_DEVTYPE	(255ul << CDMM_ACSR_DEVTYPE_SHIFT)
2862306a36Sopenharmony_ci#define CDMM_ACSR_DEVSIZE_SHIFT	16
2962306a36Sopenharmony_ci#define CDMM_ACSR_DEVSIZE	(31ul << CDMM_ACSR_DEVSIZE_SHIFT)
3062306a36Sopenharmony_ci#define CDMM_ACSR_DEVREV_SHIFT	12
3162306a36Sopenharmony_ci#define CDMM_ACSR_DEVREV	(15ul << CDMM_ACSR_DEVREV_SHIFT)
3262306a36Sopenharmony_ci#define CDMM_ACSR_UW		(1ul << 3)
3362306a36Sopenharmony_ci#define CDMM_ACSR_UR		(1ul << 2)
3462306a36Sopenharmony_ci#define CDMM_ACSR_SW		(1ul << 1)
3562306a36Sopenharmony_ci#define CDMM_ACSR_SR		(1ul << 0)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Each block of device registers is 64 bytes */
3862306a36Sopenharmony_ci#define CDMM_DRB_SIZE		64
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define to_mips_cdmm_driver(d)	container_of(d, struct mips_cdmm_driver, drv)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Default physical base address */
4362306a36Sopenharmony_cistatic phys_addr_t mips_cdmm_default_base;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Bus operations */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const struct mips_cdmm_device_id *
4862306a36Sopenharmony_cimips_cdmm_lookup(const struct mips_cdmm_device_id *table,
4962306a36Sopenharmony_ci		 struct mips_cdmm_device *dev)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	int ret = 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	for (; table->type; ++table) {
5462306a36Sopenharmony_ci		ret = (dev->type == table->type);
5562306a36Sopenharmony_ci		if (ret)
5662306a36Sopenharmony_ci			break;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return ret ? table : NULL;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int mips_cdmm_match(struct device *dev, struct device_driver *drv)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
6562306a36Sopenharmony_ci	struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int mips_cdmm_uevent(const struct device *dev, struct kobj_uevent_env *env)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	const struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
7362306a36Sopenharmony_ci	int retval = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
7662306a36Sopenharmony_ci	if (retval)
7762306a36Sopenharmony_ci		return retval;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);
8062306a36Sopenharmony_ci	if (retval)
8162306a36Sopenharmony_ci		return retval;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);
8462306a36Sopenharmony_ci	if (retval)
8562306a36Sopenharmony_ci		return retval;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);
8862306a36Sopenharmony_ci	return retval;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Device attributes */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define CDMM_ATTR(name, fmt, arg...)					\
9462306a36Sopenharmony_cistatic ssize_t name##_show(struct device *_dev,				\
9562306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)	\
9662306a36Sopenharmony_ci{									\
9762306a36Sopenharmony_ci	struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev);	\
9862306a36Sopenharmony_ci	return sprintf(buf, fmt, arg);					\
9962306a36Sopenharmony_ci}									\
10062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciCDMM_ATTR(cpu, "%u\n", dev->cpu);
10362306a36Sopenharmony_ciCDMM_ATTR(type, "0x%02x\n", dev->type);
10462306a36Sopenharmony_ciCDMM_ATTR(revision, "%u\n", dev->rev);
10562306a36Sopenharmony_ciCDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);
10662306a36Sopenharmony_ciCDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",
10762306a36Sopenharmony_ci	  (unsigned long long)dev->res.start,
10862306a36Sopenharmony_ci	  (unsigned long long)dev->res.end,
10962306a36Sopenharmony_ci	  dev->res.flags);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct attribute *mips_cdmm_dev_attrs[] = {
11262306a36Sopenharmony_ci	&dev_attr_cpu.attr,
11362306a36Sopenharmony_ci	&dev_attr_type.attr,
11462306a36Sopenharmony_ci	&dev_attr_revision.attr,
11562306a36Sopenharmony_ci	&dev_attr_modalias.attr,
11662306a36Sopenharmony_ci	&dev_attr_resource.attr,
11762306a36Sopenharmony_ci	NULL,
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ciATTRIBUTE_GROUPS(mips_cdmm_dev);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistruct bus_type mips_cdmm_bustype = {
12262306a36Sopenharmony_ci	.name		= "cdmm",
12362306a36Sopenharmony_ci	.dev_groups	= mips_cdmm_dev_groups,
12462306a36Sopenharmony_ci	.match		= mips_cdmm_match,
12562306a36Sopenharmony_ci	.uevent		= mips_cdmm_uevent,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_bustype);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Standard driver callback helpers.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * All the CDMM driver callbacks need to be executed on the appropriate CPU from
13362306a36Sopenharmony_ci * workqueues. For the standard driver callbacks we need a work function
13462306a36Sopenharmony_ci * (mips_cdmm_{void,int}_work()) to do the actual call from the right CPU, and a
13562306a36Sopenharmony_ci * wrapper function (generated with BUILD_PERCPU_HELPER) to arrange for the work
13662306a36Sopenharmony_ci * function to be called on that CPU.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/**
14062306a36Sopenharmony_ci * struct mips_cdmm_work_dev - Data for per-device call work.
14162306a36Sopenharmony_ci * @fn:		CDMM driver callback function to call for the device.
14262306a36Sopenharmony_ci * @dev:	CDMM device to pass to @fn.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_cistruct mips_cdmm_work_dev {
14562306a36Sopenharmony_ci	void			*fn;
14662306a36Sopenharmony_ci	struct mips_cdmm_device *dev;
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci * mips_cdmm_void_work() - Call a void returning CDMM driver callback.
15162306a36Sopenharmony_ci * @data:	struct mips_cdmm_work_dev pointer.
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
15462306a36Sopenharmony_ci * function which doesn't return a value.
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistatic long mips_cdmm_void_work(void *data)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct mips_cdmm_work_dev *work = data;
15962306a36Sopenharmony_ci	void (*fn)(struct mips_cdmm_device *) = work->fn;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	fn(work->dev);
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/**
16662306a36Sopenharmony_ci * mips_cdmm_int_work() - Call an int returning CDMM driver callback.
16762306a36Sopenharmony_ci * @data:	struct mips_cdmm_work_dev pointer.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
17062306a36Sopenharmony_ci * function which returns an int.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic long mips_cdmm_int_work(void *data)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct mips_cdmm_work_dev *work = data;
17562306a36Sopenharmony_ci	int (*fn)(struct mips_cdmm_device *) = work->fn;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return fn(work->dev);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#define _BUILD_RET_void
18162306a36Sopenharmony_ci#define _BUILD_RET_int	return
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/**
18462306a36Sopenharmony_ci * BUILD_PERCPU_HELPER() - Helper to call a CDMM driver callback on right CPU.
18562306a36Sopenharmony_ci * @_ret:	Return type (void or int).
18662306a36Sopenharmony_ci * @_name:	Name of CDMM driver callback function.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * Generates a specific device callback function to call a CDMM driver callback
18962306a36Sopenharmony_ci * function on the appropriate CPU for the device, and if applicable return the
19062306a36Sopenharmony_ci * result.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci#define BUILD_PERCPU_HELPER(_ret, _name)				\
19362306a36Sopenharmony_cistatic _ret mips_cdmm_##_name(struct device *dev)			\
19462306a36Sopenharmony_ci{									\
19562306a36Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);	\
19662306a36Sopenharmony_ci	struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \
19762306a36Sopenharmony_ci	struct mips_cdmm_work_dev work = {				\
19862306a36Sopenharmony_ci		.fn	= cdrv->_name,					\
19962306a36Sopenharmony_ci		.dev	= cdev,						\
20062306a36Sopenharmony_ci	};								\
20162306a36Sopenharmony_ci									\
20262306a36Sopenharmony_ci	_BUILD_RET_##_ret work_on_cpu(cdev->cpu,			\
20362306a36Sopenharmony_ci				      mips_cdmm_##_ret##_work, &work);	\
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/* Driver callback functions */
20762306a36Sopenharmony_ciBUILD_PERCPU_HELPER(int, probe)     /* int mips_cdmm_probe(struct device) */
20862306a36Sopenharmony_ciBUILD_PERCPU_HELPER(int, remove)    /* int mips_cdmm_remove(struct device) */
20962306a36Sopenharmony_ciBUILD_PERCPU_HELPER(void, shutdown) /* void mips_cdmm_shutdown(struct device) */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/* Driver registration */
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/**
21562306a36Sopenharmony_ci * mips_cdmm_driver_register() - Register a CDMM driver.
21662306a36Sopenharmony_ci * @drv:	CDMM driver information.
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * Register a CDMM driver with the CDMM subsystem. The driver will be informed
21962306a36Sopenharmony_ci * of matching devices which are discovered.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * Returns:	0 on success.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_ciint mips_cdmm_driver_register(struct mips_cdmm_driver *drv)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	drv->drv.bus = &mips_cdmm_bustype;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (drv->probe)
22862306a36Sopenharmony_ci		drv->drv.probe = mips_cdmm_probe;
22962306a36Sopenharmony_ci	if (drv->remove)
23062306a36Sopenharmony_ci		drv->drv.remove = mips_cdmm_remove;
23162306a36Sopenharmony_ci	if (drv->shutdown)
23262306a36Sopenharmony_ci		drv->drv.shutdown = mips_cdmm_shutdown;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return driver_register(&drv->drv);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_driver_register);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/**
23962306a36Sopenharmony_ci * mips_cdmm_driver_unregister() - Unregister a CDMM driver.
24062306a36Sopenharmony_ci * @drv:	CDMM driver information.
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * Unregister a CDMM driver from the CDMM subsystem.
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_civoid mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	driver_unregister(&drv->drv);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/* CDMM initialisation and bus discovery */
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/**
25462306a36Sopenharmony_ci * struct mips_cdmm_bus - Info about CDMM bus.
25562306a36Sopenharmony_ci * @phys:		Physical address at which it is mapped.
25662306a36Sopenharmony_ci * @regs:		Virtual address where registers can be accessed.
25762306a36Sopenharmony_ci * @drbs:		Total number of DRBs.
25862306a36Sopenharmony_ci * @drbs_reserved:	Number of DRBs reserved.
25962306a36Sopenharmony_ci * @discovered:		Whether the devices on the bus have been discovered yet.
26062306a36Sopenharmony_ci * @offline:		Whether the CDMM bus is going offline (or very early
26162306a36Sopenharmony_ci *			coming back online), in which case it should be
26262306a36Sopenharmony_ci *			reconfigured each time.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_cistruct mips_cdmm_bus {
26562306a36Sopenharmony_ci	phys_addr_t	 phys;
26662306a36Sopenharmony_ci	void __iomem	*regs;
26762306a36Sopenharmony_ci	unsigned int	 drbs;
26862306a36Sopenharmony_ci	unsigned int	 drbs_reserved;
26962306a36Sopenharmony_ci	bool		 discovered;
27062306a36Sopenharmony_ci	bool		 offline;
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct mips_cdmm_bus mips_cdmm_boot_bus;
27462306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);
27562306a36Sopenharmony_cistatic atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/**
27862306a36Sopenharmony_ci * mips_cdmm_get_bus() - Get the per-CPU CDMM bus information.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Get information about the per-CPU CDMM bus, if the bus is present.
28162306a36Sopenharmony_ci *
28262306a36Sopenharmony_ci * The caller must prevent migration to another CPU, either by disabling
28362306a36Sopenharmony_ci * pre-emption or by running from a pinned kernel thread.
28462306a36Sopenharmony_ci *
28562306a36Sopenharmony_ci * Returns:	Pointer to CDMM bus information for the current CPU.
28662306a36Sopenharmony_ci *		May return ERR_PTR(-errno) in case of error, so check with
28762306a36Sopenharmony_ci *		IS_ERR().
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_cistatic struct mips_cdmm_bus *mips_cdmm_get_bus(void)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct mips_cdmm_bus *bus, **bus_p;
29262306a36Sopenharmony_ci	unsigned long flags;
29362306a36Sopenharmony_ci	unsigned int cpu;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!cpu_has_cdmm)
29662306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	cpu = smp_processor_id();
29962306a36Sopenharmony_ci	/* Avoid early use of per-cpu primitives before initialised */
30062306a36Sopenharmony_ci	if (cpu == 0)
30162306a36Sopenharmony_ci		return &mips_cdmm_boot_bus;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* Get bus pointer */
30462306a36Sopenharmony_ci	bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);
30562306a36Sopenharmony_ci	local_irq_save(flags);
30662306a36Sopenharmony_ci	bus = *bus_p;
30762306a36Sopenharmony_ci	/* Attempt allocation if NULL */
30862306a36Sopenharmony_ci	if (unlikely(!bus)) {
30962306a36Sopenharmony_ci		bus = kzalloc(sizeof(*bus), GFP_ATOMIC);
31062306a36Sopenharmony_ci		if (unlikely(!bus))
31162306a36Sopenharmony_ci			bus = ERR_PTR(-ENOMEM);
31262306a36Sopenharmony_ci		else
31362306a36Sopenharmony_ci			*bus_p = bus;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	local_irq_restore(flags);
31662306a36Sopenharmony_ci	return bus;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/**
32062306a36Sopenharmony_ci * mips_cdmm_cur_base() - Find current physical base address of CDMM region.
32162306a36Sopenharmony_ci *
32262306a36Sopenharmony_ci * Returns:	Physical base address of CDMM region according to cdmmbase CP0
32362306a36Sopenharmony_ci *		register, or 0 if the CDMM region is disabled.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cistatic phys_addr_t mips_cdmm_cur_base(void)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	unsigned long cdmmbase = read_c0_cdmmbase();
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (!(cdmmbase & MIPS_CDMMBASE_EN))
33062306a36Sopenharmony_ci		return 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)
33362306a36Sopenharmony_ci		<< MIPS_CDMMBASE_ADDR_START;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/**
33762306a36Sopenharmony_ci * mips_cdmm_phys_base() - Choose a physical base address for CDMM region.
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * Picking a suitable physical address at which to map the CDMM region is
34062306a36Sopenharmony_ci * platform specific, so this weak function can be overridden by platform
34162306a36Sopenharmony_ci * code to pick a suitable value if none is configured by the bootloader.
34262306a36Sopenharmony_ci * By default this method tries to find a CDMM-specific node in the system
34362306a36Sopenharmony_ci * dtb. Note that this won't work for early serial console.
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_ciphys_addr_t __weak mips_cdmm_phys_base(void)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct device_node *np;
34862306a36Sopenharmony_ci	struct resource res;
34962306a36Sopenharmony_ci	int err;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "mti,mips-cdmm");
35262306a36Sopenharmony_ci	if (np) {
35362306a36Sopenharmony_ci		err = of_address_to_resource(np, 0, &res);
35462306a36Sopenharmony_ci		of_node_put(np);
35562306a36Sopenharmony_ci		if (!err)
35662306a36Sopenharmony_ci			return res.start;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/**
36362306a36Sopenharmony_ci * mips_cdmm_setup() - Ensure the CDMM bus is initialised and usable.
36462306a36Sopenharmony_ci * @bus:	Pointer to bus information for current CPU.
36562306a36Sopenharmony_ci *		IS_ERR(bus) is checked, so no need for caller to check.
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * The caller must prevent migration to another CPU, either by disabling
36862306a36Sopenharmony_ci * pre-emption or by running from a pinned kernel thread.
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * Returns	0 on success, -errno on failure.
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic int mips_cdmm_setup(struct mips_cdmm_bus *bus)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	unsigned long cdmmbase, flags;
37562306a36Sopenharmony_ci	int ret = 0;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (IS_ERR(bus))
37862306a36Sopenharmony_ci		return PTR_ERR(bus);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	local_irq_save(flags);
38162306a36Sopenharmony_ci	/* Don't set up bus a second time unless marked offline */
38262306a36Sopenharmony_ci	if (bus->offline) {
38362306a36Sopenharmony_ci		/* If CDMM region is still set up, nothing to do */
38462306a36Sopenharmony_ci		if (bus->phys == mips_cdmm_cur_base())
38562306a36Sopenharmony_ci			goto out;
38662306a36Sopenharmony_ci		/*
38762306a36Sopenharmony_ci		 * The CDMM region isn't set up as expected, so it needs
38862306a36Sopenharmony_ci		 * reconfiguring, but then we can stop checking it.
38962306a36Sopenharmony_ci		 */
39062306a36Sopenharmony_ci		bus->offline = false;
39162306a36Sopenharmony_ci	} else if (bus->phys > 1) {
39262306a36Sopenharmony_ci		goto out;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* If the CDMM region is already configured, inherit that setup */
39662306a36Sopenharmony_ci	if (!bus->phys)
39762306a36Sopenharmony_ci		bus->phys = mips_cdmm_cur_base();
39862306a36Sopenharmony_ci	/* Otherwise, ask platform code for suggestions */
39962306a36Sopenharmony_ci	if (!bus->phys)
40062306a36Sopenharmony_ci		bus->phys = mips_cdmm_phys_base();
40162306a36Sopenharmony_ci	/* Otherwise, copy what other CPUs have done */
40262306a36Sopenharmony_ci	if (!bus->phys)
40362306a36Sopenharmony_ci		bus->phys = mips_cdmm_default_base;
40462306a36Sopenharmony_ci	/* Otherwise, complain once */
40562306a36Sopenharmony_ci	if (!bus->phys) {
40662306a36Sopenharmony_ci		bus->phys = 1;
40762306a36Sopenharmony_ci		/*
40862306a36Sopenharmony_ci		 * If you hit this, either your bootloader needs to set up the
40962306a36Sopenharmony_ci		 * CDMM on the boot CPU, or else you need to implement
41062306a36Sopenharmony_ci		 * mips_cdmm_phys_base() for your platform (see asm/cdmm.h).
41162306a36Sopenharmony_ci		 */
41262306a36Sopenharmony_ci		pr_err("cdmm%u: Failed to choose a physical base\n",
41362306a36Sopenharmony_ci		       smp_processor_id());
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci	/* Already complained? */
41662306a36Sopenharmony_ci	if (bus->phys == 1) {
41762306a36Sopenharmony_ci		ret = -ENOMEM;
41862306a36Sopenharmony_ci		goto out;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci	/* Record our success for other CPUs to copy */
42162306a36Sopenharmony_ci	mips_cdmm_default_base = bus->phys;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	pr_debug("cdmm%u: Enabling CDMM region at %pa\n",
42462306a36Sopenharmony_ci		 smp_processor_id(), &bus->phys);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* Enable CDMM */
42762306a36Sopenharmony_ci	cdmmbase = read_c0_cdmmbase();
42862306a36Sopenharmony_ci	cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;
42962306a36Sopenharmony_ci	cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)
43062306a36Sopenharmony_ci			<< MIPS_CDMMBASE_ADDR_SHIFT;
43162306a36Sopenharmony_ci	cdmmbase |= MIPS_CDMMBASE_EN;
43262306a36Sopenharmony_ci	write_c0_cdmmbase(cdmmbase);
43362306a36Sopenharmony_ci	tlbw_use_hazard();
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);
43662306a36Sopenharmony_ci	bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>
43762306a36Sopenharmony_ci			 MIPS_CDMMBASE_SIZE_SHIFT);
43862306a36Sopenharmony_ci	bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciout:
44162306a36Sopenharmony_ci	local_irq_restore(flags);
44262306a36Sopenharmony_ci	return ret;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci/**
44662306a36Sopenharmony_ci * mips_cdmm_early_probe() - Minimally probe for a specific device on CDMM.
44762306a36Sopenharmony_ci * @dev_type:	CDMM type code to look for.
44862306a36Sopenharmony_ci *
44962306a36Sopenharmony_ci * Minimally configure the in-CPU Common Device Memory Map (CDMM) and look for a
45062306a36Sopenharmony_ci * specific device. This can be used to find a device very early in boot for
45162306a36Sopenharmony_ci * example to configure an early FDC console device.
45262306a36Sopenharmony_ci *
45362306a36Sopenharmony_ci * The caller must prevent migration to another CPU, either by disabling
45462306a36Sopenharmony_ci * pre-emption or by running from a pinned kernel thread.
45562306a36Sopenharmony_ci *
45662306a36Sopenharmony_ci * Returns:	MMIO pointer to device memory. The caller can read the ACSR
45762306a36Sopenharmony_ci *		register to find more information about the device (such as the
45862306a36Sopenharmony_ci *		version number or the number of blocks).
45962306a36Sopenharmony_ci *		May return IOMEM_ERR_PTR(-errno) in case of error, so check with
46062306a36Sopenharmony_ci *		IS_ERR().
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_civoid __iomem *mips_cdmm_early_probe(unsigned int dev_type)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct mips_cdmm_bus *bus;
46562306a36Sopenharmony_ci	void __iomem *cdmm;
46662306a36Sopenharmony_ci	u32 acsr;
46762306a36Sopenharmony_ci	unsigned int drb, type, size;
46862306a36Sopenharmony_ci	int err;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (WARN_ON(!dev_type))
47162306a36Sopenharmony_ci		return IOMEM_ERR_PTR(-ENODEV);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	bus = mips_cdmm_get_bus();
47462306a36Sopenharmony_ci	err = mips_cdmm_setup(bus);
47562306a36Sopenharmony_ci	if (err)
47662306a36Sopenharmony_ci		return IOMEM_ERR_PTR(err);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* Skip the first block if it's reserved for more registers */
47962306a36Sopenharmony_ci	drb = bus->drbs_reserved;
48062306a36Sopenharmony_ci	cdmm = bus->regs;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* Look for a specific device type */
48362306a36Sopenharmony_ci	for (; drb < bus->drbs; drb += size + 1) {
48462306a36Sopenharmony_ci		acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
48562306a36Sopenharmony_ci		type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
48662306a36Sopenharmony_ci		if (type == dev_type)
48762306a36Sopenharmony_ci			return cdmm + drb * CDMM_DRB_SIZE;
48862306a36Sopenharmony_ci		size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	return IOMEM_ERR_PTR(-ENODEV);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_early_probe);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * mips_cdmm_release() - Release a removed CDMM device.
49762306a36Sopenharmony_ci * @dev:	Device object
49862306a36Sopenharmony_ci *
49962306a36Sopenharmony_ci * Clean up the struct mips_cdmm_device for an unused CDMM device. This is
50062306a36Sopenharmony_ci * called automatically by the driver core when a device is removed.
50162306a36Sopenharmony_ci */
50262306a36Sopenharmony_cistatic void mips_cdmm_release(struct device *dev)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	kfree(cdev);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci/**
51062306a36Sopenharmony_ci * mips_cdmm_bus_discover() - Discover the devices on the CDMM bus.
51162306a36Sopenharmony_ci * @bus:	CDMM bus information, must already be set up.
51262306a36Sopenharmony_ci */
51362306a36Sopenharmony_cistatic void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	void __iomem *cdmm;
51662306a36Sopenharmony_ci	u32 acsr;
51762306a36Sopenharmony_ci	unsigned int drb, type, size, rev;
51862306a36Sopenharmony_ci	struct mips_cdmm_device *dev;
51962306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
52062306a36Sopenharmony_ci	int ret = 0;
52162306a36Sopenharmony_ci	int id = 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Skip the first block if it's reserved for more registers */
52462306a36Sopenharmony_ci	drb = bus->drbs_reserved;
52562306a36Sopenharmony_ci	cdmm = bus->regs;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Discover devices */
52862306a36Sopenharmony_ci	bus->discovered = true;
52962306a36Sopenharmony_ci	pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
53062306a36Sopenharmony_ci	for (; drb < bus->drbs; drb += size + 1) {
53162306a36Sopenharmony_ci		acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
53262306a36Sopenharmony_ci		type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
53362306a36Sopenharmony_ci		size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
53462306a36Sopenharmony_ci		rev  = (acsr & CDMM_ACSR_DEVREV)  >> CDMM_ACSR_DEVREV_SHIFT;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (!type)
53762306a36Sopenharmony_ci			continue;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",
54062306a36Sopenharmony_ci			cpu, id, drb, drb * CDMM_DRB_SIZE,
54162306a36Sopenharmony_ci			(drb + size + 1) * CDMM_DRB_SIZE - 1,
54262306a36Sopenharmony_ci			type, rev);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
54562306a36Sopenharmony_ci		if (!dev)
54662306a36Sopenharmony_ci			break;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		dev->cpu = cpu;
54962306a36Sopenharmony_ci		dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;
55062306a36Sopenharmony_ci		dev->res.end = bus->phys +
55162306a36Sopenharmony_ci				(drb + size + 1) * CDMM_DRB_SIZE - 1;
55262306a36Sopenharmony_ci		dev->res.flags = IORESOURCE_MEM;
55362306a36Sopenharmony_ci		dev->type = type;
55462306a36Sopenharmony_ci		dev->rev = rev;
55562306a36Sopenharmony_ci		dev->dev.parent = get_cpu_device(cpu);
55662306a36Sopenharmony_ci		dev->dev.bus = &mips_cdmm_bustype;
55762306a36Sopenharmony_ci		dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);
55862306a36Sopenharmony_ci		dev->dev.release = mips_cdmm_release;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);
56162306a36Sopenharmony_ci		++id;
56262306a36Sopenharmony_ci		ret = device_register(&dev->dev);
56362306a36Sopenharmony_ci		if (ret)
56462306a36Sopenharmony_ci			put_device(&dev->dev);
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/*
57062306a36Sopenharmony_ci * CPU hotplug and initialisation
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * All the CDMM driver callbacks need to be executed on the appropriate CPU from
57362306a36Sopenharmony_ci * workqueues. For the CPU callbacks, they need to be called for all devices on
57462306a36Sopenharmony_ci * that CPU, so the work function calls bus_for_each_dev, using a helper
57562306a36Sopenharmony_ci * (generated with BUILD_PERDEV_HELPER) to call the driver callback if the
57662306a36Sopenharmony_ci * device's CPU matches.
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/**
58062306a36Sopenharmony_ci * BUILD_PERDEV_HELPER() - Helper to call a CDMM driver callback if CPU matches.
58162306a36Sopenharmony_ci * @_name:	Name of CDMM driver callback function.
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * Generates a bus_for_each_dev callback function to call a specific CDMM driver
58462306a36Sopenharmony_ci * callback function for the device if the device's CPU matches that pointed to
58562306a36Sopenharmony_ci * by the data argument.
58662306a36Sopenharmony_ci *
58762306a36Sopenharmony_ci * This is used for informing drivers for all devices on a given CPU of some
58862306a36Sopenharmony_ci * event (such as the CPU going online/offline).
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * It is expected to already be called from the appropriate CPU.
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_ci#define BUILD_PERDEV_HELPER(_name)					\
59362306a36Sopenharmony_cistatic int mips_cdmm_##_name##_helper(struct device *dev, void *data)	\
59462306a36Sopenharmony_ci{									\
59562306a36Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);	\
59662306a36Sopenharmony_ci	struct mips_cdmm_driver *cdrv;					\
59762306a36Sopenharmony_ci	unsigned int cpu = *(unsigned int *)data;			\
59862306a36Sopenharmony_ci									\
59962306a36Sopenharmony_ci	if (cdev->cpu != cpu || !dev->driver)				\
60062306a36Sopenharmony_ci		return 0;						\
60162306a36Sopenharmony_ci									\
60262306a36Sopenharmony_ci	cdrv = to_mips_cdmm_driver(dev->driver);			\
60362306a36Sopenharmony_ci	if (!cdrv->_name)						\
60462306a36Sopenharmony_ci		return 0;						\
60562306a36Sopenharmony_ci	return cdrv->_name(cdev);					\
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci/* bus_for_each_dev callback helper functions */
60962306a36Sopenharmony_ciBUILD_PERDEV_HELPER(cpu_down)       /* int mips_cdmm_cpu_down_helper(...) */
61062306a36Sopenharmony_ciBUILD_PERDEV_HELPER(cpu_up)         /* int mips_cdmm_cpu_up_helper(...) */
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/**
61362306a36Sopenharmony_ci * mips_cdmm_cpu_down_prep() - Callback for CPUHP DOWN_PREP:
61462306a36Sopenharmony_ci *			       Tear down the CDMM bus.
61562306a36Sopenharmony_ci * @cpu:	unsigned int CPU number.
61662306a36Sopenharmony_ci *
61762306a36Sopenharmony_ci * This function is executed on the hotplugged CPU and calls the CDMM
61862306a36Sopenharmony_ci * driver cpu_down callback for all devices on that CPU.
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_cistatic int mips_cdmm_cpu_down_prep(unsigned int cpu)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct mips_cdmm_bus *bus;
62362306a36Sopenharmony_ci	long ret;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Inform all the devices on the bus */
62662306a36Sopenharmony_ci	ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
62762306a36Sopenharmony_ci			       mips_cdmm_cpu_down_helper);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/*
63062306a36Sopenharmony_ci	 * While bus is offline, each use of it should reconfigure it just in
63162306a36Sopenharmony_ci	 * case it is first use when coming back online again.
63262306a36Sopenharmony_ci	 */
63362306a36Sopenharmony_ci	bus = mips_cdmm_get_bus();
63462306a36Sopenharmony_ci	if (!IS_ERR(bus))
63562306a36Sopenharmony_ci		bus->offline = true;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return ret;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/**
64162306a36Sopenharmony_ci * mips_cdmm_cpu_online() - Callback for CPUHP ONLINE: Bring up the CDMM bus.
64262306a36Sopenharmony_ci * @cpu:	unsigned int CPU number.
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * This work_on_cpu callback function is executed on a given CPU to discover
64562306a36Sopenharmony_ci * CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all
64662306a36Sopenharmony_ci * devices already discovered on that CPU.
64762306a36Sopenharmony_ci *
64862306a36Sopenharmony_ci * It is used as work_on_cpu callback function during
64962306a36Sopenharmony_ci * initialisation. When CPUs are brought online the function is
65062306a36Sopenharmony_ci * invoked directly on the hotplugged CPU.
65162306a36Sopenharmony_ci */
65262306a36Sopenharmony_cistatic int mips_cdmm_cpu_online(unsigned int cpu)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct mips_cdmm_bus *bus;
65562306a36Sopenharmony_ci	long ret;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	bus = mips_cdmm_get_bus();
65862306a36Sopenharmony_ci	ret = mips_cdmm_setup(bus);
65962306a36Sopenharmony_ci	if (ret)
66062306a36Sopenharmony_ci		return ret;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/* Bus now set up, so we can drop the offline flag if still set */
66362306a36Sopenharmony_ci	bus->offline = false;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (!bus->discovered)
66662306a36Sopenharmony_ci		mips_cdmm_bus_discover(bus);
66762306a36Sopenharmony_ci	else
66862306a36Sopenharmony_ci		/* Inform all the devices on the bus */
66962306a36Sopenharmony_ci		ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
67062306a36Sopenharmony_ci				       mips_cdmm_cpu_up_helper);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return ret;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci/**
67662306a36Sopenharmony_ci * mips_cdmm_init() - Initialise CDMM bus.
67762306a36Sopenharmony_ci *
67862306a36Sopenharmony_ci * Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for
67962306a36Sopenharmony_ci * hotplug notifications so the CDMM drivers can be kept up to date.
68062306a36Sopenharmony_ci */
68162306a36Sopenharmony_cistatic int __init mips_cdmm_init(void)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	int ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* Register the bus */
68662306a36Sopenharmony_ci	ret = bus_register(&mips_cdmm_bustype);
68762306a36Sopenharmony_ci	if (ret)
68862306a36Sopenharmony_ci		return ret;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* We want to be notified about new CPUs */
69162306a36Sopenharmony_ci	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",
69262306a36Sopenharmony_ci				mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);
69362306a36Sopenharmony_ci	if (ret < 0)
69462306a36Sopenharmony_ci		pr_warn("cdmm: Failed to register CPU notifier\n");
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return ret;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_cisubsys_initcall(mips_cdmm_init);
699