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