From patchwork Mon Aug 13 22:41:29 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rafael Wysocki X-Patchwork-Id: 1317531 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 5F8B5400E6 for ; Mon, 13 Aug 2012 22:36:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754055Ab2HMWgq (ORCPT ); Mon, 13 Aug 2012 18:36:46 -0400 Received: from ogre.sisk.pl ([193.178.161.156]:37060 "EHLO ogre.sisk.pl" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753133Ab2HMWgp (ORCPT ); Mon, 13 Aug 2012 18:36:45 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by ogre.sisk.pl (Postfix) with ESMTP id 5BBAE1DC10E; Tue, 14 Aug 2012 00:40:55 +0200 (CEST) Received: from ogre.sisk.pl ([127.0.0.1]) by localhost (ogre.sisk.pl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 24838-06; Tue, 14 Aug 2012 00:40:41 +0200 (CEST) Received: from ferrari.rjw.lan (89-67-90-11.dynamic.chello.pl [89.67.90.11]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by ogre.sisk.pl (Postfix) with ESMTP id 4D02D1DC10F; Tue, 14 Aug 2012 00:40:41 +0200 (CEST) From: "Rafael J. Wysocki" To: "Linux-sh list" Subject: [Demo/RFC][PATCH 1/2] ARM: shmobile: Software-only testing device for Mackerel Date: Tue, 14 Aug 2012 00:41:29 +0200 User-Agent: KMail/1.13.6 (Linux/3.5.0+; KDE/4.6.0; x86_64; ; ) Cc: Magnus Damm , Linux PM list References: <201208140040.55978.rjw@sisk.pl> In-Reply-To: <201208140040.55978.rjw@sisk.pl> MIME-Version: 1.0 Message-Id: <201208140041.29369.rjw@sisk.pl> X-Virus-Scanned: amavisd-new at ogre.sisk.pl using MkS_Vir for Linux Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org Add a software-only testing platform device to the Mackerel board configuration and a driver for that device allowing to turn it "on" and "off" artificially with the help of a sysfs interface. Auxiliary material, no sign-off. --- arch/arm/mach-shmobile/board-mackerel.c | 9 + drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 drivers/misc/fake_device.c | 194 ++++++++++++++++++++++++++++++++ drivers/sh/pm_runtime.c | 29 ++++ 5 files changed, 238 insertions(+), 2 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-sh" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Index: linux/arch/arm/mach-shmobile/board-mackerel.c =================================================================== --- linux.orig/arch/arm/mach-shmobile/board-mackerel.c +++ linux/arch/arm/mach-shmobile/board-mackerel.c @@ -1321,6 +1321,14 @@ static struct platform_device mackerel_c }, }; +static struct platform_device fake_device = { + .name = "fake-device", + .id = 0, + .dev = { + .platform_data = "MY FAKE DEVICE", + }, +}; + static struct platform_device *mackerel_devices[] __initdata = { &nor_flash_device, &smc911x_device, @@ -1343,6 +1351,7 @@ static struct platform_device *mackerel_ &hdmi_device, &hdmi_lcdc_device, &meram_device, + &fake_device, }; /* Keypad Initialization */ Index: linux/drivers/misc/Kconfig =================================================================== --- linux.orig/drivers/misc/Kconfig +++ linux/drivers/misc/Kconfig @@ -509,6 +509,13 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port. +config MACKEREL_FAKEDEV + bool "Mackerel Fake Device Support" + depends on MACH_MACKEREL + help + Enable this if you want to experiment with the demo fake device + on the Mackerel board + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" Index: linux/drivers/misc/Makefile =================================================================== --- linux.orig/drivers/misc/Makefile +++ linux/drivers/misc/Makefile @@ -50,3 +50,4 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ +obj-$(CONFIG_MACKEREL_FAKEDEV) += fake_device.o Index: linux/drivers/misc/fake_device.c =================================================================== --- /dev/null +++ linux/drivers/misc/fake_device.c @@ -0,0 +1,194 @@ + +#include +#include +#include +#include +#include +#include + +struct fake_device_priv { + bool enabled; +}; + +static int fake_device_stop(struct device *dev) +{ + dev_info(dev, "%s: stopped\n", __func__); + return 0; +} + +static int fake_device_start(struct device *dev) +{ + dev_info(dev, "%s: started\n", __func__); + return 0; +} + +static int fake_device_save_state(struct device *dev) +{ + dev_info(dev, "%s: state saved\n", __func__); + return 0; +} + +static int fake_device_restore_state(struct device *dev) +{ + dev_info(dev, "%s: state restored\n", __func__); + return 0; +} + +static int fake_device_runtime_suspend(struct device *dev) +{ + int ret = fake_device_save_state(dev); + return ret ? : fake_device_stop(dev); +} + +static int fake_device_runtime_resume(struct device *dev) +{ + int ret = fake_device_start(dev); + return ret ? : fake_device_restore_state(dev); +} + +static const struct dev_pm_ops fake_device_pm_ops = { + .runtime_suspend = fake_device_runtime_suspend, + .runtime_resume = fake_device_runtime_resume, +}; + +static bool fake_device_enabled(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fake_device_priv *priv = platform_get_drvdata(pdev); + + return priv->enabled; +} + +static void fake_device_set_status(struct device *dev, bool enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fake_device_priv *priv = platform_get_drvdata(pdev); + + if (priv->enabled == enabled) + return; + + priv->enabled = enabled; + if (enabled) + pm_runtime_get_sync(dev); + else + pm_runtime_put_sync(dev); +} + +static const char enabled[] = "enabled"; +static const char disabled[] = "disabled"; + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", + fake_device_enabled(dev) ? enabled : disabled); +} + +static ssize_t status_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + char *cp; + int len = n; + + cp = memchr(buf, '\n', n); + if (cp) + len = cp - buf; + + if (len == sizeof(enabled) - 1 && strncmp(buf, enabled, len) == 0) + fake_device_set_status(dev, true); + else if (len == sizeof(disabled) - 1 && strncmp(buf, disabled, len) == 0) + fake_device_set_status(dev, false); + else + return -EINVAL; + + return n; +} + +static DEVICE_ATTR(status, 0644, status_show, status_store); + +static struct attribute *manip_attrs[] = { + &dev_attr_status.attr, + NULL, +}; + +static struct attribute_group manip_attr_group = { + .name = "manip", + .attrs = manip_attrs, +}; + +static int fake_device_remove(struct platform_device *pdev) +{ + struct fake_device_priv *priv = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &manip_attr_group); + pm_runtime_disable(&pdev->dev); + if (priv->enabled) + pm_runtime_put_noidle(&pdev->dev); + else + pm_runtime_set_active(&pdev->dev); + + pm_genpd_remove_callbacks(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(priv); + return 0; +} + +static int __devinit fake_device_probe(struct platform_device *pdev) +{ + struct gpd_dev_ops domain_pm_ops = { + .stop = fake_device_stop, + .start = fake_device_start, + .save_state = fake_device_save_state, + .restore_state = fake_device_restore_state, + }; + struct fake_device_priv *priv; + int ret; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + if (strcmp("MY FAKE DEVICE", pdev->dev.platform_data)) + return -ENODEV; + + dev_info(&pdev->dev, "Fake device %d found\n", pdev->id); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->enabled = true; + platform_set_drvdata(pdev, priv); + pm_genpd_add_callbacks(&pdev->dev, &domain_pm_ops, NULL); + pm_runtime_set_active(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = sysfs_create_group(&pdev->dev.kobj, &manip_attr_group); + if (ret) { + pm_runtime_disable(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(priv); + return ret; + } + + + return 0; +} + +static struct platform_driver fake_device_driver = { + .driver = { + .name = "fake-device", + .owner = THIS_MODULE, + .pm = &fake_device_pm_ops, + }, + .probe = fake_device_probe, + .remove = fake_device_remove, +}; + +module_platform_driver(fake_device_driver); + +MODULE_DESCRIPTION("Mackerel Fake Device driver"); +MODULE_AUTHOR("Rafael J. Wysocki "); +MODULE_LICENSE("GPL v2"); Index: linux/drivers/sh/pm_runtime.c =================================================================== --- linux.orig/drivers/sh/pm_runtime.c +++ linux/drivers/sh/pm_runtime.c @@ -28,10 +28,35 @@ static int default_platform_runtime_idle return pm_runtime_suspend(dev); } +static int default_runtime_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->runtime_suspend) { + int ret = drv->pm->runtime_suspend(dev); + if (ret) + return ret; + } + return pm_clk_suspend(dev); +} + +static int default_runtime_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret; + + ret = pm_clk_resume(dev); + if (ret) + return ret; + + return drv && drv->pm && drv->pm->runtime_resume ? + drv->pm->runtime_resume(dev) : 0; +} + static struct dev_pm_domain default_pm_domain = { .ops = { - .runtime_suspend = pm_clk_suspend, - .runtime_resume = pm_clk_resume, + .runtime_suspend = default_runtime_suspend, + .runtime_resume = default_runtime_resume, .runtime_idle = default_platform_runtime_idle, USE_PLATFORM_PM_SLEEP_OPS },