1cca4c231SDavid S. Miller /* rtc-bq4802.c: TI BQ4802 RTC driver. 2cca4c231SDavid S. Miller * 3cca4c231SDavid S. Miller * Copyright (C) 2008 David S. Miller <davem@davemloft.net> 4cca4c231SDavid S. Miller */ 5cca4c231SDavid S. Miller 6cca4c231SDavid S. Miller #include <linux/kernel.h> 7cca4c231SDavid S. Miller #include <linux/module.h> 8cca4c231SDavid S. Miller #include <linux/init.h> 9cca4c231SDavid S. Miller #include <linux/io.h> 10cca4c231SDavid S. Miller #include <linux/platform_device.h> 11cca4c231SDavid S. Miller #include <linux/rtc.h> 12cca4c231SDavid S. Miller #include <linux/bcd.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 14cca4c231SDavid S. Miller 15cca4c231SDavid S. Miller MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); 16cca4c231SDavid S. Miller MODULE_DESCRIPTION("TI BQ4802 RTC driver"); 17cca4c231SDavid S. Miller MODULE_LICENSE("GPL"); 18cca4c231SDavid S. Miller 19cca4c231SDavid S. Miller struct bq4802 { 20cca4c231SDavid S. Miller void __iomem *regs; 21503acc8aSDavid S. Miller unsigned long ioport; 22cca4c231SDavid S. Miller struct rtc_device *rtc; 23cca4c231SDavid S. Miller spinlock_t lock; 24cca4c231SDavid S. Miller struct resource *r; 25cca4c231SDavid S. Miller u8 (*read)(struct bq4802 *, int); 26cca4c231SDavid S. Miller void (*write)(struct bq4802 *, int, u8); 27cca4c231SDavid S. Miller }; 28cca4c231SDavid S. Miller 29cca4c231SDavid S. Miller static u8 bq4802_read_io(struct bq4802 *p, int off) 30cca4c231SDavid S. Miller { 31503acc8aSDavid S. Miller return inb(p->ioport + off); 32cca4c231SDavid S. Miller } 33cca4c231SDavid S. Miller 34cca4c231SDavid S. Miller static void bq4802_write_io(struct bq4802 *p, int off, u8 val) 35cca4c231SDavid S. Miller { 36503acc8aSDavid S. Miller outb(val, p->ioport + off); 37cca4c231SDavid S. Miller } 38cca4c231SDavid S. Miller 39cca4c231SDavid S. Miller static u8 bq4802_read_mem(struct bq4802 *p, int off) 40cca4c231SDavid S. Miller { 41cca4c231SDavid S. Miller return readb(p->regs + off); 42cca4c231SDavid S. Miller } 43cca4c231SDavid S. Miller 44cca4c231SDavid S. Miller static void bq4802_write_mem(struct bq4802 *p, int off, u8 val) 45cca4c231SDavid S. Miller { 46503acc8aSDavid S. Miller writeb(val, p->regs + off); 47cca4c231SDavid S. Miller } 48cca4c231SDavid S. Miller 49cca4c231SDavid S. Miller static int bq4802_read_time(struct device *dev, struct rtc_time *tm) 50cca4c231SDavid S. Miller { 51cca4c231SDavid S. Miller struct platform_device *pdev = to_platform_device(dev); 52cca4c231SDavid S. Miller struct bq4802 *p = platform_get_drvdata(pdev); 53cca4c231SDavid S. Miller unsigned long flags; 54cca4c231SDavid S. Miller unsigned int century; 55cca4c231SDavid S. Miller u8 val; 56cca4c231SDavid S. Miller 57cca4c231SDavid S. Miller spin_lock_irqsave(&p->lock, flags); 58cca4c231SDavid S. Miller 59cca4c231SDavid S. Miller val = p->read(p, 0x0e); 60cca4c231SDavid S. Miller p->write(p, 0xe, val | 0x08); 61cca4c231SDavid S. Miller 62cca4c231SDavid S. Miller tm->tm_sec = p->read(p, 0x00); 63cca4c231SDavid S. Miller tm->tm_min = p->read(p, 0x02); 64cca4c231SDavid S. Miller tm->tm_hour = p->read(p, 0x04); 65cca4c231SDavid S. Miller tm->tm_mday = p->read(p, 0x06); 66cca4c231SDavid S. Miller tm->tm_mon = p->read(p, 0x09); 67cca4c231SDavid S. Miller tm->tm_year = p->read(p, 0x0a); 68cca4c231SDavid S. Miller tm->tm_wday = p->read(p, 0x08); 69cca4c231SDavid S. Miller century = p->read(p, 0x0f); 70cca4c231SDavid S. Miller 71cca4c231SDavid S. Miller p->write(p, 0x0e, val); 72cca4c231SDavid S. Miller 73cca4c231SDavid S. Miller spin_unlock_irqrestore(&p->lock, flags); 74cca4c231SDavid S. Miller 75e232cfdcSAndrew Morton tm->tm_sec = bcd2bin(tm->tm_sec); 76e232cfdcSAndrew Morton tm->tm_min = bcd2bin(tm->tm_min); 77e232cfdcSAndrew Morton tm->tm_hour = bcd2bin(tm->tm_hour); 78e232cfdcSAndrew Morton tm->tm_mday = bcd2bin(tm->tm_mday); 79e232cfdcSAndrew Morton tm->tm_mon = bcd2bin(tm->tm_mon); 80e232cfdcSAndrew Morton tm->tm_year = bcd2bin(tm->tm_year); 81e232cfdcSAndrew Morton tm->tm_wday = bcd2bin(tm->tm_wday); 82e232cfdcSAndrew Morton century = bcd2bin(century); 83cca4c231SDavid S. Miller 84cca4c231SDavid S. Miller tm->tm_year += (century * 100); 85cca4c231SDavid S. Miller tm->tm_year -= 1900; 86cca4c231SDavid S. Miller 87cca4c231SDavid S. Miller tm->tm_mon--; 88cca4c231SDavid S. Miller 89cca4c231SDavid S. Miller return 0; 90cca4c231SDavid S. Miller } 91cca4c231SDavid S. Miller 92cca4c231SDavid S. Miller static int bq4802_set_time(struct device *dev, struct rtc_time *tm) 93cca4c231SDavid S. Miller { 94cca4c231SDavid S. Miller struct platform_device *pdev = to_platform_device(dev); 95cca4c231SDavid S. Miller struct bq4802 *p = platform_get_drvdata(pdev); 96cca4c231SDavid S. Miller u8 sec, min, hrs, day, mon, yrs, century, val; 97cca4c231SDavid S. Miller unsigned long flags; 98cca4c231SDavid S. Miller unsigned int year; 99cca4c231SDavid S. Miller 100cca4c231SDavid S. Miller year = tm->tm_year + 1900; 101cca4c231SDavid S. Miller century = year / 100; 102cca4c231SDavid S. Miller yrs = year % 100; 103cca4c231SDavid S. Miller 104cca4c231SDavid S. Miller mon = tm->tm_mon + 1; /* tm_mon starts at zero */ 105cca4c231SDavid S. Miller day = tm->tm_mday; 106cca4c231SDavid S. Miller hrs = tm->tm_hour; 107cca4c231SDavid S. Miller min = tm->tm_min; 108cca4c231SDavid S. Miller sec = tm->tm_sec; 109cca4c231SDavid S. Miller 110e232cfdcSAndrew Morton sec = bin2bcd(sec); 111e232cfdcSAndrew Morton min = bin2bcd(min); 112e232cfdcSAndrew Morton hrs = bin2bcd(hrs); 113e232cfdcSAndrew Morton day = bin2bcd(day); 114e232cfdcSAndrew Morton mon = bin2bcd(mon); 115e232cfdcSAndrew Morton yrs = bin2bcd(yrs); 116e232cfdcSAndrew Morton century = bin2bcd(century); 117cca4c231SDavid S. Miller 118cca4c231SDavid S. Miller spin_lock_irqsave(&p->lock, flags); 119cca4c231SDavid S. Miller 120cca4c231SDavid S. Miller val = p->read(p, 0x0e); 121cca4c231SDavid S. Miller p->write(p, 0x0e, val | 0x08); 122cca4c231SDavid S. Miller 123cca4c231SDavid S. Miller p->write(p, 0x00, sec); 124cca4c231SDavid S. Miller p->write(p, 0x02, min); 125cca4c231SDavid S. Miller p->write(p, 0x04, hrs); 126cca4c231SDavid S. Miller p->write(p, 0x06, day); 127cca4c231SDavid S. Miller p->write(p, 0x09, mon); 128cca4c231SDavid S. Miller p->write(p, 0x0a, yrs); 129cca4c231SDavid S. Miller p->write(p, 0x0f, century); 130cca4c231SDavid S. Miller 131cca4c231SDavid S. Miller p->write(p, 0x0e, val); 132cca4c231SDavid S. Miller 133cca4c231SDavid S. Miller spin_unlock_irqrestore(&p->lock, flags); 134cca4c231SDavid S. Miller 135cca4c231SDavid S. Miller return 0; 136cca4c231SDavid S. Miller } 137cca4c231SDavid S. Miller 138cca4c231SDavid S. Miller static const struct rtc_class_ops bq4802_ops = { 139cca4c231SDavid S. Miller .read_time = bq4802_read_time, 140cca4c231SDavid S. Miller .set_time = bq4802_set_time, 141cca4c231SDavid S. Miller }; 142cca4c231SDavid S. Miller 1435a167f45SGreg Kroah-Hartman static int bq4802_probe(struct platform_device *pdev) 144cca4c231SDavid S. Miller { 14526c5f7d9SJingoo Han struct bq4802 *p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); 146cca4c231SDavid S. Miller int err = -ENOMEM; 147cca4c231SDavid S. Miller 148cca4c231SDavid S. Miller if (!p) 149cca4c231SDavid S. Miller goto out; 150cca4c231SDavid S. Miller 151cca4c231SDavid S. Miller spin_lock_init(&p->lock); 152cca4c231SDavid S. Miller 153cca4c231SDavid S. Miller p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 154cca4c231SDavid S. Miller if (!p->r) { 155cca4c231SDavid S. Miller p->r = platform_get_resource(pdev, IORESOURCE_IO, 0); 156cca4c231SDavid S. Miller err = -EINVAL; 157cca4c231SDavid S. Miller if (!p->r) 15826c5f7d9SJingoo Han goto out; 159cca4c231SDavid S. Miller } 160cca4c231SDavid S. Miller if (p->r->flags & IORESOURCE_IO) { 161503acc8aSDavid S. Miller p->ioport = p->r->start; 162cca4c231SDavid S. Miller p->read = bq4802_read_io; 163cca4c231SDavid S. Miller p->write = bq4802_write_io; 164cca4c231SDavid S. Miller } else if (p->r->flags & IORESOURCE_MEM) { 16526c5f7d9SJingoo Han p->regs = devm_ioremap(&pdev->dev, p->r->start, 16626c5f7d9SJingoo Han resource_size(p->r)); 167cca4c231SDavid S. Miller p->read = bq4802_read_mem; 168cca4c231SDavid S. Miller p->write = bq4802_write_mem; 169cca4c231SDavid S. Miller } else { 170cca4c231SDavid S. Miller err = -EINVAL; 17126c5f7d9SJingoo Han goto out; 172cca4c231SDavid S. Miller } 173cca4c231SDavid S. Miller 174b74d2caaSAlessandro Zummo platform_set_drvdata(pdev, p); 175b74d2caaSAlessandro Zummo 17626c5f7d9SJingoo Han p->rtc = devm_rtc_device_register(&pdev->dev, "bq4802", 177cca4c231SDavid S. Miller &bq4802_ops, THIS_MODULE); 178cca4c231SDavid S. Miller if (IS_ERR(p->rtc)) { 179cca4c231SDavid S. Miller err = PTR_ERR(p->rtc); 18026c5f7d9SJingoo Han goto out; 181cca4c231SDavid S. Miller } 182cca4c231SDavid S. Miller 183cca4c231SDavid S. Miller err = 0; 184cca4c231SDavid S. Miller out: 185cca4c231SDavid S. Miller return err; 186cca4c231SDavid S. Miller 187cca4c231SDavid S. Miller } 188cca4c231SDavid S. Miller 1895a167f45SGreg Kroah-Hartman static int bq4802_remove(struct platform_device *pdev) 190cca4c231SDavid S. Miller { 191cca4c231SDavid S. Miller platform_set_drvdata(pdev, NULL); 192cca4c231SDavid S. Miller 193cca4c231SDavid S. Miller return 0; 194cca4c231SDavid S. Miller } 195cca4c231SDavid S. Miller 196cca4c231SDavid S. Miller /* work with hotplug and coldplug */ 197cca4c231SDavid S. Miller MODULE_ALIAS("platform:rtc-bq4802"); 198cca4c231SDavid S. Miller 199cca4c231SDavid S. Miller static struct platform_driver bq4802_driver = { 200cca4c231SDavid S. Miller .driver = { 201cca4c231SDavid S. Miller .name = "rtc-bq4802", 202cca4c231SDavid S. Miller .owner = THIS_MODULE, 203cca4c231SDavid S. Miller }, 204cca4c231SDavid S. Miller .probe = bq4802_probe, 2055a167f45SGreg Kroah-Hartman .remove = bq4802_remove, 206cca4c231SDavid S. Miller }; 207cca4c231SDavid S. Miller 2080c4eae66SAxel Lin module_platform_driver(bq4802_driver); 209