Revision f55966571d5eb2876a11e48e798b4592fa1ffbb7 authored by Linus Torvalds on 08 July 2021, 19:28:15 UTC, committed by Linus Torvalds on 08 July 2021, 19:28:15 UTC
Pull drm fixes from Dave Airlie:
 "Some fixes for rc1 that came in the past weeks, mainly a bunch of
  amdgpu fixes, some i915 and the rest are misc around the place. I'm
  sending this a bit early so some more stuff may show up, but I'll
  probably take tomorrow off.

  dma-buf:
   - doc fixes

  amdgpu:
   - Misc Navi fixes
   - Powergating fix
   - Yellow Carp updates
   - Beige Goby updates
   - S0ix fix
   - Revert overlay validation fix
   - GPU reset fix for DC
   - PPC64 fix
   - Add new dimgrey cavefish DID
   - RAS fix
   - TTM fixes

  amdkfd:
   - SVM fixes

  radeon:
   - Fix missing drm_gem_object_put in error path
   - NULL ptr deref fix

  i915:
   - display DP VSC fix
   - DG1 display fix
   - IRQ fixes
   - IRQ demidlayering

  gma500:
   - bo leaks in error paths fixed"

* tag 'drm-next-2021-07-08-1' of git://anongit.freedesktop.org/drm/drm: (52 commits)
  drm/i915: Drop all references to DRM IRQ midlayer
  drm/i915: Use the correct IRQ during resume
  drm/i915/display/dg1: Correctly map DPLLs during state readout
  drm/i915/display: Do not zero past infoframes.vsc
  drm/amdgpu: Conditionally reset SDMA RAS error counts
  drm/amdkfd: Maintain svm_bo reference in page->zone_device_data
  drm/amdkfd: add invalid pages debug at vram migration
  drm/amdkfd: skip migration for pages already in VRAM
  drm/amdkfd: skip invalid pages during migrations
  drm/amdkfd: classify and map mixed svm range pages in GPU
  drm/amdkfd: use hmm range fault to get both domain pfns
  drm/amdgpu: get owner ref in validate and map
  drm/amdkfd: set owner ref to svm range prefault
  drm/amdkfd: add owner ref param to get hmm pages
  drm/amdkfd: device pgmap owner at the svm migrate init
  drm/amdkfd: inc counter on child ranges with xnack off
  drm/amd/display: Extend DMUB diagnostic logging to DCN3.1
  drm/amdgpu: Update NV SIMD-per-CU to 2
  drm/amdgpu: add new dimgrey cavefish DID
  drm/amd/pm: skip PrepareMp1ForUnload message in s0ix
  ...
2 parent s 8c1bfd7 + 21c355b
Raw File
visconti_wdt.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2020 TOSHIBA CORPORATION
 * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation
 * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
 */

#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>

#define WDT_CNT			0x00
#define WDT_MIN			0x04
#define WDT_MAX			0x08
#define WDT_CTL			0x0c
#define WDT_CMD			0x10
#define WDT_CMD_CLEAR		0x4352
#define WDT_CMD_START_STOP	0x5354
#define WDT_DIV			0x30

#define VISCONTI_WDT_FREQ	2000000 /* 2MHz */
#define WDT_DEFAULT_TIMEOUT	10U /* in seconds */

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)")");

struct visconti_wdt_priv {
	struct watchdog_device wdev;
	void __iomem *base;
	u32 div;
};

static int visconti_wdt_start(struct watchdog_device *wdev)
{
	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
	u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;

	writel(priv->div, priv->base + WDT_DIV);
	writel(0, priv->base + WDT_MIN);
	writel(timeout, priv->base + WDT_MAX);
	writel(0, priv->base + WDT_CTL);
	writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);

	return 0;
}

static int visconti_wdt_stop(struct watchdog_device *wdev)
{
	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);

	writel(1, priv->base + WDT_CTL);
	writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);

	return 0;
}

static int visconti_wdt_ping(struct watchdog_device *wdd)
{
	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd);

	writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);

	return 0;
}

static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev)
{
	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
	u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
	u32 cnt = readl(priv->base + WDT_CNT);

	if (timeout <= cnt)
		return 0;
	timeout -= cnt;

	return timeout / VISCONTI_WDT_FREQ;
}

static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
{
	u32 val;
	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);

	wdev->timeout = timeout;
	val = wdev->timeout * VISCONTI_WDT_FREQ;

	/* Clear counter before setting timeout because WDT expires */
	writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
	writel(val, priv->base + WDT_MAX);

	return 0;
}

static const struct watchdog_info visconti_wdt_info = {
	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
	.identity = "Visconti Watchdog",
};

static const struct watchdog_ops visconti_wdt_ops = {
	.owner		= THIS_MODULE,
	.start		= visconti_wdt_start,
	.stop		= visconti_wdt_stop,
	.ping		= visconti_wdt_ping,
	.get_timeleft	= visconti_wdt_get_timeleft,
	.set_timeout	= visconti_wdt_set_timeout,
};

static void visconti_clk_disable_unprepare(void *data)
{
	clk_disable_unprepare(data);
}

static int visconti_wdt_probe(struct platform_device *pdev)
{
	struct watchdog_device *wdev;
	struct visconti_wdt_priv *priv;
	struct device *dev = &pdev->dev;
	struct clk *clk;
	int ret;
	unsigned long clk_freq;

	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(priv->base))
		return PTR_ERR(priv->base);

	clk = devm_clk_get(dev, NULL);
	if (IS_ERR(clk))
		return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n");

	ret = clk_prepare_enable(clk);
	if (ret) {
		dev_err(dev, "Could not enable clock\n");
		return ret;
	}

	ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk);
	if (ret)
		return ret;

	clk_freq = clk_get_rate(clk);
	if (!clk_freq)
		return -EINVAL;

	priv->div = clk_freq / VISCONTI_WDT_FREQ;

	/* Initialize struct watchdog_device. */
	wdev = &priv->wdev;
	wdev->info = &visconti_wdt_info;
	wdev->ops = &visconti_wdt_ops;
	wdev->parent = dev;
	wdev->min_timeout = 1;
	wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ;
	wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT);

	watchdog_set_drvdata(wdev, priv);
	watchdog_set_nowayout(wdev, nowayout);
	watchdog_stop_on_unregister(wdev);

	/* This overrides the default timeout only if DT configuration was found */
	ret = watchdog_init_timeout(wdev, 0, dev);
	if (ret)
		dev_warn(dev, "Specified timeout value invalid, using default\n");

	return devm_watchdog_register_device(dev, wdev);
}

static const struct of_device_id visconti_wdt_of_match[] = {
	{ .compatible = "toshiba,visconti-wdt", },
	{}
};
MODULE_DEVICE_TABLE(of, visconti_wdt_of_match);

static struct platform_driver visconti_wdt_driver = {
	.driver = {
			.name = "visconti_wdt",
			.of_match_table = visconti_wdt_of_match,
		},
	.probe = visconti_wdt_probe,
};
module_platform_driver(visconti_wdt_driver);

MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
MODULE_LICENSE("GPL v2");
back to top