From patchwork Tue Jan 15 12:56:03 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lee Jones X-Patchwork-Id: 1977921 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id E21B6DF264 for ; Tue, 15 Jan 2013 13:13:30 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Tv6F0-0003gk-Vp; Tue, 15 Jan 2013 13:07:49 +0000 Received: from mail-wi0-f178.google.com ([209.85.212.178]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Tv64g-0006je-Mv for linux-arm-kernel@lists.infradead.org; Tue, 15 Jan 2013 12:57:11 +0000 Received: by mail-wi0-f178.google.com with SMTP id hn3so58994wib.17 for ; Tue, 15 Jan 2013 04:57:04 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=4GBPosWl3+2eTzDKsupYj9P3SWe+xS8h2w280VbvXmk=; b=AdMGweZKPrcJTTud1MAqZjDZG9lL5yCAUcV4fmHibE4QzmcCej8miijUsrWdyJDWBH Y5KGQrQ874OOHwqZ7K6nIbBxBB+yXDGsbyOjCj4IHJUro3+wLY5F+jvqob8nOqk+CPAP VOQmplsHdLUwPKSdRiIb7LuBPWGQ7/8/1PrAfc7futuD62EgbsxCNfpPjKlm6dPGSNUc 7vEr4Y0zzydk3KHjDruUHTEfBWJoOd3HUdzf5A/dlSYWnSXSv8lTQeceCuC/GiTKE+NF ftoDlNAPhKIfx0n2wKEf+uHk0xBNF8kERPsOr1yuPyvvdMUqW5/9PaffSDK16YarYj8B Bj5w== X-Received: by 10.180.93.133 with SMTP id cu5mr3480940wib.32.1358254624681; Tue, 15 Jan 2013 04:57:04 -0800 (PST) Received: from localhost.localdomain (cpc1-aztw13-0-0-cust473.18-1.cable.virginmedia.com. [77.102.241.218]) by mx.google.com with ESMTPS id eo10sm3672833wib.9.2013.01.15.04.57.03 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 15 Jan 2013 04:57:03 -0800 (PST) From: Lee Jones To: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 23/26] mfd: ab8500-pwm: Enable support for AB8505 PWMLED blink Date: Tue, 15 Jan 2013 12:56:03 +0000 Message-Id: <1358254566-12419-24-git-send-email-lee.jones@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1358254566-12419-1-git-send-email-lee.jones@linaro.org> References: <1358254566-12419-1-git-send-email-lee.jones@linaro.org> X-Gm-Message-State: ALoCoQnTBsn9r/FU9pTFl4S3uQzSjZ0po8fbMmQP2TUb8Di6QNI/TfAHn8xz4zt84AUVukxDMnXm X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130115_075707_369632_D6DC6DC2 X-CRM114-Status: GOOD ( 27.03 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.178 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Naga Radhesh , Lee Jones , linus.walleij@stericsson.com, arnd@arndb.de, sameo@linux.intel.com X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Naga Radhesh Enable support for PWM OUT LED blinking for AB8505. Instead of having 3 pwm instances from ab8500 core file add it as platform data. Signed-off-by: Lee Jones Signed-off-by: Naga Radhesh Reviewed-by: Arun MURTHY Reviewed-by: Srinidhi KASAGAR --- drivers/mfd/ab8500-core.c | 10 -- drivers/misc/ab8500-pwm.c | 282 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/ab8500/pwmleds.h | 20 +++ include/linux/mfd/abx500/ab8500.h | 2 + 4 files changed, 304 insertions(+), 10 deletions(-) create mode 100644 drivers/misc/ab8500-pwm.c create mode 100644 include/linux/mfd/ab8500/pwmleds.h diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 580adf3..c758469 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1015,16 +1015,6 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { .id = 1, }, { - .name = "ab8500-pwm", - .of_compatible = "stericsson,ab8500-pwm", - .id = 2, - }, - { - .name = "ab8500-pwm", - .of_compatible = "stericsson,ab8500-pwm", - .id = 3, - }, - { .name = "ab8500-leds", .of_compatible = "stericsson,ab8500-leds", }, diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c new file mode 100644 index 0000000..d7a6276 --- /dev/null +++ b/drivers/misc/ab8500-pwm.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Arun R Murthy + * License terms: GNU General Public License (GPL) version 2 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * PWM Out generators + * Bank: 0x10 + */ +#define AB8500_PWM_OUT_CTRL1_REG 0x60 +#define AB8500_PWM_OUT_CTRL2_REG 0x61 +#define AB8500_PWM_OUT_CTRL7_REG 0x66 +#define AB8505_PWM_OUT_BLINK_CTRL1_REG 0x68 +#define AB8505_PWM_OUT_BLINK_CTRL4_REG 0x6B +#define AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT 4 +#define AB8505_PWM_OUT_BLINK_DUTYMASK (0x0F << AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT) + + +/* backlight driver constants */ +#define ENABLE_PWM 1 +#define DISABLE_PWM 0 + +struct pwm_device { + struct device *dev; + struct list_head node; + struct clk *clk; + const char *label; + unsigned int pwm_id; + unsigned int blink_en; + struct ab8500 *parent; + bool clk_enabled; +}; + +static LIST_HEAD(pwm_list); + +int pwm_config_blink(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + int ret; + unsigned int value; + u8 reg; + if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) { + dev_err(pwm->dev, "setting blinking for this " + "device not supported\n"); + return -EINVAL; + } + /* + * get the period value that is to be written to + * AB8500_PWM_OUT_BLINK_CTRL1 REGS[0:2] + */ + value = period_ns & 0x07; + /* + * get blink duty value to be written to + * AB8500_PWM_OUT_BLINK_CTRL REGS[7:4] + */ + value |= ((duty_ns << AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT) & + AB8505_PWM_OUT_BLINK_DUTYMASK); + + reg = AB8505_PWM_OUT_BLINK_CTRL1_REG + (pwm->pwm_id - 1); + + ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, + reg, (u8)value); + if (ret < 0) + dev_err(pwm->dev, "%s: Failed to config PWM blink,Error %d\n", + pwm->label, ret); + return ret; +} + +int pwm_blink_ctrl(struct pwm_device *pwm , int enable) +{ + int ret; + + if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) { + dev_err(pwm->dev, "setting blinking for this " + "device not supported\n"); + return -EINVAL; + } + /* + * Enable/disable blinking feature for corresponding PWMOUT + * channel depending on value of enable. + */ + ret = abx500_mask_and_set_register_interruptible(pwm->dev, + AB8500_MISC, AB8505_PWM_OUT_BLINK_CTRL4_REG, + 1 << (pwm->pwm_id-1), enable << (pwm->pwm_id-1)); + if (ret < 0) + dev_err(pwm->dev, "%s: Failed to control PWM blink,Error %d\n", + pwm->label, ret); + return ret; +} + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + int ret = 0; + unsigned int higher_val, lower_val; + u8 reg; + + /* + * get the first 8 bits that are be written to + * AB8500_PWM_OUT_CTRL1_REG[0:7] + */ + lower_val = duty_ns & 0x00FF; + /* + * get bits [9:10] that are to be written to + * AB8500_PWM_OUT_CTRL2_REG[0:1] + */ + higher_val = ((duty_ns & 0x0300) >> 8); + + reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2); + + ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, + reg, (u8)lower_val); + if (ret < 0) + return ret; + ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, + (reg + 1), (u8)higher_val); + + return ret; +} +EXPORT_SYMBOL(pwm_config); + +int pwm_enable(struct pwm_device *pwm) +{ + int ret; + + if (!pwm->clk_enabled) { + ret = clk_enable(pwm->clk); + if (ret < 0) { + dev_err(pwm->dev, "failed to enable clock\n"); + return ret; + } + pwm->clk_enabled = true; + } + ret = abx500_mask_and_set_register_interruptible(pwm->dev, + AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, + 1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1)); + if (ret < 0) + dev_err(pwm->dev, "%s: Failed to enable PWM, Error %d\n", + pwm->label, ret); + return ret; +} +EXPORT_SYMBOL(pwm_enable); + +void pwm_disable(struct pwm_device *pwm) +{ + int ret; + + ret = abx500_mask_and_set_register_interruptible(pwm->dev, + AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, + 1 << (pwm->pwm_id-1), DISABLE_PWM); + /* + * Workaround to set PWM in disable. + * If enable bit is not toggled the PWM might output 50/50 duty cycle + * even though it should be disabled + */ + ret &= abx500_mask_and_set_register_interruptible(pwm->dev, + AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, + 1 << (pwm->pwm_id-1), + ENABLE_PWM << (pwm->pwm_id-1)); + ret &= abx500_mask_and_set_register_interruptible(pwm->dev, + AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, + 1 << (pwm->pwm_id-1), DISABLE_PWM); + + if (ret < 0) + dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n", + pwm->label, ret); + if (pwm->clk_enabled) { + clk_disable(pwm->clk); + pwm->clk_enabled = false; + } + + return; +} +EXPORT_SYMBOL(pwm_disable); + +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + struct pwm_device *pwm; + list_for_each_entry(pwm, &pwm_list, node) { + if (pwm->pwm_id == pwm_id) { + pwm->label = label; + pwm->pwm_id = pwm_id; + return pwm; + } + } + + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(pwm_request); + +void pwm_free(struct pwm_device *pwm) +{ + pwm_disable(pwm); +} +EXPORT_SYMBOL(pwm_free); + +static int __devinit ab8500_pwm_probe(struct platform_device *pdev) +{ + struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent); + struct ab8500_platform_data *plat = dev_get_platdata(parent->dev); + struct ab8500_pwmled_platform_data *pdata = plat->pwmled; + struct pwm_device *pwm; + int ret = 0 , i; + + /* + * Nothing to be done in probe, this is required to get the + * device which is required for ab8500 read and write + */ + pwm = kzalloc(((sizeof(struct pwm_device)) * pdata->num_pwm), + GFP_KERNEL); + if (pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + for (i = 0; i < pdata->num_pwm; i++) { + pwm[i].dev = &pdev->dev; + pwm[i].parent = parent; + pwm[i].blink_en = pdata->leds[i].blink_en; + pwm[i].pwm_id = pdata->leds[i].pwm_id; + list_add_tail(&pwm[i].node, &pwm_list); + } + pwm->clk = clk_get(pwm->dev, NULL); + if (IS_ERR(pwm->clk)) { + dev_err(pwm->dev, "clock request failed\n"); + ret = PTR_ERR(pwm->clk); + goto fail; + } + platform_set_drvdata(pdev, pwm); + pwm->clk_enabled = false; + dev_dbg(pwm->dev, "pwm probe successful\n"); + return ret; + +fail: + list_del(&pwm->node); + kfree(pwm); + return ret; +} + +static int __devexit ab8500_pwm_remove(struct platform_device *pdev) +{ + struct pwm_device *pwm = platform_get_drvdata(pdev); + + list_del(&pwm->node); + clk_put(pwm->clk); + dev_dbg(&pdev->dev, "pwm driver removed\n"); + kfree(pwm); + return 0; +} + +static struct platform_driver ab8500_pwm_driver = { + .driver = { + .name = "ab8500-pwm", + .owner = THIS_MODULE, + }, + .probe = ab8500_pwm_probe, + .remove = __devexit_p(ab8500_pwm_remove), +}; + +static int __init ab8500_pwm_init(void) +{ + return platform_driver_register(&ab8500_pwm_driver); +} + +static void __exit ab8500_pwm_exit(void) +{ + platform_driver_unregister(&ab8500_pwm_driver); +} + +subsys_initcall(ab8500_pwm_init); +module_exit(ab8500_pwm_exit); +MODULE_AUTHOR("Arun MURTHY "); +MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver"); +MODULE_ALIAS("platform:ab8500-pwm"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/ab8500/pwmleds.h b/include/linux/mfd/ab8500/pwmleds.h new file mode 100644 index 0000000..e316582 --- /dev/null +++ b/include/linux/mfd/ab8500/pwmleds.h @@ -0,0 +1,20 @@ +/* + * Copyright ST-Ericsson 2012. + * + * Author: Naga Radhesh + * Licensed under GPLv2. + */ +#ifndef _AB8500_PWMLED_H +#define _AB8500_PWMLED_H + +struct ab8500_led_pwm { + int pwm_id; + int blink_en; +}; + +struct ab8500_pwmled_platform_data { + int num_pwm; + struct ab8500_led_pwm *leds; +}; + +#endif diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 83de910..62cbe65 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -281,6 +281,7 @@ struct ab8500_sysctrl_platform_data; * @regulator_reg_init: regulator init registers * @num_regulator: number of regulators * @regulator: machine-specific constraints for regulators + * @pwmled: machine-specific pwmled data */ struct ab8500_platform_data { int irq_base; @@ -293,6 +294,7 @@ struct ab8500_platform_data { struct ab8500_gpio_platform_data *gpio; struct ab8500_codec_platform_data *codec; struct ab8500_sysctrl_platform_data *sysctrl; + struct ab8500_pwmled_platform_data *pwmled; }; extern int __devinit ab8500_init(struct ab8500 *ab8500,