/* * omap_wdt.c * * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog * * Author: MontaVista Software, Inc. * or * * 2003 (c) MontaVista Software, Inc. This file is licensed under the * terms of the GNU General Public License version 2. This program is * licensed "as is" without any warranty of any kind, whether express * or implied. * * History: * * 20030527: George G. Davis * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c * (c) Copyright 2000 Oleg Drokin * Based on SoftDog driver by Alan Cox * * Copyright (c) 2004 Texas Instruments. * 1. Modified to support OMAP1610 32-KHz watchdog timer * 2. Ported to 2.6 kernel * * Copyright (c) 2005 David Brownell * Use the driver model and standard identifiers; handle bigger timeouts. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "omap_wdt.h" static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); static unsigned timer_margin; module_param(timer_margin, uint, 0); MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); #define to_omap_wdt_dev(_wdog) container_of(_wdog, struct omap_wdt_dev, wdog) static bool early_enable; module_param(early_enable, bool, 0); MODULE_PARM_DESC(early_enable, "Watchdog is started on module insertion (default=0)"); struct omap_wdt_dev { struct watchdog_device wdog; void __iomem *base; /* physical */ struct device *dev; bool omap_wdt_users; int wdt_trgr_pattern; struct mutex lock; /* to avoid races with PM */ }; static void omap_wdt_reload(struct omap_wdt_dev *wdev) { void __iomem *base = wdev->base; /* wait for posted write to complete */ while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08) cpu_relax(); wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern; writel_relaxed(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); /* wait for posted write to complete */ while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x08) cpu_relax(); /* reloaded WCRR from WLDR */ } static void omap_wdt_enable(struct omap_wdt_dev *wdev) { void __iomem *base = wdev->base; /* Sequence to enable the watchdog */ writel_relaxed(0xBBBB, base + OMAP_WATCHDOG_SPR); while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10) cpu_relax(); writel_relaxed(0x4444, base + OMAP_WATCHDOG_SPR); while ((readl_relaxed(base + OMAP_WATCHDOG_WPS)) & 0x10) cpu_relax(); } static void omap_wdt_disable(struct omap_wdt_dev *wdev) { void __iomem *base = wdev->base; /* sequence required to disable watchdog */ writel_relaxed(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10) cpu_relax(); writel_relaxed(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x10) cpu_relax(); } static void omap_wdt_set_timer(struct omap_wdt_dev *wdev, unsigned int timeout) { u32 pre_margin = GET_WLDR_VAL(timeout); void __iomem *base = wdev->base; /* just count up at 32 KHz */ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04) cpu_relax(); writel_relaxed(pre_margin, base + OMAP_WATCHDOG_LDR); while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x04) cpu_relax(); } static int omap_wdt_start(struct watchdog_device *wdog) { struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); void __iomem *base = wdev->base; mutex_lock(&wdev->lock); wdev->omap_wdt_users = true; pm_runtime_get_sync(wdev->dev); /* * Make sure the watchdog is disabled. This is unfortunately required * because writing to various registers with the watchdog running has no * effect. */ omap_wdt_disable(wdev); /* initialize prescaler */ while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01) cpu_relax(); writel_relaxed((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL); while (readl_relaxed(base + OMAP_WATCHDOG_WPS) & 0x01) cpu_relax(); omap_wdt_set_timer(wdev, wdog->timeout); omap_wdt_reload(wdev); /* trigger loading of new timeout value */ omap_wdt_enable(wdev); mutex_unlock(&wdev->lock); return 0; } static int omap_wdt_stop(struct watchdog_device *wdog) { struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); mutex_lock(&wdev->lock); omap_wdt_disable(wdev); pm_runtime_put_sync(wdev->dev); wdev->omap_wdt_users = false; mutex_unlock(&wdev->lock); return 0; } static int omap_wdt_ping(struct watchdog_device *wdog) { struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); mutex_lock(&wdev->lock); omap_wdt_reload(wdev); mutex_unlock(&wdev->lock); return 0; } static int omap_wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) { struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); mutex_lock(&wdev->lock); omap_wdt_disable(wdev); omap_wdt_set_timer(wdev, timeout); omap_wdt_enable(wdev); omap_wdt_reload(wdev); wdog->timeout = timeout; mutex_unlock(&wdev->lock); return 0; } static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog) { struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog); void __iomem *base = wdev->base; u32 value; value = readl_relaxed(base + OMAP_WATCHDOG_CRR); return GET_WCCR_SECS(value); } static const struct watchdog_info omap_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "OMAP Watchdog", }; static const struct watchdog_ops omap_wdt_ops = { .owner = THIS_MODULE, .start = omap_wdt_start, .stop = omap_wdt_stop, .ping = omap_wdt_ping, .set_timeout = omap_wdt_set_timeout, .get_timeleft = omap_wdt_get_timeleft, }; static int omap_wdt_probe(struct platform_device *pdev) { struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); struct resource *res; struct omap_wdt_dev *wdev; int ret; wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); if (!wdev) return -ENOMEM; wdev->omap_wdt_users = false; wdev->dev = &pdev->dev; wdev->wdt_trgr_pattern = 0x1234; mutex_init(&wdev->lock); /* reserve static register mappings */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); wdev->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(wdev->base)) return PTR_ERR(wdev->base); wdev->wdog.info = &omap_wdt_info; wdev->wdog.ops = &omap_wdt_ops; wdev->wdog.min_timeout = TIMER_MARGIN_MIN; wdev->wdog.max_timeout = TIMER_MARGIN_MAX; wdev->wdog.parent = &pdev->dev; if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0) wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; watchdog_set_nowayout(&wdev->wdog, nowayout); platform_set_drvdata(pdev, wdev); pm_runtime_enable(wdev->dev); pm_runtime_get_sync(wdev->dev); if (pdata && pdata->read_reset_sources) { u32 rs = pdata->read_reset_sources(); if (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) wdev->wdog.bootstatus = WDIOF_CARDRESET; } if (!early_enable) omap_wdt_disable(wdev); ret = watchdog_register_device(&wdev->wdog); if (ret) { pm_runtime_disable(wdev->dev); return ret; } pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, wdev->wdog.timeout); if (early_enable) omap_wdt_start(&wdev->wdog); pm_runtime_put(wdev->dev); return 0; } static void omap_wdt_shutdown(struct platform_device *pdev) { struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { omap_wdt_disable(wdev); pm_runtime_put_sync(wdev->dev); } mutex_unlock(&wdev->lock); } static int omap_wdt_remove(struct platform_device *pdev) { struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); pm_runtime_disable(wdev->dev); watchdog_unregister_device(&wdev->wdog); return 0; } #ifdef CONFIG_PM /* REVISIT ... not clear this is the best way to handle system suspend; and * it's very inappropriate for selective device suspend (e.g. suspending this * through sysfs rather than by stopping the watchdog daemon). Also, this * may not play well enough with NOWAYOUT... */ static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) { struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { omap_wdt_disable(wdev); pm_runtime_put_sync(wdev->dev); } mutex_unlock(&wdev->lock); return 0; } static int omap_wdt_resume(struct platform_device *pdev) { struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); mutex_lock(&wdev->lock); if (wdev->omap_wdt_users) { pm_runtime_get_sync(wdev->dev); omap_wdt_enable(wdev); omap_wdt_reload(wdev); } mutex_unlock(&wdev->lock); return 0; } #else #define omap_wdt_suspend NULL #define omap_wdt_resume NULL #endif static const struct of_device_id omap_wdt_of_match[] = { { .compatible = "ti,omap3-wdt", }, {}, }; MODULE_DEVICE_TABLE(of, omap_wdt_of_match); static struct platform_driver omap_wdt_driver = { .probe = omap_wdt_probe, .remove = omap_wdt_remove, .shutdown = omap_wdt_shutdown, .suspend = omap_wdt_suspend, .resume = omap_wdt_resume, .driver = { .name = "omap_wdt", .of_match_table = omap_wdt_of_match, }, }; module_platform_driver(omap_wdt_driver); MODULE_AUTHOR("George G. Davis"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:omap_wdt"); 38 e8 81 f0 a9 63 ad 0e 000050 be 5d 3e 74 2e 4e cb 36 c2 01 a8 14 e1 38 e1 bb 000060 23 79 09 56 77 19 ff 98 e8 44 f3 27 eb 6e 0a cb 000070 c9 36 e3 2a 96 13 07 a0 90 3f 3b bd 1d 04 1d 67 000080 be 33 14 f8 02 c2 a4 02 ab 8b 5b 74 86 17 f0 5e 000090 a1 d7 aa ef a6 21 7b 93 d1 85 86 eb 4e 8c d0 4c 0000a0 56 ac e4 45 27 44 84 9f 71 db 36 b9 f7 47 d7 b3 0000b0 f2 9c 62 41 a3 46 2b 5b e3 80 63 a4 35 b5 3c f4 0000c0 bc 1e 3a ad e4 59 4a 98 6c e8 8d ff 1b 16 f8 52 0000d0 05 5c 2f 52 2a 0f 45 5b 51 fb 93 97 a4 49 4f 06 0000e0 f3 a0 d1 1e ba 3d ed a7 60 8f bb 84 2c 21 94 2d 0000f0 b3 66 a6 61 1e 58 30 24 85 f8 c8 18 c3 77 00 22 000100 000000 73 ca cc a1 d9 bb 21 8d c3 5c f3 ab 43 6d a7 a4 000010 4a fd c5 f4 9c ba 4a 0f b1 2e 19 15 4e 84 26 e0 000020 67 c9 f2 52 4d 65 1f 81 b7 8b 6d 2b 56 7b 99 75 000030 2e cd d0 db 08 0c 4b df f3 83 c6 83 00 2e 2b b8 000040 0f af 61 1d f2 02 35 74 b5 a4 6f 28 f3 a1 09 12 000050 f2 53 b5 d2 da 45 01 e5 12 d6 46 f8 0b db ed 51 000060 7b f4 0d 54 e0 63 ea 22 e2 1d d0 d6 d0 e7 7e e0 000070 93 91 fb 87 95 43 41 28 de 3d 8b a3 a8 8f c4 9e 000080 30 95 12 7a b2 27 28 ff 37 04 2e 09 7c dd 7c 12 000090 e1 50 60 fb 6d 5f a8 65 14 40 89 e3 4c d2 87 8f 0000a0 34 76 7e 66 7a 8e 6b a3 fc cf 38 52 2e f9 26 f0 0000b0 98 63 15 06 34 99 b2 88 4f aa d8 14 88 71 f1 81 0000c0 be 51 11 2b f4 7e a0 1e 12 b2 44 2e f6 8d 84 ea 0000d0 63 82 2b 66 b3 9a fd 08 73 5a c2 cc ab 5a af b1 0000e0 88 e3 a6 80 4b fc db ed 71 e0 ae c0 0a a4 8c 35 0000f0 eb 89 f9 8a 4b 52 59 6f 09 7c 01 3f 56 e7 c7 bf 000100 Signed-off-by: David Daney <david.daney@cavium.com> Acked-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/usb/chipidea/debug.c')