From patchwork Thu Apr 4 01:34:37 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Kim, Milo" X-Patchwork-Id: 2389981 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) by patchwork2.kernel.org (Postfix) with ESMTP id 8304DDFB79 for ; Thu, 4 Apr 2013 01:50:51 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UNZKC-0008IK-Ry for patchwork-linux-arm@patchwork.kernel.org; Thu, 04 Apr 2013 01:50:49 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UNZ4l-0007ev-0J; Thu, 04 Apr 2013 01:34:51 +0000 Received: from arroyo.ext.ti.com ([192.94.94.40]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UNZ4i-0007eP-0i for linux-arm-kernel@lists.infradead.org; Thu, 04 Apr 2013 01:34:49 +0000 Received: from dbdp20.itg.ti.com ([172.24.170.38]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id r341YgLc010643; Wed, 3 Apr 2013 20:34:43 -0500 Received: from DQHE74.ent.ti.com (localhost [127.0.0.1]) by dbdp20.itg.ti.com (8.13.8/8.13.8) with ESMTP id r341YcRx028419; Thu, 4 Apr 2013 07:04:39 +0530 (IST) Received: from DQHE02.ent.ti.com ([fe80::f41c:f77f:5d6a:8699]) by DQHE74.ent.ti.com ([fe80::2088:fac8:8d64:a01c%35]) with mapi id 14.01.0323.003; Thu, 4 Apr 2013 09:34:38 +0800 From: "Kim, Milo" To: Mike Turquette Subject: [PATCH v2] clk: add LP8788 clock driver Thread-Topic: [PATCH v2] clk: add LP8788 clock driver Thread-Index: Ac4w1JFWk//FIWlETAG+zakIfdQIqg== Date: Thu, 4 Apr 2013 01:34:37 +0000 Message-ID: Accept-Language: ko-KR, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [157.87.185.62] MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130403_213448_196091_27F33ADC X-CRM114-Status: GOOD ( 20.90 ) X-Spam-Score: -9.2 (---------) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-9.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [192.94.94.40 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -2.3 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: "Samuel Ortiz \(sameo@linux.intel.com\)" , "linux-arm-kernel@lists.infradead.org" , "linux-kernel@vger.kernel.org" X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org LP8788 PMU consists of regulator, battery charger, RTC, ADC, backlight driver and current sinks. Here is additional driver, clock. * Clock source LP8788 clock is generated by two clock source. One is internal oscillator. The other is attached external crystal oscillator. LP8788 provides automatic clock source selection through the I2C register. This operation is executed in 'prepare' of common clock driver architecture. * Clock rate LP8788 generates a fixed 32768Hz clock which is used for the system. * Supported operations .prepare: Before the clock is enabled, automatic clock source selection is done by register access. .unprepare: No register for disable or remove the clock source, so do nothing. .recalc_rate: Fixed clock rate, 32.768KHz. * clk_register_fixed_rate() vs devm_clk_register() and clkdev_add() Fixed clock driver can be created by common clock helper function but but LP8788 should be built as a module. Using devm_clk_register() and clkdev_add() is more appropriate method to implement LP8788 clock driver. Patch v2. (a) Remove unnecessary 'is_enabled()' operation code. (b) Add CLK_IGNORE_UNUSED flag. (c) Add clock device detection code : lp8788_is_clk_device_ready() If the device is not ready, return as -ENODEV on loading the driver. Patch v1. Initial patch Signed-off-by: Milo(Woogyom) Kim Acked-by: Samuel Ortiz --- drivers/clk/Kconfig | 7 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-lp8788.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/lp8788.c | 2 + include/linux/mfd/lp8788.h | 1 + 5 files changed, 171 insertions(+) create mode 100644 drivers/clk/clk-lp8788.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a64caef..4b5109c 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -71,6 +71,13 @@ config COMMON_CLK_AXI_CLKGEN Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx FPGAs. It is commonly used in Analog Devices' reference designs. +config CLK_LP8788 + tristate "Clock driver for TI LP8788 PMU" + depends on MFD_LP8788 + ---help--- + Supports the clock subsystem of TI LP8788. It generates the 32KHz + clock output. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 79e98e4..98f3883 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o +obj-$(CONFIG_CLK_LP8788) += clk-lp8788.o diff --git a/drivers/clk/clk-lp8788.c b/drivers/clk/clk-lp8788.c new file mode 100644 index 0000000..c636fd7 --- /dev/null +++ b/drivers/clk/clk-lp8788.c @@ -0,0 +1,160 @@ +/* + * TI LP8788 Clock Driver + * + * Copyright 2013 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define LP8788_REG_STATUS 0x06 +#define LP8788_OSC_WORKING 0x10 + +#define LP8788_REG_CLKCTRL 0xA2 +#define LP8788_CLKMODE_MASK 0x02 +#define LP8788_CLKMODE_AUTO 0X02 + +#define CLKOUT_32KHZ 32768 + +struct lp8788_clk { + struct lp8788 *lp; + struct clk *clk; + struct clk_hw hw; + struct clk_lookup *lookup; +}; + +static int lp8788_clk_prepare(struct clk_hw *hw) +{ + struct lp8788_clk *pclk = container_of(hw, struct lp8788_clk, hw); + + /* Automatic oscillator source selection */ + return lp8788_update_bits(pclk->lp, LP8788_REG_CLKCTRL, + LP8788_CLKMODE_MASK, LP8788_CLKMODE_AUTO); +} + +static void lp8788_clk_unprepare(struct clk_hw *hw) +{ + /* Do nothing */ + return; +} + +static unsigned long lp8788_clk_recalc_rate(struct clk_hw *hw, + unsigned long unused) +{ + return CLKOUT_32KHZ; +} + +static const struct clk_ops lp8788_clk_ops = { + .prepare = lp8788_clk_prepare, + .unprepare = lp8788_clk_unprepare, + .recalc_rate = lp8788_clk_recalc_rate, +}; + +static struct clk_init_data lp8788_clk_cfg = { + .name = "32k_clk", + .ops = &lp8788_clk_ops, + .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, +}; + +static int lp8788_clk_register(struct device *dev, struct lp8788_clk *pclk) +{ + struct clk_lookup *lookup; + + pclk->hw.init = &lp8788_clk_cfg; + pclk->clk = devm_clk_register(dev, &pclk->hw); + if (IS_ERR(pclk->clk)) { + dev_err(dev, "clk register err\n"); + return PTR_ERR(pclk->clk); + } + + lookup = clkdev_alloc(pclk->clk, lp8788_clk_cfg.name, NULL); + if (!lookup) { + dev_err(dev, "clkdev_alloc err\n"); + return -ENOMEM; + } + pclk->lookup = lookup; + + clkdev_add(lookup); + + return 0; +} + +static void lp8788_clk_unregister(struct lp8788_clk *pclk) +{ + if (pclk->lookup) + clkdev_drop(pclk->lookup); +} + +static bool lp8788_is_clk_device_ready(struct lp8788_clk *pclk) +{ + u8 val = 0; + + lp8788_read_byte(pclk->lp, LP8788_REG_STATUS, &val); + + return val & LP8788_OSC_WORKING; +} + +static int lp8788_clk_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct lp8788_clk *pclk; + + pclk = devm_kzalloc(&pdev->dev, sizeof(struct lp8788_clk), GFP_KERNEL); + if (!pclk) + return -ENOMEM; + + pclk->lp = lp; + platform_set_drvdata(pdev, pclk); + + if (!lp8788_is_clk_device_ready(pclk)) + return -ENODEV; + + return lp8788_clk_register(&pdev->dev, pclk); +} + +static int lp8788_clk_remove(struct platform_device *pdev) +{ + struct lp8788_clk *pclk = platform_get_drvdata(pdev); + + lp8788_clk_unregister(pclk); + return 0; +} + +static struct platform_driver lp8788_clk_driver = { + .probe = lp8788_clk_probe, + .remove = lp8788_clk_remove, + .driver = { + .name = LP8788_DEV_CLK, + .owner = THIS_MODULE, + }, +}; + +static int __init lp8788_clk_init(void) +{ + return platform_driver_register(&lp8788_clk_driver); +} +subsys_initcall(lp8788_clk_init); + +static void __exit lp8788_clk_exit(void) +{ + platform_driver_unregister(&lp8788_clk_driver); +} +module_exit(lp8788_clk_exit); + +MODULE_DESCRIPTION("TI LP8788 Clock Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-clk"); diff --git a/drivers/mfd/lp8788.c b/drivers/mfd/lp8788.c index c3d3c9b..9f5596e 100644 --- a/drivers/mfd/lp8788.c +++ b/drivers/mfd/lp8788.c @@ -72,6 +72,8 @@ static struct resource rtc_irqs[] = { }; static struct mfd_cell lp8788_devs[] = { + MFD_DEV_SIMPLE(CLK), + /* 4 bucks */ MFD_DEV_WITH_ID(BUCK, 1), MFD_DEV_WITH_ID(BUCK, 2), diff --git a/include/linux/mfd/lp8788.h b/include/linux/mfd/lp8788.h index 786bf66..5f33be8 100644 --- a/include/linux/mfd/lp8788.h +++ b/include/linux/mfd/lp8788.h @@ -28,6 +28,7 @@ #define LP8788_DEV_VIBRATOR "lp8788-vibrator" #define LP8788_DEV_KEYLED "lp8788-keyled" #define LP8788_DEV_ADC "lp8788-adc" +#define LP8788_DEV_CLK "lp8788-clk" #define LP8788_NUM_BUCKS 4 #define LP8788_NUM_DLDOS 12