162306a36Sopenharmony_ci=========================================================
262306a36Sopenharmony_ciConverting old watchdog drivers to the watchdog framework
362306a36Sopenharmony_ci=========================================================
462306a36Sopenharmony_ci
562306a36Sopenharmony_ciby Wolfram Sang <wsa@kernel.org>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ciBefore the watchdog framework came into the kernel, every driver had to
862306a36Sopenharmony_ciimplement the API on its own. Now, as the framework factored out the common
962306a36Sopenharmony_cicomponents, those drivers can be lightened making it a user of the framework.
1062306a36Sopenharmony_ciThis document shall guide you for this task. The necessary steps are described
1162306a36Sopenharmony_cias well as things to look out for.
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciRemove the file_operations struct
1562306a36Sopenharmony_ci---------------------------------
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciOld drivers define their own file_operations for actions like open(), write(),
1862306a36Sopenharmony_cietc... These are now handled by the framework and just call the driver when
1962306a36Sopenharmony_cineeded. So, in general, the 'file_operations' struct and assorted functions can
2062306a36Sopenharmony_cigo. Only very few driver-specific details have to be moved to other functions.
2162306a36Sopenharmony_ciHere is a overview of the functions and probably needed actions:
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci- open: Everything dealing with resource management (file-open checks, magic
2462306a36Sopenharmony_ci  close preparations) can simply go. Device specific stuff needs to go to the
2562306a36Sopenharmony_ci  driver specific start-function. Note that for some drivers, the start-function
2662306a36Sopenharmony_ci  also serves as the ping-function. If that is the case and you need start/stop
2762306a36Sopenharmony_ci  to be balanced (clocks!), you are better off refactoring a separate start-function.
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci- close: Same hints as for open apply.
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci- write: Can simply go, all defined behaviour is taken care of by the framework,
3262306a36Sopenharmony_ci  i.e. ping on write and magic char ('V') handling.
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci- ioctl: While the driver is allowed to have extensions to the IOCTL interface,
3562306a36Sopenharmony_ci  the most common ones are handled by the framework, supported by some assistance
3662306a36Sopenharmony_ci  from the driver:
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	WDIOC_GETSUPPORT:
3962306a36Sopenharmony_ci		Returns the mandatory watchdog_info struct from the driver
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	WDIOC_GETSTATUS:
4262306a36Sopenharmony_ci		Needs the status-callback defined, otherwise returns 0
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	WDIOC_GETBOOTSTATUS:
4562306a36Sopenharmony_ci		Needs the bootstatus member properly set. Make sure it is 0 if you
4662306a36Sopenharmony_ci		don't have further support!
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	WDIOC_SETOPTIONS:
4962306a36Sopenharmony_ci		No preparations needed
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	WDIOC_KEEPALIVE:
5262306a36Sopenharmony_ci		If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING
5362306a36Sopenharmony_ci		set
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	WDIOC_SETTIMEOUT:
5662306a36Sopenharmony_ci		Options in watchdog_info need to have WDIOF_SETTIMEOUT set
5762306a36Sopenharmony_ci		and a set_timeout-callback has to be defined. The core will also
5862306a36Sopenharmony_ci		do limit-checking, if min_timeout and max_timeout in the watchdog
5962306a36Sopenharmony_ci		device are set. All is optional.
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	WDIOC_GETTIMEOUT:
6262306a36Sopenharmony_ci		No preparations needed
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	WDIOC_GETTIMELEFT:
6562306a36Sopenharmony_ci		It needs get_timeleft() callback to be defined. Otherwise it
6662306a36Sopenharmony_ci		will return EOPNOTSUPP
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci  Other IOCTLs can be served using the ioctl-callback. Note that this is mainly
6962306a36Sopenharmony_ci  intended for porting old drivers; new drivers should not invent private IOCTLs.
7062306a36Sopenharmony_ci  Private IOCTLs are processed first. When the callback returns with
7162306a36Sopenharmony_ci  -ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error
7262306a36Sopenharmony_ci  is directly given to the user.
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciExample conversion::
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci  -static const struct file_operations s3c2410wdt_fops = {
7762306a36Sopenharmony_ci  -       .owner          = THIS_MODULE,
7862306a36Sopenharmony_ci  -       .llseek         = no_llseek,
7962306a36Sopenharmony_ci  -       .write          = s3c2410wdt_write,
8062306a36Sopenharmony_ci  -       .unlocked_ioctl = s3c2410wdt_ioctl,
8162306a36Sopenharmony_ci  -       .open           = s3c2410wdt_open,
8262306a36Sopenharmony_ci  -       .release        = s3c2410wdt_release,
8362306a36Sopenharmony_ci  -};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciCheck the functions for device-specific stuff and keep it for later
8662306a36Sopenharmony_cirefactoring. The rest can go.
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciRemove the miscdevice
9062306a36Sopenharmony_ci---------------------
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciSince the file_operations are gone now, you can also remove the 'struct
9362306a36Sopenharmony_cimiscdevice'. The framework will create it on watchdog_dev_register() called by
9462306a36Sopenharmony_ciwatchdog_register_device()::
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci  -static struct miscdevice s3c2410wdt_miscdev = {
9762306a36Sopenharmony_ci  -       .minor          = WATCHDOG_MINOR,
9862306a36Sopenharmony_ci  -       .name           = "watchdog",
9962306a36Sopenharmony_ci  -       .fops           = &s3c2410wdt_fops,
10062306a36Sopenharmony_ci  -};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciRemove obsolete includes and defines
10462306a36Sopenharmony_ci------------------------------------
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciBecause of the simplifications, a few defines are probably unused now. Remove
10762306a36Sopenharmony_cithem. Includes can be removed, too. For example::
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci  - #include <linux/fs.h>
11062306a36Sopenharmony_ci  - #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used)
11162306a36Sopenharmony_ci  - #include <linux/uaccess.h> (if no custom IOCTLs are used)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciAdd the watchdog operations
11562306a36Sopenharmony_ci---------------------------
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciAll possible callbacks are defined in 'struct watchdog_ops'. You can find it
11862306a36Sopenharmony_ciexplained in 'watchdog-kernel-api.txt' in this directory. start() and
11962306a36Sopenharmony_ciowner must be set, the rest are optional. You will easily find corresponding
12062306a36Sopenharmony_cifunctions in the old driver. Note that you will now get a pointer to the
12162306a36Sopenharmony_ciwatchdog_device as a parameter to these functions, so you probably have to
12262306a36Sopenharmony_cichange the function header. Other changes are most likely not needed, because
12362306a36Sopenharmony_cihere simply happens the direct hardware access. If you have device-specific
12462306a36Sopenharmony_cicode left from the above steps, it should be refactored into these callbacks.
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciHere is a simple example::
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci  +static struct watchdog_ops s3c2410wdt_ops = {
12962306a36Sopenharmony_ci  +       .owner = THIS_MODULE,
13062306a36Sopenharmony_ci  +       .start = s3c2410wdt_start,
13162306a36Sopenharmony_ci  +       .stop = s3c2410wdt_stop,
13262306a36Sopenharmony_ci  +       .ping = s3c2410wdt_keepalive,
13362306a36Sopenharmony_ci  +       .set_timeout = s3c2410wdt_set_heartbeat,
13462306a36Sopenharmony_ci  +};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciA typical function-header change looks like::
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci  -static void s3c2410wdt_keepalive(void)
13962306a36Sopenharmony_ci  +static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
14062306a36Sopenharmony_ci   {
14162306a36Sopenharmony_ci  ...
14262306a36Sopenharmony_ci  +
14362306a36Sopenharmony_ci  +       return 0;
14462306a36Sopenharmony_ci   }
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci  ...
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci  -       s3c2410wdt_keepalive();
14962306a36Sopenharmony_ci  +       s3c2410wdt_keepalive(&s3c2410_wdd);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciAdd the watchdog device
15362306a36Sopenharmony_ci-----------------------
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciNow we need to create a 'struct watchdog_device' and populate it with the
15662306a36Sopenharmony_cinecessary information for the framework. The struct is also explained in detail
15762306a36Sopenharmony_ciin 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory
15862306a36Sopenharmony_ciwatchdog_info struct and the newly created watchdog_ops. Often, old drivers
15962306a36Sopenharmony_cihave their own record-keeping for things like bootstatus and timeout using
16062306a36Sopenharmony_cistatic variables. Those have to be converted to use the members in
16162306a36Sopenharmony_ciwatchdog_device. Note that the timeout values are unsigned int. Some drivers
16262306a36Sopenharmony_ciuse signed int, so this has to be converted, too.
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciHere is a simple example for a watchdog device::
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci  +static struct watchdog_device s3c2410_wdd = {
16762306a36Sopenharmony_ci  +       .info = &s3c2410_wdt_ident,
16862306a36Sopenharmony_ci  +       .ops = &s3c2410wdt_ops,
16962306a36Sopenharmony_ci  +};
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ciHandle the 'nowayout' feature
17362306a36Sopenharmony_ci-----------------------------
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciA few drivers use nowayout statically, i.e. there is no module parameter for it
17662306a36Sopenharmony_ciand only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be
17762306a36Sopenharmony_ciused. This needs to be converted by initializing the status variable of the
17862306a36Sopenharmony_ciwatchdog_device like this::
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci        .status = WATCHDOG_NOWAYOUT_INIT_STATUS,
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciMost drivers, however, also allow runtime configuration of nowayout, usually
18362306a36Sopenharmony_ciby adding a module parameter. The conversion for this would be something like::
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	watchdog_set_nowayout(&s3c2410_wdd, nowayout);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciThe module parameter itself needs to stay, everything else related to nowayout
18862306a36Sopenharmony_cican go, though. This will likely be some code in open(), close() or write().
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ciRegister the watchdog device
19262306a36Sopenharmony_ci----------------------------
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciReplace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev).
19562306a36Sopenharmony_ciMake sure the return value gets checked and the error message, if present,
19662306a36Sopenharmony_cistill fits. Also convert the unregister case::
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci  -       ret = misc_register(&s3c2410wdt_miscdev);
19962306a36Sopenharmony_ci  +       ret = watchdog_register_device(&s3c2410_wdd);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci  ...
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci  -       misc_deregister(&s3c2410wdt_miscdev);
20462306a36Sopenharmony_ci  +       watchdog_unregister_device(&s3c2410_wdd);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciUpdate the Kconfig-entry
20862306a36Sopenharmony_ci------------------------
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ciThe entry for the driver now needs to select WATCHDOG_CORE:
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci  +       select WATCHDOG_CORE
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ciCreate a patch and send it to upstream
21662306a36Sopenharmony_ci--------------------------------------
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ciMake sure you understood Documentation/process/submitting-patches.rst and send your patch to
21962306a36Sopenharmony_cilinux-watchdog@vger.kernel.org. We are looking forward to it :)
220