0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/atomic.h>
0012 #include <linux/err.h>
0013 #include <linux/cpu.h>
0014 #include <linux/cpumask.h>
0015 #include <linux/io.h>
0016 #include <linux/of_address.h>
0017 #include <linux/of.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/slab.h>
0020 #include <linux/smp.h>
0021 #include <asm/cdmm.h>
0022 #include <asm/hazards.h>
0023 #include <asm/mipsregs.h>
0024
0025
0026 #define CDMM_ACSR_DEVTYPE_SHIFT 24
0027 #define CDMM_ACSR_DEVTYPE (255ul << CDMM_ACSR_DEVTYPE_SHIFT)
0028 #define CDMM_ACSR_DEVSIZE_SHIFT 16
0029 #define CDMM_ACSR_DEVSIZE (31ul << CDMM_ACSR_DEVSIZE_SHIFT)
0030 #define CDMM_ACSR_DEVREV_SHIFT 12
0031 #define CDMM_ACSR_DEVREV (15ul << CDMM_ACSR_DEVREV_SHIFT)
0032 #define CDMM_ACSR_UW (1ul << 3)
0033 #define CDMM_ACSR_UR (1ul << 2)
0034 #define CDMM_ACSR_SW (1ul << 1)
0035 #define CDMM_ACSR_SR (1ul << 0)
0036
0037
0038 #define CDMM_DRB_SIZE 64
0039
0040 #define to_mips_cdmm_driver(d) container_of(d, struct mips_cdmm_driver, drv)
0041
0042
0043 static phys_addr_t mips_cdmm_default_base;
0044
0045
0046
0047 static const struct mips_cdmm_device_id *
0048 mips_cdmm_lookup(const struct mips_cdmm_device_id *table,
0049 struct mips_cdmm_device *dev)
0050 {
0051 int ret = 0;
0052
0053 for (; table->type; ++table) {
0054 ret = (dev->type == table->type);
0055 if (ret)
0056 break;
0057 }
0058
0059 return ret ? table : NULL;
0060 }
0061
0062 static int mips_cdmm_match(struct device *dev, struct device_driver *drv)
0063 {
0064 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
0065 struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
0066
0067 return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
0068 }
0069
0070 static int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env)
0071 {
0072 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
0073 int retval = 0;
0074
0075 retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
0076 if (retval)
0077 return retval;
0078
0079 retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);
0080 if (retval)
0081 return retval;
0082
0083 retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);
0084 if (retval)
0085 return retval;
0086
0087 retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);
0088 return retval;
0089 }
0090
0091
0092
0093 #define CDMM_ATTR(name, fmt, arg...) \
0094 static ssize_t name##_show(struct device *_dev, \
0095 struct device_attribute *attr, char *buf) \
0096 { \
0097 struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev); \
0098 return sprintf(buf, fmt, arg); \
0099 } \
0100 static DEVICE_ATTR_RO(name);
0101
0102 CDMM_ATTR(cpu, "%u\n", dev->cpu);
0103 CDMM_ATTR(type, "0x%02x\n", dev->type);
0104 CDMM_ATTR(revision, "%u\n", dev->rev);
0105 CDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);
0106 CDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",
0107 (unsigned long long)dev->res.start,
0108 (unsigned long long)dev->res.end,
0109 dev->res.flags);
0110
0111 static struct attribute *mips_cdmm_dev_attrs[] = {
0112 &dev_attr_cpu.attr,
0113 &dev_attr_type.attr,
0114 &dev_attr_revision.attr,
0115 &dev_attr_modalias.attr,
0116 &dev_attr_resource.attr,
0117 NULL,
0118 };
0119 ATTRIBUTE_GROUPS(mips_cdmm_dev);
0120
0121 struct bus_type mips_cdmm_bustype = {
0122 .name = "cdmm",
0123 .dev_groups = mips_cdmm_dev_groups,
0124 .match = mips_cdmm_match,
0125 .uevent = mips_cdmm_uevent,
0126 };
0127 EXPORT_SYMBOL_GPL(mips_cdmm_bustype);
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144 struct mips_cdmm_work_dev {
0145 void *fn;
0146 struct mips_cdmm_device *dev;
0147 };
0148
0149
0150
0151
0152
0153
0154
0155
0156 static long mips_cdmm_void_work(void *data)
0157 {
0158 struct mips_cdmm_work_dev *work = data;
0159 void (*fn)(struct mips_cdmm_device *) = work->fn;
0160
0161 fn(work->dev);
0162 return 0;
0163 }
0164
0165
0166
0167
0168
0169
0170
0171
0172 static long mips_cdmm_int_work(void *data)
0173 {
0174 struct mips_cdmm_work_dev *work = data;
0175 int (*fn)(struct mips_cdmm_device *) = work->fn;
0176
0177 return fn(work->dev);
0178 }
0179
0180 #define _BUILD_RET_void
0181 #define _BUILD_RET_int return
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192 #define BUILD_PERCPU_HELPER(_ret, _name) \
0193 static _ret mips_cdmm_##_name(struct device *dev) \
0194 { \
0195 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \
0196 struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \
0197 struct mips_cdmm_work_dev work = { \
0198 .fn = cdrv->_name, \
0199 .dev = cdev, \
0200 }; \
0201 \
0202 _BUILD_RET_##_ret work_on_cpu(cdev->cpu, \
0203 mips_cdmm_##_ret##_work, &work); \
0204 }
0205
0206
0207 BUILD_PERCPU_HELPER(int, probe)
0208 BUILD_PERCPU_HELPER(int, remove)
0209 BUILD_PERCPU_HELPER(void, shutdown)
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223 int mips_cdmm_driver_register(struct mips_cdmm_driver *drv)
0224 {
0225 drv->drv.bus = &mips_cdmm_bustype;
0226
0227 if (drv->probe)
0228 drv->drv.probe = mips_cdmm_probe;
0229 if (drv->remove)
0230 drv->drv.remove = mips_cdmm_remove;
0231 if (drv->shutdown)
0232 drv->drv.shutdown = mips_cdmm_shutdown;
0233
0234 return driver_register(&drv->drv);
0235 }
0236 EXPORT_SYMBOL_GPL(mips_cdmm_driver_register);
0237
0238
0239
0240
0241
0242
0243
0244 void mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)
0245 {
0246 driver_unregister(&drv->drv);
0247 }
0248 EXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264 struct mips_cdmm_bus {
0265 phys_addr_t phys;
0266 void __iomem *regs;
0267 unsigned int drbs;
0268 unsigned int drbs_reserved;
0269 bool discovered;
0270 bool offline;
0271 };
0272
0273 static struct mips_cdmm_bus mips_cdmm_boot_bus;
0274 static DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);
0275 static atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289 static struct mips_cdmm_bus *mips_cdmm_get_bus(void)
0290 {
0291 struct mips_cdmm_bus *bus, **bus_p;
0292 unsigned long flags;
0293 unsigned int cpu;
0294
0295 if (!cpu_has_cdmm)
0296 return ERR_PTR(-ENODEV);
0297
0298 cpu = smp_processor_id();
0299
0300 if (cpu == 0)
0301 return &mips_cdmm_boot_bus;
0302
0303
0304 bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);
0305 local_irq_save(flags);
0306 bus = *bus_p;
0307
0308 if (unlikely(!bus)) {
0309 bus = kzalloc(sizeof(*bus), GFP_ATOMIC);
0310 if (unlikely(!bus))
0311 bus = ERR_PTR(-ENOMEM);
0312 else
0313 *bus_p = bus;
0314 }
0315 local_irq_restore(flags);
0316 return bus;
0317 }
0318
0319
0320
0321
0322
0323
0324
0325 static phys_addr_t mips_cdmm_cur_base(void)
0326 {
0327 unsigned long cdmmbase = read_c0_cdmmbase();
0328
0329 if (!(cdmmbase & MIPS_CDMMBASE_EN))
0330 return 0;
0331
0332 return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)
0333 << MIPS_CDMMBASE_ADDR_START;
0334 }
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345 phys_addr_t __weak mips_cdmm_phys_base(void)
0346 {
0347 struct device_node *np;
0348 struct resource res;
0349 int err;
0350
0351 np = of_find_compatible_node(NULL, NULL, "mti,mips-cdmm");
0352 if (np) {
0353 err = of_address_to_resource(np, 0, &res);
0354 of_node_put(np);
0355 if (!err)
0356 return res.start;
0357 }
0358
0359 return 0;
0360 }
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372 static int mips_cdmm_setup(struct mips_cdmm_bus *bus)
0373 {
0374 unsigned long cdmmbase, flags;
0375 int ret = 0;
0376
0377 if (IS_ERR(bus))
0378 return PTR_ERR(bus);
0379
0380 local_irq_save(flags);
0381
0382 if (bus->offline) {
0383
0384 if (bus->phys == mips_cdmm_cur_base())
0385 goto out;
0386
0387
0388
0389
0390 bus->offline = false;
0391 } else if (bus->phys > 1) {
0392 goto out;
0393 }
0394
0395
0396 if (!bus->phys)
0397 bus->phys = mips_cdmm_cur_base();
0398
0399 if (!bus->phys)
0400 bus->phys = mips_cdmm_phys_base();
0401
0402 if (!bus->phys)
0403 bus->phys = mips_cdmm_default_base;
0404
0405 if (!bus->phys) {
0406 bus->phys = 1;
0407
0408
0409
0410
0411
0412 pr_err("cdmm%u: Failed to choose a physical base\n",
0413 smp_processor_id());
0414 }
0415
0416 if (bus->phys == 1) {
0417 ret = -ENOMEM;
0418 goto out;
0419 }
0420
0421 mips_cdmm_default_base = bus->phys;
0422
0423 pr_debug("cdmm%u: Enabling CDMM region at %pa\n",
0424 smp_processor_id(), &bus->phys);
0425
0426
0427 cdmmbase = read_c0_cdmmbase();
0428 cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;
0429 cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)
0430 << MIPS_CDMMBASE_ADDR_SHIFT;
0431 cdmmbase |= MIPS_CDMMBASE_EN;
0432 write_c0_cdmmbase(cdmmbase);
0433 tlbw_use_hazard();
0434
0435 bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);
0436 bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>
0437 MIPS_CDMMBASE_SIZE_SHIFT);
0438 bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);
0439
0440 out:
0441 local_irq_restore(flags);
0442 return ret;
0443 }
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462 void __iomem *mips_cdmm_early_probe(unsigned int dev_type)
0463 {
0464 struct mips_cdmm_bus *bus;
0465 void __iomem *cdmm;
0466 u32 acsr;
0467 unsigned int drb, type, size;
0468 int err;
0469
0470 if (WARN_ON(!dev_type))
0471 return IOMEM_ERR_PTR(-ENODEV);
0472
0473 bus = mips_cdmm_get_bus();
0474 err = mips_cdmm_setup(bus);
0475 if (err)
0476 return IOMEM_ERR_PTR(err);
0477
0478
0479 drb = bus->drbs_reserved;
0480 cdmm = bus->regs;
0481
0482
0483 for (; drb < bus->drbs; drb += size + 1) {
0484 acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
0485 type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
0486 if (type == dev_type)
0487 return cdmm + drb * CDMM_DRB_SIZE;
0488 size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
0489 }
0490
0491 return IOMEM_ERR_PTR(-ENODEV);
0492 }
0493 EXPORT_SYMBOL_GPL(mips_cdmm_early_probe);
0494
0495
0496
0497
0498
0499
0500
0501
0502 static void mips_cdmm_release(struct device *dev)
0503 {
0504 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
0505
0506 kfree(cdev);
0507 }
0508
0509
0510
0511
0512
0513 static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
0514 {
0515 void __iomem *cdmm;
0516 u32 acsr;
0517 unsigned int drb, type, size, rev;
0518 struct mips_cdmm_device *dev;
0519 unsigned int cpu = smp_processor_id();
0520 int ret = 0;
0521 int id = 0;
0522
0523
0524 drb = bus->drbs_reserved;
0525 cdmm = bus->regs;
0526
0527
0528 bus->discovered = true;
0529 pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
0530 for (; drb < bus->drbs; drb += size + 1) {
0531 acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
0532 type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
0533 size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
0534 rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT;
0535
0536 if (!type)
0537 continue;
0538
0539 pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",
0540 cpu, id, drb, drb * CDMM_DRB_SIZE,
0541 (drb + size + 1) * CDMM_DRB_SIZE - 1,
0542 type, rev);
0543
0544 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
0545 if (!dev)
0546 break;
0547
0548 dev->cpu = cpu;
0549 dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;
0550 dev->res.end = bus->phys +
0551 (drb + size + 1) * CDMM_DRB_SIZE - 1;
0552 dev->res.flags = IORESOURCE_MEM;
0553 dev->type = type;
0554 dev->rev = rev;
0555 dev->dev.parent = get_cpu_device(cpu);
0556 dev->dev.bus = &mips_cdmm_bustype;
0557 dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);
0558 dev->dev.release = mips_cdmm_release;
0559
0560 dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);
0561 ++id;
0562 ret = device_register(&dev->dev);
0563 if (ret)
0564 put_device(&dev->dev);
0565 }
0566 }
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592 #define BUILD_PERDEV_HELPER(_name) \
0593 static int mips_cdmm_##_name##_helper(struct device *dev, void *data) \
0594 { \
0595 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \
0596 struct mips_cdmm_driver *cdrv; \
0597 unsigned int cpu = *(unsigned int *)data; \
0598 \
0599 if (cdev->cpu != cpu || !dev->driver) \
0600 return 0; \
0601 \
0602 cdrv = to_mips_cdmm_driver(dev->driver); \
0603 if (!cdrv->_name) \
0604 return 0; \
0605 return cdrv->_name(cdev); \
0606 }
0607
0608
0609 BUILD_PERDEV_HELPER(cpu_down)
0610 BUILD_PERDEV_HELPER(cpu_up)
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620 static int mips_cdmm_cpu_down_prep(unsigned int cpu)
0621 {
0622 struct mips_cdmm_bus *bus;
0623 long ret;
0624
0625
0626 ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
0627 mips_cdmm_cpu_down_helper);
0628
0629
0630
0631
0632
0633 bus = mips_cdmm_get_bus();
0634 if (!IS_ERR(bus))
0635 bus->offline = true;
0636
0637 return ret;
0638 }
0639
0640
0641
0642
0643
0644
0645
0646
0647
0648
0649
0650
0651
0652 static int mips_cdmm_cpu_online(unsigned int cpu)
0653 {
0654 struct mips_cdmm_bus *bus;
0655 long ret;
0656
0657 bus = mips_cdmm_get_bus();
0658 ret = mips_cdmm_setup(bus);
0659 if (ret)
0660 return ret;
0661
0662
0663 bus->offline = false;
0664
0665 if (!bus->discovered)
0666 mips_cdmm_bus_discover(bus);
0667 else
0668
0669 ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
0670 mips_cdmm_cpu_up_helper);
0671
0672 return ret;
0673 }
0674
0675
0676
0677
0678
0679
0680
0681 static int __init mips_cdmm_init(void)
0682 {
0683 int ret;
0684
0685
0686 ret = bus_register(&mips_cdmm_bustype);
0687 if (ret)
0688 return ret;
0689
0690
0691 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",
0692 mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);
0693 if (ret < 0)
0694 pr_warn("cdmm: Failed to register CPU notifier\n");
0695
0696 return ret;
0697 }
0698 subsys_initcall(mips_cdmm_init);