drivers/watchdog/pseries-wdt.c

Source file repositories/reference/linux-study-clean/drivers/watchdog/pseries-wdt.c

File Facts

System
Linux kernel
Corpus path
drivers/watchdog/pseries-wdt.c
Extension
.c
Size
6546 bytes
Lines
240
Domain
Driver Families
Bucket
drivers/watchdog
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct pseries_wdt {
	struct watchdog_device wd;
	unsigned long action;
	unsigned long num;		/* Watchdog numbers are 1-based */
};

static int pseries_wdt_start(struct watchdog_device *wdd)
{
	struct pseries_wdt *pw = watchdog_get_drvdata(wdd);
	struct device *dev = wdd->parent;
	unsigned long flags, msecs;
	long rc;

	flags = pw->action | PSERIES_WDTF_OP_START;
	msecs = wdd->timeout * MSEC_PER_SEC;
	rc = plpar_hcall_norets(H_WATCHDOG, flags, pw->num, msecs);
	if (rc != H_SUCCESS) {
		dev_crit(dev, "H_WATCHDOG: %ld: failed to start timer %lu",
			 rc, pw->num);
		return -EIO;
	}
	return 0;
}

static int pseries_wdt_stop(struct watchdog_device *wdd)
{
	struct pseries_wdt *pw = watchdog_get_drvdata(wdd);
	struct device *dev = wdd->parent;
	long rc;

	rc = plpar_hcall_norets(H_WATCHDOG, PSERIES_WDTF_OP_STOP, pw->num);
	if (rc != H_SUCCESS && rc != H_NOOP) {
		dev_crit(dev, "H_WATCHDOG: %ld: failed to stop timer %lu",
			 rc, pw->num);
		return -EIO;
	}
	return 0;
}

static struct watchdog_info pseries_wdt_info = {
	.identity = DRV_NAME,
	.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT
	    | WDIOF_PRETIMEOUT,
};

static const struct watchdog_ops pseries_wdt_ops = {
	.owner = THIS_MODULE,
	.start = pseries_wdt_start,
	.stop = pseries_wdt_stop,
};

static int pseries_wdt_probe(struct platform_device *pdev)
{
	unsigned long ret[PLPAR_HCALL_BUFSIZE] = { 0 };
	struct pseries_wdt *pw;
	unsigned long cap;
	long msecs, rc;
	int err;

	rc = plpar_hcall(H_WATCHDOG, ret, PSERIES_WDTF_OP_QUERY);
	if (rc == H_FUNCTION)
		return -ENODEV;
	if (rc != H_SUCCESS)
		return -EIO;
	cap = ret[0];

	pw = devm_kzalloc(&pdev->dev, sizeof(*pw), GFP_KERNEL);
	if (!pw)
		return -ENOMEM;

	/*
	 * Assume watchdogNumber 1 for now.  If we ever support
	 * multiple timers we will need to devise a way to choose a
	 * distinct watchdogNumber for each platform device at device
	 * registration time.
	 */
	pw->num = 1;
	if (PSERIES_WDTQ_MAX_NUMBER(cap) < pw->num)
		return -ENODEV;

	if (action >= ARRAY_SIZE(pseries_wdt_action))
		return -EINVAL;
	pw->action = pseries_wdt_action[action];

	pw->wd.parent = &pdev->dev;
	pw->wd.info = &pseries_wdt_info;
	pw->wd.ops = &pseries_wdt_ops;
	msecs = PSERIES_WDTQ_MIN_TIMEOUT(cap);
	pw->wd.min_timeout = DIV_ROUND_UP(msecs, MSEC_PER_SEC);
	pw->wd.max_timeout = UINT_MAX / 1000;	/* from linux/watchdog.h */

Annotation

Implementation Notes