1
2
3
4
5
6
7
8
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/of_address.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/smp.h>
21#include <asm/cdmm.h>
22#include <asm/hazards.h>
23#include <asm/mipsregs.h>
24
25
26#define CDMM_ACSR_DEVTYPE_SHIFT 24
27#define CDMM_ACSR_DEVTYPE (255ul << CDMM_ACSR_DEVTYPE_SHIFT)
28#define CDMM_ACSR_DEVSIZE_SHIFT 16
29#define CDMM_ACSR_DEVSIZE (31ul << CDMM_ACSR_DEVSIZE_SHIFT)
30#define CDMM_ACSR_DEVREV_SHIFT 12
31#define CDMM_ACSR_DEVREV (15ul << CDMM_ACSR_DEVREV_SHIFT)
32#define CDMM_ACSR_UW (1ul << 3)
33#define CDMM_ACSR_UR (1ul << 2)
34#define CDMM_ACSR_SW (1ul << 1)
35#define CDMM_ACSR_SR (1ul << 0)
36
37
38#define CDMM_DRB_SIZE 64
39
40#define to_mips_cdmm_driver(d) container_of(d, struct mips_cdmm_driver, drv)
41
42
43static phys_addr_t mips_cdmm_default_base;
44
45
46
47static const struct mips_cdmm_device_id *
48mips_cdmm_lookup(const struct mips_cdmm_device_id *table,
49 struct mips_cdmm_device *dev)
50{
51 int ret = 0;
52
53 for (; table->type; ++table) {
54 ret = (dev->type == table->type);
55 if (ret)
56 break;
57 }
58
59 return ret ? table : NULL;
60}
61
62static int mips_cdmm_match(struct device *dev, struct device_driver *drv)
63{
64 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
65 struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);
66
67 return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;
68}
69
70static int mips_cdmm_uevent(struct device *dev, struct kobj_uevent_env *env)
71{
72 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
73 int retval = 0;
74
75 retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);
76 if (retval)
77 return retval;
78
79 retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);
80 if (retval)
81 return retval;
82
83 retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);
84 if (retval)
85 return retval;
86
87 retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);
88 return retval;
89}
90
91
92
93#define CDMM_ATTR(name, fmt, arg...) \
94static ssize_t name##_show(struct device *_dev, \
95 struct device_attribute *attr, char *buf) \
96{ \
97 struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev); \
98 return sprintf(buf, fmt, arg); \
99} \
100static DEVICE_ATTR_RO(name);
101
102CDMM_ATTR(cpu, "%u\n", dev->cpu);
103CDMM_ATTR(type, "0x%02x\n", dev->type);
104CDMM_ATTR(revision, "%u\n", dev->rev);
105CDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);
106CDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",
107 (unsigned long long)dev->res.start,
108 (unsigned long long)dev->res.end,
109 dev->res.flags);
110
111static struct attribute *mips_cdmm_dev_attrs[] = {
112 &dev_attr_cpu.attr,
113 &dev_attr_type.attr,
114 &dev_attr_revision.attr,
115 &dev_attr_modalias.attr,
116 &dev_attr_resource.attr,
117 NULL,
118};
119ATTRIBUTE_GROUPS(mips_cdmm_dev);
120
121struct bus_type mips_cdmm_bustype = {
122 .name = "cdmm",
123 .dev_groups = mips_cdmm_dev_groups,
124 .match = mips_cdmm_match,
125 .uevent = mips_cdmm_uevent,
126};
127EXPORT_SYMBOL_GPL(mips_cdmm_bustype);
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144struct mips_cdmm_work_dev {
145 void *fn;
146 struct mips_cdmm_device *dev;
147};
148
149
150
151
152
153
154
155
156static long mips_cdmm_void_work(void *data)
157{
158 struct mips_cdmm_work_dev *work = data;
159 void (*fn)(struct mips_cdmm_device *) = work->fn;
160
161 fn(work->dev);
162 return 0;
163}
164
165
166
167
168
169
170
171
172static long mips_cdmm_int_work(void *data)
173{
174 struct mips_cdmm_work_dev *work = data;
175 int (*fn)(struct mips_cdmm_device *) = work->fn;
176
177 return fn(work->dev);
178}
179
180#define _BUILD_RET_void
181#define _BUILD_RET_int return
182
183
184
185
186
187
188
189
190
191
192#define BUILD_PERCPU_HELPER(_ret, _name) \
193static _ret mips_cdmm_##_name(struct device *dev) \
194{ \
195 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \
196 struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \
197 struct mips_cdmm_work_dev work = { \
198 .fn = cdrv->_name, \
199 .dev = cdev, \
200 }; \
201 \
202 _BUILD_RET_##_ret work_on_cpu(cdev->cpu, \
203 mips_cdmm_##_ret##_work, &work); \
204}
205
206
207BUILD_PERCPU_HELPER(int, probe)
208BUILD_PERCPU_HELPER(int, remove)
209BUILD_PERCPU_HELPER(void, shutdown)
210
211
212
213
214
215
216
217
218
219
220
221
222
223int mips_cdmm_driver_register(struct mips_cdmm_driver *drv)
224{
225 drv->drv.bus = &mips_cdmm_bustype;
226
227 if (drv->probe)
228 drv->drv.probe = mips_cdmm_probe;
229 if (drv->remove)
230 drv->drv.remove = mips_cdmm_remove;
231 if (drv->shutdown)
232 drv->drv.shutdown = mips_cdmm_shutdown;
233
234 return driver_register(&drv->drv);
235}
236EXPORT_SYMBOL_GPL(mips_cdmm_driver_register);
237
238
239
240
241
242
243
244void mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)
245{
246 driver_unregister(&drv->drv);
247}
248EXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264struct mips_cdmm_bus {
265 phys_addr_t phys;
266 void __iomem *regs;
267 unsigned int drbs;
268 unsigned int drbs_reserved;
269 bool discovered;
270 bool offline;
271};
272
273static struct mips_cdmm_bus mips_cdmm_boot_bus;
274static DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);
275static atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);
276
277
278
279
280
281
282
283
284
285
286
287
288
289static struct mips_cdmm_bus *mips_cdmm_get_bus(void)
290{
291 struct mips_cdmm_bus *bus, **bus_p;
292 unsigned long flags;
293 unsigned int cpu;
294
295 if (!cpu_has_cdmm)
296 return ERR_PTR(-ENODEV);
297
298 cpu = smp_processor_id();
299
300 if (cpu == 0)
301 return &mips_cdmm_boot_bus;
302
303
304 bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);
305 local_irq_save(flags);
306 bus = *bus_p;
307
308 if (unlikely(!bus)) {
309 bus = kzalloc(sizeof(*bus), GFP_ATOMIC);
310 if (unlikely(!bus))
311 bus = ERR_PTR(-ENOMEM);
312 else
313 *bus_p = bus;
314 }
315 local_irq_restore(flags);
316 return bus;
317}
318
319
320
321
322
323
324
325static phys_addr_t mips_cdmm_cur_base(void)
326{
327 unsigned long cdmmbase = read_c0_cdmmbase();
328
329 if (!(cdmmbase & MIPS_CDMMBASE_EN))
330 return 0;
331
332 return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)
333 << MIPS_CDMMBASE_ADDR_START;
334}
335
336
337
338
339
340
341
342
343
344
345phys_addr_t __weak mips_cdmm_phys_base(void)
346{
347 struct device_node *np;
348 struct resource res;
349 int err;
350
351 np = of_find_compatible_node(NULL, NULL, "mti,mips-cdmm");
352 if (np) {
353 err = of_address_to_resource(np, 0, &res);
354 if (!err)
355 return res.start;
356 }
357
358 return 0;
359}
360
361
362
363
364
365
366
367
368
369
370
371static int mips_cdmm_setup(struct mips_cdmm_bus *bus)
372{
373 unsigned long cdmmbase, flags;
374 int ret = 0;
375
376 if (IS_ERR(bus))
377 return PTR_ERR(bus);
378
379 local_irq_save(flags);
380
381 if (bus->offline) {
382
383 if (bus->phys == mips_cdmm_cur_base())
384 goto out;
385
386
387
388
389 bus->offline = false;
390 } else if (bus->phys > 1) {
391 goto out;
392 }
393
394
395 if (!bus->phys)
396 bus->phys = mips_cdmm_cur_base();
397
398 if (!bus->phys)
399 bus->phys = mips_cdmm_phys_base();
400
401 if (!bus->phys)
402 bus->phys = mips_cdmm_default_base;
403
404 if (!bus->phys) {
405 bus->phys = 1;
406
407
408
409
410
411 pr_err("cdmm%u: Failed to choose a physical base\n",
412 smp_processor_id());
413 }
414
415 if (bus->phys == 1) {
416 ret = -ENOMEM;
417 goto out;
418 }
419
420 mips_cdmm_default_base = bus->phys;
421
422 pr_debug("cdmm%u: Enabling CDMM region at %pa\n",
423 smp_processor_id(), &bus->phys);
424
425
426 cdmmbase = read_c0_cdmmbase();
427 cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;
428 cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)
429 << MIPS_CDMMBASE_ADDR_SHIFT;
430 cdmmbase |= MIPS_CDMMBASE_EN;
431 write_c0_cdmmbase(cdmmbase);
432 tlbw_use_hazard();
433
434 bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);
435 bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>
436 MIPS_CDMMBASE_SIZE_SHIFT);
437 bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);
438
439out:
440 local_irq_restore(flags);
441 return ret;
442}
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461void __iomem *mips_cdmm_early_probe(unsigned int dev_type)
462{
463 struct mips_cdmm_bus *bus;
464 void __iomem *cdmm;
465 u32 acsr;
466 unsigned int drb, type, size;
467 int err;
468
469 if (WARN_ON(!dev_type))
470 return IOMEM_ERR_PTR(-ENODEV);
471
472 bus = mips_cdmm_get_bus();
473 err = mips_cdmm_setup(bus);
474 if (err)
475 return IOMEM_ERR_PTR(err);
476
477
478 drb = bus->drbs_reserved;
479 cdmm = bus->regs;
480
481
482 for (; drb < bus->drbs; drb += size + 1) {
483 acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
484 type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
485 if (type == dev_type)
486 return cdmm + drb * CDMM_DRB_SIZE;
487 size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
488 }
489
490 return IOMEM_ERR_PTR(-ENODEV);
491}
492EXPORT_SYMBOL_GPL(mips_cdmm_early_probe);
493
494
495
496
497
498
499
500
501static void mips_cdmm_release(struct device *dev)
502{
503 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);
504
505 kfree(cdev);
506}
507
508
509
510
511
512static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)
513{
514 void __iomem *cdmm;
515 u32 acsr;
516 unsigned int drb, type, size, rev;
517 struct mips_cdmm_device *dev;
518 unsigned int cpu = smp_processor_id();
519 int ret = 0;
520 int id = 0;
521
522
523 drb = bus->drbs_reserved;
524 cdmm = bus->regs;
525
526
527 bus->discovered = true;
528 pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);
529 for (; drb < bus->drbs; drb += size + 1) {
530 acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);
531 type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;
532 size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;
533 rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT;
534
535 if (!type)
536 continue;
537
538 pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",
539 cpu, id, drb, drb * CDMM_DRB_SIZE,
540 (drb + size + 1) * CDMM_DRB_SIZE - 1,
541 type, rev);
542
543 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
544 if (!dev)
545 break;
546
547 dev->cpu = cpu;
548 dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;
549 dev->res.end = bus->phys +
550 (drb + size + 1) * CDMM_DRB_SIZE - 1;
551 dev->res.flags = IORESOURCE_MEM;
552 dev->type = type;
553 dev->rev = rev;
554 dev->dev.parent = get_cpu_device(cpu);
555 dev->dev.bus = &mips_cdmm_bustype;
556 dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);
557 dev->dev.release = mips_cdmm_release;
558
559 dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);
560 ++id;
561 ret = device_register(&dev->dev);
562 if (ret)
563 put_device(&dev->dev);
564 }
565}
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591#define BUILD_PERDEV_HELPER(_name) \
592static int mips_cdmm_##_name##_helper(struct device *dev, void *data) \
593{ \
594 struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \
595 struct mips_cdmm_driver *cdrv; \
596 unsigned int cpu = *(unsigned int *)data; \
597 \
598 if (cdev->cpu != cpu || !dev->driver) \
599 return 0; \
600 \
601 cdrv = to_mips_cdmm_driver(dev->driver); \
602 if (!cdrv->_name) \
603 return 0; \
604 return cdrv->_name(cdev); \
605}
606
607
608BUILD_PERDEV_HELPER(cpu_down)
609BUILD_PERDEV_HELPER(cpu_up)
610
611
612
613
614
615
616
617
618
619static int mips_cdmm_cpu_down_prep(unsigned int cpu)
620{
621 struct mips_cdmm_bus *bus;
622 long ret;
623
624
625 ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
626 mips_cdmm_cpu_down_helper);
627
628
629
630
631
632 bus = mips_cdmm_get_bus();
633 if (!IS_ERR(bus))
634 bus->offline = true;
635
636 return ret;
637}
638
639
640
641
642
643
644
645
646
647
648
649
650
651static int mips_cdmm_cpu_online(unsigned int cpu)
652{
653 struct mips_cdmm_bus *bus;
654 long ret;
655
656 bus = mips_cdmm_get_bus();
657 ret = mips_cdmm_setup(bus);
658 if (ret)
659 return ret;
660
661
662 bus->offline = false;
663
664 if (!bus->discovered)
665 mips_cdmm_bus_discover(bus);
666 else
667
668 ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
669 mips_cdmm_cpu_up_helper);
670
671 return ret;
672}
673
674
675
676
677
678
679
680static int __init mips_cdmm_init(void)
681{
682 int ret;
683
684
685 ret = bus_register(&mips_cdmm_bustype);
686 if (ret)
687 return ret;
688
689
690 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",
691 mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);
692 if (ret < 0)
693 pr_warn("cdmm: Failed to register CPU notifier\n");
694
695 return ret;
696}
697subsys_initcall(mips_cdmm_init);
698