root/drivers/leds/leds-asic3.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. brightness_set
  2. blink_set
  3. asic3_led_probe
  4. asic3_led_remove
  5. asic3_led_suspend
  6. asic3_led_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *  Copyright (C) 2011 Paul Parsons <lost.distance@yahoo.com>
   4  */
   5 
   6 #include <linux/kernel.h>
   7 #include <linux/platform_device.h>
   8 #include <linux/leds.h>
   9 #include <linux/slab.h>
  10 
  11 #include <linux/mfd/asic3.h>
  12 #include <linux/mfd/core.h>
  13 #include <linux/module.h>
  14 
  15 /*
  16  *      The HTC ASIC3 LED GPIOs are inputs, not outputs.
  17  *      Hence we turn the LEDs on/off via the TimeBase register.
  18  */
  19 
  20 /*
  21  *      When TimeBase is 4 the clock resolution is about 32Hz.
  22  *      This driver supports hardware blinking with an on+off
  23  *      period from 62ms (2 clocks) to 125s (4000 clocks).
  24  */
  25 #define MS_TO_CLK(ms)   DIV_ROUND_CLOSEST(((ms)*1024), 32000)
  26 #define CLK_TO_MS(clk)  (((clk)*32000)/1024)
  27 #define MAX_CLK         4000            /* Fits into 12-bit Time registers */
  28 #define MAX_MS          CLK_TO_MS(MAX_CLK)
  29 
  30 static const unsigned int led_n_base[ASIC3_NUM_LEDS] = {
  31         [0] = ASIC3_LED_0_Base,
  32         [1] = ASIC3_LED_1_Base,
  33         [2] = ASIC3_LED_2_Base,
  34 };
  35 
  36 static void brightness_set(struct led_classdev *cdev,
  37         enum led_brightness value)
  38 {
  39         struct platform_device *pdev = to_platform_device(cdev->dev->parent);
  40         const struct mfd_cell *cell = mfd_get_cell(pdev);
  41         struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  42         u32 timebase;
  43         unsigned int base;
  44 
  45         timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4);
  46 
  47         base = led_n_base[cell->id];
  48         asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32);
  49         asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32);
  50         asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0);
  51         asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase);
  52 }
  53 
  54 static int blink_set(struct led_classdev *cdev,
  55         unsigned long *delay_on,
  56         unsigned long *delay_off)
  57 {
  58         struct platform_device *pdev = to_platform_device(cdev->dev->parent);
  59         const struct mfd_cell *cell = mfd_get_cell(pdev);
  60         struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  61         u32 on;
  62         u32 off;
  63         unsigned int base;
  64 
  65         if (*delay_on > MAX_MS || *delay_off > MAX_MS)
  66                 return -EINVAL;
  67 
  68         if (*delay_on == 0 && *delay_off == 0) {
  69                 /* If both are zero then a sensible default should be chosen */
  70                 on = MS_TO_CLK(500);
  71                 off = MS_TO_CLK(500);
  72         } else {
  73                 on = MS_TO_CLK(*delay_on);
  74                 off = MS_TO_CLK(*delay_off);
  75                 if ((on + off) > MAX_CLK)
  76                         return -EINVAL;
  77         }
  78 
  79         base = led_n_base[cell->id];
  80         asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off));
  81         asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on);
  82         asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0);
  83         asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4));
  84 
  85         *delay_on = CLK_TO_MS(on);
  86         *delay_off = CLK_TO_MS(off);
  87 
  88         return 0;
  89 }
  90 
  91 static int asic3_led_probe(struct platform_device *pdev)
  92 {
  93         struct asic3_led *led = dev_get_platdata(&pdev->dev);
  94         int ret;
  95 
  96         ret = mfd_cell_enable(pdev);
  97         if (ret < 0)
  98                 return ret;
  99 
 100         led->cdev = devm_kzalloc(&pdev->dev, sizeof(struct led_classdev),
 101                                 GFP_KERNEL);
 102         if (!led->cdev) {
 103                 ret = -ENOMEM;
 104                 goto out;
 105         }
 106 
 107         led->cdev->name = led->name;
 108         led->cdev->flags = LED_CORE_SUSPENDRESUME;
 109         led->cdev->brightness_set = brightness_set;
 110         led->cdev->blink_set = blink_set;
 111         led->cdev->default_trigger = led->default_trigger;
 112 
 113         ret = led_classdev_register(&pdev->dev, led->cdev);
 114         if (ret < 0)
 115                 goto out;
 116 
 117         return 0;
 118 
 119 out:
 120         (void) mfd_cell_disable(pdev);
 121         return ret;
 122 }
 123 
 124 static int asic3_led_remove(struct platform_device *pdev)
 125 {
 126         struct asic3_led *led = dev_get_platdata(&pdev->dev);
 127 
 128         led_classdev_unregister(led->cdev);
 129 
 130         return mfd_cell_disable(pdev);
 131 }
 132 
 133 #ifdef CONFIG_PM_SLEEP
 134 static int asic3_led_suspend(struct device *dev)
 135 {
 136         struct platform_device *pdev = to_platform_device(dev);
 137         const struct mfd_cell *cell = mfd_get_cell(pdev);
 138         int ret;
 139 
 140         ret = 0;
 141         if (cell->suspend)
 142                 ret = (*cell->suspend)(pdev);
 143 
 144         return ret;
 145 }
 146 
 147 static int asic3_led_resume(struct device *dev)
 148 {
 149         struct platform_device *pdev = to_platform_device(dev);
 150         const struct mfd_cell *cell = mfd_get_cell(pdev);
 151         int ret;
 152 
 153         ret = 0;
 154         if (cell->resume)
 155                 ret = (*cell->resume)(pdev);
 156 
 157         return ret;
 158 }
 159 #endif
 160 
 161 static SIMPLE_DEV_PM_OPS(asic3_led_pm_ops, asic3_led_suspend, asic3_led_resume);
 162 
 163 static struct platform_driver asic3_led_driver = {
 164         .probe          = asic3_led_probe,
 165         .remove         = asic3_led_remove,
 166         .driver         = {
 167                 .name   = "leds-asic3",
 168                 .pm     = &asic3_led_pm_ops,
 169         },
 170 };
 171 
 172 module_platform_driver(asic3_led_driver);
 173 
 174 MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
 175 MODULE_DESCRIPTION("HTC ASIC3 LED driver");
 176 MODULE_LICENSE("GPL");
 177 MODULE_ALIAS("platform:leds-asic3");

/* [<][>][^][v][top][bottom][index][help] */