Commit 5782fd14 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 45554b23 d4f6c6f1
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.
* 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:
- 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-names
- reg names: should contain:
......
* 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.
- interrupts: rtc alarm interrupt
Optional properties:
- interrupts: dryice security violation interrupt
Example:
rtc@80056000 {
compatible = "fsl,imx53-rtc", "fsl,imx25-rtc";
reg = <0x80056000 2000>;
interrupts = <29>;
interrupts = <29 56>;
};
* Maxim DS3231 Real Time Clock
Required properties:
see: Documentation/devicetree/bindings/i2c/trivial-admin-guide/devices.rst
- compatible: Should contain "maxim,ds3231".
- reg: I2C address for chip.
Optional property:
- #clock-cells: Should be 1.
......
......@@ -3,7 +3,8 @@
Philips PCF8563/Epson RTC8564 Real Time Clock
Required properties:
see: Documentation/devicetree/bindings/i2c/trivial-admin-guide/devices.rst
- compatible: Should contain "nxp,pcf8563".
- reg: I2C address for chip.
Optional property:
- #clock-cells: Should be 0.
......
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.
- 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:
rtc: rtc@01f00000 {
compatible = "allwinner,sun6i-a31-rtc";
reg = <0x01f00000 0x54>;
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/platform_device.h>
#include <linux/io.h>
#include <linux/platform_data/rtc-m48t86.h>
#include <linux/mtd/nand.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),
.length = TS72XX_OPTIONS2_SIZE,
.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)
{
__raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE);
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 resource ts72xx_rtc_resources[] = {
DEFINE_RES_MEM(TS72XX_RTC_INDEX_PHYS_BASE, 0x01),
DEFINE_RES_MEM(TS72XX_RTC_DATA_PHYS_BASE, 0x01),
};
static struct platform_device ts72xx_rtc_device = {
.name = "rtc-m48t86",
.id = -1,
.dev = {
.platform_data = &ts72xx_rtc_ops,
},
.num_resources = 0,
.resource = ts72xx_rtc_resources,
.num_resources = ARRAY_SIZE(ts72xx_rtc_resources),
};
static struct resource ts72xx_wdt_resources[] = {
......
......@@ -9,8 +9,6 @@
* febff000 22000000 4K model number register (bits 0-2)
* febfe000 22400000 4K options register
* 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
......@@ -40,15 +38,6 @@
#define TS72XX_OPTIONS2_TS9420 0x04
#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_FEED_PHYS_BASE 0x23c00000
......
......@@ -16,7 +16,6 @@
#include <linux/platform_device.h>
#include <linux/mv643xx_eth.h>
#include <linux/ata_platform.h>
#include <linux/platform_data/rtc-m48t86.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.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
****************************************************************************/
#define TS_RTC_CTRL (TS78XX_FPGA_REGS_VIRT_BASE + 0x808)
#define TS_RTC_DATA (TS78XX_FPGA_REGS_VIRT_BASE + 0x80c)
#define TS_RTC_CTRL (TS78XX_FPGA_REGS_PHYS_BASE + 0x808)
#define TS_RTC_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x80c)
static unsigned char ts78xx_ts_rtc_readbyte(unsigned long addr)
{
writeb(addr, TS_RTC_CTRL);
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 resource ts78xx_ts_rtc_resources[] = {
DEFINE_RES_MEM(TS_RTC_CTRL, 0x01),
DEFINE_RES_MEM(TS_RTC_DATA, 0x01),
};
static struct platform_device ts78xx_ts_rtc_device = {
.name = "rtc-m48t86",
.id = -1,
.dev = {
.platform_data = &ts78xx_ts_rtc_ops,
},
.num_resources = 0,
.resource = ts78xx_ts_rtc_resources,
.num_resources = ARRAY_SIZE(ts78xx_ts_rtc_resources),
};
/*
* 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)
{
int rc;
unsigned char tmp_rtc0, tmp_rtc1;
tmp_rtc0 = ts78xx_ts_rtc_readbyte(126);
tmp_rtc1 = ts78xx_ts_rtc_readbyte(127);
ts78xx_ts_rtc_writebyte(0x00, 126);
ts78xx_ts_rtc_writebyte(0x55, 127);
if (ts78xx_ts_rtc_readbyte(127) == 0x55) {
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
} 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 could not be registered: %d\n", rc);
pr_info("RTC not found\n");
return -ENODEV;
};
return rc;
}
static void ts78xx_ts_rtc_unload(void)
{
......
......@@ -1434,9 +1434,10 @@ config RTC_DRV_SUN4V
based RTC on SUN4V systems.
config RTC_DRV_SUN6I
tristate "Allwinner A31 RTC"
default MACH_SUN6I || MACH_SUN8I || COMPILE_TEST
depends on ARCH_SUNXI
bool "Allwinner A31 RTC"
default MACH_SUN6I || MACH_SUN8I
depends on COMMON_CLK
depends on ARCH_SUNXI || COMPILE_TEST
help
If you say Y here you will get support for the RTC found in
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
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"
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_STARFIRE) += rtc-starfire.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_ST_LPC) += rtc-st-lpc.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
......
This diff is collapsed.
......@@ -56,7 +56,7 @@ static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm)
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,
.set_time = au1xtoy_rtc_set_time,
};
......
......@@ -333,7 +333,7 @@ static int bfin_rtc_proc(struct device *dev, struct seq_file *seq)
#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,
.set_time = bfin_rtc_set_time,
.read_alarm = bfin_rtc_read_alarm,
......
......@@ -34,6 +34,7 @@
#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */
#define BQ32K_TCH2 0x08 /* Trickle charge enable */
#define BQ32K_CFG2 0x09 /* Trickle charger control */
#define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */
struct bq32k_regs {
uint8_t seconds;
......@@ -188,6 +189,65 @@ static int trickle_charger_of_init(struct device *dev, struct device_node *node)
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, &reg, 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, &reg, BQ32K_CFG2, 1);
if (error)
return error;
if (enable) {
reg |= BQ32K_TCFE;
error = bq32k_write(dev, &reg, BQ32K_CFG2, 1);
if (error)
return error;
dev_info(dev, "Enabled trickle charge FET bypass.\n");
} else {
reg &= ~BQ32K_TCFE;
error = bq32k_write(dev, &reg, 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,
const struct i2c_device_id *id)
{
......@@ -224,11 +284,26 @@ static int bq32k_probe(struct i2c_client *client,
if (IS_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);
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[] = {
{ "bq32000", 0 },
{ }
......@@ -240,6 +315,7 @@ static struct i2c_driver bq32k_driver = {
.name = "bq32k",
},
.probe = bq32k_probe,
.remove = bq32k_remove,
.id_table = bq32k_id,
};
......
......@@ -116,7 +116,7 @@ static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm)
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,
.set_time = dm355evm_rtc_set_time,
};
......
......@@ -363,6 +363,9 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
if (ret)
return ret;
if (ds3232->irq > 0)
device_init_wakeup(dev, 1);
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
THIS_MODULE);
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,
name, dev);
if (ret) {
device_set_wakeup_capable(dev, 0);
ds3232->irq = 0;
dev_err(dev, "unable to request IRQ\n");
} else
device_init_wakeup(dev, 1);
}
}
return 0;
......@@ -420,6 +423,7 @@ static int ds3232_i2c_probe(struct i2c_client *client,
static const struct regmap_config config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x13,
};
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 = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x13,
.write_flag_mask = 0x80,
};
struct regmap *regmap;
......
......@@ -159,9 +159,16 @@ static int gemini_rtc_remove(struct platform_device *pdev)
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 = {
.driver = {
.name = DRV_NAME,
.of_match_table = gemini_rtc_dt_match,
},
.probe = gemini_rtc_probe,
.remove = gemini_rtc_remove,
......
......@@ -108,7 +108,6 @@
* @pdev: pionter to platform dev
* @rtc: pointer to rtc struct
* @ioaddr: IO registers pointer
* @irq: dryice normal interrupt
* @clk: input reference clock
* @dsr: copy of the DSR register
* @irq_lock: interrupt enable register (DIER) lock
......@@ -120,7 +119,6 @@ struct imxdi_dev {
struct platform_device *pdev;
struct rtc_device *rtc;
void __iomem *ioaddr;
int irq;
struct clk *clk;
u32 dsr;
spinlock_t irq_lock;
......@@ -668,7 +666,7 @@ static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
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,
.set_mmss = dryice_rtc_set_mmss,
.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;
u32 dsr, dier;
......@@ -765,6 +763,7 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
struct imxdi_dev *imxdi;
int norm_irq, sec_irq;
int rc;
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);
imxdi->irq = platform_get_irq(pdev, 0);
if (imxdi->irq < 0)
return imxdi->irq;
norm_irq = platform_get_irq(pdev, 0);
if (norm_irq < 0)
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);
......@@ -808,13 +814,20 @@ static int __init dryice_rtc_probe(struct platform_device *pdev)
if (rc != 0)
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);
if (rc) {
dev_warn(&pdev->dev, "interrupt not available.\n");
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);
imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&dryice_rtc_ops, THIS_MODULE);
......
......@@ -138,7 +138,7 @@ static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm)
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,
.set_time = ls1x_rtc_set_time,
};
......
This diff is collapsed.
......@@ -44,12 +44,22 @@
#define MCP795_REG_DAY 0x04
#define MCP795_REG_MONTH 0x06
#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_24_BIT BIT(6)
#define MCP795_LP_BIT BIT(5)
#define MCP795_EXTOSC_BIT BIT(3)
#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)
{
......@@ -150,6 +160,30 @@ static int mcp795_start_oscillator(struct device *dev, bool *extosc)
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)
{
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[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min);
data[2] = bin2bcd(tim->tm_hour);
data[3] = (data[3] & 0xF8) | bin2bcd(tim->tm_wday + 1);
data[4] = bin2bcd(tim->tm_mday);
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)
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_hour, tim->tm_min, tim->tm_sec);
tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
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_min = bcd2bin(data[1] & 0x7F);
tim->tm_hour = bcd2bin(data[2] & 0x3F);
tim->tm_wday = bcd2bin(data[3] & 0x07) - 1;
tim->tm_mday = bcd2bin(data[4] & 0x3F);
tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1;
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_hour, tim->tm_min, tim->tm_sec);
tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec);
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 = {
.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)
......@@ -259,6 +413,23 @@ static int mcp795_probe(struct spi_device *spi)
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;
}
......
......@@ -353,7 +353,7 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
/* RTC layer */
static struct rtc_class_ops mxc_rtc_ops = {
static const struct rtc_class_ops mxc_rtc_ops = {
.release = mxc_rtc_release,
.read_time = mxc_rtc_read_time,
.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);
unsigned char buf[10];
int ret;
int i;
ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, buf,
sizeof(buf));
for (i = 0; i <= PCF2127_REG_CTRL3; i++) {
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) {
dev_err(dev, "%s: read error\n", __func__);
return ret;
......
......@@ -63,7 +63,6 @@ struct rx8010_data {
struct i2c_client *client;
struct rtc_device *rtc;
u8 ctrlreg;
spinlock_t flags_lock;
};
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);
int flagreg;
spin_lock(&rx8010->flags_lock);
mutex_lock(&rx8010->rtc->ops_lock);
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
if (flagreg <= 0) {
spin_unlock(&rx8010->flags_lock);
mutex_unlock(&rx8010->rtc->ops_lock);
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);
spin_unlock(&rx8010->flags_lock);
mutex_unlock(&rx8010->rtc->ops_lock);
return IRQ_HANDLED;
}
......@@ -143,7 +142,6 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
u8 date[7];
int ctrl, flagreg;
int ret;
unsigned long irqflags;
if ((dt->tm_year < 100) || (dt->tm_year > 199))
return -EINVAL;
......@@ -181,11 +179,8 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
if (ret < 0)
return ret;
spin_lock_irqsave(&rx8010->flags_lock, irqflags);
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
if (flagreg < 0) {
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
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,
flagreg & ~RX8010_FLAG_VLF);
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
return 0;
}
......@@ -288,12 +281,9 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
u8 alarmvals[3];
int extreg, flagreg;
int err;
unsigned long irqflags;
spin_lock_irqsave(&rx8010->flags_lock, irqflags);
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
if (flagreg < 0) {
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
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,
rx8010->ctrlreg);
if (err < 0) {
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
return err;
}
}
flagreg &= ~RX8010_FLAG_AF;
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
if (err < 0)
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);
int ret, tmp;
int flagreg;
unsigned long irqflags;
switch (cmd) {
case RTC_VL_READ:
......@@ -419,16 +406,13 @@ static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
return 0;
case RTC_VL_CLR:
spin_lock_irqsave(&rx8010->flags_lock, irqflags);
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
if (flagreg < 0) {
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
return flagreg;
}
flagreg &= ~RX8010_FLAG_VLF;
ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
if (ret < 0)
return ret;
......@@ -466,8 +450,6 @@ static int rx8010_probe(struct i2c_client *client,
rx8010->client = client;
i2c_set_clientdata(client, rx8010);
spin_lock_init(&rx8010->flags_lock);
err = rx8010_init_client(client);
if (err)
return err;
......
......@@ -535,7 +535,7 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
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,
.set_time = sh_rtc_set_time,
.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);
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);
/* Clear alarm interrupt status bit */
......
This diff is collapsed.
......@@ -20,6 +20,8 @@
* more details.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fs.h>
......@@ -33,15 +35,20 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/types.h>
/* Control register */
#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_RTC_HMS_ACC BIT(8)
#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_CLK_PRESCAL 0x0008
/* RTC */
#define SUN6I_RTC_YMD 0x0010
#define SUN6I_RTC_HMS 0x0014
......@@ -114,13 +121,142 @@ struct sun6i_rtc_dev {
void __iomem *base;
int irq;
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)
{
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
irqreturn_t ret = IRQ_NONE;
u32 val;
spin_lock(&chip->lock);
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
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);
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)
......@@ -140,6 +277,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
u32 alrm_val = 0;
u32 alrm_irq_val = 0;
u32 alrm_wake_val = 0;
unsigned long flags;
if (to) {
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);
}
spin_lock_irqsave(&chip->lock, flags);
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
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)
......@@ -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)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
unsigned long flags;
u32 alrm_st;
u32 alrm_en;
spin_lock_irqsave(&chip->lock, flags);
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
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->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
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)
{
struct sun6i_rtc_dev *chip;
struct resource *res;
struct sun6i_rtc_dev *chip = sun6i_rtc;
int ret;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
return -ENODEV;
platform_set_drvdata(pdev, chip);
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);
if (chip->irq < 0) {
dev_err(&pdev->dev, "No IRQ resource\n");
......@@ -404,7 +541,9 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
/* disable alarm wakeup */
writel(0, chip->base + SUN6I_ALARM_CONFIG);
chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
clk_prepare_enable(chip->losc);
chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-sun6i",
&sun6i_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) {
dev_err(&pdev->dev, "unable to register device\n");
......@@ -416,15 +555,6 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
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[] = {
{ .compatible = "allwinner,sun6i-a31-rtc" },
{ /* sentinel */ },
......@@ -433,15 +563,9 @@ MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
static struct platform_driver sun6i_rtc_driver = {
.probe = sun6i_rtc_probe,
.remove = sun6i_rtc_remove,
.driver = {
.name = "sun6i-rtc",
.of_match_table = sun6i_rtc_dt_ids,
},
};
module_platform_driver(sun6i_rtc_driver);
MODULE_DESCRIPTION("sun6i RTC driver");
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_LICENSE("GPL");
builtin_platform_driver(sun6i_rtc_driver);
......@@ -17,16 +17,18 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.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 */
#define TEGRA_RTC_REG_BUSY 0x004
......@@ -59,6 +61,7 @@ struct tegra_rtc_info {
struct platform_device *pdev;
struct rtc_device *rtc_dev;
void __iomem *rtc_base; /* NULL if not initialized. */
struct clk *clk;
int tegra_rtc_irq; /* alarm and periodic irq */
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)
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. */
info->pdev = pdev;
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);
dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
ret);
return ret;
goto disable_clk;
}
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,
"Unable to request interrupt for device (err=%d).\n",
ret);
return ret;
goto disable_clk;
}
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;
}
......@@ -413,6 +437,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev)
MODULE_ALIAS("platform:tegra_rtc");
static struct platform_driver tegra_rtc_driver = {
.remove = tegra_rtc_remove,
.shutdown = tegra_rtc_shutdown,
.driver = {
.name = "tegra_rtc",
......
......@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/math64.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/mfd/tps65910.h>
......@@ -33,7 +34,21 @@ struct tps65910_rtc {
/* Total number of RTC registers needed to set time*/
#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);
u8 val = 0;
......@@ -187,6 +202,133 @@ static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
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)
{
struct device *dev = rtc;
......@@ -219,6 +361,8 @@ static const struct rtc_class_ops tps65910_rtc_ops = {
.read_alarm = tps65910_rtc_read_alarm,
.set_alarm = tps65910_rtc_set_alarm,
.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)
......
......@@ -134,6 +134,7 @@
/* RTC_CTRL_REG bitfields */
#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
/* RTC_STATUS_REG bitfields */
......
/*
* 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);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment