/* * Kontron PLD watchdog driver * * Copyright (c) 2010-2013 Kontron Europe GmbH * Author: Michael Brunner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. * * Note: From the PLD watchdog point of view timeout and pretimeout are * defined differently than in the kernel. * First the pretimeout stage runs out before the timeout stage gets * active. * * Kernel/API: P-----| pretimeout * |-----------------------T timeout * Watchdog: |-----------------P pretimeout_stage * |-----T timeout_stage */ #include #include #include #include #include #include #define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b + (x) * 4) #define KEMPLD_WDT_STAGE_CFG(x) (0x18 + (x)) #define STAGE_CFG_GET_PRESCALER(x) (((x) & 0x30) >> 4) #define STAGE_CFG_SET_PRESCALER(x) (((x) & 0x3) << 4) #define STAGE_CFG_PRESCALER_MASK 0x30 #define STAGE_CFG_ACTION_MASK 0x7 #define STAGE_CFG_ASSERT (1 << 3) #define KEMPLD_WDT_MAX_STAGES 2 #define KEMPLD_WDT_KICK 0x16 #define KEMPLD_WDT_CFG 0x17 #define KEMPLD_WDT_CFG_ENABLE 0x10 #define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8 #define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80 enum { ACTION_NONE = 0, ACTION_RESET, ACTION_NMI, ACTION_SMI, ACTION_SCI, ACTION_DELAY, }; enum { STAGE_TIMEOUT = 0, STAGE_PRETIMEOUT, }; enum { PRESCALER_21 = 0, PRESCALER_17, PRESCALER_12, }; static const u32 kempld_prescaler[] = { [PRESCALER_21] = (1 << 21) - 1, [PRESCALER_17] = (1 << 17) - 1, [PRESCALER_12] = (1 << 12) - 1, 0, }; struct kempld_wdt_stage { unsigned int id; u32 mask; }; struct kempld_wdt_data { struct kempld_device_data *pld; struct watchdog_device wdd; unsigned int pretimeout; struct kempld_wdt_stage stage[KEMPLD_WDT_MAX_STAGES]; #ifdef CONFIG_PM u8 pm_status_store; #endif }; #define DEFAULT_TIMEOUT 30 /* seconds */ #define DEFAULT_PRETIMEOUT 0 static unsigned int timeout = DEFAULT_TIMEOUT; module_param(timeout, uint, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (>=0, default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); static unsigned int pretimeout = DEFAULT_PRETIMEOUT; module_param(pretimeout, uint, 0); MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout in seconds. (>=0, default=" __MODULE_STRING(DEFAULT_PRETIMEOUT) ")"); 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 int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data, struct kempld_wdt_stage *stage, u8 action) { struct kempld_device_data *pld = wdt_data->pld; u8 stage_cfg; if (!stage || !stage->mask) return -EINVAL; kempld_get_mutex(pld); stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); stage_cfg &= ~STAGE_CFG_ACTION_MASK; stage_cfg |= (action & STAGE_CFG_ACTION_MASK); if (action == ACTION_RESET) stage_cfg |= STAGE_CFG_ASSERT; else stage_cfg &= ~STAGE_CFG_ASSERT; kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); kempld_release_mutex(pld); return 0; } static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data, struct kempld_wdt_stage *stage, unsigned int timeout) { struct kempld_device_data *pld = wdt_data->pld; u32 prescaler = kempld_prescaler[PRESCALER_21]; u64 stage_timeout64; u32 stage_timeout; u32 remainder; u8 stage_cfg; if (!stage) return -EINVAL; stage_timeout64 = (u64)timeout * pld->pld_clock; remainder = do_div(stage_timeout64, prescaler); if (remainder) stage_timeout64++; if (stage_timeout64 > stage->mask) return -EINVAL; stage_timeout = stage_timeout64 & stage->mask; kempld_get_mutex(pld); stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); stage_cfg &= ~STAGE_CFG_PRESCALER_MASK; stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21); kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id), stage_timeout); kempld_release_mutex(pld); return 0; } /* * kempld_get_mutex must be called prior to calling this function. */ static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data, struct kempld_wdt_stage *stage) { struct kempld_device_data *pld = wdt_data->pld; unsigned int timeout; u64 stage_timeout; u32 prescaler; u32 remainder; u8 stage_cfg; if (!stage->mask) return 0; stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id)); prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)]; stage_timeout = (stage_timeout & stage->mask) * prescaler; remainder = do_div(stage_timeout, pld->pld_clock); if (remainder) stage_timeout++; timeout = stage_timeout; WARN_ON_ONCE(timeout != stage_timeout); return timeout; } static int kempld_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_wdt_stage *pretimeout_stage; struct kempld_wdt_stage *timeout_stage; int ret; timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; if (pretimeout_stage->mask && wdt_data->pretimeout > 0) timeout = wdt_data->pretimeout; ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage, ACTION_RESET); if (ret) return ret; ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage, timeout); if (ret) return ret; wdd->timeout = timeout; return 0; } static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int pretimeout) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_wdt_stage *pretimeout_stage; u8 action = ACTION_NONE; int ret; pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; if (!pretimeout_stage->mask) return -ENXIO; if (pretimeout > wdd->timeout) return -EINVAL; if (pretimeout > 0) action = ACTION_NMI; ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage, action); if (ret) return ret; ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage, wdd->timeout - pretimeout); if (ret) return ret; wdt_data->pretimeout = pretimeout; return 0; } static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data) { struct kempld_device_data *pld = wdt_data->pld; struct kempld_wdt_stage *pretimeout_stage; struct kempld_wdt_stage *timeout_stage; unsigned int pretimeout, timeout; pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; kempld_get_mutex(pld); pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage); timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage); kempld_release_mutex(pld); if (pretimeout) wdt_data->pretimeout = timeout; else wdt_data->pretimeout = 0; wdt_data->wdd.timeout = pretimeout + timeout; } static int kempld_wdt_start(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; u8 status; int ret; ret = kempld_wdt_set_timeout(wdd, wdd->timeout); if (ret) return ret; kempld_get_mutex(pld); status = kempld_read8(pld, KEMPLD_WDT_CFG); status |= KEMPLD_WDT_CFG_ENABLE; kempld_write8(pld, KEMPLD_WDT_CFG, status); status = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); /* Check if the watchdog was enabled */ if (!(status & KEMPLD_WDT_CFG_ENABLE)) return -EACCES; return 0; } static int kempld_wdt_stop(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; u8 status; kempld_get_mutex(pld); status = kempld_read8(pld, KEMPLD_WDT_CFG); status &= ~KEMPLD_WDT_CFG_ENABLE; kempld_write8(pld, KEMPLD_WDT_CFG, status); status = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); /* Check if the watchdog was disabled */ if (status & KEMPLD_WDT_CFG_ENABLE) return -EACCES; return 0; } static int kempld_wdt_keepalive(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; kempld_get_mutex(pld); kempld_write8(pld, KEMPLD_WDT_KICK, 'K'); kempld_release_mutex(pld); return 0; } static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); void __user *argp = (void __user *)arg; int ret = -ENOIOCTLCMD; int __user *p = argp; int new_value; switch (cmd) { case WDIOC_SETPRETIMEOUT: if (get_user(new_value, p)) return -EFAULT; ret = kempld_wdt_set_pretimeout(wdd, new_value); if (ret) return ret; ret = kempld_wdt_keepalive(wdd); break; case WDIOC_GETPRETIMEOUT: ret = put_user(wdt_data->pretimeout, (int __user *)arg); break; } return ret; } static int kempld_wdt_probe_stages(struct watchdog_device *wdd) { struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); struct kempld_device_data *pld = wdt_data->pld; struct kempld_wdt_stage *pretimeout_stage; struct kempld_wdt_stage *timeout_stage; u8 index, data, data_orig; u32 mask; int i, j; pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; pretimeout_stage->mask = 0; timeout_stage->mask = 0; for (i = 0; i < 3; i++) { index = KEMPLD_WDT_STAGE_TIMEOUT(i); mask = 0; kempld_get_mutex(pld); /* Probe each byte individually. */ for (j = 0; j < 4; j++) { data_orig = kempld_read8(pld, index + j); kempld_write8(pld, index + j, 0x00); data = kempld_read8(pld, index + j); /* A failed write means this byte is reserved */ if (data != 0x00) break; kempld_write8(pld, index + j, data_orig); mask |= 0xff << (j * 8); } kempld_release_mutex(pld); /* Assign available stages to timeout and pretimeout */ if (!timeout_stage->mask) { timeout_stage->mask = mask; timeout_stage->id = i; } else { if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) { pretimeout_stage->mask = timeout_stage->mask; timeout_stage->mask = mask; pretimeout_stage->id = timeout_stage->id; timeout_stage->id = i; } break; } } if (!timeout_stage->mask) return -ENODEV; return 0; } static struct watchdog_info kempld_wdt_info = { .identity = "KEMPLD Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT }; static const struct watchdog_ops kempld_wdt_ops = { .owner = THIS_MODULE, .start = kempld_wdt_start, .stop = kempld_wdt_stop, .ping = kempld_wdt_keepalive, .set_timeout = kempld_wdt_set_timeout, .ioctl = kempld_wdt_ioctl, }; static int kempld_wdt_probe(struct platform_device *pdev) { struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); struct kempld_wdt_data *wdt_data; struct device *dev = &pdev->dev; struct watchdog_device *wdd; u8 status; int ret = 0; wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); if (!wdt_data) return -ENOMEM; wdt_data->pld = pld; wdd = &wdt_data->wdd; wdd->parent = dev; kempld_get_mutex(pld); status = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); /* Enable nowayout if watchdog is already locked */ if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK | KEMPLD_WDT_CFG_GLOBAL_LOCK)) { if (!nowayout) dev_warn(dev, "Forcing nowayout - watchdog lock enabled!\n"); nowayout = true; } wdd->info = &kempld_wdt_info; wdd->ops = &kempld_wdt_ops; watchdog_set_drvdata(wdd, wdt_data); watchdog_set_nowayout(wdd, nowayout); ret = kempld_wdt_probe_stages(wdd); if (ret) return ret; kempld_wdt_set_timeout(wdd, timeout); kempld_wdt_set_pretimeout(wdd, pretimeout); /* Check if watchdog is already enabled */ if (status & KEMPLD_WDT_CFG_ENABLE) { /* Get current watchdog settings */ kempld_wdt_update_timeouts(wdt_data); dev_info(dev, "Watchdog was already enabled\n"); } platform_set_drvdata(pdev, wdt_data); ret = watchdog_register_device(wdd); if (ret) return ret; dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout); return 0; } static void kempld_wdt_shutdown(struct platform_device *pdev) { struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); kempld_wdt_stop(&wdt_data->wdd); } static int kempld_wdt_remove(struct platform_device *pdev) { struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); struct watchdog_device *wdd = &wdt_data->wdd; int ret = 0; if (!nowayout) ret = kempld_wdt_stop(wdd); watchdog_unregister_device(wdd); return ret; } #ifdef CONFIG_PM /* Disable watchdog if it is active during suspend */ static int kempld_wdt_suspend(struct platform_device *pdev, pm_message_t message) { struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); struct kempld_device_data *pld = wdt_data->pld; struct watchdog_device *wdd = &wdt_data->wdd; kempld_get_mutex(pld); wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG); kempld_release_mutex(pld); kempld_wdt_update_timeouts(wdt_data); if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) return kempld_wdt_stop(wdd); return 0; } /* Enable watchdog and configure it if necessary */ static int kempld_wdt_resume(struct platform_device *pdev) { struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); struct watchdog_device *wdd = &wdt_data->wdd; /* * If watchdog was stopped before suspend be sure it gets disabled * again, for the case BIOS has enabled it during resume */ if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) return kempld_wdt_start(wdd); else return kempld_wdt_stop(wdd); } #else #define kempld_wdt_suspend NULL #define kempld_wdt_resume NULL #endif static struct platform_driver kempld_wdt_driver = { .driver = { .name = "kempld-wdt", }, .probe = kempld_wdt_probe, .remove = kempld_wdt_remove, .shutdown = kempld_wdt_shutdown, .suspend = kempld_wdt_suspend, .resume = kempld_wdt_resume, }; module_platform_driver(kempld_wdt_driver); MODULE_DESCRIPTION("KEM PLD Watchdog Driver"); MODULE_AUTHOR("Michael Brunner "); MODULE_LICENSE("GPL"); r unix sockets. An address can contain a relative path or a file may be moved somewhere. And these properties say nothing about a mount namespace and a mount point of a socket file. With the introduced ioctl, we can get a path by reading /proc/self/fd/X and get mnt_id from /proc/self/fdinfo/X. In CRIU we are going to use this ioctl to dump and restore unix socket. Here is an example how it can be used: $ strace -e socket,bind,ioctl ./test /tmp/test_sock socket(AF_UNIX, SOCK_STREAM, 0) = 3 bind(3, {sa_family=AF_UNIX, sun_path="test_sock"}, 11) = 0 ioctl(3, SIOCUNIXFILE, 0) = 4 ^Z $ ss -a | grep test_sock u_str LISTEN 0 1 test_sock 17798 * 0 $ ls -l /proc/760/fd/{3,4} lrwx------ 1 root root 64 Feb 1 09:41 3 -> 'socket:[17798]' l--------- 1 root root 64 Feb 1 09:41 4 -> /tmp/test_sock $ cat /proc/760/fdinfo/4 pos: 0 flags: 012000000 mnt_id: 40 $ cat /proc/self/mountinfo | grep "^40\s" 40 19 0:37 / /tmp rw shared:23 - tmpfs tmpfs rw Signed-off-by: Andrei Vagin <avagin@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-02net: phy: Marvell: Add mv88e6390 internal PHYAndrew Lunn1-0/+6 The mv88e6390 Ethernet switch has internal PHYs. These PHYs don't have an model ID in the ID2 register. So the MDIO driver in the switch intercepts reads to this register, and returns the switch family ID. Extend the Marvell PHY driver by including this ID, and treat the PHY as a 88E1540. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-02Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/netDavid S. Miller9-30/+40 All merge conflicts were simple overlapping changes. Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-01Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/netLinus Torvalds4-19/+26 Pull networking fixes from David Miller: 1) Fix handling of interrupt status in stmmac driver. Just because we have masked the event from generating interrupts, doesn't mean the bit won't still be set in the interrupt status register. From Alexey Brodkin. 2) Fix DMA API debugging splats in gianfar driver, from Arseny Solokha. 3) Fix off-by-one error in __ip6_append_data(), from Vlad Yasevich. 4) cls_flow does not match on icmpv6 codes properly, from Simon Horman. 5) Initial MAC address can be set incorrectly in some scenerios, from Ivan Vecera. 6) Packet header pointer arithmetic fix in ip6_tnl_parse_tlv_end_lim(), from Dan Carpenter. 7) Fix divide by zero in __tcp_select_window(), from Eric Dumazet. 8) Fix crash in iwlwifi when unregistering thermal zone, from Jens Axboe. 9) Check for DMA mapping errors in starfire driver, from Alexey Khoroshilov. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (31 commits) tcp: fix 0 divide in __tcp_select_window() ipv6: pointer math error in ip6_tnl_parse_tlv_enc_lim() net: fix ndo_features_check/ndo_fix_features comment ordering net/sched: matchall: Fix configuration race be2net: fix initial MAC setting ipv6: fix flow labels when the traffic class is non-0 net: thunderx: avoid dereferencing xcv when NULL net/sched: cls_flower: Correct matching on ICMPv6 code ipv6: Paritially checksum full MTU frames net/mlx4_core: Avoid command timeouts during VF driver device shutdown gianfar: synchronize DMA API usage by free_skb_rx_queue w/ gfar_new_page net: ethtool: add support for 2500BaseT and 5000BaseT link modes can: bcm: fix hrtimer/tasklet termination in bcm op removal net: adaptec: starfire: add checks for dma mapping errors net: phy: micrel: KSZ8795 do not set SUPPORTED_[Asym_]Pause can: Fix kernel panic at security_sock_rcv_skb net: macb: Fix 64 bit addressing support for GEM stmmac: Discard masked flags in interrupt status register net/mlx5e: Check ets capability before ets query FW command net/mlx5e: Fix update of hash function/key via ethtool ... 2017-02-01Merge branch 'for-linus' of ↵Linus Torvalds1-0/+1 git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs Pull fscache fixes from Al Viro. * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: fscache: Fix dead object requeue fscache: Clear outstanding writes when disabling a cookie FS-Cache: Initialise stores_lock in netfs cookie 2017-02-01Merge tag 'wireless-drivers-next-for-davem-2017-02-01' of ↵David S. Miller2-1/+2 git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next Kalle Valo says: ==================== wireless-drivers-next patches for 4.11 It's nice to see rt2x00 development has becoming active, for example adding support for a new chip version. Also wcn36xx has been converted to use the recently merged QCOM_SMD subsystem. Otherwise new features and fixes it lots of drivers. Major changes: iwlwifi * some more work in preparation for A000 family support * add support for radiotap timestamps * some work on our firmware debugging capabilities wcn36xx * convert to a proper QCOM_SMD driver (from the platform_driver interface) ath10k * VHT160 support * dump Copy Engine registers during firmware crash * search board file extension from SMBIOS wil6210 * add disable_ap_sme module parameter rt2x00 * support RT3352 with external PA * support for RT3352 with 20MHz crystal * add support for RT5350 WiSoC brcmfmac * add support for BCM43455 sdio device rtl8xxxu * add support for D-Link DWA-131 rev E1, TP-Link TL-WN822N v4 and others ==================== Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-01net: fix ndo_features_check/ndo_fix_features comment orderingDimitris Michailidis1-14/+15 Commit cdba756f5803a2 ("net: move ndo_features_check() close to ndo_start_xmit()") inadvertently moved the doc comment for .ndo_fix_features instead of .ndo_features_check. Fix the comment ordering. Fixes: cdba756f5803a2 ("net: move ndo_features_check() close to ndo_start_xmit()") Signed-off-by: Dimitris Michailidis <dmichail@google.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>