rtc-coh901331.c 7.19 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2007-2009 ST-Ericsson AB
 * License terms: GNU General Public License (GPL) version 2
 * Real Time Clock interface for ST-Ericsson AB COH 901 331 RTC.
 * Author: Linus Walleij <linus.walleij@stericsson.com>
 * Based on rtc-pl031.c by Deepak Saxena <dsaxena@plexity.net>
 * Copyright 2006 (c) MontaVista Software, Inc.
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/io.h>
17
#include <linux/slab.h>
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

/*
 * Registers in the COH 901 331
 */
/* Alarm value 32bit (R/W) */
#define COH901331_ALARM		0x00U
/* Used to set current time 32bit (R/W) */
#define COH901331_SET_TIME	0x04U
/* Indication if current time is valid 32bit (R/-) */
#define COH901331_VALID		0x08U
/* Read the current time 32bit (R/-) */
#define COH901331_CUR_TIME	0x0cU
/* Event register for the "alarm" interrupt */
#define COH901331_IRQ_EVENT	0x10U
/* Mask register for the "alarm" interrupt */
#define COH901331_IRQ_MASK	0x14U
/* Force register for the "alarm" interrupt */
#define COH901331_IRQ_FORCE	0x18U

/*
 * Reference to RTC block clock
 * Notice that the frequent clk_enable()/clk_disable() on this
 * clock is mainly to be able to turn on/off other clocks in the
 * hierarchy as needed, the RTC clock is always on anyway.
 */
struct coh901331_port {
	struct rtc_device *rtc;
	struct clk *clk;
	void __iomem *virtbase;
	int irq;
48
#ifdef CONFIG_PM_SLEEP
49 50 51 52 53 54 55 56 57 58 59
	u32 irqmaskstore;
#endif
};

static irqreturn_t coh901331_interrupt(int irq, void *data)
{
	struct coh901331_port *rtap = data;

	clk_enable(rtap->clk);
	/* Ack IRQ */
	writel(1, rtap->virtbase + COH901331_IRQ_EVENT);
60 61 62 63 64 65 66 67
	/*
	 * Disable the interrupt. This is necessary because
	 * the RTC lives on a lower-clocked line and will
	 * not release the IRQ line until after a few (slower)
	 * clock cycles. The interrupt will be re-enabled when
	 * a new alarm is set anyway.
	 */
	writel(0, rtap->virtbase + COH901331_IRQ_MASK);
68
	clk_disable(rtap->clk);
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	/* Set alarm flag */
	rtc_update_irq(rtap->rtc, 1, RTC_AF);

	return IRQ_HANDLED;
}

static int coh901331_read_time(struct device *dev, struct rtc_time *tm)
{
	struct coh901331_port *rtap = dev_get_drvdata(dev);

	clk_enable(rtap->clk);
	/* Check if the time is valid */
	if (readl(rtap->virtbase + COH901331_VALID)) {
		rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm);
		clk_disable(rtap->clk);
		return rtc_valid_tm(tm);
	}
	clk_disable(rtap->clk);
	return -EINVAL;
}

static int coh901331_set_mmss(struct device *dev, unsigned long secs)
{
	struct coh901331_port *rtap = dev_get_drvdata(dev);

	clk_enable(rtap->clk);
	writel(secs, rtap->virtbase + COH901331_SET_TIME);
	clk_disable(rtap->clk);

	return 0;
}

static int coh901331_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
	struct coh901331_port *rtap = dev_get_drvdata(dev);

	clk_enable(rtap->clk);
	rtc_time_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time);
	alarm->pending = readl(rtap->virtbase + COH901331_IRQ_EVENT) & 1U;
	alarm->enabled = readl(rtap->virtbase + COH901331_IRQ_MASK) & 1U;
	clk_disable(rtap->clk);

	return 0;
}

static int coh901331_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
	struct coh901331_port *rtap = dev_get_drvdata(dev);
	unsigned long time;

	rtc_tm_to_time(&alarm->time, &time);
	clk_enable(rtap->clk);
	writel(time, rtap->virtbase + COH901331_ALARM);
	writel(alarm->enabled, rtap->virtbase + COH901331_IRQ_MASK);
	clk_disable(rtap->clk);

	return 0;
}

static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
	struct coh901331_port *rtap = dev_get_drvdata(dev);

	clk_enable(rtap->clk);
	if (enabled)
		writel(1, rtap->virtbase + COH901331_IRQ_MASK);
	else
		writel(0, rtap->virtbase + COH901331_IRQ_MASK);
	clk_disable(rtap->clk);
139 140

	return 0;
141 142
}

143
static const struct rtc_class_ops coh901331_ops = {
144 145 146 147 148 149 150 151 152
	.read_time = coh901331_read_time,
	.set_mmss = coh901331_set_mmss,
	.read_alarm = coh901331_read_alarm,
	.set_alarm = coh901331_set_alarm,
	.alarm_irq_enable = coh901331_alarm_irq_enable,
};

static int __exit coh901331_remove(struct platform_device *pdev)
{
153
	struct coh901331_port *rtap = platform_get_drvdata(pdev);
154

155
	if (rtap)
156
		clk_unprepare(rtap->clk);
157 158 159 160 161 162 163 164 165 166 167

	return 0;
}


static int __init coh901331_probe(struct platform_device *pdev)
{
	int ret;
	struct coh901331_port *rtap;
	struct resource *res;

168 169
	rtap = devm_kzalloc(&pdev->dev,
			    sizeof(struct coh901331_port), GFP_KERNEL);
170 171 172 173
	if (!rtap)
		return -ENOMEM;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
174 175 176
	rtap->virtbase  = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(rtap->virtbase))
		return PTR_ERR(rtap->virtbase);
177 178

	rtap->irq = platform_get_irq(pdev, 0);
179 180 181
	if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0,
			     "RTC COH 901 331 Alarm", rtap))
		return -EIO;
182

183
	rtap->clk = devm_clk_get(&pdev->dev, NULL);
184 185 186
	if (IS_ERR(rtap->clk)) {
		ret = PTR_ERR(rtap->clk);
		dev_err(&pdev->dev, "could not get clock\n");
187
		return ret;
188 189 190
	}

	/* We enable/disable the clock only to assure it works */
191
	ret = clk_prepare_enable(rtap->clk);
192 193
	if (ret) {
		dev_err(&pdev->dev, "could not enable clock\n");
194
		return ret;
195 196 197
	}
	clk_disable(rtap->clk);

198
	platform_set_drvdata(pdev, rtap);
199 200
	rtap->rtc = devm_rtc_device_register(&pdev->dev, "coh901331",
					&coh901331_ops, THIS_MODULE);
201 202 203 204 205 206 207 208
	if (IS_ERR(rtap->rtc)) {
		ret = PTR_ERR(rtap->rtc);
		goto out_no_rtc;
	}

	return 0;

 out_no_rtc:
209
	clk_unprepare(rtap->clk);
210 211 212
	return ret;
}

213 214
#ifdef CONFIG_PM_SLEEP
static int coh901331_suspend(struct device *dev)
215
{
216
	struct coh901331_port *rtap = dev_get_drvdata(dev);
217 218 219 220 221 222

	/*
	 * If this RTC alarm will be used for waking the system up,
	 * don't disable it of course. Else we just disable the alarm
	 * and await suspension.
	 */
223
	if (device_may_wakeup(dev)) {
224 225 226 227 228 229 230
		enable_irq_wake(rtap->irq);
	} else {
		clk_enable(rtap->clk);
		rtap->irqmaskstore = readl(rtap->virtbase + COH901331_IRQ_MASK);
		writel(0, rtap->virtbase + COH901331_IRQ_MASK);
		clk_disable(rtap->clk);
	}
231
	clk_unprepare(rtap->clk);
232 233 234
	return 0;
}

235
static int coh901331_resume(struct device *dev)
236
{
237
	struct coh901331_port *rtap = dev_get_drvdata(dev);
238

239
	clk_prepare(rtap->clk);
240
	if (device_may_wakeup(dev)) {
241
		disable_irq_wake(rtap->irq);
242
	} else {
243 244 245
		clk_enable(rtap->clk);
		writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK);
		clk_disable(rtap->clk);
246
	}
247 248 249 250
	return 0;
}
#endif

251 252
static SIMPLE_DEV_PM_OPS(coh901331_pm_ops, coh901331_suspend, coh901331_resume);

253 254
static void coh901331_shutdown(struct platform_device *pdev)
{
255
	struct coh901331_port *rtap = platform_get_drvdata(pdev);
256 257 258

	clk_enable(rtap->clk);
	writel(0, rtap->virtbase + COH901331_IRQ_MASK);
259
	clk_disable_unprepare(rtap->clk);
260 261
}

262 263 264 265
static const struct of_device_id coh901331_dt_match[] = {
	{ .compatible = "stericsson,coh901331" },
	{},
};
266
MODULE_DEVICE_TABLE(of, coh901331_dt_match);
267

268 269 270
static struct platform_driver coh901331_driver = {
	.driver = {
		.name = "rtc-coh901331",
271
		.pm = &coh901331_pm_ops,
272
		.of_match_table = coh901331_dt_match,
273 274 275 276 277
	},
	.remove = __exit_p(coh901331_remove),
	.shutdown = coh901331_shutdown,
};

278
module_platform_driver_probe(coh901331_driver, coh901331_probe);
279 280 281 282

MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("ST-Ericsson AB COH 901 331 RTC Driver");
MODULE_LICENSE("GPL");