cregit-Linux how code gets into the kernel

Release 4.7 drivers/rtc/rtc-lp8788.c

Directory: drivers/rtc
/*
 * TI LP8788 MFD - rtc driver
 *
 * Copyright 2012 Texas Instruments
 *
 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/err.h>
#include <linux/irqdomain.h>
#include <linux/mfd/lp8788.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/slab.h>

/* register address */

#define LP8788_INTEN_3			0x05

#define LP8788_RTC_UNLOCK		0x64

#define LP8788_RTC_SEC			0x70

#define LP8788_ALM1_SEC			0x77

#define LP8788_ALM1_EN			0x7D

#define LP8788_ALM2_SEC			0x7E

#define LP8788_ALM2_EN			0x84

/* mask/shift bits */

#define LP8788_INT_RTC_ALM1_M		BIT(1)	
/* Addr 05h */

#define LP8788_INT_RTC_ALM1_S		1

#define LP8788_INT_RTC_ALM2_M		BIT(2)	
/* Addr 05h */

#define LP8788_INT_RTC_ALM2_S		2

#define LP8788_ALM_EN_M			BIT(7)	
/* Addr 7Dh or 84h */

#define LP8788_ALM_EN_S			7


#define DEFAULT_ALARM_SEL		LP8788_ALARM_1

#define LP8788_MONTH_OFFSET		1

#define LP8788_BASE_YEAR		2000

#define MAX_WDAY_BITS			7

#define LP8788_WDAY_SET			1

#define RTC_UNLOCK			0x1

#define RTC_LATCH			0x2

#define ALARM_IRQ_FLAG			(RTC_IRQF | RTC_AF)


enum lp8788_time {
	
LPTIME_SEC,
	
LPTIME_MIN,
	
LPTIME_HOUR,
	
LPTIME_MDAY,
	
LPTIME_MON,
	
LPTIME_YEAR,
	
LPTIME_WDAY,
	
LPTIME_MAX,
};


struct lp8788_rtc {
	
struct lp8788 *lp;
	
struct rtc_device *rdev;
	
enum lp8788_alarm_sel alarm;
	
int irq;
};


static const u8 addr_alarm_sec[LP8788_ALARM_MAX] = {
	LP8788_ALM1_SEC,
	LP8788_ALM2_SEC,
};


static const u8 addr_alarm_en[LP8788_ALARM_MAX] = {
	LP8788_ALM1_EN,
	LP8788_ALM2_EN,
};


static const u8 mask_alarm_en[LP8788_ALARM_MAX] = {
	LP8788_INT_RTC_ALM1_M,
	LP8788_INT_RTC_ALM2_M,
};


static const u8 shift_alarm_en[LP8788_ALARM_MAX] = {
	LP8788_INT_RTC_ALM1_S,
	LP8788_INT_RTC_ALM2_S,
};


static int _to_tm_wday(u8 lp8788_wday) { int i; if (lp8788_wday == 0) return 0; /* lookup defined weekday from read register value */ for (i = 0; i < MAX_WDAY_BITS; i++) { if ((lp8788_wday >> i) == LP8788_WDAY_SET) break; } return i + 1; }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo53100.00%1100.00%
Total53100.00%1100.00%


static inline int _to_lp8788_wday(int tm_wday) { return LP8788_WDAY_SET << (tm_wday - 1); }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo19100.00%1100.00%
Total19100.00%1100.00%


static void lp8788_rtc_unlock(struct lp8788 *lp) { lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_UNLOCK); lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_LATCH); }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo29100.00%1100.00%
Total29100.00%1100.00%


static int lp8788_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct lp8788_rtc *rtc = dev_get_drvdata(dev); struct lp8788 *lp = rtc->lp; u8 data[LPTIME_MAX]; int ret; lp8788_rtc_unlock(lp); ret = lp8788_read_multi_bytes(lp, LP8788_RTC_SEC, data, LPTIME_MAX); if (ret) return ret; tm->tm_sec = data[LPTIME_SEC]; tm->tm_min = data[LPTIME_MIN]; tm->tm_hour = data[LPTIME_HOUR]; tm->tm_mday = data[LPTIME_MDAY]; tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo144100.00%1100.00%
Total144100.00%1100.00%


static int lp8788_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct lp8788_rtc *rtc = dev_get_drvdata(dev); struct lp8788 *lp = rtc->lp; u8 data[LPTIME_MAX - 1]; int ret, i, year; year = tm->tm_year + 1900 - LP8788_BASE_YEAR; if (year < 0) { dev_err(lp->dev, "invalid year: %d\n", year); return -EINVAL; } /* because rtc weekday is a readonly register, do not update */ data[LPTIME_SEC] = tm->tm_sec; data[LPTIME_MIN] = tm->tm_min; data[LPTIME_HOUR] = tm->tm_hour; data[LPTIME_MDAY] = tm->tm_mday; data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; data[LPTIME_YEAR] = year; for (i = 0; i < ARRAY_SIZE(data); i++) { ret = lp8788_write_byte(lp, LP8788_RTC_SEC + i, data[i]); if (ret) return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo182100.00%1100.00%
Total182100.00%1100.00%


static int lp8788_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct lp8788_rtc *rtc = dev_get_drvdata(dev); struct lp8788 *lp = rtc->lp; struct rtc_time *tm = &alarm->time; u8 addr, data[LPTIME_MAX]; int ret; addr = addr_alarm_sec[rtc->alarm]; ret = lp8788_read_multi_bytes(lp, addr, data, LPTIME_MAX); if (ret) return ret; tm->tm_sec = data[LPTIME_SEC]; tm->tm_min = data[LPTIME_MIN]; tm->tm_hour = data[LPTIME_HOUR]; tm->tm_mday = data[LPTIME_MDAY]; tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); alarm->enabled = data[LPTIME_WDAY] & LP8788_ALM_EN_M; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo171100.00%1100.00%
Total171100.00%1100.00%


static int lp8788_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { struct lp8788_rtc *rtc = dev_get_drvdata(dev); struct lp8788 *lp = rtc->lp; struct rtc_time *tm = &alarm->time; u8 addr, data[LPTIME_MAX]; int ret, i, year; year = tm->tm_year + 1900 - LP8788_BASE_YEAR; if (year < 0) { dev_err(lp->dev, "invalid year: %d\n", year); return -EINVAL; } data[LPTIME_SEC] = tm->tm_sec; data[LPTIME_MIN] = tm->tm_min; data[LPTIME_HOUR] = tm->tm_hour; data[LPTIME_MDAY] = tm->tm_mday; data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; data[LPTIME_YEAR] = year; data[LPTIME_WDAY] = _to_lp8788_wday(tm->tm_wday); for (i = 0; i < ARRAY_SIZE(data); i++) { addr = addr_alarm_sec[rtc->alarm] + i; ret = lp8788_write_byte(lp, addr, data[i]); if (ret) return ret; } alarm->enabled = 1; addr = addr_alarm_en[rtc->alarm]; return lp8788_update_bits(lp, addr, LP8788_ALM_EN_M, alarm->enabled << LP8788_ALM_EN_S); }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo240100.00%1100.00%
Total240100.00%1100.00%


static int lp8788_alarm_irq_enable(struct device *dev, unsigned int enable) { struct lp8788_rtc *rtc = dev_get_drvdata(dev); struct lp8788 *lp = rtc->lp; u8 mask, shift; if (!rtc->irq) return -EIO; mask = mask_alarm_en[rtc->alarm]; shift = shift_alarm_en[rtc->alarm]; return lp8788_update_bits(lp, LP8788_INTEN_3, mask, enable << shift); }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo82100.00%1100.00%
Total82100.00%1100.00%

static const struct rtc_class_ops lp8788_rtc_ops = { .read_time = lp8788_rtc_read_time, .set_time = lp8788_rtc_set_time, .read_alarm = lp8788_read_alarm, .set_alarm = lp8788_set_alarm, .alarm_irq_enable = lp8788_alarm_irq_enable, };
static irqreturn_t lp8788_alarm_irq_handler(int irq, void *ptr) { struct lp8788_rtc *rtc = ptr; rtc_update_irq(rtc->rdev, 1, ALARM_IRQ_FLAG); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo34100.00%1100.00%
Total34100.00%1100.00%


static int lp8788_alarm_irq_register(struct platform_device *pdev, struct lp8788_rtc *rtc) { struct resource *r; struct lp8788 *lp = rtc->lp; struct irq_domain *irqdm = lp->irqdm; int irq; rtc->irq = 0; /* even the alarm IRQ number is not specified, rtc time should work */ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, LP8788_ALM_IRQ); if (!r) return 0; if (rtc->alarm == LP8788_ALARM_1) irq = r->start; else irq = r->end; rtc->irq = irq_create_mapping(irqdm, irq); return devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, lp8788_alarm_irq_handler, 0, LP8788_ALM_IRQ, rtc); }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo11795.12%150.00%
jingoo hanjingoo han64.88%150.00%
Total123100.00%2100.00%


static int lp8788_rtc_probe(struct platform_device *pdev) { struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); struct lp8788_rtc *rtc; struct device *dev = &pdev->dev; rtc = devm_kzalloc(dev, sizeof(struct lp8788_rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; rtc->lp = lp; rtc->alarm = lp->pdata ? lp->pdata->alarm_sel : DEFAULT_ALARM_SEL; platform_set_drvdata(pdev, rtc); device_init_wakeup(dev, 1); rtc->rdev = devm_rtc_device_register(dev, "lp8788_rtc", &lp8788_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rdev)) { dev_err(dev, "can not register rtc device\n"); return PTR_ERR(rtc->rdev); } if (lp8788_alarm_irq_register(pdev, rtc)) dev_warn(lp->dev, "no rtc irq handler\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo16098.16%150.00%
jingoo hanjingoo han31.84%150.00%
Total163100.00%2100.00%

static struct platform_driver lp8788_rtc_driver = { .probe = lp8788_rtc_probe, .driver = { .name = LP8788_DEV_RTC, }, }; module_platform_driver(lp8788_rtc_driver); MODULE_DESCRIPTION("Texas Instruments LP8788 RTC Driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:lp8788-rtc");

Overall Contributors

PersonTokensPropCommitsCommitProp
kim milokim milo152099.41%133.33%
jingoo hanjingoo han90.59%266.67%
Total1529100.00%3100.00%
Directory: drivers/rtc
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}