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