1 // SPDX-License-Identifier: GPL-2.0
3 * Generic heartbeat driver for regular LED banks
5 * Copyright (C) 2007 - 2010 Paul Mundt
7 * Most SH reference boards include a number of individual LEDs that can
8 * be independently controlled (either via a pre-defined hardware
9 * function or via the LED class, if desired -- the hardware tends to
10 * encapsulate some of the same "triggers" that the LED class supports,
11 * so there's not too much value in it).
13 * Additionally, most of these boards also have a LED bank that we've
14 * traditionally used for strobing the load average. This use case is
15 * handled by this driver, rather than giving each LED bit position its
18 #include <linux/init.h>
19 #include <linux/platform_device.h>
20 #include <linux/sched.h>
21 #include <linux/sched/loadavg.h>
22 #include <linux/timer.h>
24 #include <linux/slab.h>
25 #include <asm/heartbeat.h>
27 #define DRV_NAME "heartbeat"
28 #define DRV_VERSION "0.1.2"
30 static unsigned char default_bit_pos
[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
32 static inline void heartbeat_toggle_bit(struct heartbeat_data
*hd
,
33 unsigned bit
, unsigned int inverted
)
37 new = (1 << hd
->bit_pos
[bit
]);
43 switch (hd
->regsize
) {
45 new |= ioread32(hd
->base
) & ~hd
->mask
;
46 iowrite32(new, hd
->base
);
49 new |= ioread16(hd
->base
) & ~hd
->mask
;
50 iowrite16(new, hd
->base
);
53 new |= ioread8(hd
->base
) & ~hd
->mask
;
54 iowrite8(new, hd
->base
);
59 static void heartbeat_timer(struct timer_list
*t
)
61 struct heartbeat_data
*hd
= from_timer(hd
, t
, timer
);
62 static unsigned bit
= 0, up
= 1;
64 heartbeat_toggle_bit(hd
, bit
, hd
->flags
& HEARTBEAT_INVERTED
);
67 if ((bit
== 0) || (bit
== (hd
->nr_bits
)-1))
70 mod_timer(&hd
->timer
, jiffies
+ (110 - ((300 << FSHIFT
) /
71 ((avenrun
[0] / 5) + (3 << FSHIFT
)))));
74 static int heartbeat_drv_probe(struct platform_device
*pdev
)
77 struct heartbeat_data
*hd
;
80 if (unlikely(pdev
->num_resources
!= 1)) {
81 dev_err(&pdev
->dev
, "invalid number of resources\n");
85 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
86 if (unlikely(res
== NULL
)) {
87 dev_err(&pdev
->dev
, "invalid resource\n");
91 if (pdev
->dev
.platform_data
) {
92 hd
= pdev
->dev
.platform_data
;
94 hd
= kzalloc(sizeof(struct heartbeat_data
), GFP_KERNEL
);
99 hd
->base
= ioremap_nocache(res
->start
, resource_size(res
));
100 if (unlikely(!hd
->base
)) {
101 dev_err(&pdev
->dev
, "ioremap failed\n");
103 if (!pdev
->dev
.platform_data
)
110 hd
->bit_pos
= default_bit_pos
;
111 hd
->nr_bits
= ARRAY_SIZE(default_bit_pos
);
115 for (i
= 0; i
< hd
->nr_bits
; i
++)
116 hd
->mask
|= (1 << hd
->bit_pos
[i
]);
119 switch (res
->flags
& IORESOURCE_MEM_TYPE_MASK
) {
120 case IORESOURCE_MEM_32BIT
:
123 case IORESOURCE_MEM_16BIT
:
126 case IORESOURCE_MEM_8BIT
:
133 timer_setup(&hd
->timer
, heartbeat_timer
, 0);
134 platform_set_drvdata(pdev
, hd
);
136 return mod_timer(&hd
->timer
, jiffies
+ 1);
139 static struct platform_driver heartbeat_driver
= {
140 .probe
= heartbeat_drv_probe
,
143 .suppress_bind_attrs
= true,
147 static int __init
heartbeat_init(void)
149 printk(KERN_NOTICE DRV_NAME
": version %s loaded\n", DRV_VERSION
);
150 return platform_driver_register(&heartbeat_driver
);
152 device_initcall(heartbeat_init
);