18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Bus driver for MIPS Common Device Memory Map (CDMM).
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Imagination Technologies Ltd.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
78c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
88c2ecf20Sopenharmony_ci * for more details.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/atomic.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/cpu.h>
148c2ecf20Sopenharmony_ci#include <linux/cpumask.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/of_address.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/smp.h>
218c2ecf20Sopenharmony_ci#include <asm/cdmm.h>
228c2ecf20Sopenharmony_ci#include <asm/hazards.h>
238c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Access control and status register fields */
268c2ecf20Sopenharmony_ci#define CDMM_ACSR_DEVTYPE_SHIFT	24
278c2ecf20Sopenharmony_ci#define CDMM_ACSR_DEVTYPE	(255ul << CDMM_ACSR_DEVTYPE_SHIFT)
288c2ecf20Sopenharmony_ci#define CDMM_ACSR_DEVSIZE_SHIFT	16
298c2ecf20Sopenharmony_ci#define CDMM_ACSR_DEVSIZE	(31ul << CDMM_ACSR_DEVSIZE_SHIFT)
308c2ecf20Sopenharmony_ci#define CDMM_ACSR_DEVREV_SHIFT	12
318c2ecf20Sopenharmony_ci#define CDMM_ACSR_DEVREV	(15ul << CDMM_ACSR_DEVREV_SHIFT)
328c2ecf20Sopenharmony_ci#define CDMM_ACSR_UW		(1ul << 3)
338c2ecf20Sopenharmony_ci#define CDMM_ACSR_UR		(1ul << 2)
348c2ecf20Sopenharmony_ci#define CDMM_ACSR_SW		(1ul << 1)
358c2ecf20Sopenharmony_ci#define CDMM_ACSR_SR		(1ul << 0)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Each block of device registers is 64 bytes */
388c2ecf20Sopenharmony_ci#define CDMM_DRB_SIZE		64
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define to_mips_cdmm_driver(d)	container_of(d, struct mips_cdmm_driver, drv)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Default physical base address */
438c2ecf20Sopenharmony_cistatic phys_addr_t mips_cdmm_default_base;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Bus operations */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const struct mips_cdmm_device_id *
488c2ecf20Sopenharmony_cimips_cdmm_lookup(const struct mips_cdmm_device_id *table,
498c2ecf20Sopenharmony_ci		 struct mips_cdmm_device *dev)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int ret = 0;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	for (; table->type; ++table) {
548c2ecf20Sopenharmony_ci		ret = (dev->type == table->type);
558c2ecf20Sopenharmony_ci		if (ret)
568c2ecf20Sopenharmony_ci			break;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return ret ? table : NULL;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int mips_cdmm_match(struct device *dev, struct device_driver *drv)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
658c2ecf20Sopenharmony_ci	struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
738c2ecf20Sopenharmony_ci	int retval = 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
768c2ecf20Sopenharmony_ci	if (retval)
778c2ecf20Sopenharmony_ci		return retval;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);
808c2ecf20Sopenharmony_ci	if (retval)
818c2ecf20Sopenharmony_ci		return retval;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);
848c2ecf20Sopenharmony_ci	if (retval)
858c2ecf20Sopenharmony_ci		return retval;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);
888c2ecf20Sopenharmony_ci	return retval;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* Device attributes */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define CDMM_ATTR(name, fmt, arg...)					\
948c2ecf20Sopenharmony_cistatic ssize_t name##_show(struct device *_dev,				\
958c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)	\
968c2ecf20Sopenharmony_ci{									\
978c2ecf20Sopenharmony_ci	struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev);	\
988c2ecf20Sopenharmony_ci	return sprintf(buf, fmt, arg);					\
998c2ecf20Sopenharmony_ci}									\
1008c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciCDMM_ATTR(cpu, "%u\n", dev->cpu);
1038c2ecf20Sopenharmony_ciCDMM_ATTR(type, "0x%02x\n", dev->type);
1048c2ecf20Sopenharmony_ciCDMM_ATTR(revision, "%u\n", dev->rev);
1058c2ecf20Sopenharmony_ciCDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);
1068c2ecf20Sopenharmony_ciCDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",
1078c2ecf20Sopenharmony_ci	  (unsigned long long)dev->res.start,
1088c2ecf20Sopenharmony_ci	  (unsigned long long)dev->res.end,
1098c2ecf20Sopenharmony_ci	  dev->res.flags);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic struct attribute *mips_cdmm_dev_attrs[] = {
1128c2ecf20Sopenharmony_ci	&dev_attr_cpu.attr,
1138c2ecf20Sopenharmony_ci	&dev_attr_type.attr,
1148c2ecf20Sopenharmony_ci	&dev_attr_revision.attr,
1158c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
1168c2ecf20Sopenharmony_ci	&dev_attr_resource.attr,
1178c2ecf20Sopenharmony_ci	NULL,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(mips_cdmm_dev);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistruct bus_type mips_cdmm_bustype = {
1228c2ecf20Sopenharmony_ci	.name		= "cdmm",
1238c2ecf20Sopenharmony_ci	.dev_groups	= mips_cdmm_dev_groups,
1248c2ecf20Sopenharmony_ci	.match		= mips_cdmm_match,
1258c2ecf20Sopenharmony_ci	.uevent		= mips_cdmm_uevent,
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_bustype);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * Standard driver callback helpers.
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * All the CDMM driver callbacks need to be executed on the appropriate CPU from
1338c2ecf20Sopenharmony_ci * workqueues. For the standard driver callbacks we need a work function
1348c2ecf20Sopenharmony_ci * (mips_cdmm_{void,int}_work()) to do the actual call from the right CPU, and a
1358c2ecf20Sopenharmony_ci * wrapper function (generated with BUILD_PERCPU_HELPER) to arrange for the work
1368c2ecf20Sopenharmony_ci * function to be called on that CPU.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/**
1408c2ecf20Sopenharmony_ci * struct mips_cdmm_work_dev - Data for per-device call work.
1418c2ecf20Sopenharmony_ci * @fn:		CDMM driver callback function to call for the device.
1428c2ecf20Sopenharmony_ci * @dev:	CDMM device to pass to @fn.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistruct mips_cdmm_work_dev {
1458c2ecf20Sopenharmony_ci	void			*fn;
1468c2ecf20Sopenharmony_ci	struct mips_cdmm_device *dev;
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/**
1508c2ecf20Sopenharmony_ci * mips_cdmm_void_work() - Call a void returning CDMM driver callback.
1518c2ecf20Sopenharmony_ci * @data:	struct mips_cdmm_work_dev pointer.
1528c2ecf20Sopenharmony_ci *
1538c2ecf20Sopenharmony_ci * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
1548c2ecf20Sopenharmony_ci * function which doesn't return a value.
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic long mips_cdmm_void_work(void *data)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct mips_cdmm_work_dev *work = data;
1598c2ecf20Sopenharmony_ci	void (*fn)(struct mips_cdmm_device *) = work->fn;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	fn(work->dev);
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * mips_cdmm_int_work() - Call an int returning CDMM driver callback.
1678c2ecf20Sopenharmony_ci * @data:	struct mips_cdmm_work_dev pointer.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
1708c2ecf20Sopenharmony_ci * function which returns an int.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic long mips_cdmm_int_work(void *data)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct mips_cdmm_work_dev *work = data;
1758c2ecf20Sopenharmony_ci	int (*fn)(struct mips_cdmm_device *) = work->fn;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return fn(work->dev);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci#define _BUILD_RET_void
1818c2ecf20Sopenharmony_ci#define _BUILD_RET_int	return
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/**
1848c2ecf20Sopenharmony_ci * BUILD_PERCPU_HELPER() - Helper to call a CDMM driver callback on right CPU.
1858c2ecf20Sopenharmony_ci * @_ret:	Return type (void or int).
1868c2ecf20Sopenharmony_ci * @_name:	Name of CDMM driver callback function.
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * Generates a specific device callback function to call a CDMM driver callback
1898c2ecf20Sopenharmony_ci * function on the appropriate CPU for the device, and if applicable return the
1908c2ecf20Sopenharmony_ci * result.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_ci#define BUILD_PERCPU_HELPER(_ret, _name)				\
1938c2ecf20Sopenharmony_cistatic _ret mips_cdmm_##_name(struct device *dev)			\
1948c2ecf20Sopenharmony_ci{									\
1958c2ecf20Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);	\
1968c2ecf20Sopenharmony_ci	struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \
1978c2ecf20Sopenharmony_ci	struct mips_cdmm_work_dev work = {				\
1988c2ecf20Sopenharmony_ci		.fn	= cdrv->_name,					\
1998c2ecf20Sopenharmony_ci		.dev	= cdev,						\
2008c2ecf20Sopenharmony_ci	};								\
2018c2ecf20Sopenharmony_ci									\
2028c2ecf20Sopenharmony_ci	_BUILD_RET_##_ret work_on_cpu(cdev->cpu,			\
2038c2ecf20Sopenharmony_ci				      mips_cdmm_##_ret##_work, &work);	\
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* Driver callback functions */
2078c2ecf20Sopenharmony_ciBUILD_PERCPU_HELPER(int, probe)     /* int mips_cdmm_probe(struct device) */
2088c2ecf20Sopenharmony_ciBUILD_PERCPU_HELPER(int, remove)    /* int mips_cdmm_remove(struct device) */
2098c2ecf20Sopenharmony_ciBUILD_PERCPU_HELPER(void, shutdown) /* void mips_cdmm_shutdown(struct device) */
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/* Driver registration */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/**
2158c2ecf20Sopenharmony_ci * mips_cdmm_driver_register() - Register a CDMM driver.
2168c2ecf20Sopenharmony_ci * @drv:	CDMM driver information.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * Register a CDMM driver with the CDMM subsystem. The driver will be informed
2198c2ecf20Sopenharmony_ci * of matching devices which are discovered.
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * Returns:	0 on success.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_ciint mips_cdmm_driver_register(struct mips_cdmm_driver *drv)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	drv->drv.bus = &mips_cdmm_bustype;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (drv->probe)
2288c2ecf20Sopenharmony_ci		drv->drv.probe = mips_cdmm_probe;
2298c2ecf20Sopenharmony_ci	if (drv->remove)
2308c2ecf20Sopenharmony_ci		drv->drv.remove = mips_cdmm_remove;
2318c2ecf20Sopenharmony_ci	if (drv->shutdown)
2328c2ecf20Sopenharmony_ci		drv->drv.shutdown = mips_cdmm_shutdown;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return driver_register(&drv->drv);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_driver_register);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/**
2398c2ecf20Sopenharmony_ci * mips_cdmm_driver_unregister() - Unregister a CDMM driver.
2408c2ecf20Sopenharmony_ci * @drv:	CDMM driver information.
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * Unregister a CDMM driver from the CDMM subsystem.
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_civoid mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	driver_unregister(&drv->drv);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* CDMM initialisation and bus discovery */
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/**
2548c2ecf20Sopenharmony_ci * struct mips_cdmm_bus - Info about CDMM bus.
2558c2ecf20Sopenharmony_ci * @phys:		Physical address at which it is mapped.
2568c2ecf20Sopenharmony_ci * @regs:		Virtual address where registers can be accessed.
2578c2ecf20Sopenharmony_ci * @drbs:		Total number of DRBs.
2588c2ecf20Sopenharmony_ci * @drbs_reserved:	Number of DRBs reserved.
2598c2ecf20Sopenharmony_ci * @discovered:		Whether the devices on the bus have been discovered yet.
2608c2ecf20Sopenharmony_ci * @offline:		Whether the CDMM bus is going offline (or very early
2618c2ecf20Sopenharmony_ci *			coming back online), in which case it should be
2628c2ecf20Sopenharmony_ci *			reconfigured each time.
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistruct mips_cdmm_bus {
2658c2ecf20Sopenharmony_ci	phys_addr_t	 phys;
2668c2ecf20Sopenharmony_ci	void __iomem	*regs;
2678c2ecf20Sopenharmony_ci	unsigned int	 drbs;
2688c2ecf20Sopenharmony_ci	unsigned int	 drbs_reserved;
2698c2ecf20Sopenharmony_ci	bool		 discovered;
2708c2ecf20Sopenharmony_ci	bool		 offline;
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic struct mips_cdmm_bus mips_cdmm_boot_bus;
2748c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);
2758c2ecf20Sopenharmony_cistatic atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/**
2788c2ecf20Sopenharmony_ci * mips_cdmm_get_bus() - Get the per-CPU CDMM bus information.
2798c2ecf20Sopenharmony_ci *
2808c2ecf20Sopenharmony_ci * Get information about the per-CPU CDMM bus, if the bus is present.
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci * The caller must prevent migration to another CPU, either by disabling
2838c2ecf20Sopenharmony_ci * pre-emption or by running from a pinned kernel thread.
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * Returns:	Pointer to CDMM bus information for the current CPU.
2868c2ecf20Sopenharmony_ci *		May return ERR_PTR(-errno) in case of error, so check with
2878c2ecf20Sopenharmony_ci *		IS_ERR().
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic struct mips_cdmm_bus *mips_cdmm_get_bus(void)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct mips_cdmm_bus *bus, **bus_p;
2928c2ecf20Sopenharmony_ci	unsigned long flags;
2938c2ecf20Sopenharmony_ci	unsigned int cpu;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (!cpu_has_cdmm)
2968c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
2998c2ecf20Sopenharmony_ci	/* Avoid early use of per-cpu primitives before initialised */
3008c2ecf20Sopenharmony_ci	if (cpu == 0)
3018c2ecf20Sopenharmony_ci		return &mips_cdmm_boot_bus;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* Get bus pointer */
3048c2ecf20Sopenharmony_ci	bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);
3058c2ecf20Sopenharmony_ci	local_irq_save(flags);
3068c2ecf20Sopenharmony_ci	bus = *bus_p;
3078c2ecf20Sopenharmony_ci	/* Attempt allocation if NULL */
3088c2ecf20Sopenharmony_ci	if (unlikely(!bus)) {
3098c2ecf20Sopenharmony_ci		bus = kzalloc(sizeof(*bus), GFP_ATOMIC);
3108c2ecf20Sopenharmony_ci		if (unlikely(!bus))
3118c2ecf20Sopenharmony_ci			bus = ERR_PTR(-ENOMEM);
3128c2ecf20Sopenharmony_ci		else
3138c2ecf20Sopenharmony_ci			*bus_p = bus;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	local_irq_restore(flags);
3168c2ecf20Sopenharmony_ci	return bus;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/**
3208c2ecf20Sopenharmony_ci * mips_cdmm_cur_base() - Find current physical base address of CDMM region.
3218c2ecf20Sopenharmony_ci *
3228c2ecf20Sopenharmony_ci * Returns:	Physical base address of CDMM region according to cdmmbase CP0
3238c2ecf20Sopenharmony_ci *		register, or 0 if the CDMM region is disabled.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic phys_addr_t mips_cdmm_cur_base(void)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	unsigned long cdmmbase = read_c0_cdmmbase();
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!(cdmmbase & MIPS_CDMMBASE_EN))
3308c2ecf20Sopenharmony_ci		return 0;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)
3338c2ecf20Sopenharmony_ci		<< MIPS_CDMMBASE_ADDR_START;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/**
3378c2ecf20Sopenharmony_ci * mips_cdmm_phys_base() - Choose a physical base address for CDMM region.
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * Picking a suitable physical address at which to map the CDMM region is
3408c2ecf20Sopenharmony_ci * platform specific, so this weak function can be overridden by platform
3418c2ecf20Sopenharmony_ci * code to pick a suitable value if none is configured by the bootloader.
3428c2ecf20Sopenharmony_ci * By default this method tries to find a CDMM-specific node in the system
3438c2ecf20Sopenharmony_ci * dtb. Note that this won't work for early serial console.
3448c2ecf20Sopenharmony_ci */
3458c2ecf20Sopenharmony_ciphys_addr_t __weak mips_cdmm_phys_base(void)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct device_node *np;
3488c2ecf20Sopenharmony_ci	struct resource res;
3498c2ecf20Sopenharmony_ci	int err;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "mti,mips-cdmm");
3528c2ecf20Sopenharmony_ci	if (np) {
3538c2ecf20Sopenharmony_ci		err = of_address_to_resource(np, 0, &res);
3548c2ecf20Sopenharmony_ci		of_node_put(np);
3558c2ecf20Sopenharmony_ci		if (!err)
3568c2ecf20Sopenharmony_ci			return res.start;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return 0;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/**
3638c2ecf20Sopenharmony_ci * mips_cdmm_setup() - Ensure the CDMM bus is initialised and usable.
3648c2ecf20Sopenharmony_ci * @bus:	Pointer to bus information for current CPU.
3658c2ecf20Sopenharmony_ci *		IS_ERR(bus) is checked, so no need for caller to check.
3668c2ecf20Sopenharmony_ci *
3678c2ecf20Sopenharmony_ci * The caller must prevent migration to another CPU, either by disabling
3688c2ecf20Sopenharmony_ci * pre-emption or by running from a pinned kernel thread.
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * Returns	0 on success, -errno on failure.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_cistatic int mips_cdmm_setup(struct mips_cdmm_bus *bus)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	unsigned long cdmmbase, flags;
3758c2ecf20Sopenharmony_ci	int ret = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (IS_ERR(bus))
3788c2ecf20Sopenharmony_ci		return PTR_ERR(bus);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	local_irq_save(flags);
3818c2ecf20Sopenharmony_ci	/* Don't set up bus a second time unless marked offline */
3828c2ecf20Sopenharmony_ci	if (bus->offline) {
3838c2ecf20Sopenharmony_ci		/* If CDMM region is still set up, nothing to do */
3848c2ecf20Sopenharmony_ci		if (bus->phys == mips_cdmm_cur_base())
3858c2ecf20Sopenharmony_ci			goto out;
3868c2ecf20Sopenharmony_ci		/*
3878c2ecf20Sopenharmony_ci		 * The CDMM region isn't set up as expected, so it needs
3888c2ecf20Sopenharmony_ci		 * reconfiguring, but then we can stop checking it.
3898c2ecf20Sopenharmony_ci		 */
3908c2ecf20Sopenharmony_ci		bus->offline = false;
3918c2ecf20Sopenharmony_ci	} else if (bus->phys > 1) {
3928c2ecf20Sopenharmony_ci		goto out;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* If the CDMM region is already configured, inherit that setup */
3968c2ecf20Sopenharmony_ci	if (!bus->phys)
3978c2ecf20Sopenharmony_ci		bus->phys = mips_cdmm_cur_base();
3988c2ecf20Sopenharmony_ci	/* Otherwise, ask platform code for suggestions */
3998c2ecf20Sopenharmony_ci	if (!bus->phys)
4008c2ecf20Sopenharmony_ci		bus->phys = mips_cdmm_phys_base();
4018c2ecf20Sopenharmony_ci	/* Otherwise, copy what other CPUs have done */
4028c2ecf20Sopenharmony_ci	if (!bus->phys)
4038c2ecf20Sopenharmony_ci		bus->phys = mips_cdmm_default_base;
4048c2ecf20Sopenharmony_ci	/* Otherwise, complain once */
4058c2ecf20Sopenharmony_ci	if (!bus->phys) {
4068c2ecf20Sopenharmony_ci		bus->phys = 1;
4078c2ecf20Sopenharmony_ci		/*
4088c2ecf20Sopenharmony_ci		 * If you hit this, either your bootloader needs to set up the
4098c2ecf20Sopenharmony_ci		 * CDMM on the boot CPU, or else you need to implement
4108c2ecf20Sopenharmony_ci		 * mips_cdmm_phys_base() for your platform (see asm/cdmm.h).
4118c2ecf20Sopenharmony_ci		 */
4128c2ecf20Sopenharmony_ci		pr_err("cdmm%u: Failed to choose a physical base\n",
4138c2ecf20Sopenharmony_ci		       smp_processor_id());
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci	/* Already complained? */
4168c2ecf20Sopenharmony_ci	if (bus->phys == 1) {
4178c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4188c2ecf20Sopenharmony_ci		goto out;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci	/* Record our success for other CPUs to copy */
4218c2ecf20Sopenharmony_ci	mips_cdmm_default_base = bus->phys;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	pr_debug("cdmm%u: Enabling CDMM region at %pa\n",
4248c2ecf20Sopenharmony_ci		 smp_processor_id(), &bus->phys);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* Enable CDMM */
4278c2ecf20Sopenharmony_ci	cdmmbase = read_c0_cdmmbase();
4288c2ecf20Sopenharmony_ci	cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;
4298c2ecf20Sopenharmony_ci	cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)
4308c2ecf20Sopenharmony_ci			<< MIPS_CDMMBASE_ADDR_SHIFT;
4318c2ecf20Sopenharmony_ci	cdmmbase |= MIPS_CDMMBASE_EN;
4328c2ecf20Sopenharmony_ci	write_c0_cdmmbase(cdmmbase);
4338c2ecf20Sopenharmony_ci	tlbw_use_hazard();
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);
4368c2ecf20Sopenharmony_ci	bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>
4378c2ecf20Sopenharmony_ci			 MIPS_CDMMBASE_SIZE_SHIFT);
4388c2ecf20Sopenharmony_ci	bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ciout:
4418c2ecf20Sopenharmony_ci	local_irq_restore(flags);
4428c2ecf20Sopenharmony_ci	return ret;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/**
4468c2ecf20Sopenharmony_ci * mips_cdmm_early_probe() - Minimally probe for a specific device on CDMM.
4478c2ecf20Sopenharmony_ci * @dev_type:	CDMM type code to look for.
4488c2ecf20Sopenharmony_ci *
4498c2ecf20Sopenharmony_ci * Minimally configure the in-CPU Common Device Memory Map (CDMM) and look for a
4508c2ecf20Sopenharmony_ci * specific device. This can be used to find a device very early in boot for
4518c2ecf20Sopenharmony_ci * example to configure an early FDC console device.
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * The caller must prevent migration to another CPU, either by disabling
4548c2ecf20Sopenharmony_ci * pre-emption or by running from a pinned kernel thread.
4558c2ecf20Sopenharmony_ci *
4568c2ecf20Sopenharmony_ci * Returns:	MMIO pointer to device memory. The caller can read the ACSR
4578c2ecf20Sopenharmony_ci *		register to find more information about the device (such as the
4588c2ecf20Sopenharmony_ci *		version number or the number of blocks).
4598c2ecf20Sopenharmony_ci *		May return IOMEM_ERR_PTR(-errno) in case of error, so check with
4608c2ecf20Sopenharmony_ci *		IS_ERR().
4618c2ecf20Sopenharmony_ci */
4628c2ecf20Sopenharmony_civoid __iomem *mips_cdmm_early_probe(unsigned int dev_type)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct mips_cdmm_bus *bus;
4658c2ecf20Sopenharmony_ci	void __iomem *cdmm;
4668c2ecf20Sopenharmony_ci	u32 acsr;
4678c2ecf20Sopenharmony_ci	unsigned int drb, type, size;
4688c2ecf20Sopenharmony_ci	int err;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (WARN_ON(!dev_type))
4718c2ecf20Sopenharmony_ci		return IOMEM_ERR_PTR(-ENODEV);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	bus = mips_cdmm_get_bus();
4748c2ecf20Sopenharmony_ci	err = mips_cdmm_setup(bus);
4758c2ecf20Sopenharmony_ci	if (err)
4768c2ecf20Sopenharmony_ci		return IOMEM_ERR_PTR(err);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* Skip the first block if it's reserved for more registers */
4798c2ecf20Sopenharmony_ci	drb = bus->drbs_reserved;
4808c2ecf20Sopenharmony_ci	cdmm = bus->regs;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* Look for a specific device type */
4838c2ecf20Sopenharmony_ci	for (; drb < bus->drbs; drb += size + 1) {
4848c2ecf20Sopenharmony_ci		acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
4858c2ecf20Sopenharmony_ci		type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
4868c2ecf20Sopenharmony_ci		if (type == dev_type)
4878c2ecf20Sopenharmony_ci			return cdmm + drb * CDMM_DRB_SIZE;
4888c2ecf20Sopenharmony_ci		size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return IOMEM_ERR_PTR(-ENODEV);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mips_cdmm_early_probe);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci/**
4968c2ecf20Sopenharmony_ci * mips_cdmm_release() - Release a removed CDMM device.
4978c2ecf20Sopenharmony_ci * @dev:	Device object
4988c2ecf20Sopenharmony_ci *
4998c2ecf20Sopenharmony_ci * Clean up the struct mips_cdmm_device for an unused CDMM device. This is
5008c2ecf20Sopenharmony_ci * called automatically by the driver core when a device is removed.
5018c2ecf20Sopenharmony_ci */
5028c2ecf20Sopenharmony_cistatic void mips_cdmm_release(struct device *dev)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	kfree(cdev);
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/**
5108c2ecf20Sopenharmony_ci * mips_cdmm_bus_discover() - Discover the devices on the CDMM bus.
5118c2ecf20Sopenharmony_ci * @bus:	CDMM bus information, must already be set up.
5128c2ecf20Sopenharmony_ci */
5138c2ecf20Sopenharmony_cistatic void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	void __iomem *cdmm;
5168c2ecf20Sopenharmony_ci	u32 acsr;
5178c2ecf20Sopenharmony_ci	unsigned int drb, type, size, rev;
5188c2ecf20Sopenharmony_ci	struct mips_cdmm_device *dev;
5198c2ecf20Sopenharmony_ci	unsigned int cpu = smp_processor_id();
5208c2ecf20Sopenharmony_ci	int ret = 0;
5218c2ecf20Sopenharmony_ci	int id = 0;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* Skip the first block if it's reserved for more registers */
5248c2ecf20Sopenharmony_ci	drb = bus->drbs_reserved;
5258c2ecf20Sopenharmony_ci	cdmm = bus->regs;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Discover devices */
5288c2ecf20Sopenharmony_ci	bus->discovered = true;
5298c2ecf20Sopenharmony_ci	pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
5308c2ecf20Sopenharmony_ci	for (; drb < bus->drbs; drb += size + 1) {
5318c2ecf20Sopenharmony_ci		acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
5328c2ecf20Sopenharmony_ci		type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
5338c2ecf20Sopenharmony_ci		size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
5348c2ecf20Sopenharmony_ci		rev  = (acsr & CDMM_ACSR_DEVREV)  >> CDMM_ACSR_DEVREV_SHIFT;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		if (!type)
5378c2ecf20Sopenharmony_ci			continue;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",
5408c2ecf20Sopenharmony_ci			cpu, id, drb, drb * CDMM_DRB_SIZE,
5418c2ecf20Sopenharmony_ci			(drb + size + 1) * CDMM_DRB_SIZE - 1,
5428c2ecf20Sopenharmony_ci			type, rev);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
5458c2ecf20Sopenharmony_ci		if (!dev)
5468c2ecf20Sopenharmony_ci			break;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		dev->cpu = cpu;
5498c2ecf20Sopenharmony_ci		dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;
5508c2ecf20Sopenharmony_ci		dev->res.end = bus->phys +
5518c2ecf20Sopenharmony_ci				(drb + size + 1) * CDMM_DRB_SIZE - 1;
5528c2ecf20Sopenharmony_ci		dev->res.flags = IORESOURCE_MEM;
5538c2ecf20Sopenharmony_ci		dev->type = type;
5548c2ecf20Sopenharmony_ci		dev->rev = rev;
5558c2ecf20Sopenharmony_ci		dev->dev.parent = get_cpu_device(cpu);
5568c2ecf20Sopenharmony_ci		dev->dev.bus = &mips_cdmm_bustype;
5578c2ecf20Sopenharmony_ci		dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);
5588c2ecf20Sopenharmony_ci		dev->dev.release = mips_cdmm_release;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);
5618c2ecf20Sopenharmony_ci		++id;
5628c2ecf20Sopenharmony_ci		ret = device_register(&dev->dev);
5638c2ecf20Sopenharmony_ci		if (ret)
5648c2ecf20Sopenharmony_ci			put_device(&dev->dev);
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/*
5708c2ecf20Sopenharmony_ci * CPU hotplug and initialisation
5718c2ecf20Sopenharmony_ci *
5728c2ecf20Sopenharmony_ci * All the CDMM driver callbacks need to be executed on the appropriate CPU from
5738c2ecf20Sopenharmony_ci * workqueues. For the CPU callbacks, they need to be called for all devices on
5748c2ecf20Sopenharmony_ci * that CPU, so the work function calls bus_for_each_dev, using a helper
5758c2ecf20Sopenharmony_ci * (generated with BUILD_PERDEV_HELPER) to call the driver callback if the
5768c2ecf20Sopenharmony_ci * device's CPU matches.
5778c2ecf20Sopenharmony_ci */
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci/**
5808c2ecf20Sopenharmony_ci * BUILD_PERDEV_HELPER() - Helper to call a CDMM driver callback if CPU matches.
5818c2ecf20Sopenharmony_ci * @_name:	Name of CDMM driver callback function.
5828c2ecf20Sopenharmony_ci *
5838c2ecf20Sopenharmony_ci * Generates a bus_for_each_dev callback function to call a specific CDMM driver
5848c2ecf20Sopenharmony_ci * callback function for the device if the device's CPU matches that pointed to
5858c2ecf20Sopenharmony_ci * by the data argument.
5868c2ecf20Sopenharmony_ci *
5878c2ecf20Sopenharmony_ci * This is used for informing drivers for all devices on a given CPU of some
5888c2ecf20Sopenharmony_ci * event (such as the CPU going online/offline).
5898c2ecf20Sopenharmony_ci *
5908c2ecf20Sopenharmony_ci * It is expected to already be called from the appropriate CPU.
5918c2ecf20Sopenharmony_ci */
5928c2ecf20Sopenharmony_ci#define BUILD_PERDEV_HELPER(_name)					\
5938c2ecf20Sopenharmony_cistatic int mips_cdmm_##_name##_helper(struct device *dev, void *data)	\
5948c2ecf20Sopenharmony_ci{									\
5958c2ecf20Sopenharmony_ci	struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);	\
5968c2ecf20Sopenharmony_ci	struct mips_cdmm_driver *cdrv;					\
5978c2ecf20Sopenharmony_ci	unsigned int cpu = *(unsigned int *)data;			\
5988c2ecf20Sopenharmony_ci									\
5998c2ecf20Sopenharmony_ci	if (cdev->cpu != cpu || !dev->driver)				\
6008c2ecf20Sopenharmony_ci		return 0;						\
6018c2ecf20Sopenharmony_ci									\
6028c2ecf20Sopenharmony_ci	cdrv = to_mips_cdmm_driver(dev->driver);			\
6038c2ecf20Sopenharmony_ci	if (!cdrv->_name)						\
6048c2ecf20Sopenharmony_ci		return 0;						\
6058c2ecf20Sopenharmony_ci	return cdrv->_name(cdev);					\
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci/* bus_for_each_dev callback helper functions */
6098c2ecf20Sopenharmony_ciBUILD_PERDEV_HELPER(cpu_down)       /* int mips_cdmm_cpu_down_helper(...) */
6108c2ecf20Sopenharmony_ciBUILD_PERDEV_HELPER(cpu_up)         /* int mips_cdmm_cpu_up_helper(...) */
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/**
6138c2ecf20Sopenharmony_ci * mips_cdmm_cpu_down_prep() - Callback for CPUHP DOWN_PREP:
6148c2ecf20Sopenharmony_ci *			       Tear down the CDMM bus.
6158c2ecf20Sopenharmony_ci * @cpu:	unsigned int CPU number.
6168c2ecf20Sopenharmony_ci *
6178c2ecf20Sopenharmony_ci * This function is executed on the hotplugged CPU and calls the CDMM
6188c2ecf20Sopenharmony_ci * driver cpu_down callback for all devices on that CPU.
6198c2ecf20Sopenharmony_ci */
6208c2ecf20Sopenharmony_cistatic int mips_cdmm_cpu_down_prep(unsigned int cpu)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	struct mips_cdmm_bus *bus;
6238c2ecf20Sopenharmony_ci	long ret;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* Inform all the devices on the bus */
6268c2ecf20Sopenharmony_ci	ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
6278c2ecf20Sopenharmony_ci			       mips_cdmm_cpu_down_helper);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/*
6308c2ecf20Sopenharmony_ci	 * While bus is offline, each use of it should reconfigure it just in
6318c2ecf20Sopenharmony_ci	 * case it is first use when coming back online again.
6328c2ecf20Sopenharmony_ci	 */
6338c2ecf20Sopenharmony_ci	bus = mips_cdmm_get_bus();
6348c2ecf20Sopenharmony_ci	if (!IS_ERR(bus))
6358c2ecf20Sopenharmony_ci		bus->offline = true;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	return ret;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci/**
6418c2ecf20Sopenharmony_ci * mips_cdmm_cpu_online() - Callback for CPUHP ONLINE: Bring up the CDMM bus.
6428c2ecf20Sopenharmony_ci * @cpu:	unsigned int CPU number.
6438c2ecf20Sopenharmony_ci *
6448c2ecf20Sopenharmony_ci * This work_on_cpu callback function is executed on a given CPU to discover
6458c2ecf20Sopenharmony_ci * CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all
6468c2ecf20Sopenharmony_ci * devices already discovered on that CPU.
6478c2ecf20Sopenharmony_ci *
6488c2ecf20Sopenharmony_ci * It is used as work_on_cpu callback function during
6498c2ecf20Sopenharmony_ci * initialisation. When CPUs are brought online the function is
6508c2ecf20Sopenharmony_ci * invoked directly on the hotplugged CPU.
6518c2ecf20Sopenharmony_ci */
6528c2ecf20Sopenharmony_cistatic int mips_cdmm_cpu_online(unsigned int cpu)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct mips_cdmm_bus *bus;
6558c2ecf20Sopenharmony_ci	long ret;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	bus = mips_cdmm_get_bus();
6588c2ecf20Sopenharmony_ci	ret = mips_cdmm_setup(bus);
6598c2ecf20Sopenharmony_ci	if (ret)
6608c2ecf20Sopenharmony_ci		return ret;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Bus now set up, so we can drop the offline flag if still set */
6638c2ecf20Sopenharmony_ci	bus->offline = false;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (!bus->discovered)
6668c2ecf20Sopenharmony_ci		mips_cdmm_bus_discover(bus);
6678c2ecf20Sopenharmony_ci	else
6688c2ecf20Sopenharmony_ci		/* Inform all the devices on the bus */
6698c2ecf20Sopenharmony_ci		ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
6708c2ecf20Sopenharmony_ci				       mips_cdmm_cpu_up_helper);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return ret;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci/**
6768c2ecf20Sopenharmony_ci * mips_cdmm_init() - Initialise CDMM bus.
6778c2ecf20Sopenharmony_ci *
6788c2ecf20Sopenharmony_ci * Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for
6798c2ecf20Sopenharmony_ci * hotplug notifications so the CDMM drivers can be kept up to date.
6808c2ecf20Sopenharmony_ci */
6818c2ecf20Sopenharmony_cistatic int __init mips_cdmm_init(void)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	int ret;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Register the bus */
6868c2ecf20Sopenharmony_ci	ret = bus_register(&mips_cdmm_bustype);
6878c2ecf20Sopenharmony_ci	if (ret)
6888c2ecf20Sopenharmony_ci		return ret;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* We want to be notified about new CPUs */
6918c2ecf20Sopenharmony_ci	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",
6928c2ecf20Sopenharmony_ci				mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);
6938c2ecf20Sopenharmony_ci	if (ret < 0)
6948c2ecf20Sopenharmony_ci		pr_warn("cdmm: Failed to register CPU notifier\n");
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return ret;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_cisubsys_initcall(mips_cdmm_init);
699