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: 14072 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 5A33A23E2E for ; Tue, 15 Jan 2013 12:57:07 +0000 (UTC) Received: from mail-vb0-f46.google.com (mail-vb0-f46.google.com [209.85.212.46]) by fiordland.canonical.com (Postfix) with ESMTP id 0132DA19346 for ; Tue, 15 Jan 2013 12:57:06 +0000 (UTC) Received: by mail-vb0-f46.google.com with SMTP id b13so54849vby.19 for ; Tue, 15 Jan 2013 04:57:06 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:x-received:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:x-gm-message-state; bh=4GBPosWl3+2eTzDKsupYj9P3SWe+xS8h2w280VbvXmk=; b=b+/hxbXgakv9LAbIYUGoUKpSuiBTOclAm2nUvgfdmKJYpfJig7kuMu2i44MznopeBV 1tjSnZ8ep83U2yV7OBl3Lg4X+M6GUJxEgO4e82TbAsLJ7EDFSvxxIRBdLVdbefhyVdQB EU707arFTg2Rp78zCpfWrFshbLZ7dRs9iYIKQki4oY9GKPXMsYO9UVpaO7XIM6+nDsJ9 BUYiU1J91xfGprEfYDLEurPcjqbazp68k1OTyrvVBHf7im9c+dEcdOfy2UX7tnZ4oifj thTkj+j76QCh5dAWX9LIbgCZ/a78H6soegllTIfi5vdBBbL2ToGmZ9xM4hawpYj+laXz JHDQ== X-Received: by 10.52.176.6 with SMTP id ce6mr91865153vdc.57.1358254626512; Tue, 15 Jan 2013 04:57:06 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.58.145.101 with SMTP id st5csp110631veb; Tue, 15 Jan 2013 04:57:05 -0800 (PST) X-Received: by 10.194.78.207 with SMTP id d15mr141433385wjx.52.1358254625090; Tue, 15 Jan 2013 04:57:05 -0800 (PST) Received: from mail-wi0-f181.google.com (mail-wi0-f181.google.com [209.85.212.181]) by mx.google.com with ESMTPS id y7si3356802wix.34.2013.01.15.04.57.04 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 15 Jan 2013 04:57:05 -0800 (PST) Received-SPF: neutral (google.com: 209.85.212.181 is neither permitted nor denied by best guess record for domain of lee.jones@linaro.org) client-ip=209.85.212.181; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.212.181 is neither permitted nor denied by best guess record for domain of lee.jones@linaro.org) smtp.mail=lee.jones@linaro.org Received: by mail-wi0-f181.google.com with SMTP id hq4so62130wib.8 for ; Tue, 15 Jan 2013 04:57:04 -0800 (PST) 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 Cc: arnd@arndb.de, linus.walleij@stericsson.com, sameo@linux.intel.com, Naga Radhesh , Lee Jones 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: ALoCoQnvviIBNANv6mHlsU8XbPQIMVnGcYrL2FE2SdECTdQxqGE8JWlJMyaTgQxYQqHhAebRdIQL 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,