^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Generic heartbeat driver for regular LED banks
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2007 - 2010 Paul Mundt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Most SH reference boards include a number of individual LEDs that can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * be independently controlled (either via a pre-defined hardware
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * function or via the LED class, if desired -- the hardware tends to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * encapsulate some of the same "triggers" that the LED class supports,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * so there's not too much value in it).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * Additionally, most of these boards also have a LED bank that we've
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * traditionally used for strobing the load average. This use case is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * handled by this driver, rather than giving each LED bit position its
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * own struct device.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/sched/loadavg.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/timer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <asm/heartbeat.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define DRV_NAME "heartbeat"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define DRV_VERSION "0.1.2"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static unsigned char default_bit_pos[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) static inline void heartbeat_toggle_bit(struct heartbeat_data *hd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) unsigned bit, unsigned int inverted)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) unsigned int new;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) new = (1 << hd->bit_pos[bit]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) if (inverted)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) new = ~new;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) new &= hd->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) switch (hd->regsize) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) case 32:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) new |= ioread32(hd->base) & ~hd->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) iowrite32(new, hd->base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) case 16:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) new |= ioread16(hd->base) & ~hd->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) iowrite16(new, hd->base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) new |= ioread8(hd->base) & ~hd->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) iowrite8(new, hd->base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) static void heartbeat_timer(struct timer_list *t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct heartbeat_data *hd = from_timer(hd, t, timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static unsigned bit = 0, up = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) heartbeat_toggle_bit(hd, bit, hd->flags & HEARTBEAT_INVERTED);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) bit += up;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if ((bit == 0) || (bit == (hd->nr_bits)-1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) up = -up;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) mod_timer(&hd->timer, jiffies + (110 - ((300 << FSHIFT) /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) ((avenrun[0] / 5) + (3 << FSHIFT)))));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) static int heartbeat_drv_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) struct resource *res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) struct heartbeat_data *hd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (unlikely(pdev->num_resources != 1)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) dev_err(&pdev->dev, "invalid number of resources\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) if (unlikely(res == NULL)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) dev_err(&pdev->dev, "invalid resource\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (pdev->dev.platform_data) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) hd = pdev->dev.platform_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) hd = kzalloc(sizeof(struct heartbeat_data), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (unlikely(!hd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) hd->base = ioremap(res->start, resource_size(res));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) if (unlikely(!hd->base)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) dev_err(&pdev->dev, "ioremap failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) if (!pdev->dev.platform_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) kfree(hd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (!hd->nr_bits) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) hd->bit_pos = default_bit_pos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) hd->nr_bits = ARRAY_SIZE(default_bit_pos);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) hd->mask = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) for (i = 0; i < hd->nr_bits; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) hd->mask |= (1 << hd->bit_pos[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) if (!hd->regsize) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) switch (res->flags & IORESOURCE_MEM_TYPE_MASK) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) case IORESOURCE_MEM_32BIT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) hd->regsize = 32;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) case IORESOURCE_MEM_16BIT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) hd->regsize = 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) case IORESOURCE_MEM_8BIT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) hd->regsize = 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) timer_setup(&hd->timer, heartbeat_timer, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) platform_set_drvdata(pdev, hd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return mod_timer(&hd->timer, jiffies + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static struct platform_driver heartbeat_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) .probe = heartbeat_drv_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) .name = DRV_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) .suppress_bind_attrs = true,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) static int __init heartbeat_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) return platform_driver_register(&heartbeat_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) device_initcall(heartbeat_init);