linux/arch/sh/drivers/heartbeat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Generic heartbeat driver for regular LED banks
   4 *
   5 * Copyright (C) 2007 - 2010  Paul Mundt
   6 *
   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).
  12 *
  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
  16 * own struct device.
  17 */
  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>
  23#include <linux/io.h>
  24#include <linux/slab.h>
  25#include <asm/heartbeat.h>
  26
  27#define DRV_NAME "heartbeat"
  28#define DRV_VERSION "0.1.2"
  29
  30static unsigned char default_bit_pos[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
  31
  32static inline void heartbeat_toggle_bit(struct heartbeat_data *hd,
  33                                        unsigned bit, unsigned int inverted)
  34{
  35        unsigned int new;
  36
  37        new = (1 << hd->bit_pos[bit]);
  38        if (inverted)
  39                new = ~new;
  40
  41        new &= hd->mask;
  42
  43        switch (hd->regsize) {
  44        case 32:
  45                new |= ioread32(hd->base) & ~hd->mask;
  46                iowrite32(new, hd->base);
  47                break;
  48        case 16:
  49                new |= ioread16(hd->base) & ~hd->mask;
  50                iowrite16(new, hd->base);
  51                break;
  52        default:
  53                new |= ioread8(hd->base) & ~hd->mask;
  54                iowrite8(new, hd->base);
  55                break;
  56        }
  57}
  58
  59static void heartbeat_timer(struct timer_list *t)
  60{
  61        struct heartbeat_data *hd = from_timer(hd, t, timer);
  62        static unsigned bit = 0, up = 1;
  63
  64        heartbeat_toggle_bit(hd, bit, hd->flags & HEARTBEAT_INVERTED);
  65
  66        bit += up;
  67        if ((bit == 0) || (bit == (hd->nr_bits)-1))
  68                up = -up;
  69
  70        mod_timer(&hd->timer, jiffies + (110 - ((300 << FSHIFT) /
  71                        ((avenrun[0] / 5) + (3 << FSHIFT)))));
  72}
  73
  74static int heartbeat_drv_probe(struct platform_device *pdev)
  75{
  76        struct resource *res;
  77        struct heartbeat_data *hd;
  78        int i;
  79
  80        if (unlikely(pdev->num_resources != 1)) {
  81                dev_err(&pdev->dev, "invalid number of resources\n");
  82                return -EINVAL;
  83        }
  84
  85        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  86        if (unlikely(res == NULL)) {
  87                dev_err(&pdev->dev, "invalid resource\n");
  88                return -EINVAL;
  89        }
  90
  91        if (pdev->dev.platform_data) {
  92                hd = pdev->dev.platform_data;
  93        } else {
  94                hd = kzalloc(sizeof(struct heartbeat_data), GFP_KERNEL);
  95                if (unlikely(!hd))
  96                        return -ENOMEM;
  97        }
  98
  99        hd->base = ioremap(res->start, resource_size(res));
 100        if (unlikely(!hd->base)) {
 101                dev_err(&pdev->dev, "ioremap failed\n");
 102
 103                if (!pdev->dev.platform_data)
 104                        kfree(hd);
 105
 106                return -ENXIO;
 107        }
 108
 109        if (!hd->nr_bits) {
 110                hd->bit_pos = default_bit_pos;
 111                hd->nr_bits = ARRAY_SIZE(default_bit_pos);
 112        }
 113
 114        hd->mask = 0;
 115        for (i = 0; i < hd->nr_bits; i++)
 116                hd->mask |= (1 << hd->bit_pos[i]);
 117
 118        if (!hd->regsize) {
 119                switch (res->flags & IORESOURCE_MEM_TYPE_MASK) {
 120                case IORESOURCE_MEM_32BIT:
 121                        hd->regsize = 32;
 122                        break;
 123                case IORESOURCE_MEM_16BIT:
 124                        hd->regsize = 16;
 125                        break;
 126                case IORESOURCE_MEM_8BIT:
 127                default:
 128                        hd->regsize = 8;
 129                        break;
 130                }
 131        }
 132
 133        timer_setup(&hd->timer, heartbeat_timer, 0);
 134        platform_set_drvdata(pdev, hd);
 135
 136        return mod_timer(&hd->timer, jiffies + 1);
 137}
 138
 139static struct platform_driver heartbeat_driver = {
 140        .probe          = heartbeat_drv_probe,
 141        .driver         = {
 142                .name                   = DRV_NAME,
 143                .suppress_bind_attrs    = true,
 144        },
 145};
 146
 147static int __init heartbeat_init(void)
 148{
 149        printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);
 150        return platform_driver_register(&heartbeat_driver);
 151}
 152device_initcall(heartbeat_init);
 153