brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · d708c09 Raw
195 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * System monitoring driver for DA9052 PMICs.4 *5 * Copyright(c) 2012 Dialog Semiconductor Ltd.6 *7 * Author: Anthony Olech <Anthony.Olech@diasemi.com>8 *9 */10 11#include <linux/module.h>12#include <linux/delay.h>13#include <linux/uaccess.h>14#include <linux/platform_device.h>15#include <linux/time.h>16#include <linux/watchdog.h>17#include <linux/types.h>18#include <linux/kernel.h>19#include <linux/jiffies.h>20 21#include <linux/mfd/da9052/reg.h>22#include <linux/mfd/da9052/da9052.h>23 24#define DA9052_DEF_TIMEOUT	425#define DA9052_TWDMIN		25626 27struct da9052_wdt_data {28	struct watchdog_device wdt;29	struct da9052 *da9052;30	unsigned long jpast;31};32 33static const struct {34	u8 reg_val;35	int time;  /* Seconds */36} da9052_wdt_maps[] = {37	{ 1, 2 },38	{ 2, 4 },39	{ 3, 8 },40	{ 4, 16 },41	{ 5, 32 },42	{ 5, 33 },  /* Actual time  32.768s so included both 32s and 33s */43	{ 6, 65 },44	{ 6, 66 },  /* Actual time 65.536s so include both, 65s and 66s */45	{ 7, 131 },46};47 48 49static int da9052_wdt_set_timeout(struct watchdog_device *wdt_dev,50				  unsigned int timeout)51{52	struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);53	struct da9052 *da9052 = driver_data->da9052;54	int ret, i;55 56	/*57	 * Disable the Watchdog timer before setting58	 * new time out.59	 */60	ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,61				DA9052_CONTROLD_TWDSCALE, 0);62	if (ret < 0) {63		dev_err(da9052->dev, "Failed to disable watchdog bit, %d\n",64			ret);65		return ret;66	}67	if (timeout) {68		/*69		 * To change the timeout, da9052 needs to70		 * be disabled for at least 150 us.71		 */72		udelay(150);73 74		/* Set the desired timeout */75		for (i = 0; i < ARRAY_SIZE(da9052_wdt_maps); i++)76			if (da9052_wdt_maps[i].time == timeout)77				break;78 79		if (i == ARRAY_SIZE(da9052_wdt_maps))80			ret = -EINVAL;81		else82			ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,83						DA9052_CONTROLD_TWDSCALE,84						da9052_wdt_maps[i].reg_val);85		if (ret < 0) {86			dev_err(da9052->dev,87				"Failed to update timescale bit, %d\n", ret);88			return ret;89		}90 91		wdt_dev->timeout = timeout;92		driver_data->jpast = jiffies;93	}94 95	return 0;96}97 98static int da9052_wdt_start(struct watchdog_device *wdt_dev)99{100	return da9052_wdt_set_timeout(wdt_dev, wdt_dev->timeout);101}102 103static int da9052_wdt_stop(struct watchdog_device *wdt_dev)104{105	return da9052_wdt_set_timeout(wdt_dev, 0);106}107 108static int da9052_wdt_ping(struct watchdog_device *wdt_dev)109{110	struct da9052_wdt_data *driver_data = watchdog_get_drvdata(wdt_dev);111	struct da9052 *da9052 = driver_data->da9052;112	unsigned long msec, jnow = jiffies;113	int ret;114 115	/*116	 * We have a minimum time for watchdog window called TWDMIN. A write117	 * to the watchdog before this elapsed time should cause an error.118	 */119	msec = (jnow - driver_data->jpast) * 1000/HZ;120	if (msec < DA9052_TWDMIN)121		mdelay(msec);122 123	/* Reset the watchdog timer */124	ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,125				DA9052_CONTROLD_WATCHDOG, 1 << 7);126	if (ret < 0)127		return ret;128 129	/*130	 * FIXME: Reset the watchdog core, in general PMIC131	 * is supposed to do this132	 */133	return da9052_reg_update(da9052, DA9052_CONTROL_D_REG,134				 DA9052_CONTROLD_WATCHDOG, 0 << 7);135}136 137static const struct watchdog_info da9052_wdt_info = {138	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,139	.identity	= "DA9052 Watchdog",140};141 142static const struct watchdog_ops da9052_wdt_ops = {143	.owner = THIS_MODULE,144	.start = da9052_wdt_start,145	.stop = da9052_wdt_stop,146	.ping = da9052_wdt_ping,147	.set_timeout = da9052_wdt_set_timeout,148};149 150 151static int da9052_wdt_probe(struct platform_device *pdev)152{153	struct device *dev = &pdev->dev;154	struct da9052 *da9052 = dev_get_drvdata(dev->parent);155	struct da9052_wdt_data *driver_data;156	struct watchdog_device *da9052_wdt;157	int ret;158 159	driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL);160	if (!driver_data)161		return -ENOMEM;162	driver_data->da9052 = da9052;163 164	da9052_wdt = &driver_data->wdt;165 166	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;167	da9052_wdt->info = &da9052_wdt_info;168	da9052_wdt->ops = &da9052_wdt_ops;169	da9052_wdt->parent = dev;170	watchdog_set_drvdata(da9052_wdt, driver_data);171 172	ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG,173				DA9052_CONTROLD_TWDSCALE, 0);174	if (ret < 0) {175		dev_err(dev, "Failed to disable watchdog bits, %d\n", ret);176		return ret;177	}178 179	return devm_watchdog_register_device(dev, &driver_data->wdt);180}181 182static struct platform_driver da9052_wdt_driver = {183	.probe = da9052_wdt_probe,184	.driver = {185		.name	= "da9052-watchdog",186	},187};188 189module_platform_driver(da9052_wdt_driver);190 191MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");192MODULE_DESCRIPTION("DA9052 SM Device Driver");193MODULE_LICENSE("GPL");194MODULE_ALIAS("platform:da9052-watchdog");195