linux/drivers/bus/mips_cdmm.c
<<
>>
Prefs
   1/*
   2 * Bus driver for MIPS Common Device Memory Map (CDMM).
   3 *
   4 * Copyright (C) 2014-2015 Imagination Technologies Ltd.
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License.  See the file "COPYING" in the main directory of this archive
   8 * for more details.
   9 */
  10
  11#include <linux/atomic.h>
  12#include <linux/err.h>
  13#include <linux/cpu.h>
  14#include <linux/cpumask.h>
  15#include <linux/io.h>
  16#include <linux/platform_device.h>
  17#include <linux/slab.h>
  18#include <linux/smp.h>
  19#include <asm/cdmm.h>
  20#include <asm/hazards.h>
  21#include <asm/mipsregs.h>
  22
  23/* Access control and status register fields */
  24#define CDMM_ACSR_DEVTYPE_SHIFT 24
  25#define CDMM_ACSR_DEVTYPE       (255ul << CDMM_ACSR_DEVTYPE_SHIFT)
  26#define CDMM_ACSR_DEVSIZE_SHIFT 16
  27#define CDMM_ACSR_DEVSIZE       (31ul << CDMM_ACSR_DEVSIZE_SHIFT)
  28#define CDMM_ACSR_DEVREV_SHIFT  12
  29#define CDMM_ACSR_DEVREV        (15ul << CDMM_ACSR_DEVREV_SHIFT)
  30#define CDMM_ACSR_UW            (1ul << 3)
  31#define CDMM_ACSR_UR            (1ul << 2)
  32#define CDMM_ACSR_SW            (1ul << 1)
  33#define CDMM_ACSR_SR            (1ul << 0)
  34
  35/* Each block of device registers is 64 bytes */
  36#define CDMM_DRB_SIZE           64
  37
  38#define to_mips_cdmm_driver(d)  container_of(d, struct mips_cdmm_driver, drv)
  39
  40/* Default physical base address */
  41static phys_addr_t mips_cdmm_default_base;
  42
  43/* Bus operations */
  44
  45static const struct mips_cdmm_device_id *
  46mips_cdmm_lookup(const struct mips_cdmm_device_id *table,
  47                 struct mips_cdmm_device *dev)
  48{
  49        int ret = 0;
  50
  51        for (; table->type; ++table) {
  52                ret = (dev->type == table->type);
  53                if (ret)
  54                        break;
  55        }
  56
  57        return ret ? table : NULL;
  58}
  59
  60static int mips_cdmm_match(struct device *dev, struct device_driver *drv)
  61{
  62        struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
  63        struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
  64
  65        return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
  66}
  67
  68static int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env)
  69{
  70        struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
  71        int retval = 0;
  72
  73        retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
  74        if (retval)
  75                return retval;
  76
  77        retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);
  78        if (retval)
  79                return retval;
  80
  81        retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);
  82        if (retval)
  83                return retval;
  84
  85        retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);
  86        return retval;
  87}
  88
  89/* Device attributes */
  90
  91#define CDMM_ATTR(name, fmt, arg...)                                    \
  92static ssize_t name##_show(struct device *_dev,                         \
  93                           struct device_attribute *attr, char *buf)    \
  94{                                                                       \
  95        struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev);       \
  96        return sprintf(buf, fmt, arg);                                  \
  97}                                                                       \
  98static DEVICE_ATTR_RO(name);
  99
 100CDMM_ATTR(cpu, "%u\n", dev->cpu);
 101CDMM_ATTR(type, "0x%02x\n", dev->type);
 102CDMM_ATTR(revision, "%u\n", dev->rev);
 103CDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);
 104CDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",
 105          (unsigned long long)dev->res.start,
 106          (unsigned long long)dev->res.end,
 107          dev->res.flags);
 108
 109static struct attribute *mips_cdmm_dev_attrs[] = {
 110        &dev_attr_cpu.attr,
 111        &dev_attr_type.attr,
 112        &dev_attr_revision.attr,
 113        &dev_attr_modalias.attr,
 114        &dev_attr_resource.attr,
 115        NULL,
 116};
 117ATTRIBUTE_GROUPS(mips_cdmm_dev);
 118
 119struct bus_type mips_cdmm_bustype = {
 120        .name           = "cdmm",
 121        .dev_groups     = mips_cdmm_dev_groups,
 122        .match          = mips_cdmm_match,
 123        .uevent         = mips_cdmm_uevent,
 124};
 125EXPORT_SYMBOL_GPL(mips_cdmm_bustype);
 126
 127/*
 128 * Standard driver callback helpers.
 129 *
 130 * All the CDMM driver callbacks need to be executed on the appropriate CPU from
 131 * workqueues. For the standard driver callbacks we need a work function
 132 * (mips_cdmm_{void,int}_work()) to do the actual call from the right CPU, and a
 133 * wrapper function (generated with BUILD_PERCPU_HELPER) to arrange for the work
 134 * function to be called on that CPU.
 135 */
 136
 137/**
 138 * struct mips_cdmm_work_dev - Data for per-device call work.
 139 * @fn:         CDMM driver callback function to call for the device.
 140 * @dev:        CDMM device to pass to @fn.
 141 */
 142struct mips_cdmm_work_dev {
 143        void                    *fn;
 144        struct mips_cdmm_device *dev;
 145};
 146
 147/**
 148 * mips_cdmm_void_work() - Call a void returning CDMM driver callback.
 149 * @data:       struct mips_cdmm_work_dev pointer.
 150 *
 151 * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
 152 * function which doesn't return a value.
 153 */
 154static long mips_cdmm_void_work(void *data)
 155{
 156        struct mips_cdmm_work_dev *work = data;
 157        void (*fn)(struct mips_cdmm_device *) = work->fn;
 158
 159        fn(work->dev);
 160        return 0;
 161}
 162
 163/**
 164 * mips_cdmm_int_work() - Call an int returning CDMM driver callback.
 165 * @data:       struct mips_cdmm_work_dev pointer.
 166 *
 167 * A work_on_cpu() callback function to call an arbitrary CDMM driver callback
 168 * function which returns an int.
 169 */
 170static long mips_cdmm_int_work(void *data)
 171{
 172        struct mips_cdmm_work_dev *work = data;
 173        int (*fn)(struct mips_cdmm_device *) = work->fn;
 174
 175        return fn(work->dev);
 176}
 177
 178#define _BUILD_RET_void
 179#define _BUILD_RET_int  return
 180
 181/**
 182 * BUILD_PERCPU_HELPER() - Helper to call a CDMM driver callback on right CPU.
 183 * @_ret:       Return type (void or int).
 184 * @_name:      Name of CDMM driver callback function.
 185 *
 186 * Generates a specific device callback function to call a CDMM driver callback
 187 * function on the appropriate CPU for the device, and if applicable return the
 188 * result.
 189 */
 190#define BUILD_PERCPU_HELPER(_ret, _name)                                \
 191static _ret mips_cdmm_##_name(struct device *dev)                       \
 192{                                                                       \
 193        struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);       \
 194        struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \
 195        struct mips_cdmm_work_dev work = {                              \
 196                .fn     = cdrv->_name,                                  \
 197                .dev    = cdev,                                         \
 198        };                                                              \
 199                                                                        \
 200        _BUILD_RET_##_ret work_on_cpu(cdev->cpu,                        \
 201                                      mips_cdmm_##_ret##_work, &work);  \
 202}
 203
 204/* Driver callback functions */
 205BUILD_PERCPU_HELPER(int, probe)     /* int mips_cdmm_probe(struct device) */
 206BUILD_PERCPU_HELPER(int, remove)    /* int mips_cdmm_remove(struct device) */
 207BUILD_PERCPU_HELPER(void, shutdown) /* void mips_cdmm_shutdown(struct device) */
 208
 209
 210/* Driver registration */
 211
 212/**
 213 * mips_cdmm_driver_register() - Register a CDMM driver.
 214 * @drv:        CDMM driver information.
 215 *
 216 * Register a CDMM driver with the CDMM subsystem. The driver will be informed
 217 * of matching devices which are discovered.
 218 *
 219 * Returns:     0 on success.
 220 */
 221int mips_cdmm_driver_register(struct mips_cdmm_driver *drv)
 222{
 223        drv->drv.bus = &mips_cdmm_bustype;
 224
 225        if (drv->probe)
 226                drv->drv.probe = mips_cdmm_probe;
 227        if (drv->remove)
 228                drv->drv.remove = mips_cdmm_remove;
 229        if (drv->shutdown)
 230                drv->drv.shutdown = mips_cdmm_shutdown;
 231
 232        return driver_register(&drv->drv);
 233}
 234EXPORT_SYMBOL_GPL(mips_cdmm_driver_register);
 235
 236/**
 237 * mips_cdmm_driver_unregister() - Unregister a CDMM driver.
 238 * @drv:        CDMM driver information.
 239 *
 240 * Unregister a CDMM driver from the CDMM subsystem.
 241 */
 242void mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)
 243{
 244        driver_unregister(&drv->drv);
 245}
 246EXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);
 247
 248
 249/* CDMM initialisation and bus discovery */
 250
 251/**
 252 * struct mips_cdmm_bus - Info about CDMM bus.
 253 * @phys:               Physical address at which it is mapped.
 254 * @regs:               Virtual address where registers can be accessed.
 255 * @drbs:               Total number of DRBs.
 256 * @drbs_reserved:      Number of DRBs reserved.
 257 * @discovered:         Whether the devices on the bus have been discovered yet.
 258 * @offline:            Whether the CDMM bus is going offline (or very early
 259 *                      coming back online), in which case it should be
 260 *                      reconfigured each time.
 261 */
 262struct mips_cdmm_bus {
 263        phys_addr_t      phys;
 264        void __iomem    *regs;
 265        unsigned int     drbs;
 266        unsigned int     drbs_reserved;
 267        bool             discovered;
 268        bool             offline;
 269};
 270
 271static struct mips_cdmm_bus mips_cdmm_boot_bus;
 272static DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);
 273static atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);
 274
 275/**
 276 * mips_cdmm_get_bus() - Get the per-CPU CDMM bus information.
 277 *
 278 * Get information about the per-CPU CDMM bus, if the bus is present.
 279 *
 280 * The caller must prevent migration to another CPU, either by disabling
 281 * pre-emption or by running from a pinned kernel thread.
 282 *
 283 * Returns:     Pointer to CDMM bus information for the current CPU.
 284 *              May return ERR_PTR(-errno) in case of error, so check with
 285 *              IS_ERR().
 286 */
 287static struct mips_cdmm_bus *mips_cdmm_get_bus(void)
 288{
 289        struct mips_cdmm_bus *bus, **bus_p;
 290        unsigned long flags;
 291        unsigned int cpu;
 292
 293        if (!cpu_has_cdmm)
 294                return ERR_PTR(-ENODEV);
 295
 296        cpu = smp_processor_id();
 297        /* Avoid early use of per-cpu primitives before initialised */
 298        if (cpu == 0)
 299                return &mips_cdmm_boot_bus;
 300
 301        /* Get bus pointer */
 302        bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);
 303        local_irq_save(flags);
 304        bus = *bus_p;
 305        /* Attempt allocation if NULL */
 306        if (unlikely(!bus)) {
 307                bus = kzalloc(sizeof(*bus), GFP_ATOMIC);
 308                if (unlikely(!bus))
 309                        bus = ERR_PTR(-ENOMEM);
 310                else
 311                        *bus_p = bus;
 312        }
 313        local_irq_restore(flags);
 314        return bus;
 315}
 316
 317/**
 318 * mips_cdmm_cur_base() - Find current physical base address of CDMM region.
 319 *
 320 * Returns:     Physical base address of CDMM region according to cdmmbase CP0
 321 *              register, or 0 if the CDMM region is disabled.
 322 */
 323static phys_addr_t mips_cdmm_cur_base(void)
 324{
 325        unsigned long cdmmbase = read_c0_cdmmbase();
 326
 327        if (!(cdmmbase & MIPS_CDMMBASE_EN))
 328                return 0;
 329
 330        return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)
 331                << MIPS_CDMMBASE_ADDR_START;
 332}
 333
 334/**
 335 * mips_cdmm_phys_base() - Choose a physical base address for CDMM region.
 336 *
 337 * Picking a suitable physical address at which to map the CDMM region is
 338 * platform specific, so this weak function can be overridden by platform
 339 * code to pick a suitable value if none is configured by the bootloader.
 340 */
 341phys_addr_t __weak mips_cdmm_phys_base(void)
 342{
 343        return 0;
 344}
 345
 346/**
 347 * mips_cdmm_setup() - Ensure the CDMM bus is initialised and usable.
 348 * @bus:        Pointer to bus information for current CPU.
 349 *              IS_ERR(bus) is checked, so no need for caller to check.
 350 *
 351 * The caller must prevent migration to another CPU, either by disabling
 352 * pre-emption or by running from a pinned kernel thread.
 353 *
 354 * Returns      0 on success, -errno on failure.
 355 */
 356static int mips_cdmm_setup(struct mips_cdmm_bus *bus)
 357{
 358        unsigned long cdmmbase, flags;
 359        int ret = 0;
 360
 361        if (IS_ERR(bus))
 362                return PTR_ERR(bus);
 363
 364        local_irq_save(flags);
 365        /* Don't set up bus a second time unless marked offline */
 366        if (bus->offline) {
 367                /* If CDMM region is still set up, nothing to do */
 368                if (bus->phys == mips_cdmm_cur_base())
 369                        goto out;
 370                /*
 371                 * The CDMM region isn't set up as expected, so it needs
 372                 * reconfiguring, but then we can stop checking it.
 373                 */
 374                bus->offline = false;
 375        } else if (bus->phys > 1) {
 376                goto out;
 377        }
 378
 379        /* If the CDMM region is already configured, inherit that setup */
 380        if (!bus->phys)
 381                bus->phys = mips_cdmm_cur_base();
 382        /* Otherwise, ask platform code for suggestions */
 383        if (!bus->phys)
 384                bus->phys = mips_cdmm_phys_base();
 385        /* Otherwise, copy what other CPUs have done */
 386        if (!bus->phys)
 387                bus->phys = mips_cdmm_default_base;
 388        /* Otherwise, complain once */
 389        if (!bus->phys) {
 390                bus->phys = 1;
 391                /*
 392                 * If you hit this, either your bootloader needs to set up the
 393                 * CDMM on the boot CPU, or else you need to implement
 394                 * mips_cdmm_phys_base() for your platform (see asm/cdmm.h).
 395                 */
 396                pr_err("cdmm%u: Failed to choose a physical base\n",
 397                       smp_processor_id());
 398        }
 399        /* Already complained? */
 400        if (bus->phys == 1) {
 401                ret = -ENOMEM;
 402                goto out;
 403        }
 404        /* Record our success for other CPUs to copy */
 405        mips_cdmm_default_base = bus->phys;
 406
 407        pr_debug("cdmm%u: Enabling CDMM region at %pa\n",
 408                 smp_processor_id(), &bus->phys);
 409
 410        /* Enable CDMM */
 411        cdmmbase = read_c0_cdmmbase();
 412        cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;
 413        cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)
 414                        << MIPS_CDMMBASE_ADDR_SHIFT;
 415        cdmmbase |= MIPS_CDMMBASE_EN;
 416        write_c0_cdmmbase(cdmmbase);
 417        tlbw_use_hazard();
 418
 419        bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);
 420        bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>
 421                         MIPS_CDMMBASE_SIZE_SHIFT);
 422        bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);
 423
 424out:
 425        local_irq_restore(flags);
 426        return ret;
 427}
 428
 429/**
 430 * mips_cdmm_early_probe() - Minimally probe for a specific device on CDMM.
 431 * @dev_type:   CDMM type code to look for.
 432 *
 433 * Minimally configure the in-CPU Common Device Memory Map (CDMM) and look for a
 434 * specific device. This can be used to find a device very early in boot for
 435 * example to configure an early FDC console device.
 436 *
 437 * The caller must prevent migration to another CPU, either by disabling
 438 * pre-emption or by running from a pinned kernel thread.
 439 *
 440 * Returns:     MMIO pointer to device memory. The caller can read the ACSR
 441 *              register to find more information about the device (such as the
 442 *              version number or the number of blocks).
 443 *              May return IOMEM_ERR_PTR(-errno) in case of error, so check with
 444 *              IS_ERR().
 445 */
 446void __iomem *mips_cdmm_early_probe(unsigned int dev_type)
 447{
 448        struct mips_cdmm_bus *bus;
 449        void __iomem *cdmm;
 450        u32 acsr;
 451        unsigned int drb, type, size;
 452        int err;
 453
 454        if (WARN_ON(!dev_type))
 455                return IOMEM_ERR_PTR(-ENODEV);
 456
 457        bus = mips_cdmm_get_bus();
 458        err = mips_cdmm_setup(bus);
 459        if (err)
 460                return IOMEM_ERR_PTR(err);
 461
 462        /* Skip the first block if it's reserved for more registers */
 463        drb = bus->drbs_reserved;
 464        cdmm = bus->regs;
 465
 466        /* Look for a specific device type */
 467        for (; drb < bus->drbs; drb += size + 1) {
 468                acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
 469                type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
 470                if (type == dev_type)
 471                        return cdmm + drb * CDMM_DRB_SIZE;
 472                size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
 473        }
 474
 475        return IOMEM_ERR_PTR(-ENODEV);
 476}
 477EXPORT_SYMBOL_GPL(mips_cdmm_early_probe);
 478
 479/**
 480 * mips_cdmm_release() - Release a removed CDMM device.
 481 * @dev:        Device object
 482 *
 483 * Clean up the struct mips_cdmm_device for an unused CDMM device. This is
 484 * called automatically by the driver core when a device is removed.
 485 */
 486static void mips_cdmm_release(struct device *dev)
 487{
 488        struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
 489
 490        kfree(cdev);
 491}
 492
 493/**
 494 * mips_cdmm_bus_discover() - Discover the devices on the CDMM bus.
 495 * @bus:        CDMM bus information, must already be set up.
 496 */
 497static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
 498{
 499        void __iomem *cdmm;
 500        u32 acsr;
 501        unsigned int drb, type, size, rev;
 502        struct mips_cdmm_device *dev;
 503        unsigned int cpu = smp_processor_id();
 504        int ret = 0;
 505        int id = 0;
 506
 507        /* Skip the first block if it's reserved for more registers */
 508        drb = bus->drbs_reserved;
 509        cdmm = bus->regs;
 510
 511        /* Discover devices */
 512        bus->discovered = true;
 513        pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
 514        for (; drb < bus->drbs; drb += size + 1) {
 515                acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
 516                type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
 517                size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
 518                rev  = (acsr & CDMM_ACSR_DEVREV)  >> CDMM_ACSR_DEVREV_SHIFT;
 519
 520                if (!type)
 521                        continue;
 522
 523                pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",
 524                        cpu, id, drb, drb * CDMM_DRB_SIZE,
 525                        (drb + size + 1) * CDMM_DRB_SIZE - 1,
 526                        type, rev);
 527
 528                dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 529                if (!dev)
 530                        break;
 531
 532                dev->cpu = cpu;
 533                dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;
 534                dev->res.end = bus->phys +
 535                                (drb + size + 1) * CDMM_DRB_SIZE - 1;
 536                dev->res.flags = IORESOURCE_MEM;
 537                dev->type = type;
 538                dev->rev = rev;
 539                dev->dev.parent = get_cpu_device(cpu);
 540                dev->dev.bus = &mips_cdmm_bustype;
 541                dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);
 542                dev->dev.release = mips_cdmm_release;
 543
 544                dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);
 545                ++id;
 546                ret = device_register(&dev->dev);
 547                if (ret) {
 548                        put_device(&dev->dev);
 549                        kfree(dev);
 550                }
 551        }
 552}
 553
 554
 555/*
 556 * CPU hotplug and initialisation
 557 *
 558 * All the CDMM driver callbacks need to be executed on the appropriate CPU from
 559 * workqueues. For the CPU callbacks, they need to be called for all devices on
 560 * that CPU, so the work function calls bus_for_each_dev, using a helper
 561 * (generated with BUILD_PERDEV_HELPER) to call the driver callback if the
 562 * device's CPU matches.
 563 */
 564
 565/**
 566 * BUILD_PERDEV_HELPER() - Helper to call a CDMM driver callback if CPU matches.
 567 * @_name:      Name of CDMM driver callback function.
 568 *
 569 * Generates a bus_for_each_dev callback function to call a specific CDMM driver
 570 * callback function for the device if the device's CPU matches that pointed to
 571 * by the data argument.
 572 *
 573 * This is used for informing drivers for all devices on a given CPU of some
 574 * event (such as the CPU going online/offline).
 575 *
 576 * It is expected to already be called from the appropriate CPU.
 577 */
 578#define BUILD_PERDEV_HELPER(_name)                                      \
 579static int mips_cdmm_##_name##_helper(struct device *dev, void *data)   \
 580{                                                                       \
 581        struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);       \
 582        struct mips_cdmm_driver *cdrv;                                  \
 583        unsigned int cpu = *(unsigned int *)data;                       \
 584                                                                        \
 585        if (cdev->cpu != cpu || !dev->driver)                           \
 586                return 0;                                               \
 587                                                                        \
 588        cdrv = to_mips_cdmm_driver(dev->driver);                        \
 589        if (!cdrv->_name)                                               \
 590                return 0;                                               \
 591        return cdrv->_name(cdev);                                       \
 592}
 593
 594/* bus_for_each_dev callback helper functions */
 595BUILD_PERDEV_HELPER(cpu_down)       /* int mips_cdmm_cpu_down_helper(...) */
 596BUILD_PERDEV_HELPER(cpu_up)         /* int mips_cdmm_cpu_up_helper(...) */
 597
 598/**
 599 * mips_cdmm_cpu_down_prep() - Callback for CPUHP DOWN_PREP:
 600 *                             Tear down the CDMM bus.
 601 * @cpu:        unsigned int CPU number.
 602 *
 603 * This function is executed on the hotplugged CPU and calls the CDMM
 604 * driver cpu_down callback for all devices on that CPU.
 605 */
 606static int mips_cdmm_cpu_down_prep(unsigned int cpu)
 607{
 608        struct mips_cdmm_bus *bus;
 609        long ret;
 610
 611        /* Inform all the devices on the bus */
 612        ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
 613                               mips_cdmm_cpu_down_helper);
 614
 615        /*
 616         * While bus is offline, each use of it should reconfigure it just in
 617         * case it is first use when coming back online again.
 618         */
 619        bus = mips_cdmm_get_bus();
 620        if (!IS_ERR(bus))
 621                bus->offline = true;
 622
 623        return ret;
 624}
 625
 626/**
 627 * mips_cdmm_cpu_online() - Callback for CPUHP ONLINE: Bring up the CDMM bus.
 628 * @cpu:        unsigned int CPU number.
 629 *
 630 * This work_on_cpu callback function is executed on a given CPU to discover
 631 * CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all
 632 * devices already discovered on that CPU.
 633 *
 634 * It is used as work_on_cpu callback function during
 635 * initialisation. When CPUs are brought online the function is
 636 * invoked directly on the hotplugged CPU.
 637 */
 638static int mips_cdmm_cpu_online(unsigned int cpu)
 639{
 640        struct mips_cdmm_bus *bus;
 641        long ret;
 642
 643        bus = mips_cdmm_get_bus();
 644        ret = mips_cdmm_setup(bus);
 645        if (ret)
 646                return ret;
 647
 648        /* Bus now set up, so we can drop the offline flag if still set */
 649        bus->offline = false;
 650
 651        if (!bus->discovered)
 652                mips_cdmm_bus_discover(bus);
 653        else
 654                /* Inform all the devices on the bus */
 655                ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
 656                                       mips_cdmm_cpu_up_helper);
 657
 658        return ret;
 659}
 660
 661/**
 662 * mips_cdmm_init() - Initialise CDMM bus.
 663 *
 664 * Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for
 665 * hotplug notifications so the CDMM drivers can be kept up to date.
 666 */
 667static int __init mips_cdmm_init(void)
 668{
 669        int ret;
 670
 671        /* Register the bus */
 672        ret = bus_register(&mips_cdmm_bustype);
 673        if (ret)
 674                return ret;
 675
 676        /* We want to be notified about new CPUs */
 677        ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",
 678                                mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);
 679        if (ret < 0)
 680                pr_warn("cdmm: Failed to register CPU notifier\n");
 681
 682        return ret;
 683}
 684subsys_initcall(mips_cdmm_init);
 685