-
Notifications
You must be signed in to change notification settings - Fork 853
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rtc: max31335: Add driver support for MAX31331
MAX31331 is an ultra-low-power, I2C Real-Time Clock RTC Signed-off-by: Swaroop Kukkillaya <[email protected]>
- Loading branch information
1 parent
04fbdbb
commit 7bd079d
Showing
1 changed file
with
121 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -184,31 +184,90 @@ | |
#define MAX31335_RAM_SIZE 32 | ||
#define MAX31335_TIME_SIZE 0x07 | ||
|
||
/* MAX31331 Register Map */ | ||
#define MAX31331_RTC_CONFIG2 0x04 | ||
|
||
#define clk_hw_to_max31335(_hw) container_of(_hw, struct max31335_data, clkout) | ||
|
||
/* Supported Maxim RTC */ | ||
enum max_rtc_ids { | ||
ID_MAX31331, | ||
ID_MAX31335, | ||
MAX_RTC_ID_NR | ||
}; | ||
|
||
struct chip_desc { | ||
u8 sec_reg; | ||
u8 alarm1_sec_reg; | ||
|
||
u8 int_en_reg; | ||
u8 int_status_reg; | ||
|
||
u8 ram_reg; | ||
u8 ram_size; | ||
|
||
u8 temp_reg; | ||
|
||
u8 trickle_reg; | ||
|
||
u8 clkout_reg; | ||
}; | ||
|
||
struct max31335_data { | ||
enum max_rtc_ids id; | ||
struct regmap *regmap; | ||
struct rtc_device *rtc; | ||
struct clk_hw clkout; | ||
struct clk *clkin; | ||
const struct chip_desc *chip; | ||
int irq; | ||
}; | ||
|
||
static const int max31335_clkout_freq[] = { 1, 64, 1024, 32768 }; | ||
|
||
static const struct chip_desc chip[MAX_RTC_ID_NR] = { | ||
[ID_MAX31331] = { | ||
.int_en_reg = 0x01, | ||
.int_status_reg = 0x00, | ||
.sec_reg = 0x08, | ||
.alarm1_sec_reg = 0x0F, | ||
.ram_reg = 0x20, | ||
.ram_size = 32, | ||
.trickle_reg = 0x1B, | ||
.clkout_reg = 0x04, | ||
}, | ||
[ID_MAX31335] = { | ||
.int_en_reg = 0x01, | ||
.int_status_reg = 0x00, | ||
.sec_reg = 0x0A, | ||
.alarm1_sec_reg = 0x11, | ||
.ram_reg = 0x40, | ||
.ram_size = 32, | ||
.temp_reg = 0x35, | ||
.trickle_reg = 0x1D, | ||
.clkout_reg = 0x06, | ||
}, | ||
}; | ||
|
||
static const u16 max31335_trickle_resistors[] = {3000, 6000, 11000}; | ||
|
||
static bool max31335_volatile_reg(struct device *dev, unsigned int reg) | ||
{ | ||
struct max31335_data *max31335 = dev_get_drvdata(dev); | ||
const struct chip_desc *chip = max31335->chip; | ||
|
||
/* time keeping registers */ | ||
if (reg >= MAX31335_SECONDS && | ||
reg < MAX31335_SECONDS + MAX31335_TIME_SIZE) | ||
if (reg >= chip->sec_reg && | ||
reg < chip->sec_reg + MAX31335_TIME_SIZE) | ||
return true; | ||
|
||
/* interrupt status register */ | ||
if (reg == MAX31335_STATUS1) | ||
if (reg == chip->int_status_reg) | ||
return true; | ||
|
||
/* temperature registers */ | ||
if (reg == MAX31335_TEMP_DATA_MSB || MAX31335_TEMP_DATA_LSB) | ||
/* temperature registers if valid*/ | ||
if (chip->temp_reg && | ||
(reg == chip->temp_reg || reg == chip->temp_reg + 1)) | ||
return true; | ||
|
||
return false; | ||
|
@@ -227,7 +286,7 @@ static int max31335_read_time(struct device *dev, struct rtc_time *tm) | |
u8 date[7]; | ||
int ret; | ||
|
||
ret = regmap_bulk_read(max31335->regmap, MAX31335_SECONDS, date, | ||
ret = regmap_bulk_read(max31335->regmap, max31335->chip->sec_reg, date, | ||
sizeof(date)); | ||
if (ret) | ||
return ret; | ||
|
@@ -262,7 +321,7 @@ static int max31335_set_time(struct device *dev, struct rtc_time *tm) | |
if (tm->tm_year >= 200) | ||
date[5] |= FIELD_PREP(MAX31335_MONTH_CENTURY, 1); | ||
|
||
return regmap_bulk_write(max31335->regmap, MAX31335_SECONDS, date, | ||
return regmap_bulk_write(max31335->regmap, max31335->chip->sec_reg, date, | ||
sizeof(date)); | ||
} | ||
|
||
|
@@ -273,7 +332,7 @@ static int max31335_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
struct rtc_time time; | ||
u8 regs[6]; | ||
|
||
ret = regmap_bulk_read(max31335->regmap, MAX31335_ALM1_SEC, regs, | ||
ret = regmap_bulk_read(max31335->regmap, max31335->chip->alarm1_sec_reg, regs, | ||
sizeof(regs)); | ||
if (ret) | ||
return ret; | ||
|
@@ -292,11 +351,11 @@ static int max31335_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
if (time.tm_year >= 200) | ||
alrm->time.tm_year += 100; | ||
|
||
ret = regmap_read(max31335->regmap, MAX31335_INT_EN1, &ctrl); | ||
ret = regmap_read(max31335->regmap, max31335->chip->int_en_reg, &ctrl); | ||
if (ret) | ||
return ret; | ||
|
||
ret = regmap_read(max31335->regmap, MAX31335_STATUS1, &status); | ||
ret = regmap_read(max31335->regmap, max31335->chip->int_status_reg, &status); | ||
if (ret) | ||
return ret; | ||
|
||
|
@@ -320,18 +379,18 @@ static int max31335_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
regs[4] = bin2bcd(alrm->time.tm_mon + 1); | ||
regs[5] = bin2bcd(alrm->time.tm_year % 100); | ||
|
||
ret = regmap_bulk_write(max31335->regmap, MAX31335_ALM1_SEC, | ||
ret = regmap_bulk_write(max31335->regmap, max31335->chip->alarm1_sec_reg, | ||
regs, sizeof(regs)); | ||
if (ret) | ||
return ret; | ||
|
||
reg = FIELD_PREP(MAX31335_INT_EN1_A1IE, alrm->enabled); | ||
ret = regmap_update_bits(max31335->regmap, MAX31335_INT_EN1, | ||
ret = regmap_update_bits(max31335->regmap, max31335->chip->int_en_reg, | ||
MAX31335_INT_EN1_A1IE, reg); | ||
if (ret) | ||
return ret; | ||
|
||
ret = regmap_update_bits(max31335->regmap, MAX31335_STATUS1, | ||
ret = regmap_update_bits(max31335->regmap, max31335->chip->int_status_reg, | ||
MAX31335_STATUS1_A1F, 0); | ||
|
||
return 0; | ||
|
@@ -341,7 +400,7 @@ static int max31335_alarm_irq_enable(struct device *dev, unsigned int enabled) | |
{ | ||
struct max31335_data *max31335 = dev_get_drvdata(dev); | ||
|
||
return regmap_update_bits(max31335->regmap, MAX31335_INT_EN1, | ||
return regmap_update_bits(max31335->regmap, max31335->chip->int_en_reg, | ||
MAX31335_INT_EN1_A1IE, enabled); | ||
} | ||
|
||
|
@@ -353,12 +412,12 @@ static irqreturn_t max31335_handle_irq(int irq, void *dev_id) | |
|
||
mutex_lock(lock); | ||
|
||
ret = regmap_read(max31335->regmap, MAX31335_STATUS1, &status); | ||
ret = regmap_read(max31335->regmap, max31335->chip->int_status_reg, &status); | ||
if (ret) | ||
goto exit; | ||
|
||
if (FIELD_GET(MAX31335_STATUS1_A1F, status)) { | ||
ret = regmap_update_bits(max31335->regmap, MAX31335_STATUS1, | ||
ret = regmap_update_bits(max31335->regmap, max31335->chip->int_status_reg, | ||
MAX31335_STATUS1_A1F, 0); | ||
if (ret) | ||
goto exit; | ||
|
@@ -414,7 +473,7 @@ static int max31335_trickle_charger_setup(struct device *dev, | |
|
||
i = i + trickle_cfg; | ||
|
||
return regmap_write(max31335->regmap, MAX31335_TRICKLE_REG, | ||
return regmap_write(max31335->regmap, max31335->chip->trickle_reg, | ||
FIELD_PREP(MAX31335_TRICKLE_REG_TRICKLE, i) | | ||
FIELD_PREP(MAX31335_TRICKLE_REG_EN_TRICKLE, | ||
chargeable)); | ||
|
@@ -428,7 +487,7 @@ static unsigned long max31335_clkout_recalc_rate(struct clk_hw *hw, | |
unsigned int reg; | ||
int ret; | ||
|
||
ret = regmap_read(max31335->regmap, MAX31335_RTC_CONFIG2, ®); | ||
ret = regmap_read(max31335->regmap, max31335->chip->clkout_reg, ®); | ||
if (ret) | ||
return 0; | ||
|
||
|
@@ -454,29 +513,32 @@ static int max31335_clkout_set_rate(struct clk_hw *hw, unsigned long rate, | |
struct max31335_data *max31335 = clk_hw_to_max31335(hw); | ||
unsigned int freq_mask; | ||
int index; | ||
int ret; | ||
|
||
index = find_closest(rate, max31335_clkout_freq, | ||
ARRAY_SIZE(max31335_clkout_freq)); | ||
freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1; | ||
|
||
return regmap_update_bits(max31335->regmap, MAX31335_RTC_CONFIG2, | ||
freq_mask, index); | ||
ret = regmap_update_bits(max31335->regmap, max31335->chip->clkout_reg, | ||
freq_mask, index); | ||
|
||
return ret; | ||
} | ||
|
||
static int max31335_clkout_enable(struct clk_hw *hw) | ||
{ | ||
struct max31335_data *max31335 = clk_hw_to_max31335(hw); | ||
|
||
return regmap_set_bits(max31335->regmap, MAX31335_RTC_CONFIG2, | ||
MAX31335_RTC_CONFIG2_ENCLKO); | ||
return regmap_set_bits(max31335->regmap, max31335->chip->clkout_reg, | ||
MAX31335_RTC_CONFIG2_ENCLKO); | ||
} | ||
|
||
static void max31335_clkout_disable(struct clk_hw *hw) | ||
{ | ||
struct max31335_data *max31335 = clk_hw_to_max31335(hw); | ||
|
||
regmap_clear_bits(max31335->regmap, MAX31335_RTC_CONFIG2, | ||
MAX31335_RTC_CONFIG2_ENCLKO); | ||
regmap_clear_bits(max31335->regmap, max31335->chip->clkout_reg, | ||
MAX31335_RTC_CONFIG2_ENCLKO); | ||
} | ||
|
||
static int max31335_clkout_is_enabled(struct clk_hw *hw) | ||
|
@@ -485,7 +547,7 @@ static int max31335_clkout_is_enabled(struct clk_hw *hw) | |
unsigned int reg; | ||
int ret; | ||
|
||
ret = regmap_read(max31335->regmap, MAX31335_RTC_CONFIG2, ®); | ||
ret = regmap_read(max31335->regmap, max31335->chip->clkout_reg, ®); | ||
if (ret) | ||
return ret; | ||
|
||
|
@@ -510,7 +572,7 @@ static int max31335_nvmem_reg_read(void *priv, unsigned int offset, | |
void *val, size_t bytes) | ||
{ | ||
struct max31335_data *max31335 = priv; | ||
unsigned int reg = MAX31335_TS0_SEC_1_128 + offset; | ||
unsigned int reg = max31335->chip->ram_reg + offset; | ||
|
||
return regmap_bulk_read(max31335->regmap, reg, val, bytes); | ||
} | ||
|
@@ -519,7 +581,7 @@ static int max31335_nvmem_reg_write(void *priv, unsigned int offset, | |
void *val, size_t bytes) | ||
{ | ||
struct max31335_data *max31335 = priv; | ||
unsigned int reg = MAX31335_TS0_SEC_1_128 + offset; | ||
unsigned int reg = max31335->chip->ram_reg + offset; | ||
|
||
return regmap_bulk_write(max31335->regmap, reg, val, bytes); | ||
} | ||
|
@@ -543,7 +605,7 @@ static int max31335_read_temp(struct device *dev, enum hwmon_sensor_types type, | |
if (type != hwmon_temp || attr != hwmon_temp_input) | ||
return -EOPNOTSUPP; | ||
|
||
ret = regmap_bulk_read(max31335->regmap, MAX31335_TEMP_DATA_MSB, | ||
ret = regmap_bulk_read(max31335->regmap, max31335->chip->temp_reg, | ||
reg, 2); | ||
if (ret) | ||
return ret; | ||
|
@@ -586,9 +648,10 @@ static int max31335_clkout_register(struct device *dev) | |
struct max31335_data *max31335 = dev_get_drvdata(dev); | ||
int ret; | ||
|
||
if (!device_property_present(dev, "#clock-cells")) | ||
return regmap_clear_bits(max31335->regmap, MAX31335_RTC_CONFIG2, | ||
MAX31335_RTC_CONFIG2_ENCLKO); | ||
if (!device_property_present(dev, "#clock-cells")) { | ||
regmap_clear_bits(max31335->regmap, max31335->chip->clkout_reg, | ||
MAX31335_RTC_CONFIG2_ENCLKO); | ||
} | ||
|
||
max31335->clkout.init = &max31335_clk_init; | ||
|
||
|
@@ -617,6 +680,7 @@ static int max31335_probe(struct i2c_client *client, | |
#if IS_REACHABLE(HWMON) | ||
struct device *hwmon; | ||
#endif | ||
const void *match; | ||
int ret; | ||
|
||
max31335 = devm_kzalloc(&client->dev, sizeof(*max31335), GFP_KERNEL); | ||
|
@@ -629,6 +693,16 @@ static int max31335_probe(struct i2c_client *client, | |
|
||
i2c_set_clientdata(client, max31335); | ||
|
||
match = i2c_get_match_data(client); | ||
if (match) | ||
max31335->chip = match; | ||
else if (id) | ||
max31335->chip = (struct chip_desc *)id->driver_data; | ||
else | ||
return -ENODEV; | ||
|
||
max31335->id = max31335->chip - chip; | ||
|
||
max31335->rtc = devm_rtc_allocate_device(&client->dev); | ||
if (IS_ERR(max31335->rtc)) | ||
return PTR_ERR(max31335->rtc); | ||
|
@@ -651,6 +725,8 @@ static int max31335_probe(struct i2c_client *client, | |
dev_warn(&client->dev, | ||
"unable to request IRQ, alarm max31335 disabled\n"); | ||
client->irq = 0; | ||
} else { | ||
max31335->irq = client->irq; | ||
} | ||
} | ||
|
||
|
@@ -664,13 +740,15 @@ static int max31335_probe(struct i2c_client *client, | |
"cannot register rtc nvmem\n"); | ||
|
||
#if IS_REACHABLE(HWMON) | ||
hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, | ||
max31335, | ||
&max31335_chip_info, | ||
NULL); | ||
if (IS_ERR(hwmon)) | ||
return dev_err_probe(&client->dev, PTR_ERR(hwmon), | ||
"cannot register hwmon device\n"); | ||
if (max313xx->chip->temp_reg) { | ||
hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, | ||
max31335, | ||
&max31335_chip_info, | ||
NULL); | ||
if (IS_ERR(hwmon)) | ||
return dev_err_probe(&client->dev, PTR_ERR(hwmon), | ||
"cannot register hwmon device\n"); | ||
} | ||
#endif | ||
|
||
ret = max31335_trickle_charger_setup(&client->dev, max31335); | ||
|
@@ -681,14 +759,16 @@ static int max31335_probe(struct i2c_client *client, | |
} | ||
|
||
static const struct i2c_device_id max31335_id[] = { | ||
{ "max31335", 0 }, | ||
{ "max31331", (kernel_ulong_t)&chip[ID_MAX31331] }, | ||
{ "max31335", (kernel_ulong_t)&chip[ID_MAX31335] }, | ||
{ } | ||
}; | ||
|
||
MODULE_DEVICE_TABLE(i2c, max31335_id); | ||
|
||
static const struct of_device_id max31335_of_match[] = { | ||
{ .compatible = "adi,max31335" }, | ||
{ .compatible = "adi,max31331", .data = &chip[ID_MAX31331] }, | ||
{ .compatible = "adi,max31335", .data = &chip[ID_MAX31335] }, | ||
{ } | ||
}; | ||
|
||
|
@@ -705,5 +785,6 @@ static struct i2c_driver max31335_driver = { | |
module_i2c_driver(max31335_driver); | ||
|
||
MODULE_AUTHOR("Antoniu Miclaus <[email protected]>"); | ||
MODULE_AUTHOR("Saket Kumar Purwar <[email protected]>"); | ||
MODULE_DESCRIPTION("MAX31335 RTC driver"); | ||
MODULE_LICENSE("GPL"); |