Commit c3e6dc65 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'at91-drivers' of...

Merge tag 'at91-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/nferre/linux-at91 into next/drivers

Pull "First batch of drivers for 3.19" from Nicolas Ferre:

It is only about a not so recent driver for old platforms: RTT as RTC driver:
- RTT as RTC driver enhancements and machine specific include files removal
- RTT as RTC driver conversion to device tree

* tag 'at91-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/nferre/linux-at91:
  rtc: at91sam9: add DT bindings documentation
  rtc: at91sam9: use clk API instead of relying on AT91_SLOW_CLOCK
  ARM: at91: add clk_lookup entry for RTT devices
  rtc: at91sam9: rework the Kconfig description
  rtc: at91sam9: make use of syscon/regmap to access GPBR registers
  rtc: at91sam9: add DT support
  rtc: at91sam9: replace devm_ioremap by devm_ioremap_resource
  rtc: at91sam9: use standard readl/writel functions instead of raw versions
  rtc: at91sam9: remove references to mach specific headers
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 27bc375a 2c91e61d
Atmel AT91SAM9260 Real Time Timer
Required properties:
- compatible: should be: "atmel,at91sam9260-rtt"
- reg: should encode the memory region of the RTT controller
- interrupts: rtt alarm/event interrupt
- clocks: should contain the 32 KHz slow clk that will drive the RTT block.
- atmel,rtt-rtc-time-reg: should encode the GPBR register used to store
the time base when the RTT is used as an RTC.
The first cell should point to the GPBR node and the second one
encode the offset within the GPBR block (or in other words, the
GPBR register used to store the time base).
Example:
rtt@fffffd20 {
compatible = "atmel,at91sam9260-rtt";
reg = <0xfffffd20 0x10>;
interrupts = <1 4 7>;
clocks = <&clk32k>;
atmel,rtt-rtc-time-reg = <&gpbr 0x0>;
};
...@@ -217,6 +217,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -217,6 +217,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk), CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "rtc-at91sam9.0", &clk32k),
/* more usart lookup table for DT entries */ /* more usart lookup table for DT entries */
CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck), CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk), CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
...@@ -237,6 +238,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -237,6 +238,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("mci_clk", "fffa8000.mmc", &mmc_clk), CLKDEV_CON_DEV_ID("mci_clk", "fffa8000.mmc", &mmc_clk),
CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk), CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk), CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffd20.rtc", &clk32k),
/* fake hclk clock */ /* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
......
...@@ -192,6 +192,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -192,6 +192,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk), CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk), CLKDEV_CON_ID("pioC", &pioC_clk),
CLKDEV_CON_DEV_ID(NULL, "rtc-at91sam9.0", &clk32k),
/* more lookup table for DT entries */ /* more lookup table for DT entries */
CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck), CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk), CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
...@@ -209,6 +210,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -209,6 +210,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioA_clk), CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioA_clk),
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioB_clk), CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioB_clk),
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioC_clk), CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioC_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffd20.rtc", &clk32k),
}; };
static struct clk_lookup usart_clocks_lookups[] = { static struct clk_lookup usart_clocks_lookups[] = {
......
...@@ -201,6 +201,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -201,6 +201,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk), CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk),
CLKDEV_CON_DEV_ID(NULL, "rtc-at91sam9.0", &clk32k),
CLKDEV_CON_DEV_ID(NULL, "rtc-at91sam9.1", &clk32k),
/* fake hclk clock */ /* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
...@@ -227,6 +229,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -227,6 +229,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCDE_clk), CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCDE_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioCDE_clk), CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioCDE_clk),
CLKDEV_CON_DEV_ID(NULL, "fffb8000.pwm", &pwm_clk), CLKDEV_CON_DEV_ID(NULL, "fffb8000.pwm", &pwm_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffd20.rtc", &clk32k),
CLKDEV_CON_DEV_ID(NULL, "fffffd50.rtc", &clk32k),
}; };
static struct clk_lookup usart_clocks_lookups[] = { static struct clk_lookup usart_clocks_lookups[] = {
......
...@@ -254,6 +254,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -254,6 +254,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk),
CLKDEV_CON_DEV_ID(NULL, "atmel_aes", &aestdessha_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_aes", &aestdessha_clk),
CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk), CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk),
CLKDEV_CON_DEV_ID(NULL, "rtc-at91sam9.0", &clk32k),
/* more usart lookup table for DT entries */ /* more usart lookup table for DT entries */
CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck), CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck),
CLKDEV_CON_DEV_ID("usart", "fff8c000.serial", &usart0_clk), CLKDEV_CON_DEV_ID("usart", "fff8c000.serial", &usart0_clk),
...@@ -280,6 +281,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -280,6 +281,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioC_clk), CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioC_clk),
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioDE_clk), CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioDE_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioDE_clk), CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioDE_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffd20.rtc", &clk32k),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
CLKDEV_CON_ID("pioB", &pioB_clk), CLKDEV_CON_ID("pioB", &pioB_clk),
......
...@@ -205,6 +205,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -205,6 +205,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("pioB", &pioB_clk), CLKDEV_CON_ID("pioB", &pioB_clk),
CLKDEV_CON_ID("pioC", &pioC_clk), CLKDEV_CON_ID("pioC", &pioC_clk),
CLKDEV_CON_ID("pioD", &pioD_clk), CLKDEV_CON_ID("pioD", &pioD_clk),
CLKDEV_CON_DEV_ID(NULL, "rtc-at91sam9.0", &clk32k),
/* more lookup table for DT entries */ /* more lookup table for DT entries */
CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck), CLKDEV_CON_DEV_ID("usart", "fffff200.serial", &mck),
CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk), CLKDEV_CON_DEV_ID("usart", "fffb0000.serial", &usart0_clk),
...@@ -223,6 +224,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -223,6 +224,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioB_clk), CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioB_clk),
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioC_clk), CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioC_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioD_clk), CLKDEV_CON_DEV_ID(NULL, "fffffa00.gpio", &pioD_clk),
CLKDEV_CON_DEV_ID(NULL, "fffffd20.rtc", &clk32k),
CLKDEV_CON_ID("adc_clk", &tsc_clk), CLKDEV_CON_ID("adc_clk", &tsc_clk),
}; };
......
...@@ -115,7 +115,7 @@ static u32 at91_pllb_usb_init; ...@@ -115,7 +115,7 @@ static u32 at91_pllb_usb_init;
* 48 MHz (unless no USB function clocks are needed). The main clock and * 48 MHz (unless no USB function clocks are needed). The main clock and
* both PLLs are turned off to run in "slow clock mode" (system suspend). * both PLLs are turned off to run in "slow clock mode" (system suspend).
*/ */
static struct clk clk32k = { struct clk clk32k = {
.name = "clk32k", .name = "clk32k",
.rate_hz = AT91_SLOW_CLOCK, .rate_hz = AT91_SLOW_CLOCK,
.users = 1, /* always on */ .users = 1, /* always on */
......
...@@ -34,6 +34,7 @@ struct clk { ...@@ -34,6 +34,7 @@ struct clk {
extern int __init clk_register(struct clk *clk); extern int __init clk_register(struct clk *clk);
extern struct clk mck; extern struct clk mck;
extern struct clk utmi_clk; extern struct clk utmi_clk;
extern struct clk clk32k;
#define CLKDEV_CON_ID(_id, _clk) \ #define CLKDEV_CON_ID(_id, _clk) \
{ \ { \
......
...@@ -1109,16 +1109,18 @@ config RTC_DRV_AT91RM9200 ...@@ -1109,16 +1109,18 @@ config RTC_DRV_AT91RM9200
this is powered by the backup power supply. this is powered by the backup power supply.
config RTC_DRV_AT91SAM9 config RTC_DRV_AT91SAM9
tristate "AT91SAM9x/AT91CAP9 RTT as RTC" tristate "AT91SAM9 RTT as RTC"
depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40) depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40)
select MFD_SYSCON
help help
RTC driver for the Atmel AT91SAM9x and AT91CAP9 internal RTT Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which
(Real Time Timer). These timers are powered by the backup power can be used as an RTC thanks to the backup power supply (e.g. a
supply (such as a small coin cell battery), but do not need to small coin cell battery) which keeps this block and the GPBR
be used as RTCs. (General Purpose Backup Registers) block powered when the device
is shutdown.
(On AT91SAM9rl and AT91SAM9G45 chips you probably want to use the Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd
dedicated RTC module and leave the RTT available for other uses.) probably want to use the real RTC block instead of the "RTT as an
RTC" driver.
config RTC_DRV_AT91SAM9_RTT config RTC_DRV_AT91SAM9_RTT
int int
...@@ -1127,6 +1129,9 @@ config RTC_DRV_AT91SAM9_RTT ...@@ -1127,6 +1129,9 @@ config RTC_DRV_AT91SAM9_RTT
prompt "RTT module Number" if ARCH_AT91SAM9263 prompt "RTT module Number" if ARCH_AT91SAM9263
depends on RTC_DRV_AT91SAM9 depends on RTC_DRV_AT91SAM9
help help
This option is only relevant for legacy board support and
won't be used when booting a DT board.
More than one RTT module is available. You can choose which More than one RTT module is available. You can choose which
one will be used as an RTC. The default of zero is normally one will be used as an RTC. The default of zero is normally
OK to use, though some systems use that for non-RTC purposes. OK to use, though some systems use that for non-RTC purposes.
...@@ -1139,6 +1144,9 @@ config RTC_DRV_AT91SAM9_GPBR ...@@ -1139,6 +1144,9 @@ config RTC_DRV_AT91SAM9_GPBR
prompt "Backup Register Number" prompt "Backup Register Number"
depends on RTC_DRV_AT91SAM9 depends on RTC_DRV_AT91SAM9
help help
This option is only relevant for legacy board support and
won't be used when booting a DT board.
The RTC driver needs to use one of the General Purpose Backup The RTC driver needs to use one of the General Purpose Backup
Registers (GPBRs) as well as the RTT. You can choose which one Registers (GPBRs) as well as the RTT. You can choose which one
will be used. The default of zero is normally OK to use, but will be used. The default of zero is normally OK to use, but
......
...@@ -21,10 +21,9 @@ ...@@ -21,10 +21,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/platform_data/atmel.h> #include <linux/platform_data/atmel.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <mach/at91_rtt.h> #include <linux/regmap.h>
#include <mach/cpu.h> #include <linux/clk.h>
#include <mach/hardware.h>
/* /*
* This driver uses two configurable hardware resources that live in the * This driver uses two configurable hardware resources that live in the
...@@ -47,6 +46,22 @@ ...@@ -47,6 +46,22 @@
* registers available, likewise usable for more than "RTC" support. * registers available, likewise usable for more than "RTC" support.
*/ */
#define AT91_RTT_MR 0x00 /* Real-time Mode Register */
#define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */
#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */
#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */
#define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */
#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */
#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */
#define AT91_RTT_VR 0x08 /* Real-time Value Register */
#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */
#define AT91_RTT_SR 0x0c /* Real-time Status Register */
#define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */
#define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */
/* /*
* We store ALARM_DISABLED in ALMV to record that no alarm is set. * We store ALARM_DISABLED in ALMV to record that no alarm is set.
* It's also the reset value for that field. * It's also the reset value for that field.
...@@ -58,19 +73,30 @@ struct sam9_rtc { ...@@ -58,19 +73,30 @@ struct sam9_rtc {
void __iomem *rtt; void __iomem *rtt;
struct rtc_device *rtcdev; struct rtc_device *rtcdev;
u32 imr; u32 imr;
void __iomem *gpbr; struct regmap *gpbr;
unsigned int gpbr_offset;
int irq; int irq;
struct clk *sclk;
}; };
#define rtt_readl(rtc, field) \ #define rtt_readl(rtc, field) \
__raw_readl((rtc)->rtt + AT91_RTT_ ## field) readl((rtc)->rtt + AT91_RTT_ ## field)
#define rtt_writel(rtc, field, val) \ #define rtt_writel(rtc, field, val) \
__raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) writel((val), (rtc)->rtt + AT91_RTT_ ## field)
#define gpbr_readl(rtc) \ static inline unsigned int gpbr_readl(struct sam9_rtc *rtc)
__raw_readl((rtc)->gpbr) {
#define gpbr_writel(rtc, val) \ unsigned int val;
__raw_writel((val), (rtc)->gpbr)
regmap_read(rtc->gpbr, rtc->gpbr_offset, &val);
return val;
}
static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val)
{
regmap_write(rtc->gpbr, rtc->gpbr_offset, val);
}
/* /*
* Read current time and date in RTC * Read current time and date in RTC
...@@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = { ...@@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = {
.alarm_irq_enable = at91_rtc_alarm_irq_enable, .alarm_irq_enable = at91_rtc_alarm_irq_enable,
}; };
static struct regmap_config gpbr_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
/* /*
* Initialize and install RTC driver * Initialize and install RTC driver
*/ */
static int at91_rtc_probe(struct platform_device *pdev) static int at91_rtc_probe(struct platform_device *pdev)
{ {
struct resource *r, *r_gpbr; struct resource *r;
struct sam9_rtc *rtc; struct sam9_rtc *rtc;
int ret, irq; int ret, irq;
u32 mr; u32 mr;
unsigned int sclk_rate;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!r || !r_gpbr) {
dev_err(&pdev->dev, "need 2 ressources\n");
return -ENODEV;
}
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
...@@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev) ...@@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1); device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, rtc); platform_set_drvdata(pdev, rtc);
rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (!rtc->rtt) { r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev_err(&pdev->dev, "failed to map registers, aborting.\n"); rtc->rtt = devm_ioremap_resource(&pdev->dev, r);
return -ENOMEM; if (IS_ERR(rtc->rtt))
return PTR_ERR(rtc->rtt);
if (!pdev->dev.of_node) {
/*
* TODO: Remove this code chunk when removing non DT board
* support. Remember to remove the gpbr_regmap_config
* variable too.
*/
void __iomem *gpbr;
r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
gpbr = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(gpbr))
return PTR_ERR(gpbr);
rtc->gpbr = regmap_init_mmio(NULL, gpbr,
&gpbr_regmap_config);
} else {
struct of_phandle_args args;
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
"atmel,rtt-rtc-time-reg", 1, 0,
&args);
if (ret)
return ret;
rtc->gpbr = syscon_node_to_regmap(args.np);
rtc->gpbr_offset = args.args[0];
} }
rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start, if (IS_ERR(rtc->gpbr)) {
resource_size(r_gpbr)); dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n");
if (!rtc->gpbr) {
dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n");
return -ENOMEM; return -ENOMEM;
} }
rtc->sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(rtc->sclk))
return PTR_ERR(rtc->sclk);
sclk_rate = clk_get_rate(rtc->sclk);
if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) {
dev_err(&pdev->dev, "Invalid slow clock rate\n");
return -EINVAL;
}
ret = clk_prepare_enable(rtc->sclk);
if (ret) {
dev_err(&pdev->dev, "Could not enable slow clock\n");
return ret;
}
mr = rtt_readl(rtc, MR); mr = rtt_readl(rtc, MR);
/* unless RTT is counting at 1 Hz, re-initialize it */ /* unless RTT is counting at 1 Hz, re-initialize it */
if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { if ((mr & AT91_RTT_RTPRES) != sclk_rate) {
mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES);
gpbr_writel(rtc, 0); gpbr_writel(rtc, 0);
} }
...@@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev) ...@@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev)
/* disable all interrupts */ /* disable all interrupts */
rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
if (!IS_ERR(rtc->sclk))
clk_disable_unprepare(rtc->sclk);
return 0; return 0;
} }
...@@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev) ...@@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
#ifdef CONFIG_OF
static const struct of_device_id at91_rtc_dt_ids[] = {
{ .compatible = "atmel,at91sam9260-rtt" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
#endif
static struct platform_driver at91_rtc_driver = { static struct platform_driver at91_rtc_driver = {
.probe = at91_rtc_probe, .probe = at91_rtc_probe,
.remove = at91_rtc_remove, .remove = at91_rtc_remove,
...@@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = { ...@@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = {
.name = "rtc-at91sam9", .name = "rtc-at91sam9",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &at91_rtc_pm_ops, .pm = &at91_rtc_pm_ops,
.of_match_table = of_match_ptr(at91_rtc_dt_ids),
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment