From patchwork Fri Apr 12 19:17:19 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomasz Figa X-Patchwork-Id: 2437531 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id 09B1EDF2A1 for ; Fri, 12 Apr 2013 19:18:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752875Ab3DLTSN (ORCPT ); Fri, 12 Apr 2013 15:18:13 -0400 Received: from mailout4.samsung.com ([203.254.224.34]:44511 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752491Ab3DLTSM (ORCPT ); Fri, 12 Apr 2013 15:18:12 -0400 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout4.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0ML50027YPMAGV20@mailout4.samsung.com> for linux-samsung-soc@vger.kernel.org; Sat, 13 Apr 2013 04:18:11 +0900 (KST) X-AuditID: cbfee61a-b7fa86d0000045ae-7c-51685df31411 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 9B.E7.17838.3FD58615; Sat, 13 Apr 2013 04:18:11 +0900 (KST) Received: from mcdsrvbld02.digital.local ([106.116.37.23]) by mmp2.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0ML500CT6PL8T090@mmp2.samsung.com>; Sat, 13 Apr 2013 04:18:11 +0900 (KST) From: Tomasz Figa To: linux-arm-kernel@lists.infradead.org Cc: devicetree-discuss@lists.ozlabs.org, linux-samsung-soc@vger.kernel.org, kgene.kim@samsung.com, kyungmin.park@samsung.com, linux@simtec.co.uk, broonie@opensource.wolfsonmicro.com, kwangwoo.lee@gmail.com, jacmet@sunsite.dk, augulis.darius@gmail.com, mcuelenaere@gmail.com, linux@arm.linux.org.uk, sylvester.nawrocki@gmail.com, buserror@gmail.com, christer@weinigel.se, jekhor@gmail.com, ghcstop@gmail.com, mark.rutland@arm.com, tomasz.figa@gmail.com, heiko@sntech.de, robherring2@gmail.com, m.szyprowski@samsung.com, arnd@arndb.de, john.stultz@linaro.org, tglx@linutronix.de, Tomasz Figa Subject: [PATCH v5 03/14] clocksource: samsung-pwm: Add infrastructure to share PWM hardware Date: Fri, 12 Apr 2013 21:17:19 +0200 Message-id: <1365794250-14436-4-git-send-email-t.figa@samsung.com> X-Mailer: git-send-email 1.7.10 In-reply-to: <1365794250-14436-1-git-send-email-t.figa@samsung.com> References: <1365794250-14436-1-git-send-email-t.figa@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrAIsWRmVeSWpSXmKPExsVy+t9jQd3PsRmBBhM2KFj8nXSM3WLv23+M Fv9mn2KzuPX5EbvFxpUf2SwOzH7IanG0x87i/6PXrBanL11jtDi4bimrxZnfuha9C66yWazY eoHF4mzTG3aLTY+vsVrMOL+PyeL2ZV6L39saWSzWHrnLbrH0+kUmi0vzmlgsvn/7xmYx7/NO Jov1M16zWGzeNJXZYtWuP4wOUh5r5q1h9Ghp7mHz+P1rEqPHzll32T3uXNvD5vHu3Dl2j81L 6j3Oz1jI6PFy4m82j74tqxg9zr+ZyuKx/do8Zo9pr8+zeXzeJOfx+sZsxgD+KC6blNSczLLU In27BK6Mj12fGQtuhlV0tvs1MK5y62Lk5JAQMJHo3LiRDcIWk7hwbz2YLSQwnVHi6ZzaLkYu ILuLSeLejD1MIAk2ATWJzw2PwIpEBDQkpnQ9ZgcpYhb4xCKx8d8UsISwQJzEh8ltYA0sAqoS m6ccYgexeQWcJKb9n8sEsU1e4un9PrB6TgFnicl3n0FtdpLo39jPOIGRdwEjwypG0dSC5ILi pPRcQ73ixNzi0rx0veT83E2M4Hh7JrWDcWWDxSFGAQ5GJR7eAzIZgUKsiWXFlbmHGCU4mJVE eGP2pgUK8aYkVlalFuXHF5XmpBYfYpTmYFES5z3Qah0oJJCeWJKanZpakFoEk2Xi4JRqYOx/ 2J+xmT1bg720R+v3hAcN9/cn8j0SUzeI+9CfsSjw6EQ2Ds6Dcn+Wq6//xscdyOMzO+/PzrXG 9vPmn+56a5OXEzudI9/2dLvlNTnrO39uT9O7m/dQ7UP2QufcxpYs2ccC91LCNe6s8/6Tk27D W/x1zez9xf721vkJjLufPRR+NpNNRviUmBJLcUaioRZzUXEiAIEEfiizAgAA Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org This patch extends samsung PWM clocksource driver with infrastructure that allows sharing of PWM hardware between clocksource and PWM drivers. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park --- .../devicetree/bindings/pwm/pwm-samsung.txt | 43 ++++ drivers/clocksource/samsung_pwm.c | 250 +++++++++++++++++++++ include/clocksource/samsung_pwm.h | 44 ++++ 3 files changed, 337 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-samsung.txt create mode 100644 include/clocksource/samsung_pwm.h diff --git a/Documentation/devicetree/bindings/pwm/pwm-samsung.txt b/Documentation/devicetree/bindings/pwm/pwm-samsung.txt new file mode 100644 index 0000000..cc17c4c --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-samsung.txt @@ -0,0 +1,43 @@ +* Samsung PWM timers + +Samsung SoCs contain PWM timer blocks which can be used for system clock source +and clock event timers, as well as to drive SoC outputs with PWM signal. Each +PWM timer block provides 5 PWM channels (not all of them can drive physical +outputs - see SoC and board manual). + +Be aware that the clocksource driver supports only uniprocessor systems. + +Required properties: +- compatible : should be one of following: + samsung,s3c2410-pwm - for 16-bit timers present on S3C24xx SoCs + samsung,s3c6400-pwm - for 32-bit timers present on S3C64xx SoCs + samsung,s5p6440-pwm - for 32-bit timers present on S5P64x0 SoCs + samsung,s5pc100-pwm - for 32-bit timers present on S5PC100, S5PV210, + Exynos4210 rev0 SoCs + samsung,exynos4210-pwm - for 32-bit timers present on Exynos4210, + Exynos4x12 and Exynos5250 SoCs +- reg: base address and size of register area +- interrupts: list of timer interrupts (one interrupt per timer, starting at + timer 0) +- #pwm-cells: number of cells used for PWM specifier - must be 4 + the specifier format is as follows: + - phandle to PWM controller node + - index of PWM channel (from 0 to 4) + - PWM signal period in nanoseconds + - bitmask of PWM flags: + 0x1 - invert PWM signal + +Optional properties: +- samsung,pwm-outputs: list of PWM channels used as PWM outputs on particular + platform - an array of up to 5 elements being indices of PWM channels + (from 0 to 4), the order does not matter. + +Example: + pwm@7f006000 { + compatible = "samsung,s3c6400-pwm"; + reg = <0x7f006000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <23>, <24>, <25>, <27>, <28>; + samsung,pwm-outputs = <0>, <1>; + #pwm-cells = <2>; + } diff --git a/drivers/clocksource/samsung_pwm.c b/drivers/clocksource/samsung_pwm.c index 974675b..7bbd55c 100644 --- a/drivers/clocksource/samsung_pwm.c +++ b/drivers/clocksource/samsung_pwm.c @@ -14,7 +14,15 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include + +#include #include #include @@ -27,6 +35,248 @@ #include #include +/* + * PWM master driver + */ + +struct samsung_pwm_drvdata { + struct samsung_pwm pwm; + struct platform_device *pdev; + struct device_node *of_node; + struct resource resource; + struct list_head list; +}; + +static LIST_HEAD(pwm_list); + +#ifdef CONFIG_OF +static int samsung_pwm_parse_dt(struct samsung_pwm_drvdata *drvdata) +{ + struct samsung_pwm *pwm = &drvdata->pwm; + struct samsung_pwm_variant *variant = &pwm->variant; + struct device_node *np = drvdata->of_node; + struct property *prop; + const __be32 *cur; + u32 val; + int i; + + for (i = 0; i < SAMSUNG_PWM_NUM; ++i) + pwm->irq[i] = irq_of_parse_and_map(np, i); + + of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) { + if (val >= SAMSUNG_PWM_NUM) { + pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n", + __func__); + continue; + } + variant->output_mask |= 1 << val; + } + + return 0; +} + +static const struct samsung_pwm_variant s3c24xx_variant = { + .bits = 16, + .div_base = 1, + .has_tint_cstat = false, + .tclk_mask = (1 << 4), +}; + +static const struct samsung_pwm_variant s3c64xx_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5), +}; + +static const struct samsung_pwm_variant s5p64x0_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = 0, +}; + +static const struct samsung_pwm_variant s5p_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = (1 << 5), +}; + +static const struct of_device_id samsung_pwm_matches[] = { + { .compatible = "samsung,s3c2410-pwm", .data = &s3c24xx_variant, }, + { .compatible = "samsung,s3c6400-pwm", .data = &s3c64xx_variant, }, + { .compatible = "samsung,s5p6440-pwm", .data = &s5p64x0_variant, }, + { .compatible = "samsung,s5pc100-pwm", .data = &s5p_variant, }, + { .compatible = "samsung,exynos4210-pwm", .data = &s5p_variant, }, + {}, +}; + +static struct samsung_pwm_drvdata *samsung_pwm_alloc( + struct platform_device *pdev, struct device_node *of_node, + const struct samsung_pwm_variant *variant) +{ + struct samsung_pwm_drvdata *drvdata; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return NULL; + + memcpy(&drvdata->pwm.variant, variant, sizeof(drvdata->pwm.variant)); + + spin_lock_init(&drvdata->pwm.slock); + + drvdata->pdev = pdev; + drvdata->of_node = of_node; + + return drvdata; +} + +static struct samsung_pwm_drvdata *samsung_pwm_of_add(struct device_node *np) +{ + const struct samsung_pwm_variant *variant; + const struct of_device_id *match; + struct samsung_pwm_drvdata *pwm; + int ret; + + if (!np) { + np = of_find_matching_node(NULL, samsung_pwm_matches); + if (!np) { + pr_err("%s: could not find PWM device\n", __func__); + return ERR_PTR(-ENODEV); + } + } + + match = of_match_node(samsung_pwm_matches, np); + if (!match) { + pr_err("%s: failed to match given OF node\n", __func__); + return ERR_PTR(-EINVAL); + } + variant = match->data; + + pwm = samsung_pwm_alloc(NULL, np, variant); + if (!pwm) { + pr_err("%s: could not allocate PWM device struct\n", __func__); + return ERR_PTR(-ENOMEM); + } + + ret = of_address_to_resource(np, 0, &pwm->resource); + if (ret < 0) { + pr_err("%s: could not get IO resource\n", __func__); + goto err_free; + } + + ret = samsung_pwm_parse_dt(pwm); + if (ret < 0) { + pr_err("%s: failed to parse device tree node\n", __func__); + goto err_free; + } + + list_add_tail(&pwm->list, &pwm_list); + + return pwm; + +err_free: + kfree(pwm); + + return ERR_PTR(ret); +} +#else +static struct samsung_pwm_drvdata *samsung_pwm_of_add(struct device_node *np) +{ + return ERR_PTR(-ENODEV); +} +#endif + +static struct samsung_pwm_drvdata *samsung_pwm_add(struct platform_device *pdev) +{ + struct samsung_pwm_variant *variant = pdev->dev.platform_data; + struct samsung_pwm_drvdata *pwm; + struct resource *res; + int i; + + if (!variant) { + pr_err("%s: no platform data specified\n", __func__); + return ERR_PTR(-EINVAL); + } + + pwm = samsung_pwm_alloc(pdev, pdev->dev.of_node, variant); + if (!pwm) { + pr_err("%s: could not allocate PWM device struct\n", __func__); + return ERR_PTR(-ENOMEM); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("%s: could not get IO resource\n", __func__); + kfree(pwm); + return ERR_PTR(-EINVAL); + } + pwm->resource = *res; + + for (i = 0; i < SAMSUNG_PWM_NUM; ++i) + pwm->pwm.irq[i] = platform_get_irq(pdev, i); + + list_add_tail(&pwm->list, &pwm_list); + + return pwm; +} + +static struct samsung_pwm_drvdata *samsung_pwm_find( + struct platform_device *pdev, struct device_node *np) +{ + struct samsung_pwm_drvdata *pwm; + + if (pdev) + np = pdev->dev.of_node; + + list_for_each_entry(pwm, &pwm_list, list) + if ((np && pwm->of_node == np) || !pdev || pwm->pdev == pdev) + return pwm; + + if (pdev && !np) + return samsung_pwm_add(pdev); + + return samsung_pwm_of_add(np); +} + +struct samsung_pwm *samsung_pwm_get(struct platform_device *pdev, + struct device_node *of_node) +{ + struct samsung_pwm_drvdata *pwm; + struct resource *res; + + pwm = samsung_pwm_find(pdev, of_node); + if (IS_ERR(pwm)) { + pr_err("%s: failed to instantiate PWM device\n", __func__); + return &pwm->pwm; + } + + if (pwm->pwm.base) + return &pwm->pwm; + + res = request_mem_region(pwm->resource.start, + resource_size(&pwm->resource), "samsung-pwm"); + if (!res) { + pr_err("%s: failed to request IO mem region\n", __func__); + return ERR_PTR(-ENOMEM); + } + + pwm->pwm.base = ioremap(res->start, resource_size(res)); + if (!pwm->pwm.base) { + pr_err("%s: failed to map PWM registers\n", __func__); + release_mem_region(res->start, resource_size(res)); + return ERR_PTR(-ENOMEM); + } + + return &pwm->pwm; +} +EXPORT_SYMBOL(samsung_pwm_get); + +/* + * Clocksource driver + */ + struct samsung_timer_source { unsigned int event_id; unsigned int source_id; diff --git a/include/clocksource/samsung_pwm.h b/include/clocksource/samsung_pwm.h new file mode 100644 index 0000000..d16415f --- /dev/null +++ b/include/clocksource/samsung_pwm.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __CLOCKSOURCE_SAMSUNG_PWM_H +#define __CLOCKSOURCE_SAMSUNG_PWM_H + +#include + +#define SAMSUNG_PWM_NUM 5 + +struct platform_device; +struct device_node; + +struct samsung_pwm_variant { + u8 bits; + u8 div_base; + u8 tclk_mask; + u8 output_mask; + bool has_tint_cstat; +}; + +struct samsung_pwm { + struct samsung_pwm_variant variant; + spinlock_t slock; + void __iomem *base; + int irq[SAMSUNG_PWM_NUM]; +}; + +extern struct samsung_pwm *samsung_pwm_get(struct platform_device *, + struct device_node *); + +#endif /* __CLOCKSOURCE_SAMSUNG_PWM_H */