mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
3.6 KiB
152 lines
3.6 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Generic heartbeat driver for regular LED banks |
|
* |
|
* Copyright (C) 2007 - 2010 Paul Mundt |
|
* |
|
* Most SH reference boards include a number of individual LEDs that can |
|
* be independently controlled (either via a pre-defined hardware |
|
* function or via the LED class, if desired -- the hardware tends to |
|
* encapsulate some of the same "triggers" that the LED class supports, |
|
* so there's not too much value in it). |
|
* |
|
* Additionally, most of these boards also have a LED bank that we've |
|
* traditionally used for strobing the load average. This use case is |
|
* handled by this driver, rather than giving each LED bit position its |
|
* own struct device. |
|
*/ |
|
#include <linux/init.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/sched.h> |
|
#include <linux/sched/loadavg.h> |
|
#include <linux/timer.h> |
|
#include <linux/io.h> |
|
#include <linux/slab.h> |
|
#include <asm/heartbeat.h> |
|
|
|
#define DRV_NAME "heartbeat" |
|
#define DRV_VERSION "0.1.2" |
|
|
|
static unsigned char default_bit_pos[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; |
|
|
|
static inline void heartbeat_toggle_bit(struct heartbeat_data *hd, |
|
unsigned bit, unsigned int inverted) |
|
{ |
|
unsigned int new; |
|
|
|
new = (1 << hd->bit_pos[bit]); |
|
if (inverted) |
|
new = ~new; |
|
|
|
new &= hd->mask; |
|
|
|
switch (hd->regsize) { |
|
case 32: |
|
new |= ioread32(hd->base) & ~hd->mask; |
|
iowrite32(new, hd->base); |
|
break; |
|
case 16: |
|
new |= ioread16(hd->base) & ~hd->mask; |
|
iowrite16(new, hd->base); |
|
break; |
|
default: |
|
new |= ioread8(hd->base) & ~hd->mask; |
|
iowrite8(new, hd->base); |
|
break; |
|
} |
|
} |
|
|
|
static void heartbeat_timer(struct timer_list *t) |
|
{ |
|
struct heartbeat_data *hd = from_timer(hd, t, timer); |
|
static unsigned bit = 0, up = 1; |
|
|
|
heartbeat_toggle_bit(hd, bit, hd->flags & HEARTBEAT_INVERTED); |
|
|
|
bit += up; |
|
if ((bit == 0) || (bit == (hd->nr_bits)-1)) |
|
up = -up; |
|
|
|
mod_timer(&hd->timer, jiffies + (110 - ((300 << FSHIFT) / |
|
((avenrun[0] / 5) + (3 << FSHIFT))))); |
|
} |
|
|
|
static int heartbeat_drv_probe(struct platform_device *pdev) |
|
{ |
|
struct resource *res; |
|
struct heartbeat_data *hd; |
|
int i; |
|
|
|
if (unlikely(pdev->num_resources != 1)) { |
|
dev_err(&pdev->dev, "invalid number of resources\n"); |
|
return -EINVAL; |
|
} |
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|
if (unlikely(res == NULL)) { |
|
dev_err(&pdev->dev, "invalid resource\n"); |
|
return -EINVAL; |
|
} |
|
|
|
if (pdev->dev.platform_data) { |
|
hd = pdev->dev.platform_data; |
|
} else { |
|
hd = kzalloc(sizeof(struct heartbeat_data), GFP_KERNEL); |
|
if (unlikely(!hd)) |
|
return -ENOMEM; |
|
} |
|
|
|
hd->base = ioremap(res->start, resource_size(res)); |
|
if (unlikely(!hd->base)) { |
|
dev_err(&pdev->dev, "ioremap failed\n"); |
|
|
|
if (!pdev->dev.platform_data) |
|
kfree(hd); |
|
|
|
return -ENXIO; |
|
} |
|
|
|
if (!hd->nr_bits) { |
|
hd->bit_pos = default_bit_pos; |
|
hd->nr_bits = ARRAY_SIZE(default_bit_pos); |
|
} |
|
|
|
hd->mask = 0; |
|
for (i = 0; i < hd->nr_bits; i++) |
|
hd->mask |= (1 << hd->bit_pos[i]); |
|
|
|
if (!hd->regsize) { |
|
switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { |
|
case IORESOURCE_MEM_32BIT: |
|
hd->regsize = 32; |
|
break; |
|
case IORESOURCE_MEM_16BIT: |
|
hd->regsize = 16; |
|
break; |
|
case IORESOURCE_MEM_8BIT: |
|
default: |
|
hd->regsize = 8; |
|
break; |
|
} |
|
} |
|
|
|
timer_setup(&hd->timer, heartbeat_timer, 0); |
|
platform_set_drvdata(pdev, hd); |
|
|
|
return mod_timer(&hd->timer, jiffies + 1); |
|
} |
|
|
|
static struct platform_driver heartbeat_driver = { |
|
.probe = heartbeat_drv_probe, |
|
.driver = { |
|
.name = DRV_NAME, |
|
.suppress_bind_attrs = true, |
|
}, |
|
}; |
|
|
|
static int __init heartbeat_init(void) |
|
{ |
|
printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION); |
|
return platform_driver_register(&heartbeat_driver); |
|
} |
|
device_initcall(heartbeat_init);
|
|
|