From patchwork Tue May 5 09:38:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shobhit Kumar X-Patchwork-Id: 468011 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id C01311409B7 for ; Tue, 5 May 2015 19:38:49 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1161157AbbEEJis (ORCPT ); Tue, 5 May 2015 05:38:48 -0400 Received: from mga11.intel.com ([192.55.52.93]:56150 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757292AbbEEJiq (ORCPT ); Tue, 5 May 2015 05:38:46 -0400 Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga102.fm.intel.com with ESMTP; 05 May 2015 02:38:42 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.13,372,1427785200"; d="scan'208";a="723869101" Received: from unknown (HELO skumar40-ivm.gar.corp.intel.com) ([10.106.9.24]) by orsmga002.jf.intel.com with ESMTP; 05 May 2015 02:38:38 -0700 From: Shobhit Kumar To: intel-gfx , linux-kernel , linux-gpio , linux-pwm , dri-devel Cc: Linus Walleij , Alexandre Courbot , Daniel Vetter , David Airlie , Samuel Ortiz , Thierry Reding , Jani Nikula , Lee Jones , Povilas Staniulis , Chih-Wei Huang , Shobhit Kumar Subject: [PATCH 6/8] pwm: crc: Add Crystalcove (CRC) PWM driver Date: Tue, 5 May 2015 15:08:36 +0530 Message-Id: <1430818716-27287-1-git-send-email-shobhit.kumar@intel.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1430316005-16480-7-git-send-email-shobhit.kumar@intel.com> References: <1430316005-16480-7-git-send-email-shobhit.kumar@intel.com> Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org The Crystalcove PMIC controls PWM signals and this driver exports that capability as a PWM chip driver. This is platform device implementtaion of the drivers/mfd cell device for CRC PMIC v2: Use the existing config callback with duty_ns and period_ns(Thierry) v3: Correct the subject line (Lee jones) CC: Samuel Ortiz Cc: Linus Walleij Cc: Alexandre Courbot Cc: Thierry Reding Signed-off-by: Shobhit Kumar --- drivers/pwm/Kconfig | 7 +++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-crc.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 drivers/pwm/pwm-crc.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index b1541f4..954da3e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -183,6 +183,13 @@ config PWM_LPC32XX To compile this driver as a module, choose M here: the module will be called pwm-lpc32xx. +config PWM_CRC + bool "Intel Crystalcove (CRC) PWM support" + depends on X86 && INTEL_SOC_PMIC + help + Generic PWM framework driver for Crystalcove (CRC) PMIC based PWM + control. + config PWM_LPSS tristate "Intel LPSS PWM support" depends on X86 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index ec50eb5..3d38fed 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o +obj-$(CONFIG_PWM_CRC) += pwm-crc.o diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c new file mode 100644 index 0000000..987f3b4 --- /dev/null +++ b/drivers/pwm/pwm-crc.c @@ -0,0 +1,171 @@ +/* + * pwm-crc.c - Intel Crystal Cove PWM Driver + * + * Copyright (C) 2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Author: Shobhit Kumar + */ + +#include +#include +#include +#include +#include + +#define PWM0_CLK_DIV 0x4B +#define PWM_OUTPUT_ENABLE (1<<7) +#define PWM_DIV_CLK_0 0x00 /* DIVIDECLK = BASECLK */ +#define PWM_DIV_CLK_100 0x63 /* DIVIDECLK = BASECLK/100 */ +#define PWM_DIV_CLK_128 0x7F /* DIVIDECLK = BASECLK/128 */ + +#define PWM0_DUTY_CYCLE 0x4E +#define BACKLIGHT_EN 0x51 + +#define PWM_MAX_LEVEL 0xFF + +#define PWM_BASE_CLK 6000 /* 6 MHz */ +#define PWM_MAX_PERIOD_NS 21333 /* 46.875KHz */ + +/** + * struct crystalcove_pwm - Crystal Cove PWM controller + * @chip: the abstract pwm_chip structure. + * @regmap: the regmap from the parent device. + */ +struct crystalcove_pwm { + struct pwm_chip chip; + struct platform_device *pdev; + struct regmap *regmap; +}; + +static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) +{ + return container_of(pc, struct crystalcove_pwm, chip); +} + +static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + + regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); + + return 0; +} + +static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + + regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); +} + +static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + struct device *dev = &crc_pwm->pdev->dev; + int level, percent; + + if (period_ns > PWM_MAX_PERIOD_NS) { + dev_err(dev, "un-supported period_ns\n"); + return -1; + } + + if (pwm->period != period_ns) { + int clk_div; + + /* changing the clk divisor, need to disable fisrt */ + crc_pwm_disable(c, pwm); + clk_div = PWM_BASE_CLK * period_ns / 1000000; + + regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, + clk_div | PWM_OUTPUT_ENABLE); + + /* enable back */ + crc_pwm_enable(c, pwm); + } + + if (duty_ns > period_ns) { + dev_err(dev, "duty cycle cannot be greater than cycle period\n"); + return -1; + } + + /* change the pwm duty cycle */ + percent = duty_ns * 100 / period_ns; + level = percent * PWM_MAX_LEVEL / 100; + regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + + return 0; +} + +static const struct pwm_ops crc_pwm_ops = { + .config = crc_pwm_config, + .enable = crc_pwm_enable, + .disable = crc_pwm_disable, + .owner = THIS_MODULE, +}; + +static int crystalcove_pwm_probe(struct platform_device *pdev) +{ + struct crystalcove_pwm *pwm; + int retval; + struct device *dev = pdev->dev.parent; + struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + + pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &crc_pwm_ops; + pwm->chip.base = -1; + pwm->chip.npwm = 1; + + /* get the PMIC regmap */ + pwm->regmap = pmic->regmap; + + retval = pwmchip_add(&pwm->chip); + if (retval < 0) + return retval; + + dev_dbg(&pdev->dev, "crc-pwm probe successful\n"); + platform_set_drvdata(pdev, pwm); + + return 0; +} + +static int crystalcove_pwm_remove(struct platform_device *pdev) +{ + struct crystalcove_pwm *pwm = platform_get_drvdata(pdev); + int retval; + + retval = pwmchip_remove(&pwm->chip); + if (retval < 0) + return retval; + + dev_dbg(&pdev->dev, "crc-pwm driver removed\n"); + + return 0; +} + +static struct platform_driver crystalcove_pwm_driver = { + .probe = crystalcove_pwm_probe, + .remove = crystalcove_pwm_remove, + .driver = { + .name = "crystal_cove_pwm", + }, +}; + +module_platform_driver(crystalcove_pwm_driver); + +MODULE_AUTHOR("Shobhit Kumar "); +MODULE_DESCRIPTION("Intel Crystal Cove PWM Driver"); +MODULE_LICENSE("GPL v2");