summaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-cmos.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2008-02-06 01:38:43 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 10:41:13 -0800
commite07e232cd96ef0092b2bddc72f9b7caf284633cb (patch)
tree442ff1cc6f2548e27315ee6c8ad5252132e5d416 /drivers/rtc/rtc-cmos.c
parent9974b6ea7b85a32f34f824443f47aa501c85ee8f (diff)
downloadlinux-e07e232cd96ef0092b2bddc72f9b7caf284633cb.tar.gz
linux-e07e232cd96ef0092b2bddc72f9b7caf284633cb.tar.bz2
linux-e07e232cd96ef0092b2bddc72f9b7caf284633cb.zip
rtc-cmos: export nvram in sysfs
This makes rtc-cmos export its NVRAM, like several other RTC drivers. It still works within the limits of the current CMOS_READ/CMOS_WRITE calls, which don't understand how to access multiple register banks. The primary impact of that limitation is that Linux can't access the uppermost 128 bytes of NVRAM on many systems. Note that this isn't aiming to be a drop-in replacement for the legacy /dev/nvram support. (Presumably that has real users, and isn't just getting carried forward automatically?) Userspace handles more work: - When userspace code updates NVRAM, that will need to include updating any platform-specific checksums that may apply. - No /proc/driver/nvram file will parse and display NVRAM data according to whichever boot firmware your board expects. Also minor pnp-related updates: update a comment, remove dead code. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> Cc: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-cmos.c')
-rw-r--r--drivers/rtc/rtc-cmos.c129
1 files changed, 117 insertions, 12 deletions
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 29cf1457ca10..ab455ddb16cf 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -393,6 +393,80 @@ static const struct rtc_class_ops cmos_rtc_ops = {
/*----------------------------------------------------------------*/
+/*
+ * All these chips have at least 64 bytes of address space, shared by
+ * RTC registers and NVRAM. Most of those bytes of NVRAM are used
+ * by boot firmware. Modern chips have 128 or 256 bytes.
+ */
+
+#define NVRAM_OFFSET (RTC_REG_D + 1)
+
+static ssize_t
+cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int retval;
+
+ if (unlikely(off >= attr->size))
+ return 0;
+ if ((off + count) > attr->size)
+ count = attr->size - off;
+
+ spin_lock_irq(&rtc_lock);
+ for (retval = 0, off += NVRAM_OFFSET; count--; retval++, off++)
+ *buf++ = CMOS_READ(off);
+ spin_unlock_irq(&rtc_lock);
+
+ return retval;
+}
+
+static ssize_t
+cmos_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct cmos_rtc *cmos;
+ int retval;
+
+ cmos = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ if (unlikely(off >= attr->size))
+ return -EFBIG;
+ if ((off + count) > attr->size)
+ count = attr->size - off;
+
+ /* NOTE: on at least PCs and Ataris, the boot firmware uses a
+ * checksum on part of the NVRAM data. That's currently ignored
+ * here. If userspace is smart enough to know what fields of
+ * NVRAM to update, updating checksums is also part of its job.
+ */
+ spin_lock_irq(&rtc_lock);
+ for (retval = 0, off += NVRAM_OFFSET; count--; retval++, off++) {
+ /* don't trash RTC registers */
+ if (off == cmos->day_alrm
+ || off == cmos->mon_alrm
+ || off == cmos->century)
+ buf++;
+ else
+ CMOS_WRITE(*buf++, off);
+ }
+ spin_unlock_irq(&rtc_lock);
+
+ return retval;
+}
+
+static struct bin_attribute nvram = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+
+ .read = cmos_nvram_read,
+ .write = cmos_nvram_write,
+ /* size gets set up later */
+};
+
+/*----------------------------------------------------------------*/
+
static struct cmos_rtc cmos_rtc;
static irqreturn_t cmos_interrupt(int irq, void *p)
@@ -412,11 +486,9 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
}
#ifdef CONFIG_PNP
-#define is_pnp() 1
#define INITSECTION
#else
-#define is_pnp() 0
#define INITSECTION __init
#endif
@@ -426,6 +498,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
struct cmos_rtc_board_info *info = dev->platform_data;
int retval = 0;
unsigned char rtc_control;
+ unsigned address_space;
/* there can be only one ... */
if (cmos_rtc.dev)
@@ -450,15 +523,36 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
cmos_rtc.irq = rtc_irq;
cmos_rtc.iomem = ports;
+ /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM
+ * driver did, but don't reject unknown configs. Old hardware
+ * won't address 128 bytes, and for now we ignore the way newer
+ * chips can address 256 bytes (using two more i/o ports).
+ */
+#if defined(CONFIG_ATARI)
+ address_space = 64;
+#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)
+ address_space = 128;
+#else
+#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
+ address_space = 128;
+#endif
+
/* For ACPI systems extension info comes from the FADT. On others,
* board specific setup provides it as appropriate. Systems where
* the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and
* some almost-clones) can provide hooks to make that behave.
+ *
+ * Note that ACPI doesn't preclude putting these registers into
+ * "extended" areas of the chip, including some that we won't yet
+ * expect CMOS_READ and friends to handle.
*/
if (info) {
- cmos_rtc.day_alrm = info->rtc_day_alarm;
- cmos_rtc.mon_alrm = info->rtc_mon_alarm;
- cmos_rtc.century = info->rtc_century;
+ if (info->rtc_day_alarm && info->rtc_day_alarm < 128)
+ cmos_rtc.day_alrm = info->rtc_day_alarm;
+ if (info->rtc_mon_alarm && info->rtc_mon_alarm < 128)
+ cmos_rtc.mon_alrm = info->rtc_mon_alarm;
+ if (info->rtc_century && info->rtc_century < 128)
+ cmos_rtc.century = info->rtc_century;
if (info->wake_on && info->wake_off) {
cmos_rtc.wake_on = info->wake_on;
@@ -518,10 +612,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
goto cleanup1;
}
- /* REVISIT optionally make 50 or 114 bytes NVRAM available,
- * like rtc-ds1553, rtc-ds1742 ... this will often include
- * registers for century, and day/month alarm.
- */
+ /* export at least the first block of NVRAM */
+ nvram.size = address_space - NVRAM_OFFSET;
+ retval = sysfs_create_bin_file(&dev->kobj, &nvram);
+ if (retval < 0) {
+ dev_dbg(dev, "can't create nvram file? %d\n", retval);
+ goto cleanup2;
+ }
pr_info("%s: alarms up to one %s%s\n",
cmos_rtc.rtc->dev.bus_id,
@@ -536,6 +633,9 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
return 0;
+cleanup2:
+ if (is_valid_irq(rtc_irq))
+ free_irq(rtc_irq, cmos_rtc.rtc);
cleanup1:
cmos_rtc.dev = NULL;
rtc_device_unregister(cmos_rtc.rtc);
@@ -563,6 +663,8 @@ static void __exit cmos_do_remove(struct device *dev)
cmos_do_shutdown();
+ sysfs_remove_bin_file(&dev->kobj, &nvram);
+
if (is_valid_irq(cmos->irq))
free_irq(cmos->irq, cmos->rtc);
@@ -659,9 +761,12 @@ static int cmos_resume(struct device *dev)
/*----------------------------------------------------------------*/
-/* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems,
- * the device node will always be created as a PNPACPI device. Plus
- * pre-ACPI PCs probably list it in the PNPBIOS tables.
+/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
+ * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs
+ * probably list them in similar PNPBIOS tables; so PNP is more common.
+ *
+ * We don't use legacy "poke at the hardware" probing. Ancient PCs that
+ * predate even PNPBIOS should set up platform_bus devices.
*/
#ifdef CONFIG_PNP