.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Intersil ISL1208 rtc class driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify it |
---|
7 | | - * under the terms of the GNU General Public License as published by the |
---|
8 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
9 | | - * option) any later version. |
---|
10 | | - * |
---|
11 | 6 | */ |
---|
12 | 7 | |
---|
13 | | -#include <linux/module.h> |
---|
14 | | -#include <linux/i2c.h> |
---|
15 | 8 | #include <linux/bcd.h> |
---|
16 | | -#include <linux/rtc.h> |
---|
17 | | -#include "rtc-core.h" |
---|
| 9 | +#include <linux/i2c.h> |
---|
| 10 | +#include <linux/module.h> |
---|
| 11 | +#include <linux/of_device.h> |
---|
18 | 12 | #include <linux/of_irq.h> |
---|
| 13 | +#include <linux/rtc.h> |
---|
19 | 14 | |
---|
20 | 15 | /* Register map */ |
---|
21 | 16 | /* rtc section */ |
---|
.. | .. |
---|
74 | 69 | static struct i2c_driver isl1208_driver; |
---|
75 | 70 | |
---|
76 | 71 | /* ISL1208 various variants */ |
---|
77 | | -enum { |
---|
| 72 | +enum isl1208_id { |
---|
78 | 73 | TYPE_ISL1208 = 0, |
---|
| 74 | + TYPE_ISL1209, |
---|
79 | 75 | TYPE_ISL1218, |
---|
80 | 76 | TYPE_ISL1219, |
---|
| 77 | + ISL_LAST_ID |
---|
| 78 | +}; |
---|
| 79 | + |
---|
| 80 | +/* Chip capabilities table */ |
---|
| 81 | +static const struct isl1208_config { |
---|
| 82 | + const char name[8]; |
---|
| 83 | + unsigned int nvmem_length; |
---|
| 84 | + unsigned has_tamper:1; |
---|
| 85 | + unsigned has_timestamp:1; |
---|
| 86 | +} isl1208_configs[] = { |
---|
| 87 | + [TYPE_ISL1208] = { "isl1208", 2, false, false }, |
---|
| 88 | + [TYPE_ISL1209] = { "isl1209", 2, true, false }, |
---|
| 89 | + [TYPE_ISL1218] = { "isl1218", 8, false, false }, |
---|
| 90 | + [TYPE_ISL1219] = { "isl1219", 2, true, true }, |
---|
| 91 | +}; |
---|
| 92 | + |
---|
| 93 | +static const struct i2c_device_id isl1208_id[] = { |
---|
| 94 | + { "isl1208", TYPE_ISL1208 }, |
---|
| 95 | + { "isl1209", TYPE_ISL1209 }, |
---|
| 96 | + { "isl1218", TYPE_ISL1218 }, |
---|
| 97 | + { "isl1219", TYPE_ISL1219 }, |
---|
| 98 | + { } |
---|
| 99 | +}; |
---|
| 100 | +MODULE_DEVICE_TABLE(i2c, isl1208_id); |
---|
| 101 | + |
---|
| 102 | +static const struct of_device_id isl1208_of_match[] = { |
---|
| 103 | + { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] }, |
---|
| 104 | + { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] }, |
---|
| 105 | + { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] }, |
---|
| 106 | + { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] }, |
---|
| 107 | + { } |
---|
| 108 | +}; |
---|
| 109 | +MODULE_DEVICE_TABLE(of, isl1208_of_match); |
---|
| 110 | + |
---|
| 111 | +/* Device state */ |
---|
| 112 | +struct isl1208_state { |
---|
| 113 | + struct nvmem_config nvmem_config; |
---|
| 114 | + struct rtc_device *rtc; |
---|
| 115 | + const struct isl1208_config *config; |
---|
81 | 116 | }; |
---|
82 | 117 | |
---|
83 | 118 | /* block read */ |
---|
.. | .. |
---|
85 | 120 | isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], |
---|
86 | 121 | unsigned len) |
---|
87 | 122 | { |
---|
88 | | - u8 reg_addr[1] = { reg }; |
---|
89 | | - struct i2c_msg msgs[2] = { |
---|
90 | | - { |
---|
91 | | - .addr = client->addr, |
---|
92 | | - .len = sizeof(reg_addr), |
---|
93 | | - .buf = reg_addr |
---|
94 | | - }, |
---|
95 | | - { |
---|
96 | | - .addr = client->addr, |
---|
97 | | - .flags = I2C_M_RD, |
---|
98 | | - .len = len, |
---|
99 | | - .buf = buf |
---|
100 | | - } |
---|
101 | | - }; |
---|
102 | 123 | int ret; |
---|
103 | 124 | |
---|
104 | 125 | WARN_ON(reg > ISL1219_REG_YRT); |
---|
105 | 126 | WARN_ON(reg + len > ISL1219_REG_YRT + 1); |
---|
106 | 127 | |
---|
107 | | - ret = i2c_transfer(client->adapter, msgs, 2); |
---|
108 | | - if (ret > 0) |
---|
109 | | - ret = 0; |
---|
110 | | - return ret; |
---|
| 128 | + ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf); |
---|
| 129 | + return (ret < 0) ? ret : 0; |
---|
111 | 130 | } |
---|
112 | 131 | |
---|
113 | 132 | /* block write */ |
---|
.. | .. |
---|
115 | 134 | isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], |
---|
116 | 135 | unsigned len) |
---|
117 | 136 | { |
---|
118 | | - u8 i2c_buf[ISL1208_REG_USR2 + 2]; |
---|
119 | | - struct i2c_msg msgs[1] = { |
---|
120 | | - { |
---|
121 | | - .addr = client->addr, |
---|
122 | | - .len = len + 1, |
---|
123 | | - .buf = i2c_buf |
---|
124 | | - } |
---|
125 | | - }; |
---|
126 | 137 | int ret; |
---|
127 | 138 | |
---|
128 | 139 | WARN_ON(reg > ISL1219_REG_YRT); |
---|
129 | 140 | WARN_ON(reg + len > ISL1219_REG_YRT + 1); |
---|
130 | 141 | |
---|
131 | | - i2c_buf[0] = reg; |
---|
132 | | - memcpy(&i2c_buf[1], &buf[0], len); |
---|
133 | | - |
---|
134 | | - ret = i2c_transfer(client->adapter, msgs, 1); |
---|
135 | | - if (ret > 0) |
---|
136 | | - ret = 0; |
---|
137 | | - return ret; |
---|
| 142 | + ret = i2c_smbus_write_i2c_block_data(client, reg, len, buf); |
---|
| 143 | + return (ret < 0) ? ret : 0; |
---|
138 | 144 | } |
---|
139 | 145 | |
---|
140 | 146 | /* simple check to see whether we have a isl1208 */ |
---|
.. | .. |
---|
191 | 197 | return atr; |
---|
192 | 198 | } |
---|
193 | 199 | |
---|
| 200 | +/* returns adjustment value + 100 */ |
---|
194 | 201 | static int |
---|
195 | 202 | isl1208_i2c_get_dtr(struct i2c_client *client) |
---|
196 | 203 | { |
---|
.. | .. |
---|
201 | 208 | /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ |
---|
202 | 209 | dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1); |
---|
203 | 210 | |
---|
204 | | - return dtr; |
---|
| 211 | + return dtr + 100; |
---|
205 | 212 | } |
---|
206 | 213 | |
---|
207 | 214 | static int |
---|
.. | .. |
---|
278 | 285 | (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); |
---|
279 | 286 | |
---|
280 | 287 | dtr = isl1208_i2c_get_dtr(client); |
---|
281 | | - if (dtr >= 0 - 1) |
---|
282 | | - seq_printf(seq, "digital_trim\t: %d ppm\n", dtr); |
---|
| 288 | + if (dtr >= 0) |
---|
| 289 | + seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100); |
---|
283 | 290 | |
---|
284 | 291 | atr = isl1208_i2c_get_atr(client); |
---|
285 | 292 | if (atr >= 0) |
---|
.. | .. |
---|
586 | 593 | { |
---|
587 | 594 | unsigned long timeout = jiffies + msecs_to_jiffies(1000); |
---|
588 | 595 | struct i2c_client *client = data; |
---|
589 | | - struct rtc_device *rtc = i2c_get_clientdata(client); |
---|
| 596 | + struct isl1208_state *isl1208 = i2c_get_clientdata(client); |
---|
590 | 597 | int handled = 0, sr, err; |
---|
591 | 598 | |
---|
592 | 599 | /* |
---|
.. | .. |
---|
609 | 616 | if (sr & ISL1208_REG_SR_ALM) { |
---|
610 | 617 | dev_dbg(&client->dev, "alarm!\n"); |
---|
611 | 618 | |
---|
612 | | - rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); |
---|
| 619 | + rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF); |
---|
613 | 620 | |
---|
614 | 621 | /* Clear the alarm */ |
---|
615 | 622 | sr &= ~ISL1208_REG_SR_ALM; |
---|
.. | .. |
---|
626 | 633 | return err; |
---|
627 | 634 | } |
---|
628 | 635 | |
---|
629 | | - if (sr & ISL1208_REG_SR_EVT) { |
---|
630 | | - sysfs_notify(&rtc->dev.kobj, NULL, |
---|
631 | | - dev_attr_timestamp0.attr.name); |
---|
| 636 | + if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) { |
---|
632 | 637 | dev_warn(&client->dev, "event detected"); |
---|
633 | 638 | handled = 1; |
---|
| 639 | + if (isl1208->config->has_timestamp) |
---|
| 640 | + sysfs_notify(&isl1208->rtc->dev.kobj, NULL, |
---|
| 641 | + dev_attr_timestamp0.attr.name); |
---|
634 | 642 | } |
---|
635 | 643 | |
---|
636 | 644 | return handled ? IRQ_HANDLED : IRQ_NONE; |
---|
.. | .. |
---|
667 | 675 | if (dtr < 0) |
---|
668 | 676 | return dtr; |
---|
669 | 677 | |
---|
670 | | - return sprintf(buf, "%d ppm\n", dtr); |
---|
| 678 | + return sprintf(buf, "%d ppm\n", dtr - 100); |
---|
671 | 679 | } |
---|
672 | 680 | |
---|
673 | 681 | static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); |
---|
.. | .. |
---|
730 | 738 | .attrs = isl1219_rtc_attrs, |
---|
731 | 739 | }; |
---|
732 | 740 | |
---|
| 741 | +static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf, |
---|
| 742 | + size_t count) |
---|
| 743 | +{ |
---|
| 744 | + struct isl1208_state *isl1208 = priv; |
---|
| 745 | + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); |
---|
| 746 | + int ret; |
---|
| 747 | + |
---|
| 748 | + /* nvmem sanitizes offset/count for us, but count==0 is possible */ |
---|
| 749 | + if (!count) |
---|
| 750 | + return count; |
---|
| 751 | + ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf, |
---|
| 752 | + count); |
---|
| 753 | + return ret == 0 ? count : ret; |
---|
| 754 | +} |
---|
| 755 | + |
---|
| 756 | +static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf, |
---|
| 757 | + size_t count) |
---|
| 758 | +{ |
---|
| 759 | + struct isl1208_state *isl1208 = priv; |
---|
| 760 | + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); |
---|
| 761 | + int ret; |
---|
| 762 | + |
---|
| 763 | + /* nvmem sanitizes off/count for us, but count==0 is possible */ |
---|
| 764 | + if (!count) |
---|
| 765 | + return count; |
---|
| 766 | + ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf, |
---|
| 767 | + count); |
---|
| 768 | + |
---|
| 769 | + return ret == 0 ? count : ret; |
---|
| 770 | +} |
---|
| 771 | + |
---|
| 772 | +static const struct nvmem_config isl1208_nvmem_config = { |
---|
| 773 | + .name = "isl1208_nvram", |
---|
| 774 | + .word_size = 1, |
---|
| 775 | + .stride = 1, |
---|
| 776 | + /* .size from chip specific config */ |
---|
| 777 | + .reg_read = isl1208_nvmem_read, |
---|
| 778 | + .reg_write = isl1208_nvmem_write, |
---|
| 779 | +}; |
---|
| 780 | + |
---|
733 | 781 | static int isl1208_setup_irq(struct i2c_client *client, int irq) |
---|
734 | 782 | { |
---|
735 | 783 | int rc = devm_request_threaded_irq(&client->dev, irq, NULL, |
---|
.. | .. |
---|
752 | 800 | isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) |
---|
753 | 801 | { |
---|
754 | 802 | int rc = 0; |
---|
755 | | - struct rtc_device *rtc; |
---|
| 803 | + struct isl1208_state *isl1208; |
---|
756 | 804 | int evdet_irq = -1; |
---|
757 | 805 | |
---|
758 | 806 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) |
---|
.. | .. |
---|
761 | 809 | if (isl1208_i2c_validate_client(client) < 0) |
---|
762 | 810 | return -ENODEV; |
---|
763 | 811 | |
---|
764 | | - rtc = devm_rtc_allocate_device(&client->dev); |
---|
765 | | - if (IS_ERR(rtc)) |
---|
766 | | - return PTR_ERR(rtc); |
---|
| 812 | + /* Allocate driver state, point i2c client data to it */ |
---|
| 813 | + isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL); |
---|
| 814 | + if (!isl1208) |
---|
| 815 | + return -ENOMEM; |
---|
| 816 | + i2c_set_clientdata(client, isl1208); |
---|
767 | 817 | |
---|
768 | | - rtc->ops = &isl1208_rtc_ops; |
---|
| 818 | + /* Determine which chip we have */ |
---|
| 819 | + if (client->dev.of_node) { |
---|
| 820 | + isl1208->config = of_device_get_match_data(&client->dev); |
---|
| 821 | + if (!isl1208->config) |
---|
| 822 | + return -ENODEV; |
---|
| 823 | + } else { |
---|
| 824 | + if (id->driver_data >= ISL_LAST_ID) |
---|
| 825 | + return -ENODEV; |
---|
| 826 | + isl1208->config = &isl1208_configs[id->driver_data]; |
---|
| 827 | + } |
---|
769 | 828 | |
---|
770 | | - i2c_set_clientdata(client, rtc); |
---|
| 829 | + isl1208->rtc = devm_rtc_allocate_device(&client->dev); |
---|
| 830 | + if (IS_ERR(isl1208->rtc)) |
---|
| 831 | + return PTR_ERR(isl1208->rtc); |
---|
| 832 | + |
---|
| 833 | + isl1208->rtc->ops = &isl1208_rtc_ops; |
---|
| 834 | + |
---|
| 835 | + /* Setup nvmem configuration in driver state struct */ |
---|
| 836 | + isl1208->nvmem_config = isl1208_nvmem_config; |
---|
| 837 | + isl1208->nvmem_config.size = isl1208->config->nvmem_length; |
---|
| 838 | + isl1208->nvmem_config.priv = isl1208; |
---|
771 | 839 | |
---|
772 | 840 | rc = isl1208_i2c_get_sr(client); |
---|
773 | 841 | if (rc < 0) { |
---|
.. | .. |
---|
779 | 847 | dev_warn(&client->dev, "rtc power failure detected, " |
---|
780 | 848 | "please set clock.\n"); |
---|
781 | 849 | |
---|
782 | | - if (id->driver_data == TYPE_ISL1219) { |
---|
| 850 | + if (isl1208->config->has_tamper) { |
---|
783 | 851 | struct device_node *np = client->dev.of_node; |
---|
784 | 852 | u32 evienb; |
---|
785 | 853 | |
---|
.. | .. |
---|
800 | 868 | dev_err(&client->dev, "could not enable tamper detection\n"); |
---|
801 | 869 | return rc; |
---|
802 | 870 | } |
---|
803 | | - rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files); |
---|
804 | | - if (rc) |
---|
805 | | - return rc; |
---|
806 | 871 | evdet_irq = of_irq_get_byname(np, "evdet"); |
---|
807 | 872 | } |
---|
| 873 | + if (isl1208->config->has_timestamp) { |
---|
| 874 | + rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); |
---|
| 875 | + if (rc) |
---|
| 876 | + return rc; |
---|
| 877 | + } |
---|
808 | 878 | |
---|
809 | | - rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files); |
---|
| 879 | + rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); |
---|
810 | 880 | if (rc) |
---|
811 | 881 | return rc; |
---|
812 | 882 | |
---|
.. | .. |
---|
820 | 890 | if (rc) |
---|
821 | 891 | return rc; |
---|
822 | 892 | |
---|
823 | | - return rtc_register_device(rtc); |
---|
| 893 | + rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config); |
---|
| 894 | + if (rc) |
---|
| 895 | + return rc; |
---|
| 896 | + |
---|
| 897 | + return rtc_register_device(isl1208->rtc); |
---|
824 | 898 | } |
---|
825 | | - |
---|
826 | | -static const struct i2c_device_id isl1208_id[] = { |
---|
827 | | - { "isl1208", TYPE_ISL1208 }, |
---|
828 | | - { "isl1218", TYPE_ISL1218 }, |
---|
829 | | - { "isl1219", TYPE_ISL1219 }, |
---|
830 | | - { } |
---|
831 | | -}; |
---|
832 | | -MODULE_DEVICE_TABLE(i2c, isl1208_id); |
---|
833 | | - |
---|
834 | | -static const struct of_device_id isl1208_of_match[] = { |
---|
835 | | - { .compatible = "isil,isl1208" }, |
---|
836 | | - { .compatible = "isil,isl1218" }, |
---|
837 | | - { .compatible = "isil,isl1219" }, |
---|
838 | | - { } |
---|
839 | | -}; |
---|
840 | | -MODULE_DEVICE_TABLE(of, isl1208_of_match); |
---|
841 | 899 | |
---|
842 | 900 | static struct i2c_driver isl1208_driver = { |
---|
843 | 901 | .driver = { |
---|