0001
0002
0003
0004
0005
0006 #include <linux/kernel.h>
0007 #include <linux/platform_device.h>
0008 #include <linux/leds.h>
0009 #include <linux/slab.h>
0010
0011 #include <linux/mfd/asic3.h>
0012 #include <linux/mfd/core.h>
0013 #include <linux/module.h>
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #define MS_TO_CLK(ms) DIV_ROUND_CLOSEST(((ms)*1024), 32000)
0026 #define CLK_TO_MS(clk) (((clk)*32000)/1024)
0027 #define MAX_CLK 4000
0028 #define MAX_MS CLK_TO_MS(MAX_CLK)
0029
0030 static const unsigned int led_n_base[ASIC3_NUM_LEDS] = {
0031 [0] = ASIC3_LED_0_Base,
0032 [1] = ASIC3_LED_1_Base,
0033 [2] = ASIC3_LED_2_Base,
0034 };
0035
0036 static void brightness_set(struct led_classdev *cdev,
0037 enum led_brightness value)
0038 {
0039 struct platform_device *pdev = to_platform_device(cdev->dev->parent);
0040 const struct mfd_cell *cell = mfd_get_cell(pdev);
0041 struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
0042 u32 timebase;
0043 unsigned int base;
0044
0045 timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4);
0046
0047 base = led_n_base[cell->id];
0048 asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32);
0049 asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32);
0050 asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0);
0051 asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase);
0052 }
0053
0054 static int blink_set(struct led_classdev *cdev,
0055 unsigned long *delay_on,
0056 unsigned long *delay_off)
0057 {
0058 struct platform_device *pdev = to_platform_device(cdev->dev->parent);
0059 const struct mfd_cell *cell = mfd_get_cell(pdev);
0060 struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
0061 u32 on;
0062 u32 off;
0063 unsigned int base;
0064
0065 if (*delay_on > MAX_MS || *delay_off > MAX_MS)
0066 return -EINVAL;
0067
0068 if (*delay_on == 0 && *delay_off == 0) {
0069
0070 on = MS_TO_CLK(500);
0071 off = MS_TO_CLK(500);
0072 } else {
0073 on = MS_TO_CLK(*delay_on);
0074 off = MS_TO_CLK(*delay_off);
0075 if ((on + off) > MAX_CLK)
0076 return -EINVAL;
0077 }
0078
0079 base = led_n_base[cell->id];
0080 asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off));
0081 asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on);
0082 asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0);
0083 asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4));
0084
0085 *delay_on = CLK_TO_MS(on);
0086 *delay_off = CLK_TO_MS(off);
0087
0088 return 0;
0089 }
0090
0091 static int asic3_led_probe(struct platform_device *pdev)
0092 {
0093 struct asic3_led *led = dev_get_platdata(&pdev->dev);
0094 int ret;
0095
0096 ret = mfd_cell_enable(pdev);
0097 if (ret < 0)
0098 return ret;
0099
0100 led->cdev = devm_kzalloc(&pdev->dev, sizeof(struct led_classdev),
0101 GFP_KERNEL);
0102 if (!led->cdev) {
0103 ret = -ENOMEM;
0104 goto out;
0105 }
0106
0107 led->cdev->name = led->name;
0108 led->cdev->flags = LED_CORE_SUSPENDRESUME;
0109 led->cdev->brightness_set = brightness_set;
0110 led->cdev->blink_set = blink_set;
0111 led->cdev->default_trigger = led->default_trigger;
0112
0113 ret = led_classdev_register(&pdev->dev, led->cdev);
0114 if (ret < 0)
0115 goto out;
0116
0117 return 0;
0118
0119 out:
0120 (void) mfd_cell_disable(pdev);
0121 return ret;
0122 }
0123
0124 static int asic3_led_remove(struct platform_device *pdev)
0125 {
0126 struct asic3_led *led = dev_get_platdata(&pdev->dev);
0127
0128 led_classdev_unregister(led->cdev);
0129
0130 return mfd_cell_disable(pdev);
0131 }
0132
0133 #ifdef CONFIG_PM_SLEEP
0134 static int asic3_led_suspend(struct device *dev)
0135 {
0136 struct platform_device *pdev = to_platform_device(dev);
0137 const struct mfd_cell *cell = mfd_get_cell(pdev);
0138 int ret;
0139
0140 ret = 0;
0141 if (cell->suspend)
0142 ret = (*cell->suspend)(pdev);
0143
0144 return ret;
0145 }
0146
0147 static int asic3_led_resume(struct device *dev)
0148 {
0149 struct platform_device *pdev = to_platform_device(dev);
0150 const struct mfd_cell *cell = mfd_get_cell(pdev);
0151 int ret;
0152
0153 ret = 0;
0154 if (cell->resume)
0155 ret = (*cell->resume)(pdev);
0156
0157 return ret;
0158 }
0159 #endif
0160
0161 static SIMPLE_DEV_PM_OPS(asic3_led_pm_ops, asic3_led_suspend, asic3_led_resume);
0162
0163 static struct platform_driver asic3_led_driver = {
0164 .probe = asic3_led_probe,
0165 .remove = asic3_led_remove,
0166 .driver = {
0167 .name = "leds-asic3",
0168 .pm = &asic3_led_pm_ops,
0169 },
0170 };
0171
0172 module_platform_driver(asic3_led_driver);
0173
0174 MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
0175 MODULE_DESCRIPTION("HTC ASIC3 LED driver");
0176 MODULE_LICENSE("GPL");
0177 MODULE_ALIAS("platform:leds-asic3");