RTC for 4.11
Subsystem: - constify rtc_class_ops structures New driver: - STM32 Drivers: - armada38x: fix errata, Armada 7K/8K support - ds3232: fix wakeup support - gemini: DT support - m48t86: huge cleanup and platform_data removal - mcp795: alarm support - sun6i: proper oscillator handling - tegra: proper clock handling - tps65910: calibration support -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEl0I5XWmUIrwBfFMm2KKDO9oT4sIFAlizZLQACgkQ2KKDO9oT 4sISIw//Zl96KIXqeC+En+8v8Sa0pham/mcLbKYujnFIi1mMaesEXJJClALXYAGQ r/fwXkYowC14AMXGuV5vMMVAVisJpj1gtMmpom+9/7mYtkFOIUsB8Sis8dMqgTqx JFBho7JvPJcwE7BLzUNRzX4tWhFhNm0epyMrsrQrBSeLx3PD8xg5v2kPYuZHdYU0 63Bovkq6zvH9/WdO8DLXw/nc/Y0Bo66rlvJkcaNfjBrdFTRvRAM5JIiJuxewR+jY 3bTQ8PQjnHAWIj/RhrwguGTLDlgJKcpitB06Y53TdRaNtVfJuEN8z6EjNkR37kyS ZJnPgihCoH6l7v28uY4e5BAg/Fe3ZhDrPmhZWq8rEkByeQpSUWgrE/DtcoC0OkZO l2fU/y2vq4za7CpRPp5bvq3sF0PbRHSF0o8rvmHlQZI/mwwYbwF9gk1vg5adyH7i 1UuTGoDXxcMYZPJm3zezE1bUa4OAyjH1NhrvPvinlDw+aekaai2eFUKIbJim+dJx tEVPATPlDk/Ngwth1hpE8D/tOdoQhWtfNk7+zo7MNtMjAO1h/DxSLHXJ9mvCwcPh lPT9BmQxT1HECIa5gjN1R+5X5or5z8LPcNGO9TedIchfZ8qBGzsWOt9bXlw2dgI2 qmXo6IrjCN88kf+qsVA4FKLmaqgpb9+Yb+5cPlhEOKvxUhY47Nc= =Go5q -----END PGP SIGNATURE----- Merge tag 'rtc-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "Subsystem: - constify rtc_class_ops structures New driver: - STM32 Drivers: - armada38x: fix errata, Armada 7K/8K support - ds3232: fix wakeup support - gemini: DT support - m48t86: huge cleanup and platform_data removal - mcp795: alarm support - sun6i: proper oscillator handling - tegra: proper clock handling - tps65910: calibration support" * tag 'rtc-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (44 commits) rtc: ds3232: Call device_init_wakeup before device_register rtc: pcf2127: bulk read only date and time registers. rtc: armada38x: Add support for Armada 7K/8K rtc: armada38x: Prepare driver to manage different versions rtc: ds3232: Add regmap max_register definition. rtc: ds3232: Cleanup whitespace around register and bit definitions. rtc: m48t86: remove unused platform_data ARM: Orion5x: ts78xx: allow rtc-m48t86 to manage it's own resources ARM: Orion5x: ts78xx: remove RTC detection ARM: ep93xx: ts72xx: allow rtc-m48t86 to manage its own resources rtc: m48t86: verify that the RTC is actually present rtc: m48t86: add NVRAM support rtc: m48t86: allow driver to manage its resources rtc: m48t86: shorten register name defines bindings: rtc: correct wrong reference in required properties rtc: sun6i: Fix return value check in sun6i_rtc_clk_init() rtc: sun6i: extend test coverage rtc: sun6i: Fix compatibility with old DT binding rtc: snvs: add a missing write sync rtc: bq32000: add support to enable disable the trickle charge FET bypass ...
This commit is contained in:
Коммит
5782fd14aa
|
@ -0,0 +1,7 @@
|
||||||
|
What: /sys/bus/i2c/devices/.../trickle_charge_bypass
|
||||||
|
Date: Jan 2017
|
||||||
|
KernelVersion: 4.11
|
||||||
|
Contact: Enric Balletbo i Serra <eballetbo@gmail.com>
|
||||||
|
Description: Attribute for enable/disable the trickle charge bypass
|
||||||
|
The trickle_charge_bypass attribute allows the userspace to
|
||||||
|
enable/disable the Trickle charge FET bypass.
|
|
@ -1,9 +1,11 @@
|
||||||
* Real Time Clock of the Armada 38x SoCs
|
* Real Time Clock of the Armada 38x/7K/8K SoCs
|
||||||
|
|
||||||
RTC controller for the Armada 38x SoCs
|
RTC controller for the Armada 38x, 7K and 8K SoCs
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : Should be "marvell,armada-380-rtc"
|
- compatible : Should be one of the following:
|
||||||
|
"marvell,armada-380-rtc" for Armada 38x SoC
|
||||||
|
"marvell,armada-8k-rtc" for Aramda 7K/8K SoCs
|
||||||
- reg: a list of base address and size pairs, one for each entry in
|
- reg: a list of base address and size pairs, one for each entry in
|
||||||
reg-names
|
reg-names
|
||||||
- reg names: should contain:
|
- reg names: should contain:
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
* Cortina Systems Gemini RTC
|
||||||
|
|
||||||
|
Gemini SoC real-time clock.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be "cortina,gemini-rtc"
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
rtc@45000000 {
|
||||||
|
compatible = "cortina,gemini-rtc";
|
||||||
|
reg = <0x45000000 0x100>;
|
||||||
|
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
};
|
|
@ -8,10 +8,13 @@ Required properties:
|
||||||
region.
|
region.
|
||||||
- interrupts: rtc alarm interrupt
|
- interrupts: rtc alarm interrupt
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- interrupts: dryice security violation interrupt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
rtc@80056000 {
|
rtc@80056000 {
|
||||||
compatible = "fsl,imx53-rtc", "fsl,imx25-rtc";
|
compatible = "fsl,imx53-rtc", "fsl,imx25-rtc";
|
||||||
reg = <0x80056000 2000>;
|
reg = <0x80056000 2000>;
|
||||||
interrupts = <29>;
|
interrupts = <29 56>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
* Maxim DS3231 Real Time Clock
|
* Maxim DS3231 Real Time Clock
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
see: Documentation/devicetree/bindings/i2c/trivial-admin-guide/devices.rst
|
- compatible: Should contain "maxim,ds3231".
|
||||||
|
- reg: I2C address for chip.
|
||||||
|
|
||||||
Optional property:
|
Optional property:
|
||||||
- #clock-cells: Should be 1.
|
- #clock-cells: Should be 1.
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
Philips PCF8563/Epson RTC8564 Real Time Clock
|
Philips PCF8563/Epson RTC8564 Real Time Clock
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
see: Documentation/devicetree/bindings/i2c/trivial-admin-guide/devices.rst
|
- compatible: Should contain "nxp,pcf8563".
|
||||||
|
- reg: I2C address for chip.
|
||||||
|
|
||||||
Optional property:
|
Optional property:
|
||||||
- #clock-cells: Should be 0.
|
- #clock-cells: Should be 0.
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
STM32 Real Time Clock
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "st,stm32-rtc".
|
||||||
|
- reg: address range of rtc register set.
|
||||||
|
- clocks: reference to the clock entry ck_rtc.
|
||||||
|
- interrupt-parent: phandle for the interrupt controller.
|
||||||
|
- interrupts: rtc alarm interrupt.
|
||||||
|
- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
|
||||||
|
(RTC registers) write protection.
|
||||||
|
|
||||||
|
Optional properties (to override default ck_rtc parent clock):
|
||||||
|
- assigned-clocks: reference to the ck_rtc clock entry.
|
||||||
|
- assigned-clock-parents: phandle of the new parent clock of ck_rtc.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
rtc: rtc@40002800 {
|
||||||
|
compatible = "st,stm32-rtc";
|
||||||
|
reg = <0x40002800 0x400>;
|
||||||
|
clocks = <&rcc 1 CLK_RTC>;
|
||||||
|
assigned-clocks = <&rcc 1 CLK_RTC>;
|
||||||
|
assigned-clock-parents = <&rcc 1 CLK_LSE>;
|
||||||
|
interrupt-parent = <&exti>;
|
||||||
|
interrupts = <17 1>;
|
||||||
|
st,syscfg = <&pwrcfg>;
|
||||||
|
};
|
|
@ -8,10 +8,20 @@ Required properties:
|
||||||
memory mapped region.
|
memory mapped region.
|
||||||
- interrupts : IRQ lines for the RTC alarm 0 and alarm 1, in that order.
|
- interrupts : IRQ lines for the RTC alarm 0 and alarm 1, in that order.
|
||||||
|
|
||||||
|
Required properties for new device trees
|
||||||
|
- clocks : phandle to the 32kHz external oscillator
|
||||||
|
- clock-output-names : name of the LOSC clock created
|
||||||
|
- #clock-cells : must be equals to 1. The RTC provides two clocks: the
|
||||||
|
LOSC and its external output, with index 0 and 1
|
||||||
|
respectively.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
rtc: rtc@01f00000 {
|
rtc: rtc@01f00000 {
|
||||||
compatible = "allwinner,sun6i-a31-rtc";
|
compatible = "allwinner,sun6i-a31-rtc";
|
||||||
reg = <0x01f00000 0x54>;
|
reg = <0x01f00000 0x54>;
|
||||||
interrupts = <0 40 4>, <0 41 4>;
|
interrupts = <0 40 4>, <0 41 4>;
|
||||||
|
clock-output-names = "osc32k";
|
||||||
|
clocks = <&ext_osc32k>;
|
||||||
|
#clock-cells = <1>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_data/rtc-m48t86.h>
|
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
|
@ -45,16 +44,6 @@ static struct map_desc ts72xx_io_desc[] __initdata = {
|
||||||
.pfn = __phys_to_pfn(TS72XX_OPTIONS2_PHYS_BASE),
|
.pfn = __phys_to_pfn(TS72XX_OPTIONS2_PHYS_BASE),
|
||||||
.length = TS72XX_OPTIONS2_SIZE,
|
.length = TS72XX_OPTIONS2_SIZE,
|
||||||
.type = MT_DEVICE,
|
.type = MT_DEVICE,
|
||||||
}, {
|
|
||||||
.virtual = (unsigned long)TS72XX_RTC_INDEX_VIRT_BASE,
|
|
||||||
.pfn = __phys_to_pfn(TS72XX_RTC_INDEX_PHYS_BASE),
|
|
||||||
.length = TS72XX_RTC_INDEX_SIZE,
|
|
||||||
.type = MT_DEVICE,
|
|
||||||
}, {
|
|
||||||
.virtual = (unsigned long)TS72XX_RTC_DATA_VIRT_BASE,
|
|
||||||
.pfn = __phys_to_pfn(TS72XX_RTC_DATA_PHYS_BASE),
|
|
||||||
.length = TS72XX_RTC_DATA_SIZE,
|
|
||||||
.type = MT_DEVICE,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,31 +168,22 @@ static void __init ts72xx_register_flash(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* RTC M48T86
|
||||||
|
*************************************************************************/
|
||||||
|
#define TS72XX_RTC_INDEX_PHYS_BASE (EP93XX_CS1_PHYS_BASE + 0x00800000)
|
||||||
|
#define TS72XX_RTC_DATA_PHYS_BASE (EP93XX_CS1_PHYS_BASE + 0x01700000)
|
||||||
|
|
||||||
static unsigned char ts72xx_rtc_readbyte(unsigned long addr)
|
static struct resource ts72xx_rtc_resources[] = {
|
||||||
{
|
DEFINE_RES_MEM(TS72XX_RTC_INDEX_PHYS_BASE, 0x01),
|
||||||
__raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE);
|
DEFINE_RES_MEM(TS72XX_RTC_DATA_PHYS_BASE, 0x01),
|
||||||
return __raw_readb(TS72XX_RTC_DATA_VIRT_BASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ts72xx_rtc_writebyte(unsigned char value, unsigned long addr)
|
|
||||||
{
|
|
||||||
__raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE);
|
|
||||||
__raw_writeb(value, TS72XX_RTC_DATA_VIRT_BASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct m48t86_ops ts72xx_rtc_ops = {
|
|
||||||
.readbyte = ts72xx_rtc_readbyte,
|
|
||||||
.writebyte = ts72xx_rtc_writebyte,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device ts72xx_rtc_device = {
|
static struct platform_device ts72xx_rtc_device = {
|
||||||
.name = "rtc-m48t86",
|
.name = "rtc-m48t86",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
.dev = {
|
.resource = ts72xx_rtc_resources,
|
||||||
.platform_data = &ts72xx_rtc_ops,
|
.num_resources = ARRAY_SIZE(ts72xx_rtc_resources),
|
||||||
},
|
|
||||||
.num_resources = 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct resource ts72xx_wdt_resources[] = {
|
static struct resource ts72xx_wdt_resources[] = {
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
* febff000 22000000 4K model number register (bits 0-2)
|
* febff000 22000000 4K model number register (bits 0-2)
|
||||||
* febfe000 22400000 4K options register
|
* febfe000 22400000 4K options register
|
||||||
* febfd000 22800000 4K options register #2
|
* febfd000 22800000 4K options register #2
|
||||||
* febf9000 10800000 4K TS-5620 RTC index register
|
|
||||||
* febf8000 11700000 4K TS-5620 RTC data register
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define TS72XX_MODEL_PHYS_BASE 0x22000000
|
#define TS72XX_MODEL_PHYS_BASE 0x22000000
|
||||||
|
@ -40,15 +38,6 @@
|
||||||
#define TS72XX_OPTIONS2_TS9420 0x04
|
#define TS72XX_OPTIONS2_TS9420 0x04
|
||||||
#define TS72XX_OPTIONS2_TS9420_BOOT 0x02
|
#define TS72XX_OPTIONS2_TS9420_BOOT 0x02
|
||||||
|
|
||||||
|
|
||||||
#define TS72XX_RTC_INDEX_VIRT_BASE IOMEM(0xfebf9000)
|
|
||||||
#define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000
|
|
||||||
#define TS72XX_RTC_INDEX_SIZE 0x00001000
|
|
||||||
|
|
||||||
#define TS72XX_RTC_DATA_VIRT_BASE IOMEM(0xfebf8000)
|
|
||||||
#define TS72XX_RTC_DATA_PHYS_BASE 0x11700000
|
|
||||||
#define TS72XX_RTC_DATA_SIZE 0x00001000
|
|
||||||
|
|
||||||
#define TS72XX_WDT_CONTROL_PHYS_BASE 0x23800000
|
#define TS72XX_WDT_CONTROL_PHYS_BASE 0x23800000
|
||||||
#define TS72XX_WDT_FEED_PHYS_BASE 0x23c00000
|
#define TS72XX_WDT_FEED_PHYS_BASE 0x23c00000
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mv643xx_eth.h>
|
#include <linux/mv643xx_eth.h>
|
||||||
#include <linux/ata_platform.h>
|
#include <linux/ata_platform.h>
|
||||||
#include <linux/platform_data/rtc-m48t86.h>
|
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/timeriomem-rng.h>
|
#include <linux/timeriomem-rng.h>
|
||||||
|
@ -80,79 +79,38 @@ static struct mv_sata_platform_data ts78xx_sata_data = {
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c
|
* RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
#define TS_RTC_CTRL (TS78XX_FPGA_REGS_VIRT_BASE + 0x808)
|
#define TS_RTC_CTRL (TS78XX_FPGA_REGS_PHYS_BASE + 0x808)
|
||||||
#define TS_RTC_DATA (TS78XX_FPGA_REGS_VIRT_BASE + 0x80c)
|
#define TS_RTC_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x80c)
|
||||||
|
|
||||||
static unsigned char ts78xx_ts_rtc_readbyte(unsigned long addr)
|
static struct resource ts78xx_ts_rtc_resources[] = {
|
||||||
{
|
DEFINE_RES_MEM(TS_RTC_CTRL, 0x01),
|
||||||
writeb(addr, TS_RTC_CTRL);
|
DEFINE_RES_MEM(TS_RTC_DATA, 0x01),
|
||||||
return readb(TS_RTC_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ts78xx_ts_rtc_writebyte(unsigned char value, unsigned long addr)
|
|
||||||
{
|
|
||||||
writeb(addr, TS_RTC_CTRL);
|
|
||||||
writeb(value, TS_RTC_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct m48t86_ops ts78xx_ts_rtc_ops = {
|
|
||||||
.readbyte = ts78xx_ts_rtc_readbyte,
|
|
||||||
.writebyte = ts78xx_ts_rtc_writebyte,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device ts78xx_ts_rtc_device = {
|
static struct platform_device ts78xx_ts_rtc_device = {
|
||||||
.name = "rtc-m48t86",
|
.name = "rtc-m48t86",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
.dev = {
|
.resource = ts78xx_ts_rtc_resources,
|
||||||
.platform_data = &ts78xx_ts_rtc_ops,
|
.num_resources = ARRAY_SIZE(ts78xx_ts_rtc_resources),
|
||||||
},
|
|
||||||
.num_resources = 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* TS uses some of the user storage space on the RTC chip so see if it is
|
|
||||||
* present; as it's an optional feature at purchase time and not all boards
|
|
||||||
* will have it present
|
|
||||||
*
|
|
||||||
* I've used the method TS use in their rtc7800.c example for the detection
|
|
||||||
*
|
|
||||||
* TODO: track down a guinea pig without an RTC to see if we can work out a
|
|
||||||
* better RTC detection routine
|
|
||||||
*/
|
|
||||||
static int ts78xx_ts_rtc_load(void)
|
static int ts78xx_ts_rtc_load(void)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned char tmp_rtc0, tmp_rtc1;
|
|
||||||
|
|
||||||
tmp_rtc0 = ts78xx_ts_rtc_readbyte(126);
|
if (ts78xx_fpga.supports.ts_rtc.init == 0) {
|
||||||
tmp_rtc1 = ts78xx_ts_rtc_readbyte(127);
|
rc = platform_device_register(&ts78xx_ts_rtc_device);
|
||||||
|
if (!rc)
|
||||||
ts78xx_ts_rtc_writebyte(0x00, 126);
|
ts78xx_fpga.supports.ts_rtc.init = 1;
|
||||||
ts78xx_ts_rtc_writebyte(0x55, 127);
|
} else {
|
||||||
if (ts78xx_ts_rtc_readbyte(127) == 0x55) {
|
rc = platform_device_add(&ts78xx_ts_rtc_device);
|
||||||
ts78xx_ts_rtc_writebyte(0xaa, 127);
|
|
||||||
if (ts78xx_ts_rtc_readbyte(127) == 0xaa
|
|
||||||
&& ts78xx_ts_rtc_readbyte(126) == 0x00) {
|
|
||||||
ts78xx_ts_rtc_writebyte(tmp_rtc0, 126);
|
|
||||||
ts78xx_ts_rtc_writebyte(tmp_rtc1, 127);
|
|
||||||
|
|
||||||
if (ts78xx_fpga.supports.ts_rtc.init == 0) {
|
|
||||||
rc = platform_device_register(&ts78xx_ts_rtc_device);
|
|
||||||
if (!rc)
|
|
||||||
ts78xx_fpga.supports.ts_rtc.init = 1;
|
|
||||||
} else
|
|
||||||
rc = platform_device_add(&ts78xx_ts_rtc_device);
|
|
||||||
|
|
||||||
if (rc)
|
|
||||||
pr_info("RTC could not be registered: %d\n",
|
|
||||||
rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("RTC not found\n");
|
if (rc)
|
||||||
return -ENODEV;
|
pr_info("RTC could not be registered: %d\n", rc);
|
||||||
};
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static void ts78xx_ts_rtc_unload(void)
|
static void ts78xx_ts_rtc_unload(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1434,9 +1434,10 @@ config RTC_DRV_SUN4V
|
||||||
based RTC on SUN4V systems.
|
based RTC on SUN4V systems.
|
||||||
|
|
||||||
config RTC_DRV_SUN6I
|
config RTC_DRV_SUN6I
|
||||||
tristate "Allwinner A31 RTC"
|
bool "Allwinner A31 RTC"
|
||||||
default MACH_SUN6I || MACH_SUN8I || COMPILE_TEST
|
default MACH_SUN6I || MACH_SUN8I
|
||||||
depends on ARCH_SUNXI
|
depends on COMMON_CLK
|
||||||
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
help
|
help
|
||||||
If you say Y here you will get support for the RTC found in
|
If you say Y here you will get support for the RTC found in
|
||||||
some Allwinner SoCs like the A31 or the A64.
|
some Allwinner SoCs like the A31 or the A64.
|
||||||
|
@ -1719,6 +1720,17 @@ config RTC_DRV_R7301
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called rtc-r7301.
|
will be called rtc-r7301.
|
||||||
|
|
||||||
|
config RTC_DRV_STM32
|
||||||
|
tristate "STM32 RTC"
|
||||||
|
select REGMAP_MMIO
|
||||||
|
depends on ARCH_STM32 || COMPILE_TEST
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the STM32 On-Chip
|
||||||
|
Real Time Clock.
|
||||||
|
|
||||||
|
This driver can also be built as a module, if so, the module
|
||||||
|
will be called "rtc-stm32".
|
||||||
|
|
||||||
comment "HID Sensor RTC drivers"
|
comment "HID Sensor RTC drivers"
|
||||||
|
|
||||||
config RTC_DRV_HID_SENSOR_TIME
|
config RTC_DRV_HID_SENSOR_TIME
|
||||||
|
|
|
@ -145,6 +145,7 @@ obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
|
||||||
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
|
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
|
||||||
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
|
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
|
||||||
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
|
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
|
||||||
|
obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
|
||||||
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
|
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
|
||||||
obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
|
obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
|
||||||
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
|
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
@ -23,17 +24,48 @@
|
||||||
#define RTC_STATUS_ALARM1 BIT(0)
|
#define RTC_STATUS_ALARM1 BIT(0)
|
||||||
#define RTC_STATUS_ALARM2 BIT(1)
|
#define RTC_STATUS_ALARM2 BIT(1)
|
||||||
#define RTC_IRQ1_CONF 0x4
|
#define RTC_IRQ1_CONF 0x4
|
||||||
#define RTC_IRQ1_AL_EN BIT(0)
|
#define RTC_IRQ2_CONF 0x8
|
||||||
#define RTC_IRQ1_FREQ_EN BIT(1)
|
#define RTC_IRQ_AL_EN BIT(0)
|
||||||
#define RTC_IRQ1_FREQ_1HZ BIT(2)
|
#define RTC_IRQ_FREQ_EN BIT(1)
|
||||||
|
#define RTC_IRQ_FREQ_1HZ BIT(2)
|
||||||
|
|
||||||
#define RTC_TIME 0xC
|
#define RTC_TIME 0xC
|
||||||
#define RTC_ALARM1 0x10
|
#define RTC_ALARM1 0x10
|
||||||
|
#define RTC_ALARM2 0x14
|
||||||
|
|
||||||
#define SOC_RTC_INTERRUPT 0x8
|
/* Armada38x SoC registers */
|
||||||
#define SOC_RTC_ALARM1 BIT(0)
|
#define RTC_38X_BRIDGE_TIMING_CTL 0x0
|
||||||
#define SOC_RTC_ALARM2 BIT(1)
|
#define RTC_38X_PERIOD_OFFS 0
|
||||||
#define SOC_RTC_ALARM1_MASK BIT(2)
|
#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS)
|
||||||
#define SOC_RTC_ALARM2_MASK BIT(3)
|
#define RTC_38X_READ_DELAY_OFFS 26
|
||||||
|
#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS)
|
||||||
|
|
||||||
|
/* Armada 7K/8K registers */
|
||||||
|
#define RTC_8K_BRIDGE_TIMING_CTL0 0x0
|
||||||
|
#define RTC_8K_WRCLK_PERIOD_OFFS 0
|
||||||
|
#define RTC_8K_WRCLK_PERIOD_MASK (0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS)
|
||||||
|
#define RTC_8K_WRCLK_SETUP_OFFS 16
|
||||||
|
#define RTC_8K_WRCLK_SETUP_MASK (0xFFFF << RTC_8K_WRCLK_SETUP_OFFS)
|
||||||
|
#define RTC_8K_BRIDGE_TIMING_CTL1 0x4
|
||||||
|
#define RTC_8K_READ_DELAY_OFFS 0
|
||||||
|
#define RTC_8K_READ_DELAY_MASK (0xFFFF << RTC_8K_READ_DELAY_OFFS)
|
||||||
|
|
||||||
|
#define RTC_8K_ISR 0x10
|
||||||
|
#define RTC_8K_IMR 0x14
|
||||||
|
#define RTC_8K_ALARM2 BIT(0)
|
||||||
|
|
||||||
|
#define SOC_RTC_INTERRUPT 0x8
|
||||||
|
#define SOC_RTC_ALARM1 BIT(0)
|
||||||
|
#define SOC_RTC_ALARM2 BIT(1)
|
||||||
|
#define SOC_RTC_ALARM1_MASK BIT(2)
|
||||||
|
#define SOC_RTC_ALARM2_MASK BIT(3)
|
||||||
|
|
||||||
|
#define SAMPLE_NR 100
|
||||||
|
|
||||||
|
struct value_to_freq {
|
||||||
|
u32 value;
|
||||||
|
u8 freq;
|
||||||
|
};
|
||||||
|
|
||||||
struct armada38x_rtc {
|
struct armada38x_rtc {
|
||||||
struct rtc_device *rtc_dev;
|
struct rtc_device *rtc_dev;
|
||||||
|
@ -41,38 +73,153 @@ struct armada38x_rtc {
|
||||||
void __iomem *regs_soc;
|
void __iomem *regs_soc;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
int irq;
|
int irq;
|
||||||
|
struct value_to_freq *val_to_freq;
|
||||||
|
struct armada38x_rtc_data *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ALARM1 0
|
||||||
|
#define ALARM2 1
|
||||||
|
|
||||||
|
#define ALARM_REG(base, alarm) ((base) + (alarm) * sizeof(u32))
|
||||||
|
|
||||||
|
struct armada38x_rtc_data {
|
||||||
|
/* Initialize the RTC-MBUS bridge timing */
|
||||||
|
void (*update_mbus_timing)(struct armada38x_rtc *rtc);
|
||||||
|
u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg);
|
||||||
|
void (*clear_isr)(struct armada38x_rtc *rtc);
|
||||||
|
void (*unmask_interrupt)(struct armada38x_rtc *rtc);
|
||||||
|
u32 alarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* According to the datasheet, the OS should wait 5us after every
|
* According to the datasheet, the OS should wait 5us after every
|
||||||
* register write to the RTC hard macro so that the required update
|
* register write to the RTC hard macro so that the required update
|
||||||
* can occur without holding off the system bus
|
* can occur without holding off the system bus
|
||||||
|
* According to errata RES-3124064, Write to any RTC register
|
||||||
|
* may fail. As a workaround, before writing to RTC
|
||||||
|
* register, issue a dummy write of 0x0 twice to RTC Status
|
||||||
|
* register.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
|
static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
|
||||||
{
|
{
|
||||||
|
writel(0, rtc->regs + RTC_STATUS);
|
||||||
|
writel(0, rtc->regs + RTC_STATUS);
|
||||||
writel(val, rtc->regs + offset);
|
writel(val, rtc->regs + offset);
|
||||||
udelay(5);
|
udelay(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update RTC-MBUS bridge timing parameters */
|
||||||
|
static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
|
||||||
|
reg &= ~RTC_38X_PERIOD_MASK;
|
||||||
|
reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
|
||||||
|
reg &= ~RTC_38X_READ_DELAY_MASK;
|
||||||
|
reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
|
||||||
|
writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
|
||||||
|
reg &= ~RTC_8K_WRCLK_PERIOD_MASK;
|
||||||
|
reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS;
|
||||||
|
reg &= ~RTC_8K_WRCLK_SETUP_MASK;
|
||||||
|
reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS;
|
||||||
|
writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
|
||||||
|
|
||||||
|
reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
|
||||||
|
reg &= ~RTC_8K_READ_DELAY_MASK;
|
||||||
|
reg |= 0x3F << RTC_8K_READ_DELAY_OFFS;
|
||||||
|
writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg)
|
||||||
|
{
|
||||||
|
return readl(rtc->regs + rtc_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
|
||||||
|
{
|
||||||
|
int i, index_max = 0, max = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < SAMPLE_NR; i++) {
|
||||||
|
rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg);
|
||||||
|
rtc->val_to_freq[i].freq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < SAMPLE_NR; i++) {
|
||||||
|
int j = 0;
|
||||||
|
u32 value = rtc->val_to_freq[i].value;
|
||||||
|
|
||||||
|
while (rtc->val_to_freq[j].freq) {
|
||||||
|
if (rtc->val_to_freq[j].value == value) {
|
||||||
|
rtc->val_to_freq[j].freq++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rtc->val_to_freq[j].freq) {
|
||||||
|
rtc->val_to_freq[j].value = value;
|
||||||
|
rtc->val_to_freq[j].freq = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtc->val_to_freq[j].freq > max) {
|
||||||
|
index_max = j;
|
||||||
|
max = rtc->val_to_freq[j].freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a value already has half of the sample this is the most
|
||||||
|
* frequent one and we can stop the research right now
|
||||||
|
*/
|
||||||
|
if (max > SAMPLE_NR / 2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtc->val_to_freq[index_max].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void armada38x_clear_isr(struct armada38x_rtc *rtc)
|
||||||
|
{
|
||||||
|
u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
|
||||||
|
|
||||||
|
writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc)
|
||||||
|
{
|
||||||
|
u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
|
||||||
|
|
||||||
|
writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void armada8k_clear_isr(struct armada38x_rtc *rtc)
|
||||||
|
{
|
||||||
|
writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc)
|
||||||
|
{
|
||||||
|
writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR);
|
||||||
|
}
|
||||||
|
|
||||||
static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||||
unsigned long time, time_check, flags;
|
unsigned long time, flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&rtc->lock, flags);
|
spin_lock_irqsave(&rtc->lock, flags);
|
||||||
time = readl(rtc->regs + RTC_TIME);
|
time = rtc->data->read_rtc_reg(rtc, RTC_TIME);
|
||||||
/*
|
|
||||||
* WA for failing time set attempts. As stated in HW ERRATA if
|
|
||||||
* more than one second between two time reads is detected
|
|
||||||
* then read once again.
|
|
||||||
*/
|
|
||||||
time_check = readl(rtc->regs + RTC_TIME);
|
|
||||||
if ((time_check - time) > 1)
|
|
||||||
time_check = readl(rtc->regs + RTC_TIME);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||||
|
|
||||||
rtc_time_to_tm(time_check, tm);
|
rtc_time_to_tm(time, tm);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -87,16 +234,9 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
/*
|
|
||||||
* According to errata FE-3124064, Write to RTC TIME register
|
|
||||||
* may fail. As a workaround, after writing to RTC TIME
|
|
||||||
* register, issue a dummy write of 0x0 twice to RTC Status
|
|
||||||
* register.
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&rtc->lock, flags);
|
spin_lock_irqsave(&rtc->lock, flags);
|
||||||
rtc_delayed_write(time, rtc, RTC_TIME);
|
rtc_delayed_write(time, rtc, RTC_TIME);
|
||||||
rtc_delayed_write(0, rtc, RTC_STATUS);
|
|
||||||
rtc_delayed_write(0, rtc, RTC_STATUS);
|
|
||||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -107,12 +247,14 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
{
|
{
|
||||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||||
unsigned long time, flags;
|
unsigned long time, flags;
|
||||||
|
u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
|
||||||
|
u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
spin_lock_irqsave(&rtc->lock, flags);
|
spin_lock_irqsave(&rtc->lock, flags);
|
||||||
|
|
||||||
time = readl(rtc->regs + RTC_ALARM1);
|
time = rtc->data->read_rtc_reg(rtc, reg);
|
||||||
val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
|
val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||||
|
|
||||||
|
@ -125,9 +267,10 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
{
|
{
|
||||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
|
||||||
|
u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
|
||||||
unsigned long time, flags;
|
unsigned long time, flags;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
u32 val;
|
|
||||||
|
|
||||||
ret = rtc_tm_to_time(&alrm->time, &time);
|
ret = rtc_tm_to_time(&alrm->time, &time);
|
||||||
|
|
||||||
|
@ -136,13 +279,11 @@ static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
|
|
||||||
spin_lock_irqsave(&rtc->lock, flags);
|
spin_lock_irqsave(&rtc->lock, flags);
|
||||||
|
|
||||||
rtc_delayed_write(time, rtc, RTC_ALARM1);
|
rtc_delayed_write(time, rtc, reg);
|
||||||
|
|
||||||
if (alrm->enabled) {
|
if (alrm->enabled) {
|
||||||
rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
|
rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
|
||||||
val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
|
rtc->data->unmask_interrupt(rtc);
|
||||||
writel(val | SOC_RTC_ALARM1_MASK,
|
|
||||||
rtc->regs_soc + SOC_RTC_INTERRUPT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||||
|
@ -155,14 +296,15 @@ static int armada38x_rtc_alarm_irq_enable(struct device *dev,
|
||||||
unsigned int enabled)
|
unsigned int enabled)
|
||||||
{
|
{
|
||||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&rtc->lock, flags);
|
spin_lock_irqsave(&rtc->lock, flags);
|
||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
|
rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
|
||||||
else
|
else
|
||||||
rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
|
rtc_delayed_write(0, rtc, reg_irq);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||||
|
|
||||||
|
@ -174,24 +316,23 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
|
||||||
struct armada38x_rtc *rtc = data;
|
struct armada38x_rtc *rtc = data;
|
||||||
u32 val;
|
u32 val;
|
||||||
int event = RTC_IRQF | RTC_AF;
|
int event = RTC_IRQF | RTC_AF;
|
||||||
|
u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
|
||||||
|
|
||||||
dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
|
dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
|
||||||
|
|
||||||
spin_lock(&rtc->lock);
|
spin_lock(&rtc->lock);
|
||||||
|
|
||||||
val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
|
rtc->data->clear_isr(rtc);
|
||||||
|
val = rtc->data->read_rtc_reg(rtc, reg_irq);
|
||||||
writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
|
/* disable all the interrupts for alarm*/
|
||||||
val = readl(rtc->regs + RTC_IRQ1_CONF);
|
rtc_delayed_write(0, rtc, reg_irq);
|
||||||
/* disable all the interrupts for alarm 1 */
|
|
||||||
rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
|
|
||||||
/* Ack the event */
|
/* Ack the event */
|
||||||
rtc_delayed_write(RTC_STATUS_ALARM1, rtc, RTC_STATUS);
|
rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS);
|
||||||
|
|
||||||
spin_unlock(&rtc->lock);
|
spin_unlock(&rtc->lock);
|
||||||
|
|
||||||
if (val & RTC_IRQ1_FREQ_EN) {
|
if (val & RTC_IRQ_FREQ_EN) {
|
||||||
if (val & RTC_IRQ1_FREQ_1HZ)
|
if (val & RTC_IRQ_FREQ_1HZ)
|
||||||
event |= RTC_UF;
|
event |= RTC_UF;
|
||||||
else
|
else
|
||||||
event |= RTC_PF;
|
event |= RTC_PF;
|
||||||
|
@ -202,7 +343,7 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops armada38x_rtc_ops = {
|
static const struct rtc_class_ops armada38x_rtc_ops = {
|
||||||
.read_time = armada38x_rtc_read_time,
|
.read_time = armada38x_rtc_read_time,
|
||||||
.set_time = armada38x_rtc_set_time,
|
.set_time = armada38x_rtc_set_time,
|
||||||
.read_alarm = armada38x_rtc_read_alarm,
|
.read_alarm = armada38x_rtc_read_alarm,
|
||||||
|
@ -210,17 +351,65 @@ static struct rtc_class_ops armada38x_rtc_ops = {
|
||||||
.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
|
.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
|
||||||
|
.read_time = armada38x_rtc_read_time,
|
||||||
|
.set_time = armada38x_rtc_set_time,
|
||||||
|
.read_alarm = armada38x_rtc_read_alarm,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct armada38x_rtc_data armada38x_data = {
|
||||||
|
.update_mbus_timing = rtc_update_38x_mbus_timing_params,
|
||||||
|
.read_rtc_reg = read_rtc_register_38x_wa,
|
||||||
|
.clear_isr = armada38x_clear_isr,
|
||||||
|
.unmask_interrupt = armada38x_unmask_interrupt,
|
||||||
|
.alarm = ALARM1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct armada38x_rtc_data armada8k_data = {
|
||||||
|
.update_mbus_timing = rtc_update_8k_mbus_timing_params,
|
||||||
|
.read_rtc_reg = read_rtc_register,
|
||||||
|
.clear_isr = armada8k_clear_isr,
|
||||||
|
.unmask_interrupt = armada8k_unmask_interrupt,
|
||||||
|
.alarm = ALARM2,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id armada38x_rtc_of_match_table[] = {
|
||||||
|
{
|
||||||
|
.compatible = "marvell,armada-380-rtc",
|
||||||
|
.data = &armada38x_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "marvell,armada-8k-rtc",
|
||||||
|
.data = &armada8k_data,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
|
||||||
|
#endif
|
||||||
|
|
||||||
static __init int armada38x_rtc_probe(struct platform_device *pdev)
|
static __init int armada38x_rtc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct rtc_class_ops *ops;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct armada38x_rtc *rtc;
|
struct armada38x_rtc *rtc;
|
||||||
|
const struct of_device_id *match;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev);
|
||||||
|
if (!match)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
|
rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!rtc)
|
if (!rtc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR,
|
||||||
|
sizeof(struct value_to_freq), GFP_KERNEL);
|
||||||
|
if (!rtc->val_to_freq)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_init(&rtc->lock);
|
spin_lock_init(&rtc->lock);
|
||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
|
||||||
|
@ -242,19 +431,27 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev)
|
||||||
0, pdev->name, rtc) < 0) {
|
0, pdev->name, rtc) < 0) {
|
||||||
dev_warn(&pdev->dev, "Interrupt not available.\n");
|
dev_warn(&pdev->dev, "Interrupt not available.\n");
|
||||||
rtc->irq = -1;
|
rtc->irq = -1;
|
||||||
|
}
|
||||||
|
platform_set_drvdata(pdev, rtc);
|
||||||
|
|
||||||
|
if (rtc->irq != -1) {
|
||||||
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
|
ops = &armada38x_rtc_ops;
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* If there is no interrupt available then we can't
|
* If there is no interrupt available then we can't
|
||||||
* use the alarm
|
* use the alarm
|
||||||
*/
|
*/
|
||||||
armada38x_rtc_ops.set_alarm = NULL;
|
ops = &armada38x_rtc_ops_noirq;
|
||||||
armada38x_rtc_ops.alarm_irq_enable = NULL;
|
|
||||||
}
|
}
|
||||||
platform_set_drvdata(pdev, rtc);
|
rtc->data = (struct armada38x_rtc_data *)match->data;
|
||||||
if (rtc->irq != -1)
|
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
|
||||||
|
/* Update RTC-MBUS bridge timing parameters */
|
||||||
|
rtc->data->update_mbus_timing(rtc);
|
||||||
|
|
||||||
rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
|
rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||||
&armada38x_rtc_ops, THIS_MODULE);
|
ops, THIS_MODULE);
|
||||||
if (IS_ERR(rtc->rtc_dev)) {
|
if (IS_ERR(rtc->rtc_dev)) {
|
||||||
ret = PTR_ERR(rtc->rtc_dev);
|
ret = PTR_ERR(rtc->rtc_dev);
|
||||||
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
|
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
|
||||||
|
@ -280,6 +477,9 @@ static int armada38x_rtc_resume(struct device *dev)
|
||||||
if (device_may_wakeup(dev)) {
|
if (device_may_wakeup(dev)) {
|
||||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
/* Update RTC-MBUS bridge timing parameters */
|
||||||
|
rtc->data->update_mbus_timing(rtc);
|
||||||
|
|
||||||
return disable_irq_wake(rtc->irq);
|
return disable_irq_wake(rtc->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,14 +490,6 @@ static int armada38x_rtc_resume(struct device *dev)
|
||||||
static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
|
static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
|
||||||
armada38x_rtc_suspend, armada38x_rtc_resume);
|
armada38x_rtc_suspend, armada38x_rtc_resume);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static const struct of_device_id armada38x_rtc_of_match_table[] = {
|
|
||||||
{ .compatible = "marvell,armada-380-rtc", },
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver armada38x_rtc_driver = {
|
static struct platform_driver armada38x_rtc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "armada38x-rtc",
|
.name = "armada38x-rtc",
|
||||||
|
|
|
@ -56,7 +56,7 @@ static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops au1xtoy_rtc_ops = {
|
static const struct rtc_class_ops au1xtoy_rtc_ops = {
|
||||||
.read_time = au1xtoy_rtc_read_time,
|
.read_time = au1xtoy_rtc_read_time,
|
||||||
.set_time = au1xtoy_rtc_set_time,
|
.set_time = au1xtoy_rtc_set_time,
|
||||||
};
|
};
|
||||||
|
|
|
@ -333,7 +333,7 @@ static int bfin_rtc_proc(struct device *dev, struct seq_file *seq)
|
||||||
#undef yesno
|
#undef yesno
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops bfin_rtc_ops = {
|
static const struct rtc_class_ops bfin_rtc_ops = {
|
||||||
.read_time = bfin_rtc_read_time,
|
.read_time = bfin_rtc_read_time,
|
||||||
.set_time = bfin_rtc_set_time,
|
.set_time = bfin_rtc_set_time,
|
||||||
.read_alarm = bfin_rtc_read_alarm,
|
.read_alarm = bfin_rtc_read_alarm,
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */
|
#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */
|
||||||
#define BQ32K_TCH2 0x08 /* Trickle charge enable */
|
#define BQ32K_TCH2 0x08 /* Trickle charge enable */
|
||||||
#define BQ32K_CFG2 0x09 /* Trickle charger control */
|
#define BQ32K_CFG2 0x09 /* Trickle charger control */
|
||||||
|
#define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */
|
||||||
|
|
||||||
struct bq32k_regs {
|
struct bq32k_regs {
|
||||||
uint8_t seconds;
|
uint8_t seconds;
|
||||||
|
@ -188,6 +189,65 @@ static int trickle_charger_of_init(struct device *dev, struct device_node *node)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t bq32k_sysfs_show_tricklecharge_bypass(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int reg, error;
|
||||||
|
|
||||||
|
error = bq32k_read(dev, ®, BQ32K_CFG2, 1);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return sprintf(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t bq32k_sysfs_store_tricklecharge_bypass(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
int reg, enable, error;
|
||||||
|
|
||||||
|
if (kstrtoint(buf, 0, &enable))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
error = bq32k_read(dev, ®, BQ32K_CFG2, 1);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
reg |= BQ32K_TCFE;
|
||||||
|
error = bq32k_write(dev, ®, BQ32K_CFG2, 1);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
dev_info(dev, "Enabled trickle charge FET bypass.\n");
|
||||||
|
} else {
|
||||||
|
reg &= ~BQ32K_TCFE;
|
||||||
|
error = bq32k_write(dev, ®, BQ32K_CFG2, 1);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
dev_info(dev, "Disabled trickle charge FET bypass.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(trickle_charge_bypass, 0644,
|
||||||
|
bq32k_sysfs_show_tricklecharge_bypass,
|
||||||
|
bq32k_sysfs_store_tricklecharge_bypass);
|
||||||
|
|
||||||
|
static int bq32k_sysfs_register(struct device *dev)
|
||||||
|
{
|
||||||
|
return device_create_file(dev, &dev_attr_trickle_charge_bypass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bq32k_sysfs_unregister(struct device *dev)
|
||||||
|
{
|
||||||
|
device_remove_file(dev, &dev_attr_trickle_charge_bypass);
|
||||||
|
}
|
||||||
|
|
||||||
static int bq32k_probe(struct i2c_client *client,
|
static int bq32k_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -224,11 +284,26 @@ static int bq32k_probe(struct i2c_client *client,
|
||||||
if (IS_ERR(rtc))
|
if (IS_ERR(rtc))
|
||||||
return PTR_ERR(rtc);
|
return PTR_ERR(rtc);
|
||||||
|
|
||||||
|
error = bq32k_sysfs_register(&client->dev);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"Unable to create sysfs entries for rtc bq32000\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, rtc);
|
i2c_set_clientdata(client, rtc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bq32k_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
bq32k_sysfs_unregister(&client->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id bq32k_id[] = {
|
static const struct i2c_device_id bq32k_id[] = {
|
||||||
{ "bq32000", 0 },
|
{ "bq32000", 0 },
|
||||||
{ }
|
{ }
|
||||||
|
@ -240,6 +315,7 @@ static struct i2c_driver bq32k_driver = {
|
||||||
.name = "bq32k",
|
.name = "bq32k",
|
||||||
},
|
},
|
||||||
.probe = bq32k_probe,
|
.probe = bq32k_probe,
|
||||||
|
.remove = bq32k_remove,
|
||||||
.id_table = bq32k_id,
|
.id_table = bq32k_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops dm355evm_rtc_ops = {
|
static const struct rtc_class_ops dm355evm_rtc_ops = {
|
||||||
.read_time = dm355evm_rtc_read_time,
|
.read_time = dm355evm_rtc_read_time,
|
||||||
.set_time = dm355evm_rtc_set_time,
|
.set_time = dm355evm_rtc_set_time,
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,28 +23,28 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#define DS3232_REG_SECONDS 0x00
|
#define DS3232_REG_SECONDS 0x00
|
||||||
#define DS3232_REG_MINUTES 0x01
|
#define DS3232_REG_MINUTES 0x01
|
||||||
#define DS3232_REG_HOURS 0x02
|
#define DS3232_REG_HOURS 0x02
|
||||||
#define DS3232_REG_AMPM 0x02
|
#define DS3232_REG_AMPM 0x02
|
||||||
#define DS3232_REG_DAY 0x03
|
#define DS3232_REG_DAY 0x03
|
||||||
#define DS3232_REG_DATE 0x04
|
#define DS3232_REG_DATE 0x04
|
||||||
#define DS3232_REG_MONTH 0x05
|
#define DS3232_REG_MONTH 0x05
|
||||||
#define DS3232_REG_CENTURY 0x05
|
#define DS3232_REG_CENTURY 0x05
|
||||||
#define DS3232_REG_YEAR 0x06
|
#define DS3232_REG_YEAR 0x06
|
||||||
#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
|
#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
|
||||||
#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
|
#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
|
||||||
#define DS3232_REG_CR 0x0E /* Control register */
|
#define DS3232_REG_CR 0x0E /* Control register */
|
||||||
# define DS3232_REG_CR_nEOSC 0x80
|
# define DS3232_REG_CR_nEOSC 0x80
|
||||||
# define DS3232_REG_CR_INTCN 0x04
|
# define DS3232_REG_CR_INTCN 0x04
|
||||||
# define DS3232_REG_CR_A2IE 0x02
|
# define DS3232_REG_CR_A2IE 0x02
|
||||||
# define DS3232_REG_CR_A1IE 0x01
|
# define DS3232_REG_CR_A1IE 0x01
|
||||||
|
|
||||||
#define DS3232_REG_SR 0x0F /* control/status register */
|
#define DS3232_REG_SR 0x0F /* control/status register */
|
||||||
# define DS3232_REG_SR_OSF 0x80
|
# define DS3232_REG_SR_OSF 0x80
|
||||||
# define DS3232_REG_SR_BSY 0x04
|
# define DS3232_REG_SR_BSY 0x04
|
||||||
# define DS3232_REG_SR_A2F 0x02
|
# define DS3232_REG_SR_A2F 0x02
|
||||||
# define DS3232_REG_SR_A1F 0x01
|
# define DS3232_REG_SR_A1F 0x01
|
||||||
|
|
||||||
struct ds3232 {
|
struct ds3232 {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -363,6 +363,9 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (ds3232->irq > 0)
|
||||||
|
device_init_wakeup(dev, 1);
|
||||||
|
|
||||||
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
|
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
|
||||||
THIS_MODULE);
|
THIS_MODULE);
|
||||||
if (IS_ERR(ds3232->rtc))
|
if (IS_ERR(ds3232->rtc))
|
||||||
|
@ -374,10 +377,10 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||||
IRQF_SHARED | IRQF_ONESHOT,
|
IRQF_SHARED | IRQF_ONESHOT,
|
||||||
name, dev);
|
name, dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
device_set_wakeup_capable(dev, 0);
|
||||||
ds3232->irq = 0;
|
ds3232->irq = 0;
|
||||||
dev_err(dev, "unable to request IRQ\n");
|
dev_err(dev, "unable to request IRQ\n");
|
||||||
} else
|
}
|
||||||
device_init_wakeup(dev, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -420,6 +423,7 @@ static int ds3232_i2c_probe(struct i2c_client *client,
|
||||||
static const struct regmap_config config = {
|
static const struct regmap_config config = {
|
||||||
.reg_bits = 8,
|
.reg_bits = 8,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
|
.max_register = 0x13,
|
||||||
};
|
};
|
||||||
|
|
||||||
regmap = devm_regmap_init_i2c(client, &config);
|
regmap = devm_regmap_init_i2c(client, &config);
|
||||||
|
@ -479,6 +483,7 @@ static int ds3234_probe(struct spi_device *spi)
|
||||||
static const struct regmap_config config = {
|
static const struct regmap_config config = {
|
||||||
.reg_bits = 8,
|
.reg_bits = 8,
|
||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
|
.max_register = 0x13,
|
||||||
.write_flag_mask = 0x80,
|
.write_flag_mask = 0x80,
|
||||||
};
|
};
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
|
|
@ -159,9 +159,16 @@ static int gemini_rtc_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id gemini_rtc_dt_match[] = {
|
||||||
|
{ .compatible = "cortina,gemini-rtc" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match);
|
||||||
|
|
||||||
static struct platform_driver gemini_rtc_driver = {
|
static struct platform_driver gemini_rtc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
|
.of_match_table = gemini_rtc_dt_match,
|
||||||
},
|
},
|
||||||
.probe = gemini_rtc_probe,
|
.probe = gemini_rtc_probe,
|
||||||
.remove = gemini_rtc_remove,
|
.remove = gemini_rtc_remove,
|
||||||
|
|
|
@ -108,7 +108,6 @@
|
||||||
* @pdev: pionter to platform dev
|
* @pdev: pionter to platform dev
|
||||||
* @rtc: pointer to rtc struct
|
* @rtc: pointer to rtc struct
|
||||||
* @ioaddr: IO registers pointer
|
* @ioaddr: IO registers pointer
|
||||||
* @irq: dryice normal interrupt
|
|
||||||
* @clk: input reference clock
|
* @clk: input reference clock
|
||||||
* @dsr: copy of the DSR register
|
* @dsr: copy of the DSR register
|
||||||
* @irq_lock: interrupt enable register (DIER) lock
|
* @irq_lock: interrupt enable register (DIER) lock
|
||||||
|
@ -120,7 +119,6 @@ struct imxdi_dev {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
void __iomem *ioaddr;
|
void __iomem *ioaddr;
|
||||||
int irq;
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u32 dsr;
|
u32 dsr;
|
||||||
spinlock_t irq_lock;
|
spinlock_t irq_lock;
|
||||||
|
@ -668,7 +666,7 @@ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops dryice_rtc_ops = {
|
static const struct rtc_class_ops dryice_rtc_ops = {
|
||||||
.read_time = dryice_rtc_read_time,
|
.read_time = dryice_rtc_read_time,
|
||||||
.set_mmss = dryice_rtc_set_mmss,
|
.set_mmss = dryice_rtc_set_mmss,
|
||||||
.alarm_irq_enable = dryice_rtc_alarm_irq_enable,
|
.alarm_irq_enable = dryice_rtc_alarm_irq_enable,
|
||||||
|
@ -677,9 +675,9 @@ static struct rtc_class_ops dryice_rtc_ops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dryice "normal" interrupt handler
|
* interrupt handler for dryice "normal" and security violation interrupt
|
||||||
*/
|
*/
|
||||||
static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
|
static irqreturn_t dryice_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct imxdi_dev *imxdi = dev_id;
|
struct imxdi_dev *imxdi = dev_id;
|
||||||
u32 dsr, dier;
|
u32 dsr, dier;
|
||||||
|
@ -765,6 +763,7 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct imxdi_dev *imxdi;
|
struct imxdi_dev *imxdi;
|
||||||
|
int norm_irq, sec_irq;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
|
imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
|
||||||
|
@ -780,9 +779,16 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
spin_lock_init(&imxdi->irq_lock);
|
spin_lock_init(&imxdi->irq_lock);
|
||||||
|
|
||||||
imxdi->irq = platform_get_irq(pdev, 0);
|
norm_irq = platform_get_irq(pdev, 0);
|
||||||
if (imxdi->irq < 0)
|
if (norm_irq < 0)
|
||||||
return imxdi->irq;
|
return norm_irq;
|
||||||
|
|
||||||
|
/* the 2nd irq is the security violation irq
|
||||||
|
* make this optional, don't break the device tree ABI
|
||||||
|
*/
|
||||||
|
sec_irq = platform_get_irq(pdev, 1);
|
||||||
|
if (sec_irq <= 0)
|
||||||
|
sec_irq = IRQ_NOTCONNECTED;
|
||||||
|
|
||||||
init_waitqueue_head(&imxdi->write_wait);
|
init_waitqueue_head(&imxdi->write_wait);
|
||||||
|
|
||||||
|
@ -808,13 +814,20 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
|
rc = devm_request_irq(&pdev->dev, norm_irq, dryice_irq,
|
||||||
IRQF_SHARED, pdev->name, imxdi);
|
IRQF_SHARED, pdev->name, imxdi);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_warn(&pdev->dev, "interrupt not available.\n");
|
dev_warn(&pdev->dev, "interrupt not available.\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = devm_request_irq(&pdev->dev, sec_irq, dryice_irq,
|
||||||
|
IRQF_SHARED, pdev->name, imxdi);
|
||||||
|
if (rc) {
|
||||||
|
dev_warn(&pdev->dev, "security violation interrupt not available.\n");
|
||||||
|
/* this is not an error, see above */
|
||||||
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, imxdi);
|
platform_set_drvdata(pdev, imxdi);
|
||||||
imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||||
&dryice_rtc_ops, THIS_MODULE);
|
&dryice_rtc_ops, THIS_MODULE);
|
||||||
|
|
|
@ -138,7 +138,7 @@ err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops ls1x_rtc_ops = {
|
static const struct rtc_class_ops ls1x_rtc_ops = {
|
||||||
.read_time = ls1x_rtc_read_time,
|
.read_time = ls1x_rtc_read_time,
|
||||||
.set_time = ls1x_rtc_set_time,
|
.set_time = ls1x_rtc_set_time,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,62 +16,88 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/platform_data/rtc-m48t86.h>
|
|
||||||
#include <linux/bcd.h>
|
#include <linux/bcd.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
#define M48T86_REG_SEC 0x00
|
#define M48T86_SEC 0x00
|
||||||
#define M48T86_REG_SECALRM 0x01
|
#define M48T86_SECALRM 0x01
|
||||||
#define M48T86_REG_MIN 0x02
|
#define M48T86_MIN 0x02
|
||||||
#define M48T86_REG_MINALRM 0x03
|
#define M48T86_MINALRM 0x03
|
||||||
#define M48T86_REG_HOUR 0x04
|
#define M48T86_HOUR 0x04
|
||||||
#define M48T86_REG_HOURALRM 0x05
|
#define M48T86_HOURALRM 0x05
|
||||||
#define M48T86_REG_DOW 0x06 /* 1 = sunday */
|
#define M48T86_DOW 0x06 /* 1 = sunday */
|
||||||
#define M48T86_REG_DOM 0x07
|
#define M48T86_DOM 0x07
|
||||||
#define M48T86_REG_MONTH 0x08 /* 1 - 12 */
|
#define M48T86_MONTH 0x08 /* 1 - 12 */
|
||||||
#define M48T86_REG_YEAR 0x09 /* 0 - 99 */
|
#define M48T86_YEAR 0x09 /* 0 - 99 */
|
||||||
#define M48T86_REG_A 0x0A
|
#define M48T86_A 0x0a
|
||||||
#define M48T86_REG_B 0x0B
|
#define M48T86_B 0x0b
|
||||||
#define M48T86_REG_C 0x0C
|
#define M48T86_B_SET BIT(7)
|
||||||
#define M48T86_REG_D 0x0D
|
#define M48T86_B_DM BIT(2)
|
||||||
|
#define M48T86_B_H24 BIT(1)
|
||||||
|
#define M48T86_C 0x0c
|
||||||
|
#define M48T86_D 0x0d
|
||||||
|
#define M48T86_D_VRT BIT(7)
|
||||||
|
#define M48T86_NVRAM(x) (0x0e + (x))
|
||||||
|
#define M48T86_NVRAM_LEN 114
|
||||||
|
|
||||||
#define M48T86_REG_B_H24 (1 << 1)
|
struct m48t86_rtc_info {
|
||||||
#define M48T86_REG_B_DM (1 << 2)
|
void __iomem *index_reg;
|
||||||
#define M48T86_REG_B_SET (1 << 7)
|
void __iomem *data_reg;
|
||||||
#define M48T86_REG_D_VRT (1 << 7)
|
struct rtc_device *rtc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned char m48t86_readb(struct device *dev, unsigned long addr)
|
||||||
|
{
|
||||||
|
struct m48t86_rtc_info *info = dev_get_drvdata(dev);
|
||||||
|
unsigned char value;
|
||||||
|
|
||||||
|
writeb(addr, info->index_reg);
|
||||||
|
value = readb(info->data_reg);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void m48t86_writeb(struct device *dev,
|
||||||
|
unsigned char value, unsigned long addr)
|
||||||
|
{
|
||||||
|
struct m48t86_rtc_info *info = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
writeb(addr, info->index_reg);
|
||||||
|
writeb(value, info->data_reg);
|
||||||
|
}
|
||||||
|
|
||||||
static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
unsigned char reg;
|
unsigned char reg;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
|
||||||
struct m48t86_ops *ops = dev_get_platdata(&pdev->dev);
|
|
||||||
|
|
||||||
reg = ops->readbyte(M48T86_REG_B);
|
reg = m48t86_readb(dev, M48T86_B);
|
||||||
|
|
||||||
if (reg & M48T86_REG_B_DM) {
|
if (reg & M48T86_B_DM) {
|
||||||
/* data (binary) mode */
|
/* data (binary) mode */
|
||||||
tm->tm_sec = ops->readbyte(M48T86_REG_SEC);
|
tm->tm_sec = m48t86_readb(dev, M48T86_SEC);
|
||||||
tm->tm_min = ops->readbyte(M48T86_REG_MIN);
|
tm->tm_min = m48t86_readb(dev, M48T86_MIN);
|
||||||
tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F;
|
tm->tm_hour = m48t86_readb(dev, M48T86_HOUR) & 0x3f;
|
||||||
tm->tm_mday = ops->readbyte(M48T86_REG_DOM);
|
tm->tm_mday = m48t86_readb(dev, M48T86_DOM);
|
||||||
/* tm_mon is 0-11 */
|
/* tm_mon is 0-11 */
|
||||||
tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1;
|
tm->tm_mon = m48t86_readb(dev, M48T86_MONTH) - 1;
|
||||||
tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100;
|
tm->tm_year = m48t86_readb(dev, M48T86_YEAR) + 100;
|
||||||
tm->tm_wday = ops->readbyte(M48T86_REG_DOW);
|
tm->tm_wday = m48t86_readb(dev, M48T86_DOW);
|
||||||
} else {
|
} else {
|
||||||
/* bcd mode */
|
/* bcd mode */
|
||||||
tm->tm_sec = bcd2bin(ops->readbyte(M48T86_REG_SEC));
|
tm->tm_sec = bcd2bin(m48t86_readb(dev, M48T86_SEC));
|
||||||
tm->tm_min = bcd2bin(ops->readbyte(M48T86_REG_MIN));
|
tm->tm_min = bcd2bin(m48t86_readb(dev, M48T86_MIN));
|
||||||
tm->tm_hour = bcd2bin(ops->readbyte(M48T86_REG_HOUR) & 0x3F);
|
tm->tm_hour = bcd2bin(m48t86_readb(dev, M48T86_HOUR) &
|
||||||
tm->tm_mday = bcd2bin(ops->readbyte(M48T86_REG_DOM));
|
0x3f);
|
||||||
|
tm->tm_mday = bcd2bin(m48t86_readb(dev, M48T86_DOM));
|
||||||
/* tm_mon is 0-11 */
|
/* tm_mon is 0-11 */
|
||||||
tm->tm_mon = bcd2bin(ops->readbyte(M48T86_REG_MONTH)) - 1;
|
tm->tm_mon = bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1;
|
||||||
tm->tm_year = bcd2bin(ops->readbyte(M48T86_REG_YEAR)) + 100;
|
tm->tm_year = bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100;
|
||||||
tm->tm_wday = bcd2bin(ops->readbyte(M48T86_REG_DOW));
|
tm->tm_wday = bcd2bin(m48t86_readb(dev, M48T86_DOW));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* correct the hour if the clock is in 12h mode */
|
/* correct the hour if the clock is in 12h mode */
|
||||||
if (!(reg & M48T86_REG_B_H24))
|
if (!(reg & M48T86_B_H24))
|
||||||
if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
|
if (m48t86_readb(dev, M48T86_HOUR) & 0x80)
|
||||||
tm->tm_hour += 12;
|
tm->tm_hour += 12;
|
||||||
|
|
||||||
return rtc_valid_tm(tm);
|
return rtc_valid_tm(tm);
|
||||||
|
@ -80,38 +106,36 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
{
|
{
|
||||||
unsigned char reg;
|
unsigned char reg;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
|
||||||
struct m48t86_ops *ops = dev_get_platdata(&pdev->dev);
|
|
||||||
|
|
||||||
reg = ops->readbyte(M48T86_REG_B);
|
reg = m48t86_readb(dev, M48T86_B);
|
||||||
|
|
||||||
/* update flag and 24h mode */
|
/* update flag and 24h mode */
|
||||||
reg |= M48T86_REG_B_SET | M48T86_REG_B_H24;
|
reg |= M48T86_B_SET | M48T86_B_H24;
|
||||||
ops->writebyte(reg, M48T86_REG_B);
|
m48t86_writeb(dev, reg, M48T86_B);
|
||||||
|
|
||||||
if (reg & M48T86_REG_B_DM) {
|
if (reg & M48T86_B_DM) {
|
||||||
/* data (binary) mode */
|
/* data (binary) mode */
|
||||||
ops->writebyte(tm->tm_sec, M48T86_REG_SEC);
|
m48t86_writeb(dev, tm->tm_sec, M48T86_SEC);
|
||||||
ops->writebyte(tm->tm_min, M48T86_REG_MIN);
|
m48t86_writeb(dev, tm->tm_min, M48T86_MIN);
|
||||||
ops->writebyte(tm->tm_hour, M48T86_REG_HOUR);
|
m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR);
|
||||||
ops->writebyte(tm->tm_mday, M48T86_REG_DOM);
|
m48t86_writeb(dev, tm->tm_mday, M48T86_DOM);
|
||||||
ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH);
|
m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH);
|
||||||
ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR);
|
m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR);
|
||||||
ops->writebyte(tm->tm_wday, M48T86_REG_DOW);
|
m48t86_writeb(dev, tm->tm_wday, M48T86_DOW);
|
||||||
} else {
|
} else {
|
||||||
/* bcd mode */
|
/* bcd mode */
|
||||||
ops->writebyte(bin2bcd(tm->tm_sec), M48T86_REG_SEC);
|
m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC);
|
||||||
ops->writebyte(bin2bcd(tm->tm_min), M48T86_REG_MIN);
|
m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN);
|
||||||
ops->writebyte(bin2bcd(tm->tm_hour), M48T86_REG_HOUR);
|
m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR);
|
||||||
ops->writebyte(bin2bcd(tm->tm_mday), M48T86_REG_DOM);
|
m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM);
|
||||||
ops->writebyte(bin2bcd(tm->tm_mon + 1), M48T86_REG_MONTH);
|
m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH);
|
||||||
ops->writebyte(bin2bcd(tm->tm_year % 100), M48T86_REG_YEAR);
|
m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR);
|
||||||
ops->writebyte(bin2bcd(tm->tm_wday), M48T86_REG_DOW);
|
m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update ended */
|
/* update ended */
|
||||||
reg &= ~M48T86_REG_B_SET;
|
reg &= ~M48T86_B_SET;
|
||||||
ops->writebyte(reg, M48T86_REG_B);
|
m48t86_writeb(dev, reg, M48T86_B);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -119,18 +143,16 @@ static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)
|
static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)
|
||||||
{
|
{
|
||||||
unsigned char reg;
|
unsigned char reg;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
|
||||||
struct m48t86_ops *ops = dev_get_platdata(&pdev->dev);
|
|
||||||
|
|
||||||
reg = ops->readbyte(M48T86_REG_B);
|
reg = m48t86_readb(dev, M48T86_B);
|
||||||
|
|
||||||
seq_printf(seq, "mode\t\t: %s\n",
|
seq_printf(seq, "mode\t\t: %s\n",
|
||||||
(reg & M48T86_REG_B_DM) ? "binary" : "bcd");
|
(reg & M48T86_B_DM) ? "binary" : "bcd");
|
||||||
|
|
||||||
reg = ops->readbyte(M48T86_REG_D);
|
reg = m48t86_readb(dev, M48T86_D);
|
||||||
|
|
||||||
seq_printf(seq, "battery\t\t: %s\n",
|
seq_printf(seq, "battery\t\t: %s\n",
|
||||||
(reg & M48T86_REG_D_VRT) ? "ok" : "exhausted");
|
(reg & M48T86_D_VRT) ? "ok" : "exhausted");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -141,25 +163,116 @@ static const struct rtc_class_ops m48t86_rtc_ops = {
|
||||||
.proc = m48t86_rtc_proc,
|
.proc = m48t86_rtc_proc,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int m48t86_rtc_probe(struct platform_device *dev)
|
static ssize_t m48t86_nvram_read(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *attr,
|
||||||
|
char *buf, loff_t off, size_t count)
|
||||||
{
|
{
|
||||||
|
struct device *dev = kobj_to_dev(kobj);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
buf[i] = m48t86_readb(dev, M48T86_NVRAM(off + i));
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t m48t86_nvram_write(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *attr,
|
||||||
|
char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct device *dev = kobj_to_dev(kobj);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
m48t86_writeb(dev, buf[i], M48T86_NVRAM(off + i));
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BIN_ATTR(nvram, 0644, m48t86_nvram_read, m48t86_nvram_write,
|
||||||
|
M48T86_NVRAM_LEN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RTC is an optional feature at purchase time on some Technologic Systems
|
||||||
|
* boards. Verify that it actually exists by checking if the last two bytes
|
||||||
|
* of the NVRAM can be changed.
|
||||||
|
*
|
||||||
|
* This is based on the method used in their rtc7800.c example.
|
||||||
|
*/
|
||||||
|
static bool m48t86_verify_chip(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
unsigned int offset0 = M48T86_NVRAM(M48T86_NVRAM_LEN - 2);
|
||||||
|
unsigned int offset1 = M48T86_NVRAM(M48T86_NVRAM_LEN - 1);
|
||||||
|
unsigned char tmp0, tmp1;
|
||||||
|
|
||||||
|
tmp0 = m48t86_readb(&pdev->dev, offset0);
|
||||||
|
tmp1 = m48t86_readb(&pdev->dev, offset1);
|
||||||
|
|
||||||
|
m48t86_writeb(&pdev->dev, 0x00, offset0);
|
||||||
|
m48t86_writeb(&pdev->dev, 0x55, offset1);
|
||||||
|
if (m48t86_readb(&pdev->dev, offset1) == 0x55) {
|
||||||
|
m48t86_writeb(&pdev->dev, 0xaa, offset1);
|
||||||
|
if (m48t86_readb(&pdev->dev, offset1) == 0xaa &&
|
||||||
|
m48t86_readb(&pdev->dev, offset0) == 0x00) {
|
||||||
|
m48t86_writeb(&pdev->dev, tmp0, offset0);
|
||||||
|
m48t86_writeb(&pdev->dev, tmp1, offset1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m48t86_rtc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct m48t86_rtc_info *info;
|
||||||
|
struct resource *res;
|
||||||
unsigned char reg;
|
unsigned char reg;
|
||||||
struct m48t86_ops *ops = dev_get_platdata(&dev->dev);
|
|
||||||
struct rtc_device *rtc;
|
|
||||||
|
|
||||||
rtc = devm_rtc_device_register(&dev->dev, "m48t86",
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
&m48t86_rtc_ops, THIS_MODULE);
|
if (!info)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
if (IS_ERR(rtc))
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
return PTR_ERR(rtc);
|
if (!res)
|
||||||
|
return -ENODEV;
|
||||||
|
info->index_reg = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(info->index_reg))
|
||||||
|
return PTR_ERR(info->index_reg);
|
||||||
|
|
||||||
platform_set_drvdata(dev, rtc);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
if (!res)
|
||||||
|
return -ENODEV;
|
||||||
|
info->data_reg = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(info->data_reg))
|
||||||
|
return PTR_ERR(info->data_reg);
|
||||||
|
|
||||||
|
dev_set_drvdata(&pdev->dev, info);
|
||||||
|
|
||||||
|
if (!m48t86_verify_chip(pdev)) {
|
||||||
|
dev_info(&pdev->dev, "RTC not present\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->rtc = devm_rtc_device_register(&pdev->dev, "m48t86",
|
||||||
|
&m48t86_rtc_ops, THIS_MODULE);
|
||||||
|
if (IS_ERR(info->rtc))
|
||||||
|
return PTR_ERR(info->rtc);
|
||||||
|
|
||||||
/* read battery status */
|
/* read battery status */
|
||||||
reg = ops->readbyte(M48T86_REG_D);
|
reg = m48t86_readb(&pdev->dev, M48T86_D);
|
||||||
dev_info(&dev->dev, "battery %s\n",
|
dev_info(&pdev->dev, "battery %s\n",
|
||||||
(reg & M48T86_REG_D_VRT) ? "ok" : "exhausted");
|
(reg & M48T86_D_VRT) ? "ok" : "exhausted");
|
||||||
|
|
||||||
|
if (device_create_bin_file(&pdev->dev, &bin_attr_nvram))
|
||||||
|
dev_err(&pdev->dev, "failed to create nvram sysfs entry\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m48t86_rtc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
device_remove_bin_file(&pdev->dev, &bin_attr_nvram);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +281,7 @@ static struct platform_driver m48t86_rtc_platform_driver = {
|
||||||
.name = "rtc-m48t86",
|
.name = "rtc-m48t86",
|
||||||
},
|
},
|
||||||
.probe = m48t86_rtc_probe,
|
.probe = m48t86_rtc_probe,
|
||||||
|
.remove = m48t86_rtc_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(m48t86_rtc_platform_driver);
|
module_platform_driver(m48t86_rtc_platform_driver);
|
||||||
|
|
|
@ -44,12 +44,22 @@
|
||||||
#define MCP795_REG_DAY 0x04
|
#define MCP795_REG_DAY 0x04
|
||||||
#define MCP795_REG_MONTH 0x06
|
#define MCP795_REG_MONTH 0x06
|
||||||
#define MCP795_REG_CONTROL 0x08
|
#define MCP795_REG_CONTROL 0x08
|
||||||
|
#define MCP795_REG_ALM0_SECONDS 0x0C
|
||||||
|
#define MCP795_REG_ALM0_DAY 0x0F
|
||||||
|
|
||||||
#define MCP795_ST_BIT BIT(7)
|
#define MCP795_ST_BIT BIT(7)
|
||||||
#define MCP795_24_BIT BIT(6)
|
#define MCP795_24_BIT BIT(6)
|
||||||
#define MCP795_LP_BIT BIT(5)
|
#define MCP795_LP_BIT BIT(5)
|
||||||
#define MCP795_EXTOSC_BIT BIT(3)
|
#define MCP795_EXTOSC_BIT BIT(3)
|
||||||
#define MCP795_OSCON_BIT BIT(5)
|
#define MCP795_OSCON_BIT BIT(5)
|
||||||
|
#define MCP795_ALM0_BIT BIT(4)
|
||||||
|
#define MCP795_ALM1_BIT BIT(5)
|
||||||
|
#define MCP795_ALM0IF_BIT BIT(3)
|
||||||
|
#define MCP795_ALM0C0_BIT BIT(4)
|
||||||
|
#define MCP795_ALM0C1_BIT BIT(5)
|
||||||
|
#define MCP795_ALM0C2_BIT BIT(6)
|
||||||
|
|
||||||
|
#define SEC_PER_DAY (24 * 60 * 60)
|
||||||
|
|
||||||
static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count)
|
static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count)
|
||||||
{
|
{
|
||||||
|
@ -150,6 +160,30 @@ static int mcp795_start_oscillator(struct device *dev, bool *extosc)
|
||||||
dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT);
|
dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enable or disable Alarm 0 in RTC */
|
||||||
|
static int mcp795_update_alarm(struct device *dev, bool enable)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dev_dbg(dev, "%s alarm\n", enable ? "Enable" : "Disable");
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
/* clear ALM0IF (Alarm 0 Interrupt Flag) bit */
|
||||||
|
ret = mcp795_rtcc_set_bits(dev, MCP795_REG_ALM0_DAY,
|
||||||
|
MCP795_ALM0IF_BIT, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
/* enable alarm 0 */
|
||||||
|
ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL,
|
||||||
|
MCP795_ALM0_BIT, MCP795_ALM0_BIT);
|
||||||
|
} else {
|
||||||
|
/* disable alarm 0 and alarm 1 */
|
||||||
|
ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL,
|
||||||
|
MCP795_ALM0_BIT | MCP795_ALM1_BIT, 0);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
|
static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -170,6 +204,7 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
|
||||||
data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec);
|
data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec);
|
||||||
data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min);
|
data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min);
|
||||||
data[2] = bin2bcd(tim->tm_hour);
|
data[2] = bin2bcd(tim->tm_hour);
|
||||||
|
data[3] = (data[3] & 0xF8) | bin2bcd(tim->tm_wday + 1);
|
||||||
data[4] = bin2bcd(tim->tm_mday);
|
data[4] = bin2bcd(tim->tm_mday);
|
||||||
data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1);
|
data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1);
|
||||||
|
|
||||||
|
@ -198,9 +233,9 @@ static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dev_dbg(dev, "Set mcp795: %04d-%02d-%02d %02d:%02d:%02d\n",
|
dev_dbg(dev, "Set mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n",
|
||||||
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
|
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
|
||||||
tim->tm_hour, tim->tm_min, tim->tm_sec);
|
tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -218,20 +253,139 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
|
||||||
tim->tm_sec = bcd2bin(data[0] & 0x7F);
|
tim->tm_sec = bcd2bin(data[0] & 0x7F);
|
||||||
tim->tm_min = bcd2bin(data[1] & 0x7F);
|
tim->tm_min = bcd2bin(data[1] & 0x7F);
|
||||||
tim->tm_hour = bcd2bin(data[2] & 0x3F);
|
tim->tm_hour = bcd2bin(data[2] & 0x3F);
|
||||||
|
tim->tm_wday = bcd2bin(data[3] & 0x07) - 1;
|
||||||
tim->tm_mday = bcd2bin(data[4] & 0x3F);
|
tim->tm_mday = bcd2bin(data[4] & 0x3F);
|
||||||
tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1;
|
tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1;
|
||||||
tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */
|
tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */
|
||||||
|
|
||||||
dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n",
|
dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n",
|
||||||
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
|
tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
|
||||||
tim->tm_hour, tim->tm_min, tim->tm_sec);
|
tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
|
||||||
|
|
||||||
return rtc_valid_tm(tim);
|
return rtc_valid_tm(tim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mcp795_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||||
|
{
|
||||||
|
struct rtc_time now_tm;
|
||||||
|
time64_t now;
|
||||||
|
time64_t later;
|
||||||
|
u8 tmp[6];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Read current time from RTC hardware */
|
||||||
|
ret = mcp795_read_time(dev, &now_tm);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
/* Get the number of seconds since 1970 */
|
||||||
|
now = rtc_tm_to_time64(&now_tm);
|
||||||
|
later = rtc_tm_to_time64(&alm->time);
|
||||||
|
if (later <= now)
|
||||||
|
return -EINVAL;
|
||||||
|
/* make sure alarm fires within the next one year */
|
||||||
|
if ((later - now) >=
|
||||||
|
(SEC_PER_DAY * (365 + is_leap_year(alm->time.tm_year))))
|
||||||
|
return -EDOM;
|
||||||
|
/* disable alarm */
|
||||||
|
ret = mcp795_update_alarm(dev, false);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
/* Read registers, so we can leave configuration bits untouched */
|
||||||
|
ret = mcp795_rtcc_read(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
alm->time.tm_year = -1;
|
||||||
|
alm->time.tm_isdst = -1;
|
||||||
|
alm->time.tm_yday = -1;
|
||||||
|
|
||||||
|
tmp[0] = (tmp[0] & 0x80) | bin2bcd(alm->time.tm_sec);
|
||||||
|
tmp[1] = (tmp[1] & 0x80) | bin2bcd(alm->time.tm_min);
|
||||||
|
tmp[2] = (tmp[2] & 0xE0) | bin2bcd(alm->time.tm_hour);
|
||||||
|
tmp[3] = (tmp[3] & 0x80) | bin2bcd(alm->time.tm_wday + 1);
|
||||||
|
/* set alarm match: seconds, minutes, hour, day, date and month */
|
||||||
|
tmp[3] |= (MCP795_ALM0C2_BIT | MCP795_ALM0C1_BIT | MCP795_ALM0C0_BIT);
|
||||||
|
tmp[4] = (tmp[4] & 0xC0) | bin2bcd(alm->time.tm_mday);
|
||||||
|
tmp[5] = (tmp[5] & 0xE0) | bin2bcd(alm->time.tm_mon + 1);
|
||||||
|
|
||||||
|
ret = mcp795_rtcc_write(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* enable alarm if requested */
|
||||||
|
if (alm->enabled) {
|
||||||
|
ret = mcp795_update_alarm(dev, true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
dev_dbg(dev, "Alarm IRQ armed\n");
|
||||||
|
}
|
||||||
|
dev_dbg(dev, "Set alarm: %02d-%02d(%d) %02d:%02d:%02d\n",
|
||||||
|
alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday,
|
||||||
|
alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mcp795_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||||
|
{
|
||||||
|
u8 data[6];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mcp795_rtcc_read(
|
||||||
|
dev, MCP795_REG_ALM0_SECONDS, data, sizeof(data));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
alm->time.tm_sec = bcd2bin(data[0] & 0x7F);
|
||||||
|
alm->time.tm_min = bcd2bin(data[1] & 0x7F);
|
||||||
|
alm->time.tm_hour = bcd2bin(data[2] & 0x1F);
|
||||||
|
alm->time.tm_wday = bcd2bin(data[3] & 0x07) - 1;
|
||||||
|
alm->time.tm_mday = bcd2bin(data[4] & 0x3F);
|
||||||
|
alm->time.tm_mon = bcd2bin(data[5] & 0x1F) - 1;
|
||||||
|
alm->time.tm_year = -1;
|
||||||
|
alm->time.tm_isdst = -1;
|
||||||
|
alm->time.tm_yday = -1;
|
||||||
|
|
||||||
|
dev_dbg(dev, "Read alarm: %02d-%02d(%d) %02d:%02d:%02d\n",
|
||||||
|
alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday,
|
||||||
|
alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mcp795_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||||
|
{
|
||||||
|
return mcp795_update_alarm(dev, !!enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t mcp795_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = data;
|
||||||
|
struct rtc_device *rtc = spi_get_drvdata(spi);
|
||||||
|
struct mutex *lock = &rtc->ops_lock;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(lock);
|
||||||
|
|
||||||
|
/* Disable alarm.
|
||||||
|
* There is no need to clear ALM0IF (Alarm 0 Interrupt Flag) bit,
|
||||||
|
* because it is done every time when alarm is enabled.
|
||||||
|
*/
|
||||||
|
ret = mcp795_update_alarm(&spi->dev, false);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"Failed to disable alarm in IRQ (ret=%d)\n", ret);
|
||||||
|
rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
|
||||||
|
|
||||||
|
mutex_unlock(lock);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct rtc_class_ops mcp795_rtc_ops = {
|
static const struct rtc_class_ops mcp795_rtc_ops = {
|
||||||
.read_time = mcp795_read_time,
|
.read_time = mcp795_read_time,
|
||||||
.set_time = mcp795_set_time
|
.set_time = mcp795_set_time,
|
||||||
|
.read_alarm = mcp795_read_alarm,
|
||||||
|
.set_alarm = mcp795_set_alarm,
|
||||||
|
.alarm_irq_enable = mcp795_alarm_irq_enable
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mcp795_probe(struct spi_device *spi)
|
static int mcp795_probe(struct spi_device *spi)
|
||||||
|
@ -259,6 +413,23 @@ static int mcp795_probe(struct spi_device *spi)
|
||||||
|
|
||||||
spi_set_drvdata(spi, rtc);
|
spi_set_drvdata(spi, rtc);
|
||||||
|
|
||||||
|
if (spi->irq > 0) {
|
||||||
|
dev_dbg(&spi->dev, "Alarm support enabled\n");
|
||||||
|
|
||||||
|
/* Clear any pending alarm (ALM0IF bit) before requesting
|
||||||
|
* the interrupt.
|
||||||
|
*/
|
||||||
|
mcp795_rtcc_set_bits(&spi->dev, MCP795_REG_ALM0_DAY,
|
||||||
|
MCP795_ALM0IF_BIT, 0);
|
||||||
|
ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
|
||||||
|
mcp795_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
|
dev_name(&rtc->dev), spi);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&spi->dev, "Failed to request IRQ: %d: %d\n",
|
||||||
|
spi->irq, ret);
|
||||||
|
else
|
||||||
|
device_init_wakeup(&spi->dev, true);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -353,7 +353,7 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RTC layer */
|
/* RTC layer */
|
||||||
static struct rtc_class_ops mxc_rtc_ops = {
|
static const struct rtc_class_ops mxc_rtc_ops = {
|
||||||
.release = mxc_rtc_release,
|
.release = mxc_rtc_release,
|
||||||
.read_time = mxc_rtc_read_time,
|
.read_time = mxc_rtc_read_time,
|
||||||
.set_mmss64 = mxc_rtc_set_mmss,
|
.set_mmss64 = mxc_rtc_set_mmss,
|
||||||
|
|
|
@ -52,9 +52,20 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
|
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
|
||||||
unsigned char buf[10];
|
unsigned char buf[10];
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, buf,
|
for (i = 0; i <= PCF2127_REG_CTRL3; i++) {
|
||||||
sizeof(buf));
|
ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1 + i,
|
||||||
|
(unsigned int *)(buf + i));
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "%s: read error\n", __func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_SC,
|
||||||
|
(buf + PCF2127_REG_SC),
|
||||||
|
ARRAY_SIZE(buf) - PCF2127_REG_SC);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "%s: read error\n", __func__);
|
dev_err(dev, "%s: read error\n", __func__);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -63,7 +63,6 @@ struct rx8010_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
u8 ctrlreg;
|
u8 ctrlreg;
|
||||||
spinlock_t flags_lock;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
||||||
|
@ -72,12 +71,12 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
||||||
struct rx8010_data *rx8010 = i2c_get_clientdata(client);
|
struct rx8010_data *rx8010 = i2c_get_clientdata(client);
|
||||||
int flagreg;
|
int flagreg;
|
||||||
|
|
||||||
spin_lock(&rx8010->flags_lock);
|
mutex_lock(&rx8010->rtc->ops_lock);
|
||||||
|
|
||||||
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
||||||
|
|
||||||
if (flagreg <= 0) {
|
if (flagreg <= 0) {
|
||||||
spin_unlock(&rx8010->flags_lock);
|
mutex_unlock(&rx8010->rtc->ops_lock);
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +100,7 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
||||||
|
|
||||||
i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
|
i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
|
||||||
|
|
||||||
spin_unlock(&rx8010->flags_lock);
|
mutex_unlock(&rx8010->rtc->ops_lock);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +142,6 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
|
||||||
u8 date[7];
|
u8 date[7];
|
||||||
int ctrl, flagreg;
|
int ctrl, flagreg;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long irqflags;
|
|
||||||
|
|
||||||
if ((dt->tm_year < 100) || (dt->tm_year > 199))
|
if ((dt->tm_year < 100) || (dt->tm_year > 199))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -181,11 +179,8 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&rx8010->flags_lock, irqflags);
|
|
||||||
|
|
||||||
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
||||||
if (flagreg < 0) {
|
if (flagreg < 0) {
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
return flagreg;
|
return flagreg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +188,6 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
|
||||||
ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG,
|
ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG,
|
||||||
flagreg & ~RX8010_FLAG_VLF);
|
flagreg & ~RX8010_FLAG_VLF);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,12 +281,9 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
u8 alarmvals[3];
|
u8 alarmvals[3];
|
||||||
int extreg, flagreg;
|
int extreg, flagreg;
|
||||||
int err;
|
int err;
|
||||||
unsigned long irqflags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&rx8010->flags_lock, irqflags);
|
|
||||||
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
||||||
if (flagreg < 0) {
|
if (flagreg < 0) {
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
return flagreg;
|
return flagreg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,14 +292,12 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
||||||
rx8010->ctrlreg);
|
rx8010->ctrlreg);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flagreg &= ~RX8010_FLAG_AF;
|
flagreg &= ~RX8010_FLAG_AF;
|
||||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
|
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -404,7 +392,6 @@ static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||||
int ret, tmp;
|
int ret, tmp;
|
||||||
int flagreg;
|
int flagreg;
|
||||||
unsigned long irqflags;
|
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case RTC_VL_READ:
|
case RTC_VL_READ:
|
||||||
|
@ -419,16 +406,13 @@ static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case RTC_VL_CLR:
|
case RTC_VL_CLR:
|
||||||
spin_lock_irqsave(&rx8010->flags_lock, irqflags);
|
|
||||||
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
||||||
if (flagreg < 0) {
|
if (flagreg < 0) {
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
return flagreg;
|
return flagreg;
|
||||||
}
|
}
|
||||||
|
|
||||||
flagreg &= ~RX8010_FLAG_VLF;
|
flagreg &= ~RX8010_FLAG_VLF;
|
||||||
ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
|
ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
|
||||||
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -466,8 +450,6 @@ static int rx8010_probe(struct i2c_client *client,
|
||||||
rx8010->client = client;
|
rx8010->client = client;
|
||||||
i2c_set_clientdata(client, rx8010);
|
i2c_set_clientdata(client, rx8010);
|
||||||
|
|
||||||
spin_lock_init(&rx8010->flags_lock);
|
|
||||||
|
|
||||||
err = rx8010_init_client(client);
|
err = rx8010_init_client(client);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -535,7 +535,7 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rtc_class_ops sh_rtc_ops = {
|
static const struct rtc_class_ops sh_rtc_ops = {
|
||||||
.read_time = sh_rtc_read_time,
|
.read_time = sh_rtc_read_time,
|
||||||
.set_time = sh_rtc_set_time,
|
.set_time = sh_rtc_set_time,
|
||||||
.read_alarm = sh_rtc_read_alarm,
|
.read_alarm = sh_rtc_read_alarm,
|
||||||
|
|
|
@ -184,6 +184,7 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
rtc_tm_to_time(alrm_tm, &time);
|
rtc_tm_to_time(alrm_tm, &time);
|
||||||
|
|
||||||
regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
|
regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
|
||||||
|
rtc_write_sync_lp(data);
|
||||||
regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
|
regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
|
||||||
|
|
||||||
/* Clear alarm interrupt status bit */
|
/* Clear alarm interrupt status bit */
|
||||||
|
|
|
@ -0,0 +1,725 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Amelie Delaunay 2016
|
||||||
|
* Author: Amelie Delaunay <amelie.delaunay@st.com>
|
||||||
|
* License terms: GNU General Public License (GPL), version 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bcd.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "stm32_rtc"
|
||||||
|
|
||||||
|
/* STM32 RTC registers */
|
||||||
|
#define STM32_RTC_TR 0x00
|
||||||
|
#define STM32_RTC_DR 0x04
|
||||||
|
#define STM32_RTC_CR 0x08
|
||||||
|
#define STM32_RTC_ISR 0x0C
|
||||||
|
#define STM32_RTC_PRER 0x10
|
||||||
|
#define STM32_RTC_ALRMAR 0x1C
|
||||||
|
#define STM32_RTC_WPR 0x24
|
||||||
|
|
||||||
|
/* STM32_RTC_TR bit fields */
|
||||||
|
#define STM32_RTC_TR_SEC_SHIFT 0
|
||||||
|
#define STM32_RTC_TR_SEC GENMASK(6, 0)
|
||||||
|
#define STM32_RTC_TR_MIN_SHIFT 8
|
||||||
|
#define STM32_RTC_TR_MIN GENMASK(14, 8)
|
||||||
|
#define STM32_RTC_TR_HOUR_SHIFT 16
|
||||||
|
#define STM32_RTC_TR_HOUR GENMASK(21, 16)
|
||||||
|
|
||||||
|
/* STM32_RTC_DR bit fields */
|
||||||
|
#define STM32_RTC_DR_DATE_SHIFT 0
|
||||||
|
#define STM32_RTC_DR_DATE GENMASK(5, 0)
|
||||||
|
#define STM32_RTC_DR_MONTH_SHIFT 8
|
||||||
|
#define STM32_RTC_DR_MONTH GENMASK(12, 8)
|
||||||
|
#define STM32_RTC_DR_WDAY_SHIFT 13
|
||||||
|
#define STM32_RTC_DR_WDAY GENMASK(15, 13)
|
||||||
|
#define STM32_RTC_DR_YEAR_SHIFT 16
|
||||||
|
#define STM32_RTC_DR_YEAR GENMASK(23, 16)
|
||||||
|
|
||||||
|
/* STM32_RTC_CR bit fields */
|
||||||
|
#define STM32_RTC_CR_FMT BIT(6)
|
||||||
|
#define STM32_RTC_CR_ALRAE BIT(8)
|
||||||
|
#define STM32_RTC_CR_ALRAIE BIT(12)
|
||||||
|
|
||||||
|
/* STM32_RTC_ISR bit fields */
|
||||||
|
#define STM32_RTC_ISR_ALRAWF BIT(0)
|
||||||
|
#define STM32_RTC_ISR_INITS BIT(4)
|
||||||
|
#define STM32_RTC_ISR_RSF BIT(5)
|
||||||
|
#define STM32_RTC_ISR_INITF BIT(6)
|
||||||
|
#define STM32_RTC_ISR_INIT BIT(7)
|
||||||
|
#define STM32_RTC_ISR_ALRAF BIT(8)
|
||||||
|
|
||||||
|
/* STM32_RTC_PRER bit fields */
|
||||||
|
#define STM32_RTC_PRER_PRED_S_SHIFT 0
|
||||||
|
#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
|
||||||
|
#define STM32_RTC_PRER_PRED_A_SHIFT 16
|
||||||
|
#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
|
||||||
|
|
||||||
|
/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
|
||||||
|
#define STM32_RTC_ALRMXR_SEC_SHIFT 0
|
||||||
|
#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0)
|
||||||
|
#define STM32_RTC_ALRMXR_SEC_MASK BIT(7)
|
||||||
|
#define STM32_RTC_ALRMXR_MIN_SHIFT 8
|
||||||
|
#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8)
|
||||||
|
#define STM32_RTC_ALRMXR_MIN_MASK BIT(15)
|
||||||
|
#define STM32_RTC_ALRMXR_HOUR_SHIFT 16
|
||||||
|
#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16)
|
||||||
|
#define STM32_RTC_ALRMXR_PM BIT(22)
|
||||||
|
#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23)
|
||||||
|
#define STM32_RTC_ALRMXR_DATE_SHIFT 24
|
||||||
|
#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24)
|
||||||
|
#define STM32_RTC_ALRMXR_WDSEL BIT(30)
|
||||||
|
#define STM32_RTC_ALRMXR_WDAY_SHIFT 24
|
||||||
|
#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24)
|
||||||
|
#define STM32_RTC_ALRMXR_DATE_MASK BIT(31)
|
||||||
|
|
||||||
|
/* STM32_RTC_WPR key constants */
|
||||||
|
#define RTC_WPR_1ST_KEY 0xCA
|
||||||
|
#define RTC_WPR_2ND_KEY 0x53
|
||||||
|
#define RTC_WPR_WRONG_KEY 0xFF
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RTC registers are protected against parasitic write access.
|
||||||
|
* PWR_CR_DBP bit must be set to enable write access to RTC registers.
|
||||||
|
*/
|
||||||
|
/* STM32_PWR_CR */
|
||||||
|
#define PWR_CR 0x00
|
||||||
|
/* STM32_PWR_CR bit field */
|
||||||
|
#define PWR_CR_DBP BIT(8)
|
||||||
|
|
||||||
|
struct stm32_rtc {
|
||||||
|
struct rtc_device *rtc_dev;
|
||||||
|
void __iomem *base;
|
||||||
|
struct regmap *dbp;
|
||||||
|
struct clk *ck_rtc;
|
||||||
|
int irq_alarm;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
|
||||||
|
{
|
||||||
|
writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + STM32_RTC_WPR);
|
||||||
|
writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + STM32_RTC_WPR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
|
||||||
|
{
|
||||||
|
writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + STM32_RTC_WPR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
|
||||||
|
{
|
||||||
|
unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
if (!(isr & STM32_RTC_ISR_INITF)) {
|
||||||
|
isr |= STM32_RTC_ISR_INIT;
|
||||||
|
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It takes around 2 ck_rtc clock cycles to enter in
|
||||||
|
* initialization phase mode (and have INITF flag set). As
|
||||||
|
* slowest ck_rtc frequency may be 32kHz and highest should be
|
||||||
|
* 1MHz, we poll every 10 us with a timeout of 100ms.
|
||||||
|
*/
|
||||||
|
return readl_relaxed_poll_timeout_atomic(
|
||||||
|
rtc->base + STM32_RTC_ISR,
|
||||||
|
isr, (isr & STM32_RTC_ISR_INITF),
|
||||||
|
10, 100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
|
||||||
|
{
|
||||||
|
unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
isr &= ~STM32_RTC_ISR_INIT;
|
||||||
|
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
|
||||||
|
{
|
||||||
|
unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
isr &= ~STM32_RTC_ISR_RSF;
|
||||||
|
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for RSF to be set to ensure the calendar registers are
|
||||||
|
* synchronised, it takes around 2 ck_rtc clock cycles
|
||||||
|
*/
|
||||||
|
return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
|
||||||
|
isr,
|
||||||
|
(isr & STM32_RTC_ISR_RSF),
|
||||||
|
10, 100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
|
||||||
|
unsigned int isr, cr;
|
||||||
|
|
||||||
|
mutex_lock(&rtc->rtc_dev->ops_lock);
|
||||||
|
|
||||||
|
isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
|
||||||
|
cr = readl_relaxed(rtc->base + STM32_RTC_CR);
|
||||||
|
|
||||||
|
if ((isr & STM32_RTC_ISR_ALRAF) &&
|
||||||
|
(cr & STM32_RTC_CR_ALRAIE)) {
|
||||||
|
/* Alarm A flag - Alarm interrupt */
|
||||||
|
dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n");
|
||||||
|
|
||||||
|
/* Pass event to the kernel */
|
||||||
|
rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||||
|
|
||||||
|
/* Clear event flag, otherwise new events won't be received */
|
||||||
|
writel_relaxed(isr & ~STM32_RTC_ISR_ALRAF,
|
||||||
|
rtc->base + STM32_RTC_ISR);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&rtc->rtc_dev->ops_lock);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert rtc_time structure from bin to bcd format */
|
||||||
|
static void tm2bcd(struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
tm->tm_sec = bin2bcd(tm->tm_sec);
|
||||||
|
tm->tm_min = bin2bcd(tm->tm_min);
|
||||||
|
tm->tm_hour = bin2bcd(tm->tm_hour);
|
||||||
|
|
||||||
|
tm->tm_mday = bin2bcd(tm->tm_mday);
|
||||||
|
tm->tm_mon = bin2bcd(tm->tm_mon + 1);
|
||||||
|
tm->tm_year = bin2bcd(tm->tm_year - 100);
|
||||||
|
/*
|
||||||
|
* Number of days since Sunday
|
||||||
|
* - on kernel side, 0=Sunday...6=Saturday
|
||||||
|
* - on rtc side, 0=invalid,1=Monday...7=Sunday
|
||||||
|
*/
|
||||||
|
tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert rtc_time structure from bcd to bin format */
|
||||||
|
static void bcd2tm(struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
tm->tm_sec = bcd2bin(tm->tm_sec);
|
||||||
|
tm->tm_min = bcd2bin(tm->tm_min);
|
||||||
|
tm->tm_hour = bcd2bin(tm->tm_hour);
|
||||||
|
|
||||||
|
tm->tm_mday = bcd2bin(tm->tm_mday);
|
||||||
|
tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
|
||||||
|
tm->tm_year = bcd2bin(tm->tm_year) + 100;
|
||||||
|
/*
|
||||||
|
* Number of days since Sunday
|
||||||
|
* - on kernel side, 0=Sunday...6=Saturday
|
||||||
|
* - on rtc side, 0=invalid,1=Monday...7=Sunday
|
||||||
|
*/
|
||||||
|
tm->tm_wday %= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
unsigned int tr, dr;
|
||||||
|
|
||||||
|
/* Time and Date in BCD format */
|
||||||
|
tr = readl_relaxed(rtc->base + STM32_RTC_TR);
|
||||||
|
dr = readl_relaxed(rtc->base + STM32_RTC_DR);
|
||||||
|
|
||||||
|
tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
|
||||||
|
tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
|
||||||
|
tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
|
||||||
|
|
||||||
|
tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
|
||||||
|
tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
|
||||||
|
tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
|
||||||
|
tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
|
||||||
|
|
||||||
|
/* We don't report tm_yday and tm_isdst */
|
||||||
|
|
||||||
|
bcd2tm(tm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
unsigned int tr, dr;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
tm2bcd(tm);
|
||||||
|
|
||||||
|
/* Time in BCD format */
|
||||||
|
tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
|
||||||
|
((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
|
||||||
|
((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR);
|
||||||
|
|
||||||
|
/* Date in BCD format */
|
||||||
|
dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) |
|
||||||
|
((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) |
|
||||||
|
((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) |
|
||||||
|
((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY);
|
||||||
|
|
||||||
|
stm32_rtc_wpr_unlock(rtc);
|
||||||
|
|
||||||
|
ret = stm32_rtc_enter_init_mode(rtc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel_relaxed(tr, rtc->base + STM32_RTC_TR);
|
||||||
|
writel_relaxed(dr, rtc->base + STM32_RTC_DR);
|
||||||
|
|
||||||
|
stm32_rtc_exit_init_mode(rtc);
|
||||||
|
|
||||||
|
ret = stm32_rtc_wait_sync(rtc);
|
||||||
|
end:
|
||||||
|
stm32_rtc_wpr_lock(rtc);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
struct rtc_time *tm = &alrm->time;
|
||||||
|
unsigned int alrmar, cr, isr;
|
||||||
|
|
||||||
|
alrmar = readl_relaxed(rtc->base + STM32_RTC_ALRMAR);
|
||||||
|
cr = readl_relaxed(rtc->base + STM32_RTC_CR);
|
||||||
|
isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
|
||||||
|
/*
|
||||||
|
* Date/day doesn't matter in Alarm comparison so alarm
|
||||||
|
* triggers every day
|
||||||
|
*/
|
||||||
|
tm->tm_mday = -1;
|
||||||
|
tm->tm_wday = -1;
|
||||||
|
} else {
|
||||||
|
if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
|
||||||
|
/* Alarm is set to a day of week */
|
||||||
|
tm->tm_mday = -1;
|
||||||
|
tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
|
||||||
|
STM32_RTC_ALRMXR_WDAY_SHIFT;
|
||||||
|
tm->tm_wday %= 7;
|
||||||
|
} else {
|
||||||
|
/* Alarm is set to a day of month */
|
||||||
|
tm->tm_wday = -1;
|
||||||
|
tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
|
||||||
|
STM32_RTC_ALRMXR_DATE_SHIFT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
|
||||||
|
/* Hours don't matter in Alarm comparison */
|
||||||
|
tm->tm_hour = -1;
|
||||||
|
} else {
|
||||||
|
tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
|
||||||
|
STM32_RTC_ALRMXR_HOUR_SHIFT;
|
||||||
|
if (alrmar & STM32_RTC_ALRMXR_PM)
|
||||||
|
tm->tm_hour += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
|
||||||
|
/* Minutes don't matter in Alarm comparison */
|
||||||
|
tm->tm_min = -1;
|
||||||
|
} else {
|
||||||
|
tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
|
||||||
|
STM32_RTC_ALRMXR_MIN_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
|
||||||
|
/* Seconds don't matter in Alarm comparison */
|
||||||
|
tm->tm_sec = -1;
|
||||||
|
} else {
|
||||||
|
tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
|
||||||
|
STM32_RTC_ALRMXR_SEC_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bcd2tm(tm);
|
||||||
|
|
||||||
|
alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
|
||||||
|
alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
unsigned int isr, cr;
|
||||||
|
|
||||||
|
cr = readl_relaxed(rtc->base + STM32_RTC_CR);
|
||||||
|
|
||||||
|
stm32_rtc_wpr_unlock(rtc);
|
||||||
|
|
||||||
|
/* We expose Alarm A to the kernel */
|
||||||
|
if (enabled)
|
||||||
|
cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
|
||||||
|
else
|
||||||
|
cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
|
||||||
|
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
|
||||||
|
|
||||||
|
/* Clear event flag, otherwise new events won't be received */
|
||||||
|
isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
|
||||||
|
isr &= ~STM32_RTC_ISR_ALRAF;
|
||||||
|
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
|
||||||
|
|
||||||
|
stm32_rtc_wpr_lock(rtc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec;
|
||||||
|
unsigned int dr = readl_relaxed(rtc->base + STM32_RTC_DR);
|
||||||
|
unsigned int tr = readl_relaxed(rtc->base + STM32_RTC_TR);
|
||||||
|
|
||||||
|
cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
|
||||||
|
cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
|
||||||
|
cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
|
||||||
|
cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
|
||||||
|
cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
|
||||||
|
cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assuming current date is M-D-Y H:M:S.
|
||||||
|
* RTC alarm can't be set on a specific month and year.
|
||||||
|
* So the valid alarm range is:
|
||||||
|
* M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S
|
||||||
|
* with a specific case for December...
|
||||||
|
*/
|
||||||
|
if ((((tm->tm_year > cur_year) &&
|
||||||
|
(tm->tm_mon == 0x1) && (cur_mon == 0x12)) ||
|
||||||
|
((tm->tm_year == cur_year) &&
|
||||||
|
(tm->tm_mon <= cur_mon + 1))) &&
|
||||||
|
((tm->tm_mday > cur_day) ||
|
||||||
|
((tm->tm_mday == cur_day) &&
|
||||||
|
((tm->tm_hour > cur_hour) ||
|
||||||
|
((tm->tm_hour == cur_hour) && (tm->tm_min > cur_min)) ||
|
||||||
|
((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) &&
|
||||||
|
(tm->tm_sec >= cur_sec))))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
struct rtc_time *tm = &alrm->time;
|
||||||
|
unsigned int cr, isr, alrmar;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
tm2bcd(tm);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RTC alarm can't be set on a specific date, unless this date is
|
||||||
|
* up to the same day of month next month.
|
||||||
|
*/
|
||||||
|
if (stm32_rtc_valid_alrm(rtc, tm) < 0) {
|
||||||
|
dev_err(dev, "Alarm can be set only on upcoming month.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
alrmar = 0;
|
||||||
|
/* tm_year and tm_mon are not used because not supported by RTC */
|
||||||
|
alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
|
||||||
|
STM32_RTC_ALRMXR_DATE;
|
||||||
|
/* 24-hour format */
|
||||||
|
alrmar &= ~STM32_RTC_ALRMXR_PM;
|
||||||
|
alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
|
||||||
|
STM32_RTC_ALRMXR_HOUR;
|
||||||
|
alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
|
||||||
|
STM32_RTC_ALRMXR_MIN;
|
||||||
|
alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
|
||||||
|
STM32_RTC_ALRMXR_SEC;
|
||||||
|
|
||||||
|
stm32_rtc_wpr_unlock(rtc);
|
||||||
|
|
||||||
|
/* Disable Alarm */
|
||||||
|
cr = readl_relaxed(rtc->base + STM32_RTC_CR);
|
||||||
|
cr &= ~STM32_RTC_CR_ALRAE;
|
||||||
|
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Poll Alarm write flag to be sure that Alarm update is allowed: it
|
||||||
|
* takes around 2 ck_rtc clock cycles
|
||||||
|
*/
|
||||||
|
ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
|
||||||
|
isr,
|
||||||
|
(isr & STM32_RTC_ISR_ALRAWF),
|
||||||
|
10, 100000);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Alarm update not allowed\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write to Alarm register */
|
||||||
|
writel_relaxed(alrmar, rtc->base + STM32_RTC_ALRMAR);
|
||||||
|
|
||||||
|
if (alrm->enabled)
|
||||||
|
stm32_rtc_alarm_irq_enable(dev, 1);
|
||||||
|
else
|
||||||
|
stm32_rtc_alarm_irq_enable(dev, 0);
|
||||||
|
|
||||||
|
end:
|
||||||
|
stm32_rtc_wpr_lock(rtc);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rtc_class_ops stm32_rtc_ops = {
|
||||||
|
.read_time = stm32_rtc_read_time,
|
||||||
|
.set_time = stm32_rtc_set_time,
|
||||||
|
.read_alarm = stm32_rtc_read_alarm,
|
||||||
|
.set_alarm = stm32_rtc_set_alarm,
|
||||||
|
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id stm32_rtc_of_match[] = {
|
||||||
|
{ .compatible = "st,stm32-rtc" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
|
||||||
|
|
||||||
|
static int stm32_rtc_init(struct platform_device *pdev,
|
||||||
|
struct stm32_rtc *rtc)
|
||||||
|
{
|
||||||
|
unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
|
||||||
|
unsigned int rate;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
rate = clk_get_rate(rtc->ck_rtc);
|
||||||
|
|
||||||
|
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
|
||||||
|
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
|
||||||
|
pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
|
||||||
|
|
||||||
|
for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) {
|
||||||
|
pred_s = (rate / (pred_a + 1)) - 1;
|
||||||
|
|
||||||
|
if (((pred_s + 1) * (pred_a + 1)) == rate)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can't find a 1Hz, so give priority to RTC power consumption
|
||||||
|
* by choosing the higher possible value for prediv_a
|
||||||
|
*/
|
||||||
|
if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
|
||||||
|
pred_a = pred_a_max;
|
||||||
|
pred_s = (rate / (pred_a + 1)) - 1;
|
||||||
|
|
||||||
|
dev_warn(&pdev->dev, "ck_rtc is %s\n",
|
||||||
|
(rate < ((pred_a + 1) * (pred_s + 1))) ?
|
||||||
|
"fast" : "slow");
|
||||||
|
}
|
||||||
|
|
||||||
|
stm32_rtc_wpr_unlock(rtc);
|
||||||
|
|
||||||
|
ret = stm32_rtc_enter_init_mode(rtc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Can't enter in init mode. Prescaler config failed.\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
|
||||||
|
writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
|
||||||
|
prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
|
||||||
|
writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
|
||||||
|
|
||||||
|
/* Force 24h time format */
|
||||||
|
cr = readl_relaxed(rtc->base + STM32_RTC_CR);
|
||||||
|
cr &= ~STM32_RTC_CR_FMT;
|
||||||
|
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
|
||||||
|
|
||||||
|
stm32_rtc_exit_init_mode(rtc);
|
||||||
|
|
||||||
|
ret = stm32_rtc_wait_sync(rtc);
|
||||||
|
end:
|
||||||
|
stm32_rtc_wpr_lock(rtc);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc;
|
||||||
|
struct resource *res;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
||||||
|
if (!rtc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
rtc->base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(rtc->base))
|
||||||
|
return PTR_ERR(rtc->base);
|
||||||
|
|
||||||
|
rtc->dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||||
|
"st,syscfg");
|
||||||
|
if (IS_ERR(rtc->dbp)) {
|
||||||
|
dev_err(&pdev->dev, "no st,syscfg\n");
|
||||||
|
return PTR_ERR(rtc->dbp);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(rtc->ck_rtc)) {
|
||||||
|
dev_err(&pdev->dev, "no ck_rtc clock");
|
||||||
|
return PTR_ERR(rtc->ck_rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rtc->ck_rtc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After a system reset, RTC_ISR.INITS flag can be read to check if
|
||||||
|
* the calendar has been initalized or not. INITS flag is reset by a
|
||||||
|
* power-on reset (no vbat, no power-supply). It is not reset if
|
||||||
|
* ck_rtc parent clock has changed (so RTC prescalers need to be
|
||||||
|
* changed). That's why we cannot rely on this flag to know if RTC
|
||||||
|
* init has to be done.
|
||||||
|
*/
|
||||||
|
ret = stm32_rtc_init(pdev, rtc);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
rtc->irq_alarm = platform_get_irq(pdev, 0);
|
||||||
|
if (rtc->irq_alarm <= 0) {
|
||||||
|
dev_err(&pdev->dev, "no alarm irq\n");
|
||||||
|
ret = rtc->irq_alarm;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, rtc);
|
||||||
|
|
||||||
|
ret = device_init_wakeup(&pdev->dev, true);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"alarm won't be able to wake up the system");
|
||||||
|
|
||||||
|
rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||||
|
&stm32_rtc_ops, THIS_MODULE);
|
||||||
|
if (IS_ERR(rtc->rtc_dev)) {
|
||||||
|
ret = PTR_ERR(rtc->rtc_dev);
|
||||||
|
dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
|
||||||
|
ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle RTC alarm interrupts */
|
||||||
|
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
|
||||||
|
stm32_rtc_alarm_irq,
|
||||||
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||||
|
pdev->name, rtc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
|
||||||
|
rtc->irq_alarm);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If INITS flag is reset (calendar year field set to 0x00), calendar
|
||||||
|
* must be initialized
|
||||||
|
*/
|
||||||
|
if (!(readl_relaxed(rtc->base + STM32_RTC_ISR) & STM32_RTC_ISR_INITS))
|
||||||
|
dev_warn(&pdev->dev, "Date/Time must be initialized\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
clk_disable_unprepare(rtc->ck_rtc);
|
||||||
|
|
||||||
|
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
|
||||||
|
|
||||||
|
device_init_wakeup(&pdev->dev, false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = platform_get_drvdata(pdev);
|
||||||
|
unsigned int cr;
|
||||||
|
|
||||||
|
/* Disable interrupts */
|
||||||
|
stm32_rtc_wpr_unlock(rtc);
|
||||||
|
cr = readl_relaxed(rtc->base + STM32_RTC_CR);
|
||||||
|
cr &= ~STM32_RTC_CR_ALRAIE;
|
||||||
|
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
|
||||||
|
stm32_rtc_wpr_lock(rtc);
|
||||||
|
|
||||||
|
clk_disable_unprepare(rtc->ck_rtc);
|
||||||
|
|
||||||
|
/* Enable backup domain write protection */
|
||||||
|
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
|
||||||
|
|
||||||
|
device_init_wakeup(&pdev->dev, false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int stm32_rtc_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
return enable_irq_wake(rtc->irq_alarm);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_rtc_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct stm32_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = stm32_rtc_wait_sync(rtc);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
return disable_irq_wake(rtc->irq_alarm);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops,
|
||||||
|
stm32_rtc_suspend, stm32_rtc_resume);
|
||||||
|
|
||||||
|
static struct platform_driver stm32_rtc_driver = {
|
||||||
|
.probe = stm32_rtc_probe,
|
||||||
|
.remove = stm32_rtc_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.pm = &stm32_rtc_pm_ops,
|
||||||
|
.of_match_table = stm32_rtc_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(stm32_rtc_driver);
|
||||||
|
|
||||||
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||||
|
MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -20,6 +20,8 @@
|
||||||
* more details.
|
* more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
@ -33,15 +35,20 @@
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
/* Control register */
|
/* Control register */
|
||||||
#define SUN6I_LOSC_CTRL 0x0000
|
#define SUN6I_LOSC_CTRL 0x0000
|
||||||
|
#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16)
|
||||||
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
|
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
|
||||||
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
|
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
|
||||||
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
|
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
|
||||||
|
#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0)
|
||||||
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
|
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
|
||||||
|
|
||||||
|
#define SUN6I_LOSC_CLK_PRESCAL 0x0008
|
||||||
|
|
||||||
/* RTC */
|
/* RTC */
|
||||||
#define SUN6I_RTC_YMD 0x0010
|
#define SUN6I_RTC_YMD 0x0010
|
||||||
#define SUN6I_RTC_HMS 0x0014
|
#define SUN6I_RTC_HMS 0x0014
|
||||||
|
@ -114,13 +121,142 @@ struct sun6i_rtc_dev {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq;
|
int irq;
|
||||||
unsigned long alarm;
|
unsigned long alarm;
|
||||||
|
|
||||||
|
struct clk_hw hw;
|
||||||
|
struct clk_hw *int_osc;
|
||||||
|
struct clk *losc;
|
||||||
|
|
||||||
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct sun6i_rtc_dev *sun6i_rtc;
|
||||||
|
|
||||||
|
static unsigned long sun6i_rtc_osc_recalc_rate(struct clk_hw *hw,
|
||||||
|
unsigned long parent_rate)
|
||||||
|
{
|
||||||
|
struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(rtc->base + SUN6I_LOSC_CTRL);
|
||||||
|
if (val & SUN6I_LOSC_CTRL_EXT_OSC)
|
||||||
|
return parent_rate;
|
||||||
|
|
||||||
|
val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL);
|
||||||
|
val &= GENMASK(4, 0);
|
||||||
|
|
||||||
|
return parent_rate / (val + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 sun6i_rtc_osc_get_parent(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);
|
||||||
|
|
||||||
|
return readl(rtc->base + SUN6I_LOSC_CTRL) & SUN6I_LOSC_CTRL_EXT_OSC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index)
|
||||||
|
{
|
||||||
|
struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw);
|
||||||
|
unsigned long flags;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
if (index > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rtc->lock, flags);
|
||||||
|
val = readl(rtc->base + SUN6I_LOSC_CTRL);
|
||||||
|
val &= ~SUN6I_LOSC_CTRL_EXT_OSC;
|
||||||
|
val |= SUN6I_LOSC_CTRL_KEY;
|
||||||
|
val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0;
|
||||||
|
writel(val, rtc->base + SUN6I_LOSC_CTRL);
|
||||||
|
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct clk_ops sun6i_rtc_osc_ops = {
|
||||||
|
.recalc_rate = sun6i_rtc_osc_recalc_rate,
|
||||||
|
|
||||||
|
.get_parent = sun6i_rtc_osc_get_parent,
|
||||||
|
.set_parent = sun6i_rtc_osc_set_parent,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init sun6i_rtc_clk_init(struct device_node *node)
|
||||||
|
{
|
||||||
|
struct clk_hw_onecell_data *clk_data;
|
||||||
|
struct sun6i_rtc_dev *rtc;
|
||||||
|
struct clk_init_data init = {
|
||||||
|
.ops = &sun6i_rtc_osc_ops,
|
||||||
|
};
|
||||||
|
const char *parents[2];
|
||||||
|
|
||||||
|
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
|
||||||
|
if (!rtc)
|
||||||
|
return;
|
||||||
|
spin_lock_init(&rtc->lock);
|
||||||
|
|
||||||
|
clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!clk_data)
|
||||||
|
return;
|
||||||
|
spin_lock_init(&rtc->lock);
|
||||||
|
|
||||||
|
rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node));
|
||||||
|
if (IS_ERR(rtc->base)) {
|
||||||
|
pr_crit("Can't map RTC registers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch to the external, more precise, oscillator */
|
||||||
|
writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC,
|
||||||
|
rtc->base + SUN6I_LOSC_CTRL);
|
||||||
|
|
||||||
|
/* Yes, I know, this is ugly. */
|
||||||
|
sun6i_rtc = rtc;
|
||||||
|
|
||||||
|
/* Deal with old DTs */
|
||||||
|
if (!of_get_property(node, "clocks", NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL,
|
||||||
|
"rtc-int-osc",
|
||||||
|
NULL, 0,
|
||||||
|
667000,
|
||||||
|
300000000);
|
||||||
|
if (IS_ERR(rtc->int_osc)) {
|
||||||
|
pr_crit("Couldn't register the internal oscillator\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parents[0] = clk_hw_get_name(rtc->int_osc);
|
||||||
|
parents[1] = of_clk_get_parent_name(node, 0);
|
||||||
|
|
||||||
|
rtc->hw.init = &init;
|
||||||
|
|
||||||
|
init.parent_names = parents;
|
||||||
|
init.num_parents = of_clk_get_parent_count(node) + 1;
|
||||||
|
of_property_read_string(node, "clock-output-names", &init.name);
|
||||||
|
|
||||||
|
rtc->losc = clk_register(NULL, &rtc->hw);
|
||||||
|
if (IS_ERR(rtc->losc)) {
|
||||||
|
pr_crit("Couldn't register the LOSC clock\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_data->num = 1;
|
||||||
|
clk_data->hws[0] = &rtc->hw;
|
||||||
|
of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
|
||||||
|
}
|
||||||
|
CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc",
|
||||||
|
sun6i_rtc_clk_init);
|
||||||
|
|
||||||
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
|
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
|
||||||
{
|
{
|
||||||
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
|
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
|
||||||
|
irqreturn_t ret = IRQ_NONE;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
|
spin_lock(&chip->lock);
|
||||||
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
|
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
|
||||||
|
|
||||||
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
|
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
|
||||||
|
@ -129,10 +265,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
|
||||||
|
|
||||||
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
|
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
spin_unlock(&chip->lock);
|
||||||
|
|
||||||
return IRQ_NONE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
||||||
|
@ -140,6 +277,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
||||||
u32 alrm_val = 0;
|
u32 alrm_val = 0;
|
||||||
u32 alrm_irq_val = 0;
|
u32 alrm_irq_val = 0;
|
||||||
u32 alrm_wake_val = 0;
|
u32 alrm_wake_val = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
alrm_val = SUN6I_ALRM_EN_CNT_EN;
|
alrm_val = SUN6I_ALRM_EN_CNT_EN;
|
||||||
|
@ -150,9 +288,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
|
||||||
chip->base + SUN6I_ALRM_IRQ_STA);
|
chip->base + SUN6I_ALRM_IRQ_STA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chip->lock, flags);
|
||||||
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
|
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
|
||||||
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
|
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
|
||||||
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
|
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
|
||||||
|
spin_unlock_irqrestore(&chip->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
||||||
|
@ -191,11 +331,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
||||||
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||||
{
|
{
|
||||||
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
|
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
|
||||||
|
unsigned long flags;
|
||||||
u32 alrm_st;
|
u32 alrm_st;
|
||||||
u32 alrm_en;
|
u32 alrm_en;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chip->lock, flags);
|
||||||
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
|
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
|
||||||
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
|
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
|
||||||
|
spin_unlock_irqrestore(&chip->lock, flags);
|
||||||
|
|
||||||
wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
|
wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
|
||||||
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
|
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
|
||||||
rtc_time_to_tm(chip->alarm, &wkalrm->time);
|
rtc_time_to_tm(chip->alarm, &wkalrm->time);
|
||||||
|
@ -349,22 +493,15 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
|
||||||
|
|
||||||
static int sun6i_rtc_probe(struct platform_device *pdev)
|
static int sun6i_rtc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sun6i_rtc_dev *chip;
|
struct sun6i_rtc_dev *chip = sun6i_rtc;
|
||||||
struct resource *res;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
|
||||||
if (!chip)
|
if (!chip)
|
||||||
return -ENOMEM;
|
return -ENODEV;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, chip);
|
platform_set_drvdata(pdev, chip);
|
||||||
chip->dev = &pdev->dev;
|
chip->dev = &pdev->dev;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
chip->base = devm_ioremap_resource(&pdev->dev, res);
|
|
||||||
if (IS_ERR(chip->base))
|
|
||||||
return PTR_ERR(chip->base);
|
|
||||||
|
|
||||||
chip->irq = platform_get_irq(pdev, 0);
|
chip->irq = platform_get_irq(pdev, 0);
|
||||||
if (chip->irq < 0) {
|
if (chip->irq < 0) {
|
||||||
dev_err(&pdev->dev, "No IRQ resource\n");
|
dev_err(&pdev->dev, "No IRQ resource\n");
|
||||||
|
@ -404,8 +541,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
|
||||||
/* disable alarm wakeup */
|
/* disable alarm wakeup */
|
||||||
writel(0, chip->base + SUN6I_ALARM_CONFIG);
|
writel(0, chip->base + SUN6I_ALARM_CONFIG);
|
||||||
|
|
||||||
chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
|
clk_prepare_enable(chip->losc);
|
||||||
&sun6i_rtc_ops, THIS_MODULE);
|
|
||||||
|
chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-sun6i",
|
||||||
|
&sun6i_rtc_ops, THIS_MODULE);
|
||||||
if (IS_ERR(chip->rtc)) {
|
if (IS_ERR(chip->rtc)) {
|
||||||
dev_err(&pdev->dev, "unable to register device\n");
|
dev_err(&pdev->dev, "unable to register device\n");
|
||||||
return PTR_ERR(chip->rtc);
|
return PTR_ERR(chip->rtc);
|
||||||
|
@ -416,15 +555,6 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun6i_rtc_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
rtc_device_unregister(chip->rtc);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id sun6i_rtc_dt_ids[] = {
|
static const struct of_device_id sun6i_rtc_dt_ids[] = {
|
||||||
{ .compatible = "allwinner,sun6i-a31-rtc" },
|
{ .compatible = "allwinner,sun6i-a31-rtc" },
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
|
@ -433,15 +563,9 @@ MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
|
||||||
|
|
||||||
static struct platform_driver sun6i_rtc_driver = {
|
static struct platform_driver sun6i_rtc_driver = {
|
||||||
.probe = sun6i_rtc_probe,
|
.probe = sun6i_rtc_probe,
|
||||||
.remove = sun6i_rtc_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sun6i-rtc",
|
.name = "sun6i-rtc",
|
||||||
.of_match_table = sun6i_rtc_dt_ids,
|
.of_match_table = sun6i_rtc_dt_ids,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
builtin_platform_driver(sun6i_rtc_driver);
|
||||||
module_platform_driver(sun6i_rtc_driver);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("sun6i RTC driver");
|
|
||||||
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
|
@ -17,16 +17,18 @@
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/init.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
/* set to 1 = busy every eight 32kHz clocks during copy of sec+msec to AHB */
|
/* set to 1 = busy every eight 32kHz clocks during copy of sec+msec to AHB */
|
||||||
#define TEGRA_RTC_REG_BUSY 0x004
|
#define TEGRA_RTC_REG_BUSY 0x004
|
||||||
|
@ -59,6 +61,7 @@ struct tegra_rtc_info {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct rtc_device *rtc_dev;
|
struct rtc_device *rtc_dev;
|
||||||
void __iomem *rtc_base; /* NULL if not initialized. */
|
void __iomem *rtc_base; /* NULL if not initialized. */
|
||||||
|
struct clk *clk;
|
||||||
int tegra_rtc_irq; /* alarm and periodic irq */
|
int tegra_rtc_irq; /* alarm and periodic irq */
|
||||||
spinlock_t tegra_rtc_lock;
|
spinlock_t tegra_rtc_lock;
|
||||||
};
|
};
|
||||||
|
@ -326,6 +329,14 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
|
||||||
if (info->tegra_rtc_irq <= 0)
|
if (info->tegra_rtc_irq <= 0)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
|
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(info->clk))
|
||||||
|
return PTR_ERR(info->clk);
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(info->clk);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* set context info. */
|
/* set context info. */
|
||||||
info->pdev = pdev;
|
info->pdev = pdev;
|
||||||
spin_lock_init(&info->tegra_rtc_lock);
|
spin_lock_init(&info->tegra_rtc_lock);
|
||||||
|
@ -346,7 +357,7 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
|
||||||
ret = PTR_ERR(info->rtc_dev);
|
ret = PTR_ERR(info->rtc_dev);
|
||||||
dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
|
dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
goto disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
|
ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
|
||||||
|
@ -356,11 +367,24 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to request interrupt for device (err=%d).\n",
|
"Unable to request interrupt for device (err=%d).\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
goto disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
|
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
disable_clk:
|
||||||
|
clk_disable_unprepare(info->clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_rtc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct tegra_rtc_info *info = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
clk_disable_unprepare(info->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +437,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev)
|
||||||
|
|
||||||
MODULE_ALIAS("platform:tegra_rtc");
|
MODULE_ALIAS("platform:tegra_rtc");
|
||||||
static struct platform_driver tegra_rtc_driver = {
|
static struct platform_driver tegra_rtc_driver = {
|
||||||
|
.remove = tegra_rtc_remove,
|
||||||
.shutdown = tegra_rtc_shutdown,
|
.shutdown = tegra_rtc_shutdown,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "tegra_rtc",
|
.name = "tegra_rtc",
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
#include <linux/bcd.h>
|
#include <linux/bcd.h>
|
||||||
|
#include <linux/math64.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/mfd/tps65910.h>
|
#include <linux/mfd/tps65910.h>
|
||||||
|
@ -33,7 +34,21 @@ struct tps65910_rtc {
|
||||||
/* Total number of RTC registers needed to set time*/
|
/* Total number of RTC registers needed to set time*/
|
||||||
#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1)
|
#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1)
|
||||||
|
|
||||||
static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
|
/* Total number of RTC registers needed to set compensation registers */
|
||||||
|
#define NUM_COMP_REGS (TPS65910_RTC_COMP_MSB - TPS65910_RTC_COMP_LSB + 1)
|
||||||
|
|
||||||
|
/* Min and max values supported with 'offset' interface (swapped sign) */
|
||||||
|
#define MIN_OFFSET (-277761)
|
||||||
|
#define MAX_OFFSET (277778)
|
||||||
|
|
||||||
|
/* Number of ticks per hour */
|
||||||
|
#define TICKS_PER_HOUR (32768 * 3600)
|
||||||
|
|
||||||
|
/* Multiplier for ppb conversions */
|
||||||
|
#define PPB_MULT (1000000000LL)
|
||||||
|
|
||||||
|
static int tps65910_rtc_alarm_irq_enable(struct device *dev,
|
||||||
|
unsigned int enabled)
|
||||||
{
|
{
|
||||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||||
u8 val = 0;
|
u8 val = 0;
|
||||||
|
@ -187,6 +202,133 @@ static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tps65910_rtc_set_calibration(struct device *dev, int calibration)
|
||||||
|
{
|
||||||
|
unsigned char comp_data[NUM_COMP_REGS];
|
||||||
|
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||||
|
s16 value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TPS65910 uses two's complement 16 bit value for compensation for RTC
|
||||||
|
* crystal inaccuracies. One time every hour when seconds counter
|
||||||
|
* increments from 0 to 1 compensation value will be added to internal
|
||||||
|
* RTC counter value.
|
||||||
|
*
|
||||||
|
* Compensation value 0x7FFF is prohibited value.
|
||||||
|
*
|
||||||
|
* Valid range for compensation value: [-32768 .. 32766]
|
||||||
|
*/
|
||||||
|
if ((calibration < -32768) || (calibration > 32766)) {
|
||||||
|
dev_err(dev, "RTC calibration value out of range: %d\n",
|
||||||
|
calibration);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (s16)calibration;
|
||||||
|
|
||||||
|
comp_data[0] = (u16)value & 0xFF;
|
||||||
|
comp_data[1] = ((u16)value >> 8) & 0xFF;
|
||||||
|
|
||||||
|
/* Update all the compensation registers in one shot */
|
||||||
|
ret = regmap_bulk_write(tps->regmap, TPS65910_RTC_COMP_LSB,
|
||||||
|
comp_data, NUM_COMP_REGS);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "rtc_set_calibration error: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable automatic compensation */
|
||||||
|
ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
|
||||||
|
TPS65910_RTC_CTRL_AUTO_COMP, TPS65910_RTC_CTRL_AUTO_COMP);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(dev, "auto_comp enable failed with error: %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tps65910_rtc_get_calibration(struct device *dev, int *calibration)
|
||||||
|
{
|
||||||
|
unsigned char comp_data[NUM_COMP_REGS];
|
||||||
|
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||||
|
unsigned int ctrl;
|
||||||
|
u16 value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_read(tps->regmap, TPS65910_RTC_CTRL, &ctrl);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* If automatic compensation is not enabled report back zero */
|
||||||
|
if (!(ctrl & TPS65910_RTC_CTRL_AUTO_COMP)) {
|
||||||
|
*calibration = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_bulk_read(tps->regmap, TPS65910_RTC_COMP_LSB, comp_data,
|
||||||
|
NUM_COMP_REGS);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "rtc_get_calibration error: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (u16)comp_data[0] | ((u16)comp_data[1] << 8);
|
||||||
|
|
||||||
|
*calibration = (s16)value;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tps65910_read_offset(struct device *dev, long *offset)
|
||||||
|
{
|
||||||
|
int calibration;
|
||||||
|
s64 tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = tps65910_rtc_get_calibration(dev, &calibration);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Convert from RTC calibration register format to ppb format */
|
||||||
|
tmp = calibration * (s64)PPB_MULT;
|
||||||
|
if (tmp < 0)
|
||||||
|
tmp -= TICKS_PER_HOUR / 2LL;
|
||||||
|
else
|
||||||
|
tmp += TICKS_PER_HOUR / 2LL;
|
||||||
|
tmp = div_s64(tmp, TICKS_PER_HOUR);
|
||||||
|
|
||||||
|
/* Offset value operates in negative way, so swap sign */
|
||||||
|
*offset = (long)-tmp;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tps65910_set_offset(struct device *dev, long offset)
|
||||||
|
{
|
||||||
|
int calibration;
|
||||||
|
s64 tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Make sure offset value is within supported range */
|
||||||
|
if (offset < MIN_OFFSET || offset > MAX_OFFSET)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
/* Convert from ppb format to RTC calibration register format */
|
||||||
|
tmp = offset * (s64)TICKS_PER_HOUR;
|
||||||
|
if (tmp < 0)
|
||||||
|
tmp -= PPB_MULT / 2LL;
|
||||||
|
else
|
||||||
|
tmp += PPB_MULT / 2LL;
|
||||||
|
tmp = div_s64(tmp, PPB_MULT);
|
||||||
|
|
||||||
|
/* Offset value operates in negative way, so swap sign */
|
||||||
|
calibration = (int)-tmp;
|
||||||
|
|
||||||
|
ret = tps65910_rtc_set_calibration(dev, calibration);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc)
|
static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc)
|
||||||
{
|
{
|
||||||
struct device *dev = rtc;
|
struct device *dev = rtc;
|
||||||
|
@ -219,6 +361,8 @@ static const struct rtc_class_ops tps65910_rtc_ops = {
|
||||||
.read_alarm = tps65910_rtc_read_alarm,
|
.read_alarm = tps65910_rtc_read_alarm,
|
||||||
.set_alarm = tps65910_rtc_set_alarm,
|
.set_alarm = tps65910_rtc_set_alarm,
|
||||||
.alarm_irq_enable = tps65910_rtc_alarm_irq_enable,
|
.alarm_irq_enable = tps65910_rtc_alarm_irq_enable,
|
||||||
|
.read_offset = tps65910_read_offset,
|
||||||
|
.set_offset = tps65910_set_offset,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tps65910_rtc_probe(struct platform_device *pdev)
|
static int tps65910_rtc_probe(struct platform_device *pdev)
|
||||||
|
|
|
@ -134,6 +134,7 @@
|
||||||
|
|
||||||
/* RTC_CTRL_REG bitfields */
|
/* RTC_CTRL_REG bitfields */
|
||||||
#define TPS65910_RTC_CTRL_STOP_RTC 0x01 /*0=stop, 1=run */
|
#define TPS65910_RTC_CTRL_STOP_RTC 0x01 /*0=stop, 1=run */
|
||||||
|
#define TPS65910_RTC_CTRL_AUTO_COMP 0x04
|
||||||
#define TPS65910_RTC_CTRL_GET_TIME 0x40
|
#define TPS65910_RTC_CTRL_GET_TIME 0x40
|
||||||
|
|
||||||
/* RTC_STATUS_REG bitfields */
|
/* RTC_STATUS_REG bitfields */
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* ST M48T86 / Dallas DS12887 RTC driver
|
|
||||||
* Copyright (c) 2006 Tower Technologies
|
|
||||||
*
|
|
||||||
* Author: Alessandro Zummo <a.zummo@towertech.it>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct m48t86_ops
|
|
||||||
{
|
|
||||||
void (*writebyte)(unsigned char value, unsigned long addr);
|
|
||||||
unsigned char (*readbyte)(unsigned long addr);
|
|
||||||
};
|
|
Загрузка…
Ссылка в новой задаче