mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
RTC for 4.6
Core: - New sysfs interface to set and read clock offset - Drivers can now be both I2C and SPI (see pcf2127 and ds3232) New drivers: - Alphascale ASM9260 - Epson RX6110SA - Maxim max20024 and max77620 (in max77686) - Microchip PIC32 - NXP pcf2129 (in pcf2127) Subsystem wide cleanups: - remove IRQF_EARLY_RESUME when unecessary Drivers: - ds1307: clock output, temperature sensor and wakeup-source support - ds1685: actually spin forever in poweroff error path - ds3232: many cleanups - ds3234: merged in ds3232 - hym8563: fix invalid year calculation - max77686: many cleanups - max77802 merged in max77686 - pcf2123: cleanups and offset support - pcf85063: cleanups - pcf8523: propely handle oscillator stop bit - rv3029: many cleanups, trickle charger and temperature sensor support - rv8803: convert spin_lock to mutex_lock - rx8025: many fixes - vr41xx: restore alarm_irq_enable -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJW5uWTAAoJEKbNnwlvZCyzUmAP/3/bxOtQ9QUq4nLIVb/dR9G0 yNY5L/RmgQ/x3CJqMkGQImyj4rSEmwdHJSWakv/hGh5iTINtZrw4sl79YKNoTQlB LaXAse1sXzhyfLzH8jYrrzpq14z1kv/LlUPtgFf1arcaRMdjw1kabdIqHnsv0nH2 fvyj3O0qW/KWWYongViiXW94dr8uQXoMVR4M/M8OmG6j6Lw1Kd7gKDZx02dqbo8+ NNQQYAswSxzHCZirIv5Xyjdrz1gIuaWghEdbaVY6BW+CuT8NcC2if1BoYo6xR/Qe KC6M5MGY4xogOxP8+EkH1/GycXx1mwrisJSTxJh9YKMDnLgi9YnTjXnO4t9kQyoK HOspgaHhGHRONNc2dADJqm55SbLUKBEXVTk/Tzq6BJvTKmZmrisAUhWxISLscHwP QPU//fLxyerC/tMKoFyuG7yDtd5VofWThLQV8sF0wcX8AKfLaUIEnN69euCk9mVj +eCwkYyOly0UeYdeXkrnHczIFffzMTENpz9Q8TkrLF1UOMI7nCOb/jCo6hKidEEB ymgHHzhhGeppfEQgU+MsXOxG0E9Ku4gXGOaVk/q7zxyiBhXOmMpoHk6nshKq0A0S eBKmd0BWS0GwXJ+dABfHuI6MSay3A7Bn+fLLoD8LTPMJEONyvOGxDNSH3DymD9JR LpDO4HnalG1ao+eXcD+U =+x1Q -----END PGP SIGNATURE----- Merge tag 'rtc-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "Core: - New sysfs interface to set and read clock offset - Drivers can now be both I2C and SPI (see pcf2127 and ds3232) New drivers: - Alphascale ASM9260 - Epson RX6110SA - Maxim max20024 and max77620 (in max77686) - Microchip PIC32 - NXP pcf2129 (in pcf2127) Subsystem wide cleanups: - remove IRQF_EARLY_RESUME when unecessary Drivers: - ds1307: clock output, temperature sensor and wakeup-source support - ds1685: actually spin forever in poweroff error path - ds3232: many cleanups - ds3234: merged in ds3232 - hym8563: fix invalid year calculation - max77686: many cleanups - max77802 merged in max77686 - pcf2123: cleanups and offset support - pcf85063: cleanups - pcf8523: propely handle oscillator stop bit - rv3029: many cleanups, trickle charger and temperature sensor support - rv8803: convert spin_lock to mutex_lock - rx8025: many fixes - vr41xx: restore alarm_irq_enable" * tag 'rtc-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (86 commits) rtc: pcf2127: add pcf2129 device id rtc: pcf2127: add support for spi interface rtc: pcf2127: convert to use regmap rtc: rv3029: Add thermometer hwmon support rtc: rv3029: Add update_bits helper for eeprom access rtc: ds1685: actually spin forever in poweroff error path rtc: hym8563: fix invalid year calculation rtc: ds3232: use rtc->ops_lock to protect alarm operations rtc: ds3232: fix issue when irq is shared several devices rtc: ds3232: remove unused UIE code rtc: ds3232: add register access error checks rtc: ds3232: fix read on /dev/rtc after RTC_AIE_ON rtc: merge ds3232 and ds3234 rtc: ds3232: convert to use regmap rtc: pxa: fix Kconfig indentation rtc: rv3029: Add device tree property for trickle charger rtc: rv3029: Add functions for EEPROM access rtc: rv3029: Add i2c register update-bits helper rtc: rv3029: Add missing register definitions rtc: rv3029: Add "rv3029" I2C device id ...
This commit is contained in:
commit
13f6f62f61
@ -0,0 +1,19 @@
|
||||
* Alphascale asm9260 SoC Real Time Clock
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "alphascale,asm9260-rtc"
|
||||
- reg: Physical base address of the controller and length
|
||||
of memory mapped region.
|
||||
- interrupts: IRQ line for the RTC.
|
||||
- clocks: Reference to the clock entry.
|
||||
- clock-names: should contain:
|
||||
* "ahb" for the SoC RTC clock
|
||||
|
||||
Example:
|
||||
rtc0: rtc@800a0000 {
|
||||
compatible = "alphascale,asm9260-rtc";
|
||||
reg = <0x800a0000 0x100>;
|
||||
clocks = <&acc CLKID_AHB_RTC>;
|
||||
clock-names = "ahb";
|
||||
interrupts = <2>;
|
||||
};
|
39
Documentation/devicetree/bindings/rtc/epson,rx6110.txt
Normal file
39
Documentation/devicetree/bindings/rtc/epson,rx6110.txt
Normal file
@ -0,0 +1,39 @@
|
||||
Epson RX6110 Real Time Clock
|
||||
============================
|
||||
|
||||
The Epson RX6110 can be used with SPI or I2C busses. The kind of
|
||||
bus depends on the SPISEL pin and can not be configured via software.
|
||||
|
||||
I2C mode
|
||||
--------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be: "epson,rx6110"
|
||||
- reg : the I2C address of the device for I2C
|
||||
|
||||
Example:
|
||||
|
||||
rtc: rtc@32 {
|
||||
compatible = "epson,rx6110"
|
||||
reg = <0x32>;
|
||||
};
|
||||
|
||||
SPI mode
|
||||
--------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be: "epson,rx6110"
|
||||
- reg: chip select number
|
||||
- spi-cs-high: RX6110 needs chipselect high
|
||||
- spi-cpha: RX6110 works with SPI shifted clock phase
|
||||
- spi-cpol: RX6110 works with SPI inverse clock polarity
|
||||
|
||||
Example:
|
||||
|
||||
rtc: rtc@3 {
|
||||
compatible = "epson,rx6110"
|
||||
reg = <3>
|
||||
spi-cs-high;
|
||||
spi-cpha;
|
||||
spi-cpol;
|
||||
};
|
37
Documentation/devicetree/bindings/rtc/maxim,ds3231.txt
Normal file
37
Documentation/devicetree/bindings/rtc/maxim,ds3231.txt
Normal file
@ -0,0 +1,37 @@
|
||||
* Maxim DS3231 Real Time Clock
|
||||
|
||||
Required properties:
|
||||
see: Documentation/devicetree/bindings/i2c/trivial-devices.txt
|
||||
|
||||
Optional property:
|
||||
- #clock-cells: Should be 1.
|
||||
- clock-output-names:
|
||||
overwrite the default clock names "ds3231_clk_sqw" and "ds3231_clk_32khz".
|
||||
|
||||
Each clock is assigned an identifier and client nodes can use this identifier
|
||||
to specify the clock which they consume. Following indices are allowed:
|
||||
- 0: square-wave output on the SQW pin
|
||||
- 1: square-wave output on the 32kHz pin
|
||||
|
||||
- interrupts: rtc alarm/event interrupt. When this property is selected,
|
||||
clock on the SQW pin cannot be used.
|
||||
|
||||
Example:
|
||||
|
||||
ds3231: ds3231@51 {
|
||||
compatible = "maxim,ds3231";
|
||||
reg = <0x68>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
device1 {
|
||||
...
|
||||
clocks = <&ds3231 0>;
|
||||
...
|
||||
};
|
||||
|
||||
device2 {
|
||||
...
|
||||
clocks = <&ds3231 1>;
|
||||
...
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
* Microchip PIC32 Real Time Clock and Calendar
|
||||
|
||||
The RTCC keeps time in hours, minutes, and seconds, and one half second. It
|
||||
provides a calendar in weekday, date, month, and year. It also provides a
|
||||
configurable alarm.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be: "microchip,pic32mzda-rtc"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: RTC alarm/event interrupt
|
||||
- clocks: clock phandle
|
||||
|
||||
Example:
|
||||
|
||||
rtc: rtc@1f8c0000 {
|
||||
compatible = "microchip,pic32mzda-rtc";
|
||||
reg = <0x1f8c0000 0x60>;
|
||||
interrupts = <166 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&PBCLK6>;
|
||||
};
|
@ -157,6 +157,12 @@ wakealarm: The time at which the clock will generate a system wakeup
|
||||
the epoch by default, or if there's a leading +, seconds in the
|
||||
future, or if there is a leading +=, seconds ahead of the current
|
||||
alarm.
|
||||
offset: The amount which the rtc clock has been adjusted in firmware.
|
||||
Visible only if the driver supports clock offset adjustment.
|
||||
The unit is parts per billion, i.e. The number of clock ticks
|
||||
which are added to or removed from the rtc's base clock per
|
||||
billion ticks. A positive value makes a day pass more slowly,
|
||||
longer, and a negative value makes a day pass more quickly.
|
||||
|
||||
IOCTL INTERFACE
|
||||
---------------
|
||||
|
@ -35,8 +35,6 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define I2C_ADDR_RTC (0x0C >> 1)
|
||||
|
||||
static const struct mfd_cell max77686_devs[] = {
|
||||
{ .name = "max77686-pmic", },
|
||||
{ .name = "max77686-rtc", },
|
||||
@ -116,11 +114,6 @@ static const struct regmap_config max77686_regmap_config = {
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static const struct regmap_config max77686_rtc_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static const struct regmap_config max77802_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
@ -156,25 +149,6 @@ static const struct regmap_irq_chip max77686_irq_chip = {
|
||||
.num_irqs = ARRAY_SIZE(max77686_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77686_rtc_irqs[] = {
|
||||
/* RTC interrupts */
|
||||
{ .reg_offset = 0, .mask = MAX77686_RTCINT_RTC60S_MSK, },
|
||||
{ .reg_offset = 0, .mask = MAX77686_RTCINT_RTCA1_MSK, },
|
||||
{ .reg_offset = 0, .mask = MAX77686_RTCINT_RTCA2_MSK, },
|
||||
{ .reg_offset = 0, .mask = MAX77686_RTCINT_SMPL_MSK, },
|
||||
{ .reg_offset = 0, .mask = MAX77686_RTCINT_RTC1S_MSK, },
|
||||
{ .reg_offset = 0, .mask = MAX77686_RTCINT_WTSR_MSK, },
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77686_rtc_irq_chip = {
|
||||
.name = "max77686-rtc",
|
||||
.status_base = MAX77686_RTC_INT,
|
||||
.mask_base = MAX77686_RTC_INTM,
|
||||
.num_regs = 1,
|
||||
.irqs = max77686_rtc_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77802_irq_chip = {
|
||||
.name = "max77802-pmic",
|
||||
.status_base = MAX77802_REG_INT1,
|
||||
@ -184,15 +158,6 @@ static const struct regmap_irq_chip max77802_irq_chip = {
|
||||
.num_irqs = ARRAY_SIZE(max77686_irqs),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77802_rtc_irq_chip = {
|
||||
.name = "max77802-rtc",
|
||||
.status_base = MAX77802_RTC_INT,
|
||||
.mask_base = MAX77802_RTC_INTM,
|
||||
.num_regs = 1,
|
||||
.irqs = max77686_rtc_irqs, /* same masks as 77686 */
|
||||
.num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
|
||||
};
|
||||
|
||||
static const struct of_device_id max77686_pmic_dt_match[] = {
|
||||
{
|
||||
.compatible = "maxim,max77686",
|
||||
@ -214,8 +179,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
int ret = 0;
|
||||
const struct regmap_config *config;
|
||||
const struct regmap_irq_chip *irq_chip;
|
||||
const struct regmap_irq_chip *rtc_irq_chip;
|
||||
struct regmap **rtc_regmap;
|
||||
const struct mfd_cell *cells;
|
||||
int n_devs;
|
||||
|
||||
@ -242,15 +205,11 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
if (max77686->type == TYPE_MAX77686) {
|
||||
config = &max77686_regmap_config;
|
||||
irq_chip = &max77686_irq_chip;
|
||||
rtc_irq_chip = &max77686_rtc_irq_chip;
|
||||
rtc_regmap = &max77686->rtc_regmap;
|
||||
cells = max77686_devs;
|
||||
n_devs = ARRAY_SIZE(max77686_devs);
|
||||
} else {
|
||||
config = &max77802_regmap_config;
|
||||
irq_chip = &max77802_irq_chip;
|
||||
rtc_irq_chip = &max77802_rtc_irq_chip;
|
||||
rtc_regmap = &max77686->regmap;
|
||||
cells = max77802_devs;
|
||||
n_devs = ARRAY_SIZE(max77802_devs);
|
||||
}
|
||||
@ -270,60 +229,25 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (max77686->type == TYPE_MAX77686) {
|
||||
max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
|
||||
if (!max77686->rtc) {
|
||||
dev_err(max77686->dev,
|
||||
"Failed to allocate I2C device for RTC\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
i2c_set_clientdata(max77686->rtc, max77686);
|
||||
|
||||
max77686->rtc_regmap =
|
||||
devm_regmap_init_i2c(max77686->rtc,
|
||||
&max77686_rtc_regmap_config);
|
||||
if (IS_ERR(max77686->rtc_regmap)) {
|
||||
ret = PTR_ERR(max77686->rtc_regmap);
|
||||
dev_err(max77686->dev,
|
||||
"failed to allocate RTC regmap: %d\n",
|
||||
ret);
|
||||
goto err_unregister_i2c;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(max77686->regmap, max77686->irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
|
||||
IRQF_SHARED, 0, irq_chip,
|
||||
&max77686->irq_data);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "failed to add PMIC irq chip: %d\n", ret);
|
||||
goto err_unregister_i2c;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(*rtc_regmap, max77686->irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
|
||||
IRQF_SHARED, 0, rtc_irq_chip,
|
||||
&max77686->rtc_irq_data);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "failed to add RTC irq chip: %d\n", ret);
|
||||
goto err_del_irqc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(max77686->dev, -1, cells, n_devs, NULL, 0, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
|
||||
goto err_del_rtc_irqc;
|
||||
goto err_del_irqc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_del_rtc_irqc:
|
||||
regmap_del_irq_chip(max77686->irq, max77686->rtc_irq_data);
|
||||
err_del_irqc:
|
||||
regmap_del_irq_chip(max77686->irq, max77686->irq_data);
|
||||
err_unregister_i2c:
|
||||
if (max77686->type == TYPE_MAX77686)
|
||||
i2c_unregister_device(max77686->rtc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -334,12 +258,8 @@ static int max77686_i2c_remove(struct i2c_client *i2c)
|
||||
|
||||
mfd_remove_devices(max77686->dev);
|
||||
|
||||
regmap_del_irq_chip(max77686->irq, max77686->rtc_irq_data);
|
||||
regmap_del_irq_chip(max77686->irq, max77686->irq_data);
|
||||
|
||||
if (max77686->type == TYPE_MAX77686)
|
||||
i2c_unregister_device(max77686->rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,6 @@ config RTC_DRV_TEST
|
||||
will be called rtc-test.
|
||||
|
||||
comment "I2C RTC drivers"
|
||||
depends on I2C
|
||||
|
||||
if I2C
|
||||
|
||||
@ -212,6 +211,15 @@ config RTC_DRV_DS1307
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds1307.
|
||||
|
||||
config RTC_DRV_DS1307_HWMON
|
||||
bool "HWMON support for rtc-ds1307"
|
||||
depends on RTC_DRV_DS1307 && HWMON
|
||||
depends on !(RTC_DRV_DS1307=y && HWMON=m)
|
||||
default y
|
||||
help
|
||||
Say Y here if you want to expose temperature sensor data on
|
||||
rtc-ds1307 (only DS3231)
|
||||
|
||||
config RTC_DRV_DS1374
|
||||
tristate "Dallas/Maxim DS1374"
|
||||
help
|
||||
@ -239,16 +247,6 @@ config RTC_DRV_DS1672
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds1672.
|
||||
|
||||
config RTC_DRV_DS3232
|
||||
tristate "Dallas/Maxim DS3232"
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
DS3232 real-time clock chips. If an interrupt is associated
|
||||
with the device, the alarm functionality is supported.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds3232.
|
||||
|
||||
config RTC_DRV_HYM8563
|
||||
tristate "Haoyu Microelectronics HYM8563"
|
||||
depends on OF
|
||||
@ -317,10 +315,10 @@ config RTC_DRV_MAX8997
|
||||
|
||||
config RTC_DRV_MAX77686
|
||||
tristate "Maxim MAX77686"
|
||||
depends on MFD_MAX77686
|
||||
depends on MFD_MAX77686 || MFD_MAX77620
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
RTC of Maxim MAX77686 PMIC.
|
||||
RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max77686.
|
||||
@ -335,16 +333,6 @@ config RTC_DRV_RK808
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rk808-rtc.
|
||||
|
||||
config RTC_DRV_MAX77802
|
||||
tristate "Maxim 77802 RTC"
|
||||
depends on MFD_MAX77686
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
RTC of Maxim MAX77802 PMIC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max77802.
|
||||
|
||||
config RTC_DRV_RS5C372
|
||||
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
|
||||
help
|
||||
@ -391,25 +379,6 @@ config RTC_DRV_X1205
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-x1205.
|
||||
|
||||
config RTC_DRV_PALMAS
|
||||
tristate "TI Palmas RTC driver"
|
||||
depends on MFD_PALMAS
|
||||
help
|
||||
If you say yes here you get support for the RTC of TI PALMA series PMIC
|
||||
chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-palma.
|
||||
|
||||
config RTC_DRV_PCF2127
|
||||
tristate "NXP PCF2127"
|
||||
help
|
||||
If you say yes here you get support for the NXP PCF2127/29 RTC
|
||||
chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf2127.
|
||||
|
||||
config RTC_DRV_PCF8523
|
||||
tristate "NXP PCF8523"
|
||||
help
|
||||
@ -419,6 +388,14 @@ config RTC_DRV_PCF8523
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf8523.
|
||||
|
||||
config RTC_DRV_PCF85063
|
||||
tristate "NXP PCF85063"
|
||||
help
|
||||
If you say yes here you get support for the PCF85063 RTC chip
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf85063.
|
||||
|
||||
config RTC_DRV_PCF8563
|
||||
tristate "Philips PCF8563/Epson RTC8564"
|
||||
help
|
||||
@ -429,14 +406,6 @@ config RTC_DRV_PCF8563
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf8563.
|
||||
|
||||
config RTC_DRV_PCF85063
|
||||
tristate "nxp PCF85063"
|
||||
help
|
||||
If you say yes here you get support for the PCF85063 RTC chip
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf85063.
|
||||
|
||||
config RTC_DRV_PCF8583
|
||||
tristate "Philips PCF8583"
|
||||
help
|
||||
@ -501,6 +470,16 @@ config RTC_DRV_TWL4030
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-twl.
|
||||
|
||||
config RTC_DRV_PALMAS
|
||||
tristate "TI Palmas RTC driver"
|
||||
depends on MFD_PALMAS
|
||||
help
|
||||
If you say yes here you get support for the RTC of TI PALMA series PMIC
|
||||
chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-palma.
|
||||
|
||||
config RTC_DRV_TPS6586X
|
||||
tristate "TI TPS6586X RTC driver"
|
||||
depends on MFD_TPS6586X
|
||||
@ -595,14 +574,23 @@ config RTC_DRV_EM3027
|
||||
will be called rtc-em3027.
|
||||
|
||||
config RTC_DRV_RV3029C2
|
||||
tristate "Micro Crystal RTC"
|
||||
tristate "Micro Crystal RV3029"
|
||||
help
|
||||
If you say yes here you get support for the Micro Crystal
|
||||
RV3029-C2 RTC chips.
|
||||
RV3029 RTC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rv3029c2.
|
||||
|
||||
config RTC_DRV_RV3029_HWMON
|
||||
bool "HWMON support for RV3029"
|
||||
depends on RTC_DRV_RV3029C2 && HWMON
|
||||
depends on !(RTC_DRV_RV3029C2=y && HWMON=m)
|
||||
default y
|
||||
help
|
||||
Say Y here if you want to expose temperature sensor data on
|
||||
rtc-rv3029c2.
|
||||
|
||||
config RTC_DRV_RV8803
|
||||
tristate "Micro Crystal RV8803"
|
||||
help
|
||||
@ -691,15 +679,6 @@ config RTC_DRV_DS1390
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds1390.
|
||||
|
||||
config RTC_DRV_MAX6902
|
||||
tristate "Maxim MAX6902"
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
Maxim MAX6902 SPI RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max6902.
|
||||
|
||||
config RTC_DRV_R9701
|
||||
tristate "Epson RTC-9701JE"
|
||||
help
|
||||
@ -709,6 +688,23 @@ config RTC_DRV_R9701
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-r9701.
|
||||
|
||||
config RTC_DRV_RX4581
|
||||
tristate "Epson RX-4581"
|
||||
help
|
||||
If you say yes here you will get support for the Epson RX-4581.
|
||||
|
||||
This driver can also be built as a module. If so the module
|
||||
will be called rtc-rx4581.
|
||||
|
||||
config RTC_DRV_RX6110
|
||||
tristate "Epson RX-6110"
|
||||
select REGMAP_SPI
|
||||
help
|
||||
If you say yes here you will get support for the Epson RX-6610.
|
||||
|
||||
This driver can also be built as a module. If so the module
|
||||
will be called rtc-rx6110.
|
||||
|
||||
config RTC_DRV_RS5C348
|
||||
tristate "Ricoh RS5C348A/B"
|
||||
help
|
||||
@ -718,14 +714,14 @@ config RTC_DRV_RS5C348
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rs5c348.
|
||||
|
||||
config RTC_DRV_DS3234
|
||||
tristate "Maxim/Dallas DS3234"
|
||||
config RTC_DRV_MAX6902
|
||||
tristate "Maxim MAX6902"
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Maxim/Dallas DS3234 SPI RTC chip.
|
||||
If you say yes here you will get support for the
|
||||
Maxim MAX6902 SPI RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds3234.
|
||||
will be called rtc-max6902.
|
||||
|
||||
config RTC_DRV_PCF2123
|
||||
tristate "NXP PCF2123"
|
||||
@ -736,14 +732,6 @@ config RTC_DRV_PCF2123
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf2123.
|
||||
|
||||
config RTC_DRV_RX4581
|
||||
tristate "Epson RX-4581"
|
||||
help
|
||||
If you say yes here you will get support for the Epson RX-4581.
|
||||
|
||||
This driver can also be built as a module. If so the module
|
||||
will be called rtc-rx4581.
|
||||
|
||||
config RTC_DRV_MCP795
|
||||
tristate "Microchip MCP795"
|
||||
help
|
||||
@ -754,6 +742,41 @@ config RTC_DRV_MCP795
|
||||
|
||||
endif # SPI_MASTER
|
||||
|
||||
#
|
||||
# Helper to resolve issues with configs that have SPI enabled but I2C
|
||||
# modular. See SND_SOC_I2C_AND_SPI for more information
|
||||
#
|
||||
config RTC_I2C_AND_SPI
|
||||
tristate
|
||||
default m if I2C=m
|
||||
default y if I2C=y
|
||||
default y if SPI_MASTER=y
|
||||
select REGMAP_I2C if I2C
|
||||
select REGMAP_SPI if SPI_MASTER
|
||||
|
||||
comment "SPI and I2C RTC drivers"
|
||||
|
||||
config RTC_DRV_DS3232
|
||||
tristate "Dallas/Maxim DS3232/DS3234"
|
||||
depends on RTC_I2C_AND_SPI
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
DS3232 and DS3234 real-time clock chips. If an interrupt is associated
|
||||
with the device, the alarm functionality is supported.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds3232.
|
||||
|
||||
config RTC_DRV_PCF2127
|
||||
tristate "NXP PCF2127"
|
||||
depends on RTC_I2C_AND_SPI
|
||||
help
|
||||
If you say yes here you get support for the NXP PCF2127/29 RTC
|
||||
chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf2127.
|
||||
|
||||
comment "Platform RTC drivers"
|
||||
|
||||
# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
|
||||
@ -1087,7 +1110,7 @@ config RTC_DRV_WM8350
|
||||
|
||||
config RTC_DRV_SPEAR
|
||||
tristate "SPEAR ST RTC"
|
||||
depends on PLAT_SPEAR
|
||||
depends on PLAT_SPEAR || COMPILE_TEST
|
||||
default y
|
||||
help
|
||||
If you say Y here you will get support for the RTC found on
|
||||
@ -1119,7 +1142,7 @@ config RTC_DRV_AB8500
|
||||
|
||||
config RTC_DRV_NUC900
|
||||
tristate "NUC910/NUC920 RTC driver"
|
||||
depends on ARCH_W90X900
|
||||
depends on ARCH_W90X900 || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the RTC subsystem of the
|
||||
NUC910/NUC920 used in embedded systems.
|
||||
@ -1144,9 +1167,19 @@ config RTC_DRV_ZYNQMP
|
||||
|
||||
comment "on-CPU RTC drivers"
|
||||
|
||||
config RTC_DRV_ASM9260
|
||||
tristate "Alphascale asm9260 RTC"
|
||||
depends on MACH_ASM9260
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
Alphascale asm9260 SoC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-asm9260.
|
||||
|
||||
config RTC_DRV_DAVINCI
|
||||
tristate "TI DaVinci RTC"
|
||||
depends on ARCH_DAVINCI_DM365
|
||||
depends on ARCH_DAVINCI_DM365 || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
DaVinci platforms (DM365).
|
||||
@ -1156,7 +1189,7 @@ config RTC_DRV_DAVINCI
|
||||
|
||||
config RTC_DRV_DIGICOLOR
|
||||
tristate "Conexant Digicolor RTC"
|
||||
depends on ARCH_DIGICOLOR
|
||||
depends on ARCH_DIGICOLOR || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the RTC on Conexant
|
||||
Digicolor platforms. This currently includes the CX92755 SoC.
|
||||
@ -1175,7 +1208,7 @@ config RTC_DRV_IMXDI
|
||||
|
||||
config RTC_DRV_OMAP
|
||||
tristate "TI OMAP Real Time Clock"
|
||||
depends on ARCH_OMAP || ARCH_DAVINCI
|
||||
depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
|
||||
help
|
||||
Say "yes" here to support the on chip real time clock
|
||||
present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx.
|
||||
@ -1192,7 +1225,7 @@ config HAVE_S3C_RTC
|
||||
|
||||
config RTC_DRV_S3C
|
||||
tristate "Samsung S3C series SoC RTC"
|
||||
depends on ARCH_S3C64XX || HAVE_S3C_RTC
|
||||
depends on ARCH_S3C64XX || HAVE_S3C_RTC || COMPILE_TEST
|
||||
help
|
||||
RTC (Realtime Clock) driver for the clock inbuilt into the
|
||||
Samsung S3C24XX series of SoCs. This can provide periodic
|
||||
@ -1208,7 +1241,7 @@ config RTC_DRV_S3C
|
||||
|
||||
config RTC_DRV_EP93XX
|
||||
tristate "Cirrus Logic EP93XX"
|
||||
depends on ARCH_EP93XX
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
RTC embedded in the Cirrus Logic EP93XX processors.
|
||||
@ -1238,7 +1271,7 @@ config RTC_DRV_SH
|
||||
|
||||
config RTC_DRV_VR41XX
|
||||
tristate "NEC VR41XX"
|
||||
depends on CPU_VR41XX
|
||||
depends on CPU_VR41XX || COMPILE_TEST
|
||||
help
|
||||
If you say Y here you will get access to the real time clock
|
||||
built into your NEC VR41XX CPU.
|
||||
@ -1268,14 +1301,14 @@ config RTC_DRV_PL031
|
||||
|
||||
config RTC_DRV_AT32AP700X
|
||||
tristate "AT32AP700X series RTC"
|
||||
depends on PLATFORM_AT32AP
|
||||
depends on PLATFORM_AT32AP || COMPILE_TEST
|
||||
help
|
||||
Driver for the internal RTC (Realtime Clock) on Atmel AVR32
|
||||
AT32AP700x family processors.
|
||||
|
||||
config RTC_DRV_AT91RM9200
|
||||
tristate "AT91RM9200 or some AT91SAM9 RTC"
|
||||
depends on ARCH_AT91
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
help
|
||||
Driver for the internal RTC (Realtime Clock) module found on
|
||||
Atmel AT91RM9200's and some AT91SAM9 chips. On AT91SAM9 chips
|
||||
@ -1283,7 +1316,7 @@ config RTC_DRV_AT91RM9200
|
||||
|
||||
config RTC_DRV_AT91SAM9
|
||||
tristate "AT91SAM9 RTT as RTC"
|
||||
depends on ARCH_AT91
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which
|
||||
@ -1325,17 +1358,17 @@ config RTC_DRV_GENERIC
|
||||
tristate "Generic RTC support"
|
||||
# Please consider writing a new RTC driver instead of using the generic
|
||||
# RTC abstraction
|
||||
depends on PARISC || M68K || PPC || SUPERH32
|
||||
depends on PARISC || M68K || PPC || SUPERH32 || COMPILE_TEST
|
||||
help
|
||||
Say Y or M here to enable RTC support on systems using the generic
|
||||
RTC abstraction. If you do not know what you are doing, you should
|
||||
just say Y.
|
||||
|
||||
config RTC_DRV_PXA
|
||||
tristate "PXA27x/PXA3xx"
|
||||
depends on ARCH_PXA
|
||||
select RTC_DRV_SA1100
|
||||
help
|
||||
tristate "PXA27x/PXA3xx"
|
||||
depends on ARCH_PXA
|
||||
select RTC_DRV_SA1100
|
||||
help
|
||||
If you say Y here you will get access to the real time clock
|
||||
built into your PXA27x or PXA3xx CPU. This RTC is actually 2 RTCs
|
||||
consisting of an SA1100 compatible RTC and the extended PXA RTC.
|
||||
@ -1345,7 +1378,7 @@ config RTC_DRV_PXA
|
||||
|
||||
config RTC_DRV_VT8500
|
||||
tristate "VIA/WonderMedia 85xx SoC RTC"
|
||||
depends on ARCH_VT8500
|
||||
depends on ARCH_VT8500 || COMPILE_TEST
|
||||
help
|
||||
If you say Y here you will get access to the real time clock
|
||||
built into your VIA VT8500 SoC or its relatives.
|
||||
@ -1360,14 +1393,15 @@ config RTC_DRV_SUN4V
|
||||
|
||||
config RTC_DRV_SUN6I
|
||||
tristate "Allwinner A31 RTC"
|
||||
depends on MACH_SUN6I || MACH_SUN8I
|
||||
default MACH_SUN6I || MACH_SUN8I || COMPILE_TEST
|
||||
depends on ARCH_SUNXI
|
||||
help
|
||||
If you say Y here you will get support for the RTC found on
|
||||
Allwinner A31.
|
||||
If you say Y here you will get support for the RTC found in
|
||||
some Allwinner SoCs like the A31 or the A64.
|
||||
|
||||
config RTC_DRV_SUNXI
|
||||
tristate "Allwinner sun4i/sun7i RTC"
|
||||
depends on MACH_SUN4I || MACH_SUN7I
|
||||
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
|
||||
help
|
||||
If you say Y here you will get support for the RTC found on
|
||||
Allwinner A10/A20.
|
||||
@ -1388,7 +1422,7 @@ config RTC_DRV_TX4939
|
||||
|
||||
config RTC_DRV_MV
|
||||
tristate "Marvell SoC RTC"
|
||||
depends on ARCH_DOVE || ARCH_MVEBU
|
||||
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you will get support for the in-chip RTC
|
||||
that can be found in some of Marvell's SoC devices, such as
|
||||
@ -1399,7 +1433,7 @@ config RTC_DRV_MV
|
||||
|
||||
config RTC_DRV_ARMADA38X
|
||||
tristate "Armada 38x Marvell SoC RTC"
|
||||
depends on ARCH_MVEBU
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you will get support for the in-chip RTC
|
||||
that can be found in the Armada 38x Marvell's SoC device
|
||||
@ -1429,7 +1463,7 @@ config RTC_DRV_PS3
|
||||
|
||||
config RTC_DRV_COH901331
|
||||
tristate "ST-Ericsson COH 901 331 RTC"
|
||||
depends on ARCH_U300
|
||||
depends on ARCH_U300 || COMPILE_TEST
|
||||
help
|
||||
If you say Y here you will get access to ST-Ericsson
|
||||
COH 901 331 RTC clock found in some ST-Ericsson Mobile
|
||||
@ -1441,7 +1475,7 @@ config RTC_DRV_COH901331
|
||||
|
||||
config RTC_DRV_STMP
|
||||
tristate "Freescale STMP3xxx/i.MX23/i.MX28 RTC"
|
||||
depends on ARCH_MXS
|
||||
depends on ARCH_MXS || COMPILE_TEST
|
||||
select STMP_DEVICE
|
||||
help
|
||||
If you say yes here you will get support for the onboard
|
||||
@ -1476,7 +1510,7 @@ config RTC_DRV_MPC5121
|
||||
|
||||
config RTC_DRV_JZ4740
|
||||
tristate "Ingenic JZ4740 SoC"
|
||||
depends on MACH_JZ4740
|
||||
depends on MACH_JZ4740 || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the Ingenic JZ4740 SoC RTC
|
||||
controller.
|
||||
@ -1497,7 +1531,7 @@ config RTC_DRV_LPC24XX
|
||||
so, the module will be called rtc-lpc24xx.
|
||||
|
||||
config RTC_DRV_LPC32XX
|
||||
depends on ARCH_LPC32XX
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
tristate "NXP LPC32XX RTC"
|
||||
help
|
||||
This enables support for the NXP RTC in the LPC32XX
|
||||
@ -1507,7 +1541,7 @@ config RTC_DRV_LPC32XX
|
||||
|
||||
config RTC_DRV_PM8XXX
|
||||
tristate "Qualcomm PMIC8XXX RTC"
|
||||
depends on MFD_PM8XXX || MFD_SPMI_PMIC
|
||||
depends on MFD_PM8XXX || MFD_SPMI_PMIC || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Qualcomm PMIC8XXX RTC.
|
||||
@ -1517,7 +1551,7 @@ config RTC_DRV_PM8XXX
|
||||
|
||||
config RTC_DRV_TEGRA
|
||||
tristate "NVIDIA Tegra Internal RTC driver"
|
||||
depends on ARCH_TEGRA
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Tegra 200 series internal RTC module.
|
||||
@ -1603,7 +1637,7 @@ config RTC_DRV_MOXART
|
||||
|
||||
config RTC_DRV_MT6397
|
||||
tristate "Mediatek Real Time Clock driver"
|
||||
depends on MFD_MT6397 || COMPILE_TEST
|
||||
depends on MFD_MT6397 || (COMPILE_TEST && IRQ_DOMAIN)
|
||||
help
|
||||
This selects the Mediatek(R) RTC driver. RTC is part of Mediatek
|
||||
MT6397 PMIC. You should enable MT6397 PMIC MFD before select
|
||||
@ -1622,6 +1656,16 @@ config RTC_DRV_XGENE
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-xgene".
|
||||
|
||||
config RTC_DRV_PIC32
|
||||
tristate "Microchip PIC32 RTC"
|
||||
depends on MACH_PIC32
|
||||
default y
|
||||
help
|
||||
If you say yes here you get support for the PIC32 RTC module.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pic32
|
||||
|
||||
comment "HID Sensor RTC drivers"
|
||||
|
||||
config RTC_DRV_HID_SENSOR_TIME
|
||||
|
@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
|
||||
obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o
|
||||
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
|
||||
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
|
||||
obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
|
||||
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
|
||||
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
|
||||
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
||||
@ -59,7 +60,6 @@ obj-$(CONFIG_RTC_DRV_DS1685_FAMILY) += rtc-ds1685.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
|
||||
obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o
|
||||
obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
|
||||
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
|
||||
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
|
||||
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
|
||||
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
|
||||
@ -86,7 +86,6 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
|
||||
@ -112,6 +111,7 @@ obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
|
||||
obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o
|
||||
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
|
||||
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
|
||||
obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
|
||||
@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
|
||||
obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
|
||||
obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o
|
||||
obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
|
||||
obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
|
||||
|
@ -361,17 +361,4 @@ static int __init rtc_init(void)
|
||||
rtc_dev_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rtc_exit(void)
|
||||
{
|
||||
rtc_dev_exit();
|
||||
class_destroy(rtc_class);
|
||||
ida_destroy(&rtc_ida);
|
||||
}
|
||||
|
||||
subsys_initcall(rtc_init);
|
||||
module_exit(rtc_exit);
|
||||
|
||||
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
|
||||
MODULE_DESCRIPTION("RTC class support");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -939,4 +939,58 @@ void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
|
||||
mutex_unlock(&rtc->ops_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* rtc_read_offset - Read the amount of rtc offset in parts per billion
|
||||
* @ rtc: rtc device to be used
|
||||
* @ offset: the offset in parts per billion
|
||||
*
|
||||
* see below for details.
|
||||
*
|
||||
* Kernel interface to read rtc clock offset
|
||||
* Returns 0 on success, or a negative number on error.
|
||||
* If read_offset() is not implemented for the rtc, return -EINVAL
|
||||
*/
|
||||
int rtc_read_offset(struct rtc_device *rtc, long *offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!rtc->ops)
|
||||
return -ENODEV;
|
||||
|
||||
if (!rtc->ops->read_offset)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&rtc->ops_lock);
|
||||
ret = rtc->ops->read_offset(rtc->dev.parent, offset);
|
||||
mutex_unlock(&rtc->ops_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rtc_set_offset - Adjusts the duration of the average second
|
||||
* @ rtc: rtc device to be used
|
||||
* @ offset: the offset in parts per billion
|
||||
*
|
||||
* Some rtc's allow an adjustment to the average duration of a second
|
||||
* to compensate for differences in the actual clock rate due to temperature,
|
||||
* the crystal, capacitor, etc.
|
||||
*
|
||||
* Kernel interface to adjust an rtc clock offset.
|
||||
* Return 0 on success, or a negative number on error.
|
||||
* If the rtc offset is not setable (or not implemented), return -EINVAL
|
||||
*/
|
||||
int rtc_set_offset(struct rtc_device *rtc, long offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!rtc->ops)
|
||||
return -ENODEV;
|
||||
|
||||
if (!rtc->ops->set_offset)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&rtc->ops_lock);
|
||||
ret = rtc->ops->set_offset(rtc->dev.parent, offset);
|
||||
mutex_unlock(&rtc->ops_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ static int as3722_rtc_probe(struct platform_device *pdev)
|
||||
dev_info(&pdev->dev, "RTC interrupt %d\n", as3722_rtc->alarm_irq);
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, as3722_rtc->alarm_irq, NULL,
|
||||
as3722_alarm_irq, IRQF_ONESHOT | IRQF_EARLY_RESUME,
|
||||
as3722_alarm_irq, IRQF_ONESHOT,
|
||||
"rtc-alarm", as3722_rtc);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
|
||||
|
355
drivers/rtc/rtc-asm9260.c
Normal file
355
drivers/rtc/rtc-asm9260.c
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Oleksij Rempel <linux@rempel-privat.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License,
|
||||
* or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
/* Miscellaneous registers */
|
||||
/* Interrupt Location Register */
|
||||
#define HW_ILR 0x00
|
||||
#define BM_RTCALF BIT(1)
|
||||
#define BM_RTCCIF BIT(0)
|
||||
|
||||
/* Clock Control Register */
|
||||
#define HW_CCR 0x08
|
||||
/* Calibration counter disable */
|
||||
#define BM_CCALOFF BIT(4)
|
||||
/* Reset internal oscillator divider */
|
||||
#define BM_CTCRST BIT(1)
|
||||
/* Clock Enable */
|
||||
#define BM_CLKEN BIT(0)
|
||||
|
||||
/* Counter Increment Interrupt Register */
|
||||
#define HW_CIIR 0x0C
|
||||
#define BM_CIIR_IMYEAR BIT(7)
|
||||
#define BM_CIIR_IMMON BIT(6)
|
||||
#define BM_CIIR_IMDOY BIT(5)
|
||||
#define BM_CIIR_IMDOW BIT(4)
|
||||
#define BM_CIIR_IMDOM BIT(3)
|
||||
#define BM_CIIR_IMHOUR BIT(2)
|
||||
#define BM_CIIR_IMMIN BIT(1)
|
||||
#define BM_CIIR_IMSEC BIT(0)
|
||||
|
||||
/* Alarm Mask Register */
|
||||
#define HW_AMR 0x10
|
||||
#define BM_AMR_IMYEAR BIT(7)
|
||||
#define BM_AMR_IMMON BIT(6)
|
||||
#define BM_AMR_IMDOY BIT(5)
|
||||
#define BM_AMR_IMDOW BIT(4)
|
||||
#define BM_AMR_IMDOM BIT(3)
|
||||
#define BM_AMR_IMHOUR BIT(2)
|
||||
#define BM_AMR_IMMIN BIT(1)
|
||||
#define BM_AMR_IMSEC BIT(0)
|
||||
#define BM_AMR_OFF 0xff
|
||||
|
||||
/* Consolidated time registers */
|
||||
#define HW_CTIME0 0x14
|
||||
#define BM_CTIME0_DOW_S 24
|
||||
#define BM_CTIME0_DOW_M 0x7
|
||||
#define BM_CTIME0_HOUR_S 16
|
||||
#define BM_CTIME0_HOUR_M 0x1f
|
||||
#define BM_CTIME0_MIN_S 8
|
||||
#define BM_CTIME0_MIN_M 0x3f
|
||||
#define BM_CTIME0_SEC_S 0
|
||||
#define BM_CTIME0_SEC_M 0x3f
|
||||
|
||||
#define HW_CTIME1 0x18
|
||||
#define BM_CTIME1_YEAR_S 16
|
||||
#define BM_CTIME1_YEAR_M 0xfff
|
||||
#define BM_CTIME1_MON_S 8
|
||||
#define BM_CTIME1_MON_M 0xf
|
||||
#define BM_CTIME1_DOM_S 0
|
||||
#define BM_CTIME1_DOM_M 0x1f
|
||||
|
||||
#define HW_CTIME2 0x1C
|
||||
#define BM_CTIME2_DOY_S 0
|
||||
#define BM_CTIME2_DOY_M 0xfff
|
||||
|
||||
/* Time counter registers */
|
||||
#define HW_SEC 0x20
|
||||
#define HW_MIN 0x24
|
||||
#define HW_HOUR 0x28
|
||||
#define HW_DOM 0x2C
|
||||
#define HW_DOW 0x30
|
||||
#define HW_DOY 0x34
|
||||
#define HW_MONTH 0x38
|
||||
#define HW_YEAR 0x3C
|
||||
|
||||
#define HW_CALIBRATION 0x40
|
||||
#define BM_CALDIR_BACK BIT(17)
|
||||
#define BM_CALVAL_M 0x1ffff
|
||||
|
||||
/* General purpose registers */
|
||||
#define HW_GPREG0 0x44
|
||||
#define HW_GPREG1 0x48
|
||||
#define HW_GPREG2 0x4C
|
||||
#define HW_GPREG3 0x50
|
||||
#define HW_GPREG4 0x54
|
||||
|
||||
/* Alarm register group */
|
||||
#define HW_ALSEC 0x60
|
||||
#define HW_ALMIN 0x64
|
||||
#define HW_ALHOUR 0x68
|
||||
#define HW_ALDOM 0x6C
|
||||
#define HW_ALDOW 0x70
|
||||
#define HW_ALDOY 0x74
|
||||
#define HW_ALMON 0x78
|
||||
#define HW_ALYEAR 0x7C
|
||||
|
||||
struct asm9260_rtc_priv {
|
||||
struct device *dev;
|
||||
void __iomem *iobase;
|
||||
struct rtc_device *rtc;
|
||||
struct clk *clk;
|
||||
/* io lock */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = dev_id;
|
||||
u32 isr;
|
||||
unsigned long events = 0;
|
||||
|
||||
isr = ioread32(priv->iobase + HW_CIIR);
|
||||
if (!isr)
|
||||
return IRQ_NONE;
|
||||
|
||||
iowrite32(0, priv->iobase + HW_CIIR);
|
||||
|
||||
events |= RTC_AF | RTC_IRQF;
|
||||
|
||||
rtc_update_irq(priv->rtc, 1, events);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
|
||||
u32 ctime0, ctime1, ctime2;
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irq_flags);
|
||||
ctime0 = ioread32(priv->iobase + HW_CTIME0);
|
||||
ctime1 = ioread32(priv->iobase + HW_CTIME1);
|
||||
ctime2 = ioread32(priv->iobase + HW_CTIME2);
|
||||
|
||||
if (ctime1 != ioread32(priv->iobase + HW_CTIME1)) {
|
||||
/*
|
||||
* woops, counter flipped right now. Now we are safe
|
||||
* to reread.
|
||||
*/
|
||||
ctime0 = ioread32(priv->iobase + HW_CTIME0);
|
||||
ctime1 = ioread32(priv->iobase + HW_CTIME1);
|
||||
ctime2 = ioread32(priv->iobase + HW_CTIME2);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, irq_flags);
|
||||
|
||||
tm->tm_sec = (ctime0 >> BM_CTIME0_SEC_S) & BM_CTIME0_SEC_M;
|
||||
tm->tm_min = (ctime0 >> BM_CTIME0_MIN_S) & BM_CTIME0_MIN_M;
|
||||
tm->tm_hour = (ctime0 >> BM_CTIME0_HOUR_S) & BM_CTIME0_HOUR_M;
|
||||
tm->tm_wday = (ctime0 >> BM_CTIME0_DOW_S) & BM_CTIME0_DOW_M;
|
||||
|
||||
tm->tm_mday = (ctime1 >> BM_CTIME1_DOM_S) & BM_CTIME1_DOM_M;
|
||||
tm->tm_mon = (ctime1 >> BM_CTIME1_MON_S) & BM_CTIME1_MON_M;
|
||||
tm->tm_year = (ctime1 >> BM_CTIME1_YEAR_S) & BM_CTIME1_YEAR_M;
|
||||
|
||||
tm->tm_yday = (ctime2 >> BM_CTIME2_DOY_S) & BM_CTIME2_DOY_M;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irq_flags);
|
||||
/*
|
||||
* make sure SEC counter will not flip other counter on write time,
|
||||
* real value will be written at the enf of sequence.
|
||||
*/
|
||||
iowrite32(0, priv->iobase + HW_SEC);
|
||||
|
||||
iowrite32(tm->tm_year, priv->iobase + HW_YEAR);
|
||||
iowrite32(tm->tm_mon, priv->iobase + HW_MONTH);
|
||||
iowrite32(tm->tm_mday, priv->iobase + HW_DOM);
|
||||
iowrite32(tm->tm_wday, priv->iobase + HW_DOW);
|
||||
iowrite32(tm->tm_yday, priv->iobase + HW_DOY);
|
||||
iowrite32(tm->tm_hour, priv->iobase + HW_HOUR);
|
||||
iowrite32(tm->tm_min, priv->iobase + HW_MIN);
|
||||
iowrite32(tm->tm_sec, priv->iobase + HW_SEC);
|
||||
spin_unlock_irqrestore(&priv->lock, irq_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irq_flags);
|
||||
alrm->time.tm_year = ioread32(priv->iobase + HW_ALYEAR);
|
||||
alrm->time.tm_mon = ioread32(priv->iobase + HW_ALMON);
|
||||
alrm->time.tm_mday = ioread32(priv->iobase + HW_ALDOM);
|
||||
alrm->time.tm_wday = ioread32(priv->iobase + HW_ALDOW);
|
||||
alrm->time.tm_yday = ioread32(priv->iobase + HW_ALDOY);
|
||||
alrm->time.tm_hour = ioread32(priv->iobase + HW_ALHOUR);
|
||||
alrm->time.tm_min = ioread32(priv->iobase + HW_ALMIN);
|
||||
alrm->time.tm_sec = ioread32(priv->iobase + HW_ALSEC);
|
||||
|
||||
alrm->enabled = ioread32(priv->iobase + HW_AMR) ? 1 : 0;
|
||||
alrm->pending = ioread32(priv->iobase + HW_CIIR) ? 1 : 0;
|
||||
spin_unlock_irqrestore(&priv->lock, irq_flags);
|
||||
|
||||
return rtc_valid_tm(&alrm->time);
|
||||
}
|
||||
|
||||
static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irq_flags);
|
||||
iowrite32(alrm->time.tm_year, priv->iobase + HW_ALYEAR);
|
||||
iowrite32(alrm->time.tm_mon, priv->iobase + HW_ALMON);
|
||||
iowrite32(alrm->time.tm_mday, priv->iobase + HW_ALDOM);
|
||||
iowrite32(alrm->time.tm_wday, priv->iobase + HW_ALDOW);
|
||||
iowrite32(alrm->time.tm_yday, priv->iobase + HW_ALDOY);
|
||||
iowrite32(alrm->time.tm_hour, priv->iobase + HW_ALHOUR);
|
||||
iowrite32(alrm->time.tm_min, priv->iobase + HW_ALMIN);
|
||||
iowrite32(alrm->time.tm_sec, priv->iobase + HW_ALSEC);
|
||||
|
||||
iowrite32(alrm->enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR);
|
||||
spin_unlock_irqrestore(&priv->lock, irq_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asm9260_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
iowrite32(enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops asm9260_rtc_ops = {
|
||||
.read_time = asm9260_rtc_read_time,
|
||||
.set_time = asm9260_rtc_set_time,
|
||||
.read_alarm = asm9260_rtc_read_alarm,
|
||||
.set_alarm = asm9260_rtc_set_alarm,
|
||||
.alarm_irq_enable = asm9260_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int __init asm9260_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int irq_alarm, ret;
|
||||
u32 ccr;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(struct asm9260_rtc_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
irq_alarm = platform_get_irq(pdev, 0);
|
||||
if (irq_alarm < 0) {
|
||||
dev_err(dev, "No alarm IRQ resource defined\n");
|
||||
return irq_alarm;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->iobase = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->iobase))
|
||||
return PTR_ERR(priv->iobase);
|
||||
|
||||
priv->clk = devm_clk_get(dev, "ahb");
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable clk!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ccr = ioread32(priv->iobase + HW_CCR);
|
||||
/* if dev is not enabled, reset it */
|
||||
if ((ccr & (BM_CLKEN | BM_CTCRST)) != BM_CLKEN) {
|
||||
iowrite32(BM_CTCRST, priv->iobase + HW_CCR);
|
||||
ccr = 0;
|
||||
}
|
||||
|
||||
iowrite32(BM_CLKEN | ccr, priv->iobase + HW_CCR);
|
||||
iowrite32(0, priv->iobase + HW_CIIR);
|
||||
iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR);
|
||||
|
||||
priv->rtc = devm_rtc_device_register(dev, dev_name(dev),
|
||||
&asm9260_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(priv->rtc)) {
|
||||
ret = PTR_ERR(priv->rtc);
|
||||
dev_err(dev, "Failed to register RTC device: %d\n", ret);
|
||||
goto err_return;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq_alarm, NULL,
|
||||
asm9260_rtc_irq, IRQF_ONESHOT,
|
||||
dev_name(dev), priv);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't get irq %i, err %d\n",
|
||||
irq_alarm, ret);
|
||||
goto err_return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_return:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit asm9260_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct asm9260_rtc_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
/* Disable alarm matching */
|
||||
iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id asm9260_dt_ids[] = {
|
||||
{ .compatible = "alphascale,asm9260-rtc", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver asm9260_rtc_driver = {
|
||||
.probe = asm9260_rtc_probe,
|
||||
.remove = asm9260_rtc_remove,
|
||||
.driver = {
|
||||
.name = "asm9260-rtc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = asm9260_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(asm9260_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
|
||||
MODULE_DESCRIPTION("Alphascale asm9260 SoC Realtime Clock Driver (RTC)");
|
||||
MODULE_LICENSE("GPL");
|
@ -532,7 +532,7 @@ ds1305_nvram_read(struct file *filp, struct kobject *kobj,
|
||||
struct spi_transfer x[2];
|
||||
int status;
|
||||
|
||||
spi = container_of(kobj, struct spi_device, dev.kobj);
|
||||
spi = to_spi_device(kobj_to_dev(kobj));
|
||||
|
||||
addr = DS1305_NVRAM + off;
|
||||
msg_init(&m, x, &addr, count, NULL, buf);
|
||||
@ -554,7 +554,7 @@ ds1305_nvram_write(struct file *filp, struct kobject *kobj,
|
||||
struct spi_transfer x[2];
|
||||
int status;
|
||||
|
||||
spi = container_of(kobj, struct spi_device, dev.kobj);
|
||||
spi = to_spi_device(kobj_to_dev(kobj));
|
||||
|
||||
addr = (DS1305_WRITE | DS1305_NVRAM) + off;
|
||||
msg_init(&m, x, &addr, count, buf, NULL);
|
||||
|
@ -19,6 +19,9 @@
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
/*
|
||||
* We can't determine type by probing, but if we expect pre-Linux code
|
||||
@ -89,6 +92,7 @@ enum ds_type {
|
||||
# define DS1340_BIT_OSF 0x80
|
||||
#define DS1337_REG_STATUS 0x0f
|
||||
# define DS1337_BIT_OSF 0x80
|
||||
# define DS3231_BIT_EN32KHZ 0x08
|
||||
# define DS1337_BIT_A2I 0x02
|
||||
# define DS1337_BIT_A1I 0x01
|
||||
#define DS1339_REG_ALARM1_SECS 0x07
|
||||
@ -118,6 +122,9 @@ struct ds1307 {
|
||||
u8 length, u8 *values);
|
||||
s32 (*write_block_data)(const struct i2c_client *client, u8 command,
|
||||
u8 length, const u8 *values);
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw clks[2];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct chip_desc {
|
||||
@ -842,6 +849,378 @@ out:
|
||||
return;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_RTC_DRV_DS1307_HWMON
|
||||
|
||||
/*
|
||||
* Temperature sensor support for ds3231 devices.
|
||||
*/
|
||||
|
||||
#define DS3231_REG_TEMPERATURE 0x11
|
||||
|
||||
/*
|
||||
* A user-initiated temperature conversion is not started by this function,
|
||||
* so the temperature is updated once every 64 seconds.
|
||||
*/
|
||||
static int ds3231_hwmon_read_temp(struct device *dev, s16 *mC)
|
||||
{
|
||||
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
||||
u8 temp_buf[2];
|
||||
s16 temp;
|
||||
int ret;
|
||||
|
||||
ret = ds1307->read_block_data(ds1307->client, DS3231_REG_TEMPERATURE,
|
||||
sizeof(temp_buf), temp_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != sizeof(temp_buf))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Temperature is represented as a 10-bit code with a resolution of
|
||||
* 0.25 degree celsius and encoded in two's complement format.
|
||||
*/
|
||||
temp = (temp_buf[0] << 8) | temp_buf[1];
|
||||
temp >>= 6;
|
||||
*mC = temp * 250;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ds3231_hwmon_show_temp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
s16 temp;
|
||||
|
||||
ret = ds3231_hwmon_read_temp(dev, &temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ds3231_hwmon_show_temp,
|
||||
NULL, 0);
|
||||
|
||||
static struct attribute *ds3231_hwmon_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ds3231_hwmon);
|
||||
|
||||
static void ds1307_hwmon_register(struct ds1307 *ds1307)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
if (ds1307->type != ds_3231)
|
||||
return;
|
||||
|
||||
dev = devm_hwmon_device_register_with_groups(&ds1307->client->dev,
|
||||
ds1307->client->name,
|
||||
ds1307, ds3231_hwmon_groups);
|
||||
if (IS_ERR(dev)) {
|
||||
dev_warn(&ds1307->client->dev,
|
||||
"unable to register hwmon device %ld\n", PTR_ERR(dev));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void ds1307_hwmon_register(struct ds1307 *ds1307)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_RTC_DRV_DS1307_HWMON */
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Square-wave output support for DS3231
|
||||
* Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
|
||||
*/
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
|
||||
enum {
|
||||
DS3231_CLK_SQW = 0,
|
||||
DS3231_CLK_32KHZ,
|
||||
};
|
||||
|
||||
#define clk_sqw_to_ds1307(clk) \
|
||||
container_of(clk, struct ds1307, clks[DS3231_CLK_SQW])
|
||||
#define clk_32khz_to_ds1307(clk) \
|
||||
container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ])
|
||||
|
||||
static int ds3231_clk_sqw_rates[] = {
|
||||
1,
|
||||
1024,
|
||||
4096,
|
||||
8192,
|
||||
};
|
||||
|
||||
static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value)
|
||||
{
|
||||
struct i2c_client *client = ds1307->client;
|
||||
struct mutex *lock = &ds1307->rtc->ops_lock;
|
||||
int control;
|
||||
int ret;
|
||||
|
||||
mutex_lock(lock);
|
||||
|
||||
control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
|
||||
if (control < 0) {
|
||||
ret = control;
|
||||
goto out;
|
||||
}
|
||||
|
||||
control &= ~mask;
|
||||
control |= value;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control);
|
||||
out:
|
||||
mutex_unlock(lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||
int control;
|
||||
int rate_sel = 0;
|
||||
|
||||
control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL);
|
||||
if (control < 0)
|
||||
return control;
|
||||
if (control & DS1337_BIT_RS1)
|
||||
rate_sel += 1;
|
||||
if (control & DS1337_BIT_RS2)
|
||||
rate_sel += 2;
|
||||
|
||||
return ds3231_clk_sqw_rates[rate_sel];
|
||||
}
|
||||
|
||||
static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) {
|
||||
if (ds3231_clk_sqw_rates[i] <= rate)
|
||||
return ds3231_clk_sqw_rates[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||
int control = 0;
|
||||
int rate_sel;
|
||||
|
||||
for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds3231_clk_sqw_rates);
|
||||
rate_sel++) {
|
||||
if (ds3231_clk_sqw_rates[rate_sel] == rate)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate_sel == ARRAY_SIZE(ds3231_clk_sqw_rates))
|
||||
return -EINVAL;
|
||||
|
||||
if (rate_sel & 1)
|
||||
control |= DS1337_BIT_RS1;
|
||||
if (rate_sel & 2)
|
||||
control |= DS1337_BIT_RS2;
|
||||
|
||||
return ds1337_write_control(ds1307, DS1337_BIT_RS1 | DS1337_BIT_RS2,
|
||||
control);
|
||||
}
|
||||
|
||||
static int ds3231_clk_sqw_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||
|
||||
return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0);
|
||||
}
|
||||
|
||||
static void ds3231_clk_sqw_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||
|
||||
ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN);
|
||||
}
|
||||
|
||||
static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
||||
int control;
|
||||
|
||||
control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL);
|
||||
if (control < 0)
|
||||
return control;
|
||||
|
||||
return !(control & DS1337_BIT_INTCN);
|
||||
}
|
||||
|
||||
static const struct clk_ops ds3231_clk_sqw_ops = {
|
||||
.prepare = ds3231_clk_sqw_prepare,
|
||||
.unprepare = ds3231_clk_sqw_unprepare,
|
||||
.is_prepared = ds3231_clk_sqw_is_prepared,
|
||||
.recalc_rate = ds3231_clk_sqw_recalc_rate,
|
||||
.round_rate = ds3231_clk_sqw_round_rate,
|
||||
.set_rate = ds3231_clk_sqw_set_rate,
|
||||
};
|
||||
|
||||
static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 32768;
|
||||
}
|
||||
|
||||
static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable)
|
||||
{
|
||||
struct i2c_client *client = ds1307->client;
|
||||
struct mutex *lock = &ds1307->rtc->ops_lock;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
mutex_lock(lock);
|
||||
|
||||
status = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
|
||||
if (status < 0) {
|
||||
ret = status;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
status |= DS3231_BIT_EN32KHZ;
|
||||
else
|
||||
status &= ~DS3231_BIT_EN32KHZ;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, status);
|
||||
out:
|
||||
mutex_unlock(lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ds3231_clk_32khz_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
||||
|
||||
return ds3231_clk_32khz_control(ds1307, true);
|
||||
}
|
||||
|
||||
static void ds3231_clk_32khz_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
||||
|
||||
ds3231_clk_32khz_control(ds1307, false);
|
||||
}
|
||||
|
||||
static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
||||
int status;
|
||||
|
||||
status = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_STATUS);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return !!(status & DS3231_BIT_EN32KHZ);
|
||||
}
|
||||
|
||||
static const struct clk_ops ds3231_clk_32khz_ops = {
|
||||
.prepare = ds3231_clk_32khz_prepare,
|
||||
.unprepare = ds3231_clk_32khz_unprepare,
|
||||
.is_prepared = ds3231_clk_32khz_is_prepared,
|
||||
.recalc_rate = ds3231_clk_32khz_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk_init_data ds3231_clks_init[] = {
|
||||
[DS3231_CLK_SQW] = {
|
||||
.name = "ds3231_clk_sqw",
|
||||
.ops = &ds3231_clk_sqw_ops,
|
||||
.flags = CLK_IS_ROOT,
|
||||
},
|
||||
[DS3231_CLK_32KHZ] = {
|
||||
.name = "ds3231_clk_32khz",
|
||||
.ops = &ds3231_clk_32khz_ops,
|
||||
.flags = CLK_IS_ROOT,
|
||||
},
|
||||
};
|
||||
|
||||
static int ds3231_clks_register(struct ds1307 *ds1307)
|
||||
{
|
||||
struct i2c_client *client = ds1307->client;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
struct clk_onecell_data *onecell;
|
||||
int i;
|
||||
|
||||
onecell = devm_kzalloc(&client->dev, sizeof(*onecell), GFP_KERNEL);
|
||||
if (!onecell)
|
||||
return -ENOMEM;
|
||||
|
||||
onecell->clk_num = ARRAY_SIZE(ds3231_clks_init);
|
||||
onecell->clks = devm_kcalloc(&client->dev, onecell->clk_num,
|
||||
sizeof(onecell->clks[0]), GFP_KERNEL);
|
||||
if (!onecell->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ds3231_clks_init); i++) {
|
||||
struct clk_init_data init = ds3231_clks_init[i];
|
||||
|
||||
/*
|
||||
* Interrupt signal due to alarm conditions and square-wave
|
||||
* output share same pin, so don't initialize both.
|
||||
*/
|
||||
if (i == DS3231_CLK_SQW && test_bit(HAS_ALARM, &ds1307->flags))
|
||||
continue;
|
||||
|
||||
/* optional override of the clockname */
|
||||
of_property_read_string_index(node, "clock-output-names", i,
|
||||
&init.name);
|
||||
ds1307->clks[i].init = &init;
|
||||
|
||||
onecell->clks[i] = devm_clk_register(&client->dev,
|
||||
&ds1307->clks[i]);
|
||||
if (IS_ERR(onecell->clks[i]))
|
||||
return PTR_ERR(onecell->clks[i]);
|
||||
}
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
of_clk_add_provider(node, of_clk_src_onecell_get, onecell);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds1307_clks_register(struct ds1307 *ds1307)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ds1307->type != ds_3231)
|
||||
return;
|
||||
|
||||
ret = ds3231_clks_register(ds1307);
|
||||
if (ret) {
|
||||
dev_warn(&ds1307->client->dev,
|
||||
"unable to register clock device %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void ds1307_clks_register(struct ds1307 *ds1307)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_COMMON_CLK */
|
||||
|
||||
static int ds1307_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -851,6 +1230,7 @@ static int ds1307_probe(struct i2c_client *client,
|
||||
struct chip_desc *chip = &chips[id->driver_data];
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
bool want_irq = false;
|
||||
bool ds1307_can_wakeup_device = false;
|
||||
unsigned char *buf;
|
||||
struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
irq_handler_t irq_handler = ds1307_irq;
|
||||
@ -898,6 +1278,20 @@ static int ds1307_probe(struct i2c_client *client,
|
||||
ds1307->write_block_data = ds1307_write_block_data;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For devices with no IRQ directly connected to the SoC, the RTC chip
|
||||
* can be forced as a wakeup source by stating that explicitly in
|
||||
* the device's .dts file using the "wakeup-source" boolean property.
|
||||
* If the "wakeup-source" property is set, don't request an IRQ.
|
||||
* This will guarantee the 'wakealarm' sysfs entry is available on the device,
|
||||
* if supported by the RTC.
|
||||
*/
|
||||
if (of_property_read_bool(client->dev.of_node, "wakeup-source")) {
|
||||
ds1307_can_wakeup_device = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (ds1307->type) {
|
||||
case ds_1337:
|
||||
case ds_1339:
|
||||
@ -916,11 +1310,13 @@ static int ds1307_probe(struct i2c_client *client,
|
||||
ds1307->regs[0] &= ~DS1337_BIT_nEOSC;
|
||||
|
||||
/*
|
||||
* Using IRQ? Disable the square wave and both alarms.
|
||||
* Using IRQ or defined as wakeup-source?
|
||||
* Disable the square wave and both alarms.
|
||||
* For some variants, be sure alarms can trigger when we're
|
||||
* running on Vbackup (BBSQI/BBSQW)
|
||||
*/
|
||||
if (ds1307->client->irq > 0 && chip->alarm) {
|
||||
if (chip->alarm && (ds1307->client->irq > 0 ||
|
||||
ds1307_can_wakeup_device)) {
|
||||
ds1307->regs[0] |= DS1337_BIT_INTCN
|
||||
| bbsqi_bitpos[ds1307->type];
|
||||
ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
|
||||
@ -1135,6 +1531,14 @@ read_rtc:
|
||||
return PTR_ERR(ds1307->rtc);
|
||||
}
|
||||
|
||||
if (ds1307_can_wakeup_device) {
|
||||
/* Disable request for an IRQ */
|
||||
want_irq = false;
|
||||
dev_info(&client->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n");
|
||||
/* We cannot support UIE mode if we do not have an IRQ line */
|
||||
ds1307->rtc->uie_unsupported = 1;
|
||||
}
|
||||
|
||||
if (want_irq) {
|
||||
err = devm_request_threaded_irq(&client->dev,
|
||||
client->irq, NULL, irq_handler,
|
||||
@ -1182,6 +1586,9 @@ read_rtc:
|
||||
}
|
||||
}
|
||||
|
||||
ds1307_hwmon_register(ds1307);
|
||||
ds1307_clks_register(ds1307);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
|
@ -187,9 +187,9 @@ ds1685_rtc_end_data_access(struct ds1685_priv *rtc)
|
||||
* Only use this where you are certain another lock will not be held.
|
||||
*/
|
||||
static inline void
|
||||
ds1685_rtc_begin_ctrl_access(struct ds1685_priv *rtc, unsigned long flags)
|
||||
ds1685_rtc_begin_ctrl_access(struct ds1685_priv *rtc, unsigned long *flags)
|
||||
{
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
spin_lock_irqsave(&rtc->lock, *flags);
|
||||
ds1685_rtc_switch_to_bank1(rtc);
|
||||
}
|
||||
|
||||
@ -1300,7 +1300,7 @@ ds1685_rtc_sysfs_ctrl_regs_store(struct device *dev,
|
||||
{
|
||||
struct ds1685_priv *rtc = dev_get_drvdata(dev);
|
||||
u8 reg = 0, bit = 0, tmp;
|
||||
unsigned long flags = 0;
|
||||
unsigned long flags;
|
||||
long int val = 0;
|
||||
const struct ds1685_rtc_ctrl_regs *reg_info =
|
||||
ds1685_rtc_sysfs_ctrl_regs_lookup(attr->attr.name);
|
||||
@ -1321,7 +1321,7 @@ ds1685_rtc_sysfs_ctrl_regs_store(struct device *dev,
|
||||
bit = reg_info->bit;
|
||||
|
||||
/* Safe to spinlock during a write. */
|
||||
ds1685_rtc_begin_ctrl_access(rtc, flags);
|
||||
ds1685_rtc_begin_ctrl_access(rtc, &flags);
|
||||
tmp = rtc->read(rtc, reg);
|
||||
rtc->write(rtc, reg, (val ? (tmp | bit) : (tmp & ~(bit))));
|
||||
ds1685_rtc_end_ctrl_access(rtc, flags);
|
||||
@ -2161,6 +2161,7 @@ ds1685_rtc_poweroff(struct platform_device *pdev)
|
||||
/* Check for valid RTC data, else, spin forever. */
|
||||
if (unlikely(!pdev)) {
|
||||
pr_emerg("platform device data not available, spinning forever ...\n");
|
||||
while(1);
|
||||
unreachable();
|
||||
} else {
|
||||
/* Get the rtc data. */
|
||||
|
@ -1,19 +1,15 @@
|
||||
/*
|
||||
* RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
|
||||
* RTC client/driver for the Maxim/Dallas DS3232/DS3234 Real-Time Clock
|
||||
*
|
||||
* Copyright (C) 2009-2011 Freescale Semiconductor.
|
||||
* Author: Jack Lan <jack.lan@freescale.com>
|
||||
* Copyright (C) 2008 MIMOMax Wireless Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
/*
|
||||
* It would be more efficient to use i2c msgs/i2c_transfer directly but, as
|
||||
* recommened in .../Documentation/i2c/writing-clients section
|
||||
* "Sending and receiving", using SMBus level communication is preferred.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
@ -21,10 +17,11 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define DS3232_REG_SECONDS 0x00
|
||||
#define DS3232_REG_MINUTES 0x01
|
||||
@ -50,39 +47,33 @@
|
||||
# define DS3232_REG_SR_A1F 0x01
|
||||
|
||||
struct ds3232 {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
int irq;
|
||||
struct rtc_device *rtc;
|
||||
struct work_struct work;
|
||||
|
||||
/* The mutex protects alarm operations, and prevents a race
|
||||
* between the enable_irq() in the workqueue and the free_irq()
|
||||
* in the remove function.
|
||||
*/
|
||||
struct mutex mutex;
|
||||
bool suspended;
|
||||
int exiting;
|
||||
};
|
||||
|
||||
static struct i2c_driver ds3232_driver;
|
||||
|
||||
static int ds3232_check_rtc_status(struct i2c_client *client)
|
||||
static int ds3232_check_rtc_status(struct device *dev)
|
||||
{
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
int control, stat;
|
||||
|
||||
stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
|
||||
if (stat < 0)
|
||||
return stat;
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (stat & DS3232_REG_SR_OSF)
|
||||
dev_warn(&client->dev,
|
||||
dev_warn(dev,
|
||||
"oscillator discontinuity flagged, "
|
||||
"time unreliable\n");
|
||||
|
||||
stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
|
||||
if (ret < 0)
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If the alarm is pending, clear it before requesting
|
||||
@ -90,31 +81,28 @@ static int ds3232_check_rtc_status(struct i2c_client *client)
|
||||
* before everything is initialized.
|
||||
*/
|
||||
|
||||
control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
|
||||
if (control < 0)
|
||||
return control;
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
|
||||
control |= DS3232_REG_CR_INTCN;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
|
||||
return regmap_write(ds3232->regmap, DS3232_REG_CR, control);
|
||||
}
|
||||
|
||||
static int ds3232_read_time(struct device *dev, struct rtc_time *time)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
u8 buf[7];
|
||||
unsigned int year, month, day, hour, minute, second;
|
||||
unsigned int week, twelve_hr, am_pm;
|
||||
unsigned int century, add_century = 0;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
|
||||
|
||||
if (ret < 0)
|
||||
ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_SECONDS, buf, 7);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret < 7)
|
||||
return -EIO;
|
||||
|
||||
second = buf[0];
|
||||
minute = buf[1];
|
||||
@ -159,7 +147,7 @@ static int ds3232_read_time(struct device *dev, struct rtc_time *time)
|
||||
|
||||
static int ds3232_set_time(struct device *dev, struct rtc_time *time)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
u8 buf[7];
|
||||
|
||||
/* Extract time from rtc_time and load into ds3232*/
|
||||
@ -179,8 +167,7 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time)
|
||||
buf[6] = bin2bcd(time->tm_year);
|
||||
}
|
||||
|
||||
return i2c_smbus_write_i2c_block_data(client,
|
||||
DS3232_REG_SECONDS, 7, buf);
|
||||
return regmap_bulk_write(ds3232->regmap, DS3232_REG_SECONDS, buf, 7);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -190,24 +177,19 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time)
|
||||
*/
|
||||
static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds3232 *ds3232 = i2c_get_clientdata(client);
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
int control, stat;
|
||||
int ret;
|
||||
u8 buf[4];
|
||||
|
||||
mutex_lock(&ds3232->mutex);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
|
||||
if (ret < 0)
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat);
|
||||
if (ret)
|
||||
goto out;
|
||||
stat = ret;
|
||||
ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
|
||||
if (ret < 0)
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control);
|
||||
if (ret)
|
||||
goto out;
|
||||
control = ret;
|
||||
ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
|
||||
if (ret < 0)
|
||||
ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_ALARM1, buf, 4);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
|
||||
@ -226,7 +208,6 @@ static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&ds3232->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -236,166 +217,129 @@ out:
|
||||
*/
|
||||
static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds3232 *ds3232 = i2c_get_clientdata(client);
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
int control, stat;
|
||||
int ret;
|
||||
u8 buf[4];
|
||||
|
||||
if (client->irq <= 0)
|
||||
if (ds3232->irq <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ds3232->mutex);
|
||||
|
||||
buf[0] = bin2bcd(alarm->time.tm_sec);
|
||||
buf[1] = bin2bcd(alarm->time.tm_min);
|
||||
buf[2] = bin2bcd(alarm->time.tm_hour);
|
||||
buf[3] = bin2bcd(alarm->time.tm_mday);
|
||||
|
||||
/* clear alarm interrupt enable bit */
|
||||
ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
|
||||
if (ret < 0)
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control);
|
||||
if (ret)
|
||||
goto out;
|
||||
control = ret;
|
||||
control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
|
||||
ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
|
||||
if (ret < 0)
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* clear any pending alarm flag */
|
||||
ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
|
||||
if (ret < 0)
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat);
|
||||
if (ret)
|
||||
goto out;
|
||||
stat = ret;
|
||||
stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
|
||||
ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
|
||||
if (ret < 0)
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
|
||||
ret = regmap_bulk_write(ds3232->regmap, DS3232_REG_ALARM1, buf, 4);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (alarm->enabled) {
|
||||
control |= DS3232_REG_CR_A1IE;
|
||||
ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ds3232->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ds3232_update_alarm(struct i2c_client *client)
|
||||
static int ds3232_update_alarm(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct ds3232 *ds3232 = i2c_get_clientdata(client);
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
int control;
|
||||
int ret;
|
||||
u8 buf[4];
|
||||
|
||||
mutex_lock(&ds3232->mutex);
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
|
||||
0x80 : buf[0];
|
||||
buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
|
||||
0x80 : buf[1];
|
||||
buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
|
||||
0x80 : buf[2];
|
||||
buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
|
||||
0x80 : buf[3];
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
|
||||
if (control < 0)
|
||||
goto unlock;
|
||||
|
||||
if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF))
|
||||
if (enabled)
|
||||
/* enable alarm1 interrupt */
|
||||
control |= DS3232_REG_CR_A1IE;
|
||||
else
|
||||
/* disable alarm1 interrupt */
|
||||
control &= ~(DS3232_REG_CR_A1IE);
|
||||
i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ds3232->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds3232 *ds3232 = i2c_get_clientdata(client);
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
|
||||
if (client->irq <= 0)
|
||||
if (ds3232->irq <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (enabled)
|
||||
ds3232->rtc->irq_data |= RTC_AF;
|
||||
else
|
||||
ds3232->rtc->irq_data &= ~RTC_AF;
|
||||
|
||||
ds3232_update_alarm(client);
|
||||
return 0;
|
||||
return ds3232_update_alarm(dev, enabled);
|
||||
}
|
||||
|
||||
static irqreturn_t ds3232_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *client = dev_id;
|
||||
struct ds3232 *ds3232 = i2c_get_clientdata(client);
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
|
||||
/*
|
||||
* If rtc as a wakeup source, can't schedule the work
|
||||
* at system resume flow, because at this time the i2c bus
|
||||
* has not been resumed.
|
||||
*/
|
||||
if (!ds3232->suspended)
|
||||
schedule_work(&ds3232->work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ds3232_work(struct work_struct *work)
|
||||
{
|
||||
struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
|
||||
struct i2c_client *client = ds3232->client;
|
||||
struct device *dev = dev_id;
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
struct mutex *lock = &ds3232->rtc->ops_lock;
|
||||
int ret;
|
||||
int stat, control;
|
||||
|
||||
mutex_lock(&ds3232->mutex);
|
||||
mutex_lock(lock);
|
||||
|
||||
stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
|
||||
if (stat < 0)
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if (stat & DS3232_REG_SR_A1F) {
|
||||
control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
|
||||
if (control < 0) {
|
||||
pr_warn("Read Control Register error - Disable IRQ%d\n",
|
||||
client->irq);
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Read Control Register error %d\n", ret);
|
||||
} else {
|
||||
/* disable alarm1 interrupt */
|
||||
control &= ~(DS3232_REG_CR_A1IE);
|
||||
i2c_smbus_write_byte_data(client, DS3232_REG_CR,
|
||||
control);
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_CR,
|
||||
control);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Write Control Register error %d\n",
|
||||
ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* clear the alarm pend flag */
|
||||
stat &= ~DS3232_REG_SR_A1F;
|
||||
i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Write Status Register error %d\n",
|
||||
ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
if (!ds3232->exiting)
|
||||
enable_irq(client->irq);
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ds3232->mutex);
|
||||
mutex_unlock(lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds3232_rtc_ops = {
|
||||
@ -406,67 +350,50 @@ static const struct rtc_class_ops ds3232_rtc_ops = {
|
||||
.alarm_irq_enable = ds3232_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int ds3232_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name)
|
||||
{
|
||||
struct ds3232 *ds3232;
|
||||
int ret;
|
||||
|
||||
ds3232 = devm_kzalloc(&client->dev, sizeof(struct ds3232), GFP_KERNEL);
|
||||
ds3232 = devm_kzalloc(dev, sizeof(*ds3232), GFP_KERNEL);
|
||||
if (!ds3232)
|
||||
return -ENOMEM;
|
||||
|
||||
ds3232->client = client;
|
||||
i2c_set_clientdata(client, ds3232);
|
||||
ds3232->regmap = regmap;
|
||||
ds3232->irq = irq;
|
||||
ds3232->dev = dev;
|
||||
dev_set_drvdata(dev, ds3232);
|
||||
|
||||
INIT_WORK(&ds3232->work, ds3232_work);
|
||||
mutex_init(&ds3232->mutex);
|
||||
|
||||
ret = ds3232_check_rtc_status(client);
|
||||
ret = ds3232_check_rtc_status(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_irq(&client->dev, client->irq, ds3232_irq,
|
||||
IRQF_SHARED, "ds3232", client);
|
||||
if (ds3232->irq > 0) {
|
||||
ret = devm_request_threaded_irq(dev, ds3232->irq, NULL,
|
||||
ds3232_irq,
|
||||
IRQF_SHARED | IRQF_ONESHOT,
|
||||
name, dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "unable to request IRQ\n");
|
||||
}
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
ds3232->irq = 0;
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
} else
|
||||
device_init_wakeup(dev, 1);
|
||||
}
|
||||
ds3232->rtc = devm_rtc_device_register(&client->dev, client->name,
|
||||
&ds3232_rtc_ops, THIS_MODULE);
|
||||
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
|
||||
THIS_MODULE);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ds3232->rtc);
|
||||
}
|
||||
|
||||
static int ds3232_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ds3232 *ds3232 = i2c_get_clientdata(client);
|
||||
|
||||
if (client->irq > 0) {
|
||||
mutex_lock(&ds3232->mutex);
|
||||
ds3232->exiting = 1;
|
||||
mutex_unlock(&ds3232->mutex);
|
||||
|
||||
devm_free_irq(&client->dev, client->irq, client);
|
||||
cancel_work_sync(&ds3232->work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ds3232_suspend(struct device *dev)
|
||||
{
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (device_can_wakeup(dev)) {
|
||||
ds3232->suspended = true;
|
||||
if (irq_set_irq_wake(client->irq, 1)) {
|
||||
if (device_may_wakeup(dev)) {
|
||||
if (enable_irq_wake(ds3232->irq))
|
||||
dev_warn_once(dev, "Cannot set wakeup source\n");
|
||||
ds3232->suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -475,16 +402,9 @@ static int ds3232_suspend(struct device *dev)
|
||||
static int ds3232_resume(struct device *dev)
|
||||
{
|
||||
struct ds3232 *ds3232 = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (ds3232->suspended) {
|
||||
ds3232->suspended = false;
|
||||
|
||||
/* Clear the hardware alarm pend flag */
|
||||
schedule_work(&ds3232->work);
|
||||
|
||||
irq_set_irq_wake(client->irq, 0);
|
||||
}
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(ds3232->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -494,6 +414,27 @@ static const struct dev_pm_ops ds3232_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume)
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int ds3232_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "%s: regmap allocation failed: %ld\n",
|
||||
__func__, PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return ds3232_probe(&client->dev, regmap, client->irq, client->name);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ds3232_id[] = {
|
||||
{ "ds3232", 0 },
|
||||
{ }
|
||||
@ -505,13 +446,162 @@ static struct i2c_driver ds3232_driver = {
|
||||
.name = "rtc-ds3232",
|
||||
.pm = &ds3232_pm_ops,
|
||||
},
|
||||
.probe = ds3232_probe,
|
||||
.remove = ds3232_remove,
|
||||
.probe = ds3232_i2c_probe,
|
||||
.id_table = ds3232_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ds3232_driver);
|
||||
static int ds3232_register_driver(void)
|
||||
{
|
||||
return i2c_add_driver(&ds3232_driver);
|
||||
}
|
||||
|
||||
static void ds3232_unregister_driver(void)
|
||||
{
|
||||
i2c_del_driver(&ds3232_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int ds3232_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds3232_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
static int ds3234_probe(struct spi_device *spi)
|
||||
{
|
||||
int res;
|
||||
unsigned int tmp;
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.write_flag_mask = 0x80,
|
||||
};
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n",
|
||||
__func__, PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
spi->mode = SPI_MODE_3;
|
||||
spi->bits_per_word = 8;
|
||||
spi_setup(spi);
|
||||
|
||||
res = regmap_read(regmap, DS3232_REG_SECONDS, &tmp);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
/* Control settings
|
||||
*
|
||||
* CONTROL_REG
|
||||
* BIT 7 6 5 4 3 2 1 0
|
||||
* EOSC BBSQW CONV RS2 RS1 INTCN A2IE A1IE
|
||||
*
|
||||
* 0 0 0 1 1 1 0 0
|
||||
*
|
||||
* CONTROL_STAT_REG
|
||||
* BIT 7 6 5 4 3 2 1 0
|
||||
* OSF BB32kHz CRATE1 CRATE0 EN32kHz BSY A2F A1F
|
||||
*
|
||||
* 1 0 0 0 1 0 0 0
|
||||
*/
|
||||
res = regmap_read(regmap, DS3232_REG_CR, &tmp);
|
||||
if (res)
|
||||
return res;
|
||||
res = regmap_write(regmap, DS3232_REG_CR, tmp & 0x1c);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
res = regmap_read(regmap, DS3232_REG_SR, &tmp);
|
||||
if (res)
|
||||
return res;
|
||||
res = regmap_write(regmap, DS3232_REG_SR, tmp & 0x88);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
/* Print our settings */
|
||||
res = regmap_read(regmap, DS3232_REG_CR, &tmp);
|
||||
if (res)
|
||||
return res;
|
||||
dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp);
|
||||
|
||||
res = regmap_read(regmap, DS3232_REG_SR, &tmp);
|
||||
if (res)
|
||||
return res;
|
||||
dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp);
|
||||
|
||||
return ds3232_probe(&spi->dev, regmap, spi->irq, "ds3234");
|
||||
}
|
||||
|
||||
static struct spi_driver ds3234_driver = {
|
||||
.driver = {
|
||||
.name = "ds3234",
|
||||
},
|
||||
.probe = ds3234_probe,
|
||||
};
|
||||
|
||||
static int ds3234_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&ds3234_driver);
|
||||
}
|
||||
|
||||
static void ds3234_unregister_driver(void)
|
||||
{
|
||||
spi_unregister_driver(&ds3234_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int ds3234_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds3234_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int __init ds323x_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ds3232_register_driver();
|
||||
if (ret) {
|
||||
pr_err("Failed to register ds3232 driver: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ds3234_register_driver();
|
||||
if (ret) {
|
||||
pr_err("Failed to register ds3234 driver: %d\n", ret);
|
||||
ds3232_unregister_driver();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(ds323x_init)
|
||||
|
||||
static void __exit ds323x_exit(void)
|
||||
{
|
||||
ds3234_unregister_driver();
|
||||
ds3232_unregister_driver();
|
||||
}
|
||||
module_exit(ds323x_exit)
|
||||
|
||||
MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
|
||||
MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
|
||||
MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>");
|
||||
MODULE_DESCRIPTION("Maxim/Dallas DS3232/DS3234 RTC Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("spi:ds3234");
|
||||
|
@ -1,171 +0,0 @@
|
||||
/* rtc-ds3234.c
|
||||
*
|
||||
* Driver for Dallas Semiconductor (DS3234) SPI RTC with Integrated Crystal
|
||||
* and SRAM.
|
||||
*
|
||||
* Copyright (C) 2008 MIMOMax Wireless Ltd.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
#define DS3234_REG_SECONDS 0x00
|
||||
#define DS3234_REG_MINUTES 0x01
|
||||
#define DS3234_REG_HOURS 0x02
|
||||
#define DS3234_REG_DAY 0x03
|
||||
#define DS3234_REG_DATE 0x04
|
||||
#define DS3234_REG_MONTH 0x05
|
||||
#define DS3234_REG_YEAR 0x06
|
||||
#define DS3234_REG_CENTURY (1 << 7) /* Bit 7 of the Month register */
|
||||
|
||||
#define DS3234_REG_CONTROL 0x0E
|
||||
#define DS3234_REG_CONT_STAT 0x0F
|
||||
|
||||
static int ds3234_set_reg(struct device *dev, unsigned char address,
|
||||
unsigned char data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
unsigned char buf[2];
|
||||
|
||||
/* MSB must be '1' to indicate write */
|
||||
buf[0] = address | 0x80;
|
||||
buf[1] = data;
|
||||
|
||||
return spi_write_then_read(spi, buf, 2, NULL, 0);
|
||||
}
|
||||
|
||||
static int ds3234_get_reg(struct device *dev, unsigned char address,
|
||||
unsigned char *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
*data = address & 0x7f;
|
||||
|
||||
return spi_write_then_read(spi, data, 1, data, 1);
|
||||
}
|
||||
|
||||
static int ds3234_read_time(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
int err;
|
||||
unsigned char buf[8];
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
buf[0] = 0x00; /* Start address */
|
||||
|
||||
err = spi_write_then_read(spi, buf, 1, buf, 8);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
/* Seconds, Minutes, Hours, Day, Date, Month, Year */
|
||||
dt->tm_sec = bcd2bin(buf[0]);
|
||||
dt->tm_min = bcd2bin(buf[1]);
|
||||
dt->tm_hour = bcd2bin(buf[2] & 0x3f);
|
||||
dt->tm_wday = bcd2bin(buf[3]) - 1; /* 0 = Sun */
|
||||
dt->tm_mday = bcd2bin(buf[4]);
|
||||
dt->tm_mon = bcd2bin(buf[5] & 0x1f) - 1; /* 0 = Jan */
|
||||
dt->tm_year = bcd2bin(buf[6] & 0xff) + 100; /* Assume 20YY */
|
||||
|
||||
return rtc_valid_tm(dt);
|
||||
}
|
||||
|
||||
static int ds3234_set_time(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
ds3234_set_reg(dev, DS3234_REG_SECONDS, bin2bcd(dt->tm_sec));
|
||||
ds3234_set_reg(dev, DS3234_REG_MINUTES, bin2bcd(dt->tm_min));
|
||||
ds3234_set_reg(dev, DS3234_REG_HOURS, bin2bcd(dt->tm_hour) & 0x3f);
|
||||
|
||||
/* 0 = Sun */
|
||||
ds3234_set_reg(dev, DS3234_REG_DAY, bin2bcd(dt->tm_wday + 1));
|
||||
ds3234_set_reg(dev, DS3234_REG_DATE, bin2bcd(dt->tm_mday));
|
||||
|
||||
/* 0 = Jan */
|
||||
ds3234_set_reg(dev, DS3234_REG_MONTH, bin2bcd(dt->tm_mon + 1));
|
||||
|
||||
/* Assume 20YY although we just want to make sure not to go negative. */
|
||||
if (dt->tm_year > 100)
|
||||
dt->tm_year -= 100;
|
||||
|
||||
ds3234_set_reg(dev, DS3234_REG_YEAR, bin2bcd(dt->tm_year));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds3234_rtc_ops = {
|
||||
.read_time = ds3234_read_time,
|
||||
.set_time = ds3234_set_time,
|
||||
};
|
||||
|
||||
static int ds3234_probe(struct spi_device *spi)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
unsigned char tmp;
|
||||
int res;
|
||||
|
||||
spi->mode = SPI_MODE_3;
|
||||
spi->bits_per_word = 8;
|
||||
spi_setup(spi);
|
||||
|
||||
res = ds3234_get_reg(&spi->dev, DS3234_REG_SECONDS, &tmp);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
/* Control settings
|
||||
*
|
||||
* CONTROL_REG
|
||||
* BIT 7 6 5 4 3 2 1 0
|
||||
* EOSC BBSQW CONV RS2 RS1 INTCN A2IE A1IE
|
||||
*
|
||||
* 0 0 0 1 1 1 0 0
|
||||
*
|
||||
* CONTROL_STAT_REG
|
||||
* BIT 7 6 5 4 3 2 1 0
|
||||
* OSF BB32kHz CRATE1 CRATE0 EN32kHz BSY A2F A1F
|
||||
*
|
||||
* 1 0 0 0 1 0 0 0
|
||||
*/
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp);
|
||||
ds3234_set_reg(&spi->dev, DS3234_REG_CONTROL, tmp & 0x1c);
|
||||
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp);
|
||||
ds3234_set_reg(&spi->dev, DS3234_REG_CONT_STAT, tmp & 0x88);
|
||||
|
||||
/* Print our settings */
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp);
|
||||
dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp);
|
||||
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp);
|
||||
dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp);
|
||||
|
||||
rtc = devm_rtc_device_register(&spi->dev, "ds3234",
|
||||
&ds3234_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
spi_set_drvdata(spi, rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver ds3234_driver = {
|
||||
.driver = {
|
||||
.name = "ds3234",
|
||||
},
|
||||
.probe = ds3234_probe,
|
||||
};
|
||||
|
||||
module_spi_driver(ds3234_driver);
|
||||
|
||||
MODULE_DESCRIPTION("DS3234 SPI RTC driver");
|
||||
MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("spi:ds3234");
|
@ -9,6 +9,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#if defined(CONFIG_M68K) || defined(CONFIG_PARISC) || \
|
||||
defined(CONFIG_PPC) || defined(CONFIG_SUPERH32)
|
||||
#include <asm/rtc.h>
|
||||
|
||||
static int generic_get_time(struct device *dev, struct rtc_time *tm)
|
||||
@ -33,13 +35,21 @@ static const struct rtc_class_ops generic_rtc_ops = {
|
||||
.read_time = generic_get_time,
|
||||
.set_time = generic_set_time,
|
||||
};
|
||||
#else
|
||||
#define generic_rtc_ops *(struct rtc_class_ops*)NULL
|
||||
#endif
|
||||
|
||||
static int __init generic_rtc_probe(struct platform_device *dev)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
const struct rtc_class_ops *ops;
|
||||
|
||||
ops = dev_get_platdata(&dev->dev);
|
||||
if (!ops)
|
||||
ops = &generic_rtc_ops;
|
||||
|
||||
rtc = devm_rtc_device_register(&dev->dev, "rtc-generic",
|
||||
&generic_rtc_ops, THIS_MODULE);
|
||||
ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
|
@ -144,7 +144,7 @@ static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
* it does not seem to carry it over a subsequent write/read.
|
||||
* So we'll limit ourself to 100 years, starting at 2000 for now.
|
||||
*/
|
||||
buf[6] = tm->tm_year - 100;
|
||||
buf[6] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
/*
|
||||
* CTL1 only contains TEST-mode bits apart from stop,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* RTC driver for Maxim MAX77686
|
||||
* RTC driver for Maxim MAX77686 and MAX77802
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co.Ltd
|
||||
*
|
||||
@ -12,8 +12,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/delay.h>
|
||||
@ -24,24 +23,38 @@
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define MAX77686_I2C_ADDR_RTC (0x0C >> 1)
|
||||
#define MAX77620_I2C_ADDR_RTC 0x68
|
||||
#define MAX77686_INVALID_I2C_ADDR (-1)
|
||||
|
||||
/* Define non existing register */
|
||||
#define MAX77686_INVALID_REG (-1)
|
||||
|
||||
/* RTC Control Register */
|
||||
#define BCD_EN_SHIFT 0
|
||||
#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
|
||||
#define BCD_EN_MASK BIT(BCD_EN_SHIFT)
|
||||
#define MODEL24_SHIFT 1
|
||||
#define MODEL24_MASK (1 << MODEL24_SHIFT)
|
||||
#define MODEL24_MASK BIT(MODEL24_SHIFT)
|
||||
/* RTC Update Register1 */
|
||||
#define RTC_UDR_SHIFT 0
|
||||
#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
|
||||
#define RTC_UDR_MASK BIT(RTC_UDR_SHIFT)
|
||||
#define RTC_RBUDR_SHIFT 4
|
||||
#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
|
||||
#define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT)
|
||||
/* RTC Hour register */
|
||||
#define HOUR_PM_SHIFT 6
|
||||
#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
|
||||
#define HOUR_PM_MASK BIT(HOUR_PM_SHIFT)
|
||||
/* RTC Alarm Enable */
|
||||
#define ALARM_ENABLE_SHIFT 7
|
||||
#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
|
||||
#define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT)
|
||||
|
||||
#define MAX77686_RTC_UPDATE_DELAY 16
|
||||
#define REG_RTC_NONE 0xdeadbeef
|
||||
|
||||
/*
|
||||
* MAX77802 has separate register (RTCAE1) for alarm enable instead
|
||||
* using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE}
|
||||
* as in done in MAX77686.
|
||||
*/
|
||||
#define MAX77802_ALARM_ENABLE_VALUE 0x77
|
||||
|
||||
enum {
|
||||
RTC_SEC = 0,
|
||||
@ -54,15 +67,38 @@ enum {
|
||||
RTC_NR_TIME
|
||||
};
|
||||
|
||||
struct max77686_rtc_driver_data {
|
||||
/* Minimum usecs needed for a RTC update */
|
||||
unsigned long delay;
|
||||
/* Mask used to read RTC registers value */
|
||||
u8 mask;
|
||||
/* Registers offset to I2C addresses map */
|
||||
const unsigned int *map;
|
||||
/* Has a separate alarm enable register? */
|
||||
bool alarm_enable_reg;
|
||||
/* I2C address for RTC block */
|
||||
int rtc_i2c_addr;
|
||||
/* RTC interrupt via platform resource */
|
||||
bool rtc_irq_from_platform;
|
||||
/* Pending alarm status register */
|
||||
int alarm_pending_status_reg;
|
||||
/* RTC IRQ CHIP for regmap */
|
||||
const struct regmap_irq_chip *rtc_irq_chip;
|
||||
};
|
||||
|
||||
struct max77686_rtc_info {
|
||||
struct device *dev;
|
||||
struct max77686_dev *max77686;
|
||||
struct i2c_client *rtc;
|
||||
struct rtc_device *rtc_dev;
|
||||
struct mutex lock;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap *rtc_regmap;
|
||||
|
||||
const struct max77686_rtc_driver_data *drv_data;
|
||||
struct regmap_irq_chip_data *rtc_irq_data;
|
||||
|
||||
int rtc_irq;
|
||||
int virq;
|
||||
int rtc_24hr_mode;
|
||||
};
|
||||
@ -72,29 +108,190 @@ enum MAX77686_RTC_OP {
|
||||
MAX77686_RTC_READ,
|
||||
};
|
||||
|
||||
/* These are not registers but just offsets that are mapped to addresses */
|
||||
enum max77686_rtc_reg_offset {
|
||||
REG_RTC_CONTROLM = 0,
|
||||
REG_RTC_CONTROL,
|
||||
REG_RTC_UPDATE0,
|
||||
REG_WTSR_SMPL_CNTL,
|
||||
REG_RTC_SEC,
|
||||
REG_RTC_MIN,
|
||||
REG_RTC_HOUR,
|
||||
REG_RTC_WEEKDAY,
|
||||
REG_RTC_MONTH,
|
||||
REG_RTC_YEAR,
|
||||
REG_RTC_DATE,
|
||||
REG_ALARM1_SEC,
|
||||
REG_ALARM1_MIN,
|
||||
REG_ALARM1_HOUR,
|
||||
REG_ALARM1_WEEKDAY,
|
||||
REG_ALARM1_MONTH,
|
||||
REG_ALARM1_YEAR,
|
||||
REG_ALARM1_DATE,
|
||||
REG_ALARM2_SEC,
|
||||
REG_ALARM2_MIN,
|
||||
REG_ALARM2_HOUR,
|
||||
REG_ALARM2_WEEKDAY,
|
||||
REG_ALARM2_MONTH,
|
||||
REG_ALARM2_YEAR,
|
||||
REG_ALARM2_DATE,
|
||||
REG_RTC_AE1,
|
||||
REG_RTC_END,
|
||||
};
|
||||
|
||||
/* Maps RTC registers offset to the MAX77686 register addresses */
|
||||
static const unsigned int max77686_map[REG_RTC_END] = {
|
||||
[REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM,
|
||||
[REG_RTC_CONTROL] = MAX77686_RTC_CONTROL,
|
||||
[REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0,
|
||||
[REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL,
|
||||
[REG_RTC_SEC] = MAX77686_RTC_SEC,
|
||||
[REG_RTC_MIN] = MAX77686_RTC_MIN,
|
||||
[REG_RTC_HOUR] = MAX77686_RTC_HOUR,
|
||||
[REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY,
|
||||
[REG_RTC_MONTH] = MAX77686_RTC_MONTH,
|
||||
[REG_RTC_YEAR] = MAX77686_RTC_YEAR,
|
||||
[REG_RTC_DATE] = MAX77686_RTC_DATE,
|
||||
[REG_ALARM1_SEC] = MAX77686_ALARM1_SEC,
|
||||
[REG_ALARM1_MIN] = MAX77686_ALARM1_MIN,
|
||||
[REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR,
|
||||
[REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY,
|
||||
[REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH,
|
||||
[REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR,
|
||||
[REG_ALARM1_DATE] = MAX77686_ALARM1_DATE,
|
||||
[REG_ALARM2_SEC] = MAX77686_ALARM2_SEC,
|
||||
[REG_ALARM2_MIN] = MAX77686_ALARM2_MIN,
|
||||
[REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR,
|
||||
[REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY,
|
||||
[REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH,
|
||||
[REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR,
|
||||
[REG_ALARM2_DATE] = MAX77686_ALARM2_DATE,
|
||||
[REG_RTC_AE1] = REG_RTC_NONE,
|
||||
};
|
||||
|
||||
static const struct regmap_irq max77686_rtc_irqs[] = {
|
||||
/* RTC interrupts */
|
||||
REGMAP_IRQ_REG(0, 0, MAX77686_RTCINT_RTC60S_MSK),
|
||||
REGMAP_IRQ_REG(1, 0, MAX77686_RTCINT_RTCA1_MSK),
|
||||
REGMAP_IRQ_REG(2, 0, MAX77686_RTCINT_RTCA2_MSK),
|
||||
REGMAP_IRQ_REG(3, 0, MAX77686_RTCINT_SMPL_MSK),
|
||||
REGMAP_IRQ_REG(4, 0, MAX77686_RTCINT_RTC1S_MSK),
|
||||
REGMAP_IRQ_REG(5, 0, MAX77686_RTCINT_WTSR_MSK),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77686_rtc_irq_chip = {
|
||||
.name = "max77686-rtc",
|
||||
.status_base = MAX77686_RTC_INT,
|
||||
.mask_base = MAX77686_RTC_INTM,
|
||||
.num_regs = 1,
|
||||
.irqs = max77686_rtc_irqs,
|
||||
.num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
|
||||
};
|
||||
|
||||
static const struct max77686_rtc_driver_data max77686_drv_data = {
|
||||
.delay = 16000,
|
||||
.mask = 0x7f,
|
||||
.map = max77686_map,
|
||||
.alarm_enable_reg = false,
|
||||
.rtc_irq_from_platform = false,
|
||||
.alarm_pending_status_reg = MAX77686_REG_STATUS2,
|
||||
.rtc_i2c_addr = MAX77686_I2C_ADDR_RTC,
|
||||
.rtc_irq_chip = &max77686_rtc_irq_chip,
|
||||
};
|
||||
|
||||
static const struct max77686_rtc_driver_data max77620_drv_data = {
|
||||
.delay = 16000,
|
||||
.mask = 0x7f,
|
||||
.map = max77686_map,
|
||||
.alarm_enable_reg = false,
|
||||
.rtc_irq_from_platform = true,
|
||||
.alarm_pending_status_reg = MAX77686_INVALID_REG,
|
||||
.rtc_i2c_addr = MAX77620_I2C_ADDR_RTC,
|
||||
.rtc_irq_chip = &max77686_rtc_irq_chip,
|
||||
};
|
||||
|
||||
static const unsigned int max77802_map[REG_RTC_END] = {
|
||||
[REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM,
|
||||
[REG_RTC_CONTROL] = MAX77802_RTC_CONTROL,
|
||||
[REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0,
|
||||
[REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL,
|
||||
[REG_RTC_SEC] = MAX77802_RTC_SEC,
|
||||
[REG_RTC_MIN] = MAX77802_RTC_MIN,
|
||||
[REG_RTC_HOUR] = MAX77802_RTC_HOUR,
|
||||
[REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY,
|
||||
[REG_RTC_MONTH] = MAX77802_RTC_MONTH,
|
||||
[REG_RTC_YEAR] = MAX77802_RTC_YEAR,
|
||||
[REG_RTC_DATE] = MAX77802_RTC_DATE,
|
||||
[REG_ALARM1_SEC] = MAX77802_ALARM1_SEC,
|
||||
[REG_ALARM1_MIN] = MAX77802_ALARM1_MIN,
|
||||
[REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR,
|
||||
[REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY,
|
||||
[REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH,
|
||||
[REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR,
|
||||
[REG_ALARM1_DATE] = MAX77802_ALARM1_DATE,
|
||||
[REG_ALARM2_SEC] = MAX77802_ALARM2_SEC,
|
||||
[REG_ALARM2_MIN] = MAX77802_ALARM2_MIN,
|
||||
[REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR,
|
||||
[REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY,
|
||||
[REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH,
|
||||
[REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR,
|
||||
[REG_ALARM2_DATE] = MAX77802_ALARM2_DATE,
|
||||
[REG_RTC_AE1] = MAX77802_RTC_AE1,
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip max77802_rtc_irq_chip = {
|
||||
.name = "max77802-rtc",
|
||||
.status_base = MAX77802_RTC_INT,
|
||||
.mask_base = MAX77802_RTC_INTM,
|
||||
.num_regs = 1,
|
||||
.irqs = max77686_rtc_irqs, /* same masks as 77686 */
|
||||
.num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
|
||||
};
|
||||
|
||||
static const struct max77686_rtc_driver_data max77802_drv_data = {
|
||||
.delay = 200,
|
||||
.mask = 0xff,
|
||||
.map = max77802_map,
|
||||
.alarm_enable_reg = true,
|
||||
.rtc_irq_from_platform = false,
|
||||
.alarm_pending_status_reg = MAX77686_REG_STATUS2,
|
||||
.rtc_i2c_addr = MAX77686_INVALID_I2C_ADDR,
|
||||
.rtc_irq_chip = &max77802_rtc_irq_chip,
|
||||
};
|
||||
|
||||
static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
|
||||
int rtc_24hr_mode)
|
||||
struct max77686_rtc_info *info)
|
||||
{
|
||||
tm->tm_sec = data[RTC_SEC] & 0x7f;
|
||||
tm->tm_min = data[RTC_MIN] & 0x7f;
|
||||
if (rtc_24hr_mode)
|
||||
u8 mask = info->drv_data->mask;
|
||||
|
||||
tm->tm_sec = data[RTC_SEC] & mask;
|
||||
tm->tm_min = data[RTC_MIN] & mask;
|
||||
if (info->rtc_24hr_mode) {
|
||||
tm->tm_hour = data[RTC_HOUR] & 0x1f;
|
||||
else {
|
||||
} else {
|
||||
tm->tm_hour = data[RTC_HOUR] & 0x0f;
|
||||
if (data[RTC_HOUR] & HOUR_PM_MASK)
|
||||
tm->tm_hour += 12;
|
||||
}
|
||||
|
||||
/* Only a single bit is set in data[], so fls() would be equivalent */
|
||||
tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f) - 1;
|
||||
tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1;
|
||||
tm->tm_mday = data[RTC_DATE] & 0x1f;
|
||||
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
|
||||
tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
|
||||
tm->tm_year = data[RTC_YEAR] & mask;
|
||||
tm->tm_yday = 0;
|
||||
tm->tm_isdst = 0;
|
||||
|
||||
/*
|
||||
* MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the
|
||||
* year values are just 0..99 so add 100 to support up to 2099.
|
||||
*/
|
||||
if (!info->drv_data->alarm_enable_reg)
|
||||
tm->tm_year += 100;
|
||||
}
|
||||
|
||||
static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
|
||||
static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data,
|
||||
struct max77686_rtc_info *info)
|
||||
{
|
||||
data[RTC_SEC] = tm->tm_sec;
|
||||
data[RTC_MIN] = tm->tm_min;
|
||||
@ -102,35 +299,44 @@ static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
|
||||
data[RTC_WEEKDAY] = 1 << tm->tm_wday;
|
||||
data[RTC_DATE] = tm->tm_mday;
|
||||
data[RTC_MONTH] = tm->tm_mon + 1;
|
||||
|
||||
if (info->drv_data->alarm_enable_reg) {
|
||||
data[RTC_YEAR] = tm->tm_year;
|
||||
return 0;
|
||||
}
|
||||
|
||||
data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
|
||||
|
||||
if (tm->tm_year < 100) {
|
||||
pr_warn("RTC cannot handle the year %d. Assume it's 2000.\n",
|
||||
dev_err(info->dev, "RTC cannot handle the year %d.\n",
|
||||
1900 + tm->tm_year);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77686_rtc_update(struct max77686_rtc_info *info,
|
||||
enum MAX77686_RTC_OP op)
|
||||
enum MAX77686_RTC_OP op)
|
||||
{
|
||||
int ret;
|
||||
unsigned int data;
|
||||
unsigned long delay = info->drv_data->delay;
|
||||
|
||||
if (op == MAX77686_RTC_WRITE)
|
||||
data = 1 << RTC_UDR_SHIFT;
|
||||
else
|
||||
data = 1 << RTC_RBUDR_SHIFT;
|
||||
|
||||
ret = regmap_update_bits(info->max77686->rtc_regmap,
|
||||
MAX77686_RTC_UPDATE0, data, data);
|
||||
ret = regmap_update_bits(info->rtc_regmap,
|
||||
info->drv_data->map[REG_RTC_UPDATE0],
|
||||
data, data);
|
||||
if (ret < 0)
|
||||
dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
|
||||
__func__, ret, data);
|
||||
dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n",
|
||||
ret, data);
|
||||
else {
|
||||
/* Minimum 16ms delay required before RTC update. */
|
||||
msleep(MAX77686_RTC_UPDATE_DELAY);
|
||||
/* Minimum delay required before RTC update. */
|
||||
usleep_range(delay, delay * 2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -148,14 +354,15 @@ static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(info->max77686->rtc_regmap,
|
||||
MAX77686_RTC_SEC, data, RTC_NR_TIME);
|
||||
ret = regmap_bulk_read(info->rtc_regmap,
|
||||
info->drv_data->map[REG_RTC_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, ret);
|
||||
dev_err(info->dev, "Fail to read time reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
max77686_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
|
||||
max77686_rtc_data_to_tm(data, tm, info);
|
||||
|
||||
ret = rtc_valid_tm(tm);
|
||||
|
||||
@ -170,17 +377,17 @@ static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
|
||||
ret = max77686_rtc_tm_to_data(tm, data);
|
||||
ret = max77686_rtc_tm_to_data(tm, data, info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = regmap_bulk_write(info->max77686->rtc_regmap,
|
||||
MAX77686_RTC_SEC, data, RTC_NR_TIME);
|
||||
ret = regmap_bulk_write(info->rtc_regmap,
|
||||
info->drv_data->map[REG_RTC_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
|
||||
ret);
|
||||
dev_err(info->dev, "Fail to write time reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -196,6 +403,7 @@ static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
struct max77686_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
unsigned int val;
|
||||
const unsigned int *map = info->drv_data->map;
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
@ -204,29 +412,53 @@ static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(info->max77686->rtc_regmap,
|
||||
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
|
||||
__func__, __LINE__, ret);
|
||||
dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
max77686_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
|
||||
max77686_rtc_data_to_tm(data, &alrm->time, info);
|
||||
|
||||
alrm->enabled = 0;
|
||||
for (i = 0; i < RTC_NR_TIME; i++) {
|
||||
if (data[i] & ALARM_ENABLE_MASK) {
|
||||
|
||||
if (info->drv_data->alarm_enable_reg) {
|
||||
if (map[REG_RTC_AE1] == REG_RTC_NONE) {
|
||||
ret = -EINVAL;
|
||||
dev_err(info->dev,
|
||||
"alarm enable register not set(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev,
|
||||
"fail to read alarm enable(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val)
|
||||
alrm->enabled = 1;
|
||||
break;
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||
if (data[i] & ALARM_ENABLE_MASK) {
|
||||
alrm->enabled = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alrm->pending = 0;
|
||||
ret = regmap_read(info->max77686->regmap, MAX77686_REG_STATUS2, &val);
|
||||
|
||||
if (info->drv_data->alarm_pending_status_reg == MAX77686_INVALID_REG)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read(info->regmap,
|
||||
info->drv_data->alarm_pending_status_reg, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
|
||||
__func__, __LINE__, ret);
|
||||
dev_err(info->dev,
|
||||
"Fail to read alarm pending status reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -235,7 +467,7 @@ static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
|
||||
@ -243,6 +475,7 @@ static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret, i;
|
||||
struct rtc_time tm;
|
||||
const unsigned int *map = info->drv_data->map;
|
||||
|
||||
if (!mutex_is_locked(&info->lock))
|
||||
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
|
||||
@ -251,24 +484,34 @@ static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(info->max77686->rtc_regmap,
|
||||
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
if (info->drv_data->alarm_enable_reg) {
|
||||
if (map[REG_RTC_AE1] == REG_RTC_NONE) {
|
||||
ret = -EINVAL;
|
||||
dev_err(info->dev,
|
||||
"alarm enable register not set(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0);
|
||||
} else {
|
||||
ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
max77686_rtc_data_to_tm(data, &tm, info);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++)
|
||||
data[i] &= ~ALARM_ENABLE_MASK;
|
||||
|
||||
ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
}
|
||||
|
||||
max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
|
||||
|
||||
for (i = 0; i < RTC_NR_TIME; i++)
|
||||
data[i] &= ~ALARM_ENABLE_MASK;
|
||||
|
||||
ret = regmap_bulk_write(info->max77686->rtc_regmap,
|
||||
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -282,6 +525,7 @@ static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
struct rtc_time tm;
|
||||
const unsigned int *map = info->drv_data->map;
|
||||
|
||||
if (!mutex_is_locked(&info->lock))
|
||||
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
|
||||
@ -290,32 +534,36 @@ static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(info->max77686->rtc_regmap,
|
||||
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
if (info->drv_data->alarm_enable_reg) {
|
||||
ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1],
|
||||
MAX77802_ALARM_ENABLE_VALUE);
|
||||
} else {
|
||||
ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
max77686_rtc_data_to_tm(data, &tm, info);
|
||||
|
||||
data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
|
||||
if (data[RTC_MONTH] & 0xf)
|
||||
data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
if (data[RTC_YEAR] & info->drv_data->mask)
|
||||
data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
if (data[RTC_DATE] & 0x1f)
|
||||
data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
|
||||
ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
}
|
||||
|
||||
max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
|
||||
|
||||
data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
|
||||
if (data[RTC_MONTH] & 0xf)
|
||||
data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
if (data[RTC_YEAR] & 0x7f)
|
||||
data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
if (data[RTC_DATE] & 0x1f)
|
||||
data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
|
||||
|
||||
ret = regmap_bulk_write(info->max77686->rtc_regmap,
|
||||
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -330,7 +578,7 @@ static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
|
||||
ret = max77686_rtc_tm_to_data(&alrm->time, data);
|
||||
ret = max77686_rtc_tm_to_data(&alrm->time, data, info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -340,12 +588,12 @@ static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_write(info->max77686->rtc_regmap,
|
||||
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
ret = regmap_bulk_write(info->rtc_regmap,
|
||||
info->drv_data->map[REG_ALARM1_SEC],
|
||||
data, ARRAY_SIZE(data));
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -361,7 +609,7 @@ out:
|
||||
}
|
||||
|
||||
static int max77686_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct max77686_rtc_info *info = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -380,7 +628,7 @@ static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
|
||||
{
|
||||
struct max77686_rtc_info *info = data;
|
||||
|
||||
dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
|
||||
dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq);
|
||||
|
||||
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
@ -406,10 +654,11 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
|
||||
|
||||
info->rtc_24hr_mode = 1;
|
||||
|
||||
ret = regmap_bulk_write(info->max77686->rtc_regmap, MAX77686_RTC_CONTROLM, data, 2);
|
||||
ret = regmap_bulk_write(info->rtc_regmap,
|
||||
info->drv_data->map[REG_RTC_CONTROLM],
|
||||
data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
|
||||
__func__, ret);
|
||||
dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -417,28 +666,97 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77686_rtc_probe(struct platform_device *pdev)
|
||||
static const struct regmap_config max77686_rtc_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int max77686_init_rtc_regmap(struct max77686_rtc_info *info)
|
||||
{
|
||||
struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77686_rtc_info *info;
|
||||
struct device *parent = info->dev->parent;
|
||||
struct i2c_client *parent_i2c = to_i2c_client(parent);
|
||||
int ret;
|
||||
|
||||
dev_info(&pdev->dev, "%s\n", __func__);
|
||||
if (info->drv_data->rtc_irq_from_platform) {
|
||||
struct platform_device *pdev = to_platform_device(info->dev);
|
||||
|
||||
info->rtc_irq = platform_get_irq(pdev, 0);
|
||||
if (info->rtc_irq < 0) {
|
||||
dev_err(info->dev, "Failed to get rtc interrupts: %d\n",
|
||||
info->rtc_irq);
|
||||
return info->rtc_irq;
|
||||
}
|
||||
} else {
|
||||
info->rtc_irq = parent_i2c->irq;
|
||||
}
|
||||
|
||||
info->regmap = dev_get_regmap(parent, NULL);
|
||||
if (!info->regmap) {
|
||||
dev_err(info->dev, "Failed to get rtc regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) {
|
||||
info->rtc_regmap = info->regmap;
|
||||
goto add_rtc_irq;
|
||||
}
|
||||
|
||||
info->rtc = i2c_new_dummy(parent_i2c->adapter,
|
||||
info->drv_data->rtc_i2c_addr);
|
||||
if (!info->rtc) {
|
||||
dev_err(info->dev, "Failed to allocate I2C device for RTC\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info->rtc_regmap = devm_regmap_init_i2c(info->rtc,
|
||||
&max77686_rtc_regmap_config);
|
||||
if (IS_ERR(info->rtc_regmap)) {
|
||||
ret = PTR_ERR(info->rtc_regmap);
|
||||
dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret);
|
||||
goto err_unregister_i2c;
|
||||
}
|
||||
|
||||
add_rtc_irq:
|
||||
ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
|
||||
IRQF_SHARED, 0, info->drv_data->rtc_irq_chip,
|
||||
&info->rtc_irq_data);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret);
|
||||
goto err_unregister_i2c;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_i2c:
|
||||
if (info->rtc)
|
||||
i2c_unregister_device(info->rtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77686_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77686_rtc_info *info;
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
int ret;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info),
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&info->lock);
|
||||
info->dev = &pdev->dev;
|
||||
info->max77686 = max77686;
|
||||
info->rtc = max77686->rtc;
|
||||
info->drv_data = (const struct max77686_rtc_driver_data *)
|
||||
id->driver_data;
|
||||
|
||||
ret = max77686_init_rtc_regmap(info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
ret = max77686_rtc_init_reg(info);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
|
||||
goto err_rtc;
|
||||
@ -446,7 +764,7 @@ static int max77686_rtc_probe(struct platform_device *pdev)
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77686-rtc",
|
||||
info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name,
|
||||
&max77686_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(info->rtc_dev)) {
|
||||
@ -457,29 +775,43 @@ static int max77686_rtc_probe(struct platform_device *pdev)
|
||||
goto err_rtc;
|
||||
}
|
||||
|
||||
if (!max77686->rtc_irq_data) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "%s: no RTC regmap IRQ chip\n", __func__);
|
||||
goto err_rtc;
|
||||
}
|
||||
|
||||
info->virq = regmap_irq_get_virq(max77686->rtc_irq_data,
|
||||
info->virq = regmap_irq_get_virq(info->rtc_irq_data,
|
||||
MAX77686_RTCIRQ_RTCA1);
|
||||
if (!info->virq) {
|
||||
if (info->virq <= 0) {
|
||||
ret = -ENXIO;
|
||||
goto err_rtc;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL,
|
||||
max77686_rtc_alarm_irq, 0, "rtc-alarm1", info);
|
||||
if (ret < 0)
|
||||
ret = request_threaded_irq(info->virq, NULL, max77686_rtc_alarm_irq, 0,
|
||||
"rtc-alarm1", info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
|
||||
info->virq, ret);
|
||||
goto err_rtc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_rtc:
|
||||
regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
|
||||
if (info->rtc)
|
||||
i2c_unregister_device(info->rtc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77686_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max77686_rtc_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(info->virq, info);
|
||||
regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
|
||||
if (info->rtc)
|
||||
i2c_unregister_device(info->rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max77686_rtc_suspend(struct device *dev)
|
||||
{
|
||||
@ -508,7 +840,9 @@ static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops,
|
||||
max77686_rtc_suspend, max77686_rtc_resume);
|
||||
|
||||
static const struct platform_device_id rtc_id[] = {
|
||||
{ "max77686-rtc", 0 },
|
||||
{ "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, },
|
||||
{ "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, },
|
||||
{ "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, rtc_id);
|
||||
@ -519,6 +853,7 @@ static struct platform_driver max77686_rtc_driver = {
|
||||
.pm = &max77686_rtc_pm_ops,
|
||||
},
|
||||
.probe = max77686_rtc_probe,
|
||||
.remove = max77686_rtc_remove,
|
||||
.id_table = rtc_id,
|
||||
};
|
||||
|
||||
|
@ -1,502 +0,0 @@
|
||||
/*
|
||||
* RTC driver for Maxim MAX77802
|
||||
*
|
||||
* Copyright (C) 2013 Google, Inc
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co.Ltd
|
||||
*
|
||||
* based on rtc-max8997.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/max77686-private.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* RTC Control Register */
|
||||
#define BCD_EN_SHIFT 0
|
||||
#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
|
||||
#define MODEL24_SHIFT 1
|
||||
#define MODEL24_MASK (1 << MODEL24_SHIFT)
|
||||
/* RTC Update Register1 */
|
||||
#define RTC_UDR_SHIFT 0
|
||||
#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
|
||||
#define RTC_RBUDR_SHIFT 4
|
||||
#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
|
||||
/* RTC Hour register */
|
||||
#define HOUR_PM_SHIFT 6
|
||||
#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
|
||||
/* RTC Alarm Enable */
|
||||
#define ALARM_ENABLE_SHIFT 7
|
||||
#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
|
||||
|
||||
/* For the RTCAE1 register, we write this value to enable the alarm */
|
||||
#define ALARM_ENABLE_VALUE 0x77
|
||||
|
||||
#define MAX77802_RTC_UPDATE_DELAY_US 200
|
||||
|
||||
enum {
|
||||
RTC_SEC = 0,
|
||||
RTC_MIN,
|
||||
RTC_HOUR,
|
||||
RTC_WEEKDAY,
|
||||
RTC_MONTH,
|
||||
RTC_YEAR,
|
||||
RTC_DATE,
|
||||
RTC_NR_TIME
|
||||
};
|
||||
|
||||
struct max77802_rtc_info {
|
||||
struct device *dev;
|
||||
struct max77686_dev *max77802;
|
||||
struct i2c_client *rtc;
|
||||
struct rtc_device *rtc_dev;
|
||||
struct mutex lock;
|
||||
|
||||
struct regmap *regmap;
|
||||
|
||||
int virq;
|
||||
int rtc_24hr_mode;
|
||||
};
|
||||
|
||||
enum MAX77802_RTC_OP {
|
||||
MAX77802_RTC_WRITE,
|
||||
MAX77802_RTC_READ,
|
||||
};
|
||||
|
||||
static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
|
||||
int rtc_24hr_mode)
|
||||
{
|
||||
tm->tm_sec = data[RTC_SEC] & 0xff;
|
||||
tm->tm_min = data[RTC_MIN] & 0xff;
|
||||
if (rtc_24hr_mode)
|
||||
tm->tm_hour = data[RTC_HOUR] & 0x1f;
|
||||
else {
|
||||
tm->tm_hour = data[RTC_HOUR] & 0x0f;
|
||||
if (data[RTC_HOUR] & HOUR_PM_MASK)
|
||||
tm->tm_hour += 12;
|
||||
}
|
||||
|
||||
/* Only a single bit is set in data[], so fls() would be equivalent */
|
||||
tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0xff) - 1;
|
||||
tm->tm_mday = data[RTC_DATE] & 0x1f;
|
||||
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
|
||||
|
||||
tm->tm_year = data[RTC_YEAR] & 0xff;
|
||||
tm->tm_yday = 0;
|
||||
tm->tm_isdst = 0;
|
||||
}
|
||||
|
||||
static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
|
||||
{
|
||||
data[RTC_SEC] = tm->tm_sec;
|
||||
data[RTC_MIN] = tm->tm_min;
|
||||
data[RTC_HOUR] = tm->tm_hour;
|
||||
data[RTC_WEEKDAY] = 1 << tm->tm_wday;
|
||||
data[RTC_DATE] = tm->tm_mday;
|
||||
data[RTC_MONTH] = tm->tm_mon + 1;
|
||||
data[RTC_YEAR] = tm->tm_year;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77802_rtc_update(struct max77802_rtc_info *info,
|
||||
enum MAX77802_RTC_OP op)
|
||||
{
|
||||
int ret;
|
||||
unsigned int data;
|
||||
|
||||
if (op == MAX77802_RTC_WRITE)
|
||||
data = 1 << RTC_UDR_SHIFT;
|
||||
else
|
||||
data = 1 << RTC_RBUDR_SHIFT;
|
||||
|
||||
ret = regmap_update_bits(info->max77802->regmap,
|
||||
MAX77802_RTC_UPDATE0, data, data);
|
||||
if (ret < 0)
|
||||
dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
|
||||
__func__, ret, data);
|
||||
else {
|
||||
/* Minimum delay required before RTC update. */
|
||||
usleep_range(MAX77802_RTC_UPDATE_DELAY_US,
|
||||
MAX77802_RTC_UPDATE_DELAY_US * 2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_READ);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(info->max77802->regmap,
|
||||
MAX77802_RTC_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
|
||||
|
||||
ret = rtc_valid_tm(tm);
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
|
||||
ret = max77802_rtc_tm_to_data(tm, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = regmap_bulk_write(info->max77802->regmap,
|
||||
MAX77802_RTC_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_READ);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_read(info->max77802->regmap,
|
||||
MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
|
||||
__func__, __LINE__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
|
||||
|
||||
alrm->enabled = 0;
|
||||
ret = regmap_read(info->max77802->regmap,
|
||||
MAX77802_RTC_AE1, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n",
|
||||
__func__, __LINE__, ret);
|
||||
goto out;
|
||||
}
|
||||
if (val)
|
||||
alrm->enabled = 1;
|
||||
|
||||
alrm->pending = 0;
|
||||
ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
|
||||
__func__, __LINE__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val & (1 << 2)) /* RTCA1 */
|
||||
alrm->pending = 1;
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!mutex_is_locked(&info->lock))
|
||||
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_READ);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_write(info->max77802->regmap,
|
||||
MAX77802_RTC_AE1, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_start_alarm(struct max77802_rtc_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!mutex_is_locked(&info->lock))
|
||||
dev_warn(info->dev, "%s: should have mutex locked\n",
|
||||
__func__);
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_READ);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_write(info->max77802->regmap,
|
||||
MAX77802_RTC_AE1,
|
||||
ALARM_ENABLE_VALUE);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
|
||||
ret = max77802_rtc_tm_to_data(&alrm->time, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = max77802_rtc_stop_alarm(info);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_bulk_write(info->max77802->regmap,
|
||||
MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (alrm->enabled)
|
||||
ret = max77802_rtc_start_alarm(info);
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
if (enabled)
|
||||
ret = max77802_rtc_start_alarm(info);
|
||||
else
|
||||
ret = max77802_rtc_stop_alarm(info);
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data)
|
||||
{
|
||||
struct max77802_rtc_info *info = data;
|
||||
|
||||
dev_dbg(info->dev, "%s:irq(%d)\n", __func__, irq);
|
||||
|
||||
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops max77802_rtc_ops = {
|
||||
.read_time = max77802_rtc_read_time,
|
||||
.set_time = max77802_rtc_set_time,
|
||||
.read_alarm = max77802_rtc_read_alarm,
|
||||
.set_alarm = max77802_rtc_set_alarm,
|
||||
.alarm_irq_enable = max77802_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int max77802_rtc_init_reg(struct max77802_rtc_info *info)
|
||||
{
|
||||
u8 data[2];
|
||||
int ret;
|
||||
|
||||
max77802_rtc_update(info, MAX77802_RTC_READ);
|
||||
|
||||
/* Set RTC control register : Binary mode, 24hour mdoe */
|
||||
data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
|
||||
data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
|
||||
|
||||
info->rtc_24hr_mode = 1;
|
||||
|
||||
ret = regmap_bulk_write(info->max77802->regmap,
|
||||
MAX77802_RTC_CONTROLM, data, ARRAY_SIZE(data));
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77802_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77686_dev *max77802 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77802_rtc_info *info;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s\n", __func__);
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&info->lock);
|
||||
info->dev = &pdev->dev;
|
||||
info->max77802 = max77802;
|
||||
info->rtc = max77802->i2c;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
ret = max77802_rtc_init_reg(info);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc",
|
||||
&max77802_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(info->rtc_dev)) {
|
||||
ret = PTR_ERR(info->rtc_dev);
|
||||
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
|
||||
if (ret == 0)
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!max77802->rtc_irq_data) {
|
||||
dev_err(&pdev->dev, "No RTC regmap IRQ chip\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->virq = regmap_irq_get_virq(max77802->rtc_irq_data,
|
||||
MAX77686_RTCIRQ_RTCA1);
|
||||
|
||||
if (info->virq <= 0) {
|
||||
dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
|
||||
MAX77686_RTCIRQ_RTCA1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL,
|
||||
max77802_rtc_alarm_irq, 0, "rtc-alarm1",
|
||||
info);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
|
||||
info->virq, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max77802_rtc_suspend(struct device *dev)
|
||||
{
|
||||
if (device_may_wakeup(dev)) {
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
|
||||
return enable_irq_wake(info->virq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77802_rtc_resume(struct device *dev)
|
||||
{
|
||||
if (device_may_wakeup(dev)) {
|
||||
struct max77802_rtc_info *info = dev_get_drvdata(dev);
|
||||
|
||||
return disable_irq_wake(info->virq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops,
|
||||
max77802_rtc_suspend, max77802_rtc_resume);
|
||||
|
||||
static const struct platform_device_id rtc_id[] = {
|
||||
{ "max77802-rtc", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, rtc_id);
|
||||
|
||||
static struct platform_driver max77802_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "max77802-rtc",
|
||||
.pm = &max77802_rtc_pm_ops,
|
||||
},
|
||||
.probe = max77802_rtc_probe,
|
||||
.id_table = rtc_id,
|
||||
};
|
||||
|
||||
module_platform_driver(max77802_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Maxim MAX77802 RTC driver");
|
||||
MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
@ -419,4 +419,3 @@ module_platform_driver(mtk_rtc_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Tianping Fang <tianping.fang@mediatek.com>");
|
||||
MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC");
|
||||
MODULE_ALIAS("platform:mt6397-rtc");
|
||||
|
@ -311,8 +311,7 @@ static int palmas_rtc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, palmas_rtc->irq, NULL,
|
||||
palmas_rtc_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
|
||||
IRQF_EARLY_RESUME,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), palmas_rtc);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret);
|
||||
|
@ -48,6 +48,7 @@
|
||||
|
||||
#define DRV_VERSION "0.6"
|
||||
|
||||
/* REGISTERS */
|
||||
#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */
|
||||
#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */
|
||||
#define PCF2123_REG_SC (0x02) /* datetime */
|
||||
@ -57,10 +58,54 @@
|
||||
#define PCF2123_REG_DW (0x06)
|
||||
#define PCF2123_REG_MO (0x07)
|
||||
#define PCF2123_REG_YR (0x08)
|
||||
#define PCF2123_REG_ALRM_MN (0x09) /* Alarm Registers */
|
||||
#define PCF2123_REG_ALRM_HR (0x0a)
|
||||
#define PCF2123_REG_ALRM_DM (0x0b)
|
||||
#define PCF2123_REG_ALRM_DW (0x0c)
|
||||
#define PCF2123_REG_OFFSET (0x0d) /* Clock Rate Offset Register */
|
||||
#define PCF2123_REG_TMR_CLKOUT (0x0e) /* Timer Registers */
|
||||
#define PCF2123_REG_CTDWN_TMR (0x0f)
|
||||
|
||||
/* PCF2123_REG_CTRL1 BITS */
|
||||
#define CTRL1_CLEAR (0) /* Clear */
|
||||
#define CTRL1_CORR_INT BIT(1) /* Correction irq enable */
|
||||
#define CTRL1_12_HOUR BIT(2) /* 12 hour time */
|
||||
#define CTRL1_SW_RESET (BIT(3) | BIT(4) | BIT(6)) /* Software reset */
|
||||
#define CTRL1_STOP BIT(5) /* Stop the clock */
|
||||
#define CTRL1_EXT_TEST BIT(7) /* External clock test mode */
|
||||
|
||||
/* PCF2123_REG_CTRL2 BITS */
|
||||
#define CTRL2_TIE BIT(0) /* Countdown timer irq enable */
|
||||
#define CTRL2_AIE BIT(1) /* Alarm irq enable */
|
||||
#define CTRL2_TF BIT(2) /* Countdown timer flag */
|
||||
#define CTRL2_AF BIT(3) /* Alarm flag */
|
||||
#define CTRL2_TI_TP BIT(4) /* Irq pin generates pulse */
|
||||
#define CTRL2_MSF BIT(5) /* Minute or second irq flag */
|
||||
#define CTRL2_SI BIT(6) /* Second irq enable */
|
||||
#define CTRL2_MI BIT(7) /* Minute irq enable */
|
||||
|
||||
/* PCF2123_REG_SC BITS */
|
||||
#define OSC_HAS_STOPPED BIT(7) /* Clock has been stopped */
|
||||
|
||||
/* PCF2123_REG_ALRM_XX BITS */
|
||||
#define ALRM_ENABLE BIT(7) /* MN, HR, DM, or DW alarm enable */
|
||||
|
||||
/* PCF2123_REG_TMR_CLKOUT BITS */
|
||||
#define CD_TMR_4096KHZ (0) /* 4096 KHz countdown timer */
|
||||
#define CD_TMR_64HZ (1) /* 64 Hz countdown timer */
|
||||
#define CD_TMR_1HZ (2) /* 1 Hz countdown timer */
|
||||
#define CD_TMR_60th_HZ (3) /* 60th Hz countdown timer */
|
||||
#define CD_TMR_TE BIT(3) /* Countdown timer enable */
|
||||
|
||||
/* PCF2123_REG_OFFSET BITS */
|
||||
#define OFFSET_SIGN_BIT BIT(6) /* 2's complement sign bit */
|
||||
#define OFFSET_COARSE BIT(7) /* Coarse mode offset */
|
||||
#define OFFSET_STEP (2170) /* Offset step in parts per billion */
|
||||
|
||||
/* READ/WRITE ADDRESS BITS */
|
||||
#define PCF2123_WRITE BIT(4)
|
||||
#define PCF2123_READ (BIT(4) | BIT(7))
|
||||
|
||||
#define PCF2123_SUBADDR (1 << 4)
|
||||
#define PCF2123_WRITE ((0 << 7) | PCF2123_SUBADDR)
|
||||
#define PCF2123_READ ((1 << 7) | PCF2123_SUBADDR)
|
||||
|
||||
static struct spi_driver pcf2123_driver;
|
||||
|
||||
@ -84,12 +129,44 @@ static inline void pcf2123_delay_trec(void)
|
||||
ndelay(30);
|
||||
}
|
||||
|
||||
static int pcf2123_read(struct device *dev, u8 reg, u8 *rxbuf, size_t size)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
int ret;
|
||||
|
||||
reg |= PCF2123_READ;
|
||||
ret = spi_write_then_read(spi, ®, 1, rxbuf, size);
|
||||
pcf2123_delay_trec();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcf2123_write(struct device *dev, u8 *txbuf, size_t size)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
int ret;
|
||||
|
||||
txbuf[0] |= PCF2123_WRITE;
|
||||
ret = spi_write(spi, txbuf, size);
|
||||
pcf2123_delay_trec();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcf2123_write_reg(struct device *dev, u8 reg, u8 val)
|
||||
{
|
||||
u8 txbuf[2];
|
||||
|
||||
txbuf[0] = reg;
|
||||
txbuf[1] = val;
|
||||
return pcf2123_write(dev, txbuf, sizeof(txbuf));
|
||||
}
|
||||
|
||||
static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buffer)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct pcf2123_sysfs_reg *r;
|
||||
u8 txbuf[1], rxbuf[1];
|
||||
u8 rxbuf[1];
|
||||
unsigned long reg;
|
||||
int ret;
|
||||
|
||||
@ -99,19 +176,16 @@ static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
txbuf[0] = PCF2123_READ | reg;
|
||||
ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1);
|
||||
ret = pcf2123_read(dev, reg, rxbuf, 1);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
return sprintf(buffer, "0x%x\n", rxbuf[0]);
|
||||
}
|
||||
|
||||
static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buffer, size_t count) {
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct pcf2123_sysfs_reg *r;
|
||||
u8 txbuf[2];
|
||||
unsigned long reg;
|
||||
unsigned long val;
|
||||
|
||||
@ -127,27 +201,78 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
txbuf[0] = PCF2123_WRITE | reg;
|
||||
txbuf[1] = val;
|
||||
ret = spi_write(spi, txbuf, sizeof(txbuf));
|
||||
pcf2123_write_reg(dev, reg, val);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
pcf2123_delay_trec();
|
||||
return count;
|
||||
}
|
||||
|
||||
static int pcf2123_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
int ret;
|
||||
s8 reg;
|
||||
|
||||
ret = pcf2123_read(dev, PCF2123_REG_OFFSET, ®, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (reg & OFFSET_COARSE)
|
||||
reg <<= 1; /* multiply by 2 and sign extend */
|
||||
else
|
||||
reg |= (reg & OFFSET_SIGN_BIT) << 1; /* sign extend only */
|
||||
|
||||
*offset = ((long)reg) * OFFSET_STEP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The offset register is a 7 bit signed value with a coarse bit in bit 7.
|
||||
* The main difference between the two is normal offset adjusts the first
|
||||
* second of n minutes every other hour, with 61, 62 and 63 being shoved
|
||||
* into the 60th minute.
|
||||
* The coarse adjustment does the same, but every hour.
|
||||
* the two overlap, with every even normal offset value corresponding
|
||||
* to a coarse offset. Based on this algorithm, it seems that despite the
|
||||
* name, coarse offset is a better fit for overlapping values.
|
||||
*/
|
||||
static int pcf2123_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
s8 reg;
|
||||
|
||||
if (offset > OFFSET_STEP * 127)
|
||||
reg = 127;
|
||||
else if (offset < OFFSET_STEP * -128)
|
||||
reg = -128;
|
||||
else
|
||||
reg = (s8)((offset + (OFFSET_STEP >> 1)) / OFFSET_STEP);
|
||||
|
||||
/* choose fine offset only for odd values in the normal range */
|
||||
if (reg & 1 && reg <= 63 && reg >= -64) {
|
||||
/* Normal offset. Clear the coarse bit */
|
||||
reg &= ~OFFSET_COARSE;
|
||||
} else {
|
||||
/* Coarse offset. Divide by 2 and set the coarse bit */
|
||||
reg >>= 1;
|
||||
reg |= OFFSET_COARSE;
|
||||
}
|
||||
|
||||
return pcf2123_write_reg(dev, PCF2123_REG_OFFSET, reg);
|
||||
}
|
||||
|
||||
static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
u8 txbuf[1], rxbuf[7];
|
||||
u8 rxbuf[7];
|
||||
int ret;
|
||||
|
||||
txbuf[0] = PCF2123_READ | PCF2123_REG_SC;
|
||||
ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
|
||||
rxbuf, sizeof(rxbuf));
|
||||
ret = pcf2123_read(dev, PCF2123_REG_SC, rxbuf, sizeof(rxbuf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
if (rxbuf[0] & OSC_HAS_STOPPED) {
|
||||
dev_info(dev, "clock was stopped. Time is not valid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F);
|
||||
tm->tm_min = bcd2bin(rxbuf[1] & 0x7F);
|
||||
@ -170,7 +295,6 @@ static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
u8 txbuf[8];
|
||||
int ret;
|
||||
|
||||
@ -181,15 +305,12 @@ static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
|
||||
|
||||
/* Stop the counter first */
|
||||
txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
|
||||
txbuf[1] = 0x20;
|
||||
ret = spi_write(spi, txbuf, 2);
|
||||
ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_STOP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
/* Set the new time */
|
||||
txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC;
|
||||
txbuf[0] = PCF2123_REG_SC;
|
||||
txbuf[1] = bin2bcd(tm->tm_sec & 0x7F);
|
||||
txbuf[2] = bin2bcd(tm->tm_min & 0x7F);
|
||||
txbuf[3] = bin2bcd(tm->tm_hour & 0x3F);
|
||||
@ -198,18 +319,48 @@ static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
|
||||
txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100);
|
||||
|
||||
ret = spi_write(spi, txbuf, sizeof(txbuf));
|
||||
ret = pcf2123_write(dev, txbuf, sizeof(txbuf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
/* Start the counter */
|
||||
txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
|
||||
txbuf[1] = 0x00;
|
||||
ret = spi_write(spi, txbuf, 2);
|
||||
ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_CLEAR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf2123_reset(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
u8 rxbuf[2];
|
||||
|
||||
ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_SW_RESET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Stop the counter */
|
||||
dev_dbg(dev, "stopping RTC\n");
|
||||
ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_STOP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* See if the counter was actually stopped */
|
||||
dev_dbg(dev, "checking for presence of RTC\n");
|
||||
ret = pcf2123_read(dev, PCF2123_REG_CTRL1, rxbuf, sizeof(rxbuf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "received data from RTC (0x%02X 0x%02X)\n",
|
||||
rxbuf[0], rxbuf[1]);
|
||||
if (!(rxbuf[0] & CTRL1_STOP))
|
||||
return -ENODEV;
|
||||
|
||||
/* Start the counter */
|
||||
ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_CLEAR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -217,13 +368,16 @@ static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
static const struct rtc_class_ops pcf2123_rtc_ops = {
|
||||
.read_time = pcf2123_rtc_read_time,
|
||||
.set_time = pcf2123_rtc_set_time,
|
||||
.read_offset = pcf2123_read_offset,
|
||||
.set_offset = pcf2123_set_offset,
|
||||
|
||||
};
|
||||
|
||||
static int pcf2123_probe(struct spi_device *spi)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct rtc_time tm;
|
||||
struct pcf2123_plat_data *pdata;
|
||||
u8 txbuf[2], rxbuf[2];
|
||||
int ret, i;
|
||||
|
||||
pdata = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_plat_data),
|
||||
@ -232,56 +386,19 @@ static int pcf2123_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
spi->dev.platform_data = pdata;
|
||||
|
||||
/* Send a software reset command */
|
||||
txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
|
||||
txbuf[1] = 0x58;
|
||||
dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n",
|
||||
txbuf[0], txbuf[1]);
|
||||
ret = spi_write(spi, txbuf, 2 * sizeof(u8));
|
||||
if (ret < 0)
|
||||
goto kfree_exit;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
/* Stop the counter */
|
||||
txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
|
||||
txbuf[1] = 0x20;
|
||||
dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n",
|
||||
txbuf[0], txbuf[1]);
|
||||
ret = spi_write(spi, txbuf, 2 * sizeof(u8));
|
||||
if (ret < 0)
|
||||
goto kfree_exit;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
/* See if the counter was actually stopped */
|
||||
txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1;
|
||||
dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n",
|
||||
txbuf[0]);
|
||||
ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8),
|
||||
rxbuf, 2 * sizeof(u8));
|
||||
dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n",
|
||||
rxbuf[0], rxbuf[1]);
|
||||
if (ret < 0)
|
||||
goto kfree_exit;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
if (!(rxbuf[0] & 0x20)) {
|
||||
dev_err(&spi->dev, "chip not found\n");
|
||||
ret = -ENODEV;
|
||||
goto kfree_exit;
|
||||
ret = pcf2123_rtc_read_time(&spi->dev, &tm);
|
||||
if (ret < 0) {
|
||||
ret = pcf2123_reset(&spi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "chip not found\n");
|
||||
goto kfree_exit;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
|
||||
dev_info(&spi->dev, "spiclk %u KHz.\n",
|
||||
(spi->max_speed_hz + 500) / 1000);
|
||||
|
||||
/* Start the counter */
|
||||
txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
|
||||
txbuf[1] = 0x00;
|
||||
ret = spi_write(spi, txbuf, sizeof(txbuf));
|
||||
if (ret < 0)
|
||||
goto kfree_exit;
|
||||
pcf2123_delay_trec();
|
||||
|
||||
/* Finalize the initialization */
|
||||
rtc = devm_rtc_device_register(&spi->dev, pcf2123_driver.driver.name,
|
||||
&pcf2123_rtc_ops, THIS_MODULE);
|
||||
|
@ -1,12 +1,12 @@
|
||||
/*
|
||||
* An I2C driver for the NXP PCF2127 RTC
|
||||
* An I2C and SPI driver for the NXP PCF2127/29 RTC
|
||||
* Copyright 2013 Til-Technologies
|
||||
*
|
||||
* Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
|
||||
*
|
||||
* based on the other drivers in this same directory.
|
||||
*
|
||||
* http://www.nxp.com/documents/data_sheet/PCF2127AT.pdf
|
||||
* Datasheet: http://cache.nxp.com/documents/data_sheet/PCF2127.pdf
|
||||
*
|
||||
* 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
|
||||
@ -14,11 +14,13 @@
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */
|
||||
#define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */
|
||||
@ -36,29 +38,30 @@
|
||||
|
||||
#define PCF2127_OSF BIT(7) /* Oscillator Fail flag */
|
||||
|
||||
static struct i2c_driver pcf2127_driver;
|
||||
|
||||
struct pcf2127 {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
/*
|
||||
* In the routines that deal directly with the pcf2127 hardware, we use
|
||||
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
|
||||
*/
|
||||
static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned char buf[10] = { PCF2127_REG_CTRL1 };
|
||||
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
|
||||
unsigned char buf[10];
|
||||
int ret;
|
||||
|
||||
/* read registers */
|
||||
if (i2c_master_send(client, buf, 1) != 1 ||
|
||||
i2c_master_recv(client, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
dev_err(&client->dev, "%s: read error\n", __func__);
|
||||
return -EIO;
|
||||
ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, buf,
|
||||
sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: read error\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF)
|
||||
dev_info(&client->dev,
|
||||
dev_info(dev,
|
||||
"low voltage detected, check/replace RTC battery.\n");
|
||||
|
||||
if (buf[PCF2127_REG_SC] & PCF2127_OSF) {
|
||||
@ -66,12 +69,12 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
* no need clear the flag here,
|
||||
* it will be cleared once the new date is saved
|
||||
*/
|
||||
dev_warn(&client->dev,
|
||||
dev_warn(dev,
|
||||
"oscillator stop detected, date/time is not reliable\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
dev_dbg(dev,
|
||||
"%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, "
|
||||
"sec=%02x, min=%02x, hr=%02x, "
|
||||
"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
|
||||
@ -91,7 +94,7 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
if (tm->tm_year < 70)
|
||||
tm->tm_year += 100; /* assume we are in 1970...2069 */
|
||||
|
||||
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
|
||||
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
|
||||
"mday=%d, mon=%d, year=%d, wday=%d\n",
|
||||
__func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
@ -100,20 +103,18 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
|
||||
unsigned char buf[7];
|
||||
int i = 0, err;
|
||||
|
||||
dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
|
||||
dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, "
|
||||
"mday=%d, mon=%d, year=%d, wday=%d\n",
|
||||
__func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
|
||||
|
||||
/* start register address */
|
||||
buf[i++] = PCF2127_REG_SC;
|
||||
|
||||
/* hours, minutes and seconds */
|
||||
buf[i++] = bin2bcd(tm->tm_sec); /* this will also clear OSF flag */
|
||||
buf[i++] = bin2bcd(tm->tm_min);
|
||||
@ -128,11 +129,11 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
buf[i++] = bin2bcd(tm->tm_year % 100);
|
||||
|
||||
/* write register's data */
|
||||
err = i2c_master_send(client, buf, i);
|
||||
if (err != i) {
|
||||
dev_err(&client->dev,
|
||||
err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"%s: err=%d", __func__, err);
|
||||
return -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -142,26 +143,17 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
static int pcf2127_rtc_ioctl(struct device *dev,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
unsigned char buf = PCF2127_REG_CTRL3;
|
||||
struct pcf2127 *pcf2127 = dev_get_drvdata(dev);
|
||||
int touser;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
ret = i2c_master_send(client, &buf, 1);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
if (ret < 0)
|
||||
ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &touser);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_master_recv(client, &buf, 1);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
touser = buf & PCF2127_REG_CTRL3_BLF ? 1 : 0;
|
||||
touser = touser & PCF2127_REG_CTRL3_BLF ? 1 : 0;
|
||||
|
||||
if (copy_to_user((void __user *)arg, &touser, sizeof(int)))
|
||||
return -EFAULT;
|
||||
@ -174,71 +166,270 @@ static int pcf2127_rtc_ioctl(struct device *dev,
|
||||
#define pcf2127_rtc_ioctl NULL
|
||||
#endif
|
||||
|
||||
static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return pcf2127_get_datetime(to_i2c_client(dev), tm);
|
||||
}
|
||||
|
||||
static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return pcf2127_set_datetime(to_i2c_client(dev), tm);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pcf2127_rtc_ops = {
|
||||
.ioctl = pcf2127_rtc_ioctl,
|
||||
.read_time = pcf2127_rtc_read_time,
|
||||
.set_time = pcf2127_rtc_set_time,
|
||||
};
|
||||
|
||||
static int pcf2127_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pcf2127_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name)
|
||||
{
|
||||
struct pcf2127 *pcf2127;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
pcf2127 = devm_kzalloc(&client->dev, sizeof(struct pcf2127),
|
||||
GFP_KERNEL);
|
||||
pcf2127 = devm_kzalloc(dev, sizeof(*pcf2127), GFP_KERNEL);
|
||||
if (!pcf2127)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, pcf2127);
|
||||
pcf2127->regmap = regmap;
|
||||
|
||||
pcf2127->rtc = devm_rtc_device_register(&client->dev,
|
||||
pcf2127_driver.driver.name,
|
||||
&pcf2127_rtc_ops, THIS_MODULE);
|
||||
dev_set_drvdata(dev, pcf2127);
|
||||
|
||||
pcf2127->rtc = devm_rtc_device_register(dev, name, &pcf2127_rtc_ops,
|
||||
THIS_MODULE);
|
||||
|
||||
return PTR_ERR_OR_ZERO(pcf2127->rtc);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pcf2127_id[] = {
|
||||
{ "pcf2127", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pcf2127_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pcf2127_of_match[] = {
|
||||
{ .compatible = "nxp,pcf2127" },
|
||||
{ .compatible = "nxp,pcf2129" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcf2127_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver pcf2127_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-pcf2127",
|
||||
.of_match_table = of_match_ptr(pcf2127_of_match),
|
||||
},
|
||||
.probe = pcf2127_probe,
|
||||
.id_table = pcf2127_id,
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int pcf2127_i2c_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, data, count);
|
||||
if (ret != count)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf2127_i2c_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
void *buf;
|
||||
|
||||
if (WARN_ON(reg_size != 1))
|
||||
return -EINVAL;
|
||||
|
||||
buf = kmalloc(val_size + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf, reg, 1);
|
||||
memcpy(buf + 1, val, val_size);
|
||||
|
||||
ret = i2c_master_send(client, buf, val_size + 1);
|
||||
if (ret != val_size + 1)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf2127_i2c_read(void *context, const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(reg_size != 1))
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_master_send(client, reg, 1);
|
||||
if (ret != 1)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
ret = i2c_master_recv(client, val, val_size);
|
||||
if (ret != val_size)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The reason we need this custom regmap_bus instead of using regmap_init_i2c()
|
||||
* is that the STOP condition is required between set register address and
|
||||
* read register data when reading from registers.
|
||||
*/
|
||||
static const struct regmap_bus pcf2127_i2c_regmap = {
|
||||
.write = pcf2127_i2c_write,
|
||||
.gather_write = pcf2127_i2c_gather_write,
|
||||
.read = pcf2127_i2c_read,
|
||||
};
|
||||
|
||||
module_i2c_driver(pcf2127_driver);
|
||||
static struct i2c_driver pcf2127_i2c_driver;
|
||||
|
||||
static int pcf2127_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap,
|
||||
&client->dev, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "%s: regmap allocation failed: %ld\n",
|
||||
__func__, PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return pcf2127_probe(&client->dev, regmap,
|
||||
pcf2127_i2c_driver.driver.name);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pcf2127_i2c_id[] = {
|
||||
{ "pcf2127", 0 },
|
||||
{ "pcf2129", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id);
|
||||
|
||||
static struct i2c_driver pcf2127_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-pcf2127-i2c",
|
||||
.of_match_table = of_match_ptr(pcf2127_of_match),
|
||||
},
|
||||
.probe = pcf2127_i2c_probe,
|
||||
.id_table = pcf2127_i2c_id,
|
||||
};
|
||||
|
||||
static int pcf2127_i2c_register_driver(void)
|
||||
{
|
||||
return i2c_add_driver(&pcf2127_i2c_driver);
|
||||
}
|
||||
|
||||
static void pcf2127_i2c_unregister_driver(void)
|
||||
{
|
||||
i2c_del_driver(&pcf2127_i2c_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int pcf2127_i2c_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcf2127_i2c_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
static struct spi_driver pcf2127_spi_driver;
|
||||
|
||||
static int pcf2127_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = 0xa0,
|
||||
.write_flag_mask = 0x20,
|
||||
};
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n",
|
||||
__func__, PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return pcf2127_probe(&spi->dev, regmap, pcf2127_spi_driver.driver.name);
|
||||
}
|
||||
|
||||
static const struct spi_device_id pcf2127_spi_id[] = {
|
||||
{ "pcf2127", 0 },
|
||||
{ "pcf2129", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, pcf2127_spi_id);
|
||||
|
||||
static struct spi_driver pcf2127_spi_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-pcf2127-spi",
|
||||
.of_match_table = of_match_ptr(pcf2127_of_match),
|
||||
},
|
||||
.probe = pcf2127_spi_probe,
|
||||
.id_table = pcf2127_spi_id,
|
||||
};
|
||||
|
||||
static int pcf2127_spi_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&pcf2127_spi_driver);
|
||||
}
|
||||
|
||||
static void pcf2127_spi_unregister_driver(void)
|
||||
{
|
||||
spi_unregister_driver(&pcf2127_spi_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int pcf2127_spi_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcf2127_spi_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int __init pcf2127_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pcf2127_i2c_register_driver();
|
||||
if (ret) {
|
||||
pr_err("Failed to register pcf2127 i2c driver: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pcf2127_spi_register_driver();
|
||||
if (ret) {
|
||||
pr_err("Failed to register pcf2127 spi driver: %d\n", ret);
|
||||
pcf2127_i2c_unregister_driver();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(pcf2127_init)
|
||||
|
||||
static void __exit pcf2127_exit(void)
|
||||
{
|
||||
pcf2127_spi_unregister_driver();
|
||||
pcf2127_i2c_unregister_driver();
|
||||
}
|
||||
module_exit(pcf2127_exit)
|
||||
|
||||
MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>");
|
||||
MODULE_DESCRIPTION("NXP PCF2127 RTC driver");
|
||||
MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -16,12 +16,12 @@
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define DRV_VERSION "0.0.1"
|
||||
|
||||
#define PCF85063_REG_CTRL1 0x00 /* status */
|
||||
#define PCF85063_REG_CTRL1_STOP BIT(5)
|
||||
#define PCF85063_REG_CTRL2 0x01
|
||||
|
||||
#define PCF85063_REG_SC 0x04 /* datetime */
|
||||
#define PCF85063_REG_SC_OS 0x80
|
||||
#define PCF85063_REG_MN 0x05
|
||||
#define PCF85063_REG_HR 0x06
|
||||
#define PCF85063_REG_DM 0x07
|
||||
@ -29,15 +29,31 @@
|
||||
#define PCF85063_REG_MO 0x09
|
||||
#define PCF85063_REG_YR 0x0A
|
||||
|
||||
#define PCF85063_MO_C 0x80 /* century */
|
||||
|
||||
static struct i2c_driver pcf85063_driver;
|
||||
|
||||
struct pcf85063 {
|
||||
struct rtc_device *rtc;
|
||||
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
|
||||
int voltage_low; /* indicates if a low_voltage was detected */
|
||||
};
|
||||
static int pcf85063_stop_clock(struct i2c_client *client, u8 *ctrl1)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failing to stop the clock\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* stop the clock */
|
||||
ret |= PCF85063_REG_CTRL1_STOP;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failing to stop the clock\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*ctrl1 = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the routines that deal directly with the pcf85063 hardware, we use
|
||||
@ -45,81 +61,85 @@ struct pcf85063 {
|
||||
*/
|
||||
static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = i2c_get_clientdata(client);
|
||||
unsigned char buf[13] = { PCF85063_REG_CTRL1 };
|
||||
struct i2c_msg msgs[] = {
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = buf
|
||||
},
|
||||
{/* read status + date */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 13,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
u8 regs[7];
|
||||
|
||||
/* read registers */
|
||||
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
|
||||
dev_err(&client->dev, "%s: read error\n", __func__);
|
||||
/*
|
||||
* while reading, the time/date registers are blocked and not updated
|
||||
* anymore until the access is finished. To not lose a second
|
||||
* event, the access must be finished within one second. So, read all
|
||||
* time/date registers in one turn.
|
||||
*/
|
||||
rc = i2c_smbus_read_i2c_block_data(client, PCF85063_REG_SC,
|
||||
sizeof(regs), regs);
|
||||
if (rc != sizeof(regs)) {
|
||||
dev_err(&client->dev, "date/time register read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F);
|
||||
tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F);
|
||||
tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */
|
||||
tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F);
|
||||
tm->tm_wday = buf[PCF85063_REG_DW] & 0x07;
|
||||
tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
|
||||
tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]);
|
||||
/* if the clock has lost its power it makes no sense to use its time */
|
||||
if (regs[0] & PCF85063_REG_SC_OS) {
|
||||
dev_warn(&client->dev, "Power loss detected, invalid time\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(regs[0] & 0x7F);
|
||||
tm->tm_min = bcd2bin(regs[1] & 0x7F);
|
||||
tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
|
||||
tm->tm_mday = bcd2bin(regs[3] & 0x3F);
|
||||
tm->tm_wday = regs[4] & 0x07;
|
||||
tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */
|
||||
tm->tm_year = bcd2bin(regs[6]);
|
||||
if (tm->tm_year < 70)
|
||||
tm->tm_year += 100; /* assume we are in 1970...2069 */
|
||||
/* detect the polarity heuristically. see note above. */
|
||||
pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
|
||||
(tm->tm_year >= 100) : (tm->tm_year < 100);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
{
|
||||
int i = 0, err = 0;
|
||||
unsigned char buf[11];
|
||||
int rc;
|
||||
u8 regs[8];
|
||||
|
||||
/* Control & status */
|
||||
buf[PCF85063_REG_CTRL1] = 0;
|
||||
buf[PCF85063_REG_CTRL2] = 5;
|
||||
/*
|
||||
* to accurately set the time, reset the divider chain and keep it in
|
||||
* reset state until all time/date registers are written
|
||||
*/
|
||||
rc = pcf85063_stop_clock(client, ®s[7]);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/* hours, minutes and seconds */
|
||||
buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F;
|
||||
regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */
|
||||
|
||||
buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min);
|
||||
buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour);
|
||||
regs[1] = bin2bcd(tm->tm_min);
|
||||
regs[2] = bin2bcd(tm->tm_hour);
|
||||
|
||||
/* Day of month, 1 - 31 */
|
||||
buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday);
|
||||
regs[3] = bin2bcd(tm->tm_mday);
|
||||
|
||||
/* Day, 0 - 6 */
|
||||
buf[PCF85063_REG_DW] = tm->tm_wday & 0x07;
|
||||
regs[4] = tm->tm_wday & 0x07;
|
||||
|
||||
/* month, 1 - 12 */
|
||||
buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1);
|
||||
regs[5] = bin2bcd(tm->tm_mon + 1);
|
||||
|
||||
/* year and century */
|
||||
buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100);
|
||||
regs[6] = bin2bcd(tm->tm_year % 100);
|
||||
|
||||
/* write register's data */
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
unsigned char data[2] = { i, buf[i] };
|
||||
/*
|
||||
* after all time/date registers are written, let the 'address auto
|
||||
* increment' feature wrap around and write register CTRL1 to re-enable
|
||||
* the clock divider chain again
|
||||
*/
|
||||
regs[7] &= ~PCF85063_REG_CTRL1_STOP;
|
||||
|
||||
err = i2c_master_send(client, data, sizeof(data));
|
||||
if (err != sizeof(data)) {
|
||||
dev_err(&client->dev, "%s: err=%d addr=%02x, data=%02x\n",
|
||||
__func__, err, data[0], data[1]);
|
||||
return -EIO;
|
||||
}
|
||||
/* write all registers at once */
|
||||
rc = i2c_smbus_write_i2c_block_data(client, PCF85063_REG_SC,
|
||||
sizeof(regs), regs);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "date/time register write error\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -143,27 +163,18 @@ static const struct rtc_class_ops pcf85063_rtc_ops = {
|
||||
static int pcf85063_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pcf85063 *pcf85063;
|
||||
struct rtc_device *rtc;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
|
||||
GFP_KERNEL);
|
||||
if (!pcf85063)
|
||||
return -ENOMEM;
|
||||
rtc = devm_rtc_device_register(&client->dev,
|
||||
pcf85063_driver.driver.name,
|
||||
&pcf85063_rtc_ops, THIS_MODULE);
|
||||
|
||||
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
|
||||
|
||||
i2c_set_clientdata(client, pcf85063);
|
||||
|
||||
pcf85063->rtc = devm_rtc_device_register(&client->dev,
|
||||
pcf85063_driver.driver.name,
|
||||
&pcf85063_rtc_ops, THIS_MODULE);
|
||||
|
||||
return PTR_ERR_OR_ZERO(pcf85063->rtc);
|
||||
return PTR_ERR_OR_ZERO(rtc);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pcf85063_id[] = {
|
||||
@ -194,4 +205,3 @@ module_i2c_driver(pcf85063_driver);
|
||||
MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
|
||||
MODULE_DESCRIPTION("PCF85063 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
@ -178,28 +178,8 @@ static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (regs[0] & REG_SECONDS_OS) {
|
||||
/*
|
||||
* If the oscillator was stopped, try to clear the flag. Upon
|
||||
* power-up the flag is always set, but if we cannot clear it
|
||||
* the oscillator isn't running properly for some reason. The
|
||||
* sensible thing therefore is to return an error, signalling
|
||||
* that the clock cannot be assumed to be correct.
|
||||
*/
|
||||
|
||||
regs[0] &= ~REG_SECONDS_OS;
|
||||
|
||||
err = pcf8523_write(client, REG_SECONDS, regs[0]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = pcf8523_read(client, REG_SECONDS, ®s[0]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (regs[0] & REG_SECONDS_OS)
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (regs[0] & REG_SECONDS_OS)
|
||||
return -EINVAL;
|
||||
|
||||
tm->tm_sec = bcd2bin(regs[0] & 0x7f);
|
||||
tm->tm_min = bcd2bin(regs[1] & 0x7f);
|
||||
@ -235,6 +215,7 @@ static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
return err;
|
||||
|
||||
regs[0] = REG_SECONDS;
|
||||
/* This will purposely overwrite REG_SECONDS_OS */
|
||||
regs[1] = bin2bcd(tm->tm_sec);
|
||||
regs[2] = bin2bcd(tm->tm_min);
|
||||
regs[3] = bin2bcd(tm->tm_hour);
|
||||
|
411
drivers/rtc/rtc-pic32.c
Normal file
411
drivers/rtc/rtc-pic32.c
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* PIC32 RTC driver
|
||||
*
|
||||
* Joshua Henderson <joshua.henderson@microchip.com>
|
||||
* Copyright (C) 2016 Microchip Technology Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
#include <asm/mach-pic32/pic32.h>
|
||||
|
||||
#define PIC32_RTCCON 0x00
|
||||
#define PIC32_RTCCON_ON BIT(15)
|
||||
#define PIC32_RTCCON_SIDL BIT(13)
|
||||
#define PIC32_RTCCON_RTCCLKSEL (3 << 9)
|
||||
#define PIC32_RTCCON_RTCCLKON BIT(6)
|
||||
#define PIC32_RTCCON_RTCWREN BIT(3)
|
||||
#define PIC32_RTCCON_RTCSYNC BIT(2)
|
||||
#define PIC32_RTCCON_HALFSEC BIT(1)
|
||||
#define PIC32_RTCCON_RTCOE BIT(0)
|
||||
|
||||
#define PIC32_RTCALRM 0x10
|
||||
#define PIC32_RTCALRM_ALRMEN BIT(15)
|
||||
#define PIC32_RTCALRM_CHIME BIT(14)
|
||||
#define PIC32_RTCALRM_PIV BIT(13)
|
||||
#define PIC32_RTCALRM_ALARMSYNC BIT(12)
|
||||
#define PIC32_RTCALRM_AMASK 0x0F00
|
||||
#define PIC32_RTCALRM_ARPT 0xFF
|
||||
|
||||
#define PIC32_RTCHOUR 0x23
|
||||
#define PIC32_RTCMIN 0x22
|
||||
#define PIC32_RTCSEC 0x21
|
||||
#define PIC32_RTCYEAR 0x33
|
||||
#define PIC32_RTCMON 0x32
|
||||
#define PIC32_RTCDAY 0x31
|
||||
|
||||
#define PIC32_ALRMTIME 0x40
|
||||
#define PIC32_ALRMDATE 0x50
|
||||
|
||||
#define PIC32_ALRMHOUR 0x43
|
||||
#define PIC32_ALRMMIN 0x42
|
||||
#define PIC32_ALRMSEC 0x41
|
||||
#define PIC32_ALRMYEAR 0x53
|
||||
#define PIC32_ALRMMON 0x52
|
||||
#define PIC32_ALRMDAY 0x51
|
||||
|
||||
struct pic32_rtc_dev {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *reg_base;
|
||||
struct clk *clk;
|
||||
spinlock_t alarm_lock;
|
||||
int alarm_irq;
|
||||
bool alarm_clk_enabled;
|
||||
};
|
||||
|
||||
static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata,
|
||||
bool enable)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pdata->alarm_lock, flags);
|
||||
if (enable) {
|
||||
if (!pdata->alarm_clk_enabled) {
|
||||
clk_enable(pdata->clk);
|
||||
pdata->alarm_clk_enabled = true;
|
||||
}
|
||||
} else {
|
||||
if (pdata->alarm_clk_enabled) {
|
||||
clk_disable(pdata->clk);
|
||||
pdata->alarm_clk_enabled = false;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&pdata->alarm_lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t pic32_rtc_alarmirq(int irq, void *id)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
clk_disable(pdata->clk);
|
||||
|
||||
pic32_rtc_alarm_clk_enable(pdata, false);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pic32_rtc_setaie(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *base = pdata->reg_base;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
|
||||
writel(PIC32_RTCALRM_ALRMEN,
|
||||
base + (enabled ? PIC32_SET(PIC32_RTCALRM) :
|
||||
PIC32_CLR(PIC32_RTCALRM)));
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
|
||||
pic32_rtc_alarm_clk_enable(pdata, enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pic32_rtc_setfreq(struct device *dev, int freq)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *base = pdata->reg_base;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
|
||||
writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM));
|
||||
writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM));
|
||||
writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM));
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *base = pdata->reg_base;
|
||||
unsigned int tries = 0;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
|
||||
do {
|
||||
rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR);
|
||||
rtc_tm->tm_min = readb(base + PIC32_RTCMIN);
|
||||
rtc_tm->tm_mon = readb(base + PIC32_RTCMON);
|
||||
rtc_tm->tm_mday = readb(base + PIC32_RTCDAY);
|
||||
rtc_tm->tm_year = readb(base + PIC32_RTCYEAR);
|
||||
rtc_tm->tm_sec = readb(base + PIC32_RTCSEC);
|
||||
|
||||
/*
|
||||
* The only way to work out whether the system was mid-update
|
||||
* when we read it is to check the second counter, and if it
|
||||
* is zero, then we re-try the entire read.
|
||||
*/
|
||||
tries += 1;
|
||||
} while (rtc_tm->tm_sec == 0 && tries < 2);
|
||||
|
||||
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
|
||||
rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
|
||||
rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
|
||||
rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
|
||||
rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1;
|
||||
rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
|
||||
|
||||
rtc_tm->tm_year += 100;
|
||||
|
||||
dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||
1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
|
||||
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
return rtc_valid_tm(rtc_tm);
|
||||
}
|
||||
|
||||
static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *base = pdata->reg_base;
|
||||
int year = tm->tm_year - 100;
|
||||
|
||||
dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
if (year < 0 || year >= 100) {
|
||||
dev_err(dev, "rtc only supports 100 years\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC);
|
||||
writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN);
|
||||
writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR);
|
||||
writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY);
|
||||
writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON);
|
||||
writeb(bin2bcd(year), base + PIC32_RTCYEAR);
|
||||
clk_disable(pdata->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
struct rtc_time *alm_tm = &alrm->time;
|
||||
void __iomem *base = pdata->reg_base;
|
||||
unsigned int alm_en;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
alm_tm->tm_sec = readb(base + PIC32_ALRMSEC);
|
||||
alm_tm->tm_min = readb(base + PIC32_ALRMMIN);
|
||||
alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR);
|
||||
alm_tm->tm_mon = readb(base + PIC32_ALRMMON);
|
||||
alm_tm->tm_mday = readb(base + PIC32_ALRMDAY);
|
||||
alm_tm->tm_year = readb(base + PIC32_ALRMYEAR);
|
||||
|
||||
alm_en = readb(base + PIC32_RTCALRM);
|
||||
|
||||
alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0;
|
||||
|
||||
dev_dbg(dev, "getalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||
alm_en,
|
||||
1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
|
||||
alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
|
||||
|
||||
alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
|
||||
alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
|
||||
alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
|
||||
alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
|
||||
alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1;
|
||||
alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
struct rtc_time *tm = &alrm->time;
|
||||
void __iomem *base = pdata->reg_base;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
dev_dbg(dev, "setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||
alrm->enabled,
|
||||
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
writel(0x00, base + PIC32_ALRMTIME);
|
||||
writel(0x00, base + PIC32_ALRMDATE);
|
||||
|
||||
pic32_rtc_setaie(dev, alrm->enabled);
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pic32_rtc_proc(struct device *dev, struct seq_file *seq)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
void __iomem *base = pdata->reg_base;
|
||||
unsigned int repeat;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
|
||||
repeat = readw(base + PIC32_RTCALRM);
|
||||
repeat &= PIC32_RTCALRM_ARPT;
|
||||
seq_printf(seq, "periodic_IRQ\t: %s\n", repeat ? "yes" : "no");
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pic32_rtcops = {
|
||||
.read_time = pic32_rtc_gettime,
|
||||
.set_time = pic32_rtc_settime,
|
||||
.read_alarm = pic32_rtc_getalarm,
|
||||
.set_alarm = pic32_rtc_setalarm,
|
||||
.proc = pic32_rtc_proc,
|
||||
.alarm_irq_enable = pic32_rtc_setaie,
|
||||
};
|
||||
|
||||
static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en)
|
||||
{
|
||||
void __iomem *base = pdata->reg_base;
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
if (!en) {
|
||||
writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON));
|
||||
} else {
|
||||
pic32_syskey_unlock();
|
||||
|
||||
writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON));
|
||||
writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON));
|
||||
|
||||
if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON))
|
||||
writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON));
|
||||
}
|
||||
clk_disable(pdata->clk);
|
||||
}
|
||||
|
||||
static int pic32_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
pic32_rtc_setaie(&pdev->dev, 0);
|
||||
clk_unprepare(pdata->clk);
|
||||
pdata->clk = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pic32_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pic32_rtc_dev *pdata;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
pdata->alarm_irq = platform_get_irq(pdev, 0);
|
||||
if (pdata->alarm_irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for alarm\n");
|
||||
return pdata->alarm_irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pdata->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pdata->reg_base))
|
||||
return PTR_ERR(pdata->reg_base);
|
||||
|
||||
pdata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pdata->clk)) {
|
||||
dev_err(&pdev->dev, "failed to find rtc clock source\n");
|
||||
ret = PTR_ERR(pdata->clk);
|
||||
pdata->clk = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_init(&pdata->alarm_lock);
|
||||
|
||||
clk_prepare_enable(pdata->clk);
|
||||
|
||||
pic32_rtc_enable(pdata, 1);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&pic32_rtcops,
|
||||
THIS_MODULE);
|
||||
if (IS_ERR(pdata->rtc)) {
|
||||
ret = PTR_ERR(pdata->rtc);
|
||||
goto err_nortc;
|
||||
}
|
||||
|
||||
pdata->rtc->max_user_freq = 128;
|
||||
|
||||
pic32_rtc_setfreq(&pdev->dev, 1);
|
||||
ret = devm_request_irq(&pdev->dev, pdata->alarm_irq,
|
||||
pic32_rtc_alarmirq, 0,
|
||||
dev_name(&pdev->dev), pdata);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"IRQ %d error %d\n", pdata->alarm_irq, ret);
|
||||
goto err_nortc;
|
||||
}
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
|
||||
return 0;
|
||||
|
||||
err_nortc:
|
||||
pic32_rtc_enable(pdata, 0);
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id pic32_rtc_dt_ids[] = {
|
||||
{ .compatible = "microchip,pic32mzda-rtc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids);
|
||||
|
||||
static struct platform_driver pic32_rtc_driver = {
|
||||
.probe = pic32_rtc_probe,
|
||||
.remove = pic32_rtc_remove,
|
||||
.driver = {
|
||||
.name = "pic32-rtc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(pic32_rtc_dt_ids),
|
||||
},
|
||||
};
|
||||
module_platform_driver(pic32_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Microchip PIC32 RTC Driver");
|
||||
MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Micro Crystal RV-3029C2 rtc class driver
|
||||
* Micro Crystal RV-3029 rtc class driver
|
||||
*
|
||||
* Author: Gregory Hermant <gregory.hermant@calao-systems.com>
|
||||
* Michael Buesch <m@bues.ch>
|
||||
*
|
||||
* based on previously existing rtc class drivers
|
||||
*
|
||||
@ -9,89 +10,120 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* NOTE: Currently this driver only supports the bare minimum for read
|
||||
* and write the RTC and alarms. The extra features provided by this chip
|
||||
* (trickle charger, eeprom, T° compensation) are unavailable.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
|
||||
|
||||
/* Register map */
|
||||
/* control section */
|
||||
#define RV3029C2_ONOFF_CTRL 0x00
|
||||
#define RV3029C2_IRQ_CTRL 0x01
|
||||
#define RV3029C2_IRQ_CTRL_AIE (1 << 0)
|
||||
#define RV3029C2_IRQ_FLAGS 0x02
|
||||
#define RV3029C2_IRQ_FLAGS_AF (1 << 0)
|
||||
#define RV3029C2_STATUS 0x03
|
||||
#define RV3029C2_STATUS_VLOW1 (1 << 2)
|
||||
#define RV3029C2_STATUS_VLOW2 (1 << 3)
|
||||
#define RV3029C2_STATUS_SR (1 << 4)
|
||||
#define RV3029C2_STATUS_PON (1 << 5)
|
||||
#define RV3029C2_STATUS_EEBUSY (1 << 7)
|
||||
#define RV3029C2_RST_CTRL 0x04
|
||||
#define RV3029C2_CONTROL_SECTION_LEN 0x05
|
||||
#define RV3029_ONOFF_CTRL 0x00
|
||||
#define RV3029_ONOFF_CTRL_WE BIT(0)
|
||||
#define RV3029_ONOFF_CTRL_TE BIT(1)
|
||||
#define RV3029_ONOFF_CTRL_TAR BIT(2)
|
||||
#define RV3029_ONOFF_CTRL_EERE BIT(3)
|
||||
#define RV3029_ONOFF_CTRL_SRON BIT(4)
|
||||
#define RV3029_ONOFF_CTRL_TD0 BIT(5)
|
||||
#define RV3029_ONOFF_CTRL_TD1 BIT(6)
|
||||
#define RV3029_ONOFF_CTRL_CLKINT BIT(7)
|
||||
#define RV3029_IRQ_CTRL 0x01
|
||||
#define RV3029_IRQ_CTRL_AIE BIT(0)
|
||||
#define RV3029_IRQ_CTRL_TIE BIT(1)
|
||||
#define RV3029_IRQ_CTRL_V1IE BIT(2)
|
||||
#define RV3029_IRQ_CTRL_V2IE BIT(3)
|
||||
#define RV3029_IRQ_CTRL_SRIE BIT(4)
|
||||
#define RV3029_IRQ_FLAGS 0x02
|
||||
#define RV3029_IRQ_FLAGS_AF BIT(0)
|
||||
#define RV3029_IRQ_FLAGS_TF BIT(1)
|
||||
#define RV3029_IRQ_FLAGS_V1IF BIT(2)
|
||||
#define RV3029_IRQ_FLAGS_V2IF BIT(3)
|
||||
#define RV3029_IRQ_FLAGS_SRF BIT(4)
|
||||
#define RV3029_STATUS 0x03
|
||||
#define RV3029_STATUS_VLOW1 BIT(2)
|
||||
#define RV3029_STATUS_VLOW2 BIT(3)
|
||||
#define RV3029_STATUS_SR BIT(4)
|
||||
#define RV3029_STATUS_PON BIT(5)
|
||||
#define RV3029_STATUS_EEBUSY BIT(7)
|
||||
#define RV3029_RST_CTRL 0x04
|
||||
#define RV3029_RST_CTRL_SYSR BIT(4)
|
||||
#define RV3029_CONTROL_SECTION_LEN 0x05
|
||||
|
||||
/* watch section */
|
||||
#define RV3029C2_W_SEC 0x08
|
||||
#define RV3029C2_W_MINUTES 0x09
|
||||
#define RV3029C2_W_HOURS 0x0A
|
||||
#define RV3029C2_REG_HR_12_24 (1<<6) /* 24h/12h mode */
|
||||
#define RV3029C2_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
|
||||
#define RV3029C2_W_DATE 0x0B
|
||||
#define RV3029C2_W_DAYS 0x0C
|
||||
#define RV3029C2_W_MONTHS 0x0D
|
||||
#define RV3029C2_W_YEARS 0x0E
|
||||
#define RV3029C2_WATCH_SECTION_LEN 0x07
|
||||
#define RV3029_W_SEC 0x08
|
||||
#define RV3029_W_MINUTES 0x09
|
||||
#define RV3029_W_HOURS 0x0A
|
||||
#define RV3029_REG_HR_12_24 BIT(6) /* 24h/12h mode */
|
||||
#define RV3029_REG_HR_PM BIT(5) /* PM/AM bit in 12h mode */
|
||||
#define RV3029_W_DATE 0x0B
|
||||
#define RV3029_W_DAYS 0x0C
|
||||
#define RV3029_W_MONTHS 0x0D
|
||||
#define RV3029_W_YEARS 0x0E
|
||||
#define RV3029_WATCH_SECTION_LEN 0x07
|
||||
|
||||
/* alarm section */
|
||||
#define RV3029C2_A_SC 0x10
|
||||
#define RV3029C2_A_MN 0x11
|
||||
#define RV3029C2_A_HR 0x12
|
||||
#define RV3029C2_A_DT 0x13
|
||||
#define RV3029C2_A_DW 0x14
|
||||
#define RV3029C2_A_MO 0x15
|
||||
#define RV3029C2_A_YR 0x16
|
||||
#define RV3029C2_ALARM_SECTION_LEN 0x07
|
||||
#define RV3029_A_SC 0x10
|
||||
#define RV3029_A_MN 0x11
|
||||
#define RV3029_A_HR 0x12
|
||||
#define RV3029_A_DT 0x13
|
||||
#define RV3029_A_DW 0x14
|
||||
#define RV3029_A_MO 0x15
|
||||
#define RV3029_A_YR 0x16
|
||||
#define RV3029_ALARM_SECTION_LEN 0x07
|
||||
|
||||
/* timer section */
|
||||
#define RV3029C2_TIMER_LOW 0x18
|
||||
#define RV3029C2_TIMER_HIGH 0x19
|
||||
#define RV3029_TIMER_LOW 0x18
|
||||
#define RV3029_TIMER_HIGH 0x19
|
||||
|
||||
/* temperature section */
|
||||
#define RV3029C2_TEMP_PAGE 0x20
|
||||
#define RV3029_TEMP_PAGE 0x20
|
||||
|
||||
/* eeprom data section */
|
||||
#define RV3029C2_E2P_EEDATA1 0x28
|
||||
#define RV3029C2_E2P_EEDATA2 0x29
|
||||
#define RV3029_E2P_EEDATA1 0x28
|
||||
#define RV3029_E2P_EEDATA2 0x29
|
||||
#define RV3029_E2PDATA_SECTION_LEN 0x02
|
||||
|
||||
/* eeprom control section */
|
||||
#define RV3029C2_CONTROL_E2P_EECTRL 0x30
|
||||
#define RV3029C2_TRICKLE_1K (1<<0) /* 1K resistance */
|
||||
#define RV3029C2_TRICKLE_5K (1<<1) /* 5K resistance */
|
||||
#define RV3029C2_TRICKLE_20K (1<<2) /* 20K resistance */
|
||||
#define RV3029C2_TRICKLE_80K (1<<3) /* 80K resistance */
|
||||
#define RV3029C2_CONTROL_E2P_XTALOFFSET 0x31
|
||||
#define RV3029C2_CONTROL_E2P_QCOEF 0x32
|
||||
#define RV3029C2_CONTROL_E2P_TURNOVER 0x33
|
||||
#define RV3029_CONTROL_E2P_EECTRL 0x30
|
||||
#define RV3029_EECTRL_THP BIT(0) /* temp scan interval */
|
||||
#define RV3029_EECTRL_THE BIT(1) /* thermometer enable */
|
||||
#define RV3029_EECTRL_FD0 BIT(2) /* CLKOUT */
|
||||
#define RV3029_EECTRL_FD1 BIT(3) /* CLKOUT */
|
||||
#define RV3029_TRICKLE_1K BIT(4) /* 1.5K resistance */
|
||||
#define RV3029_TRICKLE_5K BIT(5) /* 5K resistance */
|
||||
#define RV3029_TRICKLE_20K BIT(6) /* 20K resistance */
|
||||
#define RV3029_TRICKLE_80K BIT(7) /* 80K resistance */
|
||||
#define RV3029_TRICKLE_MASK (RV3029_TRICKLE_1K |\
|
||||
RV3029_TRICKLE_5K |\
|
||||
RV3029_TRICKLE_20K |\
|
||||
RV3029_TRICKLE_80K)
|
||||
#define RV3029_TRICKLE_SHIFT 4
|
||||
#define RV3029_CONTROL_E2P_XOFFS 0x31 /* XTAL offset */
|
||||
#define RV3029_CONTROL_E2P_XOFFS_SIGN BIT(7) /* Sign: 1->pos, 0->neg */
|
||||
#define RV3029_CONTROL_E2P_QCOEF 0x32 /* XTAL temp drift coef */
|
||||
#define RV3029_CONTROL_E2P_TURNOVER 0x33 /* XTAL turnover temp (in *C) */
|
||||
#define RV3029_CONTROL_E2P_TOV_MASK 0x3F /* XTAL turnover temp mask */
|
||||
|
||||
/* user ram section */
|
||||
#define RV3029C2_USR1_RAM_PAGE 0x38
|
||||
#define RV3029C2_USR1_SECTION_LEN 0x04
|
||||
#define RV3029C2_USR2_RAM_PAGE 0x3C
|
||||
#define RV3029C2_USR2_SECTION_LEN 0x04
|
||||
#define RV3029_USR1_RAM_PAGE 0x38
|
||||
#define RV3029_USR1_SECTION_LEN 0x04
|
||||
#define RV3029_USR2_RAM_PAGE 0x3C
|
||||
#define RV3029_USR2_SECTION_LEN 0x04
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf,
|
||||
unsigned len)
|
||||
rv3029_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf,
|
||||
unsigned len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
|
||||
(reg + len > RV3029C2_USR1_RAM_PAGE + 8))
|
||||
if ((reg > RV3029_USR1_RAM_PAGE + 7) ||
|
||||
(reg + len > RV3029_USR1_RAM_PAGE + 8))
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
|
||||
@ -103,20 +135,38 @@ rv3029c2_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf,
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_write_regs(struct i2c_client *client, u8 reg, u8 const buf[],
|
||||
unsigned len)
|
||||
rv3029_i2c_write_regs(struct i2c_client *client, u8 reg, u8 const buf[],
|
||||
unsigned len)
|
||||
{
|
||||
if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
|
||||
(reg + len > RV3029C2_USR1_RAM_PAGE + 8))
|
||||
if ((reg > RV3029_USR1_RAM_PAGE + 7) ||
|
||||
(reg + len > RV3029_USR1_RAM_PAGE + 8))
|
||||
return -EINVAL;
|
||||
|
||||
return i2c_smbus_write_i2c_block_data(client, reg, len, buf);
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_get_sr(struct i2c_client *client, u8 *buf)
|
||||
rv3029_i2c_update_bits(struct i2c_client *client, u8 reg, u8 mask, u8 set)
|
||||
{
|
||||
int ret = rv3029c2_i2c_read_regs(client, RV3029C2_STATUS, buf, 1);
|
||||
u8 buf;
|
||||
int ret;
|
||||
|
||||
ret = rv3029_i2c_read_regs(client, reg, &buf, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf &= ~mask;
|
||||
buf |= set & mask;
|
||||
ret = rv3029_i2c_write_regs(client, reg, &buf, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029_i2c_get_sr(struct i2c_client *client, u8 *buf)
|
||||
{
|
||||
int ret = rv3029_i2c_read_regs(client, RV3029_STATUS, buf, 1);
|
||||
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
@ -125,83 +175,224 @@ rv3029c2_i2c_get_sr(struct i2c_client *client, u8 *buf)
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_set_sr(struct i2c_client *client, u8 val)
|
||||
rv3029_i2c_set_sr(struct i2c_client *client, u8 val)
|
||||
{
|
||||
u8 buf[1];
|
||||
int sr;
|
||||
|
||||
buf[0] = val;
|
||||
sr = rv3029c2_i2c_write_regs(client, RV3029C2_STATUS, buf, 1);
|
||||
sr = rv3029_i2c_write_regs(client, RV3029_STATUS, buf, 1);
|
||||
dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]);
|
||||
if (sr < 0)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_busywait(struct i2c_client *client)
|
||||
{
|
||||
int i, ret;
|
||||
u8 sr;
|
||||
|
||||
for (i = 100; i > 0; i--) {
|
||||
ret = rv3029_i2c_get_sr(client, &sr);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (!(sr & RV3029_STATUS_EEBUSY))
|
||||
break;
|
||||
usleep_range(1000, 10000);
|
||||
}
|
||||
if (i <= 0) {
|
||||
dev_err(&client->dev, "EEPROM busy wait timeout.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_exit(struct i2c_client *client)
|
||||
{
|
||||
/* Re-enable eeprom refresh */
|
||||
return rv3029_i2c_update_bits(client, RV3029_ONOFF_CTRL,
|
||||
RV3029_ONOFF_CTRL_EERE,
|
||||
RV3029_ONOFF_CTRL_EERE);
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_enter(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
u8 sr;
|
||||
|
||||
/* Check whether we are in the allowed voltage range. */
|
||||
ret = rv3029_i2c_get_sr(client, &sr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) {
|
||||
/* We clear the bits and retry once just in case
|
||||
* we had a brown out in early startup.
|
||||
*/
|
||||
sr &= ~RV3029_STATUS_VLOW1;
|
||||
sr &= ~RV3029_STATUS_VLOW2;
|
||||
ret = rv3029_i2c_set_sr(client, sr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
usleep_range(1000, 10000);
|
||||
ret = rv3029_i2c_get_sr(client, &sr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) {
|
||||
dev_err(&client->dev,
|
||||
"Supply voltage is too low to safely access the EEPROM.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable eeprom refresh. */
|
||||
ret = rv3029_i2c_update_bits(client, RV3029_ONOFF_CTRL,
|
||||
RV3029_ONOFF_CTRL_EERE, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Wait for any previous eeprom accesses to finish. */
|
||||
ret = rv3029_eeprom_busywait(client);
|
||||
if (ret < 0)
|
||||
rv3029_eeprom_exit(client);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_read(struct i2c_client *client, u8 reg,
|
||||
u8 buf[], size_t len)
|
||||
{
|
||||
int ret, err;
|
||||
|
||||
err = rv3029_eeprom_enter(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ret = rv3029_i2c_read_regs(client, reg, buf, len);
|
||||
|
||||
err = rv3029_eeprom_exit(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_write(struct i2c_client *client, u8 reg,
|
||||
u8 const buf[], size_t len)
|
||||
{
|
||||
int ret, err;
|
||||
size_t i;
|
||||
u8 tmp;
|
||||
|
||||
err = rv3029_eeprom_enter(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < len; i++, reg++) {
|
||||
ret = rv3029_i2c_read_regs(client, reg, &tmp, 1);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (tmp != buf[i]) {
|
||||
ret = rv3029_i2c_write_regs(client, reg, &buf[i], 1);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
ret = rv3029_eeprom_busywait(client);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
err = rv3029_eeprom_exit(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_update_bits(struct i2c_client *client,
|
||||
u8 reg, u8 mask, u8 set)
|
||||
{
|
||||
u8 buf;
|
||||
int ret;
|
||||
|
||||
ret = rv3029_eeprom_read(client, reg, &buf, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf &= ~mask;
|
||||
buf |= set & mask;
|
||||
ret = rv3029_eeprom_write(client, reg, &buf, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
|
||||
rv3029_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
|
||||
{
|
||||
u8 buf[1];
|
||||
int ret;
|
||||
u8 regs[RV3029C2_WATCH_SECTION_LEN] = { 0, };
|
||||
u8 regs[RV3029_WATCH_SECTION_LEN] = { 0, };
|
||||
|
||||
ret = rv3029c2_i2c_get_sr(client, buf);
|
||||
ret = rv3029_i2c_get_sr(client, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = rv3029c2_i2c_read_regs(client, RV3029C2_W_SEC , regs,
|
||||
RV3029C2_WATCH_SECTION_LEN);
|
||||
ret = rv3029_i2c_read_regs(client, RV3029_W_SEC, regs,
|
||||
RV3029_WATCH_SECTION_LEN);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading RTC section failed\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(regs[RV3029C2_W_SEC-RV3029C2_W_SEC]);
|
||||
tm->tm_min = bcd2bin(regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC]);
|
||||
tm->tm_sec = bcd2bin(regs[RV3029_W_SEC-RV3029_W_SEC]);
|
||||
tm->tm_min = bcd2bin(regs[RV3029_W_MINUTES-RV3029_W_SEC]);
|
||||
|
||||
/* HR field has a more complex interpretation */
|
||||
{
|
||||
const u8 _hr = regs[RV3029C2_W_HOURS-RV3029C2_W_SEC];
|
||||
if (_hr & RV3029C2_REG_HR_12_24) {
|
||||
const u8 _hr = regs[RV3029_W_HOURS-RV3029_W_SEC];
|
||||
|
||||
if (_hr & RV3029_REG_HR_12_24) {
|
||||
/* 12h format */
|
||||
tm->tm_hour = bcd2bin(_hr & 0x1f);
|
||||
if (_hr & RV3029C2_REG_HR_PM) /* PM flag set */
|
||||
if (_hr & RV3029_REG_HR_PM) /* PM flag set */
|
||||
tm->tm_hour += 12;
|
||||
} else /* 24h format */
|
||||
tm->tm_hour = bcd2bin(_hr & 0x3f);
|
||||
}
|
||||
|
||||
tm->tm_mday = bcd2bin(regs[RV3029C2_W_DATE-RV3029C2_W_SEC]);
|
||||
tm->tm_mon = bcd2bin(regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC]) - 1;
|
||||
tm->tm_year = bcd2bin(regs[RV3029C2_W_YEARS-RV3029C2_W_SEC]) + 100;
|
||||
tm->tm_wday = bcd2bin(regs[RV3029C2_W_DAYS-RV3029C2_W_SEC]) - 1;
|
||||
tm->tm_mday = bcd2bin(regs[RV3029_W_DATE-RV3029_W_SEC]);
|
||||
tm->tm_mon = bcd2bin(regs[RV3029_W_MONTHS-RV3029_W_SEC]) - 1;
|
||||
tm->tm_year = bcd2bin(regs[RV3029_W_YEARS-RV3029_W_SEC]) + 100;
|
||||
tm->tm_wday = bcd2bin(regs[RV3029_W_DAYS-RV3029_W_SEC]) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3029c2_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
static int rv3029_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return rv3029c2_i2c_read_time(to_i2c_client(dev), tm);
|
||||
return rv3029_i2c_read_time(to_i2c_client(dev), tm);
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
|
||||
rv3029_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct rtc_time *const tm = &alarm->time;
|
||||
int ret;
|
||||
u8 regs[8];
|
||||
|
||||
ret = rv3029c2_i2c_get_sr(client, regs);
|
||||
ret = rv3029_i2c_get_sr(client, regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = rv3029c2_i2c_read_regs(client, RV3029C2_A_SC, regs,
|
||||
RV3029C2_ALARM_SECTION_LEN);
|
||||
ret = rv3029_i2c_read_regs(client, RV3029_A_SC, regs,
|
||||
RV3029_ALARM_SECTION_LEN);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading alarm section failed\n",
|
||||
@ -209,51 +400,42 @@ rv3029c2_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
|
||||
return ret;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(regs[RV3029C2_A_SC-RV3029C2_A_SC] & 0x7f);
|
||||
tm->tm_min = bcd2bin(regs[RV3029C2_A_MN-RV3029C2_A_SC] & 0x7f);
|
||||
tm->tm_hour = bcd2bin(regs[RV3029C2_A_HR-RV3029C2_A_SC] & 0x3f);
|
||||
tm->tm_mday = bcd2bin(regs[RV3029C2_A_DT-RV3029C2_A_SC] & 0x3f);
|
||||
tm->tm_mon = bcd2bin(regs[RV3029C2_A_MO-RV3029C2_A_SC] & 0x1f) - 1;
|
||||
tm->tm_year = bcd2bin(regs[RV3029C2_A_YR-RV3029C2_A_SC] & 0x7f) + 100;
|
||||
tm->tm_wday = bcd2bin(regs[RV3029C2_A_DW-RV3029C2_A_SC] & 0x07) - 1;
|
||||
tm->tm_sec = bcd2bin(regs[RV3029_A_SC-RV3029_A_SC] & 0x7f);
|
||||
tm->tm_min = bcd2bin(regs[RV3029_A_MN-RV3029_A_SC] & 0x7f);
|
||||
tm->tm_hour = bcd2bin(regs[RV3029_A_HR-RV3029_A_SC] & 0x3f);
|
||||
tm->tm_mday = bcd2bin(regs[RV3029_A_DT-RV3029_A_SC] & 0x3f);
|
||||
tm->tm_mon = bcd2bin(regs[RV3029_A_MO-RV3029_A_SC] & 0x1f) - 1;
|
||||
tm->tm_year = bcd2bin(regs[RV3029_A_YR-RV3029_A_SC] & 0x7f) + 100;
|
||||
tm->tm_wday = bcd2bin(regs[RV3029_A_DW-RV3029_A_SC] & 0x07) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
rv3029_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
return rv3029c2_i2c_read_alarm(to_i2c_client(dev), alarm);
|
||||
return rv3029_i2c_read_alarm(to_i2c_client(dev), alarm);
|
||||
}
|
||||
|
||||
static int rv3029c2_rtc_i2c_alarm_set_irq(struct i2c_client *client,
|
||||
static int rv3029_rtc_i2c_alarm_set_irq(struct i2c_client *client,
|
||||
int enable)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[1];
|
||||
|
||||
/* enable AIE irq */
|
||||
ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
|
||||
/* enable/disable AIE irq */
|
||||
ret = rv3029_i2c_update_bits(client, RV3029_IRQ_CTRL,
|
||||
RV3029_IRQ_CTRL_AIE,
|
||||
(enable ? RV3029_IRQ_CTRL_AIE : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "can't read INT reg\n");
|
||||
return ret;
|
||||
}
|
||||
if (enable)
|
||||
buf[0] |= RV3029C2_IRQ_CTRL_AIE;
|
||||
else
|
||||
buf[0] &= ~RV3029C2_IRQ_CTRL_AIE;
|
||||
|
||||
ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "can't set INT reg\n");
|
||||
dev_err(&client->dev, "can't update INT reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client,
|
||||
struct rtc_wkalrm *alarm)
|
||||
static int rv3029_rtc_i2c_set_alarm(struct i2c_client *client,
|
||||
struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct rtc_time *const tm = &alarm->time;
|
||||
int ret;
|
||||
@ -267,50 +449,41 @@ static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client,
|
||||
if (tm->tm_year < 100)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rv3029c2_i2c_get_sr(client, regs);
|
||||
ret = rv3029_i2c_get_sr(client, regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
regs[RV3029C2_A_SC-RV3029C2_A_SC] = bin2bcd(tm->tm_sec & 0x7f);
|
||||
regs[RV3029C2_A_MN-RV3029C2_A_SC] = bin2bcd(tm->tm_min & 0x7f);
|
||||
regs[RV3029C2_A_HR-RV3029C2_A_SC] = bin2bcd(tm->tm_hour & 0x3f);
|
||||
regs[RV3029C2_A_DT-RV3029C2_A_SC] = bin2bcd(tm->tm_mday & 0x3f);
|
||||
regs[RV3029C2_A_MO-RV3029C2_A_SC] = bin2bcd((tm->tm_mon & 0x1f) - 1);
|
||||
regs[RV3029C2_A_DW-RV3029C2_A_SC] = bin2bcd((tm->tm_wday & 7) - 1);
|
||||
regs[RV3029C2_A_YR-RV3029C2_A_SC] = bin2bcd((tm->tm_year & 0x7f) - 100);
|
||||
regs[RV3029_A_SC-RV3029_A_SC] = bin2bcd(tm->tm_sec & 0x7f);
|
||||
regs[RV3029_A_MN-RV3029_A_SC] = bin2bcd(tm->tm_min & 0x7f);
|
||||
regs[RV3029_A_HR-RV3029_A_SC] = bin2bcd(tm->tm_hour & 0x3f);
|
||||
regs[RV3029_A_DT-RV3029_A_SC] = bin2bcd(tm->tm_mday & 0x3f);
|
||||
regs[RV3029_A_MO-RV3029_A_SC] = bin2bcd((tm->tm_mon & 0x1f) - 1);
|
||||
regs[RV3029_A_DW-RV3029_A_SC] = bin2bcd((tm->tm_wday & 7) - 1);
|
||||
regs[RV3029_A_YR-RV3029_A_SC] = bin2bcd((tm->tm_year & 0x7f) - 100);
|
||||
|
||||
ret = rv3029c2_i2c_write_regs(client, RV3029C2_A_SC, regs,
|
||||
RV3029C2_ALARM_SECTION_LEN);
|
||||
ret = rv3029_i2c_write_regs(client, RV3029_A_SC, regs,
|
||||
RV3029_ALARM_SECTION_LEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (alarm->enabled) {
|
||||
u8 buf[1];
|
||||
|
||||
/* clear AF flag */
|
||||
ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_FLAGS,
|
||||
buf, 1);
|
||||
ret = rv3029_i2c_update_bits(client, RV3029_IRQ_FLAGS,
|
||||
RV3029_IRQ_FLAGS_AF, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "can't read alarm flag\n");
|
||||
return ret;
|
||||
}
|
||||
buf[0] &= ~RV3029C2_IRQ_FLAGS_AF;
|
||||
ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_FLAGS,
|
||||
buf, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "can't set alarm flag\n");
|
||||
dev_err(&client->dev, "can't clear alarm flag\n");
|
||||
return ret;
|
||||
}
|
||||
/* enable AIE irq */
|
||||
ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1);
|
||||
ret = rv3029_rtc_i2c_alarm_set_irq(client, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&client->dev, "alarm IRQ armed\n");
|
||||
} else {
|
||||
/* disable AIE irq */
|
||||
ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 0);
|
||||
ret = rv3029_rtc_i2c_alarm_set_irq(client, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -320,13 +493,13 @@ static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3029c2_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
static int rv3029_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
return rv3029c2_rtc_i2c_set_alarm(to_i2c_client(dev), alarm);
|
||||
return rv3029_rtc_i2c_set_alarm(to_i2c_client(dev), alarm);
|
||||
}
|
||||
|
||||
static int
|
||||
rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
|
||||
rv3029_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
|
||||
{
|
||||
u8 regs[8];
|
||||
int ret;
|
||||
@ -339,26 +512,26 @@ rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
|
||||
if (tm->tm_year < 100)
|
||||
return -EINVAL;
|
||||
|
||||
regs[RV3029C2_W_SEC-RV3029C2_W_SEC] = bin2bcd(tm->tm_sec);
|
||||
regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC] = bin2bcd(tm->tm_min);
|
||||
regs[RV3029C2_W_HOURS-RV3029C2_W_SEC] = bin2bcd(tm->tm_hour);
|
||||
regs[RV3029C2_W_DATE-RV3029C2_W_SEC] = bin2bcd(tm->tm_mday);
|
||||
regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC] = bin2bcd(tm->tm_mon+1);
|
||||
regs[RV3029C2_W_DAYS-RV3029C2_W_SEC] = bin2bcd((tm->tm_wday & 7)+1);
|
||||
regs[RV3029C2_W_YEARS-RV3029C2_W_SEC] = bin2bcd(tm->tm_year - 100);
|
||||
regs[RV3029_W_SEC-RV3029_W_SEC] = bin2bcd(tm->tm_sec);
|
||||
regs[RV3029_W_MINUTES-RV3029_W_SEC] = bin2bcd(tm->tm_min);
|
||||
regs[RV3029_W_HOURS-RV3029_W_SEC] = bin2bcd(tm->tm_hour);
|
||||
regs[RV3029_W_DATE-RV3029_W_SEC] = bin2bcd(tm->tm_mday);
|
||||
regs[RV3029_W_MONTHS-RV3029_W_SEC] = bin2bcd(tm->tm_mon+1);
|
||||
regs[RV3029_W_DAYS-RV3029_W_SEC] = bin2bcd((tm->tm_wday & 7)+1);
|
||||
regs[RV3029_W_YEARS-RV3029_W_SEC] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
ret = rv3029c2_i2c_write_regs(client, RV3029C2_W_SEC, regs,
|
||||
RV3029C2_WATCH_SECTION_LEN);
|
||||
ret = rv3029_i2c_write_regs(client, RV3029_W_SEC, regs,
|
||||
RV3029_WATCH_SECTION_LEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rv3029c2_i2c_get_sr(client, regs);
|
||||
ret = rv3029_i2c_get_sr(client, regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
/* clear PON bit */
|
||||
ret = rv3029c2_i2c_set_sr(client, (regs[0] & ~RV3029C2_STATUS_PON));
|
||||
ret = rv3029_i2c_set_sr(client, (regs[0] & ~RV3029_STATUS_PON));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
|
||||
return ret;
|
||||
@ -367,26 +540,238 @@ rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3029c2_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
static int rv3029_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return rv3029c2_i2c_set_time(to_i2c_client(dev), tm);
|
||||
return rv3029_i2c_set_time(to_i2c_client(dev), tm);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rv3029c2_rtc_ops = {
|
||||
.read_time = rv3029c2_rtc_read_time,
|
||||
.set_time = rv3029c2_rtc_set_time,
|
||||
.read_alarm = rv3029c2_rtc_read_alarm,
|
||||
.set_alarm = rv3029c2_rtc_set_alarm,
|
||||
static const struct rv3029_trickle_tab_elem {
|
||||
u32 r; /* resistance in ohms */
|
||||
u8 conf; /* trickle config bits */
|
||||
} rv3029_trickle_tab[] = {
|
||||
{
|
||||
.r = 1076,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
|
||||
RV3029_TRICKLE_20K | RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 1091,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
|
||||
RV3029_TRICKLE_20K,
|
||||
}, {
|
||||
.r = 1137,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K |
|
||||
RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 1154,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K,
|
||||
}, {
|
||||
.r = 1371,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K |
|
||||
RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 1395,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K,
|
||||
}, {
|
||||
.r = 1472,
|
||||
.conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 1500,
|
||||
.conf = RV3029_TRICKLE_1K,
|
||||
}, {
|
||||
.r = 3810,
|
||||
.conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K |
|
||||
RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 4000,
|
||||
.conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K,
|
||||
}, {
|
||||
.r = 4706,
|
||||
.conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 5000,
|
||||
.conf = RV3029_TRICKLE_5K,
|
||||
}, {
|
||||
.r = 16000,
|
||||
.conf = RV3029_TRICKLE_20K | RV3029_TRICKLE_80K,
|
||||
}, {
|
||||
.r = 20000,
|
||||
.conf = RV3029_TRICKLE_20K,
|
||||
}, {
|
||||
.r = 80000,
|
||||
.conf = RV3029_TRICKLE_80K,
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_device_id rv3029c2_id[] = {
|
||||
static void rv3029_trickle_config(struct i2c_client *client)
|
||||
{
|
||||
struct device_node *of_node = client->dev.of_node;
|
||||
const struct rv3029_trickle_tab_elem *elem;
|
||||
int i, err;
|
||||
u32 ohms;
|
||||
u8 trickle_set_bits;
|
||||
|
||||
if (!of_node)
|
||||
return;
|
||||
|
||||
/* Configure the trickle charger. */
|
||||
err = of_property_read_u32(of_node, "trickle-resistor-ohms", &ohms);
|
||||
if (err) {
|
||||
/* Disable trickle charger. */
|
||||
trickle_set_bits = 0;
|
||||
} else {
|
||||
/* Enable trickle charger. */
|
||||
for (i = 0; i < ARRAY_SIZE(rv3029_trickle_tab); i++) {
|
||||
elem = &rv3029_trickle_tab[i];
|
||||
if (elem->r >= ohms)
|
||||
break;
|
||||
}
|
||||
trickle_set_bits = elem->conf;
|
||||
dev_info(&client->dev,
|
||||
"Trickle charger enabled at %d ohms resistance.\n",
|
||||
elem->r);
|
||||
}
|
||||
err = rv3029_eeprom_update_bits(client, RV3029_CONTROL_E2P_EECTRL,
|
||||
RV3029_TRICKLE_MASK,
|
||||
trickle_set_bits);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to update trickle charger config\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RTC_DRV_RV3029_HWMON
|
||||
|
||||
static int rv3029_read_temp(struct i2c_client *client, int *temp_mC)
|
||||
{
|
||||
int ret;
|
||||
u8 temp;
|
||||
|
||||
ret = rv3029_i2c_read_regs(client, RV3029_TEMP_PAGE, &temp, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*temp_mC = ((int)temp - 60) * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t rv3029_hwmon_show_temp(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = dev_get_drvdata(dev);
|
||||
int ret, temp_mC;
|
||||
|
||||
ret = rv3029_read_temp(client, &temp_mC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", temp_mC);
|
||||
}
|
||||
|
||||
static ssize_t rv3029_hwmon_set_update_interval(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct i2c_client *client = dev_get_drvdata(dev);
|
||||
unsigned long interval_ms;
|
||||
int ret;
|
||||
u8 th_set_bits = 0;
|
||||
|
||||
ret = kstrtoul(buf, 10, &interval_ms);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (interval_ms != 0) {
|
||||
th_set_bits |= RV3029_EECTRL_THE;
|
||||
if (interval_ms >= 16000)
|
||||
th_set_bits |= RV3029_EECTRL_THP;
|
||||
}
|
||||
ret = rv3029_eeprom_update_bits(client, RV3029_CONTROL_E2P_EECTRL,
|
||||
RV3029_EECTRL_THE | RV3029_EECTRL_THP,
|
||||
th_set_bits);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rv3029_hwmon_show_update_interval(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = dev_get_drvdata(dev);
|
||||
int ret, interval_ms;
|
||||
u8 eectrl;
|
||||
|
||||
ret = rv3029_eeprom_read(client, RV3029_CONTROL_E2P_EECTRL,
|
||||
&eectrl, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (eectrl & RV3029_EECTRL_THE) {
|
||||
if (eectrl & RV3029_EECTRL_THP)
|
||||
interval_ms = 16000;
|
||||
else
|
||||
interval_ms = 1000;
|
||||
} else {
|
||||
interval_ms = 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", interval_ms);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, rv3029_hwmon_show_temp,
|
||||
NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO,
|
||||
rv3029_hwmon_show_update_interval,
|
||||
rv3029_hwmon_set_update_interval, 0);
|
||||
|
||||
static struct attribute *rv3029_hwmon_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_update_interval.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(rv3029_hwmon);
|
||||
|
||||
static void rv3029_hwmon_register(struct i2c_client *client)
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(
|
||||
&client->dev, client->name, client, rv3029_hwmon_groups);
|
||||
if (IS_ERR(hwmon_dev)) {
|
||||
dev_warn(&client->dev,
|
||||
"unable to register hwmon device %ld\n",
|
||||
PTR_ERR(hwmon_dev));
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_RTC_DRV_RV3029_HWMON */
|
||||
|
||||
static void rv3029_hwmon_register(struct i2c_client *client)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_RTC_DRV_RV3029_HWMON */
|
||||
|
||||
static const struct rtc_class_ops rv3029_rtc_ops = {
|
||||
.read_time = rv3029_rtc_read_time,
|
||||
.set_time = rv3029_rtc_set_time,
|
||||
.read_alarm = rv3029_rtc_read_alarm,
|
||||
.set_alarm = rv3029_rtc_set_alarm,
|
||||
};
|
||||
|
||||
static struct i2c_device_id rv3029_id[] = {
|
||||
{ "rv3029", 0 },
|
||||
{ "rv3029c2", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rv3029c2_id);
|
||||
MODULE_DEVICE_TABLE(i2c, rv3029_id);
|
||||
|
||||
static int rv3029c2_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int rv3029_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
int rc = 0;
|
||||
@ -395,14 +780,17 @@ static int rv3029c2_probe(struct i2c_client *client,
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL))
|
||||
return -ENODEV;
|
||||
|
||||
rc = rv3029c2_i2c_get_sr(client, buf);
|
||||
rc = rv3029_i2c_get_sr(client, buf);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "reading status failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rv3029_trickle_config(client);
|
||||
rv3029_hwmon_register(client);
|
||||
|
||||
rtc = devm_rtc_device_register(&client->dev, client->name,
|
||||
&rv3029c2_rtc_ops, THIS_MODULE);
|
||||
&rv3029_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
@ -412,16 +800,17 @@ static int rv3029c2_probe(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver rv3029c2_driver = {
|
||||
static struct i2c_driver rv3029_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-rv3029c2",
|
||||
},
|
||||
.probe = rv3029c2_probe,
|
||||
.id_table = rv3029c2_id,
|
||||
.probe = rv3029_probe,
|
||||
.id_table = rv3029_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(rv3029c2_driver);
|
||||
module_i2c_driver(rv3029_driver);
|
||||
|
||||
MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>");
|
||||
MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver");
|
||||
MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
|
||||
MODULE_DESCRIPTION("Micro Crystal RV3029 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -52,7 +52,7 @@
|
||||
struct rv8803_data {
|
||||
struct i2c_client *client;
|
||||
struct rtc_device *rtc;
|
||||
spinlock_t flags_lock;
|
||||
struct mutex flags_lock;
|
||||
u8 ctrl;
|
||||
};
|
||||
|
||||
@ -63,11 +63,11 @@ static irqreturn_t rv8803_handle_irq(int irq, void *dev_id)
|
||||
unsigned long events = 0;
|
||||
int flags;
|
||||
|
||||
spin_lock(&rv8803->flags_lock);
|
||||
mutex_lock(&rv8803->flags_lock);
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, RV8803_FLAG);
|
||||
if (flags <= 0) {
|
||||
spin_unlock(&rv8803->flags_lock);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ static irqreturn_t rv8803_handle_irq(int irq, void *dev_id)
|
||||
rv8803->ctrl);
|
||||
}
|
||||
|
||||
spin_unlock(&rv8803->flags_lock);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -155,7 +155,6 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
|
||||
struct rv8803_data *rv8803 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int flags, ret;
|
||||
unsigned long irqflags;
|
||||
|
||||
if ((tm->tm_year < 100) || (tm->tm_year > 199))
|
||||
return -EINVAL;
|
||||
@ -173,18 +172,18 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&rv8803->flags_lock, irqflags);
|
||||
mutex_lock(&rv8803->flags_lock);
|
||||
|
||||
flags = i2c_smbus_read_byte_data(rv8803->client, RV8803_FLAG);
|
||||
if (flags < 0) {
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return flags;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(rv8803->client, RV8803_FLAG,
|
||||
flags & ~RV8803_FLAG_V2F);
|
||||
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -226,7 +225,6 @@ static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
u8 alarmvals[3];
|
||||
u8 ctrl[2];
|
||||
int ret, err;
|
||||
unsigned long irqflags;
|
||||
|
||||
/* The alarm has no seconds, round up to nearest minute */
|
||||
if (alrm->time.tm_sec) {
|
||||
@ -236,11 +234,11 @@ static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
rtc_time64_to_tm(alarm_time, &alrm->time);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rv8803->flags_lock, irqflags);
|
||||
mutex_lock(&rv8803->flags_lock);
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, RV8803_FLAG, 2, ctrl);
|
||||
if (ret != 2) {
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return ret < 0 ? ret : -EIO;
|
||||
}
|
||||
|
||||
@ -253,14 +251,14 @@ static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
err = i2c_smbus_write_byte_data(rv8803->client, RV8803_CTRL,
|
||||
rv8803->ctrl);
|
||||
if (err) {
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ctrl[1] &= ~RV8803_FLAG_AF;
|
||||
err = i2c_smbus_write_byte_data(rv8803->client, RV8803_FLAG, ctrl[1]);
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -289,7 +287,6 @@ static int rv8803_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct rv8803_data *rv8803 = dev_get_drvdata(dev);
|
||||
int ctrl, flags, err;
|
||||
unsigned long irqflags;
|
||||
|
||||
ctrl = rv8803->ctrl;
|
||||
|
||||
@ -305,15 +302,15 @@ static int rv8803_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
ctrl &= ~RV8803_CTRL_AIE;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rv8803->flags_lock, irqflags);
|
||||
mutex_lock(&rv8803->flags_lock);
|
||||
flags = i2c_smbus_read_byte_data(client, RV8803_FLAG);
|
||||
if (flags < 0) {
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return flags;
|
||||
}
|
||||
flags &= ~(RV8803_FLAG_AF | RV8803_FLAG_UF);
|
||||
err = i2c_smbus_write_byte_data(client, RV8803_FLAG, flags);
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -333,7 +330,6 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct rv8803_data *rv8803 = dev_get_drvdata(dev);
|
||||
int flags, ret = 0;
|
||||
unsigned long irqflags;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
@ -355,16 +351,16 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
return 0;
|
||||
|
||||
case RTC_VL_CLR:
|
||||
spin_lock_irqsave(&rv8803->flags_lock, irqflags);
|
||||
mutex_lock(&rv8803->flags_lock);
|
||||
flags = i2c_smbus_read_byte_data(client, RV8803_FLAG);
|
||||
if (flags < 0) {
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return flags;
|
||||
}
|
||||
|
||||
flags &= ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F);
|
||||
ret = i2c_smbus_write_byte_data(client, RV8803_FLAG, flags);
|
||||
spin_unlock_irqrestore(&rv8803->flags_lock, irqflags);
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -441,6 +437,7 @@ static int rv8803_probe(struct i2c_client *client,
|
||||
if (!rv8803)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&rv8803->flags_lock);
|
||||
rv8803->client = client;
|
||||
i2c_set_clientdata(client, rv8803);
|
||||
|
||||
|
402
drivers/rtc/rtc-rx6110.c
Normal file
402
drivers/rtc/rtc-rx6110.c
Normal file
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Driver for the Epson RTC module RX-6110 SA
|
||||
*
|
||||
* Copyright(C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
|
||||
* Copyright(C) SEIKO EPSON CORPORATION 2013. All rights reserved.
|
||||
*
|
||||
* This driver software is distributed as is, without any warranty of any kind,
|
||||
* either express or implied as further specified in the GNU Public License.
|
||||
* This software may be used and distributed according to the terms of the GNU
|
||||
* Public License, version 2 as published by the Free Software Foundation.
|
||||
* See the file COPYING in the main directory of this archive for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* RX-6110 Register definitions */
|
||||
#define RX6110_REG_SEC 0x10
|
||||
#define RX6110_REG_MIN 0x11
|
||||
#define RX6110_REG_HOUR 0x12
|
||||
#define RX6110_REG_WDAY 0x13
|
||||
#define RX6110_REG_MDAY 0x14
|
||||
#define RX6110_REG_MONTH 0x15
|
||||
#define RX6110_REG_YEAR 0x16
|
||||
#define RX6110_REG_RES1 0x17
|
||||
#define RX6110_REG_ALMIN 0x18
|
||||
#define RX6110_REG_ALHOUR 0x19
|
||||
#define RX6110_REG_ALWDAY 0x1A
|
||||
#define RX6110_REG_TCOUNT0 0x1B
|
||||
#define RX6110_REG_TCOUNT1 0x1C
|
||||
#define RX6110_REG_EXT 0x1D
|
||||
#define RX6110_REG_FLAG 0x1E
|
||||
#define RX6110_REG_CTRL 0x1F
|
||||
#define RX6110_REG_USER0 0x20
|
||||
#define RX6110_REG_USER1 0x21
|
||||
#define RX6110_REG_USER2 0x22
|
||||
#define RX6110_REG_USER3 0x23
|
||||
#define RX6110_REG_USER4 0x24
|
||||
#define RX6110_REG_USER5 0x25
|
||||
#define RX6110_REG_USER6 0x26
|
||||
#define RX6110_REG_USER7 0x27
|
||||
#define RX6110_REG_USER8 0x28
|
||||
#define RX6110_REG_USER9 0x29
|
||||
#define RX6110_REG_USERA 0x2A
|
||||
#define RX6110_REG_USERB 0x2B
|
||||
#define RX6110_REG_USERC 0x2C
|
||||
#define RX6110_REG_USERD 0x2D
|
||||
#define RX6110_REG_USERE 0x2E
|
||||
#define RX6110_REG_USERF 0x2F
|
||||
#define RX6110_REG_RES2 0x30
|
||||
#define RX6110_REG_RES3 0x31
|
||||
#define RX6110_REG_IRQ 0x32
|
||||
|
||||
#define RX6110_BIT_ALARM_EN BIT(7)
|
||||
|
||||
/* Extension Register (1Dh) bit positions */
|
||||
#define RX6110_BIT_EXT_TSEL0 BIT(0)
|
||||
#define RX6110_BIT_EXT_TSEL1 BIT(1)
|
||||
#define RX6110_BIT_EXT_TSEL2 BIT(2)
|
||||
#define RX6110_BIT_EXT_WADA BIT(3)
|
||||
#define RX6110_BIT_EXT_TE BIT(4)
|
||||
#define RX6110_BIT_EXT_USEL BIT(5)
|
||||
#define RX6110_BIT_EXT_FSEL0 BIT(6)
|
||||
#define RX6110_BIT_EXT_FSEL1 BIT(7)
|
||||
|
||||
/* Flag Register (1Eh) bit positions */
|
||||
#define RX6110_BIT_FLAG_VLF BIT(1)
|
||||
#define RX6110_BIT_FLAG_AF BIT(3)
|
||||
#define RX6110_BIT_FLAG_TF BIT(4)
|
||||
#define RX6110_BIT_FLAG_UF BIT(5)
|
||||
|
||||
/* Control Register (1Fh) bit positions */
|
||||
#define RX6110_BIT_CTRL_TBKE BIT(0)
|
||||
#define RX6110_BIT_CTRL_TBKON BIT(1)
|
||||
#define RX6110_BIT_CTRL_TSTP BIT(2)
|
||||
#define RX6110_BIT_CTRL_AIE BIT(3)
|
||||
#define RX6110_BIT_CTRL_TIE BIT(4)
|
||||
#define RX6110_BIT_CTRL_UIE BIT(5)
|
||||
#define RX6110_BIT_CTRL_STOP BIT(6)
|
||||
#define RX6110_BIT_CTRL_TEST BIT(7)
|
||||
|
||||
enum {
|
||||
RTC_SEC = 0,
|
||||
RTC_MIN,
|
||||
RTC_HOUR,
|
||||
RTC_WDAY,
|
||||
RTC_MDAY,
|
||||
RTC_MONTH,
|
||||
RTC_YEAR,
|
||||
RTC_NR_TIME
|
||||
};
|
||||
|
||||
#define RX6110_DRIVER_NAME "rx6110"
|
||||
|
||||
struct rx6110_data {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
/**
|
||||
* rx6110_rtc_tm_to_data - convert rtc_time to native time encoding
|
||||
*
|
||||
* @tm: holds date and time
|
||||
* @data: holds the encoding in rx6110 native form
|
||||
*/
|
||||
static int rx6110_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
|
||||
{
|
||||
pr_debug("%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||
|
||||
/*
|
||||
* The year in the RTC is a value between 0 and 99.
|
||||
* Assume that this represents the current century
|
||||
* and disregard all other values.
|
||||
*/
|
||||
if (tm->tm_year < 100 || tm->tm_year >= 200)
|
||||
return -EINVAL;
|
||||
|
||||
data[RTC_SEC] = bin2bcd(tm->tm_sec);
|
||||
data[RTC_MIN] = bin2bcd(tm->tm_min);
|
||||
data[RTC_HOUR] = bin2bcd(tm->tm_hour);
|
||||
data[RTC_WDAY] = BIT(bin2bcd(tm->tm_wday));
|
||||
data[RTC_MDAY] = bin2bcd(tm->tm_mday);
|
||||
data[RTC_MONTH] = bin2bcd(tm->tm_mon + 1);
|
||||
data[RTC_YEAR] = bin2bcd(tm->tm_year % 100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rx6110_data_to_rtc_tm - convert native time encoding to rtc_time
|
||||
*
|
||||
* @data: holds the encoding in rx6110 native form
|
||||
* @tm: holds date and time
|
||||
*/
|
||||
static int rx6110_data_to_rtc_tm(u8 *data, struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_sec = bcd2bin(data[RTC_SEC] & 0x7f);
|
||||
tm->tm_min = bcd2bin(data[RTC_MIN] & 0x7f);
|
||||
/* only 24-hour clock */
|
||||
tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
|
||||
tm->tm_wday = ffs(data[RTC_WDAY] & 0x7f);
|
||||
tm->tm_mday = bcd2bin(data[RTC_MDAY] & 0x3f);
|
||||
tm->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1f) - 1;
|
||||
tm->tm_year = bcd2bin(data[RTC_YEAR]) + 100;
|
||||
|
||||
pr_debug("%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||
|
||||
/*
|
||||
* The year in the RTC is a value between 0 and 99.
|
||||
* Assume that this represents the current century
|
||||
* and disregard all other values.
|
||||
*/
|
||||
if (tm->tm_year < 100 || tm->tm_year >= 200)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* rx6110_set_time - set the current time in the rx6110 registers
|
||||
*
|
||||
* @dev: the rtc device in use
|
||||
* @tm: holds date and time
|
||||
*
|
||||
* BUG: The HW assumes every year that is a multiple of 4 to be a leap
|
||||
* year. Next time this is wrong is 2100, which will not be a leap year
|
||||
*
|
||||
* Note: If STOP is not set/cleared, the clock will start when the seconds
|
||||
* register is written
|
||||
*
|
||||
*/
|
||||
static int rx6110_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rx6110_data *rx6110 = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
int ret;
|
||||
|
||||
ret = rx6110_rtc_tm_to_data(tm, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set STOP bit before changing clock/calendar */
|
||||
ret = regmap_update_bits(rx6110->regmap, RX6110_REG_CTRL,
|
||||
RX6110_BIT_CTRL_STOP, RX6110_BIT_CTRL_STOP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_write(rx6110->regmap, RX6110_REG_SEC, data,
|
||||
RTC_NR_TIME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The time in the RTC is valid. Be sure to have VLF cleared. */
|
||||
ret = regmap_update_bits(rx6110->regmap, RX6110_REG_FLAG,
|
||||
RX6110_BIT_FLAG_VLF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* clear STOP bit after changing clock/calendar */
|
||||
ret = regmap_update_bits(rx6110->regmap, RX6110_REG_CTRL,
|
||||
RX6110_BIT_CTRL_STOP, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rx6110_get_time - get the current time from the rx6110 registers
|
||||
* @dev: the rtc device in use
|
||||
* @tm: holds date and time
|
||||
*/
|
||||
static int rx6110_get_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rx6110_data *rx6110 = dev_get_drvdata(dev);
|
||||
u8 data[RTC_NR_TIME];
|
||||
int flags;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rx6110->regmap, RX6110_REG_FLAG, &flags);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
/* check for VLF Flag (set at power-on) */
|
||||
if ((flags & RX6110_BIT_FLAG_VLF)) {
|
||||
dev_warn(dev, "Voltage low, data is invalid.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* read registers to date */
|
||||
ret = regmap_bulk_read(rx6110->regmap, RX6110_REG_SEC, data,
|
||||
RTC_NR_TIME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rx6110_data_to_rtc_tm(data, tm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static const struct reg_sequence rx6110_default_regs[] = {
|
||||
{ RX6110_REG_RES1, 0xB8 },
|
||||
{ RX6110_REG_RES2, 0x00 },
|
||||
{ RX6110_REG_RES3, 0x10 },
|
||||
{ RX6110_REG_IRQ, 0x00 },
|
||||
{ RX6110_REG_ALMIN, 0x00 },
|
||||
{ RX6110_REG_ALHOUR, 0x00 },
|
||||
{ RX6110_REG_ALWDAY, 0x00 },
|
||||
};
|
||||
|
||||
/**
|
||||
* rx6110_init - initialize the rx6110 registers
|
||||
*
|
||||
* @rx6110: pointer to the rx6110 struct in use
|
||||
*
|
||||
*/
|
||||
static int rx6110_init(struct rx6110_data *rx6110)
|
||||
{
|
||||
struct rtc_device *rtc = rx6110->rtc;
|
||||
int flags;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(rx6110->regmap, RX6110_REG_EXT,
|
||||
RX6110_BIT_EXT_TE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_register_patch(rx6110->regmap, rx6110_default_regs,
|
||||
ARRAY_SIZE(rx6110_default_regs));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(rx6110->regmap, RX6110_REG_FLAG, &flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* check for VLF Flag (set at power-on) */
|
||||
if ((flags & RX6110_BIT_FLAG_VLF))
|
||||
dev_warn(&rtc->dev, "Voltage low, data loss detected.\n");
|
||||
|
||||
/* check for Alarm Flag */
|
||||
if (flags & RX6110_BIT_FLAG_AF)
|
||||
dev_warn(&rtc->dev, "An alarm may have been missed.\n");
|
||||
|
||||
/* check for Periodic Timer Flag */
|
||||
if (flags & RX6110_BIT_FLAG_TF)
|
||||
dev_warn(&rtc->dev, "Periodic timer was detected\n");
|
||||
|
||||
/* check for Update Timer Flag */
|
||||
if (flags & RX6110_BIT_FLAG_UF)
|
||||
dev_warn(&rtc->dev, "Update timer was detected\n");
|
||||
|
||||
/* clear all flags BUT VLF */
|
||||
ret = regmap_update_bits(rx6110->regmap, RX6110_REG_FLAG,
|
||||
RX6110_BIT_FLAG_AF |
|
||||
RX6110_BIT_FLAG_UF |
|
||||
RX6110_BIT_FLAG_TF,
|
||||
0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct rtc_class_ops rx6110_rtc_ops = {
|
||||
.read_time = rx6110_get_time,
|
||||
.set_time = rx6110_set_time,
|
||||
};
|
||||
|
||||
static struct regmap_config regmap_spi_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = RX6110_REG_IRQ,
|
||||
.read_flag_mask = 0x80,
|
||||
};
|
||||
|
||||
/**
|
||||
* rx6110_probe - initialize rtc driver
|
||||
* @spi: pointer to spi device
|
||||
*/
|
||||
static int rx6110_probe(struct spi_device *spi)
|
||||
{
|
||||
struct rx6110_data *rx6110;
|
||||
int err;
|
||||
|
||||
if ((spi->bits_per_word && spi->bits_per_word != 8) ||
|
||||
(spi->max_speed_hz > 2000000) ||
|
||||
(spi->mode != (SPI_CS_HIGH | SPI_CPOL | SPI_CPHA))) {
|
||||
dev_warn(&spi->dev, "SPI settings: bits_per_word: %d, max_speed_hz: %d, mode: %xh\n",
|
||||
spi->bits_per_word, spi->max_speed_hz, spi->mode);
|
||||
dev_warn(&spi->dev, "driving device in an unsupported mode");
|
||||
}
|
||||
|
||||
rx6110 = devm_kzalloc(&spi->dev, sizeof(*rx6110), GFP_KERNEL);
|
||||
if (!rx6110)
|
||||
return -ENOMEM;
|
||||
|
||||
rx6110->regmap = devm_regmap_init_spi(spi, ®map_spi_config);
|
||||
if (IS_ERR(rx6110->regmap)) {
|
||||
dev_err(&spi->dev, "regmap init failed for rtc rx6110\n");
|
||||
return PTR_ERR(rx6110->regmap);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, rx6110);
|
||||
|
||||
rx6110->rtc = devm_rtc_device_register(&spi->dev,
|
||||
RX6110_DRIVER_NAME,
|
||||
&rx6110_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(rx6110->rtc))
|
||||
return PTR_ERR(rx6110->rtc);
|
||||
|
||||
err = rx6110_init(rx6110);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rx6110->rtc->max_user_freq = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx6110_remove(struct spi_device *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id rx6110_id[] = {
|
||||
{ "rx6110", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, rx6110_id);
|
||||
|
||||
static struct spi_driver rx6110_driver = {
|
||||
.driver = {
|
||||
.name = RX6110_DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = rx6110_probe,
|
||||
.remove = rx6110_remove,
|
||||
.id_table = rx6110_id,
|
||||
};
|
||||
|
||||
module_spi_driver(rx6110_driver);
|
||||
|
||||
MODULE_AUTHOR("Val Krutov <val.krutov@erd.epson.com>");
|
||||
MODULE_DESCRIPTION("RX-6110 SA RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -65,7 +65,6 @@
|
||||
|
||||
static const struct i2c_device_id rx8025_id[] = {
|
||||
{ "rx8025", 0 },
|
||||
{ "rv8803", 1 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rx8025_id);
|
||||
@ -147,8 +146,10 @@ static irqreturn_t rx8025_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *client = dev_id;
|
||||
struct rx8025_data *rx8025 = i2c_get_clientdata(client);
|
||||
struct mutex *lock = &rx8025->rtc->ops_lock;
|
||||
int status;
|
||||
|
||||
mutex_lock(lock);
|
||||
status = rx8025_read_reg(client, RX8025_REG_CTRL2);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
@ -173,6 +174,8 @@ static irqreturn_t rx8025_handle_irq(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -341,7 +344,17 @@ static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
if (client->irq <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Hardware alarm precision is 1 minute! */
|
||||
/*
|
||||
* Hardware alarm precision is 1 minute!
|
||||
* round up to nearest minute
|
||||
*/
|
||||
if (t->time.tm_sec) {
|
||||
time64_t alarm_time = rtc_tm_to_time64(&t->time);
|
||||
|
||||
alarm_time += 60 - t->time.tm_sec;
|
||||
rtc_time64_to_tm(alarm_time, &t->time);
|
||||
}
|
||||
|
||||
ald[0] = bin2bcd(t->time.tm_min);
|
||||
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
|
||||
ald[1] = bin2bcd(t->time.tm_hour);
|
||||
@ -539,8 +552,9 @@ static int rx8025_probe(struct i2c_client *client,
|
||||
if (client->irq > 0) {
|
||||
dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
rx8025_handle_irq, 0, "rx8025",
|
||||
client);
|
||||
rx8025_handle_irq,
|
||||
IRQF_ONESHOT,
|
||||
"rx8025", client);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
client->irq = 0;
|
||||
@ -549,6 +563,9 @@ static int rx8025_probe(struct i2c_client *client,
|
||||
|
||||
rx8025->rtc->max_user_freq = 1;
|
||||
|
||||
/* the rx8025 alarm only supports a minute accuracy */
|
||||
rx8025->rtc->uie_unsupported = 1;
|
||||
|
||||
err = rx8025_sysfs_register(&client->dev);
|
||||
return err;
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data)
|
||||
* Read RTC_UDR_CON register and wait till UDR field is cleared.
|
||||
* This indicates that time/alarm update ended.
|
||||
*/
|
||||
static inline int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info)
|
||||
static int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info)
|
||||
{
|
||||
int ret, retry = UDR_READ_RETRY_CNT;
|
||||
unsigned int data;
|
||||
@ -231,7 +231,7 @@ static inline int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info,
|
||||
static int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info,
|
||||
struct rtc_wkalrm *alarm)
|
||||
{
|
||||
int ret;
|
||||
@ -264,7 +264,7 @@ static inline int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
|
||||
static int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
|
||||
{
|
||||
int ret;
|
||||
unsigned int data;
|
||||
@ -288,7 +288,7 @@ static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
|
||||
static int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
|
||||
{
|
||||
int ret;
|
||||
unsigned int data;
|
||||
|
@ -218,6 +218,34 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
static DEVICE_ATTR_RW(wakealarm);
|
||||
|
||||
static ssize_t
|
||||
offset_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t retval;
|
||||
long offset;
|
||||
|
||||
retval = rtc_read_offset(to_rtc_device(dev), &offset);
|
||||
if (retval == 0)
|
||||
retval = sprintf(buf, "%ld\n", offset);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
offset_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
ssize_t retval;
|
||||
long offset;
|
||||
|
||||
retval = kstrtol(buf, 10, &offset);
|
||||
if (retval == 0)
|
||||
retval = rtc_set_offset(to_rtc_device(dev), offset);
|
||||
|
||||
return (retval < 0) ? retval : n;
|
||||
}
|
||||
static DEVICE_ATTR_RW(offset);
|
||||
|
||||
static struct attribute *rtc_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_date.attr,
|
||||
@ -226,6 +254,7 @@ static struct attribute *rtc_attrs[] = {
|
||||
&dev_attr_max_user_freq.attr,
|
||||
&dev_attr_hctosys.attr,
|
||||
&dev_attr_wakealarm.attr,
|
||||
&dev_attr_offset.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -249,9 +278,13 @@ static umode_t rtc_attr_is_visible(struct kobject *kobj,
|
||||
struct rtc_device *rtc = to_rtc_device(dev);
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if (attr == &dev_attr_wakealarm.attr)
|
||||
if (attr == &dev_attr_wakealarm.attr) {
|
||||
if (!rtc_does_wakealarm(rtc))
|
||||
mode = 0;
|
||||
} else if (attr == &dev_attr_offset.attr) {
|
||||
if (!rtc->ops->set_offset)
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ static int tps6586x_rtc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
|
||||
tps6586x_rtc_irq,
|
||||
IRQF_ONESHOT | IRQF_EARLY_RESUME,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), rtc);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request IRQ(%d) failed with ret %d\n",
|
||||
|
@ -268,7 +268,7 @@ static int tps65910_rtc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
tps65910_rtc_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME,
|
||||
tps65910_rtc_interrupt, IRQF_TRIGGER_LOW,
|
||||
dev_name(&pdev->dev), &pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "IRQ is not free.\n");
|
||||
|
@ -287,7 +287,7 @@ static int tps80031_rtc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
|
||||
tps80031_rtc_irq,
|
||||
IRQF_ONESHOT | IRQF_EARLY_RESUME,
|
||||
IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), rtc);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request IRQ:%d failed, err = %d\n",
|
||||
|
@ -272,12 +272,13 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops vr41xx_rtc_ops = {
|
||||
.release = vr41xx_rtc_release,
|
||||
.ioctl = vr41xx_rtc_ioctl,
|
||||
.read_time = vr41xx_rtc_read_time,
|
||||
.set_time = vr41xx_rtc_set_time,
|
||||
.read_alarm = vr41xx_rtc_read_alarm,
|
||||
.set_alarm = vr41xx_rtc_set_alarm,
|
||||
.release = vr41xx_rtc_release,
|
||||
.ioctl = vr41xx_rtc_ioctl,
|
||||
.read_time = vr41xx_rtc_read_time,
|
||||
.set_time = vr41xx_rtc_set_time,
|
||||
.read_alarm = vr41xx_rtc_read_alarm,
|
||||
.set_alarm = vr41xx_rtc_set_alarm,
|
||||
.alarm_irq_enable = vr41xx_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int rtc_probe(struct platform_device *pdev)
|
||||
|
@ -437,14 +437,11 @@ enum max77686_irq {
|
||||
struct max77686_dev {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
|
||||
struct i2c_client *rtc; /* slave addr 0x0c */
|
||||
|
||||
unsigned long type;
|
||||
|
||||
struct regmap *regmap; /* regmap for mfd */
|
||||
struct regmap *rtc_regmap; /* regmap for rtc */
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
struct regmap_irq_chip_data *rtc_irq_data;
|
||||
|
||||
int irq;
|
||||
struct mutex irqlock;
|
||||
|
@ -89,6 +89,8 @@ struct rtc_class_ops {
|
||||
int (*set_mmss)(struct device *, unsigned long secs);
|
||||
int (*read_callback)(struct device *, int data);
|
||||
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
|
||||
int (*read_offset)(struct device *, long *offset);
|
||||
int (*set_offset)(struct device *, long offset);
|
||||
};
|
||||
|
||||
#define RTC_DEVICE_NAME_SIZE 20
|
||||
@ -208,6 +210,8 @@ void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data);
|
||||
int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
|
||||
ktime_t expires, ktime_t period);
|
||||
void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer);
|
||||
int rtc_read_offset(struct rtc_device *rtc, long *offset);
|
||||
int rtc_set_offset(struct rtc_device *rtc, long offset);
|
||||
void rtc_timer_do_work(struct work_struct *work);
|
||||
|
||||
static inline bool is_leap_year(unsigned int year)
|
||||
|
Loading…
Reference in New Issue
Block a user